From 5b3599a7b15256be28d55fb0775e3a908f317f29 Mon Sep 17 00:00:00 2001 From: Summers Pittman Date: Fri, 22 Mar 2024 13:34:19 -0400 Subject: [PATCH] Glance Widget Solution Change-Id: Ia78dfdb509b31fb22b46c3cba3e5734856225674 --- app/build.gradle.kts | 3 + .../socialite/repository/TestRepository.kt | 1 + app/src/main/AndroidManifest.xml | 19 +++ .../socialite/widget/SociaLiteAppWidget.kt | 151 +++++++++++++++++- .../widget/SociaLiteAppWidgetReceiver.kt | 35 ++++ .../main/res/xml/socialite_widget_info.xml | 9 ++ 6 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/google/android/samples/socialite/widget/SociaLiteAppWidgetReceiver.kt create mode 100644 app/src/main/res/xml/socialite_widget_info.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9e66f891..d698e34b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -82,6 +82,9 @@ dependencies { androidTestImplementation(libs.espresso.core) baselineProfile(project(":baselineprofile")) + implementation(libs.glance.appwidget) + implementation(libs.glance.material) + val composeBom = platform(libs.compose.bom) implementation(composeBom) androidTestImplementation(composeBom) diff --git a/app/src/androidTest/java/com/google/android/samples/socialite/repository/TestRepository.kt b/app/src/androidTest/java/com/google/android/samples/socialite/repository/TestRepository.kt index 4761808e..e349f771 100644 --- a/app/src/androidTest/java/com/google/android/samples/socialite/repository/TestRepository.kt +++ b/app/src/androidTest/java/com/google/android/samples/socialite/repository/TestRepository.kt @@ -35,3 +35,4 @@ fun createTestRepository(): ChatRepository { coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default), ) } + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fffc9253..c7a378b7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -40,6 +40,24 @@ android:enableOnBackInvokedCallback="true" android:theme="@style/Theme.Social" tools:targetApi="34"> + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/google/android/samples/socialite/widget/SociaLiteAppWidget.kt b/app/src/main/java/com/google/android/samples/socialite/widget/SociaLiteAppWidget.kt index 2af3d23e..2384258f 100644 --- a/app/src/main/java/com/google/android/samples/socialite/widget/SociaLiteAppWidget.kt +++ b/app/src/main/java/com/google/android/samples/socialite/widget/SociaLiteAppWidget.kt @@ -63,11 +63,160 @@ import com.google.android.samples.socialite.R import com.google.android.samples.socialite.model.Contact import com.google.android.samples.socialite.widget.model.WidgetModel import com.google.android.samples.socialite.widget.model.WidgetModelRepository +import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking class SociaLiteAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { - TODO("Not yet implemented") + provideContent { + GlanceTheme { + Content(context, id) + } + } } + @Composable + private fun Content(context: Context, id: GlanceId) { + + val widgetId = GlanceAppWidgetManager(context).getAppWidgetId(id) + val repository = WidgetModelRepository.get(context) + val model = repository.loadModel(widgetId).collectAsState(null).value + + Scaffold( + titleBar = { + TitleBar( + textColor = GlanceTheme.colors.onSurface, + startIcon = ImageProvider(R.mipmap.ic_launcher), + title = "SociaLite" + ) + }, + backgroundColor = GlanceTheme.colors.widgetBackground, + modifier = GlanceModifier.fillMaxSize() + ) { + when (model) { + null -> ZeroState(repository, widgetId, context) + else -> { + FavoriteContact(model) + } + } + + } + } + + @Composable + private fun FavoriteContact(model: WidgetModel) { + val appContext = LocalContext.current.applicationContext + Box( + modifier = GlanceModifier.fillMaxSize().then( + + if (model.unreadMessages) { + GlanceModifier.clickable( + actionStartActivity( + Intent(appContext, MainActivity::class.java) + .setAction(Intent.ACTION_VIEW) + .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + .setData("https://socialite.google.com/chat/${model.contactId}".toUri()), + ), + ) + } else { + GlanceModifier + }, + ), + contentAlignment = Alignment.Center, + ) { + Column { + Image( + provider = ImageProvider(model.photo.toUri()), + contentDescription = model.displayName, + ) + Text( + text = model.displayName, + style = TextStyle( + fontWeight = FontWeight.Bold, + color = GlanceTheme.colors.onBackground, + ), + ) + Text( + text = if (model.unreadMessages) "New Message!" else "No messages", + style = TextStyle(color = GlanceTheme.colors.onBackground), + ) + } + } + } + + + @Composable + fun ZeroState(repository: WidgetModelRepository, widgetId: Int, context: Context) { + Box(modifier = GlanceModifier.fillMaxSize(), contentAlignment = Alignment.TopStart) { + LazyColumn { + items(Contact.CONTACTS.size) { contactIndex -> + val contact = Contact.CONTACTS[contactIndex] + val profileImage = remember(contact.iconUri) { mutableStateOf(null) } + + + LaunchedEffect(contact.iconUri) { + profileImage.value = repository.getImage(contact.iconUri, false, context) + } + + + Row( + modifier = GlanceModifier.fillMaxWidth().clickable { + runBlocking { + repository.create( + WidgetModel( + widgetId, + contact.id, + contact.name, + contact.iconUri.toString(), + false, + ), + ) + } + }.wrapContentHeight().padding(4.dp), + verticalAlignment = Alignment.Vertical.CenterVertically, + ) { + Image( + modifier = GlanceModifier.size(48.dp).padding(end = 8.dp), + contentScale = ContentScale.Fit, + provider = if (profileImage.value == null) { + ImageProvider(R.drawable.ic_launcher_background) + } else { + ImageProvider( + profileImage.value!!, + ) + }, + contentDescription = "Avatar", + + + ) + Column( + modifier = GlanceModifier.defaultWeight(), + horizontalAlignment = Alignment.Horizontal.Start, + ) { + Text( + text = contact.name, + style = TextStyle( + fontWeight = FontWeight.Bold, + fontSize = 16.sp, + color = GlanceTheme.colors.onBackground, + ), + ) + Text( + text = "Click to select as favorite contact", + style = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 12.sp, + fontFamily = FontFamily.Monospace, + color = GlanceTheme.colors.onBackground, + ), + ) + } + } + } + } + } + } + + } + diff --git a/app/src/main/java/com/google/android/samples/socialite/widget/SociaLiteAppWidgetReceiver.kt b/app/src/main/java/com/google/android/samples/socialite/widget/SociaLiteAppWidgetReceiver.kt new file mode 100644 index 00000000..4a78b0f2 --- /dev/null +++ b/app/src/main/java/com/google/android/samples/socialite/widget/SociaLiteAppWidgetReceiver.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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 + +class SociaLiteAppWidgetReceiver :GlanceAppWidgetReceiver() { + override val glanceAppWidget: GlanceAppWidget = SociaLiteAppWidget() + + + override fun onDeleted(context: Context, appWidgetIds: IntArray) { + super.onDeleted(context, appWidgetIds) + val repository = WidgetModelRepository.get(context) + repository.delete(appWidgetIds) + } + + +} diff --git a/app/src/main/res/xml/socialite_widget_info.xml b/app/src/main/res/xml/socialite_widget_info.xml new file mode 100644 index 00000000..c6f9833e --- /dev/null +++ b/app/src/main/res/xml/socialite_widget_info.xml @@ -0,0 +1,9 @@ + +