diff --git a/powerbell/build.properties b/powerbell/build.properties index 82ce9bcf..ffcb06f9 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Nov 30 18:04:35 GMT 2025 +#Sun Nov 30 18:31:19 GMT 2025 stageCount=13 libraryProject= baseVersion=15.11 publishVersion=15.11.12 -buildCount=13 +buildCount=15 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 0ea69ddd..5bde3cbf 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 @@ -528,131 +528,133 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg } } - /** - * 保存剪裁后的Bitmap(彻底修复:路径拼接+权限+解析异常) - */ - private void saveCropBitmap(Bitmap bitmap) { - if (bitmap == null || bitmap.isRecycled()) { - ToastUtils.show("剪裁图片为空或已回收"); - // 清理临时文件 - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - LogUtils.d(TAG, "裁剪图片为空,清理临时文件:" + _mSourceCropTempFile.getPath()); - } - return; - } + * 保存剪裁后的Bitmap(彻底修复:路径拼接+权限+解析异常) + */ + private void saveCropBitmap(Bitmap bitmap) { + if (bitmap == null || bitmap.isRecycled()) { + ToastUtils.show("剪裁图片为空或已回收"); + // 清理临时文件 + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + LogUtils.d(TAG, "裁剪图片为空,清理临时文件:" + _mSourceCropTempFile.getPath()); + } + return; + } - // 内存优化:大图片自动缩放(保持原逻辑) - Bitmap scaledBitmap = bitmap; - if (bitmap.getByteCount() > 10 * 1024 * 1024) { // 超过10MB - float scale = 1.0f; - while (scaledBitmap.getByteCount() > 5 * 1024 * 1024) { - scale -= 0.2f; // 每次缩小20% - if (scale < 0.2f) break; // 最小缩放到20% - scaledBitmap = scaleBitmap(scaledBitmap, scale); - } - if (scaledBitmap != bitmap) { - bitmap.recycle(); // 回收原Bitmap - } - } + // 内存优化:大图片自动缩放(保持原逻辑) + Bitmap scaledBitmap = bitmap; + if (bitmap.getByteCount() > 10 * 1024 * 1024) { // 超过10MB + float scale = 1.0f; + while (scaledBitmap.getByteCount() > 5 * 1024 * 1024) { + scale -= 0.2f; // 每次缩小20% + if (scale < 0.2f) break; // 最小缩放到20% + scaledBitmap = scaleBitmap(scaledBitmap, scale); + } + if (scaledBitmap != bitmap) { + bitmap.recycle(); // 回收原Bitmap + } + } - // 核心修复1:正确获取保存路径(直接使用完整路径,避免重复拼接) - BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this); - String scaledCompressFilePath = utils.getPreviewBackgroundScaledCompressFilePath(); // 完整路径(无嵌套) - File fScaledCompressBitmapFile = new File(scaledCompressFilePath); + // 核心修复1:统一保存路径到_mSourceCroppedFile(最终目标文件) + File fScaledCompressBitmapFile = _mSourceCroppedFile; + String scaledCompressFilePath = fScaledCompressBitmapFile.getAbsolutePath(); + BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this); - // 确保保存目录存在(避免路径无效) - File parentDir = fScaledCompressBitmapFile.getParentFile(); - if (!parentDir.exists()) { - if (!parentDir.mkdirs()) { - ToastUtils.show("无法创建保存目录:" + parentDir.getAbsolutePath()); - if (scaledBitmap != bitmap) scaledBitmap.recycle(); - return; - } - } + // 确保保存目录存在(避免路径无效) + File parentDir = fScaledCompressBitmapFile.getParentFile(); + if (!parentDir.exists()) { + if (!parentDir.mkdirs()) { + ToastUtils.show("无法创建保存目录:" + parentDir.getAbsolutePath()); + if (scaledBitmap != bitmap) scaledBitmap.recycle(); + return; + } + } - // 优化:检查文件权限(确保可写) - if (fScaledCompressBitmapFile.exists()) { - if (!fScaledCompressBitmapFile.canWrite()) { - if (!fScaledCompressBitmapFile.delete()) { - ToastUtils.show("无法删除旧文件(权限不足):" + fScaledCompressBitmapFile.getPath()); - if (scaledBitmap != bitmap) scaledBitmap.recycle(); - return; - } - } - } + // 优化:检查文件权限(确保可写) + if (fScaledCompressBitmapFile.exists()) { + if (!fScaledCompressBitmapFile.canWrite()) { + if (!fScaledCompressBitmapFile.delete()) { + ToastUtils.show("无法删除旧文件(权限不足):" + fScaledCompressBitmapFile.getPath()); + if (scaledBitmap != bitmap) scaledBitmap.recycle(); + return; + } + } + } - FileOutputStream fos = null; - try { - // 修复2:设置文件可写权限(避免写入失败) - fScaledCompressBitmapFile.setWritable(true, false); - fos = new FileOutputStream(fScaledCompressBitmapFile); - // 压缩保存(80%质量,平衡清晰度和大小) - boolean success = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos); - fos.flush(); - if (success) { - ToastUtils.show("图片压缩保存成功"); - BackgroundBean previewBean = utils.getPreviewBackgroundBean(); - // 修复3:同步裁剪后文件名到预览Bean(仅传文件名,避免路径污染) - String cropFileName = fScaledCompressBitmapFile.getName(); - previewBean.setBackgroundFileName(cropFileName); - previewBean.setIsUseBackgroundFile(true); // 强制启用背景图 - previewBean.setIsUseScaledCompress(true); - utils.saveSettings(); // 持久化配置 + FileOutputStream fos = null; + try { + // 修复2:设置文件可写权限(避免写入失败) + fScaledCompressBitmapFile.setWritable(true, false); + fos = new FileOutputStream(fScaledCompressBitmapFile); + // 核心修复3:根据目标文件名自动适配压缩格式(兼容JPEG/PNG) + Bitmap.CompressFormat compressFormat = _mSourceCroppedFileName.endsWith(".png") + ? Bitmap.CompressFormat.PNG + : Bitmap.CompressFormat.JPEG; + // 压缩保存(80%质量,平衡清晰度和大小) + boolean success = scaledBitmap.compress(compressFormat, 80, fos); + fos.flush(); + if (success) { + ToastUtils.show("图片压缩保存成功"); + BackgroundBean previewBean = utils.getPreviewBackgroundBean(); + // 修复4:同步目标文件名到预览Bean(确保后续读取正确) + previewBean.setBackgroundFileName(_mSourceCroppedFileName); + previewBean.setIsUseBackgroundFile(true); // 强制启用背景图 + previewBean.setIsUseScaledCompress(true); + utils.saveSettings(); // 持久化配置 - // 清理临时文件(双重保障) - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - LogUtils.d(TAG, "裁剪成功,清理临时文件:" + _mSourceCropTempFile.getPath()); - } + // 核心修复5:保存成功后再清理临时文件(避免提前删除导致读取失败) + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + LogUtils.d(TAG, "裁剪成功,清理临时文件:" + _mSourceCropTempFile.getPath()); + } - // 刷新预览视图(确保裁剪图实时显示) - bvPreviewBackground.reloadPreviewBackground(); + // 刷新预览视图(确保裁剪图实时显示) + bvPreviewBackground.reloadPreviewBackground(); - } else { - ToastUtils.show("图片压缩保存失败(Bitmap压缩异常)"); - BackgroundBean previewBean = utils.getPreviewBackgroundBean(); - previewBean.setIsUseScaledCompress(false); - utils.saveSettings(); - // 清理临时文件 - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - LogUtils.d(TAG, "裁剪失败,清理临时文件:" + _mSourceCropTempFile.getPath()); - } - // 刷新原始预览图 - bvPreviewBackground.reloadPreviewBackground(); - } - } catch (FileNotFoundException e) { - LogUtils.e(TAG, "文件未找到:" + e.getMessage() + ",保存路径:" + fScaledCompressBitmapFile.getPath()); - ToastUtils.show("保存失败:文件路径无效"); - // 异常时清理临时文件 - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - } - } catch (IOException e) { - LogUtils.e(TAG, "写入异常:" + e.getMessage()); - ToastUtils.show("保存失败:无写入权限或文件损坏"); - // 异常时清理临时文件 - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - } - } finally { - // 关闭流(避免资源泄漏) - if (fos != null) { - try { - fos.close(); - } catch (IOException e) { - LogUtils.e(TAG, "流关闭异常" + e); - ToastUtils.show("流关闭异常:" + e.getMessage()); - } - } - // 回收Bitmap(避免内存泄漏) - if (scaledBitmap != null && !scaledBitmap.isRecycled()) { - scaledBitmap.recycle(); - } - } - } + } else { + ToastUtils.show("图片压缩保存失败(Bitmap压缩异常)"); + BackgroundBean previewBean = utils.getPreviewBackgroundBean(); + previewBean.setIsUseScaledCompress(false); + utils.saveSettings(); + // 清理临时文件 + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + LogUtils.d(TAG, "裁剪失败,清理临时文件:" + _mSourceCropTempFile.getPath()); + } + // 刷新原始预览图 + bvPreviewBackground.reloadPreviewBackground(); + } + } catch (FileNotFoundException e) { + LogUtils.e(TAG, "文件未找到:" + e.getMessage() + ",保存路径:" + scaledCompressFilePath); + ToastUtils.show("保存失败:文件路径无效"); + // 异常时清理临时文件 + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + } + } catch (IOException e) { + LogUtils.e(TAG, "写入异常:" + e.getMessage()); + ToastUtils.show("保存失败:无写入权限或文件损坏"); + // 异常时清理临时文件 + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + } + } finally { + // 关闭流(避免资源泄漏) + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + LogUtils.e(TAG, "流关闭异常" + e); + ToastUtils.show("流关闭异常:" + e.getMessage()); + } + } + // 回收Bitmap(避免内存泄漏) + if (scaledBitmap != null && !scaledBitmap.isRecycled()) { + scaledBitmap.recycle(); + } + } + } /** * 缩放Bitmap @@ -695,6 +697,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); } } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); @@ -710,8 +713,8 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg // 核心修复:对ACTION_GET_CONTENT返回的Uri,添加持久化权限(Android 4.4+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { getContentResolver().takePersistableUriPermission( - selectedImage, - Intent.FLAG_GRANT_READ_URI_PERMISSION + selectedImage, + Intent.FLAG_GRANT_READ_URI_PERMISSION ); } @@ -790,12 +793,29 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg Bitmap cropBitmap = null; // 核心修复:优先读取裁剪临时文件(放弃data.getParcelableExtra,避免缩略图) if (_mSourceCropTempFile.exists() && _mSourceCropTempFile.length() > 0) { - LogUtils.d(TAG, String.format("_mSourceCropTempFile 信息:路径=%s , 大小=%d bytes", + LogUtils.d(TAG, String.format("_mSourceCropTempFile 信息:路径=%s , 大小=%d bytes", _mSourceCropTempFile.getPath(), _mSourceCropTempFile.length())); - // 优化Bitmap解析选项(避免OOM和损坏图片解析失败) + // 核心修复:优化Bitmap解析选项(自动适配格式+防止OOM+避免损坏图片解析失败) BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Bitmap.Config.RGB_565; // 省内存(仅RGB,无透明通道) - options.inSampleSize = 1; // 不缩放(保证清晰度) + // 第一步:仅获取图片信息,不加载Bitmap(避免OOM) + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(_mSourceCropTempFile.getPath(), options); + + // 自动适配图片格式(PNG用ARGB_8888,JPEG用RGB_565省内存) + String imageMimeType = options.outMimeType; + options.inPreferredConfig = (imageMimeType != null && imageMimeType.contains("png")) + ? Bitmap.Config.ARGB_8888 + : Bitmap.Config.RGB_565; + + // 自动计算采样率(防止大图片OOM,最大边长限制为2048) + int maxImageSize = 2048; + int sampleRate = 1; + while (options.outWidth / sampleRate > maxImageSize || options.outHeight / sampleRate > maxImageSize) { + sampleRate *= 2; + } + options.inSampleSize = sampleRate; + + // 第二步:正式加载Bitmap options.inJustDecodeBounds = false; cropBitmap = BitmapFactory.decodeFile(_mSourceCropTempFile.getPath(), options); } else { @@ -805,7 +825,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg // 检查解析后的Bitmap是否有效 if (cropBitmap != null && !cropBitmap.isRecycled()) { - saveCropBitmap(cropBitmap); // 调用保存方法(内含修复逻辑) + saveCropBitmap(cropBitmap); // 调用修复后的保存方法 } else { ToastUtils.show("获取剪裁图片失败(Bitmap解析异常)"); // 清理无效临时文件 @@ -829,11 +849,8 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg _mSourceCropTempFile.delete(); } } finally { - // 裁剪流程结束,强制清理临时文件(双重保障,避免残留) - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - LogUtils.d(TAG, "裁剪流程结束,强制清理临时文件:" + _mSourceCropTempFile.getPath()); - } + // 核心修复:移除临时文件清理代码(已移至saveCropBitmap成功后清理) + LogUtils.d(TAG, "裁剪流程结束"); } } else if (resultCode != RESULT_OK) { LogUtils.d(TAG, "操作取消或失败,requestCode: " + requestCode); @@ -844,7 +861,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg LogUtils.d(TAG, "操作取消/失败,清理临时文件:" + _mSourceCropTempFile.getPath()); } } - } + } /** * 检查类型是否为图片