From 9a4e454ac55e1f9325057272b3f49e95f27ce35d Mon Sep 17 00:00:00 2001 From: itgaojian Date: Fri, 1 Nov 2024 17:54:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 +- .../tenlionsoft/aimz_k/ConvertBeanUtils.kt | 17 +++ .../aimz_k/adapter/ChatMsgAdapter.kt | 41 ++++--- .../aimz_k/model/MsgCategoryBean.kt | 2 +- .../aimz_k/model/MsgCategoryDao.kt | 37 ++++-- .../aimz_k/model/MsgCategoryDaoImpl.kt | 17 --- .../com/tenlionsoft/aimz_k/model/MsgDao.kt | 2 +- .../aimz_k/page/activity/ChatActivity.kt | 54 ++++++--- .../aimz_k/page/activity/MainActivity.kt | 27 +++-- .../aimz_k/page/fragments/MsgFragment.kt | 20 ++-- .../aimz_k/viewmodel/ChatPageViewModel.kt | 111 +++++++++++++----- .../aimz_k/viewmodel/MsgViewModel.kt | 90 ++++++++++++-- .../tenlionsoft/aimz_k/widget/BindingUtils.kt | 63 +++++++--- app/src/main/res/layout/activity_chat.xml | 18 ++- app/src/main/res/layout/fragment_msg.xml | 2 +- app/src/main/res/layout/item_category.xml | 20 ++-- app/src/main/res/layout/item_msg_my.xml | 4 +- app/src/main/res/layout/item_msg_my_audio.xml | 3 +- app/src/main/res/layout/item_msg_my_img.xml | 3 +- app/src/main/res/layout/item_msg_my_video.xml | 3 +- app/src/main/res/layout/item_msg_other.xml | 8 +- .../main/res/layout/item_msg_other_img.xml | 3 +- .../main/res/layout/item_msg_other_video.xml | 3 +- app/src/main/res/layout/layout_search.xml | 63 +++++----- baselib/build.gradle.kts | 1 + .../baselib/base/AndroidBug5497Workaround.kt | 79 +++++++++++++ .../tenlionsoft/baselib/base/BaseActivity.kt | 16 +++ .../baselib/base/BaseBindingAdapter.kt | 2 + .../tenlionsoft/baselib/utils/TimeUtils.kt | 38 ++++++ .../baselib/widget/SoftKeyBoardListener.kt | 53 +++++++++ .../src/main/res/drawable/shp_tr_radius_5.xml | 4 +- gradle/libs.versions.toml | 7 +- 32 files changed, 617 insertions(+), 196 deletions(-) delete mode 100644 app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryDaoImpl.kt create mode 100644 baselib/src/main/java/com/tenlionsoft/baselib/base/AndroidBug5497Workaround.kt create mode 100644 baselib/src/main/java/com/tenlionsoft/baselib/widget/SoftKeyBoardListener.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2bb1445..350275b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,7 +30,7 @@ + android:windowSoftInputMode="adjustPan|stateAlwaysHidden" /> , 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, viewModel: ChatPageViewModel) : override fun bindItem(holder: BaseViewHolder, 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") } diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryBean.kt b/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryBean.kt index 410d1ea..b06c59d 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryBean.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryBean.kt @@ -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") diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryDao.kt b/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryDao.kt index 544918b..48e631a 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryDao.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryDao.kt @@ -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 + suspend fun getAllCategory(): List /** * 根据发送人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 + suspend fun getCategoryById(id: String): List /** * 根据信息来源查询消息 @@ -38,14 +39,14 @@ interface MsgCategoryDao { * @return */ @Query("SELECT * FROM db_category WHERE `senderId`=(:from)") - fun getCategoryByFrom(from: String?): List + suspend fun getCategoryByFrom(from: String?): List /** * 查询与单人的聊天记录 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 @@ -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) + suspend fun insertAll(beans: List) /** @@ -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) } \ No newline at end of file diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryDaoImpl.kt b/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryDaoImpl.kt deleted file mode 100644 index 7c31ec5..0000000 --- a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryDaoImpl.kt +++ /dev/null @@ -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!!) - } - } - - -} \ No newline at end of file diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgDao.kt b/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgDao.kt index f859930..79f3883 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgDao.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgDao.kt @@ -111,7 +111,7 @@ interface MsgDao { * @param bean */ @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertMsg(bean: MsgBean) + suspend fun insertMsg(bean: MsgBean) /** * 清除全部聊天记录 diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/ChatActivity.kt b/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/ChatActivity.kt index d8a3aa1..305ad68 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/ChatActivity.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/ChatActivity.kt @@ -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 create(modelClass: Class): 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() } diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/MainActivity.kt b/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/MainActivity.kt index 73334fb..1a124f0 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/MainActivity.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/MainActivity.kt @@ -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 -> {} } } diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MsgFragment.kt b/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MsgFragment.kt index 1e9bda4..4305faf 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MsgFragment.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MsgFragment.kt @@ -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) } } diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ChatPageViewModel.kt b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ChatPageViewModel.kt index 1324cf4..7e988f4 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ChatPageViewModel.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ChatPageViewModel.kt @@ -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("") 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 = "", + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MsgViewModel.kt b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MsgViewModel.kt index d1e1ad7..3280248 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MsgViewModel.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MsgViewModel.kt @@ -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>() var adapter: CategoryAdapter = CategoryAdapter(_pwdList.value ?: emptyList(), this) var onItemClickListener: OnItemClickListener? = null - val isLogining = MutableLiveData(false) + val isLogining = MutableLiveData(false) + private val mGson = Gson() private var mCurrentPage: Int = 1 var isHasMore: MutableLiveData = MutableLiveData(false) - init { - getList(true) - } - /** * 获取列表数据 @@ -72,14 +80,76 @@ class MsgViewModel : ViewModel() { } //接收到信息 - fun fromMsg(msg: String) { - val bean = ConvertBeanUtils.covertBean(msg) - when (bean) { - is CoverSealedBean.StateBean -> {}//消息状态 - is CoverSealedBean.Msg -> {}//消息 + fun fromMsg(msg: String, context: Context) { + Log.e("MsgViewModel", "fromMsg: ${msg}") + if (msg.isNotEmpty()) { + val bean = ConvertBeanUtils.covertBean(msg) + when (bean) { + //状态消息 + 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 = "", + ) + } + /** * 刷新 */ diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/widget/BindingUtils.kt b/app/src/main/java/com/tenlionsoft/aimz_k/widget/BindingUtils.kt index cece061..7d14e58 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/widget/BindingUtils.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/widget/BindingUtils.kt @@ -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,28 +53,58 @@ 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) { - 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 - else -> view.visibility = View.GONE + 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 + ProjectConfig.MSG_SEND_ING -> view.visibility = View.VISIBLE + else -> view.visibility = View.GONE + } } } + //发送失败 @BindingAdapter("isShowFail") @JvmStatic - fun sendFail(view: View, status: String) { - 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 - else -> view.visibility = View.GONE + 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.VISIBLE + ProjectConfig.MSG_SEND_ING -> view.visibility = View.GONE + else -> view.visibility = View.GONE + } } } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_chat.xml b/app/src/main/res/layout/activity_chat.xml index cc7d3e2..f52765d 100644 --- a/app/src/main/res/layout/activity_chat.xml +++ b/app/src/main/res/layout/activity_chat.xml @@ -42,6 +42,7 @@ android:src="@drawable/ic_back_left" /> - + android:layout_height="match_parent"> + + + diff --git a/app/src/main/res/layout/item_category.xml b/app/src/main/res/layout/item_category.xml index 79f281c..c6e5c38 100644 --- a/app/src/main/res/layout/item_category.xml +++ b/app/src/main/res/layout/item_category.xml @@ -37,13 +37,12 @@ android:layout_centerInParent="true" android:scaleType="fitXY" tools:src="@drawable/app_logo_small" /> - + + android:src="@drawable/shp_circle_red" /> @@ -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="测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试" /> + android:background="#E6E6E6" + android:visibility="@{pos< size?View.VISIBLE:View.GONE}" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_msg_my.xml b/app/src/main/res/layout/item_msg_my.xml index f18041c..4945df0 100644 --- a/app/src/main/res/layout/item_msg_my.xml +++ b/app/src/main/res/layout/item_msg_my.xml @@ -46,6 +46,7 @@ android:layout_height="15dp" android:layout_centerInParent="true" android:indeterminateBehavior="repeat" + isShowLoading="@{state}" android:indeterminateDrawable="@drawable/anim_loading" /> @@ -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" diff --git a/app/src/main/res/layout/item_msg_my_audio.xml b/app/src/main/res/layout/item_msg_my_audio.xml index c24ee8e..074495c 100644 --- a/app/src/main/res/layout/item_msg_my_audio.xml +++ b/app/src/main/res/layout/item_msg_my_audio.xml @@ -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" /> diff --git a/app/src/main/res/layout/item_msg_my_img.xml b/app/src/main/res/layout/item_msg_my_img.xml index 464e21f..b140e3f 100644 --- a/app/src/main/res/layout/item_msg_my_img.xml +++ b/app/src/main/res/layout/item_msg_my_img.xml @@ -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" /> diff --git a/app/src/main/res/layout/item_msg_my_video.xml b/app/src/main/res/layout/item_msg_my_video.xml index 687b99e..c58df55 100644 --- a/app/src/main/res/layout/item_msg_my_video.xml +++ b/app/src/main/res/layout/item_msg_my_video.xml @@ -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" /> diff --git a/app/src/main/res/layout/item_msg_other.xml b/app/src/main/res/layout/item_msg_other.xml index 54181f5..d82fb05 100644 --- a/app/src/main/res/layout/item_msg_other.xml +++ b/app/src/main/res/layout/item_msg_other.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> + @@ -34,6 +35,7 @@ + android:textSize="14sp" /> diff --git a/app/src/main/res/layout/item_msg_other_img.xml b/app/src/main/res/layout/item_msg_other_img.xml index 7f79b8c..5b85c45 100644 --- a/app/src/main/res/layout/item_msg_other_img.xml +++ b/app/src/main/res/layout/item_msg_other_img.xml @@ -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" /> diff --git a/app/src/main/res/layout/item_msg_other_video.xml b/app/src/main/res/layout/item_msg_other_video.xml index a2edba9..504a9d3 100644 --- a/app/src/main/res/layout/item_msg_other_video.xml +++ b/app/src/main/res/layout/item_msg_other_video.xml @@ -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" /> diff --git a/app/src/main/res/layout/layout_search.xml b/app/src/main/res/layout/layout_search.xml index 7b5396d..bb4b4a9 100644 --- a/app/src/main/res/layout/layout_search.xml +++ b/app/src/main/res/layout/layout_search.xml @@ -7,29 +7,44 @@ android:layout_height="48dp" android:layout_margin="10dp" android:background="@drawable/shp_white_5_radius" - android:gravity="center_vertical" android:orientation="horizontal"> + + + + + + + + + android:layout_marginTop="10dp" + android:layout_marginRight="10dp"> - @@ -42,27 +57,7 @@ android:layout_marginStart="8dp" android:text="登陆中..." android:textColor="@color/gray_6f" - android:textSize="12sp" /> - - - - - - - + android:textSize="10sp" /> \ No newline at end of file diff --git a/baselib/build.gradle.kts b/baselib/build.gradle.kts index b02b89a..1968211 100644 --- a/baselib/build.gradle.kts +++ b/baselib/build.gradle.kts @@ -66,4 +66,5 @@ dependencies { api(libs.emojipicker) api(libs.xxpermissions) api(libs.filepicker) + api(libs.room.ktx) } \ No newline at end of file diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/base/AndroidBug5497Workaround.kt b/baselib/src/main/java/com/tenlionsoft/baselib/base/AndroidBug5497Workaround.kt new file mode 100644 index 0000000..d2736fc --- /dev/null +++ b/baselib/src/main/java/com/tenlionsoft/baselib/base/AndroidBug5497Workaround.kt @@ -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 + } + } + } +} \ No newline at end of file diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseActivity.kt b/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseActivity.kt index 71a2e77..1b54c25 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseActivity.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseActivity.kt @@ -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代表正常退出 diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseBindingAdapter.kt b/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseBindingAdapter.kt index cb917de..896a7fd 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseBindingAdapter.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseBindingAdapter.kt @@ -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( @SuppressLint("NotifyDataSetChanged") fun setData(list: List) { + Log.e("BaseBindingAdapter", "setData: $list") this.list = list this.notifyDataSetChanged() } diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/utils/TimeUtils.kt b/baselib/src/main/java/com/tenlionsoft/baselib/utils/TimeUtils.kt index 3139f8c..7b741ef 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/utils/TimeUtils.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/utils/TimeUtils.kt @@ -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 = 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)) + } + + } \ No newline at end of file diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/widget/SoftKeyBoardListener.kt b/baselib/src/main/java/com/tenlionsoft/baselib/widget/SoftKeyBoardListener.kt new file mode 100644 index 0000000..070e6d9 --- /dev/null +++ b/baselib/src/main/java/com/tenlionsoft/baselib/widget/SoftKeyBoardListener.kt @@ -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 + } + }) + } + + +} \ No newline at end of file diff --git a/baselib/src/main/res/drawable/shp_tr_radius_5.xml b/baselib/src/main/res/drawable/shp_tr_radius_5.xml index 27e5291..19f9f48 100644 --- a/baselib/src/main/res/drawable/shp_tr_radius_5.xml +++ b/baselib/src/main/res/drawable/shp_tr_radius_5.xml @@ -1,7 +1,7 @@ - - + + \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 576bd39..fcb60db 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" }