feat: 完善页面
1.完善聊天界面,好友详情页 2.抽象网络请求接口,以减少重复代码 3.引入pictureselector、spannable库
This commit is contained in:
parent
c45acb5bda
commit
e1816f525a
@ -4,10 +4,10 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2024-11-19T05:46:34.362744600Z">
|
||||
<DropdownSelection timestamp="2024-11-25T08:35:02.927264800Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="Default" identifier="serial=10.127.185.244:5555;connection=5b2cc3eb" />
|
||||
<DeviceId pluginId="Default" identifier="serial=10.39.42.8:5555;connection=563cba5e" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
|
@ -80,6 +80,16 @@ dependencies {
|
||||
|
||||
implementation(libs.okhttp3.logging.interceptor)
|
||||
|
||||
// 图片选择器
|
||||
implementation(libs.pictureselector)
|
||||
// 图片压缩
|
||||
implementation(libs.compress)
|
||||
// 图片裁剪
|
||||
implementation(libs.ucrop)
|
||||
|
||||
// 自定义spannable
|
||||
implementation(libs.spannable)
|
||||
|
||||
// implementation(libs.therouter)
|
||||
// ksp(libs.therouter.ksp)
|
||||
}
|
||||
|
10
app/proguard-rules.pro
vendored
10
app/proguard-rules.pro
vendored
@ -69,4 +69,12 @@
|
||||
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
|
||||
|
||||
# With R8 full mode generic signatures are stripped for classes that are not kept.
|
||||
-keep,allowobfuscation,allowshrinking class retrofit2.Response
|
||||
-keep,allowobfuscation,allowshrinking class retrofit2.Response
|
||||
|
||||
|
||||
|
||||
-keep class com.luck.picture.lib.** { *; }
|
||||
|
||||
-dontwarn com.yalantis.ucrop**
|
||||
-keep class com.yalantis.ucrop** { *; }
|
||||
-keep interface com.yalantis.ucrop** { *; }
|
@ -2,9 +2,16 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
@ -19,7 +26,7 @@
|
||||
tools:ignore="ScopedStorage" />
|
||||
|
||||
<application
|
||||
android:name=".KchatApplication"
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
android:appComponentFactory="androidx.core.app.CoreComponentFactory"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
@ -32,6 +39,12 @@
|
||||
android:theme="@style/Theme.KChatAndroid"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".ui.activity.TestActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.ContactPermissionActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.SetRemarkAndLabelActivity"
|
||||
android:exported="false" />
|
||||
@ -82,9 +95,6 @@
|
||||
<activity
|
||||
android:name=".ui.activity.ApproveContactRequestActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.MessageActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.ApplyAddFriendActivity"
|
||||
android:exported="false" />
|
||||
|
@ -1,24 +1,24 @@
|
||||
package com.kaixed.kchat
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.init
|
||||
import com.kaixed.kchat.utils.DensityUtil
|
||||
import com.tencent.mmkv.MMKV
|
||||
import io.objectbox.android.Admin
|
||||
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/10/24 17:04
|
||||
*/
|
||||
class KchatApplication : Application() {
|
||||
class App : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
MMKV.initialize(this)
|
||||
|
||||
init(this)
|
||||
Admin(get()).start(this)
|
||||
Admin(getBoxStore()).start(this)
|
||||
|
||||
DensityUtil.init(this)
|
||||
}
|
@ -17,5 +17,5 @@ object ObjectBox {
|
||||
.build()
|
||||
}
|
||||
|
||||
fun get(): BoxStore = checkNotNull(store) { "ObjectBox is not initialized." }
|
||||
fun getBoxStore(): BoxStore = checkNotNull(store) { "ObjectBox is not initialized." }
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
package com.kaixed.kchat.data.objectbox.data
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact_
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/11/24 13:34
|
||||
*/
|
||||
object LocalContact {
|
||||
private val contactBox by lazy { getBoxStore().boxFor(Contact::class.java) }
|
||||
|
||||
fun getContactByUsername(contactId: String): Contact? {
|
||||
return contactBox.query(Contact_.username.equal(contactId)).build().findFirst()
|
||||
}
|
||||
|
||||
}
|
25
app/src/main/java/com/kaixed/kchat/network/ApiCall.kt
Normal file
25
app/src/main/java/com/kaixed/kchat/network/ApiCall.kt
Normal file
@ -0,0 +1,25 @@
|
||||
package com.kaixed.kchat.network
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/11/24 9:29
|
||||
*/
|
||||
object ApiCall {
|
||||
suspend fun <T> apiCall(
|
||||
apiCall: suspend () -> ApiResponse<T>,
|
||||
errorMessage: String = "操作失败"
|
||||
): Result<T?> {
|
||||
return try {
|
||||
val response = apiCall()
|
||||
if (response.isSuccess()) {
|
||||
response.getResponseData()?.let {
|
||||
Result.success(it)
|
||||
} ?: Result.failure(Exception(errorMessage))
|
||||
} else {
|
||||
Result.failure(Exception(response.getResponseMsg()))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
}
|
@ -57,4 +57,13 @@ interface ContactService {
|
||||
@Field("userId") username: String,
|
||||
@Field("contactId") contactId: String,
|
||||
): ApiResponse<String?>
|
||||
|
||||
// 设置好友备注
|
||||
@FormUrlEncoded
|
||||
@POST("friend/remark")
|
||||
suspend fun setRemark(
|
||||
@Field("userId") userId: String,
|
||||
@Field("contactId") contactId: String,
|
||||
@Field("remark") remark: String,
|
||||
): ApiResponse<String?>
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ interface UserApiService {
|
||||
suspend fun loginByUsername(
|
||||
@Field("username") username: String,
|
||||
@Field("password") password: String
|
||||
): ApiResponse<UserInfo>
|
||||
): ApiResponse<UserInfo?>
|
||||
|
||||
// 登录接口(根据电话登录)
|
||||
@FormUrlEncoded
|
||||
|
@ -1,10 +1,11 @@
|
||||
package com.kaixed.kchat.repository
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact_
|
||||
import com.kaixed.kchat.model.friend.FriendRequestItem
|
||||
import com.kaixed.kchat.model.search.User
|
||||
import com.kaixed.kchat.network.ApiCall.apiCall
|
||||
import com.kaixed.kchat.network.RetrofitClient
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
|
||||
import com.kaixed.kchat.utils.Pinyin4jUtil
|
||||
@ -16,24 +17,28 @@ class ContactRepository {
|
||||
private val contactApiService = RetrofitClient.contactApiService
|
||||
|
||||
private val contactBox: Box<Contact> by lazy {
|
||||
get().boxFor(Contact::class.java)
|
||||
getBoxStore().boxFor(Contact::class.java)
|
||||
}
|
||||
|
||||
// 获取联系人请求列表
|
||||
suspend fun getContactRequestList(username: String): Result<List<FriendRequestItem>?> {
|
||||
return try {
|
||||
val response = contactApiService.getContactRequestList(username)
|
||||
if (response.isSuccess()) {
|
||||
val searchUser = response.getResponseData()
|
||||
searchUser?.let {
|
||||
Result.success(searchUser)
|
||||
} ?: Result.failure(Exception("没有好友申请"))
|
||||
} else {
|
||||
Result.failure(Exception(response.getResponseMsg()))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
return apiCall(
|
||||
apiCall = { contactApiService.getContactRequestList(username) },
|
||||
errorMessage = "获取好友申请列表失败"
|
||||
)
|
||||
// return try {
|
||||
// val response = contactApiService.getContactRequestList(username)
|
||||
// if (response.isSuccess()) {
|
||||
// val searchUser = response.getResponseData()
|
||||
// searchUser?.let {
|
||||
// Result.success(searchUser)
|
||||
// } ?: Result.failure(Exception("没有好友申请"))
|
||||
// } else {
|
||||
// Result.failure(Exception(response.getResponseMsg()))
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Result.failure(e)
|
||||
// }
|
||||
}
|
||||
|
||||
// 接受联系人请求
|
||||
@ -42,19 +47,27 @@ class ContactRepository {
|
||||
contactId: String,
|
||||
remark: String
|
||||
): Result<Contact?> {
|
||||
return try {
|
||||
val response = contactApiService.acceptContactRequest(contactId, username, remark)
|
||||
if (response.isSuccess()) {
|
||||
val searchUser = response.getResponseData()
|
||||
searchUser?.let {
|
||||
ContactUtil.handleContact(searchUser)
|
||||
Result.success(searchUser)
|
||||
} ?: Result.failure(Exception("添加好友失败"))
|
||||
} else {
|
||||
Result.failure(Exception(response.getResponseMsg()))
|
||||
return apiCall(
|
||||
apiCall = { contactApiService.acceptContactRequest(contactId, username, remark) },
|
||||
errorMessage = "添加好友失败"
|
||||
).onSuccess {
|
||||
it?.let {
|
||||
ContactUtil.handleContact(it)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
// return try {
|
||||
// val response = contactApiService.acceptContactRequest(contactId, username, remark)
|
||||
// if (response.isSuccess()) {
|
||||
// val searchUser = response.getResponseData()
|
||||
// searchUser?.let {
|
||||
// ContactUtil.handleContact(searchUser)
|
||||
// Result.success(searchUser)
|
||||
// } ?: Result.failure(Exception("添加好友失败"))
|
||||
// } else {
|
||||
// Result.failure(Exception(response.getResponseMsg()))
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Result.failure(e)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,105 +76,105 @@ class ContactRepository {
|
||||
username: String,
|
||||
contactId: String,
|
||||
): Result<String?> {
|
||||
return try {
|
||||
val response = contactApiService.deleteContact(username, contactId)
|
||||
if (response.isSuccess()) {
|
||||
val searchUser = response.getResponseData()
|
||||
searchUser?.let {
|
||||
val con =
|
||||
contactBox.query(Contact_.username.equal(contactId)).build().findFirst()
|
||||
contactBox.remove(con!!)
|
||||
Result.success(searchUser)
|
||||
} ?: Result.failure(Exception("删除好友失败"))
|
||||
} else {
|
||||
Result.failure(Exception(response.getResponseMsg()))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
return apiCall(
|
||||
apiCall = { contactApiService.deleteContact(username, contactId) },
|
||||
errorMessage = "删除好友失败"
|
||||
).onSuccess {
|
||||
val con =
|
||||
contactBox.query(Contact_.username.equal(contactId)).build().findFirst()
|
||||
contactBox.remove(con!!)
|
||||
}
|
||||
// return try {
|
||||
// val response = contactApiService.deleteContact(username, contactId)
|
||||
// if (response.isSuccess()) {
|
||||
// val searchUser = response.getResponseData()
|
||||
// searchUser?.let {
|
||||
// val con =
|
||||
// contactBox.query(Contact_.username.equal(contactId)).build().findFirst()
|
||||
// contactBox.remove(con!!)
|
||||
// Result.success(searchUser)
|
||||
// } ?: Result.failure(Exception("删除好友失败"))
|
||||
// } else {
|
||||
// Result.failure(Exception(response.getResponseMsg()))
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Result.failure(e)
|
||||
// }
|
||||
}
|
||||
|
||||
// 添加联系人
|
||||
suspend fun addContact(contactId: String, message: String): Result<String> {
|
||||
return try {
|
||||
val response = contactApiService.addContact(
|
||||
senderId = contactId,
|
||||
receiverId = getUsername(),
|
||||
message = message
|
||||
)
|
||||
if (response.isSuccess()) {
|
||||
val searchUser = response.getResponseData()
|
||||
searchUser?.let {
|
||||
Result.success(searchUser)
|
||||
} ?: Result.failure(Exception("添加联系人失败"))
|
||||
} else {
|
||||
Result.failure(Exception(response.getResponseMsg()))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
suspend fun addContact(contactId: String, message: String): Result<String?> {
|
||||
return apiCall(
|
||||
apiCall = { contactApiService.addContact(contactId, getUsername(), message) },
|
||||
errorMessage = "添加联系人失败"
|
||||
)
|
||||
// return try {
|
||||
// val response = contactApiService.addContact(
|
||||
// senderId = contactId,
|
||||
// receiverId = getUsername(),
|
||||
// message = message
|
||||
// )
|
||||
// if (response.isSuccess()) {
|
||||
// val searchUser = response.getResponseData()
|
||||
// searchUser?.let {
|
||||
// Result.success(searchUser)
|
||||
// } ?: Result.failure(Exception("添加联系人失败"))
|
||||
// } else {
|
||||
// Result.failure(Exception(response.getResponseMsg()))
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Result.failure(e)
|
||||
// }
|
||||
}
|
||||
|
||||
// 搜索联系人
|
||||
suspend fun searchContact(username: String): Result<User?> {
|
||||
return try {
|
||||
val response = contactApiService.searchContact(username)
|
||||
if (response.isSuccess()) {
|
||||
val searchUser = response.getResponseData()
|
||||
searchUser?.let {
|
||||
Result.success(searchUser)
|
||||
} ?: Result.failure(Exception("没有找到用户"))
|
||||
} else {
|
||||
Result.failure(Exception(response.getResponseMsg()))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
return apiCall(
|
||||
apiCall = { contactApiService.searchContact(username) },
|
||||
errorMessage = "搜索用户失败"
|
||||
)
|
||||
// return try {
|
||||
// val response = contactApiService.searchContact(username)
|
||||
// if (response.isSuccess()) {
|
||||
// val searchUser = response.getResponseData()
|
||||
// searchUser?.let {
|
||||
// Result.success(searchUser)
|
||||
// } ?: Result.failure(Exception("没有找到用户"))
|
||||
// } else {
|
||||
// Result.failure(Exception(response.getResponseMsg()))
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Result.failure(e)
|
||||
// }
|
||||
}
|
||||
|
||||
// 获取联系人列表
|
||||
suspend fun getContactList(username: String): Result<List<Contact>?> {
|
||||
// return safeApiCall(
|
||||
// apiCall = { contactApiService.getContactList(username) },
|
||||
// errorMessage = "获取好友列表失败"
|
||||
// ).onSuccess {
|
||||
//
|
||||
// }
|
||||
return try {
|
||||
val response = contactApiService.getContactList(username)
|
||||
if (response.isSuccess()) {
|
||||
val searchUsers = response.getResponseData()
|
||||
searchUsers?.let {
|
||||
|
||||
val uiFriendList = searchUsers?.map { networkItem ->
|
||||
val uiFriendList = searchUsers.map { networkItem ->
|
||||
mapToFriendItem(networkItem) // 数据转换
|
||||
}?.sortedWith { f1, f2 ->
|
||||
val str1 = f1.remarkquanpin ?: f1.quanpin ?: f1.nickname
|
||||
val str2 = f2.remarkquanpin ?: f2.quanpin ?: f2.nickname
|
||||
|
||||
val type1 = when {
|
||||
str1.matches(Regex("[a-zA-Z]+")) -> 1 // 字母
|
||||
str1.matches(Regex("[0-9]+")) -> 2 // 数字
|
||||
else -> 3 // 特殊字符
|
||||
}
|
||||
|
||||
val type2 = when {
|
||||
str2.matches(Regex("[a-zA-Z]+")) -> 1 // 字母
|
||||
str2.matches(Regex("[0-9]+")) -> 2 // 数字
|
||||
else -> 3 // 特殊字符
|
||||
}
|
||||
|
||||
// 比较类型,如果相同再按字母升序排序
|
||||
if (type1 != type2) {
|
||||
type1 - type2 // 按类型排序
|
||||
} else {
|
||||
str1.compareTo(str2) // 如果类型相同,按字母顺序排序
|
||||
}
|
||||
}
|
||||
}.sortedWith(::compareContacts)
|
||||
|
||||
// 设置是否显示分组头
|
||||
uiFriendList?.forEachIndexed { index, item ->
|
||||
uiFriendList.forEachIndexed { index, item ->
|
||||
item.showHeader =
|
||||
(index == 0 || item.quanpin?.get(index) != uiFriendList[index - 1].quanpin?.get(
|
||||
index - 1
|
||||
))
|
||||
}
|
||||
|
||||
Result.success(searchUsers)
|
||||
Result.success(uiFriendList)
|
||||
} ?: Result.failure(Exception("当前没有好友"))
|
||||
} else {
|
||||
Result.failure(Exception(response.getResponseMsg()))
|
||||
@ -171,6 +184,20 @@ class ContactRepository {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setRemark(userId: String, contactId: String, remark: String): Result<String?> {
|
||||
return apiCall(
|
||||
apiCall = { contactApiService.setRemark(userId, contactId, remark) },
|
||||
errorMessage = "设置备注失败"
|
||||
).onSuccess {
|
||||
val con = contactBox.query(Contact_.username.equal(contactId)).build().findFirst()
|
||||
if (con != null) {
|
||||
con.remark = remark
|
||||
con.remarkquanpin = Pinyin4jUtil.toPinyin(remark)
|
||||
contactBox.put(con)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapToFriendItem(response: Contact): Contact {
|
||||
val pinyin = Pinyin4jUtil.toPinyin(response.nickname)
|
||||
response.remark?.let {
|
||||
@ -179,4 +206,27 @@ class ContactRepository {
|
||||
response.quanpin = pinyin
|
||||
return response
|
||||
}
|
||||
|
||||
private fun compareContacts(c1: Contact, c2: Contact): Int {
|
||||
val str1 = c1.remarkquanpin ?: c1.quanpin ?: c1.nickname
|
||||
val str2 = c2.remarkquanpin ?: c2.quanpin ?: c2.nickname
|
||||
|
||||
val type1 = when {
|
||||
str1.matches(Regex("[a-zA-Z]+")) -> 1
|
||||
str1.matches(Regex("[0-9]+")) -> 2
|
||||
else -> 3
|
||||
}
|
||||
|
||||
val type2 = when {
|
||||
str2.matches(Regex("[a-zA-Z]+")) -> 1
|
||||
str2.matches(Regex("[0-9]+")) -> 2
|
||||
else -> 3
|
||||
}
|
||||
|
||||
return if (type1 != type2) {
|
||||
type1 - type2
|
||||
} else {
|
||||
str1.compareTo(str2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
package com.kaixed.kchat.repository
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo_
|
||||
import com.kaixed.kchat.model.request.RegisterRequest
|
||||
import com.kaixed.kchat.model.response.register.Register
|
||||
import com.kaixed.kchat.network.ApiCall.apiCall
|
||||
import com.kaixed.kchat.network.RetrofitClient
|
||||
import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION
|
||||
import com.tencent.mmkv.MMKV
|
||||
@ -18,54 +19,107 @@ class UserAuthRepository {
|
||||
|
||||
private val userApiService = RetrofitClient.userApiService
|
||||
|
||||
private val userInfoBox: Box<UserInfo> by lazy { get().boxFor(UserInfo::class.java) }
|
||||
private val userInfoBox: Box<UserInfo> by lazy { getBoxStore().boxFor(UserInfo::class.java) }
|
||||
|
||||
private val mmkv by lazy { MMKV.mmkvWithID(MMKV_USER_SESSION) }
|
||||
|
||||
// 用户注册
|
||||
suspend fun register(registerRequest: RegisterRequest): Result<Register> {
|
||||
return try {
|
||||
val response = userApiService.register(registerRequest)
|
||||
if (response.isSuccess()) {
|
||||
val register = response.getResponseData()
|
||||
register?.let {
|
||||
val userInfo = UserInfo(
|
||||
username = register.username,
|
||||
nickname = register.nickname,
|
||||
avatarUrl = "",
|
||||
signature = "",
|
||||
telephone = registerRequest.telephone
|
||||
)
|
||||
userInfoBox.put(userInfo)
|
||||
|
||||
Result.success(register)
|
||||
} ?: Result.failure(Exception("注册成功,但未返回用户数据"))
|
||||
} else {
|
||||
Result.failure(Exception(response.getResponseMsg()))
|
||||
suspend fun register(registerRequest: RegisterRequest): Result<Register?> {
|
||||
return apiCall(
|
||||
apiCall = { userApiService.register(registerRequest) },
|
||||
errorMessage = "注册成功,但未返回用户数据"
|
||||
).onSuccess { register ->
|
||||
register?.let {
|
||||
insertUserInfo(register, registerRequest.telephone)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
// return try {
|
||||
// val response = userApiService.register(registerRequest)
|
||||
// if (response.isSuccess()) {
|
||||
// val register = response.getResponseData()
|
||||
// register?.let {
|
||||
// val userInfo = UserInfo(
|
||||
// username = register.username,
|
||||
// nickname = register.nickname,
|
||||
// avatarUrl = "",
|
||||
// signature = "",
|
||||
// telephone = registerRequest.telephone
|
||||
// )
|
||||
// userInfoBox.put(userInfo)
|
||||
//
|
||||
// Result.success(register)
|
||||
// } ?: Result.failure(Exception("注册成功,但未返回用户数据"))
|
||||
// } else {
|
||||
// Result.failure(Exception(response.getResponseMsg()))
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Result.failure(e)
|
||||
// }
|
||||
}
|
||||
|
||||
// 登录方法
|
||||
suspend fun loginByUsername(username: String, password: String): Result<UserInfo> {
|
||||
return try {
|
||||
val response = userApiService.loginByUsername(username, password)
|
||||
if (response.isSuccess()) {
|
||||
val userInfo = response.getResponseData()
|
||||
if (userInfo != null) {
|
||||
updateDb(userInfo)
|
||||
Result.success(userInfo)
|
||||
} else {
|
||||
Result.failure(Exception("登录成功,但未返回用户数据"))
|
||||
}
|
||||
} else {
|
||||
Result.failure(Exception(response.getResponseMsg()))
|
||||
suspend fun loginByUsername(username: String, password: String): Result<UserInfo?> {
|
||||
return apiCall(
|
||||
apiCall = { userApiService.loginByUsername(username, password) },
|
||||
errorMessage = "登录成功,但未返回用户数据"
|
||||
).onSuccess { userInfo ->
|
||||
userInfo?.let {
|
||||
updateDb(userInfo)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
// return try {
|
||||
// val response = userApiService.loginByUsername(username, password)
|
||||
// if (response.isSuccess()) {
|
||||
// val userInfo = response.getResponseData()
|
||||
// if (userInfo != null) {
|
||||
// updateDb(userInfo)
|
||||
// Result.success(userInfo)
|
||||
// } else {
|
||||
// Result.failure(Exception("登录成功,但未返回用户数据"))
|
||||
// }
|
||||
// } else {
|
||||
// Result.failure(Exception(response.getResponseMsg()))
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Result.failure(e)
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
suspend fun loginByTelephone(telephone: String, password: String): Result<UserInfo?> {
|
||||
return apiCall(
|
||||
apiCall = { userApiService.loginByTelephone(telephone, password) },
|
||||
errorMessage = "登录成功,但未返回用户数据"
|
||||
).onSuccess { userInfo ->
|
||||
userInfo?.let {
|
||||
updateDb(userInfo)
|
||||
}
|
||||
}
|
||||
// return try {
|
||||
// val response = userApiService.loginByTelephone(telephone, password)
|
||||
// if (response.isSuccess()) {
|
||||
// val userInfo = response.getResponseData()
|
||||
// userInfo?.let {
|
||||
// updateDb(userInfo)
|
||||
// Result.success(userInfo)
|
||||
// } ?: Result.failure(Exception("登录成功,但未返回用户数据"))
|
||||
// } else {
|
||||
// Result.failure(Exception(response.getResponseMsg()))
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Result.failure(e)
|
||||
// }
|
||||
}
|
||||
|
||||
private fun insertUserInfo(register: Register, telephone: String) {
|
||||
val userInfo = UserInfo(
|
||||
username = register.username,
|
||||
nickname = register.nickname,
|
||||
avatarUrl = "",
|
||||
signature = "",
|
||||
telephone = telephone
|
||||
)
|
||||
userInfoBox.put(userInfo)
|
||||
}
|
||||
|
||||
private fun updateDb(userInfo: UserInfo) {
|
||||
@ -83,21 +137,4 @@ class UserAuthRepository {
|
||||
userInfoBox.put(userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loginByTelephone(telephone: String, password: String): Result<UserInfo> {
|
||||
return try {
|
||||
val response = userApiService.loginByTelephone(telephone, password)
|
||||
if (response.isSuccess()) {
|
||||
val userInfo = response.getResponseData()
|
||||
userInfo?.let {
|
||||
updateDb(userInfo)
|
||||
Result.success(userInfo)
|
||||
} ?: Result.failure(Exception("登录成功,但未返回用户数据"))
|
||||
} else {
|
||||
Result.failure(Exception(response.getResponseMsg()))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.kaixed.kchat.repository
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo_
|
||||
import com.kaixed.kchat.model.request.UserRequest
|
||||
@ -42,7 +42,7 @@ class UserProfileRepository {
|
||||
return try {
|
||||
val response = userApiService.changeNickname(userRequest)
|
||||
if (response.isSuccess()) {
|
||||
updateNickname(response.getResponseData().toString())
|
||||
updateNickname(userRequest.nickname!!)
|
||||
Result.success(true)
|
||||
} else {
|
||||
Result.failure(Exception(response.getResponseMsg()))
|
||||
@ -71,7 +71,7 @@ class UserProfileRepository {
|
||||
}
|
||||
|
||||
private fun updateNickname(newNickname: String) {
|
||||
val userInfoBox = get().boxFor(UserInfo::class.java)
|
||||
val userInfoBox = getBoxStore().boxFor(UserInfo::class.java)
|
||||
val user = userInfoBox
|
||||
.query(UserInfo_.username.equal(getUsername()))
|
||||
.build()
|
||||
@ -87,7 +87,7 @@ class UserProfileRepository {
|
||||
}
|
||||
|
||||
private fun updateDb(url: String) {
|
||||
val userInfoBox = get().boxFor(UserInfo::class.java)
|
||||
val userInfoBox = getBoxStore().boxFor(UserInfo::class.java)
|
||||
val userInfo =
|
||||
userInfoBox.query(UserInfo_.username.equal(getUsername())).build().findFirst()
|
||||
if (userInfo != null) {
|
||||
|
@ -8,7 +8,7 @@ import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.google.gson.Gson
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation_
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages
|
||||
@ -39,7 +39,7 @@ class WebSocketService : Service() {
|
||||
|
||||
private lateinit var messagesBox: Box<Messages>
|
||||
|
||||
private val conversationBox: Box<Conversation> by lazy { get().boxFor(Conversation::class.java) }
|
||||
private val conversationBox: Box<Conversation> by lazy { getBoxStore().boxFor(Conversation::class.java) }
|
||||
|
||||
private lateinit var username: String
|
||||
|
||||
@ -69,7 +69,7 @@ class WebSocketService : Service() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
messagesBox = get().boxFor(Messages::class.java)
|
||||
messagesBox = getBoxStore().boxFor(Messages::class.java)
|
||||
username = getUsername()
|
||||
firstLoad()
|
||||
}
|
||||
@ -113,7 +113,7 @@ class WebSocketService : Service() {
|
||||
if (conversation.talkerId == currentContactId || messages.senderId == getUsername()) 0
|
||||
else conversation.unreadCount + 1
|
||||
|
||||
conversation.lastContent = messages.content
|
||||
conversation.lastContent = if (messages.type == "4") "[图片]" else messages.content
|
||||
conversation.timestamp = messages.timestamp
|
||||
conversationBox.put(conversation)
|
||||
} ?: run {
|
||||
@ -157,8 +157,6 @@ class WebSocketService : Service() {
|
||||
|
||||
messages.takerId = messages.senderId
|
||||
|
||||
updateDbConversation(messages)
|
||||
|
||||
serviceScope.launch {
|
||||
if ("ack" == messages.type) {
|
||||
val existingMessage = messagesBox.get(messages.msgLocalId)
|
||||
@ -202,7 +200,7 @@ class WebSocketService : Service() {
|
||||
return
|
||||
}
|
||||
conversationList[index].apply {
|
||||
lastContent = messages.content
|
||||
lastContent = if (messages.type == "4") "[图片]" else messages.content
|
||||
timestamp = messages.timestamp
|
||||
unreadCount =
|
||||
if (this.talkerId == getCurrentContactId() || messages.senderId == getUsername()) 0 else unreadCount + 1
|
||||
@ -212,7 +210,7 @@ class WebSocketService : Service() {
|
||||
createChatList(
|
||||
talkerId = messages.takerId,
|
||||
nickname = messages.takerId,
|
||||
content = messages.content,
|
||||
content = if (messages.type == "4") "[图片]" else messages.content,
|
||||
timestamp = messages.timestamp
|
||||
)
|
||||
)
|
||||
|
@ -6,19 +6,27 @@ import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.kaixed.kchat.databinding.ActivityAddFriendsBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
|
||||
class AddFriendsActivity : BaseActivity<ActivityAddFriendsBinding>() {
|
||||
|
||||
override fun inflateBinding(): ActivityAddFriendsBinding {
|
||||
return ActivityAddFriendsBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
class AddFriendsActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityAddFriendsBinding
|
||||
private val mContext: Context by lazy { this }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = ActivityAddFriendsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
initView()
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.ivBack.setOnClickListener {
|
||||
finish()
|
||||
|
@ -26,6 +26,10 @@ class ApplyAddFriendActivity : BaseActivity<ActivityApplyAddFriendBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
}
|
||||
|
||||
private fun sendContactRequest(contactId: String?) {
|
||||
contactId?.let {
|
||||
contactViewModel.addContactResult
|
||||
|
@ -21,21 +21,22 @@ class ApplyFriendsDetailActivity : BaseActivity<ActivityApplyFriendsDetailBindin
|
||||
|
||||
private var user: User? = null
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
||||
user = intent.getParcelableExtra("user", User::class.java)
|
||||
request = intent.getParcelableExtra("request", FriendRequestItem::class.java)
|
||||
|
||||
setContent()
|
||||
|
||||
setOnClick()
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
override fun initData() {
|
||||
user = intent.getParcelableExtra("user", User::class.java)
|
||||
request = intent.getParcelableExtra("request", FriendRequestItem::class.java)
|
||||
}
|
||||
|
||||
private fun setContent() {
|
||||
// 使用 Elvis 操作符 ?:,如果 request 不为空则使用 request,否则使用 user
|
||||
val nickname = request?.nickname ?: user?.nickname
|
||||
val signature = request?.signature ?: user?.signature
|
||||
val avatarUrl = request?.avatarUrl ?: user?.avatarUrl
|
||||
|
@ -9,16 +9,17 @@ import androidx.activity.viewModels
|
||||
import com.kaixed.kchat.databinding.ActivityApproveContactRequestBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
|
||||
import com.kaixed.kchat.utils.handle.ContactUtil
|
||||
import com.kaixed.kchat.viewmodel.ContactViewModel
|
||||
|
||||
class ApproveContactRequestActivity : BaseActivity<ActivityApproveContactRequestBinding>() {
|
||||
|
||||
private val contactViewModel: ContactViewModel by viewModels()
|
||||
|
||||
private var nickname: String? = null
|
||||
private var message: String? = null
|
||||
private var contactId: String? = null
|
||||
private lateinit var nickname: String
|
||||
|
||||
private lateinit var message: String
|
||||
|
||||
private lateinit var contactId: String
|
||||
|
||||
override fun inflateBinding(): ActivityApproveContactRequestBinding {
|
||||
return ActivityApproveContactRequestBinding.inflate(layoutInflater)
|
||||
@ -28,17 +29,16 @@ class ApproveContactRequestActivity : BaseActivity<ActivityApproveContactRequest
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
||||
handleIntent(intent)
|
||||
|
||||
setContent()
|
||||
|
||||
setOnClickListener()
|
||||
}
|
||||
|
||||
private fun handleIntent(intent: Intent) {
|
||||
nickname = intent.getStringExtra("nickname")
|
||||
message = intent.getStringExtra("message")
|
||||
contactId = intent.getStringExtra("contactId")
|
||||
override fun initData() {
|
||||
nickname = intent.getStringExtra("nickname").toString()
|
||||
message = intent.getStringExtra("message").toString()
|
||||
contactId = intent.getStringExtra("contactId").toString()
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
|
@ -30,6 +30,10 @@ class ApproveDetailActivity : BaseActivity<ActivityApproveDetailBinding>() {
|
||||
setOnClick()
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
}
|
||||
|
||||
private fun handleIntent(intent: Intent) {
|
||||
request = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
intent.getParcelableExtra("request", FriendRequestItem::class.java)
|
||||
|
@ -24,7 +24,7 @@ import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages_
|
||||
import com.kaixed.kchat.databinding.ActivityChatBinding
|
||||
@ -99,10 +99,6 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
||||
handleIntent(intent)
|
||||
|
||||
initData()
|
||||
|
||||
firstLoadData()
|
||||
|
||||
initView()
|
||||
@ -153,7 +149,7 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -253,9 +249,6 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
|
||||
|
||||
private fun setListener() {
|
||||
setBackPressListener()
|
||||
binding.tvContactName.setOnLongClickListener {
|
||||
false
|
||||
}
|
||||
|
||||
binding.gvFunctionPanel.selector = ColorDrawable(Color.TRANSPARENT)
|
||||
|
||||
@ -277,26 +270,24 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
|
||||
// }
|
||||
}
|
||||
})
|
||||
binding.ivBack.setOnClickListener { finish() }
|
||||
|
||||
binding.tvContactName.setOnClickListener {
|
||||
binding.ctb.setOnTitleClickListener {
|
||||
startActivity(
|
||||
Intent(
|
||||
context,
|
||||
ContactsDetailActivity::class.java
|
||||
)
|
||||
Intent(context, ContactsDetailActivity::class.java).apply {
|
||||
putExtra("contactId", contactId)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
binding.ivMore.setOnClickListener {
|
||||
binding.ctb.setOnSettingClickListener {
|
||||
startActivity(
|
||||
Intent(
|
||||
context,
|
||||
ChatDetailActivity::class.java
|
||||
)
|
||||
Intent(context, ChatDetailActivity::class.java).apply {
|
||||
putExtra("contactId", contactId)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
binding.etInput.addTextChangedListener(
|
||||
afterTextChanged = { _ ->
|
||||
val isInputEmpty = binding.etInput.text.toString().isEmpty()
|
||||
@ -339,12 +330,7 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
|
||||
|
||||
val functionItems = mutableListOf<FunctionItem>()
|
||||
for (i in map.keys) {
|
||||
functionItems.add(
|
||||
FunctionItem(
|
||||
i,
|
||||
map[i]!!
|
||||
)
|
||||
)
|
||||
functionItems.add(FunctionItem(i, map[i]!!))
|
||||
}
|
||||
|
||||
val functionPanelAdapter = FunctionPanelAdapter(functionItems, this)
|
||||
@ -375,7 +361,7 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
|
||||
setupFunctionPanel()
|
||||
setRecycleView()
|
||||
contactNickname?.let {
|
||||
binding.tvContactName.text = contactNickname
|
||||
binding.ctb.setTitleName(contactNickname!!)
|
||||
}
|
||||
setPanel()
|
||||
}
|
||||
@ -386,18 +372,16 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
|
||||
binding.clBottomPanel.layoutParams = layoutParams
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
messagesBox = get().boxFor(Messages::class.java)
|
||||
softKeyboardHeight = getKeyboardHeight()
|
||||
mmkv.putString(CURRENT_CONTACT_ID, contactId)
|
||||
}
|
||||
|
||||
private fun handleIntent(intent: Intent?) {
|
||||
override fun initData() {
|
||||
intent?.let {
|
||||
contactId = intent.getStringExtra("contactId").toString()
|
||||
contactNickname = intent.getStringExtra("contactNickname")
|
||||
binding.tvContactName.text = contactNickname
|
||||
binding.ctb.setTitleName(contactNickname!!)
|
||||
}
|
||||
|
||||
messagesBox = getBoxStore().boxFor(Messages::class.java)
|
||||
softKeyboardHeight = getKeyboardHeight()
|
||||
mmkv.putString(CURRENT_CONTACT_ID, contactId)
|
||||
}
|
||||
|
||||
private fun setRecycleView() {
|
||||
|
@ -3,21 +3,44 @@ package com.kaixed.kchat.ui.activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.bumptech.glide.Glide
|
||||
import com.kaixed.kchat.data.objectbox.data.LocalContact.getContactByUsername
|
||||
import com.kaixed.kchat.databinding.ActivityChatDetailBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
|
||||
class ChatDetailActivity : BaseActivity<ActivityChatDetailBinding>() {
|
||||
|
||||
private lateinit var contactId: String
|
||||
|
||||
private val contact by lazy { getContactByUsername(contactId) }
|
||||
|
||||
override fun inflateBinding(): ActivityChatDetailBinding {
|
||||
return ActivityChatDetailBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
class ChatDetailActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityChatDetailBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = ActivityChatDetailBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
binding.ifvAvatar.setOnClickListener { v ->
|
||||
initView()
|
||||
|
||||
setListener()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
Glide.with(this).load(contact?.avatarUrl).into(binding.ifvAvatar)
|
||||
binding.tvContactName.text = contact?.remark ?: contact?.nickname
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
contactId = intent.getStringExtra("contactId") ?: ""
|
||||
}
|
||||
|
||||
private fun setListener() {
|
||||
binding.ifvAvatar.setOnClickListener {
|
||||
val intent =
|
||||
Intent(this@ChatDetailActivity, ContactsDetailActivity::class.java)
|
||||
intent.putExtra("friendId", "kaixed")
|
||||
Intent(this, ContactsDetailActivity::class.java)
|
||||
intent.putExtra("contactId", "kaixed")
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import com.kaixed.kchat.databinding.ActivityContactPermissionBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
|
||||
class ContactPermissionActivity : BaseActivity<ActivityContactPermissionBinding>() {
|
||||
|
||||
override fun inflateBinding(): ActivityContactPermissionBinding {
|
||||
return ActivityContactPermissionBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
||||
initView()
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.ciChat.setYesSelected(false)
|
||||
}
|
||||
}
|
@ -38,6 +38,10 @@ class ContactRequestListActivity : BaseActivity<ActivityContactRequestListBindin
|
||||
binding.recycleFriendRequestList.adapter = contactRequestListAdapter
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
}
|
||||
|
||||
private fun getItems() {
|
||||
contactViewModel.contactRequestListResult
|
||||
.observe(this) { result ->
|
||||
|
@ -5,7 +5,7 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact_
|
||||
import com.kaixed.kchat.databinding.ActivityContactsDetailBinding
|
||||
@ -14,7 +14,7 @@ import io.objectbox.Box
|
||||
|
||||
class ContactsDetailActivity : BaseActivity<ActivityContactsDetailBinding>() {
|
||||
|
||||
private val contactBox: Box<Contact> by lazy { get().boxFor(Contact::class.java) }
|
||||
private val contactBox: Box<Contact> by lazy { getBoxStore().boxFor(Contact::class.java) }
|
||||
|
||||
private var contactId: String? = null
|
||||
|
||||
@ -33,25 +33,36 @@ class ContactsDetailActivity : BaseActivity<ActivityContactsDetailBinding>() {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
||||
contactId = intent.getStringExtra("contactId")
|
||||
|
||||
val contact = getUserInfo(contactId!!)
|
||||
if (contact != null) {
|
||||
binding.tvContactId.text = "kid: ${contact.username}"
|
||||
binding.tvContactSignature.text = contact.signature
|
||||
// Log.d(TAG, "onCreate: $contact")
|
||||
if (contact.remark?.isNotEmpty() == true) {
|
||||
binding.tvRemark.text = contact.remark
|
||||
contactNickname = contact.remark
|
||||
binding.tvContactName.text = "昵称:${contact.nickname}"
|
||||
} else {
|
||||
contactNickname = contact.nickname
|
||||
binding.tvRemark.text = contact.nickname
|
||||
binding.tvContactName.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
setOnListener()
|
||||
|
||||
updateContent()
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
contactId = intent.getStringExtra("contactId")
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun updateContent() {
|
||||
val contact: Contact by lazy {
|
||||
getContactInfo(contactId!!)
|
||||
}
|
||||
binding.tvContactId.text = "kid: $contactId"
|
||||
binding.tvContactSignature.text = contact.signature
|
||||
if (contact.remark?.isNotEmpty() == true) {
|
||||
binding.tvRemark.text = contact.remark
|
||||
contactNickname = contact.remark
|
||||
binding.tvContactName.text = "昵称:${contact.nickname}"
|
||||
} else {
|
||||
contactNickname = contact.nickname
|
||||
binding.tvRemark.text = contact.nickname
|
||||
binding.tvContactName.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateContent()
|
||||
}
|
||||
|
||||
private fun setOnListener() {
|
||||
@ -74,12 +85,18 @@ class ContactsDetailActivity : BaseActivity<ActivityContactsDetailBinding>() {
|
||||
putExtra("contactId", contactId)
|
||||
})
|
||||
}
|
||||
|
||||
binding.ciPermission.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(this, ContactPermissionActivity::class.java)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUserInfo(contactId: String): Contact? {
|
||||
private fun getContactInfo(contactId: String): Contact {
|
||||
return contactBox
|
||||
.query(Contact_.username.equal(contactId))
|
||||
.build()
|
||||
.findFirst()
|
||||
.findFirst()!!
|
||||
}
|
||||
}
|
||||
|
@ -5,23 +5,18 @@ import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.databinding.ActivityDataSettingBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import com.kaixed.kchat.ui.fragment.DeleteContactDialogFragment
|
||||
import com.kaixed.kchat.ui.fragment.LoadingDialogFragment
|
||||
import com.kaixed.kchat.ui.widget.DeleteContactDialogFragment
|
||||
import com.kaixed.kchat.ui.widget.LoadingDialogFragment
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
|
||||
import com.kaixed.kchat.viewmodel.ContactViewModel
|
||||
import io.objectbox.Box
|
||||
|
||||
class DataSettingActivity : BaseActivity<ActivityDataSettingBinding>(),
|
||||
DeleteContactDialogFragment.OnContactDeletedListener {
|
||||
|
||||
private val contactViewModel by viewModels<ContactViewModel>()
|
||||
|
||||
private val contactBox: Box<Contact> by lazy { get().boxFor(Contact::class.java) }
|
||||
|
||||
private var contactId = ""
|
||||
|
||||
private val loadingDialogFragment by lazy { LoadingDialogFragment.newInstance("加载中...") }
|
||||
@ -38,16 +33,35 @@ class DataSettingActivity : BaseActivity<ActivityDataSettingBinding>(),
|
||||
handleIntent(intent)
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
}
|
||||
|
||||
private fun handleIntent(intent: Intent) {
|
||||
contactId = intent.getStringExtra("contactId") ?: ""
|
||||
}
|
||||
|
||||
private fun setOnClick() {
|
||||
binding.ciSetRemarkAndLabel.setOnClickListener {
|
||||
val intent = Intent(this, SetRemarkActivity::class.java)
|
||||
val intent = Intent(this, SetRemarkAndLabelActivity::class.java).apply {
|
||||
putExtra("contactId", contactId)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.ciPermission.setOnClickListener {
|
||||
val intent = Intent(this, ContactPermissionActivity::class.java).apply {
|
||||
putExtra("contactId", contactId)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.ciRecommendToContact.setOnClickListener { toast() }
|
||||
|
||||
binding.ciComplaints.setOnClickListener {
|
||||
toast()
|
||||
}
|
||||
|
||||
binding.tvDelete.setOnClickListener {
|
||||
val deleteDialogFragment = DeleteContactDialogFragment.newInstance(contactId)
|
||||
deleteDialogFragment.show(supportFragmentManager, "DeleteContactDialogFragment")
|
||||
|
@ -34,6 +34,10 @@ class LaunchActivity : BaseActivity<ActivityLaunchBinding>() {
|
||||
setOnClickListener()
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("DiscouragedApi", "InternalInsetResource")
|
||||
private fun getStatusBarHeight(): Int {
|
||||
|
@ -10,30 +10,21 @@ import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo_
|
||||
import com.kaixed.kchat.databinding.ActivityLoginBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import com.kaixed.kchat.utils.Constants.KEYBOARD_HEIGHT_RATIO
|
||||
import com.kaixed.kchat.utils.Constants.MMKV_COMMON_DATA
|
||||
import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION
|
||||
import com.kaixed.kchat.utils.DensityUtil.dpToPx
|
||||
import com.kaixed.kchat.utils.DrawableUtil.createDrawable
|
||||
import com.kaixed.kchat.viewmodel.UserViewModel
|
||||
import com.tencent.mmkv.MMKV
|
||||
import io.objectbox.Box
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class LoginActivity : BaseActivity<ActivityLoginBinding>() {
|
||||
|
||||
private val userViewModel: UserViewModel by viewModels()
|
||||
|
||||
|
||||
private var previousKeyboardHeight = 0
|
||||
|
||||
private var loginByUsername = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -56,6 +47,10 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>() {
|
||||
getKeyboardHeight()
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
}
|
||||
|
||||
private fun login(username: String, password: String) {
|
||||
if (!loginByUsername) {
|
||||
binding.etUsername.text.toString().length != 11
|
||||
|
@ -11,7 +11,7 @@ import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.databinding.ActivityMainBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
@ -19,10 +19,9 @@ import com.kaixed.kchat.ui.fragment.ContactFragment
|
||||
import com.kaixed.kchat.ui.fragment.DiscoveryFragment
|
||||
import com.kaixed.kchat.ui.fragment.HomeFragment
|
||||
import com.kaixed.kchat.ui.fragment.MineFragment
|
||||
import com.kaixed.kchat.ui.widget.LoadingDialogFragment
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.isFirstLaunchApp
|
||||
import com.kaixed.kchat.utils.Pinyin4jUtil
|
||||
import com.kaixed.kchat.utils.WidgetUtil
|
||||
import com.kaixed.kchat.viewmodel.ContactViewModel
|
||||
import io.objectbox.Box
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -33,9 +32,11 @@ import kotlinx.coroutines.launch
|
||||
class MainActivity : BaseActivity<ActivityMainBinding>(), View.OnClickListener {
|
||||
|
||||
private val colorPrimary: Int by lazy { ContextCompat.getColor(this, R.color.green) }
|
||||
|
||||
private val colorBlack: Int by lazy { ContextCompat.getColor(this, R.color.black) }
|
||||
|
||||
private val contactBox: Box<Contact> by lazy { get().boxFor(Contact::class.java) }
|
||||
private val contactBox: Box<Contact> by lazy { getBoxStore().boxFor(Contact::class.java) }
|
||||
|
||||
private val contactViewModel: ContactViewModel by viewModels()
|
||||
|
||||
companion object {
|
||||
@ -65,17 +66,19 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), View.OnClickListener {
|
||||
enableEdgeToEdge()
|
||||
|
||||
initView()
|
||||
|
||||
initViewPager()
|
||||
|
||||
updateSelection(KEY_HOME_FRAGMENT)
|
||||
initData()
|
||||
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
override fun initData() {
|
||||
if (isFirstLaunchApp()) {
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
val dialog = WidgetUtil.showLoadingDialog(this@MainActivity, "同步数据中")
|
||||
dialog.show()
|
||||
val loadingDialogFragment = LoadingDialogFragment.newInstance("同步数据中")
|
||||
delay(200)
|
||||
loadingDialogFragment.showLoading(supportFragmentManager)
|
||||
|
||||
contactViewModel.contactListResult.observe(this@MainActivity) { result ->
|
||||
result.onSuccess {
|
||||
@ -85,7 +88,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), View.OnClickListener {
|
||||
|
||||
contactViewModel.loadFriendList(getUsername())
|
||||
delay(2000)
|
||||
dialog.dismiss()
|
||||
loadingDialogFragment.dismissLoading()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,7 +108,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), View.OnClickListener {
|
||||
}
|
||||
|
||||
private fun initViewPager() {
|
||||
// 使用 FragmentStateAdapter 构造函数
|
||||
viewPagerAdapter = object : FragmentStateAdapter(this) {
|
||||
override fun getItemCount(): Int {
|
||||
return fragments.size
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
@ -14,15 +13,15 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo_
|
||||
import com.kaixed.kchat.databinding.ActivityProfileDetailBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import com.kaixed.kchat.ui.widget.LoadingDialogFragment
|
||||
import com.kaixed.kchat.utils.Constants.AVATAR_URL
|
||||
import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
|
||||
import com.kaixed.kchat.utils.WidgetUtil
|
||||
import com.kaixed.kchat.viewmodel.UserViewModel
|
||||
import com.tencent.mmkv.MMKV
|
||||
import io.objectbox.Box
|
||||
@ -34,7 +33,7 @@ import java.io.File
|
||||
|
||||
class ProfileDetailActivity : BaseActivity<ActivityProfileDetailBinding>() {
|
||||
|
||||
private val userInfoBox: Box<UserInfo> by lazy { get().boxFor(UserInfo::class.java) }
|
||||
private val userInfoBox: Box<UserInfo> by lazy { getBoxStore().boxFor(UserInfo::class.java) }
|
||||
|
||||
private val userSessionMMKV by lazy { MMKV.mmkvWithID(MMKV_USER_SESSION) }
|
||||
|
||||
@ -76,11 +75,17 @@ class ProfileDetailActivity : BaseActivity<ActivityProfileDetailBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
}
|
||||
|
||||
private fun updateAvatar(uri: Uri) {
|
||||
val filePath = getPathFromUri(uri)
|
||||
|
||||
val dialog = WidgetUtil.showLoadingDialog(this, "正在上传")
|
||||
dialog.show()
|
||||
val loadingDialogFragment = LoadingDialogFragment.newInstance("正在上传")
|
||||
|
||||
loadingDialogFragment.showLoading(supportFragmentManager)
|
||||
|
||||
userViewModel.uploadAvatarResult.observe(this) { result ->
|
||||
result.onSuccess {
|
||||
userSessionMMKV.putString(AVATAR_URL, it)
|
||||
@ -93,8 +98,8 @@ class ProfileDetailActivity : BaseActivity<ActivityProfileDetailBinding>() {
|
||||
}
|
||||
}
|
||||
val file = File(filePath!!)
|
||||
|
||||
userViewModel.uploadAvatar(username = getUsername(), file = prepareFilePart(file))
|
||||
loadingDialogFragment.dismissLoading()
|
||||
}
|
||||
|
||||
private fun prepareFilePart(file: File): MultipartBody.Part {
|
||||
@ -103,8 +108,6 @@ class ProfileDetailActivity : BaseActivity<ActivityProfileDetailBinding>() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private fun getPathFromUri(uri: Uri): String? {
|
||||
val projection = arrayOf(MediaStore.Images.Media.DATA)
|
||||
val cursor = contentResolver.query(uri, projection, null, null, null)
|
||||
|
@ -14,7 +14,7 @@ import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.databinding.ActivityRegisterBinding
|
||||
import com.kaixed.kchat.model.request.RegisterRequest
|
||||
@ -30,7 +30,7 @@ class RegisterActivity : BaseActivity<ActivityRegisterBinding>() {
|
||||
|
||||
private val userViewModel: UserViewModel by viewModels()
|
||||
|
||||
private val userInfoBox: Box<UserInfo> by lazy { get().boxFor(UserInfo::class.java) }
|
||||
private val userInfoBox: Box<UserInfo> by lazy { getBoxStore().boxFor(UserInfo::class.java) }
|
||||
|
||||
override fun inflateBinding(): ActivityRegisterBinding {
|
||||
return ActivityRegisterBinding.inflate(layoutInflater)
|
||||
@ -45,6 +45,10 @@ class RegisterActivity : BaseActivity<ActivityRegisterBinding>() {
|
||||
setOnClickListener()
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
}
|
||||
|
||||
private fun setOnClickListener() {
|
||||
binding.ivClose.setOnClickListener {
|
||||
finish()
|
||||
|
@ -6,20 +6,20 @@ import android.os.Looper
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.databinding.ActivityRenameBinding
|
||||
import com.kaixed.kchat.model.request.UserRequest
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import com.kaixed.kchat.ui.widget.LoadingDialogFragment
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.getNickName
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
|
||||
import com.kaixed.kchat.utils.WidgetUtil
|
||||
import com.kaixed.kchat.viewmodel.UserViewModel
|
||||
import io.objectbox.Box
|
||||
|
||||
class RenameActivity : BaseActivity<ActivityRenameBinding>() {
|
||||
|
||||
private val userInfoBox: Box<UserInfo> by lazy { get().boxFor(UserInfo::class.java) }
|
||||
private val userInfoBox: Box<UserInfo> by lazy { getBoxStore().boxFor(UserInfo::class.java) }
|
||||
|
||||
private val userViewModel: UserViewModel by viewModels()
|
||||
|
||||
@ -36,8 +36,6 @@ class RenameActivity : BaseActivity<ActivityRenameBinding>() {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
||||
oldNickname = getNickName()
|
||||
|
||||
val nickname = oldNickname
|
||||
var enable = false
|
||||
|
||||
@ -58,10 +56,14 @@ class RenameActivity : BaseActivity<ActivityRenameBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
oldNickname = getNickName()
|
||||
}
|
||||
|
||||
private fun updateNicknamesByCondition(newNickname: String, username: String) {
|
||||
binding.ctb.setBtnEnable(false)
|
||||
val dialog = WidgetUtil.showLoadingDialog(this, "正在保存")
|
||||
dialog.show()
|
||||
val dialog: LoadingDialogFragment = LoadingDialogFragment.newInstance("正在保存")
|
||||
dialog.showLoading(supportFragmentManager)
|
||||
userViewModel.changeNicknameResult.observe(this) { result ->
|
||||
|
||||
result.onSuccess {
|
||||
@ -76,21 +78,20 @@ class RenameActivity : BaseActivity<ActivityRenameBinding>() {
|
||||
|
||||
binding.etNickname.setSelection(binding.etNickname.text.length)
|
||||
}
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
dialog.dismiss()
|
||||
toast(if (updateSucceed) "更新成功" else "更新失败")
|
||||
}, 800)
|
||||
|
||||
val userRequest = UserRequest(
|
||||
username = username,
|
||||
nickname = newNickname
|
||||
)
|
||||
|
||||
userViewModel.changeNickname(userRequest)
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
dialog.dismissLoading()
|
||||
toast(if (updateSucceed) "更新成功" else "更新失败")
|
||||
}, 800)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
binding.etNickname.setText(getNickName())
|
||||
val nickname = getNickName()
|
||||
binding.etNickname.setText(nickname)
|
||||
}
|
||||
}
|
@ -12,21 +12,23 @@ import androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory
|
||||
import com.kaixed.kchat.databinding.ActivitySearchBinding
|
||||
import com.kaixed.kchat.model.search.SearchItem
|
||||
import com.kaixed.kchat.ui.adapter.SearchResultAdapter
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
|
||||
class SearchActivity : AppCompatActivity() {
|
||||
private var binding: ActivitySearchBinding? = null
|
||||
class SearchActivity : BaseActivity<ActivitySearchBinding>() {
|
||||
|
||||
override fun inflateBinding(): ActivitySearchBinding {
|
||||
return ActivitySearchBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.enableEdgeToEdge()
|
||||
binding = ActivitySearchBinding.inflate(layoutInflater)
|
||||
setContentView(binding!!.root)
|
||||
|
||||
setOnClickListener()
|
||||
|
||||
setupRecycleView()
|
||||
|
||||
binding!!.etSearch.addTextChangedListener(
|
||||
binding.etSearch.addTextChangedListener(
|
||||
afterTextChanged = { s ->
|
||||
s?.let {
|
||||
setupViewVisibility(s.length)
|
||||
@ -35,9 +37,13 @@ class SearchActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
}
|
||||
|
||||
private fun setupViewVisibility(length: Int) {
|
||||
binding!!.rvSearchResult.visibility = if (length > 0) View.VISIBLE else View.INVISIBLE
|
||||
binding!!.tvPageSetting.visibility = if (length > 0) View.INVISIBLE else View.VISIBLE
|
||||
binding.rvSearchResult.visibility = if (length > 0) View.VISIBLE else View.INVISIBLE
|
||||
binding.tvPageSetting.visibility = if (length > 0) View.INVISIBLE else View.VISIBLE
|
||||
}
|
||||
|
||||
private fun setupRecycleView() {
|
||||
@ -60,13 +66,13 @@ class SearchActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
val searchResultAdapter: SearchResultAdapter = getAdapter()
|
||||
binding!!.rvSearchResult.adapter = searchResultAdapter
|
||||
binding.rvSearchResult.adapter = searchResultAdapter
|
||||
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
binding!!.rvSearchResult.layoutManager = layoutManager
|
||||
binding.rvSearchResult.layoutManager = layoutManager
|
||||
}
|
||||
|
||||
private fun setOnClickListener() {
|
||||
binding!!.tvCancel.setOnClickListener { v ->
|
||||
binding.tvCancel.setOnClickListener { v ->
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,11 @@ import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import com.kaixed.kchat.viewmodel.ContactViewModel
|
||||
|
||||
class SearchFriendsActivity : BaseActivity<ActivitySearchFriendsBinding>() {
|
||||
|
||||
private var isSearching = false
|
||||
|
||||
private lateinit var userItem: User
|
||||
|
||||
private lateinit var loadingDialog: Dialog
|
||||
|
||||
override fun inflateBinding(): ActivitySearchFriendsBinding {
|
||||
@ -43,6 +46,10 @@ class SearchFriendsActivity : BaseActivity<ActivitySearchFriendsBinding>() {
|
||||
setOnClick()
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
}
|
||||
|
||||
private fun setOnClick() {
|
||||
binding.clFriends.setOnClickListener {
|
||||
val intent = Intent(this, ApplyFriendsDetailActivity::class.java)
|
||||
|
@ -1,15 +1,47 @@
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.data.LocalContact
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact_
|
||||
import com.kaixed.kchat.databinding.ActivitySetRemarkAndLabelBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import com.kaixed.kchat.ui.widget.LoadingDialogFragment
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
|
||||
import com.kaixed.kchat.viewmodel.ContactViewModel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SetRemarkAndLabelActivity : BaseActivity<ActivitySetRemarkAndLabelBinding>() {
|
||||
|
||||
private val contactBox by lazy {
|
||||
getBoxStore().boxFor(Contact::class.java)
|
||||
}
|
||||
|
||||
private var contactId: String? = null
|
||||
|
||||
private var hasFocus = false
|
||||
|
||||
private var remark = ""
|
||||
|
||||
private val contact: Contact? by lazy {
|
||||
LocalContact.getContactByUsername(contactId!!)
|
||||
}
|
||||
|
||||
private val contactViewModel by lazy { ContactViewModel() }
|
||||
|
||||
override fun inflateBinding(): ActivitySetRemarkAndLabelBinding {
|
||||
return ActivitySetRemarkAndLabelBinding.inflate(layoutInflater)
|
||||
}
|
||||
@ -18,5 +50,94 @@ class SetRemarkAndLabelActivity : BaseActivity<ActivitySetRemarkAndLabelBinding>
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
||||
updateContent()
|
||||
|
||||
setOnListener()
|
||||
}
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
contactId = intent?.getStringExtra("contactId") ?: ""
|
||||
remark = contact?.remark ?: contact?.nickname ?: ""
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateContent()
|
||||
}
|
||||
|
||||
private fun updateContent() {
|
||||
binding.etRemark.setText(remark)
|
||||
updateByFocus()
|
||||
if (hasFocus) {
|
||||
binding.etRemark.setSelection(remark.length)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setOnListener() {
|
||||
binding.etRemark.setOnFocusChangeListener { _, hasFocus ->
|
||||
this.hasFocus = hasFocus
|
||||
updateByFocus()
|
||||
}
|
||||
|
||||
binding.ivClean.setOnClickListener {
|
||||
remark = ""
|
||||
binding.etRemark.setText(remark)
|
||||
binding.ivClean.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
binding.ctb.setOnBtnClickListener {
|
||||
updateRemark()
|
||||
}
|
||||
|
||||
binding.etRemark.addTextChangedListener(afterTextChanged = { text ->
|
||||
remark = text.toString()
|
||||
})
|
||||
|
||||
binding.cvAddLabel.setOnClickListener { toast() }
|
||||
|
||||
binding.cvAddTelephone.setOnClickListener { toast() }
|
||||
}
|
||||
|
||||
private fun hideKeyboard(view: View) {
|
||||
view.clearFocus()
|
||||
val imm = this.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||
imm?.hideSoftInputFromWindow(view.windowToken, 0)
|
||||
}
|
||||
|
||||
private fun updateRemark() {
|
||||
hideKeyboard(binding.etRemark)
|
||||
|
||||
val dialog = LoadingDialogFragment.newInstance("正在保存")
|
||||
dialog.showLoading(supportFragmentManager)
|
||||
|
||||
contactViewModel.setRemarkResult.observe(this) { result ->
|
||||
result.onSuccess {
|
||||
val deleteIntent = Intent("com.kaixed.kchat.broadcast.UPDATE_CONTACT_LIST")
|
||||
deleteIntent.putExtra("update", true)
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(deleteIntent)
|
||||
lifecycleScope.launch {
|
||||
delay(1000)
|
||||
dialog.dismissLoading()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
result.onFailure {
|
||||
toast(it.message.toString())
|
||||
dialog.dismissLoading()
|
||||
}
|
||||
|
||||
}
|
||||
contactViewModel.setRemark(getUsername(), contactId!!, remark)
|
||||
}
|
||||
|
||||
private fun updateByFocus() {
|
||||
if (hasFocus) {
|
||||
binding.etRemark.setTextColor(ContextCompat.getColor(this, R.color.black))
|
||||
binding.ivClean.visibility = View.VISIBLE
|
||||
binding.ctb.setBtnEnable(true)
|
||||
} else {
|
||||
binding.etRemark.setTextColor(Color.parseColor("#ACACAC"))
|
||||
binding.ivClean.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import com.kaixed.kchat.databinding.ActivityTestBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import com.kaixed.kchat.utils.ImageEngines
|
||||
import com.luck.picture.lib.basic.PictureSelector
|
||||
import com.luck.picture.lib.config.SelectMimeType
|
||||
import com.luck.picture.lib.entity.LocalMedia
|
||||
import com.luck.picture.lib.interfaces.OnResultCallbackListener
|
||||
|
||||
|
||||
class TestActivity : BaseActivity<ActivityTestBinding>() {
|
||||
|
||||
override fun inflateBinding(): ActivityTestBinding {
|
||||
return ActivityTestBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
|
||||
PictureSelector.create(this)
|
||||
.openGallery(SelectMimeType.ofImage())
|
||||
.setImageEngine(ImageEngines())
|
||||
.forResult(object : OnResultCallbackListener<LocalMedia?> {
|
||||
override fun onResult(result: ArrayList<LocalMedia?>) {
|
||||
result.forEach { localMedia ->
|
||||
localMedia?.let {
|
||||
val imagePath = it.realPath
|
||||
toast("图片路径: $imagePath")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCancel() {
|
||||
toast("已取消")
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
@ -1,25 +1,23 @@
|
||||
package com.kaixed.kchat.ui.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupWindow
|
||||
import androidx.core.view.updateMargins
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.drake.spannable.replaceSpan
|
||||
import com.drake.spannable.span.CenterImageSpan
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemCustomMineBinding
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemCustomOtherBinding
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemImageMineBinding
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemImageOtherBinding
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemTipBinding
|
||||
import com.kaixed.kchat.databinding.PopwindowsBinding
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
|
||||
import com.kaixed.kchat.utils.DensityUtil.dpToPx
|
||||
import com.kaixed.kchat.utils.ImageSpanUtil
|
||||
import com.kaixed.kchat.utils.PopWindowUtil.showPopupWindow
|
||||
import com.kaixed.kchat.utils.TextUtil
|
||||
import io.objectbox.Box
|
||||
import java.util.LinkedList
|
||||
@ -79,6 +77,23 @@ class ChatAdapter(
|
||||
)
|
||||
}
|
||||
|
||||
ITEM_TYPE_IMAGE_OTHER -> {
|
||||
ImageViewOtherHolder(
|
||||
ChatRecycleItemImageOtherBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ITEM_TYPE_IMAGE_MINE -> {
|
||||
ImageViewMineHolder(
|
||||
ChatRecycleItemImageMineBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
else -> {
|
||||
TipViewHolder(
|
||||
ChatRecycleItemTipBinding.inflate(
|
||||
@ -93,6 +108,23 @@ class ChatAdapter(
|
||||
val singleMessage = messages[position]
|
||||
|
||||
when (holder) {
|
||||
is ImageViewOtherHolder -> {
|
||||
Glide.with(context).load(singleMessage.content).into(holder.binding.image)
|
||||
if (position == messages.size - 1) {
|
||||
holder.binding.tvTimer.visibility = View.VISIBLE
|
||||
holder.binding.tvTimer.text =
|
||||
TextUtil.getTimestampString(singleMessage.timestamp)
|
||||
} else {
|
||||
if (singleMessage.timestamp - messages[position + 1].timestamp < 1L * 60 * 1000) {
|
||||
holder.binding.tvTimer.visibility = View.GONE
|
||||
} else {
|
||||
holder.binding.tvTimer.visibility = View.VISIBLE
|
||||
holder.binding.tvTimer.text =
|
||||
TextUtil.getTimestampString(singleMessage.timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is TipViewHolder -> {
|
||||
holder.bindData(singleMessage)
|
||||
}
|
||||
@ -113,13 +145,16 @@ class ChatAdapter(
|
||||
}
|
||||
holder.binding.tvMessageContent.setOnLongClickListener {
|
||||
showPopupWindow(
|
||||
holder.binding.tvMessageContent, singleMessage.senderId == getUsername()
|
||||
context,
|
||||
holder.binding.tvMessageContent,
|
||||
singleMessage.senderId == getUsername()
|
||||
)
|
||||
true
|
||||
}
|
||||
holder.binding.tvMessageContent.text = ImageSpanUtil.setEmojiSpan(
|
||||
context, singleMessage.content, dpToPx(18)
|
||||
)
|
||||
|
||||
holder.binding.tvMessageContent.text = singleMessage.content.replaceSpan("[委屈]") {
|
||||
CenterImageSpan(context, R.drawable.emoji).setDrawableSize(55)
|
||||
}
|
||||
}
|
||||
|
||||
is CustomViewOtherHolder -> {
|
||||
@ -138,13 +173,17 @@ class ChatAdapter(
|
||||
}
|
||||
holder.binding.tvMessageContent.setOnLongClickListener {
|
||||
showPopupWindow(
|
||||
holder.binding.tvMessageContent, singleMessage.senderId == getUsername()
|
||||
context,
|
||||
holder.binding.tvMessageContent,
|
||||
singleMessage.senderId == getUsername()
|
||||
)
|
||||
true
|
||||
}
|
||||
holder.binding.tvMessageContent.text = ImageSpanUtil.setEmojiSpan(
|
||||
context, singleMessage.content, dpToPx(18)
|
||||
)
|
||||
|
||||
|
||||
holder.binding.tvMessageContent.text = singleMessage.content.replaceSpan("[委屈]") {
|
||||
CenterImageSpan(context, R.drawable.emoji).setDrawableSize(55)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,6 +205,12 @@ class ChatAdapter(
|
||||
class CustomViewOtherHolder(val binding: ChatRecycleItemCustomOtherBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class ImageViewMineHolder(val binding: ChatRecycleItemImageMineBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class ImageViewOtherHolder(val binding: ChatRecycleItemImageOtherBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
val message = messages[position]
|
||||
return if (message.senderId == getUsername()) {
|
||||
@ -176,100 +221,10 @@ class ChatAdapter(
|
||||
}
|
||||
|
||||
private fun updateDb(message: Messages) {
|
||||
val messagesBox: Box<Messages> = ObjectBox.get().boxFor(Messages::class.java)
|
||||
val messagesBox: Box<Messages> = ObjectBox.getBoxStore().boxFor(Messages::class.java)
|
||||
messagesBox.put(message)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = messages.size
|
||||
|
||||
private fun showPopupWindow(parentView: View, isMine: Boolean) {
|
||||
val binding: PopwindowsBinding = PopwindowsBinding.inflate(LayoutInflater.from(context))
|
||||
val popupWindow: PopupWindow = PopupWindow(
|
||||
binding.root, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
).apply {
|
||||
isFocusable = true
|
||||
isOutsideTouchable = true
|
||||
setBackgroundDrawable(ColorDrawable())
|
||||
}
|
||||
|
||||
// 获取屏幕的总宽度
|
||||
val screenWidth = context.resources.displayMetrics.widthPixels
|
||||
val screenHeight = context.resources.displayMetrics.heightPixels
|
||||
|
||||
val location = IntArray(2)
|
||||
parentView.getLocationOnScreen(location)
|
||||
binding.root.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
|
||||
|
||||
// popupWindow的长度
|
||||
val popupWidth = binding.root.measuredWidth
|
||||
val popupHeight = binding.root.measuredHeight
|
||||
|
||||
val parentWidth = parentView.width
|
||||
val parentHeight = parentView.height
|
||||
|
||||
val parentX = location[0]
|
||||
|
||||
// 父布局中心点横坐标
|
||||
val distanceToLeftEdge = parentX + parentView.width / 2
|
||||
// 中心点距离右侧距离
|
||||
val distanceToRightEdge = screenWidth - distanceToLeftEdge
|
||||
|
||||
|
||||
binding.ivArrowDown.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
|
||||
val arrowWidth = binding.ivArrowDown.measuredWidth
|
||||
|
||||
val isYEnoughSpace = (screenHeight - popupHeight) > popupHeight
|
||||
val xOffset: Int
|
||||
val yOffset = if (isYEnoughSpace) -popupHeight - parentHeight + arrowWidth / 3 else 0
|
||||
val marginStart: Int
|
||||
|
||||
var rightEnough = false
|
||||
var leftEnough = false
|
||||
|
||||
if (isYEnoughSpace) {
|
||||
binding.ivArrowUp.visibility = View.GONE
|
||||
binding.ivArrowDown.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.ivArrowUp.visibility = View.VISIBLE
|
||||
binding.ivArrowDown.visibility = View.GONE
|
||||
}
|
||||
|
||||
|
||||
if (isMine) {
|
||||
rightEnough = (popupWidth / 2) < distanceToRightEdge
|
||||
xOffset = if (rightEnough)
|
||||
(parentWidth - popupWidth) / 2
|
||||
else
|
||||
parentWidth - popupWidth + dpToPx(45)
|
||||
|
||||
marginStart = popupWidth - (dpToPx(55) + parentWidth / 2)
|
||||
|
||||
} else {
|
||||
leftEnough = (popupWidth / 2) < distanceToLeftEdge
|
||||
xOffset = if (leftEnough) parentWidth - popupWidth
|
||||
else -dpToPx(45)
|
||||
|
||||
marginStart = dpToPx(35) + parentWidth / 2
|
||||
}
|
||||
|
||||
|
||||
binding.ivArrowDown.layoutParams =
|
||||
(binding.ivArrowUp.layoutParams as LinearLayout.LayoutParams).apply {
|
||||
val enoughSpace = if (isMine) rightEnough else leftEnough
|
||||
if (enoughSpace) {
|
||||
gravity = Gravity.CENTER_HORIZONTAL
|
||||
} else {
|
||||
updateMargins(left = marginStart)
|
||||
}
|
||||
}
|
||||
|
||||
popupWindow.showAsDropDown(parentView, xOffset, yOffset)
|
||||
|
||||
// binding.ivWithdraw.setOnClickListener {
|
||||
// messages[position].status = "withdraw"
|
||||
// updateDb(messages[position])
|
||||
// notifyItemChanged(position)
|
||||
// popupWindow.dismiss()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.drake.spannable.replaceSpan
|
||||
import com.drake.spannable.span.CenterImageSpan
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation
|
||||
import com.kaixed.kchat.databinding.ChatMainItemBinding
|
||||
@ -24,6 +26,7 @@ import com.kaixed.kchat.utils.TextUtil
|
||||
class ConversationAdapter(
|
||||
private var chatLists: List<Conversation>, private val context: Context
|
||||
) : RecyclerView.Adapter<ConversationAdapter.MyViewHolder?>() {
|
||||
|
||||
private var onChatListItemClickListener: OnChatListItemClickListener? = null
|
||||
|
||||
companion object {
|
||||
@ -35,8 +38,10 @@ class ConversationAdapter(
|
||||
}
|
||||
|
||||
class MyViewHolder(val binding: ChatMainItemBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bindData(chatList: Conversation) {
|
||||
binding.tvContent.text = chatList.lastContent
|
||||
fun bindData(chatList: Conversation, context: Context) {
|
||||
binding.tvContent.text = chatList.lastContent.replaceSpan("[委屈]") {
|
||||
CenterImageSpan(context, R.drawable.emoji).setDrawableSize(55)
|
||||
}
|
||||
binding.tvNickname.text = chatList.talkerId
|
||||
binding.tvTimestamp.text = TextUtil.getTimestampString(chatList.timestamp)
|
||||
}
|
||||
@ -74,7 +79,7 @@ class ConversationAdapter(
|
||||
holder.binding.rlUnreadCount.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
holder.bindData(chatLists[position])
|
||||
holder.bindData(chatLists[position], context)
|
||||
holder.itemView.setOnClickListener {
|
||||
onChatListItemClickListener!!.onItemClick(chatLists[position].talkerId)
|
||||
|
||||
|
@ -19,9 +19,16 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = inflateBinding()
|
||||
setContentView(binding.root)
|
||||
initData()
|
||||
}
|
||||
|
||||
abstract fun initData()
|
||||
|
||||
fun toast(msg: String) {
|
||||
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
fun toast() {
|
||||
Toast.makeText(this, "暂未开发", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
@ -13,7 +13,6 @@ import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@ -21,7 +20,7 @@ import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation_
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages
|
||||
@ -32,6 +31,7 @@ import com.kaixed.kchat.service.WebSocketService.LocalBinder
|
||||
import com.kaixed.kchat.ui.activity.AddFriendsActivity
|
||||
import com.kaixed.kchat.ui.activity.ContactUpdatesActivity
|
||||
import com.kaixed.kchat.ui.activity.SearchActivity
|
||||
import com.kaixed.kchat.ui.activity.TestActivity
|
||||
import com.kaixed.kchat.ui.adapter.ConversationAdapter
|
||||
import com.kaixed.kchat.ui.adapter.MyGridAdapter
|
||||
import com.kaixed.kchat.ui.base.BaseFragment
|
||||
@ -44,15 +44,13 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(), OnChatListItemClickLis
|
||||
|
||||
private var chatLists: MutableList<Conversation> = mutableListOf()
|
||||
|
||||
private var oldLists: MutableList<Conversation> = mutableListOf()
|
||||
|
||||
private val conversationAdapter: ConversationAdapter by lazy {
|
||||
ConversationAdapter(chatLists, context)
|
||||
}
|
||||
|
||||
private val conversationBox: Box<Conversation> by lazy { get().boxFor(Conversation::class.java) }
|
||||
private val conversationBox: Box<Conversation> by lazy { getBoxStore().boxFor(Conversation::class.java) }
|
||||
|
||||
private val messagesBox: Box<Messages> by lazy { get().boxFor(Messages::class.java) }
|
||||
private val messagesBox: Box<Messages> by lazy { getBoxStore().boxFor(Messages::class.java) }
|
||||
|
||||
private var talkerId: String? = null
|
||||
|
||||
@ -97,6 +95,9 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(), OnChatListItemClickLis
|
||||
}
|
||||
|
||||
private fun setOnClick() {
|
||||
binding.ifvAvatar.setOnClickListener {
|
||||
startActivity(Intent(context, TestActivity::class.java))
|
||||
}
|
||||
binding.ivMessage.setOnClickListener {
|
||||
}
|
||||
binding.ivSearch.setOnClickListener {
|
||||
@ -268,9 +269,15 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(), OnChatListItemClickLis
|
||||
binding.recycleChatList.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
binding.recycleChatList.adapter = conversationAdapter
|
||||
binding.recycleChatList.setHasFixedSize(true)
|
||||
conversationAdapter.setItemListener(this)
|
||||
}
|
||||
|
||||
private fun resetContent() {
|
||||
flipImage(binding.ivMore)
|
||||
flipped = false
|
||||
}
|
||||
|
||||
private fun setupGridView() {
|
||||
initMenuData()
|
||||
val myGridAdapter = MyGridAdapter(context, items)
|
||||
@ -282,11 +289,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(), OnChatListItemClickLis
|
||||
when (position) {
|
||||
0 -> {}
|
||||
2 -> {
|
||||
val intent =
|
||||
Intent(
|
||||
context,
|
||||
AddFriendsActivity::class.java
|
||||
)
|
||||
val intent = Intent(context, AddFriendsActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
@ -329,6 +332,9 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(), OnChatListItemClickLis
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (flipped) {
|
||||
resetContent()
|
||||
}
|
||||
// updateChatLists()
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages
|
||||
import com.kaixed.kchat.databinding.BottomSheetLayoutBinding
|
||||
@ -75,7 +75,7 @@ class MyBottomSheetFragment : BottomSheetDialogFragment() {
|
||||
MMKV.defaultMMKV().putBoolean(USER_LOGIN_STATUS, false)
|
||||
|
||||
|
||||
val boxStore: BoxStore = get()
|
||||
val boxStore: BoxStore = getBoxStore()
|
||||
|
||||
boxStore.boxFor(Conversation::class.java).removeAll()
|
||||
boxStore.boxFor(Messages::class.java).removeAll()
|
||||
|
@ -63,13 +63,21 @@ class CustomItem @JvmOverloads constructor(
|
||||
private fun setupRightContent(type: String) {
|
||||
when (type) {
|
||||
"switch" -> {
|
||||
binding.ssvSwitch.visibility = View.VISIBLE
|
||||
binding.sbSwitch.visibility = View.VISIBLE
|
||||
binding.ivArrowRight.visibility = View.GONE
|
||||
binding.ivYes.visibility = View.GONE
|
||||
}
|
||||
|
||||
"yes" -> {
|
||||
binding.sbSwitch.visibility = View.GONE
|
||||
binding.ivArrowRight.visibility = View.GONE
|
||||
binding.ivYes.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
else -> {
|
||||
binding.ssvSwitch.visibility = View.GONE
|
||||
binding.sbSwitch.visibility = View.GONE
|
||||
binding.ivArrowRight.visibility = View.VISIBLE
|
||||
binding.ivYes.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -159,6 +167,10 @@ class CustomItem @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
fun setSwitchChecked(checked: Boolean) {
|
||||
binding.ssvSwitch.setOn(checked)
|
||||
binding.sbSwitch.setChecked(checked)
|
||||
}
|
||||
|
||||
fun setYesSelected(selected: Boolean) {
|
||||
binding.ivYes.visibility = if (selected) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ class CustomTitleBar @JvmOverloads constructor(
|
||||
private var binding: ViewTitleBarBinding =
|
||||
ViewTitleBarBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
|
||||
private var isBtnEnabled: Boolean = false
|
||||
|
||||
init {
|
||||
attrs?.let {
|
||||
val typedArray = context.obtainStyledAttributes(it, R.styleable.CustomTitleBar, 0, 0)
|
||||
@ -65,7 +67,12 @@ class CustomTitleBar @JvmOverloads constructor(
|
||||
binding.ivSetting.setOnClickListener(listener)
|
||||
}
|
||||
|
||||
fun setOnTitleClickListener(listener: OnClickListener?) {
|
||||
binding.tvTitleName.setOnClickListener(listener)
|
||||
}
|
||||
|
||||
fun setBtnEnable(enable: Boolean) {
|
||||
isBtnEnabled = enable
|
||||
val colorEnable = ContextCompat.getColor(context, R.color.white)
|
||||
val color = Color.parseColor("#BFBFBF")
|
||||
binding.tvSave.setTextColor(if (enable) colorEnable else color)
|
||||
@ -73,6 +80,14 @@ class CustomTitleBar @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
fun setOnBtnClickListener(listener: OnClickListener?) {
|
||||
binding.tvSave.setOnClickListener(listener)
|
||||
binding.tvSave.setOnClickListener {
|
||||
if (isBtnEnabled) {
|
||||
listener?.onClick(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setTitleName(titleName: String) {
|
||||
binding.tvTitleName.text = titleName
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.ui.fragment
|
||||
package com.kaixed.kchat.ui.widget
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.ui.fragment
|
||||
package com.kaixed.kchat.ui.widget
|
||||
|
||||
import android.app.Dialog
|
||||
import android.graphics.Color
|
||||
@ -12,11 +12,9 @@ import com.kaixed.kchat.utils.DensityUtil.dpToPx
|
||||
|
||||
class LoadingDialogFragment : DialogFragment() {
|
||||
|
||||
// 设置加载文本
|
||||
private var loadingText: String = ""
|
||||
|
||||
companion object {
|
||||
// 通过这个方法设置加载文本,并创建实例
|
||||
fun newInstance(text: String): LoadingDialogFragment {
|
||||
val fragment = LoadingDialogFragment()
|
||||
val bundle = Bundle()
|
||||
@ -53,11 +51,15 @@ class LoadingDialogFragment : DialogFragment() {
|
||||
|
||||
// 显示对话框
|
||||
fun showLoading(fragmentManager: FragmentManager) {
|
||||
this.show(fragmentManager, "LoadingDialogFragment")
|
||||
if (!this.isAdded) { // 避免重复添加
|
||||
this.show(fragmentManager, "LoadingDialogFragment")
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭对话框
|
||||
fun dismissLoading() {
|
||||
this.dismissAllowingStateLoss()
|
||||
if (isAdded && !isStateSaved) {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,600 +0,0 @@
|
||||
package com.kaixed.kchat.ui.widget
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Path
|
||||
import android.graphics.RectF
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.GestureDetector
|
||||
import android.view.GestureDetector.SimpleOnGestureListener
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.kaixed.kchat.R
|
||||
|
||||
/**
|
||||
* @Author: 7heaven
|
||||
* @Date: 2024/10/23 19:29
|
||||
*/
|
||||
class ShSwitchView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0
|
||||
) :
|
||||
View(context, attrs, defStyle) {
|
||||
private var innerContentAnimator: ValueAnimator? = null
|
||||
private var knobExpandAnimator: ValueAnimator? = null
|
||||
private var knobMoveAnimator: ValueAnimator? = null
|
||||
|
||||
private val gestureDetector: GestureDetector
|
||||
private val gestureListener: SimpleOnGestureListener = object : SimpleOnGestureListener() {
|
||||
override fun onDown(event: MotionEvent): Boolean {
|
||||
if (!isEnabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
preIsOn = isOn
|
||||
|
||||
innerContentAnimator!!.setFloatValues(innerContentRate, 0.0f)
|
||||
innerContentAnimator!!.start()
|
||||
|
||||
knobExpandAnimator!!.setFloatValues(knobExpandRate, 1.0f)
|
||||
knobExpandAnimator!!.start()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onShowPress(event: MotionEvent) {
|
||||
}
|
||||
|
||||
override fun onSingleTapUp(event: MotionEvent): Boolean {
|
||||
isOn = knobState
|
||||
|
||||
if (preIsOn == isOn) {
|
||||
isOn = !isOn
|
||||
knobState = !knobState
|
||||
}
|
||||
|
||||
if (knobState) {
|
||||
knobMoveAnimator!!.setFloatValues(knobMoveRate, 1.0f)
|
||||
knobMoveAnimator!!.start()
|
||||
|
||||
innerContentAnimator!!.setFloatValues(innerContentRate, 0.0f)
|
||||
innerContentAnimator!!.start()
|
||||
} else {
|
||||
knobMoveAnimator!!.setFloatValues(knobMoveRate, 0.0f)
|
||||
knobMoveAnimator!!.start()
|
||||
|
||||
innerContentAnimator!!.setFloatValues(innerContentRate, 1.0f)
|
||||
innerContentAnimator!!.start()
|
||||
}
|
||||
|
||||
knobExpandAnimator!!.setFloatValues(knobExpandRate, 0.0f)
|
||||
knobExpandAnimator!!.start()
|
||||
|
||||
if (this@ShSwitchView.onSwitchStateChangeListener != null && isOn != preIsOn) {
|
||||
onSwitchStateChangeListener!!.onSwitchStateChange(isOn)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onScroll(
|
||||
e1: MotionEvent?,
|
||||
e2: MotionEvent,
|
||||
distanceX: Float,
|
||||
distanceY: Float
|
||||
): Boolean {
|
||||
if (e2.x > centerX) {
|
||||
if (!knobState) {
|
||||
knobState = !knobState
|
||||
|
||||
knobMoveAnimator!!.setFloatValues(knobMoveRate, 1.0f)
|
||||
knobMoveAnimator!!.start()
|
||||
|
||||
innerContentAnimator!!.setFloatValues(innerContentRate, 0.0f)
|
||||
innerContentAnimator!!.start()
|
||||
}
|
||||
} else {
|
||||
if (knobState) {
|
||||
knobState = !knobState
|
||||
|
||||
knobMoveAnimator!!.setFloatValues(knobMoveRate, 0.0f)
|
||||
knobMoveAnimator!!.start()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private var width = 0
|
||||
private var height = 0
|
||||
|
||||
private var centerX = 0
|
||||
private var centerY = 0
|
||||
|
||||
private var cornerRadius = 0f
|
||||
|
||||
private val shadowSpace: Int
|
||||
private val outerStrokeWidth: Int
|
||||
|
||||
private val shadowDrawable: Drawable
|
||||
|
||||
private val knobBound: RectF
|
||||
private var knobMaxExpandWidth = 0f
|
||||
private var intrinsicKnobWidth = 0f
|
||||
private var knobExpandRate = 0f
|
||||
private var knobMoveRate = 0f
|
||||
|
||||
private var knobState = false
|
||||
private var isOn = false
|
||||
private var preIsOn = false
|
||||
|
||||
private val innerContentBound: RectF
|
||||
private var innerContentRate = 1.0f
|
||||
private var intrinsicInnerWidth = 0f
|
||||
private var intrinsicInnerHeight = 0f
|
||||
|
||||
private var tintColor: Int
|
||||
|
||||
private var tempTintColor: Int
|
||||
|
||||
private var colorStep = backgroundColor
|
||||
private val paint: Paint
|
||||
|
||||
private val ovalForPath: RectF
|
||||
private val roundRectPath: Path
|
||||
|
||||
private val tempForRoundRect: RectF
|
||||
|
||||
private var dirtyAnimation = false
|
||||
private var isAttachedToWindow = false
|
||||
|
||||
interface OnSwitchStateChangeListener {
|
||||
fun onSwitchStateChange(isOn: Boolean)
|
||||
}
|
||||
|
||||
var onSwitchStateChangeListener: OnSwitchStateChangeListener? = null
|
||||
|
||||
init {
|
||||
val ta = context.obtainStyledAttributes(attrs, R.styleable.ShSwitchView)
|
||||
|
||||
tintColor = ta.getColor(R.styleable.ShSwitchView_tintColor, -0x6316b7)
|
||||
tempTintColor = tintColor
|
||||
|
||||
val defaultOuterStrokeWidth = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
1.5f,
|
||||
context.resources.displayMetrics
|
||||
).toInt()
|
||||
val defaultShadowSpace = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
5f,
|
||||
context.resources.displayMetrics
|
||||
).toInt()
|
||||
|
||||
outerStrokeWidth = ta.getDimensionPixelOffset(
|
||||
R.styleable.ShSwitchView_outerStrokeWidth,
|
||||
defaultOuterStrokeWidth
|
||||
)
|
||||
shadowSpace =
|
||||
ta.getDimensionPixelOffset(R.styleable.ShSwitchView_shadowSpace, defaultShadowSpace)
|
||||
|
||||
ta.recycle()
|
||||
|
||||
knobBound = RectF()
|
||||
innerContentBound = RectF()
|
||||
ovalForPath = RectF()
|
||||
|
||||
tempForRoundRect = RectF()
|
||||
|
||||
paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
roundRectPath = Path()
|
||||
|
||||
gestureDetector = GestureDetector(context, gestureListener)
|
||||
gestureDetector.setIsLongpressEnabled(false)
|
||||
|
||||
setLayerType(LAYER_TYPE_SOFTWARE, null)
|
||||
|
||||
initAnimators()
|
||||
|
||||
shadowDrawable = ContextCompat.getDrawable(context, R.drawable.shadow)!!
|
||||
|
||||
}
|
||||
|
||||
private fun initAnimators() {
|
||||
innerContentAnimator = ValueAnimator.ofFloat(innerContentRate, 1.0f).apply {
|
||||
duration = COMMON_DURATION
|
||||
interpolator = DecelerateInterpolator()
|
||||
addUpdateListener { animation ->
|
||||
(animation as? ValueAnimator)?.let {
|
||||
setInnerContentRate(it.animatedValue as Float)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
knobExpandAnimator = ValueAnimator.ofFloat(knobExpandRate, 1.0f).apply {
|
||||
duration = COMMON_DURATION
|
||||
interpolator = DecelerateInterpolator()
|
||||
addUpdateListener { animation ->
|
||||
(animation as? ValueAnimator)?.let {
|
||||
setKnobExpandRate(it.animatedValue as Float)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
knobMoveAnimator = ValueAnimator.ofFloat(knobMoveRate, 1.0f).apply {
|
||||
duration = COMMON_DURATION
|
||||
interpolator = DecelerateInterpolator()
|
||||
addUpdateListener { animation ->
|
||||
(animation as? ValueAnimator)?.let {
|
||||
setKnobMoveRate(it.animatedValue as Float)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun setInnerContentRate(rate: Float) {
|
||||
this.innerContentRate = rate
|
||||
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun getInnerContentRate(): Float {
|
||||
return this.innerContentRate
|
||||
}
|
||||
|
||||
fun setKnobExpandRate(rate: Float) {
|
||||
this.knobExpandRate = rate
|
||||
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun getKnobExpandRate(): Float {
|
||||
return this.knobExpandRate
|
||||
}
|
||||
|
||||
fun setKnobMoveRate(rate: Float) {
|
||||
this.knobMoveRate = rate
|
||||
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun getKnobMoveRate(): Float {
|
||||
return knobMoveRate
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
isAttachedToWindow = true
|
||||
|
||||
if (dirtyAnimation) {
|
||||
knobState = this.isOn
|
||||
if (knobState) {
|
||||
knobMoveAnimator!!.setFloatValues(knobMoveRate, 1.0f)
|
||||
knobMoveAnimator!!.start()
|
||||
|
||||
innerContentAnimator!!.setFloatValues(innerContentRate, 0.0f)
|
||||
innerContentAnimator!!.start()
|
||||
} else {
|
||||
knobMoveAnimator!!.setFloatValues(knobMoveRate, 0.0f)
|
||||
knobMoveAnimator!!.start()
|
||||
|
||||
innerContentAnimator!!.setFloatValues(innerContentRate, 1.0f)
|
||||
innerContentAnimator!!.start()
|
||||
}
|
||||
|
||||
knobExpandAnimator!!.setFloatValues(knobExpandRate, 0.0f)
|
||||
knobExpandAnimator!!.start()
|
||||
|
||||
if (this@ShSwitchView.onSwitchStateChangeListener != null && isOn != preIsOn) {
|
||||
onSwitchStateChangeListener!!.onSwitchStateChange(isOn)
|
||||
}
|
||||
|
||||
dirtyAnimation = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
isAttachedToWindow = false
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
var widthMeasureSpec = widthMeasureSpec
|
||||
var heightMeasureSpec = heightMeasureSpec
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
|
||||
width = MeasureSpec.getSize(widthMeasureSpec)
|
||||
height = MeasureSpec.getSize(heightMeasureSpec)
|
||||
|
||||
//make sure widget remain in a good appearance
|
||||
if (height.toFloat() / width.toFloat() < 0.33333f) {
|
||||
height = (width.toFloat() * 0.33333f).toInt()
|
||||
|
||||
widthMeasureSpec =
|
||||
MeasureSpec.makeMeasureSpec(width, MeasureSpec.getMode(widthMeasureSpec))
|
||||
heightMeasureSpec =
|
||||
MeasureSpec.makeMeasureSpec(height, MeasureSpec.getMode(heightMeasureSpec))
|
||||
|
||||
super.setMeasuredDimension(widthMeasureSpec, heightMeasureSpec)
|
||||
}
|
||||
|
||||
centerX = width / 2
|
||||
centerY = height / 2
|
||||
|
||||
cornerRadius = (centerY - shadowSpace).toFloat()
|
||||
|
||||
innerContentBound.left = (outerStrokeWidth + shadowSpace).toFloat()
|
||||
innerContentBound.top = (outerStrokeWidth + shadowSpace).toFloat()
|
||||
innerContentBound.right = (width - outerStrokeWidth - shadowSpace).toFloat()
|
||||
innerContentBound.bottom = (height - outerStrokeWidth - shadowSpace).toFloat()
|
||||
|
||||
intrinsicInnerWidth = innerContentBound.width()
|
||||
intrinsicInnerHeight = innerContentBound.height()
|
||||
|
||||
knobBound.left = (outerStrokeWidth + shadowSpace).toFloat()
|
||||
knobBound.top = (outerStrokeWidth + shadowSpace).toFloat()
|
||||
knobBound.right = (height - outerStrokeWidth - shadowSpace).toFloat()
|
||||
knobBound.bottom = (height - outerStrokeWidth - shadowSpace).toFloat()
|
||||
|
||||
intrinsicKnobWidth = knobBound.height()
|
||||
knobMaxExpandWidth = width.toFloat() * 0.7f
|
||||
if (knobMaxExpandWidth > knobBound.width() * 1.25f) {
|
||||
knobMaxExpandWidth = knobBound.width() * 1.25f
|
||||
}
|
||||
}
|
||||
|
||||
fun isOn(): Boolean {
|
||||
return this.isOn
|
||||
}
|
||||
|
||||
fun setOn(on: Boolean) {
|
||||
setOn(on, false)
|
||||
}
|
||||
|
||||
fun setOn(on: Boolean, animated: Boolean) {
|
||||
if (this.isOn == on) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!isAttachedToWindow && animated) {
|
||||
dirtyAnimation = true
|
||||
this.isOn = on
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.isOn = on
|
||||
knobState = this.isOn
|
||||
|
||||
if (!animated) {
|
||||
if (on) {
|
||||
setKnobMoveRate(1.0f)
|
||||
setInnerContentRate(0.0f)
|
||||
} else {
|
||||
setKnobMoveRate(0.0f)
|
||||
setInnerContentRate(1.0f)
|
||||
}
|
||||
|
||||
setKnobExpandRate(0.0f)
|
||||
} else {
|
||||
if (knobState) {
|
||||
knobMoveAnimator!!.setFloatValues(knobMoveRate, 1.0f)
|
||||
knobMoveAnimator!!.start()
|
||||
|
||||
innerContentAnimator!!.setFloatValues(innerContentRate, 0.0f)
|
||||
innerContentAnimator!!.start()
|
||||
} else {
|
||||
knobMoveAnimator!!.setFloatValues(knobMoveRate, 0.0f)
|
||||
knobMoveAnimator!!.start()
|
||||
|
||||
innerContentAnimator!!.setFloatValues(innerContentRate, 1.0f)
|
||||
innerContentAnimator!!.start()
|
||||
}
|
||||
|
||||
knobExpandAnimator!!.setFloatValues(knobExpandRate, 0.0f)
|
||||
knobExpandAnimator!!.start()
|
||||
}
|
||||
|
||||
if (this@ShSwitchView.onSwitchStateChangeListener != null && isOn != preIsOn) {
|
||||
onSwitchStateChangeListener!!.onSwitchStateChange(isOn)
|
||||
}
|
||||
}
|
||||
|
||||
fun setTintColor(tintColor: Int) {
|
||||
this.tintColor = tintColor
|
||||
tempTintColor = this.tintColor
|
||||
}
|
||||
|
||||
fun getTintColor(): Int {
|
||||
return this.tintColor
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
if (!isEnabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
if (!knobState) {
|
||||
innerContentAnimator!!.setFloatValues(innerContentRate, 1.0f)
|
||||
|
||||
innerContentAnimator!!.start()
|
||||
}
|
||||
|
||||
knobExpandAnimator!!.setFloatValues(knobExpandRate, 0.0f)
|
||||
|
||||
knobExpandAnimator!!.start()
|
||||
|
||||
isOn = knobState
|
||||
|
||||
if (this@ShSwitchView.onSwitchStateChangeListener != null && isOn != preIsOn) {
|
||||
onSwitchStateChangeListener!!.onSwitchStateChange(isOn)
|
||||
}
|
||||
}
|
||||
}
|
||||
return gestureDetector.onTouchEvent(event)
|
||||
}
|
||||
|
||||
override fun setEnabled(enabled: Boolean) {
|
||||
super.setEnabled(enabled)
|
||||
|
||||
if (enabled) {
|
||||
this.tintColor = tempTintColor
|
||||
} else {
|
||||
this.tintColor = this.RGBColorTransform(0.5f, tempTintColor, -0x1)
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
//innerContentCalculation begin
|
||||
var w = intrinsicInnerWidth / 2.0f * innerContentRate
|
||||
val h = intrinsicInnerHeight / 2.0f * innerContentRate
|
||||
|
||||
innerContentBound.left = centerX - w
|
||||
innerContentBound.top = centerY - h
|
||||
innerContentBound.right = centerX + w
|
||||
innerContentBound.bottom = centerY + h
|
||||
|
||||
//innerContentCalculation end
|
||||
|
||||
//knobExpandCalculation begin
|
||||
w = intrinsicKnobWidth + (knobMaxExpandWidth - intrinsicKnobWidth) * knobExpandRate
|
||||
|
||||
val left = knobBound.left + knobBound.width() / 2 > centerX
|
||||
|
||||
if (left) {
|
||||
knobBound.left = knobBound.right - w
|
||||
} else {
|
||||
knobBound.right = knobBound.left + w
|
||||
}
|
||||
|
||||
//knobExpandCalculation end
|
||||
|
||||
//knobMoveCalculation begin
|
||||
val kw = knobBound.width()
|
||||
w = (width - kw - ((shadowSpace + outerStrokeWidth) * 2)) * knobMoveRate
|
||||
|
||||
this.colorStep = RGBColorTransform(knobMoveRate, backgroundColor, tintColor)
|
||||
|
||||
|
||||
knobBound.left = shadowSpace + outerStrokeWidth + w
|
||||
knobBound.right = knobBound.left + kw
|
||||
|
||||
//knobMoveCalculation end
|
||||
|
||||
//background
|
||||
paint.color = colorStep
|
||||
paint.style = Paint.Style.FILL
|
||||
|
||||
drawRoundRect(
|
||||
shadowSpace.toFloat(),
|
||||
shadowSpace.toFloat(),
|
||||
(width - shadowSpace).toFloat(),
|
||||
(height - shadowSpace).toFloat(),
|
||||
cornerRadius,
|
||||
canvas,
|
||||
paint
|
||||
)
|
||||
|
||||
//innerContent
|
||||
paint.color = Color.WHITE
|
||||
canvas.drawRoundRect(
|
||||
innerContentBound,
|
||||
innerContentBound.height() / 2,
|
||||
innerContentBound.height() / 2,
|
||||
paint
|
||||
)
|
||||
|
||||
//knob
|
||||
// shadowDrawable.setBounds((int) (knobBound.left - shadowSpace), (int) (knobBound.top - shadowSpace), (int) (knobBound.right + shadowSpace), (int) (knobBound.bottom + shadowSpace));
|
||||
// shadowDrawable.draw(canvas);
|
||||
paint.setShadowLayer(
|
||||
2f,
|
||||
0f,
|
||||
(shadowSpace / 2).toFloat(),
|
||||
if (isEnabled) 0x20000000 else 0x10000000
|
||||
)
|
||||
|
||||
// paint.setColor(isEnabled() ? 0x20000000 : 0x10000000);
|
||||
// drawRoundRect(knobBound.left, knobBound.top + shadowSpace / 2, knobBound.right, knobBound.bottom + shadowSpace / 2, cornerRadius - outerStrokeWidth, canvas, paint);
|
||||
//
|
||||
// paint.setColor(foregroundColor);
|
||||
canvas.drawRoundRect(
|
||||
knobBound,
|
||||
cornerRadius - outerStrokeWidth,
|
||||
cornerRadius - outerStrokeWidth,
|
||||
paint
|
||||
)
|
||||
paint.setShadowLayer(0f, 0f, 0f, 0)
|
||||
|
||||
paint.color = backgroundColor
|
||||
paint.style = Paint.Style.STROKE
|
||||
paint.strokeWidth = 1f
|
||||
|
||||
canvas.drawRoundRect(
|
||||
knobBound,
|
||||
cornerRadius - outerStrokeWidth,
|
||||
cornerRadius - outerStrokeWidth,
|
||||
paint
|
||||
)
|
||||
}
|
||||
|
||||
private fun drawRoundRect(
|
||||
left: Float,
|
||||
top: Float,
|
||||
right: Float,
|
||||
bottom: Float,
|
||||
radius: Float,
|
||||
canvas: Canvas,
|
||||
paint: Paint
|
||||
) {
|
||||
tempForRoundRect.left = left
|
||||
tempForRoundRect.top = top
|
||||
tempForRoundRect.right = right
|
||||
tempForRoundRect.bottom = bottom
|
||||
|
||||
canvas.drawRoundRect(tempForRoundRect, radius, radius, paint)
|
||||
}
|
||||
|
||||
//seperate RGB channels and calculate new value for each channel
|
||||
//ignore alpha channel
|
||||
private fun RGBColorTransform(progress: Float, fromColor: Int, toColor: Int): Int {
|
||||
val or = (fromColor shr 16) and 0xFF
|
||||
val og = (fromColor shr 8) and 0xFF
|
||||
val ob = fromColor and 0xFF
|
||||
|
||||
val nr = (toColor shr 16) and 0xFF
|
||||
val ng = (toColor shr 8) and 0xFF
|
||||
val nb = toColor and 0xFF
|
||||
|
||||
val rGap = ((nr - or).toFloat() * progress).toInt()
|
||||
val gGap = ((ng - og).toFloat() * progress).toInt()
|
||||
val bGap = ((nb - ob).toFloat() * progress).toInt()
|
||||
|
||||
return -0x1000000 or ((or + rGap) shl 16) or ((og + gGap) shl 8) or (ob + bGap)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val COMMON_DURATION = 300L
|
||||
|
||||
private const val intrinsicWidth = 0
|
||||
private const val intrinsicHeight = 0
|
||||
|
||||
private const val backgroundColor = -0x333334
|
||||
private const val foregroundColor = -0xa0a0b
|
||||
}
|
||||
}
|
246
app/src/main/java/com/kaixed/kchat/ui/widget/SwitchButton.kt
Normal file
246
app/src/main/java/com/kaixed/kchat/ui/widget/SwitchButton.kt
Normal file
@ -0,0 +1,246 @@
|
||||
package com.kaixed.kchat.ui.widget
|
||||
|
||||
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.util.AttributeSet
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.View
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
import androidx.annotation.Nullable
|
||||
import com.kaixed.kchat.R
|
||||
|
||||
class SwitchButton @JvmOverloads constructor(
|
||||
context: Context,
|
||||
@Nullable attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
defStyleRes: Int = 0
|
||||
) : View(context, attrs, defStyleAttr, defStyleRes) {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* 背景动画的延时与持续时间
|
||||
*/
|
||||
private const val BG_ANIMATOR_START_DELAY = 0
|
||||
private const val BG_ANIMATOR_DURATION = 200
|
||||
|
||||
/**
|
||||
* dp 转 px 的工具方法
|
||||
*/
|
||||
fun dip2px(context: Context, dipValue: Float): Int {
|
||||
val scale = context.resources.displayMetrics.density
|
||||
return (dipValue * scale + 0.5f).toInt()
|
||||
}
|
||||
|
||||
fun getDisplayMetrics(context: Context): DisplayMetrics {
|
||||
return context.resources.displayMetrics
|
||||
}
|
||||
}
|
||||
|
||||
// 属性变量
|
||||
private var mCheckedBg: Int
|
||||
private var mUncheckedBg: Int
|
||||
private var mCurrentBoundBg: Int
|
||||
private var mSmallCircleMargin: Int
|
||||
private val mBgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
|
||||
private val mSmallCirclePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
style = Paint.Style.FILL
|
||||
color = Color.WHITE
|
||||
}
|
||||
private var mDefaultWidth: Int
|
||||
private var mDefaultHeight: Int
|
||||
private var mSmallCircleRadius = 0
|
||||
private var mWidth = 0
|
||||
private var mHeight = 0
|
||||
private var mBoundRadius = 0f
|
||||
private var mBoundRect: RectF? = null
|
||||
private var mSmallCircleCenterX = 0
|
||||
private var mSmallCircleCenterY = 0
|
||||
private var isChecked = false
|
||||
private var mCheckedChangeListener: OnCheckedChangeListener? = null
|
||||
private var mSmallCircleStartX = 0
|
||||
private var mSmallCircleEndX = 0
|
||||
private val mArgbEvaluator = ArgbEvaluator()
|
||||
private var mOffAnimatorSet: AnimatorSet? = null
|
||||
private var mOpenAnimatorSet: AnimatorSet? = null
|
||||
|
||||
init {
|
||||
val typedArray = context.obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.SwitchButton,
|
||||
defStyleAttr,
|
||||
defStyleRes
|
||||
)
|
||||
mCheckedBg = typedArray.getColor(R.styleable.SwitchButton_sb_checked_bg, Color.BLUE)
|
||||
mUncheckedBg = typedArray.getColor(R.styleable.SwitchButton_sb_unchecked_bg, Color.GRAY)
|
||||
isChecked = typedArray.getBoolean(R.styleable.SwitchButton_sb_checked, false)
|
||||
mSmallCircleMargin = typedArray.getDimension(
|
||||
R.styleable.SwitchButton_sb_circle_bg_margin,
|
||||
dip2px(context, 3f).toFloat()
|
||||
).toInt()
|
||||
typedArray.recycle()
|
||||
|
||||
mCurrentBoundBg = if (isChecked) mCheckedBg else mUncheckedBg
|
||||
mDefaultWidth = dip2px(context, 50f)
|
||||
mDefaultHeight = dip2px(context, 30f)
|
||||
init()
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
setOnClickListener {
|
||||
if (isChecked) {
|
||||
if (mOffAnimatorSet?.isRunning == true) return@setOnClickListener
|
||||
off()
|
||||
} else {
|
||||
if (mOpenAnimatorSet?.isRunning == true) return@setOnClickListener
|
||||
open()
|
||||
}
|
||||
isChecked = !isChecked
|
||||
mCheckedChangeListener?.onCheckedChanged(this, isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
mWidth = w
|
||||
mHeight = h
|
||||
mSmallCircleRadius = (mHeight - mSmallCircleMargin * 2) / 2
|
||||
mBoundRadius = (mHeight / 2).toFloat()
|
||||
mSmallCircleCenterY = mHeight / 2
|
||||
mSmallCircleStartX = mWidth / 4
|
||||
mSmallCircleEndX = (mWidth / 4) * 3
|
||||
updateCheckStatus()
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
|
||||
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
|
||||
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
|
||||
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
|
||||
|
||||
val resultWidth: Int
|
||||
val resultHeight: Int
|
||||
|
||||
if (widthMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.EXACTLY) {
|
||||
resultWidth = widthSize
|
||||
resultHeight = heightSize
|
||||
} else {
|
||||
resultWidth = mDefaultWidth
|
||||
resultHeight = mDefaultHeight
|
||||
}
|
||||
setMeasuredDimension(resultWidth, resultHeight)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
drawBg(canvas, mBgPaint)
|
||||
drawCircle(canvas, mSmallCirclePaint)
|
||||
}
|
||||
|
||||
private fun drawBg(canvas: Canvas, paint: Paint) {
|
||||
mBgPaint.color = mCurrentBoundBg
|
||||
if (mBoundRect == null) {
|
||||
mBoundRect = RectF(0f, 0f, mWidth.toFloat(), mHeight.toFloat())
|
||||
}
|
||||
canvas.drawRoundRect(mBoundRect!!, mBoundRadius, mBoundRadius, paint)
|
||||
}
|
||||
|
||||
private fun drawCircle(canvas: Canvas, paint: Paint) {
|
||||
canvas.drawCircle(
|
||||
mSmallCircleCenterX.toFloat(),
|
||||
mSmallCircleCenterY.toFloat(),
|
||||
mSmallCircleRadius.toFloat(),
|
||||
paint
|
||||
)
|
||||
}
|
||||
|
||||
private fun open() {
|
||||
val startX = mSmallCircleStartX
|
||||
val endX = mSmallCircleEndX
|
||||
|
||||
val openAnimator = ValueAnimator.ofInt(startX, endX).apply {
|
||||
duration = 300
|
||||
addUpdateListener { animation ->
|
||||
mSmallCircleCenterX = animation.animatedValue as Int
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
val bgAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
|
||||
startDelay = BG_ANIMATOR_START_DELAY.toLong()
|
||||
duration = BG_ANIMATOR_DURATION.toLong()
|
||||
interpolator = AccelerateInterpolator()
|
||||
addUpdateListener { animation ->
|
||||
val fraction = animation.animatedValue as Float
|
||||
mCurrentBoundBg = mArgbEvaluator.evaluate(fraction, mUncheckedBg, mCheckedBg) as Int
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
mOpenAnimatorSet = AnimatorSet().apply {
|
||||
playTogether(openAnimator, bgAnimator)
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun off() {
|
||||
val startX = mSmallCircleEndX
|
||||
val endX = mSmallCircleStartX
|
||||
|
||||
val offAnimator = ValueAnimator.ofInt(startX, endX).apply {
|
||||
duration = 200
|
||||
addUpdateListener { animation ->
|
||||
mSmallCircleCenterX = animation.animatedValue as Int
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
val bgAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
|
||||
startDelay = BG_ANIMATOR_START_DELAY.toLong()
|
||||
duration = BG_ANIMATOR_DURATION.toLong()
|
||||
interpolator = AccelerateInterpolator()
|
||||
addUpdateListener { animation ->
|
||||
val fraction = animation.animatedValue as Float
|
||||
mCurrentBoundBg = mArgbEvaluator.evaluate(fraction, mCheckedBg, mUncheckedBg) as Int
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
mOffAnimatorSet = AnimatorSet().apply {
|
||||
playTogether(offAnimator, bgAnimator)
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
fun setChecked(checked: Boolean) {
|
||||
isChecked = checked
|
||||
updateCheckStatus()
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
private fun updateCheckStatus() {
|
||||
if (isChecked) {
|
||||
mSmallCircleCenterX = mSmallCircleEndX
|
||||
mCurrentBoundBg = mCheckedBg
|
||||
} else {
|
||||
mSmallCircleCenterX = mSmallCircleStartX
|
||||
mCurrentBoundBg = mUncheckedBg
|
||||
}
|
||||
}
|
||||
|
||||
fun isChecked(): Boolean = isChecked
|
||||
|
||||
fun setOnCheckedChangeListener(listener: OnCheckedChangeListener) {
|
||||
mCheckedChangeListener = listener
|
||||
}
|
||||
|
||||
interface OnCheckedChangeListener {
|
||||
fun onCheckedChanged(button: SwitchButton, isChecked: Boolean)
|
||||
}
|
||||
}
|
42
app/src/main/java/com/kaixed/kchat/utils/ImageEngines.kt
Normal file
42
app/src/main/java/com/kaixed/kchat/utils/ImageEngines.kt
Normal file
@ -0,0 +1,42 @@
|
||||
package com.kaixed.kchat.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.ImageView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.luck.picture.lib.engine.ImageEngine
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/11/25 13:50
|
||||
*/
|
||||
class ImageEngines: ImageEngine {
|
||||
override fun loadImage(context: Context?, url: String?, imageView: ImageView?) {
|
||||
Glide.with(context!!).load(url).into(imageView!!)
|
||||
}
|
||||
|
||||
override fun loadImage(
|
||||
context: Context?,
|
||||
imageView: ImageView?,
|
||||
url: String?,
|
||||
maxWidth: Int,
|
||||
maxHeight: Int
|
||||
) {
|
||||
Glide.with(context!!).load(url).override(maxWidth, maxHeight).into(imageView!!)
|
||||
}
|
||||
|
||||
override fun loadAlbumCover(context: Context?, url: String?, imageView: ImageView?) {
|
||||
Glide.with(context!!).load(url).into(imageView!!)
|
||||
}
|
||||
|
||||
override fun loadGridImage(context: Context?, url: String?, imageView: ImageView?) {
|
||||
Glide.with(context!!).load(url).into(imageView!!)
|
||||
}
|
||||
|
||||
override fun pauseRequests(context: Context?) {
|
||||
Glide.with(context!!).pauseRequests()
|
||||
}
|
||||
|
||||
override fun resumeRequests(context: Context?) {
|
||||
Glide.with(context!!).resumeRequests()
|
||||
}
|
||||
}
|
110
app/src/main/java/com/kaixed/kchat/utils/PopWindowUtil.kt
Normal file
110
app/src/main/java/com/kaixed/kchat/utils/PopWindowUtil.kt
Normal file
@ -0,0 +1,110 @@
|
||||
package com.kaixed.kchat.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupWindow
|
||||
import androidx.core.view.updateMargins
|
||||
import com.kaixed.kchat.databinding.PopwindowsBinding
|
||||
import com.kaixed.kchat.utils.DensityUtil.dpToPx
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/11/25 15:44
|
||||
*/
|
||||
object PopWindowUtil {
|
||||
fun showPopupWindow(context: Context, parentView: View, isMine: Boolean) {
|
||||
val binding: PopwindowsBinding = PopwindowsBinding.inflate(LayoutInflater.from(context))
|
||||
val popupWindow: PopupWindow = PopupWindow(
|
||||
binding.root, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
).apply {
|
||||
isFocusable = true
|
||||
isOutsideTouchable = true
|
||||
setBackgroundDrawable(ColorDrawable())
|
||||
}
|
||||
|
||||
// 获取屏幕的总宽度
|
||||
val screenWidth = context.resources.displayMetrics.widthPixels
|
||||
val screenHeight = context.resources.displayMetrics.heightPixels
|
||||
|
||||
val location = IntArray(2)
|
||||
parentView.getLocationOnScreen(location)
|
||||
binding.root.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
|
||||
|
||||
// popupWindow的长度
|
||||
val popupWidth = binding.root.measuredWidth
|
||||
val popupHeight = binding.root.measuredHeight
|
||||
|
||||
val parentWidth = parentView.width
|
||||
val parentHeight = parentView.height
|
||||
|
||||
val parentX = location[0]
|
||||
|
||||
// 父布局中心点横坐标
|
||||
val distanceToLeftEdge = parentX + parentView.width / 2
|
||||
// 中心点距离右侧距离
|
||||
val distanceToRightEdge = screenWidth - distanceToLeftEdge
|
||||
|
||||
|
||||
binding.ivArrowDown.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
|
||||
val arrowWidth = binding.ivArrowDown.measuredWidth
|
||||
|
||||
val isYEnoughSpace = (screenHeight - popupHeight) > popupHeight
|
||||
val xOffset: Int
|
||||
val yOffset = if (isYEnoughSpace) -popupHeight - parentHeight + arrowWidth / 3 else 0
|
||||
val marginStart: Int
|
||||
|
||||
var rightEnough = false
|
||||
var leftEnough = false
|
||||
|
||||
if (isYEnoughSpace) {
|
||||
binding.ivArrowUp.visibility = View.GONE
|
||||
binding.ivArrowDown.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.ivArrowUp.visibility = View.VISIBLE
|
||||
binding.ivArrowDown.visibility = View.GONE
|
||||
}
|
||||
|
||||
|
||||
if (isMine) {
|
||||
rightEnough = (popupWidth / 2) < distanceToRightEdge
|
||||
xOffset = if (rightEnough)
|
||||
(parentWidth - popupWidth) / 2
|
||||
else
|
||||
parentWidth - popupWidth + dpToPx(45)
|
||||
|
||||
marginStart = popupWidth - (dpToPx(55) + parentWidth / 2)
|
||||
|
||||
} else {
|
||||
leftEnough = (popupWidth / 2) < distanceToLeftEdge
|
||||
xOffset = if (leftEnough) parentWidth - popupWidth
|
||||
else -dpToPx(45)
|
||||
|
||||
marginStart = dpToPx(35) + parentWidth / 2
|
||||
}
|
||||
|
||||
|
||||
binding.ivArrowDown.layoutParams =
|
||||
(binding.ivArrowUp.layoutParams as LinearLayout.LayoutParams).apply {
|
||||
val enoughSpace = if (isMine) rightEnough else leftEnough
|
||||
if (enoughSpace) {
|
||||
gravity = Gravity.CENTER_HORIZONTAL
|
||||
} else {
|
||||
updateMargins(left = marginStart)
|
||||
}
|
||||
}
|
||||
|
||||
popupWindow.showAsDropDown(parentView, xOffset, yOffset)
|
||||
|
||||
// binding.ivWithdraw.setOnClickListener {
|
||||
// messages[position].status = "withdraw"
|
||||
// updateDb(messages[position])
|
||||
// notifyItemChanged(position)
|
||||
// popupWindow.dismiss()
|
||||
// }
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package com.kaixed.kchat.utils
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import com.kaixed.kchat.databinding.DialogDeleteContactBinding
|
||||
import com.kaixed.kchat.databinding.DialogLoadingBinding
|
||||
import com.kaixed.kchat.utils.DensityUtil.dpToPx
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/11/5 21:26
|
||||
*/
|
||||
object WidgetUtil {
|
||||
fun showLoadingDialog(context: Context, str: String): Dialog {
|
||||
val binding = DialogLoadingBinding.inflate(LayoutInflater.from(context))
|
||||
val dialog = Dialog(context)
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
dialog.setCancelable(false)
|
||||
dialog.setContentView(binding.root)
|
||||
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
val params = binding.root.layoutParams
|
||||
params.height = dpToPx(150)
|
||||
params.width = dpToPx(150)
|
||||
binding.root.layoutParams = params
|
||||
binding.tvLoading.text = str
|
||||
return dialog
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package com.kaixed.kchat.utils.handle
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.utils.Pinyin4jUtil
|
||||
import io.objectbox.Box
|
||||
@ -10,7 +10,8 @@ import io.objectbox.Box
|
||||
* @Date: 2024/11/19 16:11
|
||||
*/
|
||||
object ContactUtil {
|
||||
private val contactBox: Box<Contact> by lazy { get().boxFor(Contact::class.java) }
|
||||
|
||||
private val contactBox: Box<Contact> by lazy { getBoxStore().boxFor(Contact::class.java) }
|
||||
|
||||
fun handleContact(contact: Contact) {
|
||||
val contactLists = getDbContactLists()
|
||||
|
@ -4,7 +4,7 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.get
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.model.friend.FriendRequestItem
|
||||
import com.kaixed.kchat.model.search.User
|
||||
@ -27,8 +27,8 @@ class ContactViewModel : ViewModel() {
|
||||
_acceptContactRequestResult
|
||||
|
||||
// 添加联系人
|
||||
private val _addContactResult = MutableLiveData<Result<String>>()
|
||||
val addContactResult: LiveData<Result<String>> = _addContactResult
|
||||
private val _addContactResult = MutableLiveData<Result<String?>>()
|
||||
val addContactResult: LiveData<Result<String?>> = _addContactResult
|
||||
|
||||
// 搜索联系人
|
||||
private val _searchContactResult = MutableLiveData<Result<User?>>()
|
||||
@ -42,6 +42,18 @@ class ContactViewModel : ViewModel() {
|
||||
private val _deleteContactResult = MutableLiveData<Result<String?>>()
|
||||
val deleteContactResult: LiveData<Result<String?>> = _deleteContactResult
|
||||
|
||||
// 删除联系人
|
||||
private val _setRemarkResult = MutableLiveData<Result<String?>>()
|
||||
val setRemarkResult: LiveData<Result<String?>> = _setRemarkResult
|
||||
|
||||
// 设置备注
|
||||
fun setRemark(username: String, contactId: String, remark: String) {
|
||||
viewModelScope.launch {
|
||||
val result = contactRepository.setRemark(username, contactId, remark)
|
||||
_setRemarkResult.postValue(result)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除联系人
|
||||
fun deleteContact(username: String, contactId: String) {
|
||||
viewModelScope.launch {
|
||||
@ -83,7 +95,7 @@ class ContactViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun loadFriendListInDb(): List<Contact> {
|
||||
val contactBox: Box<Contact> = get().boxFor(Contact::class.java)
|
||||
val contactBox: Box<Contact> = getBoxStore().boxFor(Contact::class.java)
|
||||
|
||||
val sortedContacts = contactBox.query()
|
||||
.sort { contact1, contact2 ->
|
||||
|
@ -24,8 +24,8 @@ class UserViewModel : ViewModel() {
|
||||
|
||||
private val userSearchRepo = UserSearchRepository()
|
||||
|
||||
private val _loginResult = MutableLiveData<Result<UserInfo>>()
|
||||
val loginResult: LiveData<Result<UserInfo>> = _loginResult
|
||||
private val _loginResult = MutableLiveData<Result<UserInfo?>>()
|
||||
val loginResult: LiveData<Result<UserInfo?>> = _loginResult
|
||||
|
||||
fun loginByUsername(username: String, password: String) {
|
||||
viewModelScope.launch {
|
||||
@ -41,8 +41,8 @@ class UserViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private val _registerResult = MutableLiveData<Result<Register>>()
|
||||
val registerResult: LiveData<Result<Register>> = _registerResult
|
||||
private val _registerResult = MutableLiveData<Result<Register?>>()
|
||||
val registerResult: LiveData<Result<Register?>> = _registerResult
|
||||
|
||||
// 注册请求
|
||||
fun register(registerRequest: RegisterRequest) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<size android:width="1dp" />
|
||||
<size android:width="1.5dp" />
|
||||
<!-- <solid android:color="#1772F6"/>-->
|
||||
<solid android:color="@color/green" />
|
||||
</shape>
|
||||
|
12
app/src/main/res/drawable/ic_clean.xml
Normal file
12
app/src/main/res/drawable/ic_clean.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="70dp"
|
||||
android:height="70dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M512,0a512,512 0,0 0,-512 512,512 512,0 0,0 512,512 512,512 0,0 0,512 -512,512 512,0 0,0 -512,-512z"
|
||||
android:fillColor="#7D7D7D"/>
|
||||
<path
|
||||
android:pathData="M717.2,306.2a36,36 0,0 0,-50.8 0.1L512,461 357.7,306.3a36,36 0,0 0,-51 50.8L461.2,512 306.7,666.9a36,36 0,0 0,51 50.8L512,563l154.3,154.8a35.7,35.7 0,0 0,50.8 0.1,36 36,0 0,0 0.1,-50.9L562.8,512l154.3,-154.9a35.9,35.9 0,0 0,0 -50.9z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_yes.xml
Normal file
9
app/src/main/res/drawable/ic_yes.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="70dp"
|
||||
android:height="70dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="@color/green"
|
||||
android:pathData="M925.9,268.8c-17.1,-17.1 -42.7,-17.1 -59.7,0L426.7,708.3l-183.5,-183.5c-17.1,-17.1 -42.7,-12.8 -59.7,4.3 -17.1,17.1 -17.1,42.7 0,59.7l213.3,213.3c17.1,17.1 42.7,17.1 59.7,0l469.3,-469.3c17.1,-21.3 17.1,-46.9 0,-64z" />
|
||||
</vector>
|
@ -8,57 +8,12 @@
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<com.kaixed.kchat.ui.widget.CustomTitleBar
|
||||
android:id="@+id/ctb"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="22dp"
|
||||
android:src="@drawable/ic_left_arrow"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_contact_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="7dp"
|
||||
android:text="contact"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/tv_contact_status"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_back" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_contact_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="在线"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="8sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_back"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_contact_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_contact_name" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_more"
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="22dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:src="@drawable/ic_more"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_back"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_back" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
app:titleIcon="@drawable/ic_more"
|
||||
app:titleName="contact" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycle_chat_list"
|
||||
|
@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/gray"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.activity.ChatDetailActivity">
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomTitleBar
|
||||
@ -15,259 +17,79 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:titleName="聊天信息" />
|
||||
|
||||
<ImageView
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="150dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/bac_contacts_detail"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/ifv_avatar"
|
||||
android:layout_width="55dp"
|
||||
android:layout_height="55dp"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:src="@drawable/ic_avatar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/ctb"
|
||||
app:round="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_contact_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:text="kaixed"
|
||||
app:layout_constraintEnd_toEndOf="@id/ifv_avatar"
|
||||
app:layout_constraintStart_toStartOf="@id/ifv_avatar"
|
||||
app:layout_constraintTop_toBottomOf="@id/ifv_avatar" />
|
||||
android:background="@color/white"
|
||||
android:paddingVertical="10dp">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/ifv_avatar"
|
||||
android:layout_width="45dp"
|
||||
android:layout_height="45dp"
|
||||
android:layout_marginStart="15dp"
|
||||
android:src="@drawable/ic_avatar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:roundPercent="0.2" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_contact_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:text="kaixed"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="@id/ifv_avatar"
|
||||
app:layout_constraintStart_toStartOf="@id/ifv_avatar"
|
||||
app:layout_constraintTop_toBottomOf="@id/ifv_avatar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_contact_name">
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:itemName="查找聊天记录" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:background="#E5E5E5" />
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:isBottomDividerVisible="true"
|
||||
app:itemName="消息免打扰"
|
||||
app:rightType="switch" />
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:isBottomDividerVisible="true"
|
||||
app:itemName="置顶聊天"
|
||||
app:rightType="switch" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:paddingStart="15dp">
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:itemName="提醒"
|
||||
app:rightType="switch" />
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:itemName="设置当前聊天背景" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="查找聊天记录"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp" />
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:itemName="清空聊天记录" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:src="@drawable/icon_arrow_right" />
|
||||
</RelativeLayout>
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:itemName="投诉" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:background="#E5E5E5" />
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:paddingStart="15dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="消息免打扰"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:src="@drawable/icon_arrow_right" />
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginStart="15dp"
|
||||
android:background="#E5E5E5" />
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:paddingStart="15dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="置顶聊天"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:src="@drawable/icon_arrow_right" />
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginStart="15dp"
|
||||
android:background="#E5E5E5" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:paddingStart="15dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="提醒"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:src="@drawable/icon_arrow_right" />
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:background="#E5E5E5" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:paddingStart="15dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="设置当前聊天背景"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:src="@drawable/icon_arrow_right" />
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:background="#E5E5E5" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:paddingStart="15dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="清空聊天记录"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:src="@drawable/icon_arrow_right" />
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:background="#E5E5E5" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:paddingStart="15dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="投诉"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:src="@drawable/icon_arrow_right" />
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#E5E5E5" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
69
app/src/main/res/layout/activity_contact_permission.xml
Normal file
69
app/src/main/res/layout/activity_contact_permission.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#EDEDED"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.activity.ContactPermissionActivity">
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomTitleBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:titleName="朋友权限" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:background="#E3E3E3" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:paddingVertical="10dp"
|
||||
android:text="设置朋友权限"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:isBottomDividerVisible="true"
|
||||
app:itemName="聊天、朋友圈、运动等"
|
||||
app:rightType="yes" />
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:id="@+id/ci_chat"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:itemName="仅聊天"
|
||||
app:rightType="yes" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:paddingVertical="10dp"
|
||||
android:text="朋友圈和状态"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:isBottomDividerVisible="true"
|
||||
app:itemName="不让他看我"
|
||||
app:rightType="switch" />
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:itemName="不看他"
|
||||
app:rightType="switch" />
|
||||
|
||||
</LinearLayout>
|
@ -95,6 +95,7 @@
|
||||
app:itemName="设置备注和标签" />
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:id="@+id/ci_permission"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:isTopDividerVisible="true"
|
||||
|
@ -28,11 +28,13 @@
|
||||
app:itemName="设置备注和标签" />
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:id="@+id/ci_permission"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:itemName="朋友权限" />
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:id="@+id/ci_recommend_to_contact"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin"
|
||||
@ -61,6 +63,7 @@
|
||||
app:rightType="switch" />
|
||||
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:id="@+id/ci_complaints"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:itemName="投诉" />
|
||||
|
@ -6,7 +6,7 @@
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- <FrameLayout-->
|
||||
<!-- <FrameLayout-->
|
||||
<!-- android:id="@+id/fl_main"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="0dp"-->
|
||||
@ -28,8 +28,8 @@
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/gray"
|
||||
android:layout_height="@dimen/divider_height"
|
||||
android:background="#D2D2D2"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
@ -57,13 +57,33 @@
|
||||
app:cardElevation="0dp"
|
||||
app:cardMaxElevation="0dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_remark"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#F7F7F7"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:textCursorDrawable="@drawable/cursor" />
|
||||
android:background="#F7F7F7">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_remark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#F7F7F7"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:textCursorDrawable="@drawable/cursor"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_clean"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:src="@drawable/ic_clean"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<TextView
|
||||
@ -75,6 +95,7 @@
|
||||
android:textSize="13sp" />
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/cv_add_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginHorizontal="15dp"
|
||||
@ -118,6 +139,7 @@
|
||||
android:textSize="13sp" />
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/cv_add_telephone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginHorizontal="15dp"
|
||||
|
@ -7,6 +7,7 @@
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -37,7 +37,7 @@
|
||||
android:minHeight="35dp"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:text="你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好"
|
||||
android:text="haha"
|
||||
android:textColor="@color/white"
|
||||
android:textColorHighlight="#CCCCCC"
|
||||
android:textIsSelectable="true"
|
||||
|
@ -6,14 +6,32 @@
|
||||
android:paddingVertical="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/tv_timer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="这是一个小tip"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingTop="20dp"
|
||||
android:text="昨天"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/ifv_avatar"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:src="@drawable/ic_avatar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_timer"
|
||||
app:roundPercent="0.3" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintStart_toEndOf="@id/ifv_avatar"
|
||||
app:layout_constraintTop_toTopOf="@id/ifv_avatar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -33,7 +33,7 @@
|
||||
android:layout_marginStart="15dp"
|
||||
android:text="xxx"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="17sp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_item_left_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
@ -92,13 +92,31 @@
|
||||
android:layout_centerInParent="true"
|
||||
android:src="@drawable/icon_arrow_right" />
|
||||
|
||||
<com.kaixed.kchat.ui.widget.ShSwitchView
|
||||
android:id="@+id/ssv_switch"
|
||||
android:layout_width="57dp"
|
||||
android:layout_height="38dp"
|
||||
<com.kaixed.kchat.ui.widget.SwitchButton
|
||||
android:id="@+id/sb_switch"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_gravity="center_vertical"
|
||||
app:sb_checked="false"
|
||||
android:visibility="gone"
|
||||
app:tintColor="@color/green" />
|
||||
app:sb_checked_bg="@color/green"
|
||||
app:sb_circle_bg_margin="3.5dp"
|
||||
app:sb_unchecked_bg="#EAEAEA" />
|
||||
|
||||
<!-- <com.kaixed.kchat.ui.widget.ShSwitchView-->
|
||||
<!-- android:id="@+id/ssv_switch"-->
|
||||
<!-- android:layout_width="57dp"-->
|
||||
<!-- android:layout_height="38dp"-->
|
||||
<!-- app:tintColor="@color/green" />-->
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_yes"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:src="@drawable/ic_yes"
|
||||
android:visibility="invisible" />
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
|
@ -36,4 +36,15 @@
|
||||
<attr name="outerStrokeWidth" format="reference|dimension" />
|
||||
<attr name="shadowSpace" format="reference|dimension" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SwitchButton">
|
||||
<!-- 关闭时的背景颜色 -->
|
||||
<attr name="sb_unchecked_bg" format="color" />
|
||||
<!-- 打开时的背景颜色 -->
|
||||
<attr name="sb_checked_bg" format="color" />
|
||||
<!-- 开关小圆距离开关背景的距离 -->
|
||||
<attr name="sb_circle_bg_margin" format="dimension" />
|
||||
<!-- 是否默认打开 -->
|
||||
<attr name="sb_checked" format="boolean" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
3
app/src/main/res/values/ids.xml
Normal file
3
app/src/main/res/values/ids.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
</resources>
|
@ -17,11 +17,13 @@ activity = "1.9.3"
|
||||
constraintlayout = "2.2.0"
|
||||
mmkv = "1.3.9"
|
||||
okhttp = "4.12.0"
|
||||
pictureselector = "v3.11.2"
|
||||
pinyin4j = "2.5.1"
|
||||
preference = "1.2.1"
|
||||
retrofit = "2.11.0"
|
||||
shapedrawable = "3.2"
|
||||
shapeview = "9.2"
|
||||
spannable = "1.2.7"
|
||||
therouter = "1.2.2"
|
||||
window = "1.3.0"
|
||||
#noinspection GradleDependency
|
||||
@ -31,14 +33,17 @@ objectbox = "4.0.2"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
compress = { module = "io.github.lucksiege:compress", version.ref = "pictureselector" }
|
||||
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesCore" }
|
||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
|
||||
okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "loggingInterceptorVersion" }
|
||||
pictureselector = { module = "io.github.lucksiege:pictureselector", version.ref = "pictureselector" }
|
||||
pinyin4j = { module = "com.belerweb:pinyin4j", version.ref = "pinyin4j" }
|
||||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||
retrofit2-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
|
||||
shapedrawable = { module = "com.github.getActivity:ShapeDrawable", version.ref = "shapedrawable" }
|
||||
shapeview = { module = "com.github.getActivity:ShapeView", version.ref = "shapeview" }
|
||||
spannable = { module = "com.github.liangjingkanji:spannable", version.ref = "spannable" }
|
||||
therouter-ksp = { module = "cn.therouter:apt", version.ref = "therouter" }
|
||||
objectbox-android-objectbrowser = { group = "io.objectbox", name = "objectbox-android-objectbrowser", version.ref = "objectbox" }
|
||||
objectbox-kotlin = { group = "io.objectbox", name = "objectbox-kotlin", version.ref = "objectbox" }
|
||||
@ -59,6 +64,7 @@ mmkv = { module = "com.tencent:mmkv", version.ref = "mmkv" }
|
||||
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||
preference = { module = "androidx.preference:preference", version.ref = "preference" }
|
||||
therouter = { module = "cn.therouter:router", version.ref = "therouter" }
|
||||
ucrop = { module = "io.github.lucksiege:ucrop", version.ref = "pictureselector" }
|
||||
window = { module = "androidx.window:window", version.ref = "window" }
|
||||
|
||||
[plugins]
|
||||
|
Loading…
Reference in New Issue
Block a user