问题修正
This commit is contained in:
9
app/src/main/java/com/aiosman/riderpro/exp/Bitmap.kt
Normal file
9
app/src/main/java/com/aiosman/riderpro/exp/Bitmap.kt
Normal file
@@ -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)
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -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<String>) {
|
||||
textContent = ""
|
||||
searchPlaceAddressResult = null
|
||||
modificationList = listOf()
|
||||
imageList = imageUris.map {
|
||||
ImageItem(it, UUID.randomUUID().toString())
|
||||
}
|
||||
// imageList = imageUris.map {
|
||||
// ImageItem(it, UUID.randomUUID().toString())
|
||||
// }
|
||||
relPostId = null
|
||||
}
|
||||
|
||||
@@ -71,20 +125,32 @@ object NewPostViewModel : ViewModel() {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun createMoment(context: Context, onUploadProgress: (Float) -> Unit) {
|
||||
val uploadImageList = emptyList<UploadImage>().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 更新进度
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user