Note widget: add export functionality
This commit is contained in:
parent
1ab89fc9b8
commit
db79a19ebc
@ -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*/ },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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 -->
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user