基本完成图片设置流程。繁琐步骤未测试。。。
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 09:19:24 GMT 2025
|
#Wed Dec 03 10:59:39 GMT 2025
|
||||||
stageCount=13
|
stageCount=13
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.11
|
baseVersion=15.11
|
||||||
publishVersion=15.11.12
|
publishVersion=15.11.12
|
||||||
buildCount=122
|
buildCount=129
|
||||||
baseBetaVersion=15.11.13
|
baseBetaVersion=15.11.13
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import java.util.List;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import cc.winboll.studio.powerbell.utils.FileUtils;
|
import cc.winboll.studio.powerbell.utils.FileUtils;
|
||||||
|
import cc.winboll.studio.powerbell.dialogs.YesNoAlertDialog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 背景设置Activity(图片选择/拍照/裁剪/预览/保存核心页面)
|
* 背景设置Activity(图片选择/拍照/裁剪/预览/保存核心页面)
|
||||||
@@ -77,9 +78,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
private File mfTakePhoto;
|
private File mfTakePhoto;
|
||||||
// 配置标记(是否提交设置)
|
// 配置标记(是否提交设置)
|
||||||
boolean isCommitSettings = false;
|
boolean isCommitSettings = false;
|
||||||
// 预览图片信息(用于退出确认)
|
|
||||||
//private String preViewFilePath = "";
|
|
||||||
//private String preViewFileUrl = "";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Activity getActivity() {
|
public Activity getActivity() {
|
||||||
@@ -969,4 +967,27 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
LogUtils.d(TAG, "【选图权限】已添加持久化读取权限");
|
LogUtils.d(TAG, "【选图权限】已添加持久化读取权限");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
if(isCommitSettings) {
|
||||||
|
super.finish();
|
||||||
|
} else{
|
||||||
|
YesNoAlertDialog.show(this, "背景更换问题", "是否确定背景图片设置?", new YesNoAlertDialog.OnDialogResultListener(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onYes() {
|
||||||
|
mBgSourceUtils.commitPreviewSourceToCurrent();
|
||||||
|
isCommitSettings = true;
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNo() {
|
||||||
|
isCommitSettings = true;
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ public class MainViewFragment extends Fragment {
|
|||||||
Switch mswIsEnableService;
|
Switch mswIsEnableService;
|
||||||
TextView mtvTips;
|
TextView mtvTips;
|
||||||
|
|
||||||
|
private BackgroundSourceUtils mBgSourceUtils;
|
||||||
|
|
||||||
// 背景布局
|
// 背景布局
|
||||||
//LinearLayout mLinearLayoutloadBackground;
|
//LinearLayout mLinearLayoutloadBackground;
|
||||||
|
|
||||||
@@ -79,14 +81,11 @@ public class MainViewFragment extends Fragment {
|
|||||||
mView = inflater.inflate(R.layout.fragment_mainview, container, false);
|
mView = inflater.inflate(R.layout.fragment_mainview, container, false);
|
||||||
_mMainViewFragment = MainViewFragment.this;
|
_mMainViewFragment = MainViewFragment.this;
|
||||||
mAppConfigUtils = App.getAppConfigUtils(getActivity());
|
mAppConfigUtils = App.getAppConfigUtils(getActivity());
|
||||||
|
mBgSourceUtils = BackgroundSourceUtils.getInstance(getActivity());
|
||||||
// 获取指定ID的View实例
|
// 获取指定ID的View实例
|
||||||
mBackgroundView = mView.findViewById(R.id.fragmentmainviewBackgroundView1);
|
mBackgroundView = mView.findViewById(R.id.fragmentmainviewBackgroundView1);
|
||||||
|
|
||||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(getActivity());
|
loadBackground();
|
||||||
BackgroundBean bean = utils.getCurrentBackgroundBean();
|
|
||||||
int nPixelColor = bean.getPixelColor();
|
|
||||||
mBackgroundView.setBackgroundColor(nPixelColor);
|
|
||||||
/*final View mainImageView = mView.findViewById(R.id.fragmentmainviewImageView1);
|
/*final View mainImageView = mView.findViewById(R.id.fragmentmainviewImageView1);
|
||||||
|
|
||||||
// 注册OnGlobalLayoutListener
|
// 注册OnGlobalLayoutListener
|
||||||
@@ -149,16 +148,18 @@ public class MainViewFragment extends Fragment {
|
|||||||
return mView;
|
return mView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadBackground() {
|
||||||
|
BackgroundBean bean = mBgSourceUtils.getCurrentBackgroundBean();
|
||||||
|
mBackgroundView.loadBackground(bean);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(getActivity());
|
loadBackground();
|
||||||
BackgroundBean bean = utils.getCurrentBackgroundBean();
|
|
||||||
int nPixelColor = bean.getPixelColor();
|
|
||||||
mBackgroundView.setBackgroundColor(nPixelColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void setViewData() {
|
void setViewData() {
|
||||||
int nChargeReminderValue = mAppConfigUtils.getChargeReminderValue();
|
int nChargeReminderValue = mAppConfigUtils.getChargeReminderValue();
|
||||||
|
|||||||
@@ -311,49 +311,6 @@ public class BackgroundSourceUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 核心优化函数:带原图参数的裁剪路径创建(优先使用原图,不复制)
|
|
||||||
* 替代原逻辑中"复制原图到BackgroundSource再裁剪"的流程
|
|
||||||
*/
|
|
||||||
// File createFileProviderPath(File originalImageFile) {
|
|
||||||
// Log.d(TAG, "【裁剪优化】createCropFileProviderPath(原图) 触发,优先使用原图路径");
|
|
||||||
//
|
|
||||||
// // 核心逻辑1:直接使用裁剪前的原图(不复制),仅校验合法性和权限
|
|
||||||
// if (originalImageFile != null && originalImageFile.exists()
|
|
||||||
// && originalImageFile.isFile() && originalImageFile.length() > 0) {
|
|
||||||
// // 校验原图目录是否可写(系统裁剪工具需读写权限)
|
|
||||||
// if (isDirectoryWritable(originalImageFile.getParentFile())) {
|
|
||||||
// // 强制开放原图权限(解决跨进程访问问题)
|
|
||||||
// //setFileReadWritePermission(originalImageFile);
|
|
||||||
// Log.d(TAG, "【裁剪优化】直接使用原图启动裁剪(无复制):" + originalImageFile.getAbsolutePath());
|
|
||||||
// return originalImageFile; // 直接返回原图,不做任何复制
|
|
||||||
// } else {
|
|
||||||
// Log.w(TAG, "【裁剪优化】原图目录不可写,切换到临时文件兜底");
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// Log.w(TAG, "【裁剪优化】原图无效(空/不存在/0大小),切换到临时文件兜底");
|
|
||||||
// }
|
|
||||||
// Log.e(TAG, "【裁剪优化】所有裁剪路径创建失败,裁剪功能不可用");
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 辅助方法:校验目录是否可写(新增,保障裁剪权限)
|
|
||||||
// private boolean isDirectoryWritable(File dir) {
|
|
||||||
// if (dir == null) return false;
|
|
||||||
// // 目录存在且可写,或目录不存在但能创建
|
|
||||||
// return (dir.exists() && dir.isDirectory() && dir.canWrite())
|
|
||||||
// || (!dir.exists() && dir.mkdirs());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 辅助方法:设置文件读写权限(新增,解决系统裁剪跨进程访问)
|
|
||||||
// private void setFileReadWritePermission(File file) {
|
|
||||||
// if (file == null || !file.exists()) return;
|
|
||||||
// // 开放读写权限(兼容Android 10+)
|
|
||||||
// file.setReadable(true, false);
|
|
||||||
// file.setWritable(true, false);
|
|
||||||
// file.setExecutable(true, false);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载背景图片配置数据(核心:确保current/preview是两份独立的BackgroundBean实例)
|
* 加载背景图片配置数据(核心:确保current/preview是两份独立的BackgroundBean实例)
|
||||||
*/
|
*/
|
||||||
@@ -453,9 +410,13 @@ public class BackgroundSourceUtils {
|
|||||||
* 保存配置(核心:将两份独立Bean实例,分别写入各自的JSON文件)
|
* 保存配置(核心:将两份独立Bean实例,分别写入各自的JSON文件)
|
||||||
*/
|
*/
|
||||||
public void saveSettings() {
|
public void saveSettings() {
|
||||||
BackgroundBean.saveBeanToFile(currentBackgroundBeanFile.getAbsolutePath(), currentBackgroundBean); // 正式Bean→正式JSON
|
if(currentBackgroundBean != null && previewBackgroundBean != null) {
|
||||||
BackgroundBean.saveBeanToFile(previewBackgroundBeanFile.getAbsolutePath(), previewBackgroundBean); // 预览Bean→预览JSON
|
BackgroundBean.saveBeanToFile(currentBackgroundBeanFile.getAbsolutePath(), currentBackgroundBean); // 正式Bean→正式JSON
|
||||||
LogUtils.d(TAG, "【配置管理】两份配置保存成功:正式JSON=" + currentBackgroundBeanFile.getAbsolutePath() + ",预览JSON=" + previewBackgroundBeanFile.getAbsolutePath());
|
BackgroundBean.saveBeanToFile(previewBackgroundBeanFile.getAbsolutePath(), previewBackgroundBean); // 预览Bean→预览JSON
|
||||||
|
LogUtils.d(TAG, "【配置管理】两份配置保存成功:正式JSON=" + currentBackgroundBeanFile.getAbsolutePath() + ",预览JSON=" + previewBackgroundBeanFile.getAbsolutePath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LogUtils.d(TAG, "【配置管理】两份配置保存失败。currentBackgroundBean 与 previewBackgroundBean 有空值。");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -544,11 +505,24 @@ public class BackgroundSourceUtils {
|
|||||||
currentBackgroundBean.setBackgroundHeight(previewBackgroundBean.getBackgroundHeight());
|
currentBackgroundBean.setBackgroundHeight(previewBackgroundBean.getBackgroundHeight());
|
||||||
currentBackgroundBean.setPixelColor(previewBackgroundBean.getPixelColor());
|
currentBackgroundBean.setPixelColor(previewBackgroundBean.getPixelColor());
|
||||||
|
|
||||||
|
// 拷贝一份缓存图片文件到正式背景文件夹
|
||||||
|
String previewFileName = previewBackgroundBean.getBackgroundFileName();
|
||||||
|
String previewCropFileName = previewBackgroundBean.getBackgroundScaledCompressFileName();
|
||||||
|
File previewFile = new File(previewBackgroundBean.getBackgroundFilePath());
|
||||||
|
File previewCropFile = new File(previewBackgroundBean.getBackgroundScaledCompressFilePath());
|
||||||
|
File currentFile = new File(fBackgroundSourceDir, previewFileName);
|
||||||
|
File currentCropFile = new File(fBackgroundCompressDir, previewCropFileName);
|
||||||
|
FileUtils.copyFile(previewFile, currentFile);
|
||||||
|
FileUtils.copyFile(previewCropFile, currentCropFile);
|
||||||
|
// 更新当前背景文件路径
|
||||||
|
currentBackgroundBean.setBackgroundFilePath(currentFile.getAbsolutePath()); // 原图路径(BackgroundSource)
|
||||||
|
currentBackgroundBean.setBackgroundScaledCompressFilePath(currentCropFile.getAbsolutePath()); // 压缩图路径(BackgroundCrops)
|
||||||
|
|
||||||
saveSettings(); // 分别保存:正式Bean→currentJSON,预览Bean→previewJSON(两份独立)
|
saveSettings(); // 分别保存:正式Bean→currentJSON,预览Bean→previewJSON(两份独立)
|
||||||
LogUtils.d(TAG, "【配置管理】预览背景深拷贝到正式Bean:两份实例独立,压缩图统一存储到BackgroundCrops");
|
LogUtils.d(TAG, "【配置管理】预览背景深拷贝到正式Bean:两份实例独立,压缩图统一存储到BackgroundCrops");
|
||||||
ToastUtils.show("背景图片应用成功");
|
ToastUtils.show("背景图片应用成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将正式背景同步到预览背景(正式Bean → 预览Bean:深拷贝,新建预览Bean实例+逐字段拷贝)
|
* 将正式背景同步到预览背景(正式Bean → 预览Bean:深拷贝,新建预览Bean实例+逐字段拷贝)
|
||||||
* 核心:深拷贝后,修改预览Bean不会影响正式Bean,两份实例完全独立,压缩图路径统一指向BackgroundCrops
|
* 核心:深拷贝后,修改预览Bean不会影响正式Bean,两份实例完全独立,压缩图路径统一指向BackgroundCrops
|
||||||
@@ -572,75 +546,6 @@ public class BackgroundSourceUtils {
|
|||||||
LogUtils.d(TAG, "【配置管理】正式背景深拷贝到预览Bean:两份实例独立,压缩图统一存储到BackgroundCrops");
|
LogUtils.d(TAG, "【配置管理】正式背景深拷贝到预览Bean:两份实例独立,压缩图统一存储到BackgroundCrops");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------ 必需保留的工具方法(与业务/权限强相关,无法复用FileUtils)------------------------------
|
|
||||||
/**
|
|
||||||
* 工具方法:递归设置目录及子目录/文件的读写权限(适配系统公共目录/Pictures/PowerBell)
|
|
||||||
* @param dir 要设置权限的目录
|
|
||||||
*/
|
|
||||||
// private void setDirPermissionsRecursively(File dir) {
|
|
||||||
// if (dir == null || !dir.exists()) {
|
|
||||||
// String dirPath = (dir != null) ? dir.getAbsolutePath() : "null";
|
|
||||||
// LogUtils.d(TAG, "【权限管理】目录无效,无需设置权限:" + dirPath);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// try {
|
|
||||||
// // 设置目录权限(允许所有用户读写,系统裁剪应用/预览功能必需)
|
|
||||||
// dir.setReadable(true, false);
|
|
||||||
// dir.setWritable(true, false);
|
|
||||||
// dir.setExecutable(false, false);
|
|
||||||
//
|
|
||||||
// LogUtils.d(TAG, "【权限管理】目录权限设置完成:路径=" + dir.getAbsolutePath() + ",可写=" + dir.canWrite() + ",可读=" + dir.canRead());
|
|
||||||
//
|
|
||||||
// // 递归处理子目录和文件(Java7 普通for循环,兼容语法)
|
|
||||||
// File[] files = dir.listFiles();
|
|
||||||
// if (files != null && files.length > 0) {
|
|
||||||
// for (int i = 0; i < files.length; i++) {
|
|
||||||
// File file = files[i];
|
|
||||||
// if (file.isDirectory()) {
|
|
||||||
// setDirPermissionsRecursively(file);
|
|
||||||
// } else {
|
|
||||||
// // 设置文件权限(与目录一致,确保可读写)
|
|
||||||
// file.setReadable(true, false);
|
|
||||||
// file.setWritable(true, false);
|
|
||||||
// file.setExecutable(false, false);
|
|
||||||
// // 裁剪/压缩/预览相关文件单独打印日志
|
|
||||||
// if (file.getName().contains(CROP_TEMP_FILE_NAME) ||
|
|
||||||
// file.getName().contains(CROP_RESULT_FILE_NAME) ||
|
|
||||||
// file.getName().startsWith("ScaledCompress_")) {
|
|
||||||
// LogUtils.d(TAG, "【权限管理】关键文件权限设置:文件名=" + file.getName() + ",可写=" + file.canWrite());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch (SecurityException e) {
|
|
||||||
// LogUtils.e(TAG, "【权限管理】设置目录权限失败(系统禁止):" + dir.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
|
||||||
// ToastUtils.show("目录权限设置失败,请授予应用存储权限");
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// LogUtils.e(TAG, "【权限管理】设置目录权限异常:" + dir.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 工具方法:设置单个文件权限(确保系统裁剪应用/预览功能可读写,适配/Pictures/PowerBell目录)
|
|
||||||
* 【关键调整】public修饰,适配BackgroundSettingsActivity的外部调用
|
|
||||||
* @param file 要设置权限的文件
|
|
||||||
*/
|
|
||||||
// public void setFilePermissions(File file) {
|
|
||||||
// if (file == null || !file.exists()) {
|
|
||||||
// LogUtils.d(TAG, "【权限管理】文件无效,无需设置权限:" + (file != null ? file.getAbsolutePath() : "null"));
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// try {
|
|
||||||
// // 核心:允许所有用户读写(系统裁剪应用非本应用进程,需开放权限)
|
|
||||||
// file.setReadable(true, false);
|
|
||||||
// file.setWritable(true, false);
|
|
||||||
// file.setExecutable(false, false);
|
|
||||||
// LogUtils.d(TAG, "【权限管理】文件权限设置完成(/Pictures/PowerBell下):路径=" + file.getAbsolutePath() + ",可写=" + file.canWrite() + ",可读=" + file.canRead());
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// LogUtils.e(TAG, "【权限管理】设置文件权限失败:" + file.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工具方法:清理旧文件(避免文件锁定/残留,适配系统公共目录)【内部私有,不对外暴露】
|
* 工具方法:清理旧文件(避免文件锁定/残留,适配系统公共目录)【内部私有,不对外暴露】
|
||||||
* @param file 要清理的文件
|
* @param file 要清理的文件
|
||||||
@@ -667,45 +572,6 @@ public class BackgroundSourceUtils {
|
|||||||
mCropResultFile = null;
|
mCropResultFile = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 工具方法:验证目录实际写入能力(解决Android14+ canWrite()假阳性问题,适配/Pictures/PowerBell)
|
|
||||||
* 原理:通过创建临时空文件并删除,验证目录是否真的可写
|
|
||||||
* @param dir 要验证的目录
|
|
||||||
* @return true=实际可写,false=实际不可写
|
|
||||||
*/
|
|
||||||
// private boolean isDirActuallyWritable(File dir) {
|
|
||||||
// if (dir == null || !dir.exists() || !dir.isDirectory()) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// // 创建临时空文件(随机文件名,避免冲突)
|
|
||||||
// File testFile = new File(dir, "test_write_" + System.currentTimeMillis() + ".tmp");
|
|
||||||
// try {
|
|
||||||
// boolean createSuccess = testFile.createNewFile();
|
|
||||||
// if (createSuccess) {
|
|
||||||
// boolean canWrite = testFile.canWrite();
|
|
||||||
// boolean canRead = testFile.canRead();
|
|
||||||
// testFile.delete(); // 删除临时文件,不占用空间
|
|
||||||
// LogUtils.d(TAG, "【权限校验】目录实际写入校验(/Pictures/PowerBell下):" + dir.getAbsolutePath() + ",创建成功=" + createSuccess + ",可写=" + canWrite + ",可读=" + canRead + ",结果=" + (canWrite ? "通过" : "失败"));
|
|
||||||
// return canWrite;
|
|
||||||
// } else {
|
|
||||||
// LogUtils.d(TAG, "【权限校验】目录实际写入校验失败:" + dir.getAbsolutePath() + ",创建临时文件失败(Permission denied)");
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// } catch (IOException e) {
|
|
||||||
// LogUtils.e(TAG, "【权限校验】目录实际写入校验异常:" + dir.getAbsolutePath() + ",错误:" + e.getMessage(), e);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对外接口:清理指定旧文件(适配BackgroundSettingsActivity调用,支持/Pictures/PowerBell目录)
|
|
||||||
* @param file 要清理的文件
|
|
||||||
* @param fileDesc 文件描述(用于日志打印)
|
|
||||||
*/
|
|
||||||
// public void clearOldFileByExternal(File file, String fileDesc) {
|
|
||||||
// clearOldFile(file, fileDesc); // 调用内部private方法,复用逻辑
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 适配原调用:mBgSourceUtils.copyFile(new File(""), parentDir)
|
* 适配原调用:mBgSourceUtils.copyFile(new File(""), parentDir)
|
||||||
* 核心:复用FileUtils,支持「空源文件→仅创建目标目录」和「正常文件复制」两种场景
|
* 核心:复用FileUtils,支持「空源文件→仅创建目标目录」和「正常文件复制」两种场景
|
||||||
|
|||||||
Reference in New Issue
Block a user