Compare commits

..

8 Commits

11 changed files with 255 additions and 185 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed Dec 24 21:23:33 HKT 2025
stageCount=30
#Thu Dec 25 16:51:19 HKT 2025
stageCount=33
libraryProject=
baseVersion=15.14
publishVersion=15.14.29
publishVersion=15.14.32
buildCount=0
baseBetaVersion=15.14.30
baseBetaVersion=15.14.33

View File

@@ -28,9 +28,10 @@
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<!-- 外部存储权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<!-- 相机权限 -->
@@ -62,14 +63,15 @@
android:supportsRtl="true"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<!-- 主活动 -->
<!-- 主活动(主进程) -->
<activity
android:process=".main"
android:name=".MainActivity"
android:label="@string/app_name"
android:exported="true"
android:launchMode="singleTask"/>
<!-- 活动别名(启动器) -->
<!-- 活动别名(启动器,主进程 -->
<activity-alias
android:name=".MainActivityEN1"
android:targetActivity=".MainActivity"
@@ -118,18 +120,21 @@
android:resource="@xml/shortcutsmaincn2"/>
</activity-alias>
<!-- 功能活动 -->
<!-- 功能活动(主进程) -->
<activity
android:process=".main"
android:name=".activities.CrashActivity"
android:exported="false"/>
<activity
android:process=".main"
android:name=".activities.ClearRecordActivity"
android:parentActivityName="cc.winboll.studio.powerbell.MainActivity"
android:launchMode="singleTask"
android:exported="false"/>
<activity
android:process=".main"
android:name=".activities.BackgroundSettingsActivity"
android:parentActivityName="cc.winboll.studio.powerbell.MainActivity"
android:exported="true"
@@ -146,44 +151,53 @@
</activity>
<activity
android:process=".main"
android:name=".activities.BatteryReporterActivity"
android:exported="false"/>
<activity
android:process=".main"
android:name=".activities.PixelPickerActivity"
android:exported="false"/>
<activity
android:process=".main"
android:name=".activities.BatteryReportActivity"
android:exported="false"/>
<activity
android:process=".main"
android:name=".unittest.MainUnitTestActivity"
android:exported="false"/>
<activity
android:process=".main"
android:name=".activities.ShortcutActionActivity"
android:exported="false"/>
<activity
android:process=".main"
android:name=".activities.SettingsActivity"
android:exported="false"/>
<activity
android:process=".main"
android:name="cc.winboll.studio.powerbell.unittest.MainUnitTest2Activity"
android:exported="false"/>
<!-- 第三方活动 -->
<!-- 第三方活动(主进程) -->
<activity
android:process=".main"
android:name="com.yalantis.ucrop.UCropActivity"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:exported="true"/>
<!-- 广播接收器 -->
<!-- 广播接收器(主进程) -->
<receiver
android:name=".receivers.MainReceiver"
android:enabled="true"
android:exported="true"
android:process=".main"
android:directBootAware="true">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED"/>
@@ -192,13 +206,13 @@
</intent-filter>
</receiver>
<!-- 服务 -->
<!-- 服务ControlCenterService主进程AssistantService独立进程 -->
<service
android:name=".services.ControlCenterService"
android:priority="1000"
android:enabled="true"
android:exported="false"
android:process=".controlcenterservice"
android:process=".main"
android:foregroundServiceType="dataSync">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FOREGROUND_SERVICE"
@@ -209,18 +223,20 @@
android:name=".services.AssistantService"
android:enabled="true"
android:exported="false"
android:process=".assistantservice">
android:process=".assistant"
android:foregroundServiceType="dataSync">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FOREGROUND_SERVICE"
android:value="辅助核心功能运行"/>
</service>
<!-- 内容提供者 -->
<!-- 内容提供者(主进程) -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
android:grantUriPermissions="true"
android:process=".main">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/>

View File

@@ -5,9 +5,11 @@ import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.models.NotificationMessage;
import cc.winboll.studio.powerbell.receivers.GlobalApplicationReceiver;
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.NotificationManagerUtils;
import cc.winboll.studio.powerbell.views.MemoryCachedBackgroundView;
@@ -16,6 +18,8 @@ import cc.winboll.studio.powerbell.views.MemoryCachedBackgroundView;
* 应用全局入口类适配Android API 30基于Java 7编写
* 核心策略:极致强制缓存 - 无论内存紧张程度永不自动清理任何缓存Bitmap/视图控件/路径记录)
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Version 1.0.0
* @Date 2025-12-25
*/
public class App extends GlobalApplication {
// ===================== 常量定义区(按功能分类排序) =====================
@@ -33,6 +37,7 @@ public class App extends GlobalApplication {
// 缓存防护常量
private static final String CACHE_PROTECT_TAG = "FORCE_CACHE_PROTECT";
private static final int INVALID_BATTERY_VALUE = -1;
// ===================== 静态属性区(按工具类优先级排序) =====================
// 数据配置工具
@@ -40,17 +45,21 @@ public class App extends GlobalApplication {
private static AppCacheUtils sAppCacheUtils;
// 全局Bitmap缓存工具极致强制保持一旦初始化永不销毁
public static BackgroundSourceUtils sBackgroundSourceUtils;
public static BitmapCacheUtils sBitmapCacheUtils;
// 全局视图控件缓存工具(极致强制保持:一旦初始化,永不销毁)
public static MemoryCachedBackgroundView sMemoryCachedBackgroundView;
// 核心修改:改为私有静态,通过实例方法获取,避免全局通用暴露
private static MemoryCachedBackgroundView sMemoryCachedBackgroundView;
public static volatile int sQuantityOfElectricity = INVALID_BATTERY_VALUE;
// ===================== 成员属性区(按生命周期关联度排序) =====================
// 全局广播接收器
private GlobalApplicationReceiver mGlobalReceiver;
// 通知管理工具
private NotificationManagerUtils mNotificationManager;
private static NotificationManagerUtils sNotificationManagerUtils;
private static App sApp;
// ===================== 公共静态方法区(工具类实例获取) =====================
/**
@@ -58,7 +67,7 @@ public class App extends GlobalApplication {
*/
public static AppConfigUtils getAppConfigUtils(Context context) {
LogUtils.d(TAG, String.format("getAppConfigUtils() 调用 | 传入Context类型=%s",
context != null ? context.getClass().getSimpleName() : "null"));
context != null ? context.getClass().getSimpleName() : "null"));
if (sAppConfigUtils == null) {
sAppConfigUtils = AppConfigUtils.getInstance(context);
LogUtils.d(TAG, "getAppConfigUtils()AppConfigUtils实例已初始化");
@@ -71,7 +80,7 @@ public class App extends GlobalApplication {
*/
public static AppCacheUtils getAppCacheUtils(Context context) {
LogUtils.d(TAG, String.format("getAppCacheUtils() 调用 | 传入Context类型=%s",
context != null ? context.getClass().getSimpleName() : "null"));
context != null ? context.getClass().getSimpleName() : "null"));
if (sAppCacheUtils == null) {
sAppCacheUtils = AppCacheUtils.getInstance(context);
LogUtils.d(TAG, "getAppCacheUtils()AppCacheUtils实例已初始化");
@@ -79,6 +88,13 @@ public class App extends GlobalApplication {
return sAppCacheUtils;
}
/**
* 获取应用单例实例
*/
public static App getInstance() {
return sApp;
}
// ===================== 公共成员方法区(业务功能) =====================
/**
* 清除电池历史数据
@@ -112,10 +128,22 @@ public class App extends GlobalApplication {
LogUtils.w(TAG, String.format("%s 手动清理缓存完成(部分缓存实例仍可能保留在内存中)", CACHE_PROTECT_TAG));
}
/**
* 获取视图控件缓存实例非通用仅通过App实例调用避免全局直接访问
*/
public MemoryCachedBackgroundView getMemoryCachedBackgroundView() {
LogUtils.d(TAG, "getMemoryCachedBackgroundView() 调用 | 视图控件缓存实例获取");
if (sMemoryCachedBackgroundView == null) {
LogUtils.w(TAG, "getMemoryCachedBackgroundView()视图控件缓存实例未初始化返回null");
}
return sMemoryCachedBackgroundView;
}
// ===================== 生命周期方法区(按执行顺序排序) =====================
@Override
public void onCreate() {
super.onCreate();
sApp = this;
LogUtils.d(TAG, "onCreate() 应用启动,开始初始化");
// 初始化调试模式
@@ -124,6 +152,7 @@ public class App extends GlobalApplication {
// 初始化基础工具
initBaseTools();
//App.notifyMessage(TAG, "onCreate() 应用启动中。。。");
// 初始化工具类实例(核心:极致强制缓存,永不销毁)
initUtils();
// 初始化广播接收器
@@ -156,7 +185,7 @@ public class App extends GlobalApplication {
super.onTrimMemory(level);
// 极致强制缓存:禁止任何缓存清理操作,仅记录日志
LogUtils.w(TAG, String.format("%s onTrimMemory() 调用 | 内存等级level=%d | 极致强制保持所有缓存",
CACHE_PROTECT_TAG, level));
CACHE_PROTECT_TAG, level));
// 记录详细缓存状态,不执行任何清理
logDetailedCacheStatus();
}
@@ -179,6 +208,8 @@ public class App extends GlobalApplication {
WinBoLLActivityManager.init(this);
ToastUtils.init(this);
LogUtils.d(TAG, "initBaseTools() 基础工具初始化完成");
sNotificationManagerUtils = new NotificationManagerUtils(this);
LogUtils.d(TAG, "initBaseTools() 工具类初始化完成,极致强制缓存策略已生效");
}
/**
@@ -186,19 +217,23 @@ public class App extends GlobalApplication {
*/
private void initUtils() {
LogUtils.d(TAG, "initUtils() 开始初始化工具类,启用极致强制缓存策略");
//App.notifyMessage(TAG, "initUtils() 开始初始化工具类,启用极致强制缓存策略");
sAppConfigUtils = getAppConfigUtils(this);
sAppCacheUtils = getAppCacheUtils(this);
sBackgroundSourceUtils = BackgroundSourceUtils.getInstance(this);
sBackgroundSourceUtils.loadSettings();
// 极致强制初始化Bitmap缓存工具必初始化永不销毁
sBitmapCacheUtils = BitmapCacheUtils.getInstance();
LogUtils.d(TAG, "initUtils() Bitmap缓存工具已初始化极致强制保持永不销毁");
// 极致强制初始化视图控件缓存工具(必初始化,永不销毁)
sMemoryCachedBackgroundView = MemoryCachedBackgroundView.getLastInstance(this);
if (sMemoryCachedBackgroundView == null) {
//App.notifyMessage(TAG, "sMemoryCachedBackgroundView == null");
sMemoryCachedBackgroundView = MemoryCachedBackgroundView.getInstance(this, sBackgroundSourceUtils.getCurrentBackgroundBean(), true);
}
LogUtils.d(TAG, "initUtils() 视图控件缓存工具已初始化(极致强制保持,永不销毁)");
mNotificationManager = new NotificationManagerUtils(this);
LogUtils.d(TAG, "initUtils() 工具类初始化完成,极致强制缓存策略已生效");
}
/**
@@ -231,9 +266,9 @@ public class App extends GlobalApplication {
*/
private void releaseNotificationManager() {
LogUtils.d(TAG, "releaseNotificationManager() 开始释放通知工具");
if (mNotificationManager != null) {
mNotificationManager.release();
mNotificationManager = null;
if (sNotificationManagerUtils != null) {
sNotificationManagerUtils.release();
sNotificationManagerUtils = null;
LogUtils.d(TAG, "releaseNotificationManager() 通知工具资源已释放");
} else {
LogUtils.d(TAG, "releaseNotificationManager() 通知工具未初始化,无需释放");
@@ -255,7 +290,7 @@ public class App extends GlobalApplication {
LogUtils.d(TAG, String.format("%s Bitmap缓存数量=%d", CACHE_PROTECT_TAG, cacheCount));
} catch (Exception e) {
LogUtils.d(TAG, String.format("%s Bitmap缓存数量获取失败不影响缓存| 异常信息=%s",
CACHE_PROTECT_TAG, e.getMessage()));
CACHE_PROTECT_TAG, e.getMessage()));
}
}
// 视图控件缓存状态
@@ -267,5 +302,12 @@ public class App extends GlobalApplication {
}
LogUtils.d(TAG, "logDetailedCacheStatus() 详细缓存状态记录完成,所有缓存均极致强制保持");
}
public static void notifyMessage(String title, String content) {
if (isDebugging() && sApp != null && sNotificationManagerUtils != null) {
NotificationMessage message = new NotificationMessage(title, content, "");
sNotificationManagerUtils.showMessageNotification(sApp, message);
}
}
}

View File

@@ -5,10 +5,10 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.models.AppConfigBean;
import cc.winboll.studio.powerbell.models.NotificationMessage;
import cc.winboll.studio.powerbell.services.ControlCenterService;
import cc.winboll.studio.powerbell.threads.RemindThread;
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import cc.winboll.studio.powerbell.utils.BatteryUtils;
import cc.winboll.studio.powerbell.utils.NotificationManagerUtils;
@@ -142,7 +142,7 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
latestConfig.getChargeReminderValue(), latestConfig.getUsageReminderValue()));
// 同步缓存的电池状态到配置
RemindThread.sQuantityOfElectricity = sLastBatteryLevel;
App.sQuantityOfElectricity = sLastBatteryLevel;
latestConfig.setIsCharging(sIsCharging);
service.notifyAppConfigUpdate(latestConfig);

View File

@@ -3,6 +3,7 @@ package cc.winboll.studio.powerbell.threads;
import android.content.Context;
import android.os.Message;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.handlers.ControlCenterServiceHandler;
import cc.winboll.studio.powerbell.models.AppConfigBean;
import java.lang.ref.WeakReference;
@@ -26,7 +27,6 @@ public class RemindThread extends Thread {
private static final long THREAD_JOIN_TIMEOUT = 1000L;
// 状态常量
private static final int INVALID_BATTERY_VALUE = -1;
private static final int BATTERY_LEVEL_MIN = 0;
private static final int BATTERY_LEVEL_MAX = 100;
@@ -55,7 +55,6 @@ public class RemindThread extends Thread {
private volatile long sleepTime;
private volatile int chargeReminderValue;
private volatile int usageReminderValue;
public static volatile int sQuantityOfElectricity = INVALID_BATTERY_VALUE;
private volatile boolean isCharging;
// ====================== 私有构造器(禁止外部实例化) ======================
@@ -170,21 +169,21 @@ public class RemindThread extends Thread {
if (isExist) break;
// 电量有效性校验非0-100视为无效退出电量提醒线程
if (sQuantityOfElectricity < BATTERY_LEVEL_MIN || sQuantityOfElectricity > BATTERY_LEVEL_MAX) {
LogUtils.w(TAG, String.format("电量无效,退出电量提醒线程 | 当前电量=%d | threadId=%d", sQuantityOfElectricity, getId()));
if (App.sQuantityOfElectricity < BATTERY_LEVEL_MIN || App.sQuantityOfElectricity > BATTERY_LEVEL_MAX) {
LogUtils.w(TAG, String.format("电量无效,退出电量提醒线程 | 当前电量=%d | threadId=%d", App.sQuantityOfElectricity, getId()));
break;
}
// 充电/耗电提醒触发逻辑
boolean chargeRemindTrigger = isCharging && isEnableChargeReminder && sQuantityOfElectricity >= chargeReminderValue;
boolean usageRemindTrigger = !isCharging && isEnableUsageReminder && sQuantityOfElectricity <= usageReminderValue;
boolean chargeRemindTrigger = isCharging && isEnableChargeReminder && App.sQuantityOfElectricity >= chargeReminderValue;
boolean usageRemindTrigger = !isCharging && isEnableUsageReminder && App.sQuantityOfElectricity <= usageReminderValue;
if (chargeRemindTrigger) {
LogUtils.d(TAG, String.format("触发充电提醒 | 当前电量=%d ≥ 阈值=%d | threadId=%d", sQuantityOfElectricity, chargeReminderValue, getId()));
sendNotificationMessageInternal(REMIND_TYPE_CHARGE, sQuantityOfElectricity, isCharging);
LogUtils.d(TAG, String.format("触发充电提醒 | 当前电量=%d ≥ 阈值=%d | threadId=%d", App.sQuantityOfElectricity, chargeReminderValue, getId()));
sendNotificationMessageInternal(REMIND_TYPE_CHARGE, App.sQuantityOfElectricity, isCharging);
} else if (usageRemindTrigger) {
LogUtils.d(TAG, String.format("触发耗电提醒 | 当前电量=%d ≤ 阈值=%d | threadId=%d", sQuantityOfElectricity, usageReminderValue, getId()));
sendNotificationMessageInternal(REMIND_TYPE_USAGE, sQuantityOfElectricity, isCharging);
LogUtils.d(TAG, String.format("触发耗电提醒 | 当前电量=%d ≤ 阈值=%d | threadId=%d", App.sQuantityOfElectricity, usageReminderValue, getId()));
sendNotificationMessageInternal(REMIND_TYPE_USAGE, App.sQuantityOfElectricity, isCharging);
} else {
LogUtils.d(TAG, String.format("未有合适类型提醒,退出提醒线程 | threadId=%d", getId()));
break;
@@ -194,7 +193,7 @@ public class RemindThread extends Thread {
safeSleepInternal(sleepTime);
} catch (Exception e) {
LogUtils.e(TAG, String.format("循环运行异常,退出电量提醒线程 | 当前电量=%d | threadId=%d", sQuantityOfElectricity, getId()), e);
LogUtils.e(TAG, String.format("循环运行异常,退出电量提醒线程 | 当前电量=%d | threadId=%d", App.sQuantityOfElectricity, getId()), e);
break;
}
}
@@ -283,7 +282,6 @@ public class RemindThread extends Thread {
LogUtils.d(TAG, String.format("cleanThreadStateInternal() 调用 | threadId=%d", getId()));
isReminding = false;
isExist = true;
sQuantityOfElectricity = INVALID_BATTERY_VALUE;
// 中断当前线程(如果存活)
if (isAlive()) {
interrupt();
@@ -300,7 +298,6 @@ public class RemindThread extends Thread {
LogUtils.d(TAG, String.format("setAppConfigBean() 调用 | config=%s | threadId=%d", config, getId()));
if (config == null) {
LogUtils.e(TAG, String.format("配置同步失败配置Bean为空 | threadId=%d", getId()));
sQuantityOfElectricity = INVALID_BATTERY_VALUE;
return;
}
@@ -315,7 +312,7 @@ public class RemindThread extends Thread {
isCharging = config.isCharging();
LogUtils.d(TAG, String.format("配置同步完成 | 休眠时间=%dms | 充电提醒=%b | 耗电提醒=%b | 当前电量=%d | 充电阈值=%d | 耗电阈值=%d | threadId=%d",
sleepTime, isEnableChargeReminder, isEnableUsageReminder, sQuantityOfElectricity, chargeReminderValue, usageReminderValue, getId()));
sleepTime, isEnableChargeReminder, isEnableUsageReminder, App.sQuantityOfElectricity, chargeReminderValue, usageReminderValue, getId()));
}
/**
@@ -348,7 +345,7 @@ public class RemindThread extends Thread {
", isReminding=" + isReminding +
", chargeThreshold=" + chargeReminderValue +
", usageThreshold=" + usageReminderValue +
", currentBattery=" + sQuantityOfElectricity +
", currentBattery=" + App.sQuantityOfElectricity +
", isCharging=" + isCharging +
", sleepTime=" + sleepTime + "ms" +
'}';

View File

@@ -106,7 +106,7 @@ public class MainUnitTest2Activity extends AppCompatActivity {
// 创建MemoryCachedBackgroundView单例并添加到布局
int nCurrentPixelColor = BackgroundSourceUtils.getInstance(this).getCurrentBackgroundBean().getPixelColor();
mMemoryCachedBackgroundView = MemoryCachedBackgroundView.getInstance(this, nCurrentPixelColor, "", false);
mMemoryCachedBackgroundView = MemoryCachedBackgroundView.getLastInstance(this);
mllBackgroundView.addView(mMemoryCachedBackgroundView);
LogUtils.d(TAG, "initViewAndEvent内存缓存背景视图实例创建并添加完成");

View File

@@ -249,12 +249,12 @@ public class AppConfigUtils {
LogUtils.d(TAG, String.format("setCurrentBatteryValue() 调用 | 传入电量=%d", value));
int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE);
if (calibratedValue == RemindThread.sQuantityOfElectricity) {
if (calibratedValue == App.sQuantityOfElectricity) {
LogUtils.d(TAG, "setCurrentBatteryValue():电池电量无变化,无需操作");
return;
}
RemindThread.sQuantityOfElectricity = calibratedValue;
App.sQuantityOfElectricity = calibratedValue;
LogUtils.d(TAG, String.format("setCurrentBatteryValue() 成功 | 电池电量=%d%%", calibratedValue));
}
@@ -263,7 +263,7 @@ public class AppConfigUtils {
* @return 当前电池电量0-100
*/
public int getCurrentBatteryValue() {
int value = RemindThread.sQuantityOfElectricity;
int value = App.sQuantityOfElectricity;
LogUtils.d(TAG, String.format("getCurrentBatteryValue():获取电池电量=%d%%", value));
return value;
}

View File

@@ -51,6 +51,7 @@ public class BitmapCacheUtils {
*/
private BitmapCacheUtils() {
LogUtils.d(TAG, "【BitmapCacheUtils】单例构造开始");
//App.notifyMessage(TAG, "【BitmapCacheUtils】单例构造开始");
// 使用 ConcurrentHashMap 保证线程安全,避免手动同步
mHardCacheMap = new ConcurrentHashMap<>();
mRefCountMap = new ConcurrentHashMap<>();

View File

@@ -22,7 +22,7 @@ import cc.winboll.studio.powerbell.models.NotificationMessage;
/**
* 通知工具类:统一管理前台服务/电池提醒/应用配置信息通知
* 适配API19-30 | Java7 | 小米手机
* 特性:前台服务无铃声、提醒通知系统默认铃声、配置通知低优先级无打扰、API分级适配、内存泄漏防护
* 特性:前台服务无铃声、提醒通知系统默认铃声、配置通知系统默认铃声无振动、API分级适配、内存泄漏防护
*/
public class NotificationManagerUtils {
// ================================== 静态常量(置顶统一管理,杜绝魔法值)=================================
@@ -48,6 +48,7 @@ public class NotificationManagerUtils {
private static final int PENDING_INTENT_REQUEST_CODE_FOREGROUND = 0;
private static final int PENDING_INTENT_REQUEST_CODE_REMIND = 1;
private static final int PENDING_INTENT_REQUEST_CODE_CONFIG = 2; // 新增:配置通知请求码
private static int snMessageNotificationID = 10000;
// ================================== 成员变量(私有封装,按依赖优先级排序)=================================
// 核心上下文(应用级,避免内存泄漏)
@@ -59,35 +60,35 @@ public class NotificationManagerUtils {
// ================================== 构造方法(初始化核心资源,前置校验)=================================
public NotificationManagerUtils(Context context) {
LogUtils.d(TAG, "NotificationManagerUtils: 构造方法执行 | context=" + context);
LogUtils.d(TAG, "NotificationManagerUtils() 构造 | context=" + context);
// 前置校验Context非空
if (context == null) {
LogUtils.e(TAG, "NotificationManagerUtils: 构造失败context is null");
LogUtils.e(TAG, "NotificationManagerUtils() 构造失败context is null");
return;
}
// 初始化核心资源
this.mContext = context.getApplicationContext();
this.mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
LogUtils.d(TAG, "NotificationManagerUtils: 核心资源初始化完成 | mContext=" + mContext + " | mNotificationManager=" + mNotificationManager);
LogUtils.d(TAG, "NotificationManagerUtils() 核心资源初始化完成 | mContext=" + mContext + " | mNotificationManager=" + mNotificationManager);
// 初始化通知渠道API26+ 必需)
initNotificationChannels();
LogUtils.d(TAG, "NotificationManagerUtils: 构造完成");
LogUtils.d(TAG, "NotificationManagerUtils() 构造完成");
}
// ================================== 核心初始化方法通知渠道API分级适配=================================
/**
* 初始化通知渠道:前台服务渠道(无铃声+无振动)、提醒渠道(系统默认铃声+无振动)、配置信息渠道(低优先级无打扰
* 初始化通知渠道:前台服务渠道(无铃声+无振动)、提醒渠道(系统默认铃声+无振动)、配置信息渠道(系统默认铃声+无振动
*/
private void initNotificationChannels() {
LogUtils.d(TAG, "initNotificationChannels: 执行通知渠道初始化");
LogUtils.d(TAG, "initNotificationChannels() 执行通知渠道初始化");
// API<26 无渠道机制,直接返回
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
LogUtils.d(TAG, "initNotificationChannels: API<26无需创建渠道");
LogUtils.d(TAG, "initNotificationChannels() API<26无需创建渠道");
return;
}
// 通知服务为空,避免空指针
if (mNotificationManager == null) {
LogUtils.e(TAG, "initNotificationChannels: 失败NotificationManager is null");
LogUtils.e(TAG, "initNotificationChannels() 失败NotificationManager is null");
return;
}
@@ -103,7 +104,7 @@ public class NotificationManagerUtils {
foregroundChannel.setSound(null, null); // 强制无铃声
foregroundChannel.setShowBadge(false);
foregroundChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
LogUtils.d(TAG, "initNotificationChannels: 前台服务渠道配置完成");
LogUtils.d(TAG, "initNotificationChannels() 前台服务渠道配置完成");
// 2. 电池提醒渠道(中优先级,系统默认铃声,无振动)
NotificationChannel remindChannel = new NotificationChannel(
@@ -117,27 +118,27 @@ public class NotificationManagerUtils {
remindChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), Notification.AUDIO_ATTRIBUTES_DEFAULT);
remindChannel.setShowBadge(false);
remindChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
LogUtils.d(TAG, "initNotificationChannels: 电池提醒渠道配置完成");
LogUtils.d(TAG, "initNotificationChannels() 电池提醒渠道配置完成");
// 3. 应用配置信息渠道(新增:最低优先级,无铃声无振动,仅提示不打扰
// 3. 应用配置信息渠道(方案1修复默认优先级系统默认铃声无振动)
NotificationChannel configChannel = new NotificationChannel(
CHANNEL_ID_CONFIG,
"应用配置信息",
NotificationManager.IMPORTANCE_MIN
NotificationManager.IMPORTANCE_DEFAULT
);
configChannel.setDescription("应用配置更新、参数变更等提示,无声音、无振动");
configChannel.enableLights(false);
configChannel.setDescription("应用配置更新、参数变更等提示,系统默认铃声、无振动");
configChannel.enableLights(true);
configChannel.enableVibration(false);
configChannel.setSound(null, null);
configChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), Notification.AUDIO_ATTRIBUTES_DEFAULT);
configChannel.setShowBadge(false);
configChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
LogUtils.d(TAG, "initNotificationChannels: 应用配置信息渠道配置完成");
configChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
LogUtils.d(TAG, "initNotificationChannels() 应用配置信息渠道配置完成");
// 注册渠道到系统
mNotificationManager.createNotificationChannel(foregroundChannel);
mNotificationManager.createNotificationChannel(remindChannel);
mNotificationManager.createNotificationChannel(configChannel); // 注册新增渠道
LogUtils.d(TAG, "initNotificationChannels: 成功:创建前台服务+电池提醒+应用配置信息渠道");
LogUtils.d(TAG, "initNotificationChannels() 成功:创建前台服务+电池提醒+应用配置信息渠道");
}
// ================================== 对外核心方法(前台服务通知:启动/更新/取消)=================================
@@ -145,26 +146,26 @@ public class NotificationManagerUtils {
* 启动前台服务通知API30适配无铃声
*/
public void startForegroundServiceNotify(Service service, NotificationMessage message) {
LogUtils.d(TAG, "startForegroundServiceNotify: 执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE + " | service=" + service + " | message=" + message);
LogUtils.d(TAG, "startForegroundServiceNotify() 执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE + " | service=" + service + " | message=" + message);
// 前置校验:参数非空
if (service == null || message == null || mNotificationManager == null) {
LogUtils.e(TAG, "startForegroundServiceNotify: 失败param is null | service=" + service + " | message=" + message + " | mNotificationManager=" + mNotificationManager);
LogUtils.e(TAG, "startForegroundServiceNotify() 失败param is null | service=" + service + " | message=" + message + " | mNotificationManager=" + mNotificationManager);
return;
}
// 构建前台通知
mForegroundServiceNotify = buildForegroundNotification(message);
if (mForegroundServiceNotify == null) {
LogUtils.e(TAG, "startForegroundServiceNotify: 失败:构建通知为空");
LogUtils.e(TAG, "startForegroundServiceNotify() 失败:构建通知为空");
return;
}
// 启动前台服务API30无FOREGROUND_SERVICE_TYPE限制全版本通用
try {
service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
LogUtils.d(TAG, "startForegroundServiceNotify: 成功");
LogUtils.d(TAG, "startForegroundServiceNotify() 成功");
} catch (Exception e) {
LogUtils.e(TAG, "startForegroundServiceNotify: 异常", e);
LogUtils.e(TAG, "startForegroundServiceNotify() 异常", e);
}
}
@@ -172,23 +173,23 @@ public class NotificationManagerUtils {
* 更新前台服务通知内容复用通知ID保持无铃声
*/
public void updateForegroundServiceNotify(NotificationMessage message) {
LogUtils.d(TAG, "updateForegroundServiceNotify: 执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE + " | message=" + message);
LogUtils.d(TAG, "updateForegroundServiceNotify() 执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE + " | message=" + message);
if (message == null || mNotificationManager == null) {
LogUtils.e(TAG, "updateForegroundServiceNotify: 失败param is null | message=" + message + " | mNotificationManager=" + mNotificationManager);
LogUtils.e(TAG, "updateForegroundServiceNotify() 失败param is null | message=" + message + " | mNotificationManager=" + mNotificationManager);
return;
}
mForegroundServiceNotify = buildForegroundNotification(message);
if (mForegroundServiceNotify == null) {
LogUtils.e(TAG, "updateForegroundServiceNotify: 失败:构建通知为空");
LogUtils.e(TAG, "updateForegroundServiceNotify() 失败:构建通知为空");
return;
}
try {
mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
LogUtils.d(TAG, "updateForegroundServiceNotify: 成功");
LogUtils.d(TAG, "updateForegroundServiceNotify() 成功");
} catch (Exception e) {
LogUtils.e(TAG, "updateForegroundServiceNotify: 异常", e);
LogUtils.e(TAG, "updateForegroundServiceNotify() 异常", e);
}
}
@@ -196,10 +197,10 @@ public class NotificationManagerUtils {
* 取消前台服务通知Service销毁时调用
*/
public void cancelForegroundServiceNotify() {
LogUtils.d(TAG, "cancelForegroundServiceNotify: 执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE);
LogUtils.d(TAG, "cancelForegroundServiceNotify() 执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE);
cancelNotification(NOTIFY_ID_FOREGROUND_SERVICE);
mForegroundServiceNotify = null; // 置空释放
LogUtils.d(TAG, "cancelForegroundServiceNotify: 成功");
LogUtils.d(TAG, "cancelForegroundServiceNotify() 成功");
}
// ================================== 对外核心方法(电池提醒通知:发送)=================================
@@ -207,48 +208,70 @@ public class NotificationManagerUtils {
* 发送电池提醒通知(系统默认铃声,无振动)
*/
public void showRemindNotification(Context context, NotificationMessage message) {
LogUtils.d(TAG, "showRemindNotification: 执行 | notifyId=" + NOTIFY_ID_REMIND + " | context=" + context + " | message=" + message);
LogUtils.d(TAG, "showRemindNotification() 执行 | notifyId=" + NOTIFY_ID_REMIND + " | context=" + context + " | message=" + message);
if (context == null || message == null || mNotificationManager == null) {
LogUtils.e(TAG, "showRemindNotification: 失败param is null | context=" + context + " | message=" + message + " | mNotificationManager=" + mNotificationManager);
LogUtils.e(TAG, "showRemindNotification() 失败param is null | context=" + context + " | message=" + message + " | mNotificationManager=" + mNotificationManager);
return;
}
Notification remindNotify = buildRemindNotification(context, message);
if (remindNotify == null) {
LogUtils.e(TAG, "showRemindNotification: 失败:构建通知为空");
LogUtils.e(TAG, "showRemindNotification() 失败:构建通知为空");
return;
}
try {
mNotificationManager.notify(NOTIFY_ID_REMIND, remindNotify);
LogUtils.d(TAG, "showRemindNotification: 成功");
LogUtils.d(TAG, "showRemindNotification() 成功");
} catch (Exception e) {
LogUtils.e(TAG, "showRemindNotification: 异常", e);
LogUtils.e(TAG, "showRemindNotification() 异常", e);
}
}
// ================================== 对外核心方法(应用配置信息通知:发送)=================================
/**
* 发送应用配置信息通知(新增:低优先级无铃声,仅提示不打扰)
*/
public void showConfigNotification(Context context, NotificationMessage message) {
LogUtils.d(TAG, "showConfigNotification: 执行 | notifyId=" + NOTIFY_ID_CONFIG + " | context=" + context + " | message=" + message);
public synchronized void showMessageNotification(Context context, NotificationMessage message) {
snMessageNotificationID++;
LogUtils.d(TAG, "showMessageNotification() 执行 | notifyId=" + snMessageNotificationID + " | context=" + context + " | message=" + message);
if (context == null || message == null || mNotificationManager == null) {
LogUtils.e(TAG, "showConfigNotification: 失败param is null | context=" + context + " | message=" + message + " | mNotificationManager=" + mNotificationManager);
LogUtils.e(TAG, "showMessageNotification() 失败param is null | context=" + context + " | message=" + message + " | mNotificationManager=" + mNotificationManager);
return;
}
Notification configNotify = buildConfigNotification(context, message);
if (configNotify == null) {
LogUtils.e(TAG, "showConfigNotification: 失败:构建通知为空");
LogUtils.e(TAG, "showMessageNotification() 失败:构建通知为空");
return;
}
try {
mNotificationManager.notify(snMessageNotificationID, configNotify);
LogUtils.d(TAG, "showMessageNotification() 成功");
} catch (Exception e) {
LogUtils.e(TAG, "showMessageNotification() 异常", e);
}
}
// ================================== 对外核心方法(应用配置信息通知:发送)=================================
/**
* 发送应用配置信息通知方案1修复系统默认铃声无振动
*/
public void showConfigNotification(Context context, NotificationMessage message) {
LogUtils.d(TAG, "showConfigNotification() 执行 | notifyId=" + NOTIFY_ID_CONFIG + " | context=" + context + " | message=" + message);
if (context == null || message == null || mNotificationManager == null) {
LogUtils.e(TAG, "showConfigNotification() 失败param is null | context=" + context + " | message=" + message + " | mNotificationManager=" + mNotificationManager);
return;
}
Notification configNotify = buildConfigNotification(context, message);
if (configNotify == null) {
LogUtils.e(TAG, "showConfigNotification() 失败:构建通知为空");
return;
}
try {
mNotificationManager.notify(NOTIFY_ID_CONFIG, configNotify);
LogUtils.d(TAG, "showConfigNotification: 成功");
LogUtils.d(TAG, "showConfigNotification() 成功");
} catch (Exception e) {
LogUtils.e(TAG, "showConfigNotification: 异常", e);
LogUtils.e(TAG, "showConfigNotification() 异常", e);
}
}
@@ -257,16 +280,16 @@ public class NotificationManagerUtils {
* 取消指定ID的通知
*/
public void cancelNotification(int notifyId) {
LogUtils.d(TAG, "cancelNotification: 执行 | notifyId=" + notifyId);
LogUtils.d(TAG, "cancelNotification() 执行 | notifyId=" + notifyId);
if (mNotificationManager == null) {
LogUtils.e(TAG, "cancelNotification: 失败NotificationManager is null");
LogUtils.e(TAG, "cancelNotification() 失败NotificationManager is null");
return;
}
try {
mNotificationManager.cancel(notifyId);
LogUtils.d(TAG, "cancelNotification: 成功 | notifyId=" + notifyId);
LogUtils.d(TAG, "cancelNotification() 成功 | notifyId=" + notifyId);
} catch (Exception e) {
LogUtils.e(TAG, "cancelNotification: 异常 | notifyId=" + notifyId, e);
LogUtils.e(TAG, "cancelNotification() 异常 | notifyId=" + notifyId, e);
}
}
@@ -274,16 +297,16 @@ public class NotificationManagerUtils {
* 取消所有通知(兜底场景使用)
*/
public void cancelAllNotifications() {
LogUtils.d(TAG, "cancelAllNotifications: 执行");
LogUtils.d(TAG, "cancelAllNotifications() 执行");
if (mNotificationManager == null) {
LogUtils.e(TAG, "cancelAllNotifications: 失败NotificationManager is null");
LogUtils.e(TAG, "cancelAllNotifications() 失败NotificationManager is null");
return;
}
try {
mNotificationManager.cancelAll();
LogUtils.d(TAG, "cancelAllNotifications: 成功");
LogUtils.d(TAG, "cancelAllNotifications() 成功");
} catch (Exception e) {
LogUtils.e(TAG, "cancelAllNotifications: 异常", e);
LogUtils.e(TAG, "cancelAllNotifications() 异常", e);
}
}
@@ -292,30 +315,30 @@ public class NotificationManagerUtils {
* 构建前台服务通知(全版本无铃声+无振动)
*/
private Notification buildForegroundNotification(NotificationMessage message) {
LogUtils.d(TAG, "buildForegroundNotification: 执行 | message=" + message);
LogUtils.d(TAG, "buildForegroundNotification() 执行 | message=" + message);
if (message == null || mContext == null) {
LogUtils.e(TAG, "buildForegroundNotification: 失败param is null | message=" + message + " | mContext=" + mContext);
LogUtils.e(TAG, "buildForegroundNotification() 失败param is null | message=" + message + " | mContext=" + mContext);
return null;
}
// 内容兜底
String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : FOREGROUND_NOTIFY_TITLE_DEFAULT;
String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : FOREGROUND_NOTIFY_CONTENT_DEFAULT;
LogUtils.d(TAG, "buildForegroundNotification: 内容兜底完成 | title=" + title + " | content=" + content);
LogUtils.d(TAG, "buildForegroundNotification() 内容兜底完成 | title=" + title + " | content=" + content);
Notification.Builder builder;
// API分级构建
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// API26+:绑定前台渠道(渠道已配置无铃声)
builder = new Notification.Builder(mContext, CHANNEL_ID_FOREGROUND);
LogUtils.d(TAG, "buildForegroundNotification: 使用API26+渠道构建");
LogUtils.d(TAG, "buildForegroundNotification() 使用API26+渠道构建");
} else {
// API<26直接构建手动禁用铃声振动
builder = new Notification.Builder(mContext);
builder.setSound(null);
builder.setVibrate(new long[]{0});
builder.setDefaults(0);
LogUtils.d(TAG, "buildForegroundNotification: 使用API<26手动配置");
LogUtils.d(TAG, "buildForegroundNotification() 使用API<26手动配置");
}
// 通用配置
@@ -332,11 +355,11 @@ public class NotificationManagerUtils {
builder.setLargeIcon(getAppIcon(mContext))
.setColor(mContext.getResources().getColor(R.color.colorPrimary))
.setPriority(Notification.PRIORITY_LOW);
LogUtils.d(TAG, "buildForegroundNotification: 补充API21+配置");
LogUtils.d(TAG, "buildForegroundNotification() 补充API21+配置");
}
Notification notification = builder.build();
LogUtils.d(TAG, "buildForegroundNotification: 成功构建前台通知");
LogUtils.d(TAG, "buildForegroundNotification() 成功构建前台通知");
return notification;
}
@@ -345,30 +368,30 @@ public class NotificationManagerUtils {
* 构建电池提醒通知(全版本系统默认铃声+无振动)
*/
private Notification buildRemindNotification(Context context, NotificationMessage message) {
LogUtils.d(TAG, "buildRemindNotification: 执行 | context=" + context + " | message=" + message);
LogUtils.d(TAG, "buildRemindNotification() 执行 | context=" + context + " | message=" + message);
if (context == null || message == null) {
LogUtils.e(TAG, "buildRemindNotification: 失败param is null | context=" + context + " | message=" + message);
LogUtils.e(TAG, "buildRemindNotification() 失败param is null | context=" + context + " | message=" + message);
return null;
}
// 内容兜底
String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : REMIND_NOTIFY_TITLE_DEFAULT;
String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : REMIND_NOTIFY_CONTENT_DEFAULT;
LogUtils.d(TAG, "buildRemindNotification: 内容兜底完成 | title=" + title + " | content=" + content);
LogUtils.d(TAG, "buildRemindNotification() 内容兜底完成 | title=" + title + " | content=" + content);
Notification.Builder builder;
// API分级构建
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// API26+:绑定提醒渠道(渠道已配置默认铃声)
builder = new Notification.Builder(context, CHANNEL_ID_REMIND);
LogUtils.d(TAG, "buildRemindNotification: 使用API26+渠道构建");
LogUtils.d(TAG, "buildRemindNotification() 使用API26+渠道构建");
} else {
// API<26手动配置默认铃声关闭振动
builder = new Notification.Builder(context);
builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI) // 显式默认铃声
.setVibrate(new long[]{0})
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND);
LogUtils.d(TAG, "buildRemindNotification: 使用API<26手动配置");
LogUtils.d(TAG, "buildRemindNotification() 使用API<26手动配置");
}
// 通用配置
@@ -386,43 +409,43 @@ public class NotificationManagerUtils {
builder.setLargeIcon(getAppIcon(context))
.setColor(context.getResources().getColor(R.color.colorPrimary))
.setPriority(Notification.PRIORITY_DEFAULT);
LogUtils.d(TAG, "buildRemindNotification: 补充API21+配置");
LogUtils.d(TAG, "buildRemindNotification() 补充API21+配置");
}
Notification notification = builder.build();
LogUtils.d(TAG, "buildRemindNotification: 成功构建提醒通知");
LogUtils.d(TAG, "buildRemindNotification() 成功构建提醒通知");
return notification;
}
// ================================== 内部辅助方法(通知构建:应用配置信息通知)=================================
/**
* 构建应用配置信息通知(新增:全版本无铃声+无振动,低优先级
* 构建应用配置信息通知(方案1修复全版本系统默认铃声+无振动)
*/
private Notification buildConfigNotification(Context context, NotificationMessage message) {
LogUtils.d(TAG, "buildConfigNotification: 执行 | context=" + context + " | message=" + message);
LogUtils.d(TAG, "buildConfigNotification() 执行 | context=" + context + " | message=" + message);
if (context == null || message == null) {
LogUtils.e(TAG, "buildConfigNotification: 失败param is null | context=" + context + " | message=" + message);
LogUtils.e(TAG, "buildConfigNotification() 失败param is null | context=" + context + " | message=" + message);
return null;
}
// 内容兜底
String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : CONFIG_NOTIFY_TITLE_DEFAULT;
String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : CONFIG_NOTIFY_CONTENT_DEFAULT;
LogUtils.d(TAG, "buildConfigNotification: 内容兜底完成 | title=" + title + " | content=" + content);
LogUtils.d(TAG, "buildConfigNotification() 内容兜底完成 | title=" + title + " | content=" + content);
Notification.Builder builder;
// API分级构建
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// API26+:绑定配置渠道(渠道已配置铃声)
// API26+:绑定配置渠道(渠道已配置默认铃声)
builder = new Notification.Builder(context, CHANNEL_ID_CONFIG);
LogUtils.d(TAG, "buildConfigNotification: 使用API26+渠道构建");
LogUtils.d(TAG, "buildConfigNotification() 使用API26+渠道构建");
} else {
// API<26直接构建,手动禁用铃声振动
// API<26手动配置默认铃声关闭振动方案1修复保留铃声配置删除冗余DEFAULT_SOUND
builder = new Notification.Builder(context);
builder.setSound(null);
builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI);
builder.setVibrate(new long[]{0});
builder.setDefaults(0);
LogUtils.d(TAG, "buildConfigNotification: 使用API<26手动配置");
builder.setDefaults(Notification.DEFAULT_LIGHTS);
LogUtils.d(TAG, "buildConfigNotification() 使用API<26手动配置");
}
// 通用配置
@@ -438,12 +461,12 @@ public class NotificationManagerUtils {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setLargeIcon(getAppIcon(context))
.setColor(context.getResources().getColor(R.color.colorPrimary))
.setPriority(Notification.PRIORITY_MIN); // 最低优先级
LogUtils.d(TAG, "buildConfigNotification: 补充API21+配置");
.setPriority(Notification.PRIORITY_DEFAULT);
LogUtils.d(TAG, "buildConfigNotification() 补充API21+配置");
}
Notification notification = builder.build();
LogUtils.d(TAG, "buildConfigNotification: 成功构建配置信息通知");
LogUtils.d(TAG, "buildConfigNotification() 成功构建配置信息通知");
return notification;
}
@@ -452,20 +475,20 @@ public class NotificationManagerUtils {
* 创建跳转MainActivity的PendingIntentAPI23+ 添加IMMUTABLE标记避免安全异常
*/
private PendingIntent createJumpPendingIntent(Context context, int requestCode) {
LogUtils.d(TAG, "createJumpPendingIntent: 执行 | requestCode=" + requestCode + " | context=" + context);
LogUtils.d(TAG, "createJumpPendingIntent() 执行 | requestCode=" + requestCode + " | context=" + context);
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
LogUtils.d(TAG, "createJumpPendingIntent: 跳转Intent配置完成");
LogUtils.d(TAG, "createJumpPendingIntent() 跳转Intent配置完成");
// API23+ 必需添加IMMUTABLE适配API30安全规范
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
flags |= PendingIntent.FLAG_IMMUTABLE;
LogUtils.d(TAG, "createJumpPendingIntent: 添加FLAG_IMMUTABLE标记API23+");
LogUtils.d(TAG, "createJumpPendingIntent() 添加FLAG_IMMUTABLE标记API23+");
}
PendingIntent pendingIntent = PendingIntent.getActivity(context, requestCode, intent, flags);
LogUtils.d(TAG, "createJumpPendingIntent: 成功 | requestCode=" + requestCode);
LogUtils.d(TAG, "createJumpPendingIntent() 成功 | requestCode=" + requestCode);
return pendingIntent;
}
@@ -474,14 +497,14 @@ public class NotificationManagerUtils {
* 获取APP图标失败返回默认图标
*/
private Bitmap getAppIcon(Context context) {
LogUtils.d(TAG, "getAppIcon: 执行 | context=" + context);
LogUtils.d(TAG, "getAppIcon() 执行 | context=" + context);
try {
PackageInfo pkgInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
Bitmap appIcon = BitmapFactory.decodeResource(context.getResources(), pkgInfo.applicationInfo.icon);
LogUtils.d(TAG, "getAppIcon: 成功:获取应用图标");
LogUtils.d(TAG, "getAppIcon() 成功:获取应用图标");
return appIcon;
} catch (PackageManager.NameNotFoundException e) {
LogUtils.e(TAG, "getAppIcon: 异常:获取应用图标失败,使用默认图标", e);
LogUtils.e(TAG, "getAppIcon() 异常:获取应用图标失败,使用默认图标", e);
return BitmapFactory.decodeResource(context.getResources(), NOTIFICATION_DEFAULT_ICON);
}
}
@@ -491,11 +514,11 @@ public class NotificationManagerUtils {
* 释放资源,销毁时调用
*/
public void release() {
LogUtils.d(TAG, "release: 执行资源释放");
LogUtils.d(TAG, "release() 执行资源释放");
cancelForegroundServiceNotify();
mNotificationManager = null;
mContext = null;
LogUtils.d(TAG, "release: 成功:所有资源已释放");
LogUtils.d(TAG, "release() 成功:所有资源已释放");
}
// ================================== 对外 getter 方法(仅前台通知实例,只读)=================================

View File

@@ -17,9 +17,11 @@ import android.widget.TextView;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.models.BackgroundBean;
import cc.winboll.studio.powerbell.models.ControlCenterServiceBean;
import cc.winboll.studio.powerbell.services.ControlCenterService;
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
/**
* 主页面核心视图封装类:统一管理视图绑定、数据更新、事件监听,解耦 Activity 逻辑
@@ -151,13 +153,20 @@ public class MainContentView {
// 基础布局绑定
mainLayout = (RelativeLayout) rootView.findViewById(R.id.activitymainRelativeLayout1);
mllBackgroundView = (LinearLayout) rootView.findViewById(R.id.ll_backgroundview);
backgroundView = App.sMemoryCachedBackgroundView.getLastInstance(mContext);
backgroundView = App.getInstance().getMemoryCachedBackgroundView().getLastInstance(mContext);
if (backgroundView == null) {
App.sBackgroundSourceUtils.loadSettings();
BackgroundBean backgroundBean = App.sBackgroundSourceUtils.getCurrentBackgroundBean();
backgroundView = App.getInstance().getMemoryCachedBackgroundView().getInstance(mContext, backgroundBean, true);
}
if (backgroundView.getParent() != null) {
((ViewGroup) backgroundView.getParent()).removeView(backgroundView);
LogUtils.d(TAG, "【bindViews】移除背景视图旧父容器");
}
mllBackgroundView.addView(backgroundView);
// 容器布局绑定
// 容器布局绑定
llLeftSeekBar = (LinearLayout) rootView.findViewById(R.id.fragmentmainviewLinearLayout1);
llRightSeekBar = (LinearLayout) rootView.findViewById(R.id.fragmentmainviewLinearLayout2);
// 开关控件绑定

View File

@@ -58,36 +58,27 @@ public class MemoryCachedBackgroundView extends BackgroundView {
* @param isReload 是否强制重新加载图片(路径匹配时仍刷新)
* @return 缓存/新创建的MemoryCachedBackgroundView实例
*/
public static MemoryCachedBackgroundView getInstance(Context context, int bgColor, String imagePath, boolean isReload) {
LogUtils.d(TAG, String.format("getInstance 调用 | 图片路径=%s | 是否重载=%b", imagePath, isReload));
// 空路径校验
if (TextUtils.isEmpty(imagePath)) {
LogUtils.e(TAG, "getInstance: 图片路径为空,创建空实例");
return new MemoryCachedBackgroundView(context);
}
// 1. 路径匹配缓存 → 判断是否强制重载
if (imagePath.equals(sCachedImagePath) && sCachedView != null) {
LogUtils.d(TAG, "getInstance: 路径已缓存,当前缓存实例有效");
if (isReload) {
LogUtils.d(TAG, String.format("getInstance: 强制重载图片 | 路径=%s", imagePath));
sCachedView.loadImage(bgColor, imagePath, isReload);
} else {
LogUtils.d(TAG, String.format("getInstance: 使用缓存实例,无需重载 | 路径=%s", imagePath));
}
return sCachedView;
}
// 2. 路径不匹配/无缓存 → 新建实例并更新静态缓存(核心:保留旧实例,仅更新引用)
LogUtils.d(TAG, String.format("getInstance: 路径未缓存,新建实例(保留旧实例) | 路径=%s", imagePath));
String oldPath = sCachedImagePath;
public static MemoryCachedBackgroundView getInstance(Context context, BackgroundBean bean, boolean isReload) {
LogUtils.d(TAG, String.format("getInstance 调用 | BackgroundBean=%s | 是否重载=%b", bean.toString(), isReload));
//App.notifyMessage(TAG, String.format("getInstance 调用 | BackgroundBean=%s | 是否重载=%b", bean.toString(), isReload));
sCachedView = new MemoryCachedBackgroundView(context);
sCachedImagePath = imagePath;
sCachedView.loadImage(bgColor, imagePath, isReload);
LogUtils.d(TAG, String.format("getInstance: 已更新当前缓存实例,旧实例路径=%s强制保持", oldPath));
sCachedView.loadByBackgroundBean(bean, isReload);
saveLastLoadImagePath(context, getBackgroundBeanImagePath(bean));
LogUtils.d(TAG, String.format("getInstance: 已更新当前缓存实例,旧实例路径=%s强制保持", getBackgroundBeanImagePath(bean)));
//App.notifyMessage(TAG, String.format("getInstance: 已更新当前缓存实例,旧实例路径=%s强制保持", getBackgroundBeanImagePath(bean)));
return sCachedView;
}
static String getBackgroundBeanImagePath(BackgroundBean bean) {
if (bean.isUseBackgroundFile()) {
if (bean.isUseBackgroundScaledCompressFile()) {
return bean.getBackgroundScaledCompressFilePath();
}
return bean.getBackgroundFilePath();
}
return "";
}
// ====================================== 新增功能:获取最后加载的实例(强制缓存版) ======================================
/**
* 获取最后一次loadImage的路径对应的实例强制保持所有实例
@@ -97,28 +88,19 @@ public class MemoryCachedBackgroundView extends BackgroundView {
*/
public static MemoryCachedBackgroundView getLastInstance(Context context) {
LogUtils.d(TAG, "getLastInstance 调用");
//App.notifyMessage(TAG, "getLastInstance 调用");
// 1. 从SP获取最后加载的路径强制保持不自动删除
String lastPath = getLastLoadImagePath(context);
if (TextUtils.isEmpty(lastPath)) {
LogUtils.e(TAG, "getLastInstance: 无最后加载路径,创建空实例");
return new MemoryCachedBackgroundView(context);
}
// 2. 路径匹配当前缓存 → 直接返回
sCachedImagePath = getLastLoadImagePath(context);
String lastPath = getBackgroundBeanImagePath(App.sBackgroundSourceUtils.getCurrentBackgroundBean());
//App.notifyMessage(TAG, String.format("sCachedImagePath : %s", sCachedImagePath));
//App.notifyMessage(TAG, String.format("lastPath : %s", lastPath));
if (lastPath.equals(sCachedImagePath) && sCachedView != null) {
LogUtils.d(TAG, String.format("getLastInstance: 使用最后路径缓存实例 | 路径=%s", lastPath));
//App.notifyMessage(TAG, String.format("getLastInstance: 使用最后路径缓存实例 | 路径=%s", lastPath));
return sCachedView;
}
// 3. 路径不匹配 → 新建实例并更新缓存(保留旧实例)
LogUtils.d(TAG, String.format("getLastInstance: 最后路径未缓存,新建实例并加载(保留旧实例) | 路径=%s", lastPath));
String oldPath = sCachedImagePath;
sCachedView = new MemoryCachedBackgroundView(context);
sCachedImagePath = lastPath;
int nCurrentPixelColor = BackgroundSourceUtils.getInstance(context).getCurrentBackgroundBean().getPixelColor();
sCachedView.loadImage(nCurrentPixelColor, sCachedImagePath, false);
LogUtils.d(TAG, String.format("getLastInstance: 已更新最后路径实例,旧实例路径=%s强制保持", oldPath));
return sCachedView;
//App.notifyMessage(TAG, "getLastInstance 返回 null");
return null;
}
// ====================================== 工具方法SP持久化最后加载路径强制保持版 ======================================