From 16c44e5e0e61932ee6bbb98c7ae6c9df0393657e Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 3 Dec 2025 16:29:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=BE=E7=89=87=E9=80=89=E6=8B=A9=E4=B8=8E?= =?UTF-8?q?=E5=89=AA=E8=A3=81=E9=98=B6=E6=AE=B5=E8=B0=83=E8=AF=95=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.properties | 4 +- .../BackgroundSettingsActivity.java | 446 +++++++++--------- .../utils/BackgroundSourceUtils.java | 99 ++-- 3 files changed, 257 insertions(+), 292 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index cab9d94d..b34da277 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Dec 02 06:01:03 GMT 2025 +#Wed Dec 03 08:28:19 GMT 2025 stageCount=13 libraryProject= baseVersion=15.11 publishVersion=15.11.12 -buildCount=110 +buildCount=121 baseBetaVersion=15.11.13 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java index 10726a14..7d4879ae 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java @@ -115,7 +115,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg selectTempDir.mkdirs(); LogUtils.d(TAG, "【选图初始化】选图临时目录创建完成:" + selectTempDir.getAbsolutePath()); } - + // 初始化UI及数据 initToolbar(); initClickListeners(); @@ -181,7 +181,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg } } } - + boolean isImageType(String lowerMimeType) { return lowerMimeType.equals("image/jpeg") || lowerMimeType.equals("image/png") @@ -237,7 +237,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg @Override public void onClick(View v) { LogUtils.d(TAG, "【按钮点击】触发选择图片功能"); - + // 调用权限工具类校验存储权限 if (mPermissionUtils.checkAndRequestStoragePermission(BackgroundSettingsActivity.this)) { LogUtils.d(TAG, "【选图权限】存储权限已获取,开始查找图片选择意图"); @@ -483,10 +483,10 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean(); previewBean.setIsUseBackgroundScaledCompressFile(false); mBgSourceUtils.saveSettings(); - + doubleRefreshPreview(); - - File previewFile = mBgSourceUtils.getCropSourceFile(); // 裁剪缓存图片 + + File previewFile = new File(previewBean.getBackgroundFilePath()); // 裁剪缓存图片 // 2. 生成裁剪输入Uri(强化MIUI权限授予,直接用原图Uri) Uri inputUri = null; @@ -503,7 +503,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg // 3. 【核心优化】直接使用预览图原图作为裁剪输入,删除原工具类创建裁剪路径的复制逻辑 // 仅创建裁剪结果临时文件(用于接收裁剪输出,不涉及原图复制) - File cropResultTempFile = mBgSourceUtils.getCropResultFile(); + File cropResultTempFile = new File(previewBean.getBackgroundScaledCompressFilePath()); if (cropResultTempFile == null) { ToastUtils.show("裁剪路径创建失败,请重试"); return; @@ -595,7 +595,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg ToastUtils.show("无法启动裁剪工具"); } } - + /** * 计算两个整数的最大公约数(GCD,Greatest Common Divisor) * 用途:裁剪时获取视图宽高的最简比例(避免比例值过大导致裁剪工具崩溃) @@ -629,119 +629,119 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg * 保存裁剪后的图片(修复:路径同步时序+压缩图路径强绑定) * 作用:裁剪后保存原图→生成压缩图→同步双路径到预览Bean→清理临时文件 */ - private void saveCropBitmap(Bitmap bitmap) { - LogUtils.d(TAG, "【保存启动】开始保存裁剪图片(仅更新预览Bean,不影响正式Bean)"); - if (bitmap == null || bitmap.isRecycled()) { - ToastUtils.show("裁剪图片为空"); - //mBgSourceUtils.clearCropTempFiles(); - return; - } - - // 内存优化:大图片自动缩放(保留原有逻辑,避免OOM) - Bitmap scaledBitmap = bitmap; - int originalSize = bitmap.getByteCount() / 1024 / 1024; - if (originalSize > 5) { - float scale = 1.0f; - while (scaledBitmap.getByteCount() / 1024 / 1024 > 2) { - scale -= 0.2f; - if (scale < 0.2f) break; - scaledBitmap = scaleBitmap(scaledBitmap, scale); // 复用原有缩放方法 - } - LogUtils.d(TAG, "【内存优化】大图片自动缩放:原始大小=" + originalSize + "MB,缩放后大小=" + scaledBitmap.getByteCount() / 1024 / 1024 + "MB"); - } - - File cropSaveFile = new File(mBgSourceUtils.getPreviewBackgroundFilePath()); - FileOutputStream fos = null; - BufferedOutputStream bos = null; - try { - // 1. 清理旧的裁剪预览图(避免文件残留) -// if (cropSaveFile.exists()) { -// mBgSourceUtils.clearOldFileByExternal(cropSaveFile, "旧裁剪预览图"); +// private void saveCropBitmap(Bitmap bitmap) { +// LogUtils.d(TAG, "【保存启动】开始保存裁剪图片(仅更新预览Bean,不影响正式Bean)"); +// if (bitmap == null || bitmap.isRecycled()) { +// ToastUtils.show("裁剪图片为空"); +// //mBgSourceUtils.clearCropTempFiles(); +// return; +// } +// +// // 内存优化:大图片自动缩放(保留原有逻辑,避免OOM) +// Bitmap scaledBitmap = bitmap; +// int originalSize = bitmap.getByteCount() / 1024 / 1024; +// if (originalSize > 5) { +// float scale = 1.0f; +// while (scaledBitmap.getByteCount() / 1024 / 1024 > 2) { +// scale -= 0.2f; +// if (scale < 0.2f) break; +// scaledBitmap = scaleBitmap(scaledBitmap, scale); // 复用原有缩放方法 // } - // 确保父目录存在(兼容Android 10+分区存储,多包名环境下目录适配) - File parentDir = cropSaveFile.getParentFile(); - if (parentDir != null && !parentDir.exists()) { - parentDir.mkdirs(); - mBgSourceUtils.copyFile(new File(""), parentDir); // 复用原有目录创建逻辑 - } - if (parentDir == null) { - LogUtils.e(TAG, "【裁剪保存失败】目标文件父目录为空"); - ToastUtils.show("裁剪图片保存失败"); - return; - } - cropSaveFile.createNewFile(); +// LogUtils.d(TAG, "【内存优化】大图片自动缩放:原始大小=" + originalSize + "MB,缩放后大小=" + scaledBitmap.getByteCount() / 1024 / 1024 + "MB"); +// } +// +// File cropSaveFile = new File(mBgSourceUtils.getPreviewBackgroundFilePath()); +// FileOutputStream fos = null; +// BufferedOutputStream bos = null; +// try { +// // 1. 清理旧的裁剪预览图(避免文件残留) +//// if (cropSaveFile.exists()) { +//// mBgSourceUtils.clearOldFileByExternal(cropSaveFile, "旧裁剪预览图"); +//// } +// // 确保父目录存在(兼容Android 10+分区存储,多包名环境下目录适配) +// File parentDir = cropSaveFile.getParentFile(); +// if (parentDir != null && !parentDir.exists()) { +// parentDir.mkdirs(); +// mBgSourceUtils.copyFile(new File(""), parentDir); // 复用原有目录创建逻辑 +// } +// if (parentDir == null) { +// LogUtils.e(TAG, "【裁剪保存失败】目标文件父目录为空"); +// ToastUtils.show("裁剪图片保存失败"); +// return; +// } +// cropSaveFile.createNewFile(); +// +// // 2. 写入裁剪原图到指定路径(覆盖旧文件) +// fos = new FileOutputStream(cropSaveFile); +// bos = new BufferedOutputStream(fos); +// scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 85, bos); +// bos.flush(); +// // 强制同步到磁盘(避免Android 10+异步写入导致文件读取不到) +// if (fos != null) { +// try { +// fos.getFD().sync(); +// } catch (IOException e) { +// LogUtils.w(TAG, "【裁剪保存】sync()调用失败,用flush()兜底:" + e.getMessage()); +// bos.flush(); +// } +// } +// LogUtils.d(TAG, "【裁剪保存】预览原图保存成功:" + cropSaveFile.getAbsolutePath() + ",大小=" + cropSaveFile.length() + "bytes"); +// +// // 3. 生成新压缩图(关键:先获取/生成压缩路径,再压缩) +// String newCompressPath = mBgSourceUtils.getPreviewBackgroundScaledCompressFilePath(); +// // 兜底:若压缩路径为空,生成新的唯一路径(避免空指针,兼容多包名路径隔离) +// if (TextUtils.isEmpty(newCompressPath)) { +// String sourceDir = mBgSourceUtils.getBackgroundSourceDirPath(); +// newCompressPath = new File(sourceDir, "ScaledCompress_" + System.currentTimeMillis() + ".jpg").getAbsolutePath(); +// } +// // 调用重载方法压缩,传入指定压缩路径 +// mBgSourceUtils.compressQualityToRecivedPicture(scaledBitmap, newCompressPath); +// +// // 4. 同步更新预览Bean(核心修复:同时绑定原图路径+压缩图路径) +// mBgSourceUtils.saveFileToPreviewBean(cropSaveFile, "裁剪后图片"); // 原有方法:更新原图路径 +// BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean(); +// if (previewBean != null) { +// previewBean.setBackgroundScaledCompressFilePath(newCompressPath); // 新增:绑定压缩图路径 +// mBgSourceUtils.saveSettings(); // 持久化Bean配置,避免路径丢失(多包名环境下配置隔离) +// LogUtils.d(TAG, "【路径同步】预览Bean双路径同步完成:"); +// LogUtils.d(TAG, "→ 原图路径:" + previewBean.getBackgroundFilePath()); +// LogUtils.d(TAG, "→ 压缩图路径:" + previewBean.getBackgroundScaledCompressFilePath()); +// } else { +// LogUtils.e(TAG, "【路径同步失败】预览Bean为空"); +// } +// +// ToastUtils.show("裁剪图片保存成功"); +// } catch (IOException e) { +// LogUtils.e(TAG, "【裁剪保存失败】IO异常:" + e.getMessage(), e); +// ToastUtils.show("裁剪图片保存失败"); +// } finally { +// // 资源回收(避免内存泄漏,兼容低版本Android) +// if (bos != null) { +// try { +// bos.close(); +// } catch (IOException e) { +// LogUtils.e(TAG, "【流关闭失败】BufferedOutputStream:" + e.getMessage()); +// } +// } +// if (fos != null) { +// try { +// fos.close(); +// } catch (IOException e) { +// LogUtils.e(TAG, "【流关闭失败】FileOutputStream:" + e.getMessage()); +// } +// } +// // 回收缩放后的Bitmap(避免重复回收) +// if (scaledBitmap != bitmap && scaledBitmap != null && !scaledBitmap.isRecycled()) { +// scaledBitmap.recycle(); +// } +// if (bitmap != null && !bitmap.isRecycled()) { +// bitmap.recycle(); +// } +// } +// +// LogUtils.d(TAG, "【裁剪保存】流程结束。"); +// } - // 2. 写入裁剪原图到指定路径(覆盖旧文件) - fos = new FileOutputStream(cropSaveFile); - bos = new BufferedOutputStream(fos); - scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 85, bos); - bos.flush(); - // 强制同步到磁盘(避免Android 10+异步写入导致文件读取不到) - if (fos != null) { - try { - fos.getFD().sync(); - } catch (IOException e) { - LogUtils.w(TAG, "【裁剪保存】sync()调用失败,用flush()兜底:" + e.getMessage()); - bos.flush(); - } - } - LogUtils.d(TAG, "【裁剪保存】预览原图保存成功:" + cropSaveFile.getAbsolutePath() + ",大小=" + cropSaveFile.length() + "bytes"); - - // 3. 生成新压缩图(关键:先获取/生成压缩路径,再压缩) - String newCompressPath = mBgSourceUtils.getPreviewBackgroundScaledCompressFilePath(); - // 兜底:若压缩路径为空,生成新的唯一路径(避免空指针,兼容多包名路径隔离) - if (TextUtils.isEmpty(newCompressPath)) { - String sourceDir = mBgSourceUtils.getBackgroundSourceDirPath(); - newCompressPath = new File(sourceDir, "ScaledCompress_" + System.currentTimeMillis() + ".jpg").getAbsolutePath(); - } - // 调用重载方法压缩,传入指定压缩路径 - mBgSourceUtils.compressQualityToRecivedPicture(scaledBitmap, newCompressPath); - - // 4. 同步更新预览Bean(核心修复:同时绑定原图路径+压缩图路径) - mBgSourceUtils.saveFileToPreviewBean(cropSaveFile, "裁剪后图片"); // 原有方法:更新原图路径 - BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean(); - if (previewBean != null) { - previewBean.setBackgroundScaledCompressFilePath(newCompressPath); // 新增:绑定压缩图路径 - mBgSourceUtils.saveSettings(); // 持久化Bean配置,避免路径丢失(多包名环境下配置隔离) - LogUtils.d(TAG, "【路径同步】预览Bean双路径同步完成:"); - LogUtils.d(TAG, "→ 原图路径:" + previewBean.getBackgroundFilePath()); - LogUtils.d(TAG, "→ 压缩图路径:" + previewBean.getBackgroundScaledCompressFilePath()); - } else { - LogUtils.e(TAG, "【路径同步失败】预览Bean为空"); - } - - ToastUtils.show("裁剪图片保存成功"); - } catch (IOException e) { - LogUtils.e(TAG, "【裁剪保存失败】IO异常:" + e.getMessage(), e); - ToastUtils.show("裁剪图片保存失败"); - } finally { - // 资源回收(避免内存泄漏,兼容低版本Android) - if (bos != null) { - try { - bos.close(); - } catch (IOException e) { - LogUtils.e(TAG, "【流关闭失败】BufferedOutputStream:" + e.getMessage()); - } - } - if (fos != null) { - try { - fos.close(); - } catch (IOException e) { - LogUtils.e(TAG, "【流关闭失败】FileOutputStream:" + e.getMessage()); - } - } - // 回收缩放后的Bitmap(避免重复回收) - if (scaledBitmap != bitmap && scaledBitmap != null && !scaledBitmap.isRecycled()) { - scaledBitmap.recycle(); - } - if (bitmap != null && !bitmap.isRecycled()) { - bitmap.recycle(); - } - } - - LogUtils.d(TAG, "【裁剪保存】流程结束。"); - } - /** * 图片缩放(核心用途:大图片自动缩小,降低内存占用,避免OOM崩溃) * 逻辑:按指定缩放比例(0~1之间)缩小Bitmap,保持宽高比不变,生成新的Bitmap并回收旧图 @@ -901,11 +901,11 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg // 同步预览并启动裁剪 mBgSourceUtils.saveFileToPreviewBean(mfTakePhoto, mfTakePhoto.getAbsolutePath()); doubleRefreshPreview(); - + startCropImageActivity(false); LogUtils.d(TAG, "【拍照完成】拍照回调处理结束,已启动裁剪"); } - + /** * 解析拍照结果为Bitmap(核心用途:拍照回调后,从拍照临时文件/Intent中获取Bitmap,用于后续压缩保存) * 逻辑:优先从拍照临时文件解析(清晰原图),失败则从Intent获取缩略图(兜底),双重保障避免解析失败 @@ -960,50 +960,65 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg */ private void handleCropImageResult(int requestCode, int resultCode, Intent data) { // 从工具类获取裁剪临时文件(唯一入口) - File cropTempFile = mBgSourceUtils.getCropSourceFile(); - boolean isFileExist = cropTempFile != null && cropTempFile.exists(); + File cropTempFile = new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath()); + boolean isFileExist = cropTempFile.exists(); boolean isFileReadable = isFileExist ? cropTempFile.canRead() : false; long fileSize = isFileExist ? cropTempFile.length() : 0; // 适配MIUI:仅resultCode=RESULT_OK且文件有效时视为成功 boolean isCropSuccess = (resultCode == RESULT_OK) && isFileExist && isFileReadable && fileSize > 100; - + if (isCropSuccess) { + BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean(); + previewBean.setIsUseBackgroundScaledCompressFile(isCropSuccess); + mBgSourceUtils.saveSettings(); + doubleRefreshPreview(); + } else { + // 其他失败场景 + handleOperationCancelOrFail(); + } // 打印校验日志 - LogUtils.d(TAG, "【裁剪回调】校验:resultCode=" + resultCode + ",文件存在=" + isFileExist + ",大小=" + fileSize + "bytes,是否成功=" + isCropSuccess); - - // 处理MIUI裁剪取消(resultCode=0视为取消) - if (resultCode == 0 && !isCropSuccess) { - LogUtils.d(TAG, "【裁剪回调】MIUI 裁剪工具已取消"); - ToastUtils.show("裁剪已取消"); - //mBgSourceUtils.clearCropTempFiles(); - return; - } - - // 处理裁剪文件为空(真正的裁剪失败) - if (isFileExist && fileSize == 0) { - LogUtils.e(TAG, "【裁剪失败】裁剪文件为空(MIUI适配问题)"); - ToastUtils.show("裁剪失败,请尝试选择「系统相机」裁剪或更换图片"); - //mBgSourceUtils.clearCropTempFiles(); - return; - } - - // 裁剪成功:解析Bitmap并保存 - if (isCropSuccess) { - Bitmap cropBitmap = parseCropTempFileToBitmap(cropTempFile); - if (cropBitmap != null && !cropBitmap.isRecycled()) { - saveCropBitmap(cropBitmap); // 保存裁剪结果 - doubleRefreshPreview(); // 双重刷新预览(适配MIUI渲染延迟) - LogUtils.d(TAG, "【裁剪完成】裁剪回调处理结束"); - } else { - ToastUtils.show("获取裁剪图片失败"); - LogUtils.e(TAG, "【裁剪回调失败】Bitmap解析异常"); - //mBgSourceUtils.clearCropTempFiles(); - } - } else { - // 其他失败场景 - handleOperationCancelOrFail(); - } +// LogUtils.d(TAG, "【裁剪回调】校验:resultCode=" + resultCode + ",文件存在=" + isFileExist + ",大小=" + fileSize + "bytes,是否成功=" + isCropSuccess); +// +// // 处理MIUI裁剪取消(resultCode=0视为取消) +// if (resultCode == 0 && !isCropSuccess) { +// LogUtils.d(TAG, "【裁剪回调】MIUI 裁剪工具已取消"); +// ToastUtils.show("裁剪已取消"); +// //mBgSourceUtils.clearCropTempFiles(); +// return; +// } +// +// // 处理裁剪文件为空(真正的裁剪失败) +// if (isFileExist && fileSize == 0) { +// LogUtils.e(TAG, "【裁剪失败】裁剪文件为空(MIUI适配问题)"); +// ToastUtils.show("裁剪失败,请尝试选择「系统相机」裁剪或更换图片"); +// //mBgSourceUtils.clearCropTempFiles(); +// return; +// } +// +// // 裁剪成功:解析Bitmap并保存 +// if (isCropSuccess) { +// BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean(); +// previewBean.setIsUseBackgroundScaledCompressFile(isCropSuccess); +// mBgSourceUtils.saveSettings(); +// +// doubleRefreshPreview(); +// +// Bitmap cropBitmap = parseCropTempFileToBitmap(cropTempFile); +// if (cropBitmap != null && !cropBitmap.isRecycled()) { +// saveCropBitmap(cropBitmap); // 保存裁剪结果 +// // 更新预览数据集设置 +// BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean(); +// previewBean.setIsUseBackgroundScaledCompressFile(isCropSuccess); +// mBgSourceUtils.saveSettings(); +// +// doubleRefreshPreview(); // 双重刷新预览(适配MIUI渲染延迟) +// LogUtils.d(TAG, "【裁剪完成】裁剪回调处理结束"); +// } else { +// ToastUtils.show("获取裁剪图片失败"); +// LogUtils.e(TAG, "【裁剪回调失败】Bitmap解析异常"); +// //mBgSourceUtils.clearCropTempFiles(); +// } } - + /** * 解析裁剪临时文件为Bitmap(核心用途:裁剪回调后,将裁剪生成的临时文件转为Bitmap,用于后续保存/预览) * 逻辑:采用采样率加载(避免OOM),支持JPEG/PNG等主流格式,兼容破损文件/空文件场景 @@ -1081,7 +1096,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg return cropBitmap; } - + /** * 双重刷新预览(核心用途:适配MIUI等机型裁剪/选图后预览渲染延迟,解决“图片已保存但界面空白”问题) * 逻辑:先立即刷新一次,再延迟短时间二次刷新,确保控件能获取到最新的图片文件(规避异步渲染/文件写入延迟) @@ -1134,78 +1149,61 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg // 关键修复1:解析Uri为文件(强制使用独立的选图临时文件,避免路径混淆) mBgSourceUtils.createCropFileProviderPath(selectedImage); - if (mBgSourceUtils.getCropSourceFile() == null || !mBgSourceUtils.getCropSourceFile().exists() || mBgSourceUtils.getCropSourceFile().length() <= 0) { - ToastUtils.show("选择的图片文件无效或无法读取"); - return; - } - LogUtils.d(TAG, "【选图解析】选图临时文件生成成功:" + mBgSourceUtils.getCropSourceFile().getAbsolutePath() + ",大小:" + mBgSourceUtils.getCropSourceFile().length() + "bytes"); - // 关键修复2:同步预览Bean(强制绑定「原图路径+压缩图路径」双路径,避免路径错位) - BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean(); - if (previewBean == null) { - ToastUtils.show("预览配置初始化失败"); - return; - } - // 关键:强制绑定压缩图路径到BackgroundCrops目录(统一存储,避免路径错乱) - String targetCompressPath = mBgSourceUtils.getCropResultFile().getAbsolutePath(); - + String targetCompressPath = mBgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath(); + LogUtils.d(TAG, "【选图同步】预览Bean双路径绑定完成:"); - LogUtils.d(TAG, "→ 原图路径(选图临时文件):" + previewBean.getBackgroundFilePath()); - LogUtils.d(TAG, "→ 压缩图路径(BackgroundCrops):" + previewBean.getBackgroundScaledCompressFilePath()); + LogUtils.d(TAG, "→ 原图路径(选图临时文件):" + mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath()); + LogUtils.d(TAG, "→ 压缩图路径 :" + targetCompressPath); + + startCropImageActivity(false); // 关键修复3:生成压缩图(指定BackgroundCrops目录下的路径,确保与预览Bean路径一致) - LogUtils.d(TAG, "【选图压缩】开始生成预览压缩图,目标路径:" + targetCompressPath); - Bitmap selectBitmap = null; - try { - // 优化:使用采样率加载Bitmap,避免OOM(原有直接decodeFile易导致大图片崩溃) - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(mBgSourceUtils.getCropSourceFile().getAbsolutePath(), options); - // 计算采样率(最大尺寸限制为2048px) - int maxSize = 2048; - int sampleSize = 1; - while (options.outWidth / sampleSize > maxSize || options.outHeight / sampleSize > maxSize) { - sampleSize *= 2; - } - options.inJustDecodeBounds = false; - options.inSampleSize = sampleSize; - options.inPreferredConfig = Bitmap.Config.RGB_565; // 节省内存 - selectBitmap = BitmapFactory.decodeFile(mBgSourceUtils.getCropSourceFile().getAbsolutePath(), options); - } catch (Exception e) { - LogUtils.e(TAG, "【选图压缩】Bitmap加载失败:" + e.getMessage(), e); - } - - if (selectBitmap != null && !selectBitmap.isRecycled()) { - mBgSourceUtils.compressQualityToRecivedPicture(selectBitmap, targetCompressPath); // 调用重载函数,指定路径压缩 - selectBitmap.recycle(); // 及时回收,避免内存泄漏 - } else { - ToastUtils.show("选图后压缩图生成失败"); - LogUtils.e(TAG, "【选图压缩失败】无法解析选图文件为Bitmap"); - return; - } + //LogUtils.d(TAG, "【选图压缩】开始生成预览压缩图,目标路径:" + targetCompressPath); +// Bitmap selectBitmap = null; +// try { +// // 优化:使用采样率加载Bitmap,避免OOM(原有直接decodeFile易导致大图片崩溃) +// BitmapFactory.Options options = new BitmapFactory.Options(); +// options.inJustDecodeBounds = true; +// BitmapFactory.decodeFile(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath(), options); +// // 计算采样率(最大尺寸限制为2048px) +// int maxSize = 2048; +// int sampleSize = 1; +// while (options.outWidth / sampleSize > maxSize || options.outHeight / sampleSize > maxSize) { +// sampleSize *= 2; +// } +// options.inJustDecodeBounds = false; +// options.inSampleSize = sampleSize; +// options.inPreferredConfig = Bitmap.Config.RGB_565; // 节省内存 +// selectBitmap = BitmapFactory.decodeFile(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath(), options); +// LogUtils.d(TAG, ""); +// } catch (Exception e) { +// LogUtils.e(TAG, "【选图压缩】Bitmap加载失败:" + e.getMessage(), e); +// } // 关键修复4:校验压缩图是否生成成功(避免压缩失败但仍启动裁剪) - File compressFile = new File(targetCompressPath); - if (!compressFile.exists() || compressFile.length() <= 0) { - ToastUtils.show("压缩图生成失败,请重新选择图片"); - LogUtils.e(TAG, "【选图压缩失败】压缩图文件无效:" + targetCompressPath); - return; - } +// File compressFile = new File(targetCompressPath); +// if (!compressFile.exists() || compressFile.length() <= 0) { +// ToastUtils.show("压缩图生成失败,请重新选择图片"); +// LogUtils.e(TAG, "【选图压缩失败】压缩图文件无效:" + targetCompressPath); +// return; +// } // 刷新预览并启动裁剪(无需重复同步预览Bean,已提前绑定) - doubleRefreshPreview(); - - // 新增:延迟50ms启动裁剪(适配部分机型选图后文件写入延迟) - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - if (!isFinishing()) { - startCropImageActivity(false); - LogUtils.d(TAG, "【选图完成】选图回调处理结束,已启动裁剪(正式图+压缩图均已生成)"); - } - } - }, 50); + /*doubleRefreshPreview(); + + // 新增:延迟50ms启动裁剪(适配部分机型选图后文件写入延迟) + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + if (!isFinishing()) { + startCropImageActivity(false); + LogUtils.d(TAG, "【选图完成】选图回调处理结束,已启动裁剪(正式图+压缩图均已生成)"); + } + } + }, 50); + */ } /** @@ -1214,7 +1212,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg private void handleOperationCancelOrFail() { initBackgroundViewByPreviewBean(); doubleRefreshPreview(); - + LogUtils.d(TAG, "【操作回调】操作取消或失败"); ToastUtils.show("操作已取消"); } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/BackgroundSourceUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/BackgroundSourceUtils.java index 7fccf4db..3d295193 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/BackgroundSourceUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/BackgroundSourceUtils.java @@ -208,7 +208,7 @@ public class BackgroundSourceUtils { */ private void initAllFiles() { // 1. 裁剪临时文件 - mCropSourceFile = new File(fCropCacheDir, CROP_TEMP_FILE_NAME); + //mCropSourceFile = new File(fCropCacheDir, CROP_TEMP_FILE_NAME); // 2. 裁剪结果文件 //cropResultFile = new File(fCropCacheDir, CROP_RESULT_FILE_NAME); @@ -216,18 +216,32 @@ public class BackgroundSourceUtils { clearCropTempFiles(); LogUtils.d(TAG, "【文件初始化】完成。"); } - + public boolean createCropFileProviderPath(Uri uri) { InputStream is = null; FileOutputStream fos = null; - + try { clearCropTempFiles(); + String szType = mContext.getContentResolver().getType(uri); + // 2. 截取MIME类型后缀(如从image/jpeg中提取jpeg)【核心新增逻辑】 + String fileSuffix = ""; + if (szType != null && szType.contains("/")) { + // 分割字符串,取"/"后面的部分(如"image/jpeg" → 分割后取索引1的"jpeg") + fileSuffix = szType.split("/")[1]; + // 调试日志:打印截取后的文件后缀 + } else { + // 异常处理:若类型为空或格式错误,默认后缀设为jpeg(保留原逻辑兼容性) + fileSuffix = "jpeg"; + } String newCropFileName = UUID.randomUUID().toString() + System.currentTimeMillis(); - mCropSourceFile = new File(fCropCacheDir, newCropFileName + ".jpg"); - mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + ".jpg"); - + mCropSourceFile = new File(fCropCacheDir, newCropFileName + "." + fileSuffix); + mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + "." + fileSuffix); + + mCropSourceFile.createNewFile(); + mCropResultFile.createNewFile(); + // 1. 打开Uri输入流(兼容content:///file:// 等多种Uri格式) is = mContext.getContentResolver().openInputStream(uri); if (is == null) { @@ -255,13 +269,13 @@ public class BackgroundSourceUtils { 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()); @@ -458,14 +472,6 @@ public class BackgroundSourceUtils { return fBackgroundCompressDir.getAbsolutePath(); } - public File getCropSourceFile() { - return mCropSourceFile; - } - - public File getCropResultFile() { - return mCropResultFile; - } - public String getFileProviderAuthority() { return FILE_PROVIDER_AUTHORITY; } @@ -654,7 +660,7 @@ public class BackgroundSourceUtils { * 新增:清理压缩图目录下的旧文件(避免残留,初始化时调用) */ void clearCropTempFiles() { - for(File file : fCropCacheDir.listFiles()){ + for (File file : fCropCacheDir.listFiles()) { clearOldFile(file, "旧裁剪缓存文件(" + file.getAbsolutePath() + ")"); } mCropSourceFile = null; @@ -881,6 +887,7 @@ public class BackgroundSourceUtils { */ public void compressQualityToRecivedPicture(Bitmap bitmap, String targetCompressPath) { LogUtils.d(TAG, "【压缩启动】开始压缩图片(指定路径),Bitmap状态:" + (bitmap != null && !bitmap.isRecycled())); + if (bitmap == null || bitmap.isRecycled()) { ToastUtils.show("压缩失败:图片为空"); LogUtils.e(TAG, "【压缩失败】Bitmap为空或已回收"); @@ -890,48 +897,16 @@ public class BackgroundSourceUtils { OutputStream outStream = null; FileOutputStream fos = null; try { - String scaledCompressFilePath = targetCompressPath; - // 兜底:若传入路径为空,生成BackgroundCrops目录下的临时压缩路径(兼容异常场景) - if (TextUtils.isEmpty(scaledCompressFilePath)) { - LogUtils.e(TAG, "【压缩异常】指定路径为空,使用BackgroundCrops临时兜底路径"); - // 强制在BackgroundCrops目录下生成临时路径(不再用其他目录) - String tempCompressName = "preview_compress_" + System.currentTimeMillis() + ".jpg"; - scaledCompressFilePath = new File(fBackgroundCompressDir, tempCompressName).getAbsolutePath(); - LogUtils.d(TAG, "【压缩兜底】BackgroundCrops临时路径:" + scaledCompressFilePath); - } else { - // 强制校验:传入路径必须在BackgroundCrops目录下,否则重置(确保统一存储) - if (!scaledCompressFilePath.contains(fBackgroundCompressDir.getAbsolutePath())) { - LogUtils.w(TAG, "【压缩校验】传入路径不在BackgroundCrops目录,自动重置"); - String compressFileName = new File(scaledCompressFilePath).getName(); - scaledCompressFilePath = new File(fBackgroundCompressDir, compressFileName).getAbsolutePath(); - LogUtils.d(TAG, "【压缩校验】重置后路径:" + scaledCompressFilePath); - } - } + LogUtils.d(TAG, "【压缩配置】目标路径(BackgroundCrops):" + targetCompressPath + ",Bitmap原始大小:" + bitmap.getByteCount() / 1024 + "KB"); - File compressFile = new File(scaledCompressFilePath); - LogUtils.d(TAG, "【压缩配置】目标路径(BackgroundCrops):" + scaledCompressFilePath + ",Bitmap原始大小:" + bitmap.getByteCount() / 1024 + "KB"); - - // 确保压缩图目录存在(实例化时已创建,此处二次确认) - File parentDir = compressFile.getParentFile(); - if (parentDir == null) { - LogUtils.e(TAG, "【压缩异常】父目录为空,无法创建文件"); - ToastUtils.show("压缩失败:路径无效"); - return; + File targetCompressFile = new File(targetCompressPath); + if (targetCompressFile.exists()) { + targetCompressFile.delete(); } - if (!parentDir.exists()) { - parentDir.mkdirs(); - FileUtils.copyFile(new File(""), parentDir); - LogUtils.d(TAG, "【压缩准备】BackgroundCrops目录已创建:" + parentDir.getAbsolutePath()); - } - - // 清理旧的压缩文件(避免文件残留) -// if (compressFile.exists()) { -// clearOldFileByExternal(compressFile, "旧压缩文件(BackgroundCrops)"); -// } - compressFile.createNewFile(); + targetCompressFile.createNewFile(); // 写入压缩图(质量80,平衡清晰度和内存) - fos = new FileOutputStream(compressFile); + fos = new FileOutputStream(targetCompressFile); outStream = new BufferedOutputStream(fos); boolean compressSuccess = bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outStream); outStream.flush(); @@ -946,21 +921,13 @@ public class BackgroundSourceUtils { } } - LogUtils.d(TAG, "【压缩结果】" + (compressSuccess ? "成功" : "失败") + ",大小:" + compressFile.length() / 1024 + "KB,路径:" + scaledCompressFilePath); + LogUtils.d(TAG, "【压缩结果】" + (compressSuccess ? "成功" : "失败") + ",大小:" + targetCompressFile.length() / 1024 + "KB,路径:" + targetCompressFile); // 关键修复:压缩成功后,强制同步路径到预览Bean(双重保障,避免时序错位) if (compressSuccess) { - BackgroundBean previewBean = getPreviewBackgroundBean(); - if (previewBean != null) { - previewBean.setBackgroundScaledCompressFilePath(scaledCompressFilePath); - saveSettings(); // 持久化配置,多包名环境下配置隔离 - LogUtils.d(TAG, "【压缩路径同步】已绑定到预览Bean(BackgroundCrops):" + scaledCompressFilePath); - } else { - LogUtils.e(TAG, "【压缩路径同步失败】预览Bean为空"); - } + ToastUtils.show("图片压缩成功"); } else { ToastUtils.show("图片压缩失败"); - LogUtils.e(TAG, "【压缩失败】Bitmap.compress()返回false"); } } catch (IOException e) { LogUtils.e(TAG, "【压缩异常】IO错误:" + e.getMessage(), e);