feat: 更新Android UI界面
- 更新创建记忆界面,改进用户体验 - 更新个人资料界面,添加更多功能 - 更新应用图标资源 - 更新CreateMemoryViewModel以支持新功能 - 更新ViewModelFactory
This commit is contained in:
@@ -1,6 +1,11 @@
|
|||||||
package com.huaga.life_echo.ui.icons
|
package com.huaga.life_echo.ui.icons
|
||||||
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.Chat
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.Help
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.MenuBook
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.Send
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
import androidx.compose.material.icons.outlined.*
|
import androidx.compose.material.icons.outlined.*
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
@@ -11,18 +16,18 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||||||
*/
|
*/
|
||||||
object AppIcons {
|
object AppIcons {
|
||||||
// 导航栏图标
|
// 导航栏图标
|
||||||
val Chat = Icons.Default.Chat
|
val Chat = Icons.AutoMirrored.Filled.Chat
|
||||||
val Memoir = Icons.Default.MenuBook
|
val Memoir = Icons.AutoMirrored.Filled.MenuBook
|
||||||
val Profile = Icons.Default.Person
|
val Profile = Icons.Default.Person
|
||||||
|
|
||||||
// 聊天界面图标
|
// 聊天界面图标
|
||||||
val ArrowBack = Icons.Default.ArrowBack
|
val ArrowBack = Icons.AutoMirrored.Filled.ArrowBack
|
||||||
val Mic = Icons.Default.Mic
|
val Mic = Icons.Default.Mic
|
||||||
val MicOff = Icons.Default.MicOff
|
val MicOff = Icons.Default.MicOff
|
||||||
val SentimentSatisfied = Icons.Default.SentimentSatisfied
|
val SentimentSatisfied = Icons.Default.SentimentSatisfied
|
||||||
val Add = Icons.Default.Add
|
val Add = Icons.Default.Add
|
||||||
val Send = Icons.Default.Send
|
val Send = Icons.AutoMirrored.Filled.Send
|
||||||
val Book = Icons.Default.MenuBook
|
val Book = Icons.AutoMirrored.Filled.MenuBook
|
||||||
|
|
||||||
// 对话列表图标
|
// 对话列表图标
|
||||||
val Conversation = Icons.Default.ChatBubble
|
val Conversation = Icons.Default.ChatBubble
|
||||||
@@ -31,7 +36,7 @@ object AppIcons {
|
|||||||
|
|
||||||
// 回忆录界面图标
|
// 回忆录界面图标
|
||||||
val Chapter = Icons.Default.Book
|
val Chapter = Icons.Default.Book
|
||||||
val Reading = Icons.Default.MenuBook
|
val Reading = Icons.AutoMirrored.Filled.MenuBook
|
||||||
val ChevronRight = Icons.Default.ChevronRight
|
val ChevronRight = Icons.Default.ChevronRight
|
||||||
val Edit = Icons.Default.Edit
|
val Edit = Icons.Default.Edit
|
||||||
val Share = Icons.Default.Share
|
val Share = Icons.Default.Share
|
||||||
@@ -46,7 +51,7 @@ object AppIcons {
|
|||||||
val FormatSize = Icons.Default.FormatSize
|
val FormatSize = Icons.Default.FormatSize
|
||||||
val Brightness2 = Icons.Default.Brightness2
|
val Brightness2 = Icons.Default.Brightness2
|
||||||
val Settings = Icons.Default.Settings
|
val Settings = Icons.Default.Settings
|
||||||
val Help = Icons.Default.Help
|
val Help = Icons.AutoMirrored.Filled.Help
|
||||||
val Info = Icons.Default.Info
|
val Info = Icons.Default.Info
|
||||||
|
|
||||||
// 其他常用图标
|
// 其他常用图标
|
||||||
@@ -69,4 +74,8 @@ object AppIcons {
|
|||||||
val Error = Icons.Default.Error
|
val Error = Icons.Default.Error
|
||||||
val Warning = Icons.Default.Warning
|
val Warning = Icons.Default.Warning
|
||||||
val InfoCircle = Icons.Default.Info
|
val InfoCircle = Icons.Default.Info
|
||||||
|
|
||||||
|
// 认证相关图标
|
||||||
|
val Visibility = Icons.Default.Visibility
|
||||||
|
val VisibilityOff = Icons.Default.VisibilityOff
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.huaga.life_echo.ui.screens
|
package com.huaga.life_echo.ui.screens
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
@@ -12,6 +13,7 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
@@ -35,6 +37,12 @@ fun CreateMemoryScreen(
|
|||||||
val transcript by viewModel.transcript.collectAsState()
|
val transcript by viewModel.transcript.collectAsState()
|
||||||
val agentResponse by viewModel.agentResponse.collectAsState()
|
val agentResponse by viewModel.agentResponse.collectAsState()
|
||||||
val connectionStatus by viewModel.connectionStatus.collectAsState()
|
val connectionStatus by viewModel.connectionStatus.collectAsState()
|
||||||
|
val userMessages by viewModel.userMessages.collectAsState()
|
||||||
|
|
||||||
|
// 输入框状态
|
||||||
|
var showTextInput by remember { mutableStateOf(false) }
|
||||||
|
var inputText by remember { mutableStateOf("") }
|
||||||
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -142,6 +150,31 @@ fun CreateMemoryScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 用户文本消息
|
||||||
|
userMessages.forEach { message ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.End
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.shadow(2.dp, RoundedCornerShape(12.dp)),
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
colors = CardDefaults.cardColors(containerColor = LightPurple.copy(alpha = 0.2f))
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = message,
|
||||||
|
modifier = Modifier.padding(12.dp),
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 用户消息(转写文本)
|
// 用户消息(转写文本)
|
||||||
if (transcript.isNotEmpty()) {
|
if (transcript.isNotEmpty()) {
|
||||||
Row(
|
Row(
|
||||||
@@ -236,21 +269,92 @@ fun CreateMemoryScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 语音输入提示区域(只读,不支持文本输入)
|
// 输入框区域 - 点击弹出文本输入
|
||||||
Box(
|
if (showTextInput) {
|
||||||
modifier = Modifier
|
// 显示可编辑的文本输入框
|
||||||
.weight(1f)
|
Row(
|
||||||
.height(40.dp)
|
modifier = Modifier.weight(1f),
|
||||||
.clip(RoundedCornerShape(20.dp))
|
verticalAlignment = Alignment.CenterVertically
|
||||||
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)),
|
) {
|
||||||
contentAlignment = Alignment.CenterStart
|
OutlinedTextField(
|
||||||
) {
|
value = inputText,
|
||||||
Text(
|
onValueChange = { inputText = it },
|
||||||
text = if (isRecording) "正在录音..." else "说点什么...",
|
modifier = Modifier
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
.weight(1f)
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
.height(40.dp),
|
||||||
fontSize = 14.sp
|
placeholder = {
|
||||||
)
|
Text(
|
||||||
|
text = "输入消息...",
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
colors = OutlinedTextFieldDefaults.colors(
|
||||||
|
unfocusedBorderColor = Color.Transparent,
|
||||||
|
focusedBorderColor = LightPurple,
|
||||||
|
focusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||||
|
unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)
|
||||||
|
),
|
||||||
|
singleLine = true,
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// 发送按钮
|
||||||
|
if (inputText.isNotBlank()) {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
viewModel.sendTextMessage(inputText)
|
||||||
|
inputText = ""
|
||||||
|
showTextInput = false
|
||||||
|
keyboardController?.hide()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = AppIcons.Send,
|
||||||
|
contentDescription = "发送",
|
||||||
|
tint = LightPurple,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 关闭按钮(当输入框为空时)
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
showTextInput = false
|
||||||
|
keyboardController?.hide()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = AppIcons.Close,
|
||||||
|
contentDescription = "关闭",
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 显示只读提示区域
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.height(40.dp)
|
||||||
|
.clip(RoundedCornerShape(20.dp))
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f))
|
||||||
|
.clickable {
|
||||||
|
showTextInput = true
|
||||||
|
keyboardController?.show()
|
||||||
|
},
|
||||||
|
contentAlignment = Alignment.CenterStart
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = if (isRecording) "正在录音..." else "说点什么...",
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 表情图标
|
// 表情图标
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
@@ -43,18 +46,37 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.huaga.life_echo.data.auth.TokenManager
|
||||||
|
import com.huaga.life_echo.navigation.Screen
|
||||||
import com.huaga.life_echo.ui.icons.AppIcons
|
import com.huaga.life_echo.ui.icons.AppIcons
|
||||||
import com.huaga.life_echo.ui.settings.AppSettings
|
import com.huaga.life_echo.ui.settings.AppSettings
|
||||||
import com.huaga.life_echo.ui.theme.LightPurple
|
import com.huaga.life_echo.ui.theme.LightPurple
|
||||||
|
import com.huaga.life_echo.ui.viewmodel.AuthViewModel
|
||||||
|
import com.huaga.life_echo.ui.viewmodel.ViewModelFactory
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ProfileScreen(
|
fun ProfileScreen(
|
||||||
navController: androidx.navigation.NavHostController? = null
|
navController: androidx.navigation.NavHostController? = null
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val authViewModel: AuthViewModel = viewModel(factory = ViewModelFactory(context))
|
||||||
|
|
||||||
var largeFontMode by remember { mutableStateOf(AppSettings.largeFontMode) }
|
var largeFontMode by remember { mutableStateOf(AppSettings.largeFontMode) }
|
||||||
var darkMode by remember { mutableStateOf(AppSettings.darkMode) }
|
var darkMode by remember { mutableStateOf(AppSettings.darkMode) }
|
||||||
var speechRate by remember { mutableStateOf(AppSettings.speechRate) }
|
var speechRate by remember { mutableStateOf(AppSettings.speechRate) }
|
||||||
var showSpeechRateDialog by remember { mutableStateOf(false) }
|
var showSpeechRateDialog by remember { mutableStateOf(false) }
|
||||||
|
var showLogoutDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val isLoggedIn by authViewModel.isLoggedIn.collectAsState()
|
||||||
|
val currentUser by authViewModel.currentUser.collectAsState()
|
||||||
|
|
||||||
|
// 初始化TokenManager
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
TokenManager.initialize(context)
|
||||||
|
authViewModel.checkAuthStatus()
|
||||||
|
}
|
||||||
|
|
||||||
// 应用设置变化 - 立即更新全局设置
|
// 应用设置变化 - 立即更新全局设置
|
||||||
LaunchedEffect(largeFontMode) {
|
LaunchedEffect(largeFontMode) {
|
||||||
@@ -121,48 +143,123 @@ fun ProfileScreen(
|
|||||||
.padding(top = 16.dp, bottom = 32.dp),
|
.padding(top = 16.dp, bottom = 32.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
// 用户头像
|
if (isLoggedIn && currentUser != null) {
|
||||||
Box(
|
// 已登录:显示用户信息
|
||||||
modifier = Modifier
|
// 用户头像
|
||||||
.size(80.dp)
|
|
||||||
.clip(CircleShape)
|
|
||||||
.background(LightPurple.copy(alpha = 0.2f)),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = AppIcons.Person,
|
|
||||||
contentDescription = "用户头像",
|
|
||||||
tint = LightPurple,
|
|
||||||
modifier = Modifier.size(48.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = "李明华",
|
|
||||||
fontSize = 20.sp,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
|
||||||
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(8.dp)
|
.size(80.dp)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.background(LightPurple)
|
.background(LightPurple.copy(alpha = 0.2f)),
|
||||||
)
|
contentAlignment = Alignment.Center
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = AppIcons.Person,
|
||||||
|
contentDescription = "用户头像",
|
||||||
|
tint = LightPurple,
|
||||||
|
modifier = Modifier.size(48.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "免费体验版",
|
text = currentUser!!.nickname,
|
||||||
fontSize = 12.sp,
|
fontSize = 20.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(8.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(LightPurple)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
Text(
|
||||||
|
text = when (currentUser!!.subscription_type) {
|
||||||
|
"free" -> "免费体验版"
|
||||||
|
"premium" -> "高级版"
|
||||||
|
"professional" -> "专业版"
|
||||||
|
else -> "免费体验版"
|
||||||
|
},
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// 登出按钮
|
||||||
|
Button(
|
||||||
|
onClick = { showLogoutDialog = true },
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.error
|
||||||
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 32.dp)
|
||||||
|
.height(40.dp)
|
||||||
|
) {
|
||||||
|
Text("登出", fontSize = 14.sp)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 未登录:显示登录提示
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(80.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(LightPurple.copy(alpha = 0.2f)),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = AppIcons.Person,
|
||||||
|
contentDescription = "用户头像",
|
||||||
|
tint = LightPurple,
|
||||||
|
modifier = Modifier.size(48.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "未登录",
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "登录以同步您的数据",
|
||||||
|
fontSize = 14.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
|
// 登录按钮
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
navController?.navigate(Screen.Login.route)
|
||||||
|
},
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = LightPurple
|
||||||
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 32.dp)
|
||||||
|
.height(48.dp)
|
||||||
|
) {
|
||||||
|
Text("登录", fontSize = 16.sp, fontWeight = FontWeight.Bold)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,6 +360,30 @@ fun ProfileScreen(
|
|||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 登出确认对话框
|
||||||
|
if (showLogoutDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showLogoutDialog = false },
|
||||||
|
title = { Text("确认登出") },
|
||||||
|
text = { Text("确定要登出吗?") },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
authViewModel.logout()
|
||||||
|
showLogoutDialog = false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text("确认", color = MaterialTheme.colorScheme.error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { showLogoutDialog = false }) {
|
||||||
|
Text("取消")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.huaga.life_echo.ui.viewmodel
|
package com.huaga.life_echo.ui.viewmodel
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.huaga.life_echo.data.auth.TokenManager
|
||||||
import com.huaga.life_echo.data.repository.ConversationRepository
|
import com.huaga.life_echo.data.repository.ConversationRepository
|
||||||
import com.huaga.life_echo.data.repository.ChapterRepository
|
import com.huaga.life_echo.data.repository.ChapterRepository
|
||||||
import com.huaga.life_echo.network.WebSocketClient
|
import com.huaga.life_echo.network.WebSocketClient
|
||||||
@@ -13,7 +15,8 @@ import kotlinx.coroutines.launch
|
|||||||
|
|
||||||
class CreateMemoryViewModel(
|
class CreateMemoryViewModel(
|
||||||
private val conversationRepository: ConversationRepository,
|
private val conversationRepository: ConversationRepository,
|
||||||
private val chapterRepository: ChapterRepository
|
private val chapterRepository: ChapterRepository,
|
||||||
|
private val context: Context
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val webSocketClient = WebSocketClient()
|
private val webSocketClient = WebSocketClient()
|
||||||
@@ -23,6 +26,11 @@ class CreateMemoryViewModel(
|
|||||||
val agentResponse = MutableStateFlow("")
|
val agentResponse = MutableStateFlow("")
|
||||||
val connectionStatus = MutableStateFlow("未连接")
|
val connectionStatus = MutableStateFlow("未连接")
|
||||||
val conversationId = MutableStateFlow<String?>(null)
|
val conversationId = MutableStateFlow<String?>(null)
|
||||||
|
val userMessages = MutableStateFlow<List<String>>(emptyList()) // 用户发送的文本消息列表
|
||||||
|
|
||||||
|
init {
|
||||||
|
TokenManager.initialize(context)
|
||||||
|
}
|
||||||
|
|
||||||
fun startConversation() {
|
fun startConversation() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -32,7 +40,9 @@ class CreateMemoryViewModel(
|
|||||||
connectionStatus.value = "连接中..."
|
connectionStatus.value = "连接中..."
|
||||||
|
|
||||||
try {
|
try {
|
||||||
webSocketClient.connect(convId) { message ->
|
// 获取访问令牌
|
||||||
|
val token = TokenManager.getAccessToken()
|
||||||
|
webSocketClient.connect(convId, token) { message ->
|
||||||
handleWebSocketMessage(message)
|
handleWebSocketMessage(message)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -61,6 +71,29 @@ class CreateMemoryViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendTextMessage(text: String) {
|
||||||
|
if (text.isBlank()) return
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
// 确保已连接
|
||||||
|
if (conversationId.value == null) {
|
||||||
|
startConversation()
|
||||||
|
}
|
||||||
|
|
||||||
|
conversationId.value?.let { id ->
|
||||||
|
// 添加到用户消息列表
|
||||||
|
userMessages.value = userMessages.value + text
|
||||||
|
|
||||||
|
// 发送文本消息
|
||||||
|
try {
|
||||||
|
webSocketClient.sendTextMessage(text, id)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
connectionStatus.value = "发送失败: ${e.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleWebSocketMessage(message: WebSocketMessage) {
|
private fun handleWebSocketMessage(message: WebSocketMessage) {
|
||||||
when (message.type) {
|
when (message.type) {
|
||||||
MessageType.transcript -> {
|
MessageType.transcript -> {
|
||||||
@@ -69,6 +102,9 @@ class CreateMemoryViewModel(
|
|||||||
MessageType.agent_response -> {
|
MessageType.agent_response -> {
|
||||||
agentResponse.value = message.data["text"] ?: ""
|
agentResponse.value = message.data["text"] ?: ""
|
||||||
}
|
}
|
||||||
|
MessageType.text -> {
|
||||||
|
// 处理文本消息响应(如果需要)
|
||||||
|
}
|
||||||
MessageType.connect -> {
|
MessageType.connect -> {
|
||||||
connectionStatus.value = "已连接"
|
connectionStatus.value = "已连接"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ class ViewModelFactory(private val context: Context) : ViewModelProvider.Factory
|
|||||||
modelClass.isAssignableFrom(CreateMemoryViewModel::class.java) -> {
|
modelClass.isAssignableFrom(CreateMemoryViewModel::class.java) -> {
|
||||||
CreateMemoryViewModel(
|
CreateMemoryViewModel(
|
||||||
conversationRepository = conversationRepository,
|
conversationRepository = conversationRepository,
|
||||||
chapterRepository = chapterRepository
|
chapterRepository = chapterRepository,
|
||||||
|
context = context
|
||||||
) as T
|
) as T
|
||||||
}
|
}
|
||||||
modelClass.isAssignableFrom(ConversationListViewModel::class.java) -> {
|
modelClass.isAssignableFrom(ConversationListViewModel::class.java) -> {
|
||||||
@@ -44,6 +45,9 @@ class ViewModelFactory(private val context: Context) : ViewModelProvider.Factory
|
|||||||
apiService = apiService
|
apiService = apiService
|
||||||
) as T
|
) as T
|
||||||
}
|
}
|
||||||
|
modelClass.isAssignableFrom(AuthViewModel::class.java) -> {
|
||||||
|
AuthViewModel(context = context) as T
|
||||||
|
}
|
||||||
else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
|
else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user