Skip to content

Commit 2d4c3c0

Browse files
committed
Added sync icon widget
Signed-off-by: Arnau Mora <[email protected]>
1 parent 5b7fa4e commit 2d4c3c0

6 files changed

+148
-25
lines changed

app/src/main/AndroidManifest.xml

+10
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,16 @@
289289
android:name="android.appwidget.provider"
290290
android:resource="@xml/widget_info_labeled_sync_button" />
291291
</receiver>
292+
<receiver android:name=".ui.widget.IconSyncButtonWidgetReceiver"
293+
android:exported="true">
294+
<intent-filter>
295+
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
296+
</intent-filter>
297+
298+
<meta-data
299+
android:name="android.appwidget.provider"
300+
android:resource="@xml/widget_info_icon_sync_button" />
301+
</receiver>
292302

293303
</application>
294304

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3+
*/
4+
5+
package at.bitfire.davdroid.ui.widget
6+
7+
import android.content.Context
8+
import android.widget.Toast
9+
import androidx.compose.runtime.Composable
10+
import androidx.compose.ui.unit.dp
11+
import androidx.glance.ColorFilter
12+
import androidx.glance.GlanceId
13+
import androidx.glance.GlanceModifier
14+
import androidx.glance.Image
15+
import androidx.glance.ImageProvider
16+
import androidx.glance.LocalContext
17+
import androidx.glance.action.clickable
18+
import androidx.glance.appwidget.GlanceAppWidget
19+
import androidx.glance.appwidget.cornerRadius
20+
import androidx.glance.appwidget.provideContent
21+
import androidx.glance.background
22+
import androidx.glance.layout.Alignment
23+
import androidx.glance.layout.Box
24+
import androidx.glance.layout.fillMaxSize
25+
import androidx.glance.layout.size
26+
import androidx.glance.unit.ColorProvider
27+
import at.bitfire.davdroid.R
28+
import at.bitfire.davdroid.ui.M3ColorScheme
29+
import dagger.hilt.EntryPoint
30+
import dagger.hilt.InstallIn
31+
import dagger.hilt.android.EntryPointAccessors
32+
import dagger.hilt.components.SingletonComponent
33+
34+
/**
35+
* A widget with a "Sync all" button displaying just an icon to indicate the action.
36+
*/
37+
class IconSyncButtonWidget : GlanceAppWidget() {
38+
39+
// Hilt over @AndroidEntryPoint is not available for widgets
40+
@EntryPoint
41+
@InstallIn(SingletonComponent::class)
42+
interface SyncButtonWidgetEntryPoint {
43+
fun model(): SyncWidgetModel
44+
}
45+
46+
47+
override suspend fun provideGlance(context: Context, id: GlanceId) {
48+
// initial data
49+
val entryPoint = EntryPointAccessors.fromApplication<SyncButtonWidgetEntryPoint>(context)
50+
val model = entryPoint.model()
51+
52+
// will be called when the widget is updated
53+
provideContent {
54+
WidgetContent(model)
55+
}
56+
}
57+
58+
@Composable
59+
private fun WidgetContent(model: SyncWidgetModel) {
60+
val context = LocalContext.current
61+
62+
Box(
63+
modifier = GlanceModifier
64+
.size(50.dp)
65+
.background(ColorProvider(M3ColorScheme.primaryLight))
66+
.cornerRadius(25.dp)
67+
.clickable {
68+
model.requestSync()
69+
Toast.makeText(context, R.string.sync_started, Toast.LENGTH_SHORT).show()
70+
},
71+
contentAlignment = Alignment.Center,
72+
) {
73+
Image(
74+
provider = ImageProvider(R.drawable.ic_sync),
75+
contentDescription = context.getString(R.string.widget_sync_all_accounts),
76+
modifier = GlanceModifier.fillMaxSize().size(32.dp),
77+
colorFilter = ColorFilter.tint(
78+
ColorProvider(M3ColorScheme.onPrimaryLight)
79+
)
80+
)
81+
}
82+
}
83+
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3+
*/
4+
5+
package at.bitfire.davdroid.ui.widget
6+
7+
import androidx.glance.appwidget.GlanceAppWidget
8+
import androidx.glance.appwidget.GlanceAppWidgetReceiver
9+
10+
class IconSyncButtonWidgetReceiver : GlanceAppWidgetReceiver() {
11+
override val glanceAppWidget: GlanceAppWidget = IconSyncButtonWidget()
12+
}

app/src/main/kotlin/at/bitfire/davdroid/ui/widget/LabeledSyncButtonWidget.kt

+3-25
Original file line numberDiff line numberDiff line change
@@ -28,31 +28,23 @@ import androidx.glance.layout.size
2828
import androidx.glance.text.Text
2929
import androidx.glance.text.TextDefaults
3030
import androidx.glance.unit.ColorProvider
31-
import androidx.lifecycle.ViewModel
32-
import androidx.lifecycle.viewModelScope
3331
import at.bitfire.davdroid.R
34-
import at.bitfire.davdroid.repository.AccountRepository
35-
import at.bitfire.davdroid.sync.worker.SyncWorkerManager
3632
import at.bitfire.davdroid.ui.M3ColorScheme
3733
import dagger.hilt.EntryPoint
3834
import dagger.hilt.InstallIn
3935
import dagger.hilt.android.EntryPointAccessors
40-
import dagger.hilt.android.qualifiers.ApplicationContext
4136
import dagger.hilt.components.SingletonComponent
42-
import kotlinx.coroutines.Dispatchers
43-
import kotlinx.coroutines.launch
44-
import javax.inject.Inject
4537

4638
/**
47-
* A widget with a "Sync all" button.
39+
* A widget with a "Sync all" button displaying an icon and a label.
4840
*/
4941
class LabeledSyncButtonWidget : GlanceAppWidget() {
5042

5143
// Hilt over @AndroidEntryPoint is not available for widgets
5244
@EntryPoint
5345
@InstallIn(SingletonComponent::class)
5446
interface SyncButtonWidgetEntryPoint {
55-
fun model(): Model
47+
fun model(): SyncWidgetModel
5648
}
5749

5850

@@ -68,7 +60,7 @@ class LabeledSyncButtonWidget : GlanceAppWidget() {
6860
}
6961

7062
@Composable
71-
private fun WidgetContent(model: Model) {
63+
private fun WidgetContent(model: SyncWidgetModel) {
7264
val context = LocalContext.current
7365

7466
Row(
@@ -105,18 +97,4 @@ class LabeledSyncButtonWidget : GlanceAppWidget() {
10597
}
10698
}
10799

108-
109-
class Model @Inject constructor(
110-
private val accountRepository: AccountRepository,
111-
@ApplicationContext val context: Context,
112-
private val syncWorkerManager: SyncWorkerManager
113-
): ViewModel() {
114-
115-
fun requestSync() = viewModelScope.launch(Dispatchers.Default) {
116-
for (account in accountRepository.getAll())
117-
syncWorkerManager.enqueueOneTimeAllAuthorities(account, manual = true)
118-
}
119-
120-
}
121-
122100
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3+
*/
4+
5+
package at.bitfire.davdroid.ui.widget
6+
7+
import android.content.Context
8+
import androidx.lifecycle.ViewModel
9+
import androidx.lifecycle.viewModelScope
10+
import at.bitfire.davdroid.repository.AccountRepository
11+
import at.bitfire.davdroid.sync.worker.SyncWorkerManager
12+
import dagger.hilt.android.qualifiers.ApplicationContext
13+
import kotlinx.coroutines.Dispatchers
14+
import kotlinx.coroutines.launch
15+
import javax.inject.Inject
16+
17+
class SyncWidgetModel @Inject constructor(
18+
private val accountRepository: AccountRepository,
19+
@ApplicationContext val context: Context,
20+
private val syncWorkerManager: SyncWorkerManager
21+
): ViewModel() {
22+
23+
fun requestSync() = viewModelScope.launch(Dispatchers.Default) {
24+
for (account in accountRepository.getAll())
25+
syncWorkerManager.enqueueOneTimeAllAuthorities(account, manual = true)
26+
}
27+
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
4+
android:initialLayout="@layout/glance_default_loading_layout"
5+
android:minWidth="30dp"
6+
android:minHeight="50dp"
7+
android:targetCellWidth="1"
8+
android:targetCellHeight="1"
9+
android:resizeMode="none"
10+
tools:ignore="UnusedAttribute">
11+
</appwidget-provider>

0 commit comments

Comments
 (0)