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

[JetChat] Add glance widget for JetChat App #1424 #1425

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions Jetchat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ This sample showcases:

<img src="screenshots/screenshots.png"/>

<img src="screenshots/widget.png" width="300"/>

<img src="screenshots/widget_discoverability.png" width="300"/>

### Status: 🚧 In progress

Jetchat is still in under development, and some features are not yet implemented.
Expand Down
2 changes: 2 additions & 0 deletions Jetchat/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
11 changes: 10 additions & 1 deletion Jetchat/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:name=".JetchatApplication"
android:theme="@style/Theme.Jetchat.NoActionBar">

<activity
Expand All @@ -35,6 +36,14 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<receiver android:name=".widget.WidgetReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_unread_messages_info" />
</receiver>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2020 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

import android.app.Application
import com.example.compose.jetchat.data.AppContainer
import com.example.compose.jetchat.data.AppContainerImpl

class JetchatApplication : Application() {

// AppContainer instance used by the rest of classes to obtain dependencies
lateinit var container: AppContainer

override fun onCreate() {
super.onCreate()
container = AppContainerImpl()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@

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.compose.foundation.Image
import androidx.compose.foundation.background
Expand Down Expand Up @@ -44,13 +49,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(
Expand All @@ -73,6 +81,11 @@ fun JetchatDrawerContent(
ProfileItem("Taylor Brooks", colleagueProfile.photo) {
onProfileClicked(colleagueProfile.userId)
}
DividerItem(modifier = Modifier.padding(horizontal = 28.dp))
DrawerItemHeader("Settings")
if (widgetAddingIsSupported(LocalContext.current)) {
WidgetDiscoverability()
}
}
}

Expand All @@ -90,6 +103,7 @@ private fun DrawerHeader() {
)
}
}

@Composable
private fun DrawerItemHeader(text: String) {
Box(
Expand Down Expand Up @@ -199,6 +213,7 @@ fun DrawerPreview() {
}
}
}

@Composable
@Preview
fun DrawerPreviewDark() {
Expand All @@ -210,3 +225,41 @@ fun DrawerPreviewDark() {
}
}
}

@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)
)
}
}

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 {
val appWidgetManager = AppWidgetManager.getInstance(context)
return Build.VERSION.SDK_INT >=
Build.VERSION_CODES.O && appWidgetManager.isRequestPinAppWidgetSupported
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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.data

import com.example.compose.jetchat.data.messages.MessagesRepository
import com.example.compose.jetchat.data.messages.MessagesRepositoryImpl

interface AppContainer {
val messagesRepository: MessagesRepository
}

class AppContainerImpl : AppContainer {
override val messagesRepository by lazy {
MessagesRepositoryImpl()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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!",
Expand Down Expand Up @@ -62,7 +62,22 @@ 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 exampleUiState = ConversationUiState(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.data.messages

import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.snapshots.SnapshotStateList
import com.example.compose.jetchat.conversation.Message
import com.example.compose.jetchat.data.initialMessages

interface MessagesRepository {

fun observeUnReadMessages(): SnapshotStateList<Message>
}

class MessagesRepositoryImpl : MessagesRepository {

private val unReadMessages = mutableStateListOf<Message>()

init {
unReadMessages.addAll(initialMessages.filter { it.author != "me" })
}

override fun observeUnReadMessages(): SnapshotStateList<Message> {
return unReadMessages
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -57,7 +57,7 @@ private val JetchatDarkColorScheme = darkColorScheme(
outline = BlueGrey60
)

private val JetchatLightColorScheme = lightColorScheme(
val JetchatLightColorScheme = lightColorScheme(
primary = Blue40,
onPrimary = Color.White,
primaryContainer = Blue90,
Expand All @@ -83,7 +83,7 @@ private val JetchatLightColorScheme = lightColorScheme(
inverseOnSurface = Grey95,
surfaceVariant = BlueGrey90,
onSurfaceVariant = BlueGrey30,
outline = BlueGrey50
outline = BlueGrey50,
)

@SuppressLint("NewApi")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.JetchatApplication
import com.example.compose.jetchat.widget.composables.MessagesWidget

class JetChatWidget : GlanceAppWidget() {

override suspend fun provideGlance(context: Context, id: GlanceId) {
val application = context.applicationContext as JetchatApplication
val messagesRepository = application.container.messagesRepository

provideContent {
val bookmarks = messagesRepository.observeUnReadMessages()
GlanceTheme {
MessagesWidget(bookmarks.toList())
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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() {
shangeethsivan marked this conversation as resolved.
Show resolved Hide resolved

override val glanceAppWidget: GlanceAppWidget
get() = JetChatWidget()
}
Loading