From cf5d2c41b61fb69b520072c2c93c763783d41669 Mon Sep 17 00:00:00 2001 From: itgaojian163 Date: Fri, 8 Nov 2024 17:54:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/codeStyles/Project.xml | 1 + .idea/copyright/profiles_settings.xml | 7 + app/src/main/AndroidManifest.xml | 33 +++- .../tenlionsoft/aimz_k/ConvertBeanUtils.kt | 11 +- .../aimz_k/adapter/ContactAdapter.kt | 12 +- .../tenlionsoft/aimz_k/dao/MsgCategoryDao.kt | 14 +- .../java/com/tenlionsoft/aimz_k/dao/MsgDao.kt | 6 + .../com/tenlionsoft/aimz_k/dao/ReplyDao.kt | 2 +- .../com/tenlionsoft/aimz_k/model/MsgBean.kt | 43 +---- .../aimz_k/model/MsgCategoryBean.kt | 2 + .../tenlionsoft/aimz_k/model/UserInfoBean.kt | 37 ++++ .../aimz_k/net/{UserApi.kt => BaseApi.kt} | 79 ++++++-- .../aimz_k/page/activity/ChatActivity.kt | 66 +++++-- .../aimz_k/page/activity/MainActivity.kt | 56 +++--- .../aimz_k/page/fragments/ContactFragment.kt | 63 +++++-- .../aimz_k/page/fragments/MineFragment.kt | 64 +++++-- .../aimz_k/page/fragments/MsgFragment.kt | 47 +++-- .../aimz_k/services/SocketService.kt | 14 +- .../aimz_k/viewmodel/ApplyContactViewModel.kt | 4 +- .../aimz_k/viewmodel/ChatPageViewModel.kt | 174 +++++++++--------- .../aimz_k/viewmodel/ContactViewModel.kt | 39 +++- .../aimz_k/viewmodel/LoginPageViewModel.kt | 4 +- .../aimz_k/viewmodel/MainPageViewModel.kt | 4 +- .../aimz_k/viewmodel/ManageReplyViewModel.kt | 10 +- .../aimz_k/viewmodel/MineViewModel.kt | 44 ++++- .../viewmodel/SearchContactViewModel.kt | 4 +- .../tenlionsoft/aimz_k/widget/BindingUtils.kt | 67 +++---- .../main/res/anim/activity_close_enter.xml | 7 + app/src/main/res/anim/activity_close_exit.xml | 8 + ...e_in_right.xml => activity_open_enter.xml} | 4 +- ...de_out_left.xml => activity_open_exit.xml} | 4 +- app/src/main/res/anim/fade_in.xml | 6 - app/src/main/res/anim/fade_out.xml | 6 - .../main/res/drawable-xhdpi/ic_title_icon.png | Bin 0 -> 9730 bytes .../res/drawable-xhdpi/ic_user_default.png | Bin 7783 -> 3835 bytes app/src/main/res/layout/activity_chat.xml | 9 +- app/src/main/res/layout/activity_login.xml | 15 +- app/src/main/res/layout/fragment_contact.xml | 7 +- app/src/main/res/layout/fragment_mine.xml | 15 +- app/src/main/res/layout/item_category.xml | 10 +- app/src/main/res/layout/item_contact.xml | 17 +- app/src/main/res/values/styles.xml | 22 ++- app/src/main/res/xml/provider_paths.xml | 44 +++++ .../tenlionsoft/baselib/base/BaseActivity.kt | 29 +-- .../tenlionsoft/baselib/contacts/NetConfig.kt | 8 + .../baselib/contacts/ProjectConfig.kt | 2 + .../baselib/model/TimeConstants.kt | 22 +++ .../tenlionsoft/baselib/net/RetrofitClient.kt | 26 +-- .../com/tenlionsoft/baselib/utils/AppUtils.kt | 34 +++- .../tenlionsoft/baselib/utils/FileUtils.kt | 119 ++++++++++++ .../tenlionsoft/baselib/utils/StorageUtils.kt | 17 +- .../tenlionsoft/baselib/utils/TimeUtils.kt | 87 +++++++++ medialib/src/main/AndroidManifest.xml | 6 +- 53 files changed, 1018 insertions(+), 413 deletions(-) create mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 app/src/main/java/com/tenlionsoft/aimz_k/model/UserInfoBean.kt rename app/src/main/java/com/tenlionsoft/aimz_k/net/{UserApi.kt => BaseApi.kt} (63%) create mode 100644 app/src/main/res/anim/activity_close_enter.xml create mode 100644 app/src/main/res/anim/activity_close_exit.xml rename app/src/main/res/anim/{slide_in_right.xml => activity_open_enter.xml} (66%) rename app/src/main/res/anim/{slide_out_left.xml => activity_open_exit.xml} (66%) delete mode 100644 app/src/main/res/anim/fade_in.xml delete mode 100644 app/src/main/res/anim/fade_out.xml create mode 100644 app/src/main/res/drawable-xhdpi/ic_title_icon.png create mode 100755 app/src/main/res/xml/provider_paths.xml create mode 100644 baselib/src/main/java/com/tenlionsoft/baselib/model/TimeConstants.kt create mode 100644 baselib/src/main/java/com/tenlionsoft/baselib/utils/FileUtils.kt diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 7643783..ca945a3 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,6 +1,7 @@ + diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..f925024 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 094ae06..dcae2ec 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,18 +21,21 @@ android:networkSecurityConfig="@xml/network_config" android:roundIcon="@drawable/app_logo" android:supportsRtl="true" - android:theme="@style/Anim_fade" + android:theme="@style/AppTheme" android:usesCleartextTraffic="true" tools:targetApi="31"> + android:exported="false" + android:launchMode="singleTop" /> + android:exported="false" + android:launchMode="singleTop" /> + android:exported="true" + android:launchMode="singleTop"> @@ -41,22 +44,36 @@ + android:exported="false" + android:launchMode="singleTop" /> + android:exported="false" + android:launchMode="singleTop" /> + android:launchMode="singleTop" /> + android:exported="false" + android:process=":tenlion_socket" /> + + + + \ No newline at end of file diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/ConvertBeanUtils.kt b/app/src/main/java/com/tenlionsoft/aimz_k/ConvertBeanUtils.kt index 578c641..087289e 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/ConvertBeanUtils.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/ConvertBeanUtils.kt @@ -34,7 +34,9 @@ object ConvertBeanUtils { msgType = type, timestamp = sBean.timestamp, status = sBean.status, - customMessageType = "" + customMessageType = "", + userNickName = "", + userAvatar = "" ) ) } else { @@ -62,7 +64,9 @@ object ConvertBeanUtils { msgType = type, timestamp = cBean.timestamp, status = cBean.status, - customMessageType = "" + customMessageType = "", + userNickName = "", + userAvatar = "" ) } @@ -78,7 +82,8 @@ object ConvertBeanUtils { status = msgBean.status, timestamp = msgBean.timestamp, msgType = msgBean.msgType, - body = msgBean.body + body = msgBean.body, + userNickName = "" ) } diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/adapter/ContactAdapter.kt b/app/src/main/java/com/tenlionsoft/aimz_k/adapter/ContactAdapter.kt index a25024f..6f2b5d5 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/adapter/ContactAdapter.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/adapter/ContactAdapter.kt @@ -3,6 +3,7 @@ package com.tenlionsoft.aimz_k.adapter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import com.lxj.xpopup.XPopup import com.tenlionsoft.aimz_k.databinding.ItemContactBinding import com.tenlionsoft.aimz_k.model.ContactListBean import com.tenlionsoft.aimz_k.viewmodel.ContactViewModel @@ -29,8 +30,17 @@ class ContactAdapter(datas: List, val viewModel: BaseViewModel) holder.binding.root.setOnClickListener { viewModel.itemClickListener?.onItemClick(list[position]) } + val xPopup = XPopup.Builder(holder.binding.root.context).watchView(holder.binding.root) 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 } } else { diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/dao/MsgCategoryDao.kt b/app/src/main/java/com/tenlionsoft/aimz_k/dao/MsgCategoryDao.kt index 389c49e..60fbe8e 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/dao/MsgCategoryDao.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/dao/MsgCategoryDao.kt @@ -22,7 +22,7 @@ interface MsgCategoryDao { /** * 根据发送人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? /** @@ -98,7 +98,7 @@ interface MsgCategoryDao { */ suspend fun updateOrInsert(senderId: String, msgBean: MsgBean) { val bean = getCategoryBySenderId(senderId) - if (bean == null) { + if (bean==null) { //需要转换成MsgCategoryBean insertMsg(ConvertBeanUtils.convertCategoryBean(msgBean)) } 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) /** * 更新 消息体 时间戳 状态 * @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) + + /** + * 更新 头像和名称 + */ + @Query("UPDATE db_category SET userNickName=:nickName,avatar=:avatar WHERE receiverId=:userId OR senderId=:userId") + suspend fun updateUserInfoById(nickName: String, avatar: String, userId: String) } \ No newline at end of file diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/dao/MsgDao.kt b/app/src/main/java/com/tenlionsoft/aimz_k/dao/MsgDao.kt index 631212b..cceadfd 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/dao/MsgDao.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/dao/MsgDao.kt @@ -119,4 +119,10 @@ interface MsgDao { */ @Query("DELETE FROM db_msg") 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) } \ No newline at end of file diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/dao/ReplyDao.kt b/app/src/main/java/com/tenlionsoft/aimz_k/dao/ReplyDao.kt index ab698be..7cdc2d4 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/dao/ReplyDao.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/dao/ReplyDao.kt @@ -20,7 +20,7 @@ interface ReplyDao { * @return */ @Query("SELECT * FROM db_reply") - suspend fun getAllReply(): List? + suspend fun getAllReply(): MutableList /** * 添加多个 diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgBean.kt b/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgBean.kt index efecf9b..e3a88f5 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgBean.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgBean.kt @@ -6,40 +6,6 @@ import androidx.room.PrimaryKey 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") data class MsgBean( @PrimaryKey(autoGenerate = true) @@ -65,10 +31,13 @@ data class MsgBean( @ColumnInfo(name = "body") var body: String?, //消息体 @ColumnInfo(name = "msgType") - var msgType: Int?, + var msgType: Int?, //标识是发送的消息类型和接收还是发送 @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? //用户头像 /** * 信息类型 *

diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryBean.kt b/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryBean.kt index b06c59d..54901b0 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryBean.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/model/MsgCategoryBean.kt @@ -36,6 +36,8 @@ data class MsgCategoryBean( var id: Long = 0, @ColumnInfo(name = "avatar") var avatar: String, + @ColumnInfo(name = "userNickName") + var userNickName: String, @ColumnInfo(name = "messageId") var messageId: String?, //消息ID @ColumnInfo(name = "messageType") diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/model/UserInfoBean.kt b/app/src/main/java/com/tenlionsoft/aimz_k/model/UserInfoBean.kt new file mode 100644 index 0000000..43da59d --- /dev/null +++ b/app/src/main/java/com/tenlionsoft/aimz_k/model/UserInfoBean.kt @@ -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 +) \ No newline at end of file diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/net/UserApi.kt b/app/src/main/java/com/tenlionsoft/aimz_k/net/BaseApi.kt similarity index 63% rename from app/src/main/java/com/tenlionsoft/aimz_k/net/UserApi.kt rename to app/src/main/java/com/tenlionsoft/aimz_k/net/BaseApi.kt index 76edaee..4a5c55f 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/net/UserApi.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/net/BaseApi.kt @@ -5,10 +5,12 @@ import com.tenlionsoft.aimz_k.model.BaseResponseBean import com.tenlionsoft.aimz_k.model.ContactListBean import com.tenlionsoft.aimz_k.model.FileUploadStateBean import com.tenlionsoft.aimz_k.model.PageListBean +import com.tenlionsoft.aimz_k.model.UserInfoBean import com.tenlionsoft.baselib.model.VersionBean import okhttp3.MultipartBody import okhttp3.RequestBody import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.Headers import retrofit2.http.Multipart @@ -19,7 +21,7 @@ import retrofit2.http.Path import retrofit2.http.Query -interface UserApi { +interface BaseApi { /** * 登录 * @@ -28,16 +30,30 @@ interface UserApi { */ @Headers("Content-Type: application/json", "Accept: application/json", "projectName:usercenter") @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系统 - * 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") - suspend fun doLoginSocket(@Body user: RequestBody): BaseResponseBean + suspend fun doLoginSocket( + @Body + user: RequestBody + ): BaseResponseBean /** * 获取App版本 @@ -46,7 +62,10 @@ interface UserApi { */ @Headers("token:need", "Content-Type: application/json", "Accept: application/json") @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") @Multipart @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") @Multipart @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") @Multipart @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") @Multipart @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") @GET("api/user/listpage") - suspend fun doSearchContact(@Query("keyword") keyword: String): PageListBean + suspend fun doSearchContact( + @Query("keyword") + keyword: String + ): PageListBean /** * 新增联系人 @@ -113,7 +147,10 @@ interface UserApi { */ @Headers("Content-Type: application/json", "Accept: application/json", "token:need") @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") @PUT("api/contact/user/apply/update-status/self/uuid/{uuid}/status/{status}") 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 } \ No newline at end of file diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/ChatActivity.kt b/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/ChatActivity.kt index cfe4b6a..8caf910 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/ChatActivity.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/ChatActivity.kt @@ -25,14 +25,20 @@ import com.tenlionsoft.aimz_k.viewmodel.ChatPageViewModel import com.tenlionsoft.baselib.base.BaseActivity import com.tenlionsoft.baselib.contacts.NetConfig 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.FileUtils 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.LoadingDialog import com.tenlionsoft.baselib.widget.SoftKeyBoardListener import com.tenlionsoft.baselib.widget.wheel.WheelView import com.tenlionsoft.medialib.base.SimplePhotoActivity import com.tenlionsoft.medialib.base.SimpleVideoActivity +import java.io.File /** @@ -46,6 +52,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener { private var mContentHeight: Int = 0 private var mLoading: LoadingDialog? = null; private var bottomHeight = -1 + val replyList = arrayListOf() override fun bindView() { mBinding = DataBindingUtil.setContentView(this, R.layout.activity_chat); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) @@ -54,7 +61,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener { val fromId = intent.getStringExtra("fromId") //传递过来的接收人 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 { override fun create(modelClass: Class): T { @@ -120,7 +127,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener { mLoading = null } } - mBinding.wvView.setTextSize(14F, isSp = true) + mBinding.wvView.setTextSize(16F, isSp = true) mBinding.wvView.setAutoFitTextSize(true) mBinding.wvView.setOnItemSelectedListener(object : WheelView.OnItemSelectedListener { override fun onItemSelected(wheelView: WheelView, data: Any, position: Int) { @@ -165,9 +172,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener { //滚动到底部 mBinding.rlvChats.postDelayed({ - layoutManager.scrollToPositionWithOffset( - itemCount - 1, 0 - ) + layoutManager.scrollToPositionWithOffset(itemCount - 1, 0) }, 50) chatPageViewModel!!.scrollListToBottom.value = false } @@ -198,9 +203,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener { val itemCount = layoutManager.itemCount //滚动到底部 mBinding.rlvChats.postDelayed({ - layoutManager.scrollToPositionWithOffset( - itemCount - 1, 0 - ) + layoutManager.scrollToPositionWithOffset(itemCount - 1, 0) }, 100) }, { _ -> Log.e("ChatActivity", "软键盘: 隐藏") @@ -213,9 +216,24 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener { 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() } + override fun onResume() { + super.onResume() + chatPageViewModel!!.getReplyData() + } + @SuppressLint("UnspecifiedRegisterReceiverFlag") private fun registerLocalReceiver() { @@ -240,9 +258,7 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener { val itemCount = layoutManager.itemCount //滚动到底部 mBinding.rlvChats.postDelayed({ - layoutManager.scrollToPositionWithOffset( - itemCount - 1, 0 - ) + layoutManager.scrollToPositionWithOffset(itemCount - 1, 0) }, 100) } @@ -286,8 +302,34 @@ class ChatActivity : BaseActivity(), AdapterItemClickListener { } 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 -> { diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/MainActivity.kt b/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/MainActivity.kt index 9a2ba28..e944fca 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/MainActivity.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/page/activity/MainActivity.kt @@ -37,12 +37,6 @@ class MainActivity : BaseActivity() { private lateinit var mFragments: ArrayList private var exitTime: Long = 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 var mUpdateView: CenterProgressUpdateView? = null private var mApkFile: File? = null @@ -52,42 +46,42 @@ class MainActivity : BaseActivity() { } override fun bindView() { - mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); + mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main) mBinding.mainModel = mainPageViewModel - initView(); + initView() } /** * 初始化页面 */ private fun initView() { - mFragments = ArrayList(); - mFragments.add(MsgFragment.newInstance()); - mFragments.add(ContactFragment.newInstance()); - mFragments.add(MineFragment.newInstance()); - mBinding.vpContent.adapter = VpAdapter(this, mFragments); - mBinding.bnvTab.itemTextColor = resources.getColorStateList(R.color.col_tabs, theme); - mBinding.bnvTab.itemIconSize = DensityUtils.dp2px(this, 24F); - mBinding.vpContent.isUserInputEnabled = false; + mFragments = ArrayList() + mFragments.add(MsgFragment.newInstance()) + mFragments.add(ContactFragment.newInstance()) + mFragments.add(MineFragment.newInstance()) + mBinding.vpContent.adapter = VpAdapter(this, mFragments) + mBinding.bnvTab.itemTextColor = resources.getColorStateList(R.color.col_tabs, theme) + mBinding.bnvTab.itemIconSize = DensityUtils.dp2px(this, 24F) + mBinding.vpContent.isUserInputEnabled = false mBinding.bnvTab.setOnItemSelectedListener { item -> when (item.itemId) { R.id.menu_item_msg -> { - setStatusBarColor(true); - mBinding.vpContent.setCurrentItem(0, false); - mCurPosition = 0; - return@setOnItemSelectedListener true; + setStatusBarColor(true) + mBinding.vpContent.setCurrentItem(0, false) + mCurPosition = 0 + return@setOnItemSelectedListener true }//首页 R.id.menu_item_center -> { - setStatusBarColor(true); - mBinding.vpContent.setCurrentItem(1, false); - mCurPosition = 1; - return@setOnItemSelectedListener true; + setStatusBarColor(true) + mBinding.vpContent.setCurrentItem(1, false) + mCurPosition = 1 + return@setOnItemSelectedListener true }//统计 R.id.menu_item_mine -> { - setStatusBarColor(false); - mBinding.vpContent.setCurrentItem(2, false); - mCurPosition = 2; - return@setOnItemSelectedListener true; + setStatusBarColor(false) + mBinding.vpContent.setCurrentItem(2, false) + mCurPosition = 2 + return@setOnItemSelectedListener true }//我的 } false @@ -98,9 +92,9 @@ class MainActivity : BaseActivity() { if (mCurPosition == 0) { if ((System.currentTimeMillis() - exitTime) > 2000) { ToastUtils.normal("再按一次退出程序") - exitTime = System.currentTimeMillis(); + exitTime = System.currentTimeMillis() } else { - exitApp(); + exitApp() } } else { mBinding.bnvTab.selectedItemId = R.id.menu_item_msg @@ -162,7 +156,7 @@ class MainActivity : BaseActivity() { } else { //申请权限 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) startActivityForResult(intent, 298) } diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/ContactFragment.kt b/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/ContactFragment.kt index 2722822..96b43bd 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/ContactFragment.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/ContactFragment.kt @@ -1,5 +1,6 @@ package com.tenlionsoft.aimz_k.page.fragments +import android.app.AlertDialog import android.content.Context import android.content.Intent 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.baselib.widget.AdapterItemClickListener import com.tenlionsoft.baselib.widget.AdapterItemLongClickListener +import com.tenlionsoft.baselib.widget.LoadingDialog import com.tenlionsoft.baselib.widget.StackedImageDecoration -class ContactFragment : Fragment(), AdapterItemLongClickListener, +class ContactFragment : Fragment(), + AdapterItemLongClickListener, AdapterItemClickListener { companion object { - fun newInstance() = ContactFragment() + fun newInstance() = + ContactFragment() } private lateinit var viewModel: ContactViewModel private lateinit var mBind: FragmentContactBinding private lateinit var mActivity: MainActivity - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - } + private var mLoading: LoadingDialog? = null private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { @@ -62,12 +62,27 @@ class ContactFragment : Fragment(), AdapterItemLongClickListener + dialog.dismiss() + viewModel.delContact(d) + }.setNegativeButton("取消") { dialog, _ -> dialog.dismiss() }.create().show() } override fun onItemClick(data: ContactListBean) { mActivity.startActivity( - Intent(mActivity, ChatActivity::class.java) - .putExtra("fromId", data.contactUserId) + Intent(mActivity, ChatActivity::class.java).putExtra("fromId", data.contactUserId) .putExtra("name", data.userIdNickname) ) } diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MineFragment.kt b/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MineFragment.kt index bf08140..4235cd3 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MineFragment.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MineFragment.kt @@ -10,6 +10,7 @@ import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import com.atwa.filepicker.core.FilePicker import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy 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.viewmodel.MineViewModel import com.tenlionsoft.baselib.utils.SpUtils +import com.tenlionsoft.baselib.widget.LoadingDialog class MineFragment : Fragment() { private lateinit var mBind: FragmentMineBinding private lateinit var mActivity: MainActivity - - companion object { - fun newInstance() = MineFragment() - } - + private val filePicker = FilePicker.getInstance(this) + private var mLoading: LoadingDialog? = null private lateinit var viewModel: MineViewModel override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, + inflater: LayoutInflater, + container: ViewGroup?, savedInstanceState: Bundle? ): View { - mBind = DataBindingUtil.inflate( - layoutInflater, - R.layout.fragment_mine, - container, - false - ) + mBind = DataBindingUtil.inflate(layoutInflater, R.layout.fragment_mine, container, false) viewModel = ViewModelProvider(this, object : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return MineViewModel() as T + return MineViewModel(mActivity) as T } })[MineViewModel::class.java] mBind.model = viewModel mBind.lifecycleOwner = this - + mBind.ivUserIcon.setOnClickListener { + filePicker.pickImage { meta -> + if (meta != null) { + viewModel.doUploadImg(meta) + } + } + } initView() return mBind.root } private fun initView() { mBind.tvName.text = SpUtils.getNickName() - mBind.tvAccount.text = SpUtils.getUserName() - val requestOptions = RequestOptions() - .error(R.drawable.ic_user_default) + mBind.tvAccount.text = SpUtils.getPhone() + val requestOptions = RequestOptions().error(R.drawable.ic_user_default) .placeholder(R.drawable.ic_user_default) .skipMemoryCache(false) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) @@ -70,7 +70,30 @@ class MineFragment : Fragment() { startActivity(Intent(mActivity, ManageReplyActivity::class.java)) } 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 } } + + + companion object { + fun newInstance() = MineFragment() + } } \ No newline at end of file diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MsgFragment.kt b/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MsgFragment.kt index 802aac4..50587f9 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MsgFragment.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/page/fragments/MsgFragment.kt @@ -14,11 +14,13 @@ import androidx.lifecycle.ViewModelProvider import com.tenlionsoft.aimz_k.R import com.tenlionsoft.aimz_k.databinding.FragmentMsgBinding 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.MainActivity import com.tenlionsoft.aimz_k.services.SocketService import com.tenlionsoft.aimz_k.viewmodel.MsgViewModel import com.tenlionsoft.baselib.contacts.ProjectConfig +import com.tenlionsoft.baselib.utils.AppUtils import com.tenlionsoft.baselib.widget.AdapterItemClickListener import com.tenlionsoft.baselib.widget.AdapterItemLongClickListener import com.tenlionsoft.baselib.widget.LoadingDialog @@ -37,17 +39,13 @@ class MsgFragment : Fragment(), AdapterItemClickListener, override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View { + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { mMsgBinding = DataBindingUtil.inflate( - layoutInflater, - R.layout.fragment_msg, - container, - false - ) + layoutInflater, R.layout.fragment_msg, container, false + ) 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 { override fun create(modelClass: Class): T { @@ -87,9 +85,11 @@ class MsgFragment : Fragment(), AdapterItemClickListener, viewModel.showLoadDialog.observe(viewLifecycleOwner) { if (it) { //显示loading - mLoading = LoadingDialog.Builder(mActivity as Activity).setCancelOutside(false) + mLoading = LoadingDialog.Builder(mActivity as Activity) + .setCancelOutside(false) .setCancelable(false) - .setMessage("删除中...").create() + .setMessage("删除中...") + .create() mLoading!!.show() } else { //隐藏loading @@ -111,7 +111,13 @@ class MsgFragment : Fragment(), AdapterItemClickListener, //开启service 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() { @@ -123,12 +129,17 @@ class MsgFragment : Fragment(), AdapterItemClickListener, * 条目点击 */ override fun onItemClick(data: MsgCategoryBean) { - startActivity( - Intent(mActivity, ChatActivity::class.java).putExtra( - "fromId", - data.senderId - ) - ) + val intent = Intent(mActivity, ChatActivity::class.java) + when (data.msgType) { + 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 -> { + 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连接状态 diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/services/SocketService.kt b/app/src/main/java/com/tenlionsoft/aimz_k/services/SocketService.kt index bd80b8b..5882f15 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/services/SocketService.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/services/SocketService.kt @@ -67,6 +67,8 @@ class SocketService : Service(), WsManager.MsgCallBack { val intentFilter = IntentFilter() mLocalReceiver = LocalReceiver() 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) } @@ -111,11 +113,21 @@ class SocketService : Service(), WsManager.MsgCallBack { val isConnect = mWsManager?.isNetworkConnected(context) if (true == isConnect) { when (intent?.action) { + //发送消息 ProjectConfig.A_S_MSG_SEND -> { val msgConvertBean = intent.getSerializableExtra("msgBean") val msg = gson.toJson(msgConvertBean) mWsManager?.sendMessage(msg) - }//发送消息 + } + //断开连接 + ProjectConfig.A_S_LOGOUT -> { + stopSocket() + } + //重新连接 + ProjectConfig.A_S_RE_LOGIN -> { + stopSocket() + startSocket() + } } } else { ToastUtils.error("请检查网络") diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ApplyContactViewModel.kt b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ApplyContactViewModel.kt index 49fe79b..8ee2e26 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ApplyContactViewModel.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ApplyContactViewModel.kt @@ -5,7 +5,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.tenlionsoft.aimz_k.adapter.ApplyContactAdapter 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.net.ExParse import com.tenlionsoft.baselib.net.RetrofitClient @@ -16,7 +16,7 @@ class ApplyContactViewModel(private val context: Context) : BaseViewModel() { val _applyList = MutableLiveData>() val adapter = ApplyContactAdapter(_applyList.value ?: emptyList(), this) val retrofit = RetrofitClient.getInstance(context) - val userApi = retrofit.create(UserApi::class.java) + val userApi = retrofit.create(BaseApi::class.java) init { doGetApplyList() diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ChatPageViewModel.kt b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ChatPageViewModel.kt index 5dea0a0..64ed0dc 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ChatPageViewModel.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ChatPageViewModel.kt @@ -15,9 +15,9 @@ import com.atwa.filepicker.result.VideoMeta import com.google.gson.Gson import com.tenlionsoft.aimz_k.ConvertBeanUtils 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.CoverSealedBean -import com.tenlionsoft.aimz_k.dao.DbManager import com.tenlionsoft.aimz_k.model.FileDataBean import com.tenlionsoft.aimz_k.model.MsgBean 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.ReplyBean 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.contacts.ProjectConfig +import com.tenlionsoft.baselib.contacts.toFileBody import com.tenlionsoft.baselib.net.ExParse import com.tenlionsoft.baselib.net.RetrofitClient import com.tenlionsoft.baselib.utils.SpUtils @@ -36,15 +38,9 @@ import com.tenlionsoft.baselib.widget.AdapterItemClickListener import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.MultipartBody -import okhttp3.RequestBody -import okhttp3.RequestBody.Companion.asRequestBody class ChatPageViewModel( - private val fromId: String, - private val toId: String, - private var context: Context + private val fromId: String, private val toId: String, private var context: Context ) : BaseViewModel() { val txtMsg = MutableLiveData("") val showSendBtn = MutableLiveData(false)//显示/隐藏发送按钮 @@ -55,12 +51,16 @@ class ChatPageViewModel( val scrollListToBottom = MutableLiveData(false) private val _msgList = MutableLiveData>() private val retrofitClient = RetrofitClient.getInstance(context) - private val netApi = retrofitClient.create(UserApi::class.java) - var adapter: ChatMsgAdapter = ChatMsgAdapter(_msgList.value ?: emptyList(), this) + private val netApi = retrofitClient.create(BaseApi::class.java) + var adapter: ChatMsgAdapter = ChatMsgAdapter( + _msgList.value + ?: emptyList(), this + ) var onItemClickListener: AdapterItemClickListener? = null private val mGson: Gson = Gson() private var msgHandlerList: ArrayList = arrayListOf() - private val _replyList = MutableLiveData?>() + val _replyList = MutableLiveData>() + private val handler = object : Handler(Looper.getMainLooper()) { override fun handleMessage(msg: Message) { super.handleMessage(msg) @@ -92,10 +92,10 @@ class ChatPageViewModel( val list = getList() val rList = getReplyList() Log.e("ChatPageViewModel", "Init:${list} ") - if (rList != null) { + if (rList.isNotEmpty()) { _replyList.value = rList } else { - _replyList.value = emptyList() + _replyList.value = arrayListOf() } _msgList.value = list adapter.setData(list) @@ -103,7 +103,7 @@ class ChatPageViewModel( } } - private suspend fun getReplyList(): List? { + private suspend fun getReplyList(): MutableList { return withContext(Dispatchers.IO) { val rDao = DbManager.db.replayDao() return@withContext rDao.getAllReply() @@ -112,6 +112,7 @@ class ChatPageViewModel( //获取列表 private suspend fun getList(): List { + Log.e("ChatPageViewModel", "getList:${fromId}\n${toId}"); return withContext(Dispatchers.IO) { val msgDao = DbManager.db.msgDao() return@withContext msgDao.getMsgByFromOrToPage(fromId, toId) @@ -205,15 +206,7 @@ class ChatPageViewModel( private suspend fun doUploadVideo(meta: VideoMeta) { try { - val requestFile: RequestBody = - meta.file!!.asRequestBody("multipart/form-data".toMediaTypeOrNull()) - val body: MultipartBody.Part = - MultipartBody.Part.createFormData( - "video", - meta.file!!.getName(), - requestFile - ) - + val body = meta.file!!.toFileBody("video") val bean = retrofitClient.makeApiCall { netApi.doUploadVideo(body) } @@ -230,14 +223,7 @@ class ChatPageViewModel( private suspend fun doUploadFile(meta: FileMeta) { try { - val requestFile: RequestBody = - meta.file!!.asRequestBody("multipart/form-data".toMediaTypeOrNull()) - val body: MultipartBody.Part = - MultipartBody.Part.createFormData( - "file", - meta.file!!.getName(), - requestFile - ) + val body = meta.file!!.toFileBody("file") val bean = retrofitClient.makeApiCall { netApi.doUploadFile(body) } @@ -257,17 +243,9 @@ class ChatPageViewModel( try { for (item in list) { if (item != null) { - val requestFile: RequestBody = - item.file!!.asRequestBody("multipart/form-data".toMediaTypeOrNull()) - val body: MultipartBody.Part = - MultipartBody.Part.createFormData( - "image", - item.file!!.getName(), - requestFile - ) - + val toFileBody = item.file!!.toFileBody("image") val bean = retrofitClient.makeApiCall { - netApi.doUploadImage(body) + netApi.doUploadImage(toFileBody) } Log.e("ChatPageViewModel", "doUploadImgs: $bean") if (bean.code == 200) { @@ -348,7 +326,7 @@ class ChatPageViewModel( val dao = DbManager.db.msgDao() val cDao = DbManager.db.categoryDao() val msgBean = ConvertBeanUtils.convertBeanToMsgBean(b) - cDao.updateCategory(b.receiver.receiverId!!, b.status, b.body, b.timestamp) + cDao.updateOrInsert(b.receiver.receiverId!!, msgBean) dao.insertMsg(msgBean) return@withContext msgBean } @@ -370,10 +348,11 @@ class ChatPageViewModel( is CoverSealedBean.StateBean -> { viewModelScope.launch { withContext(Dispatchers.IO) { + //更新状态 val dao = DbManager.db.msgDao() val cDao = DbManager.db.categoryDao() val statusType = - mGson.fromJson(bean.data.body, BodyContent::class.java).statusType!! + mGson.fromJson(bean.data.body, BodyContent::class.java).statusType!! dao.updateStatus(bean.data.messageId, statusType) cDao.updateStatus(bean.data.sender.senderId!!, statusType) } @@ -416,72 +395,87 @@ class ChatPageViewModel( val bodyBean = BodyContent(content = txtMsg.value, null, null) val body = mGson.toJson(bodyBean) return MsgConvertBean( - body = body, - customMessageType = "", - messageId = TimeUtils.getNowDateMillis().toString(), - messageType = type, - metadata = "", - timestamp = TimeUtils.getNowDateMillis(), - sender = Sender( - senderId = SpUtils.getId(), - senderType = "" - ), - receiver = Receiver( - receiverId = fromId, - receiverType = "SINGLE_USER" - ), - status = ProjectConfig.MSG_SEND_ING, + body = body, + customMessageType = "", + messageId = TimeUtils.getNowDateMillis() + .toString(), + messageType = type, + metadata = "", + timestamp = TimeUtils.getNowDateMillis(), + sender = Sender(senderId = SpUtils.getId(), senderType = ""), + receiver = Receiver(receiverId = fromId, receiverType = "SINGLE_USER"), + status = ProjectConfig.MSG_SEND_ING, ) } + 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 { val bodyBean = BodyContent(content = mGson.toJson(fileBean), null, null) val body = mGson.toJson(bodyBean) return MsgConvertBean( - body = body, - customMessageType = "", - messageId = TimeUtils.getNowDateMillis().toString(), - messageType = type, - metadata = "", - timestamp = TimeUtils.getNowDateMillis(), - sender = Sender( - senderId = SpUtils.getId(), - senderType = "" - ), - receiver = Receiver( - receiverId = fromId, - receiverType = "SINGLE_USER" - ), - status = ProjectConfig.MSG_SEND_ING, + body = body, + customMessageType = "", + messageId = TimeUtils.getNowDateMillis() + .toString(), + messageType = type, + metadata = "", + timestamp = TimeUtils.getNowDateMillis(), + sender = Sender(senderId = SpUtils.getId(), senderType = ""), + receiver = Receiver(receiverId = fromId, receiverType = "SINGLE_USER"), + status = ProjectConfig.MSG_SEND_ING, ) } - //构建状态信息 private fun buildStatusBean(messageId: String): MsgConvertBean { val bodyBean = BodyContent(content = null, msg = "", statusType = "SUCCESS_RECEIVED") val body = mGson.toJson(bodyBean) return MsgConvertBean( - body = body, - customMessageType = "", - messageId = messageId, - messageType = ProjectConfig.MSG_STATUS, - metadata = "", - timestamp = TimeUtils.getNowDateMillis(), - sender = Sender( - senderId = SpUtils.getId(), - senderType = "" - ), - receiver = Receiver( - receiverId = fromId, - receiverType = "SINGLE_USER" - ), - status = "", + body = body, + customMessageType = "", + messageId = messageId, + messageType = ProjectConfig.MSG_STATUS, + metadata = "", + timestamp = TimeUtils.getNowDateMillis(), + sender = Sender(senderId = SpUtils.getId(), senderType = ""), + receiver = Receiver(receiverId = fromId, receiverType = "SINGLE_USER"), + status = "", ) } + //获取回复短语 + fun getReplyData() { + viewModelScope.launch { + val list = getReplyList() + if (list.isNotEmpty()) { + _replyList.value = list + } + } + } + } diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ContactViewModel.kt b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ContactViewModel.kt index 27fbab0..8714b29 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ContactViewModel.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ContactViewModel.kt @@ -5,15 +5,20 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.tenlionsoft.aimz_k.adapter.ApplyContactUserIconAdapter 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.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.net.ExParse 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.AdapterItemLongClickListener +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class ContactViewModel(private val context: Context) : BaseViewModel() { private val _contactList = MutableLiveData?>() @@ -25,7 +30,7 @@ class ContactViewModel(private val context: Context) : BaseViewModel() { val isHasApply = MutableLiveData(false) val isRefreshing = MutableLiveData(false) private val retrofit = RetrofitClient.getInstance(context) - private val userApi = retrofit.create(UserApi::class.java) + private val userApi = retrofit.create(BaseApi::class.java) init { 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 + } + + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/LoginPageViewModel.kt b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/LoginPageViewModel.kt index 207ec23..36df48d 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/LoginPageViewModel.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/LoginPageViewModel.kt @@ -6,7 +6,7 @@ import androidx.lifecycle.viewModelScope import com.google.gson.Gson import com.tenlionsoft.aimz_k.model.AppTokenUser 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.BaseViewModel import com.tenlionsoft.baselib.net.ExParse @@ -25,7 +25,7 @@ class LoginPageViewModel : BaseViewModel() { val isLoginSuccess = MutableLiveData() private val gson = Gson() 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) { diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MainPageViewModel.kt b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MainPageViewModel.kt index 76e86a5..40ab2c0 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MainPageViewModel.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MainPageViewModel.kt @@ -3,7 +3,7 @@ package com.tenlionsoft.aimz_k.viewmodel import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope -import com.tenlionsoft.aimz_k.net.UserApi +import com.tenlionsoft.aimz_k.net.BaseApi import com.tenlionsoft.baselib.base.BaseViewModel import com.tenlionsoft.baselib.contacts.ProjectConfig import com.tenlionsoft.baselib.net.ExParse @@ -24,7 +24,7 @@ class MainPageViewModel : BaseViewModel() { private suspend fun getAppVersion(context: Context) { try { val appVersion = RetrofitClient.getInstance(context) - .create(UserApi::class.java) + .create(BaseApi::class.java) .doCheckAppVersion(ProjectConfig.APP_VERSION_ID) if (appVersion.versioncode.isNotEmpty()) { val isNeedUpdate = AppUtils.checkcode(appVersion.versioncode) diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ManageReplyViewModel.kt b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ManageReplyViewModel.kt index 90a744c..b45aab8 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ManageReplyViewModel.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/ManageReplyViewModel.kt @@ -16,8 +16,8 @@ import kotlinx.coroutines.withContext class ManageReplyViewModel : BaseViewModel(), AdapterItemLongClickListener { val txtMsg = MutableLiveData("") - val _replyList = MutableLiveData?>() - var adapter: ReplyAdapter = ReplyAdapter(_replyList.value ?: emptyList(), this) + val _replyList = MutableLiveData>() + var adapter: ReplyAdapter = ReplyAdapter(_replyList.value ?: arrayListOf(), this) var onItemLongClickListener: AdapterItemLongClickListener = this val curBean = MutableLiveData() @@ -27,15 +27,15 @@ class ManageReplyViewModel : BaseViewModel(), AdapterItemLongClickListener? = withContext(Dispatchers.IO) { + val list: MutableList = withContext(Dispatchers.IO) { val rDao = DbManager.db.replayDao() val list = rDao.getAllReply() list } - if (!list.isNullOrEmpty()) { + if (list.isNotEmpty()) { _replyList.value = list } else { - _replyList.value = emptyList() + _replyList.value = arrayListOf() } Log.e("ManageReplyViewModel", "init:${adapter} ${Thread.currentThread().name}"); adapter.setData(_replyList.value!!) diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MineViewModel.kt b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MineViewModel.kt index c2c16f3..f4e5f8b 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MineViewModel.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/MineViewModel.kt @@ -1,7 +1,45 @@ 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() { - // TODO: Implement the ViewModel +class MineViewModel(val context: Context) : BaseViewModel() { + private val retrofitClient = RetrofitClient.getInstance(context) + private val baseApi = retrofitClient.create(BaseApi::class.java) + val isUploadIconSuccess = MutableLiveData() + + + 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) + } + + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/SearchContactViewModel.kt b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/SearchContactViewModel.kt index 2296874..710d8d7 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/SearchContactViewModel.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/viewmodel/SearchContactViewModel.kt @@ -7,7 +7,7 @@ import androidx.lifecycle.viewModelScope import com.tenlionsoft.aimz_k.adapter.ContactAdapter import com.tenlionsoft.aimz_k.model.AddContactBean 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.contacts.toBody import com.tenlionsoft.baselib.model.ViewState @@ -23,7 +23,7 @@ class SearchContactViewModel(val context: Context) : BaseViewModel() { val adapter = ContactAdapter(_contactList.value ?: emptyList(), this) val showApplyContentDialog = MutableLiveData(false) 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) { txtMsg.value = s.toString() } diff --git a/app/src/main/java/com/tenlionsoft/aimz_k/widget/BindingUtils.kt b/app/src/main/java/com/tenlionsoft/aimz_k/widget/BindingUtils.kt index 027f91e..2566137 100644 --- a/app/src/main/java/com/tenlionsoft/aimz_k/widget/BindingUtils.kt +++ b/app/src/main/java/com/tenlionsoft/aimz_k/widget/BindingUtils.kt @@ -54,16 +54,10 @@ object BindingUtils { if (url.isNullOrEmpty()) { imageView.setImageResource(R.drawable.app_logo_small) } else { - val requestOptions = RequestOptions() - .error(R.drawable.app_logo_small) - .placeholder(R.drawable.app_logo_small) - .skipMemoryCache(false) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .centerInside() - Glide.with(imageView.context) - .load(url) - .apply(requestOptions) - .into(imageView) + val requestOptions = RequestOptions().error(R.drawable.app_logo_small) + .placeholder(R.drawable.app_logo_small).skipMemoryCache(false) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE).centerInside() + Glide.with(imageView.context).load(url).apply(requestOptions).into(imageView) } } @@ -74,16 +68,10 @@ object BindingUtils { if (url.isNullOrEmpty()) { imageView.setImageResource(R.drawable.ic_user_default) } else { - val requestOptions = RequestOptions() - .error(R.drawable.ic_psd) - .placeholder(R.drawable.ic_psd) - .skipMemoryCache(false) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .circleCrop() - Glide.with(imageView.context) - .load(url) - .apply(requestOptions) - .into(imageView) + val requestOptions = RequestOptions().error(R.drawable.ic_user_default) + .placeholder(R.drawable.ic_user_default).skipMemoryCache(false) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE).circleCrop() + Glide.with(imageView.context).load(url).apply(requestOptions).into(imageView) } } @@ -126,8 +114,7 @@ object BindingUtils { if (!fileDataBean.fileName.isNullOrEmpty()) { iv.visibility = View.VISIBLE val suffix = fileDataBean.fileName!!.substring( - fileDataBean.fileName!!.lastIndexOf("."), - fileDataBean.fileName!!.length + fileDataBean.fileName!!.lastIndexOf("."), fileDataBean.fileName!!.length ) Log.e("BindingUtils", "jsonConvertFileIcon: $suffix") var id = R.drawable.ic_audio @@ -172,20 +159,17 @@ object BindingUtils { if (str.isNullOrEmpty()) { iv.setImageResource(com.tenlionsoft.baselib.R.drawable.ic_img_load_err) } else { - val options: RequestOptions = RequestOptions() - .placeholder(com.tenlionsoft.baselib.R.drawable.ic_loading) // 正在加载中的图片 - .error(com.tenlionsoft.baselib.R.drawable.ic_img_load_err) // 加载失败的图片 + val options: RequestOptions = + RequestOptions().placeholder(com.tenlionsoft.baselib.R.drawable.ic_loading) // 正在加载中的图片 + .error(com.tenlionsoft.baselib.R.drawable.ic_img_load_err) // 加载失败的图片 val gson = Gson() val bodyContent = gson.fromJson(str, BodyContent::class.java) val fileBean = gson.fromJson(bodyContent.content, FileDataBean::class.java) - Glide.with(iv.context) - .load(NetConfig.MAIN_URL + fileBean.fileUrl) // 图片地址 - .apply(options) - .into(object : CustomTarget() { + Glide.with(iv.context).load(NetConfig.MAIN_URL + fileBean.fileUrl) // 图片地址 + .apply(options).into(object : CustomTarget() { override fun onResourceReady( - resource: Drawable, - transition: Transition? + resource: Drawable, transition: Transition? ) { val imageSize: ImageSize? = ImageUtils.getImageSize((resource as BitmapDrawable).bitmap) @@ -194,9 +178,7 @@ object BindingUtils { imageLP?.width = imageSize.getWidth() imageLP?.height = imageSize.getHeight() iv.setLayoutParams(imageLP) - Glide.with(iv.context) - .load(resource) - .apply(options) // 参数 + Glide.with(iv.context).load(resource).apply(options) // 参数 .into(iv) } } @@ -217,16 +199,11 @@ object BindingUtils { val body = gson.fromJson(str, BodyContent::class.java) val fileData = gson.fromJson(body.content, FileDataBean::class.java) - val requestOptions = RequestOptions() - .error(R.drawable.app_logo_small) - .placeholder(R.drawable.app_logo_small) - .skipMemoryCache(false) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .centerInside() - Glide.with(view.context) - .load(NetConfig.MAIN_URL + fileData.fileUrl) - .apply(requestOptions) - .into(view) + val requestOptions = RequestOptions().error(R.drawable.app_logo_small) + .placeholder(R.drawable.app_logo_small).skipMemoryCache(false) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE).centerInside() + Glide.with(view.context).load(NetConfig.MAIN_URL + fileData.fileUrl) + .apply(requestOptions).into(view) } } @@ -236,7 +213,7 @@ object BindingUtils { if (long == null) { tv.text = "" } else { - tv.text = TimeUtils.millis2HMStr(long) + tv.text = TimeUtils.getFriendlyTimeSpanByNow(long) } } diff --git a/app/src/main/res/anim/activity_close_enter.xml b/app/src/main/res/anim/activity_close_enter.xml new file mode 100644 index 0000000..8ef7cdf --- /dev/null +++ b/app/src/main/res/anim/activity_close_enter.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/activity_close_exit.xml b/app/src/main/res/anim/activity_close_exit.xml new file mode 100644 index 0000000..6a88693 --- /dev/null +++ b/app/src/main/res/anim/activity_close_exit.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_right.xml b/app/src/main/res/anim/activity_open_enter.xml similarity index 66% rename from app/src/main/res/anim/slide_in_right.xml rename to app/src/main/res/anim/activity_open_enter.xml index 7a4ddb0..78f1a17 100644 --- a/app/src/main/res/anim/slide_in_right.xml +++ b/app/src/main/res/anim/activity_open_enter.xml @@ -1,7 +1,7 @@ + android:duration="300"/> \ No newline at end of file diff --git a/app/src/main/res/anim/slide_out_left.xml b/app/src/main/res/anim/activity_open_exit.xml similarity index 66% rename from app/src/main/res/anim/slide_out_left.xml rename to app/src/main/res/anim/activity_open_exit.xml index c646592..1211489 100644 --- a/app/src/main/res/anim/slide_out_left.xml +++ b/app/src/main/res/anim/activity_open_exit.xml @@ -1,7 +1,7 @@ + android:toXDelta="-100%" /> \ No newline at end of file diff --git a/app/src/main/res/anim/fade_in.xml b/app/src/main/res/anim/fade_in.xml deleted file mode 100644 index ce513c2..0000000 --- a/app/src/main/res/anim/fade_in.xml +++ /dev/null @@ -1,6 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/anim/fade_out.xml b/app/src/main/res/anim/fade_out.xml deleted file mode 100644 index 2ba6dd1..0000000 --- a/app/src/main/res/anim/fade_out.xml +++ /dev/null @@ -1,6 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/ic_title_icon.png b/app/src/main/res/drawable-xhdpi/ic_title_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a502900f9dd4c8b2e8e79e95eb37b5f4268ddeab GIT binary patch literal 9730 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 @@ -124,9 +125,9 @@ android:minLines="1" android:onTextChanged="@{viewModel::onTxtChange}" android:padding="10dp" - tools:text="测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试" android:text="@{viewModel.txtMsg}" - android:textSize="14sp" /> + android:textSize="14sp" + tools:text="测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试" /> + android:visibility="@{viewModel.showReplyLayout? View.VISIBLE:View.GONE}" + app:wv_normalItemTextColor="@color/gray_d1" + app:wv_selectedItemTextColor="@color/black" /> - - - + android:src="@drawable/ic_title_icon" /> diff --git a/app/src/main/res/layout/fragment_contact.xml b/app/src/main/res/layout/fragment_contact.xml index 7304606..9050f04 100644 --- a/app/src/main/res/layout/fragment_contact.xml +++ b/app/src/main/res/layout/fragment_contact.xml @@ -39,7 +39,7 @@ android:layout_height="wrap_content" android:gravity="center_vertical" android:orientation="horizontal" - android:paddingLeft="25dp" + android:paddingLeft="10dp" android:paddingTop="15dp" android:paddingRight="25dp" android:paddingBottom="15dp"> @@ -73,16 +73,13 @@ + android:layout_height="match_parent"> diff --git a/app/src/main/res/layout/fragment_mine.xml b/app/src/main/res/layout/fragment_mine.xml index 9fa1bbd..19cdb8c 100644 --- a/app/src/main/res/layout/fragment_mine.xml +++ b/app/src/main/res/layout/fragment_mine.xml @@ -28,7 +28,7 @@ android:id="@+id/iv_user_icon" android:layout_width="64dp" android:layout_height="64dp" - android:src="@drawable/ic_load_error" /> + android:src="@drawable/ic_user_default" /> + android:textSize="16sp" + android:textStyle="bold" /> @@ -80,8 +81,8 @@ @@ -110,7 +111,7 @@ diff --git a/app/src/main/res/layout/item_category.xml b/app/src/main/res/layout/item_category.xml index c6e5c38..ca779f8 100644 --- a/app/src/main/res/layout/item_category.xml +++ b/app/src/main/res/layout/item_category.xml @@ -31,25 +31,25 @@ android:layout_margin="10dp"> + tools:src="@drawable/ic_user_default" /> + android:src="@drawable/shp_circle_red" + android:visibility="gone" /> diff --git a/app/src/main/res/layout/item_contact.xml b/app/src/main/res/layout/item_contact.xml index 1b27a1c..6a9a3b3 100644 --- a/app/src/main/res/layout/item_contact.xml +++ b/app/src/main/res/layout/item_contact.xml @@ -23,14 +23,15 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" - android:orientation="horizontal"> + android:gravity="center_vertical" + android:orientation="horizontal" + android:padding="10dp"> @@ -42,8 +43,7 @@ @@ -52,7 +52,6 @@ android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical" - android:paddingLeft="10dp" android:paddingRight="10dp"> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 7c92536..8015054 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,16 +1,22 @@ - - + \ No newline at end of file diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100755 index 0000000..ce0105e --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseActivity.kt b/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseActivity.kt index f3579b6..096053b 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseActivity.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/base/BaseActivity.kt @@ -1,29 +1,30 @@ package com.tenlionsoft.baselib.base import android.content.Context -import android.util.Log -import android.view.MotionEvent +import android.content.Intent import android.view.View import android.view.WindowManager import android.view.inputmethod.InputMethodManager -import android.widget.EditText import androidx.core.content.ContextCompat import com.tenlionsoft.baselib.R +import com.tenlionsoft.baselib.utils.SpUtils import kotlin.system.exitProcess abstract class BaseActivity : DataBindingActivity() { - //点击空白隐藏软键盘 - override fun onTouchEvent(event: MotionEvent): Boolean { - if (event.action == MotionEvent.ACTION_DOWN) { - Log.e("BaseActivity", "onTouchEvent: ") - val v = currentFocus - if (v != null && v is EditText) { - val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(v.windowToken, 0) - } - } - return super.onTouchEvent(event) + + fun reStartApp(){ + SpUtils.putToken("") + SpUtils.putId("") + SpUtils.putPassword("") + SpUtils.putUserName("") +// val broadcast = Intent() +// broadcast.setAction(PathConfig.ACTION_PUSH_STOP_SOCKET) +// sendBroadcast(broadcast) + val intent = packageManager.getLaunchIntentForPackage(packageName) + intent!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(intent) + finish() } fun exitApp() { diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/contacts/NetConfig.kt b/baselib/src/main/java/com/tenlionsoft/baselib/contacts/NetConfig.kt index dfcfad8..7892621 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/contacts/NetConfig.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/contacts/NetConfig.kt @@ -2,8 +2,11 @@ package com.tenlionsoft.baselib.contacts import com.google.gson.Gson import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.toRequestBody +import java.io.File object NetConfig { const val BASE_URL = "http://192.168.0.26:8888/" @@ -20,4 +23,9 @@ fun T.toBody(): RequestBody { val gson = Gson() val str = gson.toJson(this) 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) } \ No newline at end of file diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/contacts/ProjectConfig.kt b/baselib/src/main/java/com/tenlionsoft/baselib/contacts/ProjectConfig.kt index 5fd148c..56cbc60 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/contacts/ProjectConfig.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/contacts/ProjectConfig.kt @@ -40,5 +40,7 @@ object ProjectConfig { const val A_S_DISCONNECT: String = "com.tenlion.soft.aimz_k.socket.disconnect"//连接断开 const val A_S_MSG_RECEIVER: String = "com.tenlion.soft.aimz_k.socket.receiver" //接收到信息 const val A_S_MSG_SEND: String = "com.tenlion.soft.aimz_k.socket.send"//发送信息 + 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 } \ No newline at end of file diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/model/TimeConstants.kt b/baselib/src/main/java/com/tenlionsoft/baselib/model/TimeConstants.kt new file mode 100644 index 0000000..aab0c70 --- /dev/null +++ b/baselib/src/main/java/com/tenlionsoft/baselib/model/TimeConstants.kt @@ -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 \ No newline at end of file diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/net/RetrofitClient.kt b/baselib/src/main/java/com/tenlionsoft/baselib/net/RetrofitClient.kt index 65f35b4..007e1ca 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/net/RetrofitClient.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/net/RetrofitClient.kt @@ -11,42 +11,42 @@ import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit open class RetrofitClient { - private var mCtx: Context? = null; - private val TIMEOUT: Long = 5;//超时时间 - private lateinit var mRetrofit: Retrofit; + private var mCtx: Context? = null + private val TIMEOUT: Long = 5//超时时间 + private lateinit var mRetrofit: Retrofit private constructor(ctx: Context) { - mCtx = ctx; - initManager(); + mCtx = ctx + initManager() } /** * 初始化Retrofit */ private fun initManager() { - val client = getHttpClient(); + val client = getHttpClient() mRetrofit = Retrofit.Builder() .baseUrl(NetConfig.MAIN_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) .client(client) - .build(); + .build() } private fun getHttpClient(): OkHttpClient { - val builder: OkHttpClient.Builder = OkHttpClient.Builder(); + val builder: OkHttpClient.Builder = OkHttpClient.Builder() // setLocalProxy(); // 设置超时 - builder.connectTimeout(TIMEOUT, TimeUnit.MINUTES); - builder.readTimeout(TIMEOUT, TimeUnit.MINUTES); - builder.writeTimeout(TIMEOUT, TimeUnit.MINUTES); + builder.connectTimeout(TIMEOUT, TimeUnit.MINUTES) + builder.readTimeout(TIMEOUT, TimeUnit.MINUTES) + builder.writeTimeout(TIMEOUT, TimeUnit.MINUTES) //封装公共参数 // builder.addInterceptor(new AreaInterceptor()); //builder.addInterceptor(new CommInterceptor()); //多BaseUrl连接器 - builder.addInterceptor(BaseUrlInterceptor()); - return builder.build(); + builder.addInterceptor(BaseUrlInterceptor()) + return builder.build() } suspend fun makeApiCall(apiCall: suspend () -> T): T { diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/utils/AppUtils.kt b/baselib/src/main/java/com/tenlionsoft/baselib/utils/AppUtils.kt index 5f3fb57..a681f11 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/utils/AppUtils.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/utils/AppUtils.kt @@ -1,5 +1,6 @@ package com.tenlionsoft.baselib.utils +import android.app.ActivityManager import android.content.Context import android.content.Intent import android.content.pm.PackageManager @@ -23,7 +24,8 @@ class AppUtils { try { val pm = App.context.packageManager val pi = pm.getPackageInfo(pkgName, 0) - return pi?.versionName ?: "" + return pi?.versionName + ?: "" } catch (e: PackageManager.NameNotFoundException) { e.printStackTrace(); return "" @@ -34,15 +36,38 @@ class AppUtils { 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 = 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 { if (pkgName.isNullOrEmpty()) return -1 try { val pm = App.context.packageManager val pi = pm.getPackageInfo(pkgName, 0); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - return pi?.longVersionCode ?: -1 + return pi?.longVersionCode + ?: -1 } else { - return (pi?.versionCode ?: -1).toLong() + return (pi?.versionCode + ?: -1).toLong() } } catch (e: PackageManager.NameNotFoundException) { e.printStackTrace() @@ -59,7 +84,8 @@ class AppUtils { if (!file.exists()) { try { val newFile = file.createNewFile() - uuid = UUID.randomUUID().toString() + uuid = UUID.randomUUID() + .toString() FileIOUtils.writeFileFromString(file, uuid) } catch (e: IOException) { e.printStackTrace() diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/utils/FileUtils.kt b/baselib/src/main/java/com/tenlionsoft/baselib/utils/FileUtils.kt new file mode 100644 index 0000000..2d2250d --- /dev/null +++ b/baselib/src/main/java/com/tenlionsoft/baselib/utils/FileUtils.kt @@ -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> = 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 + } + + +} \ No newline at end of file diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/utils/StorageUtils.kt b/baselib/src/main/java/com/tenlionsoft/baselib/utils/StorageUtils.kt index e503054..dd6cb3a 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/utils/StorageUtils.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/utils/StorageUtils.kt @@ -28,7 +28,7 @@ class StorageUtils { Log.w( "StorageUtils", "Can't define system cache directory! The app should be re-installed." - ) + ) } return appCacheDir } @@ -43,7 +43,7 @@ class StorageUtils { Log.w( "StorageUtils", "Can't define system cache directory! The app should be re-installed." - ) + ) } return appCacheDir } @@ -52,6 +52,19 @@ class StorageUtils { * 在cache下新增自定义缓存路径 * 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? { //在SDCard/Android/data/com.winfo.update/cache/update_file创建文件夹 val appCacheDir = File(context.externalCacheDir, "update") diff --git a/baselib/src/main/java/com/tenlionsoft/baselib/utils/TimeUtils.kt b/baselib/src/main/java/com/tenlionsoft/baselib/utils/TimeUtils.kt index 7b741ef..213e7a3 100644 --- a/baselib/src/main/java/com/tenlionsoft/baselib/utils/TimeUtils.kt +++ b/baselib/src/main/java/com/tenlionsoft/baselib/utils/TimeUtils.kt @@ -1,7 +1,10 @@ package com.tenlionsoft.baselib.utils +import com.tenlionsoft.baselib.model.TimeConstants import java.text.DateFormat +import java.text.ParseException import java.text.SimpleDateFormat +import java.util.Calendar import java.util.Date import java.util.Locale @@ -41,5 +44,89 @@ object TimeUtils { 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 + } } \ No newline at end of file diff --git a/medialib/src/main/AndroidManifest.xml b/medialib/src/main/AndroidManifest.xml index 257a975..59c4dac 100644 --- a/medialib/src/main/AndroidManifest.xml +++ b/medialib/src/main/AndroidManifest.xml @@ -5,11 +5,13 @@ + android:exported="false" + android:launchMode="singleTop" /> + android:exported="false" + android:launchMode="singleTop" /> \ No newline at end of file