From db9ad383fa56a908962f8a3dfd1b98cd944d93d7 Mon Sep 17 00:00:00 2001 From: itgaojian Date: Mon, 4 Nov 2024 17:36:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E9=80=81=E6=96=87=E4=BB=B6,=E5=9B=BE?= =?UTF-8?q?=E7=89=87,=E8=A7=86=E9=A2=91,=E5=A4=84=E7=90=86=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aimz_k/adapter/ChatMsgAdapter.kt | 110 ++++++--- .../aimz_k/model/FileUploadStateBean.kt | 14 ++ .../com/tenlionsoft/aimz_k/net/UserApi.kt | 28 ++- .../aimz_k/page/activity/ChatActivity.kt | 153 +++++++++---- .../aimz_k/page/activity/LoginActivity.kt | 18 +- .../aimz_k/page/activity/MainActivity.kt | 2 +- .../aimz_k/page/fragments/MsgFragment.kt | 3 +- .../aimz_k/services/SocketService.kt | 1 + .../aimz_k/viewmodel/ChatPageViewModel.kt | 211 +++++++++++++++--- .../aimz_k/viewmodel/LoginPageViewModel.kt | 2 +- .../aimz_k/viewmodel/MsgViewModel.kt | 7 +- .../tenlionsoft/aimz_k/widget/BindingUtils.kt | 136 ++++++++++- app/src/main/res/drawable-xhdpi/ic_audio.png | Bin 0 -> 798 bytes app/src/main/res/drawable-xhdpi/ic_excel.png | Bin 0 -> 1037 bytes app/src/main/res/drawable-xhdpi/ic_file.png | Bin 0 -> 556 bytes app/src/main/res/drawable-xhdpi/ic_image.png | Bin 0 -> 805 bytes app/src/main/res/drawable-xhdpi/ic_mp.png | Bin 0 -> 1014 bytes app/src/main/res/drawable-xhdpi/ic_pdf.png | Bin 0 -> 961 bytes app/src/main/res/drawable-xhdpi/ic_ppt.png | Bin 0 -> 891 bytes app/src/main/res/drawable-xhdpi/ic_psd.png | Bin 0 -> 1032 bytes app/src/main/res/drawable-xhdpi/ic_word.png | Bin 0 -> 1038 bytes app/src/main/res/drawable-xhdpi/ic_zip.png | Bin 0 -> 830 bytes app/src/main/res/layout/activity_chat.xml | 1 - app/src/main/res/layout/item_msg_my_file.xml | 94 ++++++++ app/src/main/res/layout/item_msg_my_img.xml | 7 +- app/src/main/res/layout/item_msg_my_video.xml | 11 +- .../main/res/layout/item_msg_other_file.xml | 83 +++++++ .../main/res/layout/item_msg_other_img.xml | 1 + .../main/res/layout/item_msg_other_video.xml | 1 + .../baselib/contacts/ProjectConfig.kt | 3 +- .../tenlionsoft/baselib/model/ImageSize.kt | 21 ++ .../baselib/net/BaseUrlInterceptor.kt | 8 +- .../tenlionsoft/baselib/utils/ImageUtils.kt | 62 +++++ .../widget/AdapterItemClickListener.kt | 5 + .../res/drawable-xhdpi/ic_img_load_err.png | Bin 0 -> 8456 bytes 35 files changed, 844 insertions(+), 138 deletions(-) create mode 100644 app/src/main/java/com/tenlionsoft/aimz_k/model/FileUploadStateBean.kt create mode 100644 app/src/main/res/drawable-xhdpi/ic_audio.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_excel.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_file.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_image.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_mp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_pdf.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_ppt.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_psd.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_word.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_zip.png create mode 100644 app/src/main/res/layout/item_msg_my_file.xml create mode 100644 app/src/main/res/layout/item_msg_other_file.xml create mode 100644 baselib/src/main/java/com/tenlionsoft/baselib/model/ImageSize.kt create mode 100644 baselib/src/main/java/com/tenlionsoft/baselib/utils/ImageUtils.kt create mode 100644 baselib/src/main/java/com/tenlionsoft/baselib/widget/AdapterItemClickListener.kt create mode 100644 baselib/src/main/res/drawable-xhdpi/ic_img_load_err.png diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/adapter/ChatMsgAdapter.kt b/app/src/main/java/com/tenlionsoft/aimz_k/adapter/ChatMsgAdapter.kt index 1a6823c..5315d95 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/adapter/ChatMsgAdapter.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/adapter/ChatMsgAdapter.kt @@ -5,10 +5,12 @@ import android.view.ViewGroup import androidx.databinding.ViewDataBinding import com.tenlionsoft.aimz_k.databinding.ItemMsgMyAudioBinding import com.tenlionsoft.aimz_k.databinding.ItemMsgMyBinding +import com.tenlionsoft.aimz_k.databinding.ItemMsgMyFileBinding import com.tenlionsoft.aimz_k.databinding.ItemMsgMyImgBinding import com.tenlionsoft.aimz_k.databinding.ItemMsgMyVideoBinding import com.tenlionsoft.aimz_k.databinding.ItemMsgOtherAudioBinding import com.tenlionsoft.aimz_k.databinding.ItemMsgOtherBinding +import com.tenlionsoft.aimz_k.databinding.ItemMsgOtherFileBinding import com.tenlionsoft.aimz_k.databinding.ItemMsgOtherImgBinding import com.tenlionsoft.aimz_k.databinding.ItemMsgOtherVideoBinding import com.tenlionsoft.aimz_k.model.MsgBean @@ -16,67 +18,90 @@ import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum import com.tenlionsoft.aimz_k.viewmodel.ChatPageViewModel import com.tenlionsoft.baselib.base.BaseBindingAdapter -class ChatMsgAdapter(datas: List, viewModel: ChatPageViewModel) : +class ChatMsgAdapter(datas: List, val viewModel: ChatPageViewModel) : BaseBindingAdapter(datas) { override fun getItemBinding(parent: ViewGroup, viewType: Int): ViewDataBinding { when (viewType) { - MsgTypeStateEnum.MSG_TO_OTHER_TXT -> { + //发送文本 + MsgTypeStateEnum.MSG_TO_OTHER_TXT -> return ItemMsgOtherBinding.inflate( LayoutInflater.from(parent.context), parent, false ) - }//发送文本 - MsgTypeStateEnum.MSG_FROM_OTHER_TXT -> { + + //收到文本信息 + MsgTypeStateEnum.MSG_FROM_OTHER_TXT -> return ItemMsgMyBinding.inflate( LayoutInflater.from(parent.context), parent, false ) - }//收到文本信息 - MsgTypeStateEnum.MSG_TO_OTHER_MOVIE -> { - return ItemMsgMyVideoBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ) - }//发送视频 - MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE -> { + + //发送视频 + MsgTypeStateEnum.MSG_TO_OTHER_MOVIE -> return ItemMsgOtherVideoBinding.inflate( LayoutInflater.from(parent.context), parent, false ) - }//收到视频 - MsgTypeStateEnum.MSG_TO_OTHER_IMG -> { - return ItemMsgMyImgBinding.inflate( + + //收到视频 + MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE -> + return ItemMsgMyVideoBinding.inflate( LayoutInflater.from(parent.context), parent, false ) - }//发送图片 - MsgTypeStateEnum.MSG_FROM_OTHER_IMG -> { + + //发送图片 + MsgTypeStateEnum.MSG_TO_OTHER_IMG -> return ItemMsgOtherImgBinding.inflate( LayoutInflater.from(parent.context), parent, false ) - }//收到图片 - MsgTypeStateEnum.MSG_TO_OTHER_VOICE -> { + + //收到图片 + MsgTypeStateEnum.MSG_FROM_OTHER_IMG -> + return ItemMsgMyImgBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + + //语音 + MsgTypeStateEnum.MSG_TO_OTHER_VOICE -> return ItemMsgMyAudioBinding.inflate( LayoutInflater.from(parent.context), parent, false ) - }//语音 - MsgTypeStateEnum.MSG_FROM_OTHER_VOICE -> { + //语音 + MsgTypeStateEnum.MSG_FROM_OTHER_VOICE -> return ItemMsgOtherAudioBinding.inflate( LayoutInflater.from(parent.context), parent, false ) - }//语音 + //发送文件 + MsgTypeStateEnum.MSG_TO_OTHER_FILE -> + return ItemMsgOtherFileBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + + //收到文件 + MsgTypeStateEnum.MSG_FROM_OTHER_FILE -> { + return ItemMsgMyFileBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + } + else -> { throw IllegalArgumentException("Invalid view type") } @@ -89,6 +114,9 @@ class ChatMsgAdapter(datas: List, viewModel: ChatPageViewModel) : override fun bindItem(holder: BaseViewHolder, position: Int) { val itemType = getItemViewType(position) + holder.binding.root.setOnClickListener { + viewModel.onItemClickListener?.onItemClick(list[position]) + } when (itemType) { //发送文本 MsgTypeStateEnum.MSG_TO_OTHER_TXT -> { @@ -104,28 +132,28 @@ class ChatMsgAdapter(datas: List, viewModel: ChatPageViewModel) : } //发送视频 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_FROM_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_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_FROM_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_TO_OTHER_VOICE -> { (holder.binding as ItemMsgMyAudioBinding).pos = position @@ -138,6 +166,18 @@ class ChatMsgAdapter(datas: List, viewModel: ChatPageViewModel) : (holder.binding as ItemMsgOtherAudioBinding).bean = list[position] (holder.binding as ItemMsgOtherAudioBinding).state = list[position].status } + //发送文件 + MsgTypeStateEnum.MSG_TO_OTHER_FILE -> { + (holder.binding as ItemMsgOtherFileBinding).pos = position + (holder.binding as ItemMsgOtherFileBinding).bean = list[position] + (holder.binding as ItemMsgOtherFileBinding).state = list[position].status + } + //收到文件 + MsgTypeStateEnum.MSG_FROM_OTHER_FILE -> { + (holder.binding as ItemMsgMyFileBinding).pos = position + (holder.binding as ItemMsgMyFileBinding).bean = list[position] + (holder.binding as ItemMsgMyFileBinding).state = list[position].status + } else -> { throw IllegalArgumentException("Invalid view type") diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/model/FileUploadStateBean.kt b/app/src/main/java/com/tenlionsoft/aimz_k/model/FileUploadStateBean.kt new file mode 100644 index 0000000..c057fc8 --- /dev/null +++ b/app/src/main/java/com/tenlionsoft/aimz_k/model/FileUploadStateBean.kt @@ -0,0 +1,14 @@ +package com.tenlionsoft.aimz_k.model + +data class FileUploadStateBean(var code: Int, var data: FileDataBean) + +//"fileId": "2b9dfe0b-ef6f-466c-84f6-8aedb7c97d8f", +//"fileName": "Screenshot_20230626_092232.jpg", +//"fileSize": 103874, +//"fileUrl": "api/file/download/2b9dfe0b-ef6f-466c-84f6-8aedb7c97d8f" +data class FileDataBean( + var fileId: String?, + var fileName: String?, + var fileSize: Long?, + var fileUrl: String +) diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/net/UserApi.kt b/app/src/main/java/com/tenlionsoft/aimz_k/net/UserApi.kt index fa45e40..ee6ed5b 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/net/UserApi.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/net/UserApi.kt @@ -1,6 +1,7 @@ package com.tenlionsoft.aimz_k.net import com.tenlionsoft.aimz_k.model.BaseSuccessBean +import com.tenlionsoft.aimz_k.model.FileUploadStateBean import com.tenlionsoft.baselib.model.VersionBean import okhttp3.MultipartBody import okhttp3.RequestBody @@ -23,6 +24,7 @@ interface UserApi { @Headers("Content-Type: application/json", "Accept: application/json", "projectName:usercenter") @POST("api/jwt/login") suspend fun doLogin(@Body user: RequestBody): BaseSuccessBean + /** * 登陆Socket系统 * http://192.168.0.26:8888/system/api/anonymous/login @@ -43,7 +45,7 @@ interface UserApi { /** - * 上传图片文件 + * 上传图片 * image * * @param file @@ -52,8 +54,8 @@ interface UserApi { */ @Headers("token:need") @Multipart - @POST("app/file/uploadimage") - suspend fun doUploadImage(@Part file: MultipartBody.Part): BaseSuccessBean + @POST("api/file/upload/image") + suspend fun doUploadImage(@Part file: MultipartBody.Part): FileUploadStateBean /** @@ -66,6 +68,22 @@ interface UserApi { */ @Headers("token:need") @Multipart - @POST("app/file/uploadvideo") - fun doUploadVideo(@Part file: MultipartBody.Part): BaseSuccessBean + @POST("api/file/upload/video") + suspend fun doUploadVideo(@Part file: MultipartBody.Part): FileUploadStateBean + + /** + * 上传文件 + */ + @Headers("token:need") + @Multipart + @POST("api/file/upload/file") + suspend fun doUploadFile(@Part file: MultipartBody.Part): FileUploadStateBean + + /** + * 上传音频 + */ + @Headers("token:need") + @Multipart + @POST("api/file/upload/audio") + suspend fun doUploadAudio(@Part file: MultipartBody.Part): FileUploadStateBean } \ No newline at end of file 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 db2a179..fb559c3 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 @@ -16,11 +16,15 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.atwa.filepicker.core.FilePicker import com.tenlionsoft.aimz_k.R import com.tenlionsoft.aimz_k.databinding.ActivityChatBinding +import com.tenlionsoft.aimz_k.model.MsgBean import com.tenlionsoft.aimz_k.model.PickerType import com.tenlionsoft.aimz_k.viewmodel.ChatPageViewModel import com.tenlionsoft.baselib.base.BaseActivity import com.tenlionsoft.baselib.contacts.ProjectConfig +import com.tenlionsoft.baselib.utils.DensityUtils import com.tenlionsoft.baselib.utils.SpUtils +import com.tenlionsoft.baselib.widget.AdapterItemClickListener +import com.tenlionsoft.baselib.widget.LoadingDialog import com.tenlionsoft.baselib.widget.SoftKeyBoardListener import com.tenlionsoft.baselib.widget.wheel.WheelView @@ -28,13 +32,14 @@ import com.tenlionsoft.baselib.widget.wheel.WheelView /** * 聊天页面 */ -class ChatActivity : BaseActivity() { +class ChatActivity : BaseActivity(), AdapterItemClickListener { private lateinit var mBinding: ActivityChatBinding private lateinit var mLocalReceiver: LocalReceiver var chatPageViewModel: ChatPageViewModel? = null private val filePicker = FilePicker.getInstance(this) private var mContentHeight: Int = 0 - + private var mLoading: LoadingDialog? = null; + private var bottomHeight = -1 override fun bindView() { mBinding = DataBindingUtil.setContentView(this, R.layout.activity_chat); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) @@ -43,9 +48,7 @@ class ChatActivity : BaseActivity() { val fromId = intent.getStringExtra("fromId") //传递过来的接收人 val name = intent.getStringExtra("name") - if (name.isNullOrEmpty()) { - mBinding.tvTitle.text = "临时客户" - } + mBinding.tvTitle.text = if (name.isNullOrEmpty()) "临时客户" else name chatPageViewModel = ViewModelProvider(this, object : ViewModelProvider.Factory { override fun create(modelClass: Class): T { @@ -54,7 +57,7 @@ class ChatActivity : BaseActivity() { ) as T } })[ChatPageViewModel::class.java] - + chatPageViewModel!!.onItemClickListener = this mBinding.ivBack.setOnClickListener { finish(); } mBinding.viewModel = chatPageViewModel mBinding.lifecycleOwner = this @@ -63,27 +66,57 @@ 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 -// 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 -// hideSoftKeyboard() -// } + chatPageViewModel!!.showReplyLayout.observe(this) { + if (it) { + hideSoftKeyboard()//隐藏软键盘 + setContentLayoutHeight() + } else { + val isShow = + chatPageViewModel!!.showEmojiLayout.value!! || chatPageViewModel!!.showChooseLayout.value!! + if (isShow) { + setContentDefaultHeight() + } + } + } + chatPageViewModel!!.showEmojiLayout.observe(this) { + if (it) { + hideSoftKeyboard()//隐藏软键盘 + setContentLayoutHeight() + } else { + val isShow = + chatPageViewModel!!.showReplyLayout.value!! || chatPageViewModel!!.showChooseLayout.value!! + if (isShow) { + setContentDefaultHeight() + } + } + } + chatPageViewModel!!.showChooseLayout.observe(this) { + if (it) { + hideSoftKeyboard()//隐藏软键盘 + setContentLayoutHeight() + } else { + val isShow = + chatPageViewModel!!.showEmojiLayout.value!! || chatPageViewModel!!.showReplyLayout.value!! + if (isShow) { + setContentDefaultHeight() + } + } + } + chatPageViewModel!!.showLoadDialog.observe(this) { + if (it) { + //显示loading + mLoading = LoadingDialog.Builder(this) + .setCancelOutside(false) + .setCancelable(false) + .setMessage("上传中...") + .create() + mLoading!!.show() + } else { + //隐藏loading + mLoading?.dismiss() + mLoading = null + } + } mBinding.wvView.setTextSize(14F, isSp = true) mBinding.wvView.setAutoFitTextSize(true) mBinding.wvView.setOnItemSelectedListener(object : WheelView.OnItemSelectedListener { @@ -91,6 +124,7 @@ class ChatActivity : BaseActivity() { mBinding.etMsg.setText(data.toString()) } }) + chatPageViewModel!!.chooseType.observe(this) { when (it) { PickerType.TYPE_PIC -> { @@ -121,7 +155,7 @@ class ChatActivity : BaseActivity() { } } chatPageViewModel!!.scrollListToBottom.observe(this) { - Log.e("TAG", "bindView:滚动 ${it} ") + Log.e("TAG", "bindView:滚动 $it ") if (it) { val layoutManager = mBinding.rlvChats.layoutManager as LinearLayoutManager val itemCount = layoutManager.itemCount @@ -136,14 +170,24 @@ class ChatActivity : BaseActivity() { chatPageViewModel!!.scrollListToBottom.value = false } } - mBinding.llBottomLayout.visibility = View.VISIBLE mBinding.rlBottom.visibility = View.GONE mBinding.rlvChats.viewTreeObserver.addOnGlobalLayoutListener { mContentHeight = mBinding.rlvChats.height } + + mBinding.llBottomLayout.viewTreeObserver.addOnGlobalLayoutListener { + if (mBinding.llBottomLayout.height == 0) { + bottomHeight = DensityUtils.dp2px(this@ChatActivity, 300F) + } else { + bottomHeight = mBinding.llBottomLayout.height + } + } SoftKeyBoardListener().setChangeListener(this@ChatActivity, { h -> - mBinding.llBottomLayout.visibility = View.VISIBLE + Log.e("ChatActivity", "软键盘: 显示") + chatPageViewModel!!.showReplyLayout.value = false + chatPageViewModel!!.showEmojiLayout.value = false + chatPageViewModel!!.showChooseLayout.value = false mBinding.rlBottom.visibility = View.VISIBLE //重置layout高度 val layoutParams = mBinding.rlvChats.layoutParams @@ -158,16 +202,17 @@ class ChatActivity : BaseActivity() { 0 ) }, 100) - - }, { _ -> - val layoutParams = mBinding.rlvChats.layoutParams - layoutParams.height = mContentHeight - mBinding.rlvChats.layoutParams = layoutParams - mBinding.llBottomLayout.visibility = View.VISIBLE - mBinding.rlBottom.visibility = View.GONE - + Log.e("ChatActivity", "软键盘: 隐藏") + val isShowOther = + chatPageViewModel!!.showReplyLayout.value!! || chatPageViewModel!!.showEmojiLayout.value!! || chatPageViewModel!!.showChooseLayout.value!! + if (!isShowOther) { + val layoutParams = mBinding.rlvChats.layoutParams + layoutParams.height = mContentHeight + mBinding.rlvChats.layoutParams = layoutParams + mBinding.rlBottom.visibility = View.GONE + } }) registerLocalReceiver() } @@ -185,6 +230,31 @@ class ChatActivity : BaseActivity() { unregisterReceiver(mLocalReceiver) } + private fun setContentLayoutHeight() { + mBinding.rlBottom.visibility = View.VISIBLE + //重置layout高度 + val layoutParams = mBinding.rlvChats.layoutParams + layoutParams.height = mContentHeight - bottomHeight + Log.e("ChatActivity", "setContentLayoutHeight: $mContentHeight $bottomHeight") + mBinding.rlvChats.layoutParams = layoutParams + val layoutManager = mBinding.rlvChats.layoutManager as LinearLayoutManager + val itemCount = layoutManager.itemCount + //滚动到底部 + mBinding.rlvChats.postDelayed({ + layoutManager.scrollToPositionWithOffset( + itemCount - 1, + 0 + ) + }, 100) + } + + private fun setContentDefaultHeight() { + val layoutParams = mBinding.rlvChats.layoutParams + layoutParams.height = mContentHeight + mBinding.rlvChats.layoutParams = layoutParams + mBinding.rlBottom.visibility = View.GONE + } + override fun onDestroy() { super.onDestroy() unRegisterLocalReceiver() @@ -195,12 +265,17 @@ class ChatActivity : BaseActivity() { when (intent?.action) { ProjectConfig.A_S_MSG_RECEIVER -> { val msg = intent.getStringExtra("msg") - Log.e("LocalReceiver", "onReceive: ${msg}") + Log.e("LocalReceiver", "onReceive: $msg") chatPageViewModel!!.receiveMsg(msg) }//接收到信息 } } } + + //条目点击 + override fun onItemClick(data: MsgBean) { + + } } diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/LoginActivity.kt b/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/LoginActivity.kt index aafe554..365b938 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/LoginActivity.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/LoginActivity.kt @@ -10,8 +10,8 @@ import com.tenlionsoft.baselib.base.BaseActivity import com.tenlionsoft.baselib.widget.LoadingDialog class LoginActivity : BaseActivity() { - private lateinit var mBinding: ActivityLoginBinding; - private var mLoading: LoadingDialog? = null; + private lateinit var mBinding: ActivityLoginBinding + private var mLoading: LoadingDialog? = null private val loginPageViewModel: LoginPageViewModel by lazy { ViewModelProvider(this)[LoginPageViewModel::class.java] } @@ -40,13 +40,13 @@ class LoginActivity : BaseActivity() { } loginPageViewModel.isLoginSuccess.observe(this) { if (it) { -// startActivity(Intent(this@LoginActivity, MainActivity::class.java)) - startActivity( - Intent( - this@LoginActivity, - ChatActivity::class.java - ).putExtra("fromId", "2") - ) + startActivity(Intent(this@LoginActivity, MainActivity::class.java)) +// startActivity( +// Intent( +// this@LoginActivity, +// ChatActivity::class.java +// ).putExtra("fromId", "2").putExtra("name", "测试") +// ) finish() } } 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 1a124f0..fd1b023 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 @@ -90,7 +90,7 @@ class MainActivity : BaseActivity() { return@setOnItemSelectedListener true; }//我的 } - false; + false } onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { 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 4305faf..3156f64 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 @@ -17,9 +17,10 @@ import com.tenlionsoft.aimz_k.page.activity.MainActivity import com.tenlionsoft.aimz_k.services.SocketService import com.tenlionsoft.aimz_k.viewmodel.MsgViewModel import com.tenlionsoft.baselib.contacts.ProjectConfig +import com.tenlionsoft.baselib.widget.AdapterItemClickListener import com.tenlionsoft.baselib.widget.LoadingDialog -class MsgFragment : Fragment(), MsgViewModel.OnItemClickListener, +class MsgFragment : Fragment(), AdapterItemClickListener, MainActivity.OnSocketConnectListener { private lateinit var mMsgBinding: FragmentMsgBinding private var mActivity: MainActivity? = null diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/services/SocketService.kt b/app/src/main/java/com/tenlionsoft/aimz_k/services/SocketService.kt index c2acbe7..414de22 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/services/SocketService.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/services/SocketService.kt @@ -59,6 +59,7 @@ class SocketService : Service(), WsManager.MsgCallBack { } + @SuppressLint("UnspecifiedRegisterReceiverFlag") private fun registerLocalReceiver() { val intentFilter = IntentFilter() mLocalReceiver = LocalReceiver() 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 311a6d4..5decb01 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 @@ -2,6 +2,9 @@ package com.tenlionsoft.aimz_k.viewmodel import android.content.Context import android.content.Intent +import android.os.Handler +import android.os.Looper +import android.os.Message import android.util.Log import android.view.View import androidx.lifecycle.MutableLiveData @@ -15,6 +18,7 @@ import com.tenlionsoft.aimz_k.adapter.ChatMsgAdapter 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.FileDataBean import com.tenlionsoft.aimz_k.model.MsgBean import com.tenlionsoft.aimz_k.model.MsgConvertBean import com.tenlionsoft.aimz_k.model.PickerType @@ -23,9 +27,11 @@ import com.tenlionsoft.aimz_k.model.Sender import com.tenlionsoft.aimz_k.net.UserApi import com.tenlionsoft.baselib.base.BaseViewModel import com.tenlionsoft.baselib.contacts.ProjectConfig +import com.tenlionsoft.baselib.net.ExParse import com.tenlionsoft.baselib.net.RetrofitClient import com.tenlionsoft.baselib.utils.SpUtils import com.tenlionsoft.baselib.utils.TimeUtils +import com.tenlionsoft.baselib.widget.AdapterItemClickListener import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -34,7 +40,6 @@ import okhttp3.MultipartBody import okhttp3.RequestBody import okhttp3.RequestBody.Companion.asRequestBody - class ChatPageViewModel( private val fromId: String, private val toId: String, @@ -51,7 +56,34 @@ class ChatPageViewModel( private val retrofitClient = RetrofitClient.getInstance(context) private val netApi = retrofitClient.create(UserApi::class.java) var adapter: ChatMsgAdapter = ChatMsgAdapter(_msgList.value ?: emptyList(), this) + var onItemClickListener: AdapterItemClickListener? = null private val mGson: Gson = Gson() + private var msgHandlerList: ArrayList = arrayListOf() + + private val handler = object : Handler(Looper.getMainLooper()) { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + val mBean: MsgConvertBean = msg.obj as MsgConvertBean + val isExist = msgHandlerList.contains(mBean) + if (isExist) { + // 更新数据库 发送失败 + viewModelScope.launch { + withContext(Dispatchers.IO) { + val mDao = DbManager.db.msgDao() + val cDao = DbManager.db.categoryDao() + mDao.updateStatus(mBean.messageId, ProjectConfig.MSG_SEND_FAIL) + cDao.updateStatus(mBean.sender.senderId!!, ProjectConfig.MSG_SEND_FAIL) + } + for (item in _msgList.value!!) { + if (item.messageId == mBean.messageId) { + item.status = ProjectConfig.MSG_SEND_FAIL + } + } + adapter.setData(_msgList.value!!) + } + } + } + } init { Log.e("ChatPageViewModel", "Init: ${showSendBtn.value}") @@ -78,20 +110,18 @@ class ChatPageViewModel( } fun onShowReplyLayout() { - showReplyLayout.value = !showReplyLayout.value!! showEmojiLayout.value = false showChooseLayout.value = false - showSoftKeyboard.value = !showReplyLayout.value!! + showReplyLayout.value = !showReplyLayout.value!! } /** * 切换emoji layout */ fun onShowEmojiLayout() { - showEmojiLayout.value = !showEmojiLayout.value!! showChooseLayout.value = false showReplyLayout.value = false - showSoftKeyboard.value = !showEmojiLayout.value!! + showEmojiLayout.value = !showEmojiLayout.value!! } @@ -99,10 +129,9 @@ class ChatPageViewModel( * 切换选择layout */ fun onShowChooseLayout() { - showChooseLayout.value = !showChooseLayout.value!! showEmojiLayout.value = false showReplyLayout.value = false - showSoftKeyboard.value = !showChooseLayout.value!! + showChooseLayout.value = !showChooseLayout.value!! } /** @@ -134,11 +163,83 @@ class ChatPageViewModel( return } viewModelScope.launch { - doUploadImgs(list) + showLoadDialog.value = true + doUploadImages(list) } } - private suspend fun doUploadImgs(list: List) { + + /** + * 上传视频 + */ + fun uploadVideo(meta: VideoMeta) { + viewModelScope.launch { + showLoadDialog.value = true + doUploadVideo(meta) + } + } + + /** + * 上传文件 + */ + fun uploadFiles(meta: FileMeta) { + viewModelScope.launch { + showLoadDialog.value = true + doUploadFile(meta) + } + } + + private suspend fun doUploadVideo(meta: VideoMeta) { + try { + val requestFile: RequestBody = + meta.file!!.asRequestBody("multipart/form-data".toMediaTypeOrNull()) + val body: MultipartBody.Part = + MultipartBody.Part.createFormData( + "video", + meta.file!!.getName(), + requestFile + ) + + val bean = retrofitClient.makeApiCall { + netApi.doUploadVideo(body) + } + if (bean.code == 200) { + sendMedia(ProjectConfig.MSG_VIDEO, bean.data) + } + showLoadDialog.value = false + } catch (e: Exception) { + e.printStackTrace() + showLoadDialog.value = false + ExParse.parse(e) + } + } + + private suspend fun doUploadFile(meta: FileMeta) { + try { + val requestFile: RequestBody = + meta.file!!.asRequestBody("multipart/form-data".toMediaTypeOrNull()) + val body: MultipartBody.Part = + MultipartBody.Part.createFormData( + "file", + meta.file!!.getName(), + requestFile + ) + val bean = retrofitClient.makeApiCall { + netApi.doUploadFile(body) + } + if (bean.code == 200) { + sendMedia(ProjectConfig.MSG_FILE, bean.data) + } + showLoadDialog.value = false + } catch (e: Exception) { + e.printStackTrace() + showLoadDialog.value = false + ExParse.parse(e) + } + } + + private var count: Int = 1 + private suspend fun doUploadImages(list: List) { try { for (item in list) { if (item != null) { @@ -146,7 +247,7 @@ class ChatPageViewModel( item.file!!.asRequestBody("multipart/form-data".toMediaTypeOrNull()) val body: MultipartBody.Part = MultipartBody.Part.createFormData( - "audio", + "image", item.file!!.getName(), requestFile ) @@ -154,35 +255,40 @@ class ChatPageViewModel( val bean = retrofitClient.makeApiCall { netApi.doUploadImage(body) } + Log.e("ChatPageViewModel", "doUploadImgs: $bean") if (bean.code == 200) { //上传成功 - //TODO 构建消息实体 - //TODO 发送消息 - // - } else { - //TODO 上传失败 + sendMedia(ProjectConfig.MSG_IMG, bean.data) } + ++count } } - + if (count == list.size) { + showLoadDialog.value = false + count = 1 + } } catch (e: Exception) { e.printStackTrace() + showLoadDialog.value = false + count = 1 + ExParse.parse(e) } } - /** - * 上传视频 - */ - fun uploadVideo(meta: VideoMeta) { - + //发送 图片/视频/音频/文件 + private fun sendMedia(type: String, data: FileDataBean) { + val intent = Intent(ProjectConfig.A_S_MSG_SEND) + val b = buildSendMediaBean(type, data) + viewModelScope.launch { + val msgBean = insertMsgBeanToDb(b) + _msgList.value = _msgList.value?.plus(msgBean) + adapter.setData(_msgList.value!!) + } + intent.putExtra("msgBean", b) + context.sendBroadcast(intent) + scrollListToBottom.value = true } - /** - * 上传文件 - */ - fun uploadFiles(meta: FileMeta) { - - } //刷新 private fun refresh() { @@ -198,6 +304,7 @@ class ChatPageViewModel( _msgList.value = _msgList.value?.plus(msgBean) adapter.setData(_msgList.value!!) } + /** * 发送文本信息 */ @@ -210,6 +317,10 @@ class ChatPageViewModel( _msgList.value = _msgList.value?.plus(msgBean) adapter.setData(_msgList.value!!) } + // 保存发送信息到消息队列 + //添加到消息队列 + + sendHandlerToLooper(b) intent.putExtra("msgBean", b) v.context.sendBroadcast(intent) txtMsg.value = "" @@ -228,11 +339,18 @@ class ChatPageViewModel( } } + private fun sendHandlerToLooper(b: MsgConvertBean) { + msgHandlerList.add(b) + val msg = Message() + msg.what = b.timestamp.toInt() + msg.obj = b + handler.sendMessageDelayed(msg, 5000) + } + //收到消息 fun receiveMsg(msg: String?) { if (!msg.isNullOrEmpty()) { - val bean = ConvertBeanUtils.covertBean(msg) - when (bean) { + when (val bean = ConvertBeanUtils.covertBean(msg)) { //消息状态 is CoverSealedBean.StateBean -> { viewModelScope.launch { @@ -244,6 +362,9 @@ class ChatPageViewModel( dao.updateStatus(bean.data.messageId, statusType) cDao.updateStatus(bean.data.sender.senderId!!, statusType) } + //删除队列里面的Bean + msgHandlerList.removeIf { it.messageId == bean.data.messageId } + handler.removeMessages(bean.data.timestamp.toInt()) refresh() } } @@ -275,7 +396,7 @@ class ChatPageViewModel( context.sendBroadcast(intent) } - //构建发送实体 + //构建文字发送实体 private fun buildSendBean(type: String): MsgConvertBean { val bodyBean = BodyContent(content = txtMsg.value, null, null) val body = mGson.toJson(bodyBean) @@ -298,6 +419,31 @@ class ChatPageViewModel( ) } + + //构建 图片/视频/文件发送实体 + private fun buildSendMediaBean(type: String, fileBean: FileDataBean): MsgConvertBean { + val bodyBean = BodyContent(content = mGson.toJson(fileBean), null, null) + val body = mGson.toJson(bodyBean) + return MsgConvertBean( + body = body, + customMessageType = "", + messageId = TimeUtils.getNowDateMillis().toString(), + messageType = type, + metadata = "", + timestamp = TimeUtils.getNowDateMillis(), + sender = Sender( + senderId = SpUtils.getId(), + senderType = "" + ), + receiver = Receiver( + 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") @@ -320,4 +466,7 @@ class ChatPageViewModel( status = "", ) } -} \ No newline at end of file + +} + + diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/LoginPageViewModel.kt b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/LoginPageViewModel.kt index 6b1eb5f..ca5b097 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/LoginPageViewModel.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/LoginPageViewModel.kt @@ -37,7 +37,7 @@ class LoginPageViewModel : BaseViewModel() { } fun doLogin() { - isLoginSuccess.value = true +// isLoginSuccess.value= true val isLegal = checkParams(); if (isLegal) { viewModelScope.launch { 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 3280248..94158fe 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 @@ -19,6 +19,7 @@ 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 com.tenlionsoft.baselib.widget.AdapterItemClickListener import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -26,7 +27,7 @@ import kotlinx.coroutines.withContext class MsgViewModel : ViewModel() { private val _pwdList = MutableLiveData>() var adapter: CategoryAdapter = CategoryAdapter(_pwdList.value ?: emptyList(), this) - var onItemClickListener: OnItemClickListener? = null + var onItemClickListener: AdapterItemClickListener? = null val isLogining = MutableLiveData(false) private val mGson = Gson() private var mCurrentPage: Int = 1 @@ -156,8 +157,4 @@ class MsgViewModel : ViewModel() { fun refresh() { getList(true) } - - interface OnItemClickListener { - fun onItemClick(msgCategoryBean: MsgCategoryBean) - } } \ No newline at end of file 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 7d14e58..5403e1a 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 @@ -1,5 +1,8 @@ package com.tenlionsoft.aimz_k.widget +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.util.Log import android.view.View import android.widget.ImageView import android.widget.TextView @@ -10,11 +13,18 @@ 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.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition import com.google.gson.Gson import com.tenlionsoft.aimz_k.R import com.tenlionsoft.aimz_k.model.BodyContent +import com.tenlionsoft.aimz_k.model.FileDataBean +import com.tenlionsoft.baselib.contacts.NetConfig import com.tenlionsoft.baselib.contacts.ProjectConfig +import com.tenlionsoft.baselib.model.ImageSize +import com.tenlionsoft.baselib.utils.ImageUtils import com.tenlionsoft.baselib.utils.TimeUtils +import com.tenlionsoft.baselib.widget.chat.BubbleImageView object BindingUtils { @@ -61,7 +71,131 @@ object BindingUtils { } else { val gson = Gson() val body = gson.fromJson(str, BodyContent::class.java) - tv.text = body.content + val contains = body.content!!.contains("fileName") + if (contains) { + tv.text = "文件" + } else { + tv.text = body.content + } + } + } + + @BindingAdapter("jsonConvertMsgFileIcon") + @JvmStatic + fun jsonConvertFileIcon(iv: ImageView, str: String?) { + if (str.isNullOrEmpty()) { + iv.visibility = View.GONE + } else { + iv.visibility = View.VISIBLE + val gson = Gson() + val body = gson.fromJson(str, BodyContent::class.java) + val contains = body.content!!.contains("fileName") + if (contains) { + val fileDataBean = gson.fromJson(body.content, FileDataBean::class.java) + if (!fileDataBean.fileName.isNullOrEmpty()) { + iv.visibility = View.VISIBLE + val suffix = fileDataBean.fileName!!.substring( + fileDataBean.fileName!!.lastIndexOf("."), + fileDataBean.fileName!!.length + ) + Log.e("BindingUtils", "jsonConvertFileIcon: $suffix") + var id = R.drawable.ic_audio + when (suffix) { + ".zip", ".rar", ".7z", ".gz", ".xz" -> iv.setImageResource(R.drawable.ic_zip) + ".png", ".jpg", ".jpeg", ".svg", ".gif" -> iv.setImageResource(R.drawable.ic_image) + ".doc", ".docx", ".wps", ".wpt" -> iv.setImageResource(R.drawable.ic_word) + ".xls", ".xlsx", ".et", ".ett" -> iv.setImageResource(R.drawable.ic_excel) + ".pdf" -> iv.setImageResource(R.drawable.ic_pdf) + else -> iv.setImageResource(R.drawable.ic_file) + } + } else { + iv.visibility = View.GONE + } + } else { + iv.visibility = View.GONE + } + } + } + + @BindingAdapter("jsonConvertMsgFileName") + @JvmStatic + fun jsonConvertFileName(tv: TextView, str: String?) { + if (str.isNullOrEmpty()) { + tv.text = "" + } else { + val gson = Gson() + val body = gson.fromJson(str, BodyContent::class.java) + val contains = body.content!!.contains("fileName") + if (contains) { + val fileBean = gson.fromJson(body.content, FileDataBean::class.java) + tv.text = fileBean.fileName + } else { + tv.text = body.content + } + } + } + + @BindingAdapter("jsonConvertMsgAudioImg") + @JvmStatic + fun jsonConvertAudioCover(iv: ImageView, str: String?) { + if (str.isNullOrEmpty()) { + iv.setImageResource(com.tenlionsoft.baselib.R.drawable.ic_img_load_err) + } else { + val options: RequestOptions = RequestOptions() + .placeholder(com.tenlionsoft.baselib.R.drawable.ic_loading) // 正在加载中的图片 + .error(com.tenlionsoft.baselib.R.drawable.ic_img_load_err) // 加载失败的图片 + val gson = Gson() + val bodyContent = gson.fromJson(str, BodyContent::class.java) + + val fileBean = gson.fromJson(bodyContent.content, FileDataBean::class.java) + Glide.with(iv.context) + .load(NetConfig.MAIN_URL + fileBean.fileUrl) // 图片地址 + .apply(options) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Drawable, + transition: Transition? + ) { + val imageSize: ImageSize? = + ImageUtils.getImageSize((resource as BitmapDrawable).bitmap) + if (imageSize != null) { + val imageLP = iv.layoutParams + imageLP?.width = imageSize.getWidth() + imageLP?.height = imageSize.getHeight() + iv.setLayoutParams(imageLP) + Glide.with(iv.context) + .load(resource) + .apply(options) // 参数 + .into(iv) + } + } + + override fun onLoadCleared(placeholder: Drawable?) { + } + }) + } + } + + @BindingAdapter("jsonConvertMsgImgBody") + @JvmStatic + fun jsonConvertImg(view: BubbleImageView, str: String?) { + if (str.isNullOrEmpty()) { + view.setImageResource(com.tenlionsoft.baselib.R.drawable.ic_load_error) + } else { + val gson = Gson() + val body = gson.fromJson(str, BodyContent::class.java) + val fileData = gson.fromJson(body.content, FileDataBean::class.java) + + val requestOptions = RequestOptions() + .error(R.drawable.app_logo_small) + .placeholder(R.drawable.app_logo_small) + .skipMemoryCache(false) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE) + .centerInside() + Glide.with(view.context) + .load(NetConfig.MAIN_URL + fileData.fileUrl) + .apply(requestOptions) + .into(view) } } diff --git a/app/src/main/res/drawable-xhdpi/ic_audio.png b/app/src/main/res/drawable-xhdpi/ic_audio.png new file mode 100644 index 0000000000000000000000000000000000000000..a3a4f5f9a26ea77fbab7bd5540ef1152a26d89df GIT binary patch literal 798 zcmV+(1L6FMP)rCeKU)DUS9Hs#~6Tz z$N$;QKxzzt@d6m^P$L{x3+!}QN3RaF-M~~d9D!W%$qzVOv%>R`z))vHEF4Xp7#(!J zz#&m^1orr+pW@69V_0)Y;8>R((hMU>f+z544u{;J0C@pIiUdy}$Y7@kg#rXgOwIr4 zzc;*T7SoE+i~tc&W#*UCwp~{|)-a|Op(z2PS_#bw5G3(rI^H+w>sZ9JESeM`e#Mf3 z%X^(349lWf0V47@_41;p?I4CF(X;>|B%zvs&0>O)EzKAS^JO7EaqE!lIEHIbO@J8L z+zht$CX9p$FQoPnUT=mY-HjMkMllIh1&B_Y1@^f%f@Pf%Ghu>1W0&q6b>GG? zi|PVII+2E!IurI8%*vBUXW-SjeztBlAk-45mVj>?TQ?gJx`0CH0&1fRsEsb5Hi`&1 ztY&N$Y-V`>V;MrxB#z31Yyvx4>+v(!TP;TDY1+W{g`9srmz9tUSp=x)yF(oq38M?} zsF^@0n!uLCw%T7CaB5p%d@2O5zb|32#ebyl&h%__n| zXAC#7iLZ;8DT^q9JoHTva?fftND|Njx`5i06!7?i-_XE?p@v~10bidTgDe~YC0{iV zkKtLs*Lwa08|qgo&8Itv5vIK12vG4L(Kcgfb=;gb!6Ems$-&8?*q#9(N3NFaK91ZWxUiX=D!^6FDRt+{Lr(FqVF@%r0a+5M<{O_*B5 zB7p=3|JejX0b+dmm*$JQJFqA+w-Zyd5sd&bH9OTm!D#lU`kWiY)GS0LKv*gvIsrmG zInw*8w;`Zf@n95FlZaA)82K~OnCgH2AciIptpG7LHU9FF-`OY(jUs9RVmS%H1lBt` z!?9g*jFzW;xa(JPPX^q?j$wkVc)A7MPraAqT+(h;3`#AaX|- zM&|K92|)#joziV^U~3RKtaiX`c{)bA((^(x3Nd66T!5IFo`Ow|8(?mB7BkaK&rQR# zlaKUu>jOe40ZUQEmh0=*2ZUX~M%V>}#x5WddUcwW1$3tOr zDP{{$yTaozl(s1`1H9%0)b`iIpymf=OOd;W>tE)($9kcv=ef~qjloNRC=AWS5B&-y zZDp9vQzFZQR&|HIjjmC4K86-`J7!$sJdS{Tdju#ySHQ%q20}LPV`b9bQ0)=>IM2`N5Fkf^#0 zzh@?}XC5ljm_T;$RgkW6Vwa(%%f^ru2@q)kDfsBaA-nO;lG!vRKu<7vZxTCp00kPp zzq9QgAgG9f&>I*@u!teJ)pQr*1xyL(pnK4hBoPni9gvPo&?b9;$O^s!6El-|>Xe~K zCaC)Ap|Yn2d*(4mz!18=Mcsj8Dljt--U7N)gl3G^y(i9IVdzcV#0#eg6=Y0+j$v9d zG*$;qNY7XwjJ>aMeekpQi4!*$&{+`$Ub-U-ojo3z35W^1 zfY2Bhs7e^c7H15_3j<>+MY#(`EF1w_{?vdk5(b{sD#~LqWZ?*~KlT7^9kx1SDn4-b z0anRWiiaQJ9~=RKBqX5C#?2tZUU7(E;aF%L1FNzPizog9%!nsXBhp8Q00000NkvXX Hu0mjfqL;}z literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_file.png b/app/src/main/res/drawable-xhdpi/ic_file.png new file mode 100644 index 0000000000000000000000000000000000000000..e328a150fc0594ba59761d12c843d20694ed9101 GIT binary patch literal 556 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezr3(Fn;!QaSW-L^Y+$hZI?ok10RdK z_Z-rg82&_bQC%k|H902y1wAuTETjLP+y{>-D4*MUly$W-NR? zM|%6tIsU$|xHu&0`u5-N=zP!NaPEyVO+AvfZb|#YDa(f(Yf`rtCxRXyLD=?#i|?3CvNZNE;7D+ z;N`C)bMy7z`^}JhckSF--WF|zh--%3WqG*@N(x>s4CaCdd|3*F8Sh|c{wrnJ8{&O5 zHHDAI;9=-e{*Fh>*%T)yoVCh&dDL?$_m6)!lDT`T#bpeIW-NEtWw%&e!syb=Fyo+g nBHQB@iw{ep4H=BzwX4T(WzuyJd?KX^Oa%;{u6{1-oD!M9h` literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_image.png b/app/src/main/res/drawable-xhdpi/ic_image.png new file mode 100644 index 0000000000000000000000000000000000000000..0e1428541c30ffb9161ceb9732e51dd87778f3f6 GIT binary patch literal 805 zcmV+=1KRwFP)4w)WRQ31lP-wNl<9iUxTiM{*`%}n(kFyO)eMTd13PBET-ZqO5n!yIQSO7QA*F1+{R*%heZ^g^l6oWMeY7qVyGk5?gx zXO|(dUb78Oy&A$E;Ok;+3S=v!wo(fNt=&-dXJ3Ce_(o_<3(y3O#ACwG<3LL{G->rX zx(v;aD<7V+D?r8fZ?_Ad_$1DSwpDAy$4Cgyza3#yfaXA|_*^)~?y)IQNFmG#T$izs zon>hulZz^lhAJQj#RX`km3kjD8?*tWt;PR9mHc3jx+$9#UXS+%;Q^cOaxLR138K_x^dK#;`O_rELKA6;C>W>`ciKy0t? ze$=+A)EI_Ev;xGRt-W_OEtNV9Ln3McB9eq)0uq@7D(g;S6q!})hN*G~L)eOp?b}+{r90|= z3|RyhAe>GYWaLP}<#b~!JZ`54ZVg@Y}0NOScWZ^e1 zVsFOGBF8MH7KB}UWtj=z|=pBJ^N@;Ora@(xv>S9 z8?`{8G7k)$8XPx+?*d5W=}@67#fZVaz;i3`dvUqG6r{3rcz*j0_CzLTfn%xyob4PK zzBi6D#?3TUrnyg#RdK*~^UnZ*LE{*X;2??C$Cu${?-Y*ZDoE!Vx-@K6>uyvBj1Nc5 z0)>iv*u+xPGN3<7P=6*qU>mWdu` z%UkZI2RfL3YUY*~Pr&q$TS5dqLdnXPV+E$M#VwseWj?%p@(GXJ_@oHXx^C*zv569t z2qi@c@C9h&dZ8sYH)dgH+q-n=JZgK#CrzLXH>^TajJ*JsnPpg?~5k(F*W6==it=2)cC=(|GSED1mqc=q~i^-@b}OOae6P zS-5t+cMw{~gty%E&ukN8ss=$x`R{>n(6#COXvfDSASO}*LK8+{WF5P5FcdM2!#LQO zivbHyAbCGEh=UQVz*Ri51PE|H_Ha*PcMgaW#vw_Q)V2>8%-CQF5Fj`SPY({R#kWNw k!Z)>{#)CkG=!w#pm>4Z+ z#KcfE3J24In(Bd)Xh&(`cE|xwP+ucLIOK1N5^LM^<{&sh}3~WKK zP#pLA*bV?j1uUC|LkU5amj?%x%8u$@eJ7@Z;R$fXd)ZGICL9tdYN-GH)8#x@)7SR~ zhXlbB=+4f0&azO8VZtGSl1q)?^ZIrq!4v4pR;(nlZ9)kM5Id3J3mnK{@g*sQ3J@gm zZR}lXv-!mrOk+ke0>sAJx?^K`>11VZ?*gVVAt?bOUJ1zw5F}wU8jfjaXMbQC5lIRV zzvdSvtGe!-!Z0F|6(H7@SKe0MzEy$Ywn$olh$bPLK!!{PWmj7;iq7KH^vk*j#!a;P zEbKx|S1 zVv`aOo0Ndqqy)q!B_K9}1n46C^i3UP$Yq$(b*del6B95qV#dsm`mP@2dAS$?I4oAY zf^vY}G>F+I8FD$OaGk)s05-z_Z>>lO5-4wLg@S4|jJNB6kYNEz9GiM#0&7<{4}7Q+4Ws>jZG4ED6VgU;!$~bg=<5KLs__pw{=m z{Ub$q%rDTLt-vFjoWKkB;-*G;c%}+7zd(80b>2Gh^4|pZmlR{sLvZhKAs!1aKxjTp zg87;j+seYt6HTB%t%kAo4$P>RWuR?kHQUQ{( z2g)=pu=w|lJ?>SQF`L1Tdw`(AA_>fljDoGdpPLtM574?Ew;QAA2o~7dLZMJ$;RNX0 zOY?q$+XJ+bX`W~;6t4gkUuztIRi_h%FJHkC|2#z32FpRrx-j#&` z2TvfGj~WPtVPLrR`&=D{96SO4!yYg$-A{pWr>BET>vDa@go7tQkc7|2>R6UhNk@cn jGYmVPmd5Bpizog7!t?}B#9;~700000NkvXXu0mjf=UTD0 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_ppt.png b/app/src/main/res/drawable-xhdpi/ic_ppt.png new file mode 100644 index 0000000000000000000000000000000000000000..0586719092117faa37dad779a49941d4913d974a GIT binary patch literal 891 zcmV->1BCpEP)k^puCn4t)@07}=%LG76&v4;6F?FGZA~ zhY$_LOW>YdJ19{mn(0tI0$ z+gl{MUCp8>8nH4HLoq?O(?Cf@CCyn)ZMRTH4SMt;HScncAjTXPNIqI^r{j2Y})wa5nwhWfqs2Vl@^Ft#07r= zh9y=;hY#sHUcN^eF{%+@M2CGU!~W~AaOsW>Wkje-fZ3>o>I4{;5PM%7$m+Jtp+v%nReGZo4zg}GaL;1ZHZ{Oa)C?&8_@&+fpeR~6=3B(CaKxs4q zrO^bGMiWpPO+aZh0i}sbAQ3m=ndh(J7BC$Zk<(@+hs8`w{avVJvYPIZZifL0*Z7MI z$%Qla7>ATFog6#oXiDwDNHbDKerGNI%&iH8KCN6_qT+V@rLnXrZ$MBSeOQ zPc+k5hlCG?sC3gViZpY%PI|vbF5|*=so|wQSspj-l4PVrEwJriF&aFMN`{59vDLp; z6`_&^tfoRF8J5WHv5@U-h19SIefx9lonf!|M)_v~TlVZH{ex#~3NSek_;KT$)UXG| z1d>Y1X#SBrg?Q_xpJ*V*JF|CMv4Fo|YE3;Q?#iR-GsjT*P)vXuTA3I}C1ZcvPm5ho zkz`na4_psCx4FsS`K_2h@DvJ$1%jtgC^UipQh*Oy{zcjUbQlZ^a%17(vzPO`b=9#E zDNzZi2~9w0))c@W-E$boKvXT*!t=WaCo>Tx(65hpK57s(0`=q#OjaXF1PJiM9&VR& z2!k0YRR?eVghZXu@aBUBr388nge63`vjtbdMyynY2sbVWG$bVGTKt=5{sOa&^*{`+ RAq)Tj002ovPDHLkV1kH+kAMIG literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_psd.png b/app/src/main/res/drawable-xhdpi/ic_psd.png new file mode 100644 index 0000000000000000000000000000000000000000..46ce2629edf582a90e069e5005498b51928329ef GIT binary patch literal 1032 zcmV+j1o!)iP)(A zo-%^GKvJ5a;6vU~Omxm`)6Mn$9oyV=^K7T)IS>25IKSUHf9LP>|Ns7HoGpOnq0O$- zS*+HF2=`ctbmSv+YlB9v#BEN$5x?;*Qpp71WoNednFcEbXp#Dq$kjoV5*s$zW1K$w zfsaH4& ztjMax0xS)V0!*MPptp52F!ovEhJK_TqZk2Ze0rw4e>(VpHxh~xV7!%3oWNfib5BI< z#ZeTc3ee!#)W-+sqmCn76vYZKo=7NKfDx0BO+XW*qKqTqh{Q2WSnl0V)ZIk50NDhX zjKdpAvpf)yINipnTg6FHcMv)utH2KJGD_ON0g-38l8{w^(XR`lgqXDy9H>GjPWzxK zTZE%=(n94e zLr&K-;y2Oh=+%gbntCUw%kZ0CeH)-*jP(Xd0=X#cOw1a}D(fJIi_A%gK!Lv~|3iW@fSp+1&1iRyaUCL3*&c%htkE^Fv`KX7Y?FgNESG< zZ55p?X+bs%3*i}+B5v0UF&FH3eLgI3s-#&Ew4o0Vp^D2f6kpUN$a=zm6X1VdnI1!1 z^6G`SCUZAdsNHQ_xDsFr&+5=}W?>~gL9)QQT0;Scn$jq(-)J+AIUX1?0x90Dh?- zhJgm5VnM;i(ZV?UkMYRofIuk(5)mpE6#O0n=MV}YpF^AbWslinZa}D5)GGDf;rxS* z2nC!37?!YD%y~p~46C8RQFc(I5rR7 ziJ&w{q39u$2Op{UNX!SWrR!sL`hEZFZZkQ%>iC#B><5 zMgTY`<&J@%)$xz^hxKrCPMAsn%vZv60ss=vTYevC%ZM4|re91c0J@#74-exbk8tQ0 z(+Yt8G1uF>agirD^ogkjz-$tR30N%>L59IYryvY+E{28IcM++8-Zu3Ev- z>~xH{H2-fhRA6U_g)YQcIhq?<5{3$Zy}TAqZV97cOAxna=Traa_0nVZ2OJ6v7Xb43 zBy9~|LGr{DxBN3YIYkZGd%Ssj17MWEeCf-I_2%sjKtN!b2nZNWK)`4M0!9-MFq(jX z(F6pHMwdWTsD-i=58;mSDXQ&t^7HB#w`P(UzJ`uQtP$H9KCbqcQ*5=k7`>ME*@DFf z*L&79NbSQe{);xc1a9nI&o^2}#ce~}a)b6azTvfxJ>*b4j1(N)Al^7UiYNyH`bLw^dfPScrGq(9TLf(wTFt1x?I!kQ&_hEdNZxMi(77x06(Kr$M6K* zoK6hK6me3AE80|2IQC0wG-WFiD~yk%Z9KBdYXV#Y=R!z}MH2v+TAx#6_{)(dZV{Cy z+r^DNLQ3!!0zHgtK&Uxm2gOyi`kgLXEUEzbk-pvAv&|g)8h)H#$RifVql36CWs6sQ zJ}kNbl1}IS=y^<&tGp(B2^1U(rNNC~M&siCDQNl-%LPjaP~oEzp~} zi_)t)HD&#=XacA%ScoNDT~KiV_EnvV61|KO@qqnhygoc51#r#No5Scz?N8M@$lVUzA}067bUAYPdjxA$^ZZW07*qo IM6N<$f-NuK5C8xG literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_zip.png b/app/src/main/res/drawable-xhdpi/ic_zip.png new file mode 100644 index 0000000000000000000000000000000000000000..325dbcdeec971935590189c8fec39b59691edd71 GIT binary patch literal 830 zcmV-E1Ht@>P)2*b76BJ9YN8wv0ufDQH3}|~f`$XKRSz6YjK-uMjR(D$SS0aa;>nx6$-$ce zq8>C+At6x?Zh=J6U{IqK<3hFRLW}i#2U?g?x=fecBl>mwl1nDKmj01tH@?ww6V(%ZIAa!!gI1s4bUQEG%l#+y6;ATH}PId?hfK();5CD+);BeHQ>1>|hVZulw z0Q`Zu&jt$aInq!$&BKI{N&qA)A)Nq##Q2-{Rp(DzW_TDADFuMb?Hz49RNKHYCejK3 zw{P}w>w)US99KkY0T53@m_UxnNc*bFImKuCm-BviOG6vSWe5`hdv}#l-f9!4`22Bs zqW*3YDp0UCk7_EmaY~G65<&&QhV^S`XK4|cjH`GQpD*8kUpUda|2jtn;R3+x^HY9q z4*C269>s>Q5p?Zbcew6w0HOqv#dg*bt~(rnE|4X30nz9JqR|CJqYH>e7Z8muAR0{q zTQ(L@Mafo9%Nc(DmS0J^NuU1dq;I@{dcI}MU2@Af^VdzU-+khg8kz)hb4*lUS;lD* z2;tv_8@C^lch*lQ+YZu4$B(ekWouVB18k3*m%e)=JkpLPfk;re^;i=P4!@v@DF?5i zr~<-+7|?Ub3VlfXQq<-lP-}nyewB2~2%m+|y2%Tlox=ITbVupodZX==da! zj!p6!HPHkjTPPi97U*cKQzq|9XA!9d(DU6b_4JJQyf31Eac)OjNG*W&iM$SkKs#Yl zLJ}7;$N+4Q&(e)10h!PRL=#J3hTjT!kr^io6!N=*S|E#$8feBqgEp(Bi<5#%VBukp zA(HnfIE@A}U$R diff --git a/app/src/main/res/layout/item_msg_my_file.xml b/app/src/main/res/layout/item_msg_my_file.xml new file mode 100644 index 0000000..fc9348a --- /dev/null +++ b/app/src/main/res/layout/item_msg_my_file.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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 b140e3f..cd001e7 100644 --- a/app/src/main/res/layout/item_msg_my_img.xml +++ b/app/src/main/res/layout/item_msg_my_img.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools"> + @@ -11,6 +12,7 @@ + @@ -41,19 +43,19 @@ @@ -68,6 +70,7 @@ + @@ -11,6 +12,7 @@ + > @@ -41,18 +43,18 @@ @@ -68,8 +70,9 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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 5b85c45..34472df 100644 --- a/app/src/main/res/layout/item_msg_other_img.xml +++ b/app/src/main/res/layout/item_msg_other_img.xml @@ -47,6 +47,7 @@ android:layout_marginStart="0dp" android:adjustViewBounds="true" android:scaleType="centerCrop" + jsonConvertMsgImgBody="@{bean.body}" android:transitionName="sharedView" app:angle="6dp" app:arrowHeight="8dp" 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 504a9d3..05a671c 100644 --- a/app/src/main/res/layout/item_msg_other_video.xml +++ b/app/src/main/res/layout/item_msg_other_video.xml @@ -45,6 +45,7 @@ android:layout_width="100dp" android:layout_height="100dp" android:layout_alignParentStart="true" + jsonConvertMsgAudioImg="@{bean.body}" android:layout_marginStart="0dp" android:adjustViewBounds="true" android:scaleType="centerCrop" diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/contacts/ProjectConfig.kt b/baselib/src/main/java/com/tenlionsoft/baselib/contacts/ProjectConfig.kt index fffd18b..5fd148c 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/contacts/ProjectConfig.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/contacts/ProjectConfig.kt @@ -19,8 +19,9 @@ object ProjectConfig { //消息类型 const val MSG_TEXT = "MSG_TEXT" const val MSG_FILE = "MSG_FILE" - const val MSG_IMG = "MSG_IMG" + const val MSG_IMG = "MSG_IMAGE" const val MSG_VIDEO = "MSG_VIDEO" + const val MSG_AUDIO = "MSG_AUDIO" const val MSG_STATUS = "STATUS" //状态消息 const val MSG_SEND_ING: String = "PENDING"//11 //发送中 const val MSG_SEND_FAIL: String = "ERROR"//12 //发送失败 diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/model/ImageSize.kt b/baselib/src/main/java/com/tenlionsoft/baselib/model/ImageSize.kt new file mode 100644 index 0000000..a21fae4 --- /dev/null +++ b/baselib/src/main/java/com/tenlionsoft/baselib/model/ImageSize.kt @@ -0,0 +1,21 @@ +package com.tenlionsoft.baselib.model + +class ImageSize { + private var width = 0 + private var height = 0 + fun getWidth(): Int { + return width + } + + fun setWidth(width: Int) { + this.width = width + } + + fun getHeight(): Int { + return height + } + + fun setHeight(height: Int) { + this.height = height + } +} \ No newline at end of file diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/net/BaseUrlInterceptor.kt b/baselib/src/main/java/com/tenlionsoft/baselib/net/BaseUrlInterceptor.kt index 8dd68f8..cb331e0 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/net/BaseUrlInterceptor.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/net/BaseUrlInterceptor.kt @@ -1,5 +1,6 @@ package com.tenlionsoft.baselib.net +import android.util.Log import com.tenlionsoft.baselib.contacts.NetConfig import com.tenlionsoft.baselib.utils.SpUtils import okhttp3.HttpUrl @@ -19,7 +20,10 @@ class BaseUrlInterceptor : Interceptor { val oldHttpUrl = request.url; val headers = request.headers("project"); //公共Token header - if (headers.indexOf("token") != -1) { + val tokens = request.headers("token") + Log.e("BaseUrlInterceptor", "intercept: $tokens") + if (tokens.isNotEmpty()) { + Log.e("BaseUrlInterceptor", "intercept: 添加公共Header") builder.removeHeader("token") builder.addHeader("X-WG-TOKEN", SpUtils.getToken()) builder.addHeader("X-WG-TYPE", "APP") @@ -43,7 +47,7 @@ class BaseUrlInterceptor : Interceptor { .build(); return chain.proceed(builder.url(newFullUrl).build()); } else { - return chain.proceed(request); + return chain.proceed(builder.url(oldHttpUrl).build()); } } } \ No newline at end of file diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/utils/ImageUtils.kt b/baselib/src/main/java/com/tenlionsoft/baselib/utils/ImageUtils.kt new file mode 100644 index 0000000..8a614eb --- /dev/null +++ b/baselib/src/main/java/com/tenlionsoft/baselib/utils/ImageUtils.kt @@ -0,0 +1,62 @@ +package com.tenlionsoft.baselib.utils + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import com.tenlionsoft.baselib.model.ImageSize +import java.io.ByteArrayOutputStream +import java.io.IOException + +object ImageUtils { + fun getImageSize(bitmap: Bitmap?): ImageSize? { + val imageSize: ImageSize = ImageSize() + if (null == bitmap || bitmap.isRecycled) { + return null + } + val baos = ByteArrayOutputStream() + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos) + val byteTmp = baos.toByteArray() + try { + baos.close() + } catch (e: IOException) { + e.printStackTrace() + } + val bitmapOptions = BitmapFactory.Options() + bitmapOptions.inJustDecodeBounds = true + BitmapFactory.decodeByteArray(byteTmp, 0, byteTmp.size, bitmapOptions) + val outWidth = bitmapOptions.outWidth + val outHeight = bitmapOptions.outHeight + val maxWidth = 400 + val maxHeight = 400 + val minWidth = 150 + val minHeight = 150 + if (outWidth / maxWidth > outHeight / maxHeight) { // + if (outWidth >= maxWidth) { // + imageSize.setWidth(maxWidth) + imageSize.setHeight(outHeight * maxWidth / outWidth) + } else { + imageSize.setWidth(outWidth) + imageSize.setHeight(outHeight) + } + if (outHeight < minHeight) { + imageSize.setHeight(minHeight) + val width = outWidth * minHeight / outHeight + imageSize.setWidth(Math.min(width, maxWidth)) + } + } else { + if (outHeight >= maxHeight) { + imageSize.setHeight(maxHeight) + imageSize.setWidth(outWidth * maxHeight / outHeight) + } else { + imageSize.setHeight(outHeight) + imageSize.setWidth(outWidth) + } + if (outWidth < minWidth) { + imageSize.setWidth(minWidth) + val height = outHeight * minWidth / outWidth + imageSize.setHeight(Math.min(height, maxHeight)) + } + } + + return imageSize + } +} \ No newline at end of file diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/widget/AdapterItemClickListener.kt b/baselib/src/main/java/com/tenlionsoft/baselib/widget/AdapterItemClickListener.kt new file mode 100644 index 0000000..842c268 --- /dev/null +++ b/baselib/src/main/java/com/tenlionsoft/baselib/widget/AdapterItemClickListener.kt @@ -0,0 +1,5 @@ +package com.tenlionsoft.baselib.widget + +interface AdapterItemClickListener { + fun onItemClick(data: D) +} \ No newline at end of file diff --git a/baselib/src/main/res/drawable-xhdpi/ic_img_load_err.png b/baselib/src/main/res/drawable-xhdpi/ic_img_load_err.png new file mode 100644 index 0000000000000000000000000000000000000000..2f04368b533d5bf1df6ff8bfa5b4bfb912242fdb GIT binary patch literal 8456 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