20251204_134218_045
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#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
|
||||
libraryProject=
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.12
|
||||
buildCount=195
|
||||
buildCount=205
|
||||
baseBetaVersion=15.11.13
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -14,10 +13,10 @@ import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.FileProvider;
|
||||
import cc.winboll.studio.libaes.views.AToolbar;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
@@ -236,9 +235,8 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "【按钮点击】固定比例裁剪");
|
||||
// 调用裁剪工具类:传入上下文、预览图、固定比例(按视图宽高)、请求码
|
||||
ImageCropUtils.startSystemCrop(
|
||||
BackgroundSettingsActivity.this,
|
||||
new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath()),
|
||||
startSystemCrop(
|
||||
mBgSourceUtils.getPreviewBackgroundBean(),
|
||||
mBackgroundView.getWidth(),
|
||||
mBackgroundView.getHeight(),
|
||||
false,
|
||||
@@ -252,9 +250,8 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "【按钮点击】自由裁剪");
|
||||
// 调用裁剪工具类:传入上下文、预览图、自由裁剪(比例参数传0)、请求码
|
||||
ImageCropUtils.startSystemCrop(
|
||||
BackgroundSettingsActivity.this,
|
||||
new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath()),
|
||||
startSystemCrop(
|
||||
mBgSourceUtils.getPreviewBackgroundBean(),
|
||||
0,
|
||||
0,
|
||||
true,
|
||||
@@ -288,7 +285,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
LogUtils.d(TAG, "【拍照权限】已获取");
|
||||
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
try {
|
||||
Uri photoUri = ImageCropUtils.getFileProviderUri(BackgroundSettingsActivity.this, mfTakePhoto);
|
||||
Uri photoUri = getFileProviderUri(mfTakePhoto);
|
||||
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
|
||||
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
|
||||
LogUtils.d(TAG, "【拍照启动】Uri:" + photoUri.toString());
|
||||
@@ -412,9 +409,8 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
doubleRefreshPreview();
|
||||
|
||||
// 拍照后启动固定比例裁剪(调用工具类)
|
||||
ImageCropUtils.startSystemCrop(
|
||||
this,
|
||||
mfTakePhoto,
|
||||
startSystemCrop(
|
||||
mBgSourceUtils.getPreviewBackgroundBean(),
|
||||
mBackgroundView.getWidth(),
|
||||
mBackgroundView.getHeight(),
|
||||
false,
|
||||
@@ -460,7 +456,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
|
||||
private void handleCropImageResult(int requestCode, int resultCode, Intent data) {
|
||||
Log.d(TAG, "handleCropImageResult: 处理裁剪结果");
|
||||
LogUtils.d(TAG, "handleCropImageResult: 处理裁剪结果");
|
||||
File cropTempFile = new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath());
|
||||
boolean isFileExist = cropTempFile.exists();
|
||||
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;
|
||||
|
||||
if (isCropSuccess) {
|
||||
Log.d(TAG, "handleCropImageResult: 裁剪成功");
|
||||
LogUtils.d(TAG, "handleCropImageResult: 裁剪成功");
|
||||
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
previewBean.setIsUseBackgroundFile(true);
|
||||
previewBean.setIsUseBackgroundScaledCompressFile(true);
|
||||
@@ -495,7 +491,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
public void run() {
|
||||
if (mBackgroundView != null && !isFinishing()) {
|
||||
mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean());
|
||||
Log.d(TAG, "handleCropImageResult: 裁剪图片加载完成");
|
||||
LogUtils.d(TAG, "handleCropImageResult: 裁剪图片加载完成");
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
@@ -505,9 +501,9 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
|
||||
private float getRatioFromSystemCropFile(File systemCropFile) {
|
||||
Log.d(TAG, "getRatioFromSystemCropFile: 读取比例");
|
||||
LogUtils.d(TAG, "getRatioFromSystemCropFile: 读取比例");
|
||||
if (systemCropFile == null || !systemCropFile.exists() || !systemCropFile.isFile()) {
|
||||
Log.e(TAG, "getRatioFromSystemCropFile: 文件无效");
|
||||
LogUtils.e(TAG, "getRatioFromSystemCropFile: 文件无效");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -518,19 +514,19 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
int cropWidth = options.outWidth;
|
||||
int cropHeight = options.outHeight;
|
||||
if (cropWidth <= 0 || cropHeight <= 0) {
|
||||
Log.e(TAG, "getRatioFromSystemCropFile: 尺寸无效");
|
||||
LogUtils.e(TAG, "getRatioFromSystemCropFile: 尺寸无效");
|
||||
return -1;
|
||||
}
|
||||
|
||||
float systemRatio = (float) cropWidth / cropHeight;
|
||||
Log.d(TAG, "getRatioFromSystemCropFile: 比例:" + systemRatio);
|
||||
LogUtils.d(TAG, "getRatioFromSystemCropFile: 比例:" + systemRatio);
|
||||
return systemRatio;
|
||||
}
|
||||
|
||||
private Bitmap adjustBitmapToFinalRatio(Bitmap originalBitmap, float finalCropRatio) {
|
||||
Log.d(TAG, "adjustBitmapToFinalRatio: 调整比例");
|
||||
LogUtils.d(TAG, "adjustBitmapToFinalRatio: 调整比例");
|
||||
if (originalBitmap == null || originalBitmap.isRecycled() || finalCropRatio <= 0) {
|
||||
Log.e(TAG, "adjustBitmapToFinalRatio: 参数无效");
|
||||
LogUtils.e(TAG, "adjustBitmapToFinalRatio: 参数无效");
|
||||
return originalBitmap;
|
||||
}
|
||||
|
||||
@@ -539,7 +535,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
float originalRatio = (float) originalWidth / originalHeight;
|
||||
|
||||
if (Math.abs(originalRatio - finalCropRatio) < 0.001f) {
|
||||
Log.d(TAG, "adjustBitmapToFinalRatio: 比例一致");
|
||||
LogUtils.d(TAG, "adjustBitmapToFinalRatio: 比例一致");
|
||||
return originalBitmap;
|
||||
}
|
||||
|
||||
@@ -552,7 +548,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
|
||||
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(
|
||||
originalBitmap,
|
||||
@@ -566,15 +562,15 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
|
||||
private void saveScaledBitmapToFile(Bitmap bitmap, File targetFile) {
|
||||
Log.d(TAG, "saveScaledBitmapToFile: 保存图片");
|
||||
LogUtils.d(TAG, "saveScaledBitmapToFile: 保存图片");
|
||||
if (bitmap == null || bitmap.isRecycled() || targetFile == null) {
|
||||
Log.e(TAG, "saveScaledBitmapToFile: 参数无效");
|
||||
LogUtils.e(TAG, "saveScaledBitmapToFile: 参数无效");
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetFile.exists()) {
|
||||
boolean deleteSuccess = targetFile.delete();
|
||||
Log.d(TAG, "saveScaledBitmapToFile: 删除原文件:" + (deleteSuccess ? "成功" : "失败"));
|
||||
LogUtils.d(TAG, "saveScaledBitmapToFile: 删除原文件:" + (deleteSuccess ? "成功" : "失败"));
|
||||
}
|
||||
|
||||
OutputStream outputStream = null;
|
||||
@@ -582,24 +578,24 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
outputStream = new BufferedOutputStream(new FileOutputStream(targetFile));
|
||||
bitmap.compress(CompressFormat.JPEG, 100, outputStream);
|
||||
outputStream.flush();
|
||||
Log.d(TAG, "saveScaledBitmapToFile: 保存成功");
|
||||
LogUtils.d(TAG, "saveScaledBitmapToFile: 保存成功");
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "saveScaledBitmapToFile: 异常:" + e.getMessage());
|
||||
LogUtils.e(TAG, "saveScaledBitmapToFile: 异常:" + e.getMessage());
|
||||
} finally {
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "saveScaledBitmapToFile: 关闭流异常");
|
||||
LogUtils.e(TAG, "saveScaledBitmapToFile: 关闭流异常");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap parseCropTempFileToBitmap(File cropTempFile) {
|
||||
Log.d(TAG, "parseCropTempFileToBitmap: 解析文件");
|
||||
LogUtils.d(TAG, "parseCropTempFileToBitmap: 解析文件");
|
||||
if (cropTempFile == null || !cropTempFile.exists() || !cropTempFile.isFile() || cropTempFile.length() <= 100) {
|
||||
Log.e(TAG, "parseCropTempFileToBitmap: 文件无效");
|
||||
LogUtils.e(TAG, "parseCropTempFileToBitmap: 文件无效");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -613,7 +609,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
sampleSize *= 2;
|
||||
}
|
||||
sampleSize = Math.min(sampleSize, 16);
|
||||
Log.d(TAG, "parseCropTempFileToBitmap: 采样率:" + sampleSize);
|
||||
LogUtils.d(TAG, "parseCropTempFileToBitmap: 采样率:" + sampleSize);
|
||||
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = sampleSize;
|
||||
@@ -625,16 +621,16 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
try {
|
||||
cropBitmap = BitmapFactory.decodeFile(cropTempFile.getAbsolutePath(), options);
|
||||
if (cropBitmap == null || cropBitmap.isRecycled()) {
|
||||
Log.e(TAG, "parseCropTempFileToBitmap: 解析失败");
|
||||
LogUtils.e(TAG, "parseCropTempFileToBitmap: 解析失败");
|
||||
return null;
|
||||
}
|
||||
Log.d(TAG, "parseCropTempFileToBitmap: 解析成功");
|
||||
LogUtils.d(TAG, "parseCropTempFileToBitmap: 解析成功");
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG, "parseCropTempFileToBitmap: OOM");
|
||||
LogUtils.e(TAG, "parseCropTempFileToBitmap: OOM");
|
||||
Toast.makeText(this, "图片解析失败", Toast.LENGTH_SHORT).show();
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "parseCropTempFileToBitmap: 异常:" + e.getMessage());
|
||||
LogUtils.e(TAG, "parseCropTempFileToBitmap: 异常:" + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -683,14 +679,12 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
LogUtils.d(TAG, "【选图权限】已添加持久化权限");
|
||||
}
|
||||
|
||||
mBgSourceUtils.createCropFileProviderPath(selectedImage);
|
||||
String targetCompressPath = mBgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath();
|
||||
|
||||
BackgroundBean cropBean = mBgSourceUtils.createCropFileProviderBackgroundBean(selectedImage);
|
||||
|
||||
LogUtils.d(TAG, "【选图同步】路径绑定完成");
|
||||
// 选图后启动固定比例裁剪(调用工具类)
|
||||
ImageCropUtils.startSystemCrop(
|
||||
this,
|
||||
new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath()),
|
||||
startSystemCrop(
|
||||
cropBean,
|
||||
mBackgroundView.getWidth(),
|
||||
mBackgroundView.getHeight(),
|
||||
false,
|
||||
@@ -703,6 +697,87 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
LogUtils.d(TAG, "【操作回调】取消或失败");
|
||||
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
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
|
||||
@@ -245,23 +245,15 @@ public class BackgroundSourceUtils {
|
||||
return contentUri;
|
||||
}
|
||||
|
||||
public boolean createCropFileProviderPath(Uri uri) {
|
||||
public BackgroundBean createCropFileProviderBackgroundBean(Uri uri) {
|
||||
InputStream is = null;
|
||||
FileOutputStream fos = null;
|
||||
|
||||
try {
|
||||
clearCropTempFiles();
|
||||
String szType = mContext.getContentResolver().getType(uri);
|
||||
//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 fileSuffix = FileUtils.getFileSuffix(mContext, uri);
|
||||
String newCropFileName = UUID.randomUUID().toString() + System.currentTimeMillis();
|
||||
mCropSourceFile = new File(fCropCacheDir, newCropFileName + "." + fileSuffix);
|
||||
mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + "." + fileSuffix);
|
||||
@@ -273,7 +265,7 @@ public class BackgroundSourceUtils {
|
||||
is = mContext.getContentResolver().openInputStream(uri);
|
||||
if (is == null) {
|
||||
LogUtils.e(TAG, "【选图解析】ContentResolver打开Uri失败,Uri:" + uri.toString());
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 初始化选图临时文件输出流(Java7 手动创建流,不依赖try-with-resources)
|
||||
@@ -310,14 +302,14 @@ public class BackgroundSourceUtils {
|
||||
LogUtils.d(TAG, "→ 目标临时文件大小:" + mCropSourceFile.length() + " bytes");
|
||||
LogUtils.d(TAG, "→ 目标剪裁临时文件:" + mCropResultFile.getAbsolutePath());
|
||||
LogUtils.d(TAG, "→ 目标剪裁临时文件大小:" + mCropResultFile.length() + " bytes");
|
||||
return true;
|
||||
return previewBackgroundBean;
|
||||
|
||||
} catch (Exception e) {
|
||||
// 捕获所有异常(IO异常/空指针等),避免崩溃
|
||||
LogUtils.e(TAG, "【选图解析】流复制异常:" + e.getMessage(), e);
|
||||
// 异常时清理无效文件,防止残留
|
||||
clearCropTempFiles();
|
||||
return false;
|
||||
return null;
|
||||
|
||||
} finally {
|
||||
// 7. 手动关闭流资源(Java7 标准写法,避免内存泄漏)
|
||||
|
||||
@@ -11,6 +11,8 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,14 @@ import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import androidx.core.content.FileProvider;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
@@ -22,127 +27,100 @@ import cc.winboll.studio.powerbell.App;
|
||||
* @Describe 图片裁剪工具类(仅调用系统裁剪,传入比例/自由裁剪参数)
|
||||
*/
|
||||
public class ImageCropUtils {
|
||||
private static final String TAG = "ImageCropUtils";
|
||||
public static final String TAG = "ImageCropUtils";
|
||||
// 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) {
|
||||
Toast.makeText(activity, "获取图片Uri失败", Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "startSystemCrop: 输入Uri生成失败");
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成输出文件(使用BackgroundSourceUtils的压缩路径)
|
||||
BackgroundSourceUtils bgSourceUtils = BackgroundSourceUtils.getInstance(activity);
|
||||
File outputFile = new File(bgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath());
|
||||
if (outputFile == null) {
|
||||
Toast.makeText(activity, "裁剪输出路径无效", Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "startSystemCrop: 输出文件为空");
|
||||
return;
|
||||
}
|
||||
Uri outputUri = getFileProviderUri(activity, outputFile);
|
||||
|
||||
// 构建裁剪意图
|
||||
Intent intent = new Intent("com.android.camera.action.CROP");
|
||||
intent.setDataAndType(inputUri, "image/*");
|
||||
intent.putExtra("crop", "true");
|
||||
intent.putExtra("noFaceDetection", true);
|
||||
|
||||
// 设置裁剪比例(自由裁剪则不设置)
|
||||
if (!isFreeCrop && aspectX > 0 && aspectY > 0) {
|
||||
intent.putExtra("aspectX", aspectX);
|
||||
intent.putExtra("aspectY", aspectY);
|
||||
Log.d(TAG, "startSystemCrop: 裁剪比例:" + aspectX + ":" + aspectY);
|
||||
}
|
||||
|
||||
// 输出配置
|
||||
intent.putExtra("outputX", 2048);
|
||||
intent.putExtra("outputY", 2048);
|
||||
intent.putExtra("scale", true);
|
||||
intent.putExtra("scaleUpIfNeeded", true);
|
||||
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
|
||||
intent.putExtra("quality", 100);
|
||||
intent.putExtra("return-data", false);
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
|
||||
|
||||
// 权限设置
|
||||
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);
|
||||
}
|
||||
|
||||
// 适配系统裁剪工具
|
||||
try {
|
||||
List<ResolveInfo> resolveInfos = activity.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
if (!resolveInfos.isEmpty()) {
|
||||
ResolveInfo resolveInfo = resolveInfos.get(0);
|
||||
String cropPackageName = resolveInfo.activityInfo.packageName;
|
||||
String cropActivityName = resolveInfo.activityInfo.name;
|
||||
|
||||
// 授予MIUI裁剪权限
|
||||
if (cropPackageName.equals("com.miui.gallery")) {
|
||||
activity.grantUriPermission(cropPackageName, inputUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
activity.grantUriPermission(cropPackageName, outputUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
}
|
||||
|
||||
// 显式启动裁剪
|
||||
Intent cropIntent = new Intent(intent);
|
||||
cropIntent.setComponent(new ComponentName(cropPackageName, cropActivityName));
|
||||
activity.startActivityForResult(cropIntent, requestCode);
|
||||
Log.d(TAG, "startSystemCrop: 启动裁剪成功,输出路径:" + outputFile.getAbsolutePath());
|
||||
} else {
|
||||
// 兜底启动
|
||||
Intent chooser = Intent.createChooser(intent, "选择裁剪工具");
|
||||
chooser.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
if (chooser.resolveActivity(activity.getPackageManager()) != null) {
|
||||
activity.startActivityForResult(chooser, requestCode);
|
||||
} else {
|
||||
Toast.makeText(activity, "无可用裁剪工具", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "startSystemCrop: 启动裁剪失败:" + e.getMessage());
|
||||
Toast.makeText(activity, "裁剪工具启动失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取FileProvider Uri(复用方法,避免重复代码)
|
||||
*/
|
||||
public static Uri getFileProviderUri(Activity activity, File file) {
|
||||
try {
|
||||
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;
|
||||
}
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 保存剪裁后的Bitmap(优化版)
|
||||
// */
|
||||
// private void saveCropBitmap(Bitmap bitmap) {
|
||||
// if (bitmap == null) {
|
||||
// ToastUtils.show("剪裁图片为空");
|
||||
// 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
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 优化:创建保存目录
|
||||
// File backgroundDir = new File(mBackgroundPictureUtils.getBackgroundDir());
|
||||
// if (!backgroundDir.exists()) {
|
||||
// if (!backgroundDir.mkdirs()) {
|
||||
// ToastUtils.show("无法创建保存目录");
|
||||
// if (scaledBitmap != bitmap) scaledBitmap.recycle();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// File saveFile = new File(backgroundDir, getBackgroundFileName());
|
||||
//
|
||||
// // 优化:检查文件是否可写
|
||||
// if (saveFile.exists() && !saveFile.canWrite()) {
|
||||
// if (!saveFile.delete()) {
|
||||
// ToastUtils.show("无法删除旧文件");
|
||||
// if (scaledBitmap != bitmap) scaledBitmap.recycle();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// FileOutputStream fos = null;
|
||||
// try {
|
||||
// fos = new FileOutputStream(saveFile);
|
||||
// boolean success = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
|
||||
// fos.flush();
|
||||
// if (success) {
|
||||
// ToastUtils.show("保存成功");
|
||||
// // 更新数据
|
||||
// mBackgroundPictureUtils.getBackgroundPictureBean().setIsUseBackgroundFile(true);
|
||||
// updatePreviewBackground();
|
||||
// } else {
|
||||
// ToastUtils.show("图片压缩保存失败");
|
||||
// }
|
||||
// } catch (FileNotFoundException e) {
|
||||
// LogUtils.e(TAG, "文件未找到" + e);
|
||||
// ToastUtils.show("保存失败:文件路径错误");
|
||||
// } catch (IOException e) {
|
||||
// LogUtils.e(TAG, "写入异常" + e);
|
||||
// ToastUtils.show("保存失败:磁盘可能已满或路径错误");
|
||||
// } finally {
|
||||
// if (fos != null) {
|
||||
// try {
|
||||
// fos.close();
|
||||
// } catch (IOException e) {
|
||||
// LogUtils.e(TAG, "流关闭异常" + e);
|
||||
// }
|
||||
// }
|
||||
// if (scaledBitmap != null && !scaledBitmap.isRecycled()) {
|
||||
// scaledBitmap.recycle();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 缩放Bitmap
|
||||
// */
|
||||
// private Bitmap scaleBitmap(Bitmap original, float scale) {
|
||||
// if (original == null) {
|
||||
// return null;
|
||||
// }
|
||||
// int width = (int) (original.getWidth() * scale);
|
||||
// int height = (int) (original.getHeight() * scale);
|
||||
// return Bitmap.createScaledBitmap(original, width, height, true);
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user