应用权限框架更新,重新调试剪裁文件流程。

This commit is contained in:
2025-12-11 09:02:41 +08:00
parent ecafd2026f
commit 220aa9dbfb
7 changed files with 532 additions and 342 deletions

View File

@@ -1,8 +1,8 @@
#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
libraryProject=
baseVersion=15.12
publishVersion=15.12.14
buildCount=5
buildCount=13
baseBetaVersion=15.12.15

View File

@@ -44,7 +44,6 @@ public class App extends GlobalApplication {
public void onCreate() {
super.onCreate();
setIsDebugging(BuildConfig.DEBUG);
PermissionUtils.init(this);
// 初始化活动窗口管理
WinBoLLActivityManager.init(this);

View File

@@ -58,7 +58,7 @@ public class MainActivity extends WinBoLLActivity {
// ======================== 静态常量(移除缓存相关键)========================
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_CURRENTVALUEBATTERY = 1;
public static final int MSG_LOAD_BACKGROUND = 2;
@@ -123,10 +123,8 @@ public class MainActivity extends WinBoLLActivity {
loadNonCriticalViewDelayed();
// 权限申请
if (!PermissionUtils.getInstance().hasFullStoragePermission()) {
PermissionUtils.getInstance().requestFullStoragePermission(this);
}
}
PermissionUtils.getInstance().checkAndRequestMediaImagesPermission(this, REQUEST_READ_MEDIA_IMAGES);
}
// 移除 onSaveInstanceState 方法
// 移除 onRestoreInstanceState 方法
@@ -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
public void onBackPressed() {
moveTaskToBack(true);

View File

@@ -3,6 +3,7 @@ package cc.winboll.studio.powerbell.activities;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
@@ -14,11 +15,24 @@ import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
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.ToastUtils;
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.model.BackgroundBean;
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.ImageCropUtils;
import cc.winboll.studio.powerbell.utils.PermissionUtils;
import cc.winboll.studio.powerbell.utils.UriUtil;
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 static final String TAG = "BackgroundSettingsActivity";
private BackgroundSourceUtils mBgSourceUtils;
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_TAKE_PHOTO = 1;
public static final int REQUEST_CROP_IMAGE = 2;
private static final int REQUEST_READ_MEDIA = 1001;
private Toolbar mToolbar;
private BackgroundView mBackgroundView;
@@ -73,6 +88,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
mBgSourceUtils = BackgroundSourceUtils.getInstance(this);
mBgSourceUtils.loadSettings();
mPermissionUtils = PermissionUtils.getInstance();
mBitmapCache = BitmapCacheUtils.getInstance();
File tempDir = new File(App.getTempDirPath());
if (!tempDir.exists()) {
@@ -95,9 +111,10 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
mBgSourceUtils.setCurrentSourceToPreview();
}
Uri uriSelectedImage = UriUtil.getUriForFile(this, new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath()));
// 创建预览数据剪裁环境
mBgSourceUtils.createAndUpdatePreviewEnvironmentForCropping(mBgSourceUtils.getPreviewBackgroundBean());
//Uri uriSelectedImage = UriUtil.getUriForFile(this, new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath()));
File fTestSourceExist = new File(mBgSourceUtils.getCurrentBackgroundBean().getBackgroundFilePath());
boolean isCreateNewPreviewBean = !fTestSourceExist.exists();
mBgSourceUtils.createAndUpdatePreviewEnvironmentForCropping(isCreateNewPreviewBean, mBgSourceUtils.getPreviewBackgroundBean());
doubleRefreshPreview();
LogUtils.d(TAG, "【初始化】BackgroundSettingsActivity 初始化完成");
}
@@ -109,18 +126,18 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
}
private void initToolbar() {
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar = findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
mToolbar.setSubtitle(getTag());
mToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d(TAG, "【导航栏】点击返回");
finish();
}
});
@Override
public void onClick(View v) {
LogUtils.d(TAG, "【导航栏】点击返回");
finish();
}
});
}
private void initClickListeners() {
@@ -151,10 +168,10 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
boolean isImageType(String lowerMimeType) {
return lowerMimeType.equals("image/jpeg")
|| lowerMimeType.equals("image/png")
|| lowerMimeType.equals("image/tiff")
|| lowerMimeType.equals("image/jpg")
|| lowerMimeType.equals("image/svg+xml");
|| lowerMimeType.equals("image/png")
|| lowerMimeType.equals("image/tiff")
|| lowerMimeType.equals("image/jpg")
|| lowerMimeType.equals("image/svg+xml");
}
@Override
@@ -173,81 +190,98 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
}
};
// ====================== 核心修改Java 7 兼容的相册选择点击事件 ======================
private View.OnClickListener onSelectPictureClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d(TAG, "【按钮点击】选择图片");
// 适配 API 30+ 权限逻辑
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// API 30+ 优先判断全文件管理权限
if (!Environment.isExternalStorageManager()) {
mPermissionUtils.requestFullStoragePermission(BackgroundSettingsActivity.this);
return;
}
} else {
// 低版本判断传统读写权限
if (!mPermissionUtils.hasFullStoragePermission()) {
mPermissionUtils.requestFullStoragePermission(BackgroundSettingsActivity.this);
return;
}
}
@Override
public void onClick(View v) {
LogUtils.d(TAG, "【按钮点击】选择图片");
// 适配Android 13+权限(已替换为手动定义的常量)
if (Build.VERSION.SDK_INT >= SDK_VERSION_TIRAMISU) {
if (mPermissionUtils.checkAndRequestMediaImagesPermission(BackgroundSettingsActivity.this, REQUEST_READ_MEDIA)) {
launchImageSelector();
}
} else {
if (mPermissionUtils.checkAndRequestStoragePermission(BackgroundSettingsActivity.this)) {
launchImageSelector();
}
}
}
};
// API 30+ 推荐的标准化相册选择 Intent
Intent pickIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
pickIntent.setType("image/*");
// 添加权限 Flag确保 Uri 可读取
pickIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 持久化 Uri 权限,避免后续操作失效
pickIntent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
}
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;
// 构建选择器,统一用户交互
Intent chooser = Intent.createChooser(pickIntent, "选择图片");
if (chooser.resolveActivity(getPackageManager()) != null) {
startActivityForResult(chooser, REQUEST_SELECT_PICTURE);
LogUtils.d(TAG, "【选图意图】启动图片选择");
} else {
LogUtils.d(TAG, "【选图意图】无相册应用");
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();
}
});
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;
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 (int i = 0; i < intents.length; i++) {
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);
LogUtils.d(TAG, "【选图意图】启动图片选择");
} else {
LogUtils.d(TAG, "【选图意图】无相册应用");
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();
}
});
}
}
private View.OnClickListener onCropPictureClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d(TAG, "【按钮点击】固定比例裁剪");
// 调用裁剪工具类:传入上下文、预览图、固定比例(按视图宽高)、请求码
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
mBgSourceUtils.getPreviewBackgroundBean(),
mBackgroundView.getWidth(),
mBackgroundView.getHeight(),
false,
REQUEST_CROP_IMAGE);
mBgSourceUtils.getPreviewBackgroundBean(),
mBackgroundView.getWidth(),
mBackgroundView.getHeight(),
false,
REQUEST_CROP_IMAGE
);
}
};
@@ -255,13 +289,13 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
@Override
public void onClick(View v) {
LogUtils.d(TAG, "【按钮点击】自由裁剪");
// 调用裁剪工具类传入上下文、预览图、自由裁剪比例参数传0、请求码
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
mBgSourceUtils.getPreviewBackgroundBean(),
0,
0,
true,
REQUEST_CROP_IMAGE);
mBgSourceUtils.getPreviewBackgroundBean(),
0,
0,
true,
REQUEST_CROP_IMAGE
);
}
};
@@ -286,7 +320,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
return;
}
if (mPermissionUtils.hasFullStoragePermission()) {
if (mPermissionUtils.checkAndRequestStoragePermission(BackgroundSettingsActivity.this)) {
LogUtils.d(TAG, "【拍照权限】已获取");
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
try {
@@ -300,7 +334,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
LogUtils.e(TAG, "【拍照失败】");
}
} else {
mPermissionUtils.requestFullStoragePermission(BackgroundSettingsActivity.this);
LogUtils.d(TAG, "【拍照权限】已申请");
}
}
@@ -318,7 +351,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
@Override
public void onClick(View v) {
LogUtils.d(TAG, "【按钮点击】像素拾取");
String targetImagePath = mBgSourceUtils.getCurrentBackgroundFilePath();
String targetImagePath = mBgSourceUtils.getCurrentBackgroundBean().getBackgroundFilePath();
File targetFile = new File(targetImagePath);
if (targetFile == null || !targetFile.exists() || targetFile.length() <= 0) {
ToastUtils.show("无有效图片可拾取像素");
@@ -352,7 +385,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
LogUtils.d(TAG, "【回调触发】requestCode" + requestCode + "resultCode" + resultCode);
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();
return;
}
@@ -393,8 +426,12 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
}
private void handleTakePhotoResult(int resultCode, Intent data) {
if (resultCode != RESULT_OK || !mfTakePhoto.exists() || mfTakePhoto.length() <= 0) {
if (resultCode != RESULT_OK || data == null) {
handleOperationCancelOrFail();
return;
}
if (!mfTakePhoto.exists() || mfTakePhoto.length() <= 0) {
ToastUtils.show("拍照文件无效");
return;
}
@@ -410,13 +447,13 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
mBgSourceUtils.saveFileToPreviewBean(mfTakePhoto, mfTakePhoto.getAbsolutePath());
doubleRefreshPreview();
// 拍照后启动固定比例裁剪(调用工具类)
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
mBgSourceUtils.getPreviewBackgroundBean(),
mBackgroundView.getWidth(),
mBackgroundView.getHeight(),
false,
REQUEST_CROP_IMAGE);
mBgSourceUtils.getPreviewBackgroundBean(),
mBackgroundView.getWidth(),
mBackgroundView.getHeight(),
false,
REQUEST_CROP_IMAGE
);
LogUtils.d(TAG, "【拍照完成】已启动裁剪");
}
@@ -457,36 +494,68 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
}
private void handleSelectPictureResult(int resultCode, Intent data) {
if (resultCode != RESULT_OK || data == null) {
handleOperationCancelOrFail();
return;
}
if (resultCode != RESULT_OK || data == null) {
handleOperationCancelOrFail();
return;
}
Uri selectedImage = data.getData();
if (selectedImage == null) {
ToastUtils.show("图片Uri为空");
return;
}
LogUtils.d(TAG, "【选图回调】Uri : " + selectedImage.toString());
Uri selectedImage = data.getData();
if (selectedImage == null) {
ToastUtils.show("图片Uri为空");
return;
}
LogUtils.d(TAG, "【选图回调】系统返回Uri : " + selectedImage.toString());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getContentResolver().takePersistableUriPermission(
selectedImage,
Intent.FLAG_GRANT_READ_URI_PERMISSION
);
LogUtils.d(TAG, "【选图权限】已添加持久化权限");
}
// 1. 申请持久化读取权限
if (Build.VERSION.SDK_INT >= SDK_VERSION_TIRAMISU) {
getContentResolver().takePersistableUriPermission(
selectedImage,
Intent.FLAG_GRANT_READ_URI_PERMISSION
);
LogUtils.d(TAG, "【选图权限】已添加持久化权限");
}
LogUtils.d(TAG, "【选图同步】路径绑定完成");
// 选图后启动固定比例裁剪(调用工具类)
putUriFileToPreviewSource(selectedImage);
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
mBgSourceUtils.getPreviewBackgroundBean(),
mBackgroundView.getWidth(),
mBackgroundView.getHeight(),
false,
REQUEST_CROP_IMAGE);
}
// 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, "【选图同步】路径绑定完成");
mBitmapCache.removeCachedBitmap(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath());
ImageCropUtils.startImageCrop(BackgroundSettingsActivity.this,
mBgSourceUtils.getPreviewBackgroundBean(),
mBackgroundView.getWidth(),
mBackgroundView.getHeight(),
false,
REQUEST_CROP_IMAGE
);
} else {
ToastUtils.show("图片同步失败");
LogUtils.e(TAG, "【选图同步】文件复制失败");
}
}
boolean putUriFileToPreviewSource(Uri srcUriFile) {
File srcFile = new File(UriUtil.getFilePathFromUri(this, srcUriFile));
@@ -494,6 +563,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
}
boolean putUriFileToPreviewSource(File srcFile) {
LogUtils.d(TAG, String.format("putUriFileToPreviewSource(File srcFile) srcFile %s", srcFile));
mBgSourceUtils.loadSettings();
File dstFile = new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath());
return FileUtils.copyFile(srcFile, dstFile);
@@ -531,16 +601,15 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
}
}
// 延迟300ms调用双重刷新确保文件写入完成
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
if (!isFinishing()) {
doubleRefreshPreview();
LogUtils.d(TAG, "handleCropImageResult: 触发双重刷新");
}
}
}, 300);
@Override
public void run() {
if (!isFinishing()) {
doubleRefreshPreview();
LogUtils.d(TAG, "handleCropImageResult: 触发双重刷新");
}
}
}, 300);
} else {
handleOperationCancelOrFail();
}
@@ -597,11 +666,11 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
LogUtils.d(TAG, "adjustBitmapToFinalRatio: 调整前:" + originalWidth + "x" + originalHeight + ",调整后:" + targetWidth + "x" + targetHeight);
Bitmap adjustedBitmap = Bitmap.createBitmap(
originalBitmap,
(originalWidth - targetWidth) / 2,
(originalHeight - targetHeight) / 2,
targetWidth,
targetHeight
originalBitmap,
(originalWidth - targetWidth) / 2,
(originalHeight - targetHeight) / 2,
targetWidth,
targetHeight
);
return adjustedBitmap;
@@ -685,20 +754,32 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
private void doubleRefreshPreview() {
LogUtils.d(TAG, "【双重刷新】开始");
// 1. 清空缓存工具类的旧数据
// 清空缓存工具类的旧数据
if (mBgSourceUtils != null) {
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
if (previewBean != null && previewBean.isUseBackgroundFile()) {
String bgPath = previewBean.isUseBackgroundScaledCompressFile()
? previewBean.getBackgroundScaledCompressFilePath()
: previewBean.getBackgroundFilePath();
// 强制清除缓存
App._mBitmapCacheUtils.removeCachedBitmap(bgPath);
? previewBean.getBackgroundScaledCompressFilePath()
: previewBean.getBackgroundFilePath();
mBitmapCache.removeCachedBitmap(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()) {
mBgSourceUtils.loadSettings();
mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean());
@@ -708,23 +789,23 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
return;
}
// 3. 延长延迟到200ms确保文件完全加载
// 延长延迟到200ms确保文件完全加载
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
if (mBackgroundView != null && !isFinishing()) {
mBgSourceUtils.loadSettings();
mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean());
LogUtils.d(TAG, "【双重刷新】第二重完成");
}
}
}, 200);
@Override
public void run() {
if (mBackgroundView != null && !isFinishing()) {
mBgSourceUtils.loadSettings();
mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean());
LogUtils.d(TAG, "【双重刷新】第二重完成");
}
}
}, 200);
}
private void handleOperationCancelOrFail() {
mBgSourceUtils.setCurrentSourceToPreview();
LogUtils.d(TAG, "【操作回调】取消或失败");
ToastUtils.show("操作取消或失败");
ToastUtils.show("操作回调】取消或失败");
doubleRefreshPreview();
}
@@ -752,30 +833,92 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
@Override
public void finish() {
if (isCommitSettings) {
setResult(RESULT_OK);
super.finish();
} else {
// 如果预览背景改变过就提示是否更换背景
if (isPreviewBackgroundChanged) {
YesNoAlertDialog.show(this, "背景更换问题", "是否确定背景图片设置?", new YesNoAlertDialog.OnDialogResultListener() {
@Override
public void onYes() {
mBgSourceUtils.commitPreviewSourceToCurrent();
isCommitSettings = true;
finish();
}
@Override
public void onYes() {
mBgSourceUtils.commitPreviewSourceToCurrent();
isCommitSettings = true;
setResult(RESULT_OK);
finish();
}
@Override
public void onNo() {
isCommitSettings = true;
finish();
}
});
@Override
public void onNo() {
isCommitSettings = true;
setResult(RESULT_CANCELED);
finish();
}
});
} else {
// 如果预览背景未改变就直接退出
setResult(RESULT_OK);
isCommitSettings = true;
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;
// }
// }
}

View File

@@ -18,6 +18,7 @@ import cc.winboll.studio.powerbell.utils.PermissionUtils;
public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivity {
public static final String TAG = "SettingsActivity";
private static final int REQUEST_READ_MEDIA_IMAGES = 1001;
private Toolbar mToolbar;
@@ -52,10 +53,15 @@ public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivit
public void onCheckPermission(View view) {
//ToastUtils.show("onCheckPermission");
if (PermissionUtils.getInstance().hasFullStoragePermission()) {
ToastUtils.show("【权限检查】存储权限已全部获取");
} else {
PermissionUtils.getInstance().requestFullStoragePermission(this);
PermissionUtils.getInstance().checkAndRequestMediaImagesPermission(this, REQUEST_READ_MEDIA_IMAGES);
}
@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);
}
}
}

View File

@@ -4,12 +4,15 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import androidx.core.content.FileProvider;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.BuildConfig;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.model.BackgroundBean;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -19,8 +22,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import android.os.Build;
import androidx.core.content.FileProvider;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
@@ -248,57 +249,72 @@ public class BackgroundSourceUtils {
/*
* 创建预览数据剪裁环境
*/
public boolean createAndUpdatePreviewEnvironmentForCropping(BackgroundBean oldPreviewBackgroundBean) {
public boolean createAndUpdatePreviewEnvironmentForCropping(boolean isCreateNewPreviewBean, BackgroundBean previewBackgroundBean) {
InputStream is = null;
FileOutputStream fos = null;
try {
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();
mCropSourceFile = new File(fCropCacheDir, newCropFileName + "." + fileSuffix);
mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + "." + fileSuffix);
String fileSuffix;
if (FileUtils.isFileExists(oldPreviewBackgroundBean.getBackgroundScaledCompressFilePath())) {
FileUtils.copyFile(new File(oldPreviewBackgroundBean.getBackgroundScaledCompressFilePath()), mCropResultFile);
} else {
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();
}
if (FileUtils.isFileExists(oldPreviewBackgroundBean.getBackgroundFilePath())) {
FileUtils.copyFile(new File(oldPreviewBackgroundBean.getBackgroundFilePath()), mCropSourceFile);
} else {
mCropSourceFile.createNewFile();
// 1. 打开Uri输入流兼容content:///file:// 等多种Uri格式
is = mContext.getContentResolver().openInputStream(uri);
if (is == null) {
LogUtils.e(TAG, "【选图解析】ContentResolver打开Uri失败Uri" + uri.toString());
return false;
}
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);
mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + "." + fileSuffix);
// 2. 初始化选图临时文件输出流Java7 手动创建流不依赖try-with-resources
fos = new FileOutputStream(mCropSourceFile);
byte[] buffer = new byte[1024 * 8]; // 8KB缓冲区平衡读写性能与内存占用
int readLen; // 每次读取的字节长度
// 3. 流复制Java7 标准while循环避免Java8+语法)
while ((readLen = is.read(buffer)) != -1) {
fos.write(buffer, 0, readLen); // 精准写入读取到的字节,避免空字节填充
}
// 4. 强制同步写入磁盘解决Android 10+ 异步写入导致的文件无效问题)
fos.flush();
if (fos != null) {
try {
fos.getFD().sync(); // 确保数据写入物理磁盘,而非缓存
} catch (IOException e) {
LogUtils.w(TAG, "【选图解析】文件同步到磁盘失败用flush()兜底:" + e.getMessage());
fos.flush();
FileUtils.copyFile(new File(previewBackgroundBean.getBackgroundFilePath()), mCropSourceFile);
if (FileUtils.isFileExists(previewBackgroundBean.getBackgroundScaledCompressFilePath())) {
FileUtils.copyFile(new File(previewBackgroundBean.getBackgroundScaledCompressFilePath()), mCropResultFile);
} else {
mCropResultFile.createNewFile();
}
mCropSourceFile.createNewFile();
// 1. 打开Uri输入流兼容content:///file:// 等多种Uri格式
is = mContext.getContentResolver().openInputStream(uri);
if (is == null) {
LogUtils.e(TAG, "【选图解析】ContentResolver打开Uri失败Uri" + uri.toString());
return false;
}
// 2. 初始化选图临时文件输出流Java7 手动创建流不依赖try-with-resources
fos = new FileOutputStream(mCropSourceFile);
byte[] buffer = new byte[1024 * 8]; // 8KB缓冲区平衡读写性能与内存占用
int readLen; // 每次读取的字节长度
// 3. 流复制Java7 标准while循环避免Java8+语法)
while ((readLen = is.read(buffer)) != -1) {
fos.write(buffer, 0, readLen); // 精准写入读取到的字节,避免空字节填充
}
// 4. 强制同步写入磁盘解决Android 10+ 异步写入导致的文件无效问题)
fos.flush();
if (fos != null) {
try {
fos.getFD().sync(); // 确保数据写入物理磁盘,而非缓存
} catch (IOException e) {
LogUtils.w(TAG, "【选图解析】文件同步到磁盘失败用flush()兜底:" + e.getMessage());
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();
// 6. 解析成功日志(打印文件信息,便于问题排查)
LogUtils.d(TAG, "【选图解析】Uri解析成功");
LogUtils.d(TAG, "→ 原Uri" + uri.toString());
// 解析成功日志(打印文件信息,便于问题排查)
LogUtils.d(TAG, "→ 图片剪裁数据源:" + mCropSourceFile.getAbsolutePath());
LogUtils.d(TAG, "→ 图片剪裁数据源文件大小:" + mCropSourceFile.length() + " bytes");
LogUtils.d(TAG, "→ 剪裁结果数据文件:" + mCropResultFile.getAbsolutePath());
LogUtils.d(TAG, "→ 剪裁结果数据文件大小:" + mCropResultFile.length() + " bytes");
return true;
} catch (Exception e) {
@@ -380,30 +395,30 @@ public class BackgroundSourceUtils {
/**
* 获取正式背景图片路径(修复:移除每次 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();
}
// 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目录

View File

@@ -1,113 +1,134 @@
package cc.winboll.studio.powerbell.utils;
import android.content.Context;
import android.content.Intent;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.widget.Toast;
import androidx.core.app.ActivityCompat;
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 {
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 Context mContext;
// 权限请求码
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();
}
private PermissionUtils() {}
public static PermissionUtils getInstance() {
if (sInstance == null) {
throw new IllegalStateException("请先调用 init(Context) 初始化");
synchronized (PermissionUtils.class) {
if (sInstance == null) {
sInstance = new PermissionUtils();
}
}
}
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 checkAndRequestStoragePermission(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// Android 11+ 无需申请 READ_EXTERNAL_STORAGE直接返回true
return true;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String[] permissions = {
android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.WRITE_EXTERNAL_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);
}
}
/**
* 检查是否有管理所有文件权限Android 11+
*/
public boolean checkManageExternalStoragePermission(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return android.os.Environment.isExternalStorageManager();
}
return true;
}
/**
* 请求管理所有文件权限Android 11+
*/
public void requestManageExternalStoragePermission(Activity activity, int requestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
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());
}
}
}
/**
* 检查是否拥有完整的文件管理权限
*/
public boolean hasFullStoragePermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// API 30+ 需判断 MANAGE_EXTERNAL_STORAGE 权限
return Environment.isExternalStorageManager();
} else {
// 低版本判断传统读写权限
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.WRITE_EXTERNAL_STORAGE
},
REQUEST_CODE_STORAGE
);
}
}
/**
* 处理低版本权限申请结果(在 Activity 的 onRequestPermissionsResult 中调用)
* @param activity 上下文
* @param requestCode 请求码
* @param permissions 权限数组
* @param grantResults 授权结果
* @return 权限是否申请成功
*/
public boolean handleStoragePermissionResult(android.app.Activity activity, int requestCode, String[] permissions, int[] grantResults) {
// 仅处理传统存储权限的请求结果
if (requestCode != REQUEST_CODE_STORAGE) {
return false;
}
// 校验授权结果:读写权限均需授予
boolean isGranted = grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED;
if (isGranted) {
// 权限授予成功,提示用户
Toast.makeText(activity, "存储权限申请成功", Toast.LENGTH_SHORT).show();
} else {
// 权限被拒绝,引导用户手动开启
Toast.makeText(activity, "存储权限被拒绝,部分功能无法使用", Toast.LENGTH_SHORT).show();
}
return isGranted;
}
}