From dad032e23308ade3142b72bdf094081d401695cd Mon Sep 17 00:00:00 2001 From: AllenTom Date: Wed, 23 Oct 2024 20:32:32 +0800 Subject: [PATCH] =?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/aiosman/riderpro/exp/Bitmap.kt | 9 ++ .../com/aiosman/riderpro/ui/post/NewPost.kt | 34 ++++--- .../riderpro/ui/post/NewPostImageGrid.kt | 2 +- .../riderpro/ui/post/NewPostViewModel.kt | 98 ++++++++++++++++--- .../com/aiosman/riderpro/utils/FileUtil.kt | 16 +++ 5 files changed, 128 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/com/aiosman/riderpro/exp/Bitmap.kt diff --git a/app/src/main/java/com/aiosman/riderpro/exp/Bitmap.kt b/app/src/main/java/com/aiosman/riderpro/exp/Bitmap.kt new file mode 100644 index 0000000..61bf27e --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/exp/Bitmap.kt @@ -0,0 +1,9 @@ +package com.aiosman.riderpro.exp + +import android.graphics.Bitmap + +fun Bitmap.rotate(degree: Int): Bitmap { + val matrix = android.graphics.Matrix() + matrix.postRotate(degree.toFloat()) + return Bitmap.createBitmap(this, 0, 0, this.width, this.height, matrix, true) +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/ui/post/NewPost.kt b/app/src/main/java/com/aiosman/riderpro/ui/post/NewPost.kt index b3b9186..5bb17c8 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/post/NewPost.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/post/NewPost.kt @@ -7,7 +7,6 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement @@ -45,7 +44,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.PathEffect import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.layout.ContentScale @@ -232,16 +230,18 @@ fun AddImageGrid() { val navController = LocalNavController.current val context = LocalContext.current val model = NewPostViewModel - + val scope = model.viewModelScope val pickImagesLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.GetMultipleContents() ) { uris -> if (uris.isNotEmpty()) { - model.imageList += uris.map { - ImageItem( - uri = it.toString(), - id = java.util.UUID.randomUUID().toString() - ) + scope.launch { + for (uri in uris) { + ImageItem.fromUri(context, uri.toString())?.let { + model.imageList += it + + } + } } } } @@ -250,10 +250,12 @@ fun AddImageGrid() { contract = ActivityResultContracts.TakePicture() ) { success -> if (success) { - model.imageList += ImageItem( - uri = model.currentPhotoUri.toString(), - id = java.util.UUID.randomUUID().toString() - ) + scope.launch { + ImageItem.fromUri(context, model.currentPhotoUri.toString())?.let { + model.imageList += it + } + + } } } @@ -283,7 +285,7 @@ fun AddImageGrid() { ) { CustomAsyncImage( LocalContext.current, - item.uri, + item.bitmap, contentDescription = "Image", modifier = Modifier .fillMaxWidth() @@ -320,7 +322,8 @@ fun AddImageGrid() { val strokeWidth = 1.dp.toPx() val dashLength = 10f val dashGap = 10f - val pathEffect = PathEffect.dashPathEffect(floatArrayOf(dashLength, dashGap)) + val pathEffect = + PathEffect.dashPathEffect(floatArrayOf(dashLength, dashGap)) drawRoundRect( color = Color(0xFFD6D6D6), style = Stroke(strokeWidth, pathEffect = pathEffect), @@ -351,7 +354,8 @@ fun AddImageGrid() { val strokeWidth = 1.dp.toPx() val dashLength = 10f val dashGap = 10f - val pathEffect = PathEffect.dashPathEffect(floatArrayOf(dashLength, dashGap)) + val pathEffect = + PathEffect.dashPathEffect(floatArrayOf(dashLength, dashGap)) drawRoundRect( color = Color(0xFFD6D6D6), style = Stroke(strokeWidth, pathEffect = pathEffect), diff --git a/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostImageGrid.kt b/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostImageGrid.kt index 5fc09a2..e6669c2 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostImageGrid.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostImageGrid.kt @@ -103,7 +103,7 @@ fun NewPostImageGridScreen() { ) { page -> val imageUrl = imageList[page] Image( - painter = rememberAsyncImagePainter(model = imageUrl), + painter = rememberAsyncImagePainter(model = imageUrl.bitmap), contentDescription = "Image $page", modifier = Modifier.fillMaxSize() ) diff --git a/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostViewModel.kt index 960eb77..d48036b 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostViewModel.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostViewModel.kt @@ -1,6 +1,9 @@ package com.aiosman.riderpro.ui.post import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.media.ExifInterface import android.net.Uri import android.util.Log import androidx.compose.runtime.getValue @@ -11,20 +14,70 @@ import com.aiosman.riderpro.data.MomentService import com.aiosman.riderpro.entity.MomentServiceImpl import com.aiosman.riderpro.data.UploadImage import com.aiosman.riderpro.entity.MomentEntity +import com.aiosman.riderpro.exp.rotate import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel import com.aiosman.riderpro.ui.index.tabs.profile.MyProfileViewModel import com.aiosman.riderpro.ui.modification.Modification +import com.aiosman.riderpro.utils.FileUtil +import com.aiosman.riderpro.utils.Utils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.File import java.io.FileOutputStream +import java.io.IOException import java.io.InputStream import java.util.UUID data class ImageItem( val uri: String, - val id: String -) + val id: String, + val bitmap: Bitmap, + val file: File +) { + companion object { + suspend fun fromUri(context: Context,uri: String): ImageItem? { + // 保存图片文件到临时文件夹 + context.contentResolver.openInputStream(Uri.parse(uri))?.use { inputStream -> + val tempFileName = UUID.randomUUID().toString() + val tempFile = File.createTempFile(tempFileName, null, context.cacheDir) + FileOutputStream(tempFile).use { outputStream -> + inputStream.copyTo(outputStream) + } + + // 读取图片文件为 Bitmap + var bitmap = BitmapFactory.decodeFile(tempFile.absolutePath) + // 读取文件exif,修正旋转 + val exif = ExifInterface(tempFile.absolutePath) + bitmap = when (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)) { + ExifInterface.ORIENTATION_ROTATE_90 -> bitmap.rotate(90) + ExifInterface.ORIENTATION_ROTATE_180 -> bitmap.rotate(180) + ExifInterface.ORIENTATION_ROTATE_270 -> bitmap.rotate(270) + else -> bitmap + } + // 保存 bitmap 到临时文件夹 + try { + val savedBitmapFilename = UUID.randomUUID().toString() + val bitmapFile = File.createTempFile(savedBitmapFilename, ".jpg", context.cacheDir) + FileOutputStream(bitmapFile).use { os -> + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os) + } + // 清理临时文件 + tempFile.delete() + return ImageItem( + Uri.fromFile(bitmapFile).toString(), + savedBitmapFilename, + bitmap, + bitmapFile + ) + } catch (e: IOException) { + Log.e("NewPost", "Failed to save bitmap to file", e) + null + } + } + return null + } + } +} object NewPostViewModel : ViewModel() { var momentService: MomentService = MomentServiceImpl() @@ -42,13 +95,14 @@ object NewPostViewModel : ViewModel() { imageList = listOf() relPostId = null } + fun asNewPostWithImageUris(imageUris: List) { textContent = "" searchPlaceAddressResult = null modificationList = listOf() - imageList = imageUris.map { - ImageItem(it, UUID.randomUUID().toString()) - } +// imageList = imageUris.map { +// ImageItem(it, UUID.randomUUID().toString()) +// } relPostId = null } @@ -71,22 +125,34 @@ object NewPostViewModel : ViewModel() { } return null } + suspend fun createMoment(context: Context, onUploadProgress: (Float) -> Unit) { val uploadImageList = emptyList().toMutableList() var index = 0 for (item in imageList) { val cursor = context.contentResolver.query(Uri.parse(item.uri), null, null, null, null) - cursor?.use { - if (it.moveToFirst()) { - val displayName = it.getString(it.getColumnIndex("_display_name")) - val extension = displayName.substringAfterLast(".") - Log.d("NewPost", "File name: $displayName, extension: $extension") - // read as file - val file = uriToFile(context, Uri.parse(item.uri)) - Log.d("NewPost", "File size: ${file.length()}") - uploadImageList += UploadImage(file, displayName, item.uri, extension) +// cursor?.use { +// if (it.moveToFirst()) { +// val columnIndex = it.getColumnIndex("_display_name") +// if (columnIndex != -1) { +// val displayName = it.getString(columnIndex) +// val extension = displayName.substringAfterLast(".") +// Log.d("NewPost", "File name: $displayName, extension: $extension") +// // read as file +// val file = uriToFile(context, Uri.parse(item.uri)) +// Log.d("NewPost", "File size: ${file.length()}") +// uploadImageList += UploadImage(file, displayName, item.uri, extension) +// } +// } +// } + // 保存图片到本地 + FileUtil.bitmapToJPG(context, item.bitmap, UUID.randomUUID().toString()) + ?.let { savedImageUri -> + // 读取保存的图片文件 + uriToFile(context, savedImageUri).let { file -> + uploadImageList += UploadImage(file, file.name, item.uri, "jpg") + } } - } // 在上传过程中调用 onUploadProgress 更新进度 onUploadProgress(((index / imageList.size).toFloat())) // progressValue 是当前上传进度,例如 0.5 表示 50% index += 1 @@ -95,6 +161,7 @@ object NewPostViewModel : ViewModel() { // 刷新个人动态 MyProfileViewModel.loadProfile(pullRefresh = true) MomentViewModel.refreshPager() + } suspend fun init() { @@ -103,6 +170,7 @@ object NewPostViewModel : ViewModel() { relMoment = moment } } + fun deleteImage(index: Int) { imageList = imageList.toMutableList().apply { removeAt(index) diff --git a/app/src/main/java/com/aiosman/riderpro/utils/FileUtil.kt b/app/src/main/java/com/aiosman/riderpro/utils/FileUtil.kt index 4895e88..8b415a4 100644 --- a/app/src/main/java/com/aiosman/riderpro/utils/FileUtil.kt +++ b/app/src/main/java/com/aiosman/riderpro/utils/FileUtil.kt @@ -15,7 +15,10 @@ import coil.request.SuccessResult import com.aiosman.riderpro.utils.Utils.getImageLoader import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import java.io.File import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.io.IOException import java.io.OutputStream object FileUtil { @@ -102,4 +105,17 @@ object FileUtil { return realPath } + suspend fun bitmapToJPG(context: Context, bitmap: Bitmap, displayName: String): Uri? { + return withContext(Dispatchers.IO) { + try { + val tempFile = File.createTempFile(displayName, ".jpg", context.cacheDir) + FileOutputStream(tempFile).use { os -> + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os) + } + Uri.fromFile(tempFile) + } catch (e: IOException) { + null + } + } + } } \ No newline at end of file