diff --git a/app/src/main/java/com/unogame/ui/screens/ScoreboardScreen.kt b/app/src/main/java/com/unogame/ui/screens/ScoreboardScreen.kt index 0697ca3..33adb64 100644 --- a/app/src/main/java/com/unogame/ui/screens/ScoreboardScreen.kt +++ b/app/src/main/java/com/unogame/ui/screens/ScoreboardScreen.kt @@ -3,7 +3,10 @@ package com.unogame.ui.screens import android.content.ClipData import android.content.ClipboardManager import android.content.Context +import android.net.Uri import android.widget.Toast +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll @@ -43,8 +46,49 @@ fun ScoreboardScreen(onBack: () -> Unit) { var detailEntry by remember { mutableStateOf(null) } var showImportDialog by remember { mutableStateOf(false) } var importJson by remember { mutableStateOf("") } + var showMenu by remember { mutableStateOf(false) } val gson = remember { Gson() } + // Export file launcher + val exportLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.CreateDocument("application/json") + ) { uri: Uri? -> + uri?.let { + try { + val json = gson.toJson(scores) + context.contentResolver.openOutputStream(it)?.use { out -> + out.write(json.toByteArray()) + } + Toast.makeText(context, "备份已导出", Toast.LENGTH_SHORT).show() + } catch (e: Exception) { + Toast.makeText(context, "导出失败: ${e.message}", Toast.LENGTH_LONG).show() + } + } + } + + // Import file launcher + val importLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.OpenDocument() + ) { uri: Uri? -> + uri?.let { + try { + val json = context.contentResolver.openInputStream(it)?.bufferedReader()?.readText() ?: "" + val imported = gson.fromJson(json, Array::class.java)?.toList() ?: emptyList() + if (imported.isNotEmpty()) { + val merged = (scores + imported).distinctBy { e -> "${e.name}_${e.mode}_${e.date}" } + .sortedByDescending { e -> e.points }.take(50) + Scoreboard.saveScores(context, merged) + scores = merged + Toast.makeText(context, "成功导入 ${imported.size} 条记录", Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(context, "文件中无有效数据", Toast.LENGTH_SHORT).show() + } + } catch (e: Exception) { + Toast.makeText(context, "导入失败: ${e.message}", Toast.LENGTH_LONG).show() + } + } + } + val filtered = if (selectedFilter == "全部") scores else scores.filter { it.mode == selectedFilter } @@ -57,43 +101,56 @@ fun ScoreboardScreen(onBack: () -> Unit) { IconButton(onClick = onBack) { Icon(Icons.Default.ArrowBack, "返回", tint = Color.White) } - Text("积分排行榜", fontSize = 24.sp, fontWeight = FontWeight.Bold, color = Color.White) + Text("积分排行榜", fontSize = 24.sp, fontWeight = FontWeight.Bold, color = Color.White, modifier = Modifier.weight(1f)) + Box { + IconButton(onClick = { showMenu = true }) { + Icon(Icons.Default.MoreVert, "菜单", tint = Color.White) + } + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + DropdownMenuItem( + text = { Text("导出到文件") }, + onClick = { + showMenu = false + exportLauncher.launch("uno_scoreboard_backup.json") + }, + leadingIcon = { Icon(Icons.Default.FileUpload, null) } + ) + DropdownMenuItem( + text = { Text("从文件导入") }, + onClick = { + showMenu = false + importLauncher.launch(arrayOf("application/json", "*/*")) + }, + leadingIcon = { Icon(Icons.Default.FileDownload, null) } + ) + DropdownMenuItem( + text = { Text("复制备份") }, + onClick = { + showMenu = false + val json = gson.toJson(scores) + val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + clipboard.setPrimaryClip(ClipData.newPlainText("uno_scoreboard", json)) + Toast.makeText(context, "已复制JSON到剪贴板", Toast.LENGTH_SHORT).show() + }, + leadingIcon = { Icon(Icons.Default.ContentCopy, null) } + ) + DropdownMenuItem( + text = { Text("粘贴导入") }, + onClick = { + showMenu = false + showImportDialog = true + }, + leadingIcon = { Icon(Icons.Default.ContentPaste, null) } + ) + } + } } Spacer(modifier = Modifier.height(16.dp)) - // Import / Export buttons - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - OutlinedButton( - onClick = { - val json = gson.toJson(scores) - val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - clipboard.setPrimaryClip(ClipData.newPlainText("uno_scoreboard", json)) - Toast.makeText(context, "已复制备份JSON到剪贴板", Toast.LENGTH_SHORT).show() - }, - modifier = Modifier.weight(1f), - colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.White.copy(alpha = 0.7f)) - ) { - Icon(Icons.Default.FileUpload, null, modifier = Modifier.size(16.dp)) - Spacer(modifier = Modifier.width(4.dp)) - Text("导出备份", fontSize = 12.sp) - } - OutlinedButton( - onClick = { showImportDialog = true }, - modifier = Modifier.weight(1f), - colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.White.copy(alpha = 0.7f)) - ) { - Icon(Icons.Default.FileDownload, null, modifier = Modifier.size(16.dp)) - Spacer(modifier = Modifier.width(4.dp)) - Text("导入备份", fontSize = 12.sp) - } - } - - Spacer(modifier = Modifier.height(12.dp)) - // Mode filter chips if (scores.isNotEmpty()) { Row( @@ -282,7 +339,7 @@ fun ScoreboardScreen(onBack: () -> Unit) { if (showImportDialog) { AlertDialog( onDismissRequest = { showImportDialog = false }, - title = { Text("导入备份", color = GoldAccent) }, + title = { Text("粘贴导入", color = GoldAccent) }, text = { Column { Text("粘贴之前导出的JSON数据:", color = Color.White.copy(alpha = 0.7f), fontSize = 14.sp)