消息状态

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
android:name=".page.activity.ChatActivity"
android:exported="false"
android:windowSoftInputMode="adjustPan|stateHidden" />
android:windowSoftInputMode="adjustPan|stateAlwaysHidden" />
<activity
android:name=".page.activity.SplashActivity"
android:exported="true"

View File

@ -3,6 +3,7 @@ package com.tenlionsoft.aimz_k
import com.google.gson.Gson
import com.tenlionsoft.aimz_k.model.CoverSealedBean
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.MsgTypeStateEnum
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 {
when (viewType) {
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 -> {
return ItemMsgOtherBinding.inflate(
return ItemMsgMyBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
@ -86,46 +90,55 @@ class ChatMsgAdapter(datas: List<MsgBean>, viewModel: ChatPageViewModel) :
override fun bindItem(holder: BaseViewHolder<ViewDataBinding>, position: Int) {
val itemType = getItemViewType(position)
when (itemType) {
//发送文本
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).bean = list[position]
(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 -> {
(holder.binding as ItemMsgMyVideoBinding).pos = position
(holder.binding as ItemMsgMyVideoBinding).bean = list[position]
(holder.binding as ItemMsgMyVideoBinding).state = list[position].status
}//发送视频
}
//收到视频
MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE -> {
(holder.binding as ItemMsgOtherVideoBinding).pos = position
(holder.binding as ItemMsgOtherVideoBinding).bean = list[position]
(holder.binding as ItemMsgOtherVideoBinding).state = list[position].status
}//收到视频
}
//发送图片
MsgTypeStateEnum.MSG_TO_OTHER_IMG -> {
(holder.binding as ItemMsgMyImgBinding).pos = position
(holder.binding as ItemMsgMyImgBinding).bean = list[position]
(holder.binding as ItemMsgMyImgBinding).state = list[position].status
}//发送图片
}
//收到图片
MsgTypeStateEnum.MSG_FROM_OTHER_IMG -> {
(holder.binding as ItemMsgOtherImgBinding).pos = position
(holder.binding as ItemMsgOtherImgBinding).bean = list[position]
(holder.binding as ItemMsgOtherImgBinding).state = list[position].status
}//收到图片
}
//发送语音
MsgTypeStateEnum.MSG_TO_OTHER_VOICE -> {
(holder.binding as ItemMsgMyAudioBinding).pos = position
(holder.binding as ItemMsgMyAudioBinding).bean = list[position]
(holder.binding as ItemMsgMyAudioBinding).state = list[position].status
}//语音
}
//收到语音
MsgTypeStateEnum.MSG_FROM_OTHER_VOICE -> {
(holder.binding as ItemMsgOtherAudioBinding).pos = position
(holder.binding as ItemMsgOtherAudioBinding).bean = list[position]
(holder.binding as ItemMsgOtherAudioBinding).state = list[position].status
}//语音
}
else -> {
throw IllegalArgumentException("Invalid view type")
}

View File

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

View File

@ -4,6 +4,7 @@ import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.tenlionsoft.aimz_k.ConvertBeanUtils
@Dao
interface MsgCategoryDao {
@ -14,13 +15,13 @@ interface MsgCategoryDao {
* @return
*/
@Query("SELECT * FROM db_category")
fun getAllCategory(): List<MsgCategoryBean>
suspend fun getAllCategory(): List<MsgCategoryBean?>
/**
* 根据发送人ID获取
*/
@Query("SELECT * FROM db_category WHERE senderId=:senderId")
fun getCategoryBySenderId(senderId: String): MsgCategoryBean?
suspend fun getCategoryBySenderId(senderId: String): MsgCategoryBean?
/**
* 根据ID获取消息
@ -29,7 +30,7 @@ interface MsgCategoryDao {
* @return
*/
@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
*/
@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)
*/
@Query("SELECT * FROM db_category t1 ORDER by t1.timestamp ASC LIMIT (((:page)-1)*(:pageSize)),(:pageSize)")
fun getCategoryPage(
suspend fun getCategoryPage(
pageSize: Int,
page: Int
): 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)")
fun delChatHistory(from: String?, to: String?)
suspend fun delChatHistory(from: String?, to: String?)
/**
* 添加消息多个
@ -63,13 +64,13 @@ interface MsgCategoryDao {
* @param beans
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(vararg beans: MsgCategoryBean)
suspend fun insertAll(vararg beans: MsgCategoryBean)
/**
* 批量添加
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(beans: List<MsgCategoryBean>)
suspend fun insertAll(beans: List<MsgCategoryBean>)
/**
@ -78,23 +79,35 @@ interface MsgCategoryDao {
* @param bean
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertMsg(bean: MsgCategoryBean)
suspend fun insertMsg(bean: MsgCategoryBean)
/**
* 清除全部聊天记录
*/
@Query("DELETE FROM db_category")
fun delAllMsg()
suspend fun delAllMsg()
/**
* 根据发送人的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_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
*/
@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.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.atwa.filepicker.core.FilePicker
import com.tenlionsoft.aimz_k.R
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.contacts.ProjectConfig
import com.tenlionsoft.baselib.utils.SpUtils
import com.tenlionsoft.baselib.widget.SoftKeyBoardListener
import com.tenlionsoft.baselib.widget.wheel.WheelView
/**
@ -26,23 +28,33 @@ import com.tenlionsoft.baselib.widget.wheel.WheelView
*/
class ChatActivity : BaseActivity() {
private lateinit var mBinding: ActivityChatBinding
private lateinit var mLocalReceiver: LocalReceiver
var chatPageViewModel: ChatPageViewModel? = null
private val filePicker = FilePicker.getInstance(this)
override fun bindView() {
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_chat);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor =
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 {
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]
mBinding.ivBack.setOnClickListener { finish(); }
mBinding.srlChats.setEnableRefresh(false)
mBinding.srlChats.setEnableLoadMore(false)
@ -53,17 +65,21 @@ class ChatActivity : BaseActivity() {
mBinding.etMsg.append(it.emoji)
}
//显示/隐藏软键盘
chatPageViewModel!!.showSoftKeyboard.observe(this) {
if (it) {
showSoftKeyBoard(mBinding.etMsg)
chatPageViewModel!!.showChooseLayout.value = false
chatPageViewModel!!.showEmojiLayout.value = false
chatPageViewModel!!.showReplyLayout.value = false
} else {
hideSoftKeyboard()
}
}
// chatPageViewModel!!.showSoftKeyboard.observe(this) {
// if (it) {
// showSoftKeyBoard(mBinding.etMsg)
// chatPageViewModel!!.showChooseLayout.value = false
// chatPageViewModel!!.showEmojiLayout.value = false
// chatPageViewModel!!.showReplyLayout.value = false
// val layoutManager = mBinding.rlvChats.layoutManager as LinearLayoutManager
// val itemCount = layoutManager.itemCount
// mBinding.rlvChats.smoothScrollToPosition(itemCount - 1)
// } else {
// hideSoftKeyboard()
// }
// }
mBinding.rlContent.setOnClickListener {
Log.e("ChatActivity", "bindView: 点击")
chatPageViewModel!!.showChooseLayout.value = false
chatPageViewModel!!.showEmojiLayout.value = false
chatPageViewModel!!.showReplyLayout.value = false
@ -107,10 +123,18 @@ class ChatActivity : BaseActivity() {
}
chatPageViewModel!!.scrollListToBottom.observe(this) {
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
}
}
SoftKeyBoardListener().setChangeListener(this@ChatActivity,
{ _ -> Log.e("ChatActivity", "bindView: 显示") },
{ _ -> Log.e("ChatActivity", "bindView: 隐藏") })
registerLocalReceiver()
}

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package com.tenlionsoft.aimz_k.viewmodel
import android.content.Context
import android.content.Intent
import android.util.Log
import android.view.View
@ -27,7 +28,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
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 showSendBtn = MutableLiveData(false)//显示/隐藏发送按钮
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}")
viewModelScope.launch {
val list = getList()
Log.e("ChatPageViewModel", "Init:${list} ")
_msgList.value = 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) {
this.txtMsg.value = s.toString()
showSendBtn.value = s.isNotEmpty()
Log.e("ChatPageViewModel", "onTxtChange: ${showSendBtn.value}")
}
fun onShowReplyLayout() {
Log.e("ChatPageViewModel", "onShowReplyLayout切换前: ${showReplyLayout.value}")
showReplyLayout.value = !showReplyLayout.value!!
Log.e("ChatPageViewModel", "onShowReplyLayout切换后: ${showReplyLayout.value}")
showEmojiLayout.value = false
showChooseLayout.value = false
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) {
if (txtMsg.value!!.isNotEmpty()) {
val intent = Intent(ProjectConfig.A_S_MSG_SEND)
val b = buildSendBean()
val b = buildSendBean(ProjectConfig.MSG_TEXT)
viewModelScope.launch {
withContext(Dispatchers.IO) {
val dao = DbManager.db.msgDao()
val msgBean = ConvertBeanUtils.convertBeanToMsgBean(b)
dao.insertMsg(msgBean)
val msgBean = insertMsgBeanToDb(b)
_msgList.value = _msgList.value?.plus(msgBean)
adapter.setData(_msgList.value!!)
}
}
Log.e("ChatPageViewModel", "sendTxtMsg: ${b}")
intent.putExtra("msgBean", b)
v.context.sendBroadcast(intent)
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?) {
if (!msg.isNullOrEmpty()) {
val bean = ConvertBeanUtils.covertBean(msg)
Log.e("ChatPageViewModel", "receiveMsg: ${bean}")
when (bean) {
//状态消息
//消息状态
is CoverSealedBean.StateBean -> {
viewModelScope.launch {
withContext(Dispatchers.IO) {
val dao = DbManager.db.msgDao()
//TODO 并且更新Category表
dao.updateStatus(
bean.data.messageId,
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)
}
//TODO 刷新列表
refresh()
}
}
//消息
//消息
is CoverSealedBean.Msg -> {
//插入信息表
//并更新category表
@ -191,24 +213,32 @@ class ChatPageViewModel(private val fromId: String, private val toId: String) :
msgDao.insertMsg(bean.msgBean)
cDao.updateOrInsert(bean.msgBean.senderId!!, bean.msgBean)
}
addBeanToView(bean.msgBean)
sendStatusMsg(bean.msgBean.messageId!!)
}
//刷新列表
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)
var body = mGson.toJson(bodyBean)
val body = mGson.toJson(bodyBean)
return MsgConvertBean(
body = body,
customMessageType = "",
messageId = TimeUtils.getNowDateMillis().toString(),
messageType = ProjectConfig.MSG_TEXT,
messageType = type,
metadata = "",
timestamp = TimeUtils.getNowDateMillis(),
sender = Sender(
@ -216,10 +246,33 @@ class ChatPageViewModel(private val fromId: String, private val toId: String) :
senderType = ""
),
receiver = Receiver(
receiverId = "2",
receiverId = fromId,
receiverType = "SINGLE_USER"
),
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
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.tenlionsoft.aimz_k.ConvertBeanUtils
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.DbManager
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.launch
import kotlinx.coroutines.withContext
@ -16,14 +27,11 @@ class MsgViewModel : ViewModel() {
private val _pwdList = MutableLiveData<List<MsgCategoryBean>>()
var adapter: CategoryAdapter = CategoryAdapter(_pwdList.value ?: emptyList(), this)
var onItemClickListener: OnItemClickListener? = null
val isLogining = MutableLiveData<Boolean>(false)
val isLogining = MutableLiveData(false)
private val mGson = Gson()
private var mCurrentPage: Int = 1
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)
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.widget.ImageView
import android.widget.TextView
import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -9,16 +10,20 @@ import androidx.recyclerview.widget.RecyclerView.Adapter
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions
import com.google.gson.Gson
import com.tenlionsoft.aimz_k.R
import com.tenlionsoft.aimz_k.model.BodyContent
import com.tenlionsoft.baselib.contacts.ProjectConfig
import com.tenlionsoft.baselib.utils.TimeUtils
object BindingUtils {
@BindingAdapter("setAdapter")
@BindingAdapter("setLinearLayoutAdapter")
@JvmStatic
fun bindAdapterToRecyclerView(recyclerView: RecyclerView, adapter: Adapter<*>) {
recyclerView.layoutManager = LinearLayoutManager(recyclerView.context)
val linearLayoutManager = LinearLayoutManager(recyclerView.context)
recyclerView.layoutManager = linearLayoutManager
recyclerView.adapter = adapter
}
@ -48,11 +53,35 @@ object BindingUtils {
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")
@JvmStatic
fun sending(view: View, status: String) {
fun sending(view: View, status: String?) {
if (status.isNullOrEmpty()) {
view.visibility = View.GONE
} else {
when (status) {
ProjectConfig.MSG_SEND_SUCCESS -> view.visibility = View.GONE
ProjectConfig.MSG_SEND_FAIL -> view.visibility = View.GONE
@ -60,16 +89,22 @@ object BindingUtils {
else -> view.visibility = View.GONE
}
}
}
//发送失败
@BindingAdapter("isShowFail")
@JvmStatic
fun sendFail(view: View, status: String) {
fun sendFail(view: View, status: String?) {
if (status.isNullOrEmpty()) {
view.visibility = View.GONE
} else {
when (status) {
ProjectConfig.MSG_SEND_SUCCESS -> view.visibility = View.GONE
ProjectConfig.MSG_SEND_FAIL -> view.visibility = View.GONE
ProjectConfig.MSG_SEND_ING -> view.visibility = View.VISIBLE
ProjectConfig.MSG_SEND_FAIL -> view.visibility = View.VISIBLE
ProjectConfig.MSG_SEND_ING -> view.visibility = View.GONE
else -> view.visibility = View.GONE
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,29 +7,44 @@
android:layout_height="48dp"
android:layout_margin="10dp"
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: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
android:id="@+id/ll_load"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp">
android:layout_marginTop="10dp"
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
android:id="@+id/pb_socket_loading"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_gravity="center_vertical"
android:indeterminateBehavior="repeat"
android:indeterminateDrawable="@drawable/anim_loading" />
@ -42,27 +57,7 @@
android:layout_marginStart="8dp"
android:text="登陆中..."
android:textColor="@color/gray_6f"
android:textSize="12sp" />
</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" />
android:textSize="10sp" />
</LinearLayout>
</RelativeLayout>
</layout>

View File

@ -66,4 +66,5 @@ dependencies {
api(libs.emojipicker)
api(libs.xxpermissions)
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
import android.content.Context
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.core.content.ContextCompat
import com.tenlionsoft.baselib.R
import kotlin.system.exitProcess
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() {
android.os.Process.killProcess(android.os.Process.myPid()); //获取PID
exitProcess(0); //常规java、c#的标准退出法返回值为0代表正常退出

View File

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

View File

@ -1,7 +1,45 @@
package com.tenlionsoft.baselib.utils
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
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 {
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"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="5dp" />
<solid android:color="#D0A4A4A4" />
<corners android:radius="8dp" />
<solid android:color="#FFFFFF" />
</shape>

View File

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