问题修正
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.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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.draw.drawBehind
|
||||||
import androidx.compose.ui.geometry.CornerRadius
|
import androidx.compose.ui.geometry.CornerRadius
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
|
||||||
import androidx.compose.ui.graphics.PathEffect
|
import androidx.compose.ui.graphics.PathEffect
|
||||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
@@ -232,16 +230,18 @@ fun AddImageGrid() {
|
|||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val model = NewPostViewModel
|
val model = NewPostViewModel
|
||||||
|
val scope = model.viewModelScope
|
||||||
val pickImagesLauncher = rememberLauncherForActivityResult(
|
val pickImagesLauncher = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.GetMultipleContents()
|
contract = ActivityResultContracts.GetMultipleContents()
|
||||||
) { uris ->
|
) { uris ->
|
||||||
if (uris.isNotEmpty()) {
|
if (uris.isNotEmpty()) {
|
||||||
model.imageList += uris.map {
|
scope.launch {
|
||||||
ImageItem(
|
for (uri in uris) {
|
||||||
uri = it.toString(),
|
ImageItem.fromUri(context, uri.toString())?.let {
|
||||||
id = java.util.UUID.randomUUID().toString()
|
model.imageList += it
|
||||||
)
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,10 +250,12 @@ fun AddImageGrid() {
|
|||||||
contract = ActivityResultContracts.TakePicture()
|
contract = ActivityResultContracts.TakePicture()
|
||||||
) { success ->
|
) { success ->
|
||||||
if (success) {
|
if (success) {
|
||||||
model.imageList += ImageItem(
|
scope.launch {
|
||||||
uri = model.currentPhotoUri.toString(),
|
ImageItem.fromUri(context, model.currentPhotoUri.toString())?.let {
|
||||||
id = java.util.UUID.randomUUID().toString()
|
model.imageList += it
|
||||||
)
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +285,7 @@ fun AddImageGrid() {
|
|||||||
) {
|
) {
|
||||||
CustomAsyncImage(
|
CustomAsyncImage(
|
||||||
LocalContext.current,
|
LocalContext.current,
|
||||||
item.uri,
|
item.bitmap,
|
||||||
contentDescription = "Image",
|
contentDescription = "Image",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -320,7 +322,8 @@ fun AddImageGrid() {
|
|||||||
val strokeWidth = 1.dp.toPx()
|
val strokeWidth = 1.dp.toPx()
|
||||||
val dashLength = 10f
|
val dashLength = 10f
|
||||||
val dashGap = 10f
|
val dashGap = 10f
|
||||||
val pathEffect = PathEffect.dashPathEffect(floatArrayOf(dashLength, dashGap))
|
val pathEffect =
|
||||||
|
PathEffect.dashPathEffect(floatArrayOf(dashLength, dashGap))
|
||||||
drawRoundRect(
|
drawRoundRect(
|
||||||
color = Color(0xFFD6D6D6),
|
color = Color(0xFFD6D6D6),
|
||||||
style = Stroke(strokeWidth, pathEffect = pathEffect),
|
style = Stroke(strokeWidth, pathEffect = pathEffect),
|
||||||
@@ -351,7 +354,8 @@ fun AddImageGrid() {
|
|||||||
val strokeWidth = 1.dp.toPx()
|
val strokeWidth = 1.dp.toPx()
|
||||||
val dashLength = 10f
|
val dashLength = 10f
|
||||||
val dashGap = 10f
|
val dashGap = 10f
|
||||||
val pathEffect = PathEffect.dashPathEffect(floatArrayOf(dashLength, dashGap))
|
val pathEffect =
|
||||||
|
PathEffect.dashPathEffect(floatArrayOf(dashLength, dashGap))
|
||||||
drawRoundRect(
|
drawRoundRect(
|
||||||
color = Color(0xFFD6D6D6),
|
color = Color(0xFFD6D6D6),
|
||||||
style = Stroke(strokeWidth, pathEffect = pathEffect),
|
style = Stroke(strokeWidth, pathEffect = pathEffect),
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ fun NewPostImageGridScreen() {
|
|||||||
) { page ->
|
) { page ->
|
||||||
val imageUrl = imageList[page]
|
val imageUrl = imageList[page]
|
||||||
Image(
|
Image(
|
||||||
painter = rememberAsyncImagePainter(model = imageUrl),
|
painter = rememberAsyncImagePainter(model = imageUrl.bitmap),
|
||||||
contentDescription = "Image $page",
|
contentDescription = "Image $page",
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.aiosman.riderpro.ui.post
|
package com.aiosman.riderpro.ui.post
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.media.ExifInterface
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.getValue
|
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.entity.MomentServiceImpl
|
||||||
import com.aiosman.riderpro.data.UploadImage
|
import com.aiosman.riderpro.data.UploadImage
|
||||||
import com.aiosman.riderpro.entity.MomentEntity
|
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.moment.MomentViewModel
|
||||||
import com.aiosman.riderpro.ui.index.tabs.profile.MyProfileViewModel
|
import com.aiosman.riderpro.ui.index.tabs.profile.MyProfileViewModel
|
||||||
import com.aiosman.riderpro.ui.modification.Modification
|
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.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
data class ImageItem(
|
data class ImageItem(
|
||||||
val uri: String,
|
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() {
|
object NewPostViewModel : ViewModel() {
|
||||||
var momentService: MomentService = MomentServiceImpl()
|
var momentService: MomentService = MomentServiceImpl()
|
||||||
@@ -42,13 +95,14 @@ object NewPostViewModel : ViewModel() {
|
|||||||
imageList = listOf()
|
imageList = listOf()
|
||||||
relPostId = null
|
relPostId = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asNewPostWithImageUris(imageUris: List<String>) {
|
fun asNewPostWithImageUris(imageUris: List<String>) {
|
||||||
textContent = ""
|
textContent = ""
|
||||||
searchPlaceAddressResult = null
|
searchPlaceAddressResult = null
|
||||||
modificationList = listOf()
|
modificationList = listOf()
|
||||||
imageList = imageUris.map {
|
// imageList = imageUris.map {
|
||||||
ImageItem(it, UUID.randomUUID().toString())
|
// ImageItem(it, UUID.randomUUID().toString())
|
||||||
}
|
// }
|
||||||
relPostId = null
|
relPostId = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,22 +125,34 @@ object NewPostViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun createMoment(context: Context, onUploadProgress: (Float) -> Unit) {
|
suspend fun createMoment(context: Context, onUploadProgress: (Float) -> Unit) {
|
||||||
val uploadImageList = emptyList<UploadImage>().toMutableList()
|
val uploadImageList = emptyList<UploadImage>().toMutableList()
|
||||||
var index = 0
|
var index = 0
|
||||||
for (item in imageList) {
|
for (item in imageList) {
|
||||||
val cursor = context.contentResolver.query(Uri.parse(item.uri), null, null, null, null)
|
val cursor = context.contentResolver.query(Uri.parse(item.uri), null, null, null, null)
|
||||||
cursor?.use {
|
// cursor?.use {
|
||||||
if (it.moveToFirst()) {
|
// if (it.moveToFirst()) {
|
||||||
val displayName = it.getString(it.getColumnIndex("_display_name"))
|
// val columnIndex = it.getColumnIndex("_display_name")
|
||||||
val extension = displayName.substringAfterLast(".")
|
// if (columnIndex != -1) {
|
||||||
Log.d("NewPost", "File name: $displayName, extension: $extension")
|
// val displayName = it.getString(columnIndex)
|
||||||
// read as file
|
// val extension = displayName.substringAfterLast(".")
|
||||||
val file = uriToFile(context, Uri.parse(item.uri))
|
// Log.d("NewPost", "File name: $displayName, extension: $extension")
|
||||||
Log.d("NewPost", "File size: ${file.length()}")
|
// // read as file
|
||||||
uploadImageList += UploadImage(file, displayName, item.uri, extension)
|
// 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 更新进度
|
||||||
onUploadProgress(((index / imageList.size).toFloat())) // progressValue 是当前上传进度,例如 0.5 表示 50%
|
onUploadProgress(((index / imageList.size).toFloat())) // progressValue 是当前上传进度,例如 0.5 表示 50%
|
||||||
index += 1
|
index += 1
|
||||||
@@ -95,6 +161,7 @@ object NewPostViewModel : ViewModel() {
|
|||||||
// 刷新个人动态
|
// 刷新个人动态
|
||||||
MyProfileViewModel.loadProfile(pullRefresh = true)
|
MyProfileViewModel.loadProfile(pullRefresh = true)
|
||||||
MomentViewModel.refreshPager()
|
MomentViewModel.refreshPager()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun init() {
|
suspend fun init() {
|
||||||
@@ -103,6 +170,7 @@ object NewPostViewModel : ViewModel() {
|
|||||||
relMoment = moment
|
relMoment = moment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteImage(index: Int) {
|
fun deleteImage(index: Int) {
|
||||||
imageList = imageList.toMutableList().apply {
|
imageList = imageList.toMutableList().apply {
|
||||||
removeAt(index)
|
removeAt(index)
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ import coil.request.SuccessResult
|
|||||||
import com.aiosman.riderpro.utils.Utils.getImageLoader
|
import com.aiosman.riderpro.utils.Utils.getImageLoader
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
object FileUtil {
|
object FileUtil {
|
||||||
@@ -102,4 +105,17 @@ object FileUtil {
|
|||||||
return realPath
|
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