调试到选择图片按钮到剪裁图片阶段
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Sun Nov 30 17:13:20 GMT 2025
|
||||
#Sun Nov 30 18:04:35 GMT 2025
|
||||
stageCount=13
|
||||
libraryProject=
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.12
|
||||
buildCount=7
|
||||
buildCount=13
|
||||
baseBetaVersion=15.11.13
|
||||
|
||||
@@ -37,6 +37,11 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import androidx.core.util.Preconditions;
|
||||
import cc.winboll.studio.powerbell.BuildConfig;
|
||||
import android.os.Environment;
|
||||
import android.provider.Settings;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
public class BackgroundSettingsActivity extends WinBoLLActivity implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
|
||||
|
||||
@@ -50,7 +55,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
private static final int STORAGE_PERMISSION_REQUEST = 100;
|
||||
|
||||
// FileProvider 授权(必须与AndroidManifest.xml中配置一致)
|
||||
private static final String FILE_PROVIDER_AUTHORITY = "cc.winboll.studio.powerbell.fileprovider";
|
||||
private static final String FILE_PROVIDER_AUTHORITY = BuildConfig.APPLICATION_ID + ".fileprovider";
|
||||
|
||||
private AToolbar mAToolbar;
|
||||
private File mfBackgroundDir; // 背景图片存储文件夹
|
||||
@@ -187,8 +192,73 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (checkAndRequestStoragePermission()) {
|
||||
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||
startActivityForResult(intent, REQUEST_SELECT_PICTURE);
|
||||
// 核心修复:创建多个意图作为兜底
|
||||
Intent[] intents = new Intent[3];
|
||||
|
||||
// 意图1:ACTION_GET_CONTENT(优先)
|
||||
Intent getContentIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
getContentIntent.setType("image/*");
|
||||
getContentIntent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
getContentIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intents[0] = getContentIntent;
|
||||
|
||||
// 意图2:ACTION_PICK(兜底)
|
||||
Intent pickIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||
pickIntent.setType("image/*");
|
||||
pickIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intents[1] = pickIntent;
|
||||
|
||||
// 意图3:ACTION_OPEN_DOCUMENT(Android 4.4+)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
Intent openDocIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
openDocIntent.setType("image/*");
|
||||
openDocIntent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
openDocIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
intents[2] = openDocIntent;
|
||||
}
|
||||
|
||||
// 遍历意图,找到第一个能响应的
|
||||
Intent validIntent = null;
|
||||
for (Intent intent : intents) {
|
||||
if (intent != null && intent.resolveActivity(getPackageManager()) != null) {
|
||||
validIntent = intent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建chooser时添加flags
|
||||
if (validIntent != null) {
|
||||
Intent chooser = Intent.createChooser(validIntent, "选择图片");
|
||||
// 核心修复:传递持久化权限flag
|
||||
chooser.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
startActivityForResult(chooser, REQUEST_SELECT_PICTURE);
|
||||
} else {
|
||||
// 确保对话框能正常显示
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ToastUtils.show("请安装相册应用,或点击确定下载系统相册");
|
||||
new AlertDialog.Builder(BackgroundSettingsActivity.this)
|
||||
.setTitle("无图片选择应用")
|
||||
.setMessage("需要安装相册应用才能选择图片")
|
||||
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent marketIntent = new Intent(Intent.ACTION_VIEW);
|
||||
marketIntent.setData(Uri.parse("market://details?id=com.android.gallery3d"));
|
||||
// 确保市场意图能响应
|
||||
if (marketIntent.resolveActivity(getPackageManager()) != null) {
|
||||
startActivity(marketIntent);
|
||||
} else {
|
||||
ToastUtils.show("无法打开应用商店");
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton("取消", null)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -356,6 +426,9 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
|
||||
try {
|
||||
_mSourceCropTempFile.createNewFile();
|
||||
// 核心优化:设置文件权限
|
||||
_mSourceCropTempFile.setReadable(true, false);
|
||||
_mSourceCropTempFile.setWritable(true, false);
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
ToastUtils.show("剪裁临时文件创建失败");
|
||||
@@ -375,7 +448,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
// 核心修复2:裁剪意图兼容(适配不同机型)
|
||||
Intent intent = new Intent("com.android.camera.action.CROP");
|
||||
// 兼容部分机型不支持隐式意图,添加包名过滤(可选,按需添加)
|
||||
intent.setPackage("com.android.camera");
|
||||
//intent.setPackage("com.android.camera");
|
||||
intent.setDataAndType(inputUri, "image/" + _mszCommonFileType);
|
||||
intent.putExtra("crop", "true");
|
||||
intent.putExtra("noFaceDetection", true);
|
||||
@@ -437,19 +510,25 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
* 工具方法:生成Content Uri(适配Android 7.0+),需在AndroidManifest.xml中配置FileProvider
|
||||
*/
|
||||
private Uri getUriForFile(Context context, File file) throws Exception {
|
||||
String targetPackage = getPackageName();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
try {
|
||||
// 与AndroidManifest.xml中配置的FileProvider授权一致
|
||||
return FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, file);
|
||||
Uri uri = FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, file);
|
||||
// 显式授予Uri权限给目标应用(如裁剪工具的包名)
|
||||
if (!TextUtils.isEmpty(targetPackage)) {
|
||||
context.grantUriPermission(targetPackage, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
}
|
||||
return uri;
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "FileProvider生成Uri失败:" + e.getMessage() + ",文件路径:" + file.getPath());
|
||||
throw e; // 抛出异常,让上层处理
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
return Uri.fromFile(file); // 低版本兼容
|
||||
return Uri.fromFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存剪裁后的Bitmap(彻底修复:路径拼接+权限+解析异常)
|
||||
*/
|
||||
@@ -628,39 +707,40 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
LogUtils.d(TAG, "选择图片Uri : " + selectedImage.toString());
|
||||
|
||||
// 核心修复1:替换路径解析方式,兼容UriUtil解析失败场景
|
||||
// 核心修复:对ACTION_GET_CONTENT返回的Uri,添加持久化权限(Android 4.4+)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
getContentResolver().takePersistableUriPermission(
|
||||
selectedImage,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
);
|
||||
}
|
||||
|
||||
// 路径解析逻辑保持不变...
|
||||
File fSrcImage = null;
|
||||
String filePath = UriUtil.getFilePathFromUri(this, selectedImage);
|
||||
if (!TextUtils.isEmpty(filePath)) {
|
||||
fSrcImage = new File(filePath);
|
||||
} else {
|
||||
// 兼容方案:通过ContentResolver读取图片流,保存为临时文件
|
||||
fSrcImage = new File(mfPictureDir, "selected_temp.jpg");
|
||||
if (fSrcImage.exists()) {
|
||||
fSrcImage.delete();
|
||||
}
|
||||
// 复制图片流到临时文件
|
||||
FileUtils.copyStreamToFile(getContentResolver().openInputStream(selectedImage), fSrcImage);
|
||||
LogUtils.d(TAG, "Uri解析失败,通过流复制生成临时文件:" + fSrcImage.getPath());
|
||||
}
|
||||
|
||||
// 核心修复2:增强文件有效性校验
|
||||
if (fSrcImage == null || !fSrcImage.exists() || fSrcImage.length() <= 0) {
|
||||
ToastUtils.show("选择的图片文件不存在或损坏");
|
||||
return;
|
||||
}
|
||||
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
|
||||
// 保存图片到预览Bean并刷新
|
||||
utils.saveFileToPreviewBean(fSrcImage, selectedImage.toString());
|
||||
bvPreviewBackground.reloadPreviewBackground();
|
||||
|
||||
// 启动裁剪(增加异常捕获)
|
||||
startCropImageActivity(false);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "选择图片异常" + e);
|
||||
ToastUtils.show("选择图片失败:" + e.getMessage());
|
||||
// 异常时清理临时文件
|
||||
if (_mSourceCropTempFile.exists()) {
|
||||
_mSourceCropTempFile.delete();
|
||||
LogUtils.d(TAG, "选择图片异常,清理临时文件:" + _mSourceCropTempFile.getPath());
|
||||
@@ -782,12 +862,21 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
* 检查并申请存储权限(修复:适配低版本API,移除Android13+依赖)
|
||||
*/
|
||||
private boolean checkAndRequestStoragePermission() {
|
||||
// 仅保留Android 6.0+(API 23)的WRITE_EXTERNAL_STORAGE权限,兼容低版本
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // M = API 23,Android 6.0
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
STORAGE_PERMISSION_REQUEST);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
|
||||
startActivity(intent);
|
||||
return false;
|
||||
}
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// 核心修复:同时申请READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE
|
||||
String[] permissions = new String[]{
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
};
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
|
||||
|| ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this, permissions, STORAGE_PERMISSION_REQUEST);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -799,7 +888,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == STORAGE_PERMISSION_REQUEST) {
|
||||
boolean isGranted = false;
|
||||
// 检查权限是否授予
|
||||
for (int result : grantResults) {
|
||||
if (result == PackageManager.PERMISSION_GRANTED) {
|
||||
isGranted = true;
|
||||
@@ -807,7 +895,9 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
}
|
||||
if (isGranted) {
|
||||
ToastUtils.show("存储权限已获取");
|
||||
ToastUtils.show("存储权限已获取,正在打开图片选择器");
|
||||
// 核心优化:自动重试图片选择
|
||||
onSelectPictureClickListener.onClick(findViewById(R.id.activitybackgroundpictureAButton2));
|
||||
} else {
|
||||
ToastUtils.show("需要存储权限才能保存/选择图片");
|
||||
}
|
||||
|
||||
@@ -238,22 +238,28 @@ public class FileUtils {
|
||||
*/
|
||||
public static void copyStreamToFile(InputStream inputStream, File file) throws IOException {
|
||||
if (inputStream == null || file == null) {
|
||||
return;
|
||||
throw new IllegalArgumentException("InputStream或File不能为空");
|
||||
}
|
||||
// 确保父目录存在
|
||||
File parentDir = file.getParentFile();
|
||||
if (!parentDir.exists()) {
|
||||
parentDir.mkdirs();
|
||||
if (!parentDir.exists() && !parentDir.mkdirs()) {
|
||||
throw new IOException("无法创建父目录:" + parentDir.getAbsolutePath());
|
||||
}
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
try {
|
||||
OutputStream outputStream = new FileOutputStream(file);
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = inputStream.read(buffer)) != -1) {
|
||||
fos.write(buffer, 0, len);
|
||||
int length;
|
||||
while ((length = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, length);
|
||||
}
|
||||
fos.flush();
|
||||
fos.close();
|
||||
outputStream.flush();
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e("FileUtils", "关闭输入流失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user