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
|
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.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material.icons.rounded.Add
|
||||||
import androidx.compose.material3.MaterialTheme
|
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.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
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.Modifier
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.markdown.MarkdownEditor
|
import de.mm20.launcher2.ui.component.markdown.MarkdownEditor
|
||||||
import de.mm20.launcher2.widgets.NotesWidget
|
import de.mm20.launcher2.widgets.NotesWidget
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NotesWidget(widget: NotesWidget) {
|
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) {
|
LaunchedEffect(widget) {
|
||||||
viewModel.updateWidget(widget)
|
viewModel.updateWidget(widget)
|
||||||
}
|
}
|
||||||
|
|
||||||
val text by viewModel.noteText
|
val exportLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.CreateDocument("text/markdown"),
|
||||||
MarkdownEditor(
|
onResult = {
|
||||||
value = text,
|
if (it != null) viewModel.exportNote(context, it)
|
||||||
onValueChange = { viewModel.setText(it) },
|
|
||||||
modifier = Modifier.fillMaxWidth().padding(16.dp),
|
|
||||||
placeholder = {
|
|
||||||
Text(stringResource(R.string.notes_widget_placeholder))
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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
|
package de.mm20.launcher2.ui.launcher.widgets.notes
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
@ -8,14 +10,11 @@ import androidx.lifecycle.viewmodel.initializer
|
|||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
import de.mm20.launcher2.services.widgets.WidgetsService
|
import de.mm20.launcher2.services.widgets.WidgetsService
|
||||||
import de.mm20.launcher2.widgets.NotesWidget
|
import de.mm20.launcher2.widgets.NotesWidget
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.launch
|
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.KoinComponent
|
||||||
import org.koin.core.component.get
|
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 {
|
companion object: KoinComponent {
|
||||||
val Factory = viewModelFactory {
|
val Factory = viewModelFactory {
|
||||||
initializer {
|
initializer {
|
||||||
|
|||||||
@ -225,6 +225,13 @@
|
|||||||
<!-- Add a third party widget (=a standard Android app widget) -->
|
<!-- Add a third party widget (=a standard Android app widget) -->
|
||||||
<string name="widget_add_external">More</string>
|
<string name="widget_add_external">More</string>
|
||||||
<string name="notes_widget_placeholder">Write a note…</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 -->
|
<!-- Indicates to which app a shortcuts belongs. %1$s: the name of the app -->
|
||||||
<string name="shortcut_summary">By %1$s</string>
|
<string name="shortcut_summary">By %1$s</string>
|
||||||
<!-- Progress of the installation of another app. %1$s: the formatted percentage of the installation progress -->
|
<!-- 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