Compare commits
7 Commits
ll
...
new_create
| Author | SHA1 | Date | |
|---|---|---|---|
| 68273ae166 | |||
| 6c7be4ba47 | |||
| cf25540417 | |||
| eca85c8377 | |||
| f8be622ba6 | |||
| f3c841779b | |||
| 922d6e72d6 |
4
.idea/deploymentTargetSelector.xml
generated
4
.idea/deploymentTargetSelector.xml
generated
@@ -4,10 +4,10 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2025-09-08T06:52:32.669239Z">
|
||||
<DropdownSelection timestamp="2025-09-09T09:51:06.656104400Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/liudikang/.android/avd/Pixel_8_API_30.avd" />
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.0.227:45035;connection=094cb92e" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
|
||||
@@ -13,9 +13,11 @@ import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
fun getSafeOkHttpClient(
|
||||
authInterceptor: AuthInterceptor? = null
|
||||
authInterceptor: AuthInterceptor? = null,
|
||||
timeoutSeconds: Long = 30
|
||||
): OkHttpClient {
|
||||
return OkHttpClient.Builder()
|
||||
.apply {
|
||||
@@ -23,6 +25,9 @@ fun getSafeOkHttpClient(
|
||||
addInterceptor(it)
|
||||
}
|
||||
}
|
||||
.connectTimeout(timeoutSeconds, TimeUnit.SECONDS)
|
||||
.readTimeout(timeoutSeconds, TimeUnit.SECONDS)
|
||||
.writeTimeout(timeoutSeconds, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -56,7 +61,7 @@ class AuthInterceptor() : Interceptor {
|
||||
val client = Retrofit.Builder()
|
||||
.baseUrl(ApiClient.RETROFIT_URL)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.client(getSafeOkHttpClient())
|
||||
.client(getSafeOkHttpClient(timeoutSeconds = 30))
|
||||
.build()
|
||||
.create(RaveNowAPI::class.java)
|
||||
|
||||
@@ -75,7 +80,10 @@ object ApiClient {
|
||||
val RETROFIT_URL = "${BASE_API_URL}/"
|
||||
const val TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"
|
||||
private val okHttpClient: OkHttpClient by lazy {
|
||||
getSafeOkHttpClient(authInterceptor = AuthInterceptor())
|
||||
getSafeOkHttpClient(authInterceptor = AuthInterceptor(), timeoutSeconds = 30)
|
||||
}
|
||||
private val longTimeoutOkHttpClient: OkHttpClient by lazy {
|
||||
getSafeOkHttpClient(authInterceptor = AuthInterceptor(), timeoutSeconds = 120)
|
||||
}
|
||||
private val retrofit: Retrofit by lazy {
|
||||
Retrofit.Builder()
|
||||
@@ -84,9 +92,19 @@ object ApiClient {
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
}
|
||||
private val longTimeoutRetrofit: Retrofit by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(RETROFIT_URL)
|
||||
.client(longTimeoutOkHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
}
|
||||
val api: RaveNowAPI by lazy {
|
||||
retrofit.create(RaveNowAPI::class.java)
|
||||
}
|
||||
val longTimeoutApi: RaveNowAPI by lazy {
|
||||
longTimeoutRetrofit.create(RaveNowAPI::class.java)
|
||||
}
|
||||
|
||||
fun formatTime(date: Date): String {
|
||||
val dateFormat = SimpleDateFormat(TIME_FORMAT, Locale.getDefault())
|
||||
|
||||
@@ -42,6 +42,20 @@ data class AgentMomentRequestBody(
|
||||
val sessionId: String
|
||||
)
|
||||
|
||||
data class GenerateAgentInfoRequestBody(
|
||||
@SerializedName("descriptionText")
|
||||
val descriptionText: String
|
||||
)
|
||||
|
||||
data class GenerateAgentInfoResponseBody(
|
||||
@SerializedName("title")
|
||||
val title: String,
|
||||
@SerializedName("description")
|
||||
val description: String,
|
||||
@SerializedName("content")
|
||||
val content: String
|
||||
)
|
||||
|
||||
data class SingleChatRequestBody(
|
||||
@SerializedName("agentOpenId")
|
||||
val agentOpenId: String? = null,
|
||||
@@ -605,7 +619,8 @@ interface RaveNowAPI {
|
||||
suspend fun joinRoom(@Body body: JoinGroupChatRequestBody,
|
||||
): Response<DataContainer<Room>>
|
||||
|
||||
|
||||
@POST("outside/generate/agent-info")
|
||||
suspend fun generateAgentInfo(@Body body: GenerateAgentInfoRequestBody): Response<DataContainer<GenerateAgentInfoResponseBody>>
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.aiosman.ravenow.ui.account.RemoveAccountScreen
|
||||
import com.aiosman.ravenow.ui.account.ResetPasswordScreen
|
||||
import com.aiosman.ravenow.ui.agent.AddAgentScreen
|
||||
import com.aiosman.ravenow.ui.agent.AgentImageCropScreen
|
||||
import com.aiosman.ravenow.ui.agent.CreateAgentV2Screen
|
||||
import com.aiosman.ravenow.ui.group.CreateGroupChatScreen
|
||||
import com.aiosman.ravenow.ui.chat.ChatAiScreen
|
||||
import com.aiosman.ravenow.ui.chat.ChatScreen
|
||||
@@ -544,7 +545,7 @@ fun NavigationController(
|
||||
composable(
|
||||
route = NavigationRoute.AddAgent.route,
|
||||
) {
|
||||
AddAgentScreen()
|
||||
CreateAgentV2Screen()
|
||||
}
|
||||
|
||||
composable(
|
||||
|
||||
@@ -24,6 +24,10 @@ object AddAgentViewModel : ViewModel() {
|
||||
var croppedBitmap by mutableStateOf<Bitmap?>(null)
|
||||
var isUpdating by mutableStateOf(false)
|
||||
var isSelectingAvatar by mutableStateOf(false) // 标记是否正在选择头像
|
||||
var hasExitedPage by mutableStateOf(false) // 标记是否已经完全退出页面
|
||||
|
||||
// 保存AI生成的输入文本,避免页面重建时丢失
|
||||
var generateInputText by mutableStateOf("")
|
||||
|
||||
suspend fun updateAgentAvatar(context: Context) {
|
||||
croppedBitmap?.let {
|
||||
@@ -84,5 +88,7 @@ object AddAgentViewModel : ViewModel() {
|
||||
croppedBitmap = null
|
||||
isUpdating = false
|
||||
isSelectingAvatar = false
|
||||
hasExitedPage = false
|
||||
generateInputText = ""
|
||||
}
|
||||
}
|
||||
648
app/src/main/java/com/aiosman/ravenow/ui/agent/CreateAgentV2.kt
Normal file
648
app/src/main/java/com/aiosman/ravenow/ui/agent/CreateAgentV2.kt
Normal file
@@ -0,0 +1,648 @@
|
||||
package com.aiosman.ravenow.ui.agent
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import kotlinx.coroutines.delay
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.aiosman.ravenow.AppState
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.LocalNavController
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.ui.NavigationRoute
|
||||
import com.aiosman.ravenow.ui.composables.ActionButton
|
||||
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
|
||||
@Composable
|
||||
fun LoadingDots(
|
||||
modifier: Modifier = Modifier,
|
||||
dotColor: Color = Color.Gray
|
||||
) {
|
||||
val infiniteTransition = rememberInfiniteTransition(label = "loading_dots")
|
||||
|
||||
val animationValues = (0..2).map { index ->
|
||||
infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(
|
||||
durationMillis = 600,
|
||||
easing = EaseInOut,
|
||||
delayMillis = index * 200
|
||||
),
|
||||
repeatMode = RepeatMode.Reverse
|
||||
),
|
||||
label = "dot_$index"
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
animationValues.forEach { animValue ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(6.dp)
|
||||
.offset(y = (-8 * animValue.value).dp)
|
||||
.background(
|
||||
color = dotColor.copy(alpha = 0.5f + 0.5f * animValue.value),
|
||||
shape = CircleShape
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CreateAgentV2Screen(
|
||||
viewModel: CreateAgentV2ViewModel = remember { CreateAgentV2ViewModel() }
|
||||
) {
|
||||
// 页面进入时的状态管理
|
||||
LaunchedEffect(Unit) {
|
||||
// 总是先同步状态
|
||||
viewModel.syncStateOnResume()
|
||||
}
|
||||
|
||||
// 页面退出时的处理
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
// 页面退出时,标记为已退出(除非是在选择头像)
|
||||
if (!viewModel.isSelectingAvatar) {
|
||||
viewModel.markPageExited()
|
||||
}
|
||||
}
|
||||
}
|
||||
val appColors = LocalAppTheme.current
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
|
||||
// 获取当前用户名,如果没有则使用默认值
|
||||
val userName = AppState.profile?.nickName ?: "用户"
|
||||
|
||||
// 渐变边框旋转动画
|
||||
val infiniteTransition = rememberInfiniteTransition(label = "gradient_rotation")
|
||||
val rotationAngle by infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 360f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(
|
||||
durationMillis = 16000,
|
||||
easing = LinearEasing
|
||||
),
|
||||
repeatMode = RepeatMode.Restart
|
||||
),
|
||||
label = "rotation_angle"
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(appColors.background)
|
||||
) {
|
||||
// 状态栏占位
|
||||
StatusBarSpacer()
|
||||
|
||||
// 顶部标题栏
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// 返回按钮
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_back_icon),
|
||||
contentDescription = "返回",
|
||||
colorFilter = ColorFilter.tint(appColors.text),
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.noRippleClickable {
|
||||
navController.navigateUp()
|
||||
}
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
// 标题 - 左对齐
|
||||
Text(
|
||||
text = "创建AI",
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.W700,
|
||||
color = appColors.text
|
||||
)
|
||||
}
|
||||
|
||||
// 主要内容区域 - 可滚动
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 24.dp),
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
|
||||
// AI头像图标
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.background(
|
||||
color = appColors.inputBackground,
|
||||
shape = CircleShape
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.ic_create_head_logo),
|
||||
contentDescription = "AI头像",
|
||||
modifier = Modifier.size(48.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
// 问候语
|
||||
Text(
|
||||
text = "$userName 你好呀!今天想创建什么?",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = appColors.text,
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// 描述性文字
|
||||
Text(
|
||||
text = "只需要一句话,你的专属AI在这里诞生",
|
||||
fontSize = 14.sp,
|
||||
color = appColors.secondaryText,
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
|
||||
// 根据模式显示不同的UI
|
||||
if (!viewModel.isManualMode) {
|
||||
// AI生成模式 - 渐变边框输入框
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.shadow(
|
||||
elevation = 8.dp,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
ambientColor = Color(0xFF6246ff).copy(alpha = 0.4f),
|
||||
spotColor = Color(0xFFd80264).copy(alpha = 0.4f)
|
||||
)
|
||||
) {
|
||||
// 渐变边框
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(
|
||||
Color(0xFF6246ff), // 紫色
|
||||
Color(0xFFd80264), // 红色
|
||||
Color(0xFF6246ff), // 紫色
|
||||
Color(0xFFd80264) // 红色
|
||||
),
|
||||
start = Offset(
|
||||
x = cos(Math.toRadians(rotationAngle.toDouble())).toFloat() * 1000f,
|
||||
y = sin(Math.toRadians(rotationAngle.toDouble())).toFloat() * 1000f
|
||||
),
|
||||
end = Offset(
|
||||
x = cos(Math.toRadians(rotationAngle.toDouble() + 180)).toFloat() * 1000f,
|
||||
y = sin(Math.toRadians(rotationAngle.toDouble() + 180)).toFloat() * 1000f
|
||||
)
|
||||
),
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
)
|
||||
.padding(1.5.dp) // 边框宽度
|
||||
) {
|
||||
// 内部输入框
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color = appColors.background,
|
||||
shape = RoundedCornerShape(15.dp)
|
||||
)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Column {
|
||||
TextField(
|
||||
value = viewModel.inputText,
|
||||
onValueChange = {
|
||||
if (!viewModel.isGenerating) {
|
||||
viewModel.updateInputText(it)
|
||||
}
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "一个会写诗的AI,一个会懂你笑点的AI",
|
||||
color = appColors.inputHint,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
},
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
disabledIndicatorColor = Color.Transparent,
|
||||
focusedTextColor = appColors.text,
|
||||
unfocusedTextColor = appColors.text,
|
||||
disabledTextColor = appColors.inputHint,
|
||||
cursorColor = if (viewModel.isGenerating) Color.Transparent else appColors.main,
|
||||
focusedPlaceholderColor = appColors.inputHint,
|
||||
unfocusedPlaceholderColor = appColors.inputHint,
|
||||
disabledPlaceholderColor = appColors.inputHint.copy(alpha = 0.5f)
|
||||
),
|
||||
enabled = !viewModel.isGenerating,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 100.dp)
|
||||
)
|
||||
|
||||
// AI美化按钮
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
TextButton(
|
||||
onClick = {
|
||||
if (!viewModel.isGenerating) {
|
||||
viewModel.generateAgentInfo()
|
||||
}
|
||||
},
|
||||
enabled = viewModel.canGenerate() && !viewModel.isGenerating,
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = if (viewModel.isGenerating) appColors.inputHint else Color(0xFF7c46ed),
|
||||
disabledContentColor = appColors.inputHint
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.ic_create_agent_generate),
|
||||
contentDescription = "AI美化图标",
|
||||
modifier = Modifier.size(16.dp),
|
||||
colorFilter = ColorFilter.tint(Color(0xFF7c46ed))
|
||||
)
|
||||
Text(
|
||||
text = if (viewModel.isGenerating) "生成中..." else "AI美化",
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// AI生成中的loading状态
|
||||
if (viewModel.isGenerating) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
LoadingDots(
|
||||
dotColor = appColors.main
|
||||
)
|
||||
Text(
|
||||
text = "正在为你构思",
|
||||
fontSize = 14.sp,
|
||||
color = appColors.secondaryText,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
// 手动创造AI按钮 - 只在非生成状态下显示
|
||||
if (!viewModel.isGenerating) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start
|
||||
) {
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
if (!viewModel.isGenerating) {
|
||||
viewModel.enableManualMode()
|
||||
}
|
||||
},
|
||||
enabled = !viewModel.isGenerating,
|
||||
modifier = Modifier.height(40.dp),
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
border = BorderStroke(1.dp, appColors.inputHint.copy(alpha = 0.3f)),
|
||||
colors = ButtonDefaults.outlinedButtonColors(
|
||||
contentColor = appColors.secondaryText,
|
||||
disabledContentColor = appColors.inputHint
|
||||
),
|
||||
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Edit,
|
||||
contentDescription = "编辑图标",
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = appColors.secondaryText
|
||||
)
|
||||
Text(
|
||||
text = "手动创造AI",
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 手动模式 - "一句话创造AI"按钮
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.shadow(
|
||||
elevation = 6.dp,
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
ambientColor = Color(0xFF6246ff).copy(alpha = 0.3f),
|
||||
spotColor = Color(0xFFd80264).copy(alpha = 0.3f)
|
||||
)
|
||||
) {
|
||||
// 渐变边框
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(
|
||||
Color(0xFF6246ff), // 紫色
|
||||
Color(0xFFd80264), // 红色
|
||||
Color(0xFF6246ff), // 紫色
|
||||
Color(0xFFd80264) // 红色
|
||||
),
|
||||
start = Offset(
|
||||
x = cos(Math.toRadians(rotationAngle.toDouble())).toFloat() * 1000f,
|
||||
y = sin(Math.toRadians(rotationAngle.toDouble())).toFloat() * 1000f
|
||||
),
|
||||
end = Offset(
|
||||
x = cos(Math.toRadians(rotationAngle.toDouble() + 180)).toFloat() * 1000f,
|
||||
y = sin(Math.toRadians(rotationAngle.toDouble() + 180)).toFloat() * 1000f
|
||||
)
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
.padding(1.dp) // 边框宽度
|
||||
) {
|
||||
// 内部按钮
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = appColors.background,
|
||||
shape = RoundedCornerShape(11.dp)
|
||||
)
|
||||
.noRippleClickable {
|
||||
viewModel.disableManualMode()
|
||||
}
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Edit,
|
||||
contentDescription = "编辑图标",
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = appColors.secondaryText
|
||||
)
|
||||
Text(
|
||||
text = "一句话创造AI",
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = appColors.secondaryText
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 生成结果显示区域
|
||||
if (viewModel.hasGeneratedResult()) {
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
// 头像选择组件
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(72.dp)
|
||||
.noRippleClickable {
|
||||
viewModel.setSelectingAvatar(true)
|
||||
navController.navigate(NavigationRoute.AgentImageCrop.route)
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (viewModel.croppedBitmap != null) {
|
||||
// 有头像时显示头像
|
||||
CustomAsyncImage(
|
||||
context,
|
||||
viewModel.croppedBitmap,
|
||||
modifier = Modifier
|
||||
.size(72.dp)
|
||||
.clip(CircleShape),
|
||||
contentDescription = "AI头像",
|
||||
contentScale = ContentScale.Crop,
|
||||
placeholderRes = R.mipmap.rider_pro_agent_avatar,
|
||||
showShimmer = false
|
||||
)
|
||||
} else {
|
||||
// 没有头像时显示渐变背景和编辑图标
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(72.dp)
|
||||
.clip(CircleShape)
|
||||
.background(
|
||||
brush = Brush.verticalGradient(
|
||||
0f to Color(0xFF7c45ed),
|
||||
0.24f to Color(0xFF7c68ef),
|
||||
1f to Color(0xFF7bd8f8)
|
||||
)
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Edit,
|
||||
contentDescription = "选择头像",
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
// 标题输入框
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = "名称",
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = appColors.text,
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
|
||||
TextField(
|
||||
value = viewModel.agentTitle,
|
||||
onValueChange = { viewModel.updateAgentTitle(it) },
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = appColors.inputBackground,
|
||||
unfocusedContainerColor = appColors.inputBackground,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
focusedTextColor = appColors.text,
|
||||
unfocusedTextColor = appColors.text
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// 描述输入框
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = "描述",
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = appColors.text,
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
|
||||
TextField(
|
||||
value = viewModel.agentDescription,
|
||||
onValueChange = { viewModel.updateAgentDescription(it) },
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = appColors.inputBackground,
|
||||
unfocusedContainerColor = appColors.inputBackground,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
focusedTextColor = appColors.text,
|
||||
unfocusedTextColor = appColors.text
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 120.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
// 错误信息显示
|
||||
viewModel.errorMessage?.let { error ->
|
||||
Text(
|
||||
text = error,
|
||||
color = Color.Red,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
fontSize = 14.sp
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
// 创建AI按钮
|
||||
ActionButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(
|
||||
Color(0xFF7bd8f8),
|
||||
Color(0xFF7c68ef),
|
||||
Color(0xFF7c45ed)
|
||||
)
|
||||
),
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
),
|
||||
color = Color.White,
|
||||
backgroundColor = Color.Transparent,
|
||||
text = "好的,就它了",
|
||||
isLoading = viewModel.isCreating,
|
||||
loadingText = "创建中...",
|
||||
enabled = viewModel.canCreate()
|
||||
) {
|
||||
viewModel.createAgent(context) {
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
package com.aiosman.ravenow.ui.agent
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.aiosman.ravenow.data.api.ApiClient
|
||||
import com.aiosman.ravenow.data.api.GenerateAgentInfoRequestBody
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class CreateAgentV2ViewModel : ViewModel() {
|
||||
// UI状态
|
||||
var inputText by mutableStateOf("")
|
||||
private set
|
||||
|
||||
var agentTitle by mutableStateOf("")
|
||||
private set
|
||||
|
||||
var agentDescription by mutableStateOf("")
|
||||
private set
|
||||
|
||||
var isGenerating by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
var errorMessage by mutableStateOf<String?>(null)
|
||||
private set
|
||||
|
||||
var isCreating by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
var isManualMode by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
// 临时保存的生成结果,用于在生成过程中暂时隐藏当前结果
|
||||
private var tempAgentTitle by mutableStateOf("")
|
||||
private var tempAgentDescription by mutableStateOf("")
|
||||
|
||||
// AddAgentViewModel实例,用于头像和创建逻辑
|
||||
private val addAgentViewModel = AddAgentViewModel
|
||||
|
||||
// 获取头像相关状态
|
||||
val croppedBitmap get() = addAgentViewModel.croppedBitmap
|
||||
val isSelectingAvatar get() = addAgentViewModel.isSelectingAvatar
|
||||
|
||||
init {
|
||||
// 初始化时检查是否需要恢复状态
|
||||
if (addAgentViewModel.hasExitedPage) {
|
||||
// 如果之前已经完全退出页面,清空所有数据
|
||||
addAgentViewModel.clearData()
|
||||
} else {
|
||||
// 否则恢复已有状态(包括从头像选择回来的情况)
|
||||
if (addAgentViewModel.name.isNotEmpty()) {
|
||||
agentTitle = addAgentViewModel.name
|
||||
}
|
||||
if (addAgentViewModel.desc.isNotEmpty()) {
|
||||
agentDescription = addAgentViewModel.desc
|
||||
}
|
||||
// 恢复输入文本
|
||||
if (addAgentViewModel.generateInputText.isNotEmpty()) {
|
||||
inputText = addAgentViewModel.generateInputText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateInputText(text: String) {
|
||||
inputText = text
|
||||
addAgentViewModel.generateInputText = text // 同时保存到AddAgentViewModel
|
||||
clearError()
|
||||
}
|
||||
|
||||
fun updateAgentTitle(title: String) {
|
||||
agentTitle = title
|
||||
syncToAddAgentViewModel()
|
||||
clearError()
|
||||
}
|
||||
|
||||
fun updateAgentDescription(description: String) {
|
||||
agentDescription = description
|
||||
syncToAddAgentViewModel()
|
||||
clearError()
|
||||
}
|
||||
|
||||
private fun clearError() {
|
||||
errorMessage = null
|
||||
}
|
||||
|
||||
private fun syncToAddAgentViewModel() {
|
||||
addAgentViewModel.name = agentTitle
|
||||
addAgentViewModel.desc = agentDescription
|
||||
}
|
||||
|
||||
fun setSelectingAvatar(isSelecting: Boolean) {
|
||||
addAgentViewModel.isSelectingAvatar = isSelecting
|
||||
}
|
||||
|
||||
fun markPageExited() {
|
||||
addAgentViewModel.hasExitedPage = true
|
||||
}
|
||||
|
||||
fun syncStateOnResume() {
|
||||
// 如果之前在选择头像,现在回来了,重置选择状态
|
||||
if (addAgentViewModel.isSelectingAvatar) {
|
||||
addAgentViewModel.isSelectingAvatar = false
|
||||
// 从头像选择页面回来,恢复文本状态
|
||||
if (addAgentViewModel.name.isNotEmpty()) {
|
||||
agentTitle = addAgentViewModel.name
|
||||
}
|
||||
if (addAgentViewModel.desc.isNotEmpty()) {
|
||||
agentDescription = addAgentViewModel.desc
|
||||
}
|
||||
if (addAgentViewModel.generateInputText.isNotEmpty()) {
|
||||
inputText = addAgentViewModel.generateInputText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun enableManualMode() {
|
||||
isManualMode = true
|
||||
// 手动模式下,如果没有现有内容,初始化为空
|
||||
if (agentTitle.isEmpty() && agentDescription.isEmpty()) {
|
||||
agentTitle = ""
|
||||
agentDescription = ""
|
||||
}
|
||||
}
|
||||
|
||||
fun disableManualMode() {
|
||||
isManualMode = false
|
||||
}
|
||||
|
||||
fun generateAgentInfo() {
|
||||
if (inputText.isBlank() || isGenerating) return
|
||||
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
isGenerating = true
|
||||
clearError()
|
||||
|
||||
// 开始生成时,暂存当前结果并清空显示
|
||||
tempAgentTitle = agentTitle
|
||||
tempAgentDescription = agentDescription
|
||||
agentTitle = ""
|
||||
agentDescription = ""
|
||||
|
||||
val response = ApiClient.longTimeoutApi.generateAgentInfo(
|
||||
GenerateAgentInfoRequestBody(inputText)
|
||||
)
|
||||
|
||||
if (response.isSuccessful) {
|
||||
val data = response.body()?.data
|
||||
data?.let {
|
||||
// 成功时,使用新结果
|
||||
agentTitle = it.title
|
||||
agentDescription = it.description
|
||||
syncToAddAgentViewModel()
|
||||
// 清空临时保存
|
||||
tempAgentTitle = ""
|
||||
tempAgentDescription = ""
|
||||
}
|
||||
} else {
|
||||
// 失败时,恢复之前的结果
|
||||
agentTitle = tempAgentTitle
|
||||
agentDescription = tempAgentDescription
|
||||
tempAgentTitle = ""
|
||||
tempAgentDescription = ""
|
||||
errorMessage = "生成失败,请重试"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 异常时,恢复之前的结果
|
||||
agentTitle = tempAgentTitle
|
||||
agentDescription = tempAgentDescription
|
||||
tempAgentTitle = ""
|
||||
tempAgentDescription = ""
|
||||
errorMessage = "网络错误: ${e.message}"
|
||||
} finally {
|
||||
isGenerating = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createAgent(context: Context, onSuccess: () -> Unit) {
|
||||
if (isCreating) return
|
||||
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
isCreating = true
|
||||
clearError()
|
||||
|
||||
// 验证输入
|
||||
val validationError = addAgentViewModel.validate()
|
||||
if (validationError != null) {
|
||||
errorMessage = validationError
|
||||
return@launch
|
||||
}
|
||||
|
||||
// 调用创建智能体API
|
||||
val result = addAgentViewModel.createAgent(context)
|
||||
if (result != null) {
|
||||
// 创建成功,清空数据
|
||||
clearData()
|
||||
onSuccess()
|
||||
} else {
|
||||
errorMessage = "创建失败,请重试"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
errorMessage = "创建智能体失败: ${e.message}"
|
||||
} finally {
|
||||
isCreating = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clearData() {
|
||||
inputText = ""
|
||||
agentTitle = ""
|
||||
agentDescription = ""
|
||||
errorMessage = null
|
||||
isGenerating = false
|
||||
isCreating = false
|
||||
addAgentViewModel.clearData()
|
||||
}
|
||||
|
||||
// 检查是否可以创建
|
||||
fun canCreate(): Boolean {
|
||||
return !isCreating && agentTitle.isNotBlank() && agentDescription.isNotBlank()
|
||||
}
|
||||
|
||||
// 检查是否可以生成
|
||||
fun canGenerate(): Boolean {
|
||||
return !isGenerating && inputText.isNotBlank()
|
||||
}
|
||||
|
||||
// 检查是否有生成结果或处于手动模式
|
||||
fun hasGeneratedResult(): Boolean {
|
||||
return agentTitle.isNotEmpty() || agentDescription.isNotEmpty() || isManualMode
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.aiosman.ravenow.ui.index
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.BottomSheetDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CreateBottomSheet(
|
||||
sheetState: SheetState,
|
||||
onDismiss: () -> Unit,
|
||||
onAiClick: () -> Unit,
|
||||
onGroupChatClick: () -> Unit,
|
||||
onMomentClick: () -> Unit
|
||||
) {
|
||||
val appColors = LocalAppTheme.current
|
||||
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = onDismiss,
|
||||
sheetState = sheetState,
|
||||
windowInsets = BottomSheetDefaults.windowInsets,
|
||||
containerColor = appColors.background,
|
||||
dragHandle = null,
|
||||
shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 24.dp, bottom = 24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
// 标题
|
||||
Text(
|
||||
text = stringResource(R.string.create_title),
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = appColors.text,
|
||||
modifier = Modifier.padding(bottom = 32.dp)
|
||||
)
|
||||
|
||||
// 三个创建选项
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
// 群聊选项
|
||||
CreateOption(
|
||||
icon = R.drawable.ic_create_group_chat,
|
||||
label = stringResource(R.string.create_group_chat_option),
|
||||
onClick = onGroupChatClick
|
||||
)
|
||||
// 动态选项
|
||||
CreateOption(
|
||||
icon = R.drawable.ic_create_monent,
|
||||
label = stringResource(R.string.create_moment),
|
||||
onClick = onMomentClick
|
||||
)
|
||||
// AI选项
|
||||
CreateOption(
|
||||
icon = R.drawable.ic_create_ai,
|
||||
label = stringResource(R.string.create_ai),
|
||||
onClick = onAiClick
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
|
||||
// 关闭按钮
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.clip(CircleShape)
|
||||
.noRippleClickable { onDismiss() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.ic_create_close),
|
||||
contentDescription = stringResource(R.string.create_close),
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreateOption(
|
||||
icon: Int,
|
||||
label: String,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
val appColors = LocalAppTheme.current
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.noRippleClickable { onClick() }
|
||||
) {
|
||||
// 直接使用图标,不要背景
|
||||
Image(
|
||||
painter = painterResource(icon),
|
||||
contentDescription = label,
|
||||
modifier = Modifier.size(72.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
// 文字标签
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = appColors.text
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.DrawerValue
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ModalNavigationDrawer
|
||||
import androidx.compose.material3.NavigationBar
|
||||
@@ -34,6 +35,7 @@ import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.material3.rememberDrawerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
@@ -78,9 +80,10 @@ import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
import com.aiosman.ravenow.ui.post.NewPostViewModel
|
||||
import com.aiosman.ravenow.utils.ResourceCleanupManager
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun IndexScreen() {
|
||||
val AppColors = LocalAppTheme.current
|
||||
@@ -101,6 +104,7 @@ fun IndexScreen() {
|
||||
val pagerState = rememberPagerState(pageCount = { item.size })
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val bottomSheetState = rememberModalBottomSheetState()
|
||||
val context = LocalContext.current
|
||||
|
||||
// 注意:不要在离开 Index 路由时全量清理资源,以免返回后列表被重置
|
||||
@@ -292,8 +296,8 @@ fun IndexScreen() {
|
||||
navController.navigate(NavigationRoute.Login.route)
|
||||
return@noRippleClickable
|
||||
}
|
||||
NewPostViewModel.asNewPost()
|
||||
navController.navigate(NavigationRoute.NewPost.route)
|
||||
// 显示创建底部弹窗
|
||||
model.showCreateBottomSheet = true
|
||||
return@noRippleClickable
|
||||
}
|
||||
|
||||
@@ -389,6 +393,56 @@ fun IndexScreen() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建底部弹窗
|
||||
if (model.showCreateBottomSheet) {
|
||||
CreateBottomSheet(
|
||||
sheetState = bottomSheetState,
|
||||
onDismiss = {
|
||||
// 使用协程来优雅地关闭弹窗
|
||||
coroutineScope.launch {
|
||||
bottomSheetState.hide()
|
||||
model.showCreateBottomSheet = false
|
||||
}
|
||||
},
|
||||
onAiClick = {
|
||||
// 使用协程来优雅地关闭弹窗并导航
|
||||
coroutineScope.launch {
|
||||
bottomSheetState.hide() // 触发关闭动画
|
||||
model.showCreateBottomSheet = false
|
||||
// 检查游客模式,如果是游客则跳转登录
|
||||
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CREATE_AGENT)) {
|
||||
navController.navigate(NavigationRoute.Login.route)
|
||||
} else {
|
||||
navController.navigate(NavigationRoute.AddAgent.route)
|
||||
}
|
||||
}
|
||||
},
|
||||
onGroupChatClick = {
|
||||
// 使用协程来优雅地关闭弹窗并导航
|
||||
coroutineScope.launch {
|
||||
bottomSheetState.hide() // 触发关闭动画
|
||||
model.showCreateBottomSheet = false
|
||||
// 检查游客模式,如果是游客则跳转登录
|
||||
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.JOIN_GROUP_CHAT)) {
|
||||
navController.navigate(NavigationRoute.Login.route)
|
||||
} else {
|
||||
navController.navigate(NavigationRoute.CreateGroupChat.route)
|
||||
}
|
||||
}
|
||||
},
|
||||
onMomentClick = {
|
||||
// 使用协程来优雅地关闭弹窗并导航
|
||||
coroutineScope.launch {
|
||||
bottomSheetState.hide() // 触发关闭动画
|
||||
model.showCreateBottomSheet = false
|
||||
// 导航到动态创建页面
|
||||
NewPostViewModel.asNewPost()
|
||||
navController.navigate(NavigationRoute.NewPost.route)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,9 +9,12 @@ object IndexViewModel:ViewModel() {
|
||||
var tabIndex by mutableStateOf(0)
|
||||
|
||||
var openDrawer by mutableStateOf(false)
|
||||
|
||||
var showCreateBottomSheet by mutableStateOf(false)
|
||||
|
||||
fun ResetModel(){
|
||||
tabIndex = 0
|
||||
showCreateBottomSheet = false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
@@ -67,6 +68,12 @@ import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.ExploreViewModel
|
||||
import com.aiosman.ravenow.utils.DebounceUtils
|
||||
import com.aiosman.ravenow.utils.ResourceCleanupManager
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
|
||||
@OptIn( ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
@@ -82,7 +89,8 @@ fun Agent() {
|
||||
var scope = rememberCoroutineScope()
|
||||
|
||||
val viewModel: AgentViewModel = viewModel()
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
// 确保推荐Agent数据已加载
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.ensureDataLoaded()
|
||||
@@ -102,6 +110,7 @@ fun Agent() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(scrollState)
|
||||
.padding(
|
||||
top = statusBarPaddingValues.calculateTopPadding(),
|
||||
bottom = navigationBarPaddings,
|
||||
@@ -196,7 +205,6 @@ fun Agent() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(400.dp)
|
||||
.padding(vertical = 8.dp)
|
||||
|
||||
) {
|
||||
@@ -221,73 +229,95 @@ fun Agent() {
|
||||
// )
|
||||
// }
|
||||
|
||||
var selectedTabIndex by remember { mutableStateOf(0) }
|
||||
|
||||
// 标签页
|
||||
Row(
|
||||
LazyRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding( bottom = 16.dp),
|
||||
.padding(bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.Bottom
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// 推荐标签(默认选中)
|
||||
CustomTabItem(
|
||||
text = stringResource(R.string.agent_recommend),
|
||||
isSelected = true,
|
||||
onClick = {
|
||||
// TODO: 实现点击切换逻辑
|
||||
}
|
||||
)
|
||||
item {
|
||||
CustomTabItem(
|
||||
text = stringResource(R.string.agent_recommend),
|
||||
isSelected = selectedTabIndex == 0,
|
||||
onClick = {
|
||||
selectedTabIndex = 0
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
TabSpacer()
|
||||
item {
|
||||
TabSpacer()
|
||||
}
|
||||
|
||||
// Scenes标签
|
||||
CustomTabItem(
|
||||
text = "scenes",
|
||||
isSelected = false,
|
||||
onClick = {
|
||||
// TODO: 实现点击切换逻辑
|
||||
}
|
||||
)
|
||||
item {
|
||||
CustomTabItem(
|
||||
text = "scenes",
|
||||
isSelected = selectedTabIndex == 1,
|
||||
onClick = {
|
||||
selectedTabIndex = 1
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
TabSpacer()
|
||||
item {
|
||||
TabSpacer()
|
||||
}
|
||||
|
||||
// Voices标签
|
||||
CustomTabItem(
|
||||
text = "voices",
|
||||
isSelected = false,
|
||||
onClick = {
|
||||
// TODO: 实现点击切换逻辑
|
||||
}
|
||||
)
|
||||
item {
|
||||
CustomTabItem(
|
||||
text = "voices",
|
||||
isSelected = selectedTabIndex == 2,
|
||||
onClick = {
|
||||
selectedTabIndex = 2
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
TabSpacer()
|
||||
item {
|
||||
TabSpacer()
|
||||
}
|
||||
|
||||
// Anime标签
|
||||
CustomTabItem(
|
||||
text = "anime",
|
||||
isSelected = false,
|
||||
onClick = {
|
||||
// TODO: 实现点击切换逻辑
|
||||
}
|
||||
)
|
||||
item {
|
||||
CustomTabItem(
|
||||
text = "anime",
|
||||
isSelected = selectedTabIndex == 3,
|
||||
onClick = {
|
||||
selectedTabIndex = 3
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// TabSpacer()
|
||||
//
|
||||
// // Assist标签
|
||||
// CustomTabItem(
|
||||
// text = "assist",
|
||||
// isSelected = false,
|
||||
// onClick = {
|
||||
// // TODO: 实现点击切换逻辑
|
||||
// }
|
||||
// )
|
||||
item {
|
||||
TabSpacer()
|
||||
}
|
||||
|
||||
item {
|
||||
CustomTabItem(
|
||||
text = "assist",
|
||||
isSelected = selectedTabIndex == 4,
|
||||
onClick = {
|
||||
selectedTabIndex = 4
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
when (selectedTabIndex) {
|
||||
0 -> {
|
||||
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15), viewModel)
|
||||
}
|
||||
else -> {
|
||||
val shuffledAgents = viewModel.agentItems.shuffled().take(15)
|
||||
AgentViewPagerSection(agentItems = shuffledAgents, viewModel)
|
||||
}
|
||||
}
|
||||
// Agent ViewPager
|
||||
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15),viewModel)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(0.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -309,92 +339,150 @@ fun Agent() {
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
|
||||
color = AppColors.text
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
// 只有非游客用户才显示"我的Agent"tab
|
||||
if (!AppStore.isGuest) {
|
||||
TabItem(
|
||||
text = stringResource(R.string.agent_mine),
|
||||
isSelected = pagerState.currentPage == 0,
|
||||
onClick = {
|
||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 300L) {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(0)
|
||||
}
|
||||
}) {
|
||||
lastClickTime = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
)
|
||||
TabSpacer()
|
||||
}
|
||||
|
||||
TabItem(
|
||||
text = stringResource(R.string.agent_hot),
|
||||
isSelected = if (AppStore.isGuest) pagerState.currentPage == 0 else pagerState.currentPage == 1,
|
||||
onClick = {
|
||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 300L) {
|
||||
scope.launch {
|
||||
val targetPage = if (AppStore.isGuest) 0 else 1
|
||||
pagerState.animateScrollToPage(targetPage)
|
||||
}
|
||||
}) {
|
||||
lastClickTime = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
)
|
||||
/*TabSpacer()
|
||||
TabItem(
|
||||
text = stringResource(R.string.agent_recommend),
|
||||
isSelected = pagerState.currentPage == 2,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(2)
|
||||
}
|
||||
}
|
||||
)
|
||||
TabSpacer()
|
||||
TabItem(
|
||||
text = stringResource(R.string.agent_other),
|
||||
isSelected = pagerState.currentPage == 3,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(3)
|
||||
}
|
||||
}
|
||||
)*/
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
Spacer(modifier = Modifier.height(50.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
beyondBoundsPageCount = 1 // 预加载相邻页面,避免切换时重新加载
|
||||
.weight(1f)
|
||||
) {
|
||||
if (AppStore.isGuest) {
|
||||
// 游客模式下只显示热门Agent
|
||||
when (it) {
|
||||
0 -> {
|
||||
HotAgent()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 正常用户显示我的Agent和热门Agent
|
||||
when (it) {
|
||||
0 -> {
|
||||
MineAgent()
|
||||
}
|
||||
|
||||
1 -> {
|
||||
HotAgent()
|
||||
}
|
||||
val agentItems = viewModel.agentItems.take(15)
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(2),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(50.dp)
|
||||
) {
|
||||
items(agentItems) { agentItem ->
|
||||
AgentCardSquare(
|
||||
agentItem = agentItem,
|
||||
viewModel = viewModel,
|
||||
navController = LocalNavController.current
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@SuppressLint("SuspiciousIndentation")
|
||||
@Composable
|
||||
fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navController: NavHostController) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val cardHeight = 180.dp
|
||||
val avatarSize = cardHeight / 3 // 头像大小为方块高度的三分之一
|
||||
|
||||
// 防抖状态
|
||||
var lastClickTime by remember { mutableStateOf(0L) }
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(cardHeight)
|
||||
.background(Color(0xFFE0E0E0), RoundedCornerShape(12.dp)) // 灰色背景
|
||||
.clickable {
|
||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
||||
viewModel.goToProfile(agentItem.openId, navController)
|
||||
}) {
|
||||
lastClickTime = System.currentTimeMillis()
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
// 头像,位于方块上方居中,部分悬于方块外部
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.offset(y = -avatarSize / 2)
|
||||
.size(avatarSize)
|
||||
.background(Color.White, RoundedCornerShape(avatarSize / 2))
|
||||
.clip(RoundedCornerShape(avatarSize / 2)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (agentItem.avatar.isNotEmpty()) {
|
||||
CustomAsyncImage(
|
||||
imageUrl = agentItem.avatar,
|
||||
contentDescription = "Agent头像",
|
||||
modifier = Modifier
|
||||
.size(avatarSize)
|
||||
.clip(RoundedCornerShape(avatarSize / 2)),
|
||||
contentScale = androidx.compose.ui.layout.ContentScale.Crop
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.rider_pro_agent),
|
||||
contentDescription = "默认头像",
|
||||
modifier = Modifier.size(avatarSize / 2),
|
||||
colorFilter = ColorFilter.tint(AppColors.secondaryText)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 内容区域(名称和描述)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = avatarSize / 2 + 8.dp, start = 8.dp, end = 8.dp, bottom = 8.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
androidx.compose.material3.Text(
|
||||
text = agentItem.title,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
|
||||
color = AppColors.text,
|
||||
maxLines = 1,
|
||||
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
androidx.compose.material3.Text(
|
||||
text = agentItem.desc,
|
||||
fontSize = 14.sp,
|
||||
color = AppColors.secondaryText,
|
||||
maxLines = 2,
|
||||
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f, fill = false)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
// 聊天按钮,位于底部居中
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(80.dp)
|
||||
.height(32.dp)
|
||||
.background(
|
||||
color = Color(0X147c7480),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.clickable {
|
||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
||||
// 检查游客模式,如果是游客则跳转登录
|
||||
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CHAT_WITH_AGENT)) {
|
||||
navController.navigate(NavigationRoute.Login.route)
|
||||
} else {
|
||||
viewModel.createSingleChat(agentItem.openId)
|
||||
viewModel.goToChatAi(
|
||||
agentItem.openId,
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
}) {
|
||||
lastClickTime = System.currentTimeMillis()
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
androidx.compose.material3.Text(
|
||||
text = stringResource(R.string.chat),
|
||||
fontSize = 12.sp,
|
||||
color = AppColors.text,
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel) {
|
||||
|
||||
18
app/src/main/res/drawable/ic_create_agent_generate.xml
Normal file
18
app/src/main/res/drawable/ic_create_agent_generate.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="m9.098,4.33 l1.475,-1.475a0.643,0.643 0,0 1,0.909 0l1.663,1.663a0.643,0.643 0,0 1,0 0.91L11.67,6.901M9.098,4.33l-6.243,6.242a0.643,0.643 0,0 0,-0.188 0.455v1.663c0,0.355 0.288,0.643 0.643,0.643h1.663c0.17,0 0.334,-0.067 0.455,-0.188l6.242,-6.243M9.098,4.33l2.572,2.572"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.333"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#7C45ED"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M3.608,6.333c0.13,0 0.202,-0.07 0.215,-0.208 0.056,-0.434 0.114,-0.775 0.172,-1.022 0.06,-0.247 0.152,-0.434 0.28,-0.56 0.128,-0.126 0.318,-0.221 0.57,-0.286 0.252,-0.066 0.599,-0.133 1.042,-0.202 0.152,-0.026 0.228,-0.1 0.228,-0.222 0,-0.06 -0.02,-0.11 -0.059,-0.146a0.304,0.304 0,0 0,-0.143 -0.075,15.533 15.533,0 0,1 -1.048,-0.225c-0.256,-0.067 -0.45,-0.161 -0.583,-0.283 -0.132,-0.121 -0.228,-0.304 -0.287,-0.547a10.18,10.18 0,0 1,-0.172 -1.015c-0.013,-0.14 -0.085,-0.209 -0.215,-0.209a0.221,0.221 0,0 0,-0.153 0.056,0.208 0.208,0 0,0 -0.068,0.146 9.948,9.948 0,0 1,-0.176 1.039c-0.06,0.25 -0.155,0.438 -0.283,0.566 -0.128,0.128 -0.32,0.224 -0.576,0.286 -0.256,0.063 -0.608,0.125 -1.055,0.186a0.251,0.251 0,0 0,-0.143 0.072,0.203 0.203,0 0,0 -0.059,0.15c0,0.06 0.02,0.109 0.059,0.146 0.039,0.037 0.086,0.062 0.143,0.075 0.447,0.082 0.799,0.158 1.055,0.228 0.256,0.069 0.448,0.166 0.576,0.29 0.128,0.123 0.221,0.306 0.28,0.55 0.058,0.242 0.118,0.581 0.179,1.015a0.202,0.202 0,0 0,0.068 0.14,0.221 0.221,0 0,0 0.153,0.055zM12.516,14.702c0.086,0 0.139,-0.05 0.156,-0.15 0.056,-0.308 0.108,-0.552 0.156,-0.732a0.963,0.963 0,0 1,0.208 -0.417,0.879 0.879,0 0,1 0.41,-0.225c0.183,-0.052 0.439,-0.106 0.769,-0.162 0.1,-0.018 0.15,-0.072 0.15,-0.163 0,-0.091 -0.05,-0.146 -0.15,-0.163a6.959,6.959 0,0 1,-0.768 -0.166,0.919 0.919,0 0,1 -0.41,-0.224 0.937,0.937 0,0 1,-0.209 -0.414c-0.048,-0.18 -0.1,-0.426 -0.156,-0.739 -0.017,-0.095 -0.07,-0.143 -0.156,-0.143 -0.091,0 -0.146,0.048 -0.163,0.143a9.897,9.897 0,0 1,-0.156 0.74,0.937 0.937,0 0,1 -0.209,0.413 0.919,0.919 0,0 1,-0.41 0.224c-0.182,0.054 -0.436,0.11 -0.762,0.166 -0.1,0.017 -0.15,0.072 -0.15,0.163 0,0.091 0.05,0.145 0.15,0.163 0.326,0.056 0.58,0.11 0.762,0.162a0.879,0.879 0,0 1,0.41 0.225c0.091,0.098 0.16,0.237 0.209,0.417 0.047,0.18 0.1,0.424 0.156,0.732 0.009,0.044 0.026,0.08 0.052,0.108a0.143,0.143 0,0 0,0.11 0.042z"
|
||||
android:fillColor="#7C45ED"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
19
app/src/main/res/drawable/ic_create_ai.xml
Normal file
19
app/src/main/res/drawable/ic_create_ai.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="72dp"
|
||||
android:height="72dp"
|
||||
android:viewportWidth="72"
|
||||
android:viewportHeight="72">
|
||||
<path
|
||||
android:pathData="M36,36m-36,0a36,36 0,1 1,72 0a36,36 0,1 1,-72 0"
|
||||
android:fillColor="#8FBFFA"
|
||||
android:fillAlpha="0.08"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M36.804,31.654h-0.108c-2.5,0 -5.222,0 -7.559,0.409a4.115,4.115 0,0 0,-3.3 3.202c-0.337,1.56 -0.337,2.86 -0.337,5.224v0.175c0,2.365 0,3.665 0.338,5.225a4.115,4.115 0,0 0,3.3 3.2c2.336,0.411 5.058,0.411 7.558,0.411h0.108c2.5,0 5.222,0 7.559,-0.41a4.115,4.115 0,0 0,3.3 -3.2C48,44.328 48,43.03 48,40.663v-0.175c0,-2.364 0,-3.663 -0.338,-5.224a4.115,4.115 0,0 0,-3.3 -3.202c-2.336,-0.409 -5.058,-0.409 -7.558,-0.409z"
|
||||
android:fillColor="#8FBFFA"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M36.75,22.5c-1.06,0 -2.031,0.302 -2.738,1.006 -0.704,0.704 -1.004,1.673 -1.004,2.735 0,1.058 0.302,2.028 1.006,2.732 0.35,0.35 0.765,0.601 1.228,0.764v1.921c0.49,-0.004 0.977,-0.004 1.454,-0.004h0.108c0.48,0 0.967,0 1.456,0.004v-1.92c0.461,-0.16 0.881,-0.421 1.228,-0.765 0.704,-0.704 1.006,-1.674 1.006,-2.734s-0.302,-2.03 -1.006,-2.733c-0.705,-0.704 -1.677,-1.006 -2.738,-1.006zM32.682,35.919c0.694,0 1.258,0.563 1.258,1.257v1.213a1.258,1.258 0,0 1,-2.516 0v-1.213c0,-0.694 0.564,-1.257 1.258,-1.257zM40.818,35.919c0.694,0 1.258,0.563 1.258,1.257v1.213a1.258,1.258 0,0 1,-2.516 0v-1.213c0,-0.694 0.564,-1.257 1.258,-1.257zM32.193,41.933a1.259,1.259 0,0 1,1.7 0.48l0.013,0.019c0.16,0.227 0.354,0.43 0.573,0.601 0.435,0.342 1.154,0.718 2.271,0.718 1.115,0 1.836,-0.378 2.273,-0.718a2.87,2.87 0,0 0,0.571 -0.603l0.012,-0.016A1.259,1.259 0,0 1,41.8 43.64l-1.1,-0.606 1.1,0.608v0.004l-0.004,0.006 -0.008,0.014 -0.022,0.036 -0.066,0.105a5.392,5.392 0,0 1,-1.128 1.202c-0.82,0.646 -2.077,1.256 -3.824,1.256 -1.743,0 -3,-0.61 -3.822,-1.256a5.392,5.392 0,0 1,-1.128 -1.202,2.99 2.99,0 0,1 -0.064,-0.105l-0.02,-0.036 -0.01,-0.014 -0.002,-0.006 -0.002,-0.002c0,-0.002 0,-0.004 1.1,-0.61l-1.102,0.608a1.256,1.256 0,0 1,0.495 -1.71z"
|
||||
android:fillColor="#2859C5"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
15
app/src/main/res/drawable/ic_create_close.xml
Normal file
15
app/src/main/res/drawable/ic_create_close.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="33dp"
|
||||
android:viewportWidth="32"
|
||||
android:viewportHeight="33">
|
||||
<path
|
||||
android:pathData="M15.478,0.75L16.522,0.75A15.478,15.478 0,0 1,32 16.228L32,17.522A15.478,15.478 0,0 1,16.522 33L15.478,33A15.478,15.478 0,0 1,0 17.522L0,16.228A15.478,15.478 0,0 1,15.478 0.75z"
|
||||
android:fillColor="#7C7480"
|
||||
android:fillAlpha="0.08"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M20.782,12.155a1.29,1.29 0,0 1,0 1.824l-2.889,2.889 2.86,2.859a1.29,1.29 0,0 1,0 1.824l-0.077,0.076a1.29,1.29 0,0 1,-1.824 0l-2.859,-2.86 -2.889,2.89a1.29,1.29 0,0 1,-1.824 0l-0.062,-0.062a1.29,1.29 0,0 1,0 -1.824l2.89,-2.89 -2.86,-2.858a1.29,1.29 0,0 1,0 -1.824l0.076,-0.076a1.29,1.29 0,0 1,1.824 0l2.86,2.859 2.888,-2.889a1.29,1.29 0,0 1,1.824 0l0.062,0.062z"
|
||||
android:fillColor="#918E93"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
28
app/src/main/res/drawable/ic_create_group_chat.xml
Normal file
28
app/src/main/res/drawable/ic_create_group_chat.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="72dp"
|
||||
android:height="72dp"
|
||||
android:viewportWidth="72"
|
||||
android:viewportHeight="72">
|
||||
<path
|
||||
android:pathData="M36,36m-36,0a36,36 0,1 1,72 0a36,36 0,1 1,-72 0"
|
||||
android:fillColor="#FC0"
|
||||
android:fillAlpha="0.08"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M31.206,26.187c1.179,-1.192 2.856,-1.826 4.61,-1.57l8.182,1.196c3.012,0.44 5.11,3.336 4.687,6.468l-1.533,11.34 -1.75,-0.255"
|
||||
android:strokeWidth="2.147"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FECE51"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="m28.156,30.17 l7.756,-1.134a5.726,5.726 0,0 1,6.502 4.899l0.766,5.662a5.726,5.726 0,0 1,-4.847 6.433L24.85,48l-1.54,-11.398a5.726,5.726 0,0 1,4.846 -6.433z"
|
||||
android:strokeWidth="2.147"
|
||||
android:fillColor="#FECE51"
|
||||
android:strokeColor="#FECE51"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="m27.74,39.562 l11.024,-0.06 -0.015,2.861 -11.024,0.06z"
|
||||
android:fillColor="#D86002"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
36
app/src/main/res/drawable/ic_create_head_logo.xml
Normal file
36
app/src/main/res/drawable/ic_create_head_logo.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M24,24m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
|
||||
android:fillColor="#5E5CE6"
|
||||
android:fillAlpha="0.1"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M29.132,11.3c0.861,0.075 1.609,0.484 2.124,1.082a3.019,3.019 50,0 1,-0.378 4.325c-0.448,0.365 -0.987,0.6 -1.56,0.68l-0.02,0.002 0.057,0.035c1.649,0.997 2.914,2.401 3.789,4.009l0.041,0.076c1.115,2.087 1.576,4.508 1.374,6.807 -0.237,2.703 -1.392,4.636 -3.188,5.873a3.07,3.07 50,0 1,0.025 1.749c-0.168,0.58 -0.476,0.756 -0.578,0.791 -0.176,0.059 -1.855,-0.393 -2.957,-1.133 -1.289,0.249 -2.726,0.304 -4.271,0.168 -3.173,-0.276 -6.14,-1.296 -8.154,-3.002 -1.84,-1.559 -2.906,-3.682 -2.672,-6.345 0.261,-2.968 1.606,-5.945 3.885,-7.972 1.978,-1.761 4.657,-2.815 7.951,-2.529 0.608,0.053 1.187,0.148 1.738,0.28l0.039,0.009 -0.01,-0.013a3.013,3.013 50,0 1,-0.627 -2.061l0.004,-0.051c0.074,-0.845 0.492,-1.579 1.109,-2.083a3.163,3.163 50,0 1,2.281 -0.698z"
|
||||
android:fillType="nonZero">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="23.669"
|
||||
android:startY="11.286"
|
||||
android:endX="23.669"
|
||||
android:endY="36.734"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FF7C45ED"/>
|
||||
<item android:offset="0.236" android:color="#FF7C68EF"/>
|
||||
<item android:offset="1" android:color="#FF7BD8F8"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M17.51,22.904L17.736,22.924A0.912,0.912 50,0 1,18.565 23.912L18.419,25.584A0.912,0.912 50,0 1,17.431 26.413L17.205,26.393A0.912,0.912 50,0 1,16.376 25.405L16.522,23.733A0.912,0.912 50,0 1,17.51 22.904z"
|
||||
android:fillColor="#FFF"
|
||||
android:fillType="nonZero"/>
|
||||
<path
|
||||
android:pathData="M22.163,23.341L22.389,23.361A0.912,0.912 50,0 1,23.218 24.349L23.072,26.021A0.912,0.912 50,0 1,22.083 26.85L21.857,26.83A0.912,0.912 50,0 1,21.028 25.842L21.175,24.17A0.912,0.912 50,0 1,22.163 23.341z"
|
||||
android:fillColor="#FFF"
|
||||
android:fillType="nonZero"/>
|
||||
</vector>
|
||||
21
app/src/main/res/drawable/ic_create_monent.xml
Normal file
21
app/src/main/res/drawable/ic_create_monent.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="72dp"
|
||||
android:height="72dp"
|
||||
android:viewportWidth="72"
|
||||
android:viewportHeight="72">
|
||||
<path
|
||||
android:pathData="M36,36m-36,0a36,36 0,1 1,72 0a36,36 0,1 1,-72 0"
|
||||
android:fillColor="#3DC779"
|
||||
android:fillAlpha="0.08"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M31.555,24.228a0.964,0.964 0,0 0,-0.845 0.498l-1.958,3.548 -0.636,0.054 -0.154,0.012a5,5 0,0 0,-4.504 4.233c-0.246,1.664 -0.476,3.433 -0.476,5.253 0,1.82 0.232,3.59 0.476,5.256a5,5 0,0 0,4.504 4.231c2.64,0.228 5.33,0.459 8.038,0.459s5.398,-0.231 8.038,-0.459a5,5 0,0 0,4.503 -4.231c0.247,-1.667 0.477,-3.433 0.477,-5.256 0,-1.82 -0.232,-3.587 -0.477,-5.253a5,5 0,0 0,-4.503 -4.233l-0.127,-0.01 -0.663,-0.058 -1.958,-3.546a0.964,0.964 0,0 0,-0.845 -0.498h-8.89z"
|
||||
android:fillColor="#61CD8C"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M36,33.345c1.253,0 2.26,0.306 2.934,0.98 0.674,0.674 0.98,1.68 0.98,2.932 0,1.253 -0.306,2.26 -0.98,2.934 -0.674,0.674 -1.681,0.98 -2.935,0.98 -1.252,0.002 -2.26,-0.305 -2.933,-0.978 -0.674,-0.674 -0.98,-1.681 -0.98,-2.934 0,-1.253 0.306,-2.26 0.98,-2.934 0.674,-0.674 1.68,-0.98 2.934,-0.98z"
|
||||
android:strokeWidth="2.25"
|
||||
android:fillColor="#61CD8C"
|
||||
android:fillType="nonZero"
|
||||
android:strokeColor="#FFF"/>
|
||||
</vector>
|
||||
14
app/src/main/res/drawable/ic_manual_create_agent.xml
Normal file
14
app/src/main/res/drawable/ic_manual_create_agent.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="m9.185,2 l2.37,2.341 -6.518,6.44h-2.37V8.438zM2.667,13.707h10.667"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.185"
|
||||
android:fillColor="#00000000"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
29
app/src/main/res/values-ja/strings.xml
Normal file
29
app/src/main/res/values-ja/strings.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Create Bottom Sheet -->
|
||||
<string name="create_title">作成</string>
|
||||
<string name="create_ai">AI</string>
|
||||
<string name="create_group_chat_option">グループチャット</string>
|
||||
<string name="create_moment">モーメント</string>
|
||||
<string name="create_close">閉じる</string>
|
||||
|
||||
<!-- Create Agent V2 -->
|
||||
<string name="create_agent_v2_back">戻る</string>
|
||||
<string name="create_agent_v2_title">AI作成</string>
|
||||
<string name="create_agent_v2_greeting">%s さん、こんにちは!今日は何を作りたいですか?</string>
|
||||
<string name="create_agent_v2_description">たった一言で、あなた専用のAIがここに誕生します</string>
|
||||
<string name="create_agent_v2_input_hint">詩を書けるAI、あなたの笑いのツボがわかるAI</string>
|
||||
<string name="create_agent_v2_ai_enhance">AI美化</string>
|
||||
<string name="create_agent_v2_generating">生成中...</string>
|
||||
<string name="create_agent_v2_manual_create">手動でAI作成</string>
|
||||
<string name="create_agent_v2_one_sentence_create">一言でAI作成</string>
|
||||
<string name="create_agent_v2_thinking">あなたのために構想中</string>
|
||||
<string name="create_agent_v2_name_label">名前</string>
|
||||
<string name="create_agent_v2_description_label">説明</string>
|
||||
<string name="create_agent_v2_create_button">よし、これで決まり</string>
|
||||
<string name="create_agent_v2_creating">作成中...</string>
|
||||
<string name="create_agent_v2_ai_avatar_desc">AIアバター</string>
|
||||
<string name="create_agent_v2_ai_enhance_icon_desc">AI美化アイコン</string>
|
||||
<string name="create_agent_v2_edit_icon_desc">編集アイコン</string>
|
||||
<string name="create_agent_v2_select_avatar_desc">アバター選択</string>
|
||||
</resources>
|
||||
@@ -190,5 +190,32 @@
|
||||
<string name="agent_createing">创建中...</string>
|
||||
<string name="agent_find">发现</string>
|
||||
<string name="text_error_password_too_long">密码不能超过 %1$d 个字符</string>
|
||||
|
||||
<!-- Create Bottom Sheet -->
|
||||
<string name="create_title">创建</string>
|
||||
<string name="create_ai">AI</string>
|
||||
<string name="create_group_chat_option">群聊</string>
|
||||
<string name="create_moment">动态</string>
|
||||
<string name="create_close">关闭</string>
|
||||
|
||||
<!-- Create Agent V2 -->
|
||||
<string name="create_agent_v2_back">返回</string>
|
||||
<string name="create_agent_v2_title">创建AI</string>
|
||||
<string name="create_agent_v2_greeting">%s 你好呀!今天想创建什么?</string>
|
||||
<string name="create_agent_v2_description">只需要一句话,你的专属AI在这里诞生</string>
|
||||
<string name="create_agent_v2_input_hint">一个会写诗的AI,一个会懂你笑点的AI</string>
|
||||
<string name="create_agent_v2_ai_enhance">AI美化</string>
|
||||
<string name="create_agent_v2_generating">生成中...</string>
|
||||
<string name="create_agent_v2_manual_create">手动创造AI</string>
|
||||
<string name="create_agent_v2_one_sentence_create">一句话创造AI</string>
|
||||
<string name="create_agent_v2_thinking">正在为你构思</string>
|
||||
<string name="create_agent_v2_name_label">名称</string>
|
||||
<string name="create_agent_v2_description_label">描述</string>
|
||||
<string name="create_agent_v2_create_button">好的,就它了</string>
|
||||
<string name="create_agent_v2_creating">创建中...</string>
|
||||
<string name="create_agent_v2_ai_avatar_desc">AI头像</string>
|
||||
<string name="create_agent_v2_ai_enhance_icon_desc">AI美化图标</string>
|
||||
<string name="create_agent_v2_edit_icon_desc">编辑图标</string>
|
||||
<string name="create_agent_v2_select_avatar_desc">选择头像</string>
|
||||
|
||||
</resources>
|
||||
@@ -186,5 +186,32 @@
|
||||
<string name="agent_createing">创建中...</string>
|
||||
<string name="agent_find">发现</string>
|
||||
<string name="text_error_password_too_long">Password cannot exceed %1$d characters</string>
|
||||
|
||||
<!-- Create Bottom Sheet -->
|
||||
<string name="create_title">Create</string>
|
||||
<string name="create_ai">AI</string>
|
||||
<string name="create_group_chat_option">Group Chat</string>
|
||||
<string name="create_moment">Moment</string>
|
||||
<string name="create_close">Close</string>
|
||||
|
||||
<!-- Create Agent V2 -->
|
||||
<string name="create_agent_v2_back">Back</string>
|
||||
<string name="create_agent_v2_title">Create AI</string>
|
||||
<string name="create_agent_v2_greeting">Hello %s! What would you like to create today?</string>
|
||||
<string name="create_agent_v2_description">Just one sentence, and your exclusive AI will be born here</string>
|
||||
<string name="create_agent_v2_input_hint">An AI that writes poetry, an AI that understands your humor</string>
|
||||
<string name="create_agent_v2_ai_enhance">AI Enhancement</string>
|
||||
<string name="create_agent_v2_generating">Generating...</string>
|
||||
<string name="create_agent_v2_manual_create">Manually Create AI</string>
|
||||
<string name="create_agent_v2_one_sentence_create">Create AI with One Sentence</string>
|
||||
<string name="create_agent_v2_thinking">Thinking for you</string>
|
||||
<string name="create_agent_v2_name_label">Name</string>
|
||||
<string name="create_agent_v2_description_label">Description</string>
|
||||
<string name="create_agent_v2_create_button">Alright, that\'s the one</string>
|
||||
<string name="create_agent_v2_creating">Creating...</string>
|
||||
<string name="create_agent_v2_ai_avatar_desc">AI Avatar</string>
|
||||
<string name="create_agent_v2_ai_enhance_icon_desc">AI Enhancement Icon</string>
|
||||
<string name="create_agent_v2_edit_icon_desc">Edit Icon</string>
|
||||
<string name="create_agent_v2_select_avatar_desc">Select Avatar</string>
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user