优化SettingActivity底部菜单按钮效果:选中状态显示白色字体和圆角下划线
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package com.sw.inbound.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
|
||||
|
||||
object AssetsTool {
|
||||
|
||||
// fun readJson(context: Context, fileName: String): List<String> {
|
||||
// val json = readAssetsFile(context, fileName)
|
||||
// val json2 = json.replace("[[", "[").replaceAfterLast("]]", "]")
|
||||
// val list = json2.split("],")
|
||||
// val resultList = mutableListOf<String>()
|
||||
// var count = 0
|
||||
// val tempList = mutableListOf<String>()
|
||||
// list.forEachIndexed { index, text ->
|
||||
// if (count >= 500) {
|
||||
// val tempJson = "[${tempList.joinToString (",")}]"
|
||||
// resultList.add(tempJson)
|
||||
// tempList.clear()
|
||||
// count = 0
|
||||
// }
|
||||
// val newText = if (index == list.size - 1) text else "${text}]"
|
||||
// tempList.add(newText)
|
||||
// count++
|
||||
// }
|
||||
// if (count < 500) {
|
||||
// val tempJson = "[${tempList.joinToString(",")}]"
|
||||
// resultList.add(tempJson)
|
||||
// }
|
||||
// return resultList
|
||||
// }
|
||||
|
||||
fun readAssetsFile(context: Context, fileName: String): String {
|
||||
val stringBuilder = StringBuilder()
|
||||
try {
|
||||
val bf = BufferedReader(InputStreamReader(context.assets.open(fileName)))
|
||||
bf.useLines { lines -> lines.forEach { stringBuilder.append(it) } }
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return stringBuilder.toString()
|
||||
}
|
||||
|
||||
fun loadImagesFromAssets(context: Context, subPath: String): MutableList<Bitmap> {
|
||||
val bitmaps: MutableList<Bitmap> = mutableListOf()
|
||||
val assetManager = context.assets
|
||||
|
||||
try {
|
||||
val files = assetManager.list(subPath)
|
||||
files?.forEach { file ->
|
||||
assetManager.open("$subPath/$file").use { `is` ->
|
||||
val bitmap = BitmapFactory.decodeStream(`is`)
|
||||
if (bitmap != null) {
|
||||
bitmaps.add(bitmap)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return bitmaps
|
||||
}
|
||||
|
||||
fun loadImageBitmapFromAssets(context: Context, imagePath:String, action:(bmp: Bitmap)-> Unit) {
|
||||
context.assets.open(imagePath).use { `is` ->
|
||||
val bitmap = BitmapFactory.decodeStream(`is`)
|
||||
action(bitmap)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.sw.dualscreen.utils
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Environment
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
object BitmapSaver {
|
||||
// 保存到公共目录(需WRITE_EXTERNAL_STORAGE权限)
|
||||
fun saveToPublicDirectory(
|
||||
bitmap: Bitmap,
|
||||
folderName: String = Environment.DIRECTORY_PICTURES,
|
||||
fileName: String,
|
||||
format: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG,
|
||||
quality: Int = 100
|
||||
): File? {
|
||||
val dir = Environment.getExternalStoragePublicDirectory(folderName)
|
||||
if (!dir.exists()) dir.mkdirs()
|
||||
|
||||
return saveBitmap(bitmap, File(dir, fileName), format, quality)
|
||||
}
|
||||
|
||||
// 保存到应用私有目录(无需权限)
|
||||
fun saveToAppFilesDir(
|
||||
bitmap: Bitmap,
|
||||
context: Context,
|
||||
fileName: String,
|
||||
format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
|
||||
quality: Int = 100
|
||||
): File? {
|
||||
//val dir = context.getExternalFilesDir(null)
|
||||
val dir = File(context.cacheDir, "crop")
|
||||
if (dir.exists().not()) {
|
||||
dir.mkdirs()
|
||||
}
|
||||
return saveBitmap(bitmap, File(dir, fileName), format, quality)
|
||||
}
|
||||
|
||||
private fun saveBitmap(
|
||||
bitmap: Bitmap,
|
||||
outputFile: File,
|
||||
format: Bitmap.CompressFormat,
|
||||
quality: Int
|
||||
): File? {
|
||||
return try {
|
||||
FileOutputStream(outputFile).use { fos ->
|
||||
bitmap.compress(format, quality, fos)
|
||||
fos.flush()
|
||||
}
|
||||
outputFile
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.sw.dualscreen.utils
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.camera.core.CameraSelector
|
||||
import androidx.camera.view.CameraController
|
||||
import androidx.camera.view.LifecycleCameraController
|
||||
import androidx.camera.view.PreviewView
|
||||
|
||||
class CameraUtils(private var activity: ComponentActivity) {
|
||||
|
||||
private var cameraController: LifecycleCameraController? = null
|
||||
private var photoCaptureHelper: PhotoCaptureHelper? = null
|
||||
private var failCallback: ((msg: String) -> Unit)? = null
|
||||
|
||||
// private var isCameraReady = false
|
||||
fun takePhoto(succCallback: (Uri) -> Unit, failCallback: (msg: String) -> Unit = { }) {
|
||||
this.failCallback = failCallback
|
||||
cameraController?.let {
|
||||
if (photoCaptureHelper == null) {
|
||||
initCaptureHelper()
|
||||
}
|
||||
}
|
||||
photoCaptureHelper?.let {
|
||||
it.addSuccessCallback(succCallback)
|
||||
it.bindCameraCallback {
|
||||
bind()
|
||||
}
|
||||
it.takePhoto()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initCaptureHelper() {
|
||||
photoCaptureHelper = PhotoCaptureHelper(
|
||||
context = activity,
|
||||
cameraController = cameraController!!,
|
||||
onSuccess = {},
|
||||
onError = { msg ->
|
||||
//toast(msg)
|
||||
failCallback?.invoke(msg)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun setPreviewController(previewView: PreviewView?) {
|
||||
if (previewView?.controller == null) {
|
||||
previewView?.controller = cameraController
|
||||
}
|
||||
}
|
||||
|
||||
fun initCamera() {
|
||||
if (cameraController == null) {
|
||||
cameraController = LifecycleCameraController(activity).apply {
|
||||
// 必须设置有效的用例
|
||||
setEnabledUseCases(
|
||||
CameraController.IMAGE_CAPTURE
|
||||
// or CameraController.VIDEO_CAPTURE
|
||||
)
|
||||
cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
||||
}
|
||||
bind()
|
||||
}
|
||||
|
||||
// if (isCameraReady.not()) {
|
||||
// try {
|
||||
// cameraController!!.initializationFuture.addListener({
|
||||
// isCameraReady = true
|
||||
// Timber.d("Camera initialized successfully")
|
||||
// }, ContextCompat.getMainExecutor(this))
|
||||
// } catch (e: Exception) {
|
||||
// Timber.d("Camera initialized error = ${e.message}")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fun bind() {
|
||||
cameraController?.bindToLifecycle(activity)
|
||||
}
|
||||
|
||||
fun unbind() {
|
||||
cameraController?.unbind()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.sw.dualscreen.utils
|
||||
|
||||
/**
|
||||
* 防抖工具
|
||||
*/
|
||||
class Debouncer(private val delayMillis: Long = 2000) {
|
||||
private var lastActionTime = 0L
|
||||
|
||||
/**
|
||||
* 执行防抖操作
|
||||
* @param action 要执行的操作
|
||||
* @return Boolean 是否执行了操作 (true=已执行, false=被防抖)
|
||||
*/
|
||||
fun debounce(action: () -> Unit): Boolean {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastActionTime >= delayMillis) {
|
||||
lastActionTime = currentTime
|
||||
action()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 重置防抖计时
|
||||
fun reset() {
|
||||
lastActionTime = 0L
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package com.sw.dualscreen.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.RequestBuilder
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.load.resource.bitmap.CircleCrop
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Glide图片加载工具类
|
||||
*/
|
||||
object GlideUtils {
|
||||
|
||||
// 默认配置
|
||||
private val defaultOptions = RequestOptions()
|
||||
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
|
||||
.skipMemoryCache(false)
|
||||
|
||||
/**
|
||||
* 普通加载图片
|
||||
* @param context 上下文
|
||||
* @param url 图片地址
|
||||
* @param imageView 目标ImageView
|
||||
* @param placeholderResId 占位图资源ID
|
||||
* @param errorResId 错误图资源ID
|
||||
*/
|
||||
fun loadImage(
|
||||
context: Context,
|
||||
url: Any?,
|
||||
imageView: ImageView,
|
||||
@DrawableRes placeholderResId: Int = 0,
|
||||
@DrawableRes errorResId: Int = 0
|
||||
) {
|
||||
val requestBuilder = Glide.with(context)
|
||||
.load(url)
|
||||
.apply(defaultOptions)
|
||||
|
||||
applyPlaceholder(requestBuilder, placeholderResId, errorResId)
|
||||
.transition(DrawableTransitionOptions.withCrossFade())
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载圆形图片
|
||||
* @param context 上下文
|
||||
* @param url 图片地址
|
||||
* @param imageView 目标ImageView
|
||||
* @param placeholderResId 占位图资源ID
|
||||
* @param errorResId 错误图资源ID
|
||||
*/
|
||||
fun loadCircleImage(
|
||||
context: Context,
|
||||
url: Any?,
|
||||
imageView: ImageView,
|
||||
@DrawableRes placeholderResId: Int = 0,
|
||||
@DrawableRes errorResId: Int = 0
|
||||
) {
|
||||
val requestBuilder = Glide.with(context)
|
||||
.load(url)
|
||||
.apply(defaultOptions)
|
||||
.transform(CircleCrop())
|
||||
|
||||
applyPlaceholder(requestBuilder, placeholderResId, errorResId)
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载圆角图片
|
||||
* @param context 上下文
|
||||
* @param url 图片地址
|
||||
* @param imageView 目标ImageView
|
||||
* @param radius 圆角半径(px)
|
||||
* @param placeholderResId 占位图资源ID
|
||||
* @param errorResId 错误图资源ID
|
||||
*/
|
||||
fun loadRoundCornerImage(
|
||||
context: Context,
|
||||
url: Any?,
|
||||
imageView: ImageView,
|
||||
radius: Int,
|
||||
@DrawableRes placeholderResId: Int = 0,
|
||||
@DrawableRes errorResId: Int = 0
|
||||
) {
|
||||
val requestBuilder = Glide.with(context)
|
||||
.load(url)
|
||||
.apply(defaultOptions)
|
||||
.transform(RoundedCorners(radius))
|
||||
|
||||
applyPlaceholder(requestBuilder, placeholderResId, errorResId)
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
fun loadRoundCornerWitBitmap(
|
||||
context: Context,
|
||||
url: Bitmap,
|
||||
imageView: ImageView,
|
||||
radius: Int,
|
||||
@DrawableRes placeholderResId: Int = 0,
|
||||
@DrawableRes errorResId: Int = 0
|
||||
) {
|
||||
val requestBuilder = Glide.with(context)
|
||||
.load(url)
|
||||
.apply(RequestOptions.bitmapTransform(RoundedCorners(radius)))
|
||||
|
||||
applyPlaceholder(requestBuilder, placeholderResId, errorResId)
|
||||
.into(imageView)
|
||||
}
|
||||
/**
|
||||
* 加载图片并获取Bitmap
|
||||
* @param context 上下文
|
||||
* @param url 图片地址
|
||||
* @param callback 加载回调
|
||||
*/
|
||||
fun loadImageAsBitmap(
|
||||
context: Context,
|
||||
url: Any?,
|
||||
callback: (Bitmap?) -> Unit
|
||||
) {
|
||||
Glide.with(context)
|
||||
.asBitmap()
|
||||
.load(url)
|
||||
.apply(defaultOptions)
|
||||
.addListener(object : RequestListener<Bitmap> {
|
||||
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<Bitmap?>,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
callback(null)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap,
|
||||
model: Any,
|
||||
target: Target<Bitmap?>?,
|
||||
dataSource: DataSource,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
callback(resource)
|
||||
return false
|
||||
}
|
||||
})
|
||||
.submit()
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除内存缓存
|
||||
* @param context 上下文
|
||||
*/
|
||||
fun clearMemoryCache(context: Context) {
|
||||
Glide.get(context).clearMemory()
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除磁盘缓存
|
||||
* @param context 上下文
|
||||
*/
|
||||
fun clearDiskCache(context: Context) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
Glide.get(context).clearDiskCache()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存文件
|
||||
* @param context 上下文
|
||||
* @param url 图片地址
|
||||
* @param callback 回调
|
||||
*/
|
||||
fun getImageCacheFile(
|
||||
context: Context,
|
||||
url: String,
|
||||
callback: (File?) -> Unit
|
||||
) {
|
||||
Glide.with(context)
|
||||
.downloadOnly()
|
||||
.load(url)
|
||||
.addListener(object : RequestListener<File> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<File?>,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
callback(null)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: File,
|
||||
model: Any,
|
||||
target: Target<File?>?,
|
||||
dataSource: DataSource,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
callback(resource)
|
||||
return false
|
||||
}
|
||||
})
|
||||
.submit()
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停请求
|
||||
* @param context 上下文
|
||||
*/
|
||||
fun pauseRequests(context: Context) {
|
||||
Glide.with(context).pauseRequests()
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复请求
|
||||
* @param context 上下文
|
||||
*/
|
||||
fun resumeRequests(context: Context) {
|
||||
Glide.with(context).resumeRequests()
|
||||
}
|
||||
|
||||
// 私有方法:应用占位图和错误图
|
||||
private fun <T> applyPlaceholder(
|
||||
requestBuilder: RequestBuilder<T>,
|
||||
@DrawableRes placeholderResId: Int,
|
||||
@DrawableRes errorResId: Int
|
||||
): RequestBuilder<T> {
|
||||
return requestBuilder.apply {
|
||||
if (placeholderResId != 0) {
|
||||
placeholder(placeholderResId)
|
||||
}
|
||||
if (errorResId != 0) {
|
||||
error(errorResId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.sw.dualscreen.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.InputStream
|
||||
|
||||
object ImageUtil {
|
||||
|
||||
fun uriToBitmap(context: Context, uri: Uri): Bitmap? {
|
||||
return try {
|
||||
val options = BitmapFactory.Options()
|
||||
//options.inSampleSize = 2; // 这会将图片的尺寸缩小到原来的1/2
|
||||
options.inJustDecodeBounds = false
|
||||
// options.inPreferredConfig = Bitmap.Config.ARGB_8888
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565
|
||||
context.contentResolver.openInputStream(uri)?.use { stream ->
|
||||
BitmapFactory.decodeStream(stream, null, options)
|
||||
// BitmapFactory.decodeStream(stream)
|
||||
}
|
||||
context.contentResolver.openInputStream(uri)?.use { stream ->
|
||||
BitmapFactory.decodeStream(stream)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun uriToMultipart(context: Context, uri: Uri, partName: String): MultipartBody.Part? {
|
||||
try {
|
||||
val contentResolver = context.contentResolver
|
||||
val inputStream: InputStream = contentResolver.openInputStream(uri) ?: return null
|
||||
|
||||
// 临时文件(避免直接访问SAF文件)
|
||||
val tempFile = File.createTempFile("upload_", ".jpg", context.cacheDir)
|
||||
|
||||
val outputStream = FileOutputStream(tempFile)
|
||||
inputStream.copyTo(outputStream)
|
||||
inputStream.close()
|
||||
outputStream.close()
|
||||
|
||||
val requestFile = RequestBody.create("image/*".toMediaTypeOrNull(), tempFile)
|
||||
return MultipartBody.Part.createFormData(partName, tempFile.name, requestFile)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.sw.inbound.utils
|
||||
|
||||
import com.sw.inbound.MyApp
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
object LogSaveUtil {
|
||||
|
||||
private const val TAG = "LogSaveUtil"
|
||||
private const val CRASH_REPORTS_DIR = "crash_reports"
|
||||
|
||||
public fun saveLogFile(msg: String) {
|
||||
saveLogFile("yyyy-MM-dd", msg)
|
||||
}
|
||||
|
||||
public fun saveLogFile(pattern: String, msg: String) {
|
||||
try {
|
||||
saveLog(pattern, msg)
|
||||
} catch (e: Exception) {
|
||||
Timber.tag(TAG).e(e, "Error saving crash info to file")
|
||||
try {
|
||||
saveLog(pattern, "Error saving crash info to file:${e.message}")
|
||||
} catch (e: Exception) {
|
||||
Timber.tag(TAG).e(e, "save exception")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun saveLog(pattern: String, logInfo: String) {
|
||||
val time = SimpleDateFormat(pattern, Locale.getDefault()).format(Date())
|
||||
val fileName = "crash_$time.log"
|
||||
|
||||
val crashDir = getCrashDir()
|
||||
|
||||
val crashFile = File(crashDir, fileName)
|
||||
|
||||
val saveTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())
|
||||
FileOutputStream(crashFile, true).use { it.write("$saveTime||$logInfo\n".toByteArray()) }
|
||||
|
||||
Timber.tag(TAG).d("Crash info saved to: ${crashFile.absolutePath}")
|
||||
}
|
||||
|
||||
fun getCrashDir(): File {
|
||||
//val crashDir = File(context.getExternalFilesDir(null), CRASH_REPORTS_DIR)
|
||||
var crashDir = File(MyApp.instance!!.filesDir, CRASH_REPORTS_DIR)
|
||||
if (!crashDir.exists()) {
|
||||
crashDir.mkdirs()
|
||||
}
|
||||
|
||||
if (!(crashDir.exists())) {
|
||||
crashDir = File(MyApp.instance!!.cacheDir, CRASH_REPORTS_DIR)
|
||||
}
|
||||
return crashDir
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.sw.dualscreen.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.camera.core.ImageCapture
|
||||
import androidx.camera.core.ImageCaptureException
|
||||
import androidx.camera.view.CameraController
|
||||
import androidx.core.content.ContextCompat
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* 拍照工具类
|
||||
* @param context Context 上下文
|
||||
* @param cameraController CameraController 相机控制器
|
||||
* @param onSuccess (Uri) -> Unit 拍照成功回调
|
||||
* @param onError (String) -> Unit 拍照失败回调
|
||||
*/
|
||||
class PhotoCaptureHelper(
|
||||
private val context: Context,
|
||||
private val cameraController: CameraController,
|
||||
private val onSuccess: (Uri) -> Unit = {},
|
||||
private val onError: (String) -> Unit = {}
|
||||
) {
|
||||
private val callbackList: MutableList<(Uri) -> Unit> = mutableListOf()
|
||||
fun addSuccessCallback(callback:(Uri) -> Unit) {
|
||||
if (callbackList.contains(callback).not()) {
|
||||
callbackList.add(callback)
|
||||
}
|
||||
}
|
||||
|
||||
private var bindCamera:(()->Unit)?=null
|
||||
fun bindCameraCallback(callback:()->Unit) {
|
||||
this.bindCamera = callback
|
||||
}
|
||||
|
||||
/**
|
||||
* 拍照方法
|
||||
* @param fileNamePrefix 文件名前缀,默认为"IMG_"
|
||||
* @param fileExtension 文件扩展名,默认为".jpg"
|
||||
*/
|
||||
fun takePhoto(
|
||||
fileNamePrefix: String = "IMG_",
|
||||
fileExtension: String = ".jpg"
|
||||
) {
|
||||
Timber.d("开始拍照采集")
|
||||
|
||||
try {
|
||||
val executor = ContextCompat.getMainExecutor(context)
|
||||
val cacheDir = context.cacheDir
|
||||
val photoFile = File.createTempFile(
|
||||
"${fileNamePrefix}${System.currentTimeMillis()}",
|
||||
fileExtension,
|
||||
cacheDir
|
||||
)
|
||||
|
||||
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
|
||||
|
||||
cameraController.takePicture(
|
||||
outputOptions,
|
||||
executor,
|
||||
object : ImageCapture.OnImageSavedCallback {
|
||||
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
||||
val photoUri = outputFileResults.savedUri ?: Uri.fromFile(photoFile)
|
||||
Timber.d("照片保存成功: $photoUri")
|
||||
onSuccess(photoUri)
|
||||
callbackList.forEach {
|
||||
it(photoUri)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(exception: ImageCaptureException) {
|
||||
val errorMsg = "拍照失败: ${exception.message}"
|
||||
if (errorMsg.contains("Not bound to a valid Camera")) {
|
||||
if (bindCamera != null) {
|
||||
bindCamera?.invoke()
|
||||
//takePhoto()
|
||||
}
|
||||
|
||||
}
|
||||
Timber.e(exception, errorMsg)
|
||||
onError(errorMsg)
|
||||
}
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
val errorMsg = "创建临时文件失败: ${e.message}"
|
||||
Timber.e(e, errorMsg)
|
||||
onError(errorMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user