[Modify]圖片保存到本地適配
This commit is contained in:
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user