新增图片发送
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
package com.aiosman.riderpro.ui.chat
|
package com.aiosman.riderpro.ui.chat
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
@@ -29,6 +34,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.Scaffold
|
import androidx.compose.material.Scaffold
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -66,6 +72,7 @@ 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.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 kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
@@ -158,7 +165,13 @@ fun ChatScreen(userId: String) {
|
|||||||
.background(Color(0xfff7f7f7))
|
.background(Color(0xfff7f7f7))
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
ChatInput() {
|
ChatInput(
|
||||||
|
onSendImage = {
|
||||||
|
it?.let {
|
||||||
|
viewModel.sendImageMessage(it, context)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
viewModel.sendMessage(it, context)
|
viewModel.sendMessage(it, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,17 +243,35 @@ fun ChatSelfItem(item: ChatItem) {
|
|||||||
.padding(vertical = 8.dp, horizontal = 16.dp)
|
.padding(vertical = 8.dp, horizontal = 16.dp)
|
||||||
.padding(bottom = 3.dp)
|
.padding(bottom = 3.dp)
|
||||||
) {
|
) {
|
||||||
|
when (item.messageType) {
|
||||||
|
V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> {
|
||||||
Text(
|
Text(
|
||||||
text = item.message,
|
text = item.message,
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
),
|
),
|
||||||
textAlign = TextAlign.Start,
|
textAlign = TextAlign.Start
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
V2TIMMessage.V2TIM_ELEM_TYPE_IMAGE -> {
|
||||||
|
CustomAsyncImage(
|
||||||
|
imageUrl = item.imageList[1].url,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentDescription = "image"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Text(
|
||||||
|
text = "Unsupported message type",
|
||||||
|
style = TextStyle(
|
||||||
|
color = Color.White,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
Box(
|
Box(
|
||||||
@@ -308,14 +339,35 @@ fun ChatOtherItem(item: ChatItem) {
|
|||||||
.padding(vertical = 8.dp, horizontal = 16.dp)
|
.padding(vertical = 8.dp, horizontal = 16.dp)
|
||||||
.padding(bottom = 3.dp)
|
.padding(bottom = 3.dp)
|
||||||
) {
|
) {
|
||||||
|
when (item.messageType) {
|
||||||
|
V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> {
|
||||||
Text(
|
Text(
|
||||||
text = item.message,
|
text = item.message,
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
color = Color.Black,
|
color = Color.Black,
|
||||||
fontSize = 16.sp
|
fontSize = 16.sp,
|
||||||
|
),
|
||||||
|
textAlign = TextAlign.Start
|
||||||
|
)
|
||||||
|
}
|
||||||
|
V2TIMMessage.V2TIM_ELEM_TYPE_IMAGE -> {
|
||||||
|
CustomAsyncImage(
|
||||||
|
imageUrl = item.imageList[1].url,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentDescription = "image"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Text(
|
||||||
|
text = "Unsupported message type",
|
||||||
|
style = TextStyle(
|
||||||
|
color = Color.White,
|
||||||
|
fontSize = 16.sp,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,7 +387,8 @@ fun ChatItem(item: ChatItem, currentUserId: String) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatInput(
|
fun ChatInput(
|
||||||
onSend: (String) -> Unit = {}
|
onSendImage: (Uri?) -> Unit = {},
|
||||||
|
onSend: (String) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val navigationBarHeight = with(LocalDensity.current) {
|
val navigationBarHeight = with(LocalDensity.current) {
|
||||||
WindowInsets.navigationBars.getBottom(this).toDp()
|
WindowInsets.navigationBars.getBottom(this).toDp()
|
||||||
@@ -367,6 +420,15 @@ fun ChatInput(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val imagePickUpLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.StartActivityForResult()
|
||||||
|
) {
|
||||||
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
|
val uri = it.data?.data
|
||||||
|
onSendImage(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -413,6 +475,24 @@ fun ChatInput(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.rider_pro_images),
|
||||||
|
contentDescription = "Emoji",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(32.dp)
|
||||||
|
.noRippleClickable {
|
||||||
|
imagePickUpLauncher.launch(
|
||||||
|
Intent.createChooser(
|
||||||
|
Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
|
type = "image/*"
|
||||||
|
},
|
||||||
|
"Select Image"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
tint = Color(0xff000000)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
Crossfade(targetState = text.isNotEmpty(), animationSpec = tween(500)) { isNotEmpty ->
|
Crossfade(targetState = text.isNotEmpty(), animationSpec = tween(500)) { isNotEmpty ->
|
||||||
Image(
|
Image(
|
||||||
painter = rememberUpdatedState(
|
painter = rememberUpdatedState(
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ package com.aiosman.riderpro.ui.chat
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.icu.util.Calendar
|
import android.icu.util.Calendar
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.MediaStore
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -16,11 +19,15 @@ import com.aiosman.riderpro.entity.AccountProfileEntity
|
|||||||
import com.aiosman.riderpro.exp.formatChatTime
|
import com.aiosman.riderpro.exp.formatChatTime
|
||||||
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
|
||||||
import com.tencent.imsdk.v2.V2TIMValueCallback
|
import com.tencent.imsdk.v2.V2TIMValueCallback
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
|
||||||
data class ChatItem(
|
data class ChatItem(
|
||||||
@@ -30,7 +37,10 @@ data class ChatItem(
|
|||||||
val userId: String,
|
val userId: String,
|
||||||
val nickname: String,
|
val nickname: String,
|
||||||
val timeCategory: String = "",
|
val timeCategory: String = "",
|
||||||
val timestamp: Long = 0
|
val timestamp: Long = 0,
|
||||||
|
val imageList: MutableList<V2TIMImageElem.V2TIMImage> = emptyList<V2TIMImageElem.V2TIMImage>().toMutableList(),
|
||||||
|
val messageType : Int = 0,
|
||||||
|
val textDisplay : String = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
class ChatViewModel(
|
class ChatViewModel(
|
||||||
@@ -44,7 +54,7 @@ class ChatViewModel(
|
|||||||
var textMessageListener: V2TIMAdvancedMsgListener? = null
|
var textMessageListener: V2TIMAdvancedMsgListener? = null
|
||||||
var hasMore by mutableStateOf(true)
|
var hasMore by mutableStateOf(true)
|
||||||
var isLoading by mutableStateOf(false)
|
var isLoading by mutableStateOf(false)
|
||||||
var lastMessage : V2TIMMessage? = null
|
var lastMessage: V2TIMMessage? = null
|
||||||
fun init(context: Context) {
|
fun init(context: Context) {
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -61,7 +71,12 @@ class ChatViewModel(
|
|||||||
textMessageListener = object : V2TIMAdvancedMsgListener() {
|
textMessageListener = object : V2TIMAdvancedMsgListener() {
|
||||||
override fun onRecvNewMessage(msg: V2TIMMessage?) {
|
override fun onRecvNewMessage(msg: V2TIMMessage?) {
|
||||||
super.onRecvNewMessage(msg)
|
super.onRecvNewMessage(msg)
|
||||||
chatData = listOf(convertToChatItem(msg!!, context)) + chatData
|
msg?.let {
|
||||||
|
val chatItem = convertToChatItem(msg, context)
|
||||||
|
chatItem?.let {
|
||||||
|
chatData = listOf(it) + chatData
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
V2TIMManager.getMessageManager().addAdvancedMsgListener(textMessageListener);
|
V2TIMManager.getMessageManager().addAdvancedMsgListener(textMessageListener);
|
||||||
@@ -70,7 +85,8 @@ class ChatViewModel(
|
|||||||
fun UnRegistListener() {
|
fun UnRegistListener() {
|
||||||
V2TIMManager.getMessageManager().removeAdvancedMsgListener(textMessageListener);
|
V2TIMManager.getMessageManager().removeAdvancedMsgListener(textMessageListener);
|
||||||
}
|
}
|
||||||
fun clearUnRead(){
|
|
||||||
|
fun clearUnRead() {
|
||||||
val conversationID = "c2c_${userProfile?.trtcUserId}"
|
val conversationID = "c2c_${userProfile?.trtcUserId}"
|
||||||
V2TIMManager.getConversationManager()
|
V2TIMManager.getConversationManager()
|
||||||
.cleanConversationUnreadMessageCount(conversationID, 0, 0, object : V2TIMCallback {
|
.cleanConversationUnreadMessageCount(conversationID, 0, 0, object : V2TIMCallback {
|
||||||
@@ -83,7 +99,8 @@ class ChatViewModel(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fun convertToChatItem(message: V2TIMMessage, context: Context): ChatItem {
|
|
||||||
|
fun convertToChatItem(message: V2TIMMessage, context: Context): ChatItem? {
|
||||||
val avatar = if (message.sender == userProfile?.trtcUserId) {
|
val avatar = if (message.sender == userProfile?.trtcUserId) {
|
||||||
userProfile?.avatar ?: ""
|
userProfile?.avatar ?: ""
|
||||||
} else {
|
} else {
|
||||||
@@ -97,16 +114,51 @@ class ChatViewModel(
|
|||||||
val timestamp = message.timestamp
|
val timestamp = message.timestamp
|
||||||
val calendar = Calendar.getInstance()
|
val calendar = Calendar.getInstance()
|
||||||
calendar.timeInMillis = timestamp * 1000
|
calendar.timeInMillis = timestamp * 1000
|
||||||
|
val imageElm = message.imageElem?.imageList
|
||||||
|
when (message.elemType) {
|
||||||
|
V2TIMMessage.V2TIM_ELEM_TYPE_IMAGE -> {
|
||||||
|
val imageElm = message.imageElem?.imageList?.all {
|
||||||
|
it.size == 0
|
||||||
|
}
|
||||||
|
if (imageElm != true) {
|
||||||
return ChatItem(
|
return ChatItem(
|
||||||
message = message.textElem.text,
|
message = "Image",
|
||||||
avatar = avatar,
|
avatar = avatar,
|
||||||
time = calendar.time.formatChatTime(context),
|
time = calendar.time.formatChatTime(context),
|
||||||
userId = message.sender,
|
userId = message.sender,
|
||||||
nickname = nickname,
|
nickname = nickname,
|
||||||
timestamp = timestamp * 1000
|
timestamp = timestamp * 1000,
|
||||||
|
imageList = message.imageElem?.imageList
|
||||||
|
?: emptyList<V2TIMImageElem.V2TIMImage>().toMutableList(),
|
||||||
|
messageType = V2TIMMessage.V2TIM_ELEM_TYPE_IMAGE,
|
||||||
|
textDisplay = "Image"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> {
|
||||||
|
return ChatItem(
|
||||||
|
message = message.textElem?.text ?: "Unsupported message type",
|
||||||
|
avatar = avatar,
|
||||||
|
time = calendar.time.formatChatTime(context),
|
||||||
|
userId = message.sender,
|
||||||
|
nickname = nickname,
|
||||||
|
timestamp = timestamp * 1000,
|
||||||
|
imageList = imageElm?.toMutableList()
|
||||||
|
?: emptyList<V2TIMImageElem.V2TIMImage>().toMutableList(),
|
||||||
|
messageType = V2TIMMessage.V2TIM_ELEM_TYPE_TEXT,
|
||||||
|
textDisplay = message.textElem?.text ?: "Unsupported message type"
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onLoadMore(context: Context) {
|
fun onLoadMore(context: Context) {
|
||||||
if (!hasMore || isLoading) {
|
if (!hasMore || isLoading) {
|
||||||
@@ -122,7 +174,7 @@ class ChatViewModel(
|
|||||||
override fun onSuccess(p0: List<V2TIMMessage>?) {
|
override fun onSuccess(p0: List<V2TIMMessage>?) {
|
||||||
chatData = chatData + (p0 ?: emptyList()).map {
|
chatData = chatData + (p0 ?: emptyList()).map {
|
||||||
convertToChatItem(it, context)
|
convertToChatItem(it, context)
|
||||||
}
|
}.filterNotNull()
|
||||||
if ((p0?.size ?: 0) < 20) {
|
if ((p0?.size ?: 0) < 20) {
|
||||||
hasMore = false
|
hasMore = false
|
||||||
}
|
}
|
||||||
@@ -130,6 +182,7 @@ class ChatViewModel(
|
|||||||
isLoading = false
|
isLoading = false
|
||||||
Log.d("ChatViewModel", "fetch history message success")
|
Log.d("ChatViewModel", "fetch history message success")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(p0: Int, p1: String?) {
|
override fun onError(p0: Int, p1: String?) {
|
||||||
Log.e("ChatViewModel", "fetch history message error: $p1")
|
Log.e("ChatViewModel", "fetch history message error: $p1")
|
||||||
isLoading = false
|
isLoading = false
|
||||||
@@ -147,18 +200,85 @@ class ChatViewModel(
|
|||||||
override fun onProgress(p0: Int) {
|
override fun onProgress(p0: Int) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(p0: Int, p1: String?) {
|
override fun onError(p0: Int, p1: String?) {
|
||||||
Log.e("ChatViewModel", "send message error: $p1")
|
Log.e("ChatViewModel", "send message error: $p1")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess(p0: V2TIMMessage?) {
|
override fun onSuccess(p0: V2TIMMessage?) {
|
||||||
Log.d("ChatViewModel", "send message success")
|
Log.d("ChatViewModel", "send message success")
|
||||||
chatData = listOf(convertToChatItem(p0!!, context)) + chatData
|
val chatItem = convertToChatItem(p0!!, context)
|
||||||
|
chatItem?.let {
|
||||||
|
chatData = listOf(it) + chatData
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendImageMessage(imageUri:Uri, context: Context) {
|
||||||
|
val tempFile = createTempFile(context, imageUri)
|
||||||
|
val imagePath = tempFile?.path
|
||||||
|
if (imagePath != null) {
|
||||||
|
val v2TIMMessage = V2TIMManager.getMessageManager().createImageMessage(imagePath)
|
||||||
|
V2TIMManager.getMessageManager().sendMessage(
|
||||||
|
v2TIMMessage,
|
||||||
|
userProfile?.trtcUserId!!,
|
||||||
|
null,
|
||||||
|
V2TIMMessage.V2TIM_PRIORITY_NORMAL,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
object : V2TIMSendCallback<V2TIMMessage> {
|
||||||
|
override fun onProgress(p0: Int) {
|
||||||
|
Log.d("ChatViewModel", "send image message progress: $p0")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(p0: Int, p1: String?) {
|
||||||
|
Log.e("ChatViewModel", "send image message error: $p1")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(p0: V2TIMMessage?) {
|
||||||
|
Log.d("ChatViewModel", "send image message success")
|
||||||
|
val chatItem = convertToChatItem(p0!!, context)
|
||||||
|
chatItem?.let {
|
||||||
|
chatData = listOf(it) + chatData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
fun createTempFile(context: Context, uri: Uri): File? {
|
||||||
|
return try {
|
||||||
|
val projection = arrayOf(MediaStore.Images.Media.DATA)
|
||||||
|
val cursor = context.contentResolver.query(uri, projection, null, null, null)
|
||||||
|
cursor?.use {
|
||||||
|
if (it.moveToFirst()) {
|
||||||
|
val columnIndex = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
||||||
|
val filePath = it.getString(columnIndex)
|
||||||
|
val inputStream: InputStream? = context.contentResolver.openInputStream(uri)
|
||||||
|
val mimeType = context.contentResolver.getType(uri)
|
||||||
|
val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)
|
||||||
|
val tempFile = File.createTempFile("temp_image", ".$extension", context.cacheDir)
|
||||||
|
val outputStream = FileOutputStream(tempFile)
|
||||||
|
|
||||||
|
inputStream?.use { input ->
|
||||||
|
outputStream.use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tempFile
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun fetchHistoryMessage(context: Context) {
|
fun fetchHistoryMessage(context: Context) {
|
||||||
V2TIMManager.getMessageManager().getC2CHistoryMessageList(
|
V2TIMManager.getMessageManager().getC2CHistoryMessageList(
|
||||||
userProfile?.trtcUserId!!,
|
userProfile?.trtcUserId!!,
|
||||||
@@ -168,7 +288,7 @@ class ChatViewModel(
|
|||||||
override fun onSuccess(p0: List<V2TIMMessage>?) {
|
override fun onSuccess(p0: List<V2TIMMessage>?) {
|
||||||
chatData = (p0 ?: emptyList()).map {
|
chatData = (p0 ?: emptyList()).map {
|
||||||
convertToChatItem(it, context)
|
convertToChatItem(it, context)
|
||||||
}
|
}.filterNotNull()
|
||||||
if ((p0?.size ?: 0) < 20) {
|
if ((p0?.size ?: 0) < 20) {
|
||||||
hasMore = false
|
hasMore = false
|
||||||
}
|
}
|
||||||
@@ -182,6 +302,7 @@ class ChatViewModel(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDisplayChatList(): List<ChatItem> {
|
fun getDisplayChatList(): List<ChatItem> {
|
||||||
return chatData
|
return chatData
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -21,7 +20,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.material.CircularProgressIndicator
|
import androidx.compose.material.CircularProgressIndicator
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -46,7 +44,7 @@ import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
|||||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||||
import com.aiosman.riderpro.ui.imageviewer.ImageViewerViewModel
|
import com.aiosman.riderpro.ui.imageviewer.ImageViewerViewModel
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
import com.aiosman.riderpro.utils.File.saveImageToGallery
|
import com.aiosman.riderpro.utils.FileUtil.saveImageToGallery
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import net.engawapg.lib.zoomable.rememberZoomState
|
import net.engawapg.lib.zoomable.rememberZoomState
|
||||||
import net.engawapg.lib.zoomable.zoomable
|
import net.engawapg.lib.zoomable.zoomable
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ fun ChatMessageList(
|
|||||||
Spacer(modifier = Modifier.height(6.dp))
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
Row {
|
Row {
|
||||||
Text(
|
Text(
|
||||||
text = item.lastMessage,
|
text = "${if (item.isSelf) "Me: " else ""}${item.displayText}",
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
color = Color(0x99000000),
|
color = Color(0x99000000),
|
||||||
|
|||||||
@@ -9,32 +9,25 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.paging.Pager
|
|
||||||
import androidx.paging.PagingConfig
|
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import androidx.paging.cachedIn
|
|
||||||
import androidx.paging.map
|
import androidx.paging.map
|
||||||
import com.aiosman.riderpro.data.AccountNotice
|
import com.aiosman.riderpro.data.AccountNotice
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.entity.CommentEntity
|
import com.aiosman.riderpro.entity.CommentEntity
|
||||||
import com.aiosman.riderpro.entity.CommentPagingSource
|
|
||||||
import com.aiosman.riderpro.data.CommentRemoteDataSource
|
|
||||||
import com.aiosman.riderpro.data.CommentService
|
|
||||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||||
import com.aiosman.riderpro.data.CommentServiceImpl
|
|
||||||
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.exp.formatChatTime
|
import com.aiosman.riderpro.exp.formatChatTime
|
||||||
import com.aiosman.riderpro.ui.NavigationRoute
|
import com.aiosman.riderpro.ui.NavigationRoute
|
||||||
|
import com.aiosman.riderpro.ui.index.tabs.profile.MyProfileViewModel
|
||||||
import com.aiosman.riderpro.ui.navigateToChat
|
import com.aiosman.riderpro.ui.navigateToChat
|
||||||
import com.aiosman.riderpro.utils.TrtcHelper
|
import com.aiosman.riderpro.utils.TrtcHelper
|
||||||
import com.tencent.imsdk.v2.V2TIMConversation
|
import com.tencent.imsdk.v2.V2TIMConversation
|
||||||
import com.tencent.imsdk.v2.V2TIMConversationResult
|
import com.tencent.imsdk.v2.V2TIMConversationResult
|
||||||
import com.tencent.imsdk.v2.V2TIMManager
|
import com.tencent.imsdk.v2.V2TIMManager
|
||||||
|
import com.tencent.imsdk.v2.V2TIMMessage
|
||||||
import com.tencent.imsdk.v2.V2TIMValueCallback
|
import com.tencent.imsdk.v2.V2TIMValueCallback
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
@@ -44,7 +37,9 @@ data class Conversation(
|
|||||||
val lastMessage: String,
|
val lastMessage: String,
|
||||||
val lastMessageTime: String,
|
val lastMessageTime: String,
|
||||||
val avatar: String = "",
|
val avatar: String = "",
|
||||||
val unreadCount: Int = 0
|
val unreadCount: Int = 0,
|
||||||
|
val displayText: String,
|
||||||
|
val isSelf: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
object MessageListViewModel : ViewModel() {
|
object MessageListViewModel : ViewModel() {
|
||||||
@@ -142,13 +137,24 @@ object MessageListViewModel : ViewModel() {
|
|||||||
timeInMillis = msg.lastMessage?.timestamp ?: 0
|
timeInMillis = msg.lastMessage?.timestamp ?: 0
|
||||||
timeInMillis *= 1000
|
timeInMillis *= 1000
|
||||||
}
|
}
|
||||||
|
var displayText = ""
|
||||||
|
when (msg.lastMessage?.elemType) {
|
||||||
|
V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> {
|
||||||
|
displayText = msg.lastMessage?.textElem?.text ?: ""
|
||||||
|
}
|
||||||
|
V2TIMMessage.V2TIM_ELEM_TYPE_IMAGE -> {
|
||||||
|
displayText = "[图片]"
|
||||||
|
}
|
||||||
|
}
|
||||||
Conversation(
|
Conversation(
|
||||||
nickname = msg.showName,
|
nickname = msg.showName,
|
||||||
lastMessage = msg.lastMessage?.textElem?.text ?: "",
|
lastMessage = msg.lastMessage?.textElem?.text ?: "",
|
||||||
lastMessageTime = lastMessage.time.formatChatTime(context),
|
lastMessageTime = lastMessage.time.formatChatTime(context),
|
||||||
avatar = msg.faceUrl,
|
avatar = msg.faceUrl,
|
||||||
unreadCount = msg.unreadCount,
|
unreadCount = msg.unreadCount,
|
||||||
trtcUserId = msg.userID
|
trtcUserId = msg.userID,
|
||||||
|
displayText = displayText,
|
||||||
|
isSelf = msg.lastMessage.sender == MyProfileViewModel.profile?.trtcUserId
|
||||||
)
|
)
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.aiosman.riderpro.utils
|
|||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.database.Cursor
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@@ -9,7 +10,6 @@ import android.os.Build
|
|||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import coil.ImageLoader
|
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import coil.request.SuccessResult
|
import coil.request.SuccessResult
|
||||||
import com.aiosman.riderpro.utils.Utils.getImageLoader
|
import com.aiosman.riderpro.utils.Utils.getImageLoader
|
||||||
@@ -18,7 +18,7 @@ import kotlinx.coroutines.withContext
|
|||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
object File {
|
object FileUtil {
|
||||||
suspend fun saveImageToGallery(context: Context, url: String) {
|
suspend fun saveImageToGallery(context: Context, url: String) {
|
||||||
val loader = getImageLoader(context)
|
val loader = getImageLoader(context)
|
||||||
|
|
||||||
@@ -89,4 +89,17 @@ object File {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getRealPathFromUri(context: Context, uri: Uri): String? {
|
||||||
|
var realPath: String? = null
|
||||||
|
val projection = arrayOf(MediaStore.Images.Media.DATA)
|
||||||
|
val cursor: Cursor? = context.contentResolver.query(uri, projection, null, null, null)
|
||||||
|
cursor?.use {
|
||||||
|
if (it.moveToFirst()) {
|
||||||
|
val columnIndex = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
||||||
|
realPath = it.getString(columnIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return realPath
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user