feat: 新增删除好友功能

1.新增删除好友功能
2.更新CustomItem显示样式
This commit is contained in:
糕小菜 2024-11-21 23:09:39 +08:00
parent 92e622792a
commit a07711d993
50 changed files with 1194 additions and 636 deletions

View File

@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2024-11-13T03:55:58.129272100Z"> <DropdownSelection timestamp="2024-11-19T05:46:34.362744600Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="Default" identifier="serial=10.160.86.233:5555;connection=a4032080" /> <DeviceId pluginId="Default" identifier="serial=10.127.185.244:5555;connection=5b2cc3eb" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

View File

@ -172,7 +172,7 @@
}, },
{ {
"id": "6:411582187056789368", "id": "6:411582187056789368",
"lastPropertyId": "7:1280810032716163450", "lastPropertyId": "8:2020630799900991467",
"name": "Conversation", "name": "Conversation",
"properties": [ "properties": [
{ {
@ -210,6 +210,11 @@
"id": "7:1280810032716163450", "id": "7:1280810032716163450",
"name": "unreadCount", "name": "unreadCount",
"type": 5 "type": 5
},
{
"id": "8:2020630799900991467",
"name": "show",
"type": 1
} }
], ],
"relations": [] "relations": []

View File

@ -57,50 +57,6 @@
], ],
"relations": [] "relations": []
}, },
{
"id": "2:6854189850259048168",
"lastPropertyId": "10:8705063061921345729",
"name": "ChatLists",
"properties": [
{
"id": "1:5279270693453549140",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "3:7775198743178107108",
"name": "nickname",
"type": 9
},
{
"id": "4:7079852095664590440",
"name": "avatarUrl",
"type": 9
},
{
"id": "5:3457793996580594247",
"name": "lastContent",
"type": 9
},
{
"id": "6:6315401035981995789",
"name": "timestamp",
"type": 6
},
{
"id": "9:2123413060720974577",
"name": "talkerId",
"type": 9
},
{
"id": "10:8705063061921345729",
"name": "unreadCount",
"type": 5
}
],
"relations": []
},
{ {
"id": "4:6179749773128044271", "id": "4:6179749773128044271",
"lastPropertyId": "13:6446821128426983596", "lastPropertyId": "13:6446821128426983596",
@ -213,16 +169,61 @@
} }
], ],
"relations": [] "relations": []
},
{
"id": "6:411582187056789368",
"lastPropertyId": "7:1280810032716163450",
"name": "Conversation",
"properties": [
{
"id": "1:8070670082991740155",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:62500236312534806",
"name": "talkerId",
"type": 9
},
{
"id": "3:1809723298214930621",
"name": "nickname",
"type": 9
},
{
"id": "4:3806556063168110787",
"name": "avatarUrl",
"type": 9
},
{
"id": "5:184921755262649486",
"name": "lastContent",
"type": 9
},
{
"id": "6:2348518020158588886",
"name": "timestamp",
"type": 6
},
{
"id": "7:1280810032716163450",
"name": "unreadCount",
"type": 5
}
],
"relations": []
} }
], ],
"lastEntityId": "5:2885532406154205395", "lastEntityId": "6:411582187056789368",
"lastIndexId": "0:0", "lastIndexId": "0:0",
"lastRelationId": "0:0", "lastRelationId": "0:0",
"lastSequenceId": "0:0", "lastSequenceId": "0:0",
"modelVersion": 5, "modelVersion": 5,
"modelVersionParserMinimum": 5, "modelVersionParserMinimum": 5,
"retiredEntityUids": [ "retiredEntityUids": [
4156940900268054047 4156940900268054047,
6854189850259048168
], ],
"retiredIndexUids": [], "retiredIndexUids": [],
"retiredPropertyUids": [ "retiredPropertyUids": [
@ -238,7 +239,14 @@
1118829668259721786, 1118829668259721786,
1490263641107250384, 1490263641107250384,
2701112806559265255, 2701112806559265255,
7345071619836824250 7345071619836824250,
5279270693453549140,
7775198743178107108,
7079852095664590440,
3457793996580594247,
6315401035981995789,
2123413060720974577,
8705063061921345729
], ],
"retiredRelationUids": [], "retiredRelationUids": [],
"version": 1 "version": 1

View File

@ -2,10 +2,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission <uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE" android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" /> android:maxSdkVersion="32" />
@ -14,7 +18,6 @@
android:maxSdkVersion="32" android:maxSdkVersion="32"
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
<application <application
android:name=".KchatApplication" android:name=".KchatApplication"
android:allowBackup="true" android:allowBackup="true"
@ -29,6 +32,12 @@
android:theme="@style/Theme.KChatAndroid" android:theme="@style/Theme.KChatAndroid"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:targetApi="31"> tools:targetApi="31">
<activity
android:name=".ui.activity.SetRemarkAndLabelActivity"
android:exported="false" />
<activity
android:name=".ui.activity.ContactRequestListActivity"
android:exported="false" />
<activity <activity
android:name=".ui.activity.RenameActivity" android:name=".ui.activity.RenameActivity"
android:exported="false" /> android:exported="false" />

View File

@ -18,4 +18,5 @@ data class Conversation(
var lastContent: String, var lastContent: String,
var timestamp: Long, var timestamp: Long,
var unreadCount: Int = 0, var unreadCount: Int = 0,
var show: Boolean = true
) )

View File

@ -1,10 +1,11 @@
package com.kaixed.kchat.model.friend package com.kaixed.kchat.model.friend
import com.kaixed.kchat.data.objectbox.entity.Contact
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
data class AcceptContactRequest( data class AcceptContactRequest(
val code: String, val code: String,
val `data`: String, val msg: String,
val msg: String val `data`: Contact?
) )

View File

@ -5,6 +5,6 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class ContactRequestResponse( data class ContactRequestResponse(
val code: String, val code: String,
val msg: String,
val `data`: List<FriendRequestItem>, val `data`: List<FriendRequestItem>,
val msg: String
) )

View File

@ -0,0 +1,7 @@
package com.kaixed.kchat.model.response.friend
data class DeleteContact(
val code: String,
val `data`: String?,
val msg: String
)

View File

@ -3,6 +3,7 @@ package com.kaixed.kchat.network.service
import com.kaixed.kchat.model.friend.AcceptContactRequest import com.kaixed.kchat.model.friend.AcceptContactRequest
import com.kaixed.kchat.model.friend.ContactRequestResponse import com.kaixed.kchat.model.friend.ContactRequestResponse
import com.kaixed.kchat.model.response.ApplyFriend import com.kaixed.kchat.model.response.ApplyFriend
import com.kaixed.kchat.model.response.friend.DeleteContact
import com.kaixed.kchat.model.response.friend.FriendList import com.kaixed.kchat.model.response.friend.FriendList
import com.kaixed.kchat.model.response.friend.SearchFriends import com.kaixed.kchat.model.response.friend.SearchFriends
import retrofit2.Response import retrofit2.Response
@ -26,7 +27,8 @@ interface ContactService {
@POST("friend/accept") @POST("friend/accept")
suspend fun acceptContactRequest( suspend fun acceptContactRequest(
@Field("requestId") contactId: String, @Field("requestId") contactId: String,
@Field("receiverId") username: String @Field("receiverId") username: String,
@Field("remark") remark: String
): Response<AcceptContactRequest> ): Response<AcceptContactRequest>
// 添加联系人 // 添加联系人
@ -50,4 +52,12 @@ interface ContactService {
suspend fun getContactList( suspend fun getContactList(
@Field("userId") username: String @Field("userId") username: String
): Response<FriendList> ): Response<FriendList>
// 删除好友
@FormUrlEncoded
@POST("friend/delete")
suspend fun deleteContact(
@Field("userId") username: String,
@Field("contactId") contactId: String,
): Response<DeleteContact>
} }

View File

@ -2,6 +2,7 @@ package com.kaixed.kchat.network.service
import com.kaixed.kchat.model.request.RegisterRequest import com.kaixed.kchat.model.request.RegisterRequest
import com.kaixed.kchat.model.request.UserRequest import com.kaixed.kchat.model.request.UserRequest
import com.kaixed.kchat.model.response.friend.SearchFriends
import com.kaixed.kchat.model.response.login.Login import com.kaixed.kchat.model.response.login.Login
import com.kaixed.kchat.model.response.register.Register import com.kaixed.kchat.model.response.register.Register
import com.kaixed.kchat.model.response.search.UserList import com.kaixed.kchat.model.response.search.UserList
@ -52,6 +53,12 @@ interface UserApiService {
@Path("username") username: String @Path("username") username: String
): Response<UserList> ): Response<UserList>
// 获取用户信息
@GET("users/{username}")
suspend fun getUserInfo(
@Path("username") username: String
): Response<SearchFriends>
// 更改昵称接口 // 更改昵称接口
@POST("users/info") @POST("users/info")
suspend fun changeNickname( suspend fun changeNickname(

View File

@ -1,14 +1,18 @@
package com.kaixed.kchat.repository package com.kaixed.kchat.repository
import android.util.Log import android.util.Log
import com.kaixed.kchat.data.objectbox.ObjectBox.get
import com.kaixed.kchat.data.objectbox.entity.Contact import com.kaixed.kchat.data.objectbox.entity.Contact
import com.kaixed.kchat.data.objectbox.entity.Contact_
import com.kaixed.kchat.model.friend.AcceptContactRequest import com.kaixed.kchat.model.friend.AcceptContactRequest
import com.kaixed.kchat.model.friend.ContactRequestResponse import com.kaixed.kchat.model.friend.ContactRequestResponse
import com.kaixed.kchat.model.response.ApplyFriend import com.kaixed.kchat.model.response.ApplyFriend
import com.kaixed.kchat.model.response.friend.DeleteContact
import com.kaixed.kchat.model.response.friend.FriendList import com.kaixed.kchat.model.response.friend.FriendList
import com.kaixed.kchat.model.response.friend.SearchFriends import com.kaixed.kchat.model.response.friend.SearchFriends
import com.kaixed.kchat.network.RetrofitClient import com.kaixed.kchat.network.RetrofitClient
import com.kaixed.kchat.utils.ConstantsUtil.getUsername import com.kaixed.kchat.utils.ConstantsUtil.getUsername
import io.objectbox.Box
import okhttp3.ResponseBody.Companion.toResponseBody import okhttp3.ResponseBody.Companion.toResponseBody
import retrofit2.Response import retrofit2.Response
@ -16,6 +20,10 @@ class ContactRepository {
private val contactApiService = RetrofitClient.contactApiService private val contactApiService = RetrofitClient.contactApiService
private val contactBox: Box<Contact> by lazy {
get().boxFor(Contact::class.java)
}
// 获取联系人请求列表 // 获取联系人请求列表
suspend fun getContactRequestList(username: String): Response<ContactRequestResponse> { suspend fun getContactRequestList(username: String): Response<ContactRequestResponse> {
return try { return try {
@ -29,16 +37,31 @@ class ContactRepository {
// 接受联系人请求 // 接受联系人请求
suspend fun acceptContactRequest( suspend fun acceptContactRequest(
username: String, username: String,
contactId: String contactId: String,
remark: String
): Response<AcceptContactRequest> { ): Response<AcceptContactRequest> {
return try { return try {
contactApiService.acceptContactRequest(contactId, username) contactApiService.acceptContactRequest(contactId, username, remark)
} catch (e: Exception) { } catch (e: Exception) {
Log.e("ContactRepository", "Accept Contact Request failed: ${e.message}") Log.e("ContactRepository", "Accept Contact Request failed: ${e.message}")
Response.error(500, "".toResponseBody()) Response.error(500, "".toResponseBody())
} }
} }
// 删除联系人
suspend fun deleteContact(
username: String,
contactId: String,
): Response<DeleteContact> {
try {
contactBox.query(Contact_.username.equal(contactId))
return contactApiService.deleteContact(username, contactId)
} catch (e: Exception) {
Log.e("ContactRepository", "Delete Contact failed: ${e.message}")
return Response.error(500, "".toResponseBody())
}
}
// 添加联系人 // 添加联系人
suspend fun addContact(contactId: String, message: String): Response<ApplyFriend> { suspend fun addContact(contactId: String, message: String): Response<ApplyFriend> {
return try { return try {

View File

@ -3,6 +3,7 @@ package com.kaixed.kchat.repository
import android.util.Log import android.util.Log
import com.kaixed.kchat.model.request.RegisterRequest import com.kaixed.kchat.model.request.RegisterRequest
import com.kaixed.kchat.model.request.UserRequest import com.kaixed.kchat.model.request.UserRequest
import com.kaixed.kchat.model.response.friend.SearchFriends
import com.kaixed.kchat.model.response.login.Login import com.kaixed.kchat.model.response.login.Login
import com.kaixed.kchat.model.response.register.Register import com.kaixed.kchat.model.response.register.Register
import com.kaixed.kchat.model.response.search.UserList import com.kaixed.kchat.model.response.search.UserList
@ -49,6 +50,16 @@ class UserRepository {
suspend fun getUserList(username: String): Response<UserList> { suspend fun getUserList(username: String): Response<UserList> {
return try { return try {
userApiService.getUserListByNickname(username) userApiService.getUserListByNickname(username)
} catch (e: Exception) {
Log.e("UserRepository", "Get User Info Failed: ${e.message}", e)
Response.error(500, "".toResponseBody())
}
}
// 获取用户信息
suspend fun getUserInfo(username: String): Response<SearchFriends> {
return try {
userApiService.getUserInfo(username)
} catch (e: Exception) { } catch (e: Exception) {
Log.e("UserRepository", "Get User List Failed: ${e.message}", e) Log.e("UserRepository", "Get User List Failed: ${e.message}", e)
Response.error(500, "".toResponseBody()) Response.error(500, "".toResponseBody())

View File

@ -29,6 +29,7 @@ import okhttp3.Response
import okhttp3.WebSocket import okhttp3.WebSocket
import okhttp3.WebSocketListener import okhttp3.WebSocketListener
class WebSocketService : Service() { class WebSocketService : Service() {
companion object { companion object {
@ -44,8 +45,6 @@ class WebSocketService : Service() {
private val binder = LocalBinder() private val binder = LocalBinder()
private val messagesMutableLiveData = MutableLiveData<Messages>()
private var webSocket: WebSocket? = null private var webSocket: WebSocket? = null
private var heartbeatJob: Job? = null private var heartbeatJob: Job? = null
@ -54,7 +53,9 @@ class WebSocketService : Service() {
private val serviceScope = CoroutineScope(IO + serviceJob) private val serviceScope = CoroutineScope(IO + serviceJob)
val liveData: LiveData<Messages> get() = messagesMutableLiveData private val _messagesMutableLiveData = MutableLiveData<Messages>()
val messageLivedata: LiveData<Messages> get() = _messagesMutableLiveData
inner class LocalBinder : Binder() { inner class LocalBinder : Binder() {
fun getService(): WebSocketService { fun getService(): WebSocketService {
@ -70,6 +71,7 @@ class WebSocketService : Service() {
super.onCreate() super.onCreate()
messagesBox = get().boxFor(Messages::class.java) messagesBox = get().boxFor(Messages::class.java)
username = getUsername() username = getUsername()
firstLoad()
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@ -85,6 +87,10 @@ class WebSocketService : Service() {
} }
} }
fun sendMessage(messages: Messages) {
updateConversationList(messages)
}
private fun establishConnection() { private fun establishConnection() {
if (webSocket == null) { if (webSocket == null) {
val request = Request.Builder() val request = Request.Builder()
@ -104,7 +110,7 @@ class WebSocketService : Service() {
conversation?.let { conversation?.let {
conversation.unreadCount = conversation.unreadCount =
if (currentContactId.isNotEmpty() && conversation.talkerId == currentContactId) 0 if (conversation.talkerId == currentContactId || messages.senderId == getUsername()) 0
else conversation.unreadCount + 1 else conversation.unreadCount + 1
conversation.lastContent = messages.content conversation.lastContent = messages.content
@ -150,13 +156,11 @@ class WebSocketService : Service() {
Log.d(TAG, "Message received: $messages") Log.d(TAG, "Message received: $messages")
messages.takerId = messages.senderId messages.takerId = messages.senderId
messagesMutableLiveData.postValue(messages)
updateDbConversation(messages) updateDbConversation(messages)
serviceScope.launch { serviceScope.launch {
if ("ack" == messages.type) { if ("ack" == messages.type) {
val existingMessage = messagesBox.get(messages.msgLocalId) val existingMessage = messagesBox.get(messages.msgLocalId)
existingMessage?.let { existingMessage?.let {
it.timestamp = messages.timestamp it.timestamp = messages.timestamp
@ -166,9 +170,10 @@ class WebSocketService : Service() {
messagesBox.put(messages) messagesBox.put(messages)
} }
} else { } else {
_messagesMutableLiveData.postValue(messages)
messagesBox.put(messages) messagesBox.put(messages)
updateConversationList(messages)
} }
Log.d(TAG, "Message stored: $messages")
} }
} }
@ -183,6 +188,46 @@ class WebSocketService : Service() {
} }
} }
private val _conversations = MutableLiveData<List<Conversation>?>()
val conversations: LiveData<List<Conversation>?> get() = _conversations
private var conversationList = mutableListOf<Conversation>()
private fun updateConversationList(messages: Messages) {
updateDbConversation(messages)
val index = conversationList.indexOfFirst { it.talkerId == messages.takerId }
if (index != -1) {
if (messages.timestamp < conversationList[index].timestamp) {
return
}
conversationList[index].apply {
lastContent = messages.content
timestamp = messages.timestamp
unreadCount =
if (this.talkerId == getCurrentContactId() || messages.senderId == getUsername()) 0 else unreadCount + 1
}
} else {
conversationList.add(
createChatList(
talkerId = messages.takerId,
nickname = messages.takerId,
content = messages.content,
timestamp = messages.timestamp
)
)
}
_conversations.postValue(conversationList.sortedByDescending { it.timestamp })
}
private fun firstLoad() {
conversationList = conversationBox.query(Conversation_.show.equal(true))
.orderDesc(Conversation_.timestamp)
.build()
.find()
_conversations.postValue(conversationList)
}
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
serviceJob.cancel() serviceJob.cancel()

View File

@ -9,13 +9,17 @@ import androidx.activity.viewModels
import com.kaixed.kchat.databinding.ActivityApproveContactRequestBinding import com.kaixed.kchat.databinding.ActivityApproveContactRequestBinding
import com.kaixed.kchat.ui.base.BaseActivity import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.utils.ConstantsUtil.getUsername import com.kaixed.kchat.utils.ConstantsUtil.getUsername
import com.kaixed.kchat.utils.handle.ContactUtil
import com.kaixed.kchat.viewmodel.ContactViewModel import com.kaixed.kchat.viewmodel.ContactViewModel
class ApproveContactRequestActivity : BaseActivity<ActivityApproveContactRequestBinding>() { class ApproveContactRequestActivity : BaseActivity<ActivityApproveContactRequestBinding>() {
private val contactViewModel: ContactViewModel by viewModels() private val contactViewModel: ContactViewModel by viewModels()
private var nickname: String? = null
private var message: String? = null
private var contactId: String? = null
override fun inflateBinding(): ActivityApproveContactRequestBinding { override fun inflateBinding(): ActivityApproveContactRequestBinding {
return ActivityApproveContactRequestBinding.inflate(layoutInflater) return ActivityApproveContactRequestBinding.inflate(layoutInflater)
} }
@ -24,28 +28,40 @@ class ApproveContactRequestActivity : BaseActivity<ActivityApproveContactRequest
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
handleIntent(intent)
setContent() setContent()
setOnClickListener() setOnClickListener()
}
private fun handleIntent(intent: Intent) {
nickname = intent.getStringExtra("nickname")
message = intent.getStringExtra("message")
contactId = intent.getStringExtra("contactId")
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun setContent() { private fun setContent() {
val nickname = intent.getStringExtra("nickname")
val message = intent.getStringExtra("message")
binding.etRemark.setText(nickname) binding.etRemark.setText(nickname)
binding.tvMessage.text = "$message" binding.tvMessage.text = "$message"
} }
private fun setOnClickListener() { private fun setOnClickListener() {
binding.tvFinish.setOnClickListener { binding.tvFinish.setOnClickListener {
val contactId = intent.getStringExtra("contactId") acceptContactRequest(contactId!!)
contactViewModel.acceptContactRequestResponse }
.observe(this) { value -> }
value?.let {
runOnUiThread { private fun acceptContactRequest(contactId: String) {
toast(it.data) contactViewModel.acceptContactRequestResponse
.observe(this) { value ->
value?.let {
if (value.code != "200") {
toast(it.msg)
} else {
value.data?.let { it1 ->
ContactUtil.handleContact(it1)
} }
val stackBuilder: TaskStackBuilder = TaskStackBuilder.create(this) val stackBuilder: TaskStackBuilder = TaskStackBuilder.create(this)
stackBuilder.addNextIntentWithParentStack( stackBuilder.addNextIntentWithParentStack(
@ -60,11 +76,12 @@ class ApproveContactRequestActivity : BaseActivity<ActivityApproveContactRequest
) )
stackBuilder.startActivities() stackBuilder.startActivities()
} }
} }
}
contactViewModel.acceptContactRequest(getUsername(), contactId!!) val remark = binding.etRemark.text.toString()
}
contactViewModel.acceptContactRequest(getUsername(), contactId, remark)
} }
} }

View File

@ -5,30 +5,44 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.kaixed.kchat.databinding.ActivityApproveDetailBinding import com.kaixed.kchat.databinding.ActivityApproveDetailBinding
import com.kaixed.kchat.model.friend.FriendRequestItem import com.kaixed.kchat.model.friend.FriendRequestItem
import com.kaixed.kchat.ui.base.BaseActivity
class ApproveDetailActivity : AppCompatActivity() { class ApproveDetailActivity : BaseActivity<ActivityApproveDetailBinding>() {
private lateinit var binding: ActivityApproveDetailBinding
private var request: FriendRequestItem? = null private var request: FriendRequestItem? = null
override fun inflateBinding(): ActivityApproveDetailBinding {
return ActivityApproveDetailBinding.inflate(layoutInflater)
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
binding = ActivityApproveDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
request = intent.getParcelableExtra("request", FriendRequestItem::class.java) handleIntent(intent)
setContent()
setOnClick() setOnClick()
} }
private fun handleIntent(intent: Intent) {
request = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra("request", FriendRequestItem::class.java)
} else {
@Suppress("DEPRECATION")
intent.getParcelableExtra("request") as? FriendRequestItem
}
}
private fun setContent() { private fun setContent() {
if (request == null) {
return
}
val nickname = request?.nickname val nickname = request?.nickname
val signature = request?.signature val signature = request?.signature
val avatarUrl = request?.avatarUrl val avatarUrl = request?.avatarUrl
@ -50,6 +64,7 @@ class ApproveDetailActivity : AppCompatActivity() {
Intent(this, ApproveContactRequestActivity::class.java).apply { Intent(this, ApproveContactRequestActivity::class.java).apply {
putExtra("nickname", request?.nickname) putExtra("nickname", request?.nickname)
putExtra("message", request?.message) putExtra("message", request?.message)
putExtra("contactId", request?.username)
} }
) )
} }

View File

@ -10,6 +10,7 @@ import android.graphics.Rect
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.Bundle import android.os.Bundle
import android.os.IBinder import android.os.IBinder
import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
@ -37,6 +38,8 @@ import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.ui.i.OnItemClickListener import com.kaixed.kchat.ui.i.OnItemClickListener
import com.kaixed.kchat.utils.Constants.CURRENT_CONTACT_ID import com.kaixed.kchat.utils.Constants.CURRENT_CONTACT_ID
import com.kaixed.kchat.utils.Constants.KEYBOARD_HEIGHT_RATIO import com.kaixed.kchat.utils.Constants.KEYBOARD_HEIGHT_RATIO
import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION
import com.kaixed.kchat.utils.ConstantsUtil.getCurrentContactId
import com.kaixed.kchat.utils.ConstantsUtil.getKeyboardHeight import com.kaixed.kchat.utils.ConstantsUtil.getKeyboardHeight
import com.kaixed.kchat.utils.ConstantsUtil.getUsername import com.kaixed.kchat.utils.ConstantsUtil.getUsername
import com.kaixed.kchat.utils.ImageSpanUtil.insertEmoji import com.kaixed.kchat.utils.ImageSpanUtil.insertEmoji
@ -60,7 +63,7 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
private var contactNickname: String? = null private var contactNickname: String? = null
private var username: String? = null private val username: String by lazy { getUsername() }
private var tempIndex: Long = 0 private var tempIndex: Long = 0
@ -80,7 +83,7 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
private var bound = false private var bound = false
private val mmkv by lazy { MMKV.defaultMMKV() } private val mmkv by lazy { MMKV.mmkvWithID(MMKV_USER_SESSION) }
companion object { companion object {
private const val TAG = "ChatActivity" private const val TAG = "ChatActivity"
@ -110,8 +113,6 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
setPanelChange() setPanelChange()
setBackPressListener()
getKeyBoardVisibility() getKeyBoardVisibility()
} }
@ -251,6 +252,7 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
} }
private fun setListener() { private fun setListener() {
setBackPressListener()
binding.tvContactName.setOnLongClickListener { binding.tvContactName.setOnLongClickListener {
false false
} }
@ -386,7 +388,6 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
private fun initData() { private fun initData() {
messagesBox = get().boxFor(Messages::class.java) messagesBox = get().boxFor(Messages::class.java)
username = getUsername()
softKeyboardHeight = getKeyboardHeight() softKeyboardHeight = getKeyboardHeight()
mmkv.putString(CURRENT_CONTACT_ID, contactId) mmkv.putString(CURRENT_CONTACT_ID, contactId)
} }
@ -458,15 +459,15 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
if (webSocketService == null) { if (webSocketService == null) {
return return
} }
webSocketService!!.liveData.observe( webSocketService!!.messageLivedata.observe(
this this
) { messages: Messages -> ) { messages: Messages ->
Log.d("haha11", messages.toString())
if ("ack" != messages.type && username != messages.senderId) { if ("ack" != messages.type && username != messages.senderId) {
if (messages.msgLocalId != 0L) { val a = getCurrentContactId()
return@observe if (messages.takerId == contactId) {
} messagesList.addFirst(messages)
messagesList.addFirst(messages)
runOnUiThread {
chatAdapter!!.notifyItemInserted(0) chatAdapter!!.notifyItemInserted(0)
binding.recycleChatList.smoothScrollToPosition(0) binding.recycleChatList.smoothScrollToPosition(0)
} }
@ -478,7 +479,7 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
val messages = Messages( val messages = Messages(
content = message, content = message,
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
senderId = username!!, senderId = username,
takerId = talkerId, takerId = talkerId,
type = type, type = type,
) )
@ -495,6 +496,7 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {
jsonObject.put("type", "single") jsonObject.put("type", "single")
jsonObject.put("body", jsonObject2) jsonObject.put("body", jsonObject2)
webSocketService!!.sendMessage(jsonObject.toString(), id) webSocketService!!.sendMessage(jsonObject.toString(), id)
webSocketService!!.sendMessage(messages)
binding.etInput.setText("") binding.etInput.setText("")
} }

View File

@ -3,32 +3,39 @@ package com.kaixed.kchat.ui.activity
import android.os.Bundle import android.os.Bundle
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.kaixed.kchat.databinding.ActivityMessageBinding import com.kaixed.kchat.databinding.ActivityContactRequestListBinding
import com.kaixed.kchat.model.friend.FriendRequestItem import com.kaixed.kchat.model.friend.FriendRequestItem
import com.kaixed.kchat.ui.adapter.MessageListAdapter import com.kaixed.kchat.ui.adapter.ContactRequestListAdapter
import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.utils.ConstantsUtil.getUsername import com.kaixed.kchat.utils.ConstantsUtil.getUsername
import com.kaixed.kchat.viewmodel.ContactViewModel import com.kaixed.kchat.viewmodel.ContactViewModel
class MessageActivity : AppCompatActivity() { class ContactRequestListActivity : BaseActivity<ActivityContactRequestListBinding>() {
private lateinit var binding: ActivityMessageBinding
private var items = mutableListOf<FriendRequestItem>() private var items = mutableListOf<FriendRequestItem>()
private val contactViewModel: ContactViewModel by viewModels() private val contactViewModel: ContactViewModel by viewModels()
private lateinit var messageListAdapter: MessageListAdapter
private lateinit var contactRequestListAdapter: ContactRequestListAdapter
override fun inflateBinding(): ActivityContactRequestListBinding {
return ActivityContactRequestListBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
binding = ActivityMessageBinding.inflate(layoutInflater)
setContentView(binding.root)
getItems() getItems()
messageListAdapter = MessageListAdapter(items, this) contactRequestListAdapter = ContactRequestListAdapter(items, this)
binding.recycleFriendRequestList.layoutManager = binding.recycleFriendRequestList.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.recycleFriendRequestList.adapter = messageListAdapter
binding.recycleFriendRequestList.adapter = contactRequestListAdapter
} }
private fun getItems() { private fun getItems() {
@ -36,10 +43,10 @@ class MessageActivity : AppCompatActivity() {
.observe(this) { value -> .observe(this) { value ->
if (value != null) { if (value != null) {
items.addAll(value.data.toMutableList()) items.addAll(value.data.toMutableList())
messageListAdapter.notifyDataSetChanged() contactRequestListAdapter.notifyDataSetChanged()
} }
} }
contactViewModel.getContactRequestList(getUsername()) contactViewModel.getContactRequestList(getUsername())
} }
} }

View File

@ -17,6 +17,7 @@ class ContactsDetailActivity : BaseActivity<ActivityContactsDetailBinding>() {
private val contactBox: Box<Contact> by lazy { get().boxFor(Contact::class.java) } private val contactBox: Box<Contact> by lazy { get().boxFor(Contact::class.java) }
private var contactId: String? = null private var contactId: String? = null
private var contactNickname: String? = null private var contactNickname: String? = null
companion object { companion object {
@ -60,6 +61,19 @@ class ContactsDetailActivity : BaseActivity<ActivityContactsDetailBinding>() {
putExtra("contactNickname", contactId) putExtra("contactNickname", contactId)
}) })
} }
binding.ciSetRemarkAndLabel.setOnClickListener {
startActivity(Intent(this, SetRemarkAndLabelActivity::class.java).apply {
putExtra("contactId", contactId)
putExtra("contactNickname", contactNickname)
})
}
binding.ctb.setOnSettingClickListener {
startActivity(Intent(this, DataSettingActivity::class.java).apply {
putExtra("contactId", contactId)
})
}
} }
private fun getUserInfo(contactId: String): Contact? { private fun getUserInfo(contactId: String): Contact? {

View File

@ -1,13 +1,42 @@
package com.kaixed.kchat.ui.activity package com.kaixed.kchat.ui.activity
import android.app.Dialog
import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.Gravity
import android.view.LayoutInflater
import android.view.Window
import android.view.WindowManager
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.activity.viewModels
import com.kaixed.kchat.data.objectbox.ObjectBox.get
import com.kaixed.kchat.data.objectbox.entity.Contact
import com.kaixed.kchat.data.objectbox.entity.Contact_
import com.kaixed.kchat.databinding.ActivityDataSettingBinding import com.kaixed.kchat.databinding.ActivityDataSettingBinding
import com.kaixed.kchat.databinding.DialogDeleteContactBinding
import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
import com.kaixed.kchat.utils.WidgetUtil
import com.kaixed.kchat.viewmodel.ContactViewModel
import io.objectbox.Box
class DataSettingActivity : BaseActivity<ActivityDataSettingBinding>() {
private val contactViewModel by viewModels<ContactViewModel>()
private val contactBox: Box<Contact> by lazy { get().boxFor(Contact::class.java) }
private var contactId = ""
override fun inflateBinding(): ActivityDataSettingBinding {
return ActivityDataSettingBinding.inflate(layoutInflater)
}
class DataSettingActivity : AppCompatActivity() {
private lateinit var binding: ActivityDataSettingBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
@ -15,13 +44,66 @@ class DataSettingActivity : AppCompatActivity() {
setContentView(binding.root) setContentView(binding.root)
setOnClick() setOnClick()
handleIntent(intent)
}
private fun handleIntent(intent: Intent) {
contactId = intent.getStringExtra("contactId") ?: ""
} }
private fun setOnClick() { private fun setOnClick() {
binding.tvSetRemark.setOnClickListener { binding.ciSetRemarkAndLabel.setOnClickListener {
val intent = Intent(this, SetRemarkActivity::class.java) val intent = Intent(this, SetRemarkActivity::class.java)
startActivity(intent) startActivity(intent)
} }
binding.ivBack.setOnClickListener { finish() }
binding.tvDelete.setOnClickListener {
val dialog = showDeleteDialog(this)
dialog.show()
}
}
private fun showDeleteDialog(context: Context): Dialog {
val binding = DialogDeleteContactBinding.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 layoutParams = WindowManager.LayoutParams().apply {
copyFrom(dialog.window?.attributes)
width = (context.resources.displayMetrics.widthPixels * 0.8).toInt()
gravity = Gravity.CENTER
}
dialog.window?.attributes = layoutParams
binding.tvCancel.setOnClickListener {
dialog.dismiss()
}
binding.tvDelete.setOnClickListener {
deleteContact(getUsername(), contactId)
}
return dialog
}
private fun deleteContact(username: String, contactId: String) {
val dialog = WidgetUtil.showLoadingDialog(this, "删除中...")
dialog.show()
contactViewModel.deleteContact.observe(this) {
it?.let {
val con = contactBox.query(Contact_.username.equal(contactId)).build().findFirst()
contactBox.remove(con!!)
val handle = Handler(Looper.getMainLooper())
handle.postDelayed({
dialog.dismiss()
}, 1000)
toast("删除成功")
} ?: toast("删除失败")
}
contactViewModel.deleteContact(username, contactId)
} }
} }

View File

@ -73,9 +73,9 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), View.OnClickListener {
if (isFirstLaunchApp()) { if (isFirstLaunchApp()) {
lifecycleScope.launch(Dispatchers.Main) { lifecycleScope.launch(Dispatchers.Main) {
val dialog = WidgetUtil.showLoadingDialog(this@MainActivity, "同步数据中") val dialog = WidgetUtil.showLoadingDialog(this@MainActivity, "同步数据中")
dialog.show()
delay(200) delay(200)
contactViewModel.contactListResponse.observe(this@MainActivity) { contacts -> contactViewModel.contactListResponse.observe(this@MainActivity) { contacts ->
contactBox.put(contacts ?: emptyList()) contactBox.put(contacts ?: emptyList())
} }

View File

@ -80,6 +80,7 @@ class ProfileDetailActivity : BaseActivity<ActivityProfileDetailBinding>() {
val filePath = getPathFromUri(uri) val filePath = getPathFromUri(uri)
val dialog = WidgetUtil.showLoadingDialog(this, "正在上传") val dialog = WidgetUtil.showLoadingDialog(this, "正在上传")
dialog.show()
userViewModel.uploadAvatarResponse.observe(this) { value -> userViewModel.uploadAvatarResponse.observe(this) { value ->
value?.let { value?.let {
userSessionMMKV.putString(AVATAR_URL, value) userSessionMMKV.putString(AVATAR_URL, value)

View File

@ -0,0 +1,22 @@
package com.kaixed.kchat.ui.activity
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.kaixed.kchat.R
import com.kaixed.kchat.databinding.ActivitySetRemarkAndLabelBinding
import com.kaixed.kchat.ui.base.BaseActivity
class SetRemarkAndLabelActivity : BaseActivity<ActivitySetRemarkAndLabelBinding>() {
override fun inflateBinding(): ActivitySetRemarkAndLabelBinding {
return ActivitySetRemarkAndLabelBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
}
}

View File

@ -17,8 +17,11 @@ import com.kaixed.kchat.ui.activity.ApproveDetailActivity
* @Author: kaixed * @Author: kaixed
* @Date: 2024/10/18 15:08 * @Date: 2024/10/18 15:08
*/ */
class MessageListAdapter(private val items: MutableList<FriendRequestItem>, val context: Context) : class ContactRequestListAdapter(
RecyclerView.Adapter<MessageListAdapter.MyViewHolder>() { private val items: MutableList<FriendRequestItem>,
val context: Context
) :
RecyclerView.Adapter<ContactRequestListAdapter.MyViewHolder>() {
class MyViewHolder(val binding: FriendRecycleItemAddRequestBinding) : ViewHolder(binding.root) class MyViewHolder(val binding: FriendRecycleItemAddRequestBinding) : ViewHolder(binding.root)
@ -43,6 +46,7 @@ class MessageListAdapter(private val items: MutableList<FriendRequestItem>, val
holder.binding.tvAdd.setOnClickListener { holder.binding.tvAdd.setOnClickListener {
context.startActivity( context.startActivity(
Intent(context, ApproveContactRequestActivity::class.java).apply { Intent(context, ApproveContactRequestActivity::class.java).apply {
putExtra("request", items[position])
putExtra("nickname", items[position].nickname) putExtra("nickname", items[position].nickname)
putExtra("contactId", items[position].username) putExtra("contactId", items[position].username)
putExtra("message", items[position].message) putExtra("message", items[position].message)

View File

@ -14,20 +14,26 @@ import com.kaixed.kchat.R
import com.kaixed.kchat.data.objectbox.entity.Contact import com.kaixed.kchat.data.objectbox.entity.Contact
import com.kaixed.kchat.databinding.FriendRecycleFooterItemBinding import com.kaixed.kchat.databinding.FriendRecycleFooterItemBinding
import com.kaixed.kchat.databinding.FriendRecycleItemBinding import com.kaixed.kchat.databinding.FriendRecycleItemBinding
import com.kaixed.kchat.ui.activity.ContactRequestListActivity
import com.kaixed.kchat.ui.activity.ContactsDetailActivity import com.kaixed.kchat.ui.activity.ContactsDetailActivity
import com.kaixed.kchat.utils.Pinyin4jUtil
/** /**
* @Author: kaixed * @Author: kaixed
* @Date: 2024/10/14 14:47 * @Date: 2024/10/14 14:47
*/ */
class FriendListAdapter(private var items: MutableList<Contact>, private val context: Context) : class FriendListAdapter(var items: MutableList<Contact>, private val context: Context) :
RecyclerView.Adapter<ViewHolder>() { RecyclerView.Adapter<ViewHolder>() {
class ItemViewHolder(val binding: FriendRecycleItemBinding) : ViewHolder(binding.root) class ItemViewHolder(val binding: FriendRecycleItemBinding) : ViewHolder(binding.root)
class FooterViewHolder(val binding: FriendRecycleFooterItemBinding) : ViewHolder(binding.root) class FooterViewHolder(val binding: FriendRecycleFooterItemBinding) : ViewHolder(binding.root)
fun updateData(newData: List<Contact>) {
items.clear()
items.addAll(newData)
notifyDataSetChanged()
}
companion object { companion object {
const val TYPE_ITEM: Int = 0 const val TYPE_ITEM: Int = 0
const val TYPE_FOOTER: Int = 1 const val TYPE_FOOTER: Int = 1
@ -68,25 +74,62 @@ class FriendListAdapter(private var items: MutableList<Contact>, private val con
} }
is ItemViewHolder -> { is ItemViewHolder -> {
if (position == 0) {
holder.binding.root.setOnClickListener {
context.startActivity(
Intent(
context,
ContactRequestListActivity::class.java
)
)
}
}
val item = items[position] val item = items[position]
// Log.d(TAG, "onBindViewHolder: $item")
holder.binding.tvItemName.text = item.remark ?: item.nickname holder.binding.tvItemName.text = item.remark ?: item.nickname
if (item.showHeader == true) { if (item.showHeader == true) {
holder.binding.tvLetter.text = if (item.remarkquanpin != null) {
Pinyin4jUtil.toPinyin(item.nickname)[0].toString().uppercase() if (item.remarkquanpin?.substring(0, 1)?.uppercase()
?.matches(Regex("[a-zA-Z]+")) == false
) {
holder.binding.tvLetter.text =
"#"
} else {
holder.binding.tvLetter.text =
item.remarkquanpin?.substring(0, 1)?.uppercase()
}
} else {
holder.binding.tvLetter.text = item.quanpin?.substring(0, 1)?.uppercase()
}
holder.binding.tvLetter.visibility = ViewGroup.VISIBLE holder.binding.tvLetter.visibility = ViewGroup.VISIBLE
} else { } else {
holder.binding.tvLetter.visibility = ViewGroup.GONE holder.binding.tvLetter.visibility = ViewGroup.GONE
} }
if (position <= defaultItems.size - 1) { if (position <= defaultItems.size - 1) {
holder.binding.ifvAvatar.setImageResource(defaultItems[position]) if (position == 0) {
holder.binding.ifvAvatar.setBackgroundColor(context.getColor(R.color.gray)) holder.binding.rlUnreadCount.visibility =
if (item.avatarUrl != null) View.VISIBLE else View.GONE
if (item.avatarUrl != null) {
val options = RequestOptions()
.placeholder(R.drawable.ic_default_avatar)
.error(R.drawable.ic_default_avatar)
Glide.with(context).load(item.avatarUrl).apply(options)
.into(holder.binding.ifvAvatar)
} else {
holder.binding.ifvAvatar.setImageResource(defaultItems[position])
holder.binding.ifvAvatar.setBackgroundColor(context.getColor(R.color.gray))
}
} else {
holder.binding.ifvAvatar.setImageResource(defaultItems[position])
holder.binding.ifvAvatar.setBackgroundColor(context.getColor(R.color.gray))
}
if (position == defaultItems.size - 1) { if (position == defaultItems.size - 1) {
holder.binding.viewBottom.visibility = holder.binding.viewBottom.visibility =
if (items.size == 5) View.VISIBLE else View.INVISIBLE if (items.size == 5) View.VISIBLE else View.INVISIBLE
} }
} else { } else {
val options = RequestOptions() val options = RequestOptions()
.placeholder(R.drawable.ic_default_avatar) .placeholder(R.drawable.ic_default_avatar)

View File

@ -1,30 +1,35 @@
package com.kaixed.kchat.ui.fragment package com.kaixed.kchat.ui.fragment
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.kaixed.kchat.data.objectbox.ObjectBox.get
import com.kaixed.kchat.data.objectbox.entity.Contact import com.kaixed.kchat.data.objectbox.entity.Contact
import com.kaixed.kchat.databinding.FragmentContactBinding import com.kaixed.kchat.databinding.FragmentContactBinding
import com.kaixed.kchat.ui.adapter.FriendListAdapter import com.kaixed.kchat.ui.adapter.FriendListAdapter
import com.kaixed.kchat.ui.base.BaseFragment import com.kaixed.kchat.ui.base.BaseFragment
import io.objectbox.Box import com.kaixed.kchat.utils.ConstantsUtil.getUsername
import com.kaixed.kchat.viewmodel.ContactViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class ContactFragment : BaseFragment<FragmentContactBinding>() { class ContactFragment : BaseFragment<FragmentContactBinding>() {
// private val friendListViewModel: FriendListViewModel by viewModels() private val contactViewModel: ContactViewModel by viewModels()
private val contactBox: Box<Contact> by lazy { get().boxFor(Contact::class.java) } private var loading = false
private lateinit var context: Context
private val friendAdapter: FriendListAdapter by lazy { private val friendAdapter: FriendListAdapter by lazy {
FriendListAdapter(friendList, requireContext()) FriendListAdapter(mutableListOf(), requireContext())
} }
private var friendList: MutableList<Contact> = mutableListOf()
companion object { companion object {
private const val TAG = "ContactFragment" private const val TAG = "ContactFragment"
} }
@ -38,32 +43,67 @@ class ContactFragment : BaseFragment<FragmentContactBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
getDefaultItem() context = requireContext()
loadData()
loadFriendRequest()
}
override fun onResume() {
super.onResume()
loadFriendRequest()
}
private fun loadFriendRequest() {
contactViewModel.contactRequestListResponse.observe(viewLifecycleOwner) { response ->
if (response?.data?.isNotEmpty() == true) {
val contactRequest = response.data[response.data.size - 1]
friendAdapter.items[0].apply {
remark =
"${contactRequest.nickname}(${contactRequest.message})"
avatarUrl = contactRequest.avatarUrl
}
friendAdapter.notifyItemChanged(0)
}
}
contactViewModel.getContactRequestList(getUsername())
}
private fun loadData() {
loading = true
binding.tvLoading.visibility = View.VISIBLE
binding.recycleFriendList.visibility = View.INVISIBLE
lifecycleScope.launch(Dispatchers.IO) {
val contacts = contactViewModel.loadFriendListInDb()
val allItems = mutableListOf<Contact>().apply {
addAll(getDefaultItems())
addAll(contacts)
}
withContext(Dispatchers.Main) {
friendAdapter.updateData(allItems)
binding.tvLoading.visibility = View.GONE
binding.recycleFriendList.visibility = View.VISIBLE
loading = false
}
}
setupRecyclerView()
}
private fun setupRecyclerView() {
binding.recycleFriendList.layoutManager = binding.recycleFriendList.layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
binding.recycleFriendList.adapter = friendAdapter binding.recycleFriendList.adapter = friendAdapter
getFriendList()
} }
private fun getFriendList() { private fun getDefaultItems(): List<Contact> {
friendList.addAll(contactBox.all) return listOf(
Contact(username = "", nickname = "新的朋友"),
Log.d(TAG, "getFriendList: ${contactBox.all}") Contact(username = "", nickname = "群聊"),
Contact(username = "", nickname = "标签"),
// friendListViewModel.friendListLiveData.observe(viewLifecycleOwner) { friends -> Contact(username = "", nickname = "公众号"),
// friends?.let { Contact(username = "", nickname = "服务号")
// friendList.addAll(friends) )
// friendAdapter.notifyDataSetChanged()
// }
// }
// friendListViewModel.loadFriendList(getUsername())
}
private fun getDefaultItem() {
friendList.add(Contact(username = "", nickname = "新的朋友"))
friendList.add(Contact(username = "", nickname = "群聊"))
friendList.add(Contact(username = "", nickname = "标签"))
friendList.add(Contact(username = "", nickname = "公众号"))
friendList.add(Contact(username = "", nickname = "服务号"))
} }
} }

View File

@ -10,11 +10,14 @@ import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.IBinder import android.os.IBinder
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.kaixed.kchat.R import com.kaixed.kchat.R
@ -28,7 +31,6 @@ import com.kaixed.kchat.service.WebSocketService
import com.kaixed.kchat.service.WebSocketService.LocalBinder import com.kaixed.kchat.service.WebSocketService.LocalBinder
import com.kaixed.kchat.ui.activity.AddFriendsActivity import com.kaixed.kchat.ui.activity.AddFriendsActivity
import com.kaixed.kchat.ui.activity.ContactUpdatesActivity import com.kaixed.kchat.ui.activity.ContactUpdatesActivity
import com.kaixed.kchat.ui.activity.MessageActivity
import com.kaixed.kchat.ui.activity.SearchActivity import com.kaixed.kchat.ui.activity.SearchActivity
import com.kaixed.kchat.ui.adapter.ConversationAdapter import com.kaixed.kchat.ui.adapter.ConversationAdapter
import com.kaixed.kchat.ui.adapter.MyGridAdapter import com.kaixed.kchat.ui.adapter.MyGridAdapter
@ -42,6 +44,8 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(), OnChatListItemClickLis
private var chatLists: MutableList<Conversation> = mutableListOf() private var chatLists: MutableList<Conversation> = mutableListOf()
private var oldLists: MutableList<Conversation> = mutableListOf()
private val conversationAdapter: ConversationAdapter by lazy { private val conversationAdapter: ConversationAdapter by lazy {
ConversationAdapter(chatLists, context) ConversationAdapter(chatLists, context)
} }
@ -94,18 +98,11 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(), OnChatListItemClickLis
private fun setOnClick() { private fun setOnClick() {
binding.ivMessage.setOnClickListener { binding.ivMessage.setOnClickListener {
val intent = Intent(
context,
MessageActivity::class.java
)
startActivity(intent)
} }
binding.ivSearch.setOnClickListener { binding.ivSearch.setOnClickListener {
val intent = val intent = Intent(context, SearchActivity::class.java)
Intent(context, SearchActivity::class.java)
startActivity(intent) startActivity(intent)
} }
} }
private fun flipImage(v: View) { private fun flipImage(v: View) {
@ -125,10 +122,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(), OnChatListItemClickLis
} }
private fun startService() { private fun startService() {
val intent = Intent( val intent = Intent(context, WebSocketService::class.java)
context,
WebSocketService::class.java
)
context.startService(intent) context.startService(intent)
} }
@ -136,19 +130,16 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(), OnChatListItemClickLis
if (webSocketService == null) { if (webSocketService == null) {
return return
} }
webSocketService!!.liveData.observe( webSocketService!!.conversations.observe(viewLifecycleOwner) {
this it?.let {
) { messages: Messages -> chatLists = it.toMutableList()
if ("0" == messages.type) { conversationAdapter.updateData(chatLists)
if (messages.msgSvrId != handleMsgSvrId) { notifyData()
processMessage(messages)
}
} }
} }
} }
private fun processMessage(messages: Messages) { private fun processMessage(messages: Messages) {
handleMsgSvrId = messages.msgSvrId handleMsgSvrId = messages.msgSvrId
val talkerId = messages.takerId val talkerId = messages.takerId
@ -271,9 +262,9 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(), OnChatListItemClickLis
} }
private fun setupRecycleView() { private fun setupRecycleView() {
conversationBox.all?.let { // conversationBox.all?.let {
chatLists = conversationBox.all // chatLists = conversationBox.query(Conversation_.show.equal(true)).build().find()
} // }
binding.recycleChatList.layoutManager = binding.recycleChatList.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
binding.recycleChatList.adapter = conversationAdapter binding.recycleChatList.adapter = conversationAdapter
@ -321,22 +312,27 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(), OnChatListItemClickLis
} }
private fun initMenuData() { private fun initMenuData() {
items.add(HomeItem("更换背景", true, R.drawable.ic_switch))
items.add(HomeItem("创建群聊", false, R.drawable.ic_troop)) val list = listOf(
items.add(HomeItem("新朋友", true, R.drawable.ic_friend)) HomeItem("更换背景", true, R.drawable.ic_switch),
items.add(HomeItem("夜间模式", true, R.drawable.ic_night)) HomeItem("创建群聊", false, R.drawable.ic_troop),
items.add(HomeItem("好友动态", false, R.drawable.ic_qzone)) HomeItem("新朋友", true, R.drawable.ic_friend),
items.add(HomeItem("扫一扫", false, R.drawable.ic_discovery_scan)) HomeItem("夜间模式", true, R.drawable.ic_night),
items.add(HomeItem("通讯录", true, R.drawable.ic_clock_in)) HomeItem("好友动态", false, R.drawable.ic_qzone),
items.add(HomeItem("关闭应用", false, R.drawable.ic_exit)) HomeItem("扫一扫", false, R.drawable.ic_discovery_scan),
HomeItem("通讯录", true, R.drawable.ic_clock_in),
HomeItem("关闭应用", false, R.drawable.ic_exit)
)
items.addAll(list)
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
updateChatLists() // updateChatLists()
} }
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
val intent = Intent( val intent = Intent(

View File

@ -37,6 +37,17 @@ class CustomItem @JvmOverloads constructor(
val itemDesc = typedArray.getString(R.styleable.CustomItem_itemDesc) ?: "" val itemDesc = typedArray.getString(R.styleable.CustomItem_itemDesc) ?: ""
val itemRedTip = typedArray.getBoolean(R.styleable.CustomItem_itemRedTip, false) val itemRedTip = typedArray.getBoolean(R.styleable.CustomItem_itemRedTip, false)
val rightType = typedArray.getString(R.styleable.CustomItem_rightType) ?: ""
val background = typedArray.getDrawable(R.styleable.CustomItem_android_background)
background?.let { bg ->
this.background = bg
} ?: run {
this.setBackgroundResource(R.color.white)
}
setupRightContent(rightType)
// 设置视图 // 设置视图
setupRedTip(itemRedTip) setupRedTip(itemRedTip)
setupItemDesc(itemDesc) setupItemDesc(itemDesc)
@ -49,6 +60,20 @@ class CustomItem @JvmOverloads constructor(
} }
} }
private fun setupRightContent(type: String) {
when (type) {
"switch" -> {
binding.ssvSwitch.visibility = View.VISIBLE
binding.ivArrowRight.visibility = View.GONE
}
else -> {
binding.ssvSwitch.visibility = View.GONE
binding.ivArrowRight.visibility = View.VISIBLE
}
}
}
// 设置红点显示 // 设置红点显示
private fun setupRedTip(itemRedTip: Boolean) { private fun setupRedTip(itemRedTip: Boolean) {
binding.viewRedTip.visibility = if (itemRedTip) View.VISIBLE else View.GONE binding.viewRedTip.visibility = if (itemRedTip) View.VISIBLE else View.GONE
@ -132,4 +157,8 @@ class CustomItem @JvmOverloads constructor(
Glide.with(context).load(url) Glide.with(context).load(url)
.into(binding.ivItemIcon) .into(binding.ivItemIcon)
} }
fun setSwitchChecked(checked: Boolean) {
binding.ssvSwitch.setOn(checked)
}
} }

View File

@ -46,7 +46,12 @@ class CustomTitleBar @JvmOverloads constructor(
binding.tvTitleName.visibility = View.GONE binding.tvTitleName.visibility = View.GONE
} }
binding.tvSave.visibility = if (btnName.isNotEmpty()) View.VISIBLE else View.INVISIBLE if (btnName.isNotEmpty()) {
binding.tvSave.visibility = View.VISIBLE
binding.tvSave.text = btnName
} else {
binding.tvSave.visibility = View.INVISIBLE
}
typedArray.recycle() typedArray.recycle()
} }

View File

@ -4,6 +4,7 @@ import android.animation.ValueAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint import android.graphics.Paint
import android.graphics.Path import android.graphics.Path
import android.graphics.RectF import android.graphics.RectF
@ -510,7 +511,7 @@ class ShSwitchView @JvmOverloads constructor(
) )
//innerContent //innerContent
paint.color = foregroundColor paint.color = Color.WHITE
canvas.drawRoundRect( canvas.drawRoundRect(
innerContentBound, innerContentBound,
innerContentBound.height() / 2, innerContentBound.height() / 2,

View File

@ -4,8 +4,11 @@ import android.app.Dialog
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.Window import android.view.Window
import android.view.WindowManager
import com.kaixed.kchat.databinding.DialogDeleteContactBinding
import com.kaixed.kchat.databinding.DialogLoadingBinding import com.kaixed.kchat.databinding.DialogLoadingBinding
import com.kaixed.kchat.utils.DensityUtil.dpToPx import com.kaixed.kchat.utils.DensityUtil.dpToPx
@ -26,7 +29,6 @@ object WidgetUtil {
params.width = dpToPx(150) params.width = dpToPx(150)
binding.root.layoutParams = params binding.root.layoutParams = params
binding.tvLoading.text = str binding.tvLoading.text = str
dialog.show()
return dialog return dialog
} }
} }

View File

@ -0,0 +1,36 @@
package com.kaixed.kchat.utils.handle
import com.kaixed.kchat.data.objectbox.ObjectBox.get
import com.kaixed.kchat.data.objectbox.entity.Contact
import com.kaixed.kchat.utils.Pinyin4jUtil
import io.objectbox.Box
/**
* @Author: kaixed
* @Date: 2024/11/19 16:11
*/
object ContactUtil {
private val contactBox: Box<Contact> by lazy { get().boxFor(Contact::class.java) }
fun handleContact(contact: Contact) {
val contactLists = getDbContactLists()
contactLists.add(contact)
contact.apply {
quanpin = Pinyin4jUtil.toPinyin(nickname)
remark?.let {
remarkquanpin = Pinyin4jUtil.toPinyin(remark!!)
}
}
contactLists.forEachIndexed { index, item ->
item.showHeader =
(index == 0 || item.quanpin?.get(index) != contactLists[index - 1].quanpin?.get(
index - 1
))
}
contactBox.put(contactLists)
}
private fun getDbContactLists(): MutableList<Contact> {
return contactBox.all.toMutableList()
}
}

View File

@ -4,13 +4,16 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.kaixed.kchat.data.objectbox.ObjectBox.get
import com.kaixed.kchat.data.objectbox.entity.Contact import com.kaixed.kchat.data.objectbox.entity.Contact
import com.kaixed.kchat.model.friend.AcceptContactRequest import com.kaixed.kchat.model.friend.AcceptContactRequest
import com.kaixed.kchat.model.friend.ContactRequestResponse import com.kaixed.kchat.model.friend.ContactRequestResponse
import com.kaixed.kchat.model.response.ApplyFriend import com.kaixed.kchat.model.response.ApplyFriend
import com.kaixed.kchat.model.response.friend.DeleteContact
import com.kaixed.kchat.model.response.friend.SearchFriends import com.kaixed.kchat.model.response.friend.SearchFriends
import com.kaixed.kchat.repository.ContactRepository import com.kaixed.kchat.repository.ContactRepository
import com.kaixed.kchat.utils.Pinyin4jUtil import com.kaixed.kchat.utils.Pinyin4jUtil
import io.objectbox.Box
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class ContactViewModel : ViewModel() { class ContactViewModel : ViewModel() {
@ -38,6 +41,21 @@ class ContactViewModel : ViewModel() {
private val _contactListResponse = MutableLiveData<List<Contact>?>() private val _contactListResponse = MutableLiveData<List<Contact>?>()
val contactListResponse: LiveData<List<Contact>?> = _contactListResponse val contactListResponse: LiveData<List<Contact>?> = _contactListResponse
// 删除联系人
private val _deleteContact = MutableLiveData<DeleteContact?>()
val deleteContact: LiveData<DeleteContact?> = _deleteContact
fun deleteContact(username: String, contactId: String) {
viewModelScope.launch {
val response = contactRepository.deleteContact(username, contactId)
if (response.isSuccessful) {
_deleteContact.value = response.body()
} else {
_deleteContact.value = null
}
}
}
// 获取联系人请求列表 // 获取联系人请求列表
fun getContactRequestList(username: String) { fun getContactRequestList(username: String) {
viewModelScope.launch { viewModelScope.launch {
@ -51,9 +69,9 @@ class ContactViewModel : ViewModel() {
} }
// 接受联系人请求 // 接受联系人请求
fun acceptContactRequest(username: String, contactId: String) { fun acceptContactRequest(username: String, contactId: String, remark: String) {
viewModelScope.launch { viewModelScope.launch {
val response = contactRepository.acceptContactRequest(username, contactId) val response = contactRepository.acceptContactRequest(username, contactId, remark)
if (response.isSuccessful) { if (response.isSuccessful) {
_acceptContactRequestResponse.value = response.body() _acceptContactRequestResponse.value = response.body()
} else { } else {
@ -86,6 +104,40 @@ class ContactViewModel : ViewModel() {
} }
} }
fun loadFriendListInDb(): List<Contact> {
val contactBox: Box<Contact> = get().boxFor(Contact::class.java)
val sortedContacts = contactBox.query()
.sort { contact1, contact2 ->
val str1 = contact1.remarkquanpin ?: contact1.quanpin ?: contact1.nickname
val str2 = contact2.remarkquanpin ?: contact2.quanpin ?: contact2.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) // 如果类型相同,按字母顺序排序
}
}
.build()
.find()
return sortedContacts.toMutableList()
}
fun loadFriendList(username: String) { fun loadFriendList(username: String) {
viewModelScope.launch { viewModelScope.launch {
val response = contactRepository.getContactList(username) val response = contactRepository.getContactList(username)
@ -94,8 +146,27 @@ class ContactViewModel : ViewModel() {
val uiFriendList = friendListResponse?.map { networkItem -> val uiFriendList = friendListResponse?.map { networkItem ->
mapToFriendItem(networkItem) // 数据转换 mapToFriendItem(networkItem) // 数据转换
}?.sortedWith { f1, f2 -> }?.sortedWith { f1, f2 ->
// 拼音排序 val str1 = f1.remarkquanpin ?: f1.quanpin ?: f1.nickname
Pinyin4jUtil.compare(f1.nickname, f2.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) // 如果类型相同,按字母顺序排序
}
} }
// 设置是否显示分组头 // 设置是否显示分组头
@ -115,6 +186,9 @@ class ContactViewModel : ViewModel() {
private fun mapToFriendItem(response: Contact): Contact { private fun mapToFriendItem(response: Contact): Contact {
val pinyin = Pinyin4jUtil.toPinyin(response.nickname) val pinyin = Pinyin4jUtil.toPinyin(response.nickname)
response.remark?.let {
response.remarkquanpin = Pinyin4jUtil.toPinyin(it)
}
response.quanpin = pinyin response.quanpin = pinyin
return response return response
} }

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.kaixed.kchat.model.request.RegisterRequest import com.kaixed.kchat.model.request.RegisterRequest
import com.kaixed.kchat.model.request.UserRequest import com.kaixed.kchat.model.request.UserRequest
import com.kaixed.kchat.model.response.friend.SearchFriends
import com.kaixed.kchat.model.response.login.Login import com.kaixed.kchat.model.response.login.Login
import com.kaixed.kchat.model.response.register.Register import com.kaixed.kchat.model.response.register.Register
import com.kaixed.kchat.model.response.search.UserList import com.kaixed.kchat.model.response.search.UserList
@ -26,6 +27,10 @@ class UserViewModel : ViewModel() {
private val _userListResponse = MutableLiveData<UserList?>() private val _userListResponse = MutableLiveData<UserList?>()
val userListResponse: LiveData<UserList?> = _userListResponse val userListResponse: LiveData<UserList?> = _userListResponse
// 获取用户信息返回
private val _userInfoResponse = MutableLiveData<SearchFriends?>()
val userInfoResponse: LiveData<SearchFriends?> = _userInfoResponse
// 修改昵称返回 // 修改昵称返回
private val _changeNicknameResponse = MutableLiveData<Boolean>() private val _changeNicknameResponse = MutableLiveData<Boolean>()
val changeNicknameResponse: LiveData<Boolean> = _changeNicknameResponse val changeNicknameResponse: LiveData<Boolean> = _changeNicknameResponse
@ -89,6 +94,23 @@ class UserViewModel : ViewModel() {
} }
} }
// 获取用户信息
fun getUserInfo(username: String) {
viewModelScope.launch {
try {
val response = userRepo.getUserInfo(username)
if (response.isSuccessful) {
_userInfoResponse.value = response.body()
} else {
_userInfoResponse.value = null
}
} catch (e: Exception) {
Log.e("UserViewModel", "Get User failed: ${e.message}")
_userInfoResponse.value = null
}
}
}
// 修改昵称 // 修改昵称
fun changeNickname(userRequest: UserRequest) { fun changeNickname(userRequest: UserRequest) {
viewModelScope.launch { viewModelScope.launch {

View File

@ -57,24 +57,67 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="25dp" android:layout_marginStart="25dp"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:text="我是" android:text="我是x"
android:textSize="13sp" /> android:textSize="13sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
android:layout_marginTop="20dp"
android:text="设置标签与备注"
android:textSize="13sp" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="10dp"
app:cardCornerRadius="8dp"
app:cardElevation="0dp"
app:cardMaxElevation="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F7F7F7"
android:orientation="vertical">
<com.kaixed.kchat.ui.widget.CustomItem
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#F7F7F7"
app:itemName="标签" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/divider_height"
android:background="#ededed" />
<com.kaixed.kchat.ui.widget.CustomItem
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#F7F7F7"
app:itemName="描述" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
<TextView <TextView
android:id="@+id/tv_finish" android:id="@+id/tv_finish"
android:layout_width="150dp" android:layout_width="160dp"
android:layout_height="48dp" android:layout_height="43dp"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_marginBottom="40dp" android:layout_marginBottom="40dp"
android:background="@drawable/add_contact_request_send_btn_bac" android:background="@drawable/add_contact_request_send_btn_bac"
android:gravity="center" android:gravity="center"
android:text="完成" android:text="完成"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="19sp" /> android:textSize="17sp" />
</LinearLayout> </LinearLayout>

View File

@ -77,13 +77,28 @@
app:barrierDirection="bottom" app:barrierDirection="bottom"
app:constraint_referenced_ids="ifv_avatar,tv_set_remark_tag" /> app:constraint_referenced_ids="ifv_avatar,tv_set_remark_tag" />
<LinearLayout
android:id="@+id/cl_reply"
android:layout_width="match_parent"
android:visibility="invisible"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="15dp"
app:layout_constraintTop_toBottomOf="@id/barrier">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="沉默是金:我是万" />
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginTop="15dp"
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/barrier"> app:layout_constraintTop_toBottomOf="@id/cl_reply">
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -7,7 +7,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/gray" android:background="@color/gray"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
tools:context=".ui.activity.MessageActivity"> tools:context=".ui.activity.ContactRequestListActivity">
<com.kaixed.kchat.ui.widget.CustomTitleBar <com.kaixed.kchat.ui.widget.CustomTitleBar
android:id="@+id/ctl" android:id="@+id/ctl"

View File

@ -88,10 +88,11 @@
android:background="#E5E5E5" /> android:background="#E5E5E5" />
<com.kaixed.kchat.ui.widget.CustomItem <com.kaixed.kchat.ui.widget.CustomItem
android:id="@+id/ci_set_remark_and_label"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:isTopDividerVisible="true" app:isTopDividerVisible="true"
app:itemName="设置备注" /> app:itemName="设置备注和标签" />
<com.kaixed.kchat.ui.widget.CustomItem <com.kaixed.kchat.ui.widget.CustomItem
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,83 +1,83 @@
<?xml version="1.0" encoding="utf-8"?> <?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:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main" android:id="@+id/main"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#E5E5E5" android:background="@color/gray"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
android:orientation="vertical"
tools:context=".ui.activity.DataSettingActivity"> tools:context=".ui.activity.DataSettingActivity">
<ImageView <com.kaixed.kchat.ui.widget.CustomTitleBar
android:id="@+id/iv_back"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginStart="15dp"
android:layout_marginTop="15dp"
android:src="@drawable/ic_left_arrow"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="资料设置"
android:textColor="@color/black"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/iv_back"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/iv_back" />
<ImageView
android:id="@+id/iv_setting"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginEnd="15dp"
android:src="@drawable/ic_more"
app:layout_constraintBottom_toBottomOf="@id/iv_back"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/iv_back" />
<TextView
android:id="@+id/tv_setRemark"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="15dp" app:titleName="资料设置" />
android:background="@color/white"
android:paddingVertical="15dp"
android:paddingStart="15dp"
android:text="设置备注和标签"
android:textColor="@color/black"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_back" />
<ImageView <View
android:layout_width="16dp" android:layout_width="match_parent"
android:layout_height="16dp" android:layout_height="@dimen/divider_height" />
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="15dp"
android:src="@drawable/icon_arrow_right"
app:layout_constraintBottom_toBottomOf="@id/tv_setRemark"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_setRemark" />
<TextView <com.kaixed.kchat.ui.widget.CustomItem
android:id="@+id/tv_addToBlockList" android:id="@+id/ci_set_remark_and_label"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" app:isBottomDividerVisible="true"
app:itemDesc="万(小)"
app:itemName="设置备注和标签" />
<com.kaixed.kchat.ui.widget.CustomItem
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemName="朋友权限" />
<com.kaixed.kchat.ui.widget.CustomItem
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin"
app:isBottomDividerVisible="true"
app:itemName="把她推荐给朋友" />
<com.kaixed.kchat.ui.widget.CustomItem
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemName="添加到桌面" />
<com.kaixed.kchat.ui.widget.CustomItem
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin"
app:itemName="设为星标朋友"
app:rightType="switch" />
<com.kaixed.kchat.ui.widget.CustomItem
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin"
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="投诉" />
<TextView
android:id="@+id/tv_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin"
android:background="@color/white" android:background="@color/white"
android:gravity="center"
android:paddingVertical="15dp" android:paddingVertical="15dp"
android:paddingStart="15dp" android:paddingStart="15dp"
android:text="加入黑名单" android:text="删除"
android:textColor="@color/black" android:textColor="@color/red"
android:textSize="16sp" android:textSize="16sp" />
app:layout_constraintTop_toBottomOf="@id/tv_setRemark" />
</androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout>

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:fitsSystemWindows="true"
android:orientation="vertical"
tools:context=".ui.activity.FriendListActivity">
<com.kaixed.kchat.ui.widget.CustomTitleBar
android:id="@+id/ctb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:titleName="通讯录" />
<TextView
android:id="@+id/tv_nothing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="您当前还没有好友哦。"
android:textColor="@color/black"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ctb"
app:layout_constraintVertical_bias="0.3" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycle_friend_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:overScrollMode="never"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/ctb" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,310 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="#e5e5e5"
android:fitsSystemWindows="true"
tools:context=".ui.activity.ProfileActivity">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/ifv_avatar"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="20dp"
android:layout_marginTop="35dp"
android:src="@drawable/ic_avatar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundPercent="0.3" />
<TextView
android:id="@+id/tv_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="糕菜菜"
android:textColor="@color/black"
android:textSize="15sp"
app:layout_constraintStart_toEndOf="@id/ifv_avatar"
app:layout_constraintTop_toTopOf="@id/ifv_avatar" />
<TextView
android:id="@+id/tv_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="kid: kaixed"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@id/ifv_avatar"
app:layout_constraintStart_toStartOf="@id/tv_nickname"
app:layout_constraintTop_toBottomOf="@id/tv_nickname" />
<ImageView
android:id="@+id/iv_service"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="15dp"
android:layout_marginTop="15dp"
android:src="@drawable/mine_icon_service"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ifv_avatar" />
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="我的服务"
android:gravity="center_vertical"
android:paddingStart="35dp"
app:layout_constraintBottom_toBottomOf="@id/iv_service"
app:layout_constraintTop_toTopOf="@id/iv_service" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="55dp"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/ifv_avatar">
<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">
<!-- <ImageView-->
<!-- android:id="@+id/iv_service"-->
<!-- android:layout_width="24dp"-->
<!-- android:layout_height="24dp"-->
<!-- android:layout_centerVertical="true"-->
<!-- android:src="@drawable/mine_icon_service" />-->
<!-- <TextView-->
<!-- android:id="@+id/tv_service"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_centerVertical="true"-->
<!-- android:layout_marginStart="15dp"-->
<!-- android:layout_toEndOf="@id/iv_service"-->
<!-- 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">
<ImageView
android:id="@+id/iv_collection"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerVertical="true"
android:src="@drawable/mine_icon_collection" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_collection"
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="55dp"
android:background="#E5E5E5" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingStart="15dp"
tools:ignore="RtlSymmetry">
<ImageView
android:id="@+id/iv_friend_circle"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerVertical="true"
android:src="@drawable/mine_icon_friend_circle" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_friend_circle"
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="55dp"
android:background="#E5E5E5" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingStart="15dp"
tools:ignore="RtlSymmetry">
<ImageView
android:id="@+id/iv_wallet"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerVertical="true"
android:src="@drawable/mine_icon_wallet" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_wallet"
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="55dp"
android:background="#E5E5E5" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingStart="15dp"
tools:ignore="RtlSymmetry">
<ImageView
android:id="@+id/iv_emoji"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerVertical="true"
android:src="@drawable/mine_icon_emoji" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_emoji"
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">
<ImageView
android:id="@+id/iv_setting"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerVertical="true"
android:src="@drawable/mine_icon_setting" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_setting"
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>

View File

@ -0,0 +1,172 @@
<?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="@color/white"
android:fitsSystemWindows="true"
android:orientation="vertical"
tools:context=".ui.activity.SetRemarkAndLabelActivity">
<com.kaixed.kchat.ui.widget.CustomTitleBar
android:id="@+id/ctb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:btnName="完成" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="30dp"
android:text="设置备注和标签"
android:textColor="@color/black"
android:textSize="19sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="15dp"
android:text="备注"
android:textSize="13sp" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="5dp"
android:background="#F7F7F7"
app:cardCornerRadius="8dp"
app:cardElevation="0dp"
app:cardMaxElevation="0dp">
<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" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="15dp"
android:text="标签"
android:textSize="13sp" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="5dp"
android:background="#F7F7F7"
app:cardCornerRadius="8dp"
app:cardElevation="0dp"
app:cardMaxElevation="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F7F7F7"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.0"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:text="添加标签"
android:textColor="@color/normal" />
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="10dp"
android:src="@drawable/icon_arrow_right" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="15dp"
android:text="电话"
android:textSize="13sp" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="5dp"
android:background="#F7F7F7"
app:cardCornerRadius="8dp"
app:cardElevation="0dp"
app:cardMaxElevation="0dp">
<TextView
android:id="@+id/tv_telephone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F7F7F7"
android:gravity="center_vertical"
android:paddingHorizontal="16dp"
android:text="添加电话"
android:textColor="@color/normal" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="15dp"
android:text="描述"
android:textSize="13sp" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="5dp"
android:background="#F7F7F7"
app:cardCornerRadius="8dp"
app:cardElevation="0dp"
app:cardMaxElevation="0dp">
<EditText
android:id="@+id/et_desc"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F7F7F7"
android:hint="添加文字"
android:paddingHorizontal="16dp"
android:textSize="15sp" />
</androidx.cardview.widget.CardView>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="25dp"
app:cardCornerRadius="8dp"
app:cardElevation="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:text="删除联系人"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="15dp"
android:text="将联系人“xxx”删除将同时删除与该联系人的聊天记录"
android:textColor="@color/black"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title" />
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="@dimen/divider_height"
android:layout_marginTop="35dp"
android:background="@color/gray"
app:layout_constraintTop_toBottomOf="@id/tv_content" />
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="45dp"
android:gravity="center"
android:text="取消"
android:textColor="@color/black"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@id/view1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/view" />
<View
android:id="@+id/view1"
android:layout_width="@dimen/divider_height"
android:layout_height="0dp"
android:background="@color/gray"
app:layout_constraintBottom_toBottomOf="@id/tv_cancel"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_cancel" />
<TextView
android:id="@+id/tv_delete"
android:layout_width="0dp"
android:layout_height="45dp"
android:gravity="center"
android:text="删除"
android:textColor="@color/red"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/view1"
app:layout_constraintTop_toBottomOf="@id/view" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -8,6 +8,7 @@
tools:context=".ui.fragment.ContactFragment"> tools:context=".ui.fragment.ContactFragment">
<TextView <TextView
android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="50dp" android:layout_height="50dp"
android:layout_gravity="center" android:layout_gravity="center"
@ -21,7 +22,17 @@
android:id="@+id/recycle_friend_list" android:id="@+id/recycle_friend_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_below="@id/title"
android:background="@color/white" android:background="@color/white"
android:overScrollMode="never" /> android:overScrollMode="never" />
</LinearLayout> <TextView
android:id="@+id/tv_loading"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/title"
android:gravity="center"
android:text="正在加载中..."
android:textColor="@color/black" />
</RelativeLayout>

View File

@ -23,7 +23,6 @@
android:layout_height="50dp" android:layout_height="50dp"
android:layout_marginStart="20dp" android:layout_marginStart="20dp"
android:layout_marginTop="55dp" android:layout_marginTop="55dp"
android:src="@drawable/ic_avatar"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:roundPercent="0.3" /> app:roundPercent="0.3" />

View File

@ -60,5 +60,28 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/tv_item_name" /> app:layout_constraintStart_toStartOf="@id/tv_item_name" />
<RelativeLayout
android:id="@+id/rl_unread_count"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="15dp"
android:background="@drawable/icon_red_dot"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_unread_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="1"
android:textColor="@color/white"
android:textSize="11sp" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -4,7 +4,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/white" android:background="@color/white"
android:minHeight="80dp"> android:minHeight="65dp">
<View <View
android:id="@+id/top" android:id="@+id/top"
@ -26,8 +26,8 @@
<androidx.constraintlayout.utils.widget.ImageFilterView <androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/ifv_avatar" android:id="@+id/ifv_avatar"
android:layout_width="50dp" android:layout_width="45dp"
android:layout_height="50dp" android:layout_height="45dp"
android:layout_marginStart="15dp" android:layout_marginStart="15dp"
android:src="@drawable/ic_avatar" android:src="@drawable/ic_avatar"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -64,7 +64,7 @@
android:layout_marginEnd="15dp" android:layout_marginEnd="15dp"
android:background="@drawable/bac_message_agree_request_btn" android:background="@drawable/bac_message_agree_request_btn"
android:paddingHorizontal="14dp" android:paddingHorizontal="14dp"
android:paddingVertical="5dp" android:paddingVertical="6dp"
android:text="添加" android:text="添加"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="12sp" android:textSize="12sp"

View File

@ -16,8 +16,8 @@
<ImageView <ImageView
android:id="@+id/iv_item" android:id="@+id/iv_item"
android:layout_width="32dp" android:layout_width="26dp"
android:layout_height="32dp" android:layout_height="26dp"
app:layout_constraintBottom_toBottomOf="@id/iv_bg" app:layout_constraintBottom_toBottomOf="@id/iv_bg"
app:layout_constraintEnd_toEndOf="@id/iv_bg" app:layout_constraintEnd_toEndOf="@id/iv_bg"
app:layout_constraintStart_toStartOf="@id/iv_bg" app:layout_constraintStart_toStartOf="@id/iv_bg"
@ -30,6 +30,7 @@
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:text="哈哈" android:text="哈哈"
android:textColor="#6F6F6F" android:textColor="#6F6F6F"
android:textSize="11sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/iv_bg" app:layout_constraintEnd_toEndOf="@id/iv_bg"
app:layout_constraintStart_toStartOf="@id/iv_bg" app:layout_constraintStart_toStartOf="@id/iv_bg"

View File

@ -3,7 +3,6 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/white"
android:minHeight="50dp"> android:minHeight="50dp">
<View <View
@ -56,13 +55,13 @@
android:id="@+id/tv_item_desc" android:id="@+id/tv_item_desc"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp" android:layout_marginEnd="4dp"
android:text="aaaa" android:text="aaaa"
android:textColor="#797979" android:textColor="#797979"
android:textSize="15sp" android:textSize="15sp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/iv_arrow_right" app:layout_constraintEnd_toStartOf="@id/right_layout"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.utils.widget.ImageFilterView <androidx.constraintlayout.utils.widget.ImageFilterView
@ -73,21 +72,34 @@
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/iv_arrow_right" app:layout_constraintEnd_toStartOf="@id/right_layout"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:roundPercent="0.2" /> app:roundPercent="0.2" />
<ImageView <RelativeLayout
android:id="@+id/iv_arrow_right" android:id="@+id/right_layout"
android:layout_width="16dp" android:layout_width="wrap_content"
android:layout_height="16dp" android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="15dp" android:layout_marginEnd="15dp"
android:src="@drawable/icon_arrow_right"
app:layout_constraintBottom_toBottomOf="@id/tv_item_name" app:layout_constraintBottom_toBottomOf="@id/tv_item_name"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_item_name" /> app:layout_constraintTop_toTopOf="@id/tv_item_name">
<ImageView
android:id="@+id/iv_arrow_right"
android:layout_width="16dp"
android:layout_height="16dp"
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"
android:layout_centerInParent="true"
android:visibility="gone"
app:tintColor="@color/green" />
</RelativeLayout>
<View <View
android:id="@+id/decoration_bottom" android:id="@+id/decoration_bottom"

View File

@ -15,11 +15,13 @@
<attr name="itemDesc" format="string" /> <attr name="itemDesc" format="string" />
<attr name="isTopDividerVisible" format="boolean" /> <attr name="isTopDividerVisible" format="boolean" />
<attr name="isBottomDividerVisible" format="boolean" /> <attr name="isBottomDividerVisible" format="boolean" />
<attr name="rightType" format="string" />
<attr name="iconSize" format="dimension" /> <attr name="iconSize" format="dimension" />
<attr name="iconRound" format="float" /> <attr name="iconRound" format="float" />
<attr name="itemIcon" format="reference" /> <attr name="itemIcon" format="reference" />
<attr name="itemLeftIcon" format="reference" /> <attr name="itemLeftIcon" format="reference" />
<attr name="itemRedTip" format="boolean" /> <attr name="itemRedTip" format="boolean" />
<attr name="android:background" />
</declare-styleable> </declare-styleable>

View File

@ -6,6 +6,7 @@
<color name="normal">#576B95</color> <color name="normal">#576B95</color>
<color name="green">#07C160</color> <color name="green">#07C160</color>
<color name="gray">#E5E5E5</color> <color name="gray">#E5E5E5</color>
<color name="red">#FF3E3E</color>
<color name="light_blue_400">#FF29B6F6</color> <color name="light_blue_400">#FF29B6F6</color>
<color name="light_blue_600">#FF039BE5</color> <color name="light_blue_600">#FF039BE5</color>
<color name="gray_400">#FFBDBDBD</color> <color name="gray_400">#FFBDBDBD</color>