[Modify]修改图片选择的逻辑

This commit is contained in:
wushaocheng
2022-12-01 20:35:52 +08:00
parent 2c398f8c8c
commit c10aa4af68
31 changed files with 440 additions and 59 deletions

View File

@@ -60,7 +60,7 @@ android {
dependencies {
def glideVersion = "4.13.1"
def glideVersion = "4.13.2"
def retrofitVersion = "2.9.0"
def okhttp3 = "4.9.3"
def okio = "2.8.0"
@@ -73,6 +73,7 @@ dependencies {
def SmartRefreshLayoutVersion = "1.0.3"
def eventbusVersion = "3.0.0"
def fragment_version = "1.3.6"
def GlideTransformationsVersion = "3.0.1"
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.13.2'
@@ -124,6 +125,11 @@ dependencies {
api 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
api 'com.github.chrisbanes:PhotoView:2.3.0'
//mmkv
api 'com.tencent:mmkv:1.2.13'
api "jp.wasabeef:glide-transformations:${GlideTransformationsVersion}"
}
repositories {
mavenCentral()

View File

@@ -0,0 +1,38 @@
package com.yizhuan.xchat_android_library.common.application;
import android.app.Application;
import android.content.Context;
/**
* Application的代理类
*/
public abstract class BaseApp extends Application{
private static final String TAG = "BaseApp";
public static Application gContext;
/**
* @return 获取Application上下文对象
*/
public static Context getContext() {
return gContext;
}
/**
* @return 获取Application实例
*/
public static Application getApplication() {
return gContext;
}
public static void init(Application application) {
gContext = application;
}
/**
* debug 环境 受到实验室模式影响
*/
public static boolean isDebug() {
return Env.isDebug();
}
}

View File

@@ -0,0 +1,146 @@
package com.yizhuan.xchat_android_library.common.application;
import com.yizhuan.xchat_android_library.R;
import com.yizhuan.xchat_android_library.utils.ResUtil;
import com.yizhuan.xchat_android_library.utils.config.BasicConfig;
import com.yizhuan.xchat_android_library.utils.pref.CommonPref;
/**
* 环境配置类
*/
public class Env {
public static final String KEY_ENVIRONMENT = "environment";
/**
* 当前环境
*/
private static EnvType mEnvType;
/**
* 真实环境
*/
private static boolean mRealDebug;
private Env() {
}
public enum EnvType {
/**
* 测试环境
*/
Debug(0),
/**
* 待发布环境
*/
Staging(1),
/**
* 线上环境
*/
Release(2);
public int code;
EnvType(int code) {
this.code = code;
}
public static EnvType create(int code) {
EnvType env = null;
if (code == EnvType.Debug.code) {
env = Debug;
} else if (code == EnvType.Staging.code) {
env = Staging;
} else if (code == EnvType.Release.code) {
env = Release;
}
return env;
}
}
/**
* 初始化环境参数
*
* @param defaultEnv 用于初始化最初环境
* @param isRealDebug 判断是否是真的debug模式不受环境影响
*/
public static void initEnv(String defaultEnv, boolean isRealDebug) {
if (defaultEnv == null || defaultEnv.isEmpty()) {
throw new RuntimeException(ResUtil.getString(R.string.yizhuan_xchat_android_core_env_01));
}
int environment = CommonPref.instance(BasicConfig.INSTANCE.getAppContext()).getInt(KEY_ENVIRONMENT);
EnvType envType;
if (environment == -1) {
envType = EnvType.valueOf(firstChar2Up(defaultEnv));
changeEnv(envType);
} else {
envType = EnvType.create(environment);
}
if (envType == null) {
throw new RuntimeException(ResUtil.getString(R.string.yizhuan_xchat_android_core_env_02));
}
mEnvType = envType;
mRealDebug = isRealDebug;
}
/**
* 第一个字符大写,用于把字符串转成类名
*
* @param s
* @return
*/
private static String firstChar2Up(String s) {
if (s == null || s.length() < 1) {
return null;
}
String newStr = s.substring(0, 1).toUpperCase() + s.substring(1);
return newStr;
}
/**
* 修改偏好设置里面的值
*
* @param env
*/
public static void changeEnv(EnvType env) {
CommonPref.instance(BasicConfig.INSTANCE.getAppContext()).putInt(KEY_ENVIRONMENT, env.code);
}
/**
* 受到环境印象(和实验室有关)
*
* @return
*/
public static boolean isDebug() {
return mEnvType == EnvType.Debug && mRealDebug;
}
/**
* 真实包环境(不受实验室环境影响)
*
* @return
*/
public static boolean isRealDebug() {
return mRealDebug;
}
/**
* 获取当前环境
* @return
*/
public static EnvType getCurrentEnv() {
if (mEnvType == null) {
throw new RuntimeException(ResUtil.getString(R.string.yizhuan_xchat_android_core_env_03));
}
return mEnvType;
}
}

View File

@@ -0,0 +1,12 @@
package com.yizhuan.xchat_android_library.common.application;
import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
public interface IAppLifeCycle {
void init(Application application);
}

View File

@@ -0,0 +1,50 @@
package com.yizhuan.xchat_android_library.common.delegate
import com.yizhuan.xchat_android_library.common.application.BaseApp
import com.yizhuan.xchat_android_library.common.util.SPUtils
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* author: wushaocheng
* time: 2022/11/15
* desc: sp存储和取出委托
*/
class SpDelegate<T>(private val key: String, private val default: T) : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
val value = when (default) {
is Boolean -> SPUtils.getBoolean(key, default)
is String -> SPUtils.getString(key, default)
is Long -> SPUtils.getLong(key, default)
is Int -> SPUtils.getInt(key, default)
is Float -> SPUtils.getFloat(key, default)
is Double -> SPUtils.getDouble(key, default)
is ByteArray -> SPUtils.getBytes(key, default)
else -> {
if (BaseApp.isDebug()) {
throw IllegalArgumentException("SpDelegate: this type is no supported")
} else {
null
}
}
}
return (value as? T) ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
when (value) {
is Boolean -> SPUtils.putBoolean(key, value)
is String -> SPUtils.putString(key, value)
is Long -> SPUtils.putLong(key, value)
is Int -> SPUtils.putInt(key, value)
is Float -> SPUtils.putFloat(key, value)
is Double -> SPUtils.putDouble(key, value)
is ByteArray -> SPUtils.putBytes(key, value)
else -> {
if (BaseApp.isDebug()) {
throw IllegalArgumentException("SpDelegate: this type is no supported")
}
}
}
}
}

View File

@@ -0,0 +1,917 @@
package com.yizhuan.xchat_android_library.common.file;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import com.qiniu.android.utils.StringUtils;
import com.yizhuan.xchat_android_library.common.application.BaseApp;
import com.yizhuan.xchat_android_library.common.util.Logger;
import com.yizhuan.xchat_android_library.utils.FP;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* 文件工具类
*/
public class FileHelper {
private static final String TAG = "FileHelper";
private static final String NO_MEDIA = ".nomedia";
private static File rootCacheDir;
private static HashMap<String, File> rootFileDirMap = new HashMap<>();
private static final String[] mCs = new String[]{"/", "\\", "?", "*", ":", "<", ">", "|", "\""};
private static final char UNICODE_SURROGATE_START_CHAR = '\ud800';
private static final char UNICODE_SURROGATE_END_CHAR = '\udfff';
/**
* 创建.nomedia文件
*
* @param parentFile 上级目录
*/
private static void createNoMediaFile(File parentFile) {
File no_media = new File(parentFile, NO_MEDIA);
if (!no_media.exists()) {
try {
no_media.createNewFile();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 获取Android/data/当前应用包名/cache文件夹
*
* @return 当前应用缓存根目录
*/
public static File getRootCacheDir() {
if (rootCacheDir != null) {
//因为频繁调用getExternalCacheDir方法会在某些机器上面出现ANR所以这里降低频率调用。
return rootCacheDir;
}
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File file = BaseApp.getContext().getExternalCacheDir();
if (file != null) {
createNoMediaFile(file);
//因为频繁调用getExternalCacheDir方法会在某些机器上面出现ANR所以这里降低频率调用。
rootCacheDir = file;
return file;
}
}
File file = BaseApp.getContext().getCacheDir();
createNoMediaFile(file);
//因为频繁调用getExternalCacheDir方法会在某些机器上面出现ANR所以这里降低频率调用。
rootCacheDir = file;
return file;
}
/**
* 获取Android/data/当前应用包名/files文件夹
*
* @param type 类型从以下方式中选择也可以为null:
* {@link Environment#DIRECTORY_MUSIC},
* {@link Environment#DIRECTORY_PODCASTS},
* {@link Environment#DIRECTORY_RINGTONES},
* {@link Environment#DIRECTORY_ALARMS},
* {@link Environment#DIRECTORY_NOTIFICATIONS},
* {@link Environment#DIRECTORY_PICTURES},
* {@link Environment#DIRECTORY_MOVIES},
* {@link Environment#DIRECTORY_DOWNLOADS},
* {@link Environment#DIRECTORY_DCIM},
* {@link Environment#DIRECTORY_DOCUMENTS},
* {@link Environment#DIRECTORY_AUDIOBOOKS}
* @return 当前应用文件根目录
*/
public static File getRootFilesDir(@androidx.annotation.Nullable String type) {
String dirName = (type != null && type.length() > 0) ? type.trim() : null;
if (TextUtils.isEmpty(dirName)) {
//因为频繁调用getExternalFilesDir方法会在某些机器上面出现ANR所以这里降低频率调用。
File file = rootFileDirMap.get("empty");
if (file != null) {
return file;
}
} else {
//因为频繁调用getExternalFilesDir方法会在某些机器上面出现ANR所以这里降低频率调用。
File file = rootFileDirMap.get(dirName);
if (file != null) {
return file;
}
}
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File file = BaseApp.getContext().getExternalFilesDir(dirName);
if (file != null) {
createNoMediaFile(file);
//因为频繁调用getExternalFilesDir方法会在某些机器上面出现ANR所以这里降低频率调用。
if (TextUtils.isEmpty(dirName)) {
rootFileDirMap.put("empty", file);
} else {
rootFileDirMap.put(dirName, file);
}
return file;
}
}
File file = BaseApp.getContext().getFilesDir();
createNoMediaFile(file);
//因为频繁调用getExternalFilesDir方法会在某些机器上面出现ANR所以这里降低频率调用。
rootFileDirMap.put("empty", file);
return file;
}
/**
* 创建临时文件
*
* @param dir 文件夹
* @return 临时文件
*/
public static File createTempFile(File dir) {
try {
File file = File.createTempFile("pic", null, dir);
file.deleteOnExit();
return file;
} catch (IOException e) {
return null;
}
}
/**
* 判断文件或文件夹是否存在
*
* @param filePath 文件路径
* @return 是否存在这个文件或文件夹
*/
public static boolean isFileExist(String filePath) {
if (TextUtils.isEmpty(filePath)) {
return false;
}
File file = new File(filePath);
return file.exists();
}
/**
* 确保文件夹存在,不存在的时候就创建该目录
*
* @param dirPath 文件夹路径
* @return 确保是否已存在这个文件夹
*/
public static boolean ensureDirExists(String dirPath) {
File dirFile = new File(dirPath);
if (!dirFile.exists()) {
return dirFile.mkdirs();
}
return true;
}
/**
* 确保该文件的文件夹存在,不存在的时候就创建该文件的文件夹
*
* @param filePath 文件路径
* @return 确保是否已存在该文件的文件夹
*/
public static boolean ensureFileDirExists(String filePath) {
String dir = getDirOfFilePath(filePath);
if (TextUtils.isEmpty(dir)) {
return false;
}
ensureDirExists(dir);
return true;
}
/**
* 确保该文件存在,不存在的时候就创建该文件
*
* @param filePath 文件路径
* @return 确保是否已存在该文件
*/
public static File ensureFileExists(String filePath) {
if (!ensureFileDirExists(filePath)) {
return null;
}
File file = new File(filePath);
if (file.exists()) {
return file;
}
try {
if (!file.exists() && !file.createNewFile()) {
file = null;
}
} catch (IOException e) {
e.printStackTrace();
file = null;
}
return file;
}
/**
* 从文件路径里提取文件夹的路径
*
* @param filePath 文件路径
* @return 文件夹的路径
*/
public static String getDirOfFilePath(String filePath) {
if (TextUtils.isEmpty(filePath)) {
return null;
}
int sepPos = filePath.lastIndexOf(File.separatorChar);
if (sepPos == -1) {
return null;
}
return filePath.substring(0, sepPos);
}
/**
* 删除单个文件
*
* @param filePath 文件路径
*/
public static void removeFile(String filePath) {
if (!TextUtils.isEmpty(filePath)) {
try {
File file = new File(filePath);
file.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 删除文件或文件夹下面所有的文件
*
* @param filePath 文件路径
* @return 是否删除成功
*/
public static boolean removeAllFile(String filePath) {
if (TextUtils.isEmpty(filePath)) {
return true;
}
File file = new File(filePath);
if (!file.exists()) {
return true;
}
if (file.isFile()) {
try {
return file.delete();
} catch (Exception e) {
return false;
}
}
if (!file.isDirectory()) {
return false;
}
File[] files = file.listFiles();
if (files != null && files.length > 0) {
for (File f : files) {
if (f.isFile()) {
try {
f.delete();
} catch (Exception e) {
e.printStackTrace();
}
} else if (f.isDirectory()) {
removeAllFile(f.getAbsolutePath());
}
}
}
try {
return file.delete();
} catch (Exception e) {
return false;
}
}
/**
* 将数据存储到缓存文件夹里
*
* @param data 数据
* @param filePath 文件路径
*/
public static void saveByteArrayIntoFile(byte[] data, String filePath) {
try {
File f = new File(filePath);
if (f.createNewFile()) {
FileOutputStream fos = new FileOutputStream(f);
fos.write(data);
fos.flush();
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 将Bitmap保存为JPEG图片
*/
public static void saveBitmapAsJPEG(Bitmap bmp, String filePath) {
if (bmp == null) {
return;
}
FileOutputStream fos = null;
try {
if (!ensureFileDirExists(filePath)) {
return;
}
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
fos = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 将Bitmap保存为PNG图片
*
* @param bmp 图片
* @param filePath 文件路径
* @return 文件路径
*/
public static String saveBitmapAsPNG(Bitmap bmp, String filePath) {
if (bmp == null) {
return "";
}
FileOutputStream fos = null;
try {
if (!ensureFileDirExists(filePath)) {
return "";
}
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
fos = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
return filePath;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return "";
}
/**
* 解压zip到指定的路径
*
* @param zipFileString ZIP的名称
* @param outPathString 要解压缩路径
*/
public static boolean unzipFile(String zipFileString, String outPathString) {
ZipInputStream inZip = null;
try {
inZip = new ZipInputStream(new FileInputStream(zipFileString));
ZipEntry zipEntry;
while ((zipEntry = inZip.getNextEntry()) != null) {
String szName = zipEntry.getName();
if (szName.contains("/")) {
szName = szName.substring(szName.indexOf("/") + 1);
if (TextUtils.isEmpty(szName)) {
continue;
}
}
if (!zipEntry.isDirectory()) {
File file = new File(outPathString + File.separator + szName);
if (!file.exists()) {
if (file.getParentFile() != null) {
file.getParentFile().mkdirs();
}
file.createNewFile();
}
FileOutputStream out = new FileOutputStream(file);
int len;
byte[] buffer = new byte[1024];
while ((len = inZip.read(buffer)) != -1) {
out.write(buffer, 0, len);
out.flush();
}
out.close();
} else {
szName = szName.substring(0, szName.length() - 1);
File folder = new File(outPathString + File.separator + szName);
folder.mkdirs();
}
}
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
try {
if (inZip != null) {
inZip.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return true;
}
/**
* 解压压缩包
*
* @param srcZipPath 原压缩包路径
* @param destFilePath 目标文件夹路径
* @return 解压之后的文件组
* @throws IOException 抛出异常
*/
public static File[] unzip(String srcZipPath, String destFilePath) throws IOException {
if (TextUtils.isEmpty(destFilePath)) {
throw new IOException();
}
if (!destFilePath.endsWith(File.separator)) {
destFilePath = destFilePath + File.separator;
}
File destDir = new File(destFilePath);
if (!destDir.isDirectory() || !destDir.exists()) {
destDir.mkdir();
}
ArrayList<File> extractedFileList = new ArrayList<>();
ZipInputStream inZip = null;
try {
ZipEntry zipEntry;
inZip = new ZipInputStream(new FileInputStream(srcZipPath));
String szName;
while ((zipEntry = inZip.getNextEntry()) != null) {
szName = zipEntry.getName();
if (zipEntry.isDirectory()) {
szName = szName.substring(0, szName.length() - 1);
File folder = new File(destDir.getAbsolutePath() + File.separator + szName);
folder.mkdirs();
continue;
}
FileOutputStream out = null;
try {
int len;
File file = new File(destDir.getAbsolutePath() + File.separator + szName);
file = ensureFileExists(file.getAbsolutePath());
if (file != null) {
out = new FileOutputStream(file);
byte[] buffer = new byte[1024];
while ((len = inZip.read(buffer)) != -1) {
out.write(buffer, 0, len);
out.flush();
}
out.close();
extractedFileList.add(file);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
}
}
} finally {
if (inZip != null) {
inZip.close();
}
}
File[] extractedFiles = new File[extractedFileList.size()];
extractedFileList.toArray(extractedFiles);
return extractedFiles;
}
/**
* 从文件路径里面提取文件的后缀
*
* @param filePath 文件路径
* @return 后缀,如.mp3
*/
public static String getFileExtension(String filePath) {
String fileName = getFileName(filePath);
if (TextUtils.isEmpty(fileName)) {
return null;
}
int index = fileName.lastIndexOf(".");
if (index != -1) {
return fileName.substring(index);
}
return null;
}
/**
* 从文件路径里面提取文件名
*
* @param filePath 文件路径
* @return 文件名称
*/
public static String getFileName(String filePath) {
if (filePath != null) {
String slash = "/";
int pos = filePath.lastIndexOf(slash) + 1;
if (pos > 0) {
String name = filePath.substring(pos);
if (!TextUtils.isEmpty(name)) {
name = name.replace("?", "");
}
return name;
}
}
return null;
}
/**
* 从URL里面提取版本名
*
* @param url 链接
* @return 文件名
*/
public static String getFileNameWithVer(String url) {
String versionName = "";
String subName = getFileName(url);
if (subName != null && subName.contains("?")) {
String[] tempArr = subName.split("\\?");
if (tempArr.length > 1) {
subName = tempArr[0];
versionName = tempArr[1];
}
}
String fileName = dropExt(subName);
fileName = fileName + versionName;
return fileName;
}
/**
* 从文件名里面踢出点得到可用的文件名
*
* @param fileName 文件名
* @return 文件名
*/
public static String dropExt(String fileName) {
if (!TextUtils.isEmpty(fileName)) {
int pos = fileName.lastIndexOf(".");
if (pos != -1) {
return FP.take(pos, fileName);
}
}
return fileName;
}
/**
* 读取文件内容转换为字符串
*
* @param filePath 文件路径
*/
public static String getStringFromFile(String filePath) {
String result = "";
InputStream is = null;
try {
is = new FileInputStream(filePath);
int length = is.available();
byte[] buffer = new byte[length];
is.read(buffer);
result = new String(buffer, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 读取Assets内容转换为字符串
*
* @param fileName 文件名
* @return 字符串内容
*/
public static String getStringFromAssets(String fileName) {
String result = "";
try {
InputStream is = BaseApp.getContext().getAssets().open(fileName);
int length = is.available();
byte[] buffer = new byte[length];
is.read(buffer);
result = new String(buffer, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 从Assets里面读取内容覆盖本地文件
*
* @param dir 文件夹路径
* @param fileName 文件名称
* @param overwrite 是否覆盖,即删除旧文件重新创建新文件
* @return 操作是否成功
*/
public static boolean copyFileFromAssets(String dir, String fileName, boolean overwrite) {
String path = dir + File.separator + fileName;
File file = new File(path);
if (file.exists() && overwrite) {
file.delete();
}
if (!file.exists()) {
try {
if (!ensureDirExists(dir)) {
return false;
}
file.createNewFile();
InputStream in = BaseApp.getContext().getAssets().open(fileName);
OutputStream out = new FileOutputStream(file);
byte[] buffer = new byte[4096];
int n;
while ((n = in.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
out.flush();
in.close();
out.close();
} catch (Exception e) {
try {
file.delete();
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
}
return true;
}
/**
* 将字节数组写入文件中
*
* @param buffer 字节数组
* @param folderPath 文件夹路径
* @param fileName 文件名称
*/
public static void saveDataToFile(byte[] buffer, String folderPath, String fileName) {
File fileDir = new File(folderPath);
if (!fileDir.exists()) {
fileDir.mkdirs();
}
File file = new File(folderPath + File.separator + fileName);
FileOutputStream out = null;
try {
out = new FileOutputStream(file);
out.write(buffer);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 格式化大小,带单位
*/
public static String formatSize(long size) {
//获取到的size为1705230
int GB = 1024 * 1024 * 1024;//定义GB的计算常量
int MB = 1024 * 1024;//定义MB的计算常量
int KB = 1024;//定义KB的计算常量
DecimalFormat df = new DecimalFormat("0.0");//格式化小数
String resultSize = "";
if (size / GB >= 1) {
//如果当前Byte的值大于等于1GB
resultSize = df.format(size / (float) GB) + "GB";
} else if (size / MB >= 1) {
//如果当前Byte的值大于等于1MB
resultSize = df.format(size / (float) MB) + "MB";
} else if (size / KB >= 1) {
//如果当前Byte的值大于等于1KB
resultSize = df.format(size / (float) KB) + "KB";
} else {
resultSize = size + "B";
}
return resultSize;
}
/**
* @param fileName 文件名
* @return 文件名是否正确
*/
public static boolean isFileNameCorrect(String fileName) {
if (null == fileName) {
return false;
} else {
fileName = fileName.trim();
if (fileName.length() == 0) {
return false;
} else {
for (String c : mCs) {
if (fileName.contains(c)) {
return false;
}
}
return !containsSurrogateChar(fileName);
}
}
}
/**
* @param string 字符串
* @return 是否包含需要替代的字符串
*/
public static boolean containsSurrogateChar(String string) {
if (TextUtils.isEmpty(string)) {
return false;
} else {
int length = string.length();
boolean hasSurrogateChar = false;
for (int i = 0; i < length; ++i) {
char c = string.charAt(i);
if (UNICODE_SURROGATE_START_CHAR <= c && c <= UNICODE_SURROGATE_END_CHAR) {
hasSurrogateChar = true;
break;
}
}
return hasSurrogateChar;
}
}
/**
* 把Uri转换为文件
*
* @param uri 源文件的Uri的路径
* @param path 要存入的文件的路径
* @param overwrite 是否需要复写
* @return 是否已经把Uri转换成功为文件了
*/
public static boolean copyFileFromUri(Uri uri, String path, boolean overwrite) {
File file = new File(path);
if (file.exists() && overwrite) {
file.delete();
}
if (!file.exists()) {
try {
if (!FileHelper.ensureDirExists(file.getParentFile().getAbsolutePath())) {
return false;
}
InputStream stream = BaseApp.getContext().getContentResolver().openInputStream(uri);
if (stream != null) {
file.createNewFile();
OutputStream out = new FileOutputStream(file);
byte buffer[] = new byte[4096];
int n;
while ((n = stream.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
out.flush();
stream.close();
out.close();
}
} catch (Exception e) {
try {
file.delete();
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
}
return true;
}
/**
* 将文件转换成字节数组
*
* @param file 文件
* @return 字节数组
*/
public static byte[] fileToByteArray(File file) {
if (file.exists() && file.canRead()) {
try {
return streamToBytes(new FileInputStream(file));
} catch (Exception e) {
Logger.error(TAG, String.valueOf(e));
}
}
return null;
}
/**
* 将文件流转换成字节数组
*
* @param inputStream 输入流
* @return 字节数组
*/
public static byte[] streamToBytes(InputStream inputStream) {
byte[] content = null;
ByteArrayOutputStream baos = null;
BufferedInputStream bis = null;
try {
baos = new ByteArrayOutputStream();
bis = new BufferedInputStream(inputStream);
byte[] buffer = new byte[1024];
int length;
while ((length = bis.read(buffer)) != -1) {
baos.write(buffer, 0, length);
}
content = baos.toByteArray();
if (content.length == 0) {
content = null;
}
} catch (IOException e) {
Logger.error(TAG, String.valueOf(e));
} finally {
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
Logger.error(TAG, String.valueOf(e));
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
Logger.error(TAG, String.valueOf(e));
}
}
}
return content;
}
/**
* 文件转换成字符串
*
* @param filePath 文件路径
* @return 字符串内容
*/
public static String getTxtFileContent(String filePath) {
String content = "";
if (!StringUtils.isNullOrEmpty(filePath)) {
File file = new File(filePath);
if (file.isFile()) {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
String line;
StringBuilder sb = new StringBuilder();
BufferedReader buffReader = new BufferedReader(new InputStreamReader(inputStream));
while ((line = buffReader.readLine()) != null) {
sb.append(line).append("\n");
}
content = sb.toString();
} catch (Exception e) {
Logger.error(TAG, "getTxtFileContent read fail, e = " + e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception ignore) {
}
}
}
}
}
return content;
}
}

View File

@@ -0,0 +1,73 @@
package com.yizhuan.xchat_android_library.common.glide;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import com.yizhuan.xchat_android_library.easyphoto.engine.ImageEngine;
/**
* Created by wushaocheng on 2021/3/29 15:18.
* DescGlide实现类
*/
public class GlideEngine implements ImageEngine {
/**
* 加载图片到ImageView
*
* @param context 上下文
* @param uri 图片路径Uri
* @param imageView 加载到的ImageView
*/
//安卓10推荐uri并且path的方式不再可用
@Override
public void loadPhoto(@NonNull Context context, @NonNull Uri uri, @NonNull ImageView imageView) {
GlideUtils.instance().loadUriCrossFade(uri, imageView);
}
/**
* 加载gif动图图片到ImageViewgif动图不动
*
* @param context 上下文
* @param gifUri gif动图路径Uri
* @param imageView 加载到的ImageView
* <p>
* 备注:不支持动图显示的情况下可以不写
*/
//安卓10推荐uri并且path的方式不再可用
@Override
public void loadGifAsBitmap(@NonNull Context context, @NonNull Uri gifUri, @NonNull ImageView imageView) {
GlideUtils.instance().loadUriGift(gifUri, imageView);
}
/**
* 加载gif动图到ImageViewgif动图动
*
* @param context 上下文
* @param gifUri gif动图路径Uri
* @param imageView 加载动图的ImageView
* <p>
* 备注:不支持动图显示的情况下可以不写
*/
//安卓10推荐uri并且path的方式不再可用
@Override
public void loadGif(@NonNull Context context, @NonNull Uri gifUri, @NonNull ImageView imageView) {
GlideUtils.instance().loadUriGiftAndCrossFade(gifUri, imageView);
}
/**
* 获取图片加载框架中的缓存Bitmap不用拼图功能可以直接返回null
*
* @param context 上下文
* @param uri 图片路径
* @param width 图片宽度
* @param height 图片高度
* @return Bitmap
* @throws Exception 异常直接抛出EasyPhotos内部处理
*/
//安卓10推荐uri并且path的方式不再可用
@Override
public Bitmap getCacheBitmap(@NonNull Context context, @NonNull Uri uri, int width, int height) throws Exception {
return null;
}
}

View File

@@ -0,0 +1,226 @@
package com.yizhuan.xchat_android_library.common.photo
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Environment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.yizhuan.xchat_android_library.common.application.BaseApp
import com.yizhuan.xchat_android_library.common.delegate.SpDelegate
import com.yizhuan.xchat_android_library.common.file.FileHelper
import com.yizhuan.xchat_android_library.common.glide.GlideEngine
import com.yizhuan.xchat_android_library.common.util.Logger
import com.yizhuan.xchat_android_library.easyphoto.EasyPhotos
import com.yizhuan.xchat_android_library.easyphoto.constant.Type.*
import com.yizhuan.xchat_android_library.easyphoto.models.album.entity.Photo
import com.yizhuan.xchat_android_library.utils.TimeUtils
import com.yizhuan.xchat_android_library.utils.TimeUtils.TIME_FORMAT
import kotlinx.coroutines.*
import java.io.File
/**
* Created by wushaocheng on 2022/11/15
* Desc图片选择二次封装
*/
object PhotoProvider {
private const val TAG = "PhotoProvider"
//上一次选择的时间,避免用户连续进入选择图片页导致删除缓存
private var mLastSelectTime: Long by SpDelegate("PhotoProvider_last_select_time", 0L)
/**
* easyPhoto库选择文件copy到内部的目录名
*/
private const val FOLD_EASY_PHOTO_INTERNAL = "selectPhotoTemp"
private var mPhotoJob: Job? = null
@JvmStatic
@JvmOverloads
fun photoCamera(fragment: Fragment, resultCode: Int, isClearCache: Boolean = true) {
cancelJop()
mPhotoJob = MainScope().launch {
if (isClearCache && isClearByTime()) {
withContext(Dispatchers.IO) { clearCache() }
}
EasyPhotos.createCamera(fragment, false)//参数说明:上下文,是否使用宽高数据false时宽高数据为0扫描速度更快
.setFileProviderAuthority("${BaseApp.getApplication().packageName}.fileprovider")//参数说明:见下方`FileProvider的配置`
.start(resultCode)
}
}
/**
* 喵圈发布动态专用去掉bmp格式的图片
*/
@JvmStatic
@JvmOverloads
fun photoProviderPublish(activity: Activity, maxSelect: Int = 1, canChooseGif: Boolean = false, resultCode: Int, isClearCache: Boolean = true) {
cancelJop()
mPhotoJob = MainScope().launch {
if (isClearCache && isClearByTime()) {
withContext(Dispatchers.IO) { clearCache() }
}
EasyPhotos.createAlbum(activity, false, false, GlideEngine())//参数说明上下文是否显示相机按钮是否使用宽高数据false时宽高数据为0扫描速度更快[配置Glide为图片加载引擎](https://github.com/HuanTanSheng/EasyPhotos/wiki/12-%E9%85%8D%E7%BD%AEImageEngine%EF%BC%8C%E6%94%AF%E6%8C%81%E6%89%80%E6%9C%89%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E5%BA%93)
.setCount(maxSelect)//参数说明最大可选数默认1
.setGif(canChooseGif)
.filter(JPEG, JPG, PNG, WEBP)
.setPuzzleMenu(false)
.setCleanMenu(false)
.start(resultCode)
}
}
@JvmStatic
@JvmOverloads
fun videoProvider(activity: Activity, maxSelect: Int = 1, resultCode: Int, isClearCache: Boolean = true) {
cancelJop()
mPhotoJob = MainScope().launch {
if (isClearCache && isClearByTime()) {
withContext(Dispatchers.IO) { clearCache() }
}
EasyPhotos.createAlbum(activity, false, false, GlideEngine())
.setCount(maxSelect)//参数说明最大可选数默认1
.setPuzzleMenu(false)
.onlyVideo()
.setCleanMenu(false)
.start(resultCode)
}
}
@JvmStatic
@JvmOverloads
fun photoProvider(activity: Activity, maxSelect: Int = 1, canChooseGif: Boolean = false, resultCode: Int, isClearCache: Boolean = true) {
cancelJop()
mPhotoJob = MainScope().launch {
if (isClearCache && isClearByTime()) {
withContext(Dispatchers.IO) { clearCache() }
}
EasyPhotos.createAlbum(activity, false, false, GlideEngine())//参数说明上下文是否显示相机按钮是否使用宽高数据false时宽高数据为0扫描速度更快[配置Glide为图片加载引擎](https://github.com/HuanTanSheng/EasyPhotos/wiki/12-%E9%85%8D%E7%BD%AEImageEngine%EF%BC%8C%E6%94%AF%E6%8C%81%E6%89%80%E6%9C%89%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E5%BA%93)
.setCount(maxSelect)//参数说明最大可选数默认1
.setGif(canChooseGif)
.setPuzzleMenu(false)
.setCleanMenu(false)
.start(resultCode)
}
}
@JvmStatic
@JvmOverloads
fun photoProvider(activity: FragmentActivity, maxSelect: Int = 1, canChooseGif: Boolean = false, resultCode: Int, isClearCache: Boolean = true) {
cancelJop()
mPhotoJob = MainScope().launch {
if (isClearCache && isClearByTime()) {
withContext(Dispatchers.IO) { clearCache() }
}
EasyPhotos.createAlbum(activity, false, false, GlideEngine())//参数说明上下文是否显示相机按钮是否使用宽高数据false时宽高数据为0扫描速度更快[配置Glide为图片加载引擎](https://github.com/HuanTanSheng/EasyPhotos/wiki/12-%E9%85%8D%E7%BD%AEImageEngine%EF%BC%8C%E6%94%AF%E6%8C%81%E6%89%80%E6%9C%89%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E5%BA%93)
.setCount(maxSelect)//参数说明最大可选数默认1
.setGif(canChooseGif)
.setPuzzleMenu(false)
.setCleanMenu(false)
.start(resultCode)
}
}
@JvmStatic
@JvmOverloads
fun photoProvider(fragment: Fragment, maxSelect: Int = 1, canChooseGif: Boolean = false, resultCode: Int, isClearCache: Boolean = true, minFileSize: Long = 0L) {
cancelJop()
mPhotoJob = MainScope().launch {
if (isClearCache && isClearByTime()) {
withContext(Dispatchers.IO) { clearCache() }
}
EasyPhotos.createAlbum(fragment, false, false, GlideEngine())//参数说明上下文是否显示相机按钮是否使用宽高数据false时宽高数据为0扫描速度更快[配置Glide为图片加载引擎](https://github.com/HuanTanSheng/EasyPhotos/wiki/12-%E9%85%8D%E7%BD%AEImageEngine%EF%BC%8C%E6%94%AF%E6%8C%81%E6%89%80%E6%9C%89%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E5%BA%93)
.setCount(maxSelect)//参数说明最大可选数默认1
.setGif(canChooseGif)
.setMinFileSize(minFileSize)
.setPuzzleMenu(false)
.setCleanMenu(false)
.start(resultCode)
}
}
@JvmStatic
fun getResultUriList(data: Intent?): List<Uri>? {
val list: List<Photo>? = data?.getParcelableArrayListExtra(EasyPhotos.RESULT_PHOTOS)
return list?.takeIf { it.isNotEmpty() }?.map { it.uri }
}
@JvmStatic
fun getResultPhotoList(data: Intent?): List<Photo>? {
return data?.getParcelableArrayListExtra(EasyPhotos.RESULT_PHOTOS)
}
@JvmStatic
fun getResultPathListAsync(data: Intent?, resultListener: ((List<String>?) -> Unit)) {
cancelJop()
mPhotoJob = MainScope().launch {
val list: List<Photo>? = data?.getParcelableArrayListExtra(EasyPhotos.RESULT_PHOTOS)
val result = withContext(Dispatchers.IO) { copyToInternalCache(list) }
resultListener.invoke(result)
}
}
/**
* 外部的文件复制到项目目录下,再获取对应路径
* 修改方案原因:
* 1. android Q 外部文件path变更为类似这种结构/external/images/media/{文件id}导致无法通过path读取文件信息文件名字及格式
* 2. android Q 支持Uri获取文件但uri获取不到文件类型及不少自身或者sdk的函数从参数需要用到path
* 3. 原本项目功能逻辑很多用到了path(包括不仅仅文件大小,文件类型,作为参数传给其他函数使用(比如BitmapFactory.decodeFile))直接全局替换为Uri,影响面过大直接copy一份到自己内部返回内部的路径使得外部调用无感知
*
* 发现几个重点问题:
* 1. 项目使用到BitmapFactory.decodeFile(imgPath, options)之类的方法该方法在android Q直接使用外部path测试中发现获取图片信息失败
*
*/
private fun copyToInternalCache(photos: List<Photo>?): List<String>? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val foldPath = getInternalPath() + File.separator
val newPaths = ArrayList<String>()
photos?.forEach {
if (it.uri != null && !it.name.isNullOrEmpty()) {
val path = "$foldPath${it.name}"
if (FileHelper.copyFileFromUri(it.uri, path, true)) {
newPaths.add(path)
Logger.debug(TAG, "path: ${it.path} , displayName: ${it.name} , newPath: $path ")
}
}
}
newPaths
} else {
photos?.takeIf { it.isNotEmpty() }?.map { it.path }
}
}
/**
* 清除复制缓存
*/
fun clearCache() {
Logger.debug(
TAG, "clearCache => mLastSelectTime: ${TimeUtils.getDateTimeString(
mLastSelectTime, TIME_FORMAT)}")
FileHelper.removeAllFile(getInternalPath() + File.separator)
}
/**
* 检查时间,判断是否要删除缓冲
*/
private fun isClearByTime(): Boolean {
val currentTime = System.currentTimeMillis()
val isClear = currentTime - mLastSelectTime > 10 * 60 * 1000
mLastSelectTime = currentTime
return isClear
}
private fun cancelJop() {
if (mPhotoJob?.isActive == true) {
mPhotoJob?.cancel()
}
}
/**
* easyPhoto内部复制缓存的路径
*/
private fun getInternalPath(): String {
return ("${FileHelper.getRootFilesDir(Environment.DIRECTORY_PICTURES).absolutePath}${File.separator}$FOLD_EASY_PHOTO_INTERNAL")
}
}

View File

@@ -0,0 +1,96 @@
package com.yizhuan.xchat_android_library.common.transform
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.RectF
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import java.security.MessageDigest
/**
* author: lishangming
* e-mail: lishangming@miya818.com
* time: 2021/09/17
* desc: glide转化器原图片宽高短的一边设置为指定目标值长的自适应拉伸按照指定宽高比例和截取方式进行截取
*/
class AssignScaleTransformation(private val targetSize: Int, private val whRadio: Float, private val clipType: ClipType) : BitmapTransformation() {
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update(("AssignScaleTransformation(${targetSize}_${whRadio}_${clipType})").toByteArray())
}
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
return crop(pool, toTransform) ?: toTransform
}
private fun crop(pool: BitmapPool, source: Bitmap?): Bitmap? {
if (targetSize <= 0 || whRadio <= 0) return null
if (source == null) return null
//计算截取原文件的宽高
var clipWidth = 0
var clipHeight = 0
when {
source.width / whRadio <= source.height -> {
//用宽度计算,按比例拉伸高度比原本的小,故宽度不变,高度按比例设置
clipWidth = source.width
clipHeight = (source.width / whRadio).toInt()
}
source.height * whRadio <= source.width -> {
//用高度计算,按比例拉伸宽度比原本的小,故高度不变,宽度按比例设置
clipWidth = (source.height * whRadio).toInt()
clipHeight = source.height
}
else -> {
clipWidth = source.width
clipHeight = source.height
}
}
//需要生成图片的宽高
val resultWidth = targetSize
val resultHeight = (targetSize / whRadio).toInt()
//截取比例
val left = if (clipWidth < source.width) (source.width - clipWidth) / 2 else 0
val right = left + clipWidth
var result: Bitmap? = pool[resultWidth, resultHeight, Bitmap.Config.ARGB_8888]
if (result == null) {
result = Bitmap.createBitmap(resultWidth, resultHeight, Bitmap.Config.ARGB_8888)
}
val targetRect = RectF(0f, 0f, resultWidth.toFloat(), resultHeight.toFloat())
val sourceRect = when (clipType) {
ClipType.TOP -> {
//顶部截取,固定截取高度
Rect(left, 0, right, clipHeight)
}
else -> {
//默认中间截取
val top = if (clipHeight < source.height) (source.height - clipHeight) / 2 else 0
val bottom = top + clipHeight
Rect(left, top, right, bottom)
}
}
if (result != null) {
val canvas = Canvas(result)
if (clipWidth < source.width || clipHeight < source.height) {
canvas.drawBitmap(source, sourceRect, targetRect, null)
} else {
canvas.drawBitmap(source, null, targetRect, null)
}
}
return result
}
override fun equals(obj: Any?): Boolean {
return obj is AssignScaleTransformation
}
override fun hashCode(): Int {
return javaClass.name.hashCode()
}
enum class ClipType {
TOP,//截取顶部
CENTER,//截取中间
}
}

View File

@@ -0,0 +1,152 @@
package com.yizhuan.xchat_android_library.common.transform;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import java.security.MessageDigest;
/**
* Created by zhl on 2020/1/3.
*/
public class ComplexTransformation extends BitmapTransformation {
private ComplexParamsBuilder mBuilder;
public ComplexTransformation(@NonNull ComplexParamsBuilder builder) {
mBuilder = builder;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return crop(pool, toTransform, mBuilder);
}
private static Bitmap crop(BitmapPool pool, Bitmap source, ComplexParamsBuilder builder) {
if (source == null) return null;
int resultWidth = builder.mMaxWidth == 0 ? source.getWidth() : builder.mMaxWidth;
int resultHeight = builder.mMaxHeight == 0 ? source.getHeight() : builder.mMaxHeight;
if (builder.mIsNeedScale) {
if ((source.getWidth() > resultWidth || source.getHeight() > resultHeight)) {
float scaleX = (float) resultWidth / source.getWidth();
float scaleY = (float) resultHeight / source.getHeight();
float scale = Math.min(scaleX, scaleY);
resultWidth = (int) (scale * source.getWidth());
resultHeight = (int) (scale * source.getHeight());
} else {
resultWidth = source.getWidth();
resultHeight = source.getHeight();
}
}
Bitmap result = pool.get(resultWidth, resultHeight, Bitmap.Config.ARGB_8888);
RectF targetRect = new RectF(0, 0, resultWidth, resultHeight);
Canvas canvas = new Canvas(result);
if (builder.mIsNeedCorner && source.getWidth() == resultWidth && source.getHeight() == resultHeight) {
//该图没有过缩放,可直接绘制圆角
Paint paint = new Paint();
paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
canvas.drawRoundRect(targetRect, builder.mCorner, builder.mCorner, paint);
return result;
}
if (builder.mIsNeedCropCenter && source.getHeight() != source.getWidth()) {
int min = Math.min(source.getWidth(), source.getHeight());
Rect sourceRect = new Rect((source.getWidth() - min) / 2,
(source.getHeight() - min) / 2,
(source.getWidth() - min) / 2 + min,
(source.getHeight() - min) / 2 + min);
canvas.drawBitmap(source, sourceRect, targetRect, null);
} else {
canvas.drawBitmap(source, null, targetRect, null);
}
if (builder.mIsNeedCorner) {
Paint paint = new Paint();
paint.setShader(new BitmapShader(result, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
Bitmap cornerResult = pool.get(resultWidth, resultHeight, Bitmap.Config.ARGB_8888);
Canvas cornerCanvas = new Canvas(cornerResult);
cornerCanvas.drawRoundRect(targetRect, builder.mCorner, builder.mCorner, paint);
return cornerResult;
}
return result;
}
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
messageDigest.update(("ComplexTransformation(" + mBuilder.toString() + ")").getBytes());
}
public static class ComplexParamsBuilder {
int mMaxWidth;
int mMaxHeight;
boolean mIsNeedScale;
boolean mIsNeedCropCenter;
boolean mIsNeedCorner;
float mCorner;
public ComplexParamsBuilder setMaxWidth(int mMaxWidth) {
this.mMaxWidth = mMaxWidth;
return this;
}
public ComplexParamsBuilder setMaxHeight(int mMaxHeight) {
this.mMaxHeight = mMaxHeight;
return this;
}
public ComplexParamsBuilder setIsNeedScale(boolean mIsNeedScale) {
this.mIsNeedScale = mIsNeedScale;
return this;
}
public ComplexParamsBuilder setIsNeedCropCenter(boolean mIsNeedCropCenter) {
this.mIsNeedCropCenter = mIsNeedCropCenter;
return this;
}
public ComplexParamsBuilder setIsNeedCorner(boolean mIsNeedCorner) {
this.mIsNeedCorner = mIsNeedCorner;
return this;
}
public ComplexParamsBuilder setCorner(float mCorner) {
this.mCorner = mCorner;
return this;
}
@Override
public String toString() {
return "ComplexParamsBuilder{" +
"mMaxWidth=" + mMaxWidth +
", mMaxHeight=" + mMaxHeight +
", mIsNeedScale=" + mIsNeedScale +
", mIsNeedCropCenter=" + mIsNeedCropCenter +
", mIsNeedCorner=" + mIsNeedCorner +
", mCorner=" + mCorner +
'}';
}
}
@Override
public boolean equals(@Nullable Object obj) {
return obj instanceof ComplexTransformation;
}
@Override
public int hashCode() {
return getClass().getName().hashCode();
}
}

View File

@@ -0,0 +1,195 @@
package com.yizhuan.xchat_android_library.common.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Parcelable;
import com.tencent.mmkv.MMKV;
import java.util.Set;
/**
* author: wushaocheng
* time: 2022/11/15
* desc: 使用腾讯MMKV框架替代SharedPreference参考文档https://github.com/Tencent/MMKV/wiki/android_tutorial_cn
*/
public class Config {
private volatile static Config instance = null;
private MMKV mmkv;
public static Config getInstance(Context context) {
if (instance == null) {
synchronized (Config.class) {
if (instance == null) {
instance = new Config(context);
}
}
}
return instance;
}
private Config(Context context) {
try {
if (context == null) {
return;
}
MMKV.initialize(context);
mmkv = MMKV.mmkvWithID("config", MMKV.MULTI_PROCESS_MODE);
} catch (Exception e) {
e.printStackTrace();
}
}
public void setOnChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
if (listener != null && mmkv != null) {
mmkv.registerOnSharedPreferenceChangeListener(listener);
}
}
/************************************** 编码方法 **************************************/
public boolean putBytes(String key, byte[] bytes) {
if (mmkv == null) {
return false;
}
return mmkv.encode(key, bytes);
}
public boolean putInt(String key, int value) {
if (mmkv == null) {
return false;
}
return mmkv.encode(key, value);
}
public boolean putLong(String key, long value) {
if (mmkv == null) {
return false;
}
return mmkv.encode(key, value);
}
public boolean putFloat(String key, float value) {
if (mmkv == null) {
return false;
}
return mmkv.encode(key, value);
}
public boolean putDouble(String key, double value) {
if (mmkv == null) {
return false;
}
return mmkv.encode(key, value);
}
public boolean putBoolean(String key, boolean value) {
if (mmkv == null) {
return false;
}
return mmkv.encode(key, value);
}
public boolean putString(String key, String value) {
if (mmkv == null) {
return false;
}
return mmkv.encode(key, value);
}
public boolean putStringSet(String key, Set<String> value) {
if (mmkv == null) {
return false;
}
return mmkv.encode(key, value);
}
public boolean putParcelable(String key, Parcelable value) {
if (mmkv == null) {
return false;
}
return mmkv.encode(key, value);
}
/************************************** 解码方法 **************************************/
public byte[] getBytes(String key, byte[] defaultValue) {
if (mmkv == null) {
return defaultValue;
}
return mmkv.decodeBytes(key, defaultValue);
}
public int getInt(String key, int defaultValue) {
if (mmkv == null) {
return defaultValue;
}
return mmkv.decodeInt(key, defaultValue);
}
public long getLong(String key, long defaultValue) {
if (mmkv == null) {
return defaultValue;
}
return mmkv.decodeLong(key, defaultValue);
}
public float getFloat(String key, float defaultValue) {
if (mmkv == null) {
return defaultValue;
}
return mmkv.decodeFloat(key, defaultValue);
}
public double getDouble(String key, double defaultValue) {
if (mmkv == null) {
return defaultValue;
}
return mmkv.decodeDouble(key, defaultValue);
}
public boolean getBoolean(String key, boolean defaultValue) {
if (mmkv == null) {
return defaultValue;
}
return mmkv.decodeBool(key, defaultValue);
}
public String getString(String key, String defaultValue) {
if (mmkv == null) {
return defaultValue;
}
return mmkv.decodeString(key, defaultValue);
}
public Set<String> getStringSet(String key, Set<String> defaultValue) {
if (mmkv == null) {
return defaultValue;
}
return mmkv.decodeStringSet(key, defaultValue);
}
public <T extends Parcelable> T getParcelable(String key, Class<T> tClass, T defaultValue) {
if (mmkv == null) {
return defaultValue;
}
return mmkv.decodeParcelable(key, tClass, defaultValue);
}
/************************************** 清理方法 **************************************/
public void remove(String key) {
if (mmkv == null) {
return;
}
mmkv.remove(key);
}
public void clearAll() {
if (mmkv == null) {
return;
}
mmkv.clearAll();
}
}

View File

@@ -0,0 +1,107 @@
package com.yizhuan.xchat_android_library.common.util;
import android.os.Parcelable;
import com.yizhuan.xchat_android_library.common.application.BaseApp;
import com.yizhuan.xchat_android_library.utils.TimeUtils;
import java.util.Date;
import java.util.Set;
/**
* author: wushaocheng
* time: 2022/11/15
* desc: 封装底层com.tcloud.core.util.Config方便使用
*/
public class SPUtils {
private static Config sConfig = Config.getInstance(BaseApp.getContext());
public static void putBytes(String key, byte[] bytes) {
sConfig.putBytes(key, bytes);
}
public static void putInt(String key, int value) {
sConfig.putInt(key, value);
}
public static void putLong(String key, long value) {
sConfig.putLong(key, value);
}
public static void putFloat(String key, float value) {
sConfig.putFloat(key, value);
}
public static void putDouble(String key, double value) {
sConfig.putDouble(key, value);
}
public static void putBoolean(String key, boolean value) {
sConfig.putBoolean(key, value);
}
public static void putString(String key, String value) {
sConfig.putString(key, value);
}
public static void putStringSet(String key, Set<String> value) {
sConfig.putStringSet(key, value);
}
public static void putParcelable(String key, Parcelable value) {
sConfig.putParcelable(key, value);
}
public static byte[] getBytes(String key, byte[] defaultValue) {
return sConfig.getBytes(key, defaultValue);
}
public static int getInt(String key, int defaultValue) {
return sConfig.getInt(key, defaultValue);
}
public static long getLong(String key, long defaultValue) {
return sConfig.getLong(key, defaultValue);
}
public static float getFloat(String key, float defaultValue) {
return sConfig.getFloat(key, defaultValue);
}
public static double getDouble(String key, double defaultValue) {
return sConfig.getDouble(key, defaultValue);
}
public static boolean getBoolean(String key, boolean defaultValue) {
return sConfig.getBoolean(key, defaultValue);
}
public static String getString(String key, String defaultValue) {
return sConfig.getString(key, defaultValue);
}
public static Set<String> getStringSet(String key, Set<String> defaultValue) {
return sConfig.getStringSet(key, defaultValue);
}
public static <T extends Parcelable> T getParcelable(String key, Class<T> tClass, T defaultValue) {
return sConfig.getParcelable(key, tClass, defaultValue);
}
public static void remove(String key) {
sConfig.remove(key);
}
public static void clearAll() {
sConfig.clearAll();
}
public static String getSharedDataKey(String constants, long playerId) {
String toDayStr = TimeUtils.date2Str(new Date(), "yyyy-MM-dd");
return getAccountKey(constants, playerId) + toDayStr;
}
public static String getAccountKey(String key, long playerId) {
return key + playerId ;
}
}

View File

@@ -1,4 +1,7 @@
<resources>
<string name="text_bitmap_too_large">上傳失敗,圖片太大啦~</string>
<string name="text_bitmap_too_small">上傳圖片不能小於20kb</string>
<string name="yizhuan_xchat_android_core_env_01">請輸入正確的環境</string>
<string name="yizhuan_xchat_android_core_env_02">請輸入正確的環境</string>
<string name="yizhuan_xchat_android_core_env_03">請先初始化環境</string>
</resources>