還剩兩天鐵人賽就結束啦,這次來做筆記頁的子功能
// Function Panel
LazyRow(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
item { IconButton(onClick = { /* TODO: Undo */ }) { Icon(Icons.Default.Undo, contentDescription = "復原") } }
item { IconButton(onClick = { /* TODO: Redo */ }) { Icon(Icons.Default.Redo, contentDescription = "重做") } }
item { IconButton(onClick = onPlayTitle) { Icon(Icons.Default.PlayArrow, contentDescription = "朗讀標題") } }
item { IconButton(onClick = onPlayContent) { Icon(Icons.Default.PlayArrow, contentDescription = "朗讀內容") } }
item { IconButton(onClick = { /* TODO: Delete */ }) { Icon(Icons.Default.Delete, contentDescription = "刪除筆記") } }
item { IconButton(onClick = { /* TODO: Custom Color */ }) { Icon(Icons.Default.Edit, contentDescription = "自訂顏色") } }
// ... Add other buttons here
}
@Singleton
class TextToSpeechHelper @Inject constructor(
@ApplicationContext private val context: Context
) : TextToSpeech.OnInitListener {
private var tts: TextToSpeech? = null
private val _isInitialized = MutableStateFlow(false)
val isInitialized = _isInitialized.asStateFlow()
init {
try {
tts = TextToSpeech(context, this)
} catch (e: Exception) {
Timber.e(e, "Failed to initialize TextToSpeech")
}
}
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
val result = tts?.setLanguage(Locale.TRADITIONAL_CHINESE)
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
Timber.e("TTS language (Traditional Chinese) not supported.")
_isInitialized.value = false
} else {
_isInitialized.value = true
Timber.d("TTS initialized successfully.")
}
} else {
Timber.e("TTS initialization failed with status: $status")
_isInitialized.value = false
}
}
fun speak(text: String) {
if (_isInitialized.value && tts != null) {
// Handle long text by splitting it
val maxLength = TextToSpeech.getMaxSpeechInputLength()
if (text.length > maxLength) {
val chunks = text.chunked(maxLength)
chunks.forEachIndexed { index, chunk ->
val utteranceId = "${System.currentTimeMillis()}_$index"
tts?.speak(chunk, TextToSpeech.QUEUE_ADD, null, utteranceId)
}
} else {
val utteranceId = "${System.currentTimeMillis()}"
tts?.speak(text, TextToSpeech.QUEUE_FLUSH, null, utteranceId)
}
} else {
Timber.w("TTS not initialized or available, cannot speak.")
}
}
fun stop() {
tts?.stop()
}
fun shutdown() {
if (tts != null) {
tts?.stop()
tts?.shutdown()
tts = null
_isInitialized.value = false
Timber.d("TTS shutdown.")
}
}
}
fun playTitle() {
_uiState.value.note?.title?.let {
if (it.isNotBlank()) {
ttsHelper.speak(it)
}
}
}
fun playContent() {
_uiState.value.note?.content?.let {
if (it.isNotBlank()) {
ttsHelper.speak(it)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun NoteEditScreenContent(
modifier: Modifier = Modifier,
uiState: NoteEditUiState,
onSaveNote: (String, String) -> Unit,
onPlayTitle: () -> Unit,
onPlayContent: () -> Unit,
onNavigateBack: () -> Unit
) {
var title by remember(uiState.note?.title) { mutableStateOf(uiState.note?.title ?: "") }
var content by remember(uiState.note?.content) { mutableStateOf(uiState.note?.content ?: "") }
Scaffold(
modifier = modifier.fillMaxSize(),
topBar = {
TopAppBar(
title = { Text(if (uiState.note?.id == 0L) "新增筆記" else "編輯筆記") },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "返回"
)
}
},
actions = {
IconButton(onClick = { onSaveNote(title, content) }) {
Icon(
imageVector = Icons.Default.Done,
contentDescription = "儲存"
)
}
}
)
}
) { innerPadding ->
if (uiState.isLoading) {
CircularProgressIndicator()
} else {
Column(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize()
) {
TextField(
value = title,
onValueChange = { title = it },
label = { Text("標題") },
modifier = Modifier.fillMaxWidth()
)
TextField(
value = content,
onValueChange = { content = it },
label = { Text("內容") },
modifier = Modifier
.fillMaxWidth()
.weight(1f)
)
// Function Panel
LazyRow(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
item { IconButton(onClick = { /* TODO: Undo */ }) { Icon(Icons.Default.Undo, contentDescription = "復原") } }
item { IconButton(onClick = { /* TODO: Redo */ }) { Icon(Icons.Default.Redo, contentDescription = "重做") } }
item { IconButton(onClick = onPlayTitle) { Icon(Icons.Default.PlayArrow, contentDescription = "朗讀標題") } }
item { IconButton(onClick = onPlayContent) { Icon(Icons.Default.PlayArrow, contentDescription = "朗讀內容") } }
item { IconButton(onClick = { /* TODO: Delete */ }) { Icon(Icons.Default.Delete, contentDescription = "刪除筆記") } }
item { IconButton(onClick = { /* TODO: Custom Color */ }) { Icon(Icons.Default.Edit, contentDescription = "自訂顏色") } }
// ... Add other buttons here
}
}
}
}
}