diff --git a/powerbell/build.properties b/powerbell/build.properties index 37aa41a..26cebb6 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sat Dec 13 18:07:45 GMT 2025 +#Sat Dec 13 20:06:18 GMT 2025 stageCount=2 libraryProject= baseVersion=15.14 publishVersion=15.14.1 -buildCount=3 +buildCount=39 baseBetaVersion=15.14.2 diff --git a/powerbell/src/main/AndroidManifest.xml b/powerbell/src/main/AndroidManifest.xml index 1f88863..4113f48 100644 --- a/powerbell/src/main/AndroidManifest.xml +++ b/powerbell/src/main/AndroidManifest.xml @@ -4,24 +4,12 @@ xmlns:tools="http://schemas.android.com/tools" package="cc.winboll.studio.powerbell"> - - - - - - - - - - - - @@ -31,9 +19,6 @@ - - - @@ -233,4 +218,4 @@ - + \ No newline at end of file diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/App.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/App.java index fddf7b6..cbdef5e 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/App.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/App.java @@ -14,7 +14,6 @@ import cc.winboll.studio.powerbell.utils.AppCacheUtils; import cc.winboll.studio.powerbell.utils.AppConfigUtils; import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; import cc.winboll.studio.powerbell.utils.BitmapCacheUtils; -import cc.winboll.studio.powerbell.utils.PermissionUtils; import java.io.File; public class App extends GlobalApplication { diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java index 807e871..a4a4b55 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java @@ -68,6 +68,7 @@ public class MainActivity extends WinBoLLActivity { public static MainActivity _mMainActivity; static MainViewFragment _mMainViewFragment; static Handler _mHandler; + PermissionUtils permissionUtils = PermissionUtils.getInstance(); private App mApplication; private AppConfigUtils mAppConfigUtils; @@ -121,13 +122,18 @@ public class MainActivity extends WinBoLLActivity { initViewHolder(); initCriticalView(); loadNonCriticalViewDelayed(); - - // 权限申请 - PermissionUtils.getInstance().checkAndRequestMediaImagesPermission(this, REQUEST_READ_MEDIA_IMAGES); } - // 移除 onSaveInstanceState 方法 - // 移除 onRestoreInstanceState 方法 + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + + // 电池优化权限(通用所有机型) + if (!permissionUtils.checkIgnoreBatteryOptimizationPermission(this)) { + // 未拥有权限,发起申请 + permissionUtils.requestIgnoreBatteryOptimizationPermission(this); + } + } @Override protected void onDestroy() { @@ -189,7 +195,7 @@ public class MainActivity extends WinBoLLActivity { startActivity(new Intent(this, ClearRecordActivity.class)); break; case R.id.action_changepicture: - startActivity(new Intent(this, BackgroundSettingsActivity.class)); + startActivityForResult(new Intent(this, BackgroundSettingsActivity.class), REQUEST_READ_MEDIA_IMAGES); break; case R.id.action_unittestactivity: startActivity(new Intent(this, MainUnitTestActivity.class)); @@ -209,20 +215,20 @@ public class MainActivity extends WinBoLLActivity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (resultCode == Activity.RESULT_OK) { + + if (requestCode == PermissionUtils.REQUEST_IGNORE_BATTERY_OPTIMIZATION) { + // 自启动权限(小米专属) + if (permissionUtils.checkAutoStartPermission(this)) { + // 小米机型,发起自启动权限申请 + permissionUtils.requestAutoStartPermission(this); + } + } else if (requestCode == REQUEST_READ_MEDIA_IMAGES) { if (_mHandler != null) { _mHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND); } } } - @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() { diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java index f44c487..0ffc69c 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java @@ -3,6 +3,7 @@ package cc.winboll.studio.powerbell.activities; import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; @@ -15,6 +16,7 @@ import android.os.Looper; import android.provider.MediaStore; import android.text.TextUtils; import android.view.View; +import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; import androidx.core.content.FileProvider; @@ -29,7 +31,6 @@ 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.UriUtils; import cc.winboll.studio.powerbell.views.BackgroundView; import java.io.BufferedOutputStream; @@ -51,7 +52,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity { // ====================== 成员变量 ====================== private BackgroundSourceUtils mBgSourceUtils; - private PermissionUtils mPermissionUtils; private BitmapCacheUtils mBitmapCache; private Toolbar mToolbar; @@ -81,7 +81,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity { mBackgroundView = findViewById(R.id.background_view); mBgSourceUtils = BackgroundSourceUtils.getInstance(this); mBgSourceUtils.loadSettings(); - mPermissionUtils = PermissionUtils.getInstance(); mBitmapCache = BitmapCacheUtils.getInstance(); // 初始化临时文件与目录 @@ -126,11 +125,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity { LogUtils.d(TAG, "【回调触发】requestCode:" + requestCode + ",resultCode:" + resultCode); try { - if (requestCode == PermissionUtils.REQUEST_READ_MEDIA_IMAGES && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - handleStoragePermissionCallback(); - return; - } - if (resultCode != RESULT_OK) { handleOperationCancelOrFail(); return; @@ -156,13 +150,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity { } } - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - LogUtils.d(TAG, "【权限回调】转发处理 requestCode:" + requestCode); - mPermissionUtils.handleStoragePermissionResult(this, requestCode, permissions, grantResults); - } - @Override public void finish() { LogUtils.d(TAG, "【生命周期】finish 触发,isCommitSettings:" + isCommitSettings + ",isPreviewBackgroundChanged:" + isPreviewBackgroundChanged); @@ -235,15 +222,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity { @Override public void onClick(View v) { LogUtils.d(TAG, "【按钮点击】选择图片"); - if (Build.VERSION.SDK_INT >= SDK_VERSION_TIRAMISU) { - if (mPermissionUtils.checkAndRequestMediaImagesPermission(BackgroundSettingsActivity.this, REQUEST_READ_MEDIA)) { - launchImageSelector(); - } - } else { - if (mPermissionUtils.checkAndRequestStoragePermission(BackgroundSettingsActivity.this)) { - launchImageSelector(); - } - } + launchImageSelector(); } }; @@ -293,22 +272,17 @@ public class BackgroundSettingsActivity extends WinBoLLActivity { return; } - if (mPermissionUtils.checkAndRequestStoragePermission(BackgroundSettingsActivity.this)) { - LogUtils.d(TAG, "【拍照权限】已获取"); - Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - try { - Uri photoUri = getFileProviderUri(mfTakePhoto); - takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); - startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); - LogUtils.d(TAG, "【拍照启动】Uri:" + photoUri.toString()); - } catch (Exception e) { - String errMsg = "拍照启动异常:" + e.getMessage(); - ToastUtils.show(errMsg.substring(0, 20)); - LogUtils.e(TAG, "【拍照失败】" + e.getMessage()); - } - } else { - LogUtils.d(TAG, "【拍照权限】已申请"); - } + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + try { + Uri photoUri = getFileProviderUri(mfTakePhoto); + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); + startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); + LogUtils.d(TAG, "【拍照启动】Uri:" + photoUri.toString()); + } catch (Exception e) { + String errMsg = "拍照启动异常:" + e.getMessage(); + ToastUtils.show(errMsg.substring(0, 20)); + LogUtils.e(TAG, "【拍照失败】" + e.getMessage()); + } } }; diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java index a8acf58..6dd136b 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java @@ -6,9 +6,7 @@ import android.view.View; import androidx.appcompat.widget.Toolbar; import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; import cc.winboll.studio.libappbase.LogUtils; -import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.powerbell.R; -import cc.winboll.studio.powerbell.utils.PermissionUtils; /** * @Author ZhanGSKen&豆包大模型 @@ -50,18 +48,4 @@ public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivit } }); } - - public void onCheckPermission(View view) { - //ToastUtils.show("onCheckPermission"); - 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); - } - } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils.java index dee3f4f..dbcf8f0 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils.java @@ -117,7 +117,7 @@ public class NotificationManagerUtils { NotificationManager.IMPORTANCE_HIGH ); channel.setDescription(CHANNEL_DESC_BATTERY_REMIND); - channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM), null); // 闹钟铃声 + channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), null); // 闹钟铃声 channel.enableVibration(true); channel.setVibrationPattern(VIBRATE_PATTERN); channel.setBypassDnd(true); // 突破免打扰 @@ -139,7 +139,8 @@ public class NotificationManagerUtils { NotificationManager.IMPORTANCE_HIGH ); channel.setDescription(CHANNEL_DESC_TEMP_ALERT); - channel.setSound(null, null); // 仅震动,不发声 + //channel.setSound(null, null); // 仅震动,不发声 + channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), null); // 闹钟铃声 channel.enableVibration(true); channel.setVibrationPattern(VIBRATE_PATTERN); channel.setBypassDnd(false); // 不突破免打扰 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/PermissionUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/PermissionUtils.java index 924e0c9..a336d7f 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/PermissionUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/PermissionUtils.java @@ -1,32 +1,35 @@ package cc.winboll.studio.powerbell.utils; import android.app.Activity; -import android.content.pm.PackageManager; +import android.app.AlertDialog; +import android.content.ComponentName; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; import android.os.Build; - -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; - +import android.os.PowerManager; +import android.provider.Settings; import cc.winboll.studio.libappbase.LogUtils; /** - * 权限申请工具类 - * 适配 Android 13+ 媒体权限 & 低版本存储权限 - * 兼容 SDK 版本低于 33 的编译环境 + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/12/14 03:05 + * @Describe 权限申请工具类(Java7兼容版) + * 适配 小米手机+API30,专注自启动权限、电池优化权限检查与申请 */ 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; + // ====================== 常量定义(统一管理,首屏可见)====================== + public static final String TAG = "PermissionUtils"; + // 权限请求码(仅保留核心权限场景) + public static final int REQUEST_IGNORE_BATTERY_OPTIMIZATION = 1000; // 电池优化权限 + public static final int REQUEST_AUTO_START = 1001; // 自启动权限(小米专属) + // SDK版本常量(适配API30,替代系统枚举) + private static final int SDK_VERSION_R = 30; // Android 11(API30) + // 小米手机自启动权限页面包名/类名(小米专属跳转路径) + private static final String XIAOMI_AUTO_START_PACKAGE = "com.miui.securitycenter"; + private static final String XIAOMI_AUTO_START_CLASS = "com.miui.permcenter.autostart.AutoStartManagementActivity"; - // 手动定义 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; - - // 单例模式 + // ====================== 单例模式(Java7标准双重校验锁)====================== private static volatile PermissionUtils sInstance; private PermissionUtils() {} @@ -36,99 +39,184 @@ public class PermissionUtils { synchronized (PermissionUtils.class) { if (sInstance == null) { sInstance = new PermissionUtils(); + LogUtils.d(TAG, "【单例初始化】PermissionUtils 实例创建成功"); } } } return sInstance; } + // ====================== 自启动权限(拆分检查+请求,小米专属)====================== /** - * 检查并请求 存储权限(Android 12及以下) + * 检查是否拥有自启动权限(小米手机专属判断,API30适配) + * 注:小米自启动无系统API直接校验,通过「是否为小米机型」+「功能场景间接判断」,此处返回机型适配状态 + * @param activity 上下文Activity(不可为null) + * @return true:小米机型(需手动开启权限);false:非小米机型(无需申请) */ - 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; - } + public boolean checkAutoStartPermission(Activity activity) { + if (activity == null) { + LogUtils.e(TAG, "【自启动权限-检查】失败:Activity为空"); + return false; } - return true; + LogUtils.d(TAG, "【自启动权限-检查】开始,设备品牌:" + Build.BRAND + ",系统版本:" + Build.VERSION.SDK_INT); + + // 仅小米机型需要申请自启动权限,非小米直接返回false(无需处理) + boolean isXiaomi = Build.BRAND.toLowerCase().contains("xiaomi"); + LogUtils.d(TAG, "【自启动权限-检查】结果:" + (isXiaomi ? "小米机型(需手动开启)" : "非小米机型(无需申请)")); + return isXiaomi; } /** - * 检查并请求 媒体图片权限(Android 13+) - * 兼容 SDK 编译版本低于 33 的情况 + * 请求自启动权限(小米手机专属,API30适配,跳转系统页面引导开启) + * @param activity 申请权限的Activity(不可为null) */ - 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; - } + public void requestAutoStartPermission(Activity activity) { + if (activity == null) { + LogUtils.e(TAG, "【自启动权限-请求】失败:Activity为空"); + return; } - // 低版本已通过存储权限覆盖,直接返回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); + // 先检查机型,非小米不执行请求 + if (!checkAutoStartPermission(activity)) { + LogUtils.d(TAG, "【自启动权限-请求】非小米机型,无需发起请求"); + return; } - } + LogUtils.d(TAG, "【自启动权限-请求】开始处理,系统版本:" + Build.VERSION.SDK_INT); - /** - * 检查是否有管理所有文件权限(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) { + // API30+ 小米手机:优先精准跳转自启动管理页 + if (Build.VERSION.SDK_INT >= SDK_VERSION_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()); + // 方案1:组件名精准跳转(成功率最高) + Intent intent = new Intent(); + intent.setComponent(new ComponentName(XIAOMI_AUTO_START_PACKAGE, XIAOMI_AUTO_START_CLASS)); + activity.startActivityForResult(intent, REQUEST_AUTO_START); + LogUtils.d(TAG, "【自启动权限-请求】跳转小米自启动管理页(组件名跳转)"); + } catch (Exception e1) { + try { + // 方案2:Action备用跳转(兼容机型差异) + Intent intent = new Intent("miui.intent.action.OP_AUTO_START"); + intent.setClassName(XIAOMI_AUTO_START_PACKAGE, XIAOMI_AUTO_START_CLASS); + activity.startActivityForResult(intent, REQUEST_AUTO_START); + LogUtils.d(TAG, "【自启动权限-请求】跳转小米自启动管理页(Action跳转)"); + } catch (Exception e2) { + // 方案3:终极备用(跳转系统设置,引导手动操作) + Intent intent = new Intent(Settings.ACTION_SETTINGS); + activity.startActivityForResult(intent, REQUEST_AUTO_START); + LogUtils.w(TAG, "【自启动权限-请求】跳转系统设置页(引导手动开启)"); + showAutoStartTipsDialog(activity); + } } + return; } + + // API30以下小米手机:兼容低版本跳转逻辑 + LogUtils.d(TAG, "【自启动权限-请求】API30以下小米机型,执行低版本跳转"); + try { + Intent intent = new Intent(XIAOMI_AUTO_START_CLASS); + intent.setPackage(XIAOMI_AUTO_START_PACKAGE); + activity.startActivityForResult(intent, REQUEST_AUTO_START); + } catch (Exception e) { + Intent intent = new Intent(Settings.ACTION_SETTINGS); + activity.startActivityForResult(intent, REQUEST_AUTO_START); + showAutoStartTipsDialog(activity); + } + } + + // ====================== 电池优化权限(拆分检查+请求,通用所有机型)====================== + /** + * 检查是否拥有「忽略电池优化」权限(API30适配,通用所有机型,精准返回权限状态) + * @param activity 上下文Activity(不可为null) + * @return true:已拥有(忽略优化);false:未拥有(需申请) + */ + public boolean checkIgnoreBatteryOptimizationPermission(Activity activity) { + if (activity == null) { + LogUtils.e(TAG, "【电池优化权限-检查】失败:Activity为空"); + return false; + } + LogUtils.d(TAG, "【电池优化权限-检查】开始,系统版本:" + Build.VERSION.SDK_INT); + + // API23以下无电池优化权限,直接视为已拥有 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + LogUtils.d(TAG, "【电池优化权限-检查】API23以下无此权限,视为已拥有"); + return true; + } + + // API23+ 精准校验权限状态 + PowerManager powerManager = (PowerManager) activity.getSystemService(Activity.POWER_SERVICE); + if (powerManager == null) { + LogUtils.e(TAG, "【电池优化权限-检查】获取PowerManager失败,无法校验"); + return false; + } + boolean isIgnored = powerManager.isIgnoringBatteryOptimizations(activity.getPackageName()); + LogUtils.d(TAG, "【电池优化权限-检查】结果:" + (isIgnored ? "已拥有(忽略优化)" : "未拥有(需申请)")); + return isIgnored; + } + + /** + * 请求「忽略电池优化」权限(API30适配,通用所有机型,自动判断是否需要申请) + * @param activity 申请权限的Activity(不可为null) + */ + public void requestIgnoreBatteryOptimizationPermission(Activity activity) { + if (activity == null) { + LogUtils.e(TAG, "【电池优化权限-请求】失败:Activity为空"); + return; + } + // 先检查权限,已拥有则直接返回 + if (checkIgnoreBatteryOptimizationPermission(activity)) { + LogUtils.d(TAG, "【电池优化权限-请求】已拥有权限,无需发起申请"); + return; + } + LogUtils.w(TAG, "【电池优化权限-请求】未拥有权限,开始发起申请"); + + try { + // 方案1:直接跳转权限申请页(用户一键同意,优先使用) + Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + intent.setData(Uri.parse("package:" + activity.getPackageName())); + activity.startActivityForResult(intent, REQUEST_IGNORE_BATTERY_OPTIMIZATION); + LogUtils.d(TAG, "【电池优化权限-请求】跳转系统权限申请页"); + } catch (Exception e) { + // 方案2:备用跳转(跳转优化管理列表,引导手动选择) + Intent intent = new Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS); + activity.startActivityForResult(intent, REQUEST_IGNORE_BATTERY_OPTIMIZATION); + LogUtils.w(TAG, "【电池优化权限-请求】跳转优化管理页(引导手动开启)"); + showBatteryOptTipsDialog(activity); + } + } + + // ====================== 辅助方法(提示弹窗+结果处理)====================== + /** + * 显示自启动权限手动开启提示弹窗(小米机型跳转失败时使用) + */ + private void showAutoStartTipsDialog(final Activity activity) { + new AlertDialog.Builder(activity) + .setTitle("权限申请提示") + .setMessage("请手动开启自启动权限,否则应用后台功能可能异常:\n1. 进入安全中心 → 应用管理 → 自启动管理\n2. 找到本应用,开启「允许自启动」开关") + .setPositiveButton("知道了", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }) + .setCancelable(false) + .show(); + LogUtils.d(TAG, "【自启动权限】显示手动开启提示弹窗"); + } + + /** + * 显示电池优化权限手动开启提示弹窗(跳转失败时使用) + */ + private void showBatteryOptTipsDialog(final Activity activity) { + new AlertDialog.Builder(activity) + .setTitle("权限申请提示") + .setMessage("请手动忽略电池优化,否则应用后台运行可能被限制:\n1. 进入设置 → 电池 → 电池优化\n2. 找到本应用,选择「不优化」") + .setPositiveButton("知道了", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }) + .setCancelable(false) + .show(); + LogUtils.d(TAG, "【电池优化权限】显示手动开启提示弹窗"); } }