Skip to content

Commit

Permalink
Glance Widget Solution
Browse files Browse the repository at this point in the history
Change-Id: Ia78dfdb509b31fb22b46c3cba3e5734856225674
  • Loading branch information
secondsun committed Mar 22, 2024
1 parent 4e032af commit 5b3599a
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 1 deletion.
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ fun createTestRepository(): ChatRepository {
coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default),
)
}

19 changes: 19 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,24 @@
android:enableOnBackInvokedCallback="true"
android:theme="@style/Theme.Social"
tools:targetApi="34">

<receiver
android:name=".widget.SociaLiteAppWidgetReceiver"
android:exported="true"
android:label="SociaLite">


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


<activity
android:name=".MainActivity"
android:exported="true"
Expand All @@ -48,6 +66,7 @@
android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
android:supportsPictureInPicture="true">


<!-- This activity is the one that's shown in the launcher. -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Bitmap?>(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,
),
)
}
}
}
}
}
}


}

Original file line number Diff line number Diff line change
@@ -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)
}


}
9 changes: 9 additions & 0 deletions app/src/main/res/xml/socialite_widget_info.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:resizeMode="horizontal|vertical"
android:minHeight="128dp"
android:minWidth="128dp"
android:updatePeriodMillis="3600000"
android:minResizeHeight="64dp"
android:minResizeWidth="64dp"
android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>

0 comments on commit 5b3599a

Please sign in to comment.