refactor: 优化部分代码

This commit is contained in:
糕小菜 2025-02-12 20:23:38 +08:00
parent da1fc07599
commit 2e9e115c7e
50 changed files with 650 additions and 322 deletions

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
KChat-Android

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" /> <bytecodeTargetLevel target="21" />
</component> </component>
</project> </project>

View File

@ -4,6 +4,7 @@
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" /> <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules"> <option name="modules">

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@ -5,8 +5,12 @@
<set> <set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" /> <option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" /> <option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" /> <option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" /> <option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set> </set>
</option> </option>
</component> </component>

View File

@ -20,15 +20,15 @@
- [x] **搜索功能**:支持通过关键词在聊天记录中搜索消息,同时支持按文件类型筛选。 - [x] **搜索功能**:支持通过关键词在聊天记录中搜索消息,同时支持按文件类型筛选。
- [x] **修改个人信息**:用户可以修改昵称、头像、个性签名等个人信息,提升个性化体验。 - [x] **修改个人信息**:用户可以修改昵称、头像、个性签名等个人信息,提升个性化体验。
- [x] **修改用户密码**:用户可以更新自己的密码。 - [x] **修改用户密码**:用户可以更新自己的密码。
- [x] **离线消息**:即使用户离线,服务器也会缓存消息,并在用户上线后自动同步到设备。
- [ ] **查看好友动态**:支持查看好友分享的动态内容,形式包括文字、图片和视频,用户可以点赞或评论。 - [ ] **查看好友动态**:支持查看好友分享的动态内容,形式包括文字、图片和视频,用户可以点赞或评论。
- [ ] **支持多媒体消息**:支持发送音频、视频、文件等多种格式,满足用户不同的交流需求。 - [ ] **支持多媒体消息**:支持发送音频、视频、文件等多种格式,满足用户不同的交流需求。
- [ ] **群聊支持**:用户可以创建群组,与多人实时互动,并支持群管理功能(如设置群管理员、踢人)。 - [ ] **群聊支持**:用户可以创建群组,与多人实时互动,并支持群管理功能(如设置群管理员、踢人)。
- [ ] **消息通知**:支持推送通知,提醒用户有新的消息,未读消息可在通知栏中展示摘要。 - [ ] **消息通知**:支持推送通知,提醒用户有新的消息,未读消息可在通知栏中展示摘要。
- [ ] **离线消息**:即使用户离线,服务器也会缓存消息,并在用户上线后自动同步到设备。
### 其他特性 ### 其他特性
- [ ] **消息加密**:所有用户消息在传输过程中都进行端到端加密,确保通信安全,防止信息泄露。 - [x] **消息加密**:所有用户消息在传输过程中都进行端到端加密,确保通信安全,防止信息泄露。
- [ ] **个性化设置**:支持用户自定义头像、昵称、聊天背景,提供多种预设主题和自定义选项。 - [ ] **个性化设置**:支持用户自定义头像、昵称、聊天背景,提供多种预设主题和自定义选项。
- [ ] **深色/浅色模式**:支持自动或手动切换界面主题,适配不同的光线环境和用户偏好。 - [ ] **深色/浅色模式**:支持自动或手动切换界面主题,适配不同的光线环境和用户偏好。

View File

@ -0,0 +1,19 @@
package com.kaixed.kchat.extensions
import android.content.Intent
import android.os.Build
import android.os.Parcelable
/**
* @Author: kaixed
* @Date: 2025/1/26 19:41
*/
// 扩展函数,用于兼容不同版本的 Intent.getParcelableExtra 方法
inline fun <reified T : Parcelable> Intent.getParcelableExtraCompat(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelableExtra(key, T::class.java)
} else {
@Suppress("DEPRECATION")
getParcelableExtra(key)
}
}

View File

@ -13,22 +13,43 @@ object NetworkInterface {
const val SERVER_URL = "http://$URL" const val SERVER_URL = "http://$URL"
const val WEBSOCKET_SERVER_URL = "ws://$URL" const val WEBSOCKET_SERVER_URL = "ws://$URL"
const val WEBSOCKET = "/websocket/single/" const val WEBSOCKET = "/websocket/single/"
const val USER_INFO = "/users/info/"
const val USER_LOGIN_BY_USERNAME = "/users/login/username"
const val USER_LOGIN_BY_TELEPHONE = "/users/login/telephone"
const val USER_REGISTER = "/users/register"
const val USER_MESSAGES_COUNT = "/users/%s/%s/msgCounts" // 获取access-token
const val USER_MESSAGES = "/users/%s/%s/messages" const val ACCESS_TOKEN = "auth/access-token"
const val MESSAGE_WITHDRAW = "/messages/" // 获取refresh-token
const val REFRESH_TOKEN = "auth/refresh-token"
// 登录接口(根据用户名登录)
const val LOGIN_BY_USERNAME = "auth/login/username"
// 登录接口(根据电话登录)
const val LOGIN_BY_TELEPHONE = "auth/login/telephone"
// 注册接口
const val REGISTER = "auth/register"
const val USER_LIST = "/users/list/" // 上传文件
const val UPLOAD_FILE = "file/upload"
const val ADD_FRIEND = "/friend/request" // 更新用户密码
const val FRIEND_LIST = "/friend/list" const val UPDATE_PASSWORD = "user/password/update"
const val FRIEND_REQUEST_LIST = "/friend/request/list" // 获取用户信息
const val ACCEPT_CONTACT_REQUEST = "/friend/accept" const val GET_USER_INFO = "user/info/fetch"
// 更新用户信息
const val UPDATE_USER_INFO = "user/info/update"
// 上传用户头像
const val UPLOAD_AVATAR = "user/upload-avatar"
const val UPDATE_USER_INFO = "/users/info" // 发送好友请求
const val UPLOAD_AVATAR = "/users/avatar" const val SEND_FRIEND_REQUEST = "friends/requests/send"
// 接受好友请求
const val ACCEPT_FRIEND_REQUEST = "friends/requests/accept"
// 获取好友列表
const val GET_FRIENDS = "friends/list"
// 获取好友请求列表
const val GET_FRIEND_LIST = "friends/requests/fetch"
// 删除好友
const val DELETE_FRIEND = "friends/delete-friend"
// 设置好友备注
const val SET_FRIEND_REMARK = "friends/set-remark"
//撤回消息
const val RECALL_MESSAGE = "message/withdraw"
} }

View File

@ -4,6 +4,11 @@ import com.kaixed.kchat.data.local.entity.UserInfo
import com.kaixed.kchat.data.model.request.RegisterRequest import com.kaixed.kchat.data.model.request.RegisterRequest
import com.kaixed.kchat.data.model.response.register.Register import com.kaixed.kchat.data.model.response.register.Register
import com.kaixed.kchat.network.ApiResponse import com.kaixed.kchat.network.ApiResponse
import com.kaixed.kchat.network.NetworkInterface.ACCESS_TOKEN
import com.kaixed.kchat.network.NetworkInterface.LOGIN_BY_TELEPHONE
import com.kaixed.kchat.network.NetworkInterface.LOGIN_BY_USERNAME
import com.kaixed.kchat.network.NetworkInterface.REFRESH_TOKEN
import com.kaixed.kchat.network.NetworkInterface.REGISTER
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.Header import retrofit2.http.Header
import retrofit2.http.POST import retrofit2.http.POST
@ -15,32 +20,32 @@ import retrofit2.http.Query
*/ */
interface AuthApiService { interface AuthApiService {
@POST("auth/access-token") @POST(ACCESS_TOKEN)
suspend fun auth( suspend fun auth(
@Header("Authorization") token: String, @Header("Authorization") token: String,
@Query("username") username: String @Query("username") username: String
): ApiResponse<String> ): ApiResponse<String>
@POST("auth/refresh-token") @POST(REFRESH_TOKEN)
suspend fun refresh( suspend fun refresh(
@Header("Authorization") token: String, @Header("Authorization") token: String,
@Query("username") username: String @Query("username") username: String
): ApiResponse<String> ): ApiResponse<String>
// 登录接口(根据用户名登录) // 登录接口(根据用户名登录)
@POST("auth/login/username") @POST(LOGIN_BY_USERNAME)
suspend fun loginByUsername( suspend fun loginByUsername(
@Body requestParams: Map<String, String>, @Body requestParams: Map<String, String>,
): ApiResponse<UserInfo?> ): ApiResponse<UserInfo?>
// 登录接口(根据电话登录) // 登录接口(根据电话登录)
@POST("auth/login/telephone") @POST(LOGIN_BY_TELEPHONE)
suspend fun loginByTelephone( suspend fun loginByTelephone(
@Body requestParams: Map<String, String>, @Body requestParams: Map<String, String>,
): ApiResponse<UserInfo> ): ApiResponse<UserInfo>
// 注册接口 // 注册接口
@POST("auth/register") @POST(REGISTER)
suspend fun register( suspend fun register(
@Body registerRequest: RegisterRequest @Body registerRequest: RegisterRequest
): ApiResponse<Register> ): ApiResponse<Register>

View File

@ -1,6 +1,7 @@
package com.kaixed.kchat.network.service package com.kaixed.kchat.network.service
import com.kaixed.kchat.network.ApiResponse import com.kaixed.kchat.network.ApiResponse
import com.kaixed.kchat.network.NetworkInterface.UPLOAD_FILE
import okhttp3.MultipartBody import okhttp3.MultipartBody
import retrofit2.http.Multipart import retrofit2.http.Multipart
import retrofit2.http.POST import retrofit2.http.POST
@ -13,7 +14,7 @@ import retrofit2.http.Part
interface FileApiService { interface FileApiService {
@Multipart @Multipart
@POST("file/upload") @POST(UPLOAD_FILE)
suspend fun uploadFile( suspend fun uploadFile(
@Part file: MultipartBody.Part @Part file: MultipartBody.Part
): ApiResponse<String> ): ApiResponse<String>

View File

@ -4,6 +4,11 @@ import com.kaixed.kchat.data.local.entity.Contact
import com.kaixed.kchat.data.model.friend.FriendRequestItem import com.kaixed.kchat.data.model.friend.FriendRequestItem
import com.kaixed.kchat.data.model.search.SearchUser import com.kaixed.kchat.data.model.search.SearchUser
import com.kaixed.kchat.network.ApiResponse import com.kaixed.kchat.network.ApiResponse
import com.kaixed.kchat.network.NetworkInterface.ACCEPT_FRIEND_REQUEST
import com.kaixed.kchat.network.NetworkInterface.DELETE_FRIEND
import com.kaixed.kchat.network.NetworkInterface.GET_FRIEND_LIST
import com.kaixed.kchat.network.NetworkInterface.SEND_FRIEND_REQUEST
import com.kaixed.kchat.network.NetworkInterface.SET_FRIEND_REMARK
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.Field import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded import retrofit2.http.FormUrlEncoded
@ -16,16 +21,16 @@ import retrofit2.http.Path
* @Date: 2025/1/25 16:38 * @Date: 2025/1/25 16:38
*/ */
interface FriendApiService { interface FriendApiService {
// 获取联系人请求列表 // 获取好友请求列表
@FormUrlEncoded @FormUrlEncoded
@POST("friend/request/list") @POST(GET_FRIEND_LIST)
suspend fun getContactRequestList( suspend fun getContactRequestList(
@Field("userId") username: String @Field("userId") username: String
): ApiResponse<List<FriendRequestItem>?> ): ApiResponse<List<FriendRequestItem>?>
// 接受联系人请求 // 接受好友请求
@FormUrlEncoded @FormUrlEncoded
@POST("friend/accept") @POST(ACCEPT_FRIEND_REQUEST)
suspend fun acceptContactRequest( suspend fun acceptContactRequest(
@Field("requestId") contactId: String, @Field("requestId") contactId: String,
@Field("receiverId") username: String, @Field("receiverId") username: String,
@ -34,7 +39,7 @@ interface FriendApiService {
// 添加联系人 // 添加联系人
@FormUrlEncoded @FormUrlEncoded
@POST("friend/request") @POST(SEND_FRIEND_REQUEST)
suspend fun addContact( suspend fun addContact(
@Field("senderId") senderId: String, @Field("senderId") senderId: String,
@Field("receiverId") receiverId: String, @Field("receiverId") receiverId: String,
@ -54,7 +59,7 @@ interface FriendApiService {
): ApiResponse<List<Contact>?> ): ApiResponse<List<Contact>?>
// 删除联系人 // 删除联系人
@POST("friends/delete") @POST(DELETE_FRIEND)
suspend fun deleteContact( suspend fun deleteContact(
@Field("userId") username: String, @Field("userId") username: String,
@Field("contactId") contactId: String, @Field("contactId") contactId: String,
@ -62,7 +67,7 @@ interface FriendApiService {
// 设置好友备注 // 设置好友备注
@FormUrlEncoded @FormUrlEncoded
@POST("friend/remark") @POST(SET_FRIEND_REMARK)
suspend fun setRemark( suspend fun setRemark(
@Field("userId") userId: String, @Field("userId") userId: String,
@Field("contactId") contactId: String, @Field("contactId") contactId: String,

View File

@ -5,6 +5,11 @@ import com.kaixed.kchat.data.model.request.UserRequest
import com.kaixed.kchat.data.model.response.search.User import com.kaixed.kchat.data.model.response.search.User
import com.kaixed.kchat.data.model.search.SearchUser import com.kaixed.kchat.data.model.search.SearchUser
import com.kaixed.kchat.network.ApiResponse import com.kaixed.kchat.network.ApiResponse
import com.kaixed.kchat.network.NetworkInterface.GET_FRIENDS
import com.kaixed.kchat.network.NetworkInterface.GET_USER_INFO
import com.kaixed.kchat.network.NetworkInterface.UPDATE_PASSWORD
import com.kaixed.kchat.network.NetworkInterface.UPDATE_USER_INFO
import com.kaixed.kchat.network.NetworkInterface.UPLOAD_AVATAR
import okhttp3.MultipartBody import okhttp3.MultipartBody
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.Multipart import retrofit2.http.Multipart
@ -18,33 +23,33 @@ import retrofit2.http.Part
interface UserApiService { interface UserApiService {
// 获取用户列表 // 获取用户列表
@POST("friends/list") @POST(GET_FRIENDS)
suspend fun fetchUserList( suspend fun fetchUserList(
@Body requestParams: Map<String, String> @Body requestParams: Map<String, String>
): ApiResponse<List<User>> ): ApiResponse<List<User>>
// 获取用户信息 // 获取用户信息
@POST("users/info/fetch") @POST(GET_USER_INFO)
suspend fun getUserInfo( suspend fun getUserInfo(
@Body requestParams: Map<String, String> @Body requestParams: Map<String, String>
): ApiResponse<SearchUser> ): ApiResponse<SearchUser>
// 更改昵称接口 // 更改昵称接口
@POST("users/info") @POST(UPDATE_USER_INFO)
suspend fun changeNickname( suspend fun changeNickname(
@Body userRequest: UserRequest @Body userRequest: UserRequest
): ApiResponse<Boolean?> ): ApiResponse<Boolean?>
// 上传头像接口 // 上传头像接口
@Multipart @Multipart
@POST("users/avatar") @POST(UPLOAD_AVATAR)
suspend fun uploadAvatar( suspend fun uploadAvatar(
@Part("username") username: String, @Part("username") username: String,
@Part file: MultipartBody.Part @Part file: MultipartBody.Part
): ApiResponse<String> ): ApiResponse<String>
// 更改密码 // 更改密码
@POST("users/password") @POST(UPDATE_PASSWORD)
suspend fun updatePassword( suspend fun updatePassword(
@Body updatePasswordRequest: UpdatePasswordRequest @Body updatePasswordRequest: UpdatePasswordRequest
): ApiResponse<String> ): ApiResponse<String>

View File

@ -7,6 +7,13 @@ import androidx.activity.enableEdgeToEdge
import com.kaixed.kchat.databinding.ActivityAddFriendsBinding import com.kaixed.kchat.databinding.ActivityAddFriendsBinding
import com.kaixed.kchat.ui.base.BaseActivity import com.kaixed.kchat.ui.base.BaseActivity
/**
* 该文件包含用于兼容不同 Android 版本的 Intent 获取 Parcelable 数据的扩展函数
* 通过扩展函数 getParcelableExtraCompat 解决了低版本和高版本 Android 系统间的差异
*
* @author kaixed
* @since 2025年1月26日
*/
class AddFriendsActivity : BaseActivity<ActivityAddFriendsBinding>() { class AddFriendsActivity : BaseActivity<ActivityAddFriendsBinding>() {
private val context: Context by lazy { this } private val context: Context by lazy { this }
@ -18,15 +25,21 @@ class AddFriendsActivity : BaseActivity<ActivityAddFriendsBinding>() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
initView()
} }
override fun initData() { override fun initData() {
} }
private fun initView() { override fun observeData() {
}
override fun setupListeners() {
}
override fun initView() {
binding.ivBack.setOnClickListener { binding.ivBack.setOnClickListener {
finish() finish()
} }

View File

@ -21,11 +21,13 @@ class ApplyAddFriendActivity : BaseActivity<ActivityApplyAddFriendBinding>() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
setListener()
observeData()
} }
private fun observeData() { override fun initView() {
}
override fun observeData() {
contactViewModel.addContactResult contactViewModel.addContactResult
.observe(this) { result -> .observe(this) { result ->
result.onSuccess { result.onSuccess {
@ -36,7 +38,7 @@ class ApplyAddFriendActivity : BaseActivity<ActivityApplyAddFriendBinding>() {
} }
} }
private fun setListener() { override fun setupListeners() {
binding.tvSendApply.setOnClickListener { binding.tvSendApply.setOnClickListener {
sendContactRequest(contactId) sendContactRequest(contactId)
} }

View File

@ -12,26 +12,14 @@ import com.kaixed.kchat.ui.base.BaseActivity
class ApplyFriendsDetailActivity : BaseActivity<ActivityApplyFriendsDetailBinding>() { class ApplyFriendsDetailActivity : BaseActivity<ActivityApplyFriendsDetailBinding>() {
override fun inflateBinding(): ActivityApplyFriendsDetailBinding {
return ActivityApplyFriendsDetailBinding.inflate(layoutInflater)
}
private var user: SearchUser? = null private var user: SearchUser? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
setContent()
setOnClick()
} }
@RequiresApi(Build.VERSION_CODES.TIRAMISU) override fun initView() {
override fun initData() {
user = intent.getParcelableExtra("user", SearchUser::class.java)
}
private fun setContent() {
val nickname = user?.nickname val nickname = user?.nickname
val signature = user?.signature val signature = user?.signature
val avatarUrl = user?.avatarUrl val avatarUrl = user?.avatarUrl
@ -43,7 +31,12 @@ class ApplyFriendsDetailActivity : BaseActivity<ActivityApplyFriendsDetailBindin
Glide.with(this).load(avatarUrl).into(binding.ifvAvatar) Glide.with(this).load(avatarUrl).into(binding.ifvAvatar)
} }
private fun setOnClick() { @RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun initData() {
user = intent.getParcelableExtra("user", SearchUser::class.java)
}
override fun setupListeners() {
binding.tvAddContact.setOnClickListener { binding.tvAddContact.setOnClickListener {
val contactId = user?.username val contactId = user?.username
contactId?.let { contactId?.let {
@ -60,4 +53,12 @@ class ApplyFriendsDetailActivity : BaseActivity<ActivityApplyFriendsDetailBindin
startActivity(intent) startActivity(intent)
} }
} }
override fun observeData() {
}
override fun inflateBinding(): ActivityApplyFriendsDetailBinding {
return ActivityApplyFriendsDetailBinding.inflate(layoutInflater)
}
} }

View File

@ -28,11 +28,6 @@ class ApproveContactRequestActivity : BaseActivity<ActivityApproveContactRequest
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
setContent()
setOnClickListener()
} }
override fun initData() { override fun initData() {
@ -41,18 +36,22 @@ class ApproveContactRequestActivity : BaseActivity<ActivityApproveContactRequest
contactId = intent.getStringExtra("contactId").toString() contactId = intent.getStringExtra("contactId").toString()
} }
@SuppressLint("SetTextI18n") override fun observeData() {
private fun setContent() {
binding.etRemark.setText(nickname)
binding.tvMessage.text = "$message"
} }
private fun setOnClickListener() { override fun setupListeners() {
binding.tvFinish.setOnClickListener { binding.tvFinish.setOnClickListener {
acceptContactRequest(contactId!!, nickname!!) acceptContactRequest(contactId, nickname)
} }
} }
@SuppressLint("SetTextI18n")
override fun initView() {
binding.etRemark.setText(nickname)
binding.tvMessage.text = "$message"
}
private fun acceptContactRequest(contactId: String, nickname: String) { private fun acceptContactRequest(contactId: String, nickname: String) {
contactViewModel.acceptContactRequestResult contactViewModel.acceptContactRequestResult
.observe(this) { result -> .observe(this) { result ->

View File

@ -1,63 +1,45 @@
package com.kaixed.kchat.ui.activity package com.kaixed.kchat.ui.activity
import android.content.Intent import android.content.Intent
import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.kaixed.kchat.databinding.ActivityApproveDetailBinding
import com.kaixed.kchat.data.model.friend.FriendRequestItem import com.kaixed.kchat.data.model.friend.FriendRequestItem
import com.kaixed.kchat.databinding.ActivityApproveDetailBinding
import com.kaixed.kchat.extensions.getParcelableExtraCompat
import com.kaixed.kchat.ui.base.BaseActivity import com.kaixed.kchat.ui.base.BaseActivity
class ApproveDetailActivity : BaseActivity<ActivityApproveDetailBinding>() { class ApproveDetailActivity : BaseActivity<ActivityApproveDetailBinding>() {
private var request: FriendRequestItem? = null private var request: FriendRequestItem? = null
override fun inflateBinding(): ActivityApproveDetailBinding {
return ActivityApproveDetailBinding.inflate(layoutInflater)
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
handleIntent(intent)
setContent()
setOnClick()
} }
override fun initData() { override fun initData() {
request = intent.getParcelableExtraCompat("request")
}
override fun observeData() {
} }
private fun handleIntent(intent: Intent) { override fun initView() {
request = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { request?.let {
intent.getParcelableExtra("request", FriendRequestItem::class.java) val nickname = it.nickname
} else { val signature = it.signature
@Suppress("DEPRECATION") val avatarUrl = it.avatarUrl
intent.getParcelableExtra("request") as? FriendRequestItem
binding.tvContactName.text = nickname
binding.tvContactSignature.text = signature
binding.tvSignatureContent.text = signature
Glide.with(this).load(avatarUrl).into(binding.ifvAvatar)
} }
} }
private fun setContent() { override fun setupListeners() {
if (request == null) {
return
}
val nickname = request?.nickname
val signature = request?.signature
val avatarUrl = request?.avatarUrl
binding.tvContactName.text = nickname
binding.tvContactSignature.text = signature
binding.tvSignatureContent.text = signature
Glide.with(this).load(avatarUrl).into(binding.ifvAvatar)
}
private fun setOnClick() {
binding.ctl.setOnSettingClickListener { binding.ctl.setOnSettingClickListener {
val intent = Intent(this, DataSettingActivity::class.java) val intent = Intent(this, DataSettingActivity::class.java)
startActivity(intent) startActivity(intent)
@ -73,4 +55,8 @@ class ApproveDetailActivity : BaseActivity<ActivityApproveDetailBinding>() {
) )
} }
} }
override fun inflateBinding(): ActivityApproveDetailBinding {
return ActivityApproveDetailBinding.inflate(layoutInflater)
}
} }

View File

@ -116,13 +116,9 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener,
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
EventBus.getDefault().register(this) EventBus.getDefault().register(this)
observeLiveData()
firstLoadData() firstLoadData()
initView()
setListener()
bindWebSocketService() bindWebSocketService()
setPanelChange() setPanelChange()
observeStateFlow()
if (isSearchHistory) { if (isSearchHistory) {
val size = MessagesManager.queryHistory(msgLocalId) val size = MessagesManager.queryHistory(msgLocalId)
binding.recycleChatList.smoothScrollToPosition(size - 1) binding.recycleChatList.smoothScrollToPosition(size - 1)
@ -136,17 +132,6 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener,
} }
} }
private fun observeStateFlow() {
lifecycleScope.launch {
MessagesManager.messages.collect {
chatAdapter?.submitList(it)
binding.recycleChatList.post {
binding.recycleChatList.smoothScrollToPosition(0)
}
}
}
}
private val connection: ServiceConnection = object : ServiceConnection { private val connection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) { override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as LocalBinder val binder = service as LocalBinder
@ -266,7 +251,114 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener,
} }
} }
private fun setListener() { private fun setupFunctionPanel() {
strings?.add("[委屈]")
val mEmojiAdapter = EmojiAdapter(strings)
mEmojiAdapter.setOnItemClickListener(this)
binding.rvEmoji.adapter = mEmojiAdapter
val gridLayoutManager = GridLayoutManager(this, 7)
binding.rvEmoji.layoutManager = gridLayoutManager
val map: MutableMap<String, Int> = mutableMapOf()
map["相册"] = R.drawable.ic_filled_picture
map["拍摄"] = R.drawable.ic_filled_camera
map["位置"] = R.drawable.ic_filled_location
map["名片"] = R.drawable.ic_filled_personal
map["转账"] = R.drawable.ic_filled_transfer
map["文件"] = R.drawable.ic_folder_filled
map["语音输入"] = R.drawable.ic_filled_voiceinput
map["我的收藏"] = R.drawable.ic_filled_favorites
val functionItems = mutableListOf<FunctionItem>()
for (i in map.keys) {
functionItems.add(FunctionItem(i, map[i]!!))
}
val functionPanelAdapter = FunctionPanelAdapter(functionItems, this)
functionPanelAdapter.setOnItemClickListener(this)
binding.gvFunctionPanel.adapter = functionPanelAdapter
}
override fun onItemClick(position: Int) {
runOnUiThread {
val editable = binding.etInput.text
// 获取当前光标位置
val index = binding.etInput.selectionStart
// 获取将要插入的表情符号
val emoji = strings!![position]
// 使用 ImageSpanUtil 插入表情符号
insertEmoji(
context, editable, binding.etInput.textSize.toInt(), emoji, index
)
// 设置新的光标位置
binding.etInput.setSelection(index + emoji.length)
}
}
override fun initView() {
binding.ctb.setUnReadCount(ConversationManager.unReadMsgCount)
setupFunctionPanel()
setRecycleView()
contactNickname?.let {
binding.ctb.setTitleName(contactNickname!!)
}
setPanel()
}
private fun setPanel() {
val layoutParams = binding.clBottomPanel.layoutParams
layoutParams.height = softKeyboardHeight
binding.clBottomPanel.layoutParams = layoutParams
}
override fun initData() {
contactId = intent.getStringExtra("contactId").toString()
contactNickname = intent.getStringExtra("contactNickname")
isSearchHistory = intent.getBooleanExtra("isSearchHistory", false) == true
msgLocalId = intent.getLongExtra("msgLocalId", 0)
binding.ctb.setTitleName(contactNickname!!)
softKeyboardHeight = getKeyboardHeight()
mmkv.putString(CURRENT_CONTACT_ID, contactId)
}
private fun setRecycleView() {
val layoutManager = LinearLayoutManager(this)
layoutManager.reverseLayout = true
binding.recycleChatList.layoutManager = layoutManager
chatAdapter = ChatAdapter(this)
binding.recycleChatList.adapter = chatAdapter
}
private fun firstLoadData() {
MessagesManager.setContactId(contactId)
MessagesManager.firstLoadMessages()
}
private fun loadMoreMessages() {
MessagesManager.loadMoreMessages()
}
override fun observeData() {
webSocketService!!.messageLivedata.observe(this) {
it?.let {
handleMsg(it)
}
}
lifecycleScope.launch {
MessagesManager.messages.collect {
chatAdapter?.submitList(it)
binding.recycleChatList.post {
binding.recycleChatList.smoothScrollToPosition(0)
}
}
}
}
override fun setupListeners() {
binding.etInput.setOnClickListener { binding.etInput.setOnClickListener {
if (hasSoftInput()){ if (hasSoftInput()){
MMKV.defaultMMKV().encode(KEYBOARD_HEIGHT, max(getSoftInputHeight(), 300)) MMKV.defaultMMKV().encode(KEYBOARD_HEIGHT, max(getSoftInputHeight(), 300))
@ -324,108 +416,6 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener,
} }
} }
private fun setupFunctionPanel() {
strings?.add("[委屈]")
val mEmojiAdapter = EmojiAdapter(strings)
mEmojiAdapter.setOnItemClickListener(this)
binding.rvEmoji.adapter = mEmojiAdapter
val gridLayoutManager = GridLayoutManager(this, 7)
binding.rvEmoji.layoutManager = gridLayoutManager
val map: MutableMap<String, Int> = mutableMapOf()
map["相册"] = R.drawable.ic_filled_picture
map["拍摄"] = R.drawable.ic_filled_camera
map["位置"] = R.drawable.ic_filled_location
map["名片"] = R.drawable.ic_filled_personal
map["转账"] = R.drawable.ic_filled_transfer
map["文件"] = R.drawable.ic_folder_filled
map["语音输入"] = R.drawable.ic_filled_voiceinput
map["我的收藏"] = R.drawable.ic_filled_favorites
val functionItems = mutableListOf<FunctionItem>()
for (i in map.keys) {
functionItems.add(FunctionItem(i, map[i]!!))
}
val functionPanelAdapter = FunctionPanelAdapter(functionItems, this)
functionPanelAdapter.setOnItemClickListener(this)
binding.gvFunctionPanel.adapter = functionPanelAdapter
}
override fun onItemClick(position: Int) {
runOnUiThread {
val editable = binding.etInput.text
// 获取当前光标位置
val index = binding.etInput.selectionStart
// 获取将要插入的表情符号
val emoji = strings!![position]
// 使用 ImageSpanUtil 插入表情符号
insertEmoji(
context, editable, binding.etInput.textSize.toInt(), emoji, index
)
// 设置新的光标位置
binding.etInput.setSelection(index + emoji.length)
}
}
private fun initView() {
binding.ctb.setUnReadCount(ConversationManager.unReadMsgCount)
setupFunctionPanel()
setRecycleView()
contactNickname?.let {
binding.ctb.setTitleName(contactNickname!!)
}
setPanel()
}
private fun setPanel() {
val layoutParams = binding.clBottomPanel.layoutParams
layoutParams.height = softKeyboardHeight
binding.clBottomPanel.layoutParams = layoutParams
}
override fun initData() {
contactId = intent.getStringExtra("contactId").toString()
contactNickname = intent.getStringExtra("contactNickname")
isSearchHistory = intent.getBooleanExtra("isSearchHistory", false) == true
msgLocalId = intent.getLongExtra("msgLocalId", 0)
binding.ctb.setTitleName(contactNickname!!)
softKeyboardHeight = getKeyboardHeight()
mmkv.putString(CURRENT_CONTACT_ID, contactId)
}
private fun setRecycleView() {
val layoutManager = LinearLayoutManager(this)
layoutManager.reverseLayout = true
binding.recycleChatList.layoutManager = layoutManager
chatAdapter = ChatAdapter(this)
binding.recycleChatList.adapter = chatAdapter
}
private fun firstLoadData() {
MessagesManager.setContactId(contactId)
MessagesManager.firstLoadMessages()
}
private fun loadMoreMessages() {
MessagesManager.loadMoreMessages()
}
private fun observeLiveData() {
if (webSocketService == null) {
return
}
webSocketService!!.messageLivedata.observe(this) {
it?.let {
handleMsg(it)
}
}
}
private fun handleMsg(messages: Messages) { private fun handleMsg(messages: Messages) {
MessagesManager.receiveMessage(messages) MessagesManager.receiveMessage(messages)
binding.recycleChatList.smoothScrollToPosition(0) binding.recycleChatList.smoothScrollToPosition(0)

View File

@ -25,13 +25,9 @@ class ChatDetailActivity : BaseActivity<ActivityChatDetailBinding>() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
initView()
setListener()
} }
private fun initView() { override fun initView() {
Glide.with(this).load(contact?.avatarUrl).into(binding.ifvAvatar) Glide.with(this).load(contact?.avatarUrl).into(binding.ifvAvatar)
binding.tvContactName.text = contact?.remark ?: contact?.nickname binding.tvContactName.text = contact?.remark ?: contact?.nickname
} }
@ -41,7 +37,11 @@ class ChatDetailActivity : BaseActivity<ActivityChatDetailBinding>() {
binding.ciSetDisturb.setSwitchChecked(contact?.doNotDisturb ?: false) binding.ciSetDisturb.setSwitchChecked(contact?.doNotDisturb ?: false)
} }
private fun setListener() { override fun observeData() {
}
override fun setupListeners() {
binding.ciSetDisturb.sbSwitch.setOnCheckedChangeListener(object : binding.ciSetDisturb.sbSwitch.setOnCheckedChangeListener(object :
SwitchButton.OnCheckedChangeListener { SwitchButton.OnCheckedChangeListener {
override fun onCheckedChanged(button: SwitchButton, isChecked: Boolean) { override fun onCheckedChanged(button: SwitchButton, isChecked: Boolean) {

View File

@ -21,7 +21,14 @@ class ContactPermissionActivity : BaseActivity<ActivityContactPermissionBinding>
override fun initData() { override fun initData() {
} }
private fun initView() { override fun observeData() {
}
override fun setupListeners() {
}
override fun initView() {
binding.ciChat.setYesSelected(false) binding.ciChat.setYesSelected(false)
} }
} }

View File

@ -13,7 +13,6 @@ import com.kaixed.kchat.viewmodel.ContactViewModel
class ContactRequestListActivity : BaseActivity<ActivityContactRequestListBinding>() { class ContactRequestListActivity : BaseActivity<ActivityContactRequestListBinding>() {
private var items = mutableListOf<FriendRequestItem>() private var items = mutableListOf<FriendRequestItem>()
private val contactViewModel: ContactViewModel by viewModels() private val contactViewModel: ContactViewModel by viewModels()
@ -38,10 +37,19 @@ class ContactRequestListActivity : BaseActivity<ActivityContactRequestListBindin
binding.recycleFriendRequestList.adapter = contactRequestListAdapter binding.recycleFriendRequestList.adapter = contactRequestListAdapter
} }
override fun initView() {
}
override fun initData() { override fun initData() {
} }
override fun observeData() {
}
override fun setupListeners() {
}
private fun getItems() { private fun getItems() {
contactViewModel.contactRequestListResult contactViewModel.contactRequestListResult
.observe(this) { result -> .observe(this) { result ->

View File

@ -5,17 +5,18 @@ import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet
import com.kaixed.kchat.databinding.ActivityContactUpdatesBinding import com.kaixed.kchat.databinding.ActivityContactUpdatesBinding
import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.utils.ScreenUtils import com.kaixed.kchat.utils.ScreenUtils
class ContactUpdatesActivity : AppCompatActivity() { class ContactUpdatesActivity : BaseActivity<ActivityContactUpdatesBinding>() {
private lateinit var binding: ActivityContactUpdatesBinding override fun inflateBinding(): ActivityContactUpdatesBinding {
return ActivityContactUpdatesBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
binding = ActivityContactUpdatesBinding.inflate(layoutInflater)
setContentView(binding.root)
val constraintSet = ConstraintSet() val constraintSet = ConstraintSet()
constraintSet.clone(binding.main) constraintSet.clone(binding.main)
@ -37,4 +38,16 @@ class ContactUpdatesActivity : AppCompatActivity() {
) )
constraintSet.applyTo(binding.main) constraintSet.applyTo(binding.main)
} }
override fun initView() {
}
override fun initData() {
}
override fun observeData() {
}
override fun setupListeners() {
}
} }

View File

@ -36,16 +36,49 @@ class ContactsDetailActivity : BaseActivity<ActivityContactsDetailBinding>() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
setOnListener()
updateContent() updateContent()
} }
override fun initView() {
}
override fun initData() { override fun initData() {
contactId = intent.getStringExtra("contactId") contactId = intent.getStringExtra("contactId")
isMine = intent.getBooleanExtra("isMine", false) isMine = intent.getBooleanExtra("isMine", false)
} }
override fun observeData() {
}
override fun setupListeners() {
binding.tvSendMessage.setOnClickListener {
startActivity(Intent(this, ChatActivity::class.java).apply {
putExtra("contactId", contactId)
putExtra("contactNickname", contactId)
})
}
binding.ciSetRemarkAndLabel.setOnClickListener {
startActivity(Intent(this, SetRemarkAndLabelActivity::class.java).apply {
putExtra("contactId", contactId)
putExtra("contactNickname", contactNickname)
})
}
binding.ctb.setOnSettingClickListener {
startActivity(Intent(this, DataSettingActivity::class.java).apply {
putExtra("contactId", contactId)
})
}
binding.ciPermission.setOnClickListener {
startActivity(
Intent(this, ContactPermissionActivity::class.java)
)
}
}
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun updateContent() { private fun updateContent() {
if (isMine) { if (isMine) {
@ -83,34 +116,6 @@ class ContactsDetailActivity : BaseActivity<ActivityContactsDetailBinding>() {
updateContent() updateContent()
} }
private fun setOnListener() {
binding.tvSendMessage.setOnClickListener {
startActivity(Intent(this, ChatActivity::class.java).apply {
putExtra("contactId", contactId)
putExtra("contactNickname", contactId)
})
}
binding.ciSetRemarkAndLabel.setOnClickListener {
startActivity(Intent(this, SetRemarkAndLabelActivity::class.java).apply {
putExtra("contactId", contactId)
putExtra("contactNickname", contactNickname)
})
}
binding.ctb.setOnSettingClickListener {
startActivity(Intent(this, DataSettingActivity::class.java).apply {
putExtra("contactId", contactId)
})
}
binding.ciPermission.setOnClickListener {
startActivity(
Intent(this, ContactPermissionActivity::class.java)
)
}
}
private fun getContactInfo(contactId: String): Contact { private fun getContactInfo(contactId: String): Contact {
return DataBase.contactBox return DataBase.contactBox
.query(Contact_.username.equal(contactId)) .query(Contact_.username.equal(contactId))

View File

@ -33,10 +33,20 @@ class DataSettingActivity : BaseActivity<ActivityDataSettingBinding>(),
handleIntent(intent) handleIntent(intent)
} }
override fun initView() {
}
override fun initData() { override fun initData() {
} }
override fun observeData() {
}
override fun setupListeners() {
}
private fun handleIntent(intent: Intent) { private fun handleIntent(intent: Intent) {
contactId = intent.getStringExtra("contactId") ?: "" contactId = intent.getStringExtra("contactId") ?: ""
} }

View File

@ -38,21 +38,21 @@ class FriendCircleActivity : BaseActivity<ActivityFriendCircleBinding>() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
initView()
setListener()
}
private fun setListener() {
binding.ivBack.setOnClickListener {
finish()
}
} }
override fun initData() { override fun initData() {
getItems() getItems()
} }
override fun observeData() {
}
override fun setupListeners() {
binding.ivBack.setOnClickListener {
finish()
}
}
private fun getItems() { private fun getItems() {
val img = listOf( val img = listOf(
R.drawable.ic_1, R.drawable.ic_1,
@ -90,7 +90,7 @@ class FriendCircleActivity : BaseActivity<ActivityFriendCircleBinding>() {
@SuppressLint("NewApi") @SuppressLint("NewApi")
private fun initView() { override fun initView() {
setContent() setContent()
binding.clTool.apply { binding.clTool.apply {
updateLayoutParams { updateLayoutParams {

View File

@ -35,10 +35,19 @@ class LaunchActivity : BaseActivity<ActivityLaunchBinding>() {
setOnClickListener() setOnClickListener()
} }
override fun initView() {
}
override fun initData() { override fun initData() {
} }
override fun observeData() {
}
override fun setupListeners() {
}
@SuppressLint("DiscouragedApi", "InternalInsetResource") @SuppressLint("DiscouragedApi", "InternalInsetResource")
private fun getStatusBarHeight(): Int { private fun getStatusBarHeight(): Int {
var result = 0 var result = 0

View File

@ -58,7 +58,7 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>() {
} }
} }
private fun initView() { override fun initView() {
setupLoginView() setupLoginView()
} }
@ -210,6 +210,11 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>() {
} }
override fun initData() {} override fun initData() {}
override fun observeData() {
}
override fun setupListeners() {
}
override fun inflateBinding(): ActivityLoginBinding { override fun inflateBinding(): ActivityLoginBinding {
return ActivityLoginBinding.inflate(layoutInflater) return ActivityLoginBinding.inflate(layoutInflater)

View File

@ -180,6 +180,12 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
} }
} }
override fun observeData() {
}
override fun setupListeners() {
}
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(unreadEvent: UnreadEvent) { fun onMessageEvent(unreadEvent: UnreadEvent) {
val unreadCount = unreadEvent.unreadCount val unreadCount = unreadEvent.unreadCount
@ -191,7 +197,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
} }
private fun initView() { override fun initView() {
navItems.forEachIndexed { index, navItem -> navItems.forEachIndexed { index, navItem ->
navItem.container.setOnClickListener { navItem.container.setOnClickListener {
updateSelection(index) updateSelection(index)

View File

@ -65,6 +65,9 @@ class ProfileDetailActivity : BaseActivity<ActivityProfileDetailBinding>() {
updateContent(username) updateContent(username)
} }
override fun initView() {
}
override fun initData() { override fun initData() {
getImageLauncher = getImageLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
@ -79,6 +82,12 @@ class ProfileDetailActivity : BaseActivity<ActivityProfileDetailBinding>() {
} }
} }
override fun observeData() {
}
override fun setupListeners() {
}
private fun updateAvatar(uri: Uri) { private fun updateAvatar(uri: Uri) {
val filePath = getPathFromUri(uri) val filePath = getPathFromUri(uri)

View File

@ -31,6 +31,9 @@ class QrCodeActivity : BaseActivity<ActivityQrCodeBinding>() {
} }
} }
override fun initView() {
}
override fun initData() { override fun initData() {
setupQrcode() setupQrcode()
binding.tvNickname.text = getNickName() binding.tvNickname.text = getNickName()
@ -38,6 +41,12 @@ class QrCodeActivity : BaseActivity<ActivityQrCodeBinding>() {
.into(binding.ifvAvatar) .into(binding.ifvAvatar)
} }
override fun observeData() {
}
override fun setupListeners() {
}
private fun setupQrcode() { private fun setupQrcode() {
val content = "kchat@${getUsername()}" val content = "kchat@${getUsername()}"
val type = HmsScan.QRCODE_SCAN_TYPE val type = HmsScan.QRCODE_SCAN_TYPE

View File

@ -33,10 +33,6 @@ class RegisterActivity : BaseActivity<ActivityRegisterBinding>() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
initView()
setOnClickListener()
} }
override fun initData() { override fun initData() {
@ -53,7 +49,10 @@ class RegisterActivity : BaseActivity<ActivityRegisterBinding>() {
} }
} }
private fun setOnClickListener() { override fun observeData() {
}
override fun setupListeners() {
binding.ivClose.setOnClickListener { binding.ivClose.setOnClickListener {
finish() finish()
} }
@ -95,7 +94,7 @@ class RegisterActivity : BaseActivity<ActivityRegisterBinding>() {
} }
private fun initView() { override fun initView() {
setupTip() setupTip()
setOnEtChangeListener() setOnEtChangeListener()
} }

View File

@ -8,8 +8,8 @@ import androidx.activity.viewModels
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
import com.kaixed.kchat.data.local.entity.UserInfo import com.kaixed.kchat.data.local.entity.UserInfo
import com.kaixed.kchat.databinding.ActivityRenameBinding
import com.kaixed.kchat.data.model.request.UserRequest import com.kaixed.kchat.data.model.request.UserRequest
import com.kaixed.kchat.databinding.ActivityRenameBinding
import com.kaixed.kchat.ui.base.BaseActivity import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.ui.widget.LoadingDialogFragment import com.kaixed.kchat.ui.widget.LoadingDialogFragment
import com.kaixed.kchat.utils.ConstantsUtils.getNickName import com.kaixed.kchat.utils.ConstantsUtils.getNickName
@ -19,8 +19,6 @@ import io.objectbox.Box
class RenameActivity : BaseActivity<ActivityRenameBinding>() { class RenameActivity : BaseActivity<ActivityRenameBinding>() {
private val userInfoBox: Box<UserInfo> by lazy { getBoxStore().boxFor(UserInfo::class.java) }
private val userViewModel: UserViewModel by viewModels() private val userViewModel: UserViewModel by viewModels()
private var oldNickname: String = "" private var oldNickname: String = ""
@ -56,10 +54,19 @@ class RenameActivity : BaseActivity<ActivityRenameBinding>() {
} }
} }
override fun initView() {
}
override fun initData() { override fun initData() {
oldNickname = getNickName() oldNickname = getNickName()
} }
override fun observeData() {
}
override fun setupListeners() {
}
private fun updateNicknamesByCondition(newNickname: String, username: String) { private fun updateNicknamesByCondition(newNickname: String, username: String) {
binding.ctb.setBtnEnable(false) binding.ctb.setBtnEnable(false)
val dialog: LoadingDialogFragment = LoadingDialogFragment.newInstance("正在保存") val dialog: LoadingDialogFragment = LoadingDialogFragment.newInstance("正在保存")

View File

@ -86,6 +86,15 @@ class ScanActivity : BaseActivity<ActivityScanBinding>() {
} }
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
private fun initPermission() { private fun initPermission() {
ActivityCompat.requestPermissions( ActivityCompat.requestPermissions(
this, this,

View File

@ -40,13 +40,22 @@ class SearchActivity : BaseActivity<ActivitySearchBinding>() {
} }
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
private fun setupViewVisibility(length: Int) { private fun setupViewVisibility(length: Int) {
binding.rvSearchResult.visibility = if (length > 0) View.VISIBLE else View.INVISIBLE binding.rvSearchResult.visibility = if (length > 0) View.VISIBLE else View.INVISIBLE
binding.tvPageSetting.visibility = if (length > 0) View.INVISIBLE else View.VISIBLE binding.tvPageSetting.visibility = if (length > 0) View.INVISIBLE else View.VISIBLE
} }
private fun setupRecycleView() { private fun setupRecycleView() {
binding!!.rvSearchResult.edgeEffectFactory = object : EdgeEffectFactory() { binding.rvSearchResult.edgeEffectFactory = object : EdgeEffectFactory() {
override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect { override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect {
return object : EdgeEffect(view.context) { return object : EdgeEffect(view.context) {
override fun onPull(deltaDistance: Float, displacement: Float) { override fun onPull(deltaDistance: Float, displacement: Float) {

View File

@ -140,4 +140,13 @@ class SearchChatHistory : BaseActivity<ActivitySearchChatHistoryBinding>() {
contactAvatarUrl = intent?.getStringExtra("contactAvatarUrl") ?: "" contactAvatarUrl = intent?.getStringExtra("contactAvatarUrl") ?: ""
contactId = intent?.getStringExtra("contactId") ?: "" contactId = intent?.getStringExtra("contactId") ?: ""
} }
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
} }

View File

@ -132,11 +132,17 @@ class SearchFriendsActivity : BaseActivity<ActivitySearchFriendsBinding>() {
override fun initData() { override fun initData() {
} }
private fun initView() { override fun initView() {
binding.clFriends.visibility = View.INVISIBLE binding.clFriends.visibility = View.INVISIBLE
binding.tvTitle.visibility = View.INVISIBLE binding.tvTitle.visibility = View.INVISIBLE
} }
override fun setupListeners() {
}
override fun observeData() {
}
private fun searchUser(username: String) { private fun searchUser(username: String) {
contactViewModel.searchContact(username) contactViewModel.searchContact(username)
} }

View File

@ -4,13 +4,28 @@ import android.os.Bundle
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.kaixed.kchat.databinding.ActivityServiceDetailBinding import com.kaixed.kchat.databinding.ActivityServiceDetailBinding
import com.kaixed.kchat.ui.base.BaseActivity
class ServiceDetailActivity : BaseActivity<ActivityServiceDetailBinding>() {
class ServiceDetailActivity : AppCompatActivity() {
private lateinit var binding: ActivityServiceDetailBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
binding = ActivityServiceDetailBinding.inflate(layoutInflater) }
setContentView(binding.root)
override fun initData() {
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
override fun inflateBinding(): ActivityServiceDetailBinding {
return ActivityServiceDetailBinding.inflate(layoutInflater)
} }
} }

View File

@ -4,19 +4,28 @@ import android.os.Bundle
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.kaixed.kchat.databinding.ActivitySetRemarkBinding import com.kaixed.kchat.databinding.ActivitySetRemarkBinding
import com.kaixed.kchat.ui.base.BaseActivity
class SetRemarkActivity : AppCompatActivity() { class SetRemarkActivity : BaseActivity<ActivitySetRemarkBinding>() {
private lateinit var binding: ActivitySetRemarkBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
binding = ActivitySetRemarkBinding.inflate(layoutInflater)
setContentView(binding.root)
setOnClickListener()
} }
private fun setOnClickListener() { override fun initData() {
}
override fun initView() {
}
override fun setupListeners() {
binding.ivBack.setOnClickListener { finish() } binding.ivBack.setOnClickListener { finish() }
} }
override fun observeData() {
}
override fun inflateBinding(): ActivitySetRemarkBinding {
return ActivitySetRemarkBinding.inflate(layoutInflater)
}
} }

View File

@ -25,10 +25,6 @@ import kotlinx.coroutines.launch
class SetRemarkAndLabelActivity : BaseActivity<ActivitySetRemarkAndLabelBinding>() { class SetRemarkAndLabelActivity : BaseActivity<ActivitySetRemarkAndLabelBinding>() {
private val contactBox by lazy {
getBoxStore().boxFor(Contact::class.java)
}
private var contactId: String? = null private var contactId: String? = null
private var hasFocus = false private var hasFocus = false
@ -59,6 +55,15 @@ class SetRemarkAndLabelActivity : BaseActivity<ActivitySetRemarkAndLabelBinding>
remark = contact?.remark ?: contact?.nickname ?: "" remark = contact?.remark ?: contact?.nickname ?: ""
} }
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
updateContent() updateContent()

View File

@ -140,4 +140,13 @@ class SettingsActivity : BaseActivity<ActivitySettingBinding>(), View.OnClickLis
override fun initData() { override fun initData() {
} }
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
} }

View File

@ -4,14 +4,8 @@ import android.os.Bundle
import android.text.Spannable import android.text.Spannable
import android.text.SpannableString import android.text.SpannableString
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.text.util.Linkify
import android.util.Patterns import android.util.Patterns
import android.view.View import android.view.View
import android.webkit.WebChromeClient
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import com.kaixed.kchat.databinding.ActivityTestBinding import com.kaixed.kchat.databinding.ActivityTestBinding
@ -70,4 +64,13 @@ class TestActivity : BaseActivity<ActivityTestBinding>() {
} }
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
} }

View File

@ -57,6 +57,15 @@ class WebViewActivity : BaseActivity<ActivityWebViewBinding>() {
url = intent.getStringExtra("url") ?: "" url = intent.getStringExtra("url") ?: ""
} }
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
private fun setupWebView() { private fun setupWebView() {
// 启用 JavaScript // 启用 JavaScript
webView.settings.javaScriptEnabled = true webView.settings.javaScriptEnabled = true

View File

@ -28,4 +28,13 @@ class AboutActivity : BaseActivity<ActivityAboutBinding>() {
loadingDialog.showLoading(supportFragmentManager) loadingDialog.showLoading(supportFragmentManager)
} }
} }
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
} }

View File

@ -16,16 +16,6 @@ class AccountSecurityActivity : BaseActivity<ActivityAccountSecurityBinding>() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
setListener()
}
private fun setListener() {
binding.ciUpdatePassword.setOnClickListener {
startActivity(
Intent(this, UpdatePasswordActivity::class.java)
)
}
} }
override fun initData() { override fun initData() {
@ -36,4 +26,18 @@ class AccountSecurityActivity : BaseActivity<ActivityAccountSecurityBinding>() {
binding.ciTelephone.setItemDesc(telephone) binding.ciTelephone.setItemDesc(telephone)
} }
} }
override fun initView() {
}
override fun setupListeners() {
binding.ciUpdatePassword.setOnClickListener {
startActivity(
Intent(this, UpdatePasswordActivity::class.java)
)
}
}
override fun observeData() {
}
} }

View File

@ -17,10 +17,18 @@ class UpdateKidActivity : BaseActivity<ActivityUpdateKidBinding>() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
setContentView(R.layout.activity_update_kid)
} }
override fun initData() { override fun initData() {
} }
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
} }

View File

@ -83,6 +83,15 @@ class UpdatePasswordActivity : BaseActivity<ActivityUpdatePasswordBinding>() {
addEdittextFilter() addEdittextFilter()
} }
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
private val textWatch = object : TextWatcher { private val textWatch = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
} }

View File

@ -22,4 +22,16 @@ class UpdateTelephoneActivity : BaseActivity<ActivityUpdateTelephoneBinding>() {
override fun initData() { override fun initData() {
} }
override fun initView() {
TODO("Not yet implemented")
}
override fun setupListeners() {
TODO("Not yet implemented")
}
override fun observeData() {
TODO("Not yet implemented")
}
} }

View File

@ -14,13 +14,13 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
protected lateinit var binding: VB protected lateinit var binding: VB
abstract fun inflateBinding(): VB
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = inflateBinding() binding = inflateBinding()
setContentView(binding.root) setContentView(binding.root)
initData() initData()
initView()
setupListeners()
AppManager.instance.addActivity(this) AppManager.instance.addActivity(this)
} }
@ -31,6 +31,12 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
abstract fun initData() abstract fun initData()
abstract fun initView()
abstract fun setupListeners()
abstract fun observeData()
fun toast(msg: String) { fun toast(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
} }
@ -38,4 +44,6 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
fun toast() { fun toast() {
Toast.makeText(this, "暂未开发", Toast.LENGTH_SHORT).show() Toast.makeText(this, "暂未开发", Toast.LENGTH_SHORT).show()
} }
abstract fun inflateBinding(): VB
} }

View File

@ -10,6 +10,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.kaixed.kchat.R import com.kaixed.kchat.R
import com.kaixed.kchat.databinding.ItemCustomBinding import com.kaixed.kchat.databinding.ItemCustomBinding
import com.kaixed.kchat.utils.ImageLoader
/** /**
* @Author: kaixed * @Author: kaixed
@ -123,8 +124,7 @@ class CustomItem @JvmOverloads constructor(
if (itemIconRound > 0) { if (itemIconRound > 0) {
binding.ivItemIcon.roundPercent = itemIconRound binding.ivItemIcon.roundPercent = itemIconRound
Glide.with(context).load(itemIcon) ImageLoader.loadImage(context, itemIcon, binding.ivItemIcon)
.into(binding.ivItemIcon)
} }
} else { } else {
binding.ivItemIcon.visibility = View.GONE binding.ivItemIcon.visibility = View.GONE

View File

@ -0,0 +1,54 @@
package com.kaixed.kchat.utils
import android.content.Context
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
object ImageLoader {
fun loadImage(context: Context, url: String, imageView: ImageView) {
Glide.with(context)
.load(url)
.into(imageView)
}
fun loadImage(context: Context, image: Int, imageView: ImageView) {
Glide.with(context)
.load(image)
.into(imageView)
}
fun loadImageWithPlaceholder(context: Context, url: String, imageView: ImageView, placeholderResId: Int) {
Glide.with(context)
.load(url)
.apply(getDefaultOptions().placeholder(placeholderResId))
.into(imageView)
}
fun loadImageWithError(context: Context, url: String, imageView: ImageView, errorResId: Int) {
Glide.with(context)
.load(url)
.apply(getDefaultOptions().error(errorResId))
.into(imageView)
}
fun loadImageCircle(context: Context, url: String, imageView: ImageView) {
Glide.with(context)
.load(url)
.apply(getCircleCropOptions())
.into(imageView)
}
private fun getDefaultOptions(): RequestOptions {
return RequestOptions()
.centerCrop() // 默认居中裁剪
.dontAnimate() // 禁止动画(如果不需要)
}
private fun getCircleCropOptions(): RequestOptions {
return RequestOptions()
.circleCrop() // 圆形裁剪
.dontAnimate() // 禁止动画
}
}