文件管理模块重构。
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Mon Dec 01 18:48:19 GMT 2025
|
||||
#Mon Dec 01 21:56:35 GMT 2025
|
||||
stageCount=13
|
||||
libraryProject=
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.12
|
||||
buildCount=103
|
||||
buildCount=106
|
||||
baseBetaVersion=15.11.13
|
||||
|
||||
@@ -75,8 +75,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
private BackgroundView bvPreviewBackground;
|
||||
// 拍照临时文件(仅拍照用,路径由工具类间接管理)
|
||||
private File mfTakePhoto;
|
||||
// 新增:选图临时文件(独立管理,与拍照临时文件隔离)
|
||||
private File mSelectTempFile;
|
||||
// 配置标记(是否提交设置)
|
||||
boolean isCommitSettings = false;
|
||||
// 预览图片信息(用于退出确认)
|
||||
@@ -117,8 +115,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
selectTempDir.mkdirs();
|
||||
LogUtils.d(TAG, "【选图初始化】选图临时目录创建完成:" + selectTempDir.getAbsolutePath());
|
||||
}
|
||||
mSelectTempFile = new File(selectTempDir, "selected_temp_" + System.currentTimeMillis() + ".jpg");
|
||||
|
||||
|
||||
// 初始化UI及数据
|
||||
initToolbar();
|
||||
initClickListeners();
|
||||
@@ -240,12 +237,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "【按钮点击】触发选择图片功能");
|
||||
// 新增:选图前清理旧的选图临时文件(避免残留文件干扰)
|
||||
if (mSelectTempFile != null && mSelectTempFile.exists()) {
|
||||
boolean deleteSuccess = mSelectTempFile.delete();
|
||||
LogUtils.d(TAG, "【选图准备】旧选图临时文件清理:" + (deleteSuccess ? "成功" : "失败") + ",路径:" + mSelectTempFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
|
||||
// 调用权限工具类校验存储权限
|
||||
if (mPermissionUtils.checkAndRequestStoragePermission(BackgroundSettingsActivity.this)) {
|
||||
LogUtils.d(TAG, "【选图权限】存储权限已获取,开始查找图片选择意图");
|
||||
@@ -285,7 +277,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
Intent chooser = Intent.createChooser(validIntent, "选择图片");
|
||||
chooser.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
startActivityForResult(chooser, REQUEST_SELECT_PICTURE);
|
||||
LogUtils.d(TAG, "【选图意图】找到有效意图,已启动图片选择(临时文件路径:" + mSelectTempFile.getAbsolutePath() + ")");
|
||||
LogUtils.d(TAG, "【选图意图】找到有效意图,已启动图片选择。");
|
||||
} else {
|
||||
LogUtils.d(TAG, "【选图意图】未找到有效图片选择应用,提示用户安装");
|
||||
runOnUiThread(new Runnable() {
|
||||
@@ -489,24 +481,11 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
public void startCropImageActivity(boolean isCropFree) {
|
||||
LogUtils.d(TAG, "【裁剪启动】startCropImageActivity 触发,自由裁剪:" + isCropFree);
|
||||
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
previewBean.setIsUseBackgroundScaledCompressFile(true);
|
||||
previewBean.setIsUseBackgroundScaledCompressFile(false);
|
||||
mBgSourceUtils.saveSettings();
|
||||
bvPreviewBackground.reloadPreviewBackground();
|
||||
|
||||
// 1. 预览图片有效性校验(直接使用预览图原图,不复制)
|
||||
String previewFilePath = mBgSourceUtils.getPreviewBackgroundFilePath();
|
||||
if (TextUtils.isEmpty(previewFilePath)) {
|
||||
ToastUtils.show("预览图片路径为空");
|
||||
LogUtils.e(TAG, "【裁剪失败】预览图片路径为空");
|
||||
return;
|
||||
}
|
||||
File previewFile = new File(previewFilePath); // 裁剪原图:直接使用预览图文件
|
||||
LogUtils.d(TAG, "【裁剪优化】直接使用预览图原图启动裁剪(不复制):" + previewFile.getAbsolutePath());
|
||||
LogUtils.d(TAG, "【裁剪校验】预览图片状态:是否存在=" + previewFile.exists() + ",是否为文件=" + previewFile.isFile() + ",大小=" + (previewFile.exists() ? previewFile.length() : 0) + "bytes");
|
||||
if (!previewFile.exists() || !previewFile.isFile() || previewFile.length() <= 100) {
|
||||
ToastUtils.show("预览图片不存在或损坏");
|
||||
LogUtils.e(TAG, "【裁剪失败】预览图片无效");
|
||||
return;
|
||||
}
|
||||
File previewFile = mBgSourceUtils.getCropSourceFile(); // 裁剪缓存图片
|
||||
|
||||
// 2. 生成裁剪输入Uri(强化MIUI权限授予,直接用原图Uri)
|
||||
Uri inputUri = null;
|
||||
@@ -518,13 +497,12 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "【裁剪异常】生成输入Uri失败:" + e.getMessage(), e);
|
||||
ToastUtils.show("图片裁剪失败:无法获取图片权限");
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 【核心优化】直接使用预览图原图作为裁剪输入,删除原工具类创建裁剪路径的复制逻辑
|
||||
// 仅创建裁剪结果临时文件(用于接收裁剪输出,不涉及原图复制)
|
||||
File cropResultTempFile = mBgSourceUtils.createCropResultTempFile();
|
||||
File cropResultTempFile = mBgSourceUtils.getCropResultFile();
|
||||
if (cropResultTempFile == null) {
|
||||
ToastUtils.show("裁剪路径创建失败,请重试");
|
||||
return;
|
||||
@@ -609,13 +587,11 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
startActivityForResult(chooser, REQUEST_CROP_IMAGE);
|
||||
} else {
|
||||
ToastUtils.show("无可用裁剪工具,请安装系统相机");
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "【裁剪异常】启动裁剪工具失败:" + e.getMessage(), e);
|
||||
ToastUtils.show("无法启动裁剪工具");
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,7 +632,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
LogUtils.d(TAG, "【保存启动】开始保存裁剪图片(仅更新预览Bean,不影响正式Bean)");
|
||||
if (bitmap == null || bitmap.isRecycled()) {
|
||||
ToastUtils.show("裁剪图片为空");
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
//mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -678,9 +654,9 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
BufferedOutputStream bos = null;
|
||||
try {
|
||||
// 1. 清理旧的裁剪预览图(避免文件残留)
|
||||
if (cropSaveFile.exists()) {
|
||||
mBgSourceUtils.clearOldFileByExternal(cropSaveFile, "旧裁剪预览图");
|
||||
}
|
||||
// if (cropSaveFile.exists()) {
|
||||
// mBgSourceUtils.clearOldFileByExternal(cropSaveFile, "旧裁剪预览图");
|
||||
// }
|
||||
// 确保父目录存在(兼容Android 10+分区存储,多包名环境下目录适配)
|
||||
File parentDir = cropSaveFile.getParentFile();
|
||||
if (parentDir != null && !parentDir.exists()) {
|
||||
@@ -737,10 +713,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【裁剪保存失败】IO异常:" + e.getMessage(), e);
|
||||
ToastUtils.show("裁剪图片保存失败");
|
||||
// 异常时清理无效文件
|
||||
if (cropSaveFile.exists()) {
|
||||
mBgSourceUtils.clearOldFileByExternal(cropSaveFile, "异常裁剪图");
|
||||
}
|
||||
} finally {
|
||||
// 资源回收(避免内存泄漏,兼容低版本Android)
|
||||
if (bos != null) {
|
||||
@@ -766,9 +738,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 清理裁剪临时文件(保留原有逻辑,避免临时文件堆积)
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
LogUtils.d(TAG, "【裁剪保存】流程结束,临时文件已清理");
|
||||
LogUtils.d(TAG, "【裁剪保存】流程结束。");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -883,7 +853,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "【回调异常】onActivityResult 全局异常:" + e.getMessage(), e);
|
||||
ToastUtils.show("操作失败,请重试");
|
||||
mBgSourceUtils.clearCropTempFiles(); // 异常时清理临时文件,避免残留
|
||||
//mBgSourceUtils.clearCropTempFiles(); // 异常时清理临时文件,避免残留
|
||||
//clearSelectTempFile(); // 新增:异常时清理选图临时文件
|
||||
}
|
||||
}
|
||||
@@ -913,7 +883,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
// 校验拍照文件有效性
|
||||
if (!mfTakePhoto.exists() || mfTakePhoto.length() <= 0) {
|
||||
ToastUtils.show("拍照文件不存在或损坏");
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
//mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -923,7 +893,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
mBgSourceUtils.compressQualityToRecivedPicture(photoBitmap);
|
||||
} else {
|
||||
ToastUtils.show("拍照图片为空");
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
//mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -988,7 +958,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
*/
|
||||
private void handleCropImageResult(int requestCode, int resultCode, Intent data) {
|
||||
// 从工具类获取裁剪临时文件(唯一入口)
|
||||
File cropTempFile = mBgSourceUtils.getCropTempFile();
|
||||
File cropTempFile = mBgSourceUtils.getCropSourceFile();
|
||||
boolean isFileExist = cropTempFile != null && cropTempFile.exists();
|
||||
boolean isFileReadable = isFileExist ? cropTempFile.canRead() : false;
|
||||
long fileSize = isFileExist ? cropTempFile.length() : 0;
|
||||
@@ -1002,7 +972,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
if (resultCode == 0 && !isCropSuccess) {
|
||||
LogUtils.d(TAG, "【裁剪回调】MIUI 裁剪工具已取消");
|
||||
ToastUtils.show("裁剪已取消");
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
//mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1010,7 +980,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
if (isFileExist && fileSize == 0) {
|
||||
LogUtils.e(TAG, "【裁剪失败】裁剪文件为空(MIUI适配问题)");
|
||||
ToastUtils.show("裁剪失败,请尝试选择「系统相机」裁剪或更换图片");
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
//mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1024,7 +994,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
} else {
|
||||
ToastUtils.show("获取裁剪图片失败");
|
||||
LogUtils.e(TAG, "【裁剪回调失败】Bitmap解析异常");
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
//mBgSourceUtils.clearCropTempFiles();
|
||||
}
|
||||
} else {
|
||||
// 其他失败场景
|
||||
@@ -1153,8 +1123,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
Uri selectedImage = data.getData();
|
||||
if (selectedImage == null) {
|
||||
ToastUtils.show("选择的图片Uri为空");
|
||||
//clearSelectTempFile(); // 修复:取消时清理选图临时文件
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
LogUtils.d(TAG, "【选图回调】选择图片Uri : " + selectedImage.toString());
|
||||
@@ -1163,36 +1131,23 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
grantPersistableUriPermission(selectedImage);
|
||||
|
||||
// 关键修复1:解析Uri为文件(强制使用独立的选图临时文件,避免路径混淆)
|
||||
boolean parseSuccess = parseUriToSelectTempFile(selectedImage);
|
||||
if (!parseSuccess || mSelectTempFile == null || !mSelectTempFile.exists() || mSelectTempFile.length() <= 0) {
|
||||
mBgSourceUtils.createCropFileProviderPath(selectedImage);
|
||||
if (mBgSourceUtils.getCropSourceFile() == null || !mBgSourceUtils.getCropSourceFile().exists() || mBgSourceUtils.getCropSourceFile().length() <= 0) {
|
||||
ToastUtils.show("选择的图片文件无效或无法读取");
|
||||
//clearSelectTempFile();
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
LogUtils.d(TAG, "【选图解析】选图临时文件生成成功:" + mSelectTempFile.getAbsolutePath() + ",大小:" + mSelectTempFile.length() + "bytes");
|
||||
LogUtils.d(TAG, "【选图解析】选图临时文件生成成功:" + mBgSourceUtils.getCropSourceFile().getAbsolutePath() + ",大小:" + mBgSourceUtils.getCropSourceFile().length() + "bytes");
|
||||
|
||||
// 关键修复2:同步预览Bean(强制绑定「原图路径+压缩图路径」双路径,避免路径错位)
|
||||
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
if (previewBean == null) {
|
||||
ToastUtils.show("预览配置初始化失败");
|
||||
//clearSelectTempFile();
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
// 绑定原图路径(选图临时文件路径)
|
||||
previewBean.setBackgroundFilePath(mSelectTempFile.getAbsolutePath());
|
||||
|
||||
// 关键:强制绑定压缩图路径到BackgroundCrops目录(统一存储,避免路径错乱)
|
||||
String targetCompressPath = mBgSourceUtils.getPreviewBackgroundScaledCompressFilePath();
|
||||
File fPreviewBackgroundScaledCompressFile = new File(targetCompressPath);
|
||||
File fBackgroundCompressDir =fPreviewBackgroundScaledCompressFile.getParentFile();
|
||||
if (TextUtils.isEmpty(targetCompressPath) || !targetCompressPath.contains(fBackgroundCompressDir.getAbsolutePath())) {
|
||||
// 兜底生成BackgroundCrops目录下的压缩路径
|
||||
targetCompressPath = new File(fBackgroundCompressDir, "SelectCompress_" + System.currentTimeMillis() + ".jpg").getAbsolutePath();
|
||||
LogUtils.d(TAG, "【选图压缩】压缩路径为空/非法,生成兜底路径:" + targetCompressPath);
|
||||
}
|
||||
previewBean.setBackgroundScaledCompressFilePath(targetCompressPath);
|
||||
mBgSourceUtils.saveSettings(); // 立即持久化,避免旋转/退后台丢失路径
|
||||
String targetCompressPath = mBgSourceUtils.getCropResultFile().getAbsolutePath();
|
||||
|
||||
LogUtils.d(TAG, "【选图同步】预览Bean双路径绑定完成:");
|
||||
LogUtils.d(TAG, "→ 原图路径(选图临时文件):" + previewBean.getBackgroundFilePath());
|
||||
LogUtils.d(TAG, "→ 压缩图路径(BackgroundCrops):" + previewBean.getBackgroundScaledCompressFilePath());
|
||||
@@ -1204,7 +1159,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
// 优化:使用采样率加载Bitmap,避免OOM(原有直接decodeFile易导致大图片崩溃)
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(mSelectTempFile.getAbsolutePath(), options);
|
||||
BitmapFactory.decodeFile(mBgSourceUtils.getCropSourceFile().getAbsolutePath(), options);
|
||||
// 计算采样率(最大尺寸限制为2048px)
|
||||
int maxSize = 2048;
|
||||
int sampleSize = 1;
|
||||
@@ -1214,7 +1169,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = sampleSize;
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565; // 节省内存
|
||||
selectBitmap = BitmapFactory.decodeFile(mSelectTempFile.getAbsolutePath(), options);
|
||||
selectBitmap = BitmapFactory.decodeFile(mBgSourceUtils.getCropSourceFile().getAbsolutePath(), options);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "【选图压缩】Bitmap加载失败:" + e.getMessage(), e);
|
||||
}
|
||||
@@ -1225,8 +1180,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
} else {
|
||||
ToastUtils.show("选图后压缩图生成失败");
|
||||
LogUtils.e(TAG, "【选图压缩失败】无法解析选图文件为Bitmap");
|
||||
//clearSelectTempFile();
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1235,8 +1188,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
if (!compressFile.exists() || compressFile.length() <= 0) {
|
||||
ToastUtils.show("压缩图生成失败,请重新选择图片");
|
||||
LogUtils.e(TAG, "【选图压缩失败】压缩图文件无效:" + targetCompressPath);
|
||||
//clearSelectTempFile();
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1258,10 +1209,10 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
* 处理所有操作取消/失败(统一清理+提示)
|
||||
*/
|
||||
private void handleOperationCancelOrFail() {
|
||||
initPreviewBeanFromFormal();
|
||||
bvPreviewBackground.reloadPreviewBackground();
|
||||
LogUtils.d(TAG, "【操作回调】操作取消或失败");
|
||||
ToastUtils.show("操作已取消");
|
||||
//clearSelectTempFile(); // 新增:清理选图临时文件
|
||||
mBgSourceUtils.clearCropTempFiles();
|
||||
}
|
||||
|
||||
// ======================================== 权限回调(转发给工具类处理) ========================================
|
||||
@@ -1287,92 +1238,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析选图Uri到独立的选图临时文件(修复:避免与拍照临时文件混淆,强化可读性校验)
|
||||
*/
|
||||
private boolean parseUriToSelectTempFile(Uri uri) {
|
||||
// 清理旧的选图临时文件(避免残留文件干扰解析结果)
|
||||
if (mSelectTempFile != null && mSelectTempFile.exists()) {
|
||||
boolean deleteSuccess = mSelectTempFile.delete();
|
||||
LogUtils.d(TAG, "【选图解析】旧选图临时文件清理:" + (deleteSuccess ? "成功" : "失败") + ",路径:" + mSelectTempFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
InputStream is = null;
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
// 1. 打开Uri输入流(兼容content:///file:// 等多种Uri格式)
|
||||
is = getContentResolver().openInputStream(uri);
|
||||
if (is == null) {
|
||||
LogUtils.e(TAG, "【选图解析】ContentResolver打开Uri失败,Uri:" + uri.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 初始化选图临时文件输出流(Java7 手动创建流,不依赖try-with-resources)
|
||||
fos = new FileOutputStream(mSelectTempFile);
|
||||
byte[] buffer = new byte[1024 * 8]; // 8KB缓冲区,平衡读写性能与内存占用
|
||||
int readLen; // 每次读取的字节长度
|
||||
|
||||
// 3. 流复制(Java7 标准while循环,避免Java8+语法)
|
||||
while ((readLen = is.read(buffer)) != -1) {
|
||||
fos.write(buffer, 0, readLen); // 精准写入读取到的字节,避免空字节填充
|
||||
}
|
||||
|
||||
// 4. 强制同步写入磁盘(解决Android 10+ 异步写入导致的文件无效问题)
|
||||
fos.flush();
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.getFD().sync(); // 确保数据写入物理磁盘,而非缓存
|
||||
} catch (IOException e) {
|
||||
LogUtils.w(TAG, "【选图解析】文件同步到磁盘失败,用flush()兜底:" + e.getMessage());
|
||||
fos.flush();
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 双重校验临时文件有效性(解决Android14+ canRead()假阳性问题)
|
||||
if (!isFileActuallyReadable(mSelectTempFile)) {
|
||||
LogUtils.e(TAG, "【选图解析】临时文件存在但无实际读取权限,路径:" + mSelectTempFile.getAbsolutePath());
|
||||
if (mSelectTempFile.exists()) {
|
||||
mSelectTempFile.delete(); // 删除不可读文件,避免后续流程异常
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 6. 解析成功日志(打印文件信息,便于问题排查)
|
||||
LogUtils.d(TAG, "【选图解析】Uri解析成功!");
|
||||
LogUtils.d(TAG, "→ 原Uri:" + uri.toString());
|
||||
LogUtils.d(TAG, "→ 目标临时文件:" + mSelectTempFile.getAbsolutePath());
|
||||
LogUtils.d(TAG, "→ 文件大小:" + mSelectTempFile.length() + " bytes");
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
// 捕获所有异常(IO异常/空指针等),避免崩溃
|
||||
LogUtils.e(TAG, "【选图解析】流复制异常:" + e.getMessage(), e);
|
||||
// 异常时清理无效文件,防止残留
|
||||
if (mSelectTempFile != null && mSelectTempFile.exists()) {
|
||||
mSelectTempFile.delete();
|
||||
LogUtils.d(TAG, "【选图解析】异常时清理无效临时文件:" + mSelectTempFile.getAbsolutePath());
|
||||
}
|
||||
return false;
|
||||
|
||||
} finally {
|
||||
// 7. 手动关闭流资源(Java7 标准写法,避免内存泄漏)
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【选图解析】输入流关闭失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【选图解析】输出流关闭失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精准校验文件是否实际可读取(核心用途:解决Android14+ 中 File.canRead() 假阳性问题,避免“文件存在但无法读取”)
|
||||
* 逻辑:不依赖canRead(),而是通过实际打开文件流读取1字节,验证是否真的有读取权限(最可靠的校验方式)
|
||||
|
||||
@@ -3,12 +3,12 @@ package cc.winboll.studio.powerbell.utils;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.ExifInterface;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.BuildConfig;
|
||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||
import java.io.BufferedOutputStream;
|
||||
@@ -18,6 +18,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -28,14 +29,15 @@ public class BackgroundSourceUtils {
|
||||
|
||||
public static final String TAG = "BackgroundSourceUtils";
|
||||
// 裁剪相关常量(统一定义,避免硬编码)
|
||||
private static final String CROP_TEMP_DIR_NAME = "cache"; // 裁剪缓存目录(基础目录下)
|
||||
private static final String CROP_CACHE_DIR_NAME = "cache"; // 裁剪缓存目录(基础目录下)
|
||||
private static final String CROP_TEMP_FILE_NAME = "SourceCropTemp.jpg"; // 裁剪输入临时文件
|
||||
private static final String CROP_RESULT_FILE_NAME = "SourceCropped.jpg"; // 裁剪输出结果文件
|
||||
public static final String FILE_PROVIDER_AUTHORITY = BuildConfig.APPLICATION_ID + ".fileprovider"; // 多包名兼容
|
||||
// 图片操作基础目录(核心:系统公共图片目录)
|
||||
private static final String PICTURE_BASE_DIR = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + "PowerBell";
|
||||
// 新增:压缩图统一存储目录(图片基础目录下/BackgroundCrops)
|
||||
private static final String COMPRESS_BASE_DIR_NAME = "BackgroundCrops";
|
||||
private static final String SOURCE_DIR_NAME = "BackgroundSource";
|
||||
private static final String COMPRESS_DIR_NAME = "BackgroundCompress";
|
||||
|
||||
// 1. 静态实例加volatile,禁止指令重排,保证可见性(双重校验锁单例核心)
|
||||
private static volatile BackgroundSourceUtils sInstance;
|
||||
@@ -48,15 +50,15 @@ public class BackgroundSourceUtils {
|
||||
// 2. 统一文件目录(分两类:图片目录→系统公共目录,JSON目录→应用外置存储)
|
||||
// 图片操作目录(系统公共目录:/storage/emulated/0/Pictures/PowerBell/)
|
||||
private File fPictureBaseDir; // 图片基础目录
|
||||
private File fPictureCacheDir; // 裁剪缓存目录(基础目录下/cache)
|
||||
private File fCropCacheDir; // 裁剪缓存目录(基础目录下/cache)
|
||||
private File fBackgroundSourceDir; // 图片存储目录(基础目录下,存储正式/预览原图)
|
||||
private File fBackgroundCompressDir; // 新增:压缩图统一存储目录(基础目录下/BackgroundCrops)
|
||||
// JSON配置目录(原应用外置存储目录,不改变)
|
||||
private File fUtilsDir; // 工具类根目录(/Android/data/包名/files/BackgroundSourceUtils)
|
||||
private File fModelDir; // 模型文件目录(存储JSON配置)
|
||||
// 裁剪文件(统一放入图片基础目录下的cache)
|
||||
private File cropTempFile; // 裁剪临时文件(fPictureCacheDir下)
|
||||
private File cropResultFile; // 裁剪结果文件(fBackgroundSourceDir下)
|
||||
private File mCropSourceFile; // 裁剪临时文件(fCropCacheDir下)
|
||||
private File mCropResultFile; // 裁剪临时文件(fCropCacheDir下)
|
||||
|
||||
// 3. 私有构造器(加防反射逻辑+初始化所有目录/文件)
|
||||
private BackgroundSourceUtils(Context context) {
|
||||
@@ -107,25 +109,26 @@ public class BackgroundSourceUtils {
|
||||
// 1. 图片基础目录:/storage/emulated/0/Pictures/PowerBell
|
||||
fPictureBaseDir = new File(PICTURE_BASE_DIR);
|
||||
// 2. 图片存储目录:基础目录下(存储正式/预览原图)
|
||||
fBackgroundSourceDir = new File(fPictureBaseDir, "BackgroundSource");
|
||||
fBackgroundSourceDir = new File(fPictureBaseDir, SOURCE_DIR_NAME);
|
||||
// 3. 裁剪缓存目录:基础目录下/cache(所有裁剪操作在此目录)
|
||||
fPictureCacheDir = new File(fPictureBaseDir, CROP_TEMP_DIR_NAME);
|
||||
fCropCacheDir = new File(fPictureBaseDir, CROP_CACHE_DIR_NAME);
|
||||
// 4. 新增:压缩图统一存储目录(基础目录下/BackgroundCrops,所有压缩图放这里)
|
||||
fBackgroundCompressDir = new File(fPictureBaseDir, COMPRESS_BASE_DIR_NAME);
|
||||
fBackgroundCompressDir = new File(fPictureBaseDir, COMPRESS_DIR_NAME);
|
||||
|
||||
// 5. 强制创建所有图片目录(带二次校验+降级兜底)
|
||||
createDirWithPermission(fPictureBaseDir, "图片基础目录(/Pictures/PowerBell)", true);
|
||||
createDirWithPermission(fBackgroundSourceDir, "图片存储目录(基础目录下)", true);
|
||||
createDirWithPermission(fPictureCacheDir, "裁剪缓存目录(基础目录/cache)", true);
|
||||
createDirWithPermission(fBackgroundCompressDir, "压缩图统一存储目录(基础目录/BackgroundCrops)", true);
|
||||
createDirWithPermission(fPictureBaseDir, "图片基础目录(" + PICTURE_BASE_DIR + ")");
|
||||
createDirWithPermission(fBackgroundSourceDir, "图片存储目录(基础目录下/" + SOURCE_DIR_NAME + ")");
|
||||
createDirWithPermission(fCropCacheDir, "裁剪缓存目录(基础目录/" + CROP_CACHE_DIR_NAME + ")");
|
||||
createDirWithPermission(fBackgroundCompressDir, "裁剪压缩图存储目录(基础目录/" + COMPRESS_DIR_NAME + ")");
|
||||
|
||||
// 6. 目录创建后最终校验(确保所有目录已就绪)
|
||||
validatePictureDirs();
|
||||
|
||||
LogUtils.d(TAG, "【图片目录初始化】完成:" +
|
||||
"基础目录=" + fPictureBaseDir.getAbsolutePath() +
|
||||
",裁剪缓存目录=" + fPictureCacheDir.getAbsolutePath() +
|
||||
",压缩图目录=" + fBackgroundCompressDir.getAbsolutePath());
|
||||
"图片存储目录=" + fBackgroundSourceDir.getAbsolutePath() +
|
||||
",裁剪缓存目录=" + fCropCacheDir.getAbsolutePath() +
|
||||
",裁剪压缩图存储目录=" + fBackgroundCompressDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,12 +140,12 @@ public class BackgroundSourceUtils {
|
||||
fUtilsDir = mContext.getExternalFilesDir(TAG);
|
||||
if (fUtilsDir == null) {
|
||||
LogUtils.e(TAG, "【JSON目录】应用外置存储不可用,切换到应用内部缓存目录");
|
||||
fUtilsDir = mContext.getCacheDir();
|
||||
fUtilsDir = mContext.getDataDir();
|
||||
}
|
||||
// 2. 模型文件目录(存储JSON配置)
|
||||
fModelDir = new File(fUtilsDir, "ModelDir");
|
||||
// 强制创建JSON目录(带二次校验+降级兜底)
|
||||
createDirWithPermission(fModelDir, "JSON配置目录(应用外置存储)", true);
|
||||
createDirWithPermission(fModelDir, "JSON配置目录(应用外置存储)");
|
||||
|
||||
// 3. 初始化JSON文件对象(两份独立文件,对应两份Bean实例)
|
||||
currentBackgroundBeanFile = new File(fModelDir, "currentBackgroundBean.json");
|
||||
@@ -155,114 +158,20 @@ public class BackgroundSourceUtils {
|
||||
* 【核心强化】创建目录并设置权限(适配系统公共目录/Pictures/PowerBell,确保实例化时目录就绪)
|
||||
* @param dir 要创建的目录
|
||||
* @param dirDesc 目录描述(用于日志打印)
|
||||
* @param needFallback 是否需要降级兜底(实例化初期必须为true,确保目录可用)
|
||||
*/
|
||||
private void createDirWithPermission(File dir, String dirDesc, boolean needFallback) {
|
||||
private void createDirWithPermission(File dir, String dirDesc) {
|
||||
if (dir == null) {
|
||||
LogUtils.e(TAG, "【文件管理】创建目录失败:目录对象为null(描述:" + dirDesc + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
// 第一步:主动检测并创建目录(递归创建所有父目录)
|
||||
boolean isCreated = true;
|
||||
if (!dir.exists()) {
|
||||
if (!dir.exists()) {
|
||||
LogUtils.d(TAG, "【文件管理】" + dirDesc + "不存在,开始创建:" + dir.getAbsolutePath());
|
||||
isCreated = dir.mkdirs(); // 递归创建所有父目录
|
||||
dir.mkdirs(); // 递归创建所有父目录
|
||||
} else {
|
||||
LogUtils.d(TAG, "【文件管理】" + dirDesc + "已存在:" + dir.getAbsolutePath());
|
||||
}
|
||||
|
||||
// 第二步:创建后二次校验(确保目录真的存在且是目录)
|
||||
if (!dir.exists() || !dir.isDirectory()) {
|
||||
LogUtils.w(TAG, "【文件管理】" + dirDesc + "创建/校验失败,路径:" + dir.getAbsolutePath());
|
||||
// 第三步:需要降级时,自动切换到应用内部缓存目录(兜底保障)
|
||||
if (needFallback) {
|
||||
File fallbackDir = getFallbackDir(dirDesc);
|
||||
if (fallbackDir != null) {
|
||||
LogUtils.w(TAG, "【文件管理】" + dirDesc + "降级到备选目录:" + fallbackDir.getAbsolutePath());
|
||||
// 更新目录引用为备选目录(确保后续业务调用使用有效目录)
|
||||
updateDirReference(dir, fallbackDir);
|
||||
// 强制创建备选目录
|
||||
if (!fallbackDir.exists()) {
|
||||
fallbackDir.mkdirs();
|
||||
}
|
||||
// 二次校验备选目录
|
||||
if (fallbackDir.exists() && fallbackDir.isDirectory()) {
|
||||
LogUtils.d(TAG, "【文件管理】" + dirDesc + "备选目录创建成功:" + fallbackDir.getAbsolutePath());
|
||||
} else {
|
||||
LogUtils.e(TAG, "【文件管理】" + dirDesc + "备选目录创建失败,功能可能异常!");
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 第四步:强制设置目录权限(递归设置,确保系统公共目录所有层级可读写)
|
||||
setDirPermissionsRecursively(dir);
|
||||
// 第五步:校验目录实际可写性(解决Android14+ canWrite()假阳性问题)
|
||||
if (!isDirActuallyWritable(dir)) {
|
||||
LogUtils.w(TAG, "【文件管理】" + dirDesc + "存在但不可写,尝试重新设置权限");
|
||||
setDirPermissionsRecursively(dir);
|
||||
// 再次校验仍失败,降级兜底
|
||||
if (needFallback && !isDirActuallyWritable(dir)) {
|
||||
File fallbackDir = getFallbackDir(dirDesc);
|
||||
if (fallbackDir != null) {
|
||||
LogUtils.w(TAG, "【文件管理】" + dirDesc + "不可写,降级到备选目录:" + fallbackDir.getAbsolutePath());
|
||||
updateDirReference(dir, fallbackDir);
|
||||
fallbackDir.mkdirs();
|
||||
setDirPermissionsRecursively(fallbackDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, "【文件管理】" + dirDesc + "创建+权限设置完成:路径=" + dir.getAbsolutePath() + ",可写=" + dir.canWrite());
|
||||
}
|
||||
|
||||
/**
|
||||
* 【新增】获取目录降级备选目录(实例化初期目录创建失败时兜底)
|
||||
* 优先使用应用内部缓存目录(无需额外权限,兼容性最好)
|
||||
*/
|
||||
private File getFallbackDir(String dirDesc) {
|
||||
File cacheDir = mContext.getCacheDir();
|
||||
if (cacheDir == null) {
|
||||
LogUtils.e(TAG, "【文件管理】应用内部缓存目录不可用,无法降级:" + dirDesc);
|
||||
return null;
|
||||
}
|
||||
// 根据目录类型创建对应备选目录(保持目录结构一致)
|
||||
if (dirDesc.contains("图片基础目录")) {
|
||||
return new File(cacheDir, "PowerBell");
|
||||
} else if (dirDesc.contains("图片存储目录")) {
|
||||
return new File(cacheDir, "PowerBell/BackgroundSource");
|
||||
} else if (dirDesc.contains("裁剪缓存目录")) {
|
||||
return new File(cacheDir, "PowerBell/cache");
|
||||
} else if (dirDesc.contains("压缩图统一存储目录")) {
|
||||
return new File(cacheDir, "PowerBell/BackgroundCrops");
|
||||
} else if (dirDesc.contains("JSON配置目录")) {
|
||||
return new File(cacheDir, "BackgroundSourceUtils/ModelDir");
|
||||
} else {
|
||||
return new File(cacheDir, "FallbackDir");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 【新增】更新目录引用(目录创建失败降级时,同步更新全局目录变量)
|
||||
*/
|
||||
private void updateDirReference(File oldDir, File newDir) {
|
||||
if (oldDir == null || newDir == null) {
|
||||
return;
|
||||
}
|
||||
// 匹配全局目录变量,更新为备选目录
|
||||
if (oldDir.equals(fPictureBaseDir)) {
|
||||
fPictureBaseDir = newDir;
|
||||
} else if (oldDir.equals(fBackgroundSourceDir)) {
|
||||
fBackgroundSourceDir = newDir;
|
||||
} else if (oldDir.equals(fPictureCacheDir)) {
|
||||
fPictureCacheDir = newDir;
|
||||
} else if (oldDir.equals(fBackgroundCompressDir)) {
|
||||
fBackgroundCompressDir = newDir;
|
||||
} else if (oldDir.equals(fModelDir)) {
|
||||
fModelDir = newDir;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,8 +188,8 @@ public class BackgroundSourceUtils {
|
||||
LogUtils.e(TAG, "【图片目录校验】图片存储目录未就绪:" + fBackgroundSourceDir.getAbsolutePath());
|
||||
allReady = false;
|
||||
}
|
||||
if (!fPictureCacheDir.exists() || !fPictureCacheDir.isDirectory()) {
|
||||
LogUtils.e(TAG, "【图片目录校验】裁剪缓存目录未就绪:" + fPictureCacheDir.getAbsolutePath());
|
||||
if (!fCropCacheDir.exists() || !fCropCacheDir.isDirectory()) {
|
||||
LogUtils.e(TAG, "【图片目录校验】裁剪缓存目录未就绪:" + fCropCacheDir.getAbsolutePath());
|
||||
allReady = false;
|
||||
}
|
||||
if (!fBackgroundCompressDir.exists() || !fBackgroundCompressDir.isDirectory()) {
|
||||
@@ -298,156 +207,138 @@ public class BackgroundSourceUtils {
|
||||
* 初始化所有文件(裁剪文件→图片缓存目录,结果文件→图片存储目录)
|
||||
*/
|
||||
private void initAllFiles() {
|
||||
// 1. 裁剪临时文件(图片基础目录/cache下,系统裁剪可读写)
|
||||
cropTempFile = new File(fPictureCacheDir, CROP_TEMP_FILE_NAME);
|
||||
// 2. 裁剪结果文件(图片存储目录下,最终保存的裁剪图)
|
||||
cropResultFile = new File(fBackgroundSourceDir, CROP_RESULT_FILE_NAME);
|
||||
// 1. 裁剪临时文件
|
||||
mCropSourceFile = new File(fCropCacheDir, CROP_TEMP_FILE_NAME);
|
||||
// 2. 裁剪结果文件
|
||||
//cropResultFile = new File(fCropCacheDir, CROP_RESULT_FILE_NAME);
|
||||
|
||||
// 3. 初始化时清理旧文件(复用FileUtils简化清理逻辑)
|
||||
clearOldFile(cropTempFile, "旧裁剪临时文件(/Pictures/PowerBell/cache)");
|
||||
clearOldFile(cropResultFile, "旧裁剪结果文件(/Pictures/PowerBell/BackgroundSource)");
|
||||
// 新增:清理压缩图目录下的旧文件(避免残留)
|
||||
clearOldCompressFiles();
|
||||
|
||||
LogUtils.d(TAG, "【文件初始化】完成:裁剪临时文件=" + cropTempFile.getAbsolutePath() + ",裁剪结果文件=" + cropResultFile.getAbsolutePath());
|
||||
// 新增:清理压缩图目录下的旧文件(避免残留)
|
||||
clearCropTempFiles();
|
||||
LogUtils.d(TAG, "【文件初始化】完成。");
|
||||
}
|
||||
|
||||
|
||||
public boolean createCropFileProviderPath(Uri uri) {
|
||||
InputStream is = null;
|
||||
FileOutputStream fos = null;
|
||||
|
||||
try {
|
||||
clearCropTempFiles();
|
||||
String newCropFileName = UUID.randomUUID().toString() + System.currentTimeMillis();
|
||||
mCropSourceFile = new File(fCropCacheDir, newCropFileName + ".jpg");
|
||||
mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + ".jpg");
|
||||
|
||||
// 1. 打开Uri输入流(兼容content:///file:// 等多种Uri格式)
|
||||
is = mContext.getContentResolver().openInputStream(uri);
|
||||
if (is == null) {
|
||||
LogUtils.e(TAG, "【选图解析】ContentResolver打开Uri失败,Uri:" + uri.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 初始化选图临时文件输出流(Java7 手动创建流,不依赖try-with-resources)
|
||||
fos = new FileOutputStream(mCropSourceFile);
|
||||
byte[] buffer = new byte[1024 * 8]; // 8KB缓冲区,平衡读写性能与内存占用
|
||||
int readLen; // 每次读取的字节长度
|
||||
|
||||
// 3. 流复制(Java7 标准while循环,避免Java8+语法)
|
||||
while ((readLen = is.read(buffer)) != -1) {
|
||||
fos.write(buffer, 0, readLen); // 精准写入读取到的字节,避免空字节填充
|
||||
}
|
||||
|
||||
// 4. 强制同步写入磁盘(解决Android 10+ 异步写入导致的文件无效问题)
|
||||
fos.flush();
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.getFD().sync(); // 确保数据写入物理磁盘,而非缓存
|
||||
} catch (IOException e) {
|
||||
LogUtils.w(TAG, "【选图解析】文件同步到磁盘失败,用flush()兜底:" + e.getMessage());
|
||||
fos.flush();
|
||||
}
|
||||
}
|
||||
|
||||
previewBackgroundBean.setBackgroundFileName(mCropSourceFile.getName());
|
||||
previewBackgroundBean.setBackgroundFilePath(mCropSourceFile.getAbsolutePath());
|
||||
|
||||
previewBackgroundBean.setBackgroundScaledCompressFileName(mCropResultFile.getName());
|
||||
previewBackgroundBean.setBackgroundScaledCompressFilePath(mCropResultFile.getAbsolutePath());
|
||||
|
||||
// 6. 解析成功日志(打印文件信息,便于问题排查)
|
||||
LogUtils.d(TAG, "【选图解析】Uri解析成功!");
|
||||
LogUtils.d(TAG, "→ 原Uri:" + uri.toString());
|
||||
LogUtils.d(TAG, "→ 目标临时文件:" + mCropSourceFile.getAbsolutePath());
|
||||
LogUtils.d(TAG, "→ 目标临时文件大小:" + mCropSourceFile.length() + " bytes");
|
||||
LogUtils.d(TAG, "→ 目标剪裁临时文件:" + mCropResultFile.getAbsolutePath());
|
||||
LogUtils.d(TAG, "→ 目标剪裁临时文件大小:" + mCropResultFile.length() + " bytes");
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
// 捕获所有异常(IO异常/空指针等),避免崩溃
|
||||
LogUtils.e(TAG, "【选图解析】流复制异常:" + e.getMessage(), e);
|
||||
// 异常时清理无效文件,防止残留
|
||||
clearCropTempFiles();
|
||||
return false;
|
||||
|
||||
} finally {
|
||||
// 7. 手动关闭流资源(Java7 标准写法,避免内存泄漏)
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【选图解析】输入流关闭失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【选图解析】输出流关闭失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心优化函数:带原图参数的裁剪路径创建(优先使用原图,不复制)
|
||||
* 替代原逻辑中"复制原图到BackgroundSource再裁剪"的流程
|
||||
*/
|
||||
public File createCropFileProviderPath(File originalImageFile) {
|
||||
final String TAG = "BackgroundSourceUtils";
|
||||
Log.d(TAG, "【裁剪优化】createCropFileProviderPath(原图) 触发,优先使用原图路径");
|
||||
|
||||
// 核心逻辑1:直接使用裁剪前的原图(不复制),仅校验合法性和权限
|
||||
if (originalImageFile != null && originalImageFile.exists()
|
||||
&& originalImageFile.isFile() && originalImageFile.length() > 0) {
|
||||
// 校验原图目录是否可写(系统裁剪工具需读写权限)
|
||||
if (isDirectoryWritable(originalImageFile.getParentFile())) {
|
||||
// 强制开放原图权限(解决跨进程访问问题)
|
||||
setFileReadWritePermission(originalImageFile);
|
||||
Log.d(TAG, "【裁剪优化】直接使用原图启动裁剪(无复制):" + originalImageFile.getAbsolutePath());
|
||||
return originalImageFile; // 直接返回原图,不做任何复制
|
||||
} else {
|
||||
Log.w(TAG, "【裁剪优化】原图目录不可写,切换到临时文件兜底");
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "【裁剪优化】原图无效(空/不存在/0大小),切换到临时文件兜底");
|
||||
}
|
||||
|
||||
// 兜底逻辑(仅原图不可用时触发,避免多复制):使用应用缓存目录(不涉及BackgroundSource)
|
||||
File cacheDir = mContext.getCacheDir(); // 应用内部缓存,无需额外权限
|
||||
if (isDirectoryWritable(cacheDir)) {
|
||||
try {
|
||||
File cropTempFile = new File(cacheDir, "crop_temp_" + System.currentTimeMillis() + ".jpg");
|
||||
// 清理旧临时文件(避免堆积)
|
||||
if (cropTempFile.exists()) {
|
||||
cropTempFile.delete();
|
||||
}
|
||||
cropTempFile.createNewFile();
|
||||
setFileReadWritePermission(cropTempFile);
|
||||
Log.d(TAG, "【裁剪优化】原图不可用,使用缓存临时文件:" + cropTempFile.getAbsolutePath());
|
||||
return cropTempFile;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "【裁剪优化】创建缓存临时文件失败:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 终极兜底:公共图片缓存目录(极端情况)
|
||||
File publicCacheDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Cache");
|
||||
if (isDirectoryWritable(publicCacheDir)) {
|
||||
try {
|
||||
if (!publicCacheDir.exists()) {
|
||||
publicCacheDir.mkdirs();
|
||||
}
|
||||
File cropTempFile = new File(publicCacheDir, "crop_temp_" + System.currentTimeMillis() + ".jpg");
|
||||
if (cropTempFile.exists()) {
|
||||
cropTempFile.delete();
|
||||
}
|
||||
cropTempFile.createNewFile();
|
||||
setFileReadWritePermission(cropTempFile);
|
||||
Log.w(TAG, "【裁剪优化】应用缓存不可用,使用公共图片缓存:" + cropTempFile.getAbsolutePath());
|
||||
return cropTempFile;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "【裁剪优化】创建公共临时文件失败:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
Log.e(TAG, "【裁剪优化】所有裁剪路径创建失败,裁剪功能不可用");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【新增辅助方法】创建裁剪结果临时文件(仅用于接收裁剪输出,不涉及原图复制)
|
||||
* 避免使用工具类的createCropFileProviderPath(原方法可能存在复制逻辑)
|
||||
*/
|
||||
public File createCropResultTempFile() {
|
||||
// 优先使用应用缓存目录(无需额外权限,避免存储权限问题)
|
||||
File cacheDir = mContext.getCacheDir();
|
||||
if (isDirectoryWritable(cacheDir)) {
|
||||
try {
|
||||
String tempFileName = "crop_result_" + System.currentTimeMillis() + ".jpg";
|
||||
File cropTempFile = new File(cacheDir, tempFileName);
|
||||
// 清理旧临时文件(避免堆积)
|
||||
if (cropTempFile.exists()) {
|
||||
cropTempFile.delete();
|
||||
}
|
||||
cropTempFile.createNewFile();
|
||||
// 开放读写权限(供系统裁剪工具访问)
|
||||
setFileReadWritePermission(cropTempFile);
|
||||
LogUtils.d(TAG, "【裁剪优化】裁剪结果临时文件创建成功:" + cropTempFile.getAbsolutePath());
|
||||
return cropTempFile;
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【裁剪优化】创建缓存临时文件失败:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 兜底:应用外部临时目录
|
||||
File externalTempDir = new File(App.getTempDirPath());
|
||||
if (isDirectoryWritable(externalTempDir)) {
|
||||
try {
|
||||
String tempFileName = "crop_result_" + System.currentTimeMillis() + ".jpg";
|
||||
File cropTempFile = new File(externalTempDir, tempFileName);
|
||||
if (cropTempFile.exists()) {
|
||||
cropTempFile.delete();
|
||||
}
|
||||
cropTempFile.createNewFile();
|
||||
setFileReadWritePermission(cropTempFile);
|
||||
LogUtils.w(TAG, "【裁剪优化】应用缓存不可用,使用外部临时目录:" + cropTempFile.getAbsolutePath());
|
||||
return cropTempFile;
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【裁剪优化】创建外部临时文件失败:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
LogUtils.e(TAG, "【裁剪优化】裁剪结果临时文件创建失败");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容旧调用的无参方法(避免影响其他代码)
|
||||
*/
|
||||
public File createCropFileProviderPath() {
|
||||
return createCropFileProviderPath(null); // 无原图时走兜底逻辑
|
||||
}
|
||||
// File createFileProviderPath(File originalImageFile) {
|
||||
// Log.d(TAG, "【裁剪优化】createCropFileProviderPath(原图) 触发,优先使用原图路径");
|
||||
//
|
||||
// // 核心逻辑1:直接使用裁剪前的原图(不复制),仅校验合法性和权限
|
||||
// if (originalImageFile != null && originalImageFile.exists()
|
||||
// && originalImageFile.isFile() && originalImageFile.length() > 0) {
|
||||
// // 校验原图目录是否可写(系统裁剪工具需读写权限)
|
||||
// if (isDirectoryWritable(originalImageFile.getParentFile())) {
|
||||
// // 强制开放原图权限(解决跨进程访问问题)
|
||||
// //setFileReadWritePermission(originalImageFile);
|
||||
// Log.d(TAG, "【裁剪优化】直接使用原图启动裁剪(无复制):" + originalImageFile.getAbsolutePath());
|
||||
// return originalImageFile; // 直接返回原图,不做任何复制
|
||||
// } else {
|
||||
// Log.w(TAG, "【裁剪优化】原图目录不可写,切换到临时文件兜底");
|
||||
// }
|
||||
// } else {
|
||||
// Log.w(TAG, "【裁剪优化】原图无效(空/不存在/0大小),切换到临时文件兜底");
|
||||
// }
|
||||
// Log.e(TAG, "【裁剪优化】所有裁剪路径创建失败,裁剪功能不可用");
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// 辅助方法:校验目录是否可写(新增,保障裁剪权限)
|
||||
private boolean isDirectoryWritable(File dir) {
|
||||
if (dir == null) return false;
|
||||
// 目录存在且可写,或目录不存在但能创建
|
||||
return (dir.exists() && dir.isDirectory() && dir.canWrite())
|
||||
|| (!dir.exists() && dir.mkdirs());
|
||||
}
|
||||
// private boolean isDirectoryWritable(File dir) {
|
||||
// if (dir == null) return false;
|
||||
// // 目录存在且可写,或目录不存在但能创建
|
||||
// return (dir.exists() && dir.isDirectory() && dir.canWrite())
|
||||
// || (!dir.exists() && dir.mkdirs());
|
||||
// }
|
||||
|
||||
// 辅助方法:设置文件读写权限(新增,解决系统裁剪跨进程访问)
|
||||
private void setFileReadWritePermission(File file) {
|
||||
if (file == null || !file.exists()) return;
|
||||
// 开放读写权限(兼容Android 10+)
|
||||
file.setReadable(true, false);
|
||||
file.setWritable(true, false);
|
||||
file.setExecutable(true, false);
|
||||
}
|
||||
// private void setFileReadWritePermission(File file) {
|
||||
// if (file == null || !file.exists()) return;
|
||||
// // 开放读写权限(兼容Android 10+)
|
||||
// file.setReadable(true, false);
|
||||
// file.setWritable(true, false);
|
||||
// file.setExecutable(true, false);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 加载背景图片配置数据(核心:确保current/preview是两份独立的BackgroundBean实例)
|
||||
@@ -567,12 +458,12 @@ public class BackgroundSourceUtils {
|
||||
return fBackgroundCompressDir.getAbsolutePath();
|
||||
}
|
||||
|
||||
public File getCropTempFile() {
|
||||
return cropTempFile;
|
||||
public File getCropSourceFile() {
|
||||
return mCropSourceFile;
|
||||
}
|
||||
|
||||
public File getCropResultFile() {
|
||||
return cropResultFile;
|
||||
return mCropResultFile;
|
||||
}
|
||||
|
||||
public String getFileProviderAuthority() {
|
||||
@@ -680,69 +571,69 @@ public class BackgroundSourceUtils {
|
||||
* 工具方法:递归设置目录及子目录/文件的读写权限(适配系统公共目录/Pictures/PowerBell)
|
||||
* @param dir 要设置权限的目录
|
||||
*/
|
||||
private void setDirPermissionsRecursively(File dir) {
|
||||
if (dir == null || !dir.exists()) {
|
||||
String dirPath = (dir != null) ? dir.getAbsolutePath() : "null";
|
||||
LogUtils.d(TAG, "【权限管理】目录无效,无需设置权限:" + dirPath);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 设置目录权限(允许所有用户读写,系统裁剪应用/预览功能必需)
|
||||
dir.setReadable(true, false);
|
||||
dir.setWritable(true, false);
|
||||
dir.setExecutable(false, false);
|
||||
|
||||
LogUtils.d(TAG, "【权限管理】目录权限设置完成:路径=" + dir.getAbsolutePath() + ",可写=" + dir.canWrite() + ",可读=" + dir.canRead());
|
||||
|
||||
// 递归处理子目录和文件(Java7 普通for循环,兼容语法)
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null && files.length > 0) {
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
File file = files[i];
|
||||
if (file.isDirectory()) {
|
||||
setDirPermissionsRecursively(file);
|
||||
} else {
|
||||
// 设置文件权限(与目录一致,确保可读写)
|
||||
file.setReadable(true, false);
|
||||
file.setWritable(true, false);
|
||||
file.setExecutable(false, false);
|
||||
// 裁剪/压缩/预览相关文件单独打印日志
|
||||
if (file.getName().contains(CROP_TEMP_FILE_NAME) ||
|
||||
file.getName().contains(CROP_RESULT_FILE_NAME) ||
|
||||
file.getName().startsWith("ScaledCompress_")) {
|
||||
LogUtils.d(TAG, "【权限管理】关键文件权限设置:文件名=" + file.getName() + ",可写=" + file.canWrite());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
LogUtils.e(TAG, "【权限管理】设置目录权限失败(系统禁止):" + dir.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
||||
ToastUtils.show("目录权限设置失败,请授予应用存储权限");
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "【权限管理】设置目录权限异常:" + dir.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
// private void setDirPermissionsRecursively(File dir) {
|
||||
// if (dir == null || !dir.exists()) {
|
||||
// String dirPath = (dir != null) ? dir.getAbsolutePath() : "null";
|
||||
// LogUtils.d(TAG, "【权限管理】目录无效,无需设置权限:" + dirPath);
|
||||
// return;
|
||||
// }
|
||||
// try {
|
||||
// // 设置目录权限(允许所有用户读写,系统裁剪应用/预览功能必需)
|
||||
// dir.setReadable(true, false);
|
||||
// dir.setWritable(true, false);
|
||||
// dir.setExecutable(false, false);
|
||||
//
|
||||
// LogUtils.d(TAG, "【权限管理】目录权限设置完成:路径=" + dir.getAbsolutePath() + ",可写=" + dir.canWrite() + ",可读=" + dir.canRead());
|
||||
//
|
||||
// // 递归处理子目录和文件(Java7 普通for循环,兼容语法)
|
||||
// File[] files = dir.listFiles();
|
||||
// if (files != null && files.length > 0) {
|
||||
// for (int i = 0; i < files.length; i++) {
|
||||
// File file = files[i];
|
||||
// if (file.isDirectory()) {
|
||||
// setDirPermissionsRecursively(file);
|
||||
// } else {
|
||||
// // 设置文件权限(与目录一致,确保可读写)
|
||||
// file.setReadable(true, false);
|
||||
// file.setWritable(true, false);
|
||||
// file.setExecutable(false, false);
|
||||
// // 裁剪/压缩/预览相关文件单独打印日志
|
||||
// if (file.getName().contains(CROP_TEMP_FILE_NAME) ||
|
||||
// file.getName().contains(CROP_RESULT_FILE_NAME) ||
|
||||
// file.getName().startsWith("ScaledCompress_")) {
|
||||
// LogUtils.d(TAG, "【权限管理】关键文件权限设置:文件名=" + file.getName() + ",可写=" + file.canWrite());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (SecurityException e) {
|
||||
// LogUtils.e(TAG, "【权限管理】设置目录权限失败(系统禁止):" + dir.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
||||
// ToastUtils.show("目录权限设置失败,请授予应用存储权限");
|
||||
// } catch (Exception e) {
|
||||
// LogUtils.e(TAG, "【权限管理】设置目录权限异常:" + dir.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 工具方法:设置单个文件权限(确保系统裁剪应用/预览功能可读写,适配/Pictures/PowerBell目录)
|
||||
* 【关键调整】public修饰,适配BackgroundSettingsActivity的外部调用
|
||||
* @param file 要设置权限的文件
|
||||
*/
|
||||
public void setFilePermissions(File file) {
|
||||
if (file == null || !file.exists()) {
|
||||
LogUtils.d(TAG, "【权限管理】文件无效,无需设置权限:" + (file != null ? file.getAbsolutePath() : "null"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 核心:允许所有用户读写(系统裁剪应用非本应用进程,需开放权限)
|
||||
file.setReadable(true, false);
|
||||
file.setWritable(true, false);
|
||||
file.setExecutable(false, false);
|
||||
LogUtils.d(TAG, "【权限管理】文件权限设置完成(/Pictures/PowerBell下):路径=" + file.getAbsolutePath() + ",可写=" + file.canWrite() + ",可读=" + file.canRead());
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "【权限管理】设置文件权限失败:" + file.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
// public void setFilePermissions(File file) {
|
||||
// if (file == null || !file.exists()) {
|
||||
// LogUtils.d(TAG, "【权限管理】文件无效,无需设置权限:" + (file != null ? file.getAbsolutePath() : "null"));
|
||||
// return;
|
||||
// }
|
||||
// try {
|
||||
// // 核心:允许所有用户读写(系统裁剪应用非本应用进程,需开放权限)
|
||||
// file.setReadable(true, false);
|
||||
// file.setWritable(true, false);
|
||||
// file.setExecutable(false, false);
|
||||
// LogUtils.d(TAG, "【权限管理】文件权限设置完成(/Pictures/PowerBell下):路径=" + file.getAbsolutePath() + ",可写=" + file.canWrite() + ",可读=" + file.canRead());
|
||||
// } catch (Exception e) {
|
||||
// LogUtils.e(TAG, "【权限管理】设置文件权限失败:" + file.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 工具方法:清理旧文件(避免文件锁定/残留,适配系统公共目录)【内部私有,不对外暴露】
|
||||
@@ -754,34 +645,20 @@ public class BackgroundSourceUtils {
|
||||
return;
|
||||
}
|
||||
if (file.exists()) {
|
||||
// 先设置文件为可写(避免系统公共目录下文件只读导致删除失败)
|
||||
file.setWritable(true, false);
|
||||
boolean deleteSuccess = file.delete();
|
||||
LogUtils.d(TAG, "【文件管理】清理" + fileDesc + ":" + (deleteSuccess ? "成功" : "失败") + ",路径:" + file.getAbsolutePath());
|
||||
// 若删除失败,标记为退出时删除(兼容文件锁定场景)
|
||||
if (!deleteSuccess) {
|
||||
file.deleteOnExit();
|
||||
LogUtils.w(TAG, "【文件管理】" + fileDesc + "删除失败,标记为退出时自动删除");
|
||||
}
|
||||
file.delete();
|
||||
LogUtils.w(TAG, "【文件管理】" + fileDesc + "已删除");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增:清理压缩图目录下的旧文件(避免残留,初始化时调用)
|
||||
*/
|
||||
private void clearOldCompressFiles() {
|
||||
if (fBackgroundCompressDir.exists()) {
|
||||
File[] files = fBackgroundCompressDir.listFiles();
|
||||
if (files != null && files.length > 0) {
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
File file = files[i];
|
||||
if (file.isFile() && file.getName().startsWith("ScaledCompress_")) {
|
||||
clearOldFile(file, "压缩图目录旧文件(BackgroundCrops)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LogUtils.d(TAG, "【文件管理】压缩图目录旧文件清理完成(BackgroundCrops)");
|
||||
void clearCropTempFiles() {
|
||||
for(File file : fCropCacheDir.listFiles()){
|
||||
clearOldFile(file, "旧裁剪缓存文件(" + file.getAbsolutePath() + ")");
|
||||
}
|
||||
mCropSourceFile = null;
|
||||
mCropResultFile = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -790,63 +667,38 @@ public class BackgroundSourceUtils {
|
||||
* @param dir 要验证的目录
|
||||
* @return true=实际可写,false=实际不可写
|
||||
*/
|
||||
private boolean isDirActuallyWritable(File dir) {
|
||||
if (dir == null || !dir.exists() || !dir.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
// 创建临时空文件(随机文件名,避免冲突)
|
||||
File testFile = new File(dir, "test_write_" + System.currentTimeMillis() + ".tmp");
|
||||
try {
|
||||
boolean createSuccess = testFile.createNewFile();
|
||||
if (createSuccess) {
|
||||
boolean canWrite = testFile.canWrite();
|
||||
boolean canRead = testFile.canRead();
|
||||
testFile.delete(); // 删除临时文件,不占用空间
|
||||
LogUtils.d(TAG, "【权限校验】目录实际写入校验(/Pictures/PowerBell下):" + dir.getAbsolutePath() + ",创建成功=" + createSuccess + ",可写=" + canWrite + ",可读=" + canRead + ",结果=" + (canWrite ? "通过" : "失败"));
|
||||
return canWrite;
|
||||
} else {
|
||||
LogUtils.d(TAG, "【权限校验】目录实际写入校验失败:" + dir.getAbsolutePath() + ",创建临时文件失败(Permission denied)");
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【权限校验】目录实际写入校验异常:" + dir.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// private boolean isDirActuallyWritable(File dir) {
|
||||
// if (dir == null || !dir.exists() || !dir.isDirectory()) {
|
||||
// return false;
|
||||
// }
|
||||
// // 创建临时空文件(随机文件名,避免冲突)
|
||||
// File testFile = new File(dir, "test_write_" + System.currentTimeMillis() + ".tmp");
|
||||
// try {
|
||||
// boolean createSuccess = testFile.createNewFile();
|
||||
// if (createSuccess) {
|
||||
// boolean canWrite = testFile.canWrite();
|
||||
// boolean canRead = testFile.canRead();
|
||||
// testFile.delete(); // 删除临时文件,不占用空间
|
||||
// LogUtils.d(TAG, "【权限校验】目录实际写入校验(/Pictures/PowerBell下):" + dir.getAbsolutePath() + ",创建成功=" + createSuccess + ",可写=" + canWrite + ",可读=" + canRead + ",结果=" + (canWrite ? "通过" : "失败"));
|
||||
// return canWrite;
|
||||
// } else {
|
||||
// LogUtils.d(TAG, "【权限校验】目录实际写入校验失败:" + dir.getAbsolutePath() + ",创建临时文件失败(Permission denied)");
|
||||
// return false;
|
||||
// }
|
||||
// } catch (IOException e) {
|
||||
// LogUtils.e(TAG, "【权限校验】目录实际写入校验异常:" + dir.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 对外接口:清理指定旧文件(适配BackgroundSettingsActivity调用,支持/Pictures/PowerBell目录)
|
||||
* @param file 要清理的文件
|
||||
* @param fileDesc 文件描述(用于日志打印)
|
||||
*/
|
||||
public void clearOldFileByExternal(File file, String fileDesc) {
|
||||
clearOldFile(file, fileDesc); // 调用内部private方法,复用逻辑
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理裁剪相关临时文件(对外提供,Activity退出时调用,适配/Pictures/PowerBell/cache目录)
|
||||
* 【恢复函数】:兼容原代码中 mBgSourceUtils.clearCropTempFiles() 调用
|
||||
*/
|
||||
public void clearCropTempFiles() {
|
||||
// 清理核心裁剪文件(复用内部clearOldFile方法,逻辑统一)
|
||||
clearOldFile(cropTempFile, "裁剪临时文件(/Pictures/PowerBell/cache)");
|
||||
//clearOldFile(cropResultFile, "裁剪结果文件(/Pictures/PowerBell/BackgroundSource)");
|
||||
|
||||
// 清理裁剪缓存目录下的所有临时文件(避免残留)
|
||||
if (fPictureCacheDir.exists()) {
|
||||
File[] files = fPictureCacheDir.listFiles();
|
||||
if (files != null && files.length > 0) {
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
File file = files[i];
|
||||
if (file.isFile()) {
|
||||
// 仅清理文件(不删除目录),确保后续裁剪可正常创建文件
|
||||
clearOldFile(file, "裁剪缓存目录残留文件");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LogUtils.d(TAG, "【文件管理】裁剪相关临时文件清理完成(/Pictures/PowerBell下)");
|
||||
}
|
||||
// public void clearOldFileByExternal(File file, String fileDesc) {
|
||||
// clearOldFile(file, fileDesc); // 调用内部private方法,复用逻辑
|
||||
// }
|
||||
|
||||
/**
|
||||
* 适配原调用:mBgSourceUtils.copyFile(new File(""), parentDir)
|
||||
@@ -864,7 +716,7 @@ public class BackgroundSourceUtils {
|
||||
}
|
||||
// 若target是文件,取其父目录;若本身是目录,直接创建(实例化时已创建,此处二次确认)
|
||||
File targetDir = target.isFile() ? target.getParentFile() : target;
|
||||
createDirWithPermission(targetDir, "空源文件场景-目录创建(/Pictures/PowerBell下)", false);
|
||||
createDirWithPermission(targetDir, "空源文件场景-目录创建(/Pictures/PowerBell下)");
|
||||
LogUtils.d(TAG, "【文件管理】空源文件场景:目录创建完成,路径=" + targetDir.getAbsolutePath());
|
||||
return true;
|
||||
}
|
||||
@@ -888,7 +740,7 @@ public class BackgroundSourceUtils {
|
||||
String cachePath = mContext.getCacheDir().getAbsolutePath();
|
||||
|
||||
if (!TextUtils.isEmpty(publicPicturePath)) {
|
||||
if (dirPath.contains(publicPicturePath + File.separator + "PowerBell" + File.separator + COMPRESS_BASE_DIR_NAME)) {
|
||||
if (dirPath.contains(publicPicturePath + File.separator + "PowerBell" + File.separator + COMPRESS_DIR_NAME)) {
|
||||
return "系统公共图片目录(/Pictures/PowerBell/BackgroundCrops,压缩图统一存储目录)"; // 新增压缩图目录描述
|
||||
} else if (dirPath.contains(publicPicturePath + File.separator + "PowerBell")) {
|
||||
return "系统公共图片目录(/Pictures/PowerBell,图片存储/裁剪目录)";
|
||||
@@ -1073,9 +925,9 @@ public class BackgroundSourceUtils {
|
||||
}
|
||||
|
||||
// 清理旧的压缩文件(避免文件残留)
|
||||
if (compressFile.exists()) {
|
||||
clearOldFileByExternal(compressFile, "旧压缩文件(BackgroundCrops)");
|
||||
}
|
||||
// if (compressFile.exists()) {
|
||||
// clearOldFileByExternal(compressFile, "旧压缩文件(BackgroundCrops)");
|
||||
// }
|
||||
compressFile.createNewFile();
|
||||
|
||||
// 写入压缩图(质量80,平衡清晰度和内存)
|
||||
|
||||
Reference in New Issue
Block a user