[Modify]圖片保存到本地適配

This commit is contained in:
wushaocheng
2022-12-20 18:44:30 +08:00
parent 8e872341fd
commit bec7657d43
8 changed files with 523 additions and 63 deletions

View File

@@ -0,0 +1,313 @@
package com.yizhuan.xchat_android_library.common.util
import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.yizhuan.xchat_android_library.common.application.BaseApp
import com.yizhuan.xchat_android_library.common.file.FileHelper
import com.yizhuan.xchat_android_library.common.glide.GlideUtils
import java.io.*
/**
* create by ysx 2020/9/25 0025
*文件工具类,为了适配android10,11
*/
object AlbumUtils {
/**
* 兼容android Q
* 网络图片保存本地
* 返回是否保存成功
*/
fun addUrlToAlbum(context: Context?, url: String?, callback: (Boolean) -> Unit) {
if (url == null) {
callback.invoke(false)
return
}
getGlideImagePath(context, url) { glideImagePath ->
glideImagePath?.also {
val fileName: String
val imageType: String
if (glideImagePath.endsWith(".gif", true)) {
fileName = System.currentTimeMillis().toString() + ".gif"
imageType = "image/gif"
} else {
fileName = System.currentTimeMillis().toString() + ".jpg"
imageType = "image/jpeg"
}
val values = ContentValues()
values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
values.put(MediaStore.MediaColumns.MIME_TYPE, imageType)
values.put(MediaStore.MediaColumns.SIZE, 1)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
} else {
values.put(
MediaStore.MediaColumns.DATA,
"${Environment.getExternalStorageDirectory().path}/${Environment.DIRECTORY_DCIM}/$fileName"
)
}
val contentResolver = BaseApp.getContext().contentResolver
val uri =
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
if (uri != null) {
try {
val oldFile = File(glideImagePath)
if (oldFile.exists()) { //文件存在时
val inStream = FileInputStream(glideImagePath) //读入原文件
val outputStream = contentResolver.openOutputStream(uri)
if (outputStream != null) {
val bos = BufferedOutputStream(outputStream)
val buffer = ByteArray(1024)
var bytes = inStream.read(buffer)
while (bytes >= 0) {
bos.write(buffer, 0, bytes)
bos.flush()
bytes = inStream.read(buffer)
}
bos.close()
callback.invoke(true)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
callback.invoke(false)
}
/**
* 兼容android Q
* 将bitmap保存到本地图库
* displayName 文件名 "xxx.jpg"
* mimeType "image/jpeg"
*/
@JvmOverloads
@JvmStatic
fun addBitmapToAlbum(
bitmap: Bitmap?,
displayName: String = "${System.currentTimeMillis()}.jpg",
mimeType: String = "image/jpeg",
compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG
): Boolean {
if (bitmap == null) return false
val values = ContentValues()
values.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName)
values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
} else {
values.put(
MediaStore.MediaColumns.DATA,
"${Environment.getExternalStorageDirectory().path}/${Environment.DIRECTORY_DCIM}/$displayName"
)
}
var outputStream: OutputStream? = null
try {
val contentResolver = BaseApp.getContext().contentResolver
val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
if (uri != null) {
outputStream = contentResolver.openOutputStream(uri)
if (outputStream != null) {
bitmap.compress(compressFormat, 100, outputStream)
return true
}
}
} catch (e: Exception) {
} finally {
try {
outputStream?.close()
} catch (ex: Exception) {
}
}
return false
}
/**
* 兼容android Q
* 本地缓存文件保存本地相册
* 返回是否保存成功
*/
fun addImageFileToAlbum(file: File?): Boolean {
if (file == null) return false
val start: Int = file.name.lastIndexOf(".")
val suffix: String = file.name.substring(start + 1)
val fileName: String
val imageType: String
if (suffix == "gif" || suffix == "GIF") {
fileName = System.currentTimeMillis().toString() + ".gif"
imageType = "image/gif"
} else {
fileName = System.currentTimeMillis().toString() + ".jpg"
imageType = "image/jpeg"
}
val values = ContentValues()
values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
values.put(MediaStore.MediaColumns.MIME_TYPE, imageType)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
} else {
values.put(
MediaStore.MediaColumns.DATA,
"${Environment.getExternalStorageDirectory().path}/${Environment.DIRECTORY_DCIM}/$fileName"
)
}
val contentResolver = BaseApp.getContext().contentResolver
val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
if (uri != null) {
var inStream: FileInputStream? = null
var outputStream: OutputStream? = null
var bos: BufferedOutputStream? = null
try {
if (file.exists()) { //文件存在时
inStream = FileInputStream(file) //读入原文件
outputStream = contentResolver.openOutputStream(uri)
if (outputStream != null) {
bos = BufferedOutputStream(outputStream)
val buffer = ByteArray(1024)
var bytes = inStream.read(buffer)
while (bytes >= 0) {
bos.write(buffer, 0, bytes)
bos.flush()
bytes = inStream.read(buffer)
}
return true
}
}
} catch (e: Exception) {
} finally {
try {
inStream?.close()
} catch (ex: Exception) {
}
try {
outputStream?.close()
} catch (ex: Exception) {
}
try {
bos?.close()
} catch (ex: Exception) {
}
}
}
return false
}
/**
* Glide 获得图片路径
*/
private fun getGlideImagePath(context: Context?, imgUrl: String, callback: (String?) -> Unit) {
try {
GlideUtils.instance().downloadFromUrl(context, imgUrl, object : RequestListener<File?> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<File?>?,
isFirstResource: Boolean
): Boolean {
callback.invoke("")
return false
}
override fun onResourceReady(
resource: File?,
model: Any?,
target: Target<File?>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
callback.invoke(resource?.absolutePath)
return false
}
})
} catch (e: Exception) {
callback.invoke("")
}
}
/**
* 动态获得h5图片路径
*/
fun getTrendImagePath(context: Context?, imgUrl: String, callback: (String?) -> Unit) {
try {
GlideUtils.instance().downloadFromUrl(context, imgUrl, object : RequestListener<File?> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<File?>?,
isFirstResource: Boolean
): Boolean {
callback.invoke("")
return false
}
override fun onResourceReady(
resource: File?,
model: Any?,
target: Target<File?>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
val fileDir =
FileHelper.getRootFilesDir(Environment.DIRECTORY_PICTURES).absolutePath + "/trend/test"
//获取到下载得到的图片,进行本地保存
//第二个参数为你想要保存的目录名称
val appDir = File(fileDir)
if (!appDir.exists()) {
appDir.mkdirs()
}
val fileName = System.currentTimeMillis().toString() + ".jpg"
val destFile = File(appDir, fileName)
//把gilde下载得到图片复制到定义好的目录中去
copy(resource, destFile) {
callback.invoke(destFile.absolutePath)
}
return false
}
})
} catch (e: Exception) {
callback.invoke("")
}
}
/**
* 复制文件
*
* @param source 输入文件
* @param target 输出文件
*/
fun copy(source: File?, target: File?, callback: () -> Unit) {
var fileInputStream: FileInputStream? = null
var fileOutputStream: FileOutputStream? = null
try {
fileInputStream = FileInputStream(source)
fileOutputStream = FileOutputStream(target)
val buffer = ByteArray(1024)
while (fileInputStream.read(buffer) > 0) {
fileOutputStream.write(buffer)
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
} finally {
try {
fileInputStream?.close()
fileOutputStream?.close()
callback.invoke()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}

View File

@@ -0,0 +1,57 @@
package com.yizhuan.xchat_android_library.common.util;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
public class ExecutorCenter {
private static final String TAG = "ExecutorCenter";
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static ExecutorCenter msInstance = null;
private static ScheduledExecutor mExecutor;
public static ExecutorCenter getInstance() {
if (null != msInstance) return msInstance;
synchronized (ExecutorCenter.class) {
if (null != msInstance) return msInstance;
msInstance = new ExecutorCenter();
return msInstance;
}
}
private ExecutorCenter() {
mExecutor = Pools.newScheduledThreadPoolExecutor("self-executor", CPU_COUNT + 1);
}
public Executor getExecutor() {
return mExecutor;
}
public void post(Runnable runnable) {
if (this.checkNull(runnable)) {
return;
}
mExecutor.execute(runnable);
}
public void postDelay(Runnable runnable, long delay) {
if (this.checkNull(runnable)) {
return;
}
mExecutor.execute(runnable, delay);
}
public Future submitDelay(Runnable runnable, long delay) {
if (this.checkNull(runnable)) {
return null;
}
return mExecutor.submit(runnable, delay);
}
private boolean checkNull(Runnable runnable) {
if (null == runnable) {
Logger.error(TAG, "runnable null!!!!");
return true;
}
return false;
}
}

View File

@@ -0,0 +1,36 @@
package com.yizhuan.xchat_android_library.common.util;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class Pools {
private static final String TAG = "Pools";
public static ScheduledExecutor newScheduledThreadPoolExecutor(String threadNamePrefix, int coreCount) {
return new ScheduledExecutorAdapter(new ScheduledThreadPoolExecutor(coreCount, new DefaultThreadFactory(threadNamePrefix)) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t != null) {
Logger.error(TAG, String.valueOf(t));
}
}
});
}
private static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory(String threadNamePrefix) {
this.namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-";
}
@Override
public Thread newThread(Runnable r) {
return new Thread(r, this.namePrefix + this.threadNumber.getAndIncrement());
}
}
}

View File

@@ -0,0 +1,11 @@
package com.yizhuan.xchat_android_library.common.util;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
public interface ScheduledExecutor extends Executor {
void execute(Runnable runnable, long delay);
Future submit(Runnable runnable, long delay);
}

View File

@@ -0,0 +1,34 @@
package com.yizhuan.xchat_android_library.common.util;
import androidx.annotation.NonNull;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorAdapter implements ScheduledExecutor {
private ScheduledExecutorService mExecutor;
public ScheduledExecutorAdapter(ScheduledExecutorService executor) {
if (null == executor) {
throw new NullPointerException("ScheduledThreadPoolExecutor may not be null");
}
this.mExecutor = executor;
}
@Override
public void execute(Runnable command, long delay) {
this.mExecutor.schedule(command, delay, TimeUnit.MILLISECONDS);
}
@Override
public void execute(@NonNull Runnable command) {
this.mExecutor.execute(command);
}
@Override
public Future submit(Runnable command, long delay) {
return this.mExecutor.schedule(command, delay, TimeUnit.MILLISECONDS);
}
}