新增聊天消息提醒
This commit is contained in:
@@ -37,9 +37,9 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:windowSoftInputMode="adjustResize"
|
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/Theme.RiderPro">
|
android:theme="@style/Theme.RiderPro"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@@ -69,6 +69,9 @@
|
|||||||
<action android:name="cn.jiguang.user.service.action" />
|
<action android:name="cn.jiguang.user.service.action" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
<service
|
||||||
|
android:name=".TrtcService"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".JpushReciver"
|
android:name=".JpushReciver"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.aiosman.riderpro
|
package com.aiosman.riderpro
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.icu.util.Calendar
|
import android.icu.util.Calendar
|
||||||
import android.icu.util.TimeZone
|
import android.icu.util.TimeZone
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -23,7 +24,6 @@ import com.tencent.imsdk.v2.V2TIMLogListener
|
|||||||
import com.tencent.imsdk.v2.V2TIMManager
|
import com.tencent.imsdk.v2.V2TIMManager
|
||||||
import com.tencent.imsdk.v2.V2TIMSDKConfig
|
import com.tencent.imsdk.v2.V2TIMSDKConfig
|
||||||
import com.tencent.imsdk.v2.V2TIMUserFullInfo
|
import com.tencent.imsdk.v2.V2TIMUserFullInfo
|
||||||
import com.tencent.imsdk.v2.V2TIMValueCallback
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
@@ -66,8 +66,12 @@ object AppState {
|
|||||||
val sign = accountService.getMyTrtcSign()
|
val sign = accountService.getMyTrtcSign()
|
||||||
loginToTrtc(sign.userId, sign.sig)
|
loginToTrtc(sign.userId, sign.sig)
|
||||||
updateTrtcUserProfile()
|
updateTrtcUserProfile()
|
||||||
|
// 登录成功后,启动TrtcService
|
||||||
|
context.startService(
|
||||||
|
Intent(context, TrtcService::class.java)
|
||||||
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +109,7 @@ object AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ReloadAppState() {
|
fun ReloadAppState(context: Context) {
|
||||||
// 重置动态列表页面
|
// 重置动态列表页面
|
||||||
MomentViewModel.ResetModel()
|
MomentViewModel.ResetModel()
|
||||||
// 重置我的页面
|
// 重置我的页面
|
||||||
@@ -129,5 +133,13 @@ object AppState {
|
|||||||
// 重置关注通知页面
|
// 重置关注通知页面
|
||||||
IndexViewModel.ResetModel()
|
IndexViewModel.ResetModel()
|
||||||
UserId = null
|
UserId = null
|
||||||
|
|
||||||
|
// 关闭 TrtcService
|
||||||
|
val trtcService = Intent(
|
||||||
|
context,
|
||||||
|
TrtcService::class.java
|
||||||
|
)
|
||||||
|
context.stopService(trtcService)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
40
app/src/main/java/com/aiosman/riderpro/ChatState.kt
Normal file
40
app/src/main/java/com/aiosman/riderpro/ChatState.kt
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package com.aiosman.riderpro
|
||||||
|
|
||||||
|
import com.aiosman.riderpro.data.ChatService
|
||||||
|
import com.aiosman.riderpro.data.ChatServiceImpl
|
||||||
|
import com.aiosman.riderpro.entity.ChatNotification
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存一些关于聊天的状态
|
||||||
|
*/
|
||||||
|
object ChatState {
|
||||||
|
val chatService: ChatService = ChatServiceImpl()
|
||||||
|
var chatNotificationList = mutableListOf<ChatNotification>()
|
||||||
|
suspend fun getStrategyByTargetTrtcId(targetTrtcId: String): ChatNotification? {
|
||||||
|
// 先从缓存中查找
|
||||||
|
if (chatNotificationList.isNotEmpty()) {
|
||||||
|
chatNotificationList.forEach {
|
||||||
|
if (it.targetTrtcId == targetTrtcId) {
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 缓存中没有再从网络获取
|
||||||
|
chatService.getChatNotifications(targetTrtcId)?.let {
|
||||||
|
chatNotificationList.add(it)
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
// 存在未设置策略的情况
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun updateChatNotification(targetUserId: Int, strategy: String): ChatNotification {
|
||||||
|
val updatedData = chatService.updateChatNotification(targetUserId, strategy)
|
||||||
|
chatNotificationList = chatNotificationList.filter {
|
||||||
|
it.targetUserId != targetUserId
|
||||||
|
}.toMutableList().apply {
|
||||||
|
add(updatedData)
|
||||||
|
}
|
||||||
|
return updatedData
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,8 @@ import cn.jiguang.api.utils.JCollectionAuth
|
|||||||
import cn.jpush.android.api.JPushInterface
|
import cn.jpush.android.api.JPushInterface
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||||
|
import com.aiosman.riderpro.data.UserService
|
||||||
|
import com.aiosman.riderpro.data.UserServiceImpl
|
||||||
import com.aiosman.riderpro.ui.Navigation
|
import com.aiosman.riderpro.ui.Navigation
|
||||||
import com.aiosman.riderpro.ui.NavigationRoute
|
import com.aiosman.riderpro.ui.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.navigateToPost
|
import com.aiosman.riderpro.ui.navigateToPost
|
||||||
@@ -109,7 +111,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
// 处理带有 postId 的通知点击
|
// 处理带有 postId 的通知点击
|
||||||
val postId = intent.getStringExtra("POST_ID")
|
val postId = intent.getStringExtra("POST_ID")
|
||||||
var commentId = intent.getStringExtra("COMMENT_ID")
|
var commentId = intent.getStringExtra("COMMENT_ID")
|
||||||
var action = intent.getStringExtra("ACTION")
|
val action = intent.getStringExtra("ACTION")
|
||||||
if (action == "newFollow") {
|
if (action == "newFollow") {
|
||||||
navController.navigate(NavigationRoute.Followers.route)
|
navController.navigate(NavigationRoute.Followers.route)
|
||||||
return@Navigation
|
return@Navigation
|
||||||
@@ -118,6 +120,25 @@ class MainActivity : ComponentActivity() {
|
|||||||
navController.navigate(NavigationRoute.Followers.route)
|
navController.navigate(NavigationRoute.Followers.route)
|
||||||
return@Navigation
|
return@Navigation
|
||||||
}
|
}
|
||||||
|
if (action == "TRTC_NEW_MESSAGE") {
|
||||||
|
val userService:UserService = UserServiceImpl()
|
||||||
|
val sender = intent.getStringExtra("SENDER")
|
||||||
|
sender?.let {
|
||||||
|
scope.launch {
|
||||||
|
try {
|
||||||
|
val profile = userService.getUserProfileByTrtcUserId(it)
|
||||||
|
navController.navigate(NavigationRoute.Chat.route.replace(
|
||||||
|
"{id}",
|
||||||
|
profile.id.toString()
|
||||||
|
))
|
||||||
|
}catch (e:Exception){
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@Navigation
|
||||||
|
}
|
||||||
|
|
||||||
if (commentId == null) {
|
if (commentId == null) {
|
||||||
commentId = "0"
|
commentId = "0"
|
||||||
}
|
}
|
||||||
|
|||||||
129
app/src/main/java/com/aiosman/riderpro/TrtcService.kt
Normal file
129
app/src/main/java/com/aiosman/riderpro/TrtcService.kt
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package com.aiosman.riderpro
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import com.aiosman.riderpro.entity.ChatItem
|
||||||
|
import com.tencent.imsdk.v2.V2TIMAdvancedMsgListener
|
||||||
|
import com.tencent.imsdk.v2.V2TIMManager
|
||||||
|
import com.tencent.imsdk.v2.V2TIMMessage
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class TrtcService : Service() {
|
||||||
|
private var trtcMessageListener: V2TIMAdvancedMsgListener? = null
|
||||||
|
private val channelId = "chat_notification"
|
||||||
|
override fun onBind(intent: Intent?): IBinder? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
Log.d("TrtcService", "onStartCommand")
|
||||||
|
createNotificationChannel()
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
registerMessageListener(applicationContext)
|
||||||
|
}
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
Log.d("TrtcService", "onDestroy")
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
unRegisterMessageListener()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerMessageListener(context: Context) {
|
||||||
|
val scope = CoroutineScope(Dispatchers.IO)
|
||||||
|
trtcMessageListener = object : V2TIMAdvancedMsgListener() {
|
||||||
|
override fun onRecvNewMessage(msg: V2TIMMessage?) {
|
||||||
|
super.onRecvNewMessage(msg)
|
||||||
|
msg?.let {
|
||||||
|
if (MainActivityLifecycle.isForeground) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
scope.launch {
|
||||||
|
// 先获取通知策略
|
||||||
|
val notiStrategy = ChatState.getStrategyByTargetTrtcId(it.sender)
|
||||||
|
if (notiStrategy == null) {
|
||||||
|
// 未设置策略, 默认通知
|
||||||
|
sendNotification(context, it)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
if (notiStrategy.strategy != "mute") {
|
||||||
|
sendNotification(context, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
V2TIMManager.getMessageManager().addAdvancedMsgListener(trtcMessageListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unRegisterMessageListener() {
|
||||||
|
V2TIMManager.getMessageManager().removeAdvancedMsgListener(trtcMessageListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNotificationChannel() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val name = "Chat Notification"
|
||||||
|
val descriptionText = "Notification for chat message"
|
||||||
|
val importance = NotificationManager.IMPORTANCE_DEFAULT
|
||||||
|
val channel = NotificationChannel(channelId, name, importance).apply {
|
||||||
|
description = descriptionText
|
||||||
|
}
|
||||||
|
val notificationManager: NotificationManager =
|
||||||
|
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
notificationManager.createNotificationChannel(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendNotification(context: Context, message: V2TIMMessage) {
|
||||||
|
val intent = Intent(context, MainActivity::class.java).apply {
|
||||||
|
putExtra("ACTION", "TRTC_NEW_MESSAGE")
|
||||||
|
putExtra("SENDER", message.sender)
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
|
}
|
||||||
|
|
||||||
|
val pendingIntent: PendingIntent = PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
val chatItem = ChatItem.convertToChatItem(message, context) ?: return
|
||||||
|
val builder = NotificationCompat.Builder(context, channelId)
|
||||||
|
.setSmallIcon(R.mipmap.rider_pro_log_round)
|
||||||
|
.setContentTitle(chatItem.nickname)
|
||||||
|
.setContentText(chatItem.textDisplay)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
|
||||||
|
with(NotificationManagerCompat.from(context)) {
|
||||||
|
if (ActivityCompat.checkSelfPermission(
|
||||||
|
context,
|
||||||
|
Manifest.permission.POST_NOTIFICATIONS
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
notify(message.msgID.hashCode(), builder.build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
app/src/main/java/com/aiosman/riderpro/data/ChatService.kt
Normal file
42
app/src/main/java/com/aiosman/riderpro/data/ChatService.kt
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package com.aiosman.riderpro.data
|
||||||
|
|
||||||
|
import com.aiosman.riderpro.data.api.ApiClient
|
||||||
|
import com.aiosman.riderpro.data.api.UpdateChatNotificationRequestBody
|
||||||
|
import com.aiosman.riderpro.entity.ChatNotification
|
||||||
|
|
||||||
|
interface ChatService {
|
||||||
|
suspend fun getChatNotifications(
|
||||||
|
targetTrtcId: String
|
||||||
|
): ChatNotification?
|
||||||
|
|
||||||
|
suspend fun updateChatNotification(
|
||||||
|
targetUserId: Int,
|
||||||
|
strategy: String
|
||||||
|
): ChatNotification
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChatServiceImpl : ChatService {
|
||||||
|
override suspend fun getChatNotifications(
|
||||||
|
targetTrtcId: String
|
||||||
|
): ChatNotification? {
|
||||||
|
val resp = ApiClient.api.getChatNotification(targetTrtcId)
|
||||||
|
if (resp.isSuccessful) {
|
||||||
|
return resp.body()?.data
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun updateChatNotification(
|
||||||
|
targetUserId: Int,
|
||||||
|
strategy: String
|
||||||
|
): ChatNotification {
|
||||||
|
val resp = ApiClient.api.updateChatNotification(UpdateChatNotificationRequestBody(
|
||||||
|
targetUserId = targetUserId,
|
||||||
|
strategy = strategy
|
||||||
|
))
|
||||||
|
if (resp.isSuccessful) {
|
||||||
|
return resp.body()?.data!!
|
||||||
|
}
|
||||||
|
throw Exception("update chat notification failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import com.aiosman.riderpro.data.Comment
|
|||||||
import com.aiosman.riderpro.data.DataContainer
|
import com.aiosman.riderpro.data.DataContainer
|
||||||
import com.aiosman.riderpro.data.ListContainer
|
import com.aiosman.riderpro.data.ListContainer
|
||||||
import com.aiosman.riderpro.data.Moment
|
import com.aiosman.riderpro.data.Moment
|
||||||
|
import com.aiosman.riderpro.entity.ChatNotification
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
@@ -154,31 +155,6 @@ data class GenerateLoginCaptchaRequestBody(
|
|||||||
@SerializedName("username")
|
@SerializedName("username")
|
||||||
val username: String,
|
val username: String,
|
||||||
)
|
)
|
||||||
//{
|
|
||||||
// "id":48,
|
|
||||||
// "dot": [
|
|
||||||
// {
|
|
||||||
// "index": 0,
|
|
||||||
// "x": 76,
|
|
||||||
// "y": 165
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "index": 1,
|
|
||||||
// "x": 144,
|
|
||||||
// "y": 21
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "index": 2,
|
|
||||||
// "x": 220,
|
|
||||||
// "y": 42
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "index": 3,
|
|
||||||
// "x": 10,
|
|
||||||
// "y": 10
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
//}
|
|
||||||
data class DotPosition(
|
data class DotPosition(
|
||||||
@SerializedName("index")
|
@SerializedName("index")
|
||||||
val index: Int,
|
val index: Int,
|
||||||
@@ -193,6 +169,15 @@ data class CaptchaInfo(
|
|||||||
@SerializedName("dot")
|
@SerializedName("dot")
|
||||||
val dot: List<DotPosition>
|
val dot: List<DotPosition>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
data class UpdateChatNotificationRequestBody(
|
||||||
|
@SerializedName("targetUserId")
|
||||||
|
val targetUserId: Int,
|
||||||
|
@SerializedName("strategy")
|
||||||
|
val strategy: String,
|
||||||
|
)
|
||||||
|
|
||||||
interface RiderProAPI {
|
interface RiderProAPI {
|
||||||
@POST("register")
|
@POST("register")
|
||||||
suspend fun register(@Body body: RegisterRequestBody): Response<Unit>
|
suspend fun register(@Body body: RegisterRequestBody): Response<Unit>
|
||||||
@@ -417,4 +402,14 @@ interface RiderProAPI {
|
|||||||
@Body body: GenerateLoginCaptchaRequestBody
|
@Body body: GenerateLoginCaptchaRequestBody
|
||||||
): Response<DataContainer<CaptchaResponseBody>>
|
): Response<DataContainer<CaptchaResponseBody>>
|
||||||
|
|
||||||
|
@GET("chat/notification")
|
||||||
|
suspend fun getChatNotification(
|
||||||
|
@Query("targetTrtcId") targetTrtcId: String
|
||||||
|
): Response<DataContainer<ChatNotification>>
|
||||||
|
|
||||||
|
@POST("chat/notification")
|
||||||
|
suspend fun updateChatNotification(
|
||||||
|
@Body body: UpdateChatNotificationRequestBody
|
||||||
|
): Response<DataContainer<ChatNotification>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.aiosman.riderpro.entity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.icu.util.Calendar
|
import android.icu.util.Calendar
|
||||||
import com.aiosman.riderpro.exp.formatChatTime
|
import com.aiosman.riderpro.exp.formatChatTime
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.tencent.imsdk.v2.V2TIMImageElem
|
import com.tencent.imsdk.v2.V2TIMImageElem
|
||||||
import com.tencent.imsdk.v2.V2TIMMessage
|
import com.tencent.imsdk.v2.V2TIMMessage
|
||||||
|
|
||||||
@@ -15,11 +16,11 @@ data class ChatItem(
|
|||||||
val timeCategory: String = "",
|
val timeCategory: String = "",
|
||||||
val timestamp: Long = 0,
|
val timestamp: Long = 0,
|
||||||
val imageList: MutableList<V2TIMImageElem.V2TIMImage> = emptyList<V2TIMImageElem.V2TIMImage>().toMutableList(),
|
val imageList: MutableList<V2TIMImageElem.V2TIMImage> = emptyList<V2TIMImageElem.V2TIMImage>().toMutableList(),
|
||||||
val messageType : Int = 0,
|
val messageType: Int = 0,
|
||||||
val textDisplay : String = "",
|
val textDisplay: String = "",
|
||||||
val msgId: String, // Add this property
|
val msgId: String, // Add this property
|
||||||
var showTimestamp: Boolean = false,
|
var showTimestamp: Boolean = false,
|
||||||
var showTimeDivider:Boolean = false
|
var showTimeDivider: Boolean = false
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun convertToChatItem(message: V2TIMMessage, context: Context): ChatItem? {
|
fun convertToChatItem(message: V2TIMMessage, context: Context): ChatItem? {
|
||||||
@@ -74,8 +75,8 @@ data class ChatItem(
|
|||||||
textDisplay = message.textElem?.text ?: "Unsupported message type",
|
textDisplay = message.textElem?.text ?: "Unsupported message type",
|
||||||
msgId = message.msgID // Add this line to include msgId
|
msgId = message.msgID // Add this line to include msgId
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -85,3 +86,16 @@ data class ChatItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class ChatNotification(
|
||||||
|
@SerializedName("userId")
|
||||||
|
val userId: Int,
|
||||||
|
@SerializedName("userTrtcId")
|
||||||
|
val userTrtcId: String,
|
||||||
|
@SerializedName("targetUserId")
|
||||||
|
val targetUserId: Int,
|
||||||
|
@SerializedName("targetTrtcId")
|
||||||
|
val targetTrtcId: String,
|
||||||
|
@SerializedName("strategy")
|
||||||
|
val strategy: String
|
||||||
|
)
|
||||||
@@ -66,12 +66,15 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.R
|
import com.aiosman.riderpro.R
|
||||||
import com.aiosman.riderpro.entity.ChatItem
|
import com.aiosman.riderpro.entity.ChatItem
|
||||||
import com.aiosman.riderpro.exp.formatChatTime
|
import com.aiosman.riderpro.exp.formatChatTime
|
||||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||||
|
import com.aiosman.riderpro.ui.composables.DropdownMenu
|
||||||
|
import com.aiosman.riderpro.ui.composables.MenuItem
|
||||||
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
import com.tencent.imsdk.v2.V2TIMMessage
|
import com.tencent.imsdk.v2.V2TIMMessage
|
||||||
@@ -80,6 +83,7 @@ import kotlinx.coroutines.launch
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatScreen(userId: String) {
|
fun ChatScreen(userId: String) {
|
||||||
|
var isMenuExpanded by remember { mutableStateOf(false) }
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val context = LocalNavController.current.context
|
val context = LocalNavController.current.context
|
||||||
val viewModel = viewModel<ChatViewModel>(
|
val viewModel = viewModel<ChatViewModel>(
|
||||||
@@ -131,7 +135,7 @@ fun ChatScreen(userId: String) {
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
.padding(vertical = 16.dp, horizontal = 16.dp),
|
||||||
horizontalArrangement = Arrangement.Start,
|
horizontalArrangement = Arrangement.Start,
|
||||||
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically
|
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
@@ -154,6 +158,41 @@ fun ChatScreen(userId: String) {
|
|||||||
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold
|
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
Box {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.rider_pro_more_horizon),
|
||||||
|
modifier = Modifier
|
||||||
|
.size(28.dp)
|
||||||
|
.noRippleClickable {
|
||||||
|
isMenuExpanded = true
|
||||||
|
},
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = isMenuExpanded,
|
||||||
|
onDismissRequest = {
|
||||||
|
isMenuExpanded = false
|
||||||
|
},
|
||||||
|
menuItems = listOf(
|
||||||
|
MenuItem(
|
||||||
|
title = if (viewModel.notificationStrategy == "mute") "Unmute" else "Mute",
|
||||||
|
icon = if (viewModel.notificationStrategy == "mute") R.drawable.rider_pro_notice_mute else R.drawable.rider_pro_notice_active,
|
||||||
|
){
|
||||||
|
|
||||||
|
isMenuExpanded = false
|
||||||
|
viewModel.viewModelScope.launch {
|
||||||
|
if (viewModel.notificationStrategy == "mute") {
|
||||||
|
viewModel.updateNotificationStrategy("active")
|
||||||
|
} else {
|
||||||
|
viewModel.updateNotificationStrategy("mute")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.aiosman.riderpro.ui.chat
|
package com.aiosman.riderpro.ui.chat
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.icu.util.Calendar
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -11,16 +10,16 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.aiosman.riderpro.ChatState
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||||
import com.aiosman.riderpro.data.UserService
|
import com.aiosman.riderpro.data.UserService
|
||||||
import com.aiosman.riderpro.data.UserServiceImpl
|
import com.aiosman.riderpro.data.UserServiceImpl
|
||||||
import com.aiosman.riderpro.entity.AccountProfileEntity
|
import com.aiosman.riderpro.entity.AccountProfileEntity
|
||||||
import com.aiosman.riderpro.entity.ChatItem
|
import com.aiosman.riderpro.entity.ChatItem
|
||||||
import com.aiosman.riderpro.exp.formatChatTime
|
import com.aiosman.riderpro.entity.ChatNotification
|
||||||
import com.tencent.imsdk.v2.V2TIMAdvancedMsgListener
|
import com.tencent.imsdk.v2.V2TIMAdvancedMsgListener
|
||||||
import com.tencent.imsdk.v2.V2TIMCallback
|
import com.tencent.imsdk.v2.V2TIMCallback
|
||||||
import com.tencent.imsdk.v2.V2TIMImageElem
|
|
||||||
import com.tencent.imsdk.v2.V2TIMManager
|
import com.tencent.imsdk.v2.V2TIMManager
|
||||||
import com.tencent.imsdk.v2.V2TIMMessage
|
import com.tencent.imsdk.v2.V2TIMMessage
|
||||||
import com.tencent.imsdk.v2.V2TIMSendCallback
|
import com.tencent.imsdk.v2.V2TIMSendCallback
|
||||||
@@ -44,6 +43,7 @@ class ChatViewModel(
|
|||||||
var isLoading by mutableStateOf(false)
|
var isLoading by mutableStateOf(false)
|
||||||
var lastMessage: V2TIMMessage? = null
|
var lastMessage: V2TIMMessage? = null
|
||||||
val showTimestampMap = mutableMapOf<String, Boolean>() // Add this map
|
val showTimestampMap = mutableMapOf<String, Boolean>() // Add this map
|
||||||
|
var chatNotification by mutableStateOf<ChatNotification?>(null)
|
||||||
fun init(context: Context) {
|
fun init(context: Context) {
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -53,6 +53,9 @@ class ChatViewModel(
|
|||||||
|
|
||||||
RegistListener(context)
|
RegistListener(context)
|
||||||
fetchHistoryMessage(context)
|
fetchHistoryMessage(context)
|
||||||
|
// 获取通知信息
|
||||||
|
val notiStrategy = ChatState.getStrategyByTargetTrtcId(resp.trtcUserId)
|
||||||
|
chatNotification = notiStrategy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,4 +246,13 @@ class ChatViewModel(
|
|||||||
}
|
}
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun updateNotificationStrategy(strategy: String) {
|
||||||
|
userProfile?.let {
|
||||||
|
val result = ChatState.updateChatNotification(it.id, strategy)
|
||||||
|
chatNotification = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val notificationStrategy get() = chatNotification?.strategy ?: "default"
|
||||||
}
|
}
|
||||||
@@ -73,13 +73,13 @@ object MyProfileViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun logout() {
|
suspend fun logout(context: Context) {
|
||||||
AppStore.apply {
|
AppStore.apply {
|
||||||
token = null
|
token = null
|
||||||
rememberMe = false
|
rememberMe = false
|
||||||
saveData()
|
saveData()
|
||||||
}
|
}
|
||||||
AppState.ReloadAppState()
|
AppState.ReloadAppState(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateUserProfileBanner(bannerImageUrl: Uri?,file:File, context: Context) {
|
fun updateUserProfileBanner(bannerImageUrl: Uri?,file:File, context: Context) {
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ fun ProfileV3(
|
|||||||
color = Color.Black
|
color = Color.Black
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
if (isSelf) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
) {
|
) {
|
||||||
@@ -435,6 +436,8 @@ fun ProfileV3(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.aiosman.riderpro.ui.index.tabs.profile
|
|||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -9,29 +10,17 @@ import kotlinx.coroutines.launch
|
|||||||
fun ProfileWrap(
|
fun ProfileWrap(
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
MyProfileViewModel.loadProfile()
|
MyProfileViewModel.loadProfile()
|
||||||
}
|
}
|
||||||
// ProfileV2(
|
|
||||||
// onUpdateBanner = { uri, context ->
|
|
||||||
// MyProfileViewModel.updateUserProfileBanner(uri, context)
|
|
||||||
// },
|
|
||||||
// onLogout = {
|
|
||||||
// MyProfileViewModel.viewModelScope.launch {
|
|
||||||
// MyProfileViewModel.logout()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
// profile = MyProfileViewModel.profile,
|
|
||||||
// sharedFlow = MyProfileViewModel.sharedFlow
|
|
||||||
// )
|
|
||||||
ProfileV3(
|
ProfileV3(
|
||||||
onUpdateBanner = { uri, file, context ->
|
onUpdateBanner = { uri, file, context ->
|
||||||
MyProfileViewModel.updateUserProfileBanner(uri, file, context)
|
MyProfileViewModel.updateUserProfileBanner(uri, file, context)
|
||||||
},
|
},
|
||||||
onLogout = {
|
onLogout = {
|
||||||
MyProfileViewModel.viewModelScope.launch {
|
MyProfileViewModel.viewModelScope.launch {
|
||||||
MyProfileViewModel.logout()
|
MyProfileViewModel.logout(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
5
app/src/main/res/drawable/rider_pro_notice_active.xml
Normal file
5
app/src/main/res/drawable/rider_pro_notice_active.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:alpha="0.77" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM18,16v-5c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.64,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2zM16,17L8,17v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5v6z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/rider_pro_notice_mute.xml
Normal file
5
app/src/main/res/drawable/rider_pro_notice_mute.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:alpha="0.77" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM12,6.5c2.49,0 4,2.02 4,4.5v0.1l2,2L18,11c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68c-0.24,0.06 -0.47,0.15 -0.69,0.23l1.64,1.64c0.18,-0.02 0.36,-0.05 0.55,-0.05zM5.41,3.35L4,4.76l2.81,2.81C6.29,8.57 6,9.74 6,11v5l-2,2v1h14.24l1.74,1.74 1.41,-1.41L5.41,3.35zM16,17L8,17v-6c0,-0.68 0.12,-1.32 0.34,-1.9L16,16.76L16,17z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
Reference in New Issue
Block a user