调试信息准备
This commit is contained in:
@@ -7,12 +7,10 @@ import android.net.Uri;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
import cc.winboll.studio.libappbase.ToastUtils;
|
import cc.winboll.studio.libappbase.ToastUtils;
|
||||||
import cc.winboll.studio.powerbell.BuildConfig;
|
import cc.winboll.studio.powerbell.BuildConfig;
|
||||||
import cc.winboll.studio.powerbell.R;
|
|
||||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -32,54 +30,44 @@ public class BackgroundSourceUtils {
|
|||||||
|
|
||||||
public static final String TAG = "BackgroundSourceUtils";
|
public static final String TAG = "BackgroundSourceUtils";
|
||||||
// 裁剪相关常量(统一定义,避免硬编码)
|
// 裁剪相关常量(统一定义,避免硬编码)
|
||||||
private static final String CROP_CACHE_DIR_NAME = "cache"; // 裁剪缓存目录(基础目录下)
|
private static final String CROP_CACHE_DIR_NAME = "cache";
|
||||||
private static final String CROP_TEMP_FILE_NAME = "SourceCropTemp.jpg"; // 裁剪输入临时文件
|
private static final String CROP_TEMP_FILE_NAME = "SourceCropTemp.jpg";
|
||||||
private static final String CROP_RESULT_FILE_NAME = "SourceCropped.jpg"; // 裁剪输出结果文件
|
private static final String CROP_RESULT_FILE_NAME = "SourceCropped.jpg";
|
||||||
public static final String FILE_PROVIDER_AUTHORITY = BuildConfig.APPLICATION_ID + ".fileprovider"; // 多包名兼容
|
public static final String FILE_PROVIDER_AUTHORITY = BuildConfig.APPLICATION_ID + ".fileprovider";
|
||||||
// 图片操作基础目录(核心:系统公共图片目录)
|
// 图片操作基础目录
|
||||||
private static final String PICTURE_BASE_DIR = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + "PowerBell";
|
private static final String PICTURE_BASE_DIR = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + "PowerBell";
|
||||||
// 新增:压缩图统一存储目录(图片基础目录下/BackgroundCrops)
|
|
||||||
private static final String SOURCE_DIR_NAME = "BackgroundSource";
|
private static final String SOURCE_DIR_NAME = "BackgroundSource";
|
||||||
private static final String COMPRESS_DIR_NAME = "BackgroundCompress";
|
private static final String COMPRESS_DIR_NAME = "BackgroundCompress";
|
||||||
|
|
||||||
// 1. 静态实例加volatile,禁止指令重排,保证可见性(双重校验锁单例核心)
|
// 单例相关
|
||||||
private static volatile BackgroundSourceUtils sInstance;
|
private static volatile BackgroundSourceUtils sInstance;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private File currentBackgroundBeanFile;
|
private File currentBackgroundBeanFile;
|
||||||
private BackgroundBean currentBackgroundBean; // 正式Bean:独立实例
|
private BackgroundBean currentBackgroundBean;
|
||||||
private File previewBackgroundBeanFile;
|
private File previewBackgroundBeanFile;
|
||||||
private BackgroundBean previewBackgroundBean; // 预览Bean:独立实例(与正式Bean完全分离)
|
private BackgroundBean previewBackgroundBean;
|
||||||
|
|
||||||
// 2. 统一文件目录(分两类:图片目录→系统公共目录,JSON目录→应用外置存储)
|
// 目录文件相关
|
||||||
// 图片操作目录(系统公共目录:/storage/emulated/0/Pictures/PowerBell/)
|
private File fPictureBaseDir;
|
||||||
private File fPictureBaseDir; // 图片基础目录
|
private File fCropCacheDir;
|
||||||
private File fCropCacheDir; // 裁剪缓存目录(基础目录下/cache)
|
private File fBackgroundSourceDir;
|
||||||
private File fBackgroundSourceDir; // 图片存储目录(基础目录下,存储正式/预览原图)
|
private File fBackgroundCompressDir;
|
||||||
private File fBackgroundCompressDir; // 新增:压缩图统一存储目录(基础目录下/BackgroundCrops)
|
private File fUtilsDir;
|
||||||
// JSON配置目录(原应用外置存储目录,不改变)
|
private File fModelDir;
|
||||||
private File fUtilsDir; // 工具类根目录(/Android/data/包名/files/BackgroundSourceUtils)
|
private File mCropSourceFile;
|
||||||
private File fModelDir; // 模型文件目录(存储JSON配置)
|
private File mCropResultFile;
|
||||||
// 裁剪文件(统一放入图片基础目录下的cache)
|
|
||||||
private File mCropSourceFile; // 裁剪临时文件(fCropCacheDir下)
|
|
||||||
private File mCropResultFile; // 裁剪临时文件(fCropCacheDir下)
|
|
||||||
|
|
||||||
// 3. 私有构造器(加防反射逻辑+初始化所有目录/文件)
|
// 双重校验锁单例
|
||||||
private BackgroundSourceUtils(Context context) {
|
private BackgroundSourceUtils(Context context) {
|
||||||
// 防反射破坏:若已有实例,抛异常阻止重复创建
|
|
||||||
if (sInstance != null) {
|
if (sInstance != null) {
|
||||||
throw new RuntimeException("BackgroundSourceUtils 是单例类,禁止重复创建!");
|
throw new RuntimeException("BackgroundSourceUtils 是单例类,禁止重复创建!");
|
||||||
}
|
}
|
||||||
// 上下文用Application Context,避免Activity内存泄漏
|
|
||||||
this.mContext = context.getApplicationContext();
|
this.mContext = context.getApplicationContext();
|
||||||
// 【核心调整1】实例化初期优先初始化所有必要目录(确保实例化完成时目录100%就绪)
|
|
||||||
initNecessaryDirs();
|
initNecessaryDirs();
|
||||||
// 初始化所有文件(裁剪临时文件/结果文件等)
|
|
||||||
initAllFiles();
|
initAllFiles();
|
||||||
// 加载配置(确保正式/预览Bean是两份独立实例)
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 双重校验锁单例(线程安全,高效,支持多线程并发调用,Java7语法兼容)
|
|
||||||
public static BackgroundSourceUtils getInstance(Context context) {
|
public static BackgroundSourceUtils getInstance(Context context) {
|
||||||
if (sInstance == null) {
|
if (sInstance == null) {
|
||||||
synchronized (BackgroundSourceUtils.class) {
|
synchronized (BackgroundSourceUtils.class) {
|
||||||
@@ -92,161 +80,109 @@ public class BackgroundSourceUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 【核心新增】统一初始化所有必要目录(实例化初期调用,确保目录优先创建)
|
* 统一初始化所有必要目录
|
||||||
* 整合图片目录+JSON目录,集中管理目录创建逻辑,保证实例化完成时所有目录就绪
|
|
||||||
*/
|
*/
|
||||||
private void initNecessaryDirs() {
|
private void initNecessaryDirs() {
|
||||||
LogUtils.d(TAG, "【实例化初期-目录初始化】开始创建所有必要目录...");
|
LogUtils.d(TAG, "【目录初始化】开始创建所有必要目录");
|
||||||
// 1. 初始化图片操作目录(系统公共目录 /Pictures/PowerBell/)
|
|
||||||
initPictureDirs();
|
initPictureDirs();
|
||||||
// 2. 初始化JSON配置目录(应用外置存储)
|
|
||||||
initJsonDirs();
|
initJsonDirs();
|
||||||
LogUtils.d(TAG, "【实例化初期-目录初始化】所有必要目录创建完成!");
|
LogUtils.d(TAG, "【目录初始化】所有必要目录创建完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化图片操作目录(核心:系统公共图片目录 /Pictures/PowerBell/,新增压缩图目录)
|
* 初始化图片操作目录
|
||||||
* 【调整强化】新增目录创建后二次校验,失败则降级到备选目录,确保目录可用
|
|
||||||
*/
|
*/
|
||||||
private void initPictureDirs() {
|
private void initPictureDirs() {
|
||||||
// 1. 图片基础目录:/storage/emulated/0/Pictures/PowerBell
|
|
||||||
fPictureBaseDir = new File(PICTURE_BASE_DIR);
|
fPictureBaseDir = new File(PICTURE_BASE_DIR);
|
||||||
// 2. 图片存储目录:基础目录下(存储正式/预览原图)
|
|
||||||
fBackgroundSourceDir = new File(fPictureBaseDir, SOURCE_DIR_NAME);
|
fBackgroundSourceDir = new File(fPictureBaseDir, SOURCE_DIR_NAME);
|
||||||
// 3. 裁剪缓存目录:基础目录下/cache(所有裁剪操作在此目录)
|
|
||||||
fCropCacheDir = new File(fPictureBaseDir, CROP_CACHE_DIR_NAME);
|
fCropCacheDir = new File(fPictureBaseDir, CROP_CACHE_DIR_NAME);
|
||||||
// 4. 新增:压缩图统一存储目录(基础目录下/BackgroundCrops,所有压缩图放这里)
|
|
||||||
fBackgroundCompressDir = new File(fPictureBaseDir, COMPRESS_DIR_NAME);
|
fBackgroundCompressDir = new File(fPictureBaseDir, COMPRESS_DIR_NAME);
|
||||||
|
|
||||||
// 5. 强制创建所有图片目录(带二次校验+降级兜底)
|
createDirWithPermission(fPictureBaseDir, "图片基础目录");
|
||||||
createDirWithPermission(fPictureBaseDir, "图片基础目录(" + PICTURE_BASE_DIR + ")");
|
createDirWithPermission(fBackgroundSourceDir, "图片存储目录");
|
||||||
createDirWithPermission(fBackgroundSourceDir, "图片存储目录(基础目录下/" + SOURCE_DIR_NAME + ")");
|
createDirWithPermission(fCropCacheDir, "裁剪缓存目录");
|
||||||
createDirWithPermission(fCropCacheDir, "裁剪缓存目录(基础目录/" + CROP_CACHE_DIR_NAME + ")");
|
createDirWithPermission(fBackgroundCompressDir, "压缩图存储目录");
|
||||||
createDirWithPermission(fBackgroundCompressDir, "裁剪压缩图存储目录(基础目录/" + COMPRESS_DIR_NAME + ")");
|
|
||||||
|
|
||||||
// 6. 目录创建后最终校验(确保所有目录已就绪)
|
|
||||||
validatePictureDirs();
|
validatePictureDirs();
|
||||||
|
|
||||||
LogUtils.d(TAG, "【图片目录初始化】完成:" +
|
|
||||||
"基础目录=" + fPictureBaseDir.getAbsolutePath() +
|
|
||||||
"图片存储目录=" + fBackgroundSourceDir.getAbsolutePath() +
|
|
||||||
",裁剪缓存目录=" + fCropCacheDir.getAbsolutePath() +
|
|
||||||
",裁剪压缩图存储目录=" + fBackgroundCompressDir.getAbsolutePath());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化JSON配置目录(保留原逻辑:应用外置存储)
|
* 初始化JSON配置目录
|
||||||
* 【调整强化】新增目录创建后二次校验,失败则降级到应用内部缓存目录
|
|
||||||
*/
|
*/
|
||||||
private void initJsonDirs() {
|
private void initJsonDirs() {
|
||||||
// 1. 工具类根目录(应用外置存储)
|
|
||||||
fUtilsDir = mContext.getExternalFilesDir(TAG);
|
fUtilsDir = mContext.getExternalFilesDir(TAG);
|
||||||
if (fUtilsDir == null) {
|
if (fUtilsDir == null) {
|
||||||
LogUtils.e(TAG, "【JSON目录】应用外置存储不可用,切换到应用内部缓存目录");
|
LogUtils.e(TAG, "应用外置存储不可用,切换到应用内部缓存目录");
|
||||||
fUtilsDir = mContext.getDataDir();
|
fUtilsDir = mContext.getDataDir();
|
||||||
}
|
}
|
||||||
// 2. 模型文件目录(存储JSON配置)
|
|
||||||
fModelDir = new File(fUtilsDir, "ModelDir");
|
fModelDir = new File(fUtilsDir, "ModelDir");
|
||||||
// 强制创建JSON目录(带二次校验+降级兜底)
|
createDirWithPermission(fModelDir, "JSON配置目录");
|
||||||
createDirWithPermission(fModelDir, "JSON配置目录(应用外置存储)");
|
|
||||||
|
|
||||||
// 3. 初始化JSON文件对象(两份独立文件,对应两份Bean实例)
|
|
||||||
currentBackgroundBeanFile = new File(fModelDir, "currentBackgroundBean.json");
|
currentBackgroundBeanFile = new File(fModelDir, "currentBackgroundBean.json");
|
||||||
previewBackgroundBeanFile = new File(fModelDir, "previewBackgroundBean.json");
|
previewBackgroundBeanFile = new File(fModelDir, "previewBackgroundBean.json");
|
||||||
|
|
||||||
LogUtils.d(TAG, "【JSON目录初始化】完成:目录=" + fModelDir.getAbsolutePath() + ",正式JSON=" + currentBackgroundBeanFile.getName() + ",预览JSON=" + previewBackgroundBeanFile.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 【核心强化】创建目录并设置权限(适配系统公共目录/Pictures/PowerBell,确保实例化时目录就绪)
|
* 创建目录并校验
|
||||||
* @param dir 要创建的目录
|
|
||||||
* @param dirDesc 目录描述(用于日志打印)
|
|
||||||
*/
|
*/
|
||||||
private void createDirWithPermission(File dir, String dirDesc) {
|
private void createDirWithPermission(File dir, String dirDesc) {
|
||||||
if (dir == null) {
|
if (dir == null) {
|
||||||
LogUtils.e(TAG, "【文件管理】创建目录失败:目录对象为null(描述:" + dirDesc + ")");
|
LogUtils.e(TAG, dirDesc + "创建失败:目录对象为null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第一步:主动检测并创建目录(递归创建所有父目录)
|
|
||||||
if (!dir.exists()) {
|
if (!dir.exists()) {
|
||||||
LogUtils.d(TAG, "【文件管理】" + dirDesc + "不存在,开始创建:" + dir.getAbsolutePath());
|
LogUtils.d(TAG, dirDesc + "不存在,开始创建:" + dir.getAbsolutePath());
|
||||||
dir.mkdirs(); // 递归创建所有父目录
|
dir.mkdirs();
|
||||||
} else {
|
|
||||||
LogUtils.d(TAG, "【文件管理】" + dirDesc + "已存在:" + dir.getAbsolutePath());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 【新增】图片目录创建后最终校验(确保实例化时所有图片目录已就绪)
|
* 校验图片目录是否就绪
|
||||||
*/
|
*/
|
||||||
private void validatePictureDirs() {
|
private void validatePictureDirs() {
|
||||||
LogUtils.d(TAG, "【图片目录校验】开始校验所有图片目录...");
|
boolean allReady = fPictureBaseDir.exists() && fBackgroundSourceDir.exists()
|
||||||
boolean allReady = true;
|
&& fCropCacheDir.exists() && fBackgroundCompressDir.exists();
|
||||||
if (!fPictureBaseDir.exists() || !fPictureBaseDir.isDirectory()) {
|
|
||||||
LogUtils.e(TAG, "【图片目录校验】图片基础目录未就绪:" + fPictureBaseDir.getAbsolutePath());
|
|
||||||
allReady = false;
|
|
||||||
}
|
|
||||||
if (!fBackgroundSourceDir.exists() || !fBackgroundSourceDir.isDirectory()) {
|
|
||||||
LogUtils.e(TAG, "【图片目录校验】图片存储目录未就绪:" + fBackgroundSourceDir.getAbsolutePath());
|
|
||||||
allReady = false;
|
|
||||||
}
|
|
||||||
if (!fCropCacheDir.exists() || !fCropCacheDir.isDirectory()) {
|
|
||||||
LogUtils.e(TAG, "【图片目录校验】裁剪缓存目录未就绪:" + fCropCacheDir.getAbsolutePath());
|
|
||||||
allReady = false;
|
|
||||||
}
|
|
||||||
if (!fBackgroundCompressDir.exists() || !fBackgroundCompressDir.isDirectory()) {
|
|
||||||
LogUtils.e(TAG, "【图片目录校验】压缩图目录未就绪:" + fBackgroundCompressDir.getAbsolutePath());
|
|
||||||
allReady = false;
|
|
||||||
}
|
|
||||||
if (allReady) {
|
if (allReady) {
|
||||||
LogUtils.d(TAG, "【图片目录校验】所有图片目录均已就绪!");
|
LogUtils.d(TAG, "所有图片目录均已就绪");
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e(TAG, "【图片目录校验】部分目录未就绪,可能影响后续功能!");
|
LogUtils.e(TAG, "部分图片目录未就绪,可能影响后续功能");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化所有文件(裁剪文件→图片缓存目录,结果文件→图片存储目录)
|
* 初始化所有文件
|
||||||
*/
|
*/
|
||||||
private void initAllFiles() {
|
private void initAllFiles() {
|
||||||
// 1. 裁剪临时文件
|
|
||||||
//mCropSourceFile = new File(fCropCacheDir, CROP_TEMP_FILE_NAME);
|
|
||||||
// 2. 裁剪结果文件
|
|
||||||
//cropResultFile = new File(fCropCacheDir, CROP_RESULT_FILE_NAME);
|
|
||||||
|
|
||||||
// 新增:清理压缩图目录下的旧文件(避免残留)
|
|
||||||
clearCropTempFiles();
|
clearCropTempFiles();
|
||||||
LogUtils.d(TAG, "【文件初始化】完成。");
|
LogUtils.d(TAG, "文件初始化完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 【核心实现】定义 getFileProviderUri 方法:将 File 转为 ContentUri(适配 FileProvider)
|
/**
|
||||||
|
* 将File转为ContentUri
|
||||||
|
*/
|
||||||
public Uri getFileProviderUri(File file) {
|
public Uri getFileProviderUri(File file) {
|
||||||
Log.d("BackgroundSourceUtils", "getFileProviderUri: 生成FileProvider Uri,文件路径:" + file.getAbsolutePath());
|
LogUtils.d(TAG, "【getFileProviderUri调用】文件路径:" + file.getAbsolutePath());
|
||||||
Uri contentUri = null;
|
Uri contentUri = null;
|
||||||
try {
|
try {
|
||||||
// 适配 Android 7.0+:使用 FileProvider 生成 ContentUri(避免 FileUriExposedException)
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
contentUri = FileProvider.getUriForFile(
|
contentUri = FileProvider.getUriForFile(mContext, FILE_PROVIDER_AUTHORITY, file);
|
||||||
mContext,
|
LogUtils.d(TAG, "7.0+ 生成ContentUri:" + contentUri.toString());
|
||||||
FILE_PROVIDER_AUTHORITY, // 与清单文件中一致
|
|
||||||
file
|
|
||||||
);
|
|
||||||
Log.d("BackgroundSourceUtils", "getFileProviderUri: 7.0+ 生成ContentUri:" + contentUri.toString());
|
|
||||||
} else {
|
} else {
|
||||||
// 适配 Android 7.0 以下:直接使用 File.toURI()(兼容旧版本)
|
|
||||||
contentUri = Uri.fromFile(file);
|
contentUri = Uri.fromFile(file);
|
||||||
Log.d("BackgroundSourceUtils", "getFileProviderUri: 7.0以下 生成FileUri:" + contentUri.toString());
|
LogUtils.d(TAG, "7.0以下 生成FileUri:" + contentUri.toString());
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// 捕获异常(如文件路径无效、授权不匹配等)
|
LogUtils.e(TAG, "生成Uri失败:" + e.getMessage(), e);
|
||||||
Log.e("BackgroundSourceUtils", "getFileProviderUri: 生成Uri失败,异常:" + e.getMessage(), e);
|
|
||||||
contentUri = null;
|
contentUri = null;
|
||||||
}
|
}
|
||||||
return contentUri;
|
return contentUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查背景是否为空并创建空白背景Bean
|
||||||
|
*/
|
||||||
boolean checkEmptyBackgroundAndCreateBlankBackgroundBean(BackgroundBean checkBackgroundBean) {
|
boolean checkEmptyBackgroundAndCreateBlankBackgroundBean(BackgroundBean checkBackgroundBean) {
|
||||||
|
LogUtils.d(TAG, "【checkEmptyBackgroundAndCreateBlankBackgroundBean调用】开始检查背景Bean");
|
||||||
File fCheckBackgroundFile = new File(checkBackgroundBean.getBackgroundFilePath());
|
File fCheckBackgroundFile = new File(checkBackgroundBean.getBackgroundFilePath());
|
||||||
if (!fCheckBackgroundFile.exists()) {
|
if (!fCheckBackgroundFile.exists()) {
|
||||||
String newCropFileName = "blank10x10";
|
String newCropFileName = "blank10x10";
|
||||||
@@ -261,40 +197,36 @@ public class BackgroundSourceUtils {
|
|||||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载图片数据模型数据
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
// 修改预览数据模型
|
|
||||||
previewBackgroundBean.setIsUseBackgroundFile(true);
|
previewBackgroundBean.setIsUseBackgroundFile(true);
|
||||||
previewBackgroundBean.setIsUseBackgroundScaledCompressFile(false);
|
previewBackgroundBean.setIsUseBackgroundScaledCompressFile(false);
|
||||||
|
|
||||||
previewBackgroundBean.setBackgroundFileName(mCropSourceFile.getName());
|
previewBackgroundBean.setBackgroundFileName(mCropSourceFile.getName());
|
||||||
previewBackgroundBean.setBackgroundFilePath(mCropSourceFile.getAbsolutePath());
|
previewBackgroundBean.setBackgroundFilePath(mCropSourceFile.getAbsolutePath());
|
||||||
|
|
||||||
previewBackgroundBean.setBackgroundScaledCompressFileName(mCropResultFile.getName());
|
previewBackgroundBean.setBackgroundScaledCompressFileName(mCropResultFile.getName());
|
||||||
previewBackgroundBean.setBackgroundScaledCompressFilePath(mCropResultFile.getAbsolutePath());
|
previewBackgroundBean.setBackgroundScaledCompressFilePath(mCropResultFile.getAbsolutePath());
|
||||||
// 保存数据模型数据更改
|
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
LogUtils.d(TAG, "背景Bean为空,已创建空白背景并更新配置");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
LogUtils.d(TAG, "背景Bean文件存在,无需创建空白背景");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* 创建预览数据剪裁环境
|
* 创建并更新预览剪裁环境
|
||||||
*/
|
*/
|
||||||
public boolean createAndUpdatePreviewEnvironmentForCropping(BackgroundBean oldPreviewBackgroundBean) {
|
public boolean createAndUpdatePreviewEnvironmentForCropping(BackgroundBean oldPreviewBackgroundBean) {
|
||||||
|
LogUtils.d(TAG, "【createAndUpdatePreviewEnvironmentForCropping调用】开始初始化预览剪裁环境");
|
||||||
InputStream is = null;
|
InputStream is = null;
|
||||||
FileOutputStream fos = null;
|
FileOutputStream fos = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
clearCropTempFiles();
|
clearCropTempFiles();
|
||||||
if(checkEmptyBackgroundAndCreateBlankBackgroundBean(oldPreviewBackgroundBean)) {
|
if (checkEmptyBackgroundAndCreateBlankBackgroundBean(oldPreviewBackgroundBean)) {
|
||||||
|
LogUtils.d(TAG, "空白背景创建成功,直接返回");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uri uri = UriUtil.getUriForFile(mContext, oldPreviewBackgroundBean.getBackgroundFilePath());
|
Uri uri = UriUtil.getUriForFile(mContext, oldPreviewBackgroundBean.getBackgroundFilePath());
|
||||||
//String szType = mContext.getContentResolver().getType(uri);
|
|
||||||
// 2. 截取MIME类型后缀(如从image/jpeg中提取jpeg)【核心新增逻辑】
|
|
||||||
String fileSuffix = FileUtils.getFileSuffix(mContext, uri);
|
String fileSuffix = FileUtils.getFileSuffix(mContext, uri);
|
||||||
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);
|
||||||
@@ -310,103 +242,80 @@ public class BackgroundSourceUtils {
|
|||||||
FileUtils.copyFile(new File(oldPreviewBackgroundBean.getBackgroundFilePath()), mCropSourceFile);
|
FileUtils.copyFile(new File(oldPreviewBackgroundBean.getBackgroundFilePath()), mCropSourceFile);
|
||||||
} else {
|
} else {
|
||||||
mCropSourceFile.createNewFile();
|
mCropSourceFile.createNewFile();
|
||||||
// 1. 打开Uri输入流(兼容content:///file:// 等多种Uri格式)
|
|
||||||
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.toString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 初始化选图临时文件输出流(Java7 手动创建流,不依赖try-with-resources)
|
|
||||||
fos = new FileOutputStream(mCropSourceFile);
|
fos = new FileOutputStream(mCropSourceFile);
|
||||||
byte[] buffer = new byte[1024 * 8]; // 8KB缓冲区,平衡读写性能与内存占用
|
byte[] buffer = new byte[1024 * 8];
|
||||||
int readLen; // 每次读取的字节长度
|
int readLen;
|
||||||
|
|
||||||
// 3. 流复制(Java7 标准while循环,避免Java8+语法)
|
|
||||||
while ((readLen = is.read(buffer)) != -1) {
|
while ((readLen = is.read(buffer)) != -1) {
|
||||||
fos.write(buffer, 0, readLen); // 精准写入读取到的字节,避免空字节填充
|
fos.write(buffer, 0, readLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 强制同步写入磁盘(解决Android 10+ 异步写入导致的文件无效问题)
|
|
||||||
fos.flush();
|
fos.flush();
|
||||||
if (fos != null) {
|
|
||||||
try {
|
try {
|
||||||
fos.getFD().sync(); // 确保数据写入物理磁盘,而非缓存
|
fos.getFD().sync();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogUtils.w(TAG, "【选图解析】文件同步到磁盘失败,用flush()兜底:" + e.getMessage());
|
LogUtils.w(TAG, "文件同步到磁盘失败,flush兜底:" + e.getMessage());
|
||||||
fos.flush();
|
fos.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 加载图片数据模型数据
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
// 修改预览数据模型
|
|
||||||
previewBackgroundBean.setBackgroundFileName(mCropSourceFile.getName());
|
previewBackgroundBean.setBackgroundFileName(mCropSourceFile.getName());
|
||||||
previewBackgroundBean.setBackgroundFilePath(mCropSourceFile.getAbsolutePath());
|
previewBackgroundBean.setBackgroundFilePath(mCropSourceFile.getAbsolutePath());
|
||||||
|
|
||||||
previewBackgroundBean.setBackgroundScaledCompressFileName(mCropResultFile.getName());
|
previewBackgroundBean.setBackgroundScaledCompressFileName(mCropResultFile.getName());
|
||||||
previewBackgroundBean.setBackgroundScaledCompressFilePath(mCropResultFile.getAbsolutePath());
|
previewBackgroundBean.setBackgroundScaledCompressFilePath(mCropResultFile.getAbsolutePath());
|
||||||
// 保存数据模型数据更改
|
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|
||||||
// 6. 解析成功日志(打印文件信息,便于问题排查)
|
LogUtils.d(TAG, "预览剪裁环境初始化成功");
|
||||||
LogUtils.d(TAG, "【选图解析】Uri解析成功!");
|
|
||||||
LogUtils.d(TAG, "→ 原Uri:" + uri.toString());
|
LogUtils.d(TAG, "→ 原Uri:" + uri.toString());
|
||||||
LogUtils.d(TAG, "→ 图片剪裁数据源:" + mCropSourceFile.getAbsolutePath());
|
LogUtils.d(TAG, "→ 剪裁数据源:" + mCropSourceFile.getAbsolutePath());
|
||||||
LogUtils.d(TAG, "→ 图片剪裁数据源文件大小:" + mCropSourceFile.length() + " bytes");
|
LogUtils.d(TAG, "→ 剪裁结果文件:" + mCropResultFile.getAbsolutePath());
|
||||||
LogUtils.d(TAG, "→ 剪裁结果数据文件:" + mCropResultFile.getAbsolutePath());
|
|
||||||
LogUtils.d(TAG, "→ 剪裁结果数据文件大小:" + mCropResultFile.length() + " bytes");
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// 捕获所有异常(IO异常/空指针等),避免崩溃
|
LogUtils.e(TAG, "预览剪裁环境初始化异常:" + e.getMessage(), e);
|
||||||
LogUtils.e(TAG, "【选图解析】流复制异常:" + e.getMessage(), e);
|
|
||||||
// 异常时清理无效文件,防止残留
|
|
||||||
clearCropTempFiles();
|
clearCropTempFiles();
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
// 7. 手动关闭流资源(Java7 标准写法,避免内存泄漏)
|
|
||||||
if (is != null) {
|
if (is != null) {
|
||||||
try {
|
try {
|
||||||
is.close();
|
is.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogUtils.e(TAG, "【选图解析】输入流关闭失败:" + e.getMessage());
|
LogUtils.e(TAG, "输入流关闭失败:" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fos != null) {
|
if (fos != null) {
|
||||||
try {
|
try {
|
||||||
fos.close();
|
fos.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogUtils.e(TAG, "【选图解析】输出流关闭失败:" + e.getMessage());
|
LogUtils.e(TAG, "输出流关闭失败:" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载背景图片配置数据(核心:确保current/preview是两份独立的BackgroundBean实例)
|
* 加载背景配置
|
||||||
*/
|
*/
|
||||||
public void loadSettings() {
|
public void loadSettings() {
|
||||||
// 1. 加载正式Bean(独立实例:从currentBackgroundBean.json加载,不存在则新建)
|
|
||||||
currentBackgroundBean = BackgroundBean.loadBeanFromFile(currentBackgroundBeanFile.getAbsolutePath(), BackgroundBean.class);
|
currentBackgroundBean = BackgroundBean.loadBeanFromFile(currentBackgroundBeanFile.getAbsolutePath(), BackgroundBean.class);
|
||||||
if (currentBackgroundBean == null) {
|
if (currentBackgroundBean == null) {
|
||||||
currentBackgroundBean = new BackgroundBean(); // 正式Bean独立实例初始化
|
currentBackgroundBean = new BackgroundBean();
|
||||||
BackgroundBean.saveBeanToFile(currentBackgroundBeanFile.getAbsolutePath(), currentBackgroundBean);
|
BackgroundBean.saveBeanToFile(currentBackgroundBeanFile.getAbsolutePath(), currentBackgroundBean);
|
||||||
LogUtils.d(TAG, "【配置管理】正式背景Bean不存在,创建独立实例并保存到JSON");
|
LogUtils.d(TAG, "正式背景Bean不存在,已创建新实例");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 加载预览Bean(独立实例:从previewBackgroundBean.json加载,不存在则新建,与正式Bean完全分离)
|
|
||||||
previewBackgroundBean = BackgroundBean.loadBeanFromFile(previewBackgroundBeanFile.getAbsolutePath(), BackgroundBean.class);
|
previewBackgroundBean = BackgroundBean.loadBeanFromFile(previewBackgroundBeanFile.getAbsolutePath(), BackgroundBean.class);
|
||||||
if (previewBackgroundBean == null) {
|
if (previewBackgroundBean == null) {
|
||||||
previewBackgroundBean = new BackgroundBean(); // 预览Bean独立实例初始化
|
previewBackgroundBean = new BackgroundBean();
|
||||||
BackgroundBean.saveBeanToFile(previewBackgroundBeanFile.getAbsolutePath(), previewBackgroundBean);
|
BackgroundBean.saveBeanToFile(previewBackgroundBeanFile.getAbsolutePath(), previewBackgroundBean);
|
||||||
LogUtils.d(TAG, "【配置管理】预览背景Bean不存在,创建独立实例并保存到JSON");
|
LogUtils.d(TAG, "预览背景Bean不存在,已创建新实例");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------ 对外提供的核心方法(路径已适配新目录)------------------------------
|
// ------------------------------ 对外提供的核心方法 ------------------------------
|
||||||
public BackgroundBean getCurrentBackgroundBean() {
|
public BackgroundBean getCurrentBackgroundBean() {
|
||||||
return currentBackgroundBean;
|
return currentBackgroundBean;
|
||||||
}
|
}
|
||||||
@@ -415,87 +324,43 @@ public class BackgroundSourceUtils {
|
|||||||
return previewBackgroundBean;
|
return previewBackgroundBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取正式背景图片路径(修复:移除每次 loadSettings(),避免 Bean 被覆盖;强化非空校验)
|
|
||||||
*/
|
|
||||||
// public String getCurrentBackgroundFilePath() {
|
|
||||||
// String fileName = currentBackgroundBean.getBackgroundFileName();
|
|
||||||
// if (TextUtils.isEmpty(fileName)) {
|
|
||||||
// LogUtils.e(TAG, "【路径管理】正式背景文件名为空,返回空路径");
|
|
||||||
// return "";
|
|
||||||
// }
|
|
||||||
// File file = new File(fBackgroundSourceDir, fileName);
|
|
||||||
// LogUtils.d(TAG, "【路径管理】正式背景路径:" + file.getAbsolutePath());
|
|
||||||
// return file.getAbsolutePath();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取预览背景图片路径(修复:移除每次 loadSettings(),避免 Bean 被覆盖;强化非空校验)
|
|
||||||
// */
|
|
||||||
// public String getPreviewBackgroundFilePath() {
|
|
||||||
// String fileName = previewBackgroundBean.getBackgroundFileName();
|
|
||||||
// if (TextUtils.isEmpty(fileName)) {
|
|
||||||
// LogUtils.e(TAG, "【路径管理】预览背景文件名为空,返回空路径");
|
|
||||||
// return "";
|
|
||||||
// }
|
|
||||||
// File file = new File(fBackgroundSourceDir, fileName);
|
|
||||||
// LogUtils.d(TAG, "【路径管理】预览背景路径:" + file.getAbsolutePath());
|
|
||||||
// return file.getAbsolutePath();
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取预览背景压缩图片路径(同步修复:移除 loadSettings(),强化非空校验,统一指向BackgroundCrops目录)
|
|
||||||
*/
|
|
||||||
public String getPreviewBackgroundScaledCompressFilePath() {
|
public String getPreviewBackgroundScaledCompressFilePath() {
|
||||||
String compressFileName = previewBackgroundBean.getBackgroundScaledCompressFileName();
|
String compressFileName = previewBackgroundBean.getBackgroundScaledCompressFileName();
|
||||||
if (TextUtils.isEmpty(compressFileName)) {
|
if (TextUtils.isEmpty(compressFileName)) {
|
||||||
LogUtils.e(TAG, "【路径管理】预览压缩背景文件名为空,返回空路径");
|
LogUtils.e(TAG, "预览压缩背景文件名为空");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
// 关键:压缩图路径统一指向BackgroundCrops目录(不再用BackgroundSource)
|
|
||||||
File file = new File(fBackgroundCompressDir, compressFileName);
|
File file = new File(fBackgroundCompressDir, compressFileName);
|
||||||
LogUtils.d(TAG, "【路径管理】预览压缩背景路径(BackgroundCrops目录):" + file.getAbsolutePath());
|
|
||||||
return file.getAbsolutePath();
|
return file.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增:获取正式背景压缩图片路径(统一指向BackgroundCrops目录,对外提供调用)
|
|
||||||
*/
|
|
||||||
public String getCurrentBackgroundScaledCompressFilePath() {
|
public String getCurrentBackgroundScaledCompressFilePath() {
|
||||||
String compressFileName = currentBackgroundBean.getBackgroundScaledCompressFileName();
|
String compressFileName = currentBackgroundBean.getBackgroundScaledCompressFileName();
|
||||||
if (TextUtils.isEmpty(compressFileName)) {
|
if (TextUtils.isEmpty(compressFileName)) {
|
||||||
LogUtils.e(TAG, "【路径管理】正式压缩背景文件名为空,返回空路径");
|
LogUtils.e(TAG, "正式压缩背景文件名为空");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
// 关键:压缩图路径统一指向BackgroundCrops目录
|
|
||||||
File file = new File(fBackgroundCompressDir, compressFileName);
|
File file = new File(fBackgroundCompressDir, compressFileName);
|
||||||
LogUtils.d(TAG, "【路径管理】正式压缩背景路径(BackgroundCrops目录):" + file.getAbsolutePath());
|
|
||||||
return file.getAbsolutePath();
|
return file.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存配置(核心:将两份独立Bean实例,分别写入各自的JSON文件)
|
* 保存配置
|
||||||
*/
|
*/
|
||||||
public void saveSettings() {
|
public void saveSettings() {
|
||||||
if (currentBackgroundBean != null && previewBackgroundBean != null) {
|
if (currentBackgroundBean != null && previewBackgroundBean != null) {
|
||||||
BackgroundBean.saveBeanToFile(currentBackgroundBeanFile.getAbsolutePath(), currentBackgroundBean); // 正式Bean→正式JSON
|
BackgroundBean.saveBeanToFile(currentBackgroundBeanFile.getAbsolutePath(), currentBackgroundBean);
|
||||||
BackgroundBean.saveBeanToFile(previewBackgroundBeanFile.getAbsolutePath(), previewBackgroundBean); // 预览Bean→预览JSON
|
BackgroundBean.saveBeanToFile(previewBackgroundBeanFile.getAbsolutePath(), previewBackgroundBean);
|
||||||
LogUtils.d(TAG, "【配置管理】两份配置保存成功:正式JSON=" + currentBackgroundBeanFile.getAbsolutePath() + ",预览JSON=" + previewBackgroundBeanFile.getAbsolutePath());
|
LogUtils.d(TAG, "两份背景配置保存成功");
|
||||||
return;
|
} else {
|
||||||
|
LogUtils.e(TAG, "配置保存失败:current/preview Bean存在空值");
|
||||||
}
|
}
|
||||||
LogUtils.d(TAG, "【配置管理】两份配置保存失败。currentBackgroundBean 与 previewBackgroundBean 有空值。");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取图片基础目录路径(对外提供:/Pictures/PowerBell/)
|
|
||||||
*/
|
|
||||||
public String getBackgroundSourceDirPath() {
|
public String getBackgroundSourceDirPath() {
|
||||||
return fBackgroundSourceDir.getAbsolutePath();
|
return fBackgroundSourceDir.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增:获取压缩图统一存储目录路径(对外提供:/Pictures/PowerBell/BackgroundCrops/)
|
|
||||||
*/
|
|
||||||
public String getBackgroundCompressDirPath() {
|
public String getBackgroundCompressDirPath() {
|
||||||
return fBackgroundCompressDir.getAbsolutePath();
|
return fBackgroundCompressDir.getAbsolutePath();
|
||||||
}
|
}
|
||||||
@@ -508,75 +373,60 @@ public class BackgroundSourceUtils {
|
|||||||
return FILE_PROVIDER_AUTHORITY;
|
return FILE_PROVIDER_AUTHORITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------ 核心业务方法(复用FileUtils简化文件操作)------------------------------
|
// ------------------------------ 核心业务方法 ------------------------------
|
||||||
/**
|
/**
|
||||||
* 优化函数:仅裁剪结果图可保存到BackgroundSource(避免启动裁剪时误复制原图)
|
* 保存裁剪结果图到预览Bean
|
||||||
* 说明:启动裁剪时不调用此方法,仅在裁剪完成后保存结果图时调用
|
|
||||||
*/
|
*/
|
||||||
public BackgroundBean saveFileToPreviewBean(File sourceFile, String fileInfo) {
|
public BackgroundBean saveFileToPreviewBean(File sourceFile, String fileInfo) {
|
||||||
final String TAG = "BackgroundSourceUtils";
|
LogUtils.d(TAG, "【saveFileToPreviewBean调用】源文件路径:" + (sourceFile != null ? sourceFile.getAbsolutePath() : "null"));
|
||||||
// 强化校验1:仅允许裁剪结果图传入(通过文件路径判断,避免原图误传入)
|
|
||||||
if (sourceFile == null || !sourceFile.exists() || sourceFile.length() <= 0) {
|
if (sourceFile == null || !sourceFile.exists() || sourceFile.length() <= 0) {
|
||||||
Log.e(TAG, "【保存优化】源文件无效,拒绝保存:" + (sourceFile != null ? sourceFile.getAbsolutePath() : "null"));
|
LogUtils.e(TAG, "源文件无效,拒绝保存");
|
||||||
return previewBackgroundBean;
|
return previewBackgroundBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 强化校验2:排除原图路径(避免启动裁剪时传入原图复制)
|
String originalImageDir = mContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath();
|
||||||
String originalImageDir = mContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath(); // 原图存储目录(如相册/拍照目录)
|
|
||||||
if (sourceFile.getAbsolutePath().contains(originalImageDir)) {
|
if (sourceFile.getAbsolutePath().contains(originalImageDir)) {
|
||||||
Log.w(TAG, "【保存优化】禁止复制原图到BackgroundSource,跳过保存");
|
LogUtils.w(TAG, "禁止复制原图,跳过保存");
|
||||||
return previewBackgroundBean;
|
return previewBackgroundBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保BackgroundSource目录存在(实例化时已创建,此处二次确认)
|
if (!fBackgroundSourceDir.exists() && !fBackgroundSourceDir.mkdirs()) {
|
||||||
if (!fBackgroundSourceDir.exists()) {
|
LogUtils.e(TAG, "BackgroundSource目录创建失败");
|
||||||
if (!fBackgroundSourceDir.mkdirs()) {
|
|
||||||
Log.e(TAG, "【保存优化】BackgroundSource目录创建失败");
|
|
||||||
return previewBackgroundBean;
|
return previewBackgroundBean;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 生成唯一文件名(避免覆盖)
|
|
||||||
String uniqueFileName = "bg_" + System.currentTimeMillis() + "_" + sourceFile.getName();
|
String uniqueFileName = "bg_" + System.currentTimeMillis() + "_" + sourceFile.getName();
|
||||||
File targetFile = new File(fBackgroundSourceDir, uniqueFileName);
|
File targetFile = new File(fBackgroundSourceDir, uniqueFileName);
|
||||||
|
|
||||||
// 执行复制(仅裁剪结果图会走到这一步)
|
|
||||||
if (FileUtils.copyFile(sourceFile, targetFile)) {
|
if (FileUtils.copyFile(sourceFile, targetFile)) {
|
||||||
Log.d(TAG, "【保存优化】裁剪结果图保存成功:" + targetFile.getAbsolutePath());
|
LogUtils.d(TAG, "裁剪结果图保存成功:" + targetFile.getAbsolutePath());
|
||||||
// 更新预览Bean(原有逻辑保留)
|
|
||||||
previewBackgroundBean.setBackgroundFileName(uniqueFileName);
|
previewBackgroundBean.setBackgroundFileName(uniqueFileName);
|
||||||
previewBackgroundBean.setBackgroundFilePath(targetFile.getAbsolutePath());
|
previewBackgroundBean.setBackgroundFilePath(targetFile.getAbsolutePath());
|
||||||
previewBackgroundBean.setBackgroundFileInfo(fileInfo);
|
previewBackgroundBean.setBackgroundFileInfo(fileInfo);
|
||||||
previewBackgroundBean.setIsUseBackgroundFile(true);
|
previewBackgroundBean.setIsUseBackgroundFile(true);
|
||||||
// 保存Bean到本地(原有逻辑保留)
|
|
||||||
saveSettings();
|
saveSettings();
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "【保存优化】裁剪结果图复制失败:" + sourceFile.getAbsolutePath() + " → " + targetFile.getAbsolutePath());
|
LogUtils.e(TAG, "裁剪结果图复制失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
return previewBackgroundBean;
|
return previewBackgroundBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交预览背景到正式背景(预览Bean → 正式Bean:深拷贝,新建正式Bean实例+逐字段拷贝)
|
* 提交预览背景到正式背景
|
||||||
* 核心:深拷贝后,修改正式Bean不会影响预览Bean,两份实例完全独立,压缩图路径统一指向BackgroundCrops
|
|
||||||
*/
|
*/
|
||||||
public void commitPreviewSourceToCurrent() {
|
public void commitPreviewSourceToCurrent() {
|
||||||
// 深拷贝第一步:新建正式Bean独立实例(彻底脱离预览Bean的引用)
|
LogUtils.d(TAG, "【commitPreviewSourceToCurrent调用】开始深拷贝预览Bean到正式Bean");
|
||||||
currentBackgroundBean = new BackgroundBean();
|
currentBackgroundBean = new BackgroundBean();
|
||||||
// 深拷贝第二步:逐字段拷贝预览Bean的所有值(压缩图路径同步指向BackgroundCrops)
|
|
||||||
currentBackgroundBean.setBackgroundFileName(previewBackgroundBean.getBackgroundFileName());
|
currentBackgroundBean.setBackgroundFileName(previewBackgroundBean.getBackgroundFileName());
|
||||||
currentBackgroundBean.setBackgroundFilePath(previewBackgroundBean.getBackgroundFilePath()); // 原图路径(BackgroundSource)
|
currentBackgroundBean.setBackgroundFilePath(previewBackgroundBean.getBackgroundFilePath());
|
||||||
currentBackgroundBean.setBackgroundFileInfo(previewBackgroundBean.getBackgroundFileInfo());
|
currentBackgroundBean.setBackgroundFileInfo(previewBackgroundBean.getBackgroundFileInfo());
|
||||||
currentBackgroundBean.setIsUseBackgroundFile(previewBackgroundBean.isUseBackgroundFile());
|
currentBackgroundBean.setIsUseBackgroundFile(previewBackgroundBean.isUseBackgroundFile());
|
||||||
currentBackgroundBean.setBackgroundScaledCompressFileName(previewBackgroundBean.getBackgroundScaledCompressFileName());
|
currentBackgroundBean.setBackgroundScaledCompressFileName(previewBackgroundBean.getBackgroundScaledCompressFileName());
|
||||||
currentBackgroundBean.setBackgroundScaledCompressFilePath(previewBackgroundBean.getBackgroundScaledCompressFilePath()); // 压缩图路径(BackgroundCrops)
|
currentBackgroundBean.setBackgroundScaledCompressFilePath(previewBackgroundBean.getBackgroundScaledCompressFilePath());
|
||||||
currentBackgroundBean.setIsUseBackgroundScaledCompressFile(previewBackgroundBean.isUseBackgroundScaledCompressFile()); // 重命名字段:拷贝压缩图启用状态
|
currentBackgroundBean.setIsUseBackgroundScaledCompressFile(previewBackgroundBean.isUseBackgroundScaledCompressFile());
|
||||||
currentBackgroundBean.setBackgroundWidth(previewBackgroundBean.getBackgroundWidth());
|
currentBackgroundBean.setBackgroundWidth(previewBackgroundBean.getBackgroundWidth());
|
||||||
currentBackgroundBean.setBackgroundHeight(previewBackgroundBean.getBackgroundHeight());
|
currentBackgroundBean.setBackgroundHeight(previewBackgroundBean.getBackgroundHeight());
|
||||||
currentBackgroundBean.setPixelColor(previewBackgroundBean.getPixelColor());
|
currentBackgroundBean.setPixelColor(previewBackgroundBean.getPixelColor());
|
||||||
|
|
||||||
// 拷贝一份缓存图片文件到正式背景文件夹
|
|
||||||
String previewFileName = previewBackgroundBean.getBackgroundFileName();
|
String previewFileName = previewBackgroundBean.getBackgroundFileName();
|
||||||
String previewCropFileName = previewBackgroundBean.getBackgroundScaledCompressFileName();
|
String previewCropFileName = previewBackgroundBean.getBackgroundScaledCompressFileName();
|
||||||
File previewFile = new File(previewBackgroundBean.getBackgroundFilePath());
|
File previewFile = new File(previewBackgroundBean.getBackgroundFilePath());
|
||||||
@@ -585,42 +435,37 @@ public class BackgroundSourceUtils {
|
|||||||
File currentCropFile = new File(fBackgroundCompressDir, previewCropFileName);
|
File currentCropFile = new File(fBackgroundCompressDir, previewCropFileName);
|
||||||
FileUtils.copyFile(previewFile, currentFile);
|
FileUtils.copyFile(previewFile, currentFile);
|
||||||
FileUtils.copyFile(previewCropFile, currentCropFile);
|
FileUtils.copyFile(previewCropFile, currentCropFile);
|
||||||
// 更新当前背景文件路径
|
currentBackgroundBean.setBackgroundFilePath(currentFile.getAbsolutePath());
|
||||||
currentBackgroundBean.setBackgroundFilePath(currentFile.getAbsolutePath()); // 原图路径(BackgroundSource)
|
currentBackgroundBean.setBackgroundScaledCompressFilePath(currentCropFile.getAbsolutePath());
|
||||||
currentBackgroundBean.setBackgroundScaledCompressFilePath(currentCropFile.getAbsolutePath()); // 压缩图路径(BackgroundCrops)
|
|
||||||
|
|
||||||
saveSettings(); // 分别保存:正式Bean→currentJSON,预览Bean→previewJSON(两份独立)
|
saveSettings();
|
||||||
LogUtils.d(TAG, "【配置管理】预览背景深拷贝到正式Bean:两份实例独立,压缩图统一存储到BackgroundCrops");
|
LogUtils.d(TAG, "预览背景提交到正式背景成功,两份实例完全独立");
|
||||||
ToastUtils.show("背景图片应用成功");
|
ToastUtils.show("背景图片应用成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将正式背景同步到预览背景(正式Bean → 预览Bean:深拷贝,新建预览Bean实例+逐字段拷贝)
|
* 将正式背景同步到预览背景
|
||||||
* 核心:深拷贝后,修改预览Bean不会影响正式Bean,两份实例完全独立,压缩图路径统一指向BackgroundCrops
|
|
||||||
*/
|
*/
|
||||||
public void setCurrentSourceToPreview() {
|
public void setCurrentSourceToPreview() {
|
||||||
LogUtils.d(TAG, "正在初始化预览数据,setCurrentSourceToPreview()");
|
LogUtils.d(TAG, "【setCurrentSourceToPreview调用】开始深拷贝正式Bean到预览Bean");
|
||||||
// 深拷贝第一步:新建预览Bean独立实例(彻底脱离正式Bean的引用)
|
|
||||||
previewBackgroundBean = new BackgroundBean();
|
previewBackgroundBean = new BackgroundBean();
|
||||||
// 深拷贝第二步:逐字段拷贝正式Bean的所有值(压缩图路径同步指向BackgroundCrops)
|
|
||||||
previewBackgroundBean.setBackgroundFileName(currentBackgroundBean.getBackgroundFileName());
|
previewBackgroundBean.setBackgroundFileName(currentBackgroundBean.getBackgroundFileName());
|
||||||
previewBackgroundBean.setBackgroundFilePath(currentBackgroundBean.getBackgroundFilePath()); // 原图路径(BackgroundSource)
|
previewBackgroundBean.setBackgroundFilePath(currentBackgroundBean.getBackgroundFilePath());
|
||||||
previewBackgroundBean.setBackgroundFileInfo(currentBackgroundBean.getBackgroundFileInfo());
|
previewBackgroundBean.setBackgroundFileInfo(currentBackgroundBean.getBackgroundFileInfo());
|
||||||
previewBackgroundBean.setIsUseBackgroundFile(currentBackgroundBean.isUseBackgroundFile());
|
previewBackgroundBean.setIsUseBackgroundFile(currentBackgroundBean.isUseBackgroundFile());
|
||||||
previewBackgroundBean.setBackgroundScaledCompressFileName(currentBackgroundBean.getBackgroundScaledCompressFileName());
|
previewBackgroundBean.setBackgroundScaledCompressFileName(currentBackgroundBean.getBackgroundScaledCompressFileName());
|
||||||
previewBackgroundBean.setBackgroundScaledCompressFilePath(currentBackgroundBean.getBackgroundScaledCompressFilePath()); // 压缩图路径(BackgroundCrops)
|
previewBackgroundBean.setBackgroundScaledCompressFilePath(currentBackgroundBean.getBackgroundScaledCompressFilePath());
|
||||||
previewBackgroundBean.setIsUseBackgroundScaledCompressFile(currentBackgroundBean.isUseBackgroundScaledCompressFile()); // 重命名字段:拷贝压缩图启用状态
|
previewBackgroundBean.setIsUseBackgroundScaledCompressFile(currentBackgroundBean.isUseBackgroundScaledCompressFile());
|
||||||
previewBackgroundBean.setBackgroundWidth(currentBackgroundBean.getBackgroundWidth());
|
previewBackgroundBean.setBackgroundWidth(currentBackgroundBean.getBackgroundWidth());
|
||||||
previewBackgroundBean.setBackgroundHeight(currentBackgroundBean.getBackgroundHeight());
|
previewBackgroundBean.setBackgroundHeight(currentBackgroundBean.getBackgroundHeight());
|
||||||
previewBackgroundBean.setPixelColor(currentBackgroundBean.getPixelColor());
|
previewBackgroundBean.setPixelColor(currentBackgroundBean.getPixelColor());
|
||||||
|
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
LogUtils.d(TAG, "正式背景同步到预览背景成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工具方法:清理旧文件(避免文件锁定/残留,适配系统公共目录)【内部私有,不对外暴露】
|
* 清理单个旧文件
|
||||||
* @param file 要清理的文件
|
|
||||||
* @param fileDesc 文件描述(用于日志打印)
|
|
||||||
*/
|
*/
|
||||||
private void clearOldFile(File file, String fileDesc) {
|
private void clearOldFile(File file, String fileDesc) {
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
@@ -628,50 +473,45 @@ public class BackgroundSourceUtils {
|
|||||||
}
|
}
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
file.delete();
|
file.delete();
|
||||||
LogUtils.w(TAG, "【文件管理】" + fileDesc + "已删除");
|
LogUtils.d(TAG, fileDesc + "已删除");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增:清理压缩图目录下的旧文件(避免残留,初始化时调用)
|
* 清理裁剪临时文件
|
||||||
*/
|
*/
|
||||||
void clearCropTempFiles() {
|
void clearCropTempFiles() {
|
||||||
for (File file : fCropCacheDir.listFiles()) {
|
File[] files = fCropCacheDir.listFiles();
|
||||||
clearOldFile(file, "旧裁剪缓存文件(" + file.getAbsolutePath() + ")");
|
if (files == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (File file : files) {
|
||||||
|
clearOldFile(file, "旧裁剪缓存文件:" + file.getAbsolutePath());
|
||||||
}
|
}
|
||||||
mCropSourceFile = null;
|
mCropSourceFile = null;
|
||||||
mCropResultFile = null;
|
mCropResultFile = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 适配原调用:mBgSourceUtils.copyFile(new File(""), parentDir)
|
* 复制文件
|
||||||
* 核心:复用FileUtils,支持「空源文件→仅创建目标目录」和「正常文件复制」两种场景
|
|
||||||
* @param source 源文件(可为空/空文件,为空时仅创建目标目录)
|
|
||||||
* @param target 目标文件/目录(若源文件为空,target视为目录并创建)
|
|
||||||
* @return true=复制/创建成功,false=失败
|
|
||||||
*/
|
*/
|
||||||
public boolean copyFile(File source, File target) {
|
public boolean copyFile(File source, File target) {
|
||||||
// 场景1:源文件为空(适配 new File("") 调用)→ 仅创建目标目录
|
LogUtils.d(TAG, "【copyFile调用】源文件:" + (source != null ? source.getAbsolutePath() : "null") + " 目标:" + (target != null ? target.getAbsolutePath() : "null"));
|
||||||
if (source == null || (source.exists() && source.length() <= 0) || TextUtils.isEmpty(source.getPath())) {
|
if (source == null || TextUtils.isEmpty(source.getPath()) || (source.exists() && source.length() <= 0)) {
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
LogUtils.e(TAG, "【文件管理】目录创建失败:目标目录对象为null");
|
LogUtils.e(TAG, "目录创建失败:目标对象为null");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 若target是文件,取其父目录;若本身是目录,直接创建(实例化时已创建,此处二次确认)
|
|
||||||
File targetDir = target.isFile() ? target.getParentFile() : target;
|
File targetDir = target.isFile() ? target.getParentFile() : target;
|
||||||
createDirWithPermission(targetDir, "空源文件场景-目录创建(/Pictures/PowerBell下)");
|
createDirWithPermission(targetDir, "空源文件场景-目录创建");
|
||||||
LogUtils.d(TAG, "【文件管理】空源文件场景:目录创建完成,路径=" + targetDir.getAbsolutePath());
|
LogUtils.d(TAG, "空源文件场景,目录创建完成");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 场景2:正常文件复制(源文件非空且存在)→ 复用FileUtils.copyFile,确保高效兼容
|
|
||||||
return FileUtils.copyFile(source, target);
|
return FileUtils.copyFile(source, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工具方法:获取目录类型描述(用于日志调试,明确目录类型,适配新目录结构)
|
* 获取目录类型描述
|
||||||
* @param dir 目标目录
|
|
||||||
* @return 目录类型描述
|
|
||||||
*/
|
*/
|
||||||
public String getDirTypeDesc(File dir) {
|
public String getDirTypeDesc(File dir) {
|
||||||
if (dir == null) {
|
if (dir == null) {
|
||||||
@@ -684,7 +524,7 @@ public class BackgroundSourceUtils {
|
|||||||
|
|
||||||
if (!TextUtils.isEmpty(publicPicturePath)) {
|
if (!TextUtils.isEmpty(publicPicturePath)) {
|
||||||
if (dirPath.contains(publicPicturePath + File.separator + "PowerBell" + File.separator + COMPRESS_DIR_NAME)) {
|
if (dirPath.contains(publicPicturePath + File.separator + "PowerBell" + File.separator + COMPRESS_DIR_NAME)) {
|
||||||
return "系统公共图片目录(/Pictures/PowerBell/BackgroundCrops,压缩图统一存储目录)"; // 新增压缩图目录描述
|
return "系统公共图片目录(/Pictures/PowerBell/BackgroundCompress,压缩图统一存储目录)";
|
||||||
} else if (dirPath.contains(publicPicturePath + File.separator + "PowerBell")) {
|
} else if (dirPath.contains(publicPicturePath + File.separator + "PowerBell")) {
|
||||||
return "系统公共图片目录(/Pictures/PowerBell,图片存储/裁剪目录)";
|
return "系统公共图片目录(/Pictures/PowerBell,图片存储/裁剪目录)";
|
||||||
}
|
}
|
||||||
@@ -699,86 +539,68 @@ public class BackgroundSourceUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增:迁移旧压缩图路径到新目录(BackgroundCrops),兼容历史数据
|
* 迁移旧压缩图路径到新目录
|
||||||
* @param bean 要迁移的BackgroundBean(正式/预览)
|
|
||||||
* @param isCurrentBean 是否是正式Bean(用于日志区分)
|
|
||||||
*/
|
*/
|
||||||
private void migrateCompressPathToNewDir(BackgroundBean bean, boolean isCurrentBean) {
|
private void migrateCompressPathToNewDir(BackgroundBean bean, boolean isCurrentBean) {
|
||||||
|
LogUtils.d(TAG, "【migrateCompressPathToNewDir调用】开始迁移" + (isCurrentBean ? "正式" : "预览") + "Bean压缩路径");
|
||||||
String oldCompressPath = bean.getBackgroundScaledCompressFilePath();
|
String oldCompressPath = bean.getBackgroundScaledCompressFilePath();
|
||||||
String beanType = isCurrentBean ? "正式Bean" : "预览Bean";
|
String beanType = isCurrentBean ? "正式Bean" : "预览Bean";
|
||||||
|
|
||||||
// 校验:旧路径非空,且不在BackgroundCrops目录下,才需要迁移
|
|
||||||
if (TextUtils.isEmpty(oldCompressPath) || oldCompressPath.contains(fBackgroundCompressDir.getAbsolutePath())) {
|
if (TextUtils.isEmpty(oldCompressPath) || oldCompressPath.contains(fBackgroundCompressDir.getAbsolutePath())) {
|
||||||
LogUtils.d(TAG, "【路径迁移】" + beanType + "无需迁移:旧路径为空或已在BackgroundCrops目录");
|
LogUtils.d(TAG, beanType + "无需迁移:旧路径为空或已在目标目录");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File oldCompressFile = new File(oldCompressPath);
|
File oldCompressFile = new File(oldCompressPath);
|
||||||
if (!oldCompressFile.exists() || !oldCompressFile.isFile() || oldCompressFile.length() <= 0) {
|
if (!oldCompressFile.exists() || !oldCompressFile.isFile() || oldCompressFile.length() <= 0) {
|
||||||
LogUtils.w(TAG, "【路径迁移】" + beanType + "旧压缩文件无效,无需迁移:" + oldCompressPath);
|
LogUtils.w(TAG, beanType + "旧压缩文件无效,无需迁移:" + oldCompressPath);
|
||||||
// 重置路径为新目录下的空文件(避免无效路径)
|
|
||||||
String compressFileName = bean.getBackgroundScaledCompressFileName();
|
String compressFileName = bean.getBackgroundScaledCompressFileName();
|
||||||
if (!TextUtils.isEmpty(compressFileName)) {
|
if (!TextUtils.isEmpty(compressFileName)) {
|
||||||
File newCompressFile = new File(fBackgroundCompressDir, compressFileName);
|
File newCompressFile = new File(fBackgroundCompressDir, compressFileName);
|
||||||
bean.setBackgroundScaledCompressFilePath(newCompressFile.getAbsolutePath());
|
bean.setBackgroundScaledCompressFilePath(newCompressFile.getAbsolutePath());
|
||||||
saveSettings();
|
saveSettings();
|
||||||
LogUtils.d(TAG, "【路径迁移】" + beanType + "重置压缩路径到BackgroundCrops:" + newCompressFile.getAbsolutePath());
|
LogUtils.d(TAG, beanType + "压缩路径已重置到目标目录");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 迁移逻辑:复制旧文件到新目录,更新Bean路径,删除旧文件
|
|
||||||
String compressFileName = bean.getBackgroundScaledCompressFileName();
|
String compressFileName = bean.getBackgroundScaledCompressFileName();
|
||||||
if (TextUtils.isEmpty(compressFileName)) {
|
if (TextUtils.isEmpty(compressFileName)) {
|
||||||
compressFileName = "ScaledCompress_" + System.currentTimeMillis() + ".jpg"; // 兜底生成文件名
|
compressFileName = "ScaledCompress_" + System.currentTimeMillis() + ".jpg";
|
||||||
}
|
}
|
||||||
File newCompressFile = new File(fBackgroundCompressDir, compressFileName);
|
File newCompressFile = new File(fBackgroundCompressDir, compressFileName);
|
||||||
|
|
||||||
// 复制旧文件到新目录
|
|
||||||
boolean copySuccess = FileUtils.copyFile(oldCompressFile, newCompressFile);
|
boolean copySuccess = FileUtils.copyFile(oldCompressFile, newCompressFile);
|
||||||
if (copySuccess) {
|
if (copySuccess) {
|
||||||
// 更新Bean路径为新目录路径
|
|
||||||
bean.setBackgroundScaledCompressFilePath(newCompressFile.getAbsolutePath());
|
bean.setBackgroundScaledCompressFilePath(newCompressFile.getAbsolutePath());
|
||||||
saveSettings();
|
saveSettings();
|
||||||
// 删除旧文件(清理残留)
|
|
||||||
clearOldFile(oldCompressFile, beanType + "旧压缩文件(迁移后清理)");
|
clearOldFile(oldCompressFile, beanType + "旧压缩文件(迁移后清理)");
|
||||||
LogUtils.d(TAG, "【路径迁移】" + beanType + "压缩路径迁移成功:" + oldCompressPath + " → " + newCompressFile.getAbsolutePath());
|
LogUtils.d(TAG, beanType + "压缩路径迁移成功:" + oldCompressPath + " → " + newCompressFile.getAbsolutePath());
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e(TAG, "【路径迁移】" + beanType + "压缩文件复制失败,迁移终止:" + oldCompressPath);
|
LogUtils.e(TAG, beanType + "压缩文件复制失败,迁移终止");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================================== 核心实现:获取图片旋转角度 ========================================
|
|
||||||
/**
|
/**
|
||||||
* 读取图片EXIF信息,获取旋转角度(适配JPEG/PNG等主流格式)
|
* 获取图片旋转角度
|
||||||
* @param imagePath 图片绝对路径(支持本地文件路径,兼容多包名临时目录)
|
|
||||||
* @return 旋转角度(0/90/180/270,无旋转返回0)
|
|
||||||
*/
|
*/
|
||||||
public int getImageRotateAngle(String imagePath) {
|
public int getImageRotateAngle(String imagePath) {
|
||||||
// 1. 入参校验(避免空指针/无效路径)
|
LogUtils.d(TAG, "【getImageRotateAngle调用】图片路径:" + imagePath);
|
||||||
if (TextUtils.isEmpty(imagePath)) {
|
if (TextUtils.isEmpty(imagePath)) {
|
||||||
Log.e(TAG, "getImageRotateAngle: 图片路径为空");
|
LogUtils.e(TAG, "图片路径为空");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
File imageFile = new File(imagePath);
|
File imageFile = new File(imagePath);
|
||||||
if (!imageFile.exists() || !imageFile.isFile() || imageFile.length() <= 0) {
|
if (!imageFile.exists() || !imageFile.isFile() || imageFile.length() <= 0) {
|
||||||
Log.e(TAG, "getImageRotateAngle: 图片文件无效,路径:" + imagePath);
|
LogUtils.e(TAG, "图片文件无效:" + imagePath);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream inputStream = null;
|
InputStream inputStream = null;
|
||||||
try {
|
try {
|
||||||
// 2. 读取图片EXIF信息(优先用流读取,避免文件占用)
|
|
||||||
inputStream = new FileInputStream(imageFile);
|
inputStream = new FileInputStream(imageFile);
|
||||||
ExifInterface exifInterface = new ExifInterface(inputStream);
|
ExifInterface exifInterface = new ExifInterface(inputStream);
|
||||||
|
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
|
||||||
// 3. 获取旋转角度标签(兼容不同设备的EXIF字段)
|
|
||||||
int orientation = exifInterface.getAttributeInt(
|
|
||||||
ExifInterface.TAG_ORIENTATION,
|
|
||||||
ExifInterface.ORIENTATION_NORMAL
|
|
||||||
);
|
|
||||||
|
|
||||||
// 4. 解析旋转角度(标准EXIF角度映射)
|
|
||||||
switch (orientation) {
|
switch (orientation) {
|
||||||
case ExifInterface.ORIENTATION_ROTATE_90:
|
case ExifInterface.ORIENTATION_ROTATE_90:
|
||||||
return 90;
|
return 90;
|
||||||
@@ -786,103 +608,83 @@ public class BackgroundSourceUtils {
|
|||||||
return 180;
|
return 180;
|
||||||
case ExifInterface.ORIENTATION_ROTATE_270:
|
case ExifInterface.ORIENTATION_ROTATE_270:
|
||||||
return 270;
|
return 270;
|
||||||
default: // 正常/翻转等其他情况,均视为0度
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// 兼容异常场景:如图片无EXIF信息、格式不支持(如WebP)
|
LogUtils.w(TAG, "读取EXIF异常:" + e.getMessage());
|
||||||
Log.w(TAG, "getImageRotateAngle: 读取EXIF异常,路径:" + imagePath + ",错误:" + e.getMessage());
|
|
||||||
return 0;
|
return 0;
|
||||||
} finally {
|
} finally {
|
||||||
// 5. 关闭流资源(避免内存泄漏/文件占用)
|
|
||||||
if (inputStream != null) {
|
if (inputStream != null) {
|
||||||
try {
|
try {
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, "getImageRotateAngle: 流关闭失败,错误:" + e.getMessage());
|
LogUtils.e(TAG, "流关闭失败:" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ======================================== 图片处理核心方法(压缩/裁剪/保存) ========================================
|
|
||||||
/**
|
/**
|
||||||
* 压缩图片并保存(核心修复:路径非空校验+兜底路径,统一存储到BackgroundCrops目录)
|
* 压缩图片并保存(默认路径)
|
||||||
*/
|
*/
|
||||||
public void compressQualityToRecivedPicture(Bitmap bitmap) {
|
public void compressQualityToRecivedPicture(Bitmap bitmap) {
|
||||||
// 兼容裁剪等旧调用:从工具类获取默认压缩路径(统一指向BackgroundCrops),转发至重载函数
|
LogUtils.d(TAG, "【compressQualityToRecivedPicture调用】使用默认路径压缩图片");
|
||||||
String defaultCompressPath = getPreviewBackgroundScaledCompressFilePath();
|
String defaultCompressPath = getPreviewBackgroundScaledCompressFilePath();
|
||||||
compressQualityToRecivedPicture(bitmap, defaultCompressPath);
|
compressQualityToRecivedPicture(bitmap, defaultCompressPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重载方法:指定路径压缩图片并保存(修复:压缩后同步路径到预览Bean,统一存储到BackgroundCrops)
|
* 压缩图片并保存(指定路径)
|
||||||
* 适配场景:裁剪后生成压缩图,强制绑定路径到预览Bean,避免路径错位
|
|
||||||
* @param bitmap 待压缩的Bitmap(裁剪后的缩放图)
|
|
||||||
* @param targetCompressPath 强制指定的压缩目标路径(从预览Bean获取/生成,默认指向BackgroundCrops)
|
|
||||||
*/
|
*/
|
||||||
public void compressQualityToRecivedPicture(Bitmap bitmap, String targetCompressPath) {
|
public void compressQualityToRecivedPicture(Bitmap bitmap, String targetCompressPath) {
|
||||||
LogUtils.d(TAG, "【压缩启动】开始压缩图片(指定路径),Bitmap状态:" + (bitmap != null && !bitmap.isRecycled()));
|
LogUtils.d(TAG, "【compressQualityToRecivedPicture调用】指定路径压缩图片,目标路径:" + targetCompressPath);
|
||||||
|
|
||||||
if (bitmap == null || bitmap.isRecycled()) {
|
if (bitmap == null || bitmap.isRecycled()) {
|
||||||
ToastUtils.show("压缩失败:图片为空");
|
ToastUtils.show("压缩失败:图片为空");
|
||||||
LogUtils.e(TAG, "【压缩失败】Bitmap为空或已回收");
|
LogUtils.e(TAG, "Bitmap为空或已回收");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputStream outStream = null;
|
OutputStream outStream = null;
|
||||||
FileOutputStream fos = null;
|
FileOutputStream fos = null;
|
||||||
try {
|
try {
|
||||||
LogUtils.d(TAG, "【压缩配置】目标路径(BackgroundCrops):" + targetCompressPath + ",Bitmap原始大小:" + bitmap.getByteCount() / 1024 + "KB");
|
LogUtils.d(TAG, "Bitmap原始大小:" + bitmap.getByteCount() / 1024 + "KB");
|
||||||
|
|
||||||
File targetCompressFile = new File(targetCompressPath);
|
File targetCompressFile = new File(targetCompressPath);
|
||||||
if (targetCompressFile.exists()) {
|
if (targetCompressFile.exists()) {
|
||||||
targetCompressFile.delete();
|
targetCompressFile.delete();
|
||||||
}
|
}
|
||||||
targetCompressFile.createNewFile();
|
targetCompressFile.createNewFile();
|
||||||
|
|
||||||
// 写入压缩图(质量80,平衡清晰度和内存)
|
|
||||||
fos = new FileOutputStream(targetCompressFile);
|
fos = new FileOutputStream(targetCompressFile);
|
||||||
outStream = new BufferedOutputStream(fos);
|
outStream = new BufferedOutputStream(fos);
|
||||||
boolean compressSuccess = bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outStream);
|
boolean compressSuccess = bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outStream);
|
||||||
outStream.flush();
|
outStream.flush();
|
||||||
// 强制同步到磁盘(避免异步写入导致控件读取不到文件)
|
|
||||||
if (fos != null) {
|
|
||||||
try {
|
try {
|
||||||
fos.getFD().sync();
|
fos.getFD().sync();
|
||||||
LogUtils.d(TAG, "【压缩保存】已强制同步到磁盘");
|
LogUtils.d(TAG, "图片已强制同步到磁盘");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogUtils.w(TAG, "【压缩保存】sync()失败,flush()兜底:" + e.getMessage());
|
LogUtils.w(TAG, "sync失败,flush兜底:" + e.getMessage());
|
||||||
outStream.flush();
|
outStream.flush();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
LogUtils.d(TAG, "【压缩结果】" + (compressSuccess ? "成功" : "失败") + ",大小:" + targetCompressFile.length() / 1024 + "KB,路径:" + targetCompressFile);
|
LogUtils.d(TAG, "图片压缩" + (compressSuccess ? "成功" : "失败") + ",大小:" + targetCompressFile.length() / 1024 + "KB");
|
||||||
|
ToastUtils.show(compressSuccess ? "图片压缩成功" : "图片压缩失败");
|
||||||
// 关键修复:压缩成功后,强制同步路径到预览Bean(双重保障,避免时序错位)
|
|
||||||
if (compressSuccess) {
|
|
||||||
ToastUtils.show("图片压缩成功");
|
|
||||||
} else {
|
|
||||||
ToastUtils.show("图片压缩失败");
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogUtils.e(TAG, "【压缩异常】IO错误:" + e.getMessage(), e);
|
LogUtils.e(TAG, "图片压缩IO异常:" + e.getMessage(), e);
|
||||||
ToastUtils.show("图片压缩失败");
|
ToastUtils.show("图片压缩失败");
|
||||||
} finally {
|
} finally {
|
||||||
// 资源回收(避免内存泄漏)
|
|
||||||
if (outStream != null) {
|
if (outStream != null) {
|
||||||
try {
|
try {
|
||||||
outStream.close();
|
outStream.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogUtils.e(TAG, "【流关闭失败】BufferedOutputStream:" + e.getMessage());
|
LogUtils.e(TAG, "BufferedOutputStream关闭失败:" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fos != null) {
|
if (fos != null) {
|
||||||
try {
|
try {
|
||||||
fos.close();
|
fos.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogUtils.e(TAG, "【流关闭失败】FileOutputStream:" + e.getMessage());
|
LogUtils.e(TAG, "FileOutputStream关闭失败:" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (bitmap != null && !bitmap.isRecycled()) {
|
if (bitmap != null && !bitmap.isRecycled()) {
|
||||||
@@ -891,3 +693,4 @@ public class BackgroundSourceUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user