mirror of
https://github.com/bitfireAT/davx5-ose.git
synced 2025-12-23 23:17:50 -05:00
Added widget with just a sync icon (#1340)
* Renamed existing widget Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Added sync icon widget Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Added labels for widgets Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Added widget preview Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Removed max width restriction Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Changed widget description Signed-off-by: Arnau Mora <arnyminerz@proton.me> --------- Signed-off-by: Arnau Mora <arnyminerz@proton.me> Co-authored-by: Ricki Hirner <hirner@bitfire.at>
This commit is contained in:
@@ -276,7 +276,8 @@
|
||||
</service>
|
||||
|
||||
<!-- Widgets -->
|
||||
<receiver android:name=".ui.widget.SyncButtonWidgetReceiver"
|
||||
<receiver android:name=".ui.widget.LabeledSyncButtonWidgetReceiver"
|
||||
android:label="@string/widget_labeled_sync_label"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
@@ -284,7 +285,18 @@
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_info_sync_button" />
|
||||
android:resource="@xml/widget_info_labeled_sync_button" />
|
||||
</receiver>
|
||||
<receiver android:name=".ui.widget.IconSyncButtonWidgetReceiver"
|
||||
android:label="@string/widget_icon_sync_label"
|
||||
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_info_icon_sync_button" />
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.ui.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.glance.ColorFilter
|
||||
import androidx.glance.GlanceId
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.Image
|
||||
import androidx.glance.ImageProvider
|
||||
import androidx.glance.LocalContext
|
||||
import androidx.glance.action.clickable
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import androidx.glance.appwidget.cornerRadius
|
||||
import androidx.glance.appwidget.provideContent
|
||||
import androidx.glance.background
|
||||
import androidx.glance.layout.Alignment
|
||||
import androidx.glance.layout.Box
|
||||
import androidx.glance.layout.fillMaxSize
|
||||
import androidx.glance.layout.size
|
||||
import androidx.glance.unit.ColorProvider
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.ui.M3ColorScheme
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
|
||||
/**
|
||||
* A widget with a "Sync all" button displaying just an icon to indicate the action.
|
||||
*/
|
||||
class IconSyncButtonWidget : GlanceAppWidget() {
|
||||
|
||||
// Hilt over @AndroidEntryPoint is not available for widgets
|
||||
@EntryPoint
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface SyncButtonWidgetEntryPoint {
|
||||
fun model(): SyncWidgetModel
|
||||
}
|
||||
|
||||
|
||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
||||
// initial data
|
||||
val entryPoint = EntryPointAccessors.fromApplication<SyncButtonWidgetEntryPoint>(context)
|
||||
val model = entryPoint.model()
|
||||
|
||||
// will be called when the widget is updated
|
||||
provideContent {
|
||||
WidgetContent(model)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WidgetContent(model: SyncWidgetModel) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Box(
|
||||
modifier = GlanceModifier
|
||||
.size(50.dp)
|
||||
.background(ColorProvider(M3ColorScheme.primaryLight))
|
||||
.cornerRadius(25.dp)
|
||||
.clickable {
|
||||
model.requestSync()
|
||||
Toast.makeText(context, R.string.sync_started, Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Image(
|
||||
provider = ImageProvider(R.drawable.ic_sync),
|
||||
contentDescription = context.getString(R.string.widget_sync_all_accounts),
|
||||
modifier = GlanceModifier.fillMaxSize().size(32.dp),
|
||||
colorFilter = ColorFilter.tint(
|
||||
ColorProvider(M3ColorScheme.onPrimaryLight)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,6 @@ package at.bitfire.davdroid.ui.widget
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import androidx.glance.appwidget.GlanceAppWidgetReceiver
|
||||
|
||||
class SyncButtonWidgetReceiver : GlanceAppWidgetReceiver() {
|
||||
override val glanceAppWidget: GlanceAppWidget = SyncButtonWidget()
|
||||
class IconSyncButtonWidgetReceiver : GlanceAppWidgetReceiver() {
|
||||
override val glanceAppWidget: GlanceAppWidget = IconSyncButtonWidget()
|
||||
}
|
||||
@@ -28,31 +28,23 @@ import androidx.glance.layout.size
|
||||
import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextDefaults
|
||||
import androidx.glance.unit.ColorProvider
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.repository.AccountRepository
|
||||
import at.bitfire.davdroid.sync.worker.SyncWorkerManager
|
||||
import at.bitfire.davdroid.ui.M3ColorScheme
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A widget with a "Sync all" button.
|
||||
* A widget with a "Sync all" button displaying an icon and a label.
|
||||
*/
|
||||
class SyncButtonWidget : GlanceAppWidget() {
|
||||
class LabeledSyncButtonWidget : GlanceAppWidget() {
|
||||
|
||||
// Hilt over @AndroidEntryPoint is not available for widgets
|
||||
@EntryPoint
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface SyncButtonWidgetEntryPoint {
|
||||
fun model(): Model
|
||||
fun model(): SyncWidgetModel
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +60,7 @@ class SyncButtonWidget : GlanceAppWidget() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WidgetContent(model: Model) {
|
||||
private fun WidgetContent(model: SyncWidgetModel) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Row(
|
||||
@@ -105,18 +97,4 @@ class SyncButtonWidget : GlanceAppWidget() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Model @Inject constructor(
|
||||
private val accountRepository: AccountRepository,
|
||||
@ApplicationContext val context: Context,
|
||||
private val syncWorkerManager: SyncWorkerManager
|
||||
): ViewModel() {
|
||||
|
||||
fun requestSync() = viewModelScope.launch(Dispatchers.Default) {
|
||||
for (account in accountRepository.getAll())
|
||||
syncWorkerManager.enqueueOneTimeAllAuthorities(account, manual = true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.ui.widget
|
||||
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import androidx.glance.appwidget.GlanceAppWidgetReceiver
|
||||
|
||||
class LabeledSyncButtonWidgetReceiver : GlanceAppWidgetReceiver() {
|
||||
override val glanceAppWidget: GlanceAppWidget = LabeledSyncButtonWidget()
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.ui.widget
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.bitfire.davdroid.repository.AccountRepository
|
||||
import at.bitfire.davdroid.sync.worker.SyncWorkerManager
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class SyncWidgetModel @Inject constructor(
|
||||
private val accountRepository: AccountRepository,
|
||||
@ApplicationContext val context: Context,
|
||||
private val syncWorkerManager: SyncWorkerManager
|
||||
): ViewModel() {
|
||||
|
||||
fun requestSync() = viewModelScope.launch(Dispatchers.Default) {
|
||||
for (account in accountRepository.getAll())
|
||||
syncWorkerManager.enqueueOneTimeAllAuthorities(account, manual = true)
|
||||
}
|
||||
|
||||
}
|
||||
9
app/src/main/res/drawable/shape_rounded_primary.xml
Normal file
9
app/src/main/res/drawable/shape_rounded_primary.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/primaryColor"/>
|
||||
<corners android:radius="15dp"/>
|
||||
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||
</shape>
|
||||
21
app/src/main/res/layout/widget_preview_icon_sync_button.xml
Normal file
21
app/src/main/res/layout/widget_preview_icon_sync_button.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/shape_rounded_primary">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/widget_sync_all_accounts"
|
||||
android:src="@drawable/ic_sync"
|
||||
android:padding="4dp"
|
||||
app:tint="@android:color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@drawable/shape_rounded_primary">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/widget_sync_all_accounts"
|
||||
android:src="@drawable/ic_sync"
|
||||
android:padding="4dp"
|
||||
app:tint="@android:color/white" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/white"
|
||||
android:text="@string/widget_sync_all"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -521,6 +521,9 @@
|
||||
<!-- widgets -->
|
||||
<string name="widget_sync_all">Sync all</string>
|
||||
<string name="widget_sync_all_accounts">Sync all accounts</string>
|
||||
<string name="widget_labeled_sync_label">Labeled Sync Button</string>
|
||||
<string name="widget_icon_sync_label">Icon Sync Button</string>
|
||||
<string name="widget_sync_description">Tap to run synchronization manually.</string>
|
||||
|
||||
<!-- cert4android -->
|
||||
|
||||
|
||||
17
app/src/main/res/xml/widget_info_icon_sync_button.xml
Normal file
17
app/src/main/res/xml/widget_info_icon_sync_button.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
-->
|
||||
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:initialLayout="@layout/glance_default_loading_layout"
|
||||
android:minWidth="30dp"
|
||||
android:minHeight="50dp"
|
||||
android:targetCellWidth="1"
|
||||
android:targetCellHeight="1"
|
||||
android:resizeMode="none"
|
||||
android:previewLayout="@layout/widget_preview_icon_sync_button"
|
||||
android:description="@string/widget_sync_description"
|
||||
tools:ignore="UnusedAttribute">
|
||||
</appwidget-provider>
|
||||
@@ -1,4 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
-->
|
||||
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:initialLayout="@layout/glance_default_loading_layout"
|
||||
@@ -6,8 +10,9 @@
|
||||
android:minHeight="30dp"
|
||||
android:targetCellWidth="1"
|
||||
android:targetCellHeight="1"
|
||||
android:maxResizeWidth="250dp"
|
||||
android:maxResizeHeight="30dp"
|
||||
android:resizeMode="horizontal"
|
||||
android:previewLayout="@layout/widget_preview_labeled_sync_button"
|
||||
android:description="@string/widget_sync_description"
|
||||
tools:ignore="UnusedAttribute">
|
||||
</appwidget-provider>
|
||||
Reference in New Issue
Block a user