This commit is contained in:
itgaojian163 2024-11-08 17:54:08 +08:00
parent 8b706266a4
commit cf5d2c41b6
53 changed files with 1018 additions and 413 deletions

View File

@ -1,6 +1,7 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="WRAP_ELVIS_EXPRESSIONS" value="2" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<codeStyleSettings language="XML"> <codeStyleSettings language="XML">

View File

@ -0,0 +1,7 @@
<component name="CopyrightManager">
<settings>
<LanguageOptions name="Kotlin">
<option name="prefixLines" value="false" />
</LanguageOptions>
</settings>
</component>

View File

@ -21,18 +21,21 @@
android:networkSecurityConfig="@xml/network_config" android:networkSecurityConfig="@xml/network_config"
android:roundIcon="@drawable/app_logo" android:roundIcon="@drawable/app_logo"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Anim_fade" android:theme="@style/AppTheme"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:targetApi="31"> tools:targetApi="31">
<activity <activity
android:name=".page.activity.ApplyContactActivity" android:name=".page.activity.ApplyContactActivity"
android:exported="false" /> android:exported="false"
android:launchMode="singleTop" />
<activity <activity
android:name=".page.activity.SearchContactActivity" android:name=".page.activity.SearchContactActivity"
android:exported="false" /> android:exported="false"
android:launchMode="singleTop" />
<activity <activity
android:name=".page.activity.SplashActivity" android:name=".page.activity.SplashActivity"
android:exported="true"> android:exported="true"
android:launchMode="singleTop">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -41,22 +44,36 @@
</activity> </activity>
<activity <activity
android:name=".page.activity.LoginActivity" android:name=".page.activity.LoginActivity"
android:exported="false" /> android:exported="false"
android:launchMode="singleTop" />
<activity <activity
android:name=".page.activity.ChatActivity" android:name=".page.activity.ChatActivity"
android:exported="false" /> android:exported="false"
android:launchMode="singleTop" />
<activity <activity
android:name=".page.activity.MainActivity" android:name=".page.activity.MainActivity"
android:exported="false" android:exported="false"
android:launchMode="singleInstance" /> android:launchMode="singleTop" />
<activity <activity
android:name=".page.activity.ManageReplyActivity" android:name=".page.activity.ManageReplyActivity"
android:exported="false" android:exported="false"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustPan|stateHidden" /> android:windowSoftInputMode="adjustPan|stateHidden" />
<service <service
android:name=".services.SocketService" android:name=".services.SocketService"
android:exported="false" /> android:exported="false"
android:process=":tenlion_socket" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
</application> </application>
</manifest> </manifest>

View File

@ -34,7 +34,9 @@ object ConvertBeanUtils {
msgType = type, msgType = type,
timestamp = sBean.timestamp, timestamp = sBean.timestamp,
status = sBean.status, status = sBean.status,
customMessageType = "" customMessageType = "",
userNickName = "",
userAvatar = ""
) )
) )
} else { } else {
@ -62,7 +64,9 @@ object ConvertBeanUtils {
msgType = type, msgType = type,
timestamp = cBean.timestamp, timestamp = cBean.timestamp,
status = cBean.status, status = cBean.status,
customMessageType = "" customMessageType = "",
userNickName = "",
userAvatar = ""
) )
} }
@ -78,7 +82,8 @@ object ConvertBeanUtils {
status = msgBean.status, status = msgBean.status,
timestamp = msgBean.timestamp, timestamp = msgBean.timestamp,
msgType = msgBean.msgType, msgType = msgBean.msgType,
body = msgBean.body body = msgBean.body,
userNickName = ""
) )
} }

View File

@ -3,6 +3,7 @@ package com.tenlionsoft.aimz_k.adapter
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.lxj.xpopup.XPopup
import com.tenlionsoft.aimz_k.databinding.ItemContactBinding import com.tenlionsoft.aimz_k.databinding.ItemContactBinding
import com.tenlionsoft.aimz_k.model.ContactListBean import com.tenlionsoft.aimz_k.model.ContactListBean
import com.tenlionsoft.aimz_k.viewmodel.ContactViewModel import com.tenlionsoft.aimz_k.viewmodel.ContactViewModel
@ -29,8 +30,17 @@ class ContactAdapter(datas: List<ContactListBean>, val viewModel: BaseViewModel)
holder.binding.root.setOnClickListener { holder.binding.root.setOnClickListener {
viewModel.itemClickListener?.onItemClick(list[position]) viewModel.itemClickListener?.onItemClick(list[position])
} }
val xPopup = XPopup.Builder(holder.binding.root.context).watchView(holder.binding.root)
holder.binding.root.setOnLongClickListener { holder.binding.root.setOnLongClickListener {
viewModel.itemLongClickListener?.onItemLongClick(2, list[position]) xPopup.asAttachList(
arrayOf("删除该联系人"),
intArrayOf(com.tenlionsoft.baselib.R.drawable.ic_del),
{ p, _ ->
viewModel.itemLongClickListener?.onItemLongClick(2, list[position])
},
0,
0
).show()
true true
} }
} else { } else {

View File

@ -22,7 +22,7 @@ interface MsgCategoryDao {
/** /**
* 根据发送人ID获取 * 根据发送人ID获取
*/ */
@Query("SELECT * FROM db_category WHERE senderId=:senderId") @Query("SELECT * FROM db_category WHERE senderId=:senderId OR receiverId=:senderId")
suspend fun getCategoryBySenderId(senderId: String): MsgCategoryBean? suspend fun getCategoryBySenderId(senderId: String): MsgCategoryBean?
/** /**
@ -98,7 +98,7 @@ interface MsgCategoryDao {
*/ */
suspend fun updateOrInsert(senderId: String, msgBean: MsgBean) { suspend fun updateOrInsert(senderId: String, msgBean: MsgBean) {
val bean = getCategoryBySenderId(senderId) val bean = getCategoryBySenderId(senderId)
if (bean == null) { if (bean==null) {
//需要转换成MsgCategoryBean //需要转换成MsgCategoryBean
insertMsg(ConvertBeanUtils.convertCategoryBean(msgBean)) insertMsg(ConvertBeanUtils.convertCategoryBean(msgBean))
} else { } else {
@ -107,13 +107,19 @@ interface MsgCategoryDao {
} }
//更新 //更新
@Query("UPDATE db_category SET status=:status WHERE senderId=:senderId") @Query("UPDATE db_category SET status=:status WHERE senderId=:senderId OR receiverId=:senderId")
suspend fun updateStatus(senderId: String, status: String) suspend fun updateStatus(senderId: String, status: String)
/** /**
* 更新 消息体 时间戳 状态 * 更新 消息体 时间戳 状态
* @Query("UPDATE db_msg SET status = :status WHERE messageId = :messageId") * @Query("UPDATE db_msg SET status = :status WHERE messageId = :messageId")
*/ */
@Query("UPDATE db_category SET status= :status , body=:body , timestamp=:time WHERE senderId=:senderId") @Query("UPDATE db_category SET status= :status , body=:body , timestamp=:time WHERE senderId=:senderId OR receiverId=:senderId")
suspend fun updateCategory(senderId: String, status: String?, body: String, time: Long) suspend fun updateCategory(senderId: String, status: String?, body: String, time: Long)
/**
* 更新 头像和名称
*/
@Query("UPDATE db_category SET userNickName=:nickName,avatar=:avatar WHERE receiverId=:userId OR senderId=:userId")
suspend fun updateUserInfoById(nickName: String, avatar: String, userId: String)
} }

View File

@ -119,4 +119,10 @@ interface MsgDao {
*/ */
@Query("DELETE FROM db_msg") @Query("DELETE FROM db_msg")
suspend fun delAllMsg() suspend fun delAllMsg()
/**
* 更新 头像和名称
*/
@Query("UPDATE db_msg SET userNickName=:nickName,avatar=:avatar WHERE receiverId=:toId AND senderId=:fromId")
suspend fun updateUserInfoById(nickName: String, avatar: String, fromId: String, toId: String)
} }

View File

@ -20,7 +20,7 @@ interface ReplyDao {
* @return * @return
*/ */
@Query("SELECT * FROM db_reply") @Query("SELECT * FROM db_reply")
suspend fun getAllReply(): List<ReplyBean>? suspend fun getAllReply(): MutableList<ReplyBean>
/** /**
* 添加多个 * 添加多个

View File

@ -6,40 +6,6 @@ import androidx.room.PrimaryKey
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)
@ -65,10 +31,13 @@ data class MsgBean(
@ColumnInfo(name = "body") @ColumnInfo(name = "body")
var body: String?, //消息体 var body: String?, //消息体
@ColumnInfo(name = "msgType") @ColumnInfo(name = "msgType")
var msgType: Int?, var msgType: Int?, //标识是发送的消息类型和接收还是发送
@ColumnInfo(name = "status") @ColumnInfo(name = "status")
var status: String? //发送状态 11发送中 12发送失败 13发送成功 var status: String?, //发送状态 11发送中 12发送失败 13发送成功
@ColumnInfo(name = "userNickName")
var userNickName: String?, //用户昵称
@ColumnInfo(name = "avatar")
var userAvatar: String? //用户头像
/** /**
* 信息类型 * 信息类型
* <p> * <p>

View File

@ -36,6 +36,8 @@ data class MsgCategoryBean(
var id: Long = 0, var id: Long = 0,
@ColumnInfo(name = "avatar") @ColumnInfo(name = "avatar")
var avatar: String, var avatar: String,
@ColumnInfo(name = "userNickName")
var userNickName: String,
@ColumnInfo(name = "messageId") @ColumnInfo(name = "messageId")
var messageId: String?, //消息ID var messageId: String?, //消息ID
@ColumnInfo(name = "messageType") @ColumnInfo(name = "messageType")

View File

@ -0,0 +1,37 @@
package com.tenlionsoft.aimz_k.model
data class UserInfoBean(
val accountType: String,
val avatar: String,
val creator: String,
val deleter: String,
val deptIds: String,
val deptNames: String,
val email: String,
val gmtAccountExpired: String,
val gmtCreate: String,
val gmtLastLogin: String,
val gmtLocked: String,
val gmtModified: String,
val gmtPasswordExpired: String,
val isAccountExpired: Int,
val isAccountLocked: Int,
val isEnable: Int,
val isPasswordExpired: Int,
val lastLoginIp: String,
val lastLoginLat: String,
val lastLoginLng: String,
val lastLoginType: String,
val modifier: String,
val nickname: String,
val password: String,
val phone: String,
val posIds: String,
val posNames: String,
val remarks: String,
val roleIds: String,
val roleNames: String,
val tenantId: String,
val userId: String,
val username: String
)

View File

@ -5,10 +5,12 @@ import com.tenlionsoft.aimz_k.model.BaseResponseBean
import com.tenlionsoft.aimz_k.model.ContactListBean import com.tenlionsoft.aimz_k.model.ContactListBean
import com.tenlionsoft.aimz_k.model.FileUploadStateBean import com.tenlionsoft.aimz_k.model.FileUploadStateBean
import com.tenlionsoft.aimz_k.model.PageListBean import com.tenlionsoft.aimz_k.model.PageListBean
import com.tenlionsoft.aimz_k.model.UserInfoBean
import com.tenlionsoft.baselib.model.VersionBean import com.tenlionsoft.baselib.model.VersionBean
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Headers import retrofit2.http.Headers
import retrofit2.http.Multipart import retrofit2.http.Multipart
@ -19,7 +21,7 @@ import retrofit2.http.Path
import retrofit2.http.Query import retrofit2.http.Query
interface UserApi { interface BaseApi {
/** /**
* 登录 * 登录
* *
@ -28,16 +30,30 @@ 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): BaseResponseBean suspend fun doLogin(
@Body
user: RequestBody
): BaseResponseBean
/**
* 根据ID获取用户信息
*/
@Headers("token:need", "Content-Type: application/json", "Accept: application/json")
@GET("/api/user/get/uuid/{uuid}")
suspend fun getUserInfo(
@Path("uuid")
uId: String
): UserInfoBean
/** /**
* 登陆Socket系统 * 登陆Socket系统
* http://192.168.0.26:8888/system/api/anonymous/login
*
*/ */
@Headers("Content-Type: application/json", "Accept: application/json", "projectName:usercenter") @Headers("Content-Type: application/json", "Accept: application/json")
@POST("api/anonymous/login") @POST("api/anonymous/login")
suspend fun doLoginSocket(@Body user: RequestBody): BaseResponseBean suspend fun doLoginSocket(
@Body
user: RequestBody
): BaseResponseBean
/** /**
* 获取App版本 * 获取App版本
@ -46,7 +62,10 @@ interface UserApi {
*/ */
@Headers("token:need", "Content-Type: application/json", "Accept: application/json") @Headers("token:need", "Content-Type: application/json", "Accept: application/json")
@GET("app/appversion/get-number/{appVersionId}") @GET("app/appversion/get-number/{appVersionId}")
suspend fun doCheckAppVersion(@Path("appVersionId") appId: String): VersionBean suspend fun doCheckAppVersion(
@Path("appVersionId")
appId: String
): VersionBean
/** /**
@ -60,7 +79,10 @@ interface UserApi {
@Headers("token:need") @Headers("token:need")
@Multipart @Multipart
@POST("api/file/upload/image") @POST("api/file/upload/image")
suspend fun doUploadImage(@Part file: MultipartBody.Part): FileUploadStateBean suspend fun doUploadImage(
@Part
file: MultipartBody.Part
): FileUploadStateBean
/** /**
@ -74,7 +96,10 @@ interface UserApi {
@Headers("token:need") @Headers("token:need")
@Multipart @Multipart
@POST("api/file/upload/video") @POST("api/file/upload/video")
suspend fun doUploadVideo(@Part file: MultipartBody.Part): FileUploadStateBean suspend fun doUploadVideo(
@Part
file: MultipartBody.Part
): FileUploadStateBean
/** /**
* 上传文件 * 上传文件
@ -82,7 +107,10 @@ interface UserApi {
@Headers("token:need") @Headers("token:need")
@Multipart @Multipart
@POST("api/file/upload/file") @POST("api/file/upload/file")
suspend fun doUploadFile(@Part file: MultipartBody.Part): FileUploadStateBean suspend fun doUploadFile(
@Part
file: MultipartBody.Part
): FileUploadStateBean
/** /**
* 上传音频 * 上传音频
@ -90,7 +118,10 @@ interface UserApi {
@Headers("token:need") @Headers("token:need")
@Multipart @Multipart
@POST("api/file/upload/audio") @POST("api/file/upload/audio")
suspend fun doUploadAudio(@Part file: MultipartBody.Part): FileUploadStateBean suspend fun doUploadAudio(
@Part
file: MultipartBody.Part
): FileUploadStateBean
/** /**
* 获取我的联系人列表 * 获取我的联系人列表
@ -104,7 +135,10 @@ interface UserApi {
*/ */
@Headers("Content-Type: application/json", "Accept: application/json", "token:need") @Headers("Content-Type: application/json", "Accept: application/json", "token:need")
@GET("api/user/listpage") @GET("api/user/listpage")
suspend fun doSearchContact(@Query("keyword") keyword: String): PageListBean<ContactListBean> suspend fun doSearchContact(
@Query("keyword")
keyword: String
): PageListBean<ContactListBean>
/** /**
* 新增联系人 * 新增联系人
@ -113,7 +147,10 @@ interface UserApi {
*/ */
@Headers("Content-Type: application/json", "Accept: application/json", "token:need") @Headers("Content-Type: application/json", "Accept: application/json", "token:need")
@POST("api/contact/user/apply/save") @POST("api/contact/user/apply/save")
suspend fun doAddContact(@Body body: RequestBody): BaseResponseBean suspend fun doAddContact(
@Body
body: RequestBody
): BaseResponseBean
/** /**
* 获取好友申请人列表 * 获取好友申请人列表
@ -130,6 +167,20 @@ interface UserApi {
@Headers("Content-Type: application/json", "Accept: application/json", "token:need") @Headers("Content-Type: application/json", "Accept: application/json", "token:need")
@PUT("api/contact/user/apply/update-status/self/uuid/{uuid}/status/{status}") @PUT("api/contact/user/apply/update-status/self/uuid/{uuid}/status/{status}")
suspend fun doPassApplyContact( suspend fun doPassApplyContact(
@Path("uuid") id: String, @Path("status") status: String @Path("uuid")
id: String,
@Path("status")
status: String
): BaseResponseBean
/**
* 删除联系人
* /
*/
@Headers("Content-Type: application/json", "Accept: application/json", "token:need")
@DELETE("api/contact/user/remove/uuids/{uuids}")
suspend fun doDelContact(
@Path("uuids")
id: String
): BaseResponseBean ): BaseResponseBean
} }

View File

@ -25,14 +25,20 @@ import com.tenlionsoft.aimz_k.viewmodel.ChatPageViewModel
import com.tenlionsoft.baselib.base.BaseActivity import com.tenlionsoft.baselib.base.BaseActivity
import com.tenlionsoft.baselib.contacts.NetConfig import com.tenlionsoft.baselib.contacts.NetConfig
import com.tenlionsoft.baselib.contacts.ProjectConfig import com.tenlionsoft.baselib.contacts.ProjectConfig
import com.tenlionsoft.baselib.net.DownloadResponseHandler
import com.tenlionsoft.baselib.net.FileDownloadUtils
import com.tenlionsoft.baselib.utils.DensityUtils import com.tenlionsoft.baselib.utils.DensityUtils
import com.tenlionsoft.baselib.utils.FileUtils
import com.tenlionsoft.baselib.utils.SpUtils import com.tenlionsoft.baselib.utils.SpUtils
import com.tenlionsoft.baselib.utils.StorageUtils
import com.tenlionsoft.baselib.utils.ToastUtils
import com.tenlionsoft.baselib.widget.AdapterItemClickListener import com.tenlionsoft.baselib.widget.AdapterItemClickListener
import com.tenlionsoft.baselib.widget.LoadingDialog import com.tenlionsoft.baselib.widget.LoadingDialog
import com.tenlionsoft.baselib.widget.SoftKeyBoardListener import com.tenlionsoft.baselib.widget.SoftKeyBoardListener
import com.tenlionsoft.baselib.widget.wheel.WheelView import com.tenlionsoft.baselib.widget.wheel.WheelView
import com.tenlionsoft.medialib.base.SimplePhotoActivity import com.tenlionsoft.medialib.base.SimplePhotoActivity
import com.tenlionsoft.medialib.base.SimpleVideoActivity import com.tenlionsoft.medialib.base.SimpleVideoActivity
import java.io.File
/** /**
@ -46,6 +52,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener<MsgBean> {
private var mContentHeight: Int = 0 private var mContentHeight: Int = 0
private var mLoading: LoadingDialog? = null; private var mLoading: LoadingDialog? = null;
private var bottomHeight = -1 private var bottomHeight = -1
val replyList = arrayListOf<String>()
override fun bindView() { override fun bindView() {
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_chat); mBinding = DataBindingUtil.setContentView(this, R.layout.activity_chat);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
@ -54,7 +61,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener<MsgBean> {
val fromId = intent.getStringExtra("fromId") //传递过来的接收人 val fromId = intent.getStringExtra("fromId") //传递过来的接收人
val name = intent.getStringExtra("name") val name = intent.getStringExtra("name")
mBinding.tvTitle.text = if (name.isNullOrEmpty()) "临时客户" else name mBinding.tvTitle.text = if (name.isNullOrEmpty()) "临时聊天" else name
chatPageViewModel = ViewModelProvider(this, object : ViewModelProvider.Factory { chatPageViewModel = ViewModelProvider(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T { override fun <T : ViewModel> create(modelClass: Class<T>): T {
@ -120,7 +127,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener<MsgBean> {
mLoading = null mLoading = null
} }
} }
mBinding.wvView.setTextSize(14F, isSp = true) mBinding.wvView.setTextSize(16F, isSp = true)
mBinding.wvView.setAutoFitTextSize(true) mBinding.wvView.setAutoFitTextSize(true)
mBinding.wvView.setOnItemSelectedListener(object : WheelView.OnItemSelectedListener { mBinding.wvView.setOnItemSelectedListener(object : WheelView.OnItemSelectedListener {
override fun onItemSelected(wheelView: WheelView, data: Any, position: Int) { override fun onItemSelected(wheelView: WheelView, data: Any, position: Int) {
@ -165,9 +172,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener<MsgBean> {
//滚动到底部 //滚动到底部
mBinding.rlvChats.postDelayed({ mBinding.rlvChats.postDelayed({
layoutManager.scrollToPositionWithOffset( layoutManager.scrollToPositionWithOffset(itemCount - 1, 0)
itemCount - 1, 0
)
}, 50) }, 50)
chatPageViewModel!!.scrollListToBottom.value = false chatPageViewModel!!.scrollListToBottom.value = false
} }
@ -198,9 +203,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener<MsgBean> {
val itemCount = layoutManager.itemCount val itemCount = layoutManager.itemCount
//滚动到底部 //滚动到底部
mBinding.rlvChats.postDelayed({ mBinding.rlvChats.postDelayed({
layoutManager.scrollToPositionWithOffset( layoutManager.scrollToPositionWithOffset(itemCount - 1, 0)
itemCount - 1, 0
)
}, 100) }, 100)
}, { _ -> }, { _ ->
Log.e("ChatActivity", "软键盘: 隐藏") Log.e("ChatActivity", "软键盘: 隐藏")
@ -213,9 +216,24 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener<MsgBean> {
mBinding.rlBottom.visibility = View.GONE mBinding.rlBottom.visibility = View.GONE
} }
}) })
chatPageViewModel!!._replyList.observe(this) { it ->
if (it.isNotEmpty()) {
replyList.clear()
it.forEach {
replyList.add(it.content)
}
mBinding.wvView.setDataItems(replyList)
}
}
registerLocalReceiver() registerLocalReceiver()
} }
override fun onResume() {
super.onResume()
chatPageViewModel!!.getReplyData()
}
@SuppressLint("UnspecifiedRegisterReceiverFlag") @SuppressLint("UnspecifiedRegisterReceiverFlag")
private fun registerLocalReceiver() { private fun registerLocalReceiver() {
@ -240,9 +258,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener<MsgBean> {
val itemCount = layoutManager.itemCount val itemCount = layoutManager.itemCount
//滚动到底部 //滚动到底部
mBinding.rlvChats.postDelayed({ mBinding.rlvChats.postDelayed({
layoutManager.scrollToPositionWithOffset( layoutManager.scrollToPositionWithOffset(itemCount - 1, 0)
itemCount - 1, 0
)
}, 100) }, 100)
} }
@ -286,8 +302,34 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener<MsgBean> {
} }
ProjectConfig.MSG_FILE -> { ProjectConfig.MSG_FILE -> {
//TODO 下载文集,调用系统打开 val gson = Gson()
val body = gson.fromJson(data.body, BodyContent::class.java)
val fileDataBean = gson.fromJson(body.content, FileDataBean::class.java)
val dir: File = StorageUtils.getExternalCacheCustomDir(this)!!
chatPageViewModel?.showLoadDialog?.value = true
FileDownloadUtils.getInstance().download(this@ChatActivity,
NetConfig.MAIN_URL + fileDataBean.fileUrl,
dir.absolutePath,
fileDataBean.fileName!!,
object : DownloadResponseHandler() {
override fun onFinish(download_file: File?) {
chatPageViewModel?.showLoadDialog?.value = false
Log.e("ChatActivity", "onFinish:${download_file}");
FileUtils.openFile(this@ChatActivity, download_file)
}
override fun onProgress(currentBytes: Long, totalBytes: Long) {
}
override fun onFailure(error_msg: String?) {
chatPageViewModel?.showLoadDialog?.value = false
ToastUtils.error(
error_msg
?: "下载失败,请稍后重试"
)
}
})
} }
ProjectConfig.MSG_VIDEO -> { ProjectConfig.MSG_VIDEO -> {

View File

@ -37,12 +37,6 @@ class MainActivity : BaseActivity() {
private lateinit var mFragments: ArrayList<Fragment> private lateinit var mFragments: ArrayList<Fragment>
private var exitTime: Long = 0 private var exitTime: Long = 0
private var mCurPosition: Int = 0 private var mCurPosition: Int = 0
private val mActions = arrayOf(
ProjectConfig.ACTION_UPDATE_SUCCESS,
ProjectConfig.ACTION_UPDATE_ERROR,
ProjectConfig.ACTION_UPDATE_START,
ProjectConfig.ACTION_UPDATE_PROGRESS
)
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
@ -52,42 +46,42 @@ class MainActivity : BaseActivity() {
} }
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 mBinding.mainModel = mainPageViewModel
initView(); initView()
} }
/** /**
* 初始化页面 * 初始化页面
*/ */
private fun initView() { private fun initView() {
mFragments = ArrayList(); mFragments = ArrayList()
mFragments.add(MsgFragment.newInstance()); mFragments.add(MsgFragment.newInstance())
mFragments.add(ContactFragment.newInstance()); mFragments.add(ContactFragment.newInstance())
mFragments.add(MineFragment.newInstance()); mFragments.add(MineFragment.newInstance())
mBinding.vpContent.adapter = VpAdapter(this, mFragments); mBinding.vpContent.adapter = VpAdapter(this, mFragments)
mBinding.bnvTab.itemTextColor = resources.getColorStateList(R.color.col_tabs, theme); mBinding.bnvTab.itemTextColor = resources.getColorStateList(R.color.col_tabs, theme)
mBinding.bnvTab.itemIconSize = DensityUtils.dp2px(this, 24F); mBinding.bnvTab.itemIconSize = DensityUtils.dp2px(this, 24F)
mBinding.vpContent.isUserInputEnabled = false; mBinding.vpContent.isUserInputEnabled = false
mBinding.bnvTab.setOnItemSelectedListener { item -> mBinding.bnvTab.setOnItemSelectedListener { item ->
when (item.itemId) { when (item.itemId) {
R.id.menu_item_msg -> { R.id.menu_item_msg -> {
setStatusBarColor(true); setStatusBarColor(true)
mBinding.vpContent.setCurrentItem(0, false); mBinding.vpContent.setCurrentItem(0, false)
mCurPosition = 0; mCurPosition = 0
return@setOnItemSelectedListener true; return@setOnItemSelectedListener true
}//首页 }//首页
R.id.menu_item_center -> { R.id.menu_item_center -> {
setStatusBarColor(true); setStatusBarColor(true)
mBinding.vpContent.setCurrentItem(1, false); mBinding.vpContent.setCurrentItem(1, false)
mCurPosition = 1; mCurPosition = 1
return@setOnItemSelectedListener true; return@setOnItemSelectedListener true
}//统计 }//统计
R.id.menu_item_mine -> { R.id.menu_item_mine -> {
setStatusBarColor(false); setStatusBarColor(false)
mBinding.vpContent.setCurrentItem(2, false); mBinding.vpContent.setCurrentItem(2, false)
mCurPosition = 2; mCurPosition = 2
return@setOnItemSelectedListener true; return@setOnItemSelectedListener true
}//我的 }//我的
} }
false false
@ -98,9 +92,9 @@ class MainActivity : BaseActivity() {
if (mCurPosition == 0) { if (mCurPosition == 0) {
if ((System.currentTimeMillis() - exitTime) > 2000) { if ((System.currentTimeMillis() - exitTime) > 2000) {
ToastUtils.normal("再按一次退出程序") ToastUtils.normal("再按一次退出程序")
exitTime = System.currentTimeMillis(); exitTime = System.currentTimeMillis()
} else { } else {
exitApp(); exitApp()
} }
} else { } else {
mBinding.bnvTab.selectedItemId = R.id.menu_item_msg mBinding.bnvTab.selectedItemId = R.id.menu_item_msg
@ -162,7 +156,7 @@ class MainActivity : BaseActivity() {
} else { } else {
//申请权限 //申请权限
mApkFile = file mApkFile = file
val packageURI = Uri.parse("package:" + this@MainActivity.getPackageName()) val packageURI = Uri.parse("package:" + this@MainActivity.packageName)
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI) val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI)
startActivityForResult(intent, 298) startActivityForResult(intent, 298)
} }

View File

@ -1,5 +1,6 @@
package com.tenlionsoft.aimz_k.page.fragments package com.tenlionsoft.aimz_k.page.fragments
import android.app.AlertDialog
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
@ -21,23 +22,22 @@ import com.tenlionsoft.aimz_k.page.activity.SearchContactActivity
import com.tenlionsoft.aimz_k.viewmodel.ContactViewModel import com.tenlionsoft.aimz_k.viewmodel.ContactViewModel
import com.tenlionsoft.baselib.widget.AdapterItemClickListener import com.tenlionsoft.baselib.widget.AdapterItemClickListener
import com.tenlionsoft.baselib.widget.AdapterItemLongClickListener import com.tenlionsoft.baselib.widget.AdapterItemLongClickListener
import com.tenlionsoft.baselib.widget.LoadingDialog
import com.tenlionsoft.baselib.widget.StackedImageDecoration import com.tenlionsoft.baselib.widget.StackedImageDecoration
class ContactFragment : Fragment(), AdapterItemLongClickListener<ContactListBean>, class ContactFragment : Fragment(),
AdapterItemLongClickListener<ContactListBean>,
AdapterItemClickListener<ContactListBean> { AdapterItemClickListener<ContactListBean> {
companion object { companion object {
fun newInstance() = ContactFragment() fun newInstance() =
ContactFragment()
} }
private lateinit var viewModel: ContactViewModel private lateinit var viewModel: ContactViewModel
private lateinit var mBind: FragmentContactBinding private lateinit var mBind: FragmentContactBinding
private lateinit var mActivity: MainActivity private lateinit var mActivity: MainActivity
private var mLoading: LoadingDialog? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
private val launcher = private val launcher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
@ -62,12 +62,27 @@ class ContactFragment : Fragment(), AdapterItemLongClickListener<ContactListBean
viewModel.itemLongClickListener = this viewModel.itemLongClickListener = this
mBind.llTitle.llLoad.visibility = View.GONE mBind.llTitle.llLoad.visibility = View.GONE
mBind.llTitle.llSearchLayout.setOnClickListener { mBind.llTitle.llSearchLayout.setOnClickListener {
launcher.launch(Intent(mActivity, SearchContactActivity::class.java)) launcher.launch(
Intent(
mActivity, SearchContactActivity::class.java
)
)
} }
mBind.llNewContact.setOnClickListener { mBind.llNewContact.setOnClickListener {
//判断是否存在好友申请 //判断是否存在好友申请
if (viewModel.isHasApply.value!!) { if (viewModel.isHasApply.value!!) {
launcher.launch(Intent(mActivity, ApplyContactActivity::class.java)) launcher.launch(
Intent(
mActivity, ApplyContactActivity::class.java
)
)
}
}
viewModel.isHasApply.observe(viewLifecycleOwner) {
if (it) {
mBind.llNewContact.visibility = View.VISIBLE
} else {
mBind.llNewContact.visibility = View.GONE
} }
} }
mBind.srlContent.setEnableLoadMore(false) mBind.srlContent.setEnableLoadMore(false)
@ -79,6 +94,22 @@ class ContactFragment : Fragment(), AdapterItemLongClickListener<ContactListBean
mBind.srlContent.finishRefresh() mBind.srlContent.finishRefresh()
} }
} }
viewModel.showLoadDialog.observe(viewLifecycleOwner) {
if (it) {
//显示loading
mLoading =
LoadingDialog.Builder(mActivity)
.setCancelOutside(false)
.setCancelable(false)
.setMessage("删除中...")
.create()
mLoading!!.show()
} else {
//隐藏loading
mLoading?.dismiss()
mLoading = null
}
}
mBind.rlvContent.addItemDecoration(StackedImageDecoration()) mBind.rlvContent.addItemDecoration(StackedImageDecoration())
return mBind.root return mBind.root
@ -97,14 +128,20 @@ class ContactFragment : Fragment(), AdapterItemLongClickListener<ContactListBean
} }
} }
override fun onItemLongClick(type: Int, d: ContactListBean) { override fun onItemLongClick(
type: Int, d: ContactListBean
) {
AlertDialog.Builder(mActivity).setTitle("警告")
.setMessage("确定要删除该联系人吗?这样会删除与该联系人的所有聊天内容!")
.setPositiveButton("确定") { dialog, _ ->
dialog.dismiss()
viewModel.delContact(d)
}.setNegativeButton("取消") { dialog, _ -> dialog.dismiss() }.create().show()
} }
override fun onItemClick(data: ContactListBean) { override fun onItemClick(data: ContactListBean) {
mActivity.startActivity( mActivity.startActivity(
Intent(mActivity, ChatActivity::class.java) Intent(mActivity, ChatActivity::class.java).putExtra("fromId", data.contactUserId)
.putExtra("fromId", data.contactUserId)
.putExtra("name", data.userIdNickname) .putExtra("name", data.userIdNickname)
) )
} }

View File

@ -10,6 +10,7 @@ import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.atwa.filepicker.core.FilePicker
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
@ -19,45 +20,44 @@ import com.tenlionsoft.aimz_k.page.activity.MainActivity
import com.tenlionsoft.aimz_k.page.activity.ManageReplyActivity import com.tenlionsoft.aimz_k.page.activity.ManageReplyActivity
import com.tenlionsoft.aimz_k.viewmodel.MineViewModel import com.tenlionsoft.aimz_k.viewmodel.MineViewModel
import com.tenlionsoft.baselib.utils.SpUtils import com.tenlionsoft.baselib.utils.SpUtils
import com.tenlionsoft.baselib.widget.LoadingDialog
class MineFragment : Fragment() { class MineFragment : Fragment() {
private lateinit var mBind: FragmentMineBinding private lateinit var mBind: FragmentMineBinding
private lateinit var mActivity: MainActivity private lateinit var mActivity: MainActivity
private val filePicker = FilePicker.getInstance(this)
companion object { private var mLoading: LoadingDialog? = null
fun newInstance() = MineFragment()
}
private lateinit var viewModel: MineViewModel private lateinit var viewModel: MineViewModel
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
mBind = DataBindingUtil.inflate( mBind = DataBindingUtil.inflate(layoutInflater, R.layout.fragment_mine, container, false)
layoutInflater,
R.layout.fragment_mine,
container,
false
)
viewModel = ViewModelProvider(this, object : ViewModelProvider.Factory { viewModel = ViewModelProvider(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T { override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MineViewModel() as T return MineViewModel(mActivity) as T
} }
})[MineViewModel::class.java] })[MineViewModel::class.java]
mBind.model = viewModel mBind.model = viewModel
mBind.lifecycleOwner = this mBind.lifecycleOwner = this
mBind.ivUserIcon.setOnClickListener {
filePicker.pickImage { meta ->
if (meta != null) {
viewModel.doUploadImg(meta)
}
}
}
initView() initView()
return mBind.root return mBind.root
} }
private fun initView() { private fun initView() {
mBind.tvName.text = SpUtils.getNickName() mBind.tvName.text = SpUtils.getNickName()
mBind.tvAccount.text = SpUtils.getUserName() mBind.tvAccount.text = SpUtils.getPhone()
val requestOptions = RequestOptions() val requestOptions = RequestOptions().error(R.drawable.ic_user_default)
.error(R.drawable.ic_user_default)
.placeholder(R.drawable.ic_user_default) .placeholder(R.drawable.ic_user_default)
.skipMemoryCache(false) .skipMemoryCache(false)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
@ -70,7 +70,30 @@ class MineFragment : Fragment() {
startActivity(Intent(mActivity, ManageReplyActivity::class.java)) startActivity(Intent(mActivity, ManageReplyActivity::class.java))
} }
mBind.llExit.setOnClickListener { mBind.llExit.setOnClickListener {
mActivity.exitApp() mActivity.reStartApp()
}
viewModel.showLoadDialog.observe(viewLifecycleOwner) {
if (it) {
//显示loading
mLoading = LoadingDialog.Builder(mActivity)
.setCancelOutside(false)
.setCancelable(false)
.setMessage("上传中...")
.create()
mLoading!!.show()
} else {
//隐藏loading
mLoading?.dismiss()
mLoading = null
}
}
viewModel.isUploadIconSuccess.observe(viewLifecycleOwner) {
if (it) {
Glide.with(mActivity)
.load(SpUtils.getAvatar())
.apply(requestOptions)
.into(mBind.ivUserIcon)
}
} }
} }
@ -81,4 +104,9 @@ class MineFragment : Fragment() {
mActivity = context mActivity = context
} }
} }
companion object {
fun newInstance() = MineFragment()
}
} }

View File

@ -14,11 +14,13 @@ import androidx.lifecycle.ViewModelProvider
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.model.MsgTypeStateEnum
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.page.activity.MainActivity
import com.tenlionsoft.aimz_k.services.SocketService 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.contacts.ProjectConfig
import com.tenlionsoft.baselib.utils.AppUtils
import com.tenlionsoft.baselib.widget.AdapterItemClickListener import com.tenlionsoft.baselib.widget.AdapterItemClickListener
import com.tenlionsoft.baselib.widget.AdapterItemLongClickListener import com.tenlionsoft.baselib.widget.AdapterItemLongClickListener
import com.tenlionsoft.baselib.widget.LoadingDialog import com.tenlionsoft.baselib.widget.LoadingDialog
@ -37,17 +39,13 @@ class MsgFragment : Fragment(), AdapterItemClickListener<MsgCategoryBean>,
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
savedInstanceState: Bundle? ): View {
): View {
mMsgBinding = DataBindingUtil.inflate( mMsgBinding = DataBindingUtil.inflate(
layoutInflater, layoutInflater, R.layout.fragment_msg, container, false
R.layout.fragment_msg, )
container,
false
)
mMsgBinding.llSearchLayout.llSearchLayout.setOnClickListener { mMsgBinding.llSearchLayout.llSearchLayout.setOnClickListener {
startActivity(Intent(this@MsgFragment.context, ChatActivity::class.java)) mActivity?.startActivity(Intent(mActivity, ChatActivity::class.java))
} }
viewModel = ViewModelProvider(this, object : ViewModelProvider.Factory { viewModel = ViewModelProvider(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T { override fun <T : ViewModel> create(modelClass: Class<T>): T {
@ -87,9 +85,11 @@ class MsgFragment : Fragment(), AdapterItemClickListener<MsgCategoryBean>,
viewModel.showLoadDialog.observe(viewLifecycleOwner) { viewModel.showLoadDialog.observe(viewLifecycleOwner) {
if (it) { if (it) {
//显示loading //显示loading
mLoading = LoadingDialog.Builder(mActivity as Activity).setCancelOutside(false) mLoading = LoadingDialog.Builder(mActivity as Activity)
.setCancelOutside(false)
.setCancelable(false) .setCancelable(false)
.setMessage("删除中...").create() .setMessage("删除中...")
.create()
mLoading!!.show() mLoading!!.show()
} else { } else {
//隐藏loading //隐藏loading
@ -111,7 +111,13 @@ class MsgFragment : Fragment(), AdapterItemClickListener<MsgCategoryBean>,
//开启service //开启service
private fun toStartService() { private fun toStartService() {
mActivity?.startService(Intent(mActivity!!, SocketService::class.java)) //判断是否允许
val isRunning =
AppUtils.isRunningService(mActivity!!, mActivity!!.packageName + ":tenlion_socket")
if (!isRunning) {
mActivity?.startService(Intent(mActivity!!, SocketService::class.java))
}
} }
override fun onResume() { override fun onResume() {
@ -123,12 +129,17 @@ class MsgFragment : Fragment(), AdapterItemClickListener<MsgCategoryBean>,
* 条目点击 * 条目点击
*/ */
override fun onItemClick(data: MsgCategoryBean) { override fun onItemClick(data: MsgCategoryBean) {
startActivity( val intent = Intent(mActivity, ChatActivity::class.java)
Intent(mActivity, ChatActivity::class.java).putExtra( when (data.msgType) {
"fromId", MsgTypeStateEnum.MSG_TO_OTHER_TXT, MsgTypeStateEnum.MSG_TO_OTHER_IMG, MsgTypeStateEnum.MSG_TO_OTHER_FILE, MsgTypeStateEnum.MSG_TO_OTHER_VOICE, MsgTypeStateEnum.MSG_TO_OTHER_MOVIE -> {
data.senderId intent.putExtra("fromId", data.receiverId)
) }
)
MsgTypeStateEnum.MSG_FROM_OTHER_IMG, MsgTypeStateEnum.MSG_FROM_OTHER_TXT, MsgTypeStateEnum.MSG_FROM_OTHER_VOICE, MsgTypeStateEnum.MSG_FROM_OTHER_MOVIE, MsgTypeStateEnum.MSG_FROM_OTHER_FILE -> {
intent.putExtra("fromId", data.senderId)
}
}
startActivity(intent)
} }
//监听socket连接状态 //监听socket连接状态

View File

@ -67,6 +67,8 @@ class SocketService : Service(), WsManager.MsgCallBack {
val intentFilter = IntentFilter() val intentFilter = IntentFilter()
mLocalReceiver = LocalReceiver() mLocalReceiver = LocalReceiver()
intentFilter.addAction(ProjectConfig.A_S_MSG_SEND)//发送消息 intentFilter.addAction(ProjectConfig.A_S_MSG_SEND)//发送消息
intentFilter.addAction(ProjectConfig.A_S_LOGOUT) //断开socket
intentFilter.addAction(ProjectConfig.A_S_RE_LOGIN)//重新连接socket
registerReceiver(mLocalReceiver, intentFilter) registerReceiver(mLocalReceiver, intentFilter)
} }
@ -111,11 +113,21 @@ class SocketService : Service(), WsManager.MsgCallBack {
val isConnect = mWsManager?.isNetworkConnected(context) val isConnect = mWsManager?.isNetworkConnected(context)
if (true == isConnect) { if (true == isConnect) {
when (intent?.action) { when (intent?.action) {
//发送消息
ProjectConfig.A_S_MSG_SEND -> { ProjectConfig.A_S_MSG_SEND -> {
val msgConvertBean = intent.getSerializableExtra("msgBean") val msgConvertBean = intent.getSerializableExtra("msgBean")
val msg = gson.toJson(msgConvertBean) val msg = gson.toJson(msgConvertBean)
mWsManager?.sendMessage(msg) mWsManager?.sendMessage(msg)
}//发送消息 }
//断开连接
ProjectConfig.A_S_LOGOUT -> {
stopSocket()
}
//重新连接
ProjectConfig.A_S_RE_LOGIN -> {
stopSocket()
startSocket()
}
} }
} else { } else {
ToastUtils.error("请检查网络") ToastUtils.error("请检查网络")

View File

@ -5,7 +5,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.tenlionsoft.aimz_k.adapter.ApplyContactAdapter import com.tenlionsoft.aimz_k.adapter.ApplyContactAdapter
import com.tenlionsoft.aimz_k.model.ApplyContactBean import com.tenlionsoft.aimz_k.model.ApplyContactBean
import com.tenlionsoft.aimz_k.net.UserApi import com.tenlionsoft.aimz_k.net.BaseApi
import com.tenlionsoft.baselib.base.BaseViewModel import com.tenlionsoft.baselib.base.BaseViewModel
import com.tenlionsoft.baselib.net.ExParse import com.tenlionsoft.baselib.net.ExParse
import com.tenlionsoft.baselib.net.RetrofitClient import com.tenlionsoft.baselib.net.RetrofitClient
@ -16,7 +16,7 @@ class ApplyContactViewModel(private val context: Context) : BaseViewModel() {
val _applyList = MutableLiveData<List<ApplyContactBean>>() val _applyList = MutableLiveData<List<ApplyContactBean>>()
val adapter = ApplyContactAdapter(_applyList.value ?: emptyList(), this) val adapter = ApplyContactAdapter(_applyList.value ?: emptyList(), this)
val retrofit = RetrofitClient.getInstance(context) val retrofit = RetrofitClient.getInstance(context)
val userApi = retrofit.create(UserApi::class.java) val userApi = retrofit.create(BaseApi::class.java)
init { init {
doGetApplyList() doGetApplyList()

View File

@ -15,9 +15,9 @@ import com.atwa.filepicker.result.VideoMeta
import com.google.gson.Gson import com.google.gson.Gson
import com.tenlionsoft.aimz_k.ConvertBeanUtils import com.tenlionsoft.aimz_k.ConvertBeanUtils
import com.tenlionsoft.aimz_k.adapter.ChatMsgAdapter import com.tenlionsoft.aimz_k.adapter.ChatMsgAdapter
import com.tenlionsoft.aimz_k.dao.DbManager
import com.tenlionsoft.aimz_k.model.BodyContent import com.tenlionsoft.aimz_k.model.BodyContent
import com.tenlionsoft.aimz_k.model.CoverSealedBean import com.tenlionsoft.aimz_k.model.CoverSealedBean
import com.tenlionsoft.aimz_k.dao.DbManager
import com.tenlionsoft.aimz_k.model.FileDataBean import com.tenlionsoft.aimz_k.model.FileDataBean
import com.tenlionsoft.aimz_k.model.MsgBean import com.tenlionsoft.aimz_k.model.MsgBean
import com.tenlionsoft.aimz_k.model.MsgConvertBean import com.tenlionsoft.aimz_k.model.MsgConvertBean
@ -25,9 +25,11 @@ import com.tenlionsoft.aimz_k.model.PickerType
import com.tenlionsoft.aimz_k.model.Receiver import com.tenlionsoft.aimz_k.model.Receiver
import com.tenlionsoft.aimz_k.model.ReplyBean import com.tenlionsoft.aimz_k.model.ReplyBean
import com.tenlionsoft.aimz_k.model.Sender import com.tenlionsoft.aimz_k.model.Sender
import com.tenlionsoft.aimz_k.net.UserApi import com.tenlionsoft.aimz_k.model.UserInfoBean
import com.tenlionsoft.aimz_k.net.BaseApi
import com.tenlionsoft.baselib.base.BaseViewModel import com.tenlionsoft.baselib.base.BaseViewModel
import com.tenlionsoft.baselib.contacts.ProjectConfig import com.tenlionsoft.baselib.contacts.ProjectConfig
import com.tenlionsoft.baselib.contacts.toFileBody
import com.tenlionsoft.baselib.net.ExParse import com.tenlionsoft.baselib.net.ExParse
import com.tenlionsoft.baselib.net.RetrofitClient import com.tenlionsoft.baselib.net.RetrofitClient
import com.tenlionsoft.baselib.utils.SpUtils import com.tenlionsoft.baselib.utils.SpUtils
@ -36,15 +38,9 @@ import com.tenlionsoft.baselib.widget.AdapterItemClickListener
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
class ChatPageViewModel( class ChatPageViewModel(
private val fromId: String, private val fromId: String, private val toId: String, private var context: Context
private val toId: String,
private var context: Context
) : BaseViewModel() { ) : BaseViewModel() {
val txtMsg = MutableLiveData<String>("") val txtMsg = MutableLiveData<String>("")
val showSendBtn = MutableLiveData(false)//显示/隐藏发送按钮 val showSendBtn = MutableLiveData(false)//显示/隐藏发送按钮
@ -55,12 +51,16 @@ class ChatPageViewModel(
val scrollListToBottom = MutableLiveData<Boolean>(false) val scrollListToBottom = MutableLiveData<Boolean>(false)
private val _msgList = MutableLiveData<List<MsgBean>>() private val _msgList = MutableLiveData<List<MsgBean>>()
private val retrofitClient = RetrofitClient.getInstance(context) private val retrofitClient = RetrofitClient.getInstance(context)
private val netApi = retrofitClient.create(UserApi::class.java) private val netApi = retrofitClient.create(BaseApi::class.java)
var adapter: ChatMsgAdapter = ChatMsgAdapter(_msgList.value ?: emptyList(), this) var adapter: ChatMsgAdapter = ChatMsgAdapter(
_msgList.value
?: emptyList(), this
)
var onItemClickListener: AdapterItemClickListener<MsgBean>? = null var onItemClickListener: AdapterItemClickListener<MsgBean>? = null
private val mGson: Gson = Gson() private val mGson: Gson = Gson()
private var msgHandlerList: ArrayList<MsgConvertBean> = arrayListOf() private var msgHandlerList: ArrayList<MsgConvertBean> = arrayListOf()
private val _replyList = MutableLiveData<List<ReplyBean>?>() val _replyList = MutableLiveData<MutableList<ReplyBean>>()
private val handler = object : Handler(Looper.getMainLooper()) { private val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) { override fun handleMessage(msg: Message) {
super.handleMessage(msg) super.handleMessage(msg)
@ -92,10 +92,10 @@ class ChatPageViewModel(
val list = getList() val list = getList()
val rList = getReplyList() val rList = getReplyList()
Log.e("ChatPageViewModel", "Init:${list} ") Log.e("ChatPageViewModel", "Init:${list} ")
if (rList != null) { if (rList.isNotEmpty()) {
_replyList.value = rList _replyList.value = rList
} else { } else {
_replyList.value = emptyList() _replyList.value = arrayListOf()
} }
_msgList.value = list _msgList.value = list
adapter.setData(list) adapter.setData(list)
@ -103,7 +103,7 @@ class ChatPageViewModel(
} }
} }
private suspend fun getReplyList(): List<ReplyBean>? { private suspend fun getReplyList(): MutableList<ReplyBean> {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val rDao = DbManager.db.replayDao() val rDao = DbManager.db.replayDao()
return@withContext rDao.getAllReply() return@withContext rDao.getAllReply()
@ -112,6 +112,7 @@ class ChatPageViewModel(
//获取列表 //获取列表
private suspend fun getList(): List<MsgBean> { private suspend fun getList(): List<MsgBean> {
Log.e("ChatPageViewModel", "getList:${fromId}\n${toId}");
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val msgDao = DbManager.db.msgDao() val msgDao = DbManager.db.msgDao()
return@withContext msgDao.getMsgByFromOrToPage(fromId, toId) return@withContext msgDao.getMsgByFromOrToPage(fromId, toId)
@ -205,15 +206,7 @@ class ChatPageViewModel(
private suspend fun doUploadVideo(meta: VideoMeta) { private suspend fun doUploadVideo(meta: VideoMeta) {
try { try {
val requestFile: RequestBody = val body = meta.file!!.toFileBody("video")
meta.file!!.asRequestBody("multipart/form-data".toMediaTypeOrNull())
val body: MultipartBody.Part =
MultipartBody.Part.createFormData(
"video",
meta.file!!.getName(),
requestFile
)
val bean = retrofitClient.makeApiCall { val bean = retrofitClient.makeApiCall {
netApi.doUploadVideo(body) netApi.doUploadVideo(body)
} }
@ -230,14 +223,7 @@ class ChatPageViewModel(
private suspend fun doUploadFile(meta: FileMeta) { private suspend fun doUploadFile(meta: FileMeta) {
try { try {
val requestFile: RequestBody = val body = meta.file!!.toFileBody("file")
meta.file!!.asRequestBody("multipart/form-data".toMediaTypeOrNull())
val body: MultipartBody.Part =
MultipartBody.Part.createFormData(
"file",
meta.file!!.getName(),
requestFile
)
val bean = retrofitClient.makeApiCall { val bean = retrofitClient.makeApiCall {
netApi.doUploadFile(body) netApi.doUploadFile(body)
} }
@ -257,17 +243,9 @@ class ChatPageViewModel(
try { try {
for (item in list) { for (item in list) {
if (item != null) { if (item != null) {
val requestFile: RequestBody = val toFileBody = item.file!!.toFileBody("image")
item.file!!.asRequestBody("multipart/form-data".toMediaTypeOrNull())
val body: MultipartBody.Part =
MultipartBody.Part.createFormData(
"image",
item.file!!.getName(),
requestFile
)
val bean = retrofitClient.makeApiCall { val bean = retrofitClient.makeApiCall {
netApi.doUploadImage(body) netApi.doUploadImage(toFileBody)
} }
Log.e("ChatPageViewModel", "doUploadImgs: $bean") Log.e("ChatPageViewModel", "doUploadImgs: $bean")
if (bean.code == 200) { if (bean.code == 200) {
@ -348,7 +326,7 @@ class ChatPageViewModel(
val dao = DbManager.db.msgDao() val dao = DbManager.db.msgDao()
val cDao = DbManager.db.categoryDao() val cDao = DbManager.db.categoryDao()
val msgBean = ConvertBeanUtils.convertBeanToMsgBean(b) val msgBean = ConvertBeanUtils.convertBeanToMsgBean(b)
cDao.updateCategory(b.receiver.receiverId!!, b.status, b.body, b.timestamp) cDao.updateOrInsert(b.receiver.receiverId!!, msgBean)
dao.insertMsg(msgBean) dao.insertMsg(msgBean)
return@withContext msgBean return@withContext msgBean
} }
@ -370,10 +348,11 @@ class ChatPageViewModel(
is CoverSealedBean.StateBean -> { is CoverSealedBean.StateBean -> {
viewModelScope.launch { viewModelScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
//更新状态
val dao = DbManager.db.msgDao() val dao = DbManager.db.msgDao()
val cDao = DbManager.db.categoryDao() val cDao = DbManager.db.categoryDao()
val statusType = val statusType =
mGson.fromJson(bean.data.body, BodyContent::class.java).statusType!! mGson.fromJson(bean.data.body, BodyContent::class.java).statusType!!
dao.updateStatus(bean.data.messageId, statusType) dao.updateStatus(bean.data.messageId, statusType)
cDao.updateStatus(bean.data.sender.senderId!!, statusType) cDao.updateStatus(bean.data.sender.senderId!!, statusType)
} }
@ -416,72 +395,87 @@ class ChatPageViewModel(
val bodyBean = BodyContent(content = txtMsg.value, null, null) val bodyBean = BodyContent(content = txtMsg.value, null, null)
val body = mGson.toJson(bodyBean) val body = mGson.toJson(bodyBean)
return MsgConvertBean( return MsgConvertBean(
body = body, body = body,
customMessageType = "", customMessageType = "",
messageId = TimeUtils.getNowDateMillis().toString(), messageId = TimeUtils.getNowDateMillis()
messageType = type, .toString(),
metadata = "", messageType = type,
timestamp = TimeUtils.getNowDateMillis(), metadata = "",
sender = Sender( timestamp = TimeUtils.getNowDateMillis(),
senderId = SpUtils.getId(), sender = Sender(senderId = SpUtils.getId(), senderType = ""),
senderType = "" receiver = Receiver(receiverId = fromId, receiverType = "SINGLE_USER"),
), status = ProjectConfig.MSG_SEND_ING,
receiver = Receiver(
receiverId = fromId,
receiverType = "SINGLE_USER"
),
status = ProjectConfig.MSG_SEND_ING,
) )
} }
var userInfo: UserInfoBean? = null
//获取用户头像和名称
private fun doGetUserInfo() {
viewModelScope.launch {
try {
val userInfoBean = retrofitClient.makeApiCall {
netApi.getUserInfo(fromId)
}
userInfo = userInfoBean
withContext(Dispatchers.IO) {
//更新数据
val cDao = DbManager.db.categoryDao()
val mDao = DbManager.db.msgDao()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
//构建 图片/视频/文件发送实体 //构建 图片/视频/文件发送实体
private fun buildSendMediaBean(type: String, fileBean: FileDataBean): MsgConvertBean { private fun buildSendMediaBean(type: String, fileBean: FileDataBean): MsgConvertBean {
val bodyBean = BodyContent(content = mGson.toJson(fileBean), null, null) val bodyBean = BodyContent(content = mGson.toJson(fileBean), null, null)
val body = mGson.toJson(bodyBean) val body = mGson.toJson(bodyBean)
return MsgConvertBean( return MsgConvertBean(
body = body, body = body,
customMessageType = "", customMessageType = "",
messageId = TimeUtils.getNowDateMillis().toString(), messageId = TimeUtils.getNowDateMillis()
messageType = type, .toString(),
metadata = "", messageType = type,
timestamp = TimeUtils.getNowDateMillis(), metadata = "",
sender = Sender( timestamp = TimeUtils.getNowDateMillis(),
senderId = SpUtils.getId(), sender = Sender(senderId = SpUtils.getId(), senderType = ""),
senderType = "" receiver = Receiver(receiverId = fromId, receiverType = "SINGLE_USER"),
), status = ProjectConfig.MSG_SEND_ING,
receiver = Receiver(
receiverId = fromId,
receiverType = "SINGLE_USER"
),
status = ProjectConfig.MSG_SEND_ING,
) )
} }
//构建状态信息 //构建状态信息
private fun buildStatusBean(messageId: String): MsgConvertBean { private fun buildStatusBean(messageId: String): MsgConvertBean {
val bodyBean = BodyContent(content = null, msg = "", statusType = "SUCCESS_RECEIVED") val bodyBean = BodyContent(content = null, msg = "", statusType = "SUCCESS_RECEIVED")
val body = mGson.toJson(bodyBean) val body = mGson.toJson(bodyBean)
return MsgConvertBean( return MsgConvertBean(
body = body, body = body,
customMessageType = "", customMessageType = "",
messageId = messageId, messageId = messageId,
messageType = ProjectConfig.MSG_STATUS, messageType = ProjectConfig.MSG_STATUS,
metadata = "", metadata = "",
timestamp = TimeUtils.getNowDateMillis(), timestamp = TimeUtils.getNowDateMillis(),
sender = Sender( sender = Sender(senderId = SpUtils.getId(), senderType = ""),
senderId = SpUtils.getId(), receiver = Receiver(receiverId = fromId, receiverType = "SINGLE_USER"),
senderType = "" status = "",
),
receiver = Receiver(
receiverId = fromId,
receiverType = "SINGLE_USER"
),
status = "",
) )
} }
//获取回复短语
fun getReplyData() {
viewModelScope.launch {
val list = getReplyList()
if (list.isNotEmpty()) {
_replyList.value = list
}
}
}
} }

View File

@ -5,15 +5,20 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.tenlionsoft.aimz_k.adapter.ApplyContactUserIconAdapter import com.tenlionsoft.aimz_k.adapter.ApplyContactUserIconAdapter
import com.tenlionsoft.aimz_k.adapter.ContactAdapter import com.tenlionsoft.aimz_k.adapter.ContactAdapter
import com.tenlionsoft.aimz_k.dao.DbManager
import com.tenlionsoft.aimz_k.model.ApplyContactBean import com.tenlionsoft.aimz_k.model.ApplyContactBean
import com.tenlionsoft.aimz_k.model.ContactListBean import com.tenlionsoft.aimz_k.model.ContactListBean
import com.tenlionsoft.aimz_k.net.UserApi import com.tenlionsoft.aimz_k.net.BaseApi
import com.tenlionsoft.baselib.base.BaseViewModel import com.tenlionsoft.baselib.base.BaseViewModel
import com.tenlionsoft.baselib.net.ExParse import com.tenlionsoft.baselib.net.ExParse
import com.tenlionsoft.baselib.net.RetrofitClient import com.tenlionsoft.baselib.net.RetrofitClient
import com.tenlionsoft.baselib.utils.SpUtils
import com.tenlionsoft.baselib.utils.ToastUtils
import com.tenlionsoft.baselib.widget.AdapterItemClickListener import com.tenlionsoft.baselib.widget.AdapterItemClickListener
import com.tenlionsoft.baselib.widget.AdapterItemLongClickListener import com.tenlionsoft.baselib.widget.AdapterItemLongClickListener
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class ContactViewModel(private val context: Context) : BaseViewModel() { class ContactViewModel(private val context: Context) : BaseViewModel() {
private val _contactList = MutableLiveData<List<ContactListBean>?>() private val _contactList = MutableLiveData<List<ContactListBean>?>()
@ -25,7 +30,7 @@ class ContactViewModel(private val context: Context) : BaseViewModel() {
val isHasApply = MutableLiveData<Boolean>(false) val isHasApply = MutableLiveData<Boolean>(false)
val isRefreshing = MutableLiveData<Boolean>(false) val isRefreshing = MutableLiveData<Boolean>(false)
private val retrofit = RetrofitClient.getInstance(context) private val retrofit = RetrofitClient.getInstance(context)
private val userApi = retrofit.create(UserApi::class.java) private val userApi = retrofit.create(BaseApi::class.java)
init { init {
getContactList() getContactList()
@ -89,5 +94,35 @@ class ContactViewModel(private val context: Context) : BaseViewModel() {
} }
} }
//删除联系人
fun delContact(d: ContactListBean) {
viewModelScope.launch {
showLoadDialog.value = true
try {
val responseBean = retrofit.makeApiCall {
userApi.doDelContact(d.contactUserId)
}
withContext(Dispatchers.IO) {
val cDao = DbManager.db.categoryDao()
val mDao = DbManager.db.msgDao()
cDao.delChatBySenderId(d.userId)
mDao.delChatHistory(SpUtils.getId(), d.userId)
}
if (responseBean.code == 200) {
ToastUtils.success("删除成功")
doRefresh()
} else {
ToastUtils.error(responseBean.msg ?: "删除失败,请稍后重试")
}
showLoadDialog.value = false
} catch (e: Exception) {
e.printStackTrace()
ExParse.parse(e)
showLoadDialog.value = false
}
}
}
} }

View File

@ -6,7 +6,7 @@ import androidx.lifecycle.viewModelScope
import com.google.gson.Gson import com.google.gson.Gson
import com.tenlionsoft.aimz_k.model.AppTokenUser import com.tenlionsoft.aimz_k.model.AppTokenUser
import com.tenlionsoft.aimz_k.model.LoginUser import com.tenlionsoft.aimz_k.model.LoginUser
import com.tenlionsoft.aimz_k.net.UserApi import com.tenlionsoft.aimz_k.net.BaseApi
import com.tenlionsoft.baselib.base.App import com.tenlionsoft.baselib.base.App
import com.tenlionsoft.baselib.base.BaseViewModel import com.tenlionsoft.baselib.base.BaseViewModel
import com.tenlionsoft.baselib.net.ExParse import com.tenlionsoft.baselib.net.ExParse
@ -25,7 +25,7 @@ class LoginPageViewModel : BaseViewModel() {
val isLoginSuccess = MutableLiveData<Boolean>() val isLoginSuccess = MutableLiveData<Boolean>()
private val gson = Gson() private val gson = Gson()
private val retrofitClient = RetrofitClient.getInstance(App.context) private val retrofitClient = RetrofitClient.getInstance(App.context)
private val userApi = retrofitClient.create(UserApi::class.java) private val userApi = retrofitClient.create(BaseApi::class.java)
fun onUserNameChange(s: CharSequence, start: Int, before: Int, count: Int) { fun onUserNameChange(s: CharSequence, start: Int, before: Int, count: Int) {

View File

@ -3,7 +3,7 @@ package com.tenlionsoft.aimz_k.viewmodel
import android.content.Context import android.content.Context
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.tenlionsoft.aimz_k.net.UserApi import com.tenlionsoft.aimz_k.net.BaseApi
import com.tenlionsoft.baselib.base.BaseViewModel import com.tenlionsoft.baselib.base.BaseViewModel
import com.tenlionsoft.baselib.contacts.ProjectConfig import com.tenlionsoft.baselib.contacts.ProjectConfig
import com.tenlionsoft.baselib.net.ExParse import com.tenlionsoft.baselib.net.ExParse
@ -24,7 +24,7 @@ class MainPageViewModel : BaseViewModel() {
private suspend fun getAppVersion(context: Context) { private suspend fun getAppVersion(context: Context) {
try { try {
val appVersion = RetrofitClient.getInstance(context) val appVersion = RetrofitClient.getInstance(context)
.create(UserApi::class.java) .create(BaseApi::class.java)
.doCheckAppVersion(ProjectConfig.APP_VERSION_ID) .doCheckAppVersion(ProjectConfig.APP_VERSION_ID)
if (appVersion.versioncode.isNotEmpty()) { if (appVersion.versioncode.isNotEmpty()) {
val isNeedUpdate = AppUtils.checkcode(appVersion.versioncode) val isNeedUpdate = AppUtils.checkcode(appVersion.versioncode)

View File

@ -16,8 +16,8 @@ import kotlinx.coroutines.withContext
class ManageReplyViewModel : BaseViewModel(), AdapterItemLongClickListener<ReplyBean> { class ManageReplyViewModel : BaseViewModel(), AdapterItemLongClickListener<ReplyBean> {
val txtMsg = MutableLiveData("") val txtMsg = MutableLiveData("")
val _replyList = MutableLiveData<List<ReplyBean>?>() val _replyList = MutableLiveData<MutableList<ReplyBean>>()
var adapter: ReplyAdapter = ReplyAdapter(_replyList.value ?: emptyList(), this) var adapter: ReplyAdapter = ReplyAdapter(_replyList.value ?: arrayListOf(), this)
var onItemLongClickListener: AdapterItemLongClickListener<ReplyBean> = this var onItemLongClickListener: AdapterItemLongClickListener<ReplyBean> = this
val curBean = MutableLiveData<ReplyBean?>() val curBean = MutableLiveData<ReplyBean?>()
@ -27,15 +27,15 @@ class ManageReplyViewModel : BaseViewModel(), AdapterItemLongClickListener<Reply
private fun getList() { private fun getList() {
viewModelScope.launch { viewModelScope.launch {
val list: List<ReplyBean>? = withContext(Dispatchers.IO) { val list: MutableList<ReplyBean> = withContext(Dispatchers.IO) {
val rDao = DbManager.db.replayDao() val rDao = DbManager.db.replayDao()
val list = rDao.getAllReply() val list = rDao.getAllReply()
list list
} }
if (!list.isNullOrEmpty()) { if (list.isNotEmpty()) {
_replyList.value = list _replyList.value = list
} else { } else {
_replyList.value = emptyList() _replyList.value = arrayListOf()
} }
Log.e("ManageReplyViewModel", "init:${adapter} ${Thread.currentThread().name}"); Log.e("ManageReplyViewModel", "init:${adapter} ${Thread.currentThread().name}");
adapter.setData(_replyList.value!!) adapter.setData(_replyList.value!!)

View File

@ -1,7 +1,45 @@
package com.tenlionsoft.aimz_k.viewmodel package com.tenlionsoft.aimz_k.viewmodel
import androidx.lifecycle.ViewModel import android.content.Context
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.atwa.filepicker.result.ImageMeta
import com.tenlionsoft.aimz_k.net.BaseApi
import com.tenlionsoft.baselib.base.BaseViewModel
import com.tenlionsoft.baselib.contacts.toFileBody
import com.tenlionsoft.baselib.net.ExParse
import com.tenlionsoft.baselib.net.RetrofitClient
import kotlinx.coroutines.launch
class MineViewModel : ViewModel() { class MineViewModel(val context: Context) : BaseViewModel() {
// TODO: Implement the ViewModel private val retrofitClient = RetrofitClient.getInstance(context)
private val baseApi = retrofitClient.create(BaseApi::class.java)
val isUploadIconSuccess = MutableLiveData<Boolean>()
fun doUploadImg(meta: ImageMeta) {
viewModelScope.launch {
showLoadDialog.value = true
try {
val responseBean = retrofitClient.makeApiCall {
val body = meta.file!!.toFileBody("image")
baseApi.doUploadImage(body)
}
if (responseBean.code == 200) {
//TODO 更新头像上传成功
//设置成功后刷新页面
isUploadIconSuccess.value = true
showLoadDialog.value = false
} else {
isUploadIconSuccess.value = false
showLoadDialog.value = false
}
} catch (e: Exception) {
e.printStackTrace()
isUploadIconSuccess.value = false
ExParse.parse(e)
}
}
}
} }

View File

@ -7,7 +7,7 @@ import androidx.lifecycle.viewModelScope
import com.tenlionsoft.aimz_k.adapter.ContactAdapter import com.tenlionsoft.aimz_k.adapter.ContactAdapter
import com.tenlionsoft.aimz_k.model.AddContactBean import com.tenlionsoft.aimz_k.model.AddContactBean
import com.tenlionsoft.aimz_k.model.ContactListBean import com.tenlionsoft.aimz_k.model.ContactListBean
import com.tenlionsoft.aimz_k.net.UserApi import com.tenlionsoft.aimz_k.net.BaseApi
import com.tenlionsoft.baselib.base.BaseViewModel import com.tenlionsoft.baselib.base.BaseViewModel
import com.tenlionsoft.baselib.contacts.toBody import com.tenlionsoft.baselib.contacts.toBody
import com.tenlionsoft.baselib.model.ViewState import com.tenlionsoft.baselib.model.ViewState
@ -23,7 +23,7 @@ class SearchContactViewModel(val context: Context) : BaseViewModel() {
val adapter = ContactAdapter(_contactList.value ?: emptyList(), this) val adapter = ContactAdapter(_contactList.value ?: emptyList(), this)
val showApplyContentDialog = MutableLiveData<Boolean>(false) val showApplyContentDialog = MutableLiveData<Boolean>(false)
private val retrofitClient = RetrofitClient.getInstance(context) private val retrofitClient = RetrofitClient.getInstance(context)
private val userApi = retrofitClient.create(UserApi::class.java) private val userApi = retrofitClient.create(BaseApi::class.java)
fun onTxtChange(s: CharSequence, start: Int, before: Int, end: Int) { fun onTxtChange(s: CharSequence, start: Int, before: Int, end: Int) {
txtMsg.value = s.toString() txtMsg.value = s.toString()
} }

View File

@ -54,16 +54,10 @@ object BindingUtils {
if (url.isNullOrEmpty()) { if (url.isNullOrEmpty()) {
imageView.setImageResource(R.drawable.app_logo_small) imageView.setImageResource(R.drawable.app_logo_small)
} else { } else {
val requestOptions = RequestOptions() val requestOptions = RequestOptions().error(R.drawable.app_logo_small)
.error(R.drawable.app_logo_small) .placeholder(R.drawable.app_logo_small).skipMemoryCache(false)
.placeholder(R.drawable.app_logo_small) .diskCacheStrategy(DiskCacheStrategy.RESOURCE).centerInside()
.skipMemoryCache(false) Glide.with(imageView.context).load(url).apply(requestOptions).into(imageView)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerInside()
Glide.with(imageView.context)
.load(url)
.apply(requestOptions)
.into(imageView)
} }
} }
@ -74,16 +68,10 @@ object BindingUtils {
if (url.isNullOrEmpty()) { if (url.isNullOrEmpty()) {
imageView.setImageResource(R.drawable.ic_user_default) imageView.setImageResource(R.drawable.ic_user_default)
} else { } else {
val requestOptions = RequestOptions() val requestOptions = RequestOptions().error(R.drawable.ic_user_default)
.error(R.drawable.ic_psd) .placeholder(R.drawable.ic_user_default).skipMemoryCache(false)
.placeholder(R.drawable.ic_psd) .diskCacheStrategy(DiskCacheStrategy.RESOURCE).circleCrop()
.skipMemoryCache(false) Glide.with(imageView.context).load(url).apply(requestOptions).into(imageView)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.circleCrop()
Glide.with(imageView.context)
.load(url)
.apply(requestOptions)
.into(imageView)
} }
} }
@ -126,8 +114,7 @@ object BindingUtils {
if (!fileDataBean.fileName.isNullOrEmpty()) { if (!fileDataBean.fileName.isNullOrEmpty()) {
iv.visibility = View.VISIBLE iv.visibility = View.VISIBLE
val suffix = fileDataBean.fileName!!.substring( val suffix = fileDataBean.fileName!!.substring(
fileDataBean.fileName!!.lastIndexOf("."), fileDataBean.fileName!!.lastIndexOf("."), fileDataBean.fileName!!.length
fileDataBean.fileName!!.length
) )
Log.e("BindingUtils", "jsonConvertFileIcon: $suffix") Log.e("BindingUtils", "jsonConvertFileIcon: $suffix")
var id = R.drawable.ic_audio var id = R.drawable.ic_audio
@ -172,20 +159,17 @@ object BindingUtils {
if (str.isNullOrEmpty()) { if (str.isNullOrEmpty()) {
iv.setImageResource(com.tenlionsoft.baselib.R.drawable.ic_img_load_err) iv.setImageResource(com.tenlionsoft.baselib.R.drawable.ic_img_load_err)
} else { } else {
val options: RequestOptions = RequestOptions() val options: RequestOptions =
.placeholder(com.tenlionsoft.baselib.R.drawable.ic_loading) // 正在加载中的图片 RequestOptions().placeholder(com.tenlionsoft.baselib.R.drawable.ic_loading) // 正在加载中的图片
.error(com.tenlionsoft.baselib.R.drawable.ic_img_load_err) // 加载失败的图片 .error(com.tenlionsoft.baselib.R.drawable.ic_img_load_err) // 加载失败的图片
val gson = Gson() val gson = Gson()
val bodyContent = gson.fromJson<BodyContent>(str, BodyContent::class.java) val bodyContent = gson.fromJson<BodyContent>(str, BodyContent::class.java)
val fileBean = gson.fromJson(bodyContent.content, FileDataBean::class.java) val fileBean = gson.fromJson(bodyContent.content, FileDataBean::class.java)
Glide.with(iv.context) Glide.with(iv.context).load(NetConfig.MAIN_URL + fileBean.fileUrl) // 图片地址
.load(NetConfig.MAIN_URL + fileBean.fileUrl) // 图片地址 .apply(options).into(object : CustomTarget<Drawable?>() {
.apply(options)
.into(object : CustomTarget<Drawable?>() {
override fun onResourceReady( override fun onResourceReady(
resource: Drawable, resource: Drawable, transition: Transition<in Drawable?>?
transition: Transition<in Drawable?>?
) { ) {
val imageSize: ImageSize? = val imageSize: ImageSize? =
ImageUtils.getImageSize((resource as BitmapDrawable).bitmap) ImageUtils.getImageSize((resource as BitmapDrawable).bitmap)
@ -194,9 +178,7 @@ object BindingUtils {
imageLP?.width = imageSize.getWidth() imageLP?.width = imageSize.getWidth()
imageLP?.height = imageSize.getHeight() imageLP?.height = imageSize.getHeight()
iv.setLayoutParams(imageLP) iv.setLayoutParams(imageLP)
Glide.with(iv.context) Glide.with(iv.context).load(resource).apply(options) // 参数
.load(resource)
.apply(options) // 参数
.into(iv) .into(iv)
} }
} }
@ -217,16 +199,11 @@ object BindingUtils {
val body = gson.fromJson(str, BodyContent::class.java) val body = gson.fromJson(str, BodyContent::class.java)
val fileData = gson.fromJson(body.content, FileDataBean::class.java) val fileData = gson.fromJson(body.content, FileDataBean::class.java)
val requestOptions = RequestOptions() val requestOptions = RequestOptions().error(R.drawable.app_logo_small)
.error(R.drawable.app_logo_small) .placeholder(R.drawable.app_logo_small).skipMemoryCache(false)
.placeholder(R.drawable.app_logo_small) .diskCacheStrategy(DiskCacheStrategy.RESOURCE).centerInside()
.skipMemoryCache(false) Glide.with(view.context).load(NetConfig.MAIN_URL + fileData.fileUrl)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) .apply(requestOptions).into(view)
.centerInside()
Glide.with(view.context)
.load(NetConfig.MAIN_URL + fileData.fileUrl)
.apply(requestOptions)
.into(view)
} }
} }
@ -236,7 +213,7 @@ object BindingUtils {
if (long == null) { if (long == null) {
tv.text = "" tv.text = ""
} else { } else {
tv.text = TimeUtils.millis2HMStr(long) tv.text = TimeUtils.getFriendlyTimeSpanByNow(long)
} }
} }

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="-100%"
android:toXDelta="0"/>
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="100%"
android:duration="300">
</translate>
</set>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"> <set xmlns:android="http://schemas.android.com/apk/res/android">
<translate <translate
android:fromXDelta="100%p" android:fromXDelta="100%"
android:toXDelta="0" android:toXDelta="0"
android:duration="500" /> android:duration="300"/>
</set> </set>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"> <set xmlns:android="http://schemas.android.com/apk/res/android">
<translate <translate
android:duration="300"
android:fromXDelta="0" android:fromXDelta="0"
android:toXDelta="-100%p" android:toXDelta="-100%" />
android:duration="500" />
</set> </set>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromAlpha="0.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="1.0" />

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromAlpha="1.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="0.0" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,5 +1,6 @@
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<data> <data>
@ -124,9 +125,9 @@
android:minLines="1" android:minLines="1"
android:onTextChanged="@{viewModel::onTxtChange}" android:onTextChanged="@{viewModel::onTxtChange}"
android:padding="10dp" android:padding="10dp"
tools:text="测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试"
android:text="@{viewModel.txtMsg}" android:text="@{viewModel.txtMsg}"
android:textSize="14sp" /> android:textSize="14sp"
tools:text="测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试" />
<ImageView <ImageView
android:id="@+id/iv_send" android:id="@+id/iv_send"
@ -172,7 +173,9 @@
android:id="@+id/wv_view" android:id="@+id/wv_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="300dp" android:layout_height="300dp"
android:visibility="@{viewModel.showReplyLayout? View.VISIBLE:View.GONE}" /> android:visibility="@{viewModel.showReplyLayout? View.VISIBLE:View.GONE}"
app:wv_normalItemTextColor="@color/gray_d1"
app:wv_selectedItemTextColor="@color/black" />
<LinearLayout <LinearLayout
android:id="@+id/ll_choose" android:id="@+id/ll_choose"

View File

@ -19,24 +19,15 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="25dp" android:layout_marginLeft="10dp"
android:layout_marginTop="50dp" android:layout_marginTop="50dp"
android:layout_marginRight="25dp" android:layout_marginRight="25dp"
android:orientation="vertical"> android:orientation="vertical">
<TextView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="你好," android:src="@drawable/ic_title_icon" />
android:textColor="#ff282828"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_hint"
android:textColor="#ff282828"
android:textSize="20sp" />
</LinearLayout> </LinearLayout>

View File

@ -39,7 +39,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingLeft="25dp" android:paddingLeft="10dp"
android:paddingTop="15dp" android:paddingTop="15dp"
android:paddingRight="25dp" android:paddingRight="25dp"
android:paddingBottom="15dp"> android:paddingBottom="15dp">
@ -73,16 +73,13 @@
<com.scwang.smart.refresh.layout.SmartRefreshLayout <com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/srl_content" android:id="@+id/srl_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:layout_marginTop="15dp">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rlv_content" android:id="@+id/rlv_content"
setLinearLayoutAdapter="@{model.adapter}" setLinearLayoutAdapter="@{model.adapter}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
tools:listitem="@layout/item_contact" /> tools:listitem="@layout/item_contact" />
</com.scwang.smart.refresh.layout.SmartRefreshLayout> </com.scwang.smart.refresh.layout.SmartRefreshLayout>

View File

@ -28,7 +28,7 @@
android:id="@+id/iv_user_icon" android:id="@+id/iv_user_icon"
android:layout_width="64dp" android:layout_width="64dp"
android:layout_height="64dp" android:layout_height="64dp"
android:src="@drawable/ic_load_error" /> android:src="@drawable/ic_user_default" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -40,9 +40,10 @@
android:id="@+id/tv_name" android:id="@+id/tv_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="名称" tools:text="名称"
android:textColor="@color/txt_black" android:textColor="@color/txt_black"
android:textSize="16sp" /> android:textSize="16sp"
android:textStyle="bold" />
<TextView <TextView
android:id="@+id/tv_account" android:id="@+id/tv_account"
@ -50,7 +51,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:drawableRight="@drawable/ic_arrow_d7_right" android:drawableRight="@drawable/ic_arrow_d7_right"
android:text="账号" tools:text="账号"
android:textColor="#ff7d7d7d" android:textColor="#ff7d7d7d"
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
@ -80,8 +81,8 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="10dp" android:layout_marginLeft="15dp"
android:text="设置" android:text="回复短语"
android:textColor="@color/txt_black" android:textColor="@color/txt_black"
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
@ -110,7 +111,7 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="10dp" android:layout_marginLeft="15dp"
android:text="退出登录" android:text="退出登录"
android:textColor="@color/txt_black" android:textColor="@color/txt_black"
android:textSize="14sp" /> android:textSize="14sp" />

View File

@ -31,25 +31,25 @@
android:layout_margin="10dp"> android:layout_margin="10dp">
<ImageView <ImageView
imageUrl="@{item.avatar}" imageUserIcon="@{item.avatar}"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:scaleType="fitXY" android:scaleType="fitXY"
tools:src="@drawable/app_logo_small" /> tools:src="@drawable/ic_user_default" />
<!-- android:visibility="@{item.read?View.VISIBLE:View.GONE}"--> <!-- android:visibility="@{item.read?View.VISIBLE:View.GONE}"-->
<ImageView <ImageView
android:layout_width="10dp" android:layout_width="10dp"
android:layout_height="10dp" android:layout_height="10dp"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:src="@drawable/shp_circle_red" /> android:src="@drawable/shp_circle_red"
android:visibility="gone" />
</RelativeLayout> </RelativeLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
@ -95,7 +95,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="#CACACA" android:textColor="#CACACA"
android:textSize="12sp" android:textSize="10sp"
tools:text="16:00" /> tools:text="16:00" />
</LinearLayout> </LinearLayout>

View File

@ -23,14 +23,15 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/white" android:background="@color/white"
android:orientation="horizontal"> android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp">
<ImageView <ImageView
imageUserIcon="@{bean.userIdAvatar}" imageUserIcon="@{bean.userIdAvatar}"
android:layout_width="48dp" android:layout_width="50dp"
android:layout_height="48dp" android:layout_height="50dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:layout_margin="10dp"
android:scaleType="fitXY" android:scaleType="fitXY"
tools:src="@drawable/ic_user_default" /> tools:src="@drawable/ic_user_default" />
@ -42,8 +43,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginLeft="5dp"
android:layout_marginBottom="15dp"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingRight="15dp"> android:paddingRight="15dp">
@ -52,7 +52,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp"> android:paddingRight="10dp">
<TextView <TextView
@ -63,12 +62,13 @@
android:text="@{bean.userIdNickname}" android:text="@{bean.userIdNickname}"
android:textColor="#1D1D1D" android:textColor="#1D1D1D"
android:textSize="14sp" android:textSize="14sp"
android:textStyle="bold"
tools:text="名字" /> tools:text="名字" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="2dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:text='@{bean.userIdPhone ==null ? "联系方式:未录入":@string/contact_item_phone(bean.userIdPhone)}' android:text='@{bean.userIdPhone ==null ? "联系方式:未录入":@string/contact_item_phone(bean.userIdPhone)}'
@ -100,6 +100,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:layout_marginTop="3dp"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
android:background="#E6E6E6" android:background="#E6E6E6"
android:visibility="@{pos&lt; size?View.VISIBLE:View.GONE}" /> android:visibility="@{pos&lt; size?View.VISIBLE:View.GONE}" />

View File

@ -1,16 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="Theme.Aimz_k" parent="Theme.AppCompat.Light.NoActionBar" /> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<style name="Anim_fade" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@style/fade</item> <item name="android:windowAnimationStyle">@style/WindowAnimationStyle</item>
</style> </style>
<style name="fade" parent="Theme.AppCompat.Light.NoActionBar"> <style name="WindowAnimationStyle" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/fade_in</item> <item name="android:activityOpenEnterAnimation">@anim/activity_open_enter</item>
<item name="android:activityOpenExitAnimation">@anim/fade_out</item> <item name="android:activityOpenExitAnimation">@anim/activity_open_exit</item>
<item name="android:activityCloseEnterAnimation">@anim/fade_in</item> <item name="android:activityCloseEnterAnimation">@anim/activity_close_enter</item>
<item name="android:activityCloseExitAnimation">@anim/fade_out</item> <item name="android:activityCloseExitAnimation">@anim/activity_close_exit</item>
<item name="android:taskOpenEnterAnimation">@anim/activity_open_enter</item>
<item name="android:taskOpenExitAnimation">@anim/activity_open_exit</item>
<item name="android:taskCloseEnterAnimation">@anim/activity_close_enter</item>
<item name="android:taskCloseExitAnimation">@anim/activity_close_exit</item>
</style> </style>
</resources> </resources>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_storage_root"
path="." />
<files-path
name="files"
path="." />
<cache-path
name="cache"
path="." />
<!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/DCIM/camerademo目录-->
<external-path
name="mq_DCIM"
path="DCIM/camerademo" />
<!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/Pictures/camerademo目录-->
<external-path
name="mq_Pictures"
path="Pictures/camerademo" />
<!--代表app 私有的存储区域 Context.getFilesDir()目录下的images目录 /data/user/0/com.hm.camerademo/files/images-->
<files-path
name="mq_private_files"
path="images" />
<!--代表app 私有的存储区域 Context.getCacheDir()目录下的images目录 /data/user/0/com.hm.camerademo/cache/images-->
<cache-path
name="mq_private_cache"
path="images" />
<!--代表app 外部存储区域根目录下的文件 Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)目录下的Pictures目录-->
<external-files-path
name="mq_external_files"
path="Pictures" />
<!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的images目录-->
<external-cache-path
name="mq_external_cache"
path="path" />
<root-path
name="mq_external_cache"
path="" />
</paths>

View File

@ -1,29 +1,30 @@
package com.tenlionsoft.baselib.base package com.tenlionsoft.baselib.base
import android.content.Context import android.content.Context
import android.util.Log import android.content.Intent
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.tenlionsoft.baselib.R import com.tenlionsoft.baselib.R
import com.tenlionsoft.baselib.utils.SpUtils
import kotlin.system.exitProcess import kotlin.system.exitProcess
abstract class BaseActivity : DataBindingActivity() { abstract class BaseActivity : DataBindingActivity() {
//点击空白隐藏软键盘
override fun onTouchEvent(event: MotionEvent): Boolean { fun reStartApp(){
if (event.action == MotionEvent.ACTION_DOWN) { SpUtils.putToken("")
Log.e("BaseActivity", "onTouchEvent: ") SpUtils.putId("")
val v = currentFocus SpUtils.putPassword("")
if (v != null && v is EditText) { SpUtils.putUserName("")
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager // val broadcast = Intent()
imm.hideSoftInputFromWindow(v.windowToken, 0) // broadcast.setAction(PathConfig.ACTION_PUSH_STOP_SOCKET)
} // sendBroadcast(broadcast)
} val intent = packageManager.getLaunchIntentForPackage(packageName)
return super.onTouchEvent(event) intent!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
finish()
} }
fun exitApp() { fun exitApp() {

View File

@ -2,8 +2,11 @@ package com.tenlionsoft.baselib.contacts
import com.google.gson.Gson import com.google.gson.Gson
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
object NetConfig { object NetConfig {
const val BASE_URL = "http://192.168.0.26:8888/" const val BASE_URL = "http://192.168.0.26:8888/"
@ -20,4 +23,9 @@ fun <T> T.toBody(): RequestBody {
val gson = Gson() val gson = Gson()
val str = gson.toJson(this) val str = gson.toJson(this)
return str.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) return str.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
}
fun File.toFileBody(paramsStr: String): MultipartBody.Part {
val requestFile: RequestBody = this.asRequestBody("multipart/form-data".toMediaTypeOrNull())
return MultipartBody.Part.createFormData(paramsStr, this.getName(), requestFile)
} }

View File

@ -40,5 +40,7 @@ object ProjectConfig {
const val A_S_DISCONNECT: String = "com.tenlion.soft.aimz_k.socket.disconnect"//连接断开 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_RECEIVER: String = "com.tenlion.soft.aimz_k.socket.receiver" //接收到信息
const val A_S_MSG_SEND: String = "com.tenlion.soft.aimz_k.socket.send"//发送信息 const val A_S_MSG_SEND: String = "com.tenlion.soft.aimz_k.socket.send"//发送信息
const val A_S_LOGOUT: String = "com.tenlion.soft.aimz_k.socket.logout"//断开socket
const val A_S_RE_LOGIN: String = "com.tenlion.soft.aimz_k.socket.re_login"//重新连接socket
} }

View File

@ -0,0 +1,22 @@
package com.tenlionsoft.baselib.model
import androidx.annotation.IntDef
import com.tenlionsoft.baselib.model.TimeConstants.DAY
import com.tenlionsoft.baselib.model.TimeConstants.HOUR
import com.tenlionsoft.baselib.model.TimeConstants.MIN
import com.tenlionsoft.baselib.model.TimeConstants.MSEC
import com.tenlionsoft.baselib.model.TimeConstants.SEC
object TimeConstants {
const val MSEC: Int = 1
const val SEC: Int = 1000
const val MIN: Int = 60000
const val HOUR: Int = 3600000
const val DAY: Int = 86400000
}
@Target(AnnotationTarget.TYPE, AnnotationTarget.VALUE_PARAMETER)
@IntDef(MSEC, SEC, MIN, HOUR, DAY)
@Retention(AnnotationRetention.SOURCE)
annotation class Unit

View File

@ -11,42 +11,42 @@ import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
open class RetrofitClient { open class RetrofitClient {
private var mCtx: Context? = null; private var mCtx: Context? = null
private val TIMEOUT: Long = 5;//超时时间 private val TIMEOUT: Long = 5//超时时间
private lateinit var mRetrofit: Retrofit; private lateinit var mRetrofit: Retrofit
private constructor(ctx: Context) { private constructor(ctx: Context) {
mCtx = ctx; mCtx = ctx
initManager(); initManager()
} }
/** /**
* 初始化Retrofit * 初始化Retrofit
*/ */
private fun initManager() { private fun initManager() {
val client = getHttpClient(); val client = getHttpClient()
mRetrofit = Retrofit.Builder() mRetrofit = Retrofit.Builder()
.baseUrl(NetConfig.MAIN_URL) .baseUrl(NetConfig.MAIN_URL)
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()) .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.client(client) .client(client)
.build(); .build()
} }
private fun getHttpClient(): OkHttpClient { private fun getHttpClient(): OkHttpClient {
val builder: OkHttpClient.Builder = OkHttpClient.Builder(); val builder: OkHttpClient.Builder = OkHttpClient.Builder()
// setLocalProxy(); // setLocalProxy();
// 设置超时 // 设置超时
builder.connectTimeout(TIMEOUT, TimeUnit.MINUTES); builder.connectTimeout(TIMEOUT, TimeUnit.MINUTES)
builder.readTimeout(TIMEOUT, TimeUnit.MINUTES); builder.readTimeout(TIMEOUT, TimeUnit.MINUTES)
builder.writeTimeout(TIMEOUT, TimeUnit.MINUTES); builder.writeTimeout(TIMEOUT, TimeUnit.MINUTES)
//封装公共参数 //封装公共参数
// builder.addInterceptor(new AreaInterceptor()); // builder.addInterceptor(new AreaInterceptor());
//builder.addInterceptor(new CommInterceptor()); //builder.addInterceptor(new CommInterceptor());
//多BaseUrl连接器 //多BaseUrl连接器
builder.addInterceptor(BaseUrlInterceptor()); builder.addInterceptor(BaseUrlInterceptor())
return builder.build(); return builder.build()
} }
suspend fun <T> makeApiCall(apiCall: suspend () -> T): T { suspend fun <T> makeApiCall(apiCall: suspend () -> T): T {

View File

@ -1,5 +1,6 @@
package com.tenlionsoft.baselib.utils package com.tenlionsoft.baselib.utils
import android.app.ActivityManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -23,7 +24,8 @@ class AppUtils {
try { try {
val pm = App.context.packageManager val pm = App.context.packageManager
val pi = pm.getPackageInfo(pkgName, 0) val pi = pm.getPackageInfo(pkgName, 0)
return pi?.versionName ?: "" return pi?.versionName
?: ""
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace(); e.printStackTrace();
return "" return ""
@ -34,15 +36,38 @@ class AppUtils {
return getAppVersionCode(App.context.packageName) return getAppVersionCode(App.context.packageName)
} }
fun isRunningService(ctx: Context, className: String): Boolean {
var isRunning = false
val activityManager = ctx
.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val servicesList = activityManager
.getRunningServices(Int.MAX_VALUE)
val l: Iterator<ActivityManager.RunningServiceInfo> = servicesList.iterator()
while (l.hasNext()) {
val si = l.next()
if (className == si.service.className) {
isRunning = true
}
if (className == si.process) {
isRunning = true
}
}
return isRunning
}
private fun getAppVersionCode(pkgName: String?): Long { private fun getAppVersionCode(pkgName: String?): Long {
if (pkgName.isNullOrEmpty()) return -1 if (pkgName.isNullOrEmpty()) return -1
try { try {
val pm = App.context.packageManager val pm = App.context.packageManager
val pi = pm.getPackageInfo(pkgName, 0); val pi = pm.getPackageInfo(pkgName, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return pi?.longVersionCode ?: -1 return pi?.longVersionCode
?: -1
} else { } else {
return (pi?.versionCode ?: -1).toLong() return (pi?.versionCode
?: -1).toLong()
} }
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace() e.printStackTrace()
@ -59,7 +84,8 @@ class AppUtils {
if (!file.exists()) { if (!file.exists()) {
try { try {
val newFile = file.createNewFile() val newFile = file.createNewFile()
uuid = UUID.randomUUID().toString() uuid = UUID.randomUUID()
.toString()
FileIOUtils.writeFileFromString(file, uuid) FileIOUtils.writeFileFromString(file, uuid)
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()

View File

@ -0,0 +1,119 @@
package com.tenlionsoft.baselib.utils
import android.content.Context
import android.content.Intent
import androidx.core.content.FileProvider
import java.io.File
import java.util.Locale
object FileUtils {
private val MIME_MapTable: Array<Array<String>> = arrayOf( //{后缀名, MIME类型}
arrayOf(".3gp", "video/3gpp"),
arrayOf(".apk", "application/vnd.android.package-archive"),
arrayOf(".asf", "video/x-ms-asf"),
arrayOf(".avi", "video/x-msvideo"),
arrayOf(".bin", "application/octet-stream"),
arrayOf(".bmp", "image/bmp"),
arrayOf(".c", "text/plain"),
arrayOf(".class", "application/octet-stream"),
arrayOf(".conf", "text/plain"),
arrayOf(".cpp", "text/plain"),
arrayOf(".doc", "application/msword"),
arrayOf(".exe", "application/octet-stream"),
arrayOf(".gif", "image/gif"),
arrayOf(".gtar", "application/x-gtar"),
arrayOf(".gz", "application/x-gzip"),
arrayOf(".h", "text/plain"),
arrayOf(".htm", "text/html"),
arrayOf(".html", "text/html"),
arrayOf(".jar", "application/java-archive"),
arrayOf(".java", "text/plain"),
arrayOf(".jpeg", "image/jpeg"),
arrayOf(".jpg", "image/jpeg"),
arrayOf(".js", "application/x-javascript"),
arrayOf(".log", "text/plain"),
arrayOf(".m3u", "audio/x-mpegurl"),
arrayOf(".m4a", "audio/mp4a-latm"),
arrayOf(".m4b", "audio/mp4a-latm"),
arrayOf(".m4p", "audio/mp4a-latm"),
arrayOf(".m4u", "video/vnd.mpegurl"),
arrayOf(".m4v", "video/x-m4v"),
arrayOf(".mov", "video/quicktime"),
arrayOf(".mp2", "audio/x-mpeg"),
arrayOf(".mp3", "audio/x-mpeg"),
arrayOf(".mp4", "video/mp4"),
arrayOf(".mpc", "application/vnd.mpohun.certificate"),
arrayOf(".mpe", "video/mpeg"),
arrayOf(".mpeg", "video/mpeg"),
arrayOf(".mpg", "video/mpeg"),
arrayOf(".mpg4", "video/mp4"),
arrayOf(".mpga", "audio/mpeg"),
arrayOf(".msg", "application/vnd.ms-outlook"),
arrayOf(".ogg", "audio/ogg"),
arrayOf(".pdf", "application/pdf"),
arrayOf(".png", "image/png"),
arrayOf(".pps", "application/vnd.ms-powerpoint"),
arrayOf(".ppt", "application/vnd.ms-powerpoint"),
arrayOf(".prop", "text/plain"),
arrayOf(".rar", "application/x-rar-compressed"),
arrayOf(".rc", "text/plain"),
arrayOf(".rmvb", "audio/x-pn-realaudio"),
arrayOf(".rtf", "application/rtf"),
arrayOf(".sh", "text/plain"),
arrayOf(".tar", "application/x-tar"),
arrayOf(".tgz", "application/x-compressed"),
arrayOf(".txt", "text/plain"),
arrayOf(".wav", "audio/x-wav"),
arrayOf(".wma", "audio/x-ms-wma"),
arrayOf(".wmv", "audio/x-ms-wmv"),
arrayOf(".wps", "application/vnd.ms-works"), //{".xml", "text/xml"},
arrayOf(".xml", "text/plain"),
arrayOf(".z", "application/x-compress"),
arrayOf(".zip", "application/zip"),
arrayOf("", "*/*")
)
fun getFileProviderName(context: Context): String {
return context.packageName + ".provider"
}
fun openFile(context: Context, fileName: String?) {
val intent = Intent()
val file = File(fileName)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) //设置标记
intent.setAction(Intent.ACTION_VIEW) //动作,查看
val uri =
FileProvider.getUriForFile(context, getFileProviderName(context), file)
intent.setDataAndType(uri, getMIMEType(file)) //设置类型
context.startActivity(intent)
}
fun openFile(context: Context, file: File?) {
if (file == null) return
val intent = Intent()
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) //设置标记
intent.setAction(Intent.ACTION_VIEW) //动作,查看
val uri =
FileProvider.getUriForFile(context, getFileProviderName(context), file)
intent.setDataAndType(uri, getMIMEType(file)) //设置类型
context.startActivity(intent)
}
private fun getMIMEType(file: File): String {
var type = "*/*"
val fName = file.name
//获取后缀名前的分隔符"."在fName中的位置。
val dotIndex = fName.lastIndexOf(".")
if (dotIndex < 0) return type
/* 获取文件的后缀名 */
val fileType = fName.substring(dotIndex).lowercase(Locale.getDefault())
if (fileType == null || "" == fileType) return type
//在MIME和文件类型的匹配表中找到对应的MIME类型。
for (i in MIME_MapTable.indices) {
if (fileType == MIME_MapTable[i][0]) type = MIME_MapTable[i][1]
}
return type
}
}

View File

@ -28,7 +28,7 @@ class StorageUtils {
Log.w( Log.w(
"StorageUtils", "StorageUtils",
"Can't define system cache directory! The app should be re-installed." "Can't define system cache directory! The app should be re-installed."
) )
} }
return appCacheDir return appCacheDir
} }
@ -43,7 +43,7 @@ class StorageUtils {
Log.w( Log.w(
"StorageUtils", "StorageUtils",
"Can't define system cache directory! The app should be re-installed." "Can't define system cache directory! The app should be re-installed."
) )
} }
return appCacheDir return appCacheDir
} }
@ -52,6 +52,19 @@ class StorageUtils {
* 在cache下新增自定义缓存路径 * 在cache下新增自定义缓存路径
* apk下载路径为:SDCard/Android/data/com.winfo.update/cache/update_file/ * apk下载路径为:SDCard/Android/data/com.winfo.update/cache/update_file/
*/ */
fun getExternalCacheCustomDir(context: Context): File? {
val appCacheDir = File(context.externalCacheDir, "download_cache")
return if (!appCacheDir.exists()) {
if (appCacheDir.mkdirs()) {
appCacheDir
} else {
context.externalCacheDir
}
} else {
appCacheDir
}
}
fun getExternalCacheCustomDirectory(context: Context): File? { fun getExternalCacheCustomDirectory(context: Context): File? {
//在SDCard/Android/data/com.winfo.update/cache/update_file创建文件夹 //在SDCard/Android/data/com.winfo.update/cache/update_file创建文件夹
val appCacheDir = File(context.externalCacheDir, "update") val appCacheDir = File(context.externalCacheDir, "update")

View File

@ -1,7 +1,10 @@
package com.tenlionsoft.baselib.utils package com.tenlionsoft.baselib.utils
import com.tenlionsoft.baselib.model.TimeConstants
import java.text.DateFormat import java.text.DateFormat
import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@ -41,5 +44,89 @@ object TimeUtils {
return format.format(Date(millis)) return format.format(Date(millis))
} }
fun getFriendlyTimeSpanByNow(millis: Long): String {
val now = System.currentTimeMillis()
val span = now - millis
if (span < 0) return String.format("%tc", millis)
if (span < 1000) {
return "刚刚"
} else if (span < TimeConstants.MIN) {
return java.lang.String.format(Locale.getDefault(), "%d秒前", span / TimeConstants.SEC)
} else if (span < TimeConstants.HOUR) {
return java.lang.String.format(
Locale.getDefault(), "%d分钟前", span / TimeConstants.MIN
)
}
// 获取当天 00:00
val wee: Long = getWeeOfToday()
return if (millis >= wee) {
String.format("%tR", millis)
} else if (millis >= wee - TimeConstants.DAY) {
String.format("昨天%tR", millis)
} else {
String.format("%tF", millis)
}
}
private fun getWeeOfToday(): Long {
val cal = Calendar.getInstance()
cal[Calendar.HOUR_OF_DAY] = 0
cal[Calendar.SECOND] = 0
cal[Calendar.MINUTE] = 0
cal[Calendar.MILLISECOND] = 0
return cal.timeInMillis
}
/**
* Return the friendly time span by now.
*
* The pattern is `yyyy-MM-dd HH:mm:ss`.
*
* @param time The formatted time string.
* @return the friendly time span by now
*
* * 如果小于 1 秒钟内显示刚刚
* * 如果在 1 分钟内显示 XXX秒前
* * 如果在 1 小时内显示 XXX分钟前
* * 如果在 1 小时外的今天内显示今天15:32
* * 如果是昨天的显示昨天15:32
* * 其余显示2016-10-15
* * 时间不合法的情况全部日期和时间信息如星期六 十月 27 14:21:20 CST 2007
*
*/
fun getFriendlyTimeSpanByNow(time: String): String {
return getFriendlyTimeSpanByNow(time, getDefaultFormat())
}
/**
* Return the friendly time span by now.
*
* @param time The formatted time string.
* @param format The format.
* @return the friendly time span by now
*
* * 如果小于 1 秒钟内显示刚刚
* * 如果在 1 分钟内显示 XXX秒前
* * 如果在 1 小时内显示 XXX分钟前
* * 如果在 1 小时外的今天内显示今天15:32
* * 如果是昨天的显示昨天15:32
* * 其余显示2016-10-15
* * 时间不合法的情况全部日期和时间信息如星期六 十月 27 14:21:20 CST 2007
*
*/
fun getFriendlyTimeSpanByNow(
time: String, format: DateFormat
): String {
return getFriendlyTimeSpanByNow(string2Millis(time, format))
}
fun string2Millis(time: String, format: DateFormat): Long {
try {
return format.parse(time)?.time ?: -1L
} catch (e: ParseException) {
e.printStackTrace()
}
return -1
}
} }

View File

@ -5,11 +5,13 @@
<activity <activity
android:name=".base.SimpleVideoActivity" android:name=".base.SimpleVideoActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="false" /> android:exported="false"
android:launchMode="singleTop" />
<activity <activity
android:name=".base.SimplePhotoActivity" android:name=".base.SimplePhotoActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="false" /> android:exported="false"
android:launchMode="singleTop" />
</application> </application>
</manifest> </manifest>