20251204_134218_045
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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 标准写法,避免内存泄漏)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user