应用权限框架更新,重新调试剪裁文件流程。
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 10 22:51:19 GMT 2025
|
#Thu Dec 11 00:57:37 GMT 2025
|
||||||
stageCount=15
|
stageCount=15
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.12
|
baseVersion=15.12
|
||||||
publishVersion=15.12.14
|
publishVersion=15.12.14
|
||||||
buildCount=5
|
buildCount=13
|
||||||
baseBetaVersion=15.12.15
|
baseBetaVersion=15.12.15
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ public class App extends GlobalApplication {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
setIsDebugging(BuildConfig.DEBUG);
|
setIsDebugging(BuildConfig.DEBUG);
|
||||||
PermissionUtils.init(this);
|
|
||||||
|
|
||||||
// 初始化活动窗口管理
|
// 初始化活动窗口管理
|
||||||
WinBoLLActivityManager.init(this);
|
WinBoLLActivityManager.init(this);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class MainActivity extends WinBoLLActivity {
|
|||||||
|
|
||||||
// ======================== 静态常量(移除缓存相关键)========================
|
// ======================== 静态常量(移除缓存相关键)========================
|
||||||
public static final String TAG = "MainActivity";
|
public static final String TAG = "MainActivity";
|
||||||
private static final int REQUEST_WRITE_STORAGE_PERMISSION = 1001;
|
private static final int REQUEST_READ_MEDIA_IMAGES = 1001;
|
||||||
public static final int MSG_RELOAD_APPCONFIG = 0;
|
public static final int MSG_RELOAD_APPCONFIG = 0;
|
||||||
public static final int MSG_CURRENTVALUEBATTERY = 1;
|
public static final int MSG_CURRENTVALUEBATTERY = 1;
|
||||||
public static final int MSG_LOAD_BACKGROUND = 2;
|
public static final int MSG_LOAD_BACKGROUND = 2;
|
||||||
@@ -123,9 +123,7 @@ public class MainActivity extends WinBoLLActivity {
|
|||||||
loadNonCriticalViewDelayed();
|
loadNonCriticalViewDelayed();
|
||||||
|
|
||||||
// 权限申请
|
// 权限申请
|
||||||
if (!PermissionUtils.getInstance().hasFullStoragePermission()) {
|
PermissionUtils.getInstance().checkAndRequestMediaImagesPermission(this, REQUEST_READ_MEDIA_IMAGES);
|
||||||
PermissionUtils.getInstance().requestFullStoragePermission(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除 onSaveInstanceState 方法
|
// 移除 onSaveInstanceState 方法
|
||||||
@@ -217,6 +215,14 @@ public class MainActivity extends WinBoLLActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
if (requestCode == REQUEST_READ_MEDIA_IMAGES) {
|
||||||
|
PermissionUtils.getInstance().handleStoragePermissionResult(this, requestCode, permissions, grantResults);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
moveTaskToBack(true);
|
moveTaskToBack(true);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package cc.winboll.studio.powerbell.activities;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
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;
|
||||||
@@ -14,11 +15,24 @@ 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.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
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;
|
||||||
import cc.winboll.studio.powerbell.App;
|
import cc.winboll.studio.powerbell.App;
|
||||||
@@ -27,26 +41,27 @@ import cc.winboll.studio.powerbell.dialogs.BackgroundPicturePreviewDialog;
|
|||||||
import cc.winboll.studio.powerbell.dialogs.YesNoAlertDialog;
|
import cc.winboll.studio.powerbell.dialogs.YesNoAlertDialog;
|
||||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||||
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
|
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
|
||||||
|
import cc.winboll.studio.powerbell.utils.BitmapCacheUtils;
|
||||||
import cc.winboll.studio.powerbell.utils.FileUtils;
|
import cc.winboll.studio.powerbell.utils.FileUtils;
|
||||||
import cc.winboll.studio.powerbell.utils.ImageCropUtils;
|
import cc.winboll.studio.powerbell.utils.ImageCropUtils;
|
||||||
import cc.winboll.studio.powerbell.utils.PermissionUtils;
|
import cc.winboll.studio.powerbell.utils.PermissionUtils;
|
||||||
import cc.winboll.studio.powerbell.utils.UriUtil;
|
import cc.winboll.studio.powerbell.utils.UriUtil;
|
||||||
import cc.winboll.studio.powerbell.views.BackgroundView;
|
import cc.winboll.studio.powerbell.views.BackgroundView;
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
public class BackgroundSettingsActivity extends WinBoLLActivity implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
|
public class BackgroundSettingsActivity extends WinBoLLActivity implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
|
||||||
|
|
||||||
public static final String TAG = "BackgroundSettingsActivity";
|
public static final String TAG = "BackgroundSettingsActivity";
|
||||||
private BackgroundSourceUtils mBgSourceUtils;
|
private BackgroundSourceUtils mBgSourceUtils;
|
||||||
private PermissionUtils mPermissionUtils;
|
private PermissionUtils mPermissionUtils;
|
||||||
|
private BitmapCacheUtils mBitmapCache;
|
||||||
|
|
||||||
|
// 新增:手动定义 Android 13 对应的 SDK 版本号
|
||||||
|
private static final int SDK_VERSION_TIRAMISU = 33;
|
||||||
|
|
||||||
public static final int REQUEST_SELECT_PICTURE = 0;
|
public static final int REQUEST_SELECT_PICTURE = 0;
|
||||||
public static final int REQUEST_TAKE_PHOTO = 1;
|
public static final int REQUEST_TAKE_PHOTO = 1;
|
||||||
public static final int REQUEST_CROP_IMAGE = 2;
|
public static final int REQUEST_CROP_IMAGE = 2;
|
||||||
|
private static final int REQUEST_READ_MEDIA = 1001;
|
||||||
|
|
||||||
private Toolbar mToolbar;
|
private Toolbar mToolbar;
|
||||||
private BackgroundView mBackgroundView;
|
private BackgroundView mBackgroundView;
|
||||||
@@ -73,6 +88,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
mBgSourceUtils = BackgroundSourceUtils.getInstance(this);
|
mBgSourceUtils = BackgroundSourceUtils.getInstance(this);
|
||||||
mBgSourceUtils.loadSettings();
|
mBgSourceUtils.loadSettings();
|
||||||
mPermissionUtils = PermissionUtils.getInstance();
|
mPermissionUtils = PermissionUtils.getInstance();
|
||||||
|
mBitmapCache = BitmapCacheUtils.getInstance();
|
||||||
|
|
||||||
File tempDir = new File(App.getTempDirPath());
|
File tempDir = new File(App.getTempDirPath());
|
||||||
if (!tempDir.exists()) {
|
if (!tempDir.exists()) {
|
||||||
@@ -95,9 +111,10 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
mBgSourceUtils.setCurrentSourceToPreview();
|
mBgSourceUtils.setCurrentSourceToPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
Uri uriSelectedImage = UriUtil.getUriForFile(this, new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath()));
|
//Uri uriSelectedImage = UriUtil.getUriForFile(this, new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath()));
|
||||||
// 创建预览数据剪裁环境
|
File fTestSourceExist = new File(mBgSourceUtils.getCurrentBackgroundBean().getBackgroundFilePath());
|
||||||
mBgSourceUtils.createAndUpdatePreviewEnvironmentForCropping(mBgSourceUtils.getPreviewBackgroundBean());
|
boolean isCreateNewPreviewBean = !fTestSourceExist.exists();
|
||||||
|
mBgSourceUtils.createAndUpdatePreviewEnvironmentForCropping(isCreateNewPreviewBean, mBgSourceUtils.getPreviewBackgroundBean());
|
||||||
doubleRefreshPreview();
|
doubleRefreshPreview();
|
||||||
LogUtils.d(TAG, "【初始化】BackgroundSettingsActivity 初始化完成");
|
LogUtils.d(TAG, "【初始化】BackgroundSettingsActivity 初始化完成");
|
||||||
}
|
}
|
||||||
@@ -109,7 +126,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initToolbar() {
|
private void initToolbar() {
|
||||||
mToolbar = (Toolbar) findViewById(R.id.toolbar);
|
mToolbar = findViewById(R.id.toolbar);
|
||||||
setSupportActionBar(mToolbar);
|
setSupportActionBar(mToolbar);
|
||||||
mToolbar.setSubtitle(getTag());
|
mToolbar.setSubtitle(getTag());
|
||||||
mToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
|
mToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
|
||||||
@@ -173,39 +190,57 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ====================== 核心修改:Java 7 兼容的相册选择点击事件 ======================
|
|
||||||
private View.OnClickListener onSelectPictureClickListener = new View.OnClickListener() {
|
private View.OnClickListener onSelectPictureClickListener = new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
LogUtils.d(TAG, "【按钮点击】选择图片");
|
LogUtils.d(TAG, "【按钮点击】选择图片");
|
||||||
// 适配 API 30+ 权限逻辑
|
// 适配Android 13+权限(已替换为手动定义的常量)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= SDK_VERSION_TIRAMISU) {
|
||||||
// API 30+ 优先判断全文件管理权限
|
if (mPermissionUtils.checkAndRequestMediaImagesPermission(BackgroundSettingsActivity.this, REQUEST_READ_MEDIA)) {
|
||||||
if (!Environment.isExternalStorageManager()) {
|
launchImageSelector();
|
||||||
mPermissionUtils.requestFullStoragePermission(BackgroundSettingsActivity.this);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 低版本判断传统读写权限
|
if (mPermissionUtils.checkAndRequestStoragePermission(BackgroundSettingsActivity.this)) {
|
||||||
if (!mPermissionUtils.hasFullStoragePermission()) {
|
launchImageSelector();
|
||||||
mPermissionUtils.requestFullStoragePermission(BackgroundSettingsActivity.this);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void launchImageSelector() {
|
||||||
|
LogUtils.d(TAG, "【选图权限】已获取,启动选择器");
|
||||||
|
Intent[] intents = new Intent[3];
|
||||||
|
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;
|
||||||
|
|
||||||
// API 30+ 推荐的标准化相册选择 Intent
|
|
||||||
Intent pickIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
Intent pickIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||||
pickIntent.setType("image/*");
|
pickIntent.setType("image/*");
|
||||||
// 添加权限 Flag,确保 Uri 可读取
|
|
||||||
pickIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
pickIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
intents[1] = pickIntent;
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
// 持久化 Uri 权限,避免后续操作失效
|
Intent openDocIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||||
pickIntent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
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;
|
||||||
Intent chooser = Intent.createChooser(pickIntent, "选择图片");
|
for (int i = 0; i < intents.length; i++) {
|
||||||
if (chooser.resolveActivity(getPackageManager()) != null) {
|
Intent intent = intents[i];
|
||||||
|
if (intent != null && intent.resolveActivity(getPackageManager()) != null) {
|
||||||
|
validIntent = intent;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validIntent != null) {
|
||||||
|
Intent chooser = Intent.createChooser(validIntent, "选择图片");
|
||||||
|
chooser.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||||
startActivityForResult(chooser, REQUEST_SELECT_PICTURE);
|
startActivityForResult(chooser, REQUEST_SELECT_PICTURE);
|
||||||
LogUtils.d(TAG, "【选图意图】启动图片选择");
|
LogUtils.d(TAG, "【选图意图】启动图片选择");
|
||||||
} else {
|
} else {
|
||||||
@@ -235,19 +270,18 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
private View.OnClickListener onCropPictureClickListener = new View.OnClickListener() {
|
private View.OnClickListener onCropPictureClickListener = new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
LogUtils.d(TAG, "【按钮点击】固定比例裁剪");
|
LogUtils.d(TAG, "【按钮点击】固定比例裁剪");
|
||||||
// 调用裁剪工具类:传入上下文、预览图、固定比例(按视图宽高)、请求码
|
|
||||||
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
|
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
|
||||||
mBgSourceUtils.getPreviewBackgroundBean(),
|
mBgSourceUtils.getPreviewBackgroundBean(),
|
||||||
mBackgroundView.getWidth(),
|
mBackgroundView.getWidth(),
|
||||||
mBackgroundView.getHeight(),
|
mBackgroundView.getHeight(),
|
||||||
false,
|
false,
|
||||||
REQUEST_CROP_IMAGE);
|
REQUEST_CROP_IMAGE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -255,13 +289,13 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
LogUtils.d(TAG, "【按钮点击】自由裁剪");
|
LogUtils.d(TAG, "【按钮点击】自由裁剪");
|
||||||
// 调用裁剪工具类:传入上下文、预览图、自由裁剪(比例参数传0)、请求码
|
|
||||||
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
|
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
|
||||||
mBgSourceUtils.getPreviewBackgroundBean(),
|
mBgSourceUtils.getPreviewBackgroundBean(),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
true,
|
true,
|
||||||
REQUEST_CROP_IMAGE);
|
REQUEST_CROP_IMAGE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -286,7 +320,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPermissionUtils.hasFullStoragePermission()) {
|
if (mPermissionUtils.checkAndRequestStoragePermission(BackgroundSettingsActivity.this)) {
|
||||||
LogUtils.d(TAG, "【拍照权限】已获取");
|
LogUtils.d(TAG, "【拍照权限】已获取");
|
||||||
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||||
try {
|
try {
|
||||||
@@ -300,7 +334,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
LogUtils.e(TAG, "【拍照失败】");
|
LogUtils.e(TAG, "【拍照失败】");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mPermissionUtils.requestFullStoragePermission(BackgroundSettingsActivity.this);
|
|
||||||
LogUtils.d(TAG, "【拍照权限】已申请");
|
LogUtils.d(TAG, "【拍照权限】已申请");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,7 +351,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
LogUtils.d(TAG, "【按钮点击】像素拾取");
|
LogUtils.d(TAG, "【按钮点击】像素拾取");
|
||||||
String targetImagePath = mBgSourceUtils.getCurrentBackgroundFilePath();
|
String targetImagePath = mBgSourceUtils.getCurrentBackgroundBean().getBackgroundFilePath();
|
||||||
File targetFile = new File(targetImagePath);
|
File targetFile = new File(targetImagePath);
|
||||||
if (targetFile == null || !targetFile.exists() || targetFile.length() <= 0) {
|
if (targetFile == null || !targetFile.exists() || targetFile.length() <= 0) {
|
||||||
ToastUtils.show("无有效图片可拾取像素");
|
ToastUtils.show("无有效图片可拾取像素");
|
||||||
@@ -352,7 +385,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
LogUtils.d(TAG, "【回调触发】requestCode:" + requestCode + ",resultCode:" + resultCode);
|
LogUtils.d(TAG, "【回调触发】requestCode:" + requestCode + ",resultCode:" + resultCode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (requestCode == PermissionUtils.REQUEST_CODE_STORAGE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (requestCode == PermissionUtils.REQUEST_READ_MEDIA_IMAGES && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
handleStoragePermissionCallback();
|
handleStoragePermissionCallback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -393,8 +426,12 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleTakePhotoResult(int resultCode, Intent data) {
|
private void handleTakePhotoResult(int resultCode, Intent data) {
|
||||||
if (resultCode != RESULT_OK || !mfTakePhoto.exists() || mfTakePhoto.length() <= 0) {
|
if (resultCode != RESULT_OK || data == null) {
|
||||||
handleOperationCancelOrFail();
|
handleOperationCancelOrFail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mfTakePhoto.exists() || mfTakePhoto.length() <= 0) {
|
||||||
ToastUtils.show("拍照文件无效");
|
ToastUtils.show("拍照文件无效");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -410,13 +447,13 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
mBgSourceUtils.saveFileToPreviewBean(mfTakePhoto, mfTakePhoto.getAbsolutePath());
|
mBgSourceUtils.saveFileToPreviewBean(mfTakePhoto, mfTakePhoto.getAbsolutePath());
|
||||||
doubleRefreshPreview();
|
doubleRefreshPreview();
|
||||||
|
|
||||||
// 拍照后启动固定比例裁剪(调用工具类)
|
|
||||||
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
|
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
|
||||||
mBgSourceUtils.getPreviewBackgroundBean(),
|
mBgSourceUtils.getPreviewBackgroundBean(),
|
||||||
mBackgroundView.getWidth(),
|
mBackgroundView.getWidth(),
|
||||||
mBackgroundView.getHeight(),
|
mBackgroundView.getHeight(),
|
||||||
false,
|
false,
|
||||||
REQUEST_CROP_IMAGE);
|
REQUEST_CROP_IMAGE
|
||||||
|
);
|
||||||
LogUtils.d(TAG, "【拍照完成】已启动裁剪");
|
LogUtils.d(TAG, "【拍照完成】已启动裁剪");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,9 +504,10 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
ToastUtils.show("图片Uri为空");
|
ToastUtils.show("图片Uri为空");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LogUtils.d(TAG, "【选图回调】Uri : " + selectedImage.toString());
|
LogUtils.d(TAG, "【选图回调】系统返回Uri : " + selectedImage.toString());
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
// 1. 申请持久化读取权限
|
||||||
|
if (Build.VERSION.SDK_INT >= SDK_VERSION_TIRAMISU) {
|
||||||
getContentResolver().takePersistableUriPermission(
|
getContentResolver().takePersistableUriPermission(
|
||||||
selectedImage,
|
selectedImage,
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
@@ -477,15 +515,46 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
LogUtils.d(TAG, "【选图权限】已添加持久化权限");
|
LogUtils.d(TAG, "【选图权限】已添加持久化权限");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 强制拷贝到应用私有目录(彻底规避系统虚拟路径问题)
|
||||||
|
// String privateImagePath = copyUriToPrivateDir(selectedImage);
|
||||||
|
// if (TextUtils.isEmpty(privateImagePath)) {
|
||||||
|
// ToastUtils.show("系统图片路径无效,拷贝失败");
|
||||||
|
// LogUtils.e(TAG, "【选图校验】系统Uri拷贝失败,Uri=" + selectedImage.toString());
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 3. 严格校验私有目录文件有效性
|
||||||
|
// File srcFile = new File(privateImagePath);
|
||||||
|
// if (srcFile == null
|
||||||
|
// || !srcFile.exists()
|
||||||
|
// || !srcFile.isFile()
|
||||||
|
// || srcFile.length() <= 0) {
|
||||||
|
// ToastUtils.show("系统图片文件无效");
|
||||||
|
// LogUtils.e(TAG, "【选图校验】私有目录文件无效,路径=" + privateImagePath);
|
||||||
|
// // 清理无效文件
|
||||||
|
// if (srcFile != null && srcFile.exists()) {
|
||||||
|
// srcFile.delete();
|
||||||
|
// }
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
//LogUtils.d(TAG, "【选图校验】系统图片校验通过,私有路径:" + privateImagePath);
|
||||||
|
|
||||||
|
// 4. 同步到预览 Bean 并启动裁剪
|
||||||
|
if (putUriFileToPreviewSource(selectedImage)) {
|
||||||
LogUtils.d(TAG, "【选图同步】路径绑定完成");
|
LogUtils.d(TAG, "【选图同步】路径绑定完成");
|
||||||
// 选图后启动固定比例裁剪(调用工具类)
|
mBitmapCache.removeCachedBitmap(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath());
|
||||||
putUriFileToPreviewSource(selectedImage);
|
|
||||||
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
|
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
|
||||||
mBgSourceUtils.getPreviewBackgroundBean(),
|
mBgSourceUtils.getPreviewBackgroundBean(),
|
||||||
mBackgroundView.getWidth(),
|
mBackgroundView.getWidth(),
|
||||||
mBackgroundView.getHeight(),
|
mBackgroundView.getHeight(),
|
||||||
false,
|
false,
|
||||||
REQUEST_CROP_IMAGE);
|
REQUEST_CROP_IMAGE
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ToastUtils.show("图片同步失败");
|
||||||
|
LogUtils.e(TAG, "【选图同步】文件复制失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean putUriFileToPreviewSource(Uri srcUriFile) {
|
boolean putUriFileToPreviewSource(Uri srcUriFile) {
|
||||||
@@ -494,6 +563,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean putUriFileToPreviewSource(File srcFile) {
|
boolean putUriFileToPreviewSource(File srcFile) {
|
||||||
|
LogUtils.d(TAG, String.format("putUriFileToPreviewSource(File srcFile) srcFile %s", srcFile));
|
||||||
mBgSourceUtils.loadSettings();
|
mBgSourceUtils.loadSettings();
|
||||||
File dstFile = new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath());
|
File dstFile = new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath());
|
||||||
return FileUtils.copyFile(srcFile, dstFile);
|
return FileUtils.copyFile(srcFile, dstFile);
|
||||||
@@ -531,7 +601,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 延迟300ms调用双重刷新,确保文件写入完成
|
|
||||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -685,20 +754,32 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
|
|
||||||
private void doubleRefreshPreview() {
|
private void doubleRefreshPreview() {
|
||||||
LogUtils.d(TAG, "【双重刷新】开始");
|
LogUtils.d(TAG, "【双重刷新】开始");
|
||||||
// 1. 清空缓存工具类的旧数据
|
// 清空缓存工具类的旧数据
|
||||||
if (mBgSourceUtils != null) {
|
if (mBgSourceUtils != null) {
|
||||||
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||||
if (previewBean != null && previewBean.isUseBackgroundFile()) {
|
if (previewBean != null && previewBean.isUseBackgroundFile()) {
|
||||||
String bgPath = previewBean.isUseBackgroundScaledCompressFile()
|
String bgPath = previewBean.isUseBackgroundScaledCompressFile()
|
||||||
? previewBean.getBackgroundScaledCompressFilePath()
|
? previewBean.getBackgroundScaledCompressFilePath()
|
||||||
: previewBean.getBackgroundFilePath();
|
: previewBean.getBackgroundFilePath();
|
||||||
// 强制清除缓存
|
mBitmapCache.removeCachedBitmap(bgPath);
|
||||||
App._mBitmapCacheUtils.removeCachedBitmap(bgPath);
|
|
||||||
LogUtils.d(TAG, "【双重刷新】已清空工具类缓存:" + bgPath);
|
LogUtils.d(TAG, "【双重刷新】已清空工具类缓存:" + bgPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 重新加载最新数据
|
// 清空 BackgroundView 自身的 Bitmap 引用
|
||||||
|
// if (mBackgroundView != null) {
|
||||||
|
// if (mBackgroundView instanceof ImageView) {
|
||||||
|
// ((ImageView) mBackgroundView).setImageBitmap(null);
|
||||||
|
// }
|
||||||
|
// Drawable drawable = mBackgroundView.getBackground();
|
||||||
|
// if (drawable != null) {
|
||||||
|
// drawable.setCallback(null);
|
||||||
|
// mBackgroundView.setBackground(null);
|
||||||
|
// }
|
||||||
|
// LogUtils.d(TAG, "【双重刷新】已清空控件 Bitmap 引用");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 重新加载最新数据
|
||||||
if (mBackgroundView != null && !isFinishing()) {
|
if (mBackgroundView != null && !isFinishing()) {
|
||||||
mBgSourceUtils.loadSettings();
|
mBgSourceUtils.loadSettings();
|
||||||
mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean());
|
mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean());
|
||||||
@@ -708,7 +789,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 延长延迟到200ms,确保文件完全加载
|
// 延长延迟到200ms,确保文件完全加载
|
||||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -724,7 +805,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
private void handleOperationCancelOrFail() {
|
private void handleOperationCancelOrFail() {
|
||||||
mBgSourceUtils.setCurrentSourceToPreview();
|
mBgSourceUtils.setCurrentSourceToPreview();
|
||||||
LogUtils.d(TAG, "【操作回调】取消或失败");
|
LogUtils.d(TAG, "【操作回调】取消或失败");
|
||||||
ToastUtils.show("操作取消或失败");
|
ToastUtils.show("【操作回调】取消或失败");
|
||||||
doubleRefreshPreview();
|
doubleRefreshPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,30 +833,92 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
@Override
|
@Override
|
||||||
public void finish() {
|
public void finish() {
|
||||||
if (isCommitSettings) {
|
if (isCommitSettings) {
|
||||||
|
setResult(RESULT_OK);
|
||||||
super.finish();
|
super.finish();
|
||||||
} else {
|
} else {
|
||||||
// 如果预览背景改变过就提示是否更换背景
|
|
||||||
if (isPreviewBackgroundChanged) {
|
if (isPreviewBackgroundChanged) {
|
||||||
YesNoAlertDialog.show(this, "背景更换问题", "是否确定背景图片设置?", new YesNoAlertDialog.OnDialogResultListener() {
|
YesNoAlertDialog.show(this, "背景更换问题", "是否确定背景图片设置?", new YesNoAlertDialog.OnDialogResultListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onYes() {
|
public void onYes() {
|
||||||
mBgSourceUtils.commitPreviewSourceToCurrent();
|
mBgSourceUtils.commitPreviewSourceToCurrent();
|
||||||
isCommitSettings = true;
|
isCommitSettings = true;
|
||||||
|
setResult(RESULT_OK);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNo() {
|
public void onNo() {
|
||||||
isCommitSettings = true;
|
isCommitSettings = true;
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 如果预览背景未改变就直接退出
|
setResult(RESULT_OK);
|
||||||
isCommitSettings = true;
|
isCommitSettings = true;
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析Uri到真实文件路径,兼容所有Android版本
|
||||||
|
* @param uri 图片的Content Uri
|
||||||
|
* @return 真实文件路径 / null
|
||||||
|
*/
|
||||||
|
// private String getRealPathFromUri(Uri uri) {
|
||||||
|
// String path = null;
|
||||||
|
// String[] projection = {MediaStore.Images.Media.DATA};
|
||||||
|
// Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
|
||||||
|
// if (cursor != null) {
|
||||||
|
// if (cursor.moveToFirst()) {
|
||||||
|
// int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||||
|
// path = cursor.getString(columnIndex);
|
||||||
|
// }
|
||||||
|
// cursor.close();
|
||||||
|
// }
|
||||||
|
// // 针对Android 10+沙箱限制,拷贝到私有目录
|
||||||
|
// if (TextUtils.isEmpty(path)) {
|
||||||
|
//
|
||||||
|
// path = copyUriToPrivateDir(uri);
|
||||||
|
// }
|
||||||
|
// return path;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Uri对应的图片拷贝到应用私有临时目录
|
||||||
|
* @param uri 图片Uri
|
||||||
|
* @return 拷贝后的文件路径 / null
|
||||||
|
*/
|
||||||
|
// private String copyUriToPrivateDir(Uri uri) {
|
||||||
|
// try {
|
||||||
|
// InputStream inputStream = getContentResolver().openInputStream(uri);
|
||||||
|
// if (inputStream == null) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// File tempDir = new File(App.getTempDirPath());
|
||||||
|
// if (!tempDir.exists()) {
|
||||||
|
// tempDir.mkdirs();
|
||||||
|
// }
|
||||||
|
// String fileName = "select_temp_" + System.currentTimeMillis() + ".jpg";
|
||||||
|
// File destFile = new File(tempDir, fileName);
|
||||||
|
//
|
||||||
|
// OutputStream outputStream = new FileOutputStream(destFile);
|
||||||
|
// byte[] buffer = new byte[1024 * 4];
|
||||||
|
// int len;
|
||||||
|
// while ((len = inputStream.read(buffer)) != -1) {
|
||||||
|
// outputStream.write(buffer, 0, len);
|
||||||
|
// }
|
||||||
|
// outputStream.flush();
|
||||||
|
// inputStream.close();
|
||||||
|
// outputStream.close();
|
||||||
|
//
|
||||||
|
// LogUtils.d(TAG, "【选图拷贝】文件已拷贝到私有目录:" + destFile.getAbsolutePath());
|
||||||
|
// return destFile.getAbsolutePath();
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// LogUtils.e(TAG, "【选图拷贝】异常:" + e.getMessage());
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import cc.winboll.studio.powerbell.utils.PermissionUtils;
|
|||||||
public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
||||||
|
|
||||||
public static final String TAG = "SettingsActivity";
|
public static final String TAG = "SettingsActivity";
|
||||||
|
private static final int REQUEST_READ_MEDIA_IMAGES = 1001;
|
||||||
|
|
||||||
private Toolbar mToolbar;
|
private Toolbar mToolbar;
|
||||||
|
|
||||||
@@ -52,10 +53,15 @@ public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivit
|
|||||||
|
|
||||||
public void onCheckPermission(View view) {
|
public void onCheckPermission(View view) {
|
||||||
//ToastUtils.show("onCheckPermission");
|
//ToastUtils.show("onCheckPermission");
|
||||||
if (PermissionUtils.getInstance().hasFullStoragePermission()) {
|
PermissionUtils.getInstance().checkAndRequestMediaImagesPermission(this, REQUEST_READ_MEDIA_IMAGES);
|
||||||
ToastUtils.show("【权限检查】存储权限已全部获取");
|
}
|
||||||
} else {
|
|
||||||
PermissionUtils.getInstance().requestFullStoragePermission(this);
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
if (requestCode == REQUEST_READ_MEDIA_IMAGES) {
|
||||||
|
PermissionUtils.getInstance().handleStoragePermissionResult(this, requestCode, permissions, grantResults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,15 @@ import android.content.Context;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.media.ExifInterface;
|
import android.media.ExifInterface;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
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 android.util.Log;
|
||||||
|
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;
|
||||||
@@ -19,8 +22,6 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import android.os.Build;
|
|
||||||
import androidx.core.content.FileProvider;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
@@ -248,29 +249,36 @@ public class BackgroundSourceUtils {
|
|||||||
/*
|
/*
|
||||||
* 创建预览数据剪裁环境
|
* 创建预览数据剪裁环境
|
||||||
*/
|
*/
|
||||||
public boolean createAndUpdatePreviewEnvironmentForCropping(BackgroundBean oldPreviewBackgroundBean) {
|
public boolean createAndUpdatePreviewEnvironmentForCropping(boolean isCreateNewPreviewBean, BackgroundBean previewBackgroundBean) {
|
||||||
InputStream is = null;
|
InputStream is = null;
|
||||||
FileOutputStream fos = null;
|
FileOutputStream fos = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
clearCropTempFiles();
|
clearCropTempFiles();
|
||||||
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 newCropFileName = UUID.randomUUID().toString() + System.currentTimeMillis();
|
String newCropFileName = UUID.randomUUID().toString() + System.currentTimeMillis();
|
||||||
|
String fileSuffix;
|
||||||
|
|
||||||
|
if (isCreateNewPreviewBean) {
|
||||||
|
fileSuffix = "png";
|
||||||
|
mCropSourceFile = new File(fCropCacheDir, newCropFileName + "." + fileSuffix);
|
||||||
|
mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + "." + fileSuffix);
|
||||||
|
DrawableToFileUtils.saveDrawableToFile(mContext, R.drawable.blank10x10, mCropSourceFile.getAbsolutePath());
|
||||||
|
mCropResultFile.createNewFile();
|
||||||
|
} else {
|
||||||
|
if (FileUtils.isFileExists(previewBackgroundBean.getBackgroundFilePath())) {
|
||||||
|
File fSourceFileOld = new File(previewBackgroundBean.getBackgroundFilePath());
|
||||||
|
Uri uri = UriUtil.getUriForFile(mContext, fSourceFileOld);
|
||||||
|
fileSuffix = FileUtils.getFileSuffix(mContext, uri);
|
||||||
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);
|
||||||
|
|
||||||
if (FileUtils.isFileExists(oldPreviewBackgroundBean.getBackgroundScaledCompressFilePath())) {
|
FileUtils.copyFile(new File(previewBackgroundBean.getBackgroundFilePath()), mCropSourceFile);
|
||||||
FileUtils.copyFile(new File(oldPreviewBackgroundBean.getBackgroundScaledCompressFilePath()), mCropResultFile);
|
if (FileUtils.isFileExists(previewBackgroundBean.getBackgroundScaledCompressFilePath())) {
|
||||||
|
FileUtils.copyFile(new File(previewBackgroundBean.getBackgroundScaledCompressFilePath()), mCropResultFile);
|
||||||
} else {
|
} else {
|
||||||
mCropResultFile.createNewFile();
|
mCropResultFile.createNewFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FileUtils.isFileExists(oldPreviewBackgroundBean.getBackgroundFilePath())) {
|
|
||||||
FileUtils.copyFile(new File(oldPreviewBackgroundBean.getBackgroundFilePath()), mCropSourceFile);
|
|
||||||
} else {
|
|
||||||
mCropSourceFile.createNewFile();
|
mCropSourceFile.createNewFile();
|
||||||
// 1. 打开Uri输入流(兼容content:///file:// 等多种Uri格式)
|
// 1. 打开Uri输入流(兼容content:///file:// 等多种Uri格式)
|
||||||
is = mContext.getContentResolver().openInputStream(uri);
|
is = mContext.getContentResolver().openInputStream(uri);
|
||||||
@@ -299,6 +307,14 @@ public class BackgroundSourceUtils {
|
|||||||
fos.flush();
|
fos.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 解析成功日志(打印文件信息,便于问题排查)
|
||||||
|
LogUtils.d(TAG, "【选图解析】Uri解析成功!");
|
||||||
|
LogUtils.d(TAG, "→ 原Uri:" + uri.toString());
|
||||||
|
} else {
|
||||||
|
return createAndUpdatePreviewEnvironmentForCropping(true, previewBackgroundBean);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载图片数据模型数据
|
// 加载图片数据模型数据
|
||||||
@@ -312,13 +328,12 @@ public class BackgroundSourceUtils {
|
|||||||
// 保存数据模型数据更改
|
// 保存数据模型数据更改
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|
||||||
// 6. 解析成功日志(打印文件信息,便于问题排查)
|
// 解析成功日志(打印文件信息,便于问题排查)
|
||||||
LogUtils.d(TAG, "【选图解析】Uri解析成功!");
|
|
||||||
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, "→ 图片剪裁数据源文件大小:" + 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 true;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -380,30 +395,30 @@ public class BackgroundSourceUtils {
|
|||||||
/**
|
/**
|
||||||
* 获取正式背景图片路径(修复:移除每次 loadSettings(),避免 Bean 被覆盖;强化非空校验)
|
* 获取正式背景图片路径(修复:移除每次 loadSettings(),避免 Bean 被覆盖;强化非空校验)
|
||||||
*/
|
*/
|
||||||
public String getCurrentBackgroundFilePath() {
|
// public String getCurrentBackgroundFilePath() {
|
||||||
String fileName = currentBackgroundBean.getBackgroundFileName();
|
// String fileName = currentBackgroundBean.getBackgroundFileName();
|
||||||
if (TextUtils.isEmpty(fileName)) {
|
// if (TextUtils.isEmpty(fileName)) {
|
||||||
LogUtils.e(TAG, "【路径管理】正式背景文件名为空,返回空路径");
|
// LogUtils.e(TAG, "【路径管理】正式背景文件名为空,返回空路径");
|
||||||
return "";
|
// return "";
|
||||||
}
|
// }
|
||||||
File file = new File(fBackgroundSourceDir, fileName);
|
// File file = new File(fBackgroundSourceDir, fileName);
|
||||||
LogUtils.d(TAG, "【路径管理】正式背景路径:" + file.getAbsolutePath());
|
// LogUtils.d(TAG, "【路径管理】正式背景路径:" + file.getAbsolutePath());
|
||||||
return file.getAbsolutePath();
|
// return file.getAbsolutePath();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 获取预览背景图片路径(修复:移除每次 loadSettings(),避免 Bean 被覆盖;强化非空校验)
|
// * 获取预览背景图片路径(修复:移除每次 loadSettings(),避免 Bean 被覆盖;强化非空校验)
|
||||||
*/
|
// */
|
||||||
public String getPreviewBackgroundFilePath() {
|
// public String getPreviewBackgroundFilePath() {
|
||||||
String fileName = previewBackgroundBean.getBackgroundFileName();
|
// String fileName = previewBackgroundBean.getBackgroundFileName();
|
||||||
if (TextUtils.isEmpty(fileName)) {
|
// if (TextUtils.isEmpty(fileName)) {
|
||||||
LogUtils.e(TAG, "【路径管理】预览背景文件名为空,返回空路径");
|
// LogUtils.e(TAG, "【路径管理】预览背景文件名为空,返回空路径");
|
||||||
return "";
|
// return "";
|
||||||
}
|
// }
|
||||||
File file = new File(fBackgroundSourceDir, fileName);
|
// File file = new File(fBackgroundSourceDir, fileName);
|
||||||
LogUtils.d(TAG, "【路径管理】预览背景路径:" + file.getAbsolutePath());
|
// LogUtils.d(TAG, "【路径管理】预览背景路径:" + file.getAbsolutePath());
|
||||||
return file.getAbsolutePath();
|
// return file.getAbsolutePath();
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取预览背景压缩图片路径(同步修复:移除 loadSettings(),强化非空校验,统一指向BackgroundCrops目录)
|
* 获取预览背景压缩图片路径(同步修复:移除 loadSettings(),强化非空校验,统一指向BackgroundCrops目录)
|
||||||
|
|||||||
@@ -1,113 +1,134 @@
|
|||||||
package cc.winboll.studio.powerbell.utils;
|
package cc.winboll.studio.powerbell.utils;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
|
||||||
import android.provider.Settings;
|
import androidx.core.app.ActivityCompat;
|
||||||
import android.widget.Toast;
|
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 存储权限工具类(适配 API 30+)
|
* 权限申请工具类
|
||||||
* 核心支持:1. 传统读写权限 2. MANAGE_EXTERNAL_STORAGE 全文件权限
|
* 适配 Android 13+ 媒体权限 & 低版本存储权限
|
||||||
|
* 兼容 SDK 版本低于 33 的编译环境
|
||||||
*/
|
*/
|
||||||
public class PermissionUtils {
|
public class PermissionUtils {
|
||||||
|
private static final String TAG = "PermissionUtils";
|
||||||
|
// 存储权限请求码
|
||||||
|
public static final int REQUEST_STORAGE = 1000;
|
||||||
|
// 媒体图片权限请求码(Android 13+)
|
||||||
|
public static final int REQUEST_READ_MEDIA_IMAGES = 1001;
|
||||||
|
|
||||||
|
// 手动定义 Android 13+ 媒体图片权限常量(解决 SDK 低于 33 无法识别问题)
|
||||||
|
private static final String READ_MEDIA_IMAGES = "android.permission.READ_MEDIA_IMAGES";
|
||||||
|
// Android 13 对应的 SDK 版本号(替代 Build.VERSION_CODES.TIRAMISU)
|
||||||
|
private static final int SDK_VERSION_TIRAMISU = 33;
|
||||||
|
|
||||||
|
// 单例模式
|
||||||
private static volatile PermissionUtils sInstance;
|
private static volatile PermissionUtils sInstance;
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
// 权限请求码
|
private PermissionUtils() {}
|
||||||
public static final int REQUEST_CODE_STORAGE = 1001;
|
|
||||||
public static final int REQUEST_CODE_MANAGE_STORAGE = 1002;
|
|
||||||
|
|
||||||
private PermissionUtils(Context context) {
|
|
||||||
this.mContext = context.getApplicationContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PermissionUtils getInstance() {
|
public static PermissionUtils getInstance() {
|
||||||
if (sInstance == null) {
|
if (sInstance == null) {
|
||||||
throw new IllegalStateException("请先调用 init(Context) 初始化");
|
synchronized (PermissionUtils.class) {
|
||||||
|
if (sInstance == null) {
|
||||||
|
sInstance = new PermissionUtils();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return sInstance;
|
return sInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init(Context context) {
|
|
||||||
if (sInstance == null) {
|
|
||||||
synchronized (PermissionUtils.class) {
|
|
||||||
if (sInstance == null) {
|
|
||||||
sInstance = new PermissionUtils(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查是否拥有完整的文件管理权限
|
* 检查并请求 存储权限(Android 12及以下)
|
||||||
*/
|
*/
|
||||||
public boolean hasFullStoragePermission() {
|
public boolean checkAndRequestStoragePermission(Activity activity) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
// API 30+ 需判断 MANAGE_EXTERNAL_STORAGE 权限
|
// Android 11+ 无需申请 READ_EXTERNAL_STORAGE,直接返回true
|
||||||
return Environment.isExternalStorageManager();
|
return true;
|
||||||
} else {
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
// 低版本判断传统读写权限
|
String[] permissions = {
|
||||||
int read = ContextCompat.checkSelfPermission(mContext, android.Manifest.permission.READ_EXTERNAL_STORAGE);
|
|
||||||
int write = ContextCompat.checkSelfPermission(mContext, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
|
||||||
return read == PackageManager.PERMISSION_GRANTED && write == PackageManager.PERMISSION_GRANTED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 申请文件管理权限(自动适配系统版本)
|
|
||||||
* @param activity 发起申请的 Activity
|
|
||||||
*/
|
|
||||||
public void requestFullStoragePermission(android.app.Activity activity) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
// API 30+ 跳转系统设置页开启权限
|
|
||||||
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
|
|
||||||
Uri uri = Uri.fromParts("package", mContext.getPackageName(), null);
|
|
||||||
intent.setData(uri);
|
|
||||||
activity.startActivityForResult(intent, REQUEST_CODE_MANAGE_STORAGE);
|
|
||||||
} else {
|
|
||||||
// 低版本申请传统读写权限
|
|
||||||
androidx.core.app.ActivityCompat.requestPermissions(
|
|
||||||
activity,
|
|
||||||
new String[]{
|
|
||||||
android.Manifest.permission.READ_EXTERNAL_STORAGE,
|
android.Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||||
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
},
|
};
|
||||||
REQUEST_CODE_STORAGE
|
if (ContextCompat.checkSelfPermission(activity, permissions[0]) != PackageManager.PERMISSION_GRANTED
|
||||||
);
|
|| ContextCompat.checkSelfPermission(activity, permissions[1]) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
ActivityCompat.requestPermissions(activity, permissions, REQUEST_STORAGE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查并请求 媒体图片权限(Android 13+)
|
||||||
|
* 兼容 SDK 编译版本低于 33 的情况
|
||||||
|
*/
|
||||||
|
public boolean checkAndRequestMediaImagesPermission(Activity activity, int requestCode) {
|
||||||
|
// 用数值 33 替代 Build.VERSION_CODES.TIRAMISU
|
||||||
|
if (Build.VERSION.SDK_INT >= SDK_VERSION_TIRAMISU) {
|
||||||
|
// 用手动定义的权限常量替代 android.Manifest.permission.READ_MEDIA_IMAGES
|
||||||
|
if (ContextCompat.checkSelfPermission(activity, READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
ActivityCompat.requestPermissions(activity, new String[]{READ_MEDIA_IMAGES}, requestCode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 低版本已通过存储权限覆盖,直接返回true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限请求结果处理
|
||||||
|
*/
|
||||||
|
public void handleStoragePermissionResult(Activity activity, int requestCode, String[] permissions, int[] grantResults) {
|
||||||
|
switch (requestCode) {
|
||||||
|
case REQUEST_STORAGE:
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
LogUtils.d(TAG, "存储权限申请成功");
|
||||||
|
} else {
|
||||||
|
LogUtils.e(TAG, "存储权限申请失败");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case REQUEST_READ_MEDIA_IMAGES:
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
LogUtils.d(TAG, "媒体图片权限申请成功");
|
||||||
|
} else {
|
||||||
|
LogUtils.e(TAG, "媒体图片权限申请失败");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LogUtils.d(TAG, "未知权限请求码:" + requestCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理低版本权限申请结果(在 Activity 的 onRequestPermissionsResult 中调用)
|
* 检查是否有管理所有文件权限(Android 11+)
|
||||||
* @param activity 上下文
|
|
||||||
* @param requestCode 请求码
|
|
||||||
* @param permissions 权限数组
|
|
||||||
* @param grantResults 授权结果
|
|
||||||
* @return 权限是否申请成功
|
|
||||||
*/
|
*/
|
||||||
public boolean handleStoragePermissionResult(android.app.Activity activity, int requestCode, String[] permissions, int[] grantResults) {
|
public boolean checkManageExternalStoragePermission(Activity activity) {
|
||||||
// 仅处理传统存储权限的请求结果
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
if (requestCode != REQUEST_CODE_STORAGE) {
|
return android.os.Environment.isExternalStorageManager();
|
||||||
return false;
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
// 校验授权结果:读写权限均需授予
|
|
||||||
boolean isGranted = grantResults.length > 0
|
|
||||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
|
|
||||||
&& grantResults[1] == PackageManager.PERMISSION_GRANTED;
|
|
||||||
|
|
||||||
if (isGranted) {
|
/**
|
||||||
// 权限授予成功,提示用户
|
* 请求管理所有文件权限(Android 11+)
|
||||||
Toast.makeText(activity, "存储权限申请成功", Toast.LENGTH_SHORT).show();
|
*/
|
||||||
} else {
|
public void requestManageExternalStoragePermission(Activity activity, int requestCode) {
|
||||||
// 权限被拒绝,引导用户手动开启
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
Toast.makeText(activity, "存储权限被拒绝,部分功能无法使用", Toast.LENGTH_SHORT).show();
|
try {
|
||||||
|
android.content.Intent intent = new android.content.Intent(
|
||||||
|
android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
|
||||||
|
);
|
||||||
|
intent.setData(android.net.Uri.parse("package:" + activity.getPackageName()));
|
||||||
|
activity.startActivityForResult(intent, requestCode);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.e(TAG, "请求管理文件权限异常:" + e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return isGranted;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user