对接Socket

This commit is contained in:
itgaojian 2024-10-31 19:00:40 +08:00
parent 7600f0d041
commit a50e97f2ed
45 changed files with 807 additions and 332 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" /> <bytecodeTargetLevel target="17" />
</component> </component>
</project> </project>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@ -9,6 +9,7 @@
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application <application
android:name="com.tenlionsoft.baselib.base.App" android:name="com.tenlionsoft.baselib.base.App"

View File

@ -0,0 +1,68 @@
package com.tenlionsoft.aimz_k
import com.google.gson.Gson
import com.tenlionsoft.aimz_k.model.CoverSealedBean
import com.tenlionsoft.aimz_k.model.MsgBean
import com.tenlionsoft.aimz_k.model.MsgConvertBean
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum
import com.tenlionsoft.baselib.contacts.ProjectConfig
object ConvertBeanUtils {
fun covertBean(str: String): CoverSealedBean {
val gson = Gson()
val sBean = gson.fromJson(str, MsgConvertBean::class.java)
var type: Int = -1
if (sBean.messageType != ProjectConfig.MSG_STATUS) {
when (sBean.messageType) {
ProjectConfig.MSG_TEXT -> type = MsgTypeStateEnum.MSG_FROM_OTHER_TXT
ProjectConfig.MSG_IMG -> type = MsgTypeStateEnum.MSG_FROM_OTHER_IMG
ProjectConfig.MSG_FILE -> type = MsgTypeStateEnum.MSG_FROM_OTHER_FILE
ProjectConfig.MSG_VIDEO -> type = MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE
}
return CoverSealedBean.Msg(
MsgBean(
messageId = sBean.messageId,
messageType = sBean.messageType,
metadata = sBean.metadata,
receiverId = sBean.receiver.receiverId,
receiverType = sBean.receiver.receiverType,
senderType = sBean.sender.senderType,
senderId = sBean.sender.senderId,
body = sBean.body,
msgType = type,
timestamp = sBean.timestamp,
status = sBean.status,
customMessageType = ""
)
)
} else {
return CoverSealedBean.StateBean(sBean)
}
}
fun convertBeanToMsgBean(cBean: MsgConvertBean): MsgBean {
var type: Int = -1
when (cBean.messageType) {
ProjectConfig.MSG_TEXT -> type = MsgTypeStateEnum.MSG_TO_OTHER_TXT
ProjectConfig.MSG_IMG -> type = MsgTypeStateEnum.MSG_TO_OTHER_IMG
ProjectConfig.MSG_FILE -> type = MsgTypeStateEnum.MSG_TO_OTHER_FILE
ProjectConfig.MSG_VIDEO -> type = MsgTypeStateEnum.MSG_TO_OTHER_MOVIE
}
return MsgBean(
messageId = cBean.messageId,
messageType = cBean.messageType,
metadata = cBean.metadata,
receiverId = cBean.receiver.receiverId,
receiverType = cBean.receiver.receiverType,
senderType = cBean.sender.senderType,
senderId = cBean.sender.senderId,
body = cBean.body,
msgType = type,
timestamp = cBean.timestamp,
status = cBean.status,
customMessageType = ""
)
}
}

View File

@ -12,13 +12,14 @@ class CategoryAdapter(
private val viewModel: MsgViewModel private val viewModel: MsgViewModel
) : BaseBindingAdapter<MsgCategoryBean, ItemCategoryBinding>(datas) { ) : BaseBindingAdapter<MsgCategoryBean, ItemCategoryBinding>(datas) {
override fun getItemBinding(parent: ViewGroup,viewType:Int): ItemCategoryBinding { override fun getItemBinding(parent: ViewGroup, viewType: Int): ItemCategoryBinding {
return ItemCategoryBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ItemCategoryBinding.inflate(LayoutInflater.from(parent.context), parent, false)
} }
override fun bindItem(holder: BaseViewHolder<ItemCategoryBinding>, position: Int) { override fun bindItem(holder: BaseViewHolder<ItemCategoryBinding>, position: Int) {
holder.binding.pos = position holder.binding.pos = position
holder.binding.item = list[position] holder.binding.item = list[position]
holder.binding.size = if (list.isNotEmpty()) list.size - 1 else 0
holder.binding.root.setOnClickListener { holder.binding.root.setOnClickListener {
viewModel.onItemClickListener?.onItemClick(list[position]) viewModel.onItemClickListener?.onItemClick(list[position])
} }

View File

@ -23,8 +23,6 @@ class ChatMsgAdapter(var datas: List<MsgBean>) :
MsgTypeStateEnum.MSG_TO_OTHER_TXT -> { MsgTypeStateEnum.MSG_TO_OTHER_TXT -> {
return ItemMsgMyBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ItemMsgMyBinding.inflate(LayoutInflater.from(parent.context), parent, false)
}//发送文本 }//发送文本
// itemView = LayoutInflater.from(mContext).inflate(R.layout.item_msg_my, parent, false);
// holder = new MsgMyTextHolder(itemView);
MsgTypeStateEnum.MSG_FROM_OTHER_TXT -> { MsgTypeStateEnum.MSG_FROM_OTHER_TXT -> {
return ItemMsgOtherBinding.inflate( return ItemMsgOtherBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@ -32,9 +30,6 @@ class ChatMsgAdapter(var datas: List<MsgBean>) :
false false
) )
}//收到文本信息 }//收到文本信息
// itemView = LayoutInflater.from(mContext).inflate(R.layout.item_msg_other, parent, false);
// holder = new MsgMyOtherHolder(itemView);
// break;
MsgTypeStateEnum.MSG_TO_OTHER_MOVIE -> { MsgTypeStateEnum.MSG_TO_OTHER_MOVIE -> {
return ItemMsgMyVideoBinding.inflate( return ItemMsgMyVideoBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@ -42,9 +37,6 @@ class ChatMsgAdapter(var datas: List<MsgBean>) :
false false
) )
}//发送视频 }//发送视频
// itemView = LayoutInflater.from(mContext).inflate(R.layout.item_msg_my_video, parent, false);
// holder = new MsgVideoHolder(itemView);
// break;
MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE -> { MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE -> {
return ItemMsgOtherVideoBinding.inflate( return ItemMsgOtherVideoBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@ -52,9 +44,6 @@ class ChatMsgAdapter(var datas: List<MsgBean>) :
false false
) )
}//收到视频 }//收到视频
// itemView = LayoutInflater.from(mContext).inflate(R.layout.item_msg_other_video, parent, false);
// holder = new MsgVideoHolder(itemView);
// break;
MsgTypeStateEnum.MSG_TO_OTHER_IMG -> { MsgTypeStateEnum.MSG_TO_OTHER_IMG -> {
return ItemMsgMyImgBinding.inflate( return ItemMsgMyImgBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@ -62,9 +51,6 @@ class ChatMsgAdapter(var datas: List<MsgBean>) :
false false
) )
}//发送图片 }//发送图片
// itemView = LayoutInflater.from(mContext).inflate(R.layout.item_msg_my_img, parent, false);
// holder = new MsgImageHolder(itemView);
// break;
MsgTypeStateEnum.MSG_FROM_OTHER_IMG -> { MsgTypeStateEnum.MSG_FROM_OTHER_IMG -> {
return ItemMsgOtherImgBinding.inflate( return ItemMsgOtherImgBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@ -72,9 +58,6 @@ class ChatMsgAdapter(var datas: List<MsgBean>) :
false false
) )
}//收到图片 }//收到图片
// itemView = LayoutInflater.from(mContext).inflate(R.layout.item_msg_other_img, parent, false);
// holder = new MsgImageHolder(itemView);
// break;
MsgTypeStateEnum.MSG_TO_OTHER_VOICE -> { MsgTypeStateEnum.MSG_TO_OTHER_VOICE -> {
return ItemMsgMyAudioBinding.inflate( return ItemMsgMyAudioBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@ -82,9 +65,6 @@ class ChatMsgAdapter(var datas: List<MsgBean>) :
false false
) )
}//语音 }//语音
// itemView = LayoutInflater.from(mContext).inflate(R.layout.item_msg_my_audio, parent, false);
// holder = new MsgAudioHolder(itemView);
// break;
MsgTypeStateEnum.MSG_FROM_OTHER_VOICE -> { MsgTypeStateEnum.MSG_FROM_OTHER_VOICE -> {
return ItemMsgOtherAudioBinding.inflate( return ItemMsgOtherAudioBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@ -92,18 +72,14 @@ class ChatMsgAdapter(var datas: List<MsgBean>) :
false false
) )
}//语音 }//语音
// itemView = LayoutInflater.from(mContext).inflate(R.layout.item_msg_other_audio, parent, false);
// holder = new MsgAudioHolder(itemView);
// break;
else -> { else -> {
// return BaseViewHolder()
throw IllegalArgumentException("Invalid view type") throw IllegalArgumentException("Invalid view type")
} }
} }
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
return list.get(position).msgType return list[position].msgType!!
} }
override fun bindItem(holder: BaseViewHolder<ViewDataBinding>, position: Int) { override fun bindItem(holder: BaseViewHolder<ViewDataBinding>, position: Int) {
@ -112,42 +88,42 @@ class ChatMsgAdapter(var datas: List<MsgBean>) :
MsgTypeStateEnum.MSG_TO_OTHER_TXT -> { MsgTypeStateEnum.MSG_TO_OTHER_TXT -> {
(holder.binding as ItemMsgMyBinding).pos = position (holder.binding as ItemMsgMyBinding).pos = position
(holder.binding as ItemMsgMyBinding).bean = list[position] (holder.binding as ItemMsgMyBinding).bean = list[position]
(holder.binding as ItemMsgMyBinding).state = list[position].sendState (holder.binding as ItemMsgMyBinding).state = list[position].status
}//发送文本 }//发送文本
MsgTypeStateEnum.MSG_FROM_OTHER_TXT -> { MsgTypeStateEnum.MSG_FROM_OTHER_TXT -> {
(holder.binding as ItemMsgOtherBinding).pos = position (holder.binding as ItemMsgOtherBinding).pos = position
(holder.binding as ItemMsgOtherBinding).bean = list[position] (holder.binding as ItemMsgOtherBinding).bean = list[position]
(holder.binding as ItemMsgOtherBinding).state = list[position].sendState (holder.binding as ItemMsgOtherBinding).state = list[position].status
}//收到文本信息 }//收到文本信息
MsgTypeStateEnum.MSG_TO_OTHER_MOVIE -> { MsgTypeStateEnum.MSG_TO_OTHER_MOVIE -> {
(holder.binding as ItemMsgMyVideoBinding).pos = position (holder.binding as ItemMsgMyVideoBinding).pos = position
(holder.binding as ItemMsgMyVideoBinding).bean = list[position] (holder.binding as ItemMsgMyVideoBinding).bean = list[position]
(holder.binding as ItemMsgMyVideoBinding).state = list[position].sendState (holder.binding as ItemMsgMyVideoBinding).state = list[position].status
}//发送视频 }//发送视频
MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE -> { MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE -> {
(holder.binding as ItemMsgOtherVideoBinding).pos = position (holder.binding as ItemMsgOtherVideoBinding).pos = position
(holder.binding as ItemMsgOtherVideoBinding).bean = list[position] (holder.binding as ItemMsgOtherVideoBinding).bean = list[position]
(holder.binding as ItemMsgOtherVideoBinding).state = list[position].sendState (holder.binding as ItemMsgOtherVideoBinding).state = list[position].status
}//收到视频 }//收到视频
MsgTypeStateEnum.MSG_TO_OTHER_IMG -> { MsgTypeStateEnum.MSG_TO_OTHER_IMG -> {
(holder.binding as ItemMsgMyImgBinding).pos = position (holder.binding as ItemMsgMyImgBinding).pos = position
(holder.binding as ItemMsgMyImgBinding).bean = list[position] (holder.binding as ItemMsgMyImgBinding).bean = list[position]
(holder.binding as ItemMsgMyImgBinding).state = list[position].sendState (holder.binding as ItemMsgMyImgBinding).state = list[position].status
}//发送图片 }//发送图片
MsgTypeStateEnum.MSG_FROM_OTHER_IMG -> { MsgTypeStateEnum.MSG_FROM_OTHER_IMG -> {
(holder.binding as ItemMsgOtherImgBinding).pos = position (holder.binding as ItemMsgOtherImgBinding).pos = position
(holder.binding as ItemMsgOtherImgBinding).bean = list[position] (holder.binding as ItemMsgOtherImgBinding).bean = list[position]
(holder.binding as ItemMsgOtherImgBinding).state = list[position].sendState (holder.binding as ItemMsgOtherImgBinding).state = list[position].status
}//收到图片 }//收到图片
MsgTypeStateEnum.MSG_TO_OTHER_VOICE -> { MsgTypeStateEnum.MSG_TO_OTHER_VOICE -> {
(holder.binding as ItemMsgMyAudioBinding).pos = position (holder.binding as ItemMsgMyAudioBinding).pos = position
(holder.binding as ItemMsgMyAudioBinding).bean = list[position] (holder.binding as ItemMsgMyAudioBinding).bean = list[position]
(holder.binding as ItemMsgMyAudioBinding).state = list[position].sendState (holder.binding as ItemMsgMyAudioBinding).state = list[position].status
}//语音 }//语音
MsgTypeStateEnum.MSG_FROM_OTHER_VOICE -> { MsgTypeStateEnum.MSG_FROM_OTHER_VOICE -> {
(holder.binding as ItemMsgOtherAudioBinding).pos = position (holder.binding as ItemMsgOtherAudioBinding).pos = position
(holder.binding as ItemMsgOtherAudioBinding).bean = list[position] (holder.binding as ItemMsgOtherAudioBinding).bean = list[position]
(holder.binding as ItemMsgOtherAudioBinding).state = list[position].sendState (holder.binding as ItemMsgOtherAudioBinding).state = list[position].status
}//语音 }//语音
else -> { else -> {
throw IllegalArgumentException("Invalid view type") throw IllegalArgumentException("Invalid view type")

View File

@ -0,0 +1,7 @@
package com.tenlionsoft.aimz_k.model
sealed class CoverSealedBean {
data class StateBean(val data: MsgConvertBean) : CoverSealedBean()
data class Msg(val msgBean: MsgBean) : CoverSealedBean()
}

View File

@ -2,33 +2,72 @@ package com.tenlionsoft.aimz_k.model
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import java.io.Serializable import java.io.Serializable
//{
// "body": "{\"msg\":\"\",\"statusType\":\"SUCCESS_SEND\"}",
// "customMessageType": "",
// "messageId": "1730344667596",
// "messageType": "STATUS",
// "metadata": "",
// "receiver": {
// "receiverId": "1",
// "receiverType": "SINGLE_USER"
//},
// "sender": {
// "senderId": "system",
// "senderType": "SYSTEM"
//},
// "timestamp": 1730344666322
//}
//发送格式
//messageId: `${datetime}`,
//timestamp: datetime,
//messageType: 'MSG_TEXT',
//body: JSON.stringify({content: chat.send.value}),
//sender: {
// senderId: chat.senderId,
// senderType: 'ANONYMOUS'
//},
//receiver: {
// receiverId: chat.receiverId,
// receiverType: 'SINGLE_USER'
//},
//metadata: '',
//status: 'PENDING',
@Entity(tableName = "db_msg") @Entity(tableName = "db_msg")
data class MsgBean( data class MsgBean(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long, var id: Long = 0,
@ColumnInfo(name = "type") @ColumnInfo(name = "messageId")
var type: Int, //消息类型 var messageId: String?, //消息唯一ID
@ColumnInfo(name = "isSystem") @ColumnInfo(name = "messageType")
var isSystem: Boolean, //是否为系统信息 var messageType: String?, //消息类型
@ColumnInfo(name = "from") @ColumnInfo(name = "customMessageType")
var from: String, //来源,系统消息为:SYSTEM 非系统消息为UserID var customMessageType: String?, //预留
@ColumnInfo(name = "to") @ColumnInfo(name = "metadata")
var to: String, //去处 系统消息为appId 非系统消息为userId var metadata: String?,
@ColumnInfo(name = "body") @ColumnInfo(name = "receiverId")
var body: String, //消息体 var receiverId: String?, //接受人ID
@ColumnInfo(name = "receiverType")
var receiverType: String?, //接受人Type
@ColumnInfo(name = "senderId")
var senderId: String?, //发送人ID
@ColumnInfo(name = "senderType")
var senderType: String?, //发送人类型
@ColumnInfo(name = "timestamp") @ColumnInfo(name = "timestamp")
var timestamp: Long, var timestamp: Long?, //时间戳
@ColumnInfo(name = "sendState") @ColumnInfo(name = "body")
var sendState: Int, //发送状态 11发送中 12发送失败 13发送成功 var body: String?, //消息体
@ColumnInfo(name = "fromName")
var fromName: String, //来源名称
@ColumnInfo(name = "msgType") @ColumnInfo(name = "msgType")
var msgType: Int, var msgType: Int?,
@ColumnInfo(name = "status")
var status: String? //发送状态 11发送中 12发送失败 13发送成功
/** /**
* 信息类型 * 信息类型
@ -48,7 +87,4 @@ data class MsgBean(
* 151 我发送给其他人的文件信息 * 151 我发送给其他人的文件信息
* 152 其他人发送给我的文件信息 * 152 其他人发送给我的文件信息
*/ */
) : Serializable { ) : Serializable
@Ignore
var msgWhat: Long = 0L
}

View File

@ -5,24 +5,55 @@ import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import java.io.Serializable import java.io.Serializable
//@ColumnInfo(name = "messageId")
//var messageId: String?, //消息唯一ID
//@ColumnInfo(name = "messageType")
//var messageType: String?, //消息类型
//@ColumnInfo(name = "customMessageType")
//var customMessageType: String?, //预留
//@ColumnInfo(name = "metadata")
//var metadata: String?,
//@ColumnInfo(name = "receiverId")
//var receiverId: String?, //接受人ID
//@ColumnInfo(name = "receiverType")
//var receiverType: String?, //接受人Type
//@ColumnInfo(name = "senderId")
//var senderId: String?, //发送人ID
//@ColumnInfo(name = "senderType")
//var senderType: String?, //发送人类型
//@ColumnInfo(name = "timestamp")
//var timestamp: Long?, //时间戳
//@ColumnInfo(name = "body")
//var body: String?, //消息体
//@ColumnInfo(name = "msgType")
//var msgType: Int?,
//@ColumnInfo(name = "status")
//var status: String? //发送状态 11发送中 12发送失败 13发送成功
@Entity(tableName = "db_category") @Entity(tableName = "db_category")
data class MsgCategoryBean( data class MsgCategoryBean(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long = 0, var id: Long = 0,
@ColumnInfo(name = "avatar") @ColumnInfo(name = "avatar")
var avatar: String, var avatar: String,
@ColumnInfo(name = "from") @ColumnInfo(name = "messageId")
var from: String, //来源,系统消息为:SYSTEM 非系统消息为UserID var messageId: String?, //消息ID
@ColumnInfo(name = "to") @ColumnInfo(name = "messageType")
var to: String, //去处 系统消息为appId 非系统消息为userId var messageType: String, //消息类型
@ColumnInfo(name = "receiverId")
var receiverId: String?, //接受人ID
@ColumnInfo(name = "receiverType")
var receiverType: String?, //接受人Type
@ColumnInfo(name = "senderId")
var senderId: String?, //发送人ID
@ColumnInfo(name = "senderType")
var senderType: String?, //发送人类型
@ColumnInfo(name = "timestamp") @ColumnInfo(name = "timestamp")
var timestamp: Long, // 时间戳 var timestamp: Long?, //时间戳
@ColumnInfo(name = "sendState")
var sendState: Int, //发送状态 11发送中 12发送失败 13发送成功
@ColumnInfo(name = "fromName")
var fromName: String, //来源名称
@ColumnInfo(name = "body") @ColumnInfo(name = "body")
var body: String, var body: String?, //消息体
@ColumnInfo(name = "isRead") //是否回复 @ColumnInfo(name = "msgType")
var isRead: Boolean, var msgType: Int?, //用来在列表中判断显示
@ColumnInfo(name = "status")
var status: String?, //消息状态
) : Serializable ) : Serializable

View File

@ -16,6 +16,12 @@ interface MsgCategoryDao {
@Query("SELECT * FROM db_category") @Query("SELECT * FROM db_category")
fun getAllCategory(): List<MsgCategoryBean> fun getAllCategory(): List<MsgCategoryBean>
/**
* 根据发送人ID获取
*/
@Query("SELECT * FROM db_category WHERE senderId=:senderId")
fun getCategoryBySenderId(senderId: String): MsgCategoryBean?
/** /**
* 根据ID获取消息 * 根据ID获取消息
* *
@ -31,7 +37,7 @@ interface MsgCategoryDao {
* @param from * @param from
* @return * @return
*/ */
@Query("SELECT * FROM db_category WHERE `from`=(:from)") @Query("SELECT * FROM db_category WHERE `senderId`=(:from)")
fun getCategoryByFrom(from: String?): List<MsgCategoryBean> fun getCategoryByFrom(from: String?): List<MsgCategoryBean>
@ -48,7 +54,7 @@ interface MsgCategoryDao {
/** /**
* 清空与某个人的聊天记录 * 清空与某个人的聊天记录
*/ */
@Query("DELETE FROM db_category WHERE `from`=(:from) AND `to`=(:to) OR `from`=(:to) AND `to`=(:from)") @Query("DELETE FROM db_category WHERE `senderId`=(:from) AND receiverId=(:to) OR `senderId`=(:to) AND receiverId=(:from)")
fun delChatHistory(from: String?, to: String?) fun delChatHistory(from: String?, to: String?)
/** /**
@ -79,4 +85,16 @@ interface MsgCategoryDao {
*/ */
@Query("DELETE FROM db_category") @Query("DELETE FROM db_category")
fun delAllMsg() fun delAllMsg()
/**
* 根据发送人的ID插入或者更新
*/
fun updateOrInsert(senderId: String, msgBean: MsgBean)
/**
* 更新 消息体 时间戳 状态
* @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)
} }

View File

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

View File

@ -0,0 +1,32 @@
package com.tenlionsoft.aimz_k.model
import java.io.Serializable
data class MsgConvertBean(
val body: String,
val customMessageType: String,
val messageId: String,
val messageType: String,
val metadata: String,
val `receiver`: Receiver,
val sender: Sender,
val timestamp: Long,
val status: String
) : Serializable
public data class Receiver(
val receiverId: String?,
val receiverType: String?
) : Serializable
public data class Sender(
val senderId: String?,
val senderType: String?
) : Serializable
//"{\"msg\":\"\",\"statusType\":\"SUCCESS_SEND\"}",
data class BodyContent(
val content: String?,
val msg: String?,
val statusType: String?
) : Serializable

View File

@ -7,6 +7,12 @@ import androidx.room.Query
@Dao @Dao
interface MsgDao { interface MsgDao {
/**
* 更新信息状态
*/
@Query("UPDATE db_msg SET status = :status WHERE messageId = :messageId")
fun updateStatus(messageId: String, status: String)
/** /**
* 获取全部信息 * 获取全部信息
* *
@ -24,13 +30,17 @@ interface MsgDao {
@Query("SELECT * FROM db_msg WHERE id =(:id) ") @Query("SELECT * FROM db_msg WHERE id =(:id) ")
fun getMsgById(id: String?): List<MsgBean?>? fun getMsgById(id: String?): List<MsgBean?>?
/**
*
*/
/** /**
* 根据信息来源查询消息 * 根据信息来源查询消息
* *
* @param from * @param from
* @return * @return
*/ */
@Query("SELECT * FROM db_msg WHERE `from`=(:from)") @Query("SELECT * FROM db_msg WHERE `senderId`=(:from)")
fun getMsgByFrom(from: String?): List<MsgBean?>? fun getMsgByFrom(from: String?): List<MsgBean?>?
/** /**
@ -39,7 +49,7 @@ interface MsgDao {
* @param to * @param to
* @return * @return
*/ */
@Query("SELECT * FROM db_msg WHERE `to` =(:to)") @Query("SELECT * FROM db_msg WHERE `receiverId` =(:to)")
fun getMsgByTo(to: String?): List<MsgBean?>? fun getMsgByTo(to: String?): List<MsgBean?>?
@ -51,13 +61,13 @@ interface MsgDao {
* @param page * @param page
* @return * @return
*/ */
@Query("SELECT * FROM db_msg WHERE `from`=(:form) ORDER BY timestamp LIMIT (((:page)-1)*(:pageSize)),(:pageSize)") @Query("SELECT * FROM db_msg WHERE `senderId`=(:form) ORDER BY timestamp LIMIT (((:page)-1)*(:pageSize)),(:pageSize)")
fun getMsgByPage(form: String?, pageSize: Int, page: Int): List<MsgBean?>? fun getMsgByPage(form: String?, pageSize: Int, page: Int): List<MsgBean?>?
/** /**
* 查询与单人的聊天记录 * 查询与单人的聊天记录
*/ */
@Query("SELECT * FROM db_msg t1 WHERE t1.id IN (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)) ORDER by t1.timestamp ASC") @Query("SELECT * FROM db_msg t1 WHERE t1.id IN (SELECT id FROM db_msg WHERE `senderId`=(:form) AND `receiverId`=(:to) OR `senderId`=(:to) AND `receiverId`=(:form) ORDER BY timestamp DESC LIMIT (((:page)-1)*(:pageSize)),(:pageSize)) ORDER by t1.timestamp ASC")
fun getMsgByFromOrToPage( fun getMsgByFromOrToPage(
form: String?, form: String?,
to: String?, to: String?,
@ -68,7 +78,7 @@ interface MsgDao {
/** /**
* 清空与某个人的聊天记录 * 清空与某个人的聊天记录
*/ */
@Query("DELETE FROM db_msg WHERE `from`=(:from) AND `to`=(:to) OR `from`=(:to) AND `to`=(:from)") @Query("DELETE FROM db_msg WHERE `senderId`=(:from) AND `receiverId`=(:to) OR `receiverId`=(:to) AND `senderId`=(:from)")
fun delChatHistory(from: String?, to: String?) fun delChatHistory(from: String?, to: String?)
/** /**

View File

@ -6,9 +6,6 @@ import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_FROM_OTHER_IMG
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_FROM_OTHER_TXT import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_FROM_OTHER_TXT
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_FROM_OTHER_VOICE import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_FROM_OTHER_VOICE
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_SEND_FAIL
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_SEND_ING
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_SEND_SUCCESS
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_TO_OTHER_FILE import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_TO_OTHER_FILE
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_TO_OTHER_IMG import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_TO_OTHER_IMG
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_TO_OTHER_MOVIE import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum.MSG_TO_OTHER_MOVIE
@ -28,9 +25,6 @@ object MsgTypeStateEnum {
const val MSG_FROM_OTHER_FILE: Int = 152 // * 152 其他人发送给我的文件信息 const val MSG_FROM_OTHER_FILE: Int = 152 // * 152 其他人发送给我的文件信息
const val MSG_TO_OTHER_HREF: Int = 161 // * 161 我发送给其他人的文件信息 const val MSG_TO_OTHER_HREF: Int = 161 // * 161 我发送给其他人的文件信息
const val MSG_FROM_OTHER_HREF: Int = 162 // * 162 其他人发送给我的文件信息 const val MSG_FROM_OTHER_HREF: Int = 162 // * 162 其他人发送给我的文件信息
const val MSG_SEND_ING: Int = 1001//11 //发送中
const val MSG_SEND_FAIL: Int = 1002//12 //发送失败
const val MSG_SEND_SUCCESS: Int = 1003////发送成功
} }
@ -46,9 +40,6 @@ object MsgTypeStateEnum {
MSG_FROM_OTHER_IMG, MSG_FROM_OTHER_IMG,
MSG_FROM_OTHER_MOVIE, MSG_FROM_OTHER_MOVIE,
MSG_FROM_OTHER_FILE, MSG_FROM_OTHER_FILE,
MSG_SEND_ING,
MSG_SEND_FAIL,
MSG_SEND_SUCCESS
) )
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
annotation class MsgState annotation class MsgState

View File

@ -22,7 +22,14 @@ interface UserApi {
@Headers("Content-Type: application/json", "Accept: application/json", "projectName:usercenter") @Headers("Content-Type: application/json", "Accept: application/json", "projectName:usercenter")
@POST("api/jwt/login") @POST("api/jwt/login")
suspend fun doLogin(@Body user: RequestBody): BaseSuccessBean suspend fun doLogin(@Body user: RequestBody): BaseSuccessBean
/**
* 登陆Socket系统
* http://192.168.0.26:8888/system/api/anonymous/login
*
*/
@Headers("Content-Type: application/json", "Accept: application/json", "projectName:usercenter")
@POST("api/anonymous/login")
suspend fun doLoginSocket(@Body user: RequestBody): BaseSuccessBean
/** /**
* 获取App版本 * 获取App版本

View File

@ -1,9 +1,11 @@
package com.tenlionsoft.aimz_k.page.activity package com.tenlionsoft.aimz_k.page.activity
import android.app.Activity import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log import android.util.Log
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -13,6 +15,7 @@ import com.tenlionsoft.aimz_k.databinding.ActivityChatBinding
import com.tenlionsoft.aimz_k.model.PickerType import com.tenlionsoft.aimz_k.model.PickerType
import com.tenlionsoft.aimz_k.viewmodel.ChatPageViewModel import com.tenlionsoft.aimz_k.viewmodel.ChatPageViewModel
import com.tenlionsoft.baselib.base.BaseActivity import com.tenlionsoft.baselib.base.BaseActivity
import com.tenlionsoft.baselib.contacts.ProjectConfig
import com.tenlionsoft.baselib.widget.wheel.WheelView import com.tenlionsoft.baselib.widget.wheel.WheelView
/** /**
@ -23,6 +26,7 @@ class ChatActivity : BaseActivity() {
private val chatPageViewModel: ChatPageViewModel by lazy { private val chatPageViewModel: ChatPageViewModel by lazy {
ViewModelProvider(this)[ChatPageViewModel::class.java] ViewModelProvider(this)[ChatPageViewModel::class.java]
} }
private lateinit var mLocalReceiver: LocalReceiver
private val filePicker = FilePicker.getInstance(this) private val filePicker = FilePicker.getInstance(this)
override fun bindView() { override fun bindView() {
@ -93,67 +97,37 @@ class ChatActivity : BaseActivity() {
else -> {} else -> {}
} }
} }
registerLocalReceiver()
// chatPageViewModel.showReplyLayout.observe(this) {
// if (it) {
// hideSoftKeyboard()
// }
// }
// chatPageViewModel.showEmojiLayout.observe(this) {
// if (it) {
// hideSoftKeyboard()
// }
// }
// chatPageViewModel.showChooseLayout.observe(this) {
// if (it) {
// hideSoftKeyboard()
// }
// }
// rootView = findViewById(android.R.id.content)
// rootView.viewTreeObserver.addOnGlobalLayoutListener {
// val r = Rect()
// rootView.getWindowVisibleDisplayFrame(r)
// val screenHeight = rootView.rootView.height
// val heightDiff = screenHeight - (r.bottom - r.top)
// Log.e("ChatActivity", "bindView: ${heightDiff}")
// if (heightDiff > 100) {
// chatPageViewModel.showEmojiLayout.value = false
// chatPageViewModel.showReplyLayout.value = false
// chatPageViewModel.showChooseLayout.value = false
// }
// }
} }
private fun registerLocalReceiver() {
mLocalReceiver = LocalReceiver()
val intentFilter = IntentFilter()
intentFilter.addAction(ProjectConfig.A_S_MSG_RECEIVER)
registerReceiver(mLocalReceiver, intentFilter)
}
private fun unRegisterLocalReceiver() {
unregisterReceiver(mLocalReceiver)
}
override fun onDestroy() {
super.onDestroy()
unRegisterLocalReceiver()
}
inner class LocalReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
ProjectConfig.A_S_MSG_RECEIVER -> {
val msg = intent.getStringExtra("msg")
Log.e("LocalReceiver", "onReceive: ${msg}")
chatPageViewModel.receiveMsg(msg)
}//接收到信息
}
}
}
} }
//XXPermissions.with(this)
//// 申请单个权限
//.permission(Permission.RECORD_AUDIO)
//// 申请多个权限
//.permission(Permission.Group.CALENDAR)
//// 设置权限请求拦截器(局部设置)
////.interceptor(new PermissionInterceptor())
//// 设置不触发错误检测机制(局部设置)
////.unchecked()
//.request(object : OnPermissionCallback {
//
// override fun onGranted(permissions: MutableList<String>, allGranted: Boolean) {
// if (!allGranted) {
// toast("获取部分权限成功,但部分权限未正常授予")
// return
// }
// toast("获取录音和日历权限成功")
// }
//
// override fun onDenied(permissions: MutableList<String>, doNotAskAgain: Boolean) {
// if (doNotAskAgain) {
// toast("被永久拒绝授权,请手动授予录音和日历权限")
// // 如果是被永久拒绝就跳转到应用权限系统设置页面
// XXPermissions.startPermissionActivity(context, permissions)
// } else {
// toast("获取录音和日历权限失败")
// }
// }
//})

View File

@ -40,8 +40,7 @@ class LoginActivity : BaseActivity() {
} }
loginPageViewModel.isLoginSuccess.observe(this) { loginPageViewModel.isLoginSuccess.observe(this) {
if (it) { if (it) {
// startActivity(Intent(this@LoginActivity, MainActivity::class.java)) startActivity(Intent(this@LoginActivity, MainActivity::class.java))
startActivity(Intent(this@LoginActivity, ChatActivity::class.java))
finish() finish()
} }
} }

View File

@ -8,30 +8,26 @@ import android.content.IntentFilter
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.provider.Settings import android.provider.Settings
import android.util.Log
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.ViewModelProvider
import com.tenlionsoft.aimz_k.R import com.tenlionsoft.aimz_k.R
import com.tenlionsoft.aimz_k.adapter.VpAdapter import com.tenlionsoft.aimz_k.adapter.VpAdapter
import com.tenlionsoft.aimz_k.databinding.ActivityMainBinding import com.tenlionsoft.aimz_k.databinding.ActivityMainBinding
import com.tenlionsoft.aimz_k.net.UserApi
import com.tenlionsoft.aimz_k.page.fragments.CenterFragment import com.tenlionsoft.aimz_k.page.fragments.CenterFragment
import com.tenlionsoft.aimz_k.page.fragments.MineFragment import com.tenlionsoft.aimz_k.page.fragments.MineFragment
import com.tenlionsoft.aimz_k.page.fragments.MsgFragment import com.tenlionsoft.aimz_k.page.fragments.MsgFragment
import com.tenlionsoft.aimz_k.viewmodel.MainPageViewModel
import com.tenlionsoft.baselib.base.BaseActivity import com.tenlionsoft.baselib.base.BaseActivity
import com.tenlionsoft.baselib.contacts.ProjectConfig import com.tenlionsoft.baselib.contacts.ProjectConfig
import com.tenlionsoft.baselib.net.DownloadApkService import com.tenlionsoft.baselib.net.DownloadApkService
import com.tenlionsoft.baselib.net.ExParse
import com.tenlionsoft.baselib.net.RetrofitClient
import com.tenlionsoft.baselib.utils.AppUtils import com.tenlionsoft.baselib.utils.AppUtils
import com.tenlionsoft.baselib.utils.DensityUtils import com.tenlionsoft.baselib.utils.DensityUtils
import com.tenlionsoft.baselib.utils.LogUtils import com.tenlionsoft.baselib.utils.LogUtils
import com.tenlionsoft.baselib.utils.ToastUtils import com.tenlionsoft.baselib.utils.ToastUtils
import com.tenlionsoft.baselib.widget.CenterProgressUpdateView import com.tenlionsoft.baselib.widget.CenterProgressUpdateView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File import java.io.File
/** /**
@ -51,10 +47,14 @@ class MainActivity : BaseActivity() {
private lateinit var mLocalReceiver: MainBroadcastReceiver private lateinit var mLocalReceiver: MainBroadcastReceiver
private var mUpdateView: CenterProgressUpdateView? = null private var mUpdateView: CenterProgressUpdateView? = null
private var mApkFile: File? = null private var mApkFile: File? = null
private var onSocketConnectListener: OnSocketConnectListener? = null
private val mainPageViewModel: MainPageViewModel by lazy {
ViewModelProvider(this)[MainPageViewModel::class.java]
}
override fun bindView() { override fun bindView() {
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mBinding.mainModel = mainPageViewModel
initView(); initView();
} }
@ -108,26 +108,10 @@ class MainActivity : BaseActivity() {
} }
} }
}) })
registerLocalReceiver() mainPageViewModel.doCheckAppVersion(this@MainActivity)
checkAppVersion() mainPageViewModel.isNeedDownload.observe(this) {
} if (it) {
startDownloadApk()
private fun checkAppVersion() {
lifecycleScope.launch {
withContext(Dispatchers.IO) {
try {
val appVersion = RetrofitClient.getInstance(this@MainActivity)
.create(UserApi::class.java)
.doCheckAppVersion(ProjectConfig.APP_VERSION_ID)
if (appVersion.versioncode.isNotEmpty()) {
val isNeedUpdate = AppUtils.checkcode(appVersion.versioncode)
if (isNeedUpdate) {
startDownloadApk()
}
}
} catch (e: Exception) {
ExParse.parse(e)
}
} }
} }
} }
@ -145,16 +129,17 @@ class MainActivity : BaseActivity() {
filter.addAction(ProjectConfig.ACTION_UPDATE_ERROR) //下载失败 filter.addAction(ProjectConfig.ACTION_UPDATE_ERROR) //下载失败
filter.addAction(ProjectConfig.ACTION_UPDATE_PROGRESS) //进度更新 filter.addAction(ProjectConfig.ACTION_UPDATE_PROGRESS) //进度更新
filter.addAction(ProjectConfig.ACTION_UPDATE_START) //开始下载App filter.addAction(ProjectConfig.ACTION_UPDATE_START) //开始下载App
filter.addAction(ProjectConfig.A_S_CONNECTED)//socket连接成功
filter.addAction(ProjectConfig.A_S_FAIL)//socket连接失败
filter.addAction(ProjectConfig.A_S_DISCONNECT)//socket连接断开
registerReceiver(mLocalReceiver, filter) registerReceiver(mLocalReceiver, filter)
} }
private suspend fun startDownloadApk() { private fun startDownloadApk() {
withContext(Dispatchers.Main) { ToastUtils.normal("检测到新版本,开始下载")
ToastUtils.normal("检测到新版本,开始下载") val intent = Intent(this@MainActivity, DownloadApkService::class.java)
val intent = Intent(this@MainActivity, DownloadApkService::class.java) intent.putExtra(ProjectConfig.APK_DOWNLOAD_URL, ProjectConfig.APP_DOWNLOAD_URL)
intent.putExtra(ProjectConfig.APK_DOWNLOAD_URL, ProjectConfig.APP_DOWNLOAD_URL) startService(intent)
startService(intent)
}
} }
@ -186,15 +171,25 @@ class MainActivity : BaseActivity() {
} }
} }
override fun onStart() {
super.onStart()
registerLocalReceiver()
}
override fun onDestroy() { override fun onPause() {
super.onDestroy() super.onPause()
unRegisterLocalReceiver() unRegisterLocalReceiver()
} }
fun setOnSocketListener(onSocketConnectListener: OnSocketConnectListener) {
this.onSocketConnectListener = onSocketConnectListener
}
inner class MainBroadcastReceiver : BroadcastReceiver() { inner class MainBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
Log.e("MainBroadcastReceiver", "onReceive: ${intent?.action}")
when (intent?.action) { when (intent?.action) {
ProjectConfig.ACTION_UPDATE_START -> { ProjectConfig.ACTION_UPDATE_START -> {
showUpdateProgress() showUpdateProgress()
@ -215,10 +210,27 @@ class MainActivity : BaseActivity() {
val apkFile = intent.getSerializableExtra("apkFile") as File? val apkFile = intent.getSerializableExtra("apkFile") as File?
installApk(apkFile!!) installApk(apkFile!!)
}//下载成功 }//下载成功
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)
if (!extra.isNullOrEmpty()) {
onSocketConnectListener?.onReceiverMsg(extra)
}
}//接收到信息
else -> {} else -> {}
} }
} }
} }
interface OnSocketConnectListener {
fun onListener(status: String)
fun onReceiverMsg(msg: String)
}
} }

View File

@ -1,5 +1,6 @@
package com.tenlionsoft.aimz_k.page.fragments package com.tenlionsoft.aimz_k.page.fragments
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
@ -8,18 +9,21 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.tenlionsoft.aimz_k.R import com.tenlionsoft.aimz_k.R
import com.tenlionsoft.aimz_k.databinding.FragmentMsgBinding import com.tenlionsoft.aimz_k.databinding.FragmentMsgBinding
import com.tenlionsoft.aimz_k.model.MsgCategoryBean import com.tenlionsoft.aimz_k.model.MsgCategoryBean
import com.tenlionsoft.aimz_k.page.activity.ChatActivity import com.tenlionsoft.aimz_k.page.activity.ChatActivity
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.aimz_k.viewmodel.MsgViewModel
import com.tenlionsoft.baselib.contacts.ProjectConfig
import com.tenlionsoft.baselib.widget.LoadingDialog import com.tenlionsoft.baselib.widget.LoadingDialog
class MsgFragment : Fragment(), MsgViewModel.OnItemClickListener { class MsgFragment : Fragment(), MsgViewModel.OnItemClickListener,
MainActivity.OnSocketConnectListener {
private lateinit var mMsgBinding: FragmentMsgBinding private lateinit var mMsgBinding: FragmentMsgBinding
private var mActivity: FragmentActivity? = null private var mActivity: MainActivity? = null
private var mLoading: LoadingDialog? = null; private var mLoading: LoadingDialog? = null;
companion object { companion object {
@ -43,7 +47,6 @@ class MsgFragment : Fragment(), MsgViewModel.OnItemClickListener {
container, container,
false false
); );
mActivity = activity
mMsgBinding.llSearchLayout.llSearchLayout.setOnClickListener { mMsgBinding.llSearchLayout.llSearchLayout.setOnClickListener {
startActivity(Intent(this@MsgFragment.context, ChatActivity::class.java)) startActivity(Intent(this@MsgFragment.context, ChatActivity::class.java))
} }
@ -66,6 +69,34 @@ class MsgFragment : Fragment(), MsgViewModel.OnItemClickListener {
mMsgBinding.srlMsgs.finishRefresh() mMsgBinding.srlMsgs.finishRefresh()
mMsgBinding.srlMsgs.finishLoadMore() mMsgBinding.srlMsgs.finishLoadMore()
} }
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
}
}
//登陆socket
viewModel.doLoginWebsocket()
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is MainActivity) {
mActivity = context
mActivity!!.setOnSocketListener(this)
}
}
//开启service
private fun toStartService() {
mActivity?.startService(Intent(mActivity!!, SocketService::class.java))
} }
/** /**
@ -76,4 +107,25 @@ class MsgFragment : Fragment(), MsgViewModel.OnItemClickListener {
startActivity(Intent(mActivity, ChatActivity::class.java)) startActivity(Intent(mActivity, ChatActivity::class.java))
} }
//监听socket连接状态
override fun onListener(status: String) {
Log.e("MsgFragment", "onListener: ${status}")
when (status) {
ProjectConfig.A_S_CONNECTED -> {
viewModel.isLogining.value = false
}//连接成功
ProjectConfig.A_S_FAIL -> {
viewModel.isLogining.value = true
}//连接失败
ProjectConfig.A_S_DISCONNECT -> {}//连接断开
}
}
//接收到socket信息
override fun onReceiverMsg(msg: String) {
if (msg.isNotEmpty()) {
viewModel.fromMsg(msg)
}
}
} }

View File

@ -1,23 +1,30 @@
package com.tenlionsoft.aimz_k.services package com.tenlionsoft.aimz_k.services
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.app.Service import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import com.tenlionsoft.aimz_k.R import android.util.Log
import com.google.gson.Gson
import com.tenlionsoft.aimz_k.socket.WsManager import com.tenlionsoft.aimz_k.socket.WsManager
import com.tenlionsoft.baselib.contacts.NetConfig
import com.tenlionsoft.baselib.contacts.ProjectConfig import com.tenlionsoft.baselib.contacts.ProjectConfig
import com.tenlionsoft.baselib.utils.SpUtils
/** /**
* Socket service * Socket service
*/ */
class SocketService : Service() { class SocketService : Service(), WsManager.MsgCallBack {
private var mWsManager: WsManager? = null private var mWsManager: WsManager? = null
private val gson = Gson()
private lateinit var mLocalReceiver: LocalReceiver
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@ -33,30 +40,40 @@ class SocketService : Service() {
@SuppressLint("ForegroundServiceType") @SuppressLint("ForegroundServiceType")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val builder: Notification.Builder // val builder: Notification.Builder
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = Notification.Builder(this, ProjectConfig.NOTIFY_CHANNEL_ID) // builder = Notification.Builder(this, ProjectConfig.NOTIFY_CHANNEL_ID)
} else { // } else {
builder = Notification.Builder(this) // builder = Notification.Builder(this)
} // }
builder.setContentTitle("Ai秒著") // builder.setContentTitle("Ai秒著")
.setContentText("聊天已经登陆") // .setContentText("聊天已经登陆")
.setSmallIcon(R.drawable.app_logo_small) // .setSmallIcon(R.drawable.app_logo_small)
val notification: Notification = builder.build() // val notification: Notification = builder.build()
startForeground(startId, notification) // startForeground(startId, notification)
this.startSocket();//开启Socket this.startSocket();//开启Socket
registerLocalReceiver()
return START_STICKY return START_STICKY
} }
private fun registerLocalReceiver() {
val intentFilter = IntentFilter()
mLocalReceiver = LocalReceiver()
intentFilter.addAction(ProjectConfig.A_S_MSG_SEND)//发送消息
registerReceiver(mLocalReceiver, intentFilter)
}
/** /**
* 开启链接socket * 开启链接socket
*/ */
fun startSocket() { private fun startSocket() {
if (mWsManager == null) { if (mWsManager == null) {
mWsManager = WsManager.Builder(applicationContext) mWsManager = WsManager.Builder(applicationContext)
.wsUrl(ProjectConfig.SOCKET_MSG_SECRET) .wsUrl(NetConfig.WS_URL + "?token=" + SpUtils.getToken())
.needReconnect(true) .needReconnect(true)
.addCallBack(this)
.build() .build()
} }
if (!mWsManager!!.isWsConnected()) { if (!mWsManager!!.isWsConnected()) {
@ -65,7 +82,7 @@ class SocketService : Service() {
} }
fun stopSocket() { private fun stopSocket() {
if (mWsManager != null && mWsManager!!.isWsConnected()) { if (mWsManager != null && mWsManager!!.isWsConnected()) {
mWsManager!!.stopConnect(); mWsManager!!.stopConnect();
mWsManager = null; mWsManager = null;
@ -78,6 +95,28 @@ class SocketService : Service() {
override fun onDestroy() { override fun onDestroy() {
stopSocket(); stopSocket();
unregisterReceiver(mLocalReceiver)
super.onDestroy() super.onDestroy()
} }
inner class LocalReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
ProjectConfig.A_S_MSG_SEND -> {
val msgConvertBean = intent.getSerializableExtra("msgBean")
val msg = gson.toJson(msgConvertBean)
mWsManager?.sendMessage(msg)
}//发送消息
}
}
}
//socket接收到信息
override fun onCallBackMsg(str: String) {
Log.e("SocketService", "接收信息: ${str}")
val intent = Intent(ProjectConfig.A_S_MSG_RECEIVER)
intent.putExtra("msg", str)
sendBroadcast(intent)
}
} }

View File

@ -40,8 +40,10 @@ class WsManager private constructor(val builder: Builder) : IWsManager {
private var mLock: Lock = ReentrantLock() private var mLock: Lock = ReentrantLock()
private val wsMainHandler: Handler = Handler(Looper.getMainLooper()) private val wsMainHandler: Handler = Handler(Looper.getMainLooper())
private var reconnectCount = 5 //重连次数 private var reconnectCount = 5 //重连次数
private var mMsgCallBack: MsgCallBack? = builder.callBack
private val reconnectRunnable = Runnable { private val reconnectRunnable = Runnable {
if (reconnectCount < 10) { if (reconnectCount < 10) {
//重新连接
val intent = Intent() val intent = Intent()
// intent.setAction(PathConfig.ACTION_SOCKET_RELINK) // intent.setAction(PathConfig.ACTION_SOCKET_RELINK)
mContext.sendBroadcast(intent) mContext.sendBroadcast(intent)
@ -50,43 +52,12 @@ class WsManager private constructor(val builder: Builder) : IWsManager {
private val mWebSocketListener: WebSocketListener = object : WebSocketListener() { private val mWebSocketListener: WebSocketListener = object : WebSocketListener() {
private fun buildMsgBean(type: Int, body: String): String {
// val message: AppSocketMessage = AppSocketMessage()
// message.setType(type)
// val userId: String = GlobalProvider.getString(mContext, "userId")
// message.setFrom(userId)
// message.setTo(userId)
//
// var bodyBean: BaseSocketBodyBean? = null
// when (type) {
// 1000 -> {
// bodyBean = SocketRegisterBodyBean()
// (bodyBean as SocketRegisterBodyBean?).setSessionId(body)
// }
// }
// val gson = Gson()
// val s = gson.toJson(bodyBean)
// message.setBody(s)
// val messageStr = gson.toJson(message)
// return messageStr
return body
}
override fun onOpen(webSocket: WebSocket, response: Response) { override fun onOpen(webSocket: WebSocket, response: Response) {
mWebSocket = webSocket mWebSocket = webSocket
setCurrentStatus(WsStatus.CONNECTED) setCurrentStatus(WsStatus.CONNECTED)
connected() connected()
//注册websocket sendBroadcast(ProjectConfig.A_S_CONNECTED)
// TODO val sessionId: String = GlobalProvider.getString(mContext, StatusCode.SESSION_ID)
var sessionId: String = ""
LogUtils.e("Session_Id==$sessionId")
val messageStr = buildMsgBean(1000, sessionId)
if (Looper.myLooper() != Looper.getMainLooper()) { //主线程
val isSend = sendMessage(messageStr)
} else { //子线程
val isSend = sendMessage(messageStr)
}
} }
override fun onMessage(webSocket: WebSocket, bytes: ByteString) { override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
@ -124,9 +95,8 @@ class WsManager private constructor(val builder: Builder) : IWsManager {
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
try { try {
tryReconnect() tryReconnect()
val intent = Intent() //发送失败广播
//TODO intent.setAction(PathConfig.ACTION_MSG_SOCKET_FAIL) sendBroadcast(ProjectConfig.A_S_FAIL)
mContext!!.sendBroadcast(intent)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
@ -152,9 +122,16 @@ class WsManager private constructor(val builder: Builder) : IWsManager {
mLock.unlock() mLock.unlock()
} }
} catch (e: InterruptedException) { } catch (e: InterruptedException) {
e.printStackTrace()
} }
} }
//发送广播
private fun sendBroadcast(action: String) {
val intent = Intent(action)
mContext.sendBroadcast(intent)
}
override fun getWebSocket(): WebSocket? { override fun getWebSocket(): WebSocket? {
return mWebSocket return mWebSocket
} }
@ -291,10 +268,7 @@ class WsManager private constructor(val builder: Builder) : IWsManager {
private fun isNetworkConnected(context: Context?): Boolean { private fun isNetworkConnected(context: Context?): Boolean {
if (context != null) { if (context != null) {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork: Network? = cm.getActiveNetwork() val activeNetwork: Network = cm.activeNetwork ?: return false
if (activeNetwork == null) {
return false
}
val capabilities = cm.getNetworkCapabilities(activeNetwork) val capabilities = cm.getNetworkCapabilities(activeNetwork)
return capabilities != null && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) return capabilities != null && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
} else { } else {
@ -303,33 +277,20 @@ class WsManager private constructor(val builder: Builder) : IWsManager {
} }
private fun sendNotify(jsonStr: String) { private fun sendNotify(jsonStr: String) {
// if (!TextUtils.isEmpty(jsonStr)) { if (jsonStr.isNotEmpty()) {
// LogUtils.e("收到消息:$jsonStr") mMsgCallBack?.onCallBackMsg(jsonStr)
// val broadCstIntent = Intent() }
// if (PathConfig.IS_SECRET) {
// try {
// val msg: String =
// AesUtil.aesCommonDecoder(PathConfig.SOCKET_MSG_SECRET, jsonStr)
// broadCstIntent.setAction(PathConfig.ACTION_FROM_SOCKET_PUSH_MSG)
// broadCstIntent.putExtra(StatusCode.PUSH_DATA_KEY, msg)
// mContext!!.sendBroadcast(broadCstIntent)
// } catch (e: Exception) {
// e.printStackTrace()
// }
// } else {
// broadCstIntent.setAction(PathConfig.ACTION_FROM_SOCKET_PUSH_MSG)
// broadCstIntent.putExtra(StatusCode.PUSH_DATA_KEY, jsonStr)
// mContext!!.sendBroadcast(broadCstIntent)
// }
// }
} }
interface MsgCallBack {
fun onCallBackMsg(str: String)
}
class Builder(val mContext: Context) { class Builder(val mContext: Context) {
lateinit var wsUrl: String lateinit var wsUrl: String
var needReconnect: Boolean = true var needReconnect: Boolean = true
var mOkHttpClient: OkHttpClient? = null var mOkHttpClient: OkHttpClient? = null
var callBack: MsgCallBack? = null
fun wsUrl(url: String): Builder { fun wsUrl(url: String): Builder {
wsUrl = url wsUrl = url
return this return this
@ -345,6 +306,11 @@ class WsManager private constructor(val builder: Builder) : IWsManager {
return this return this
} }
fun addCallBack(callBack: MsgCallBack): Builder {
this.callBack = callBack
return this
}
fun build(): WsManager { fun build(): WsManager {
return WsManager(this) return WsManager(this)
} }

View File

@ -1,27 +1,45 @@
package com.tenlionsoft.aimz_k.viewmodel package com.tenlionsoft.aimz_k.viewmodel
import android.content.Intent
import android.util.Log import android.util.Log
import android.view.View
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.atwa.filepicker.result.FileMeta import com.atwa.filepicker.result.FileMeta
import com.atwa.filepicker.result.ImageMeta import com.atwa.filepicker.result.ImageMeta
import com.atwa.filepicker.result.VideoMeta import com.atwa.filepicker.result.VideoMeta
import com.google.gson.Gson
import com.tenlionsoft.aimz_k.ConvertBeanUtils
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.MsgConvertBean
import com.tenlionsoft.aimz_k.model.PickerType import com.tenlionsoft.aimz_k.model.PickerType
import com.tenlionsoft.aimz_k.model.Receiver
import com.tenlionsoft.aimz_k.model.Sender
import com.tenlionsoft.baselib.base.BaseViewModel import com.tenlionsoft.baselib.base.BaseViewModel
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
class ChatPageViewModel : BaseViewModel() { class ChatPageViewModel : BaseViewModel() {
var txtMsg: String = "" val txtMsg = MutableLiveData<String>("")
val showSendBtn = MutableLiveData(false)//显示/隐藏发送按钮 val showSendBtn = MutableLiveData(false)//显示/隐藏发送按钮
val showEmojiLayout = MutableLiveData(false)//显示/隐藏emoji val showEmojiLayout = MutableLiveData(false)//显示/隐藏emoji
val showReplyLayout = MutableLiveData(false)//显示/隐藏快速回复 val showReplyLayout = MutableLiveData(false)//显示/隐藏快速回复
val showChooseLayout = MutableLiveData(false)//显示/隐藏选择 val showChooseLayout = MutableLiveData(false)//显示/隐藏选择
val chooseType = MutableLiveData<PickerType>()//选择文件类型 val chooseType = MutableLiveData<PickerType>()//选择文件类型
private val mGson: Gson = Gson()
init { init {
Log.e("ChatPageViewModel", "Init: ${showSendBtn.value}") Log.e("ChatPageViewModel", "Init: ${showSendBtn.value}")
} }
fun onTxtChange(s: CharSequence, start: Int, before: Int, count: Int) { fun onTxtChange(s: CharSequence, start: Int, before: Int, count: Int) {
this.txtMsg = s.toString() this.txtMsg.value = s.toString()
showSendBtn.value = s.isNotEmpty() showSendBtn.value = s.isNotEmpty()
Log.e("ChatPageViewModel", "onTxtChange: ${showSendBtn.value}") Log.e("ChatPageViewModel", "onTxtChange: ${showSendBtn.value}")
@ -103,7 +121,87 @@ class ChatPageViewModel : BaseViewModel() {
/** /**
* 发送文本信息 * 发送文本信息
*/ */
fun sendTxtMsg() { fun sendTxtMsg(v: View) {
if (txtMsg.value!!.isNotEmpty()) {
val intent = Intent(ProjectConfig.A_S_MSG_SEND)
val b = buildSendBean()
viewModelScope.launch {
withContext(Dispatchers.IO) {
val dao = DbManager.db.msgDao()
val msgBean = ConvertBeanUtils.convertBeanToMsgBean(b)
dao.insertMsg(msgBean)
}
}
Log.e("ChatPageViewModel", "sendTxtMsg: ${b}")
intent.putExtra("msgBean", b)
v.context.sendBroadcast(intent)
txtMsg.value = ""
}
//TODO 刷新列表
}
//收到消息
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,
mGson.fromJson(bean.data.body, BodyContent::class.java).statusType!!
)
}
//TODO 刷新列表
}
}
//消息
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)
}
}
//刷新列表
Log.e("ChatPageViewModel", "receiveMsg接收到消息: ${bean.msgBean}")
}
}
}
}
private fun buildSendBean(): MsgConvertBean {
val bodyBean = BodyContent(content = txtMsg.value, null, null)
var body = mGson.toJson(bodyBean)
return MsgConvertBean(
body = body,
customMessageType = "",
messageId = TimeUtils.getNowDateMillis().toString(),
messageType = ProjectConfig.MSG_TEXT,
metadata = "",
timestamp = TimeUtils.getNowDateMillis(),
sender = Sender(
senderId = SpUtils.getId(),
senderType = ""
),
receiver = Receiver(
receiverId = "2",
receiverType = "SINGLE_USER"
),
status = ProjectConfig.MSG_SEND_ING,
)
} }
} }

View File

@ -37,7 +37,6 @@ class LoginPageViewModel : BaseViewModel() {
} }
fun doLogin() { fun doLogin() {
isLoginSuccess.value = true
val isLegal = checkParams(); val isLegal = checkParams();
if (isLegal) { if (isLegal) {
viewModelScope.launch { viewModelScope.launch {
@ -56,12 +55,11 @@ class LoginPageViewModel : BaseViewModel() {
userPwd, userPwd,
userName userName
); );
Log.e("LoginPageViewModel", "登陆前: Thread${Thread.currentThread().name}")
val loginUserStr = gson.toJson(userBody) val loginUserStr = gson.toJson(userBody)
val body = val body =
loginUserStr.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) loginUserStr.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
val user = userApi.doLogin(body); val user = userApi.doLogin(body);
Log.e("LoginPageViewModel", "login: ${user.code}")
if (user.code == 200) { if (user.code == 200) {
//登陆成功 //登陆成功
val token = JwtUtils.parseToken(user.data) val token = JwtUtils.parseToken(user.data)
@ -71,12 +69,16 @@ class LoginPageViewModel : BaseViewModel() {
} else { } else {
//登陆成功 //登陆成功
//解析Token //解析Token
Log.e("LoginPageViewModel", "login: $token") Log.e("LoginPageViewModel", "login: ${token}")
val appTokenUser = gson.fromJson(token, AppTokenUser::class.java) val appTokenUser = gson.fromJson(token, AppTokenUser::class.java)
Log.e(
"LoginPageViewModel",
"Thread ${Thread.currentThread().name} login: UserId${appTokenUser.user.id}"
)
SpUtils.putPassword(userPwd) SpUtils.putPassword(userPwd)
SpUtils.putAvatar(appTokenUser.user.avatar) SpUtils.putAvatar(appTokenUser.user.avatar)
SpUtils.putNickName(appTokenUser.user.nickname) SpUtils.putNickName(appTokenUser.user.nickname)
SpUtils.putId(appTokenUser.user.id) SpUtils.putId(appTokenUser.user.userId)
SpUtils.putEmail(appTokenUser.user.email) SpUtils.putEmail(appTokenUser.user.email)
SpUtils.putUserName(appTokenUser.user.username) SpUtils.putUserName(appTokenUser.user.username)
SpUtils.putPhone(appTokenUser.user.phone) SpUtils.putPhone(appTokenUser.user.phone)

View File

@ -0,0 +1,39 @@
package com.tenlionsoft.aimz_k.viewmodel
import android.content.Context
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
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.AppUtils
import kotlinx.coroutines.launch
class MainPageViewModel : BaseViewModel() {
val isNeedDownload = MutableLiveData<Boolean>(false)
//校验Appversion
fun doCheckAppVersion(context: Context) {
viewModelScope.launch {
getAppVersion(context)
}
}
private suspend fun getAppVersion(context: Context) {
try {
val appVersion = RetrofitClient.getInstance(context)
.create(UserApi::class.java)
.doCheckAppVersion(ProjectConfig.APP_VERSION_ID)
if (appVersion.versioncode.isNotEmpty()) {
val isNeedUpdate = AppUtils.checkcode(appVersion.versioncode)
if (isNeedUpdate) {
isNeedDownload.value = true
}
}
} catch (e: Exception) {
ExParse.parse(e)
}
}
}

View File

@ -1,36 +1,34 @@
package com.tenlionsoft.aimz_k.viewmodel package com.tenlionsoft.aimz_k.viewmodel
import android.util.Log
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.tenlionsoft.aimz_k.ConvertBeanUtils
import com.tenlionsoft.aimz_k.adapter.CategoryAdapter
import com.tenlionsoft.aimz_k.model.CoverSealedBean
import com.tenlionsoft.aimz_k.model.DbManager import com.tenlionsoft.aimz_k.model.DbManager
import com.tenlionsoft.aimz_k.model.MsgCategoryBean import com.tenlionsoft.aimz_k.model.MsgCategoryBean
import com.tenlionsoft.aimz_k.adapter.CategoryAdapter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class MsgViewModel : ViewModel() { class MsgViewModel : ViewModel() {
private val _pwdList = MutableLiveData<List<MsgCategoryBean>>() private val _pwdList = MutableLiveData<List<MsgCategoryBean>>()
var adapter: CategoryAdapter = CategoryAdapter(_pwdList.value ?: emptyList(),this) var adapter: CategoryAdapter = CategoryAdapter(_pwdList.value ?: emptyList(), this)
var onItemClickListener: OnItemClickListener? = null var onItemClickListener: OnItemClickListener? = null
val isLogining = MutableLiveData<Boolean>(false)
private var mCurrentPage: Int = 1 private var mCurrentPage: Int = 1
var isHasMore: MutableLiveData<Boolean> = MutableLiveData<Boolean>().apply { var isHasMore: MutableLiveData<Boolean> = MutableLiveData<Boolean>(false)
value = false
}
init { init {
Log.e("MsgViewModel", "Init:${_pwdList.value} ") getList(true)
getPwdList(true)
} }
/** /**
* 获取列表数据 * 获取列表数据
*/ */
private fun getPwdList(isFirst: Boolean) { private fun getList(isFirst: Boolean) {
viewModelScope.launch { viewModelScope.launch {
if (isFirst) { if (isFirst) {
_pwdList.value = emptyList() _pwdList.value = emptyList()
@ -46,7 +44,6 @@ class MsgViewModel : ViewModel() {
} }
val updateData: List<MsgCategoryBean> = _pwdList.value!! + list val updateData: List<MsgCategoryBean> = _pwdList.value!! + list
_pwdList.value = updateData _pwdList.value = updateData
Log.e("MsgViewModel", "getPwdList: ${_pwdList.value}")
adapter.setData(_pwdList.value!!) adapter.setData(_pwdList.value!!)
} }
} }
@ -64,14 +61,30 @@ class MsgViewModel : ViewModel() {
* 加载更多 * 加载更多
*/ */
fun loadMore() { fun loadMore() {
getPwdList(false) getList(false)
}
/**
* 登陆websocket
*/
fun doLoginWebsocket() {
isLogining.value = true
}
//接收到信息
fun fromMsg(msg: String) {
val bean = ConvertBeanUtils.covertBean(msg)
when (bean) {
is CoverSealedBean.StateBean -> {}//消息状态
is CoverSealedBean.Msg -> {}//消息
}
} }
/** /**
* 刷新 * 刷新
*/ */
fun refresh() { fun refresh() {
getPwdList(true) getList(true)
} }
interface OnItemClickListener { interface OnItemClickListener {

View File

@ -10,7 +10,7 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.tenlionsoft.aimz_k.R import com.tenlionsoft.aimz_k.R
import com.tenlionsoft.aimz_k.model.MsgTypeStateEnum import com.tenlionsoft.baselib.contacts.ProjectConfig
object BindingUtils { object BindingUtils {
@ -52,11 +52,11 @@ object BindingUtils {
//发送中 //发送中
@BindingAdapter("isShowLoading") @BindingAdapter("isShowLoading")
@JvmStatic @JvmStatic
fun sending(view: View, status: Int) { fun sending(view: View, status: String) {
when (status) { when (status) {
MsgTypeStateEnum.MSG_SEND_SUCCESS -> view.visibility = View.GONE ProjectConfig.MSG_SEND_SUCCESS -> view.visibility = View.GONE
MsgTypeStateEnum.MSG_SEND_FAIL -> view.visibility = View.GONE ProjectConfig.MSG_SEND_FAIL -> view.visibility = View.GONE
MsgTypeStateEnum.MSG_SEND_ING -> view.visibility = View.VISIBLE ProjectConfig.MSG_SEND_ING -> view.visibility = View.VISIBLE
else -> view.visibility = View.GONE else -> view.visibility = View.GONE
} }
} }
@ -64,11 +64,11 @@ object BindingUtils {
//发送失败 //发送失败
@BindingAdapter("isShowFail") @BindingAdapter("isShowFail")
@JvmStatic @JvmStatic
fun sendFail(view: View, status: Int) { fun sendFail(view: View, status: String) {
when (status) { when (status) {
MsgTypeStateEnum.MSG_SEND_SUCCESS -> view.visibility = View.GONE ProjectConfig.MSG_SEND_SUCCESS -> view.visibility = View.GONE
MsgTypeStateEnum.MSG_SEND_FAIL -> view.visibility = View.GONE ProjectConfig.MSG_SEND_FAIL -> view.visibility = View.GONE
MsgTypeStateEnum.MSG_SEND_ING -> view.visibility = View.VISIBLE ProjectConfig.MSG_SEND_ING -> view.visibility = View.VISIBLE
else -> view.visibility = View.GONE else -> view.visibility = View.GONE
} }
} }

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/green" />
</shape>

View File

@ -4,7 +4,8 @@
<data> <data>
<import type="android.view.View"/> <import type="android.view.View" />
<variable <variable
name="viewModel" name="viewModel"
type="com.tenlionsoft.aimz_k.viewmodel.ChatPageViewModel" /> type="com.tenlionsoft.aimz_k.viewmodel.ChatPageViewModel" />
@ -125,7 +126,7 @@
android:id="@+id/iv_send" android:id="@+id/iv_send"
android:layout_width="35dp" android:layout_width="35dp"
android:layout_height="30dp" android:layout_height="30dp"
android:onClick="@{()->viewModel.sendTxtMsg()}" android:onClick="@{(v)->viewModel.sendTxtMsg(v)}"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_send_msg" android:src="@drawable/ic_send_msg"
android:visibility="@{viewModel.showSendBtn? View.VISIBLE:View.GONE}" /> android:visibility="@{viewModel.showSendBtn? View.VISIBLE:View.GONE}" />
@ -181,9 +182,9 @@
<RelativeLayout <RelativeLayout
android:id="@+id/rlPhoto" android:id="@+id/rlPhoto"
android:layout_width="0dp" android:layout_width="0dp"
android:onClick="@{()->viewModel.showChoosePic()}"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1"> android:layout_weight="1"
android:onClick="@{()->viewModel.showChoosePic()}">
<ImageView <ImageView
android:id="@+id/ivPhoto" android:id="@+id/ivPhoto"
@ -206,9 +207,9 @@
<RelativeLayout <RelativeLayout
android:id="@+id/rlVideo" android:id="@+id/rlVideo"
android:layout_width="0dp" android:layout_width="0dp"
android:onClick="@{()->viewModel.showChooseVideo()}"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1"> android:layout_weight="1"
android:onClick="@{()->viewModel.showChooseVideo()}">
<ImageView <ImageView
android:id="@+id/ivVideo" android:id="@+id/ivVideo"
@ -232,8 +233,8 @@
android:id="@+id/rlFile" android:id="@+id/rlFile"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:onClick="@{()->viewModel.showChooseFile()}" android:layout_weight="1"
android:layout_weight="1"> android:onClick="@{()->viewModel.showChooseFile()}">
<ImageView <ImageView
android:id="@+id/ivFile" android:id="@+id/ivFile"

View File

@ -4,6 +4,9 @@
<data> <data>
<variable
name="mainModel"
type="com.tenlionsoft.aimz_k.viewmodel.MainPageViewModel" />
</data> </data>
<RelativeLayout <RelativeLayout

View File

@ -8,6 +8,10 @@
name="pos" name="pos"
type="Integer" /> type="Integer" />
<variable
name="size"
type="Integer" />
<import type="android.view.View" /> <import type="android.view.View" />
<variable <variable
@ -96,6 +100,7 @@
</LinearLayout> </LinearLayout>
<View <View
android:visibility="@{pos&lt; size?View.VISIBLE:View.GONE}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"

View File

@ -14,7 +14,7 @@
<variable <variable
name="state" name="state"
type="Integer" /> type="String" />
</data> </data>
<LinearLayout <LinearLayout

View File

@ -12,7 +12,7 @@
type="com.tenlionsoft.aimz_k.model.MsgBean" /> type="com.tenlionsoft.aimz_k.model.MsgBean" />
<variable <variable
name="state" name="state"
type="Integer" /> type="String" />
</data> </data>
<LinearLayout <LinearLayout

View File

@ -13,7 +13,7 @@
type="com.tenlionsoft.aimz_k.model.MsgBean" /> type="com.tenlionsoft.aimz_k.model.MsgBean" />
<variable <variable
name="state" name="state"
type="Integer" /> type="String" />
</data> </data>
<LinearLayout <LinearLayout

View File

@ -13,7 +13,7 @@
type="com.tenlionsoft.aimz_k.model.MsgBean" /> type="com.tenlionsoft.aimz_k.model.MsgBean" />
<variable <variable
name="state" name="state"
type="Integer" /> type="String" />>
</data> </data>
<LinearLayout <LinearLayout

View File

@ -13,7 +13,7 @@
<variable <variable
name="state" name="state"
type="Integer" /> type="String" />
</data> </data>
<LinearLayout <LinearLayout

View File

@ -14,7 +14,7 @@
<variable <variable
name="state" name="state"
type="Integer" /> type="String" />
<import type="com.tenlionsoft.aimz_k.widget.BindingUtils" /> <import type="com.tenlionsoft.aimz_k.widget.BindingUtils" />
</data> </data>

View File

@ -13,7 +13,7 @@
type="com.tenlionsoft.aimz_k.model.MsgBean" /> type="com.tenlionsoft.aimz_k.model.MsgBean" />
<variable <variable
name="state" name="state"
type="Integer" /> type="String" />
</data> </data>
<LinearLayout <LinearLayout

View File

@ -14,7 +14,7 @@
type="com.tenlionsoft.aimz_k.model.MsgBean" /> type="com.tenlionsoft.aimz_k.model.MsgBean" />
<variable <variable
name="state" name="state"
type="Integer" /> type="String" />>
</data> </data>
<LinearLayout <LinearLayout

View File

@ -1,26 +1,68 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout <RelativeLayout
android:id="@+id/ll_search_layout" android:id="@+id/ll_search_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:background="@drawable/shp_white_5_radius" android:background="@drawable/shp_white_5_radius"
android:gravity="center" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <LinearLayout
android:layout_width="32dp" android:id="@+id/ll_load"
android:layout_height="32dp" android:layout_width="wrap_content"
android:src="@drawable/ic_search_gray" /> android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp">
<TextView <ImageView
android:id="@+id/iv_socket_status"
android:layout_width="5dp"
android:layout_height="5dp"
android:layout_marginTop="3dp"
android:layout_marginRight="3dp"
android:src="@drawable/shp_circle_red" />
<ProgressBar
android:id="@+id/pb_socket_loading"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_gravity="center_vertical"
android:indeterminateBehavior="repeat"
android:indeterminateDrawable="@drawable/anim_loading" />
<TextView
android:id="@+id/tv_socket_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:text="登陆中..."
android:textColor="@color/gray_6f"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="8dp" android:layout_centerInParent="true"
android:gravity="center" android:orientation="horizontal">
android:text="搜索"
android:textSize="18sp" /> <ImageView
</LinearLayout> android:layout_width="32dp"
android:layout_height="32dp"
android:src="@drawable/ic_search_gray" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:gravity="center"
android:text="搜索"
android:textSize="18sp" />
</LinearLayout>
</RelativeLayout>
</layout> </layout>

View File

@ -3,7 +3,7 @@ package com.tenlionsoft.baselib.contacts
object NetConfig { object NetConfig {
const val BASE_URL = "http://192.168.0.26:8888/" const val BASE_URL = "http://192.168.0.26:8888/"
const val WS_URL = "wss://192.168.0.0:8089/wsscoket";//Socket连接地址 const val WS_URL = "ws://192.168.0.26:1991/";//Socket连接地址
const val MAIN_URL = BASE_URL + "system/" const val MAIN_URL = BASE_URL + "system/"

View File

@ -5,6 +5,7 @@ package com.tenlionsoft.baselib.contacts
*/ */
object ProjectConfig { object ProjectConfig {
const val IS_SECRET = false; const val IS_SECRET = false;
const val SECRET: String = "CMXX_TOKEN_INFOS"; //秘钥 const val SECRET: String = "CMXX_TOKEN_INFOS"; //秘钥
const val SOCKET_MSG_SECRET: String = "SocKEtsEcReT_KeY"; //消息秘钥 const val SOCKET_MSG_SECRET: String = "SocKEtsEcReT_KeY"; //消息秘钥
@ -15,6 +16,15 @@ object ProjectConfig {
const val APP_DOWNLOAD_URL: String = const val APP_DOWNLOAD_URL: String =
NetConfig.MAIN_URL + "app/appversion/download/" + APP_VERSION_ID NetConfig.MAIN_URL + "app/appversion/download/" + APP_VERSION_ID
//消息类型
const val MSG_TEXT = "MSG_TEXT"
const val MSG_FILE = "MSG_FILE"
const val MSG_IMG = "MSG_IMG"
const val MSG_VIDEO = "MSG_VIDEO"
const val MSG_STATUS = "STATUS" //状态消息
const val MSG_SEND_ING: String = "PENDING"//11 //发送中
const val MSG_SEND_FAIL: String = "ERROR"//12 //发送失败
const val MSG_SEND_SUCCESS: String = "SUCCESS_SEND"////发送成功
/********Action********/ /********Action********/
@ -23,4 +33,11 @@ object ProjectConfig {
const val ACTION_UPDATE_SUCCESS: String = "com.tenlion.soft.aimz_k.update_success" const val ACTION_UPDATE_SUCCESS: String = "com.tenlion.soft.aimz_k.update_success"
const val ACTION_UPDATE_START: String = "com.tenlion.soft.aimz_k.update_start" const val ACTION_UPDATE_START: String = "com.tenlion.soft.aimz_k.update_start"
/********socket*********/
const val A_S_CONNECTED: String = "com.tenlion.soft.aimz_k.socket.connected" //连接socket
const val A_S_FAIL: String = "com.tenlion.soft.aimz_k.socket.fail"//socket连接失败
const val A_S_DISCONNECT: String = "com.tenlion.soft.aimz_k.socket.disconnect"//连接断开
const val A_S_MSG_RECEIVER: String = "com.tenlion.soft.aimz_k.socket.receiver" //接收到信息
const val A_S_MSG_SEND: String = "com.tenlion.soft.aimz_k.socket.send"//发送信息
} }

View File

@ -18,9 +18,13 @@ class BaseUrlInterceptor : Interceptor {
val builder = request.newBuilder(); val builder = request.newBuilder();
val oldHttpUrl = request.url; val oldHttpUrl = request.url;
val headers = request.headers("project"); val headers = request.headers("project");
//公共header //公共Token header
builder.addHeader("X-WG-TOKEN", SpUtils.getToken()) if (headers.indexOf("token") != -1) {
builder.addHeader("X-WG-TYPE", "APP") builder.removeHeader("token")
builder.addHeader("X-WG-TOKEN", SpUtils.getToken())
builder.addHeader("X-WG-TYPE", "APP")
}
if (headers.isNotEmpty()) { if (headers.isNotEmpty()) {
builder.removeHeader("project"); builder.removeHeader("project");

View File

@ -0,0 +1,7 @@
package com.tenlionsoft.baselib.utils
object TimeUtils {
fun getNowDateMillis(): Long {
return System.currentTimeMillis()
}
}

View File

@ -37,7 +37,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:clickable="true" android:clickable="true"
android:textColor="@color/white" android:textColor="@color/black"
android:textSize="18sp" android:textSize="18sp"
tools:text="加载数据失败" /> tools:text="加载数据失败" />

View File

@ -4,8 +4,10 @@
<color name="white">#FFFFFF</color> <color name="white">#FFFFFF</color>
<color name="theme_bg_color">#F5F5F5</color> <color name="theme_bg_color">#F5F5F5</color>
<color name="tr">#00FFFFFF</color> <color name="tr">#00FFFFFF</color>
<color name="tr_bg">#54000000</color> <color name="tr_bg">#A3FFFFFF</color>
<color name="chat_page_bg">#EDEDED</color> <color name="chat_page_bg">#EDEDED</color>
<color name="red">#E75D58</color> <color name="red">#E75D58</color>
<color name="green">#45F68B</color>
<color name="gray_6f">#6F6E6E</color>
<color name="black">#000000</color> <color name="black">#000000</color>
</resources> </resources>