diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt index f4a0ee37..632093db 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt @@ -74,6 +74,7 @@ import de.mm20.launcher2.widgets.AppWidget import de.mm20.launcher2.widgets.CalendarWidget import de.mm20.launcher2.widgets.FavoritesWidget import de.mm20.launcher2.widgets.MusicWidget +import de.mm20.launcher2.widgets.NotesWidget import de.mm20.launcher2.widgets.WeatherWidget import de.mm20.launcher2.widgets.Widget import org.koin.androidx.compose.get @@ -110,6 +111,7 @@ fun ConfigureWidgetSheet( is CalendarWidget -> ConfigureCalendarWidget(widget, onWidgetUpdated) is FavoritesWidget -> ConfigureFavoritesWidget(widget, onWidgetUpdated) is MusicWidget -> ConfigureMusicWidget() + is NotesWidget -> ConfigureNotesWidget(widget, onWidgetUpdated) } } @@ -304,6 +306,7 @@ fun ColumnScope.ConfigureAppWidget( is MusicWidget -> it.copy(id = widget.id) is CalendarWidget -> it.copy(id = widget.id) is FavoritesWidget -> it.copy(id = widget.id) + is NotesWidget -> it.copy(id = widget.id) } onWidgetUpdated(updatedWidget) replaceWidget = false @@ -537,4 +540,12 @@ fun ColumnScope.ConfigureCalendarWidget( text = stringResource(R.string.widget_config_calendar_no_calendars) ) } +} + +@Composable +fun ConfigureNotesWidget( + widget: NotesWidget, + onWidgetUpdated: (NotesWidget) -> Unit +) { + } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/WidgetPickerSheet.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/WidgetPickerSheet.kt index aaf82423..93b37f27 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/WidgetPickerSheet.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/WidgetPickerSheet.kt @@ -30,6 +30,7 @@ import androidx.compose.material.icons.rounded.LightMode import androidx.compose.material.icons.rounded.MusicNote import androidx.compose.material.icons.rounded.Search import androidx.compose.material.icons.rounded.Star +import androidx.compose.material.icons.rounded.StickyNote2 import androidx.compose.material.icons.rounded.Today import androidx.compose.material.icons.rounded.Widgets import androidx.compose.material.icons.rounded.Work @@ -64,6 +65,7 @@ import de.mm20.launcher2.widgets.AppWidget import de.mm20.launcher2.widgets.AppWidgetConfig import de.mm20.launcher2.widgets.FavoritesWidget import de.mm20.launcher2.widgets.MusicWidget +import de.mm20.launcher2.widgets.NotesWidget import de.mm20.launcher2.widgets.WeatherWidget import de.mm20.launcher2.widgets.Widget import java.util.UUID @@ -287,6 +289,7 @@ fun WidgetPickerSheet( CalendarWidget.Type -> CalendarWidget(id) MusicWidget.Type -> MusicWidget(id) FavoritesWidget.Type -> FavoritesWidget(id) + NotesWidget.Type -> NotesWidget(id) else -> return@OutlinedCard } onWidgetSelected(widget) @@ -302,6 +305,7 @@ fun WidgetPickerSheet( CalendarWidget.Type -> Icons.Rounded.Today MusicWidget.Type -> Icons.Rounded.MusicNote FavoritesWidget.Type -> Icons.Rounded.Star + NotesWidget.Type -> Icons.Rounded.StickyNote2 else -> Icons.Rounded.Widgets }, contentDescription = null, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetItem.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetItem.kt index 26674a43..6f3fa00e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetItem.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetItem.kt @@ -45,11 +45,13 @@ import de.mm20.launcher2.ui.launcher.widgets.calendar.CalendarWidget import de.mm20.launcher2.ui.launcher.widgets.external.ExternalWidget import de.mm20.launcher2.ui.launcher.widgets.favorites.FavoritesWidget import de.mm20.launcher2.ui.launcher.widgets.music.MusicWidget +import de.mm20.launcher2.ui.launcher.widgets.notes.NotesWidget import de.mm20.launcher2.ui.launcher.widgets.weather.WeatherWidget import de.mm20.launcher2.widgets.AppWidget import de.mm20.launcher2.widgets.CalendarWidget import de.mm20.launcher2.widgets.FavoritesWidget import de.mm20.launcher2.widgets.MusicWidget +import de.mm20.launcher2.widgets.NotesWidget import de.mm20.launcher2.widgets.WeatherWidget import de.mm20.launcher2.widgets.Widget @@ -107,6 +109,7 @@ fun WidgetItem( is MusicWidget -> stringResource(R.string.widget_name_music) is CalendarWidget -> stringResource(R.string.widget_name_calendar) is FavoritesWidget -> stringResource(R.string.widget_name_favorites) + is NotesWidget -> stringResource(R.string.widget_name_notes) is AppWidget -> remember(widget.config.widgetId) { appWidget?.loadLabel( context.packageManager @@ -155,6 +158,10 @@ fun WidgetItem( FavoritesWidget(widget) } + is NotesWidget -> { + NotesWidget(widget) + } + is AppWidget -> { val widgetInfo = remember(widget.config.widgetId) { AppWidgetManager.getInstance(context) @@ -193,6 +200,7 @@ fun WidgetItem( is MusicWidget -> it.copy(id = widget.id) is CalendarWidget -> it.copy(id = widget.id) is FavoritesWidget -> it.copy(id = widget.id) + is NotesWidget -> it.copy(id = widget.id) } onWidgetUpdate(updatedWidget) replaceWidget = false diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/notes/NotesWidget.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/notes/NotesWidget.kt new file mode 100644 index 00000000..fe882d4d --- /dev/null +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/notes/NotesWidget.kt @@ -0,0 +1,37 @@ +package de.mm20.launcher2.ui.launcher.widgets.notes + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import de.mm20.launcher2.widgets.NotesWidget + +@Composable +fun NotesWidget(widget: NotesWidget) { + val viewModel: NotesWidgetVM = viewModel(key = "notes-widget-${widget.id}", factory = NotesWidgetVM.Factory) + + LaunchedEffect(widget) { + viewModel.updateWidget(widget) + } + + val text by viewModel.noteText + + BasicTextField( + value = text, + onValueChange = { viewModel.setText(it) }, + modifier = Modifier.fillMaxWidth().padding(16.dp), + textStyle = MaterialTheme.typography.bodyMedium.copy( + color = LocalContentColor.current, + ), + cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), + ) +} \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/notes/NotesWidgetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/notes/NotesWidgetVM.kt new file mode 100644 index 00000000..a9038807 --- /dev/null +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/notes/NotesWidgetVM.kt @@ -0,0 +1,48 @@ +package de.mm20.launcher2.ui.launcher.widgets.notes + +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import de.mm20.launcher2.services.widgets.WidgetsService +import de.mm20.launcher2.widgets.NotesWidget +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch +import org.koin.core.component.KoinComponent +import org.koin.core.component.get + +class NotesWidgetVM( + private val widgetsService: WidgetsService, +) : ViewModel() { + private val widget = MutableStateFlow(null) + + val noteText = mutableStateOf(widget.value?.config?.storedText ?: "") + + fun updateWidget(widget: NotesWidget) { + val oldId = this.widget.value?.id + this.widget.value = widget + if (widget.id != oldId) noteText.value = widget.config.storedText + } + + private var updateJob: Job? = null + fun setText(text: String) { + noteText.value = text + updateJob?.cancel() + val widget = widget.value ?: return + updateJob = viewModelScope.launch { + delay(1000) + widgetsService.updateWidget(widget.copy(config = widget.config.copy(storedText = text))) + } + } + + companion object: KoinComponent { + val Factory = viewModelFactory { + initializer { + NotesWidgetVM(get()) + } + } + } +} \ No newline at end of file diff --git a/core/i18n/src/main/res/values/strings.xml b/core/i18n/src/main/res/values/strings.xml index 84e1d717..b55af7a9 100644 --- a/core/i18n/src/main/res/values/strings.xml +++ b/core/i18n/src/main/res/values/strings.xml @@ -219,6 +219,7 @@ Calendar Music Favorites + Note Unknown app widget Add widget diff --git a/data/widgets/src/main/java/de/mm20/launcher2/widgets/NotesWidget.kt b/data/widgets/src/main/java/de/mm20/launcher2/widgets/NotesWidget.kt new file mode 100644 index 00000000..87e2ee02 --- /dev/null +++ b/data/widgets/src/main/java/de/mm20/launcher2/widgets/NotesWidget.kt @@ -0,0 +1,31 @@ +package de.mm20.launcher2.widgets + +import de.mm20.launcher2.database.entities.PartialWidgetEntity +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import java.util.UUID + +@Serializable +data class NotesWidgetConfig( + val storedText: String = "", + val quickActions: Boolean = true, +) + +data class NotesWidget( + override val id: UUID, + val config: NotesWidgetConfig = NotesWidgetConfig(), +) : Widget() { + + override fun toDatabaseEntity(): PartialWidgetEntity { + return PartialWidgetEntity( + id = id, + type = Type, + config = Json.encodeToString(config), + ) + } + + companion object { + const val Type = "notes" + } +} \ No newline at end of file diff --git a/data/widgets/src/main/java/de/mm20/launcher2/widgets/Widget.kt b/data/widgets/src/main/java/de/mm20/launcher2/widgets/Widget.kt index b8f39d5d..246bcb9e 100644 --- a/data/widgets/src/main/java/de/mm20/launcher2/widgets/Widget.kt +++ b/data/widgets/src/main/java/de/mm20/launcher2/widgets/Widget.kt @@ -54,6 +54,12 @@ sealed class Widget { config, ) } + NotesWidget.Type -> { + val config: NotesWidgetConfig = + Json.decodeFromStringOrNull(entity.config?.takeIf { it.isNotBlank() }) + ?: NotesWidgetConfig() + NotesWidget(entity.id, config) + } else -> null } diff --git a/services/widgets/src/main/java/de/mm20/launcher2/services/widgets/WidgetsService.kt b/services/widgets/src/main/java/de/mm20/launcher2/services/widgets/WidgetsService.kt index ba2f8ee3..5c8e008e 100644 --- a/services/widgets/src/main/java/de/mm20/launcher2/services/widgets/WidgetsService.kt +++ b/services/widgets/src/main/java/de/mm20/launcher2/services/widgets/WidgetsService.kt @@ -8,6 +8,7 @@ import androidx.core.content.getSystemService import de.mm20.launcher2.widgets.CalendarWidget import de.mm20.launcher2.widgets.FavoritesWidget import de.mm20.launcher2.widgets.MusicWidget +import de.mm20.launcher2.widgets.NotesWidget import de.mm20.launcher2.widgets.WeatherWidget import de.mm20.launcher2.widgets.Widget import de.mm20.launcher2.widgets.WidgetRepository @@ -56,6 +57,10 @@ class WidgetsService( type = FavoritesWidget.Type, label = context.getString(R.string.widget_name_favorites), ), + BuiltInWidgetInfo( + type = NotesWidget.Type, + label = context.getString(R.string.widget_name_notes), + ), ) } @@ -63,6 +68,10 @@ class WidgetsService( widgetRepository.create(widget, position, parentId) } + fun updateWidget(widget: Widget) { + widgetRepository.update(widget) + } + fun getWidgets() = widgetRepository.get() fun isFavoritesWidgetFirst(): Flow {