Note widget: add export functionality

This commit is contained in:
MM20 2023-04-26 21:40:22 +02:00
parent 1ab89fc9b8
commit db79a19ebc
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
3 changed files with 106 additions and 18 deletions

View File

@ -1,40 +1,111 @@
package de.mm20.launcher2.ui.launcher.widgets.notes
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
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.material.icons.Icons
import androidx.compose.material.icons.rounded.Add
import androidx.compose.material.icons.rounded.Delete
import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material.icons.rounded.SaveAlt
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.markdown.MarkdownEditor
import de.mm20.launcher2.widgets.NotesWidget
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
@Composable
fun NotesWidget(widget: NotesWidget) {
val viewModel: NotesWidgetVM = viewModel(key = "notes-widget-${widget.id}", factory = NotesWidgetVM.Factory)
val context = LocalContext.current
val viewModel: NotesWidgetVM =
viewModel(key = "notes-widget-${widget.id}", factory = NotesWidgetVM.Factory)
LaunchedEffect(widget) {
viewModel.updateWidget(widget)
}
val text by viewModel.noteText
MarkdownEditor(
value = text,
onValueChange = { viewModel.setText(it) },
modifier = Modifier.fillMaxWidth().padding(16.dp),
placeholder = {
Text(stringResource(R.string.notes_widget_placeholder))
val exportLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.CreateDocument("text/markdown"),
onResult = {
if (it != null) viewModel.exportNote(context, it)
}
)
val text by viewModel.noteText
Column {
MarkdownEditor(
value = text,
onValueChange = { viewModel.setText(it) },
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
placeholder = {
Text(stringResource(R.string.notes_widget_placeholder))
}
)
AnimatedVisibility(text.isNotBlank()) {
var showMenu by remember { mutableStateOf(false) }
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.CenterEnd) {
Box {
IconButton(onClick = { showMenu = true }) {
Icon(Icons.Rounded.MoreVert, null)
}
DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
DropdownMenuItem(
text = { Text("New note") },
leadingIcon = {
Icon(Icons.Rounded.Add, null)
},
onClick = { /*TODO*/ },
)
DropdownMenuItem(
text = { Text(stringResource(R.string.notes_widget_action_save)) },
leadingIcon = {
Icon(Icons.Rounded.SaveAlt, null)
},
onClick = {
val fileName = context.getString(
R.string.notes_widget_export_filename,
ZonedDateTime.now().format(
DateTimeFormatter.ISO_INSTANT
)
)
exportLauncher.launch("$fileName.md")
showMenu = false
},
)
DropdownMenuItem(
text = { Text("Dismiss") },
leadingIcon = {
Icon(Icons.Rounded.Delete, null)
},
onClick = { /*TODO*/ },
)
}
}
}
}
}
}

View File

@ -1,5 +1,7 @@
package de.mm20.launcher2.ui.launcher.widgets.notes
import android.content.Context
import android.net.Uri
import android.util.Log
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
@ -8,14 +10,11 @@ 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.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import org.intellij.markdown.ast.ASTNode
import org.intellij.markdown.ast.getTextInNode
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
import org.intellij.markdown.parser.MarkdownParser
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
@ -43,6 +42,17 @@ class NotesWidgetVM(
}
}
fun exportNote(context: Context, uri: Uri) {
viewModelScope.launch(Dispatchers.IO) {
val text = noteText.value
Log.d("MM20", text)
val outputStream = context.contentResolver.openOutputStream(uri)
outputStream?.use {
it.write(text.toByteArray())
}
}
}
companion object: KoinComponent {
val Factory = viewModelFactory {
initializer {

View File

@ -225,6 +225,13 @@
<!-- Add a third party widget (=a standard Android app widget) -->
<string name="widget_add_external">More</string>
<string name="notes_widget_placeholder">Write a note…</string>
<string name="notes_widget_action_new">New note</string>
<!-- Save / export the current note content as markdown file -->
<string name="notes_widget_action_save">Save</string>
<!-- Default filename for exported files. %1$s: the current date and time. File extension is appended automatically. -->
<string name="notes_widget_export_filename">Note_%1$s</string>
<string name="notes_widget_action_dismiss">Dismiss</string>
<string name="notes_widget_dismissed">Note dismissed.</string>
<!-- Indicates to which app a shortcuts belongs. %1$s: the name of the app -->
<string name="shortcut_summary">By %1$s</string>
<!-- Progress of the installation of another app. %1$s: the formatted percentage of the installation progress -->