20251204_134218_045

This commit is contained in:
2025-12-04 13:42:30 +08:00
parent 7f3c91fb1d
commit 19e6e276bd
5 changed files with 240 additions and 179 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Wed Dec 03 22:57:30 GMT 2025 #Thu Dec 04 05:39:23 GMT 2025
stageCount=13 stageCount=13
libraryProject= libraryProject=
baseVersion=15.11 baseVersion=15.11
publishVersion=15.11.12 publishVersion=15.11.12
buildCount=195 buildCount=205
baseBetaVersion=15.11.13 baseBetaVersion=15.11.13

View File

@@ -6,7 +6,6 @@ import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat; import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@@ -14,10 +13,10 @@ import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.FileProvider;
import cc.winboll.studio.libaes.views.AToolbar; import cc.winboll.studio.libaes.views.AToolbar;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.libappbase.ToastUtils;
@@ -236,9 +235,8 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
public void onClick(View v) { public void onClick(View v) {
LogUtils.d(TAG, "【按钮点击】固定比例裁剪"); LogUtils.d(TAG, "【按钮点击】固定比例裁剪");
// 调用裁剪工具类:传入上下文、预览图、固定比例(按视图宽高)、请求码 // 调用裁剪工具类:传入上下文、预览图、固定比例(按视图宽高)、请求码
ImageCropUtils.startSystemCrop( startSystemCrop(
BackgroundSettingsActivity.this, mBgSourceUtils.getPreviewBackgroundBean(),
new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath()),
mBackgroundView.getWidth(), mBackgroundView.getWidth(),
mBackgroundView.getHeight(), mBackgroundView.getHeight(),
false, false,
@@ -252,9 +250,8 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
public void onClick(View v) { public void onClick(View v) {
LogUtils.d(TAG, "【按钮点击】自由裁剪"); LogUtils.d(TAG, "【按钮点击】自由裁剪");
// 调用裁剪工具类传入上下文、预览图、自由裁剪比例参数传0、请求码 // 调用裁剪工具类传入上下文、预览图、自由裁剪比例参数传0、请求码
ImageCropUtils.startSystemCrop( startSystemCrop(
BackgroundSettingsActivity.this, mBgSourceUtils.getPreviewBackgroundBean(),
new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath()),
0, 0,
0, 0,
true, true,
@@ -288,7 +285,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
LogUtils.d(TAG, "【拍照权限】已获取"); LogUtils.d(TAG, "【拍照权限】已获取");
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
try { try {
Uri photoUri = ImageCropUtils.getFileProviderUri(BackgroundSettingsActivity.this, mfTakePhoto); Uri photoUri = getFileProviderUri(mfTakePhoto);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
LogUtils.d(TAG, "【拍照启动】Uri" + photoUri.toString()); LogUtils.d(TAG, "【拍照启动】Uri" + photoUri.toString());
@@ -412,9 +409,8 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
doubleRefreshPreview(); doubleRefreshPreview();
// 拍照后启动固定比例裁剪(调用工具类) // 拍照后启动固定比例裁剪(调用工具类)
ImageCropUtils.startSystemCrop( startSystemCrop(
this, mBgSourceUtils.getPreviewBackgroundBean(),
mfTakePhoto,
mBackgroundView.getWidth(), mBackgroundView.getWidth(),
mBackgroundView.getHeight(), mBackgroundView.getHeight(),
false, false,
@@ -460,7 +456,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
} }
private void handleCropImageResult(int requestCode, int resultCode, Intent data) { private void handleCropImageResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "handleCropImageResult: 处理裁剪结果"); LogUtils.d(TAG, "handleCropImageResult: 处理裁剪结果");
File cropTempFile = new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath()); File cropTempFile = new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath());
boolean isFileExist = cropTempFile.exists(); boolean isFileExist = cropTempFile.exists();
boolean isFileReadable = isFileExist ? cropTempFile.canRead() : false; boolean isFileReadable = isFileExist ? cropTempFile.canRead() : false;
@@ -468,7 +464,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
boolean isCropSuccess = (resultCode == RESULT_OK) && isFileExist && isFileReadable && fileSize > 100; boolean isCropSuccess = (resultCode == RESULT_OK) && isFileExist && isFileReadable && fileSize > 100;
if (isCropSuccess) { if (isCropSuccess) {
Log.d(TAG, "handleCropImageResult: 裁剪成功"); LogUtils.d(TAG, "handleCropImageResult: 裁剪成功");
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean(); BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
previewBean.setIsUseBackgroundFile(true); previewBean.setIsUseBackgroundFile(true);
previewBean.setIsUseBackgroundScaledCompressFile(true); previewBean.setIsUseBackgroundScaledCompressFile(true);
@@ -495,7 +491,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
public void run() { public void run() {
if (mBackgroundView != null && !isFinishing()) { if (mBackgroundView != null && !isFinishing()) {
mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean()); mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean());
Log.d(TAG, "handleCropImageResult: 裁剪图片加载完成"); LogUtils.d(TAG, "handleCropImageResult: 裁剪图片加载完成");
} }
} }
}, 50); }, 50);
@@ -505,9 +501,9 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
} }
private float getRatioFromSystemCropFile(File systemCropFile) { private float getRatioFromSystemCropFile(File systemCropFile) {
Log.d(TAG, "getRatioFromSystemCropFile: 读取比例"); LogUtils.d(TAG, "getRatioFromSystemCropFile: 读取比例");
if (systemCropFile == null || !systemCropFile.exists() || !systemCropFile.isFile()) { if (systemCropFile == null || !systemCropFile.exists() || !systemCropFile.isFile()) {
Log.e(TAG, "getRatioFromSystemCropFile: 文件无效"); LogUtils.e(TAG, "getRatioFromSystemCropFile: 文件无效");
return -1; return -1;
} }
@@ -518,19 +514,19 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
int cropWidth = options.outWidth; int cropWidth = options.outWidth;
int cropHeight = options.outHeight; int cropHeight = options.outHeight;
if (cropWidth <= 0 || cropHeight <= 0) { if (cropWidth <= 0 || cropHeight <= 0) {
Log.e(TAG, "getRatioFromSystemCropFile: 尺寸无效"); LogUtils.e(TAG, "getRatioFromSystemCropFile: 尺寸无效");
return -1; return -1;
} }
float systemRatio = (float) cropWidth / cropHeight; float systemRatio = (float) cropWidth / cropHeight;
Log.d(TAG, "getRatioFromSystemCropFile: 比例:" + systemRatio); LogUtils.d(TAG, "getRatioFromSystemCropFile: 比例:" + systemRatio);
return systemRatio; return systemRatio;
} }
private Bitmap adjustBitmapToFinalRatio(Bitmap originalBitmap, float finalCropRatio) { private Bitmap adjustBitmapToFinalRatio(Bitmap originalBitmap, float finalCropRatio) {
Log.d(TAG, "adjustBitmapToFinalRatio: 调整比例"); LogUtils.d(TAG, "adjustBitmapToFinalRatio: 调整比例");
if (originalBitmap == null || originalBitmap.isRecycled() || finalCropRatio <= 0) { if (originalBitmap == null || originalBitmap.isRecycled() || finalCropRatio <= 0) {
Log.e(TAG, "adjustBitmapToFinalRatio: 参数无效"); LogUtils.e(TAG, "adjustBitmapToFinalRatio: 参数无效");
return originalBitmap; return originalBitmap;
} }
@@ -539,7 +535,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
float originalRatio = (float) originalWidth / originalHeight; float originalRatio = (float) originalWidth / originalHeight;
if (Math.abs(originalRatio - finalCropRatio) < 0.001f) { if (Math.abs(originalRatio - finalCropRatio) < 0.001f) {
Log.d(TAG, "adjustBitmapToFinalRatio: 比例一致"); LogUtils.d(TAG, "adjustBitmapToFinalRatio: 比例一致");
return originalBitmap; return originalBitmap;
} }
@@ -552,7 +548,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
} }
targetWidth = Math.round(targetHeight * finalCropRatio); targetWidth = Math.round(targetHeight * finalCropRatio);
Log.d(TAG, "adjustBitmapToFinalRatio: 调整前:" + originalWidth + "x" + originalHeight + ",调整后:" + targetWidth + "x" + targetHeight); LogUtils.d(TAG, "adjustBitmapToFinalRatio: 调整前:" + originalWidth + "x" + originalHeight + ",调整后:" + targetWidth + "x" + targetHeight);
Bitmap adjustedBitmap = Bitmap.createBitmap( Bitmap adjustedBitmap = Bitmap.createBitmap(
originalBitmap, originalBitmap,
@@ -566,15 +562,15 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
} }
private void saveScaledBitmapToFile(Bitmap bitmap, File targetFile) { private void saveScaledBitmapToFile(Bitmap bitmap, File targetFile) {
Log.d(TAG, "saveScaledBitmapToFile: 保存图片"); LogUtils.d(TAG, "saveScaledBitmapToFile: 保存图片");
if (bitmap == null || bitmap.isRecycled() || targetFile == null) { if (bitmap == null || bitmap.isRecycled() || targetFile == null) {
Log.e(TAG, "saveScaledBitmapToFile: 参数无效"); LogUtils.e(TAG, "saveScaledBitmapToFile: 参数无效");
return; return;
} }
if (targetFile.exists()) { if (targetFile.exists()) {
boolean deleteSuccess = targetFile.delete(); boolean deleteSuccess = targetFile.delete();
Log.d(TAG, "saveScaledBitmapToFile: 删除原文件:" + (deleteSuccess ? "成功" : "失败")); LogUtils.d(TAG, "saveScaledBitmapToFile: 删除原文件:" + (deleteSuccess ? "成功" : "失败"));
} }
OutputStream outputStream = null; OutputStream outputStream = null;
@@ -582,24 +578,24 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
outputStream = new BufferedOutputStream(new FileOutputStream(targetFile)); outputStream = new BufferedOutputStream(new FileOutputStream(targetFile));
bitmap.compress(CompressFormat.JPEG, 100, outputStream); bitmap.compress(CompressFormat.JPEG, 100, outputStream);
outputStream.flush(); outputStream.flush();
Log.d(TAG, "saveScaledBitmapToFile: 保存成功"); LogUtils.d(TAG, "saveScaledBitmapToFile: 保存成功");
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "saveScaledBitmapToFile: 异常:" + e.getMessage()); LogUtils.e(TAG, "saveScaledBitmapToFile: 异常:" + e.getMessage());
} finally { } finally {
if (outputStream != null) { if (outputStream != null) {
try { try {
outputStream.close(); outputStream.close();
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "saveScaledBitmapToFile: 关闭流异常"); LogUtils.e(TAG, "saveScaledBitmapToFile: 关闭流异常");
} }
} }
} }
} }
private Bitmap parseCropTempFileToBitmap(File cropTempFile) { private Bitmap parseCropTempFileToBitmap(File cropTempFile) {
Log.d(TAG, "parseCropTempFileToBitmap: 解析文件"); LogUtils.d(TAG, "parseCropTempFileToBitmap: 解析文件");
if (cropTempFile == null || !cropTempFile.exists() || !cropTempFile.isFile() || cropTempFile.length() <= 100) { if (cropTempFile == null || !cropTempFile.exists() || !cropTempFile.isFile() || cropTempFile.length() <= 100) {
Log.e(TAG, "parseCropTempFileToBitmap: 文件无效"); LogUtils.e(TAG, "parseCropTempFileToBitmap: 文件无效");
return null; return null;
} }
@@ -613,7 +609,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
sampleSize *= 2; sampleSize *= 2;
} }
sampleSize = Math.min(sampleSize, 16); sampleSize = Math.min(sampleSize, 16);
Log.d(TAG, "parseCropTempFileToBitmap: 采样率:" + sampleSize); LogUtils.d(TAG, "parseCropTempFileToBitmap: 采样率:" + sampleSize);
options.inJustDecodeBounds = false; options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize; options.inSampleSize = sampleSize;
@@ -625,16 +621,16 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
try { try {
cropBitmap = BitmapFactory.decodeFile(cropTempFile.getAbsolutePath(), options); cropBitmap = BitmapFactory.decodeFile(cropTempFile.getAbsolutePath(), options);
if (cropBitmap == null || cropBitmap.isRecycled()) { if (cropBitmap == null || cropBitmap.isRecycled()) {
Log.e(TAG, "parseCropTempFileToBitmap: 解析失败"); LogUtils.e(TAG, "parseCropTempFileToBitmap: 解析失败");
return null; return null;
} }
Log.d(TAG, "parseCropTempFileToBitmap: 解析成功"); LogUtils.d(TAG, "parseCropTempFileToBitmap: 解析成功");
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
Log.e(TAG, "parseCropTempFileToBitmap: OOM"); LogUtils.e(TAG, "parseCropTempFileToBitmap: OOM");
Toast.makeText(this, "图片解析失败", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "图片解析失败", Toast.LENGTH_SHORT).show();
return null; return null;
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "parseCropTempFileToBitmap: 异常:" + e.getMessage()); LogUtils.e(TAG, "parseCropTempFileToBitmap: 异常:" + e.getMessage());
return null; return null;
} }
@@ -683,14 +679,12 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
LogUtils.d(TAG, "【选图权限】已添加持久化权限"); LogUtils.d(TAG, "【选图权限】已添加持久化权限");
} }
mBgSourceUtils.createCropFileProviderPath(selectedImage); BackgroundBean cropBean = mBgSourceUtils.createCropFileProviderBackgroundBean(selectedImage);
String targetCompressPath = mBgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath();
LogUtils.d(TAG, "【选图同步】路径绑定完成"); LogUtils.d(TAG, "【选图同步】路径绑定完成");
// 选图后启动固定比例裁剪(调用工具类) // 选图后启动固定比例裁剪(调用工具类)
ImageCropUtils.startSystemCrop( startSystemCrop(
this, cropBean,
new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath()),
mBackgroundView.getWidth(), mBackgroundView.getWidth(),
mBackgroundView.getHeight(), mBackgroundView.getHeight(),
false, false,
@@ -703,6 +697,87 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
LogUtils.d(TAG, "【操作回调】取消或失败"); LogUtils.d(TAG, "【操作回调】取消或失败");
ToastUtils.show("操作已取消"); ToastUtils.show("操作已取消");
} }
/**
* 启动系统裁剪工具
* @param activity 上下文
* @param srcFile 裁剪原图
* @param aspectX 裁剪宽比例自由裁剪传0
* @param aspectY 裁剪高比例自由裁剪传0
* @param isFreeCrop 是否自由裁剪
* @param requestCode 裁剪请求码
*/
public void startSystemCrop(BackgroundBean cropBean, int aspectX, int aspectY, boolean isFreeCrop, int requestCode) {
LogUtils.d(TAG, "startSystemCrop: 启动系统裁剪,自由裁剪:" + isFreeCrop);
File srcFile = new File(cropBean.getBackgroundFilePath());
// 校验原图
if (srcFile == null || !srcFile.exists() || srcFile.length() <= 100) {
Toast.makeText(this, "无有效图片可裁剪", Toast.LENGTH_SHORT).show();
LogUtils.e(TAG, "startSystemCrop: 原图无效");
return;
}
// 生成输入Uri
Uri inputUri = getFileProviderUri(srcFile);
if (inputUri == null) {
Toast.makeText(this, "获取图片Uri失败", Toast.LENGTH_SHORT).show();
LogUtils.e(TAG, "startSystemCrop: 输入Uri生成失败");
return;
}
// 生成输出文件使用BackgroundSourceUtils的压缩路径
BackgroundSourceUtils bgSourceUtils = BackgroundSourceUtils.getInstance(this);
File outputFile = new File(bgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath());
if (outputFile == null) {
Toast.makeText(this, "裁剪输出路径无效", Toast.LENGTH_SHORT).show();
LogUtils.e(TAG, "startSystemCrop: 输出文件为空");
return;
}
Uri outputUri = getFileProviderUri(outputFile);
Uri cropOutPutUri = outputUri;
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(inputUri, "image/*");
//intent.setDataAndType(inputUri, activity.getContentResolver().getType(inputUri));
intent.putExtra("crop", "true");
intent.putExtra("noFaceDetection", true);
if (!isFreeCrop) {
intent.putExtra("aspectX", aspectX);
intent.putExtra("aspectY", aspectY);
}
intent.putExtra("return-data", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, cropOutPutUri);
intent.putExtra("scale", true);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent, requestCode);
LogUtils.d(TAG, "startSystemCrop: 启动裁剪成功,输出路径:" + outputFile.getAbsolutePath());
}
/**
* 获取FileProvider Uri复用方法避免重复代码
*/
public Uri getFileProviderUri(File file) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
String FILE_PROVIDER_AUTHORITY = getPackageName() + ".fileprovider";
return FileProvider.getUriForFile(this, FILE_PROVIDER_AUTHORITY, file);
} else {
return Uri.fromFile(file);
}
} catch (Exception e) {
LogUtils.e(TAG, "getFileProviderUri: 生成Uri失败" + e.getMessage());
return null;
}
}
@Override @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

View File

@@ -245,23 +245,15 @@ public class BackgroundSourceUtils {
return contentUri; return contentUri;
} }
public boolean createCropFileProviderPath(Uri uri) { public BackgroundBean createCropFileProviderBackgroundBean(Uri uri) {
InputStream is = null; InputStream is = null;
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
clearCropTempFiles(); clearCropTempFiles();
String szType = mContext.getContentResolver().getType(uri); //String szType = mContext.getContentResolver().getType(uri);
// 2. 截取MIME类型后缀如从image/jpeg中提取jpeg【核心新增逻辑】 // 2. 截取MIME类型后缀如从image/jpeg中提取jpeg【核心新增逻辑】
String fileSuffix = ""; String fileSuffix = FileUtils.getFileSuffix(mContext, uri);
if (szType != null && szType.contains("/")) {
// 分割字符串,取"/"后面的部分(如"image/jpeg" → 分割后取索引1的"jpeg"
fileSuffix = szType.split("/")[1];
// 调试日志:打印截取后的文件后缀
} else {
// 异常处理若类型为空或格式错误默认后缀设为jpeg保留原逻辑兼容性
fileSuffix = "jpeg";
}
String newCropFileName = UUID.randomUUID().toString() + System.currentTimeMillis(); String newCropFileName = UUID.randomUUID().toString() + System.currentTimeMillis();
mCropSourceFile = new File(fCropCacheDir, newCropFileName + "." + fileSuffix); mCropSourceFile = new File(fCropCacheDir, newCropFileName + "." + fileSuffix);
mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + "." + fileSuffix); mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + "." + fileSuffix);
@@ -273,7 +265,7 @@ public class BackgroundSourceUtils {
is = mContext.getContentResolver().openInputStream(uri); is = mContext.getContentResolver().openInputStream(uri);
if (is == null) { if (is == null) {
LogUtils.e(TAG, "【选图解析】ContentResolver打开Uri失败Uri" + uri.toString()); LogUtils.e(TAG, "【选图解析】ContentResolver打开Uri失败Uri" + uri.toString());
return false; return null;
} }
// 2. 初始化选图临时文件输出流Java7 手动创建流不依赖try-with-resources // 2. 初始化选图临时文件输出流Java7 手动创建流不依赖try-with-resources
@@ -310,14 +302,14 @@ public class BackgroundSourceUtils {
LogUtils.d(TAG, "→ 目标临时文件大小:" + mCropSourceFile.length() + " bytes"); LogUtils.d(TAG, "→ 目标临时文件大小:" + mCropSourceFile.length() + " bytes");
LogUtils.d(TAG, "→ 目标剪裁临时文件:" + mCropResultFile.getAbsolutePath()); LogUtils.d(TAG, "→ 目标剪裁临时文件:" + mCropResultFile.getAbsolutePath());
LogUtils.d(TAG, "→ 目标剪裁临时文件大小:" + mCropResultFile.length() + " bytes"); LogUtils.d(TAG, "→ 目标剪裁临时文件大小:" + mCropResultFile.length() + " bytes");
return true; return previewBackgroundBean;
} catch (Exception e) { } catch (Exception e) {
// 捕获所有异常IO异常/空指针等),避免崩溃 // 捕获所有异常IO异常/空指针等),避免崩溃
LogUtils.e(TAG, "【选图解析】流复制异常:" + e.getMessage(), e); LogUtils.e(TAG, "【选图解析】流复制异常:" + e.getMessage(), e);
// 异常时清理无效文件,防止残留 // 异常时清理无效文件,防止残留
clearCropTempFiles(); clearCropTempFiles();
return false; return null;
} finally { } finally {
// 7. 手动关闭流资源Java7 标准写法,避免内存泄漏) // 7. 手动关闭流资源Java7 标准写法,避免内存泄漏)

View File

@@ -11,6 +11,8 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.UUID; import java.util.UUID;
import android.content.Context;
import android.net.Uri;
/** /**
* 文件操作工具类 * 文件操作工具类
@@ -260,6 +262,20 @@ public class FileUtils {
} }
} }
} }
public static String getFileSuffix(Context context, Uri uri){
String szType = context.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";
}
return fileSuffix;
}
} }

View File

@@ -12,9 +12,14 @@ import android.provider.MediaStore;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List; import java.util.List;
import cc.winboll.studio.powerbell.App; import cc.winboll.studio.powerbell.model.BackgroundBean;
/** /**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
@@ -22,127 +27,100 @@ import cc.winboll.studio.powerbell.App;
* @Describe 图片裁剪工具类(仅调用系统裁剪,传入比例/自由裁剪参数) * @Describe 图片裁剪工具类(仅调用系统裁剪,传入比例/自由裁剪参数)
*/ */
public class ImageCropUtils { public class ImageCropUtils {
private static final String TAG = "ImageCropUtils"; public static final String TAG = "ImageCropUtils";
// FileProvider授权与清单文件一致 // FileProvider授权与清单文件一致
/**
* 启动系统裁剪工具
* @param activity 上下文
* @param srcFile 裁剪原图
* @param aspectX 裁剪宽比例自由裁剪传0
* @param aspectY 裁剪高比例自由裁剪传0
* @param isFreeCrop 是否自由裁剪
* @param requestCode 裁剪请求码
*/
public static void startSystemCrop(Activity activity, File srcFile, int aspectX, int aspectY, boolean isFreeCrop, int requestCode) {
Log.d(TAG, "startSystemCrop: 启动系统裁剪,自由裁剪:" + isFreeCrop);
// 校验原图
if (srcFile == null || !srcFile.exists() || srcFile.length() <= 100) {
Toast.makeText(activity, "无有效图片可裁剪", Toast.LENGTH_SHORT).show();
Log.e(TAG, "startSystemCrop: 原图无效");
return;
}
// 生成输入Uri //
Uri inputUri = getFileProviderUri(activity, srcFile); // /**
if (inputUri == null) { // * 保存剪裁后的Bitmap优化版
Toast.makeText(activity, "获取图片Uri失败", Toast.LENGTH_SHORT).show(); // */
Log.e(TAG, "startSystemCrop: 输入Uri生成失败"); // private void saveCropBitmap(Bitmap bitmap) {
return; // if (bitmap == null) {
} // ToastUtils.show("剪裁图片为空");
// return;
// 生成输出文件使用BackgroundSourceUtils的压缩路径 // }
BackgroundSourceUtils bgSourceUtils = BackgroundSourceUtils.getInstance(activity); //
File outputFile = new File(bgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath()); // // 内存优化:大图片自动缩放
if (outputFile == null) { // Bitmap scaledBitmap = bitmap;
Toast.makeText(activity, "裁剪输出路径无效", Toast.LENGTH_SHORT).show(); // if (bitmap.getByteCount() > 10 * 1024 * 1024) { // 超过10MB
Log.e(TAG, "startSystemCrop: 输出文件为空"); // float scale = 1.0f;
return; // while (scaledBitmap.getByteCount() > 5 * 1024 * 1024) {
} // scale -= 0.2f; // 每次缩小20%
Uri outputUri = getFileProviderUri(activity, outputFile); // if (scale < 0.2f) break; // 最小缩放到20%
// scaledBitmap = scaleBitmap(scaledBitmap, scale);
// 构建裁剪意图 // }
Intent intent = new Intent("com.android.camera.action.CROP"); // if (scaledBitmap != bitmap) {
intent.setDataAndType(inputUri, "image/*"); // bitmap.recycle(); // 回收原Bitmap
intent.putExtra("crop", "true"); // }
intent.putExtra("noFaceDetection", true); // }
//
// 设置裁剪比例(自由裁剪则不设置) // // 优化:创建保存目录
if (!isFreeCrop && aspectX > 0 && aspectY > 0) { // File backgroundDir = new File(mBackgroundPictureUtils.getBackgroundDir());
intent.putExtra("aspectX", aspectX); // if (!backgroundDir.exists()) {
intent.putExtra("aspectY", aspectY); // if (!backgroundDir.mkdirs()) {
Log.d(TAG, "startSystemCrop: 裁剪比例:" + aspectX + ":" + aspectY); // ToastUtils.show("无法创建保存目录");
} // if (scaledBitmap != bitmap) scaledBitmap.recycle();
// return;
// 输出配置 // }
intent.putExtra("outputX", 2048); // }
intent.putExtra("outputY", 2048); //
intent.putExtra("scale", true); // File saveFile = new File(backgroundDir, getBackgroundFileName());
intent.putExtra("scaleUpIfNeeded", true); //
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); // // 优化:检查文件是否可写
intent.putExtra("quality", 100); // if (saveFile.exists() && !saveFile.canWrite()) {
intent.putExtra("return-data", false); // if (!saveFile.delete()) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri); // ToastUtils.show("无法删除旧文件");
// if (scaledBitmap != bitmap) scaledBitmap.recycle();
// 权限设置 // return;
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // }
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); // FileOutputStream fos = null;
} // try {
// fos = new FileOutputStream(saveFile);
// 适配系统裁剪工具 // boolean success = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
try { // fos.flush();
List<ResolveInfo> resolveInfos = activity.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); // if (success) {
if (!resolveInfos.isEmpty()) { // ToastUtils.show("保存成功");
ResolveInfo resolveInfo = resolveInfos.get(0); // // 更新数据
String cropPackageName = resolveInfo.activityInfo.packageName; // mBackgroundPictureUtils.getBackgroundPictureBean().setIsUseBackgroundFile(true);
String cropActivityName = resolveInfo.activityInfo.name; // updatePreviewBackground();
// } else {
// 授予MIUI裁剪权限 // ToastUtils.show("图片压缩保存失败");
if (cropPackageName.equals("com.miui.gallery")) { // }
activity.grantUriPermission(cropPackageName, inputUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // } catch (FileNotFoundException e) {
activity.grantUriPermission(cropPackageName, outputUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // LogUtils.e(TAG, "文件未找到" + e);
} // ToastUtils.show("保存失败:文件路径错误");
// } catch (IOException e) {
// 显式启动裁剪 // LogUtils.e(TAG, "写入异常" + e);
Intent cropIntent = new Intent(intent); // ToastUtils.show("保存失败:磁盘可能已满或路径错误");
cropIntent.setComponent(new ComponentName(cropPackageName, cropActivityName)); // } finally {
activity.startActivityForResult(cropIntent, requestCode); // if (fos != null) {
Log.d(TAG, "startSystemCrop: 启动裁剪成功,输出路径:" + outputFile.getAbsolutePath()); // try {
} else { // fos.close();
// 兜底启动 // } catch (IOException e) {
Intent chooser = Intent.createChooser(intent, "选择裁剪工具"); // LogUtils.e(TAG, "流关闭异常" + e);
chooser.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // }
if (chooser.resolveActivity(activity.getPackageManager()) != null) { // }
activity.startActivityForResult(chooser, requestCode); // if (scaledBitmap != null && !scaledBitmap.isRecycled()) {
} else { // scaledBitmap.recycle();
Toast.makeText(activity, "无可用裁剪工具", Toast.LENGTH_SHORT).show(); // }
} // }
} // }
} catch (Exception e) { //
Log.e(TAG, "startSystemCrop: 启动裁剪失败:" + e.getMessage()); // /**
Toast.makeText(activity, "裁剪工具启动失败", Toast.LENGTH_SHORT).show(); // * 缩放Bitmap
} // */
} // private Bitmap scaleBitmap(Bitmap original, float scale) {
// if (original == null) {
/** // return null;
* 获取FileProvider Uri复用方法避免重复代码 // }
*/ // int width = (int) (original.getWidth() * scale);
public static Uri getFileProviderUri(Activity activity, File file) { // int height = (int) (original.getHeight() * scale);
try { // return Bitmap.createScaledBitmap(original, width, height, true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // }
String FILE_PROVIDER_AUTHORITY = activity.getPackageName() + ".fileprovider";
return FileProvider.getUriForFile(activity, FILE_PROVIDER_AUTHORITY, file);
} else {
return Uri.fromFile(file);
}
} catch (Exception e) {
Log.e(TAG, "getFileProviderUri: 生成Uri失败" + e.getMessage());
return null;
}
}
} }