Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Widget Starter Bits #51

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.google.android.samples.socialite.ui.Main
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import com.google.android.samples.socialite.data.MessageDao
import com.google.android.samples.socialite.data.RoomDatabaseManager
import com.google.android.samples.socialite.data.populateInitialData
import com.google.android.samples.socialite.widget.model.WidgetModelDao
import com.google.android.samples.socialite.widget.model.WidgetModelRepository
import dagger.Binds
import dagger.Module
import dagger.Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,58 +16,12 @@

package com.google.android.samples.socialite.widget


import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.core.net.toUri
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.LocalContext
import androidx.glance.action.actionStartActivity
import androidx.glance.action.clickable
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetManager
import androidx.glance.appwidget.ImageProvider
import androidx.glance.appwidget.action.actionStartActivity
import androidx.glance.appwidget.components.Scaffold
import androidx.glance.appwidget.components.TitleBar
import androidx.glance.appwidget.lazy.LazyColumn
import androidx.glance.appwidget.provideContent
import androidx.glance.layout.Alignment
import androidx.glance.layout.Box
import androidx.glance.layout.Column
import androidx.glance.layout.ContentScale
import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.layout.size
import androidx.glance.layout.wrapContentHeight
import androidx.glance.text.FontFamily
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import com.google.android.samples.socialite.MainActivity
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.runBlocking

class SociaLiteAppWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
TODO("Not yet implemented")
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class SociaLiteAppWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = TODO("Create instance of SociaLiteAppWidget")
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ interface WidgetModelDao {
@Delete
suspend fun delete(model: WidgetModel)

@Query("SELECT * FROM WidgetModel where widgetId NOT IN (:widgetIds)")
fun findOrphanModels(widgetIds: List<Int>): List<WidgetModel>

@Query("SELECT * FROM WidgetModel where contactId = :contactId")
fun modelsForContact(contactId: Long): List<WidgetModel?>
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import android.graphics.Bitmap
import android.net.Uri
import android.util.Log
import androidx.core.graphics.drawable.toBitmapOrNull
import androidx.glance.appwidget.GlanceAppWidgetManager
import androidx.glance.appwidget.updateAll
import coil.Coil
import coil.request.CachePolicy
Expand All @@ -44,15 +45,13 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch


@Singleton
class WidgetModelRepository @Inject internal constructor(private val widgetModelDao: WidgetModelDao, @AppCoroutineScope private val coroutineScope: CoroutineScope, @ApplicationContext private val appContext: Context) {


@EntryPoint
@InstallIn(SingletonComponent::class)
interface WidgetModelRepositoryEntryoint {
fun widgetModelRepository() : WidgetModelRepository;
fun widgetModelRepository(): WidgetModelRepository
}

companion object {
Expand All @@ -61,9 +60,8 @@ class WidgetModelRepository @Inject internal constructor(private val widgetModel
applicationContext,
WidgetModelRepositoryEntryoint::class.java,
)
return widgetModelRepositoryEntryoint.widgetModelRepository();
return widgetModelRepositoryEntryoint.widgetModelRepository()
}

}

suspend fun create(model: WidgetModel): WidgetModel {
Expand All @@ -75,14 +73,17 @@ class WidgetModelRepository @Inject internal constructor(private val widgetModel
return widgetModelDao.loadWidgetModel(widgetId).distinctUntilChanged()
}

fun delete(appWidgetIds: IntArray) {
fun cleanupWidgetModels(context: Context) {
coroutineScope.launch {
appWidgetIds.forEach { appWidgetId ->
widgetModelDao.loadWidgetModel(appWidgetId).collect { model ->
if (model != null) {
widgetModelDao.delete(model)
}
}
val widgetManager = GlanceAppWidgetManager(context)
val widgetIds = widgetManager.getGlanceIds(SociaLiteAppWidget::class.java).map { glanceId ->
widgetManager.getAppWidgetId(glanceId)
}.toList()

widgetModelDao.findOrphanModels(widgetIds).forEach { model ->
widgetModelDao.delete(
model,
)
}
}
}
Expand All @@ -96,7 +97,6 @@ class WidgetModelRepository @Inject internal constructor(private val widgetModel
}
}


suspend fun getImage(url: Uri, force: Boolean = false, context: Context): Bitmap? {
val request =
ImageRequest.Builder(context).transformations(CircleCropTransformation()).data(url)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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.ui

import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.action.clickable
import androidx.glance.appwidget.ImageProvider
import androidx.glance.layout.Alignment
import androidx.glance.layout.Column
import androidx.glance.layout.ContentScale
import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.layout.size
import androidx.glance.layout.wrapContentHeight
import androidx.glance.text.FontFamily
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import com.google.android.samples.socialite.model.Contact

@Composable
fun ContactRow(contact: Contact, profileImageUri: Uri, onClick: () -> Unit) {
Row(
modifier = GlanceModifier.fillMaxWidth().clickable(onClick).wrapContentHeight().padding(4.dp),
verticalAlignment = Alignment.Vertical.CenterVertically,
) {
Image(
modifier = GlanceModifier.size(48.dp).padding(end = 8.dp),
contentScale = ContentScale.Fit,
provider = ImageProvider(profileImageUri),
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,80 @@
/*
* 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.ui

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp
import androidx.core.net.toUri
import androidx.glance.GlanceModifier
import androidx.glance.Image
import androidx.glance.action.Action
import androidx.glance.action.clickable
import androidx.glance.appwidget.ImageProvider
import androidx.glance.background
import androidx.glance.layout.Alignment
import androidx.glance.layout.Box
import androidx.glance.layout.Column
import androidx.glance.layout.ContentScale
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.wrapContentHeight
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import androidx.glance.unit.ColorProvider
import com.google.android.samples.socialite.R
import com.google.android.samples.socialite.widget.model.WidgetModel

@Composable
fun FavoriteContact(model: WidgetModel, onClick: Action) {
Box(
modifier = GlanceModifier.fillMaxSize().clickable(onClick),
contentAlignment = Alignment.TopCenter,
) {
Image(
modifier = GlanceModifier.fillMaxSize(),
provider = ImageProvider(model.photo.toUri()),
contentScale = ContentScale.Crop,
contentDescription = model.displayName,
)
Column(
modifier = GlanceModifier.fillMaxWidth().wrapContentHeight()
.background(imageProvider = androidx.glance.ImageProvider(R.drawable.widget_text_gradient)),
verticalAlignment = Alignment.Vertical.Top,
horizontalAlignment = Alignment.Horizontal.CenterHorizontally,
) {
Text(
text = model.displayName,
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 24.sp,
color = ColorProvider(Color.White),
),
)

Text(
text = if (model.unreadMessages) "New Message!" else "No messages",
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 16.sp,
color = ColorProvider(Color.White),
),
)
}
}
}
Loading