diff --git a/android/app/src/main/java/com/meetrec/android/LibraryScreen.kt b/android/app/src/main/java/com/meetrec/android/LibraryScreen.kt index 006150a..1398a4f 100644 --- a/android/app/src/main/java/com/meetrec/android/LibraryScreen.kt +++ b/android/app/src/main/java/com/meetrec/android/LibraryScreen.kt @@ -1,9 +1,16 @@ package com.meetrec.android +import android.content.ClipData +import android.content.ClipboardManager +import android.content.ContentValues import android.content.Context import android.content.Intent import android.media.MediaPlayer +import android.os.Environment +import android.provider.MediaStore +import android.widget.Toast import androidx.compose.foundation.clickable +import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -387,14 +394,42 @@ private fun LibraryDetail( when { m.summary == "pending" -> Text("wird erstellt …", style = MaterialTheme.typography.bodySmall) - summaryText != null -> Text( - summaryText!!, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier - .fillMaxWidth() - .heightIn(max = 240.dp) - .verticalScroll(rememberScrollState()), - ) + summaryText != null -> { + SelectionContainer { + Text( + summaryText!!, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier + .fillMaxWidth() + .heightIn(max = 240.dp) + .verticalScroll(rememberScrollState()), + ) + } + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + OutlinedButton(onClick = { + copyToClipboard(context, summaryText!!) + }) { Text("Kopieren") } + OutlinedButton(onClick = { + shareText(context, summaryText!!, "MeetRec — Zusammenfassung") + }) { Text("Teilen") } + OutlinedButton(onClick = { + scope.launch { + try { + val name = "meetrec-summary-" + + (item as? LibraryItem.Server)?.meta?.id + .orEmpty() + ".md" + val path = withContext(Dispatchers.IO) { + saveTextToDownloads(context, name, summaryText!!) + } + Toast.makeText(context, "gespeichert: $path", + Toast.LENGTH_LONG).show() + } catch (e: Exception) { + detailError = "save failed: ${e.message}" + } + } + }) { Text("Datei speichern") } + } + } m.summary != null && m.summary.startsWith("error") -> { Text(m.summary, color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodySmall) @@ -481,8 +516,30 @@ private fun LibraryDetail( Text("No transcript available for this recording.", style = MaterialTheme.typography.bodySmall) } else { - Text("Transcript (${d.segments.size} segments)", - style = MaterialTheme.typography.titleSmall) + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Text("Transcript (${d.segments.size} segments)", + style = MaterialTheme.typography.titleSmall, + modifier = Modifier.align(androidx.compose.ui.Alignment.CenterVertically)) + OutlinedButton(onClick = { + scope.launch { + try { + val text = d.segments.joinToString("\n") { + "[${fmtMs(it.first)}] ${it.second}" + } + val name = "meetrec-transcript-" + + (item as? LibraryItem.Server)?.meta?.id + .orEmpty() + ".txt" + val path = withContext(Dispatchers.IO) { + saveTextToDownloads(context, name, text) + } + Toast.makeText(context, "gespeichert: $path", + Toast.LENGTH_LONG).show() + } catch (e: Exception) { + detailError = "save failed: ${e.message}" + } + } + }) { Text("Datei speichern") } + } LazyColumn(Modifier.fillMaxWidth().weight(1f)) { items(d.segments) { (startMs, text) -> Column(Modifier.fillMaxWidth().padding(vertical = 4.dp)) { @@ -563,4 +620,36 @@ private fun MediaPlayer.stopSafely() { if (isPlaying) stop() } catch (_: IllegalStateException) { } +} + +private fun copyToClipboard(context: Context, text: String) { + val cb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + cb.setPrimaryClip(ClipData.newPlainText("MeetRec", text)) + Toast.makeText(context, "kopiert", Toast.LENGTH_SHORT).show() +} + +private fun shareText(context: Context, text: String, title: String) { + context.startActivity( + Intent.createChooser( + Intent(Intent.ACTION_SEND) + .setType("text/plain") + .putExtra(Intent.EXTRA_TEXT, text), + title, + ), + ) +} + +/** Writes text to Download/meetrec/; returns the relative path. */ +private fun saveTextToDownloads(context: Context, fileName: String, text: String): String { + val values = ContentValues().apply { + put(MediaStore.Downloads.DISPLAY_NAME, fileName) + put(MediaStore.Downloads.MIME_TYPE, "text/plain") + put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/meetrec") + } + val uri = context.contentResolver.insert( + MediaStore.Downloads.EXTERNAL_CONTENT_URI, values, + ) ?: throw java.io.IOException("could not create $fileName") + context.contentResolver.openOutputStream(uri)?.use { it.write(text.toByteArray()) } + ?: throw java.io.IOException("could not write $fileName") + return "${Environment.DIRECTORY_DOWNLOADS}/meetrec/$fileName" } \ No newline at end of file