From c4029eb684a20d49f1a76a4a0b24ffa653b9dc63 Mon Sep 17 00:00:00 2001 From: alimu Date: Sat, 7 Mar 2026 18:14:59 +0400 Subject: [PATCH] Sync from upstream/ai-dev (squashed) --- .idea/.gitignore | 3 - .idea/OnlineMsgServer.iml | 9 - .idea/caches/deviceStreaming.xml | 1490 ----------------- .idea/misc.xml | 6 - .idea/modules.xml | 8 - .idea/vcs.xml | 6 - android-client/app/.DS_Store | Bin 6148 -> 0 bytes .../java/com/onlinemsg/client/MainActivity.kt | 48 +- .../client/service/ChatForegroundService.kt | 29 +- .../com/onlinemsg/client/ui/ChatScreen.kt | 142 +- 10 files changed, 69 insertions(+), 1672 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/OnlineMsgServer.iml delete mode 100644 .idea/caches/deviceStreaming.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml delete mode 100644 android-client/app/.DS_Store diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/OnlineMsgServer.iml b/.idea/OnlineMsgServer.iml deleted file mode 100644 index d6ebd48..0000000 --- a/.idea/OnlineMsgServer.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml deleted file mode 100644 index 23267a6..0000000 --- a/.idea/caches/deviceStreaming.xml +++ /dev/null @@ -1,1490 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 4b151ab..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 7801fe1..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android-client/app/.DS_Store b/android-client/app/.DS_Store deleted file mode 100644 index ca775671c94159d0188964fec2ea94aab0f39fda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5Z<+|O({YS3Oz1(E!fr~6)z#y7cim+m70*E!I&*gVh^Q|v%Zi|;`2DO zyMY#iM-e*%yWi~m>}Ed5{xHV4n}>bIY{r-c4UwZ#A!x32?U-OhuI31XMLG*&8B|R3 zH%<8M4HmGRMJ#6P-~SQJ;wa5}y-&VVt2cI=R?})*_ui9Sc$uH&sq4>gadahR9F)2r zTu0Nz#NI!bN#;k%bgmMja0(%JH&GJGg)8$UOjWL@9ahU~P3+Ec+3%0KVlW!6x?*`c z=yk>L_++(eSqFzlXP1-b_$85Vnn(_eE7>zx!aFEy6}@`%B$mk|Sj&tul8_i628aP- zU^^Kw=YiGU&hn{hVt^Rtk>1LQ&>y{Z<~HwF24$G!%?0Pyqpb;}QS^+($afsr>?Vh;s}U V8gUl1t8_rR2q;3RBL;qffiI+CO3VNN diff --git a/android-client/app/src/main/java/com/onlinemsg/client/MainActivity.kt b/android-client/app/src/main/java/com/onlinemsg/client/MainActivity.kt index a07bba6..defba49 100644 --- a/android-client/app/src/main/java/com/onlinemsg/client/MainActivity.kt +++ b/android-client/app/src/main/java/com/onlinemsg/client/MainActivity.kt @@ -9,11 +9,9 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat -import com.onlinemsg.client.service.ChatForegroundService import com.onlinemsg.client.ui.OnlineMsgApp class MainActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requestNotificationPermissionIfNeeded() @@ -23,53 +21,21 @@ class MainActivity : ComponentActivity() { } } - private val PERMISSION_REQUEST_NOTIFICATIONS = 1001 - private fun requestNotificationPermissionIfNeeded() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return val granted = ContextCompat.checkSelfPermission( this, Manifest.permission.POST_NOTIFICATIONS ) == PackageManager.PERMISSION_GRANTED - if (granted) { - // 已有权限 - startForegroundServiceIfNeeded() - } else { - // 请求权限 - ActivityCompat.requestPermissions( - this, - arrayOf(Manifest.permission.POST_NOTIFICATIONS), - REQUEST_NOTIFICATION_PERMISSION - ) - } - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - when (requestCode) { - REQUEST_NOTIFICATION_PERMISSION -> { - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - /* 授予权限的处理 */ - startForegroundServiceIfNeeded() - } else { - /* 拒绝权限的处理 */ - } - } - } - } - - /** - * 根据业务逻辑决定何时启动前台服务(@emilia-t) - */ - private fun startForegroundServiceIfNeeded() { - ChatForegroundService.start(this) + if (granted) return + ActivityCompat.requestPermissions( + this, + arrayOf(Manifest.permission.POST_NOTIFICATIONS), + REQUEST_NOTIFICATION_PERMISSION + ) } private companion object { const val REQUEST_NOTIFICATION_PERMISSION = 1002 } -} \ No newline at end of file +} diff --git a/android-client/app/src/main/java/com/onlinemsg/client/service/ChatForegroundService.kt b/android-client/app/src/main/java/com/onlinemsg/client/service/ChatForegroundService.kt index 8566d22..0ebaa12 100644 --- a/android-client/app/src/main/java/com/onlinemsg/client/service/ChatForegroundService.kt +++ b/android-client/app/src/main/java/com/onlinemsg/client/service/ChatForegroundService.kt @@ -21,10 +21,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import android.Manifest -import android.content.pm.PackageManager -import android.util.Log -import android.annotation.SuppressLint class ChatForegroundService : Service() { @@ -66,18 +62,6 @@ class ChatForegroundService : Service() { override fun onBind(intent: Intent?): IBinder? = null - /** - * 权限检查函数(@emilia-t) - */ - private fun hasNotificationPermission(): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED - } else { - true // 低于 Android 13 不需要权限 - } - } - - @SuppressLint("MissingPermission") private fun observeStatusAndRefreshNotification() { if (statusJob != null) return statusJob = serviceScope.launch { @@ -85,15 +69,10 @@ class ChatForegroundService : Service() { .map { it.status to it.statusHint } .distinctUntilChanged() .collect { (status, hint) -> - /* 检查是否有通知权限(@emilia-t) */ - if (hasNotificationPermission()) { - NotificationManagerCompat.from(this@ChatForegroundService).notify( - FOREGROUND_NOTIFICATION_ID, - buildForegroundNotification(status, hint) - ) - } else { - Log.d("ChatForegroundService", "通知权限缺失,跳过前台通知更新") - } + NotificationManagerCompat.from(this@ChatForegroundService).notify( + FOREGROUND_NOTIFICATION_ID, + buildForegroundNotification(status, hint) + ) if (status == ConnectionStatus.IDLE && !ChatSessionManager.shouldForegroundServiceRun()) { stopForeground(STOP_FOREGROUND_REMOVE) stopSelf() diff --git a/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatScreen.kt b/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatScreen.kt index 714d1e2..a8e31a4 100644 --- a/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatScreen.kt +++ b/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -101,7 +102,11 @@ fun OnlineMsgApp( ConnectionStatus.READY -> MaterialTheme.colorScheme.primary ConnectionStatus.ERROR -> MaterialTheme.colorScheme.error else -> MaterialTheme.colorScheme.secondary - } + }, + canConnect = state.canConnect, + canDisconnect = state.canDisconnect, + onConnect = viewModel::connect, + onDisconnect = viewModel::disconnect ) }, bottomBar = { @@ -133,9 +138,6 @@ fun OnlineMsgApp( onTargetKeyChange = viewModel::updateTargetKey, onDraftChange = viewModel::updateDraft, onSend = viewModel::sendMessage, - onConnect = viewModel::connect, - onDisconnect = viewModel::disconnect, - onClearMessages = viewModel::clearMessages, onCopyMessage = { content -> clipboard.setText(AnnotatedString(content)) viewModel.onMessageCopied() @@ -162,8 +164,6 @@ fun OnlineMsgApp( viewModel.onMessageCopied() } }, - onConnect = viewModel::connect, - onDisconnect = viewModel::disconnect, onClearMessages = viewModel::clearMessages ) } @@ -176,8 +176,13 @@ fun OnlineMsgApp( @Composable private fun AppTopBar( statusText: String, - statusColor: Color + statusColor: Color, + canConnect: Boolean, + canDisconnect: Boolean, + onConnect: () -> Unit, + onDisconnect: () -> Unit ) { + val enabled = canConnect || canDisconnect TopAppBar( title = { Text( @@ -187,7 +192,13 @@ private fun AppTopBar( }, actions = { AssistChip( - onClick = {}, + onClick = { + when { + canDisconnect -> onDisconnect() + canConnect -> onConnect() + } + }, + enabled = enabled, label = { Text(statusText) }, leadingIcon = { Box( @@ -211,9 +222,6 @@ private fun ChatTab( onTargetKeyChange: (String) -> Unit, onDraftChange: (String) -> Unit, onSend: () -> Unit, - onConnect: () -> Unit, - onDisconnect: () -> Unit, - onClearMessages: () -> Unit, onCopyMessage: (String) -> Unit ) { val listState = rememberLazyListState() @@ -230,17 +238,6 @@ private fun ChatTab( .imePadding() .padding(horizontal = 16.dp, vertical = 8.dp) ) { - ConnectionRow( - statusHint = state.statusHint, - canConnect = state.canConnect, - canDisconnect = state.canDisconnect, - onConnect = onConnect, - onDisconnect = onDisconnect, - onClearMessages = onClearMessages - ) - - Spacer(modifier = Modifier.height(8.dp)) - Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { FilterChip( selected = !state.directMode, @@ -254,6 +251,13 @@ private fun ChatTab( ) } + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = state.statusHint, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + if (state.directMode) { Spacer(modifier = Modifier.height(8.dp)) OutlinedTextField( @@ -331,44 +335,6 @@ private fun ChatTab( } } -@Composable -private fun ConnectionRow( - statusHint: String, - canConnect: Boolean, - canDisconnect: Boolean, - onConnect: () -> Unit, - onDisconnect: () -> Unit, - onClearMessages: () -> Unit -) { - Card( - modifier = Modifier.fillMaxWidth(), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant) - ) { - Column(modifier = Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { - Text( - text = "在线会话", - style = MaterialTheme.typography.titleMedium - ) - Text( - text = statusHint, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - Button(onClick = onConnect, enabled = canConnect) { - Text("连接") - } - OutlinedButton(onClick = onDisconnect, enabled = canDisconnect) { - Text("断开") - } - OutlinedButton(onClick = onClearMessages) { - Text("清空") - } - } - } - } -} - @Composable private fun MessageItem( message: UiMessage, @@ -517,10 +483,15 @@ private fun SettingsTab( onToggleShowSystem: (Boolean) -> Unit, onRevealPublicKey: () -> Unit, onCopyPublicKey: () -> Unit, - onConnect: () -> Unit, - onDisconnect: () -> Unit, onClearMessages: () -> Unit ) { + val settingsCardModifier = Modifier.fillMaxWidth() + val settingsCardContentModifier = Modifier + .fillMaxWidth() + .heightIn(min = 112.dp) + .padding(horizontal = 14.dp, vertical = 12.dp) + val settingsCardContentSpacing = Arrangement.spacedBy(10.dp) + LazyColumn( modifier = modifier .fillMaxSize() @@ -528,10 +499,10 @@ private fun SettingsTab( verticalArrangement = Arrangement.spacedBy(12.dp) ) { item { - Card { + Card(modifier = settingsCardModifier) { Column( - modifier = Modifier.padding(12.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) + modifier = settingsCardContentModifier, + verticalArrangement = settingsCardContentSpacing ) { Text("个人设置", style = MaterialTheme.typography.titleMedium) OutlinedTextField( @@ -542,26 +513,29 @@ private fun SettingsTab( supportingText = { Text("最长 64 字符") }, maxLines = 1 ) - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - Button(onClick = onConnect, enabled = state.canConnect) { - Text("连接") - } - OutlinedButton(onClick = onDisconnect, enabled = state.canDisconnect) { - Text("断开") - } - OutlinedButton(onClick = onClearMessages) { - Text("清空消息") - } + } + } + } + + item { + Card(modifier = settingsCardModifier) { + Column( + modifier = settingsCardContentModifier, + verticalArrangement = settingsCardContentSpacing + ) { + Text("聊天数据", style = MaterialTheme.typography.titleMedium) + OutlinedButton(onClick = onClearMessages) { + Text("清空聊天记录") } } } } item { - Card { + Card(modifier = settingsCardModifier) { Column( - modifier = Modifier.padding(12.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) + modifier = settingsCardContentModifier, + verticalArrangement = settingsCardContentSpacing ) { Text("服务器", style = MaterialTheme.typography.titleMedium) OutlinedTextField( @@ -603,10 +577,10 @@ private fun SettingsTab( } item { - Card { + Card(modifier = settingsCardModifier) { Column( - modifier = Modifier.padding(12.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) + modifier = settingsCardContentModifier, + verticalArrangement = settingsCardContentSpacing ) { Text("身份与安全", style = MaterialTheme.typography.titleMedium) Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { @@ -637,10 +611,10 @@ private fun SettingsTab( } item { - Card { + Card(modifier = settingsCardModifier) { Column( - modifier = Modifier.padding(12.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) + modifier = settingsCardContentModifier, + verticalArrangement = settingsCardContentSpacing ) { Text("诊断", style = MaterialTheme.typography.titleMedium) Text("连接提示:${state.statusHint}")