dm-40
This commit is contained in:
parent
2d1b15da7b
commit
57e546f733
2 changed files with 49 additions and 14 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
package com.example.login_v3.data.repository.dm
|
package com.example.login_v3.data.repository.dm
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
import com.example.login_v3.data.api.TecnologiaApi
|
import com.example.login_v3.data.api.TecnologiaApi
|
||||||
|
|
@ -13,6 +15,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
@ -130,10 +133,10 @@ class ChatRoomsRepository @Inject constructor(
|
||||||
//upload file
|
//upload file
|
||||||
suspend fun uploadAttachment(roomId: String, fileUri: Uri): Result<Message> {
|
suspend fun uploadAttachment(roomId: String, fileUri: Uri): Result<Message> {
|
||||||
return try {
|
return try {
|
||||||
// 1. 透過 ContentResolver 取得檔案的遮罩名稱與大小
|
|
||||||
val contentResolver = context.contentResolver
|
val contentResolver = context.contentResolver
|
||||||
var fileName = "upload_file"
|
var fileName = "upload_file"
|
||||||
|
|
||||||
|
// 1. 透過 ContentResolver 取得檔案的名稱
|
||||||
contentResolver.query(fileUri, null, null, null, null)?.use { cursor ->
|
contentResolver.query(fileUri, null, null, null, null)?.use { cursor ->
|
||||||
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||||
if (nameIndex != -1 && cursor.moveToFirst()) {
|
if (nameIndex != -1 && cursor.moveToFirst()) {
|
||||||
|
|
@ -141,15 +144,51 @@ class ChatRoomsRepository @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 取得檔案的 MIME 類型 (例如 image/jpeg, image/png)
|
// 2. 取得原始 MIME 類型
|
||||||
val mimeType = contentResolver.getType(fileUri) ?: "application/octet-stream"
|
val rawMimeType = contentResolver.getType(fileUri) ?: "application/octet-stream"
|
||||||
|
|
||||||
// 3. 讀取檔案 Byte 陣列並轉換為 RequestBody
|
val fileBytes: ByteArray
|
||||||
|
val finalMimeType: String
|
||||||
|
|
||||||
|
// ✨【關鍵轉檔邏輯】:如果是 HEIC/HEIF,在前端直接壓成 JPG
|
||||||
|
if (rawMimeType == "image/heic" || rawMimeType == "image/heif") {
|
||||||
val inputStream = contentResolver.openInputStream(fileUri)
|
val inputStream = contentResolver.openInputStream(fileUri)
|
||||||
?: return Result.failure(Exception("無法開啟檔案輸入流 (InputStream is null)"))
|
?: return Result.failure(Exception("無法開啟檔案輸入流"))
|
||||||
|
|
||||||
val fileBytes = inputStream.use { it.readBytes() }
|
// 利用 BitmapFactory 解碼,Android 系統會自動幫你把 HEIC 解成標準 Bitmap
|
||||||
val requestBody = fileBytes.toRequestBody(mimeType.toMediaTypeOrNull())
|
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||||
|
inputStream.close()
|
||||||
|
|
||||||
|
if (bitmap != null) {
|
||||||
|
val outputStream = ByteArrayOutputStream()
|
||||||
|
// 壓縮成 JPEG 格式,品質設為 85-90(兼顧畫質與檔案大小)
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream)
|
||||||
|
fileBytes = outputStream.toByteArray()
|
||||||
|
outputStream.close()
|
||||||
|
bitmap.recycle() // 釋放記憶體
|
||||||
|
|
||||||
|
finalMimeType = "image/jpeg"
|
||||||
|
|
||||||
|
// 修改副檔名為 .jpg,讓後端正確識別儲存
|
||||||
|
val dotIndex = fileName.lastIndexOf(".")
|
||||||
|
fileName = if (dotIndex != -1) {
|
||||||
|
"${fileName.substring(0, dotIndex)}.jpg"
|
||||||
|
} else {
|
||||||
|
"$fileName.jpg"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Result.failure(Exception("HEIC 圖片解碼失敗"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 非 HEIC 檔案(普通的 JPG, PNG 等),走原本的邏輯,直接讀取原始 Byte
|
||||||
|
val inputStream = contentResolver.openInputStream(fileUri)
|
||||||
|
?: return Result.failure(Exception("無法開啟檔案輸入流"))
|
||||||
|
fileBytes = inputStream.use { it.readBytes() }
|
||||||
|
finalMimeType = rawMimeType
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 轉換為 RequestBody
|
||||||
|
val requestBody = fileBytes.toRequestBody(finalMimeType.toMediaTypeOrNull())
|
||||||
|
|
||||||
// 4. 打包成 MultipartBody.Part (後端接收的 Key 值設定為 "file")
|
// 4. 打包成 MultipartBody.Part (後端接收的 Key 值設定為 "file")
|
||||||
val multipartBody = MultipartBody.Part.createFormData("file", fileName, requestBody)
|
val multipartBody = MultipartBody.Part.createFormData("file", fileName, requestBody)
|
||||||
|
|
@ -171,6 +210,5 @@ class ChatRoomsRepository @Inject constructor(
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -97,18 +97,15 @@ fun MessageMessaging(
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✨ 監聽發送狀態,如果失敗了就彈出提示並重置狀態
|
// ✨ 監聽發送狀態,如果失敗了就彈出提示並重置狀態
|
||||||
|
// 修改後的 LaunchedEffect
|
||||||
LaunchedEffect(sendStatus) {
|
LaunchedEffect(sendStatus) {
|
||||||
if (sendStatus is SendMessageState.Error) {
|
if (sendStatus is SendMessageState.Error) {
|
||||||
val errorMsg = (sendStatus as SendMessageState.Error).message
|
val errorMsg = (sendStatus as SendMessageState.Error).message
|
||||||
Toast.makeText(context, "發送失敗: $errorMsg", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "發送失敗: $errorMsg", Toast.LENGTH_SHORT).show()
|
||||||
viewModel.resetSendMessageState()
|
viewModel.resetSendMessageState()
|
||||||
} else if (sendStatus is SendMessageState.Success) {
|
} else if (sendStatus is SendMessageState.Success) {
|
||||||
// 發送成功後,重置狀態以便下一次發送
|
// ✨ 發送成功後,只重置狀態。讓 ViewModel 的 uiState 自動推播新訊息給 UI 即可!
|
||||||
viewModel.resetSendMessageState()
|
viewModel.resetSendMessageState()
|
||||||
|
|
||||||
// 【優化體驗】:你可以在這裡重新呼叫 viewModel.loadMessages(roomId) 刷新列表
|
|
||||||
// 或者如果你的 ViewModel 已經會自動把新訊息塞進 uiState,這裡就什麼都不用做。
|
|
||||||
viewModel.loadMessages(roomId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue