消息状态

This commit is contained in:
itgaojian 2024-11-01 17:54:23 +08:00
parent ae61b72fc3
commit 9a4e454ac5
32 changed files with 617 additions and 196 deletions

View File

@ -30,7 +30,7 @@
<activity <activity
android:name=".page.activity.ChatActivity" android:name=".page.activity.ChatActivity"
android:exported="false" android:exported="false"
android:windowSoftInputMode="adjustPan|stateHidden" /> android:windowSoftInputMode="adjustPan|stateAlwaysHidden" />
<activity <activity
android:name=".page.activity.SplashActivity" android:name=".page.activity.SplashActivity"
android:exported="true" android:exported="true"

View File

@ -3,6 +3,7 @@ package com.tenlionsoft.aimz_k
import com.google.gson.Gson import com.google.gson.Gson
import com.tenlionsoft.aimz_k.model.CoverSealedBean import com.tenlionsoft.aimz_k.model.CoverSealedBean
import com.tenlionsoft.aimz_k.model.MsgBean import com.tenlionsoft.aimz_k.model.MsgBean
import com.tenlionsoft.aimz_k.model.MsgCategoryBean
import com.tenlionsoft.aimz_k.model.MsgConvertBean import com.tenlionsoft.aimz_k.model.MsgConvertBean
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum
import com.tenlionsoft.baselib.contacts.ProjectConfig import com.tenlionsoft.baselib.contacts.ProjectConfig
@ -65,4 +66,20 @@ object ConvertBeanUtils {
) )
} }
fun convertCategoryBean(msgBean: MsgBean): MsgCategoryBean {
return MsgCategoryBean(
avatar = "",
messageType = msgBean.messageType,
messageId = msgBean.messageId,
receiverType = msgBean.receiverType,
receiverId = msgBean.receiverId,
senderType = msgBean.senderType,
senderId = msgBean.senderId,
status = msgBean.status,
timestamp = msgBean.timestamp,
msgType = msgBean.msgType,
body = msgBean.body
)
}
} }

View File

@ -22,10 +22,14 @@ class ChatMsgAdapter(datas: List<MsgBean>, viewModel: ChatPageViewModel) :
override fun getItemBinding(parent: ViewGroup, viewType: Int): ViewDataBinding { override fun getItemBinding(parent: ViewGroup, viewType: Int): ViewDataBinding {
when (viewType) { when (viewType) {
MsgTypeStateEnum.MSG_TO_OTHER_TXT -> { MsgTypeStateEnum.MSG_TO_OTHER_TXT -> {
return ItemMsgMyBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ItemMsgOtherBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
}//发送文本 }//发送文本
MsgTypeStateEnum.MSG_FROM_OTHER_TXT -> { MsgTypeStateEnum.MSG_FROM_OTHER_TXT -> {
return ItemMsgOtherBinding.inflate( return ItemMsgMyBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
parent, parent,
false false
@ -86,46 +90,55 @@ class ChatMsgAdapter(datas: List<MsgBean>, viewModel: ChatPageViewModel) :
override fun bindItem(holder: BaseViewHolder<ViewDataBinding>, position: Int) { override fun bindItem(holder: BaseViewHolder<ViewDataBinding>, position: Int) {
val itemType = getItemViewType(position) val itemType = getItemViewType(position)
when (itemType) { when (itemType) {
//发送文本
MsgTypeStateEnum.MSG_TO_OTHER_TXT -> { MsgTypeStateEnum.MSG_TO_OTHER_TXT -> {
(holder.binding as ItemMsgMyBinding).pos = position
(holder.binding as ItemMsgMyBinding).bean = list[position]
(holder.binding as ItemMsgMyBinding).state = list[position].status
}//发送文本
MsgTypeStateEnum.MSG_FROM_OTHER_TXT -> {
(holder.binding as ItemMsgOtherBinding).pos = position (holder.binding as ItemMsgOtherBinding).pos = position
(holder.binding as ItemMsgOtherBinding).bean = list[position] (holder.binding as ItemMsgOtherBinding).bean = list[position]
(holder.binding as ItemMsgOtherBinding).state = list[position].status (holder.binding as ItemMsgOtherBinding).state = list[position].status
}//收到文本信息 }
//收到文本信息
MsgTypeStateEnum.MSG_FROM_OTHER_TXT -> {
(holder.binding as ItemMsgMyBinding).pos = position
(holder.binding as ItemMsgMyBinding).bean = list[position]
(holder.binding as ItemMsgMyBinding).state = list[position].status
}
//发送视频
MsgTypeStateEnum.MSG_TO_OTHER_MOVIE -> { MsgTypeStateEnum.MSG_TO_OTHER_MOVIE -> {
(holder.binding as ItemMsgMyVideoBinding).pos = position (holder.binding as ItemMsgMyVideoBinding).pos = position
(holder.binding as ItemMsgMyVideoBinding).bean = list[position] (holder.binding as ItemMsgMyVideoBinding).bean = list[position]
(holder.binding as ItemMsgMyVideoBinding).state = list[position].status (holder.binding as ItemMsgMyVideoBinding).state = list[position].status
}//发送视频 }
//收到视频
MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE -> { MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE -> {
(holder.binding as ItemMsgOtherVideoBinding).pos = position (holder.binding as ItemMsgOtherVideoBinding).pos = position
(holder.binding as ItemMsgOtherVideoBinding).bean = list[position] (holder.binding as ItemMsgOtherVideoBinding).bean = list[position]
(holder.binding as ItemMsgOtherVideoBinding).state = list[position].status (holder.binding as ItemMsgOtherVideoBinding).state = list[position].status
}//收到视频 }
//发送图片
MsgTypeStateEnum.MSG_TO_OTHER_IMG -> { MsgTypeStateEnum.MSG_TO_OTHER_IMG -> {
(holder.binding as ItemMsgMyImgBinding).pos = position (holder.binding as ItemMsgMyImgBinding).pos = position
(holder.binding as ItemMsgMyImgBinding).bean = list[position] (holder.binding as ItemMsgMyImgBinding).bean = list[position]
(holder.binding as ItemMsgMyImgBinding).state = list[position].status (holder.binding as ItemMsgMyImgBinding).state = list[position].status
}//发送图片 }
//收到图片
MsgTypeStateEnum.MSG_FROM_OTHER_IMG -> { MsgTypeStateEnum.MSG_FROM_OTHER_IMG -> {
(holder.binding as ItemMsgOtherImgBinding).pos = position (holder.binding as ItemMsgOtherImgBinding).pos = position
(holder.binding as ItemMsgOtherImgBinding).bean = list[position] (holder.binding as ItemMsgOtherImgBinding).bean = list[position]
(holder.binding as ItemMsgOtherImgBinding).state = list[position].status (holder.binding as ItemMsgOtherImgBinding).state = list[position].status
}//收到图片 }
//发送语音
MsgTypeStateEnum.MSG_TO_OTHER_VOICE -> { MsgTypeStateEnum.MSG_TO_OTHER_VOICE -> {
(holder.binding as ItemMsgMyAudioBinding).pos = position (holder.binding as ItemMsgMyAudioBinding).pos = position
(holder.binding as ItemMsgMyAudioBinding).bean = list[position] (holder.binding as ItemMsgMyAudioBinding).bean = list[position]
(holder.binding as ItemMsgMyAudioBinding).state = list[position].status (holder.binding as ItemMsgMyAudioBinding).state = list[position].status
}//语音 }
//收到语音
MsgTypeStateEnum.MSG_FROM_OTHER_VOICE -> { MsgTypeStateEnum.MSG_FROM_OTHER_VOICE -> {
(holder.binding as ItemMsgOtherAudioBinding).pos = position (holder.binding as ItemMsgOtherAudioBinding).pos = position
(holder.binding as ItemMsgOtherAudioBinding).bean = list[position] (holder.binding as ItemMsgOtherAudioBinding).bean = list[position]
(holder.binding as ItemMsgOtherAudioBinding).state = list[position].status (holder.binding as ItemMsgOtherAudioBinding).state = list[position].status
}//语音 }
else -> { else -> {
throw IllegalArgumentException("Invalid view type") throw IllegalArgumentException("Invalid view type")
} }

View File

@ -39,7 +39,7 @@ data class MsgCategoryBean(
@ColumnInfo(name = "messageId") @ColumnInfo(name = "messageId")
var messageId: String?, //消息ID var messageId: String?, //消息ID
@ColumnInfo(name = "messageType") @ColumnInfo(name = "messageType")
var messageType: String, //消息类型 var messageType: String?, //消息类型
@ColumnInfo(name = "receiverId") @ColumnInfo(name = "receiverId")
var receiverId: String?, //接受人ID var receiverId: String?, //接受人ID
@ColumnInfo(name = "receiverType") @ColumnInfo(name = "receiverType")

View File

@ -4,6 +4,7 @@ import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.tenlionsoft.aimz_k.ConvertBeanUtils
@Dao @Dao
interface MsgCategoryDao { interface MsgCategoryDao {
@ -14,13 +15,13 @@ interface MsgCategoryDao {
* @return * @return
*/ */
@Query("SELECT * FROM db_category") @Query("SELECT * FROM db_category")
fun getAllCategory(): List<MsgCategoryBean> suspend fun getAllCategory(): List<MsgCategoryBean?>
/** /**
* 根据发送人ID获取 * 根据发送人ID获取
*/ */
@Query("SELECT * FROM db_category WHERE senderId=:senderId") @Query("SELECT * FROM db_category WHERE senderId=:senderId")
fun getCategoryBySenderId(senderId: String): MsgCategoryBean? suspend fun getCategoryBySenderId(senderId: String): MsgCategoryBean?
/** /**
* 根据ID获取消息 * 根据ID获取消息
@ -29,7 +30,7 @@ interface MsgCategoryDao {
* @return * @return
*/ */
@Query("SELECT * FROM db_category WHERE id =(:id) ") @Query("SELECT * FROM db_category WHERE id =(:id) ")
fun getCategoryById(id: String): List<MsgCategoryBean> suspend fun getCategoryById(id: String): List<MsgCategoryBean>
/** /**
* 根据信息来源查询消息 * 根据信息来源查询消息
@ -38,14 +39,14 @@ interface MsgCategoryDao {
* @return * @return
*/ */
@Query("SELECT * FROM db_category WHERE `senderId`=(:from)") @Query("SELECT * FROM db_category WHERE `senderId`=(:from)")
fun getCategoryByFrom(from: String?): List<MsgCategoryBean> suspend fun getCategoryByFrom(from: String?): List<MsgCategoryBean>
/** /**
* 查询与单人的聊天记录 SELECT id FROM db_msg WHERE `from`=(:form) AND `to`=(:to) OR `from`=(:to) AND `to`=(:form) ORDER BY timestamp DESC LIMIT (((:page)-1)*(:pageSize)),(:pageSize) * 查询与单人的聊天记录 SELECT id FROM db_msg WHERE `from`=(:form) AND `to`=(:to) OR `from`=(:to) AND `to`=(:form) ORDER BY timestamp DESC LIMIT (((:page)-1)*(:pageSize)),(:pageSize)
*/ */
@Query("SELECT * FROM db_category t1 ORDER by t1.timestamp ASC LIMIT (((:page)-1)*(:pageSize)),(:pageSize)") @Query("SELECT * FROM db_category t1 ORDER by t1.timestamp ASC LIMIT (((:page)-1)*(:pageSize)),(:pageSize)")
fun getCategoryPage( suspend fun getCategoryPage(
pageSize: Int, pageSize: Int,
page: Int page: Int
): MutableList<MsgCategoryBean> ): MutableList<MsgCategoryBean>
@ -55,7 +56,7 @@ interface MsgCategoryDao {
* 清空与某个人的聊天记录 * 清空与某个人的聊天记录
*/ */
@Query("DELETE FROM db_category WHERE `senderId`=(:from) AND receiverId=(:to) OR `senderId`=(:to) AND receiverId=(:from)") @Query("DELETE FROM db_category WHERE `senderId`=(:from) AND receiverId=(:to) OR `senderId`=(:to) AND receiverId=(:from)")
fun delChatHistory(from: String?, to: String?) suspend fun delChatHistory(from: String?, to: String?)
/** /**
* 添加消息多个 * 添加消息多个
@ -63,13 +64,13 @@ interface MsgCategoryDao {
* @param beans * @param beans
*/ */
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(vararg beans: MsgCategoryBean) suspend fun insertAll(vararg beans: MsgCategoryBean)
/** /**
* 批量添加 * 批量添加
*/ */
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(beans: List<MsgCategoryBean>) suspend fun insertAll(beans: List<MsgCategoryBean>)
/** /**
@ -78,23 +79,35 @@ interface MsgCategoryDao {
* @param bean * @param bean
*/ */
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertMsg(bean: MsgCategoryBean) suspend fun insertMsg(bean: MsgCategoryBean)
/** /**
* 清除全部聊天记录 * 清除全部聊天记录
*/ */
@Query("DELETE FROM db_category") @Query("DELETE FROM db_category")
fun delAllMsg() suspend fun delAllMsg()
/** /**
* 根据发送人的ID插入或者更新 * 根据发送人的ID插入或者更新
*/ */
fun updateOrInsert(senderId: String, msgBean: MsgBean) suspend fun updateOrInsert(senderId: String, msgBean: MsgBean) {
val bean = getCategoryBySenderId(senderId)
if (bean == null) {
//需要转换成MsgCategoryBean
insertMsg(ConvertBeanUtils.convertCategoryBean(msgBean))
} else {
updateCategory(senderId, msgBean.status, msgBean.body!!, msgBean.timestamp!!)
}
}
//更新
@Query("UPDATE db_category SET status=:status WHERE senderId=:senderId")
suspend fun updateStatus(senderId: String, status: String)
/** /**
* 更新 消息体 时间戳 状态 * 更新 消息体 时间戳 状态
* @Query("UPDATE db_msg SET status = :status WHERE messageId = :messageId") * @Query("UPDATE db_msg SET status = :status WHERE messageId = :messageId")
*/ */
@Query("UPDATE db_category SET status= :status , body=:body , timestamp=:time WHERE senderId=:senderId") @Query("UPDATE db_category SET status= :status , body=:body , timestamp=:time WHERE senderId=:senderId")
fun updateCategory(senderId: String, status:String,body:String,time:Long) suspend fun updateCategory(senderId: String, status: String?, body: String, time: Long)
} }

View File

@ -1,17 +0,0 @@
package com.tenlionsoft.aimz_k.model
abstract class MsgCategoryDaoImpl : MsgCategoryDao {
override fun updateOrInsert(senderId: String, msgBean: MsgBean) {
val bean = getCategoryBySenderId(senderId)
if (bean == null) {
//需要转换成MsgCategoryBean
// insertMsg(msgBean)
} else {
updateCategory(senderId, msgBean.status!!, msgBean.body!!, msgBean.timestamp!!)
}
}
}

View File

@ -111,7 +111,7 @@ interface MsgDao {
* @param bean * @param bean
*/ */
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertMsg(bean: MsgBean) suspend fun insertMsg(bean: MsgBean)
/** /**
* 清除全部聊天记录 * 清除全部聊天记录

View File

@ -11,6 +11,7 @@ import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.atwa.filepicker.core.FilePicker import com.atwa.filepicker.core.FilePicker
import com.tenlionsoft.aimz_k.R import com.tenlionsoft.aimz_k.R
import com.tenlionsoft.aimz_k.databinding.ActivityChatBinding import com.tenlionsoft.aimz_k.databinding.ActivityChatBinding
@ -19,6 +20,7 @@ import com.tenlionsoft.aimz_k.viewmodel.ChatPageViewModel
import com.tenlionsoft.baselib.base.BaseActivity import com.tenlionsoft.baselib.base.BaseActivity
import com.tenlionsoft.baselib.contacts.ProjectConfig import com.tenlionsoft.baselib.contacts.ProjectConfig
import com.tenlionsoft.baselib.utils.SpUtils import com.tenlionsoft.baselib.utils.SpUtils
import com.tenlionsoft.baselib.widget.SoftKeyBoardListener
import com.tenlionsoft.baselib.widget.wheel.WheelView import com.tenlionsoft.baselib.widget.wheel.WheelView
/** /**
@ -26,23 +28,33 @@ import com.tenlionsoft.baselib.widget.wheel.WheelView
*/ */
class ChatActivity : BaseActivity() { class ChatActivity : BaseActivity() {
private lateinit var mBinding: ActivityChatBinding private lateinit var mBinding: ActivityChatBinding
private lateinit var mLocalReceiver: LocalReceiver private lateinit var mLocalReceiver: LocalReceiver
var chatPageViewModel: ChatPageViewModel? = null var chatPageViewModel: ChatPageViewModel? = null
private val filePicker = FilePicker.getInstance(this) private val filePicker = FilePicker.getInstance(this)
override fun bindView() { override fun bindView() {
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_chat); mBinding = DataBindingUtil.setContentView(this, R.layout.activity_chat);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = window.statusBarColor =
ContextCompat.getColor(this, com.tenlionsoft.baselib.R.color.chat_page_bg) ContextCompat.getColor(this, com.tenlionsoft.baselib.R.color.chat_page_bg)
val fromId = intent.getStringExtra("fromId")
val fromId = intent.getStringExtra("fromId") //传递过来的接收人
val name = intent.getStringExtra("name")
if (name.isNullOrEmpty()) {
mBinding.tvTitle.text = "临时客户"
}
chatPageViewModel = ViewModelProvider(this, object : ViewModelProvider.Factory { chatPageViewModel = ViewModelProvider(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T { override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ChatPageViewModel(fromId!!, SpUtils.getId()) as T return ChatPageViewModel(
fromId!!,
SpUtils.getId(),
this@ChatActivity.applicationContext
) as T
} }
})[ChatPageViewModel::class.java] })[ChatPageViewModel::class.java]
mBinding.ivBack.setOnClickListener { finish(); } mBinding.ivBack.setOnClickListener { finish(); }
mBinding.srlChats.setEnableRefresh(false) mBinding.srlChats.setEnableRefresh(false)
mBinding.srlChats.setEnableLoadMore(false) mBinding.srlChats.setEnableLoadMore(false)
@ -53,17 +65,21 @@ class ChatActivity : BaseActivity() {
mBinding.etMsg.append(it.emoji) mBinding.etMsg.append(it.emoji)
} }
//显示/隐藏软键盘 //显示/隐藏软键盘
chatPageViewModel!!.showSoftKeyboard.observe(this) { // chatPageViewModel!!.showSoftKeyboard.observe(this) {
if (it) { // if (it) {
showSoftKeyBoard(mBinding.etMsg) // showSoftKeyBoard(mBinding.etMsg)
chatPageViewModel!!.showChooseLayout.value = false // chatPageViewModel!!.showChooseLayout.value = false
chatPageViewModel!!.showEmojiLayout.value = false // chatPageViewModel!!.showEmojiLayout.value = false
chatPageViewModel!!.showReplyLayout.value = false // chatPageViewModel!!.showReplyLayout.value = false
} else { // val layoutManager = mBinding.rlvChats.layoutManager as LinearLayoutManager
hideSoftKeyboard() // val itemCount = layoutManager.itemCount
} // mBinding.rlvChats.smoothScrollToPosition(itemCount - 1)
} // } else {
// hideSoftKeyboard()
// }
// }
mBinding.rlContent.setOnClickListener { mBinding.rlContent.setOnClickListener {
Log.e("ChatActivity", "bindView: 点击")
chatPageViewModel!!.showChooseLayout.value = false chatPageViewModel!!.showChooseLayout.value = false
chatPageViewModel!!.showEmojiLayout.value = false chatPageViewModel!!.showEmojiLayout.value = false
chatPageViewModel!!.showReplyLayout.value = false chatPageViewModel!!.showReplyLayout.value = false
@ -107,10 +123,18 @@ class ChatActivity : BaseActivity() {
} }
chatPageViewModel!!.scrollListToBottom.observe(this) { chatPageViewModel!!.scrollListToBottom.observe(this) {
if (it) { if (it) {
mBinding.rlvChats.scrollToPosition(chatPageViewModel!!.adapter.list.size - 1)//滚动到底部 Log.e("ChatActivity", "bindView: ${Thread.currentThread().name}")
val layoutManager = mBinding.rlvChats.layoutManager as LinearLayoutManager
val itemCount = layoutManager.itemCount
mBinding.rlvChats.scrollToPosition(itemCount - 1)//滚动到底部
chatPageViewModel!!.scrollListToBottom.value = false chatPageViewModel!!.scrollListToBottom.value = false
} }
} }
SoftKeyBoardListener().setChangeListener(this@ChatActivity,
{ _ -> Log.e("ChatActivity", "bindView: 显示") },
{ _ -> Log.e("ChatActivity", "bindView: 隐藏") })
registerLocalReceiver() registerLocalReceiver()
} }

View File

@ -8,7 +8,6 @@ import android.content.IntentFilter
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.provider.Settings import android.provider.Settings
import android.util.Log
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -132,6 +131,7 @@ class MainActivity : BaseActivity() {
filter.addAction(ProjectConfig.A_S_CONNECTED)//socket连接成功 filter.addAction(ProjectConfig.A_S_CONNECTED)//socket连接成功
filter.addAction(ProjectConfig.A_S_FAIL)//socket连接失败 filter.addAction(ProjectConfig.A_S_FAIL)//socket连接失败
filter.addAction(ProjectConfig.A_S_DISCONNECT)//socket连接断开 filter.addAction(ProjectConfig.A_S_DISCONNECT)//socket连接断开
filter.addAction(ProjectConfig.A_S_MSG_RECEIVER)//监听消息收到
registerReceiver(mLocalReceiver, filter) registerReceiver(mLocalReceiver, filter)
} }
@ -189,39 +189,46 @@ class MainActivity : BaseActivity() {
inner class MainBroadcastReceiver : BroadcastReceiver() { inner class MainBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
Log.e("MainBroadcastReceiver", "onReceive: ${intent?.action}")
when (intent?.action) { when (intent?.action) {
//开始下载
ProjectConfig.ACTION_UPDATE_START -> { ProjectConfig.ACTION_UPDATE_START -> {
showUpdateProgress() showUpdateProgress()
}//开始下载 }
//更新进度
ProjectConfig.ACTION_UPDATE_PROGRESS -> { ProjectConfig.ACTION_UPDATE_PROGRESS -> {
val progress = intent.getIntExtra("progress", 0) val progress = intent.getIntExtra("progress", 0)
LogUtils.e("当前进度:$progress") LogUtils.e("当前进度:$progress")
mUpdateView?.setCurrentProgress(progress) mUpdateView?.setCurrentProgress(progress)
}//更新进度 }
//下载失败
ProjectConfig.ACTION_UPDATE_ERROR -> { ProjectConfig.ACTION_UPDATE_ERROR -> {
//下载失败 //下载失败
mUpdateView?.dismiss() mUpdateView?.dismiss()
}//下载失败 }
//下载成功
ProjectConfig.ACTION_UPDATE_SUCCESS -> { ProjectConfig.ACTION_UPDATE_SUCCESS -> {
//下载成功 //下载成功
mUpdateView?.dismiss() mUpdateView?.dismiss()
//判断是否可以安装 //判断是否可以安装
val apkFile = intent.getSerializableExtra("apkFile") as File? val apkFile = intent.getSerializableExtra("apkFile") as File?
installApk(apkFile!!) installApk(apkFile!!)
}//下载成功 }
//socket 连接成功
ProjectConfig.A_S_CONNECTED -> { ProjectConfig.A_S_CONNECTED -> {
onSocketConnectListener?.onListener(ProjectConfig.A_S_CONNECTED) onSocketConnectListener?.onListener(ProjectConfig.A_S_CONNECTED)
}//socket 连接成功 }
//连接失败
ProjectConfig.A_S_FAIL -> { ProjectConfig.A_S_FAIL -> {
onSocketConnectListener?.onListener(ProjectConfig.A_S_FAIL) onSocketConnectListener?.onListener(ProjectConfig.A_S_FAIL)
}//连接失败 }
//接收到信息
ProjectConfig.A_S_MSG_RECEIVER -> { ProjectConfig.A_S_MSG_RECEIVER -> {
val extra = intent.getStringExtra(ProjectConfig.MSG_TEXT) val extra = intent.getStringExtra("msg")
if (!extra.isNullOrEmpty()) { if (!extra.isNullOrEmpty()) {
onSocketConnectListener?.onReceiverMsg(extra) onSocketConnectListener?.onReceiverMsg(extra)
} }
}//接收到信息 }
else -> {} else -> {}
} }
} }

View File

@ -3,7 +3,6 @@ package com.tenlionsoft.aimz_k.page.fragments
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -72,12 +71,9 @@ class MsgFragment : Fragment(), MsgViewModel.OnItemClickListener,
viewModel.isLogining.observe(viewLifecycleOwner) { viewModel.isLogining.observe(viewLifecycleOwner) {
if (it) { if (it) {
mMsgBinding.llSearchLayout.tvSocketStatus.text = "登陆中..." mMsgBinding.llSearchLayout.tvSocketStatus.text = "登陆中..."
mMsgBinding.llSearchLayout.ivSocketStatus.visibility = View.GONE
mMsgBinding.llSearchLayout.pbSocketLoading.visibility = View.VISIBLE mMsgBinding.llSearchLayout.pbSocketLoading.visibility = View.VISIBLE
toStartService() toStartService()
} else { } else {
mMsgBinding.llSearchLayout.ivSocketStatus.visibility = View.VISIBLE
mMsgBinding.llSearchLayout.ivSocketStatus.setImageResource(R.drawable.shp_circle_green)
mMsgBinding.llSearchLayout.tvSocketStatus.visibility = View.GONE mMsgBinding.llSearchLayout.tvSocketStatus.visibility = View.GONE
mMsgBinding.llSearchLayout.pbSocketLoading.visibility = View.GONE mMsgBinding.llSearchLayout.pbSocketLoading.visibility = View.GONE
} }
@ -99,17 +95,25 @@ class MsgFragment : Fragment(), MsgViewModel.OnItemClickListener,
mActivity?.startService(Intent(mActivity!!, SocketService::class.java)) mActivity?.startService(Intent(mActivity!!, SocketService::class.java))
} }
override fun onResume() {
super.onResume()
viewModel.refresh()
}
/** /**
* 条目点击 * 条目点击
*/ */
override fun onItemClick(msgCategoryBean: MsgCategoryBean) { override fun onItemClick(msgCategoryBean: MsgCategoryBean) {
Log.e("MsgFragment", "onItemClick: ${msgCategoryBean}") startActivity(
startActivity(Intent(mActivity, ChatActivity::class.java)) Intent(mActivity, ChatActivity::class.java).putExtra(
"fromId",
msgCategoryBean.senderId
)
)
} }
//监听socket连接状态 //监听socket连接状态
override fun onListener(status: String) { override fun onListener(status: String) {
Log.e("MsgFragment", "onListener: ${status}")
when (status) { when (status) {
ProjectConfig.A_S_CONNECTED -> { ProjectConfig.A_S_CONNECTED -> {
viewModel.isLogining.value = false viewModel.isLogining.value = false
@ -124,7 +128,7 @@ class MsgFragment : Fragment(), MsgViewModel.OnItemClickListener,
//接收到socket信息 //接收到socket信息
override fun onReceiverMsg(msg: String) { override fun onReceiverMsg(msg: String) {
if (msg.isNotEmpty()) { if (msg.isNotEmpty()) {
viewModel.fromMsg(msg) viewModel.fromMsg(msg, mActivity!!.applicationContext)
} }
} }

View File

@ -1,5 +1,6 @@
package com.tenlionsoft.aimz_k.viewmodel package com.tenlionsoft.aimz_k.viewmodel
import android.content.Context
import android.content.Intent import android.content.Intent
import android.util.Log import android.util.Log
import android.view.View import android.view.View
@ -27,7 +28,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class ChatPageViewModel(private val fromId: String, private val toId: String) : BaseViewModel() { class ChatPageViewModel(
private val fromId: String,
private val toId: String,
private val context: Context
) : BaseViewModel() {
val txtMsg = MutableLiveData<String>("") val txtMsg = MutableLiveData<String>("")
val showSendBtn = MutableLiveData(false)//显示/隐藏发送按钮 val showSendBtn = MutableLiveData(false)//显示/隐藏发送按钮
val showEmojiLayout = MutableLiveData(false)//显示/隐藏emoji val showEmojiLayout = MutableLiveData(false)//显示/隐藏emoji
@ -43,9 +48,11 @@ class ChatPageViewModel(private val fromId: String, private val toId: String) :
Log.e("ChatPageViewModel", "Init: ${showSendBtn.value}") Log.e("ChatPageViewModel", "Init: ${showSendBtn.value}")
viewModelScope.launch { viewModelScope.launch {
val list = getList() val list = getList()
Log.e("ChatPageViewModel", "Init:${list} ")
_msgList.value = list
adapter.setData(list) adapter.setData(list)
scrollListToBottom.value = true
} }
} }
//获取列表 //获取列表
@ -58,19 +65,14 @@ class ChatPageViewModel(private val fromId: String, private val toId: String) :
fun onTxtChange(s: CharSequence, start: Int, before: Int, count: Int) { fun onTxtChange(s: CharSequence, start: Int, before: Int, count: Int) {
this.txtMsg.value = s.toString() this.txtMsg.value = s.toString()
showSendBtn.value = s.isNotEmpty() showSendBtn.value = s.isNotEmpty()
Log.e("ChatPageViewModel", "onTxtChange: ${showSendBtn.value}")
} }
fun onShowReplyLayout() { fun onShowReplyLayout() {
Log.e("ChatPageViewModel", "onShowReplyLayout切换前: ${showReplyLayout.value}")
showReplyLayout.value = !showReplyLayout.value!! showReplyLayout.value = !showReplyLayout.value!!
Log.e("ChatPageViewModel", "onShowReplyLayout切换后: ${showReplyLayout.value}")
showEmojiLayout.value = false showEmojiLayout.value = false
showChooseLayout.value = false showChooseLayout.value = false
showSoftKeyboard.value = !showReplyLayout.value!! showSoftKeyboard.value = !showReplyLayout.value!!
Log.e("ChatPageViewModel", "onShowReplyLayout: ${showSoftKeyboard.value}")
} }
/** /**
@ -136,51 +138,71 @@ class ChatPageViewModel(private val fromId: String, private val toId: String) :
} }
//刷新
private fun refresh() {
viewModelScope.launch {
val list = getList()
_msgList.value = list
adapter.setData(_msgList.value!!)
}
}
//刷新页面
private fun addBeanToView(msgBean: MsgBean) {
_msgList.value = _msgList.value?.plus(msgBean)
adapter.setData(_msgList.value!!)
}
/** /**
* 发送文本信息 * 发送文本信息
*/ */
fun sendTxtMsg(v: View) { fun sendTxtMsg(v: View) {
if (txtMsg.value!!.isNotEmpty()) { if (txtMsg.value!!.isNotEmpty()) {
val intent = Intent(ProjectConfig.A_S_MSG_SEND) val intent = Intent(ProjectConfig.A_S_MSG_SEND)
val b = buildSendBean() val b = buildSendBean(ProjectConfig.MSG_TEXT)
viewModelScope.launch { viewModelScope.launch {
withContext(Dispatchers.IO) { val msgBean = insertMsgBeanToDb(b)
val dao = DbManager.db.msgDao() _msgList.value = _msgList.value?.plus(msgBean)
val msgBean = ConvertBeanUtils.convertBeanToMsgBean(b) adapter.setData(_msgList.value!!)
dao.insertMsg(msgBean)
} }
}
Log.e("ChatPageViewModel", "sendTxtMsg: ${b}")
intent.putExtra("msgBean", b) intent.putExtra("msgBean", b)
v.context.sendBroadcast(intent) v.context.sendBroadcast(intent)
txtMsg.value = "" txtMsg.value = ""
scrollListToBottom.value = true
}
} }
//TODO 刷新列表 private suspend fun insertMsgBeanToDb(b: MsgConvertBean): MsgBean {
return withContext(Dispatchers.IO) {
val dao = DbManager.db.msgDao()
val cDao = DbManager.db.categoryDao()
val msgBean = ConvertBeanUtils.convertBeanToMsgBean(b)
cDao.updateCategory(b.receiver.receiverId!!, b.status, b.body, b.timestamp)
dao.insertMsg(msgBean)
return@withContext msgBean
}
} }
//收到消息 //收到消息
fun receiveMsg(msg: String?) { fun receiveMsg(msg: String?) {
if (!msg.isNullOrEmpty()) { if (!msg.isNullOrEmpty()) {
val bean = ConvertBeanUtils.covertBean(msg) val bean = ConvertBeanUtils.covertBean(msg)
Log.e("ChatPageViewModel", "receiveMsg: ${bean}")
when (bean) { when (bean) {
//状态消息 //消息状态
is CoverSealedBean.StateBean -> { is CoverSealedBean.StateBean -> {
viewModelScope.launch { viewModelScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val dao = DbManager.db.msgDao() val dao = DbManager.db.msgDao()
//TODO 并且更新Category表 val cDao = DbManager.db.categoryDao()
dao.updateStatus( val statusType =
bean.data.messageId,
mGson.fromJson(bean.data.body, BodyContent::class.java).statusType!! mGson.fromJson(bean.data.body, BodyContent::class.java).statusType!!
) dao.updateStatus(bean.data.messageId, statusType)
cDao.updateStatus(bean.data.sender.senderId!!, statusType)
} }
//TODO 刷新列表 refresh()
} }
} }
//消息 //消息
is CoverSealedBean.Msg -> { is CoverSealedBean.Msg -> {
//插入信息表 //插入信息表
//并更新category表 //并更新category表
@ -191,24 +213,32 @@ class ChatPageViewModel(private val fromId: String, private val toId: String) :
msgDao.insertMsg(bean.msgBean) msgDao.insertMsg(bean.msgBean)
cDao.updateOrInsert(bean.msgBean.senderId!!, bean.msgBean) cDao.updateOrInsert(bean.msgBean.senderId!!, bean.msgBean)
} }
addBeanToView(bean.msgBean)
sendStatusMsg(bean.msgBean.messageId!!)
} }
//刷新列表 //刷新列表
scrollListToBottom.value = true scrollListToBottom.value = true
Log.e("ChatPageViewModel", "receiveMsg接收到消息: ${bean.msgBean}")
} }
} }
} }
} }
private fun sendStatusMsg(msgId: String) {
val b = buildStatusBean(msgId)
val intent = Intent(ProjectConfig.A_S_MSG_SEND)
intent.putExtra("msgBean", b)
context.sendBroadcast(intent)
}
private fun buildSendBean(): MsgConvertBean { //构建发送实体
private fun buildSendBean(type: String): MsgConvertBean {
val bodyBean = BodyContent(content = txtMsg.value, null, null) val bodyBean = BodyContent(content = txtMsg.value, null, null)
var body = mGson.toJson(bodyBean) val body = mGson.toJson(bodyBean)
return MsgConvertBean( return MsgConvertBean(
body = body, body = body,
customMessageType = "", customMessageType = "",
messageId = TimeUtils.getNowDateMillis().toString(), messageId = TimeUtils.getNowDateMillis().toString(),
messageType = ProjectConfig.MSG_TEXT, messageType = type,
metadata = "", metadata = "",
timestamp = TimeUtils.getNowDateMillis(), timestamp = TimeUtils.getNowDateMillis(),
sender = Sender( sender = Sender(
@ -216,10 +246,33 @@ class ChatPageViewModel(private val fromId: String, private val toId: String) :
senderType = "" senderType = ""
), ),
receiver = Receiver( receiver = Receiver(
receiverId = "2", receiverId = fromId,
receiverType = "SINGLE_USER" receiverType = "SINGLE_USER"
), ),
status = ProjectConfig.MSG_SEND_ING, status = ProjectConfig.MSG_SEND_ING,
) )
} }
//构建状态信息
private fun buildStatusBean(messageId: String): MsgConvertBean {
val bodyBean = BodyContent(content = null, msg = "", statusType = "SUCCESS_RECEIVED")
val body = mGson.toJson(bodyBean)
return MsgConvertBean(
body = body,
customMessageType = "",
messageId = messageId,
messageType = ProjectConfig.MSG_STATUS,
metadata = "",
timestamp = TimeUtils.getNowDateMillis(),
sender = Sender(
senderId = SpUtils.getId(),
senderType = ""
),
receiver = Receiver(
receiverId = fromId,
receiverType = "SINGLE_USER"
),
status = "",
)
}
} }

View File

@ -1,13 +1,24 @@
package com.tenlionsoft.aimz_k.viewmodel package com.tenlionsoft.aimz_k.viewmodel
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.tenlionsoft.aimz_k.ConvertBeanUtils import com.tenlionsoft.aimz_k.ConvertBeanUtils
import com.tenlionsoft.aimz_k.adapter.CategoryAdapter import com.tenlionsoft.aimz_k.adapter.CategoryAdapter
import com.tenlionsoft.aimz_k.model.BodyContent
import com.tenlionsoft.aimz_k.model.CoverSealedBean import com.tenlionsoft.aimz_k.model.CoverSealedBean
import com.tenlionsoft.aimz_k.model.DbManager import com.tenlionsoft.aimz_k.model.DbManager
import com.tenlionsoft.aimz_k.model.MsgCategoryBean import com.tenlionsoft.aimz_k.model.MsgCategoryBean
import com.tenlionsoft.aimz_k.model.MsgConvertBean
import com.tenlionsoft.aimz_k.model.Receiver
import com.tenlionsoft.aimz_k.model.Sender
import com.tenlionsoft.baselib.contacts.ProjectConfig
import com.tenlionsoft.baselib.utils.SpUtils
import com.tenlionsoft.baselib.utils.TimeUtils
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -16,14 +27,11 @@ class MsgViewModel : ViewModel() {
private val _pwdList = MutableLiveData<List<MsgCategoryBean>>() private val _pwdList = MutableLiveData<List<MsgCategoryBean>>()
var adapter: CategoryAdapter = CategoryAdapter(_pwdList.value ?: emptyList(), this) var adapter: CategoryAdapter = CategoryAdapter(_pwdList.value ?: emptyList(), this)
var onItemClickListener: OnItemClickListener? = null var onItemClickListener: OnItemClickListener? = null
val isLogining = MutableLiveData<Boolean>(false) val isLogining = MutableLiveData(false)
private val mGson = Gson()
private var mCurrentPage: Int = 1 private var mCurrentPage: Int = 1
var isHasMore: MutableLiveData<Boolean> = MutableLiveData<Boolean>(false) var isHasMore: MutableLiveData<Boolean> = MutableLiveData<Boolean>(false)
init {
getList(true)
}
/** /**
* 获取列表数据 * 获取列表数据
@ -72,12 +80,74 @@ class MsgViewModel : ViewModel() {
} }
//接收到信息 //接收到信息
fun fromMsg(msg: String) { fun fromMsg(msg: String, context: Context) {
Log.e("MsgViewModel", "fromMsg: ${msg}")
if (msg.isNotEmpty()) {
val bean = ConvertBeanUtils.covertBean(msg) val bean = ConvertBeanUtils.covertBean(msg)
when (bean) { when (bean) {
is CoverSealedBean.StateBean -> {}//消息状态 //状态消息
is CoverSealedBean.Msg -> {}//消息 is CoverSealedBean.StateBean -> {
viewModelScope.launch {
withContext(Dispatchers.IO) {
val dao = DbManager.db.msgDao()
val cDao = DbManager.db.categoryDao()
val statusType =
mGson.fromJson(bean.data.body, BodyContent::class.java).statusType!!
dao.updateStatus(bean.data.messageId, statusType)
cDao.updateStatus(bean.data.sender.senderId!!, statusType)
} }
//刷新
refresh()
}
}
//消息
is CoverSealedBean.Msg -> {
//插入信息表
//并更新category表
viewModelScope.launch {
withContext(Dispatchers.IO) {
val msgDao = DbManager.db.msgDao()
val cDao = DbManager.db.categoryDao()
msgDao.insertMsg(bean.msgBean)
cDao.updateOrInsert(bean.msgBean.senderId!!, bean.msgBean)
}
sendStatusMsg(bean.msgBean.messageId!!, bean.msgBean.senderId!!, context)
//刷新列表
refresh()
}
}
}
}
}
private fun sendStatusMsg(msgId: String, fromId: String, context: Context) {
val b = buildStatusBean(msgId, fromId)
val intent = Intent(ProjectConfig.A_S_MSG_SEND)
intent.putExtra("msgBean", b)
context.sendBroadcast(intent)
}
//构建状态信息
private fun buildStatusBean(messageId: String, fromId: String): MsgConvertBean {
val bodyBean = BodyContent(content = null, msg = "", statusType = "SUCCESS_RECEIVED")
val body = mGson.toJson(bodyBean)
return MsgConvertBean(
body = body,
customMessageType = "",
messageId = messageId,
messageType = ProjectConfig.MSG_STATUS,
metadata = "",
timestamp = TimeUtils.getNowDateMillis(),
sender = Sender(
senderId = SpUtils.getId(),
senderType = ""
),
receiver = Receiver(
receiverId = fromId,
receiverType = "SINGLE_USER"
),
status = "",
)
} }
/** /**

View File

@ -2,6 +2,7 @@ package com.tenlionsoft.aimz_k.widget
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView
import androidx.databinding.BindingAdapter import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -9,16 +10,20 @@ import androidx.recyclerview.widget.RecyclerView.Adapter
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.google.gson.Gson
import com.tenlionsoft.aimz_k.R import com.tenlionsoft.aimz_k.R
import com.tenlionsoft.aimz_k.model.BodyContent
import com.tenlionsoft.baselib.contacts.ProjectConfig import com.tenlionsoft.baselib.contacts.ProjectConfig
import com.tenlionsoft.baselib.utils.TimeUtils
object BindingUtils { object BindingUtils {
@BindingAdapter("setAdapter") @BindingAdapter("setLinearLayoutAdapter")
@JvmStatic @JvmStatic
fun bindAdapterToRecyclerView(recyclerView: RecyclerView, adapter: Adapter<*>) { fun bindAdapterToRecyclerView(recyclerView: RecyclerView, adapter: Adapter<*>) {
recyclerView.layoutManager = LinearLayoutManager(recyclerView.context) val linearLayoutManager = LinearLayoutManager(recyclerView.context)
recyclerView.layoutManager = linearLayoutManager
recyclerView.adapter = adapter recyclerView.adapter = adapter
} }
@ -48,11 +53,35 @@ object BindingUtils {
imageView.setImageResource(url) imageView.setImageResource(url)
} }
@BindingAdapter("jsonConvertMsgBody")
@JvmStatic
fun jsonConvert(tv: TextView, str: String?) {
if (str.isNullOrEmpty()) {
tv.text = ""
} else {
val gson = Gson()
val body = gson.fromJson(str, BodyContent::class.java)
tv.text = body.content
}
}
@BindingAdapter("millConvertDate")
@JvmStatic
fun millConvertDate(tv: TextView, long: Long?) {
if (long == null) {
tv.text = ""
} else {
tv.text = TimeUtils.millis2HMStr(long)
}
}
//发送中 //发送中
@BindingAdapter("isShowLoading") @BindingAdapter("isShowLoading")
@JvmStatic @JvmStatic
fun sending(view: View, status: String) { fun sending(view: View, status: String?) {
if (status.isNullOrEmpty()) {
view.visibility = View.GONE
} else {
when (status) { when (status) {
ProjectConfig.MSG_SEND_SUCCESS -> view.visibility = View.GONE ProjectConfig.MSG_SEND_SUCCESS -> view.visibility = View.GONE
ProjectConfig.MSG_SEND_FAIL -> view.visibility = View.GONE ProjectConfig.MSG_SEND_FAIL -> view.visibility = View.GONE
@ -60,16 +89,22 @@ object BindingUtils {
else -> view.visibility = View.GONE else -> view.visibility = View.GONE
} }
} }
}
//发送失败 //发送失败
@BindingAdapter("isShowFail") @BindingAdapter("isShowFail")
@JvmStatic @JvmStatic
fun sendFail(view: View, status: String) { fun sendFail(view: View, status: String?) {
if (status.isNullOrEmpty()) {
view.visibility = View.GONE
} else {
when (status) { when (status) {
ProjectConfig.MSG_SEND_SUCCESS -> view.visibility = View.GONE ProjectConfig.MSG_SEND_SUCCESS -> view.visibility = View.GONE
ProjectConfig.MSG_SEND_FAIL -> view.visibility = View.GONE ProjectConfig.MSG_SEND_FAIL -> view.visibility = View.VISIBLE
ProjectConfig.MSG_SEND_ING -> view.visibility = View.VISIBLE ProjectConfig.MSG_SEND_ING -> view.visibility = View.GONE
else -> view.visibility = View.GONE else -> view.visibility = View.GONE
} }
} }
}
} }

View File

@ -42,6 +42,7 @@
android:src="@drawable/ic_back_left" /> android:src="@drawable/ic_back_left" />
<TextView <TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true" android:layout_centerInParent="true"
@ -73,12 +74,17 @@
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1"> android:layout_weight="1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rlv_chats" android:id="@+id/rlv_chats"
setAdapter="@{viewModel.adapter}" setLinearLayoutAdapter="@{viewModel.adapter}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:listitem="@layout/item_msg_my" /> tools:listitem="@layout/item_msg_my" />
</RelativeLayout>
</com.scwang.smart.refresh.layout.SmartRefreshLayout> </com.scwang.smart.refresh.layout.SmartRefreshLayout>
<LinearLayout <LinearLayout
@ -119,6 +125,8 @@
android:background="@drawable/shp_white_5_radius" android:background="@drawable/shp_white_5_radius"
android:gravity="center_vertical" android:gravity="center_vertical"
android:hint="请输入内容" android:hint="请输入内容"
android:maxLines="5"
android:minLines="1"
android:onTextChanged="@{viewModel::onTxtChange}" android:onTextChanged="@{viewModel::onTxtChange}"
android:padding="10dp" android:padding="10dp"
android:text="@{viewModel.txtMsg}" android:text="@{viewModel.txtMsg}"

View File

@ -27,7 +27,7 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rlv_msgs" android:id="@+id/rlv_msgs"
android:layout_width="match_parent" android:layout_width="match_parent"
setAdapter="@{msgViewModel.adapter}" setLinearLayoutAdapter="@{msgViewModel.adapter}"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:listitem="@layout/item_category" /> tools:listitem="@layout/item_category" />

View File

@ -37,13 +37,12 @@
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:scaleType="fitXY" android:scaleType="fitXY"
tools:src="@drawable/app_logo_small" /> tools:src="@drawable/app_logo_small" />
<!-- android:visibility="@{item.read?View.VISIBLE:View.GONE}"-->
<ImageView <ImageView
android:layout_width="10dp" android:layout_width="10dp"
android:layout_height="10dp" android:layout_height="10dp"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:src="@drawable/shp_circle_red" android:src="@drawable/shp_circle_red" />
android:visibility="@{item.read?View.VISIBLE:View.GONE}" />
</RelativeLayout> </RelativeLayout>
@ -74,38 +73,39 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:text="@{item.fromName}" android:text="@{item.receiverType}"
android:textColor="#1D1D1D" android:textColor="#1D1D1D"
android:textSize="18sp" android:textSize="14sp"
tools:text="测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试" /> tools:text="测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试" />
<TextView <TextView
jsonConvertMsgBody="@{item.body}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:text="@{item.body}"
android:textColor="#B7B7B7" android:textColor="#B7B7B7"
android:textSize="14sp"
tools:text="测试聊天内容" /> tools:text="测试聊天内容" />
</LinearLayout> </LinearLayout>
<TextView <TextView
millConvertDate="@{item.timestamp}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@{String.valueOf(item.id)}"
android:textColor="#CACACA" android:textColor="#CACACA"
android:textSize="14sp" android:textSize="12sp"
tools:text="16:00" /> tools:text="16:00" />
</LinearLayout> </LinearLayout>
<View <View
android:visibility="@{pos&lt; size?View.VISIBLE:View.GONE}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
android:background="#E6E6E6" /> android:background="#E6E6E6"
android:visibility="@{pos&lt; size?View.VISIBLE:View.GONE}" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</layout> </layout>

View File

@ -46,6 +46,7 @@
android:layout_height="15dp" android:layout_height="15dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:indeterminateBehavior="repeat" android:indeterminateBehavior="repeat"
isShowLoading="@{state}"
android:indeterminateDrawable="@drawable/anim_loading" /> android:indeterminateDrawable="@drawable/anim_loading" />
<ImageView <ImageView
@ -54,7 +55,7 @@
android:layout_height="15dp" android:layout_height="15dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:src="@drawable/msg_state_fail_resend" android:src="@drawable/msg_state_fail_resend"
android:visibility="gone" isShowFail="@{state}"
tools:visibility="visible" /> tools:visibility="visible" />
</RelativeLayout> </RelativeLayout>
@ -65,6 +66,7 @@
android:background="@drawable/ic_message_text_send" android:background="@drawable/ic_message_text_send"
android:gravity="center|left" android:gravity="center|left"
android:maxWidth="200dp" android:maxWidth="200dp"
jsonConvertMsgBody="@{bean.body}"
android:paddingLeft="5dp" android:paddingLeft="5dp"
android:paddingRight="10dp" android:paddingRight="10dp"
android:textColor="@color/black" android:textColor="@color/black"

View File

@ -43,6 +43,7 @@
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
isShowLoading="@{state}"
android:indeterminateBehavior="repeat" android:indeterminateBehavior="repeat"
android:indeterminateDrawable="@drawable/anim_loading" /> android:indeterminateDrawable="@drawable/anim_loading" />
@ -52,7 +53,7 @@
android:layout_height="15dp" android:layout_height="15dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:src="@drawable/msg_state_fail_resend" android:src="@drawable/msg_state_fail_resend"
android:visibility="gone" isShowFail="@{state}"
tools:visibility="visible" /> tools:visibility="visible" />
</RelativeLayout> </RelativeLayout>

View File

@ -44,6 +44,7 @@
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
isShowLoading="@{state}"
android:indeterminateBehavior="repeat" android:indeterminateBehavior="repeat"
android:indeterminateDrawable="@drawable/anim_loading" /> android:indeterminateDrawable="@drawable/anim_loading" />
@ -52,8 +53,8 @@
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
isShowFail="@{state}"
android:src="@drawable/msg_state_fail_resend" android:src="@drawable/msg_state_fail_resend"
android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
</RelativeLayout> </RelativeLayout>

View File

@ -43,6 +43,7 @@
android:id="@+id/pb_sending" android:id="@+id/pb_sending"
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
isShowLoading="@{state}"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:indeterminateBehavior="repeat" android:indeterminateBehavior="repeat"
android:indeterminateDrawable="@drawable/anim_loading" /> android:indeterminateDrawable="@drawable/anim_loading" />
@ -51,9 +52,9 @@
android:id="@+id/iv_msg_state_fail" android:id="@+id/iv_msg_state_fail"
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
isShowFail="@{state}"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:src="@drawable/msg_state_fail_resend" android:src="@drawable/msg_state_fail_resend"
android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
</RelativeLayout> </RelativeLayout>

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<data> <data>
<variable <variable
name="pos" name="pos"
type="Integer" /> type="Integer" />
@ -34,6 +35,7 @@
<TextView <TextView
android:id="@+id/tv_content" android:id="@+id/tv_content"
jsonConvertMsgBody="@{bean.body}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="10dp" android:layout_marginLeft="10dp"
@ -43,8 +45,7 @@
android:paddingLeft="10dp" android:paddingLeft="10dp"
android:paddingRight="5dp" android:paddingRight="5dp"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="14sp" android:textSize="14sp" />
tools:text="测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试" />
<RelativeLayout <RelativeLayout
@ -55,6 +56,7 @@
<ProgressBar <ProgressBar
android:id="@+id/pb_sending" android:id="@+id/pb_sending"
isShowLoading="@{state}"
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
@ -63,11 +65,11 @@
<ImageView <ImageView
android:id="@+id/iv_msg_state_fail" android:id="@+id/iv_msg_state_fail"
isShowFail="@{state}"
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:src="@drawable/msg_state_fail_resend" android:src="@drawable/msg_state_fail_resend"
android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>

View File

@ -65,6 +65,7 @@
android:id="@+id/pb_sending" android:id="@+id/pb_sending"
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
isShowLoading="@{state}"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:indeterminateBehavior="repeat" android:indeterminateBehavior="repeat"
android:indeterminateDrawable="@drawable/anim_loading" /> android:indeterminateDrawable="@drawable/anim_loading" />
@ -74,8 +75,8 @@
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
isShowFail="@{state}"
android:src="@drawable/msg_state_fail_resend" android:src="@drawable/msg_state_fail_resend"
android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>

View File

@ -73,6 +73,7 @@
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
isShowLoading="@{state}"
android:indeterminateBehavior="repeat" android:indeterminateBehavior="repeat"
android:indeterminateDrawable="@drawable/anim_loading" /> android:indeterminateDrawable="@drawable/anim_loading" />
@ -80,9 +81,9 @@
android:id="@+id/iv_msg_state_fail" android:id="@+id/iv_msg_state_fail"
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
isShowFail="@{state}"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:src="@drawable/msg_state_fail_resend" android:src="@drawable/msg_state_fail_resend"
android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>

View File

@ -7,29 +7,44 @@
android:layout_height="48dp" android:layout_height="48dp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:background="@drawable/shp_white_5_radius" android:background="@drawable/shp_white_5_radius"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/ll_search_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_search_gray" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:gravity="center"
android:text="搜索"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/ll_load" android:id="@+id/ll_load"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_centerVertical="true" android:layout_marginTop="10dp"
android:layout_marginLeft="8dp"> android:layout_marginRight="10dp">
<ImageView
android:id="@+id/iv_socket_status"
android:layout_width="5dp"
android:layout_height="5dp"
android:layout_marginTop="3dp"
android:layout_marginRight="3dp"
android:src="@drawable/shp_circle_red" />
<ProgressBar <ProgressBar
android:id="@+id/pb_socket_loading" android:id="@+id/pb_socket_loading"
android:layout_width="18dp" android:layout_width="12dp"
android:layout_height="18dp" android:layout_height="12dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:indeterminateBehavior="repeat" android:indeterminateBehavior="repeat"
android:indeterminateDrawable="@drawable/anim_loading" /> android:indeterminateDrawable="@drawable/anim_loading" />
@ -42,27 +57,7 @@
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:text="登陆中..." android:text="登陆中..."
android:textColor="@color/gray_6f" android:textColor="@color/gray_6f"
android:textSize="12sp" /> android:textSize="10sp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="horizontal">
<ImageView
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@drawable/ic_search_gray" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:gravity="center"
android:text="搜索"
android:textSize="18sp" />
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
</layout> </layout>

View File

@ -66,4 +66,5 @@ dependencies {
api(libs.emojipicker) api(libs.emojipicker)
api(libs.xxpermissions) api(libs.xxpermissions)
api(libs.filepicker) api(libs.filepicker)
api(libs.room.ktx)
} }

View File

@ -0,0 +1,79 @@
package com.tenlionsoft.baselib.base
import android.app.Activity
import android.graphics.Rect
import android.view.View
import android.widget.FrameLayout
class AndroidBug5497Workaround(activity: Activity, height: Int, hideBar: Boolean) {
companion object {
/**
* height:通过外部传入初始界面高度用于改变界面后恢复原样建议在activity的onWindowFocusChanged中初始化
* hideBar:是否隐藏状态栏用于这个是用户可以控制的所以采用外部传入
*/
fun assistActivity(activity: Activity, height: Int, hideBar: Boolean = false) {
AndroidBug5497Workaround(activity, height, hideBar)
}
}
private val mChildOfContent: View
private var usableHeightPrevious: Int = 0
private val frameLayoutParams: FrameLayout.LayoutParams
private val contentHeight: Int
private val statusBarHeight: Int
init {
val content: FrameLayout = activity.findViewById(android.R.id.content)
contentHeight = height
mChildOfContent = content.getChildAt(0)
statusBarHeight = if (hideBar) getStatusBarHeight(activity) else 0
mChildOfContent.viewTreeObserver.addOnGlobalLayoutListener {
possiblyResizeChildOfContent()
}
frameLayoutParams = mChildOfContent.layoutParams as FrameLayout.LayoutParams
}
private fun possiblyResizeChildOfContent() {
val usableHeightNow = computeUsableHeight()
if (usableHeightNow != usableHeightPrevious) {
val usableHeightSansKeyboard = mChildOfContent.rootView.height
val heightDifference = usableHeightSansKeyboard - usableHeightNow
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
frameLayoutParams.height =
usableHeightSansKeyboard - heightDifference + statusBarHeight
} else {
// keyboard probably just became hidden
frameLayoutParams.height = contentHeight + statusBarHeight
}
mChildOfContent.requestLayout()
usableHeightPrevious = usableHeightNow
}
}
private fun computeUsableHeight(): Int {
val r = Rect()
mChildOfContent.getWindowVisibleDisplayFrame(r)
return (r.bottom - r.top)
}
private fun getStatusBarHeight(activity: Activity): Int {
return activity.resources.run {
//获取status_bar_height资源的ID
val resourceId = getIdentifier(
"status_bar_height",
"dimen",
"android"
)
if (resourceId > 0) {
//根据资源ID获取响应的尺寸值
getDimensionPixelSize(resourceId)
} else {
0
}
}
}
}

View File

@ -1,15 +1,31 @@
package com.tenlionsoft.baselib.base package com.tenlionsoft.baselib.base
import android.content.Context import android.content.Context
import android.util.Log
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.tenlionsoft.baselib.R import com.tenlionsoft.baselib.R
import kotlin.system.exitProcess import kotlin.system.exitProcess
abstract class BaseActivity : DataBindingActivity() { abstract class BaseActivity : DataBindingActivity() {
//点击空白隐藏软键盘
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
Log.e("BaseActivity", "onTouchEvent: ")
val v = currentFocus
if (v != null && v is EditText) {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(v.windowToken, 0)
}
}
return super.onTouchEvent(event)
}
fun exitApp() { fun exitApp() {
android.os.Process.killProcess(android.os.Process.myPid()); //获取PID android.os.Process.killProcess(android.os.Process.myPid()); //获取PID
exitProcess(0); //常规java、c#的标准退出法返回值为0代表正常退出 exitProcess(0); //常规java、c#的标准退出法返回值为0代表正常退出

View File

@ -1,6 +1,7 @@
package com.tenlionsoft.baselib.base package com.tenlionsoft.baselib.base
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.util.Log
import android.view.ViewGroup import android.view.ViewGroup
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -35,6 +36,7 @@ abstract class BaseBindingAdapter<D, I : ViewDataBinding>(
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun setData(list: List<D>) { fun setData(list: List<D>) {
Log.e("BaseBindingAdapter", "setData: $list")
this.list = list this.list = list
this.notifyDataSetChanged() this.notifyDataSetChanged()
} }

View File

@ -1,7 +1,45 @@
package com.tenlionsoft.baselib.utils package com.tenlionsoft.baselib.utils
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
object TimeUtils { object TimeUtils {
private val SDF_THREAD_LOCAL: ThreadLocal<SimpleDateFormat> = ThreadLocal()
private fun getDefaultFormat(): SimpleDateFormat {
var simpleDateFormat: SimpleDateFormat? = SDF_THREAD_LOCAL.get()
if (simpleDateFormat == null) {
simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
SDF_THREAD_LOCAL.set(simpleDateFormat)
}
return simpleDateFormat
}
private fun getHoursFormat(): SimpleDateFormat {
var simpleDateFormat: SimpleDateFormat? = SDF_THREAD_LOCAL.get()
if (simpleDateFormat == null) {
simpleDateFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
SDF_THREAD_LOCAL.set(simpleDateFormat)
}
return simpleDateFormat
}
fun getNowDateMillis(): Long { fun getNowDateMillis(): Long {
return System.currentTimeMillis() return System.currentTimeMillis()
} }
fun millis2String(millis: Long): String {
return getDefaultFormat().format(Date(millis))
}
fun millis2HMStr(millis: Long): String {
return getHoursFormat().format(Date(millis))
}
fun millis2String(millis: Long, format: DateFormat): String {
return format.format(Date(millis))
}
} }

View File

@ -0,0 +1,53 @@
package com.tenlionsoft.baselib.widget
import android.app.Activity
import android.graphics.Rect
import android.view.ViewTreeObserver
class SoftKeyBoardListener {
fun setChangeListener(
activity: Activity,
funShow: (i: Int) -> Unit,
funHide: (i: Int) -> Unit
) {
//纪录根视图的显示高度
var rootViewVisibleHeight = 0
//获取activity的根视图
val rootView = activity.window.decorView
//监听视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变
rootView.viewTreeObserver.addOnGlobalLayoutListener(
//获取当前根视图在屏幕上显示的大小
ViewTreeObserver.OnGlobalLayoutListener {
val r = Rect()
rootView.getWindowVisibleDisplayFrame(r)
val visibleHeight = r.height()
if (rootViewVisibleHeight == 0) {
rootViewVisibleHeight = visibleHeight
return@OnGlobalLayoutListener
}
//根视图显示高度没有变化,可以看作软键盘显示/隐藏状态没有改变
if (rootViewVisibleHeight == visibleHeight) {
return@OnGlobalLayoutListener
}
//根视图显示高度变小超过200可以看作软键盘显示了
if (rootViewVisibleHeight - visibleHeight > 200) {
funShow(rootViewVisibleHeight - visibleHeight)
rootViewVisibleHeight = visibleHeight
return@OnGlobalLayoutListener
}
//根视图显示高度变大超过200可以看作软键盘隐藏了
if (visibleHeight - rootViewVisibleHeight > 200) {
funHide(visibleHeight - rootViewVisibleHeight)
rootViewVisibleHeight = visibleHeight
return@OnGlobalLayoutListener
}
})
}
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<corners android:radius="5dp" /> <corners android:radius="8dp" />
<solid android:color="#D0A4A4A4" /> <solid android:color="#FFFFFF" />
</shape> </shape>

View File

@ -35,7 +35,8 @@ smartRefreshLayout = "2.1.0"
jjwt = "3.10.3" jjwt = "3.10.3"
emoji2Emojipicker = "1.0.0-alpha03" emoji2Emojipicker = "1.0.0-alpha03"
xxpermissions = "20.0" xxpermissions = "20.0"
filepicker="2.0.0" filepicker = "2.0.0"
room-ktx = "2.2.5"
[libraries] [libraries]
@ -84,8 +85,8 @@ refreshMaterial = { group = "io.github.scwang90", name = "refresh-header-materia
jjwt = { group = "com.auth0", name = "java-jwt", version.ref = "jjwt" } jjwt = { group = "com.auth0", name = "java-jwt", version.ref = "jjwt" }
emojipicker = { group = "androidx.emoji2", name = "emoji2-emojipicker", version.ref = "emoji2Emojipicker" } emojipicker = { group = "androidx.emoji2", name = "emoji2-emojipicker", version.ref = "emoji2Emojipicker" }
xxpermissions = { group = "com.github.getActivity", name = "XXPermissions", version.ref = "xxpermissions" } xxpermissions = { group = "com.github.getActivity", name = "XXPermissions", version.ref = "xxpermissions" }
filepicker={group="com.github.atwa",name="filepicker",version.ref="filepicker"} filepicker = { group = "com.github.atwa", name = "filepicker", version.ref = "filepicker" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room-ktx" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }