refactor: 更改view层为ui层
1.更改view层为ui层 2.将全部java类更改为kotlin编写
This commit is contained in:
parent
7c2a9f0619
commit
b81419b40f
@ -21,67 +21,13 @@
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".view.activity.ContactUpdatesActivity"
|
||||
android:name=".ui.activity.SearchActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.ApproveDetailActivity"
|
||||
android:name=".ui.activity.MainActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.ApproveContactRequestActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.MessageActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.ApplyAddFriendActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.FriendListActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.ServiceDetailActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.SettingActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.ProfileDetailActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.SetRemarkActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.DataSettingActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.ApplyFriendsDetailActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.SearchFriendsActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.MainActivity2"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.AddFriendsActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.TestActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.ProfileActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.SearchActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.ChatDetailActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.ContactsDetailActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.LoginActivity"
|
||||
android:name=".ui.activity.LoginActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@ -90,12 +36,62 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".view.activity.ChatActivity"
|
||||
android:exported="false"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
android:name=".ui.activity.ContactsDetailActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".view.activity.MainActivity"
|
||||
android:exported="true" />
|
||||
android:name=".ui.activity.ChatDetailActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.ChatActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.ContactUpdatesActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.ApproveDetailActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.ApproveContactRequestActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.MessageActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.ApplyAddFriendActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.FriendListActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.ServiceDetailActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.SettingActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.ProfileDetailActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.SetRemarkActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.DataSettingActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.ApplyFriendsDetailActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.SearchFriendsActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.AddFriendsActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.TestActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.ProfileActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<service android:name=".service.WebSocketService" />
|
||||
</application>
|
||||
|
@ -1,9 +1,8 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.kaixed.kchat.databinding.ActivityAddFriendsBinding
|
@ -1,17 +1,12 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.databinding.ActivityApplyAddFriendBinding
|
||||
import com.kaixed.kchat.viewmodel.ApplyFriendViewModel
|
||||
import com.kaixed.kchat.viewmodel.SearchFriendsViewModel
|
||||
|
||||
class ApplyAddFriendActivity : BaseActivity() {
|
||||
private lateinit var binding: ActivityApplyAddFriendBinding
|
||||
@ -31,7 +26,7 @@ class ApplyAddFriendActivity : BaseActivity() {
|
||||
|
||||
if (contactId != null) {
|
||||
applyAddFriendViewModel.addContact(contactId, binding.etMessage.text.toString())
|
||||
?.observe(this) { value ->
|
||||
.observe(this) { value ->
|
||||
if (value != null) {
|
||||
runOnUiThread {
|
||||
if (value.code == "200") {
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.TaskStackBuilder
|
@ -1,13 +1,10 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.bumptech.glide.Glide
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.databinding.ActivityApproveDetailBinding
|
||||
import com.kaixed.kchat.model.friend.FriendRequestItem
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
525
app/src/main/java/com/kaixed/kchat/ui/activity/ChatActivity.kt
Normal file
525
app/src/main/java/com/kaixed/kchat/ui/activity/ChatActivity.kt
Normal file
@ -0,0 +1,525 @@
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.LinearLayout
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.kaixed.kchat.database.ObjectBox.get
|
||||
import com.kaixed.kchat.database.UserManager.getUsername
|
||||
import com.kaixed.kchat.database.entity.Messages
|
||||
import com.kaixed.kchat.database.entity.Messages_
|
||||
import com.kaixed.kchat.databinding.ActivityChatBinding
|
||||
import com.kaixed.kchat.service.WebSocketService
|
||||
import com.kaixed.kchat.service.WebSocketService.LocalBinder
|
||||
import com.kaixed.kchat.ui.adapter.ChatAdapter
|
||||
import com.kaixed.kchat.ui.adapter.EmojiAdapter
|
||||
import com.kaixed.kchat.ui.adapter.FunctionPanelAdapter
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.Companion.getKeyboardHeight
|
||||
import com.kaixed.kchat.utils.ImageSpanUtil.insertEmoji
|
||||
import com.kaixed.kchat.ui.i.OnItemClickListener
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.query.QueryBuilder
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.util.LinkedList
|
||||
|
||||
class ChatActivity : AppCompatActivity(), OnItemClickListener {
|
||||
private lateinit var binding: ActivityChatBinding
|
||||
|
||||
private var chatAdapter: ChatAdapter? = null
|
||||
|
||||
private var webSocketService: WebSocketService? = null
|
||||
|
||||
private val messagesList = LinkedList<Messages>()
|
||||
|
||||
private lateinit var messagesBox: Box<Messages>
|
||||
|
||||
private var friendId: String? = null
|
||||
|
||||
private val limit: Long = 50L
|
||||
|
||||
private var isLoading = false
|
||||
|
||||
private var isHasHistory = false
|
||||
|
||||
private var isBound = false
|
||||
|
||||
private var username: String? = null
|
||||
|
||||
private var tempIndex: Long = 0
|
||||
|
||||
private val mContext: Context = this
|
||||
|
||||
private var strings: MutableList<String>? = null
|
||||
|
||||
private var imm: InputMethodManager? = null
|
||||
|
||||
private var softKeyboardHeight = 0
|
||||
private var isKeyboardShown = false
|
||||
|
||||
private var contactId: String? = null
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = ActivityChatBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
initData()
|
||||
initView()
|
||||
firstLoadData()
|
||||
|
||||
setListener()
|
||||
|
||||
handleIntent(intent)
|
||||
bindWebSocketService()
|
||||
|
||||
imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
setPanelChange()
|
||||
|
||||
setBackPressListener()
|
||||
|
||||
getKeyBoardVisibility()
|
||||
}
|
||||
|
||||
private val connection: ServiceConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
||||
val binder = service as LocalBinder
|
||||
webSocketService = binder.getService()
|
||||
isBound = true
|
||||
observeLiveData()
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(arg0: ComponentName) {
|
||||
isBound = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun getKeyBoardVisibility() {
|
||||
val rootView = binding.root
|
||||
|
||||
rootView.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
val r = Rect()
|
||||
rootView.getWindowVisibleDisplayFrame(r)
|
||||
val screenHeight = rootView.rootView.height
|
||||
val keypadHeight = screenHeight - r.bottom
|
||||
isKeyboardShown = keypadHeight > screenHeight * 0.15
|
||||
}
|
||||
}
|
||||
|
||||
private fun setBackPressListener() {
|
||||
onBackPressedDispatcher.addCallback(
|
||||
this,
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
if (binding.clBottomPanel.isShown) {
|
||||
hidePanel(false)
|
||||
} else {
|
||||
isEnabled = false
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun setPanelChange() {
|
||||
binding.etInput.setOnTouchListener { _, event ->
|
||||
if (event.action == MotionEvent.ACTION_UP && binding.clBottomPanel.isShown) {
|
||||
lockContentViewHeight()
|
||||
hidePanel(true)
|
||||
unlockContentViewHeight()
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
binding.ivFunctionPanel.setOnClickListener { v ->
|
||||
if (binding.clBottomPanel.isShown) {
|
||||
if (binding.rvEmoji.isShown) {
|
||||
handlePanelSwitch(false)
|
||||
} else {
|
||||
hideBottomPanel()
|
||||
}
|
||||
} else {
|
||||
switchPanel(false)
|
||||
}
|
||||
}
|
||||
|
||||
binding.ivEmoji.setOnClickListener { v ->
|
||||
if (binding.clBottomPanel.isShown) {
|
||||
if (binding.gvFunctionPanel.isShown) {
|
||||
handlePanelSwitch(true)
|
||||
} else {
|
||||
hideBottomPanel()
|
||||
}
|
||||
} else {
|
||||
switchPanel(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchPanel(isSwitchToEmoji: Boolean) {
|
||||
if (isKeyboardShown) {
|
||||
lockContentViewHeight()
|
||||
handlePanelSwitch(isSwitchToEmoji)
|
||||
unlockContentViewHeight()
|
||||
} else {
|
||||
handlePanelSwitch(isSwitchToEmoji)
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideBottomPanel() {
|
||||
lockContentViewHeight()
|
||||
hidePanel(true)
|
||||
unlockContentViewHeight()
|
||||
}
|
||||
|
||||
private fun lockContentViewHeight() {
|
||||
val layoutParams = binding.recycleChatList.layoutParams as LinearLayout.LayoutParams
|
||||
layoutParams.height = binding.recycleChatList.height
|
||||
layoutParams.weight = 0f
|
||||
binding.recycleChatList.layoutParams = layoutParams
|
||||
}
|
||||
|
||||
private fun unlockContentViewHeight() {
|
||||
binding.recycleChatList.postDelayed({
|
||||
val layoutParams = binding.recycleChatList.layoutParams as LinearLayout.LayoutParams
|
||||
layoutParams.weight = 1f
|
||||
binding.recycleChatList.layoutParams = layoutParams
|
||||
}, 200)
|
||||
}
|
||||
|
||||
|
||||
private fun setSoftKeyboardVisibility(isShowKeyboard: Boolean) {
|
||||
if (isShowKeyboard) {
|
||||
binding.etInput.requestFocus()
|
||||
imm!!.showSoftInput(binding.etInput, 0)
|
||||
} else {
|
||||
imm!!.hideSoftInputFromWindow(binding.etInput.windowToken, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePanelSwitch(isEmojiPanel: Boolean) {
|
||||
setSoftKeyboardVisibility(false)
|
||||
binding.clBottomPanel.visibility = View.VISIBLE
|
||||
|
||||
binding.rvEmoji.visibility = if (isEmojiPanel) View.VISIBLE else View.INVISIBLE
|
||||
binding.gvFunctionPanel.visibility = if (isEmojiPanel) View.INVISIBLE else View.VISIBLE
|
||||
}
|
||||
|
||||
private fun hidePanel(showSoftKeyboard: Boolean) {
|
||||
if (binding.clBottomPanel.isShown) {
|
||||
binding.clBottomPanel.visibility = View.GONE
|
||||
if (showSoftKeyboard) {
|
||||
setSoftKeyboardVisibility(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleIntent(intent: Intent?) {
|
||||
if (intent != null) {
|
||||
if (intent.hasExtra("friendId")) {
|
||||
contactId = intent.getStringExtra("friendId")
|
||||
binding.tvContactName.text = contactId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun setListener() {
|
||||
binding.tvContactName.setOnLongClickListener { v ->
|
||||
val intent =
|
||||
Intent(mContext, TestActivity::class.java)
|
||||
startActivity(intent)
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
binding.gvFunctionPanel.selector = ColorDrawable(Color.TRANSPARENT)
|
||||
|
||||
binding.tvSend.setOnClickListener { v -> sendMessage() }
|
||||
binding.recycleChatList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
val layoutManager = checkNotNull(recyclerView.layoutManager as LinearLayoutManager?)
|
||||
val firstVisiblePosition = layoutManager.findLastVisibleItemPosition()
|
||||
if (tempIndex == messagesList[firstVisiblePosition].msgLocalId && isHasHistory && !isLoading) {
|
||||
loadMoreMessages()
|
||||
}
|
||||
}
|
||||
})
|
||||
binding.ivBack.setOnClickListener { v -> finish() }
|
||||
|
||||
binding.tvContactName.setOnClickListener { v ->
|
||||
val intentContactsDetail = Intent(
|
||||
mContext,
|
||||
ContactsDetailActivity::class.java
|
||||
)
|
||||
startActivity(intentContactsDetail)
|
||||
}
|
||||
|
||||
binding.ivMore.setOnClickListener { v ->
|
||||
val intentChatDetail = Intent(
|
||||
mContext,
|
||||
ChatDetailActivity::class.java
|
||||
)
|
||||
startActivity(intentChatDetail)
|
||||
}
|
||||
|
||||
binding.etInput.addTextChangedListener(
|
||||
onTextChanged = { _, _, _, _ ->
|
||||
val isInputEmpty = binding.etInput.text.toString().isEmpty()
|
||||
binding.ivFunctionPanel.visibility = if (!isInputEmpty) View.GONE else View.VISIBLE
|
||||
binding.tvSend.visibility = if (isInputEmpty) View.GONE else View.VISIBLE
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
binding.etInput.setOnEditorActionListener { _, actionId, event ->
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE ||
|
||||
(event != null && event.keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN)
|
||||
) {
|
||||
sendMessage()
|
||||
return@setOnEditorActionListener true
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFunctionPanel() {
|
||||
|
||||
strings?.add("[委屈]")
|
||||
val mEmojiAdapter = EmojiAdapter(strings)
|
||||
mEmojiAdapter.setOnItemClickListener(this)
|
||||
binding.rvEmoji.adapter = mEmojiAdapter
|
||||
|
||||
val gridLayoutManager = GridLayoutManager(this, 7)
|
||||
binding.rvEmoji.layoutManager = gridLayoutManager
|
||||
|
||||
val strings1: MutableList<String> = ArrayList()
|
||||
strings1.add("haha")
|
||||
strings1.add("haha")
|
||||
strings1.add("haha")
|
||||
strings1.add("haha")
|
||||
strings1.add("haha")
|
||||
|
||||
val functionPanelAdapter = FunctionPanelAdapter(strings1, this)
|
||||
binding.gvFunctionPanel.adapter = functionPanelAdapter
|
||||
}
|
||||
|
||||
override fun onItemClick(position: Int) {
|
||||
runOnUiThread {
|
||||
val editable = binding.etInput.text
|
||||
// 获取当前光标位置
|
||||
val index = binding.etInput.selectionStart
|
||||
// 获取将要插入的表情符号
|
||||
val emoji = strings!![position]
|
||||
// 使用 ImageSpanUtil 插入表情符号
|
||||
insertEmoji(
|
||||
mContext, editable,
|
||||
binding.etInput.textSize.toInt(), emoji, index
|
||||
)
|
||||
// 设置新的光标位置
|
||||
binding.etInput.setSelection(index + emoji.length)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
setupFunctionPanel()
|
||||
setRecycleView()
|
||||
if (contactId != null) {
|
||||
binding.tvContactName.text = contactId
|
||||
}
|
||||
setPanel()
|
||||
}
|
||||
|
||||
private fun setPanel() {
|
||||
val layoutParams = binding.clBottomPanel.layoutParams
|
||||
layoutParams.height = softKeyboardHeight
|
||||
binding.clBottomPanel.layoutParams = layoutParams
|
||||
}
|
||||
|
||||
|
||||
private fun initData() {
|
||||
messagesBox = get().boxFor(Messages::class.java)
|
||||
username = getUsername()
|
||||
val intent = intent
|
||||
friendId = intent.getStringExtra("friendId")
|
||||
softKeyboardHeight = getKeyboardHeight()
|
||||
}
|
||||
|
||||
|
||||
private fun setRecycleView() {
|
||||
val layoutManager = LinearLayoutManager(this)
|
||||
layoutManager.reverseLayout = true
|
||||
binding.recycleChatList.layoutManager = layoutManager
|
||||
chatAdapter = ChatAdapter(this, messagesList)
|
||||
binding.recycleChatList.adapter = chatAdapter
|
||||
}
|
||||
|
||||
/**
|
||||
* 初次进入进行加载历史数据
|
||||
*/
|
||||
private fun firstLoadData() {
|
||||
val messages = queryData(0, limit + 1)
|
||||
if (!messages.isEmpty()) {
|
||||
val size = messages.size
|
||||
if (size >= limit) {
|
||||
isHasHistory = true
|
||||
}
|
||||
if (isHasHistory) {
|
||||
val messages1 = messages.subList(0, limit.toInt())
|
||||
messagesList.addAll(messages1)
|
||||
tempIndex = messagesList[messagesList.size - 1].msgLocalId
|
||||
} else {
|
||||
messagesList.addAll(messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadMoreMessages() {
|
||||
if (isLoading) {
|
||||
return
|
||||
}
|
||||
isLoading = true
|
||||
|
||||
val newMessages = queryDataById(tempIndex, limit + 1)
|
||||
val size = newMessages.size
|
||||
tempIndex = newMessages[size - 1].msgLocalId
|
||||
isHasHistory = size > limit
|
||||
|
||||
if (newMessages.isNotEmpty()) {
|
||||
|
||||
val messages1 = if (isHasHistory) {
|
||||
newMessages.subList(1, limit.toInt() + 1).toMutableList()
|
||||
} else {
|
||||
newMessages.subList(1, newMessages.size).toMutableList()
|
||||
}
|
||||
|
||||
val messagesSize = messagesList.size
|
||||
|
||||
messagesList.addAll(messagesSize, messages1)
|
||||
chatAdapter!!.notifyItemRangeInserted(messagesSize, newMessages.size)
|
||||
}
|
||||
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
|
||||
private fun observeLiveData() {
|
||||
if (webSocketService == null) {
|
||||
return
|
||||
}
|
||||
webSocketService!!.liveData.observe(
|
||||
this
|
||||
) { messages: Messages ->
|
||||
if ("ack" != messages.type && username != messages.senderId) {
|
||||
if (messages.msgLocalId != 0L) {
|
||||
return@observe
|
||||
}
|
||||
messagesList.addFirst(messages)
|
||||
runOnUiThread {
|
||||
chatAdapter!!.notifyItemInserted(0)
|
||||
binding.recycleChatList.smoothScrollToPosition(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendMessage() {
|
||||
val message = binding.etInput.text.toString().trim()
|
||||
|
||||
val messages = Messages(
|
||||
0,
|
||||
0,
|
||||
message,
|
||||
System.currentTimeMillis(),
|
||||
"normal",
|
||||
username!!,
|
||||
friendId!!,
|
||||
"0",
|
||||
true
|
||||
)
|
||||
|
||||
addData(messages)
|
||||
|
||||
val id = messagesBox.put(messages)
|
||||
|
||||
val jsonObject = JSONObject()
|
||||
val jsonObject2 = JSONObject()
|
||||
try {
|
||||
jsonObject2.put("receiverId", friendId)
|
||||
jsonObject2.put("content", message)
|
||||
jsonObject2.put("msgLocalId", id)
|
||||
jsonObject.put("type", "single")
|
||||
jsonObject.put("body", jsonObject2)
|
||||
} catch (e: JSONException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
webSocketService!!.sendMessage(jsonObject.toString(), id)
|
||||
|
||||
|
||||
binding.etInput.setText("")
|
||||
}
|
||||
|
||||
private fun addData(messages: Messages) {
|
||||
messagesList.addFirst(messages)
|
||||
chatAdapter!!.notifyItemInserted(0)
|
||||
binding.recycleChatList.smoothScrollToPosition(0)
|
||||
}
|
||||
|
||||
|
||||
private fun queryData(offset: Long, limit: Long): List<Messages> {
|
||||
val query = messagesBox
|
||||
.query(Messages_.takerId.equal(friendId))
|
||||
.order(Messages_.timestamp, QueryBuilder.DESCENDING)
|
||||
.build()
|
||||
return query.find(offset, limit)
|
||||
}
|
||||
|
||||
|
||||
private fun queryDataById(msgLocalId: Long, limit: Long): List<Messages> {
|
||||
val query = messagesBox
|
||||
.query(Messages_.takerId.equal(friendId))
|
||||
.lessOrEqual(Messages_.msgLocalId, msgLocalId)
|
||||
.order(Messages_.timestamp, QueryBuilder.DESCENDING)
|
||||
.build()
|
||||
val offset = 0
|
||||
return query.find(offset.toLong(), limit)
|
||||
}
|
||||
|
||||
private fun bindWebSocketService() {
|
||||
val serviceIntent = Intent(
|
||||
this,
|
||||
WebSocketService::class.java
|
||||
)
|
||||
bindService(serviceIntent, connection, BIND_AUTO_CREATE)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (isBound) {
|
||||
unbindService(connection)
|
||||
isBound = false
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.kaixed.kchat.databinding.ActivityChatDetailBinding
|
||||
|
||||
class ChatDetailActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityChatDetailBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = ActivityChatDetailBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
binding.ifvAvatar.setOnClickListener { v ->
|
||||
val intent =
|
||||
Intent(this@ChatDetailActivity, ContactsDetailActivity::class.java)
|
||||
intent.putExtra("friendId", "kaixed")
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
@ -0,0 +1,16 @@
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.kaixed.kchat.databinding.ActivityContactsDetailBinding
|
||||
|
||||
class ContactsDetailActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityContactsDetailBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = ActivityContactsDetailBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
}
|
||||
}
|
@ -1,12 +1,9 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.databinding.ActivityDataSettingBinding
|
||||
|
||||
class DataSettingActivity : AppCompatActivity() {
|
@ -1,7 +1,6 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
@ -10,8 +9,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.kaixed.kchat.database.UserManager
|
||||
import com.kaixed.kchat.databinding.ActivityFriendListBinding
|
||||
import com.kaixed.kchat.model.friend.FriendItem
|
||||
import com.kaixed.kchat.view.adapter.FriendListAdapter
|
||||
import com.kaixed.kchat.viewmodel.ApplyFriendViewModel
|
||||
import com.kaixed.kchat.ui.adapter.FriendListAdapter
|
||||
import com.kaixed.kchat.viewmodel.FriendListViewModel
|
||||
|
||||
class FriendListActivity : AppCompatActivity() {
|
139
app/src/main/java/com/kaixed/kchat/ui/activity/LoginActivity.kt
Normal file
139
app/src/main/java/com/kaixed/kchat/ui/activity/LoginActivity.kt
Normal file
@ -0,0 +1,139 @@
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.databinding.ActivityLoginBinding
|
||||
import com.kaixed.kchat.utils.Constants.MMKV_COMMON_DATA
|
||||
import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION
|
||||
import com.kaixed.kchat.utils.Constants.USER_LOGIN_STATUS
|
||||
import com.kaixed.kchat.utils.DensityUtil.dpToPx
|
||||
import com.kaixed.kchat.utils.DrawableUtil.createDrawable
|
||||
import com.kaixed.kchat.viewmodel.LoginViewModel
|
||||
import com.tencent.mmkv.MMKV
|
||||
|
||||
class LoginActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityLoginBinding
|
||||
|
||||
private val mViewModel: LoginViewModel by viewModels()
|
||||
private var mmkv: MMKV? = null
|
||||
private var previousKeyboardHeight = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = ActivityLoginBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
mmkv = MMKV.mmkvWithID(MMKV_USER_SESSION)
|
||||
|
||||
if (mmkv!!.decodeBool(USER_LOGIN_STATUS)) {
|
||||
navigateToMain()
|
||||
}
|
||||
|
||||
|
||||
binding.etUsername.addTextChangedListener(textWatcher)
|
||||
binding.etPassword.addTextChangedListener(textWatcher)
|
||||
|
||||
binding.tvLogin.setOnClickListener { v ->
|
||||
val username = binding.etUsername.text.toString().trim()
|
||||
val password = binding.etPassword.text.toString().trim()
|
||||
|
||||
if (username.isEmpty() || password.isEmpty()) {
|
||||
Toast.makeText(this, "请输入用户名或密码", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
mViewModel.login(username, password).observe(this) { loginResult ->
|
||||
if (loginResult == null) {
|
||||
runOnUiThread {
|
||||
Toast.makeText(
|
||||
this@LoginActivity,
|
||||
"登录异常",
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
}
|
||||
} else if ("200" == loginResult.code) {
|
||||
mmkv!!.encode("username", loginResult.data.username)
|
||||
mmkv!!.encode("nickname", loginResult.data.nickname)
|
||||
mmkv!!.encode("nickname", loginResult.data.avatarUrl)
|
||||
mmkv!!.encode("userLoginStatus", true)
|
||||
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show()
|
||||
navigateToMain()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getKeyboardHeight()
|
||||
}
|
||||
|
||||
private fun getKeyboardHeight() {
|
||||
val rootLayout = binding.root
|
||||
|
||||
rootLayout.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
val rect = Rect()
|
||||
rootLayout.getWindowVisibleDisplayFrame(rect)
|
||||
|
||||
val screenHeight = rootLayout.rootView.height
|
||||
val keypadHeight = screenHeight - rect.bottom
|
||||
|
||||
// 通过15%的屏幕高度差来判断是否为键盘
|
||||
if (keypadHeight > (screenHeight * 0.15)) {
|
||||
// 键盘弹出
|
||||
if (keypadHeight != previousKeyboardHeight) {
|
||||
previousKeyboardHeight = keypadHeight
|
||||
|
||||
val kv = MMKV.mmkvWithID(MMKV_COMMON_DATA)
|
||||
kv.encode("keyboardHeight", keypadHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var textWatcher: TextWatcher = object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
if (s.isNotEmpty()) {
|
||||
if (binding.etUsername.text.isNotEmpty() && binding.etPassword.text.isNotEmpty()) {
|
||||
binding.tvLogin.setTextColor(
|
||||
ContextCompat.getColor(
|
||||
this@LoginActivity,
|
||||
R.color.white
|
||||
)
|
||||
)
|
||||
binding.tvLogin.background =
|
||||
createDrawable(
|
||||
ContextCompat.getColor(this@LoginActivity, R.color.green),
|
||||
dpToPx(this@LoginActivity, 8)
|
||||
)
|
||||
} else {
|
||||
binding.tvLogin.setTextColor(Color.parseColor("#B4B4B4"))
|
||||
binding.tvLogin.background =
|
||||
createDrawable(Color.parseColor("#E1E1E1"), dpToPx(this@LoginActivity, 8))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun navigateToMain() {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
}
|
359
app/src/main/java/com/kaixed/kchat/ui/activity/MainActivity.kt
Normal file
359
app/src/main/java/com/kaixed/kchat/ui/activity/MainActivity.kt
Normal file
@ -0,0 +1,359 @@
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.view.View
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.database.ObjectBox.get
|
||||
import com.kaixed.kchat.database.entity.ChatLists
|
||||
import com.kaixed.kchat.database.entity.ChatLists_
|
||||
import com.kaixed.kchat.database.entity.Messages
|
||||
import com.kaixed.kchat.databinding.ActivityMainBinding
|
||||
import com.kaixed.kchat.model.HomeItem
|
||||
import com.kaixed.kchat.service.WebSocketService
|
||||
import com.kaixed.kchat.service.WebSocketService.LocalBinder
|
||||
import com.kaixed.kchat.ui.adapter.ChatListAdapter
|
||||
import com.kaixed.kchat.ui.adapter.MyGridAdapter
|
||||
import com.kaixed.kchat.ui.i.OnChatListItemClickListener
|
||||
import com.kaixed.kchat.utils.Constants.MMKV_COMMON_DATA
|
||||
import com.tencent.mmkv.MMKV
|
||||
import io.objectbox.Box
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/5/26 10:02
|
||||
*/
|
||||
class MainActivity : AppCompatActivity(), OnChatListItemClickListener {
|
||||
private var binding: ActivityMainBinding? = null
|
||||
private var chatLists: MutableList<ChatLists> = ArrayList()
|
||||
private var chatListAdapter: ChatListAdapter? = null
|
||||
private var chatListsBox: Box<ChatLists>? = null
|
||||
private var messagesBox: Box<Messages>? = null
|
||||
private var updateUsername: String? = null
|
||||
private val items: MutableList<HomeItem> = ArrayList()
|
||||
private var webSocketService: WebSocketService? = null
|
||||
private var isFlipped = false
|
||||
private var isBound = false
|
||||
private val mContext: Context = this
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.enableEdgeToEdge()
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding!!.root)
|
||||
|
||||
chatListsBox = get().boxFor(ChatLists::class.java)
|
||||
messagesBox = get().boxFor(Messages::class.java)
|
||||
|
||||
initView()
|
||||
|
||||
startService()
|
||||
|
||||
setOnClick()
|
||||
}
|
||||
|
||||
private fun setOnClick() {
|
||||
binding!!.ivMessage.setOnClickListener { v ->
|
||||
val intent = Intent(
|
||||
this,
|
||||
MessageActivity::class.java
|
||||
)
|
||||
startActivity(intent)
|
||||
}
|
||||
binding!!.ivSearch.setOnClickListener { v ->
|
||||
val intent =
|
||||
Intent(mContext, SearchActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding!!.ifvAvatar.setOnClickListener { v ->
|
||||
val intent =
|
||||
Intent(mContext, ProfileActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun flipImage(v: View) {
|
||||
val flipAnimation = if (isFlipped) {
|
||||
ObjectAnimator.ofFloat(v, "rotationX", 180f, 0f)
|
||||
} else {
|
||||
ObjectAnimator.ofFloat(v, "rotationX", 0f, 180f)
|
||||
}
|
||||
flipAnimation.setDuration(0)
|
||||
flipAnimation.start()
|
||||
isFlipped = !isFlipped
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun notifyData() {
|
||||
chatListAdapter!!.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private fun startService() {
|
||||
val intent = Intent(
|
||||
this@MainActivity,
|
||||
WebSocketService::class.java
|
||||
)
|
||||
startService(intent)
|
||||
}
|
||||
|
||||
private fun observeLiveData() {
|
||||
if (webSocketService == null) {
|
||||
return
|
||||
}
|
||||
webSocketService!!.liveData.observe(
|
||||
this
|
||||
) { messages: Messages ->
|
||||
if ("0" == messages.type) {
|
||||
processMessage(messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processMessage(messages: Messages) {
|
||||
val talkerId = messages.takerId
|
||||
val content = messages.content
|
||||
val timestamp = messages.timestamp
|
||||
|
||||
val chatList = createChatList(talkerId, content, timestamp)
|
||||
|
||||
val index = findChatListIndex(talkerId)
|
||||
if (index == -1) {
|
||||
chatLists.add(chatList)
|
||||
} else {
|
||||
updateChatList(chatLists[index], content, timestamp)
|
||||
}
|
||||
|
||||
runOnUiThread { this.notifyData() }
|
||||
isInDb(chatList)
|
||||
}
|
||||
|
||||
private fun createChatList(talkerId: String, content: String, timestamp: Long): ChatLists {
|
||||
return ChatLists(
|
||||
0L,
|
||||
talkerId,
|
||||
talkerId,
|
||||
"1",
|
||||
content,
|
||||
timestamp,
|
||||
1
|
||||
)
|
||||
}
|
||||
|
||||
private fun findChatListIndex(talkerId: String): Int {
|
||||
for (i in chatLists.indices) {
|
||||
if (chatLists[i].talkerId == talkerId) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
private fun updateChatList(chatList: ChatLists, content: String, timestamp: Long) {
|
||||
chatList.lastContent = content
|
||||
chatList.timestamp = timestamp
|
||||
}
|
||||
|
||||
|
||||
private fun isInDb(chatList: ChatLists) {
|
||||
// 查询数据库中是否已经存在该talkerId的记录
|
||||
val existingChatList = chatListsBox
|
||||
?.query(ChatLists_.talkerId.equal(chatList.talkerId))
|
||||
?.build()
|
||||
?.findFirst()
|
||||
|
||||
if (existingChatList != null) {
|
||||
// 如果存在,更新内容和时间戳
|
||||
existingChatList.lastContent = chatList.lastContent
|
||||
existingChatList.timestamp = chatList.timestamp
|
||||
chatListsBox!!.put(existingChatList)
|
||||
} else {
|
||||
// 如果不存在,添加新的记录
|
||||
chatListsBox!!.put(chatList)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateChatLists() {
|
||||
val kv = MMKV.mmkvWithID(MMKV_COMMON_DATA)
|
||||
val msgLocalId = kv.getLong("msgLocalId", -1L)
|
||||
if (msgLocalId != -1L) {
|
||||
val messages = messagesBox!![msgLocalId]
|
||||
processMessage(messages)
|
||||
kv.remove("msgLocalId")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var connection: ServiceConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
||||
val binder = service as LocalBinder
|
||||
webSocketService = binder.getService()
|
||||
isBound = true
|
||||
observeLiveData()
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(arg0: ComponentName) {
|
||||
isBound = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
setupGridView()
|
||||
setupRecycleView()
|
||||
setupMoreOptionsToggle()
|
||||
}
|
||||
|
||||
private fun setupMoreOptionsToggle() {
|
||||
binding!!.ivMore.setOnClickListener { v ->
|
||||
flipImage(v)
|
||||
val gridViewHeight = binding!!.gridView.height
|
||||
val startAt: Int
|
||||
val endAt: Int
|
||||
if (isFlipped) {
|
||||
binding!!.gridView.visibility = View.VISIBLE
|
||||
startAt = 0
|
||||
endAt = gridViewHeight
|
||||
} else {
|
||||
startAt = gridViewHeight
|
||||
endAt = 0
|
||||
}
|
||||
createAnimator(startAt, endAt).start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAnimator(start: Int, end: Int): ObjectAnimator {
|
||||
val animator = ObjectAnimator.ofInt(binding!!.linearlayout, "top", start, end)
|
||||
val duration: Long = 500
|
||||
animator.setDuration(duration)
|
||||
|
||||
animator.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationStart(animation: Animator) {
|
||||
super.onAnimationStart(animation)
|
||||
binding!!.ivMore.isEnabled = false
|
||||
}
|
||||
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
super.onAnimationEnd(animation)
|
||||
if (!isFlipped) {
|
||||
binding!!.gridView.visibility = View.INVISIBLE
|
||||
}
|
||||
binding!!.ivMore.isEnabled = true
|
||||
}
|
||||
})
|
||||
return animator
|
||||
}
|
||||
|
||||
private fun setupRecycleView() {
|
||||
if (chatListsBox!!.all != null) {
|
||||
chatLists = chatListsBox!!.all
|
||||
}
|
||||
chatListAdapter = ChatListAdapter(chatLists, this)
|
||||
binding!!.recycleChatList.layoutManager =
|
||||
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
binding!!.recycleChatList.adapter = chatListAdapter
|
||||
chatListAdapter!!.setItemListener(this)
|
||||
}
|
||||
|
||||
private fun setupGridView() {
|
||||
initMenuData()
|
||||
val myGridAdapter = MyGridAdapter(this, items)
|
||||
binding!!.gridView.adapter = myGridAdapter
|
||||
|
||||
binding!!.gridView.selector = ColorDrawable(Color.TRANSPARENT)
|
||||
|
||||
binding!!.gridView.setOnItemClickListener { _, _, position, _ ->
|
||||
when (position) {
|
||||
0 -> {}
|
||||
2 -> {
|
||||
val intent =
|
||||
Intent(
|
||||
this@MainActivity,
|
||||
AddFriendsActivity::class.java
|
||||
)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
4 -> {
|
||||
val intent2 =
|
||||
Intent(
|
||||
this@MainActivity,
|
||||
ContactUpdatesActivity::class.java
|
||||
)
|
||||
startActivity(intent2)
|
||||
}
|
||||
|
||||
6 -> {
|
||||
val intent1 =
|
||||
Intent(
|
||||
this@MainActivity,
|
||||
FriendListActivity::class.java
|
||||
)
|
||||
startActivity(intent1)
|
||||
}
|
||||
|
||||
else -> finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initMenuData() {
|
||||
items.add(HomeItem("更换背景", true, R.drawable.ic_switch))
|
||||
items.add(HomeItem("创建群聊", false, R.drawable.ic_troop))
|
||||
items.add(HomeItem("新朋友", true, R.drawable.ic_friend))
|
||||
items.add(HomeItem("夜间模式", true, R.drawable.ic_night))
|
||||
items.add(HomeItem("好友动态", false, R.drawable.ic_qzone))
|
||||
items.add(HomeItem("扫一扫", false, R.drawable.ic_scan))
|
||||
items.add(HomeItem("通讯录", true, R.drawable.ic_clock_in))
|
||||
items.add(HomeItem("关闭应用", false, R.drawable.ic_exit))
|
||||
}
|
||||
|
||||
|
||||
override fun onRestart() {
|
||||
super.onRestart()
|
||||
updateChatLists()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val intent = Intent(
|
||||
this,
|
||||
WebSocketService::class.java
|
||||
)
|
||||
bindService(intent, connection, BIND_AUTO_CREATE)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
if (isBound) {
|
||||
unbindService(connection)
|
||||
isBound = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
val intent = Intent(
|
||||
this@MainActivity,
|
||||
WebSocketService::class.java
|
||||
)
|
||||
stopService(intent)
|
||||
}
|
||||
|
||||
override fun onItemClick(username: String?) {
|
||||
this.updateUsername = username
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@ -10,7 +8,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.kaixed.kchat.database.UserManager
|
||||
import com.kaixed.kchat.databinding.ActivityMessageBinding
|
||||
import com.kaixed.kchat.model.friend.FriendRequestItem
|
||||
import com.kaixed.kchat.view.adapter.MessageListAdapter
|
||||
import com.kaixed.kchat.ui.adapter.MessageListAdapter
|
||||
import com.kaixed.kchat.viewmodel.ContactViewModel
|
||||
|
||||
class MessageActivity : AppCompatActivity() {
|
@ -1,13 +1,10 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.R
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.kaixed.kchat.databinding.ActivityProfileBinding
|
||||
import com.kaixed.kchat.view.fragment.SettingsFragment
|
||||
|
||||
|
||||
class ProfileActivity : AppCompatActivity() {
|
@ -1,11 +1,8 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.databinding.ActivityProfileDetailBinding
|
||||
|
||||
class ProfileDetailActivity : AppCompatActivity() {
|
110
app/src/main/java/com/kaixed/kchat/ui/activity/SearchActivity.kt
Normal file
110
app/src/main/java/com/kaixed/kchat/ui/activity/SearchActivity.kt
Normal file
@ -0,0 +1,110 @@
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.View
|
||||
import android.widget.EdgeEffect
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory
|
||||
import com.kaixed.kchat.model.search.SearchItem
|
||||
import com.kaixed.kchat.databinding.ActivitySearchBinding
|
||||
import com.kaixed.kchat.ui.adapter.SearchResultAdapter
|
||||
|
||||
class SearchActivity : AppCompatActivity() {
|
||||
private var binding: ActivitySearchBinding? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.enableEdgeToEdge()
|
||||
binding = ActivitySearchBinding.inflate(layoutInflater)
|
||||
setContentView(binding!!.root)
|
||||
|
||||
setOnClickListener()
|
||||
|
||||
setupRecycleView()
|
||||
|
||||
|
||||
binding!!.etSearch.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
setupViewVisibility(s.length)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setupViewVisibility(length: Int) {
|
||||
binding!!.rvSearchResult.visibility = if (length > 0) View.VISIBLE else View.INVISIBLE
|
||||
binding!!.tvPageSetting.visibility = if (length > 0) View.INVISIBLE else View.VISIBLE
|
||||
}
|
||||
|
||||
private fun setupRecycleView() {
|
||||
binding!!.rvSearchResult.edgeEffectFactory = object : EdgeEffectFactory() {
|
||||
override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect {
|
||||
return object : EdgeEffect(view.context) {
|
||||
override fun onPull(deltaDistance: Float, displacement: Float) {
|
||||
// 禁止下拉时的效果
|
||||
}
|
||||
|
||||
override fun onRelease() {
|
||||
// 禁止释放时的效果
|
||||
}
|
||||
|
||||
override fun onAbsorb(velocity: Int) {
|
||||
// 禁止吸收时的效果
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val searchResultAdapter: SearchResultAdapter = getAdapter()
|
||||
binding!!.rvSearchResult.adapter = searchResultAdapter
|
||||
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
binding!!.rvSearchResult.layoutManager = layoutManager
|
||||
}
|
||||
|
||||
private fun setOnClickListener() {
|
||||
binding!!.tvCancel.setOnClickListener { v ->
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAdapter(): SearchResultAdapter {
|
||||
val contactItem = SearchItem("sa", "kaixed", "as", false, "联系人")
|
||||
val chatHistoryItem = SearchItem("sa", "kaixed", "as", false, "群聊")
|
||||
val groupItem = SearchItem("sa", "kaixed", "as", false, "聊天记录")
|
||||
|
||||
val searchItem1 = SearchItem("sa", "kaixed", "as", true, "联系人")
|
||||
val searchItem2 = SearchItem("sa", "kaixed", "as", true, "群聊")
|
||||
val searchItem3 = SearchItem("sa", "kaixed", "as", true, "聊天记录")
|
||||
|
||||
|
||||
val objects: MutableList<Any> = ArrayList(12)
|
||||
objects.add("联系人")
|
||||
objects.add(contactItem)
|
||||
objects.add(contactItem)
|
||||
objects.add(searchItem1)
|
||||
|
||||
objects.add("群聊")
|
||||
objects.add(groupItem)
|
||||
objects.add(groupItem)
|
||||
objects.add(searchItem2)
|
||||
|
||||
objects.add("聊天记录")
|
||||
objects.add(chatHistoryItem)
|
||||
objects.add(chatHistoryItem)
|
||||
objects.add(searchItem3)
|
||||
|
||||
|
||||
val searchResultAdapter = SearchResultAdapter(objects)
|
||||
return searchResultAdapter
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
@ -1,11 +1,8 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.databinding.ActivityServiceDetailBinding
|
||||
|
||||
class ServiceDetailActivity : AppCompatActivity() {
|
@ -1,11 +1,8 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.databinding.ActivitySetRemarkBinding
|
||||
|
||||
class SetRemarkActivity : AppCompatActivity() {
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
@ -6,7 +6,7 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.kaixed.kchat.databinding.ActivitySettingBinding
|
||||
import com.kaixed.kchat.model.setting.SettingItem
|
||||
import com.kaixed.kchat.view.adapter.SettingListAdapter
|
||||
import com.kaixed.kchat.ui.adapter.SettingListAdapter
|
||||
|
||||
class SettingActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivitySettingBinding
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
package com.kaixed.kchat.ui.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.adapter
|
||||
package com.kaixed.kchat.ui.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.ColorDrawable
|
@ -0,0 +1,72 @@
|
||||
package com.kaixed.kchat.ui.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.kaixed.kchat.database.entity.ChatLists
|
||||
import com.kaixed.kchat.databinding.ChatMainItemBinding
|
||||
import com.kaixed.kchat.ui.activity.ChatActivity
|
||||
import com.kaixed.kchat.ui.i.OnChatListItemClickListener
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/10/27 18:22
|
||||
*/
|
||||
class ChatListAdapter(
|
||||
private val chatLists: List<ChatLists>,
|
||||
private val mContext: Context
|
||||
) :
|
||||
RecyclerView.Adapter<ChatListAdapter.MyViewHolder?>() {
|
||||
private var onChatListItemClickListener: OnChatListItemClickListener? = null
|
||||
|
||||
class MyViewHolder(val binding: ChatMainItemBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bindData(chatList: ChatLists) {
|
||||
binding.tvContent.text = chatList.lastContent
|
||||
binding.tvNickname.text = chatList.talkerId
|
||||
|
||||
val instant = Instant.ofEpochMilli(chatList.timestamp)
|
||||
// 将时间戳转换为当前系统默认时区的时间
|
||||
val zdt = instant.atZone(ZoneId.systemDefault())
|
||||
// 格式化成小时:分钟格式
|
||||
val formatter = DateTimeFormatter.ofPattern("HH:mm")
|
||||
val formattedTime = zdt.format(formatter)
|
||||
binding.tvTimestamp.text = formattedTime
|
||||
}
|
||||
}
|
||||
|
||||
fun setItemListener(i: OnChatListItemClickListener?) {
|
||||
this.onChatListItemClickListener = i
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||
val binding = ChatMainItemBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return MyViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: MyViewHolder,
|
||||
@SuppressLint("RecyclerView") position: Int
|
||||
) {
|
||||
holder.bindData(chatLists[position])
|
||||
holder.itemView.setOnClickListener { v: View? ->
|
||||
onChatListItemClickListener!!.onItemClick(chatLists[position].talkerId)
|
||||
val intent =
|
||||
Intent(mContext, ChatActivity::class.java)
|
||||
intent.putExtra("friendId", chatLists[position].talkerId)
|
||||
mContext.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = chatLists.size
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.kaixed.kchat.ui.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.kaixed.kchat.databinding.EmojiRecycleItemBinding
|
||||
import com.kaixed.kchat.ui.i.OnItemClickListener
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/10/27 18:27
|
||||
*/
|
||||
class EmojiAdapter(private val strings: MutableList<String>?) :
|
||||
RecyclerView.Adapter<EmojiAdapter.MyViewHolder?>() {
|
||||
private var onItemClickListener: OnItemClickListener? = null
|
||||
|
||||
fun setOnItemClickListener(onItemClickListener: OnItemClickListener?) {
|
||||
this.onItemClickListener = onItemClickListener
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||
|
||||
val binding: EmojiRecycleItemBinding = EmojiRecycleItemBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
|
||||
return MyViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: MyViewHolder,
|
||||
@SuppressLint("RecyclerView") position: Int
|
||||
) {
|
||||
holder.itemView.setOnClickListener { v: View? ->
|
||||
onItemClickListener!!.onItemClick(position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = strings?.size ?: 0
|
||||
|
||||
|
||||
class MyViewHolder(private val binding: EmojiRecycleItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.adapter
|
||||
package com.kaixed.kchat.ui.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@ -9,7 +9,7 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.bumptech.glide.Glide
|
||||
import com.kaixed.kchat.databinding.FriendRecycleItemBinding
|
||||
import com.kaixed.kchat.model.friend.FriendItem
|
||||
import com.kaixed.kchat.view.activity.ChatActivity
|
||||
import com.kaixed.kchat.ui.activity.ChatActivity
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
@ -0,0 +1,45 @@
|
||||
package com.kaixed.kchat.ui.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
import com.kaixed.kchat.databinding.FunctionGridItemBinding
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/10/27 18:29
|
||||
*/
|
||||
class FunctionPanelAdapter(
|
||||
private val strings: List<String>?,
|
||||
private val context: Context
|
||||
) :
|
||||
BaseAdapter() {
|
||||
override fun getCount(): Int {
|
||||
return strings?.size ?: 0
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Any? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
|
||||
var convertView = convertView
|
||||
val binding: FunctionGridItemBinding
|
||||
if (convertView == null) {
|
||||
binding = FunctionGridItemBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
convertView = binding.getRoot()
|
||||
convertView.tag = binding
|
||||
} else {
|
||||
binding = convertView.tag as FunctionGridItemBinding
|
||||
}
|
||||
|
||||
|
||||
return convertView
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.adapter
|
||||
package com.kaixed.kchat.ui.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@ -10,9 +10,8 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.bumptech.glide.Glide
|
||||
import com.kaixed.kchat.databinding.FriendRecycleItemAddRequestBinding
|
||||
import com.kaixed.kchat.model.friend.FriendRequestItem
|
||||
import com.kaixed.kchat.view.activity.ApplyFriendsDetailActivity
|
||||
import com.kaixed.kchat.view.activity.ApproveContactRequestActivity
|
||||
import com.kaixed.kchat.view.activity.ApproveDetailActivity
|
||||
import com.kaixed.kchat.ui.activity.ApproveContactRequestActivity
|
||||
import com.kaixed.kchat.ui.activity.ApproveDetailActivity
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
@ -0,0 +1,51 @@
|
||||
package com.kaixed.kchat.ui.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
import com.kaixed.kchat.databinding.ItemGridBinding
|
||||
import com.kaixed.kchat.model.HomeItem
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/10/27 18:30
|
||||
*/
|
||||
class MyGridAdapter(
|
||||
private val context: Context,
|
||||
private val items: List<HomeItem>
|
||||
) : BaseAdapter() {
|
||||
|
||||
override fun getCount(): Int {
|
||||
return items.size
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Any {
|
||||
return items[position]
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return position.toLong()
|
||||
}
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
// 使用 View Binding
|
||||
val binding: ItemGridBinding = if (convertView == null) {
|
||||
ItemGridBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
} else {
|
||||
ItemGridBinding.bind(convertView)
|
||||
}
|
||||
|
||||
val item = items[position]
|
||||
|
||||
// 设置视图内容
|
||||
binding.textview.text = item.name
|
||||
binding.imageview.setImageResource(item.img)
|
||||
|
||||
// 控制更多按钮的可见性
|
||||
binding.ivMore.visibility = if (item.more) View.VISIBLE else View.GONE
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package com.kaixed.kchat.ui.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.NonNull
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.kaixed.kchat.databinding.SearchRecycleItemContactsBinding
|
||||
import com.kaixed.kchat.databinding.SearchRecycleItemDetailsBinding
|
||||
import com.kaixed.kchat.model.search.SearchItem
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/10/27 18:34
|
||||
*/
|
||||
class SearchResultAdapter(
|
||||
private val displayedItems: List<Any>
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return if (viewType == 0) {
|
||||
val binding = SearchRecycleItemContactsBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
GroupViewHolder(binding)
|
||||
} else {
|
||||
val binding = SearchRecycleItemDetailsBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
DetailsViewHolder(binding)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder.itemViewType) {
|
||||
0 -> {
|
||||
val groupViewHolder = holder as GroupViewHolder
|
||||
groupViewHolder.binding.tvName.text = displayedItems[position].toString()
|
||||
}
|
||||
|
||||
1 -> {
|
||||
val detailsViewHolder = holder as DetailsViewHolder
|
||||
val item = displayedItems[position] as? SearchItem
|
||||
item?.let {
|
||||
detailsViewHolder.binding.tvName.text = it.name
|
||||
if (it.hasMore) {
|
||||
detailsViewHolder.binding.tvHasMore.text = "更多${it.type}"
|
||||
detailsViewHolder.binding.rlHasMore.visibility = View.VISIBLE
|
||||
detailsViewHolder.binding.view.visibility = View.INVISIBLE
|
||||
} else {
|
||||
detailsViewHolder.binding.rlHasMore.visibility = View.GONE
|
||||
detailsViewHolder.binding.view.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (displayedItems[position]) {
|
||||
is String -> 0
|
||||
is SearchItem -> 1
|
||||
else -> -1 // 处理意外情况
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return displayedItems.size
|
||||
}
|
||||
|
||||
// ViewHolder for Group Items
|
||||
class GroupViewHolder(val binding: SearchRecycleItemContactsBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
// ViewHolder for Detail Items
|
||||
class DetailsViewHolder(val binding: SearchRecycleItemDetailsBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.adapter
|
||||
package com.kaixed.kchat.ui.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.fragment
|
||||
package com.kaixed.kchat.ui.fragment
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
@ -0,0 +1,9 @@
|
||||
package com.kaixed.kchat.ui.i
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/10/27 18:35
|
||||
*/
|
||||
interface OnChatListItemClickListener {
|
||||
fun onItemClick(username: String?)
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.kaixed.kchat.ui.i
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/10/27 18:36
|
||||
*/
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(position: Int)
|
||||
}
|
@ -1,18 +1,15 @@
|
||||
package com.kaixed.kchat.view.widget
|
||||
package com.kaixed.kchat.ui.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.databinding.ItemCustomBinding
|
||||
import com.kaixed.kchat.view.activity.ProfileDetailActivity
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.widget
|
||||
package com.kaixed.kchat.ui.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.widget
|
||||
package com.kaixed.kchat.ui.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.widget
|
||||
package com.kaixed.kchat.ui.widget
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.view.widget
|
||||
package com.kaixed.kchat.ui.widget
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener
|
@ -1,557 +0,0 @@
|
||||
package com.kaixed.kchat.view.activity;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.kaixed.kchat.database.ObjectBox;
|
||||
import com.kaixed.kchat.database.UserManager;
|
||||
import com.kaixed.kchat.database.entity.Messages;
|
||||
import com.kaixed.kchat.database.entity.Messages_;
|
||||
import com.kaixed.kchat.databinding.ActivityChatBinding;
|
||||
import com.kaixed.kchat.service.WebSocketService;
|
||||
import com.kaixed.kchat.utils.ConstantsUtil;
|
||||
import com.kaixed.kchat.utils.ImageSpanUtil;
|
||||
import com.kaixed.kchat.view.adapter.ChatAdapter;
|
||||
import com.kaixed.kchat.view.adapter.EmojiAdapter;
|
||||
import com.kaixed.kchat.view.adapter.FunctionPanelAdapter;
|
||||
import com.kaixed.kchat.view.i.OnItemClickListener;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import io.objectbox.Box;
|
||||
import io.objectbox.query.Query;
|
||||
import io.objectbox.query.QueryBuilder;
|
||||
|
||||
/**
|
||||
* @author hui
|
||||
*/
|
||||
public class ChatActivity extends AppCompatActivity implements OnItemClickListener {
|
||||
|
||||
private ChatAdapter chatAdapter;
|
||||
|
||||
private WebSocketService webSocketService;
|
||||
|
||||
private final LinkedList<Messages> messagesList = new LinkedList<>();
|
||||
|
||||
public static final String TAG = "ChatActivity";
|
||||
|
||||
private Box<Messages> messagesBox;
|
||||
|
||||
private String friendId;
|
||||
|
||||
private static final long LIMIT = 50L;
|
||||
|
||||
private boolean isLoading = false;
|
||||
|
||||
private boolean isHasHistory = false;
|
||||
|
||||
private boolean isBound = false;
|
||||
|
||||
private String username;
|
||||
|
||||
private long tempIndex;
|
||||
|
||||
private final Context mContext = this;
|
||||
|
||||
private List<String> strings;
|
||||
|
||||
private InputMethodManager imm;
|
||||
|
||||
private ActivityChatBinding binding;
|
||||
|
||||
private int softKeyboardHeight;
|
||||
|
||||
private String contactId;
|
||||
|
||||
private final ServiceConnection connection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
WebSocketService.LocalBinder binder = (WebSocketService.LocalBinder) service;
|
||||
webSocketService = binder.getService();
|
||||
isBound = true;
|
||||
observeLiveData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName arg0) {
|
||||
isBound = false;
|
||||
}
|
||||
};
|
||||
|
||||
private boolean isKeyboardShown = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
binding = ActivityChatBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
initData();
|
||||
initView();
|
||||
firstLoadData();
|
||||
|
||||
setListener();
|
||||
|
||||
handleIntent(getIntent());
|
||||
bindWebSocketService();
|
||||
|
||||
imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
setPanelChange();
|
||||
|
||||
setBackPressListener();
|
||||
|
||||
getKeyBoardVisibility();
|
||||
|
||||
}
|
||||
|
||||
private void getKeyBoardVisibility() {
|
||||
final View rootView = findViewById(android.R.id.content);
|
||||
|
||||
rootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
||||
Rect r = new Rect();
|
||||
rootView.getWindowVisibleDisplayFrame(r);
|
||||
int screenHeight = rootView.getRootView().getHeight();
|
||||
int keypadHeight = screenHeight - r.bottom;
|
||||
isKeyboardShown = keypadHeight > screenHeight * 0.15;
|
||||
});
|
||||
}
|
||||
|
||||
private void setBackPressListener() {
|
||||
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
if (binding.clBottomPanel.isShown()) {
|
||||
hidePanel(false);
|
||||
} else {
|
||||
setEnabled(false);
|
||||
onBackPressed();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void setPanelChange() {
|
||||
binding.etInput.setOnTouchListener((v, event) -> {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP && binding.clBottomPanel.isShown()) {
|
||||
lockContentViewHeight();
|
||||
hidePanel(true);
|
||||
unlockContentViewHeight();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
binding.ivFunctionPanel.setOnClickListener(v -> {
|
||||
if (binding.clBottomPanel.isShown()) {
|
||||
if (binding.rvEmoji.isShown()) {
|
||||
handlePanelSwitch(false);
|
||||
} else {
|
||||
hideBottomPanel();
|
||||
}
|
||||
} else {
|
||||
switchPanel(false);
|
||||
}
|
||||
});
|
||||
|
||||
binding.ivEmoji.setOnClickListener(v -> {
|
||||
if (binding.clBottomPanel.isShown()) {
|
||||
if (binding.gvFunctionPanel.isShown()) {
|
||||
handlePanelSwitch(true);
|
||||
} else {
|
||||
hideBottomPanel();
|
||||
}
|
||||
} else {
|
||||
switchPanel(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void switchPanel(boolean isSwitchToEmoji) {
|
||||
if (isKeyboardShown) {
|
||||
lockContentViewHeight();
|
||||
handlePanelSwitch(isSwitchToEmoji);
|
||||
unlockContentViewHeight();
|
||||
} else {
|
||||
handlePanelSwitch(isSwitchToEmoji);
|
||||
}
|
||||
}
|
||||
|
||||
private void hideBottomPanel() {
|
||||
lockContentViewHeight();
|
||||
hidePanel(true);
|
||||
unlockContentViewHeight();
|
||||
}
|
||||
|
||||
private void lockContentViewHeight() {
|
||||
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) binding.recycleChatList.getLayoutParams();
|
||||
layoutParams.height = binding.recycleChatList.getHeight();
|
||||
layoutParams.weight = 0f;
|
||||
binding.recycleChatList.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
private void unlockContentViewHeight() {
|
||||
binding.recycleChatList.postDelayed(() -> {
|
||||
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) binding.recycleChatList.getLayoutParams();
|
||||
layoutParams.weight = 1f;
|
||||
binding.recycleChatList.setLayoutParams(layoutParams);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
|
||||
private void setSoftKeyboardVisibility(boolean isShowKeyboard) {
|
||||
if (isShowKeyboard) {
|
||||
binding.etInput.requestFocus();
|
||||
imm.showSoftInput(binding.etInput, 0);
|
||||
} else {
|
||||
imm.hideSoftInputFromWindow(binding.etInput.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePanelSwitch(boolean isEmojiPanel) {
|
||||
setSoftKeyboardVisibility(false);
|
||||
binding.clBottomPanel.setVisibility(View.VISIBLE);
|
||||
|
||||
binding.rvEmoji.setVisibility(isEmojiPanel ? View.VISIBLE : View.INVISIBLE);
|
||||
binding.gvFunctionPanel.setVisibility(isEmojiPanel ? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
|
||||
private void hidePanel(boolean showSoftKeyboard) {
|
||||
if (binding.clBottomPanel.isShown()) {
|
||||
binding.clBottomPanel.setVisibility(View.GONE);
|
||||
if (showSoftKeyboard) {
|
||||
setSoftKeyboardVisibility(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleIntent(Intent intent) {
|
||||
if (intent != null) {
|
||||
if (intent.hasExtra("friendId")) {
|
||||
contactId = intent.getStringExtra("friendId");
|
||||
binding.tvContactName.setText(contactId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setListener() {
|
||||
|
||||
binding.tvContactName.setOnLongClickListener(v -> {
|
||||
Intent intent = new Intent(mContext, TestActivity.class);
|
||||
startActivity(intent);
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
binding.gvFunctionPanel.setSelector(new ColorDrawable(Color.TRANSPARENT));
|
||||
|
||||
binding.tvSend.setOnClickListener(v -> sendMessage());
|
||||
binding.recycleChatList.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
|
||||
assert layoutManager != null;
|
||||
int firstVisiblePosition = layoutManager.findLastVisibleItemPosition();
|
||||
if (tempIndex == messagesList.get(firstVisiblePosition).getMsgLocalId() && isHasHistory && !isLoading) {
|
||||
Log.d(TAG, "加载历史消息");
|
||||
loadMoreMessages();
|
||||
}
|
||||
}
|
||||
});
|
||||
binding.ivBack.setOnClickListener(v -> finish());
|
||||
|
||||
binding.tvContactName.setOnClickListener(v -> {
|
||||
Intent intentContactsDetail = new Intent(mContext, ContactsDetailActivity.class);
|
||||
startActivity(intentContactsDetail);
|
||||
});
|
||||
|
||||
binding.ivMore.setOnClickListener(v -> {
|
||||
Intent intentChatDetail = new Intent(mContext, ChatDetailActivity.class);
|
||||
startActivity(intentChatDetail);
|
||||
});
|
||||
|
||||
|
||||
binding.etInput.addTextChangedListener(textWatcher);
|
||||
|
||||
binding.etInput.setOnEditorActionListener((v, actionId, event) -> {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE ||
|
||||
(event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN)) {
|
||||
sendMessage();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private final TextWatcher textWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
boolean isInputEmpty = binding.etInput.getText().toString().isEmpty();
|
||||
binding.ivFunctionPanel.setVisibility(!isInputEmpty ? View.GONE : View.VISIBLE);
|
||||
binding.tvSend.setVisibility(isInputEmpty ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
private void setupFunctionPanel() {
|
||||
strings = new ArrayList<>();
|
||||
strings.add("[委屈]");
|
||||
EmojiAdapter mEmojiAdapter = new EmojiAdapter(strings);
|
||||
mEmojiAdapter.setOnItemClickListener(this);
|
||||
binding.rvEmoji.setAdapter(mEmojiAdapter);
|
||||
|
||||
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 7);
|
||||
binding.rvEmoji.setLayoutManager(gridLayoutManager);
|
||||
|
||||
List<String> strings1 = new ArrayList<>();
|
||||
strings1.add("haha");
|
||||
strings1.add("haha");
|
||||
strings1.add("haha");
|
||||
strings1.add("haha");
|
||||
strings1.add("haha");
|
||||
|
||||
FunctionPanelAdapter functionPanelAdapter = new FunctionPanelAdapter(strings1, this);
|
||||
binding.gvFunctionPanel.setAdapter(functionPanelAdapter);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(int position) {
|
||||
runOnUiThread(() -> {
|
||||
Editable editable = binding.etInput.getText();
|
||||
// 获取当前光标位置
|
||||
int index = binding.etInput.getSelectionStart();
|
||||
// 获取将要插入的表情符号
|
||||
String emoji = strings.get(position);
|
||||
// 使用 ImageSpanUtil 插入表情符号
|
||||
ImageSpanUtil.INSTANCE.insertEmoji(mContext, editable, (int) binding.etInput.getTextSize(), emoji, index);
|
||||
// 设置新的光标位置
|
||||
binding.etInput.setSelection(index + emoji.length());
|
||||
});
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
setupFunctionPanel();
|
||||
setRecycleView();
|
||||
if (contactId != null) {
|
||||
binding.tvContactName.setText(contactId);
|
||||
}
|
||||
setPanel();
|
||||
}
|
||||
|
||||
private void setPanel() {
|
||||
ViewGroup.LayoutParams layoutParams = binding.clBottomPanel.getLayoutParams();
|
||||
layoutParams.height = softKeyboardHeight;
|
||||
binding.clBottomPanel.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
|
||||
private void initData() {
|
||||
messagesBox = ObjectBox.INSTANCE.get().boxFor(Messages.class);
|
||||
username = UserManager.INSTANCE.getUsername();
|
||||
Intent intent = getIntent();
|
||||
friendId = intent.getStringExtra("friendId");
|
||||
softKeyboardHeight = ConstantsUtil.Companion.getKeyboardHeight();
|
||||
}
|
||||
|
||||
|
||||
private void setRecycleView() {
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
|
||||
layoutManager.setReverseLayout(true);
|
||||
binding.recycleChatList.setLayoutManager(layoutManager);
|
||||
chatAdapter = new ChatAdapter(this, messagesList);
|
||||
binding.recycleChatList.setAdapter(chatAdapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初次进入进行加载历史数据
|
||||
*/
|
||||
private void firstLoadData() {
|
||||
List<Messages> messages = queryData(0, LIMIT + 1);
|
||||
if (!messages.isEmpty()) {
|
||||
int size = messages.size();
|
||||
if (size >= LIMIT) {
|
||||
isHasHistory = true;
|
||||
}
|
||||
if (isHasHistory) {
|
||||
List<Messages> messages1 = messages.subList(0, (int) LIMIT);
|
||||
messagesList.addAll(messages1);
|
||||
tempIndex = messagesList.get(messagesList.size() - 1).getMsgLocalId();
|
||||
} else {
|
||||
messagesList.addAll(messages);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void loadMoreMessages() {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
isLoading = true;
|
||||
|
||||
List<Messages> newMessages = queryDataById(tempIndex, LIMIT + 1);
|
||||
int size = newMessages.size();
|
||||
tempIndex = newMessages.get(size - 1).getMsgLocalId();
|
||||
isHasHistory = size > LIMIT;
|
||||
|
||||
if (!newMessages.isEmpty()) {
|
||||
List<Messages> messages;
|
||||
if (isHasHistory) {
|
||||
messages = newMessages.subList(1, (int) LIMIT + 1);
|
||||
} else {
|
||||
messages = newMessages.subList(1, newMessages.size());
|
||||
}
|
||||
|
||||
int messagesSize = messagesList.size();
|
||||
|
||||
messagesList.addAll(messagesSize, messages);
|
||||
chatAdapter.notifyItemRangeInserted(messagesSize, newMessages.size());
|
||||
}
|
||||
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
|
||||
private void observeLiveData() {
|
||||
if (webSocketService == null) {
|
||||
return;
|
||||
}
|
||||
webSocketService.getLiveData().observe(this, new Observer<Messages>() {
|
||||
@Override
|
||||
public void onChanged(Messages messages) {
|
||||
if (!"ack".equals(messages.getType()) && !username.equals(messages.getSenderId())) {
|
||||
if (messages.getMsgLocalId() != 0) {
|
||||
return;
|
||||
}
|
||||
messagesList.addFirst(messages);
|
||||
runOnUiThread(() -> {
|
||||
chatAdapter.notifyItemInserted(0);
|
||||
binding.recycleChatList.smoothScrollToPosition(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendMessage() {
|
||||
String message = binding.etInput.getText().toString().trim();
|
||||
|
||||
Messages messages = new Messages(
|
||||
0,
|
||||
0,
|
||||
message,
|
||||
System.currentTimeMillis(),
|
||||
"normal",
|
||||
username,
|
||||
friendId,
|
||||
"0",
|
||||
true
|
||||
);
|
||||
|
||||
addData(messages);
|
||||
|
||||
Long id = messagesBox.put(messages);
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
JSONObject jsonObject2 = new JSONObject();
|
||||
try {
|
||||
jsonObject2.put("receiverId", friendId);
|
||||
jsonObject2.put("content", message);
|
||||
jsonObject2.put("msgLocalId", id);
|
||||
jsonObject.put("type", "single");
|
||||
jsonObject.put("body", jsonObject2);
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
webSocketService.sendMessage(jsonObject.toString(), id);
|
||||
|
||||
|
||||
binding.etInput.setText("");
|
||||
}
|
||||
|
||||
private void addData(Messages messages) {
|
||||
messagesList.addFirst(messages);
|
||||
chatAdapter.notifyItemInserted(0);
|
||||
binding.recycleChatList.smoothScrollToPosition(0);
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private List<Messages> queryData(long offset, long limit) {
|
||||
Query<Messages> query = messagesBox
|
||||
.query(Messages_.takerId.equal(friendId))
|
||||
.order(Messages_.timestamp, QueryBuilder.DESCENDING)
|
||||
.build();
|
||||
return query.find(offset, limit);
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private List<Messages> queryDataById(long msgLocalId, long limit) {
|
||||
Query<Messages> query = messagesBox
|
||||
.query(Messages_.takerId.equal(friendId))
|
||||
.lessOrEqual(Messages_.msgLocalId, msgLocalId)
|
||||
.order(Messages_.timestamp, QueryBuilder.DESCENDING)
|
||||
.build();
|
||||
int offset = 0;
|
||||
return query.find(offset, limit);
|
||||
}
|
||||
|
||||
private void bindWebSocketService() {
|
||||
Intent serviceIntent = new Intent(this, WebSocketService.class);
|
||||
bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (isBound) {
|
||||
unbindService(connection);
|
||||
isBound = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package com.kaixed.kchat.view.activity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import com.kaixed.kchat.R;
|
||||
import com.kaixed.kchat.databinding.ActivityChatDetailBinding;
|
||||
|
||||
public class ChatDetailActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityChatDetailBinding binding;
|
||||
private final Context mContext = this;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
binding = ActivityChatDetailBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
binding.ifvAvatar.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(mContext, ContactsDetailActivity.class);
|
||||
intent.putExtra("friendId", "kaixed");
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
binding.ivBack.setOnClickListener(v -> {
|
||||
finish();
|
||||
});
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package com.kaixed.kchat.view.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.kaixed.kchat.databinding.ActivityContactsDetailBinding;
|
||||
|
||||
public class ContactsDetailActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityContactsDetailBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
binding = ActivityContactsDetailBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
binding.ivBack.setOnClickListener(v -> {
|
||||
finish();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
package com.kaixed.kchat.view.activity;
|
||||
|
||||
import static com.kaixed.kchat.utils.Constants.MMKV_COMMON_DATA;
|
||||
import static com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION;
|
||||
import static com.kaixed.kchat.utils.Constants.USER_LOGIN_STATUS;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.kaixed.kchat.R;
|
||||
import com.kaixed.kchat.databinding.ActivityLoginBinding;
|
||||
import com.kaixed.kchat.utils.DensityUtil;
|
||||
import com.kaixed.kchat.utils.DrawableUtil;
|
||||
import com.kaixed.kchat.viewmodel.LoginViewModel;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
|
||||
/**
|
||||
* @author kaixed
|
||||
*/
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
|
||||
private LoginViewModel mViewModel;
|
||||
private ActivityLoginBinding binding;
|
||||
private MMKV mmkv;
|
||||
private final Context mContext = this;
|
||||
|
||||
private int previousKeyboardHeight = 0;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
binding = ActivityLoginBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
|
||||
mmkv = MMKV.mmkvWithID(MMKV_USER_SESSION);
|
||||
|
||||
if (mmkv.decodeBool(USER_LOGIN_STATUS)) {
|
||||
navigateToMain();
|
||||
}
|
||||
|
||||
|
||||
binding.etUsername.addTextChangedListener(textWatcher);
|
||||
binding.etPassword.addTextChangedListener(textWatcher);
|
||||
|
||||
|
||||
mViewModel = new ViewModelProvider(this).get(LoginViewModel.class);
|
||||
|
||||
|
||||
binding.tvLogin.setOnClickListener(v -> {
|
||||
String username = binding.etUsername.getText().toString().trim();
|
||||
String password = binding.etPassword.getText().toString().trim();
|
||||
|
||||
if (username.isEmpty() || password.isEmpty()) {
|
||||
Toast.makeText(this, "请输入用户名或密码", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
mViewModel.login(username, password).observe(this, loginResult -> {
|
||||
|
||||
if (loginResult == null) {
|
||||
runOnUiThread(() -> {
|
||||
Toast.makeText(LoginActivity.this, "登录异常", Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
} else if ("200".equals(loginResult.getCode())) {
|
||||
mmkv.encode("username", loginResult.getData().getUsername());
|
||||
mmkv.encode("nickname", loginResult.getData().getNickname());
|
||||
mmkv.encode("nickname", loginResult.getData().getAvatarUrl());
|
||||
mmkv.encode("userLoginStatus", true);
|
||||
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
|
||||
navigateToMain();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
getKeyboardHeight();
|
||||
}
|
||||
|
||||
private void getKeyboardHeight() {
|
||||
final FrameLayout rootLayout = findViewById(android.R.id.content);
|
||||
|
||||
rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
||||
Rect rect = new Rect();
|
||||
rootLayout.getWindowVisibleDisplayFrame(rect);
|
||||
|
||||
int screenHeight = rootLayout.getRootView().getHeight();
|
||||
int keypadHeight = screenHeight - rect.bottom;
|
||||
|
||||
// 通过15%的屏幕高度差来判断是否为键盘
|
||||
if (keypadHeight > (screenHeight * 0.15)) {
|
||||
// 键盘弹出
|
||||
if (keypadHeight != previousKeyboardHeight) {
|
||||
previousKeyboardHeight = keypadHeight;
|
||||
|
||||
MMKV kv = MMKV.mmkvWithID(MMKV_COMMON_DATA);
|
||||
kv.encode("keyboardHeight", keypadHeight);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
TextWatcher textWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
if (s.length() != 0) {
|
||||
if (binding.etUsername.getText().length() != 0 && binding.etPassword.getText().length() != 0) {
|
||||
binding.tvLogin.setTextColor(ContextCompat.getColor(mContext, R.color.white));
|
||||
binding.tvLogin.setBackground(DrawableUtil.INSTANCE.createDrawable(ContextCompat.getColor(mContext, R.color.green), DensityUtil.INSTANCE.dpToPx(mContext, 8)));
|
||||
} else {
|
||||
binding.tvLogin.setTextColor(Color.parseColor("#B4B4B4"));
|
||||
binding.tvLogin.setBackground(DrawableUtil.INSTANCE.createDrawable(Color.parseColor("#E1E1E1"), DensityUtil.INSTANCE.dpToPx(mContext, 8)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private void navigateToMain() {
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
@ -1,350 +0,0 @@
|
||||
package com.kaixed.kchat.view.activity;
|
||||
|
||||
import static com.kaixed.kchat.utils.Constants.MMKV_COMMON_DATA;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.kaixed.kchat.R;
|
||||
import com.kaixed.kchat.database.ObjectBox;
|
||||
import com.kaixed.kchat.database.entity.ChatLists;
|
||||
import com.kaixed.kchat.database.entity.ChatLists_;
|
||||
import com.kaixed.kchat.database.entity.Messages;
|
||||
import com.kaixed.kchat.databinding.ActivityMainBinding;
|
||||
import com.kaixed.kchat.model.HomeItem;
|
||||
import com.kaixed.kchat.service.WebSocketService;
|
||||
import com.kaixed.kchat.view.adapter.ChatListAdapter;
|
||||
import com.kaixed.kchat.view.adapter.MyGridAdapter;
|
||||
import com.kaixed.kchat.view.i.OnChatListItemClickListener;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.objectbox.Box;
|
||||
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/5/26 10:02
|
||||
*/
|
||||
public class MainActivity extends AppCompatActivity implements OnChatListItemClickListener {
|
||||
private ActivityMainBinding binding;
|
||||
private List<ChatLists> chatLists = new ArrayList<>();
|
||||
private ChatListAdapter chatListAdapter;
|
||||
private Box<ChatLists> chatListsBox;
|
||||
private Box<Messages> messagesBox;
|
||||
private String updateUsername;
|
||||
private final List<HomeItem> items = new ArrayList<>();
|
||||
private WebSocketService webSocketService;
|
||||
private boolean isFlipped = false;
|
||||
private boolean isBound = false;
|
||||
private final Context mContext = this;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
chatListsBox = ObjectBox.INSTANCE.get().boxFor(ChatLists.class);
|
||||
messagesBox = ObjectBox.INSTANCE.get().boxFor(Messages.class);
|
||||
|
||||
initView();
|
||||
|
||||
startService();
|
||||
|
||||
setOnClick();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void setOnClick() {
|
||||
binding.ivMessage.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(this, MessageActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
binding.ivSearch.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(mContext, SearchActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
binding.ifvAvatar.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(mContext, ProfileActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void flipImage(View v) {
|
||||
ObjectAnimator flipAnimation;
|
||||
if (isFlipped) {
|
||||
flipAnimation = ObjectAnimator.ofFloat(v, "rotationX", 180F, 0F);
|
||||
} else {
|
||||
flipAnimation = ObjectAnimator.ofFloat(v, "rotationX", 0F, 180F);
|
||||
}
|
||||
flipAnimation.setDuration(0);
|
||||
flipAnimation.start();
|
||||
isFlipped = !isFlipped;
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public void notifyData() {
|
||||
chatListAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void startService() {
|
||||
Intent intent = new Intent(MainActivity.this, WebSocketService.class);
|
||||
startService(intent);
|
||||
}
|
||||
|
||||
private void observeLiveData() {
|
||||
if (webSocketService == null) {
|
||||
return;
|
||||
}
|
||||
webSocketService.getLiveData().observe(this, messages -> {
|
||||
if ("0".equals(messages.getType())) {
|
||||
processMessage(messages);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void processMessage(Messages messages) {
|
||||
String talkerId = messages.getTakerId();
|
||||
String content = messages.getContent();
|
||||
Long timestamp = messages.getTimestamp();
|
||||
|
||||
ChatLists chatList = createChatList(talkerId, content, timestamp);
|
||||
|
||||
int index = findChatListIndex(talkerId);
|
||||
if (index == -1) {
|
||||
chatLists.add(chatList);
|
||||
} else {
|
||||
updateChatList(chatLists.get(index), content, timestamp);
|
||||
}
|
||||
|
||||
runOnUiThread(this::notifyData);
|
||||
isInDb(chatList);
|
||||
}
|
||||
|
||||
private ChatLists createChatList(String talkerId, String content, Long timestamp) {
|
||||
return new ChatLists(
|
||||
0L,
|
||||
talkerId,
|
||||
talkerId,
|
||||
"1",
|
||||
content,
|
||||
timestamp,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
private int findChatListIndex(String talkerId) {
|
||||
for (int i = 0; i < chatLists.size(); i++) {
|
||||
if (chatLists.get(i).getTalkerId().equals(talkerId)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void updateChatList(ChatLists chatList, String content, Long timestamp) {
|
||||
chatList.setLastContent(content);
|
||||
chatList.setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
|
||||
private void isInDb(ChatLists chatList) {
|
||||
// 查询数据库中是否已经存在该talkerId的记录
|
||||
ChatLists existingChatList = chatListsBox
|
||||
.query(ChatLists_.talkerId.equal(chatList.getTalkerId()))
|
||||
.build()
|
||||
.findFirst();
|
||||
|
||||
if (existingChatList != null) {
|
||||
// 如果存在,更新内容和时间戳
|
||||
existingChatList.setLastContent(chatList.getLastContent());
|
||||
existingChatList.setTimestamp(chatList.getTimestamp());
|
||||
chatListsBox.put(existingChatList);
|
||||
} else {
|
||||
// 如果不存在,添加新的记录
|
||||
chatListsBox.put(chatList);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChatLists() {
|
||||
MMKV kv = MMKV.mmkvWithID(MMKV_COMMON_DATA);
|
||||
long msgLocalId = kv.getLong("msgLocalId", -1L);
|
||||
if (msgLocalId != -1) {
|
||||
Messages messages = messagesBox.get(msgLocalId);
|
||||
processMessage(messages);
|
||||
kv.remove("msgLocalId");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ServiceConnection connection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
WebSocketService.LocalBinder binder = (WebSocketService.LocalBinder) service;
|
||||
webSocketService = binder.getService();
|
||||
isBound = true;
|
||||
observeLiveData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName arg0) {
|
||||
isBound = false;
|
||||
}
|
||||
};
|
||||
|
||||
private void initView() {
|
||||
setupGridView();
|
||||
setupRecycleView();
|
||||
setupMoreOptionsToggle();
|
||||
}
|
||||
|
||||
private void setupMoreOptionsToggle() {
|
||||
binding.ivMore.setOnClickListener(v -> {
|
||||
flipImage(v);
|
||||
int gridViewHeight = binding.gridView.getHeight();
|
||||
int startAt;
|
||||
int endAt;
|
||||
if (isFlipped) {
|
||||
binding.gridView.setVisibility(View.VISIBLE);
|
||||
startAt = 0;
|
||||
endAt = gridViewHeight;
|
||||
} else {
|
||||
startAt = gridViewHeight;
|
||||
endAt = 0;
|
||||
}
|
||||
createAnimator(startAt, endAt).start();
|
||||
});
|
||||
}
|
||||
|
||||
private ObjectAnimator createAnimator(int start, int end) {
|
||||
ObjectAnimator animator = ObjectAnimator.ofInt(binding.linearlayout, "top", start, end);
|
||||
long duration = 500;
|
||||
animator.setDuration(duration);
|
||||
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
super.onAnimationStart(animation);
|
||||
binding.ivMore.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
if (!isFlipped) {
|
||||
binding.gridView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
binding.ivMore.setEnabled(true);
|
||||
}
|
||||
});
|
||||
return animator;
|
||||
}
|
||||
|
||||
private void setupRecycleView() {
|
||||
if (chatListsBox.getAll() != null) {
|
||||
chatLists = chatListsBox.getAll();
|
||||
}
|
||||
chatListAdapter = new ChatListAdapter(chatLists, this);
|
||||
binding.recycleChatList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
|
||||
binding.recycleChatList.setAdapter(chatListAdapter);
|
||||
chatListAdapter.setItemListener(this);
|
||||
}
|
||||
|
||||
private void setupGridView() {
|
||||
initMenuData();
|
||||
MyGridAdapter myGridAdapter = new MyGridAdapter(this, items);
|
||||
binding.gridView.setAdapter(myGridAdapter);
|
||||
|
||||
binding.gridView.setSelector(new ColorDrawable(Color.TRANSPARENT));
|
||||
|
||||
binding.gridView.setOnItemClickListener((parent, view, position, id) -> {
|
||||
switch (position) {
|
||||
case 0:
|
||||
break;
|
||||
case 2:
|
||||
Intent intent = new Intent(MainActivity.this, AddFriendsActivity.class);
|
||||
startActivity(intent);
|
||||
break;
|
||||
case 4:
|
||||
Intent intent2 = new Intent(MainActivity.this, ContactUpdatesActivity.class);
|
||||
startActivity(intent2);
|
||||
break;
|
||||
case 6:
|
||||
Intent intent1 = new Intent(MainActivity.this, FriendListActivity.class);
|
||||
startActivity(intent1);
|
||||
break;
|
||||
default:
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void initMenuData() {
|
||||
items.add(new HomeItem("更换背景", true, R.drawable.ic_switch));
|
||||
items.add(new HomeItem("创建群聊", false, R.drawable.ic_troop));
|
||||
items.add(new HomeItem("新朋友", true, R.drawable.ic_friend));
|
||||
items.add(new HomeItem("夜间模式", true, R.drawable.ic_night));
|
||||
items.add(new HomeItem("好友动态", false, R.drawable.ic_qzone));
|
||||
items.add(new HomeItem("扫一扫", false, R.drawable.ic_scan));
|
||||
items.add(new HomeItem("通讯录", true, R.drawable.ic_clock_in));
|
||||
items.add(new HomeItem("关闭应用", false, R.drawable.ic_exit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(String username) {
|
||||
this.updateUsername = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestart() {
|
||||
super.onRestart();
|
||||
updateChatLists();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
Intent intent = new Intent(this, WebSocketService.class);
|
||||
bindService(intent, connection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (isBound) {
|
||||
unbindService(connection);
|
||||
isBound = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
Intent intent = new Intent(MainActivity.this, WebSocketService.class);
|
||||
stopService(intent);
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package com.kaixed.kchat.view.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.databinding.ActivityMain2Binding
|
||||
|
||||
class MainActivity2 : AppCompatActivity() {
|
||||
private lateinit var binding:ActivityMain2Binding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = ActivityMain2Binding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
binding.btn.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
package com.kaixed.kchat.view.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.widget.EdgeEffect;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.kaixed.kchat.databinding.ActivitySearchBinding;
|
||||
import com.kaixed.kchat.model.search.SearchItem;
|
||||
import com.kaixed.kchat.view.adapter.SearchResultAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class SearchActivity extends AppCompatActivity {
|
||||
|
||||
private ActivitySearchBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
binding = ActivitySearchBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
setOnClickListener();
|
||||
|
||||
setupRecycleView();
|
||||
|
||||
|
||||
binding.etSearch.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
setupViewVisibility(s.length());
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void setupViewVisibility(int length) {
|
||||
|
||||
|
||||
|
||||
binding.rvSearchResult.setVisibility(length > 0 ? View.VISIBLE : View.INVISIBLE);
|
||||
binding.tvPageSetting.setVisibility(length > 0 ? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
|
||||
private void setupRecycleView() {
|
||||
binding.rvSearchResult.setEdgeEffectFactory(new RecyclerView.EdgeEffectFactory() {
|
||||
@NonNull
|
||||
@Override
|
||||
protected EdgeEffect createEdgeEffect(@NonNull RecyclerView view, int direction) {
|
||||
return new EdgeEffect(view.getContext()) {
|
||||
@Override
|
||||
public void onPull(float deltaDistance, float displacement) {
|
||||
// 禁止下拉时的效果
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRelease() {
|
||||
// 禁止释放时的效果
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAbsorb(int velocity) {
|
||||
// 禁止吸收时的效果
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
SearchResultAdapter searchResultAdapter = getSearchResultAdapter();
|
||||
binding.rvSearchResult.setAdapter(searchResultAdapter);
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
|
||||
binding.rvSearchResult.setLayoutManager(layoutManager);
|
||||
}
|
||||
|
||||
private void setOnClickListener() {
|
||||
binding.tvCancel.setOnClickListener(v -> {
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
private static @NonNull SearchResultAdapter getSearchResultAdapter() {
|
||||
SearchItem contactItem = new SearchItem("sa", "kaixed", "as", false, "联系人");
|
||||
SearchItem chatHistoryItem = new SearchItem("sa", "kaixed", "as", false, "群聊");
|
||||
SearchItem groupItem = new SearchItem("sa", "kaixed", "as", false, "聊天记录");
|
||||
|
||||
SearchItem searchItem1 = new SearchItem("sa", "kaixed", "as", true, "联系人");
|
||||
SearchItem searchItem2 = new SearchItem("sa", "kaixed", "as", true, "群聊");
|
||||
SearchItem searchItem3 = new SearchItem("sa", "kaixed", "as", true, "聊天记录");
|
||||
|
||||
|
||||
List<Object> objects = new ArrayList<>(12);
|
||||
objects.add("联系人");
|
||||
objects.add(contactItem);
|
||||
objects.add(contactItem);
|
||||
objects.add(searchItem1);
|
||||
|
||||
objects.add("群聊");
|
||||
objects.add(groupItem);
|
||||
objects.add(groupItem);
|
||||
objects.add(searchItem2);
|
||||
|
||||
objects.add("聊天记录");
|
||||
objects.add(chatHistoryItem);
|
||||
objects.add(chatHistoryItem);
|
||||
objects.add(searchItem3);
|
||||
|
||||
|
||||
SearchResultAdapter searchResultAdapter = new SearchResultAdapter(objects);
|
||||
return searchResultAdapter;
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
package com.kaixed.kchat.view.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.kaixed.kchat.R;
|
||||
import com.kaixed.kchat.database.entity.ChatLists;
|
||||
import com.kaixed.kchat.view.activity.ChatActivity;
|
||||
import com.kaixed.kchat.view.i.OnChatListItemClickListener;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/5/20 10:13
|
||||
*/
|
||||
public class ChatListAdapter extends RecyclerView.Adapter<ChatListAdapter.MyViewHolder> {
|
||||
private final List<ChatLists> chatLists;
|
||||
private final Context mContext;
|
||||
|
||||
private OnChatListItemClickListener onChatListItemClickListener;
|
||||
|
||||
public void setItemListener(OnChatListItemClickListener i) {
|
||||
this.onChatListItemClickListener = i;
|
||||
}
|
||||
|
||||
public ChatListAdapter(List<ChatLists> chatLists, Context mContext) {
|
||||
this.chatLists = chatLists;
|
||||
this.mContext = mContext;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
View view = inflater.inflate(R.layout.chat_main_item, parent, false);
|
||||
return new MyViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull MyViewHolder holder, @SuppressLint("RecyclerView") int position) {
|
||||
holder.bindData(chatLists.get(position));
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
onChatListItemClickListener.onItemClick(chatLists.get(position).getTalkerId());
|
||||
Intent intent = new Intent(mContext, ChatActivity.class);
|
||||
intent.putExtra("friendId", chatLists.get(position).getTalkerId());
|
||||
mContext.startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return chatLists.isEmpty() ? 0 : chatLists.size();
|
||||
}
|
||||
|
||||
public static class MyViewHolder extends RecyclerView.ViewHolder {
|
||||
private final TextView mTvContent;
|
||||
private final TextView mTvNickname;
|
||||
private final TextView mTvTimestamp;
|
||||
|
||||
public MyViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
mTvContent = itemView.findViewById(R.id.tv_content);
|
||||
mTvNickname = itemView.findViewById(R.id.tv_nickname);
|
||||
mTvTimestamp = itemView.findViewById(R.id.tv_timestamp);
|
||||
}
|
||||
|
||||
private void bindData(ChatLists chatList) {
|
||||
mTvContent.setText(chatList.getLastContent());
|
||||
mTvNickname.setText(chatList.getTalkerId());
|
||||
|
||||
Instant instant = Instant.ofEpochMilli(chatList.getTimestamp());
|
||||
// 将时间戳转换为当前系统默认时区的时间
|
||||
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
|
||||
// 格式化成小时:分钟格式
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
|
||||
String formattedTime = zdt.format(formatter);
|
||||
mTvTimestamp.setText(formattedTime);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package com.kaixed.kchat.view.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
public class CustomLayoutManager extends LinearLayoutManager {
|
||||
|
||||
public CustomLayoutManager(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollToPositionWithOffset(int position, int offset) {
|
||||
if (getChildCount() == 0 || getItemCount() == 0) {
|
||||
super.scrollToPositionWithOffset(position, offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if (position < getItemCount()) {
|
||||
View firstVisibleView = getChildAt(0);
|
||||
int firstItemPosition = getPosition(firstVisibleView);
|
||||
int lastItemPosition = getPosition(getChildAt(getChildCount() - 1));
|
||||
|
||||
if (lastItemPosition - firstItemPosition + 1 < getChildCount()) {
|
||||
// 如果列表不满一屏,则将列表置于顶部
|
||||
super.scrollToPositionWithOffset(position, 0);
|
||||
} else {
|
||||
// 如果列表满一屏,则将最新的消息显示在底部
|
||||
super.scrollToPositionWithOffset(position, Integer.MAX_VALUE);
|
||||
}
|
||||
} else {
|
||||
super.scrollToPositionWithOffset(position, offset);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package com.kaixed.kchat.view.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.helper.widget.Layer;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.kaixed.kchat.R;
|
||||
import com.kaixed.kchat.view.i.OnItemClickListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/5/30 16:51
|
||||
*/
|
||||
public class EmojiAdapter extends RecyclerView.Adapter<EmojiAdapter.MyViewHolder> {
|
||||
|
||||
private final List<String> strings;
|
||||
private OnItemClickListener onItemClickListener;
|
||||
|
||||
public EmojiAdapter(List<String> strings) {
|
||||
this.strings = strings;
|
||||
}
|
||||
|
||||
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
|
||||
this.onItemClickListener = onItemClickListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.emoji_recycle_item, parent, false);
|
||||
|
||||
return new MyViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull MyViewHolder holder, @SuppressLint("RecyclerView") int position) {
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
onItemClickListener.onItemClick(position);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return strings.isEmpty() ? 0 : strings.size();
|
||||
}
|
||||
|
||||
public static class MyViewHolder extends RecyclerView.ViewHolder {
|
||||
public MyViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
package com.kaixed.kchat.view.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
|
||||
import com.kaixed.kchat.databinding.FunctionGridItemBinding;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/8/16 20:53
|
||||
*/
|
||||
public class FunctionPanelAdapter extends BaseAdapter {
|
||||
|
||||
private List<String> strings;
|
||||
private final Context context;
|
||||
|
||||
public FunctionPanelAdapter(List<String> strings, Context context) {
|
||||
this.strings = strings;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return strings == null ? 0 : strings.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
FunctionGridItemBinding binding;
|
||||
if (convertView == null) {
|
||||
binding = FunctionGridItemBinding.inflate(LayoutInflater.from(context), parent, false);
|
||||
convertView = binding.getRoot();
|
||||
convertView.setTag(binding);
|
||||
} else {
|
||||
binding = (FunctionGridItemBinding) convertView.getTag();
|
||||
}
|
||||
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
package com.kaixed.kchat.view.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kaixed.kchat.R;
|
||||
import com.kaixed.kchat.model.HomeItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author kaixed
|
||||
*/
|
||||
public class MyGridAdapter extends BaseAdapter {
|
||||
private Context context;
|
||||
private List<HomeItem> items;
|
||||
|
||||
public MyGridAdapter(Context context, List<HomeItem> items) {
|
||||
this.context = context;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return items.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(context).inflate(R.layout.item_grid, parent, false);
|
||||
}
|
||||
|
||||
|
||||
HomeItem item = items.get(position);
|
||||
|
||||
TextView title = convertView.findViewById(R.id.textview);
|
||||
ImageView image = convertView.findViewById(R.id.imageview);
|
||||
ImageView imageView = convertView.findViewById(R.id.iv_more);
|
||||
|
||||
if (item.getMore()) {
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
imageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
title.setText(item.getName());
|
||||
image.setImageResource(item.getImg());
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
package com.kaixed.kchat.view.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.kaixed.kchat.databinding.SearchRecycleItemContactsBinding;
|
||||
import com.kaixed.kchat.databinding.SearchRecycleItemDetailsBinding;
|
||||
import com.kaixed.kchat.model.search.SearchItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/8/12 20:25
|
||||
*/
|
||||
public class SearchResultAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private final List<Object> displayedItems;
|
||||
|
||||
public SearchResultAdapter(List<Object> objects) {
|
||||
this.displayedItems = objects;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
if (viewType == 0) {
|
||||
SearchRecycleItemContactsBinding binding = SearchRecycleItemContactsBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new GroupViewHolder(binding);
|
||||
} else {
|
||||
SearchRecycleItemDetailsBinding binding = SearchRecycleItemDetailsBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()), parent, false);
|
||||
|
||||
return new DetailsViewHolder(binding);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
if (holder.getItemViewType() == 0) {
|
||||
GroupViewHolder groupViewHolder = (GroupViewHolder) holder;
|
||||
groupViewHolder.binding.tvName.setText(String.valueOf(displayedItems.get(position)));
|
||||
} else {
|
||||
DetailsViewHolder detailsViewHolder = (DetailsViewHolder) holder;
|
||||
if (displayedItems.get(position) instanceof SearchItem item) {
|
||||
detailsViewHolder.binding.tvName.setText((item.getName()));
|
||||
if ((item.getHasMore())) {
|
||||
detailsViewHolder.binding.tvHasMore.setText("更多" + item.getType());
|
||||
detailsViewHolder.binding.rlHasMore.setVisibility(View.VISIBLE);
|
||||
detailsViewHolder.binding.view.setVisibility(View.INVISIBLE);
|
||||
|
||||
} else {
|
||||
detailsViewHolder.binding.rlHasMore.setVisibility(View.GONE);
|
||||
detailsViewHolder.binding.view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (displayedItems.get(position) instanceof String) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return displayedItems == null ? 0 : displayedItems.size();
|
||||
}
|
||||
|
||||
// ViewHolder for Group Items
|
||||
static class GroupViewHolder extends RecyclerView.ViewHolder {
|
||||
private final SearchRecycleItemContactsBinding binding;
|
||||
|
||||
public GroupViewHolder(SearchRecycleItemContactsBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ViewHolder for Detail Items
|
||||
static class DetailsViewHolder extends RecyclerView.ViewHolder {
|
||||
private final SearchRecycleItemDetailsBinding binding;
|
||||
|
||||
public DetailsViewHolder(SearchRecycleItemDetailsBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package com.kaixed.kchat.view.i;
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/6/16 19:28
|
||||
*/
|
||||
public interface OnChatListItemClickListener {
|
||||
void onItemClick(String username);
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package com.kaixed.kchat.view.i;
|
||||
|
||||
/**
|
||||
* @author hui
|
||||
*/
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(int position);
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="#e5e5e5"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.AddFriendsActivity">
|
||||
tools:context=".ui.activity.AddFriendsActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
|
@ -8,7 +8,7 @@
|
||||
android:background="@color/white"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical"
|
||||
tools:context=".view.activity.ApplyAddFriendActivity">
|
||||
tools:context=".ui.activity.ApplyAddFriendActivity">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
@ -20,7 +20,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomTitleBar
|
||||
<com.kaixed.kchat.ui.widget.CustomTitleBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:titleName="申请添加朋友" />
|
||||
|
@ -6,9 +6,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.ApplyFriendsDetailActivity">
|
||||
tools:context=".ui.activity.ApplyFriendsDetailActivity">
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomTitleBar
|
||||
<com.kaixed.kchat.ui.widget.CustomTitleBar
|
||||
android:id="@+id/ctl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -8,7 +8,7 @@
|
||||
android:background="@color/white"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical"
|
||||
tools:context=".view.activity.ApproveContactRequestActivity">
|
||||
tools:context=".ui.activity.ApproveContactRequestActivity">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
@ -20,7 +20,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomTitleBar
|
||||
<com.kaixed.kchat.ui.widget.CustomTitleBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:titleName="通过朋友验证" />
|
||||
|
@ -6,9 +6,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.ApproveDetailActivity">
|
||||
tools:context=".ui.activity.ApproveDetailActivity">
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomTitleBar
|
||||
<com.kaixed.kchat.ui.widget.CustomTitleBar
|
||||
android:id="@+id/ctl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -6,7 +6,14 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.ChatDetailActivity">
|
||||
tools:context=".ui.activity.ChatDetailActivity">
|
||||
|
||||
<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="聊天信息" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
@ -18,28 +25,6 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
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:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="7dp"
|
||||
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" />
|
||||
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/ifv_avatar"
|
||||
@ -49,7 +34,7 @@
|
||||
android:layout_marginTop="15dp"
|
||||
android:src="@drawable/ic_avatar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_back"
|
||||
app:layout_constraintTop_toBottomOf="@id/ctb"
|
||||
app:round="8dp" />
|
||||
|
||||
<TextView
|
||||
|
@ -6,7 +6,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.ContactUpdatesActivity">
|
||||
tools:context=".ui.activity.ContactUpdatesActivity">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/ifv_avatar"
|
||||
|
@ -6,38 +6,15 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.ContactsDetailActivity">
|
||||
tools:context=".ui.activity.ContactsDetailActivity">
|
||||
|
||||
<!-- <com.kaixed.kchat.utils.CustomToolbarView-->
|
||||
<!-- app:customTitle="哈哈"-->
|
||||
<!-- android:id="@+id/custom_toolbar"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:background="?attr/colorPrimary"-->
|
||||
<!-- tools:ignore="MissingConstraints"-->
|
||||
<!-- app:layout_constraintStart_toStartOf="parent"-->
|
||||
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
||||
<!-- />-->
|
||||
<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:titleIcon="@drawable/ic_setting" />
|
||||
|
||||
<ImageView
|
||||
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" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_setting"
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="22dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:src="@drawable/ic_setting"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_back"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_back" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/ifv_avatar"
|
||||
@ -47,7 +24,7 @@
|
||||
android:layout_marginTop="15dp"
|
||||
android:src="@drawable/ic_avatar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_back"
|
||||
app:layout_constraintTop_toBottomOf="@id/ctb"
|
||||
app:round="8dp" />
|
||||
|
||||
<TextView
|
||||
|
@ -7,7 +7,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="#E5E5E5"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.DataSettingActivity">
|
||||
tools:context=".ui.activity.DataSettingActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
|
@ -7,9 +7,9 @@
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical"
|
||||
tools:context=".view.activity.FriendListActivity">
|
||||
tools:context=".ui.activity.FriendListActivity">
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomTitleBar
|
||||
<com.kaixed.kchat.ui.widget.CustomTitleBar
|
||||
android:id="@+id/ctb"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -7,7 +7,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.LoginActivity">
|
||||
tools:context=".ui.activity.LoginActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
|
@ -1,14 +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"
|
||||
tools:context=".view.activity.MainActivity2">
|
||||
<Button
|
||||
android:id="@+id/btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -7,9 +7,9 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/gray"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.MessageActivity">
|
||||
tools:context=".ui.activity.MessageActivity">
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomTitleBar
|
||||
<com.kaixed.kchat.ui.widget.CustomTitleBar
|
||||
android:id="@+id/ctl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -6,7 +6,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.ProfileActivity">
|
||||
tools:context=".ui.activity.ProfileActivity">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_container"
|
||||
|
@ -8,9 +8,9 @@
|
||||
android:background="@color/gray"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical"
|
||||
tools:context=".view.activity.ProfileDetailActivity">
|
||||
tools:context=".ui.activity.ProfileDetailActivity">
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomTitleBar
|
||||
<com.kaixed.kchat.ui.widget.CustomTitleBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:titleName="个人信息" />
|
||||
@ -24,7 +24,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomItem
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:id="@+id/ci_avatar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -33,7 +33,7 @@
|
||||
app:itemIcon="@drawable/ic_avatar"
|
||||
app:itemName="头像" />
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomItem
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:id="@+id/ci_nickname"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -41,21 +41,21 @@
|
||||
app:itemDesc="糕菜菜"
|
||||
app:itemName="名字" />
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomItem
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:isTopDividerVisible="true"
|
||||
app:itemDesc="的八块腹肌,说你好棒"
|
||||
app:itemName="拍一拍" />
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomItem
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:isTopDividerVisible="true"
|
||||
app:itemDesc="kaixed"
|
||||
app:itemName="微信号" />
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomItem
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconSize="10dp"
|
||||
@ -63,26 +63,26 @@
|
||||
app:itemIcon="@drawable/icon_qrcode"
|
||||
app:itemName="二维码名片" />
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomItem
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:isTopDividerVisible="true"
|
||||
app:itemName="更多信息" />
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomItem
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:itemDesc="白月光与朱砂痣"
|
||||
app:itemName="来电铃声" />
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomItem
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:itemName="蔬菜豆" />
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomItem
|
||||
<com.kaixed.kchat.ui.widget.CustomItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
|
@ -7,7 +7,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="#e5e5e5"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.ProfileActivity">
|
||||
tools:context=".ui.activity.ProfileActivity">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/ifv_avatar"
|
||||
|
@ -7,7 +7,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="#ededed"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.SearchActivity">
|
||||
tools:context=".ui.activity.SearchActivity">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/cv_search"
|
||||
|
@ -7,7 +7,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="#ededed"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.SearchActivity">
|
||||
tools:context=".ui.activity.SearchActivity">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/cv_search"
|
||||
|
@ -7,9 +7,9 @@
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical"
|
||||
tools:context=".view.activity.ServiceDetailActivity">
|
||||
tools:context=".ui.activity.ServiceDetailActivity">
|
||||
|
||||
<com.kaixed.kchat.view.widget.CustomTitleBar
|
||||
<com.kaixed.kchat.ui.widget.CustomTitleBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
@ -6,7 +6,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.SetRemarkActivity">
|
||||
tools:context=".ui.activity.SetRemarkActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
|
@ -7,7 +7,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/gray"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".view.activity.SettingActivity">
|
||||
tools:context=".ui.activity.SettingActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
|
Loading…
Reference in New Issue
Block a user