From 29d2bb753fb6a9004438bbdf6703603d96275670 Mon Sep 17 00:00:00 2001 From: weber Date: Thu, 31 Jul 2025 15:22:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86=20Agent=20=E5=92=8C?= =?UTF-8?q?=20Profile=20=E6=95=B0=E6=8D=AE=E7=B1=BB=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=BA=86=20AddAgent=20=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 +- .../main/java/com/aiosman/ravenow/Colors.kt | 3 + .../com/aiosman/ravenow/data/AgentService.kt | 98 +++++++ .../aiosman/ravenow/data/api/RiderProAPI.kt | 14 + .../java/com/aiosman/ravenow/entity/Agent.kt | 72 +++++ .../main/java/com/aiosman/ravenow/ui/Navi.kt | 13 + .../com/aiosman/ravenow/ui/agent/AddAgent.kt | 179 +++++++++++++ .../ravenow/ui/agent/AddAgentViewModel.kt | 25 ++ .../ravenow/ui/comment/CommentsScreen.kt | 50 +++- .../aiosman/ravenow/ui/composables/Agent.kt | 108 ++++++++ .../aiosman/ravenow/ui/composables/Moment.kt | 7 +- .../com/aiosman/ravenow/ui/index/Index.kt | 3 +- .../aiosman/ravenow/ui/index/tabs/ai/Agent.kt | 250 ++++++++++++++++++ .../ui/index/tabs/ai/AgentViewModel.kt | 7 + .../ui/index/tabs/ai/BaseAgentModel.kt | 59 +++++ .../ui/index/tabs/ai/tabs/mine/MineAgent.kt | 119 +++++++++ .../tabs/ai/tabs/mine/MineAgentViewModel.kt | 22 ++ .../ui/index/tabs/message/MessageList.kt | 22 +- .../ui/index/tabs/profile/ProfileV3.kt | 15 +- .../com/aiosman/ravenow/ui/login/login.kt | 3 +- .../java/com/aiosman/ravenow/ui/post/Post.kt | 121 +++++---- app/src/main/res/drawable/rider_pro_group.xml | 38 +++ .../res/drawable/rider_pro_nav_home_hl2.png | Bin 0 -> 1911 bytes .../mipmap-xhdpi/rider_pro_agent_avatar.png | Bin 0 -> 6233 bytes .../mipmap-xhdpi/rider_pro_bg_add_agent_.png | Bin 0 -> 36506 bytes app/src/main/res/values-zh/strings.xml | 11 + app/src/main/res/values/strings.xml | 10 + gradle/libs.versions.toml | 2 +- 28 files changed, 1181 insertions(+), 72 deletions(-) create mode 100644 app/src/main/java/com/aiosman/ravenow/data/AgentService.kt create mode 100644 app/src/main/java/com/aiosman/ravenow/entity/Agent.kt create mode 100644 app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgent.kt create mode 100644 app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgentViewModel.kt create mode 100644 app/src/main/java/com/aiosman/ravenow/ui/composables/Agent.kt create mode 100644 app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/Agent.kt create mode 100644 app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/AgentViewModel.kt create mode 100644 app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/BaseAgentModel.kt create mode 100644 app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/tabs/mine/MineAgent.kt create mode 100644 app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/tabs/mine/MineAgentViewModel.kt create mode 100644 app/src/main/res/drawable/rider_pro_group.xml create mode 100644 app/src/main/res/drawable/rider_pro_nav_home_hl2.png create mode 100644 app/src/main/res/mipmap-xhdpi/rider_pro_agent_avatar.png create mode 100644 app/src/main/res/mipmap-xhdpi/rider_pro_bg_add_agent_.png diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 858fd70..2ff560b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -51,7 +51,7 @@ android { compose = true } composeOptions { - kotlinCompilerExtensionVersion = "1.5.1" + kotlinCompilerExtensionVersion = "1.5.3" } packaging { resources { diff --git a/app/src/main/java/com/aiosman/ravenow/Colors.kt b/app/src/main/java/com/aiosman/ravenow/Colors.kt index 789624d..c8fdf92 100644 --- a/app/src/main/java/com/aiosman/ravenow/Colors.kt +++ b/app/src/main/java/com/aiosman/ravenow/Colors.kt @@ -23,6 +23,7 @@ open class AppThemeData( var inputHint: Color, var error: Color, var checkedBackground: Color, + var unCheckedBackground: Color, var checkedText: Color, var chatActionColor: Color, var brandColorsColor: Color, @@ -45,6 +46,7 @@ class LightThemeColors : AppThemeData( inputHint = Color(0xffdadada), error = Color(0xffFF0000), checkedBackground = Color(0xff000000), + unCheckedBackground = Color(0xFFECEAEC), checkedText = Color(0xffFFFFFF), decentBackground = Color(0xfff5f5f5), chatActionColor = Color(0xffe0e0e0), @@ -69,6 +71,7 @@ class DarkThemeColors : AppThemeData( inputHint = Color(0xff888888), error = Color(0xffFF0000), checkedBackground = Color(0xffffffff), + unCheckedBackground = Color(0xFF7C7480), checkedText = Color(0xff000000), decentBackground = Color(0xFF171717), chatActionColor = Color(0xFF3D3D3D), diff --git a/app/src/main/java/com/aiosman/ravenow/data/AgentService.kt b/app/src/main/java/com/aiosman/ravenow/data/AgentService.kt new file mode 100644 index 0000000..f1fbf62 --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/data/AgentService.kt @@ -0,0 +1,98 @@ +package com.aiosman.ravenow.data + +import com.aiosman.ravenow.data.api.ApiClient +import com.aiosman.ravenow.entity.AgentEntity +import com.aiosman.ravenow.entity.ProfileEntity +import com.google.gson.annotations.SerializedName + +data class Agent( + @SerializedName("author") + val author: String, + @SerializedName("avatar") + val avatar: String, + @SerializedName("breakMode") + val breakMode: Boolean, + @SerializedName("createdAt") + val createdAt: String, + @SerializedName("desc") + val desc: String, + @SerializedName("id") + val id: Int, + @SerializedName("isPublic") + val isPublic: Boolean, + @SerializedName("openId") + val openId: String, + @SerializedName("profile") + val profile: Profile, + @SerializedName("title") + val title: String, + @SerializedName("updatedAt") + val updatedAt: String, + @SerializedName("useCount") + val useCount: Int + ) { + fun toAgentEntity(): AgentEntity { + return AgentEntity( + id = id, + title = title, + desc = desc, + createdAt = createdAt, + updatedAt = updatedAt, + avatar = "${ApiClient.BASE_SERVER}$avatar", + author = author, + isPublic = isPublic, + openId = openId, + breakMode = breakMode, + useCount = useCount, + profile = profile.toProfileEntity(), + ) + } +} + + +data class Profile( + @SerializedName("aiAccount") + val aiAccount: Boolean, + @SerializedName("avatar") + val avatar: String, + @SerializedName("banner") + val banner: String, + @SerializedName("bio") + val bio: String, + @SerializedName("chatAIId") + val chatAIId: String, + @SerializedName("id") + val id: Int, + @SerializedName("nickname") + val nickname: String, + @SerializedName("trtcUserId") + val trtcUserId: String, + @SerializedName("username") + val username: String +){ + fun toProfileEntity(): ProfileEntity { + return ProfileEntity( + id = id, + username = username, + nickname = nickname, + avatar = "${ApiClient.BASE_SERVER}$avatar", + bio = bio, + banner = "${ApiClient.BASE_SERVER}$banner", + trtcUserId = trtcUserId, + chatAIId = chatAIId, + aiAccount = aiAccount + ) + } +} +interface AgentService { + /** + * 获取智能体列表 + */ + suspend fun getAgent( + pageNumber: Int, + pageSize: Int = 20 + ): ListContainer + +} + + diff --git a/app/src/main/java/com/aiosman/ravenow/data/api/RiderProAPI.kt b/app/src/main/java/com/aiosman/ravenow/data/api/RiderProAPI.kt index 2049ecc..475fd1c 100644 --- a/app/src/main/java/com/aiosman/ravenow/data/api/RiderProAPI.kt +++ b/app/src/main/java/com/aiosman/ravenow/data/api/RiderProAPI.kt @@ -5,6 +5,7 @@ import com.aiosman.ravenow.data.AccountFollow import com.aiosman.ravenow.data.AccountLike import com.aiosman.ravenow.data.AccountNotice import com.aiosman.ravenow.data.AccountProfile +import com.aiosman.ravenow.data.Agent import com.aiosman.ravenow.data.Comment import com.aiosman.ravenow.data.DataContainer import com.aiosman.ravenow.data.ListContainer @@ -459,5 +460,18 @@ interface RaveNowAPI { @Body body: RemoveAccountRequestBody ): Response + @GET("outside/prompts") + suspend fun getAgent( + @Query("page") page: Int = 1, + @Query("pageSize") pageSize: Int = 20, + ): Response> + + @GET("outside/my/prompts") + suspend fun getMyAgent( + @Query("page") page: Int = 1, + @Query("pageSize") pageSize: Int = 20, + ): Response> + + } diff --git a/app/src/main/java/com/aiosman/ravenow/entity/Agent.kt b/app/src/main/java/com/aiosman/ravenow/entity/Agent.kt new file mode 100644 index 0000000..d3800d0 --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/entity/Agent.kt @@ -0,0 +1,72 @@ +package com.aiosman.ravenow.entity + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.aiosman.ravenow.data.ListContainer +import com.aiosman.ravenow.data.AgentService +import com.aiosman.ravenow.data.ServiceException +import com.aiosman.ravenow.data.api.ApiClient +import java.io.IOException + +/** + * 智能体 + */ + + +data class AgentEntity( + val author: String, + val avatar: String, + val breakMode: Boolean, + val createdAt: String, + val desc: String, + val id: Int, + val isPublic: Boolean, + val openId: String, + val profile: ProfileEntity, + val title: String, + val updatedAt: String, + val useCount: Int +) + +data class ProfileEntity( + val aiAccount: Boolean, + val avatar: String, + val banner: String, + val bio: String, + val chatAIId: String, + val id: Int, + val nickname: String, + val trtcUserId: String, + val username: String +) + +class AgentLoaderExtraArgs( + +) +class AgentLoader : DataLoader() { + override suspend fun fetchData( + page: Int, + pageSize: Int, + extra: AgentLoaderExtraArgs + ): ListContainer { + val result = ApiClient.api.getAgent( + page = page, + pageSize = pageSize, + + ) + val data = result.body()?.let { + ListContainer( + list = it.list.map { it.toAgentEntity()}, + total = it.total, + page = page, + pageSize = pageSize + ) + } + if (data == null) { + throw ServiceException("Failed to get agent") + } + return data + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/Navi.kt b/app/src/main/java/com/aiosman/ravenow/ui/Navi.kt index 21c5625..45b2de3 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/Navi.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/Navi.kt @@ -31,6 +31,7 @@ import com.aiosman.ravenow.ui.about.AboutScreen import com.aiosman.ravenow.ui.account.AccountEditScreen2 import com.aiosman.ravenow.ui.account.AccountSetting import com.aiosman.ravenow.ui.account.ResetPasswordScreen +import com.aiosman.ravenow.ui.agent.AddAgentScreen import com.aiosman.ravenow.ui.chat.ChatScreen import com.aiosman.ravenow.ui.comment.CommentsScreen import com.aiosman.ravenow.ui.comment.notice.CommentNoticeScreen @@ -94,6 +95,7 @@ sealed class NavigationRoute( data object ImageCrop : NavigationRoute("ImageCrop") data object AccountSetting : NavigationRoute("AccountSetting") data object AboutScreen : NavigationRoute("AboutScreen") + data object AddAgent : NavigationRoute("AddAgent") } @@ -395,6 +397,17 @@ fun NavigationController( AboutScreen() } } + composable( + route = NavigationRoute.AddAgent.route, + enterTransition = { + fadeIn(animationSpec = tween(durationMillis = 0)) + }, + exitTransition = { + fadeOut(animationSpec = tween(durationMillis = 0)) + } + ) { + AddAgentScreen() + } } } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgent.kt b/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgent.kt new file mode 100644 index 0000000..ecc003a --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgent.kt @@ -0,0 +1,179 @@ +package com.aiosman.ravenow.ui.agent + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.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.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Check +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +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.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewModelScope +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.account.AccountEditViewModel +import com.aiosman.ravenow.ui.comment.NoticeScreenHeader +import com.aiosman.ravenow.ui.comment.ScreenHeader +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.composables.form.FormTextInput +import com.aiosman.ravenow.ui.modifiers.noRippleClickable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +/** + * 添加智能体界面 + */ +@Composable +fun AddAgentScreen() { + val model = AddAgentViewModel + val navController = LocalNavController.current + val context = LocalContext.current + var agnetNameError by remember { mutableStateOf(null) } + var agnetDescError by remember { mutableStateOf(null) } + fun onNicknameChange(value: String) { + model.name = value + agnetNameError = when { + /*value.isEmpty() -> "昵称不能为空" + value.length < 3 -> "昵称长度不能小于3" + value.length > 20 -> "昵称长度不能大于20"*/ + else -> null + } + } + + val appColors = LocalAppTheme.current + + fun onDescChange(value: String) { + model.desc = value + agnetDescError = when { + value.length > 100 -> "个人简介长度不能大于24" + else -> null + } + } + + fun validate(): Boolean { + return agnetNameError == null && agnetDescError == null + } + + + Column( + modifier = Modifier + .fillMaxSize() + .background(color = appColors.background), + horizontalAlignment = Alignment.CenterHorizontally + ) { + StatusBarSpacer() + Box( + modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp) + ) { + ScreenHeader ( + title = stringResource(R.string.agent_add), + moreIcon = false + ) { + Icon( + modifier = Modifier + .size(24.dp) + + .noRippleClickable { + }, + imageVector = Icons.Default.Check, + contentDescription = "Add") + } + } + Spacer(modifier = Modifier.height(44.dp)) + Box( + modifier = Modifier.size(88.dp), + contentAlignment = Alignment.Center + ) { + CustomAsyncImage( + context, + "", + modifier = Modifier + .size(88.dp) + .clip( + RoundedCornerShape(88.dp) + ), + contentDescription = "", + contentScale = ContentScale.Crop, + placeholderRes = R.mipmap.rider_pro_agent_avatar + ) + Box( + modifier = Modifier + .size(32.dp) + .clip(CircleShape) + .background(appColors.main) + .align(Alignment.BottomEnd) + .noRippleClickable { + navController.navigate(NavigationRoute.ImageCrop.route) + }, + contentAlignment = Alignment.Center + ) { + Icon( + Icons.Default.Add, + contentDescription = "Add", + tint = Color.White, + ) + } + + } + Spacer(modifier = Modifier.height(58.dp)) + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + ) { + FormTextInput( + value = model.name, + label = stringResource(R.string.agent_name), + hint = stringResource(R.string.agent_name_hint), + modifier = Modifier.fillMaxWidth(), + ) { value -> + onNicknameChange(value) + } +// Spacer(modifier = Modifier.height(16.dp)) + FormTextInput( + value = model.desc, + label = stringResource(R.string.agent_desc), + hint = stringResource(R.string.agent_desc_hint), + modifier = Modifier.fillMaxWidth(), + ) { value -> + onDescChange(value) + } + } + Spacer(modifier = Modifier.height(58.dp)) + ActionButton( + modifier = Modifier + .width(345.dp), + text = stringResource(R.string.agent_create), + ) { + + + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgentViewModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgentViewModel.kt new file mode 100644 index 0000000..a037bdb --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgentViewModel.kt @@ -0,0 +1,25 @@ +package com.aiosman.ravenow.ui.agent + +import android.content.Context +import android.graphics.Bitmap +import android.net.Uri +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import com.aiosman.ravenow.data.AccountService +import com.aiosman.ravenow.data.AccountServiceImpl +import com.aiosman.ravenow.data.UploadImage +import com.aiosman.ravenow.entity.AccountProfileEntity +import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel +import com.aiosman.ravenow.utils.TrtcHelper +import java.io.File + +object AddAgentViewModel : ViewModel() { + var name by mutableStateOf("") + var desc by mutableStateOf("") + var imageUrl by mutableStateOf(null) + var croppedBitmap by mutableStateOf(null) + var isUpdating by mutableStateOf(false) + +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/comment/CommentsScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/comment/CommentsScreen.kt index 2156717..aa35158 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/comment/CommentsScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/comment/CommentsScreen.kt @@ -13,6 +13,7 @@ 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.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -23,6 +24,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -77,7 +79,7 @@ fun NoticeScreenHeader( Image( painter = painterResource(id = R.drawable.rider_pro_back_icon,), contentDescription = title, - modifier = Modifier.size(16.dp).clickable( + modifier = Modifier.size(24.dp).clickable( indication = null, interactionSource = remember { MutableInteractionSource() } ) { @@ -102,6 +104,52 @@ fun NoticeScreenHeader( } } +@Composable +fun ScreenHeader( + title:String, + moreIcon: Boolean = true, + rightIcon: @Composable (() -> Unit)? = null +) { + val nav = LocalNavController.current + val AppColors = LocalAppTheme.current + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + painter = painterResource(id = R.drawable.rider_pro_close,), + contentDescription = title, + modifier = Modifier.size(24.dp).clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { + nav.navigateUp() + }, + colorFilter = ColorFilter.tint(AppColors.text) + ) + Spacer(modifier = Modifier.size(12.dp)) + Text(title, + fontWeight = FontWeight.W800, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center, + fontSize = 17.sp, + color = AppColors.text) + Spacer(modifier = Modifier.size(12.dp)) + if (moreIcon) { + Spacer(modifier = Modifier.weight(1f)) + Image( + painter = painterResource(id = R.drawable.rider_pro_more_horizon), + contentDescription = "More", + modifier = Modifier + .size(24.dp), + ) + } + if (rightIcon != null) { + //rightIcon() + } + } +} + @Composable fun CommentsItem() { Box( diff --git a/app/src/main/java/com/aiosman/ravenow/ui/composables/Agent.kt b/app/src/main/java/com/aiosman/ravenow/ui/composables/Agent.kt new file mode 100644 index 0000000..c50343e --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/composables/Agent.kt @@ -0,0 +1,108 @@ +package com.aiosman.ravenow.ui.composables + +import androidx.compose.foundation.background +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.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.aiosman.ravenow.LocalAppTheme +import com.aiosman.ravenow.LocalNavController +import com.aiosman.ravenow.entity.AgentEntity +import com.aiosman.ravenow.entity.MomentEntity +import com.aiosman.ravenow.exp.timeAgo +import com.aiosman.ravenow.ui.NavigationRoute +import com.aiosman.ravenow.ui.modifiers.noRippleClickable + +@Composable +fun AgentCard( + modifier: Modifier = Modifier, + agentEntity: AgentEntity, +) { + val AppColors = LocalAppTheme.current + val context = LocalContext.current + Column( + modifier = modifier + .fillMaxWidth() + .background(AppColors.background) + ) { + Box( + modifier = Modifier.padding(start = 0.dp, end = 0.dp, top = 16.dp, bottom = 8.dp) + ) { + Row( + modifier = Modifier + ) { + CustomAsyncImage( + context, + agentEntity.avatar, + contentDescription = "", + modifier = Modifier + .size(40.dp) + .clip(RoundedCornerShape(40.dp)) + , + contentScale = ContentScale.Crop + ) + Column( + modifier = Modifier + .weight(1f) + .padding(start = 12.dp, end = 12.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .height(22.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + modifier = Modifier.weight(1f), + textAlign = TextAlign.Start, + text = agentEntity.title, + color = AppColors.text, + fontSize = 16.sp, style = TextStyle(fontWeight = FontWeight.Bold) + ) + } + + Spacer(modifier = Modifier.width(16.dp)) + + } + Row( + modifier = Modifier + .fillMaxWidth() + .height(21.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + modifier = Modifier, + text = agentEntity.desc, + color = AppColors.text, + fontSize = 12.sp + ) + Spacer(modifier = Modifier.width(8.dp)) + //MomentPostLocation(momentEntity.location) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/composables/Moment.kt b/app/src/main/java/com/aiosman/ravenow/ui/composables/Moment.kt index 0ddfe8f..8959570 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/composables/Moment.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/composables/Moment.kt @@ -212,7 +212,8 @@ fun MomentPostLocation(location: String) { Text( text = location, color = AppColors.secondaryText, - fontSize = 12.sp + fontSize = 12.sp, + ) } @@ -280,7 +281,7 @@ fun MomentTopRowGroup( ) { MomentPostTime(momentEntity.time.timeAgo(context)) Spacer(modifier = Modifier.width(8.dp)) - MomentPostLocation(momentEntity.location) + //MomentPostLocation(momentEntity.location) } } val isFollowing = momentEntity.followStatus @@ -382,7 +383,7 @@ fun MomentOperateBtn(@DrawableRes icon: Int, count: String) { Text( text = count, modifier = Modifier.padding(start = 7.dp), - fontSize = 12.sp, + fontSize = 14.sp, color = AppColors.text ) } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt index 4ce45ac..93c16fa 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt @@ -61,6 +61,7 @@ import com.aiosman.ravenow.Messaging import com.aiosman.ravenow.R import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.index.tabs.add.AddPage +import com.aiosman.ravenow.ui.index.tabs.ai.Agent import com.aiosman.ravenow.ui.index.tabs.message.NotificationsScreen import com.aiosman.ravenow.ui.index.tabs.moment.MomentsList import com.aiosman.ravenow.ui.index.tabs.profile.ProfileWrap @@ -332,7 +333,7 @@ fun IndexScreen() { ) { page -> when (page) { 0 -> Home() - 1 -> DiscoverScreen() + 1 -> Agent() 2 -> Add() 3 -> Notifications() 4 -> Profile() diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/Agent.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/Agent.kt new file mode 100644 index 0000000..701bd3b --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/Agent.kt @@ -0,0 +1,250 @@ +package com.aiosman.ravenow.ui.index.tabs.ai + +import androidx.compose.foundation.ExperimentalFoundationApi +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.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues +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.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Icon +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +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.modifiers.noRippleClickable +import kotlinx.coroutines.launch + +@OptIn( ExperimentalFoundationApi::class) +@Composable +fun Agent() { + val AppColors = LocalAppTheme.current + val navController = LocalNavController.current + val navigationBarPaddings = + WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp + val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues() + var pagerState = rememberPagerState { 4 } + var scope = rememberCoroutineScope() + Column( + modifier = Modifier + .fillMaxSize() + .padding( + top = statusBarPaddingValues.calculateTopPadding()+18.dp, + bottom = navigationBarPaddings, + start = 16.dp, + end = 16.dp + ), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Row( + modifier = Modifier + .height(36.dp) // 设置高度为36dp + .fillMaxWidth(), // 占据整行宽度 + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.Bottom + ) { + // 搜索框 - 占据剩余空间 + Row( + modifier = Modifier + .height(36.dp) + .weight(1f) // 权重为1,占据剩余空间 + .clip(shape = RoundedCornerShape(18.dp)) + .background(AppColors.inputBackground) + .padding(horizontal = 8.dp, vertical = 0.dp) + .noRippleClickable { + // 搜索框点击事件 + }, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = painterResource(id = R.drawable.rider_pro_nav_search), + contentDescription = null, + tint = AppColors.inputHint + ) + Box { + Text( + text = stringResource(R.string.search), + modifier = Modifier.padding(start = 8.dp), + color = AppColors.inputHint, + fontSize = 17.sp + ) + } + } + + // 间隔 + Spacer(modifier = Modifier.width(16.dp)) + + // 新增 + Icon( + modifier = Modifier + .size(36.dp) + .noRippleClickable { + // 图标点击事件 + navController.navigate( + NavigationRoute.AddAgent.route + ) + }, + painter = painterResource(id = R.drawable.rider_pro_new_post_add_pic), + contentDescription = null, + tint = AppColors.text + ) + } + Spacer(modifier = Modifier.height(16.dp)) + Row( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + // center the tabs + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.Bottom + ) { + Column( + modifier = Modifier + .noRippleClickable { + scope.launch { + pagerState.animateScrollToPage(0) + } + }, + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + + ) { + Text( + text = stringResource(R.string.agent_mine), + fontSize = 14.sp, + color = if (pagerState.currentPage == 0) AppColors.mainText else AppColors.checkedBackground, + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .background(if (pagerState.currentPage == 0) AppColors.checkedBackground else AppColors.unCheckedBackground) + .padding(horizontal = 11.dp, vertical = 4.dp) + ) + + + } + Spacer(modifier = Modifier.width(8.dp)) + Column( + modifier = Modifier + .noRippleClickable { + scope.launch { + pagerState.animateScrollToPage(1) + } + }, + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + + ) { + Text( + text = stringResource(R.string.agent_hot), + fontSize = 14.sp, + color = if (pagerState.currentPage == 1) AppColors.mainText else AppColors.checkedBackground, + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .background(if (pagerState.currentPage == 1) AppColors.checkedBackground else AppColors.unCheckedBackground) + .padding(horizontal = 11.dp, vertical = 4.dp) + ) + + + } + Spacer(modifier = Modifier.width(8.dp)) + Column( + modifier = Modifier + .noRippleClickable { + scope.launch { + pagerState.animateScrollToPage(2) + } + }, + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + + ) { + Text( + text = stringResource(R.string.agent_recommend), + fontSize = 14.sp, + color = if (pagerState.currentPage == 2) AppColors.mainText else AppColors.checkedBackground, + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .background(if (pagerState.currentPage == 2) AppColors.checkedBackground else AppColors.unCheckedBackground) + .padding(horizontal = 11.dp, vertical = 4.dp) + ) + + + } + Spacer(modifier = Modifier.width(8.dp)) + Column( + modifier = Modifier + .noRippleClickable { + scope.launch { + pagerState.animateScrollToPage(3) + } + }, + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + + ) { + Text( + text = stringResource(R.string.agent_other), + fontSize = 14.sp, + color = if (pagerState.currentPage == 3) AppColors.mainText else AppColors.checkedBackground, + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .background(if (pagerState.currentPage == 3) AppColors.checkedBackground else AppColors.unCheckedBackground) + .padding(horizontal = 11.dp, vertical = 4.dp) + ) + + + } + + + } + Spacer(modifier = Modifier.height(16.dp)) + HorizontalPager( + state = pagerState, + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + when (it) { + 0 -> { + // ExploreMomentsList() + } + + 1 -> { + //TimelineMomentsList() + } + + 2 -> { + //HotMomentsList() + } + + 3 -> { + //MineAgent() + } + + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/AgentViewModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/AgentViewModel.kt new file mode 100644 index 0000000..5a197b9 --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/AgentViewModel.kt @@ -0,0 +1,7 @@ +package com.aiosman.ravenow.ui.index.tabs.ai + +import androidx.lifecycle.ViewModel + +object AgentViewModel: ViewModel() { + +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/BaseAgentModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/BaseAgentModel.kt new file mode 100644 index 0000000..3e92ecc --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/BaseAgentModel.kt @@ -0,0 +1,59 @@ +package com.aiosman.ravenow.ui.index.tabs.ai + +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.AgentService +import com.aiosman.ravenow.data.MomentService +import com.aiosman.ravenow.data.UserServiceImpl +import com.aiosman.ravenow.entity.AgentEntity +import com.aiosman.ravenow.entity.AgentLoader +import com.aiosman.ravenow.entity.AgentLoaderExtraArgs +import kotlinx.coroutines.launch +import org.greenrobot.eventbus.EventBus + +open class BaseAgentModel :ViewModel(){ + // private val agentService: AgentService = AgentServiceImpl() + + val agentLoader = AgentLoader().apply { + onListChanged = { + agentList = it + } + } + var refreshing by mutableStateOf(false) + var isFirstLoad = true + var agentList by mutableStateOf>(listOf()) + open fun extraArgs(): AgentLoaderExtraArgs { + return AgentLoaderExtraArgs() + } + fun refreshPager(pullRefresh: Boolean = false) { + if (!isFirstLoad && !pullRefresh) { + return + } + isFirstLoad = false + agentLoader.clear() + viewModelScope.launch { + agentLoader.loadData(extraArgs()) + } + } + + fun loadMore() { + viewModelScope.launch { + agentLoader.loadMore(extraArgs()) + agentList = agentLoader.list + } + } + + + fun ResetModel() { + agentLoader.clear() + isFirstLoad = true + } + + override fun onCleared() { + super.onCleared() + EventBus.getDefault().unregister(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/tabs/mine/MineAgent.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/tabs/mine/MineAgent.kt new file mode 100644 index 0000000..a7dedca --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/tabs/mine/MineAgent.kt @@ -0,0 +1,119 @@ +package com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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 com.aiosman.ravenow.LocalAppTheme +import com.aiosman.ravenow.R +import com.aiosman.ravenow.ui.composables.AgentCard + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun MineAgent() { + val AppColors = LocalAppTheme.current + val model = MineAgentViewModel + var agentList = model.agentList + val scope = rememberCoroutineScope() + val state = rememberPullRefreshState(model.refreshing, onRefresh = { + model.refreshPager( + pullRefresh = true + ) + }) + val listState = rememberLazyListState() + + // observe list scrolling + val reachedBottom by remember { + derivedStateOf { + val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull() + lastVisibleItem?.index != 0 && lastVisibleItem?.index == listState.layoutInfo.totalItemsCount - 2 + } + } + + // load more if scrolled to bottom + LaunchedEffect(reachedBottom) { + if (reachedBottom) { + model.loadMore() + } + } + LaunchedEffect(Unit) { + model.refreshPager() + } + Column( + modifier = Modifier + .fillMaxSize() + + ) { + if(agentList.size == 0) { + Box( + modifier = Modifier + .fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth() + ) { + Image( + painter = painterResource(id = R.mipmap.rider_pro_following_empty), + contentDescription = null, + modifier = Modifier.size(140.dp) + ) + Spacer(modifier = Modifier.size(32.dp)) + androidx.compose.material.Text( + text = "You haven't followed anyone yet", + color = AppColors.text, + fontSize = 16.sp, + fontWeight = FontWeight.W600 + ) + Spacer(modifier = Modifier.size(16.dp)) + androidx.compose.material.Text( + text = "Click start your social journey.", + color = AppColors.text, + fontSize = 16.sp, + fontWeight = FontWeight.W400 + ) + } + } + }else{ + Box(Modifier.pullRefresh(state)) { + LazyColumn( + modifier = Modifier.fillMaxSize(), + state = listState + ) { + items( + agentList.size, + key = { idx -> idx } + ) { idx -> + val agentItem = agentList[idx] ?: return@items + AgentCard(agentEntity = agentItem, + + ) + } + } + PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/tabs/mine/MineAgentViewModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/tabs/mine/MineAgentViewModel.kt new file mode 100644 index 0000000..06ee4e0 --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/tabs/mine/MineAgentViewModel.kt @@ -0,0 +1,22 @@ +package com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import com.aiosman.ravenow.entity.AgentLoaderExtraArgs +import com.aiosman.ravenow.entity.MomentLoaderExtraArgs +import com.aiosman.ravenow.ui.index.tabs.ai.BaseAgentModel +import com.aiosman.ravenow.ui.index.tabs.moment.BaseMomentModel +import org.greenrobot.eventbus.EventBus + + +object MineAgentViewModel : BaseAgentModel() { + + init { + //EventBus.getDefault().register(this) + } + override fun extraArgs(): AgentLoaderExtraArgs { + return AgentLoaderExtraArgs() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/MessageList.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/MessageList.kt index a640b6d..1c9c8ea 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/MessageList.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/MessageList.kt @@ -93,9 +93,12 @@ fun NotificationsScreen() { Row( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 8.dp), + .padding(horizontal = 15.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically ) { + // 左侧占位元素 + Box(modifier = Modifier.size(24.dp)) + // 左侧 Column:label 居中显示 Column( modifier = Modifier @@ -105,21 +108,24 @@ fun NotificationsScreen() { ) { Text( text = stringResource(R.string.main_message), - fontSize = 16.sp, + fontSize = 17.sp, + fontWeight = FontWeight.W700, color = AppColors.text ) } - - // 右侧图片按钮:靠右显示 + // 右侧图标 Image( - painter = painterResource(id = R.drawable.rider_pro_favourite), - contentDescription = stringResource(R.string.main_message), + painter = painterResource(id = R.drawable.rider_pro_group), + contentDescription = "add", modifier = Modifier .size(24.dp) .noRippleClickable { - // 点击事件 - } + + }, + colorFilter = ColorFilter.tint(AppColors.text) ) + + } Row( modifier = Modifier diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt index b426b15..7e6b249 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt @@ -53,6 +53,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.onGloballyPositioned @@ -61,6 +62,7 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.aiosman.ravenow.AppState @@ -384,11 +386,22 @@ fun ProfileV3( ), verticalAlignment = Alignment.CenterVertically ) { + Image( + painter = painterResource(id = R.drawable.rider_pro_back_icon), // Replace with your image resource + contentDescription = "Back", + modifier = Modifier + .noRippleClickable { + navController.navigateUp() + } + .size(24.dp), + colorFilter = ColorFilter.tint(AppColors.text) + ) + Spacer(modifier = Modifier.width(8.dp)) CustomAsyncImage( LocalContext.current, profile?.avatar, modifier = Modifier - .size(48.dp) + .size(32.dp) .clip(CircleShape), contentDescription = "", contentScale = ContentScale.Crop diff --git a/app/src/main/java/com/aiosman/ravenow/ui/login/login.kt b/app/src/main/java/com/aiosman/ravenow/ui/login/login.kt index 4206e01..d9a2cc0 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/login/login.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/login/login.kt @@ -46,6 +46,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.aiosman.ravenow.AppState @@ -69,7 +70,7 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch - +@Preview @Composable fun LoginPage() { val navController = LocalNavController.current diff --git a/app/src/main/java/com/aiosman/ravenow/ui/post/Post.kt b/app/src/main/java/com/aiosman/ravenow/ui/post/Post.kt index 3e35254..1b6de2e 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/post/Post.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/post/Post.kt @@ -395,18 +395,6 @@ fun PostScreen( viewModel.moment ) Spacer(modifier = Modifier.height(16.dp)) - Box( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp) - .height(1.dp) - .background( - AppColors.divider - ) - ) { - - } - Spacer(modifier = Modifier.height(24.dp)) Row( modifier = Modifier .fillMaxWidth() @@ -419,7 +407,8 @@ fun PostScreen( R.string.comment_count, (viewModel.moment?.commentCount ?: 0) ), fontSize = 14.sp, - color = AppColors.text + fontWeight = FontWeight.Bold, + color = AppColors.nonActiveText ) Spacer(modifier = Modifier.weight(1f)) OrderSelectionComponent() { @@ -724,13 +713,13 @@ fun Header( .noRippleClickable { navController.navigateUp() } - .size(32.dp), + .size(24.dp), colorFilter = ColorFilter.tint(AppColors.text) ) Spacer(modifier = Modifier.width(8.dp)) Box( modifier = Modifier - .size(40.dp) + .size(32.dp) .clip(CircleShape) .background(AppColors.secondaryText.copy(alpha = 0.1f)) ) { @@ -739,7 +728,7 @@ fun Header( avatar, contentDescription = "Profile Picture", modifier = Modifier - .size(40.dp) + .size(32.dp) .clip(CircleShape) .noRippleClickable { userId?.let { @@ -761,6 +750,7 @@ fun Header( fontWeight = FontWeight.Bold, modifier = Modifier.weight(1f), color = AppColors.text, + fontSize = 17.sp ) if (AppState.UserId != userId && !isFollowing) { FollowButton( @@ -774,7 +764,7 @@ fun Header( Box { Image( modifier = Modifier - .height(20.dp) + .height(24.dp) .padding(end = 8.dp) .noRippleClickable { expanded = true @@ -1049,12 +1039,15 @@ fun PostDetails( if (!momentEntity?.momentTextContent.isNullOrEmpty()) { Text( text = momentEntity?.momentTextContent ?: "", - fontSize = 16.sp, - fontWeight = FontWeight.Bold, + fontSize = 14.sp, color = AppColors.text, ) } - Text(text = "${momentEntity?.time?.formatPostTime()}", color = AppColors.text) + Text( + modifier = Modifier.padding(top = 16.dp), + text = "${momentEntity?.time?.formatPostTime()}", + color = AppColors.nonActiveText, + fontSize = 12.sp) } } @@ -1084,7 +1077,8 @@ fun CommentItem( Row(modifier = Modifier.padding(vertical = 8.dp)) { Box( modifier = Modifier - .size(if (isChild) 24.dp else 40.dp) + .size(if (isChild) 24.dp else 32.dp) + .clip(CircleShape) .background(Color.Gray.copy(alpha = 0.1f)) .noRippleClickable { navController.navigate( @@ -1099,7 +1093,8 @@ fun CommentItem( context = context, imageUrl = commentEntity.avatar, contentDescription = "Comment Profile Picture", - modifier = Modifier.size(if (isChild) 24.dp else 40.dp), + modifier = Modifier.size(if (isChild) 24.dp else 32.dp) + .clip(CircleShape), contentScale = ContentScale.Crop ) } @@ -1115,13 +1110,21 @@ fun CommentItem( } ) {} ) { - Text( - text = commentEntity.name, - fontWeight = FontWeight.W600, - fontSize = 14.sp, - color = AppColors.text - ) Row { + Text( + text = commentEntity.name, + fontWeight = FontWeight.Bold, + fontSize = 11.sp, + color = AppColors.text + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = commentEntity.date.timeAgo(context), + fontSize = 11.sp, + color = Color.Gray + ) + } + Row (modifier = Modifier.padding(top = 4.dp)){ if (isChild) { val annotatedText = buildAnnotatedString { if (commentEntity.replyUserId != null) { @@ -1169,9 +1172,10 @@ fun CommentItem( } else { Text( text = commentEntity.comment, - fontSize = 14.sp, + fontSize = 13.sp, maxLines = Int.MAX_VALUE, softWrap = true, + lineHeight = 20.sp, color = AppColors.text, modifier = Modifier.combinedClickable( interactionSource = remember { MutableInteractionSource() }, @@ -1187,18 +1191,42 @@ fun CommentItem( ) } } - Row { - Text( - text = commentEntity.date.timeAgo(context), - fontSize = 12.sp, - color = Color.Gray + Row (modifier = Modifier.padding(top = 12.dp), + verticalAlignment = Alignment.CenterVertically,){ + AnimatedLikeIcon( + liked = commentEntity.liked, + onClick = { + onLike(commentEntity) + }, + modifier = Modifier.size(16.dp) ) - Spacer(modifier = Modifier.width(8.dp)) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = commentEntity.likes.toString(), + fontSize = 12.sp, + fontWeight = FontWeight.Bold, + color = AppColors.text + ) + Text( + text = stringResource(R.string.like), + fontSize = 12.sp, + fontWeight = FontWeight.Bold, + color = AppColors.nonActiveText, + ) + Spacer(modifier = Modifier.width(27.dp)) if (AppState.UserId?.toLong() != commentEntity.author) { + Icon( + painter = painterResource(id = R.drawable.rider_pro_comment), + contentDescription = "", + modifier = Modifier.size(16.dp), + tint = AppColors.nonActiveText + ) + Spacer(modifier = Modifier.width(4.dp)) Text( text = stringResource(R.string.reply), fontSize = 12.sp, - color = AppColors.secondaryText, + fontWeight = FontWeight.Bold, + color = AppColors.nonActiveText, modifier = Modifier.noRippleClickable { onReply( commentEntity, @@ -1213,23 +1241,6 @@ fun CommentItem( } } - Spacer(modifier = Modifier.width(16.dp)) - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - AnimatedLikeIcon( - liked = commentEntity.liked, - onClick = { - onLike(commentEntity) - }, - modifier = Modifier.size(20.dp) - ) - Text( - text = commentEntity.likes.toString(), - fontSize = 12.sp, - color = AppColors.text - ) - } } Spacer(modifier = Modifier.height(8.dp)) Column( @@ -1267,8 +1278,8 @@ fun CommentItem( val remaining = commentEntity.replyCount - commentEntity.reply.size Text( text = stringResource(R.string.view_more_reply, remaining), - fontSize = 12.sp, - color = Color(0xFF6F94AE), + fontSize = 14.sp, + color = AppColors.nonActiveText, modifier = Modifier.noRippleClickable { onLoadMoreSubComments?.invoke(commentEntity) } diff --git a/app/src/main/res/drawable/rider_pro_group.xml b/app/src/main/res/drawable/rider_pro_group.xml new file mode 100644 index 0000000..67acce1 --- /dev/null +++ b/app/src/main/res/drawable/rider_pro_group.xml @@ -0,0 +1,38 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rider_pro_nav_home_hl2.png b/app/src/main/res/drawable/rider_pro_nav_home_hl2.png new file mode 100644 index 0000000000000000000000000000000000000000..fc3ac0872eddc19e7a85f34b88f335b86f82bace GIT binary patch literal 1911 zcmV--2Z;EIP)Px+F-b&0RA@u(SY3!!RTN%p9q-II{@fWgtWZO22+|UZis+>%E7Yk`8zVDIQ!G)T zdhm~6(ub&!3V%}23c)O;9zyenKwktC6chtfN5>y5GHfPwymRNAy=w1$_CEWZd+*FW zjv$8fa^T!^=B#h6@B7wT8{roJ!&^LlxDDWDSMYWP{&y8z7|)D#Adr}` z1;E>t@q^XFI=*YZk)UrVg-&HB&PK!)z|@Z!0R#dw0RRFE1YqhAz{|hMZTheYK->r* zHw3^rXzcCR1Z#nDx(ZeX&-$tY2eS9%ePG&=&$mw-30p7q6hw~!oXs|j5J=xM;AoX` z>C9p9z)|24DAKy}p+85qppwF18 z(iN(9nb0mB_lal|4_>|-NZLf14};erm{9l#%lEXdT2_wLb_?J%JXuAx*>wy!U1!mu zNGM!OpCSOHxQ(?Ip^XY!>%ggm1cI=$J?NY|q3w&myQ)T)04_j%b>{%@M*w)5rK`j& zvb4`zdbz^wBJ)xMzN!d7fFunp1V)Dg^2d(*W97wCAi4z5GN54t0?w4eRAyuS-88E% zeBS8=Qr9ZWA=fSEHD$v400EIInAU@)lpy+f!tX1_rUuYFu>KK`yaNDHwpVX%VP&4Te0FH?olBSFH`K0TV_W5M+p-&x<*ctrDq5hkm|#%K1;PB|(&=(CO?; zt-zQSF`rL(y0O(_oVU#>Y*IGU+GUJjZ9hoT#u2i7<}z^2oKqjQl?~uj|3(GC9f#d9 z+3;SE$J&*Q&lk|fselUJwaPG1x3Ptzh3U@fdX>`x=r^U669C*`H5kW6L!VDJrYOUq zmBxI&B8=p6vHyM-yRX#6WSowYA^9A~d^$4dY zgzt3tX#wR~O30_vw&->{raI#tgAO#@l^L8{&(8dNI zI!L62F!Qb!jPt~Xx+3CM6(rz}d^!Lps;0IAXaFwXBkI6*pT*l%;PKT8Z%#lsUl3-k z&nvD+^l`Pph;0IKrAOYnJo}i>cxxGHoE{%cgVU6g^pli?Tv`B?uj&fel`uZfX9Ntb zgZ1Z)t?hxieS7x}o(J%r^@$#2gyBA6R?j-rCJ;_D;*n0|-PeNr&M6nyTrux%zWd#w zTvc&a%|RjO-MLB9@1ORLnnh`B^hCvLtqd57xX^9b&kTD2)|@MM@#yNg%_Ei$4Int1 z+!5TN?n*_jX3@bEec*V->sG`iIKMs9;ZGk48+vp!vSr9S1M-!X8wJsLL!X!xhgxax zbuAr$-+H~#3bq1zK~I)dAY5khR&@rj?up#$Gij=^Y505hAZO+X59SG?ezD+V-=x?!+am(g-d#O`4CJ717K$v6azuWVNhV*GUF% zmLB6|0i*wc?WN25_@7Z@zoG0ynz?{{rTVv3xdHkx&2t002ovPDHLkV1gT&hwT6W literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/rider_pro_agent_avatar.png b/app/src/main/res/mipmap-xhdpi/rider_pro_agent_avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..dc041e66cae979fa415119c9a028af280b8788cf GIT binary patch literal 6233 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 D#V52z literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/rider_pro_bg_add_agent_.png b/app/src/main/res/mipmap-xhdpi/rider_pro_bg_add_agent_.png new file mode 100644 index 0000000000000000000000000000000000000000..8029a2cb489bae7e631959c87f81f51d70db4181 GIT binary patch literal 36506 zcmV)nK%KvdP)PyA07*naRCr#@y<5y~*;O65zrSn)EpEclQl1*o##Mz7w{u+P)}6RrMZkfeM2bzv zs7Yi;k@A#~2TZ#RPF_MDLh=;5j7^djL|B#*kZQ*2f8So0c^hNRHP_mg?LX81UVi%9zG!>()i>W> zF3;R-mp5`If{;SKjfBAOVzHHyO&%bP!&)c`{sWOGjw(V*%m#utJzQ>>3eOJGh zawy;L`|-4Uwm#RMIU1$E_4Y2?e%FBfKJS0uF4kw5m+Py4SH}wT$Yjz)g5O&=_3t=o z*D`)zo)a>;j*^k2F`~za03+W-c7`-w~WO``Ek2(d>DVVESrRE3`M?o!G_0bau4yV$&Yp7!W_=C$j6(p zl&^QFYG2Ymo&3P=j+r?5`Z>9&cU^m%flyP&+HAbe%f^4%?)oi~Q}T1R$bQphO-)-9-Zrk>$ZM*q@w#)v1J=*qv_tbXzjYm&C z`gcF{li&M$j57Z@d?q#jryJ6PH@)?XZ!i0Qdvm#Y+wFe&?=IW^b^CU?!RTE+0m;MY zo=m`^DPpFZ{s>Gf0j@5sN!!u07U$01#J{D!J=oU?HxFU2r1=uqpC%>^EFglrK0;LJ z`Y?v*qfH`{V}cbpmBysJtSwF0914RxEf{tE847B9q5geDA&;y(9LiGOAFV8%pB^jHhBz{ zKp(p(!5`-CKRY1I$(Cp%1sDXSGCtH({OokeWNbi^fQ%6_DpEX-W^1kzvi z-ZbpxC*|vjV7FG?tRJry4U}iec!%+yKLXK@7wEa`kFLBX2Hv92*j3+H8NTswxuUYz8Vu{ zV*{89(gaRFB?zp70@~vYa~nGjIS~Ae)D%>ztj7hqo_U0&pOj0Q-G<3K=b%`U_-M$)&TlL&kla zLtE(#0Q52@vhAnFq>6Z)eZv6g_ri{xcx@cBMUNkGAhIzdD#;lTRrHq(^iiB1m;@+6 zRu%!Iv@^-jdSH^E?-r~71G2y z=%2~;eTJJC7lrJ&K?ed#+16^evkW)ttPDunULUI_NwW-Nch!*TqZq^T^(4wd0ob&( z@vvCKe&W2N{8+ca7K6<@`LUlk`2`}YGOOM`Pwi}2d{%^+^#C#0%Qw?gx`TspM#mkB zLzl~|mu>ssx9#SqZa@9hC*Jpo7k&o-;&b@nb53OMu=bTVfAe=f+V&r~Y?nW|Z`&7W z0H!EG1OT|ofpDX3bhkwa!gZPlf}ZM$`$2|mivH*khbmgme=8)?GE9Sy@x1jhD z(if3wucps&rDLC$Rft1aCS=fO@e6j_L>%h)mg+>5Y2aex54M||f3okFfAGGa{Eq)+ z+b$9%V~l<>e@T$*$uv&*{nV$v`nh*qZf<^f+xM?+3etoPiAzCfYYkSmF}lXVJAe*A zNNsn+@j)r5#EET4iW;#{Vg}2EF~j^6F(JYFF^8c#LYI<^P$ryjVfQwG$SM+Pq=gmI zNNjiR@Po`OM|G;Tkwjk6&IY87N%PTkt}h4@PA+mQ{91cScMfVW4Rze)LjyZ%WHv1= zNVA@m+{uuod?X?G6Pes0Mc~5CW>zz@6=1O|ALvb9d3K6_pVy8CFadRl`auS&1W*D& znw1&|Vpc*PXL)HGHjqpvkeX0pxLJ^|D9ae@sMahszF@P zi6ZPsT!pQjF3YI03ZY<9$IN9B;xoFRBf65v*6%%mg79!+$Dz&zv)i2Q*7FXFwb49f zyjZY3q5EOKPF@^$ctH)Hhw%ax4t2j&zHr!gX7e1vVPTugeBbt8zufMBVT|XN0e*1#RQ(a!Ip)RW4cfugTmDDAbkW!9d|&6I+c33#)JjB{1DS+wi+-kbU=Y zpRpB8-$X)r#(!HS^@&>Uen8W*8hskWMOvwW&K7=d~Opvc9W0GKMVg{L1C#%EnyiEri!G$UiGbUci!l3vcWw-s< zYoA@dy$`*!)wn|=SWU;4e@`}?mop?yt>?1MKw_buDa_ILNoBMXWh>u0%FuQ)>$H^SzGs^zaGo^9Q z6nuBl?S_k`v;@WG|730~$ihJ+t7tcaQWW|-XcI@I%5Z&6Uj0f($8Mct!UcqFqI%6dw&1_IW0D#<+{ zOeFVZkcoYhzRk>^MG`)#i$Rl5EzY zP9NC8Ej@7+1T@)x?{UKuTL)UG&UMf)&yoBVYC2C7SJ@BuJG;SvL7vMg@)~6(oQi;| zI_4txN!iAvKDM|Dlc34gca>MyJAE$hH77|-X2NGi>m;*0li#>qo!zsLu1Tzg5dI!@ zVqp;D5FC_ac}o8r>nQ97a9M^Z(_pxT!_(fEdw8qy+WYom9WKi&FDqa_%0H{P6Z9hL zi#a~Xc(JgEQu6Cqb=hwB?eh0Nd-K`%eefgS^C`rz$N%)iiR`DIe(U#Mw*6!Kw!Jab z+_{+o*e?;2!XP9`NP*Bm9 zXA_NCsqNZj?eFRyOqW^mB)0h_fqwT- z0HS!c&g@Dr&dPQ9PUL4gW6)ohADX9cM-a@z^ja*;zpri({4FkOgx*6oJ7)X3JUaxj zuCZNM;8nfy0jwDQ^cHoS8)nU&eNy%x{mL!gWVwmO4=g?Lq1ompGo!HB$o<6b^Ykipo-24rQ4Bu=QALY!P7t(=yHj34Oi-qY zdIfO-z=@2Z4%B;#2wZJCLt->I9iJYka<$PXWa6dUADfakdkR6gT2R)%GcX`|UM9^< zeb&2ve_fW^1*m1YU{LEr8p(ySwKi3>2ejtsL&ar;O_G22cHG~k|E6|Y=7=CvTJV89 zb}LTdi>>e;3wZij?WNelRtGn_e4t&v^2VV}{ig(R1||ON)zC^e+TW(Nm@MC>F}x@~ znAE~W@ja|Mpqw4PQ`@lC4%!=B6i*;K)X6}8rxpRJ&gvI#OtUTO=<<=+9&%tZvHV#b zW;A)~Eoz$w#?QhE*muhKavg>5dGXc>r`_|*ek5&SbkA=fvCh81{mQtx78vFY@x%E| zBzSe*!Nhe^DswdHbV8q@Y+yNF@WQMnt9(BLvfRpAviN$r+}&uf|D7+pyzz(r;z#cP zA>zh$pPmSjedU>N_{vB7>p#A2m*?~_ROYclX!KSjrOsPZf^c-2k+y1VbzmL12c)Ub zA$t>8`#}c44Y*E_rsizM^9Du5@_{JTj^qruN7FD{PgBZ2Lw3v-Gbffn+P(69qD#wk_SkHdI{b8d)UF zrBO{Dm;}@rdE&~T#9QT~%m@RpD4U3Ne`O4;#AUM7*XbUWdl* z=JI15NSz$b<)9A-<-AUy)Bxuxi5c~?R>O(%mfkO-UthkM3u)8rhY5e!H!D(n;`$?P#0E&>!Ox_EUA}`>Y~l z`AM6fW=m+b3_Hx_#FVfB3upJt6Vwj|ux*Z|Aq4{>J;S zZm;|K%jNPF`ao5UrbGZhDZn-e(3-X~f-xir#p*n50T52NCS9?MnfMu57*kH!YyWdvWDJs_93 z?g_v5x>5!v?K?)sLV7B}K3j&;w+{O~$6O`j#@zi+%OrgfLt`VE{{~$o{#(h85t(X> zQ(H;wyC8cKsA3)I^5G=kBxG%7HJ%jM3G_?&Vvrw`mpPdkfVxwIylRWS=WT4SRv>sC zRFPcZBA}>cg?OcelS$KPyR?@|3;B9-Q^{R38qTB@+e}CklcdpSkzqvli)8q~7ig0S zocXPd-;b5PuXDAE#FB7ah9PC^J@6c@>FY0jPLE36h z`}XpFv%TZRm%ihd)>&u#WrhSxZ9aJR{tNql^N+TD`@;MIT?cwMvF_ZQCa~e?tsGgD z7G(13e^2He>Zzx;TE-jYGPYH8j}>C8K^^otJaKPwW%3ZnM0()DKueMpkzob@%eHPq zP3J7TLiV|E-)1zUwd^&j|H*8Pt1axD(0MW#x4yf4!Q9TtZ@6_Xh*^;(T^)5Mb6VR} zItc`6R(g=x{D=%gC>@igG9HT6VbQ`m;x@%+hRad;g4aQJV+%@s*F%kiSynuCSe9n-9{VfjM01=JxC+fQ$on?L>jm!ALU3Vv4p zmNCHTvk#xW|9y9FaoD%*4dW}QPz#;l`83u^BHIb+BFSt&Ga_66H7RRAf{>FBFRV@| zhX9>z-(N@AV1DqL(qzmv{Q~6I6FC!zs{dX4D>msr-+wK7nD9vs=k+!S5)@!+;NklhNneyM- z&xx{vMsUIAuJ&JD@?ZnkC+fRdg<4i4kQV6c!Mg=O@qzuAUz%Mpz)cAf07}ftd65lk z6d6Fg`%A3}zp&UyUL0Y%cKGfHyv>EWDgR1d->_@vfYe{Ns~+j!@f!g zY)XK9Cr;v|`yvQOF5n_N2nPEfZlO2LQ+@8y*W$QZ*G5LG6NUkU1q8COPE636g7{_j zFX{xJ%ZV8bv_6!-3N$wJ@;2(+F@QQ_@iqeRYtD1|&1GUG-eE?U73i;Ob0u zAoea$S!8l-KH9ex9t-&+b2vtBqR1CyeTwzR@Qk1T&`++rhdE?$M{g#QfFeLXsa5TM z;DCGvAo*20`AOmccAco2zWKyG*rE>~1b*Q}_vrgNPc-LPAA6{nNG1^G(Y7neGAGD- zVF0$~!+gkGp|7O!gRxF7h8J7o>c)pApB9tQ4dNyHYO#SZ6Bln0hT>sH?iX~HWcrjX z<_F67z}REl^XmyZs7?@D2xaljZxGHsq3i83an&}?HjVAB)x#co@-|OnXFm!M4GVLM z@3LLtH%^|j-GEqM9Cy58iH`NzZJuevWxKuH-n{!7GJBfH-jx$<`}T9&c6mb&;#ZKi z{N42lm^xz6jRjF8(t}dm)$3&O2Xhdf2j;BJ&^*Cr!YwHOEBt>}S~e zhETvDKik4~_Al$ws^1JiKOFQD_te6|pv&lS$%6)YBVW#c9T>03&RmURz+`(lt&e3L zu{^!qEZy^qpdN{XWkg%KMK`?3BhD}PUV}H44tTt~XN`^3*R}w#yeb&@BYRJ_WK!gv`MDY>h0DyrZK5$U6{@1ff3H$si-BktREU!^;QU ziVhf@zR@cicNxsNtG7%hVJaD!6yyYmS6~gTWr#ldKHm}ft^&$4kJ!G+)B0bpk41uu zgblxge%#!P%`~vp#Egld9V{e}?z(6l2wOT`2Z}@%^{?Ca#~#5aGFuUK+~`Y##b9tc zx7}oHxTYdR{22PL-s$nWOvy|Q%5lmf<07_Ix!wG8)ejR)@BX|+-6qhU8EhMt@Rytn z?xvx>_n@RsvAcFFfrOCY|JDg+*%q+hu`DAogBuDQPzF#>%QQr$&$)JK#}h5|3I3i5 zo;u*x!&`56e$Q{*9VmnJ^PJn_Nn-1Wf5?>j@W+{EK0mpb={w}&7C82Ka?dZ5b1p5A zivl-+V2kEJ!wj*nH|B*|_fJuy4A4Qo!#fPYris6J9g(f3HQvSPjrMSSAiw7lmu|l; zt|z4ht&bwji7y{x;MajCHQwN#BwvglpWe3Hx1BivwoGL2uGreQ*L`x|E??2i#R!6G z#{G@R5t0PISD6f_-#nlQ0$7(}8IfEZ!iY3k6Gohf%z*&nnEU(!DXb5=OtBlV8OYhmCN^0BHo#f`5AM-9}40GYU?e+8L5Y(d-ia1ogj;4}V<oa)>C@uHtPAB|PBRRdzdU zht`nbi9ap^iv0y5J0eM z!#U~B<=5rub$I-n+jWDa%x)gvW;;PR@s-zE%f;GIe%s5hZm&M~!Nse&5>zLSaQCu< z-@p9pZO40JkHMWJO(j@4ubhx3KazZFGAAeeOd$)ke@QgWG9-8cJJN)$$R!gkM-Q7} z8CBh~MHLL{F|lsg$ljp;I=e0v|Wu^x3An`zyWUb-&I-^27LcBHe^nSZ$;w_wLgCN{n~!B)3h z!2Cu!A-M#l&Ex{&_5`oU0K2Y#^Qs9KrkCtnAODn0xPE%OK8s@@W#d`D;w#kI^44Jw zGuRK863h>*@L?}4KXacVp=x?3S%$k3ucu8^iO#{q~7Qk1@$mWfyY zEHYpV_x#FI8Im1_IBoM53Q5my?x^b`$FMMvHb2>-Ciw<=A8m3;i<_kQzKo7u)WOg= z-Pwr!s!Zj|b#qd_URHrRkj*oFfW-$WyoPTV^Q(Ls(3BV`;se+w+5BSZQM|`@T)v=6 zoVIQI9?=EG=Wtb@Z#nm8>Gx_UA(Rlpxa$IOdB?wdk3-im!q?os-O(<8T-ZFQVNnW$u zI&^LI7e$Enhx)X>qoJ{f+ArXqT-AOMfOFm|)B=!+Js!`i1BrDEgABS;U}r~=5$p;c zWDwJPWGihNW$>#!I>GY62@Hw=80<*f@F?rrgJ7`J+LrtK+#$X9q145m z1B?LbbTH`4>tvAUg;%vl`FQHg?fjkDqm3F}zF; z;3cI6r{e?9E2(7-_RHC9X9peiygVXN&&tj%g3&&U~IbIOhPaWq&0hbb2S}xyD|aT*6`r z(F4U&#}$2dz6-Q;ViwFq6EDywfc&fkfNancF_Ag#F)`_*k0WFhot;Slj@i9$D;F3Wh&>Hq*B07*naRCK|9 z8%FhM*Z4fr8JF5*HcxG;Lw?2ICM7uJD{29KRlEgN$6E95pZm5y+V(&7qc6SivjI2V zA2LRD{N%UZeDBLY|K{==`?kGN)(;LrFt4x6U&E>iKx6aj-(}K|H^ObSXb^z?F67a*TDYxb5Ad*{;OQtyVaR61py-*4( z=ZH5!_yGWlgNYvV8(lL60wB;cHlp=m=y~+nqy~g7u&JhiIOe|k^i!KGE*iN9wYKL| z61{KfHD;nlwb>ZV@8qJ#=w5}F}&-t4DeIVPb4*;>R^|T!P#LOd4H)_%i!%tEySnw+s?i)d%U6(wsCKZ&7#hh zWl&A1lVvFX@WiXSi(j0vjw~eOVr2C8s1njRW9#rKA#|~nhO+ZZTjgS*JKV7;5omVL zuZ%-L5A=aDgDm1VVe#Rx@MtP2%WyV4tuLmy6K{V|{fy(#F&|g}2Hy1G)!V0@`QS(1 z_9+jmf<5T3UwQWaM{oD-J-qD-fIlLWKq9OUaAqJ03GtIBx@&t`{}q`cF#%7biGxNv zu?d6}B6}o^*OXgy$ColAxw!u2{IdGNPIA329Pp2}&;v1XK_rYh>Tyh5MhADYy5USF zIIb?A)J~&41l~G2>{UC!mlBX2b!MgIGOin5SolCzel8c7-=bXFGe#0Ua)^*ZLInF> zu$Q(seagNoTZkb=5HBzju>}ued|W_rjqxo8o^+;QtZESFZARic2b4K5&1*R4b+AQv5HquOdMzqt{=$L0)l=deCPrKCRX)6^l8Y=_ zT;znF%QoVo2{2KV5~SZl*cg-=c)v(=qIEra)vw6VUUe1MC_Z?b?Q!P1#YPQjHCeS5 zhYq=rjjQseBENiRL&{ey*3Q46VshY={iqD&V=un+{CkP8^^bx%mhT=s^OkSDZ2Q03 zFWXIapv$s68v%iMOHqbeULSRxkkqw-{ ztWPKf$p?~z#d3?5)K!a%i9YW#h(?5Xc#m06EfyCrzmcubcTz4vnz@aW;tT&$_Zh55?5{Hz_5$I$=lTw)1;ig)MgnA0*0*K~+8v%`xb{%zZKyFGgJ z-@Nz}fBfGFVx#wf_icOX;WPJsdAnS`zC@)6b_EQx6ZUP7Hi8X+Ab_+WYsdHPM`Rc& znAK@IuMGe?%CNP^`(ZEZ9|V|?6C(-98ess!N0aPjtTpjH5U-Rp)p^wK;hCckMi)VF z!XdFS!N#GF?i=o&Mzgh`a*TeNIN74-5b4lXVi}5QJgZqI+0=2_@RVEU$pnx6w@Dx#oYRLTGnv>Mz4Y`m63h1)Do}F3FG~RG_l^)W z_gDcCAWg#qWMjaXZt*!{RK|xHpROlQY@B(7XX+AjwjGM?4&>X$C&P*#s{`XczUsd{ zgW2keDAVNSo75;8e4(xNdG_%MlEh^f942WyG=`^G)-oeaU_UimmbU9`fJu_U023s$ ze^?7%TjR+)3_g^@R=15q>SS;j5B<-#lgQWmE`kMN+d?Pg z!9b92V>6k%j+opDJSQ!cg^(VKkuB8@W5fcTSlfgCoiK2I-3r6l;1Ai21;C7B|5iK9 zCfFjM^HR2>QzX`G67(Lu$$BAu1-%C(A-e~HFJzzS%#y7u!c_cD2j9hlbo#J?n{JAgn_P-JZON=pL3!67W((u4TNB+8RMJ~VW= zNA1@Um@^jlTBx#uq}sIi$ro<0Xtp1!Tg&(rxDW%q)g3SwJ8@~%Ar;?3PTJq5iz>n>XP+!fph-#1x0El(j}|DLJITX~@cm?G zsKz0N5qY4L`(^og@Pj!G1yEXXE1eAf=z@j)Ss(quGoT*1B} z3ZX1pW99Ihu{@=Wkhyi+nUM23dr+#qpbK2*`h%o_Ge&tXndGC zb#0RTm^7IzYarstne&{8aWR!=^q}4Bo-ga7NgE4^7X`){ixf*q{hB&6J+qR?Z5e}4_c+~dy^7%1Y-L!Gyf*f2^K%*5iT}hqr$uRy-ViDPKO*>( z(jX=;Oo$jbkNKt9KDuX8ZTDsw*)%(4Bq=3YOqvEHshDq4THpbMLo98buVhY5c4pH- zzLf1l_hMq}Zl`b{k6}a{NJ+sslAFVzPGrrceNYCLK|W-!C2@`9FwAqfsG>tyjw~GE zFOlDYpR5xIN$c0ig~f%luOeqx9S51o{F0p$<>!4F z!XuN;V2ft6_T1!Pmy=Jh#cm5mp!s#R9p3;(Nk@}KfO?Z4oQ3dYHYM;JUt=M2uRp*XtJ* z8=tFxVPb-_%(855jw87YzB|4e$^;9buqh<}Hs(kei}Gf1KOojrNbe?kQB=<8^UiK_SvrZqV^Jjt%; zY?bv{WPqPTrt}zgLbUpn;+D0|+2%byWTda!K^I-{@Z?uU3bEs z$tBpYWpS77`=~k7{g+b5?64P~mDI#6WTzalsBYp2G1*ceDCJ=sR{h%uO=ve&zVapl%KXhr^V-Xx9soZk?3z4x^5f(evbrhZr0nYv4jrh|XSJ2PcHbsv zd65_#_(NHAS(X7|A#S_!;kyVw^Tk_$aj5C60}v$1p38~rtUi{BERNrNXa0f;lCLVu z2wC%44LFz1Q2eg;C*{|};_RMZ!a{G;9jZ+SjSGbE&)My^P1&i?m2n85?A(hvV%ZeO zN_}%m9)GJ)ITI{c?Y`gsi4VN=9X~JGTy!%ZWc$_@ z23X5l%##Vq;B$8ivx|nG9$sZ8mBQU{J2%Ku$xrBL@#-x}Y`rYRJi-5cco^xV&clZ< zh_C)2n3K;e6TNap6)!fro}6z7ct7*zHJY0zgR{7l*?b{}4y&(_Uzab0@Ik48w>6Fr zCv;NaUndI!9YJdF@fSbz{P!X1Z`=0ptL}Zt{&|=Gux;BH^xq2-+k(Tv4MsJxBGcA0 zY^{SB(aJZ#D6+c45h*GSNMaH|Jcy#!=g|r60K|F!=MUkG>UW7W9fB{G66Tlat2tvM z%26)e63KQVVKeb5^21TrHMa{r7AsB(e4de|emsh^`Sbyg>;0V-$!2f9WcuW&)$3Qwq1Ur|9l8$D|d5- zj^Q4&wOUX!tj^y6qy;&t<8E8%7JTb(137WTq>=;s%job9f>!S*C6lgy5KXD8)K_n1 zWYM<(t>&!LS`$8TvD0TF^WYh5QR)9Fz@ESi}r0%S}a1_xT4=R znM4!xG z7}km(Pvi$axA@xAGH>(~kZ-psaR95z$DqXNsE^-l4YKt8G84ANlSI~M7`;6^`7t?` znN%pjadzkC!H|i&4k~8&45BEN@$!?l=lsg+h~q<5Ul|YI(vcsW3%ch8pl{cibgXq9 z$D!hr9A90zW1X9nueW(wB}Q1*^hN)_TrPj%{U3V!N5Rq`K70RP?A!K7;kOCcbw`&7 z?3#7o7l^16mmt^>khj={0WN}Y9Nq#kAu=1b$`i8K@q0M7aqaLS`;3eVVg&Q+-zP>u z#HrDnqVK%su-ilJtm~eQYclVWj7_F_1|4p(a5B~z*+HM>h*Xg$YT_`SnkVZ7!#me> zl1;PxJlP_GJlqN&lBCHZi~7`Abhi2GYtC+lB>=30I&Tb_A|8m$DKDZRMEQQaO>>Pr zz%c8CV#>6v1>+9fqnwr=@nU04mP3h4}VBfaaF>!&dGRSgeKvY2`$>^NB^xqx$@w1tbCclM_LhzBue?!0#EwTI< z7?J>jBbJTcc2R0)-n(DKhC@<&@Z8DMjprq18L$m*5sGIDXhsj=wU`4#P>d|hb?TgF)3e9 zngAXInMnFM*KGB$v(7jeB*;%3kSVicz*TO-}~$vKlsz{_}!z6m!J8j z@4mU*{8L~A-OKt?7{6X5lTE321`Y}iY>EPDZQlTtO7_zJYd`%Jvpr(k_!s(?01Q@c zaiA`xOtHYul@_|r;Bk&CiR#hfCO7HxnRxl=fb{ij_Wr$xbap=9aB2(zonfh*Rg*! zKLY&?byDmhiXH72f|0WcYKs}z0 zR|I)*qFZKdG)ZFl=`HN$qMEz}9dz?d%}MEP za8nROr$9iQXW7;d&_v>N7;8!pav;`qs@OgvI{}>`AgB=aDIB@~U{YG(ga`q8Q72~) z1$O6cfEHK{dt4icnBxh;2BtSyctE*H9h;QPNG}Eb^a~;|iUpl&d1f*Z{xJ0+vb|pM za!pwQfz-5bBYTHG!R#0Ow|BP&>N!vunaRP9{4fwE7sEXGjX>Rjf|-x&$AeOBUJ~13 zAAA)TA1o;i^P&LFIgu~T6ZDs5Une>XATNll>c`wDk)I&4OT=rM-6ortr3PkS5bq}P zW1TGN$hSG$8n5;zZJyfETmE(K0C$gZhwK%^tmvI$SRm+x71gh1#0x`nZNqmpD*tfu z8y@W9g~5QhGGUJdK4wbU#^vW}`GT9YpI7S3E~X6R;FG#JnQ`Cj+mFBa((`}yX!7B+ z_y6s_ZQp>z2g!lwEG1KRAaU%g0<=e<<`gZ%c4RLw@>n~qS=h>kfy4GX4xlzH34-%1 zKuDYe-XnYE`~m>Gz$Qr9@h3NLn-fF>mo-hc&$De;=bf>ejKn(qx-pl(p4-0Xdt{@| z*o@rvX^_`kmR-FIq={|V=BHl6+FDk1wPnwtce&7as2%NM7}ruC{Rlfc9uEqB^7b9; zLr5o85IsP8_4fFnW4k5+y!`ajU-ZjZp@&3)t&HtHZS4GyfBx48Le32=w*Vj2#0kxxk3Mr@| z(Bd!_NJd5Nv^-!+W+^KNVjpMOj-QU&2P7#2T_JY+U3w*WLD zBUM*Sf%m0{?>hO4=!<>OfvP~-PRQkq|Fl8el?ZfR7hE)7Tcjd+_lcPh3>$ocHYb$jIh!8?h6E5IO~dR}4_ha$)p39^o|ic2 zMe{jA4`o~!+?GD4;JhNg6Y}+0P6+wu@Fo$%s20Al)w+sn=U%fqL?>D~Ly<);D= zCuFfJJ3Un$$q4`&umu68g3PTEwn_==xRI1NWsCMbVk1`4_=d!2P;QYB1StIO5y zk<^)-F1N^JQpOh!Kz7yV52mghT(&PdtQ@bgDi~idhh<4=r z@)F1WQ1+4Xly}dVwGqI?wU%5z2wUF-@)i90{6+^XI#5>O3nFH6asfS@kf!@VWu^~7 zDiTlvm8C7~`ixVX%<4AVYVnS?u)!7_u$PeS{B{^WFy=Ag)HSnWVMg-d@-)y(QX1p4>UsPk3<$o2bwx_iJi@jmTPr(vVmK~oT23t$wa&k@mEt@a3=Xl zU~&&+1UZmw(#hyboK7aH4&2;uJG7Jtnahctn*_M@wkpZj+0r#70A}(+=3rh3(BXu= zClbx%NAmUZ6M8RA@a_QD6IX9&@{>B@b`trCnq9B6j`1{4Ca$6OvWK7bQkmXYSi}e& zI9FJ?MFO^|>B_oRA`#)i>p|#*Utqy7A!@#rJg2tE3vB3z56)qXnC%Y8I1}n{XNv%i zOL!$9$k&G&s>cp6!&&tN1;d{&+h4yteD?m&?A!J(_!3c}e^qStT;`Dp(BYC9nIr-L zE3sw~M;6V2wjX!}lk|>F9E^|51;61QS3xEkSP3va{*-PN>#_m>w@fB&e&luG=NUP1 zW0upOkS4Ah&JTJC(lRS6kYg@E`v_JEX#s4D%>AbJWQd5wd)wm1$2AjFF@8*6GFkGZ{3>56U`)|G&Eb2IS4>J$}20NI3 zEG0nmE7=ztK5xTnB8w(giEQsTC#v?GBi?x|8UO$w07*naR2)m>Apv>Ur=M5XLGNZl znvyy{b^!T62EQel@lHXjXz4Bs*5$ic%gQ<$*4?_=V3x17lMq>XC~GBiN|~o{psgro zCcoJ|zv`fzYST8Esx3lT@-|)LEM(f8{q}IDZ91>%oY?&7MU`{nPT<|kCysGOo1WFk zo-d`T@t5pG9acjBa|#GV*XE`UWwqGGhx33EOiKck{4Bo!G1?R6Nd}hhjGbQz7!QXA z_`jaWe*E(A*?YgZZvzzMAbKZcwWsQ)SKx?spDZ>K`4#i_@k1{*7=DmEFyVA@)Y-fuMLp&*ggfF@2(tZP zHg83c%J})cT^ZMer~^~XWJ;;>RLO#>PDvFe4b*)z$;*@aOS_ZuIF`jI0s z?;5l2zVY)^Z5G`oamf2kIIzbh3}+8Fy0bIFKZ{>S`KiN;07dH`{*OB%`+x7-VV zWVj+SP?8g@+asiB3CF*2cxr6gCa$?xw-M%JeiOO3)Bk;{Z2Vcq`Ed?luS|`RV zdEp>SZu<5Uo!fT@`yq+dwgjLg2b}jGexAh^@IPTolbU=Kd2-OJgP#VB9ONk*OXNk@ zyD?}P@}^tklkCdc>30JSMI4U5O|oL=vsJT$bDYg~vLKzzK);ONv6!q5*Z+o}f$AAS zI#V*Ei0yeIJ0o7F40fiT?{*{(gXfo#1B^1qClvo0k!{0!qGbh! zNMNUBd<%8Mi5}Mo851kq#5DI*q#Un= zr6-7(5flV@-VtF!C%0Hb2J!qMqAp)YiZJBxiDV`L*OX;c-=XYvu*uQExrCSn-FG)d zLSE*G{xvKCkmU)ntbp|-56+SMFJHaY$6U*^JdqvkIJk^i4_}%5(mGpSAc_M&yynfa z*;Oa|hZk7JL?-eLuXA8<)zI z__lbkHeC=F3wu%-*?r$0ULHJq|1;aR{TDN^0k9Zlxd6l+F^DY6b*TuItuT)!VE_c) z7Wq3_!#FUnw_HD1#DT2>?+G$#V{^oGX@14KQ5SyUkjTZ=9h!Qa%t>edfDYCTM`Feq zQKnz*bbjRW8HuLtkxVZ1cP695tIPz6Ub62NK;4glc(=(z^b&;iDA&lY-pXusR3n?| z((K_FQfC3ePRNJ+RLuX9=fsXfkju69eGDX*Ng3xn1Ixrqk(rf4M2fgVc1mRbI8#zY zF$NGOQGrx-m22<=Qv@;=UqyKym?ClIQ`h6{w$0zmlR+O%a($kxBh$6>hvT23O$u>g zJ9kuCP}}tL06*|L16P{JbMzvKPF!1tu4%sFXYWTWHUzOXnG?=AdF2_yT%IPAS-bB0 zg}UV~U-#IltG25A6yfx48nWcPRX%;$hqHO76AW)rXW@Gfn_efqc@bA&tmC*-e1+ot z=rE|4pNL5ipC>wE?r`Gs@81#GSGR3@3LZ*GSvQ)$A(SES`=h(k zf>(D>WCtL5e3g;|h%yeL$|-QYT^z}ekpz*k8jwj}9}D1HW}0MmB9XO$=VRk5g-Yvc zJ35nzWvUyoK}c7`sLZPOI( z$JGMp9m9{AYFb#qjGFw>dphVyf>eTg4#qY3vukAagG}%`cIa{iNa}mA3tQNeEj~B5 z;s)*t91r?IMR-pwfNveUZL>Q0GD!J(av26+Q*FE-FN0=Ws>xmtcmT*cP7mme>nx~?&Iq;@5SS#gn31Y4LMoMS(62GYVO?*vpk zurMc{=`p?8boFiU$7aKce1Uk;f;7m_;){<70aC-A&UAPwS?C%(sM` zAP92MJfjjLN!TVJoW5$b4PY|wyG#F>;F=UPG3rBFCJ5#X&HZYWcKgOr_U$GHct&iK z!*~hddSLg>Pjq>mtt@PzgHDRw0e?(LGT0JjQSJHBHa4;wcqYgtvD3qc24w{#_tLBj zwkK`Hi|ShcJ%VVjF%kCF{jZo|k&C*;p*=e$_%VWwb%KF(UXvJ4(CWiV3_@KsC$cRC zK&t)#>`okl>G=Z>{PTG-_{Hr}MHXgd8g)J?v86UEaR9TPX15bS9@P_bKUaZ#om6-8 zah5J_!NyEqB$!ctv%_~QS#+~amYsbb6I-?gQ}FN>34ndS*kDJDqzaI~Kp+_M04-bk|jUZdHLk|RYHZm6xI{_(B7%+Q6P=*8{vfDZYBPRyH zf|Mqt4asN@&kS>+bWj&B3tP~6$F1YHWyuR zepzOn2v}{~hDdrxW<{!>9Ap$(>e9gVuk7t+*8Nx5clS>$(E&eYVbyUR|08{!Ew?BH z_`14b5b(5C&4VrKb^SEFBMbeX2CzjG+>ic#IYWCqo{)=#coj2u&m12yuBM<~MKcHe zYMTW5Y1xMR-3;^(v-0g)sJ53+c;!RZ9DOq0Ea4Xg?r@^-Bgk*I_;3q2uM;pcK2Roz z8LC=lvoX9`{4)~~Tkc*YA=Aqj?dvv8C+FnXePSeEZ{x{UcwZnVHqWF#W+u60sM;_1 zF~5mT#0(KxS1dY_b1w9q7D7W&QA-5g@1jK?zJ9 ztJ9UyI<<2EDAb*cGx(ssg~f$76^`m;Iux`9)N5|9Hrd@89L*)yg93 zNz@nj1+<&jG(K=SO;&m|h)dHFr^Ou{3y-WLTx?)}YWG0x7I2(N>WhH_#Kl^OzfAoe zD=T|kzeO)Z7!aoetRvm7uB$;90uy+W0<%bP`f$>IYxkprB9Z`O|HIOP2{F;HV08pY zvK9vt2|zdEVzDvFQuAS>{=nz}+yZHm#K}kM5h1OCa76B@DKn2rjp{&Njd+!&#X{-m zgmmxah0)%P@GDYcXr3q#7oxqjtPp~4M^nG@!5ax zZIbE2hcI+=LI&w_YWy;w+(8Q80~#>tOVnMUr*W?M#MD*ik2+#pNpWP0%H zoF7>{n;pm-`$?HI$F~!0vc5Dh&i;D$LIiq6t;l{fXVeFpS|1VU`%26YBLlSZV|}8# z44kb#QZfV#A*8t|RPZ9{IS^pgNeH%lfrqLy{_8XYHIzF#xC4`1t~2=Tg>E=bmjOzzkN9HqnyWQGN2xa}jwfFqOdUE>8+e(Na6zH07nAb3) z=m=+T*ZL{h?dsnPHr?gx#KkrpW#tHx8-ovD!7pa7s;#>vJ&gJIkjigz@50|Ezswo;6QzP62qtTHpXVgEub%Axc^zQxU=BFa6 zlc5cPR)c8=L5TY8Lk9GeRq4-WAYXsrNC0B`+M%ZIv}!v#Y6iuXd|yVE_ut1EBU5=a zA&cX)-ZGpn19W zo`76T4{m;P-u8XW#=77Y#x3SIvQuG0Hk@@IGPBLOVGjn^q+}AeMMi_*y_= z@)G(Lb z`D0wrmvK(#na1NflEFQ3-vg8X*`)vTm;M)*hD+Zx4k3;D8##D{wDNMD99 zx9>9t;Gib3ZPyRShW(%NUnlb(D_FoTWb67_QU8r;H|f7NC9|LY!*Bq@e7k4mDw60^ z_M-OEgCi3!2aXmJ4frg?O=>!89~f50pbiIRI;i?*gnY%_u1sFoWM&7G4&93WWxKLm zg26E-4n>|-f!U1(Ok6$4GjVXCKaeCY%kVn#Hqnf)+J>}GPj*6BbG8v~7HAbXjXdxo zz&f|^sO8t~yZO_7bz%Tvj_HuLo^77M9f6>j&7yb6OZ8yhLf*>4!9ugN#jDP?E!3dTE$LZxY zJjuA4lCYi=qDlE+EUtd(EYqFr&&wLbm}^V=uot%PH4?x+BN$@L@Tb$n#ARb=GI8H` zuu%1&Sq6J{i-R;#TUg-N6|5w&v0&7Gb>?LVD2DTy$NW%}AknomX=63mL_4iPXypYF zT>#Nm$2H5^@6Fb9oj2&n8RqHIpuGf9{T8^fPQV|slf&J71Somp)pQ_0or&0?N&85@u!eknl=FSCPkEWg=jzL_BcYRm!~5Q6>HKYNmdB+-TW zsXSRH)j$?ci5D}OFAOB{0@s|78}28NV{6P4S4kWi{?;}j?j^egQoezJs>425)6MOa z@i`k8S#l;`WxPhp)xj^$qR;Q&)%G|foJ#kLSN>Vd(0-!{=zOupacEdN(@iGU{?6{- z9cH(?e|jBN`4SX9$*Z@vZM*&5&wl1hk1ifOd;i~V+x875o24={l>pscnTRA? zlO55EO__UQ*R=U;ZvfdQlWt6r z$c_(sRa;4Hwb@$qKVUNMs1(X&RR4bHPuMZcG0~a#?_zDvzQIn&zS>7;!>Ru&1FN70 z!x9--2ERDf*)>EYN6wNtVAm(^9og{LJf#Hm{6UTg@{wGWQ0U1+Bx3QpqevcDezVNw z@Jg>r3b4&&u_3oht6>aC(Tp^4`GT1&0y4KVd>KNW7l-F*c|urJ4PH&#&C1zlutl`J zA}t}`B%5s^At#-w{Foe1GmMBasV%yp%(lj(&6hrWsN?blnK>1UR=tBm3pc5w7U1RI9dwI@C?(w}R3SbuEIR-xnk78Ta1N3Mc)&miU ze|F+{MqCLvz5Lv~3!pU<5b|8jW;>8S%$Krqo@_Kdq#%Ey{upX66P=WPP)FfK5(M%w zTQy$<$ofL>qG8d*I&z;`C(b^P1Oe$qEdZC~>uo{Q;da?U|CE6hzO-^r@_1n|Ii$() zpz2S?2WOjT?-&z$_`J9mxdgv)f?yl>I%8X`kImwnjn5g5=qJ8Nf^{OwG-!bcS@Mzm z+`Q0_%cMe%i{hOFnE5~V?Z;ny>G{8Uboua^Z~E?iyZNV9#^$}LWn&4@p^JR`uPb7S zn3P7t)?`8^`w&J4y}Eb$0n_|OW1IAS_FmmEza>IyvJXQY9rP;bTxCl04-IkT@z0^W zFlA2P^^9uvmL`>@Zey!pyQB}a$7pA9TqX$n&bop~LJs6vhRNl8jqK>HURgG{I{#7~ zx5=>wS@=gLljQHs7e}^2{?!Sfm*9f`HZHzw-<*+UpmKmaBwh>ImOe!ppc68YkfajW z$^HhPkG@A+<*Nrd7%UdILzz$pf_aMAR$B%$WiniU111^ozeTei$kP)qk=2EG+;w)M zhPj3zmDyXen}vBYnVIQCEdrRh&OUefPUHvvo|Vo_4iWKIxZWB^q9#iV7Va-*;u=F! zyy1Z%c6R$mx&`lvYqy*Fk5Rk`*;vB9k&Z?-&#)GDHWHcl?q>(R)PcOXGelr}>f^p` z-}8YFKmT*bNFTiEtzW#o{?UVN+g>NP7^}ARHB*|z0zp_Nu|+VBe>palf$g9aB0Vgk ziR^1%R?yBOEf6*$2)>r=kk61DwE7PRvLM&BT2NG*^E6OUlx4A#rb)8PFI4SzfDk?xLC!Nehh8|1w=U z{W?P_f2Hi-sDJl1y=GS3yi!?~U+}?$F>x>w1=+tet5j7tI@sX=B-G2M0K(gk=P&bL zc?%VbE|6g&)KCK837rEiDPQjbRaRYpNk$Rd;e#x5?5EAByG`+ z!;?b_RkPPG&j}~XuXZ-fZd;r=G%d=Vu)M?C1p9^Rzrod8VkaZL=XXF{W0^**V?JM& z!He)GoQ>VKS6}_T&%W`4KmCs1?NfXB%>93{Z`&W`!z&1eYh=<~HQTPu2ml#D)9m57 zN#B*Yq$j#1Gq$#th?6?dR44hl$;ggM9w>-5;a7Rd=?_XCj>jK|n7E8AvAD(z#KAiY zOi0pO3#5BTj9`BK!@yQZm-zgeeZwNH%iGU3w$tqM+<_*W|C70~U~PWjc9Vq%*9{kt zrHE$weD?5eGCmR}wpOg?PWsg0l6!QsW4h>d;mMqwJ?NQgR8HlGsP{%AZoh6lkJ!NcQ6&lfqW`}i$9WxnB zJ*YJFtt{qD_Rn{|^p>We3zv1q<+cWSH|{`6(~K2F6*@eZ=_Hbm;mZ(?acN?7{_3sL z7A~3+TLD`mz&FFwW20J6lxMXgGoXf&Hjt{X@?5sKXblM2xWz?O_9RQ5T`dQ5MxCsY z^uR1=S7a@FZ}S<+g=P%{RL@PyEx<(Rd7mAW~cU;!RFW zf*6QO7$9F5l_S*OWB5sKEa)bsw-GtJjqa%lriHDXfI?PtRBt>5dFK=IbbQ?YoXL~F zKjxg(6Q-cPwuRlpQJAOAYbH;XIW<^yI$%5Ue#1Kq!y52&hnJv~rZMHbf$S<&n zxS0Rta=HA4_kZZ^9|dVV{Hl9jvcKWxKWy9n1u83B_ItR|Dy+b;uGvB_CM8KiO4wZg zm`Fqtl8w9mO1@UJ69i36`Zz<|tx>&&4$lE;BI46!YW+i3LlTP_?5LiBEEyJDE$PRRQe8t|vCMUi}!iG+6k5@TTrrNrk%cLDfGL4+rLa+O!=dRu|kU9EX z_CH$`9P=C63YMXCv-f0O+PJe<_k9nN3$|i69uy76ZZZ-r>gWTYRNEy00a3uV>V(K1 zzipO=Hwzq7l?OSTjA%Quh)3On=}3UDe{FtApw;~ofGB1{(mGovT751^BtSpy86e%& zR(%*7l>oSTpn3XsCinPq?PasMCJU|~4)=(vOkWiMn|D?}*dd5@J(iz3fEB}^UtCx$ zX!eF#b7!AatBQW5@A>V0SG^S0F0TLpAOJ~3K~xl-B=!r3B5|HflH?*xwkzZZI^cdN zYcB5j#RFtoeqA|G51)O@&+Pm5uE-rU*tKAM z{9k}9ahx~anoOj-DFk*XwX9+lxCj#5-}7@qUd*&3P?1g)0t+!-+u{m1!D z$}Pkede@gECe9q{_x3ZI|Bx6>uHH&*rQRcYLMC3i{jn)&bNcK5boRAC7K<(fiEYh@ zO!akHYSYOzr3GL!Be~FSi#^w)o-pL-Lq)a1SIED6WA5+Lex|K$kKm* zj6)F22*!290qhK1`#sw(#Bn=aJ|I@!cEzyF*(#8+2PJS{0rEb4P7lR6S;27dqWEA^ z3m3(A_pgNM_zYk-xu2KA)Bu2oS#s9FKz^t0*X%asiC3)o%ua86n~tF<65AE|ZE zoJ_tH851Qq;7j9Dg5|@DT_<8;rWP?kkjeiK`#%#OTxsi*uSq}idwz8sQl%Lk22cFsa=E9H@c1Csy9yEw z=9S9Dd+{KHFWQIAfciZIosoSvSyua&(>tEeOB>b+K9bn3zmfkY^6K+DsgLmUq5l>? zk@k&jkc^4zVVz$&Qd3yi7W7^&qrJ9Kh@lZ~}4VgB)BVD{Ir#VWo3hyYp9h>M{*< zr|aI3R%GA+ec`j(mNgSkK%Vs=$&A>#O^7RmoKbyYB}wdp@n(q=nbRfWWpRLa2bds9 z!Zgtd?`#0vdJ=RDgJSh#Z;g*{l?Z<2bjdLY zylQKF1z>5{31Z=M-$nNFOA-eGD<^-rT=sXr|3feQL(S;wU-xZ$>cKPjf8(-U-qa0z zkxaG~!>QL)J{DmsR@s^`B3u6z@5#Wz=qpHUCzK~-VgkR8u)+Lb`(2QOV}OIYVPM43 z6qc?dJKApzq17$j~ zaI-Z_o@?++iI%JDQ{;gO<*93)9Ph{MioUzi%P+l_$hZ)^=_&&$@5{%*jx6IiAW7(^ zlk3n}o+mT1y8f7nR|I)JM1;0T_9)5@Fw<9@^iEccUo-h3Az@o&o3|R#-p`aUnE~jV z1)PESg@Ln0K9ycVp(D0p2O{3%i$sj&$8E-iStMe`R=sYk{9Kp{-;W5VBg<00l5ncH zr0e#3D0l6|dDB@RS@Pntt3qcjetK#Y<4~;m?qzdV$PXFB?9=|6zxv>nXFlZ!$KO~W&2d1COs49|!M1y=T8U^vgA=yr zNOV@W+Z%BT0Q&|z(S0;O3U#%;@r=x7b;A%X^0J*)TRT{IluY!*e}hV>e=kS=?a)u! z3myigD8UzsjM05Wtsqn~Er2}NzXgzYpiW?%*WDTbcboAb&!q?+>}q%3)>7id2VaQL zMS@KJEUple3k4#eoRJ0h4vPs*`q4a?OlbL;?(-4=Yd2qPSQvM|VOg@SMTR!Luhhwl z=c!MzJLg%@Nra(zm@&wAxa*L}YM3Q=hp3L>Pwq+P#fF!!Csq(jvEFheo6YblOJxAY zlS5kGRYXRS;}Gh^6LuFr!2R@MEpHcn*T;mFD*(&*W~D}h_|W5$`%Si3W7;4Vwvs0d z{&ayR7WPXWsJ5`?h`kvAVc|wB_%v7hO{OU5TAd$zhf~sbhfR z(gd-M%tYcE659-QTvh-m*yNX4ZEFYTI#6(fSqVUW2F%3BU6Dfs z;2~)m-Batj<5TtL$wORhpgC%gSKFgK4uLEuI*|3o48DuRs)X5~@qRW6OB_n)RU!;PsRN)+*(uX)TDJ&Zza=+^&w zeJll8K*-FjW7cte!d|q~uX!W6^hnhVUE6o$qnS*+Z1}abdR+cAKiKKWR)+qY*|GaO zkX*Oj1Op)Z^14b(VDwIp8T_Khnc&$-iVpKkbPw{On_=8zfd3R+XbC4SsZLp2RZod+8R^E z0XHQ--b4cV9!=@`Y;S*PckY=1L5`>HlU)b5a0{;fgzou0+C6m9K-FE!4-2!ZycTMC zXMpu0?b{iEmv6cNWWT0Gf|HN;6}d12o1WBsS3hE#L4P#-)_3yKj>6%=u3wqG_V%L| zW+*>-k1v%}1p5rv=<(~oMOK&M>n!WbnR|=v>A!Ao_wCz%7t7k~2L>E}MW zU-tLt*TV)2?hgkN#L?D+S^c*pPT6x!;VuFrNf?nPo1+n@0oeK?XC7;0CKXLGv8u9y zW_T7P)RjrV%VT6j#ff`C%MTXh0(bI?c?@-OK6~HB)#o>|6(UYxM!kVV_D*IXOJPeO zE3(xxyi6_wz3=s&+~9=^na5yfiYUi%^~bV%m=indbIj~JCBTZ{C-O1`I3yCg8kAdj z88VrupN}>P2?PD*#^!$61Zxtfj zPk-yp_rCn|E;qliZ9(Ga>SK9SpNC550TK#u8 z$i>99k|w1i+vHl4Us`wTz*&)|;o^c9IbKI25a)S`@&(Ay_I8b2tb+bkue|kkQ00@? zEzgJ;G?u&;RoFb(`?v&vZN3bd>tT^-%7b#8VdQW#-Pwr!%90(5A2Gyvo1WQsi$y;3 zD>Ag%PLSAj&}KINF(_Tbgqr=I!XN8a|S>^D5#TS?qt#NRcA|1we&kS%iw?cOZ-aT!kFQg;R}(#7Gpq{nk1T z;Y{7IoZn=8o_P5Ypj?&u^o<31%`yh8W0sxdX9T3tl@KwKdS%y#u$M;GkFJ%y1OZsT z$IshM2e|)f3Ok-x2b$_nT&*=~*+pf?&;t1q?fZ7xfyr3Q?Z)KVw`Xn9gI_zjCj>hB zUv4WZICL_W*{S>cMcUj<+r zPaUOeV|0&bF(1rk4?(`W(?WeLyZ05Ke)uP6tDxQ4)m!#sQ)?n$aTuxFBAdOJ??8U$ zo(Af@O`3-{JKmN(bs8Q*o~R$B??Jwzju{E^B1t;+rZvJxXVbwx;r0)IRAqes?(ooI z54CO_qQ19;`*Cf#>Ziw-I3{}xgX#6?XRRD z@4kQlQLN7WC%UT^)woK)%}Hivc+e}LVIgr!Nevsu{BrEx04w# zn1~&1cp{q;+sT$S1$MpdYDBE*dpytiK@KigC^8uz4pG?^4D)D*ql0leb6RZ~Le}ht z0!JiBmaEz9KwQt1Wz->S(mgqB!+<`0g)oyQvkUNFT71Ade{u*5ugGeyYsAaPo1uIK zF~iU|Ca$u^bCNYbRcauK?Ia#J`{H6I(Z0d5K9OUA-BYlT;v=D~? zB6XPtvk>)p-!6aq10R0-56*tOL}d4S_g??t(>MQm+b+*RgMwrhLVAv{HRo@*cl>`y z4s3hbz{rnmzAd^=oKQq|9a*E^B;w=Gs{a}UR_;md1fUpZ6GfLx-+yQIRk_>+!muK6 z5rx|KGr)UXg?UVfQFa#YX{3OjPan_gOj3Bo1eoY1phh;_m9)!fgA=KSEok)7&w8{fe=hyfxE6_t7Op1I(2pcet~J-coL@$8jvr69(B+`BXPjIndYmsj zw6t>Kl<5TZg~TZ{=YdR`{xh94-j>ZD6cqcPzaXMD!1AfUa)&ptu`!nm3^hb#-;c?q zJ!5@EUm5v*@S~BvTx;J|SFvev3V%TGRx^s%01A)^aG&wEYs~6;0!nepkw~vznNj5M zCJu}tFo{#(;q|KX!%C>5MPf_jSY`SYh%-Jk*s|`?I5j~=8&{l$;>>z-~C5VRG*+JJL43dW+Gqv~ZU}MAGF|muJcQI2XxJ zH+6q_D$QQPQQtC7ICM47-nR(VNjF>MkSq=k=cQTd8W=@2_ zo!%01EDybaxd{@*YoIJ!MeT&F`J((H*%=VC6yzD4=e3yXKvsmUg*s->9Y#8u$cyDS zJlt5xagW!dQUfAMuxL#e0homwmdez5;i1E|#oNlFbWAX-4@?#p!+U*?CNz;f?BS&R zX7S?)fAwrY54A*W^+I7@AAA1(;^ZnWkmtG5f_>XQec5l{_To$5@kuGzHHTcmf-uIO1A?- z>c%DK7Yzur^!0oDnXqL=*qYu)S8sLXL(%jDaHE%f2>&wstOtnX+!~wDYGl=-$9EPp z-1<)5JiL?=6w^P>J}b@P`wZRl95^euV4yb!oNqTzG8DdDCckvx3|x-u`gj{Q)cpZ# zS-5(Ofs_we6#dO#ds_Q-z!*bL115D3rt6o$)xt-OPacec{2HnB&w#6pvVDGG(A#we z()7k#)K26~jsw6;^1_RYXrI`w*rN9BvJU$Aa0kMOjk9kKiAlCk%6CM(d?*JUn9jPM z24yW#zKXa;o5sajlpnOqa~{YqUj)>+C(1#NZ@O8ruIuN~_ul3ke(~SbcE8BT#)Yr< z<6;*~kd;j%=~nW0PEze8j}P`+wHrWWpbm2X>}9)r_xoRZ{-4Y5lRxH2Gic@IuYT^o zzg)JT+P2FL|Evp30<Jt(E6l-mn!8@Gz^* zG}{Pz;;nHX#zY;dhmZJD07rfg^T&D3O8|sDjt|-Q9%(kP-(I%MyWjue=YJ++ z3u6{>_Ni^#9=`dl-?wl3KiId+8<6X$%p(OXumUGw*eVv!_;E^d0NyHQWtM{&oe^wu z=wp1>WFBobWCX(x&slV569lGBX2;6Skp_^7EyE)>o0k?m)gIKf^-h_BfYxAm{I9mp z1V7sMn8-#aT_E`Gp_ThKkKqz&VnC5#(cM1*5V37&qG5u!e(JuAHvy`I9G8AioQ~&q z++00S&A_l7@R`YvzwdH#q|yj{t*PPKa1Z>WdO}ZBMV4GIHmU$d^38No?J?S-*me!q zgsp+T>4b2h&XvDcHJ$0=&C82O(QvWh;IZ^?Han|j)$Pt~o`~1klAItb;}}7{5VkzA zt#LCukmuqIlj9aXz5S_7Wt3$dCoHHjuIdH0uxa<0>L(t^L!HQaSQBF)%Vo|!*^uDM zzJK;|x%|L2B-WW>gp-xO+?5&Zm+c>I+xCU!_Zg6iuAEoFJ)*5+ru{IW?2rm;$v;Gj zp8|a@9-WcR`)&lN2k#MSqj9y5jdrM$=3e%mV4w~?3V`MLWDpP($gs8P3BBa^q_zm3 zZT;J#r}J~xa}!z@00r{?{eS?8TGSp)7RX{FYp^;6I~};vRRn)fKN|WaT{e-Quy326 z+kP6^XgB22S+j3=Waz)~70+G0EshNOm!yfwOAVmWJj8b7#n*P{Cz6l02B5DFCuss# zokrAPu6aaZTvll zvlCf!o1?{&(LKLKCvjJ0Wf7&wOuncxqUN7N`MLPlWw3V7ubOwWH^A;C-P2eHhlq~& zFuf;P&&$SF44>th62TslXEpTo_7o};^6iFulttDT!g8?1I+%qzduCy=GtKK`oemu2g>nx%esSj~5Z^Bw53# zqjge=Z2U6oo91_dkiu}tjza%Q#sIDG|s8R}9Ws9>TMKErfo zCz3x02R9h>C-t$q)PTt_W%gwh@8O$`!!OC)w*kJ#o>si|xH# z_cuq(Y(8W8#Y^`oE-cO~5HAk-3wOq{EA!aGfhX~nI<^CZ6PDjhc6Ab?#YM1bG?H}3 zf$U-|zxnHzS*$cTk_X! z+h5-H{T*ixb}69E5Gx@{`p>UC^UYuR=yw13-959NF9n2BWU&Uq+AOg44A9E*;~+1Q zG-cc8CVAXG=cwXJ)sp^~YC&NlqO1UyCn(h3FCA>b$dl+N%j8vZLgOlpxM)u>apHHqOYUd_;mZ^)t$cvGKwfa~Wc5LT~ z07YL@BtU;aTuVPlILlZ)@p3iE-Bg*#>bo1gEnbsRL^+59W#~HTX>PUH0KXiSZH&qa z7%0V7dAtV`b<{~owv}54I+LFyoCG<1_^$hJ|J{7?7Q!DLOlo#%;kzh_b#VMmk|dK) zv_(8bRK{QJGs`GSLg(UCYSVmY?5r$hN|jgE&l4A47c$7CmUMao6S%8JE7VKOFT-HQ=1hzD6fyd{iTQyzA%kcmB)UFZrRBmmPRK z&HkMr&B~+tH=labTfcX^xqNKjwl}6^7RW=aJsNKnTQ(_j5YQ40&=~?;GA~3RfWCoJ zIqaq84pA}u;4p#}($tw2tsXlnW-80d3N!&J1Jq-dq-{#1g&xhzq_N}~*=%eEzFkZw z7KXmTrP>NR{xT2 zG$=z+1j(k!WX1tV|EJ!)JHyC6({!5SEu`0^+BMD}ytC7KAS&P+;yM(_93BaI{wbk;->a zhQT(@{oxUOl$v0U9DEY0tiWbA5Tf8mzGrQqou!!=+RV)BieQzGmX8nlx0MI|* zvEld{%M)BY_XItS7u9|}3|uY`9_{;ke)OdmepZr#EC2Y!iR|4te(TNmzWnvu>woOB zZ-0K@wt7Vub>+N7@`;2@7BXmC=oWl465A+GHDX6%&tCd}3@IBIdbv|a*=ErMLCK^% znje63)(J|tatoU=k;}jwV*irF2I7Rz<%BKMiDlxCxh{;WknUzFv?e&A6YOeaGj-Aq zmEqZLv^cJ=l4_^A1g1?TE4!%n-PUYJqnWKlxl{v+&v<(g8Eha9nNGl24H()e)^=jV zA`Mxd2xkt2T;P->$^!@!Lr;t6`_NJGx1J*3+W|3@GTwV7Z(fp9YTSZYn8(lqni!=-cBqq5^C%YvlMLJ8#!E!jWEK`ahdmg{#fhR$#$?T8fBegh ztF)di3=(^&GK+TV&`!AAw01ZMzyVqx_rSG@1MLIiX}MtVQkl;_zs$ayhQvXu_DEh@Kz;J$$e`}XS2w97!x+|8$-yC0?TkFQMa1z>L3C3v#w)8DS@G)Q?2EaK zAh%xBlT+id7BimaKKcUH>Z{ATZzfygrW2FTaB(rJH=b;b$9phQC(=Hswsnn6Bt+Jc z7k2;IHARuA#=}Y|%SCyLMHSmO%O%rUdQgtTLv|o9-60t-)>tR89y#8oDuwL5cS7J{4NMWBx}Z_$efXGoL^R= z;z$t=bWmDAw%?i_k!WIbVBgMRG2UqZYJLRs8|tJ)-!Q#_|A4JJabpcv1S!l^3INH# zwyVe536t6CWLqsfJpLwrj0?Rb;B!9PG4mYQ^#%*5a+N?7@ZwUX{0Kt z*cTS5X!FWX&fb$8wmdu~4JfMR9i9Rli^XT|yHr)={V*6!pg7N7+F++8VcZPZBSZU#yQs zOi}WbHyD7+&t6bLvCfkuPcVnJSjV|aps8JuAB4-i{38<3`MshmcglvtjpBv^41HRD zOfUoC6a$_rB!O&FBA_ZQH+p+qQ4px9xT9FqeJ} zYu$F2+xyPb03k_1u)Qy)FNkW>NZWRe0PF%=P+v9l&V;>ah)nf{lkM{?E9%A^yICtM z@I>KkEoi%XE0v3sNge5k$Rc_jLxlOIi;6m%M3=pMA{MDx53cV<@?d^bvPfGo;1T2P z^P?XjvnT)pR4Da`-`VrJZn&2fNJU2f>g3nY?NE@F$8aE6x`$ark6t(I0yzT;lBhEO zJi9^}!`d#`SF&x`%aa1RFv6rjs{CyLC?ZoDPY%0U?XD7WpJtl~lRG!hL(Hy?L+vR+ zo)E8A>-zr}clEka#6Wm_Q3MSUiqg|@9W4bCQjln9cpNBs4hkx$cn6S>cmcXgM;$-{ zB*ahdjI!&sXTJII-YLqJdwX7cXTBMKGah?|d(%X=!h>~=IugWIWX*eZee#&RIb|7E zeQfxdPD6ge4hkmf1~X0$!R*81&`dq`Pb`q~g$+B1J+vi;=K-ty8i{OlKMFL=>~;a7 zNto>H_8?!eA_7<&zOv20T=gO`FDnQQ`G&Rb#m7vTkIG&E2h~4d(4=&N1XKETYvY$$ z{(;$TVf%V@2{A4G@A$$wr3<3;F>mPg!9jZa{N>~ChU>nTpY}>*`5S&czJIj2y?98A zbef4CrA+rSrE=nd@QwqrO9PUQG_!i9g11S|th4he6sJ*uACb zYC&pal0;8u30)BlSsaf?*rvZbWVSvnB(v?kWjCJQ!MdlosU|t+ z|{csiOVbjz*^R#f0uwFndw~f2^X?cy?R+$(4`Ee~wwbMy3+wFIF=O+(t{X3%N zA<>eE?oy&1JAqx28%FXtDVmZ+G87yC;gHGNJi}%mA)*智能体 消息 我的 + 我的 + 热门 + 推荐 + 其他 + 创建Ai智能体 + 名称 + 请输入名称 + 设定描述 + 示例: 一位经验丰富的销售员,擅长通过幽默风趣的语言和生动的案例,将复杂的产品转化为客户易于理解并感兴趣的话题 + 创建智能体 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0cf10ce..adea928 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -114,4 +114,14 @@ Agent Message Profile + Mine + Hot + Recommend + Other + 创建Ai智能体 + 名称 + 请输入名称 + 设定描述 + 示例: 一位经验丰富的销售员,擅长通过幽默风趣的语言和生动的案例,将复杂的产品转化为客户易于理解并感兴趣的话题 + 创建智能体 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1863eb9..000b27a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ accompanistSystemuicontroller = "0.27.0" agp = "8.4.0" animation = "1.7.0-beta05" composeImageBlurhash = "3.0.2" -kotlin = "1.9.0" +kotlin = "1.9.10" coreKtx = "1.10.1" junit = "4.13.2" junitVersion = "1.1.5"