添加注释和日志,添加自采添加物品添加后删除图片

This commit is contained in:
zxj
2025-07-14 11:07:04 +08:00
parent f1283ba11e
commit ab55bb0fdc
9 changed files with 103 additions and 176 deletions
@@ -6,9 +6,66 @@ import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import androidx.annotation.RequiresApi
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import timber.log.Timber
import java.io.File
object FileUtils {
/**
* 从Uri获取File
* example: file:///data/user/0/com.sw.inbound/cache/IMG_17515262353556856678814444882273.jpg
*/
private fun getFileFromUri(context: Context, uri: Uri): File? {
Timber.d("getFileFromUri uri = ${uri.scheme}")
return when (uri.scheme) {
"file" -> File(uri.path ?: return null)
"content" -> {
try {
val inputStream = context.contentResolver.openInputStream(uri) ?: return null
val cacheDir = context.cacheDir
val file = File.createTempFile(
"upload_${System.currentTimeMillis()}",
".jpg",
cacheDir
)
file.outputStream().use { output ->
inputStream.copyTo(output)
}
file
} catch (e: Exception) {
Timber.e(e)
null
}
}
else -> null
}
}
/**
* 通过uri生成http请求体
*/
fun genRequestPart(context: Context, imageUri: Uri): MultipartBody.Part? {
Timber.d("genRequestPart imageUri = $imageUri")
// 1. 从Uri获取文件
val file = getFileFromUri(context, imageUri)
if (file == null) {
Timber.e("getFileFromUri file is null")
return null
}
// 2. 创建请求体
val requestFile = file
.asRequestBody("application/octet-stream".toMediaTypeOrNull())
val imagePart = MultipartBody.Part.createFormData(
"file",
file.name,
requestFile
)
return imagePart
}
/**
* 通过Uri删除文件
* @param context 上下文
@@ -16,6 +73,7 @@ object FileUtils {
* @return Boolean 是否删除成功
*/
fun deleteFileWithUri(context: Context, uri: Uri): Boolean {
Timber.d("deleteFileWithUri uri = ${uri.scheme}")
return when {
// 1. 处理 content:// 类型的Uri (MediaStore)
uri.scheme.equals("content", ignoreCase = true) -> {
@@ -34,6 +92,7 @@ object FileUtils {
// 删除Content Uri文件
private fun deleteContentUriFile(context: Context, uri: Uri): Boolean {
Timber.d("deleteContentUriFile uri = ${uri.scheme}")
return try {
context.contentResolver.delete(uri, null, null) > 0
} catch (e: SecurityException) {
@@ -44,6 +103,7 @@ object FileUtils {
false
}
} catch (e: Exception) {
Timber.e(e)
false
}
}
@@ -51,6 +111,7 @@ object FileUtils {
// Android 10+删除MediaStore文件
@RequiresApi(Build.VERSION_CODES.Q)
private fun deleteMediaStoreFile(context: Context, uri: Uri): Boolean {
Timber.d("deleteMediaStoreFile uri = ${uri.scheme}")
val contentResolver = context.contentResolver
val projection = arrayOf(MediaStore.MediaColumns._ID)
@@ -66,24 +127,29 @@ object FileUtils {
}
} ?: false
} catch (e: Exception) {
Timber.e(e)
false
}
}
// 删除File Uri文件
private fun deleteFileUriFile(uri: Uri): Boolean {
Timber.d("deleteFileUriFile uri = $uri")
return try {
File(uri.path ?: return false).delete()
} catch (e: Exception) {
Timber.e(e)
false
}
}
// 直接通过路径删除文件
private fun deleteFileFromPath(path: String): Boolean {
Timber.d("deleteFileFromPath path = $path")
return try {
File(path).delete()
} catch (e: Exception) {
Timber.e(e)
false
}
}
@@ -1,61 +0,0 @@
package com.sw.inbound.utils
import android.content.Context
import android.net.Uri
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import timber.log.Timber
import java.io.File
/**
* file:///data/user/0/com.sw.inbound/cache/IMG_17515262353556856678814444882273.jpg
*/
object ImageUtils {
// 从Uri获取File
private fun getFileFromUri(context: Context, uri: Uri): File? {
return when (uri.scheme) {
"file" -> File(uri.path ?: return null)
"content" -> {
try {
val inputStream = context.contentResolver.openInputStream(uri) ?: return null
val cacheDir = context.cacheDir
val file = File.createTempFile(
"upload_${System.currentTimeMillis()}",
".jpg",
cacheDir
)
file.outputStream().use { output ->
inputStream.copyTo(output)
}
file
} catch (e: Exception) {
null
}
}
else -> null
}
}
fun genRequestPart(context: Context, imageUri: Uri): MultipartBody.Part? {
Timber.d("genRequestPart imageUri = $imageUri")
// 1. 从Uri获取文件
val file = getFileFromUri(context, imageUri)
if (file == null) {
Timber.e("getFileFromUri file is null")
return null
}
// 2. 创建请求体
val requestFile = RequestBody.create(
"application/octet-stream".toMediaTypeOrNull(),
file
)
val imagePart = MultipartBody.Part.createFormData(
"file",
file.name,
requestFile
)
return imagePart
}
}
@@ -1,105 +0,0 @@
package com.sw.inbound.utils
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
open class TextFieldState(
initialText: String = "",
initialSelection: TextRange = TextRange(initialText.length)
) {
private var _value by mutableStateOf(TextFieldValue(initialText, initialSelection))
open var value: TextFieldValue
get() = _value
set(newValue) {
_value = newValue
}
open fun updateFromString(text: String) {
_value = TextFieldValue(text, TextRange(text.length))
}
val text: String get() = _value.text
}
class AmountFieldState(initialAmount: String = "") : TextFieldState(initialAmount) {
// 获取原始数字字符串(不含格式字符)
fun getRawAmount(): String = value.text.filter { it.isDigit() }
// 获取Double类型的金额值
fun getAmountValue(): Double = value.text
.filter { it.isDigit() || it == '.' }
.toDoubleOrNull() ?: 0.0
// 重写value的setter以实现金额格式化
override var value: TextFieldValue
get() = super.value
set(newValue) {
super.value = formatAmountValue(newValue)
}
// 从外部更新金额(如从数据库加载)
override fun updateFromString(amount: String) {
super.updateFromString(formatAmount(amount))
}
private fun formatAmountValue(input: TextFieldValue): TextFieldValue {
val filtered = input.text.filter { it.isDigit() }
val formatted = formatAmount(filtered)
// 计算新光标位置
val newCursorPos = calculateNewCursorPosition(
originalText = input.text,
originalSelection = input.selection,
filteredText = filtered,
formattedText = formatted
)
return TextFieldValue(
text = formatted,
selection = TextRange(newCursorPos)
)
}
private fun formatAmount(amount: String): String {
val filtered = amount.filter { it.isDigit() }
return when {
filtered.isEmpty() -> "0.00"
filtered.length <= 2 -> "0.${filtered.padStart(2, '0')}"
else -> "${filtered.dropLast(2)}.${filtered.takeLast(2)}"
}
}
private fun calculateNewCursorPosition(
originalText: String,
originalSelection: TextRange,
filteredText: String,
formattedText: String
): Int {
// 如果在末尾添加,保持光标在末尾
if (originalSelection.start >= originalText.length) {
return formattedText.length
}
// 计算原始文本中光标前的数字个数
val digitsBeforeCursor = originalText
.substring(0, originalSelection.start)
.count { it.isDigit() }
// 在格式化文本中找到对应位置
var digitCount = 0
formattedText.forEachIndexed { index, char ->
if (char.isDigit()) {
digitCount++
if (digitCount > digitsBeforeCursor) {
return index
}
}
}
return formattedText.length
}
}