diff --git a/Jetchat/README.md b/Jetchat/README.md index 992389bdb5..0433a4585d 100644 --- a/Jetchat/README.md +++ b/Jetchat/README.md @@ -25,6 +25,10 @@ This sample showcases: + + + + ### Status: 🚧 In progress Jetchat is still in under development, and some features are not yet implemented. diff --git a/Jetchat/app/build.gradle.kts b/Jetchat/app/build.gradle.kts index f23f12439e..4972acab23 100644 --- a/Jetchat/app/build.gradle.kts +++ b/Jetchat/app/build.gradle.kts @@ -87,6 +87,8 @@ dependencies { implementation(composeBom) androidTestImplementation(composeBom) + implementation(libs.androidx.glance.appwidget) + implementation(libs.androidx.glance.material3) implementation(libs.kotlin.stdlib) implementation(libs.kotlinx.coroutines.android) diff --git a/Jetchat/app/src/main/AndroidManifest.xml b/Jetchat/app/src/main/AndroidManifest.xml index 602380c02e..b4ea01d944 100644 --- a/Jetchat/app/src/main/AndroidManifest.xml +++ b/Jetchat/app/src/main/AndroidManifest.xml @@ -35,6 +35,14 @@ - + + + + + + diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/components/JetchatDrawer.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/components/JetchatDrawer.kt index 5a5d11e482..1a78f008c1 100644 --- a/Jetchat/app/src/main/java/com/example/compose/jetchat/components/JetchatDrawer.kt +++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/components/JetchatDrawer.kt @@ -16,7 +16,13 @@ package com.example.compose.jetchat.components +import android.appwidget.AppWidgetManager +import android.content.ComponentName +import android.content.Context +import android.os.Build +import androidx.annotation.ChecksSdkIntAtLeast import androidx.annotation.DrawableRes +import androidx.annotation.RequiresApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -33,7 +39,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.windowInsetsTopHeight import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.Divider +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -44,13 +50,16 @@ import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.example.compose.jetchat.R import com.example.compose.jetchat.data.colleagueProfile import com.example.compose.jetchat.data.meProfile import com.example.compose.jetchat.theme.JetchatTheme +import com.example.compose.jetchat.widget.WidgetReceiver @Composable fun JetchatDrawerContent( @@ -73,6 +82,11 @@ fun JetchatDrawerContent( ProfileItem("Taylor Brooks", colleagueProfile.photo) { onProfileClicked(colleagueProfile.userId) } + if (widgetAddingIsSupported(LocalContext.current)) { + DividerItem(modifier = Modifier.padding(horizontal = 28.dp)) + DrawerItemHeader("Settings") + WidgetDiscoverability() + } } } @@ -90,6 +104,7 @@ private fun DrawerHeader() { ) } } + @Composable private fun DrawerItemHeader(text: String) { Box( @@ -182,7 +197,7 @@ private fun ProfileItem(text: String, @DrawableRes profilePic: Int?, onProfileCl @Composable fun DividerItem(modifier: Modifier = Modifier) { - Divider( + HorizontalDivider( modifier = modifier, color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f) ) @@ -199,6 +214,7 @@ fun DrawerPreview() { } } } + @Composable @Preview fun DrawerPreviewDark() { @@ -210,3 +226,42 @@ fun DrawerPreviewDark() { } } } + +@RequiresApi(Build.VERSION_CODES.O) +@Composable +private fun WidgetDiscoverability() { + val context = LocalContext.current + Row( + modifier = Modifier + .height(56.dp) + .fillMaxWidth() + .padding(horizontal = 12.dp) + .clip(CircleShape) + .clickable(onClick = { + addWidgetToHomeScreen(context) + }), + verticalAlignment = CenterVertically + ) { + Text( + stringResource(id = R.string.add_widget_to_home_page), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.padding(start = 12.dp) + ) + } +} + +@RequiresApi(Build.VERSION_CODES.O) +private fun addWidgetToHomeScreen(context: Context) { + val appWidgetManager = AppWidgetManager.getInstance(context) + val myProvider = ComponentName(context, WidgetReceiver::class.java) + if (widgetAddingIsSupported(context)) { + appWidgetManager.requestPinAppWidget(myProvider, null, null) + } +} + +@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O) +private fun widgetAddingIsSupported(context: Context): Boolean { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && + AppWidgetManager.getInstance(context).isRequestPinAppWidgetSupported +} diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/ConversationUiState.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/ConversationUiState.kt index 452e4eeb41..edd61c738d 100644 --- a/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/ConversationUiState.kt +++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/ConversationUiState.kt @@ -39,5 +39,5 @@ data class Message( val content: String, val timestamp: String, val image: Int? = null, - val authorImage: Int = if (author == "me") R.drawable.ali else R.drawable.someone_else + val authorImage: Int = if (author == "me") R.drawable.ali else R.drawable.someone_else, ) diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/data/FakeData.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/data/FakeData.kt index 1c996a40d3..102930dd64 100644 --- a/Jetchat/app/src/main/java/com/example/compose/jetchat/data/FakeData.kt +++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/data/FakeData.kt @@ -26,7 +26,7 @@ import com.example.compose.jetchat.data.EMOJIS.EMOJI_PINK_HEART import com.example.compose.jetchat.data.EMOJIS.EMOJI_POINTS import com.example.compose.jetchat.profile.ProfileScreenState -private val initialMessages = listOf( +val initialMessages = listOf( Message( "me", "Check it out!", @@ -62,9 +62,26 @@ private val initialMessages = listOf( "loading but haven’t found any good ones $EMOJI_MELTING $EMOJI_CLOUDS. " + "What’s the recommended way to load async data and emit composable widgets?", "8:03 PM" - ) + ), + Message( + "Shangeeth Sivan", + "Does anyone know about Glance Widgets its the new way to build widgets in Android!", + "8:08 PM" + ), + Message( + "Taylor Brooks", + "Wow! I never knew about Glance Widgets when was this added to the android ecosystem", + "8:10 PM" + ), + Message( + "John Glenn", + "Yeah its seems to be pretty new!", + "8:12 PM" + ), ) +val unreadMessages = initialMessages.filter { it.author != "me" } + val exampleUiState = ConversationUiState( initialMessages = initialMessages, channelName = "#composers", diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/theme/Themes.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/theme/Themes.kt index 667ef184f8..8fce79255d 100644 --- a/Jetchat/app/src/main/java/com/example/compose/jetchat/theme/Themes.kt +++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/theme/Themes.kt @@ -28,7 +28,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -private val JetchatDarkColorScheme = darkColorScheme( +val JetchatDarkColorScheme = darkColorScheme( primary = Blue80, onPrimary = Blue20, primaryContainer = Blue30, @@ -57,7 +57,7 @@ private val JetchatDarkColorScheme = darkColorScheme( outline = BlueGrey60 ) -private val JetchatLightColorScheme = lightColorScheme( +val JetchatLightColorScheme = lightColorScheme( primary = Blue40, onPrimary = Color.White, primaryContainer = Blue90, @@ -83,7 +83,7 @@ private val JetchatLightColorScheme = lightColorScheme( inverseOnSurface = Grey95, surfaceVariant = BlueGrey90, onSurfaceVariant = BlueGrey30, - outline = BlueGrey50 + outline = BlueGrey50, ) @SuppressLint("NewApi") diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/JetChatWidget.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/JetChatWidget.kt new file mode 100644 index 0000000000..437309351f --- /dev/null +++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/JetChatWidget.kt @@ -0,0 +1,36 @@ +/* + * Copyright 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 + * + * https://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.example.compose.jetchat.widget + +import android.content.Context +import androidx.glance.GlanceId +import androidx.glance.GlanceTheme +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.provideContent +import com.example.compose.jetchat.data.unreadMessages +import com.example.compose.jetchat.widget.composables.MessagesWidget + +class JetChatWidget : GlanceAppWidget() { + + override suspend fun provideGlance(context: Context, id: GlanceId) { + provideContent { + GlanceTheme { + MessagesWidget(unreadMessages.toList()) + } + } + } +} diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/WidgetReceiver.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/WidgetReceiver.kt new file mode 100644 index 0000000000..ad67c3d170 --- /dev/null +++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/WidgetReceiver.kt @@ -0,0 +1,26 @@ +/* + * Copyright 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 + * + * https://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.example.compose.jetchat.widget + +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.GlanceAppWidgetReceiver + +class WidgetReceiver : GlanceAppWidgetReceiver() { + + override val glanceAppWidget: GlanceAppWidget + get() = JetChatWidget() +} diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/composables/MessagesWidget.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/composables/MessagesWidget.kt new file mode 100644 index 0000000000..0f6c4ea1a9 --- /dev/null +++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/composables/MessagesWidget.kt @@ -0,0 +1,87 @@ +/* + * Copyright 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 + * + * https://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.example.compose.jetchat.widget.composables + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.glance.GlanceModifier +import androidx.glance.ImageProvider +import androidx.glance.LocalContext +import androidx.glance.action.actionStartActivity +import androidx.glance.action.clickable +import androidx.glance.appwidget.components.Scaffold +import androidx.glance.appwidget.components.TitleBar +import androidx.glance.appwidget.lazy.LazyColumn +import androidx.glance.layout.Column +import androidx.glance.layout.Spacer +import androidx.glance.layout.fillMaxWidth +import androidx.glance.layout.height +import androidx.glance.text.Text +import com.example.compose.jetchat.NavActivity +import com.example.compose.jetchat.R +import com.example.compose.jetchat.conversation.Message +import com.example.compose.jetchat.widget.theme.JetChatGlanceTextStyles +import com.example.compose.jetchat.widget.theme.JetchatGlanceColorScheme + +@Composable +fun MessagesWidget(messages: List) { + Scaffold(titleBar = { + TitleBar( + startIcon = ImageProvider(R.drawable.ic_jetchat), + iconColor = null, + title = LocalContext.current.getString(R.string.messages_widget_title), + ) + }, backgroundColor = JetchatGlanceColorScheme.colors.background) { + LazyColumn(modifier = GlanceModifier.fillMaxWidth()) { + messages.forEach { + item { + Column(modifier = GlanceModifier.fillMaxWidth()) { + MessageItem(it) + Spacer(modifier = GlanceModifier.height(10.dp)) + } + } + } + } + } +} + +@Composable +fun MessageItem(message: Message) { + Column(modifier = GlanceModifier.clickable(actionStartActivity()).fillMaxWidth()) { + Text( + text = message.author, + style = JetChatGlanceTextStyles.titleMedium + ) + Text( + text = message.content, + style = JetChatGlanceTextStyles.bodyMedium, + ) + } +} + +@Preview +@Composable +fun MessageItemPreview() { + MessageItem(Message("John", "This is a preview of the message Item", "8:02PM")) +} + +@Preview +@Composable +fun WidgetPreview() { + MessagesWidget(listOf(Message("John", "This is a preview of the message Item", "8:02PM"))) +} diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/theme/Theme.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/theme/Theme.kt new file mode 100644 index 0000000000..12a7199a95 --- /dev/null +++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/theme/Theme.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2023 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 + * + * https://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.example.compose.jetchat.widget.theme + +import androidx.glance.material3.ColorProviders +import com.example.compose.jetchat.theme.JetchatDarkColorScheme +import com.example.compose.jetchat.theme.JetchatLightColorScheme + +object JetchatGlanceColorScheme { + val colors = ColorProviders( + light = JetchatLightColorScheme, + dark = JetchatDarkColorScheme, + ) +} diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/theme/Type.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/theme/Type.kt new file mode 100644 index 0000000000..99b1b4183e --- /dev/null +++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/widget/theme/Type.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2023 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 + * + * https://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.example.compose.jetchat.widget.theme + +import androidx.compose.ui.unit.sp +import androidx.glance.text.FontWeight +import androidx.glance.text.TextStyle + +object JetChatGlanceTextStyles { + + val titleMedium = TextStyle( + fontSize = 16.sp, + color = JetchatGlanceColorScheme.colors.onSurfaceVariant, + fontWeight = FontWeight.Bold + ) + val bodyMedium = TextStyle( + fontSize = 16.sp, + color = JetchatGlanceColorScheme.colors.onSurfaceVariant, + fontWeight = FontWeight.Normal + ) +} diff --git a/Jetchat/app/src/main/res/drawable/widget_icon.png b/Jetchat/app/src/main/res/drawable/widget_icon.png new file mode 100644 index 0000000000..70d386ee16 Binary files /dev/null and b/Jetchat/app/src/main/res/drawable/widget_icon.png differ diff --git a/Jetchat/app/src/main/res/values/strings.xml b/Jetchat/app/src/main/res/values/strings.xml index aae598c1fe..bc04038a7a 100644 --- a/Jetchat/app/src/main/res/values/strings.xml +++ b/Jetchat/app/src/main/res/values/strings.xml @@ -67,5 +67,7 @@ More options Touch and hold to record Record voice message + JetChat unread messages + Add Widget to Home Page diff --git a/Jetchat/app/src/main/res/xml/widget_unread_messages_info.xml b/Jetchat/app/src/main/res/xml/widget_unread_messages_info.xml new file mode 100644 index 0000000000..69ea543dc8 --- /dev/null +++ b/Jetchat/app/src/main/res/xml/widget_unread_messages_info.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/Jetchat/screenshots/widget.png b/Jetchat/screenshots/widget.png new file mode 100644 index 0000000000..7a3cf48e39 Binary files /dev/null and b/Jetchat/screenshots/widget.png differ diff --git a/Jetchat/screenshots/widget_discoverability.png b/Jetchat/screenshots/widget_discoverability.png new file mode 100644 index 0000000000..a59e4d5b09 Binary files /dev/null and b/Jetchat/screenshots/widget_discoverability.png differ