Compare commits

...

13 Commits

15 changed files with 970 additions and 677 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Sat Dec 20 19:28:00 HKT 2025 #Sun Dec 21 13:40:20 HKT 2025
stageCount=12 stageCount=16
libraryProject= libraryProject=
baseVersion=15.14 baseVersion=15.14
publishVersion=15.14.11 publishVersion=15.14.15
buildCount=0 buildCount=0
baseBetaVersion=15.14.12 baseBetaVersion=15.14.16

View File

@@ -4,54 +4,73 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="cc.winboll.studio.powerbell"> package="cc.winboll.studio.powerbell">
<!-- ====================== 原有权限保留 + 补充核心权限 ====================== --> <!-- 运行前台服务 -->
<!-- 运行前台服务(原有保留,补充 Android 12+ 特殊前台服务权限) -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<!-- 开机启动(原有保留,确保自启广播生效) --> <!-- 运行“specialUse”类型的前台服务 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
<!-- 开机启动 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- 显示通知(原有保留,前台服务/保活必备) --> <!-- 显示通知 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<!-- 应用使用统计相关(原有保留,忽略保护权限警告) --> <!-- PACKAGE_USAGE_STATS -->
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<!-- BATTERY_STATS -->
<uses-permission android:name="android.permission.BATTERY_STATS"/> <uses-permission android:name="android.permission.BATTERY_STATS"/>
<!-- 计算应用存储空间 -->
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
<!-- 读取您共享存储空间中的内容 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 修改或删除您共享存储空间中的内容 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- MANAGE_EXTERNAL_STORAGE -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<!-- 请求忽略电池优化 -->
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<!-- 拍摄照片和视频 -->
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission <uses-permission
android:name="android.permission.ACCESS_PACKAGE_USAGE_STATS" android:name="android.permission.ACCESS_PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/> tools:ignore="ProtectedPermissions"/>
<!-- 相机相关(原有保留,补充权限等级声明,避免安装警告) -->
<uses-feature <uses-feature
android:name="android.hardware.camera" android:name="android.hardware.camera"
android:required="false"/> <!-- 非核心功能设为非必须,兼容无相机设备 --> android:required="false"/>
<uses-feature <uses-feature
android:name="android.hardware.camera.autofocus" android:name="android.hardware.camera.autofocus"
android:required="false"/> android:required="false"/>
<uses-permission android:name="android.permission.CAMERA" /> <!-- 补充相机权限原有仅声明feature无权限 -->
<!-- 应用信息相关(原有保留) --> <uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/> <uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
<uses-permission <uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES" android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission"/> tools:ignore="QueryAllPackagesPermission"/>
<!-- 新增:文件管理权限(对应 PermissionUtils 全文件管理逻辑) --> <uses-permission android:name="android.permission.READ_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" /> <!-- API30+ 全文件权限 -->
<!-- 新增:忽略电池优化权限(对应 PermissionUtils 电池优化逻辑,必须声明) --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<!-- 新增API30+ 跳转系统权限页兼容(避免自启/文件权限跳转失败) -->
<queries> <queries>
<!-- 全文件管理权限跳转兼容 -->
<!-- 小米自启权限跳转兼容(精准匹配安全中心包名) --> <package android:name="com.miui.securitycenter"/>
<package android:name="com.miui.securitycenter" />
<!-- 电池优化权限跳转兼容 -->
</queries> </queries>
@@ -68,17 +87,17 @@
android:supportsRtl="true" android:supportsRtl="true"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"> tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<!-- ====================== 页面配置(原有保留,优化 exported 安全) ====================== -->
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:exported="true" android:exported="true"
android:launchMode="singleTask"> android:launchMode="singleTask">
</activity> </activity>
<activity <activity
android:name=".activities.CrashActivity" android:name=".activities.CrashActivity"
android:exported="false"/> <!-- 新增:非外部调用,设为 false提升安全 --> android:exported="false"/>
<activity-alias <activity-alias
android:name=".MainActivityEN1" android:name=".MainActivityEN1"
@@ -87,13 +106,19 @@
android:label="@string/app_name" android:label="@string/app_name"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:enabled="true"> android:enabled="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.app.shortcuts" android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmainen1"/> android:resource="@xml/shortcutsmainen1"/>
</activity-alias> </activity-alias>
<activity-alias <activity-alias
@@ -103,13 +128,19 @@
android:label="@string/app_name_cn1" android:label="@string/app_name_cn1"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:enabled="false"> android:enabled="false">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.app.shortcuts" android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmaincn1"/> android:resource="@xml/shortcutsmaincn1"/>
</activity-alias> </activity-alias>
<activity-alias <activity-alias
@@ -119,53 +150,71 @@
android:label="@string/app_name_cn2" android:label="@string/app_name_cn2"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:enabled="false"> android:enabled="false">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.app.shortcuts" android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmaincn2"/> android:resource="@xml/shortcutsmaincn2"/>
</activity-alias> </activity-alias>
<activity <activity
android:name=".activities.ClearRecordActivity" android:name=".activities.ClearRecordActivity"
android:parentActivityName="cc.winboll.studio.powerbell.MainActivity" android:parentActivityName="cc.winboll.studio.powerbell.MainActivity"
android:launchMode="singleTask" android:launchMode="singleTask"
android:exported="false"/> <!-- 新增:非外部调用,设为 false --> android:exported="false"/>
<activity <activity
android:name=".activities.BackgroundSettingsActivity" android:name=".activities.BackgroundSettingsActivity"
android:parentActivityName="cc.winboll.studio.powerbell.MainActivity" android:parentActivityName="cc.winboll.studio.powerbell.MainActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTask"> android:launchMode="singleTask">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND"/> <action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/jpeg"/> <data android:mimeType="image/jpeg"/>
<data android:mimeType="image/jpg"/> <data android:mimeType="image/jpg"/>
<data android:mimeType="image/png"/> <data android:mimeType="image/png"/>
<data android:mimeType="image/webp"/> <data android:mimeType="image/webp"/>
<data android:mimeType="image/*"/> <data android:mimeType="image/*"/>
</intent-filter> </intent-filter>
</activity> </activity>
<!-- ====================== 广播接收器(优化自启广播,提升保活成功率) ====================== -->
<receiver <receiver
android:name=".receivers.MainReceiver" android:name=".receivers.MainReceiver"
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="true"
android:directBootAware="true"> android:directBootAware="true">
<intent-filter android:priority="1000"> <!-- 新增:广播优先级最高,确保优先接收开机广播 -->
<intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.BOOT_COMPLETED"/>
<!-- 补充:充电/解锁广播,增强后台唤醒机会 -->
<action android:name="android.intent.action.POWER_CONNECTED"/> <action android:name="android.intent.action.POWER_CONNECTED"/>
<action android:name="android.intent.action.USER_PRESENT"/> <action android:name="android.intent.action.USER_PRESENT"/>
</intent-filter> </intent-filter>
</receiver> </receiver>
<!-- ====================== 服务配置(核心优化:前台服务保活,适配 API29-30 ====================== -->
<!-- 核心前台服务ControlCenterService保活核心重点优化 -->
<service <service
android:name=".services.ControlCenterService" android:name=".services.ControlCenterService"
android:priority="1000" android:priority="1000"
@@ -173,67 +222,72 @@
android:exported="false" android:exported="false"
android:process=".controlcenterservice" android:process=".controlcenterservice"
android:foregroundServiceType="dataSync"> android:foregroundServiceType="dataSync">
<!-- 新增Android 12+ 前台服务用途声明(系统强制,否则拦截服务启动) -->
<property <property
android:name="android.app.PROPERTY_SPECIAL_USE_FOREGROUND_SERVICE" android:name="android.app.PROPERTY_SPECIAL_USE_FOREGROUND_SERVICE"
android:value="后台核心功能运行、持续保活" /> <!-- 按实际用途填写,不可空 --> android:value="后台核心功能运行、持续保活"/>
</service> </service>
<!-- 辅助服务AssistantService按需优化增强稳定性 -->
<service <service
android:name=".services.AssistantService" android:name=".services.AssistantService"
android:enabled="true" android:enabled="true"
android:exported="false" android:exported="false"
android:process=".assistantservice"> <!-- 若需前台启动,添加此配置;纯后台可移除 --> android:process=".assistantservice">
<property <property
android:name="android.app.PROPERTY_SPECIAL_USE_FOREGROUND_SERVICE" android:name="android.app.PROPERTY_SPECIAL_USE_FOREGROUND_SERVICE"
android:value="辅助核心功能运行" /> android:value="辅助核心功能运行"/>
</service> </service>
<!-- ====================== 其他配置(原有保留,补充优化) ====================== -->
<meta-data <meta-data
android:name="android.max_aspect" android:name="android.max_aspect"
android:value="4.0"/> android:value="4.0"/>
<!-- 所有非外部调用的 Activity统一设 exported=false提升安全 -->
<activity <activity
android:name=".activities.BatteryReporterActivity" android:name=".activities.BatteryReporterActivity"
android:exported="false"/> android:exported="false"/>
<activity <activity
android:name=".activities.PixelPickerActivity" android:name=".activities.PixelPickerActivity"
android:exported="false"/> android:exported="false"/>
<activity <activity
android:name=".activities.BatteryReportActivity" android:name=".activities.BatteryReportActivity"
android:exported="false"/> android:exported="false"/>
<activity <activity
android:name=".unittest.MainUnitTestActivity" android:name=".unittest.MainUnitTestActivity"
android:exported="false"/> android:exported="false"/>
<activity <activity
android:name=".activities.ShortcutActionActivity" android:name=".activities.ShortcutActionActivity"
android:exported="false"/> android:exported="false"/>
<activity <activity
android:name=".activities.SettingsActivity" android:name=".activities.SettingsActivity"
android:exported="false"/> android:exported="false"/>
<!-- 文件提供者(原有保留,正常使用) -->
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider" android:authorities="${applicationId}.fileprovider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
<meta-data <meta-data
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/> android:resource="@xml/file_provider"/>
</provider> </provider>
<!-- UCrop 第三方页面原有保留exported=true 正常) -->
<activity <activity
android:name="com.yalantis.ucrop.UCropActivity" android:name="com.yalantis.ucrop.UCropActivity"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:exported="true"> android:exported="true">
</activity> </activity>
</application> </application>
</manifest> </manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

View File

@@ -1,5 +1,6 @@
package cc.winboll.studio.powerbell; package cc.winboll.studio.powerbell;
import android.content.ComponentCallbacks2;
import android.content.Context; import android.content.Context;
import android.os.Environment; import android.os.Environment;
import android.os.Handler; import android.os.Handler;
@@ -9,11 +10,13 @@ import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.models.BackgroundBean; import cc.winboll.studio.powerbell.models.BackgroundBean;
import cc.winboll.studio.powerbell.models.NotificationMessage;
import cc.winboll.studio.powerbell.receivers.GlobalApplicationReceiver; import cc.winboll.studio.powerbell.receivers.GlobalApplicationReceiver;
import cc.winboll.studio.powerbell.utils.AppCacheUtils; import cc.winboll.studio.powerbell.utils.AppCacheUtils;
import cc.winboll.studio.powerbell.utils.AppConfigUtils; import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
import cc.winboll.studio.powerbell.utils.BitmapCacheUtils; import cc.winboll.studio.powerbell.utils.BitmapCacheUtils;
import cc.winboll.studio.powerbell.utils.NotificationManagerUtils;
import java.io.File; import java.io.File;
public class App extends GlobalApplication { public class App extends GlobalApplication {
@@ -26,14 +29,20 @@ public class App extends GlobalApplication {
public static final String ACTION_SWITCHTO_CN1 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"; public static final String ACTION_SWITCHTO_CN1 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1";
public static final String ACTION_SWITCHTO_CN2 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"; public static final String ACTION_SWITCHTO_CN2 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2";
// 内存紧张通知常量
private static final String TRIM_MEMORY_NOTIFY_TITLE = "应用使用时内存紧张提醒";
private static final String TRIM_MEMORY_NOTIFY_CONTENT = "由于本应用使用时,系统通知内存紧张程度级别较高,图片缓存功能暂时不启用。";
// 数据配置存储工具 // 数据配置存储工具
static AppConfigUtils _mAppConfigUtils; static AppConfigUtils _mAppConfigUtils;
static AppCacheUtils _mAppCacheUtils; static AppCacheUtils _mAppCacheUtils;
// 新增:全局 Bitmap 缓存工具(常驻内存) // 全局 Bitmap 缓存工具(常驻内存)
public static BitmapCacheUtils _mBitmapCacheUtils; public static BitmapCacheUtils _mBitmapCacheUtils;
GlobalApplicationReceiver mReceiver; GlobalApplicationReceiver mReceiver;
static String szTempDir = ""; static String szTempDir = "";
// 通知工具类实例(用于发送内存紧张通知)
private NotificationManagerUtils mNotificationManager;
public static String getTempDirPath() { public static String getTempDirPath() {
return szTempDir; return szTempDir;
@@ -60,13 +69,17 @@ public class App extends GlobalApplication {
// 设置数据配置存储工具 // 设置数据配置存储工具
_mAppConfigUtils = getAppConfigUtils(this); _mAppConfigUtils = getAppConfigUtils(this);
_mAppCacheUtils = getAppCacheUtils(this); _mAppCacheUtils = getAppCacheUtils(this);
// 初始化全局 Bitmap 缓存工具关键App 启动时初始化,常驻内存) // 初始化全局 Bitmap 缓存工具
_mBitmapCacheUtils = BitmapCacheUtils.getInstance(); _mBitmapCacheUtils = BitmapCacheUtils.getInstance();
// 初始化通知工具类(使用整理后的 NotificationManagerUtils
mNotificationManager = new NotificationManagerUtils(this);
LogUtils.d(TAG, "onCreate: 通知工具类初始化完成");
mReceiver = new GlobalApplicationReceiver(this); mReceiver = new GlobalApplicationReceiver(this);
mReceiver.registerAction(); mReceiver.registerAction();
// ======================== 新增:异步预加载背景图 ======================== // 异步预加载背景图(保持原有逻辑)
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -74,23 +87,19 @@ public class App extends GlobalApplication {
@Override @Override
public void run() { public void run() {
try { try {
// 1. 获取背景源工具类实例
BackgroundSourceUtils bgSourceUtils = BackgroundSourceUtils.getInstance(App.this); BackgroundSourceUtils bgSourceUtils = BackgroundSourceUtils.getInstance(App.this);
if (bgSourceUtils == null) { if (bgSourceUtils == null) {
LogUtils.e(TAG, "preloadBitmap: BackgroundSourceUtils 实例为空"); LogUtils.e(TAG, "preloadBitmap: BackgroundSourceUtils 实例为空");
return; return;
} }
// 2. 获取当前背景Bean
BackgroundBean bgBean = bgSourceUtils.getCurrentBackgroundBean(); BackgroundBean bgBean = bgSourceUtils.getCurrentBackgroundBean();
if (bgBean == null || !bgBean.isUseBackgroundFile()) { if (bgBean == null || !bgBean.isUseBackgroundFile()) {
LogUtils.d(TAG, "preloadBitmap: 无有效背景文件,跳过预加载"); LogUtils.d(TAG, "preloadBitmap: 无有效背景文件,跳过预加载");
return; return;
} }
// 3. 获取背景图路径(优先取压缩图路径)
String bgPath = bgBean.isUseBackgroundScaledCompressFile() String bgPath = bgBean.isUseBackgroundScaledCompressFile()
? bgBean.getBackgroundScaledCompressFilePath() ? bgBean.getBackgroundScaledCompressFilePath()
: bgBean.getBackgroundFilePath(); : bgBean.getBackgroundFilePath();
// 4. 预加载到全局缓存
if (_mBitmapCacheUtils != null) { if (_mBitmapCacheUtils != null) {
_mBitmapCacheUtils.cacheBitmap(bgPath); _mBitmapCacheUtils.cacheBitmap(bgPath);
LogUtils.d(TAG, "preloadBitmap: 应用启动时预加载成功 - " + bgPath); LogUtils.d(TAG, "preloadBitmap: 应用启动时预加载成功 - " + bgPath);
@@ -103,8 +112,7 @@ public class App extends GlobalApplication {
} }
}).start(); }).start();
} }
}, 1000); // 延迟1秒执行避免阻塞应用初始化 }, 1000);
// ======================== 预加载逻辑结束 ========================
} }
// 保持原有方法不变 // 保持原有方法不变
@@ -130,11 +138,91 @@ public class App extends GlobalApplication {
public void onTerminate() { public void onTerminate() {
super.onTerminate(); super.onTerminate();
ToastUtils.release(); ToastUtils.release();
// 释放通知工具类资源,避免内存泄漏
if (mNotificationManager != null) {
mNotificationManager.release();
mNotificationManager = null;
LogUtils.d(TAG, "onTerminate: 通知工具类资源已释放");
}
} }
@Override @Override
public void onTrimMemory(int level) { public void onTrimMemory(int level) {
super.onTrimMemory(level); super.onTrimMemory(level);
LogUtils.d(TAG, "onTrimMemory: 内存等级变化 | level=" + getTrimMemoryLevelDesc(level));
// 仅在中等及以上内存紧张等级发送通知,避免频繁打扰
if (mNotificationManager == null) {
mNotificationManager = new NotificationManagerUtils(this);
}
if (level > ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
sendTrimMemoryNotification(level);
} else {
// 再次缓存 Bitmap 缓存工具
_mBitmapCacheUtils = BitmapCacheUtils.getInstance();
LogUtils.d(TAG, "Bitmap 缓存启用中。");
}
}
/**
* 发送内存紧张通知(完全复用 NotificationManagerUtils 的 showRemindNotification 方法)
*/
private void sendTrimMemoryNotification(int level) {
LogUtils.d(TAG, "sendTrimMemoryNotification: 准备发送内存紧张通知");
// 构建通知消息体
NotificationMessage message = new NotificationMessage();
message.setTitle(TRIM_MEMORY_NOTIFY_TITLE);
message.setContent(String.format("%s [ 缓存紧张级别描述: Level %d | %s ]",TRIM_MEMORY_NOTIFY_CONTENT, level, getTrimMemoryLevelDesc(level)));
// 使用整理后的 NotificationManagerUtils 发送通知(复用提醒渠道配置)
mNotificationManager.showConfigNotification(this, message);
LogUtils.d(TAG, "sendTrimMemoryNotification: 通知已通过 NotificationManagerUtils 发送");
}
/**
* 转换内存等级为可读描述,便于日志调试
* 排序规则:按 ComponentCallbacks2 枚举数值从高到低排列(数值越高,内存越紧张)
*/
/**
* 转换内存等级为可读描述,便于日志调试
* 排序规则:按 ComponentCallbacks2 枚举实际数值10进制从高到低排列
* 数值来源:接口中定义的 16进制注释10进制数值
*/
private String getTrimMemoryLevelDesc(int level) {
switch (level) {
// 数值 800x50应用内存完全紧张补充接口中实际存在的枚举项
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
return "TRIM_MEMORY_COMPLETE应用内存完全紧张";
// 数值 600x3c中等内存紧张
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
return "MODERATE中等内存紧张";
// 数值 400x28应用进入后台
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
return "BACKGROUND应用进入后台";
// 数值 200x14应用UI隐藏
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
return "BACKGROUND应用UI隐藏";
// 数值 150xf应用运行时关键级紧张
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
return "RUNNING_CRITICAL应用运行关键级紧张";
// 数值 100xa应用运行时低内存
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
return "RUNNING_LOW应用运行低内存";
// 数值 50x5应用运行时中等紧张
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
return "RUNNING_MODERATE应用运行中等内存紧张";
// 以下为注释备用项(接口中未提供,按你的原有注释保留)
// 数值 100内存极度紧张系统可能强制杀死应用
// case ComponentCallbacks2.TRIM_MEMORY_URGENT:
// return "URGENT内存极度紧张";
// 数值 20用户正在离开应用如按Home键
// case ComponentCallbacks2.TRIM_MEMORY_USER_LEAVING:
// return "USER_LEAVING用户正在离开应用";
// 未知等级
default:
return "UNKNOWN(" + level + ")";
}
} }
} }

View File

@@ -21,6 +21,7 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import cc.winboll.studio.powerbell.utils.ImageDownloader; import cc.winboll.studio.powerbell.utils.ImageDownloader;
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
import android.text.TextUtils;
/** /**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
@@ -52,7 +53,7 @@ public class NetworkBackgroundDialog extends AlertDialog {
// 按钮点击回调接口Java7 接口实现) // 按钮点击回调接口Java7 接口实现)
public interface OnDialogClickListener { public interface OnDialogClickListener {
void onConfirm(String szConfirmFilePath, String previewFileUrl); // 确认按钮点击 void onConfirm(String szConfirmFilePath); // 确认按钮点击
void onCancel(); // 取消按钮点击 void onCancel(); // 取消按钮点击
} }
@@ -91,7 +92,7 @@ public class NetworkBackgroundDialog extends AlertDialog {
case MSG_IMAGE_LOAD_SUCCESS: case MSG_IMAGE_LOAD_SUCCESS:
// 图片加载成功,获取文件路径并设置背景 // 图片加载成功,获取文件路径并设置背景
mDownloadSavedPath = (String) msg.obj; mDownloadSavedPath = (String) msg.obj;
previewBackground(mDownloadSavedPath); mBackgroundView.loadImage(mDownloadSavedPath);
break; break;
case MSG_IMAGE_LOAD_FAILED: case MSG_IMAGE_LOAD_FAILED:
// 图片加载失败,设置默认背景 // 图片加载失败,设置默认背景
@@ -139,7 +140,7 @@ public class NetworkBackgroundDialog extends AlertDialog {
etURL = (EditText) dialogView.findViewById(R.id.et_url); etURL = (EditText) dialogView.findViewById(R.id.et_url);
mBackgroundView = (BackgroundView) dialogView.findViewById(R.id.bv_background_preview); mBackgroundView = (BackgroundView) dialogView.findViewById(R.id.bv_background_preview);
// 加载初始图片 // 加载初始图片
mBackgroundView.setBackgroundResource(R.drawable.ic_launcher); mBackgroundView.setBackgroundResource(R.drawable.blank100x100);
// 设置按钮点击事件 // 设置按钮点击事件
setButtonClickListeners(); setButtonClickListeners();
} }
@@ -168,13 +169,13 @@ public class NetworkBackgroundDialog extends AlertDialog {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
LogUtils.d("NetworkBackgroundDialog", "确认按钮点击"); LogUtils.d("NetworkBackgroundDialog", "确认按钮点击");
// 确定预览背景资源
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(mContext);
utils.saveFileToPreviewBean(new File(mPreviewFilePath), mPreviewFileUrl);
dismiss(); // 关闭对话框 dismiss(); // 关闭对话框
if(TextUtils.isEmpty(mDownloadSavedPath)) {
ToastUtils.show("未下载图片。");
return;
}
if (listener != null) { if (listener != null) {
listener.onConfirm(mPreviewFilePath, mPreviewFileUrl); listener.onConfirm(mDownloadSavedPath);
} }
} }
}); });
@@ -258,6 +259,7 @@ public class NetworkBackgroundDialog extends AlertDialog {
// 发送消息到主线程,携带图片路径 // 发送消息到主线程,携带图片路径
Message successMsg = mUiHandler.obtainMessage(MSG_IMAGE_LOAD_SUCCESS, savePath); Message successMsg = mUiHandler.obtainMessage(MSG_IMAGE_LOAD_SUCCESS, savePath);
mUiHandler.sendMessage(successMsg); mUiHandler.sendMessage(successMsg);
} }
@Override @Override

View File

@@ -222,10 +222,14 @@ public class RemindThread extends Thread {
} else if (!isCharging && isEnableUsageReminder && quantityOfElectricity <= usageReminderValue) { } else if (!isCharging && isEnableUsageReminder && quantityOfElectricity <= usageReminderValue) {
LogUtils.d(TAG, "触发耗电提醒 | 当前电量=" + quantityOfElectricity + " ≤ 阈值=" + usageReminderValue + " | threadId=" + getId()); LogUtils.d(TAG, "触发耗电提醒 | 当前电量=" + quantityOfElectricity + " ≤ 阈值=" + usageReminderValue + " | threadId=" + getId());
sendNotificationMessageInternal(REMIND_TYPE_USAGE, quantityOfElectricity, isCharging); sendNotificationMessageInternal(REMIND_TYPE_USAGE, quantityOfElectricity, isCharging);
} } else {
// 未有合适类型提醒,退出提醒线程
// 安全休眠,保留中断标记 LogUtils.d(TAG, "未有合适类型提醒,退出提醒线程");
break;
}
// 安全休眠,保留中断标记
safeSleepInternal(sleepTime); safeSleepInternal(sleepTime);
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "循环运行异常,退出电量提醒线程 | 当前电量=" + quantityOfElectricity + " | threadId=" + getId(), e); LogUtils.e(TAG, "循环运行异常,退出电量提醒线程 | 当前电量=" + quantityOfElectricity + " | threadId=" + getId(), e);
break; break;

View File

@@ -1,6 +1,7 @@
package cc.winboll.studio.powerbell.utils; package cc.winboll.studio.powerbell.utils;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.ExifInterface; import android.media.ExifInterface;
import android.net.Uri; import android.net.Uri;
@@ -11,6 +12,7 @@ import androidx.core.content.FileProvider;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.BuildConfig; import cc.winboll.studio.powerbell.BuildConfig;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.models.BackgroundBean; import cc.winboll.studio.powerbell.models.BackgroundBean;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
@@ -181,37 +183,42 @@ public class BackgroundSourceUtils {
/** /**
* 检查背景是否为空并创建空白背景Bean * 检查背景是否为空并创建空白背景Bean
*/ */
boolean checkEmptyBackgroundAndCreateBlankBackgroundBean(BackgroundBean checkBackgroundBean) { public boolean checkEmptyBackgroundAndCreateBlankBackgroundBean(BackgroundBean checkBackgroundBean) {
LogUtils.d(TAG, "【checkEmptyBackgroundAndCreateBlankBackgroundBean调用】开始检查背景Bean"); LogUtils.d(TAG, "【checkEmptyBackgroundAndCreateBlankBackgroundBean调用】开始检查背景Bean");
File fCheckBackgroundFile = new File(checkBackgroundBean.getBackgroundFilePath()); File fCheckBackgroundFile = new File(checkBackgroundBean.getBackgroundFilePath());
if (!fCheckBackgroundFile.exists()) { if (!fCheckBackgroundFile.exists()) {
String newCropFileName = genNewCropFileName(); return createBlankBackgroundBean(checkBackgroundBean.getPixelColor());
String fileSuffix = "png";
mCropSourceFile = new File(fCropCacheDir, newCropFileName + "." + fileSuffix);
mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + "." + fileSuffix);
AssetsCopyUtils.copyAssetsFileToDir(mContext, "images/blank10x10.png", mCropSourceFile.getAbsolutePath());
try {
mCropResultFile.createNewFile();
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
loadSettings();
previewBackgroundBean.setIsUseBackgroundFile(true);
previewBackgroundBean.setIsUseBackgroundScaledCompressFile(false);
previewBackgroundBean.setBackgroundFileName(mCropSourceFile.getName());
previewBackgroundBean.setBackgroundFilePath(mCropSourceFile.getAbsolutePath());
previewBackgroundBean.setBackgroundScaledCompressFileName(mCropResultFile.getName());
previewBackgroundBean.setBackgroundScaledCompressFilePath(mCropResultFile.getAbsolutePath());
saveSettings();
LogUtils.d(TAG, "背景Bean为空已创建空白背景并更新配置");
return true;
} }
LogUtils.d(TAG, "背景Bean文件存在无需创建空白背景"); LogUtils.d(TAG, "背景Bean文件存在无需创建空白背景");
return false; return false;
} }
public boolean createBlankBackgroundBean(int nBackgroundPixelColor) {
String newCropFileName = genNewCropFileName();
String fileSuffix = "png";
mCropSourceFile = new File(fCropCacheDir, newCropFileName + "." + fileSuffix);
mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + "." + fileSuffix);
AssetsCopyUtils.copyAssetsFileToDir(mContext, "images/blank100x100.png", mCropSourceFile.getAbsolutePath());
try {
mCropResultFile.createNewFile();
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
loadSettings();
previewBackgroundBean.setPixelColor(nBackgroundPixelColor);
previewBackgroundBean.setIsUseBackgroundFile(true);
previewBackgroundBean.setIsUseBackgroundScaledCompressFile(false);
previewBackgroundBean.setBackgroundFileName(mCropSourceFile.getName());
previewBackgroundBean.setBackgroundFilePath(mCropSourceFile.getAbsolutePath());
previewBackgroundBean.setBackgroundScaledCompressFileName(mCropResultFile.getName());
previewBackgroundBean.setBackgroundScaledCompressFilePath(mCropResultFile.getAbsolutePath());
saveSettings();
LogUtils.d(TAG, "背景Bean为空已创建空白背景并更新配置");
return true;
}
String genNewCropFileName() { String genNewCropFileName() {
return UUID.randomUUID().toString() + System.currentTimeMillis(); return UUID.randomUUID().toString() + System.currentTimeMillis();
} }

View File

@@ -20,9 +20,9 @@ import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.models.NotificationMessage; import cc.winboll.studio.powerbell.models.NotificationMessage;
/** /**
* 通知工具类:统一管理前台服务/电池提醒通知 * 通知工具类:统一管理前台服务/电池提醒/应用配置信息通知
* 适配API19-30 | Java7 | 小米手机 * 适配API19-30 | Java7 | 小米手机
* 特性前台服务无铃声、提醒通知系统默认铃声、API分级适配、内存泄漏防护 * 特性:前台服务无铃声、提醒通知系统默认铃声、配置通知低优先级无打扰、API分级适配、内存泄漏防护
*/ */
public class NotificationManagerUtils { public class NotificationManagerUtils {
// ================================== 静态常量(置顶统一管理,杜绝魔法值)================================= // ================================== 静态常量(置顶统一管理,杜绝魔法值)=================================
@@ -30,9 +30,11 @@ public class NotificationManagerUtils {
// 通知渠道IDAPI26+ 必需,区分通知类型) // 通知渠道IDAPI26+ 必需,区分通知类型)
public static final String CHANNEL_ID_FOREGROUND = "cc.winboll.studio.powerbell.channel.foreground"; public static final String CHANNEL_ID_FOREGROUND = "cc.winboll.studio.powerbell.channel.foreground";
public static final String CHANNEL_ID_REMIND = "cc.winboll.studio.powerbell.channel.remind"; public static final String CHANNEL_ID_REMIND = "cc.winboll.studio.powerbell.channel.remind";
public static final String CHANNEL_ID_CONFIG = "cc.winboll.studio.powerbell.channel.config"; // 新增:应用配置信息渠道
// 通知ID唯一标识避免重复 // 通知ID唯一标识避免重复
public static final int NOTIFY_ID_FOREGROUND_SERVICE = 1001; public static final int NOTIFY_ID_FOREGROUND_SERVICE = 1001;
public static final int NOTIFY_ID_REMIND = 1002; public static final int NOTIFY_ID_REMIND = 1002;
public static final int NOTIFY_ID_CONFIG = 1003; // 新增应用配置信息通知ID
// 低版本兼容默认通知图标API<21 避免显示异常) // 低版本兼容默认通知图标API<21 避免显示异常)
private static final int NOTIFICATION_DEFAULT_ICON = R.drawable.ic_launcher; private static final int NOTIFICATION_DEFAULT_ICON = R.drawable.ic_launcher;
// 通知内容兜底常量 // 通知内容兜底常量
@@ -40,9 +42,12 @@ public class NotificationManagerUtils {
private static final String FOREGROUND_NOTIFY_CONTENT_DEFAULT = "后台监测电池状态"; private static final String FOREGROUND_NOTIFY_CONTENT_DEFAULT = "后台监测电池状态";
private static final String REMIND_NOTIFY_TITLE_DEFAULT = "电池状态提醒"; private static final String REMIND_NOTIFY_TITLE_DEFAULT = "电池状态提醒";
private static final String REMIND_NOTIFY_CONTENT_DEFAULT = "电池状态异常,请及时处理"; private static final String REMIND_NOTIFY_CONTENT_DEFAULT = "电池状态异常,请及时处理";
private static final String CONFIG_NOTIFY_TITLE_DEFAULT = "应用配置更新"; // 新增:配置通知默认标题
private static final String CONFIG_NOTIFY_CONTENT_DEFAULT = "配置信息已更新,生效中"; // 新增:配置通知默认内容
// PendingIntent请求码 // PendingIntent请求码
private static final int PENDING_INTENT_REQUEST_CODE_FOREGROUND = 0; 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_REMIND = 1;
private static final int PENDING_INTENT_REQUEST_CODE_CONFIG = 2; // 新增:配置通知请求码
// ================================== 成员变量(私有封装,按依赖优先级排序)================================= // ================================== 成员变量(私有封装,按依赖优先级排序)=================================
// 核心上下文(应用级,避免内存泄漏) // 核心上下文(应用级,避免内存泄漏)
@@ -54,33 +59,35 @@ public class NotificationManagerUtils {
// ================================== 构造方法(初始化核心资源,前置校验)================================= // ================================== 构造方法(初始化核心资源,前置校验)=================================
public NotificationManagerUtils(Context context) { public NotificationManagerUtils(Context context) {
LogUtils.d(TAG, "构造方法执行"); LogUtils.d(TAG, "NotificationManagerUtils: 构造方法执行 | context=" + context);
// 前置校验Context非空 // 前置校验Context非空
if (context == null) { if (context == null) {
LogUtils.e(TAG, "构造失败context is null"); LogUtils.e(TAG, "NotificationManagerUtils: 构造失败context is null");
return; return;
} }
// 初始化核心资源 // 初始化核心资源
this.mContext = context.getApplicationContext(); this.mContext = context.getApplicationContext();
this.mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); this.mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
LogUtils.d(TAG, "NotificationManagerUtils: 核心资源初始化完成 | mContext=" + mContext + " | mNotificationManager=" + mNotificationManager);
// 初始化通知渠道API26+ 必需) // 初始化通知渠道API26+ 必需)
initNotificationChannels(); initNotificationChannels();
LogUtils.d(TAG, "构造完成:核心资源初始化成功"); LogUtils.d(TAG, "NotificationManagerUtils: 构造完成");
} }
// ================================== 核心初始化方法通知渠道API分级适配================================= // ================================== 核心初始化方法通知渠道API分级适配=================================
/** /**
* 初始化通知渠道:前台服务渠道(无铃声+无振动)、提醒渠道(系统默认铃声+无振动) * 初始化通知渠道:前台服务渠道(无铃声+无振动)、提醒渠道(系统默认铃声+无振动)、配置信息渠道(低优先级无打扰)
*/ */
private void initNotificationChannels() { private void initNotificationChannels() {
LogUtils.d(TAG, "initNotificationChannels: 执行通知渠道初始化");
// API<26 无渠道机制,直接返回 // API<26 无渠道机制,直接返回
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
LogUtils.d(TAG, "initNotificationChannelsAPI<26无需创建渠道"); LogUtils.d(TAG, "initNotificationChannels: API<26无需创建渠道");
return; return;
} }
// 通知服务为空,避免空指针 // 通知服务为空,避免空指针
if (mNotificationManager == null) { if (mNotificationManager == null) {
LogUtils.e(TAG, "initNotificationChannels失败NotificationManager is null"); LogUtils.e(TAG, "initNotificationChannels: 失败NotificationManager is null");
return; return;
} }
@@ -96,6 +103,7 @@ public class NotificationManagerUtils {
foregroundChannel.setSound(null, null); // 强制无铃声 foregroundChannel.setSound(null, null); // 强制无铃声
foregroundChannel.setShowBadge(false); foregroundChannel.setShowBadge(false);
foregroundChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); foregroundChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
LogUtils.d(TAG, "initNotificationChannels: 前台服务渠道配置完成");
// 2. 电池提醒渠道(中优先级,系统默认铃声,无振动) // 2. 电池提醒渠道(中优先级,系统默认铃声,无振动)
NotificationChannel remindChannel = new NotificationChannel( NotificationChannel remindChannel = new NotificationChannel(
@@ -109,11 +117,27 @@ public class NotificationManagerUtils {
remindChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), Notification.AUDIO_ATTRIBUTES_DEFAULT); remindChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), Notification.AUDIO_ATTRIBUTES_DEFAULT);
remindChannel.setShowBadge(false); remindChannel.setShowBadge(false);
remindChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); remindChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
LogUtils.d(TAG, "initNotificationChannels: 电池提醒渠道配置完成");
// 3. 应用配置信息渠道(新增:最低优先级,无铃声无振动,仅提示不打扰)
NotificationChannel configChannel = new NotificationChannel(
CHANNEL_ID_CONFIG,
"应用配置信息",
NotificationManager.IMPORTANCE_MIN
);
configChannel.setDescription("应用配置更新、参数变更等提示,无声音、无振动");
configChannel.enableLights(false);
configChannel.enableVibration(false);
configChannel.setSound(null, null);
configChannel.setShowBadge(false);
configChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
LogUtils.d(TAG, "initNotificationChannels: 应用配置信息渠道配置完成");
// 注册渠道到系统 // 注册渠道到系统
mNotificationManager.createNotificationChannel(foregroundChannel); mNotificationManager.createNotificationChannel(foregroundChannel);
mNotificationManager.createNotificationChannel(remindChannel); mNotificationManager.createNotificationChannel(remindChannel);
LogUtils.d(TAG, "initNotificationChannels成功创建前台服务+电池提醒渠道"); mNotificationManager.createNotificationChannel(configChannel); // 注册新增渠道
LogUtils.d(TAG, "initNotificationChannels: 成功:创建前台服务+电池提醒+应用配置信息渠道");
} }
// ================================== 对外核心方法(前台服务通知:启动/更新/取消)================================= // ================================== 对外核心方法(前台服务通知:启动/更新/取消)=================================
@@ -121,26 +145,26 @@ public class NotificationManagerUtils {
* 启动前台服务通知API30适配无铃声 * 启动前台服务通知API30适配无铃声
*/ */
public void startForegroundServiceNotify(Service service, NotificationMessage message) { public void startForegroundServiceNotify(Service service, NotificationMessage message) {
LogUtils.d(TAG, "startForegroundServiceNotify执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE); LogUtils.d(TAG, "startForegroundServiceNotify: 执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE + " | service=" + service + " | message=" + message);
// 前置校验:参数非空 // 前置校验:参数非空
if (service == null || message == null || mNotificationManager == null) { 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; return;
} }
// 构建前台通知 // 构建前台通知
mForegroundServiceNotify = buildForegroundNotification(message); mForegroundServiceNotify = buildForegroundNotification(message);
if (mForegroundServiceNotify == null) { if (mForegroundServiceNotify == null) {
LogUtils.e(TAG, "startForegroundServiceNotify失败构建通知为空"); LogUtils.e(TAG, "startForegroundServiceNotify: 失败:构建通知为空");
return; return;
} }
// 启动前台服务API30无FOREGROUND_SERVICE_TYPE限制全版本通用 // 启动前台服务API30无FOREGROUND_SERVICE_TYPE限制全版本通用
try { try {
service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify); service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
LogUtils.d(TAG, "startForegroundServiceNotify成功"); LogUtils.d(TAG, "startForegroundServiceNotify: 成功");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "startForegroundServiceNotify异常", e); LogUtils.e(TAG, "startForegroundServiceNotify: 异常", e);
} }
} }
@@ -148,23 +172,23 @@ public class NotificationManagerUtils {
* 更新前台服务通知内容复用通知ID保持无铃声 * 更新前台服务通知内容复用通知ID保持无铃声
*/ */
public void updateForegroundServiceNotify(NotificationMessage message) { public void updateForegroundServiceNotify(NotificationMessage message) {
LogUtils.d(TAG, "updateForegroundServiceNotify执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE); LogUtils.d(TAG, "updateForegroundServiceNotify: 执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE + " | message=" + message);
if (message == null || mNotificationManager == null) { 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; return;
} }
mForegroundServiceNotify = buildForegroundNotification(message); mForegroundServiceNotify = buildForegroundNotification(message);
if (mForegroundServiceNotify == null) { if (mForegroundServiceNotify == null) {
LogUtils.e(TAG, "updateForegroundServiceNotify失败构建通知为空"); LogUtils.e(TAG, "updateForegroundServiceNotify: 失败:构建通知为空");
return; return;
} }
try { try {
mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify); mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
LogUtils.d(TAG, "updateForegroundServiceNotify成功"); LogUtils.d(TAG, "updateForegroundServiceNotify: 成功");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "updateForegroundServiceNotify异常", e); LogUtils.e(TAG, "updateForegroundServiceNotify: 异常", e);
} }
} }
@@ -172,10 +196,10 @@ public class NotificationManagerUtils {
* 取消前台服务通知Service销毁时调用 * 取消前台服务通知Service销毁时调用
*/ */
public void cancelForegroundServiceNotify() { 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); cancelNotification(NOTIFY_ID_FOREGROUND_SERVICE);
mForegroundServiceNotify = null; // 置空释放 mForegroundServiceNotify = null; // 置空释放
LogUtils.d(TAG, "cancelForegroundServiceNotify成功"); LogUtils.d(TAG, "cancelForegroundServiceNotify: 成功");
} }
// ================================== 对外核心方法(电池提醒通知:发送)================================= // ================================== 对外核心方法(电池提醒通知:发送)=================================
@@ -183,23 +207,48 @@ public class NotificationManagerUtils {
* 发送电池提醒通知(系统默认铃声,无振动) * 发送电池提醒通知(系统默认铃声,无振动)
*/ */
public void showRemindNotification(Context context, NotificationMessage message) { public void showRemindNotification(Context context, NotificationMessage message) {
LogUtils.d(TAG, "showRemindNotification执行 | notifyId=" + NOTIFY_ID_REMIND); LogUtils.d(TAG, "showRemindNotification: 执行 | notifyId=" + NOTIFY_ID_REMIND + " | context=" + context + " | message=" + message);
if (context == null || message == null || mNotificationManager == null) { 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; return;
} }
Notification remindNotify = buildRemindNotification(context, message); Notification remindNotify = buildRemindNotification(context, message);
if (remindNotify == null) { if (remindNotify == null) {
LogUtils.e(TAG, "showRemindNotification失败构建通知为空"); LogUtils.e(TAG, "showRemindNotification: 失败:构建通知为空");
return; return;
} }
try { try {
mNotificationManager.notify(NOTIFY_ID_REMIND, remindNotify); mNotificationManager.notify(NOTIFY_ID_REMIND, remindNotify);
LogUtils.d(TAG, "showRemindNotification成功"); LogUtils.d(TAG, "showRemindNotification: 成功");
} catch (Exception e) { } 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);
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: 成功");
} catch (Exception e) {
LogUtils.e(TAG, "showConfigNotification: 异常", e);
} }
} }
@@ -208,16 +257,16 @@ public class NotificationManagerUtils {
* 取消指定ID的通知 * 取消指定ID的通知
*/ */
public void cancelNotification(int notifyId) { public void cancelNotification(int notifyId) {
LogUtils.d(TAG, "cancelNotification执行 | notifyId=" + notifyId); LogUtils.d(TAG, "cancelNotification: 执行 | notifyId=" + notifyId);
if (mNotificationManager == null) { if (mNotificationManager == null) {
LogUtils.e(TAG, "cancelNotification失败NotificationManager is null"); LogUtils.e(TAG, "cancelNotification: 失败NotificationManager is null");
return; return;
} }
try { try {
mNotificationManager.cancel(notifyId); mNotificationManager.cancel(notifyId);
LogUtils.d(TAG, "cancelNotification成功 | notifyId=" + notifyId); LogUtils.d(TAG, "cancelNotification: 成功 | notifyId=" + notifyId);
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "cancelNotification异常 | notifyId=" + notifyId, e); LogUtils.e(TAG, "cancelNotification: 异常 | notifyId=" + notifyId, e);
} }
} }
@@ -225,16 +274,16 @@ public class NotificationManagerUtils {
* 取消所有通知(兜底场景使用) * 取消所有通知(兜底场景使用)
*/ */
public void cancelAllNotifications() { public void cancelAllNotifications() {
LogUtils.d(TAG, "cancelAllNotifications执行"); LogUtils.d(TAG, "cancelAllNotifications: 执行");
if (mNotificationManager == null) { if (mNotificationManager == null) {
LogUtils.e(TAG, "cancelAllNotifications失败NotificationManager is null"); LogUtils.e(TAG, "cancelAllNotifications: 失败NotificationManager is null");
return; return;
} }
try { try {
mNotificationManager.cancelAll(); mNotificationManager.cancelAll();
LogUtils.d(TAG, "cancelAllNotifications成功"); LogUtils.d(TAG, "cancelAllNotifications: 成功");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "cancelAllNotifications异常", e); LogUtils.e(TAG, "cancelAllNotifications: 异常", e);
} }
} }
@@ -243,28 +292,30 @@ public class NotificationManagerUtils {
* 构建前台服务通知(全版本无铃声+无振动) * 构建前台服务通知(全版本无铃声+无振动)
*/ */
private Notification buildForegroundNotification(NotificationMessage message) { private Notification buildForegroundNotification(NotificationMessage message) {
LogUtils.d(TAG, "buildForegroundNotification执行"); LogUtils.d(TAG, "buildForegroundNotification: 执行 | message=" + message);
if (message == null || mContext == null) { 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; return null;
} }
// 内容兜底 // 内容兜底
String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : FOREGROUND_NOTIFY_TITLE_DEFAULT; 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; String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : FOREGROUND_NOTIFY_CONTENT_DEFAULT;
LogUtils.d(TAG, "buildForegroundNotificationtitle=" + title + " | content=" + content); LogUtils.d(TAG, "buildForegroundNotification: 内容兜底完成 | title=" + title + " | content=" + content);
Notification.Builder builder; Notification.Builder builder;
// API分级构建 // API分级构建
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// API26+:绑定前台渠道(渠道已配置无铃声) // API26+:绑定前台渠道(渠道已配置无铃声)
builder = new Notification.Builder(mContext, CHANNEL_ID_FOREGROUND); builder = new Notification.Builder(mContext, CHANNEL_ID_FOREGROUND);
LogUtils.d(TAG, "buildForegroundNotification: 使用API26+渠道构建");
} else { } else {
// API<26直接构建手动禁用铃声振动 // API<26直接构建手动禁用铃声振动
builder = new Notification.Builder(mContext); builder = new Notification.Builder(mContext);
builder.setSound(null); builder.setSound(null);
builder.setVibrate(new long[]{0}); builder.setVibrate(new long[]{0});
builder.setDefaults(0); builder.setDefaults(0);
LogUtils.d(TAG, "buildForegroundNotification: 使用API<26手动配置");
} }
// 通用配置 // 通用配置
@@ -281,9 +332,12 @@ public class NotificationManagerUtils {
builder.setLargeIcon(getAppIcon(mContext)) builder.setLargeIcon(getAppIcon(mContext))
.setColor(mContext.getResources().getColor(R.color.colorPrimary)) .setColor(mContext.getResources().getColor(R.color.colorPrimary))
.setPriority(Notification.PRIORITY_LOW); .setPriority(Notification.PRIORITY_LOW);
LogUtils.d(TAG, "buildForegroundNotification: 补充API21+配置");
} }
return builder.build(); Notification notification = builder.build();
LogUtils.d(TAG, "buildForegroundNotification: 成功构建前台通知");
return notification;
} }
// ================================== 内部辅助方法(通知构建:电池提醒通知)================================= // ================================== 内部辅助方法(通知构建:电池提醒通知)=================================
@@ -291,28 +345,30 @@ public class NotificationManagerUtils {
* 构建电池提醒通知(全版本系统默认铃声+无振动) * 构建电池提醒通知(全版本系统默认铃声+无振动)
*/ */
private Notification buildRemindNotification(Context context, NotificationMessage message) { private Notification buildRemindNotification(Context context, NotificationMessage message) {
LogUtils.d(TAG, "buildRemindNotification执行"); LogUtils.d(TAG, "buildRemindNotification: 执行 | context=" + context + " | message=" + message);
if (context == null || message == null) { 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; return null;
} }
// 内容兜底 // 内容兜底
String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : REMIND_NOTIFY_TITLE_DEFAULT; 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; String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : REMIND_NOTIFY_CONTENT_DEFAULT;
LogUtils.d(TAG, "buildRemindNotificationtitle=" + title + " | content=" + content); LogUtils.d(TAG, "buildRemindNotification: 内容兜底完成 | title=" + title + " | content=" + content);
Notification.Builder builder; Notification.Builder builder;
// API分级构建 // API分级构建
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// API26+:绑定提醒渠道(渠道已配置默认铃声) // API26+:绑定提醒渠道(渠道已配置默认铃声)
builder = new Notification.Builder(context, CHANNEL_ID_REMIND); builder = new Notification.Builder(context, CHANNEL_ID_REMIND);
LogUtils.d(TAG, "buildRemindNotification: 使用API26+渠道构建");
} else { } else {
// API<26手动配置默认铃声关闭振动 // API<26手动配置默认铃声关闭振动
builder = new Notification.Builder(context); builder = new Notification.Builder(context);
builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI) // 显式默认铃声 builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI) // 显式默认铃声
.setVibrate(new long[]{0}) .setVibrate(new long[]{0})
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND); .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND);
LogUtils.d(TAG, "buildRemindNotification: 使用API<26手动配置");
} }
// 通用配置 // 通用配置
@@ -330,9 +386,65 @@ public class NotificationManagerUtils {
builder.setLargeIcon(getAppIcon(context)) builder.setLargeIcon(getAppIcon(context))
.setColor(context.getResources().getColor(R.color.colorPrimary)) .setColor(context.getResources().getColor(R.color.colorPrimary))
.setPriority(Notification.PRIORITY_DEFAULT); .setPriority(Notification.PRIORITY_DEFAULT);
LogUtils.d(TAG, "buildRemindNotification: 补充API21+配置");
} }
return builder.build(); Notification notification = builder.build();
LogUtils.d(TAG, "buildRemindNotification: 成功构建提醒通知");
return notification;
}
// ================================== 内部辅助方法(通知构建:应用配置信息通知)=================================
/**
* 构建应用配置信息通知(新增:全版本无铃声+无振动,低优先级)
*/
private Notification buildConfigNotification(Context context, NotificationMessage 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);
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);
Notification.Builder builder;
// API分级构建
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// API26+:绑定配置渠道(渠道已配置无铃声)
builder = new Notification.Builder(context, CHANNEL_ID_CONFIG);
LogUtils.d(TAG, "buildConfigNotification: 使用API26+渠道构建");
} else {
// API<26直接构建手动禁用铃声振动
builder = new Notification.Builder(context);
builder.setSound(null);
builder.setVibrate(new long[]{0});
builder.setDefaults(0);
LogUtils.d(TAG, "buildConfigNotification: 使用API<26手动配置");
}
// 通用配置
builder.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setContentTitle(title)
.setContentText(content)
.setAutoCancel(true) // 点击关闭
.setOngoing(false)
.setWhen(System.currentTimeMillis())
.setContentIntent(createJumpPendingIntent(context, PENDING_INTENT_REQUEST_CODE_CONFIG));
// API21+ 新增大图标+主题色
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+配置");
}
Notification notification = builder.build();
LogUtils.d(TAG, "buildConfigNotification: 成功构建配置信息通知");
return notification;
} }
// ================================== 内部辅助方法创建跳转PendingIntentAPI30安全适配================================= // ================================== 内部辅助方法创建跳转PendingIntentAPI30安全适配=================================
@@ -340,19 +452,20 @@ public class NotificationManagerUtils {
* 创建跳转MainActivity的PendingIntentAPI23+ 添加IMMUTABLE标记避免安全异常 * 创建跳转MainActivity的PendingIntentAPI23+ 添加IMMUTABLE标记避免安全异常
*/ */
private PendingIntent createJumpPendingIntent(Context context, int requestCode) { private PendingIntent createJumpPendingIntent(Context context, int requestCode) {
LogUtils.d(TAG, "createJumpPendingIntent执行 | requestCode=" + requestCode); LogUtils.d(TAG, "createJumpPendingIntent: 执行 | requestCode=" + requestCode + " | context=" + context);
Intent intent = new Intent(context, MainActivity.class); Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
LogUtils.d(TAG, "createJumpPendingIntent: 跳转Intent配置完成");
// API23+ 必需添加IMMUTABLE适配API30安全规范 // API23+ 必需添加IMMUTABLE适配API30安全规范
int flags = PendingIntent.FLAG_UPDATE_CURRENT; int flags = PendingIntent.FLAG_UPDATE_CURRENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
flags |= PendingIntent.FLAG_IMMUTABLE; flags |= PendingIntent.FLAG_IMMUTABLE;
LogUtils.d(TAG, "createJumpPendingIntent添加FLAG_IMMUTABLE标记"); LogUtils.d(TAG, "createJumpPendingIntent: 添加FLAG_IMMUTABLE标记API23+");
} }
PendingIntent pendingIntent = PendingIntent.getActivity(context, requestCode, intent, flags); PendingIntent pendingIntent = PendingIntent.getActivity(context, requestCode, intent, flags);
LogUtils.d(TAG, "createJumpPendingIntent成功 | requestCode=" + requestCode); LogUtils.d(TAG, "createJumpPendingIntent: 成功 | requestCode=" + requestCode);
return pendingIntent; return pendingIntent;
} }
@@ -361,33 +474,33 @@ public class NotificationManagerUtils {
* 获取APP图标失败返回默认图标 * 获取APP图标失败返回默认图标
*/ */
private Bitmap getAppIcon(Context context) { private Bitmap getAppIcon(Context context) {
LogUtils.d(TAG, "getAppIcon执行"); LogUtils.d(TAG, "getAppIcon: 执行 | context=" + context);
try { try {
PackageInfo pkgInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); PackageInfo pkgInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
Bitmap appIcon = BitmapFactory.decodeResource(context.getResources(), pkgInfo.applicationInfo.icon); Bitmap appIcon = BitmapFactory.decodeResource(context.getResources(), pkgInfo.applicationInfo.icon);
LogUtils.d(TAG, "getAppIcon成功获取应用图标"); LogUtils.d(TAG, "getAppIcon: 成功:获取应用图标");
return appIcon; return appIcon;
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
LogUtils.e(TAG, "getAppIcon异常获取应用图标失败使用默认图标", e); LogUtils.e(TAG, "getAppIcon: 异常:获取应用图标失败,使用默认图标", e);
return BitmapFactory.decodeResource(context.getResources(), NOTIFICATION_DEFAULT_ICON); return BitmapFactory.decodeResource(context.getResources(), NOTIFICATION_DEFAULT_ICON);
} }
} }
// ================================== 对外 getter 方法(仅前台通知实例,只读)=================================
public Notification getForegroundServiceNotify() {
return mForegroundServiceNotify;
}
// ================================== 资源释放方法(避免内存泄漏)================================= // ================================== 资源释放方法(避免内存泄漏)=================================
/** /**
* 释放资源,销毁时调用 * 释放资源,销毁时调用
*/ */
public void release() { public void release() {
LogUtils.d(TAG, "release执行"); LogUtils.d(TAG, "release: 执行资源释放");
cancelForegroundServiceNotify(); cancelForegroundServiceNotify();
mNotificationManager = null; mNotificationManager = null;
mContext = null; mContext = null;
LogUtils.d(TAG, "release成功所有资源已释放"); LogUtils.d(TAG, "release: 成功:所有资源已释放");
}
// ================================== 对外 getter 方法(仅前台通知实例,只读)=================================
public Notification getForegroundServiceNotify() {
return mForegroundServiceNotify;
} }
} }

View File

@@ -15,6 +15,7 @@ import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.App; import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.models.BackgroundBean; import cc.winboll.studio.powerbell.models.BackgroundBean;
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
import java.io.File; import java.io.File;
/** /**
@@ -289,5 +290,6 @@ public class BackgroundView extends RelativeLayout {
super.onSizeChanged(w, h, oldw, oldh); super.onSizeChanged(w, h, oldw, oldh);
adjustImageViewSize(); // 尺寸变化时重新调整 adjustImageViewSize(); // 尺寸变化时重新调整
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

View File

@@ -40,17 +40,17 @@
android:layout_width="160dp" android:layout_width="160dp"
android:layout_height="36dp" android:layout_height="36dp"
android:text="Origin BG" android:text="Origin BG"
android:id="@+id/activitybackgroundpictureAButton5"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_margin="5dp"/> android:layout_margin="5dp"
android:id="@+id/activitybackgroundsettingsAButton1"/>
<cc.winboll.studio.libaes.views.AButton <cc.winboll.studio.libaes.views.AButton
android:layout_width="160dp" android:layout_width="160dp"
android:layout_height="36dp" android:layout_height="36dp"
android:text="Received BG" android:text="Received BG"
android:id="@+id/activitybackgroundpictureAButton4"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_margin="5dp"/> android:layout_margin="5dp"
android:id="@+id/activitybackgroundsettingsAButton2"/>
</RelativeLayout> </RelativeLayout>
@@ -66,7 +66,7 @@
android:text="◎" android:text="◎"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="5dp" android:layout_margin="5dp"
android:id="@+id/activitybackgroundpictureAButton1"/> android:id="@+id/activitybackgroundsettingsAButton3"/>
<cc.winboll.studio.libaes.views.AButton <cc.winboll.studio.libaes.views.AButton
android:layout_width="50dp" android:layout_width="50dp"
@@ -74,7 +74,7 @@
android:text="☑" android:text="☑"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="5dp" android:layout_margin="5dp"
android:id="@+id/activitybackgroundpictureAButton2"/> android:id="@+id/activitybackgroundsettingsAButton4"/>
<cc.winboll.studio.libaes.views.AButton <cc.winboll.studio.libaes.views.AButton
android:layout_width="50dp" android:layout_width="50dp"
@@ -82,8 +82,7 @@
android:text="♾" android:text="♾"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="5dp" android:layout_margin="5dp"
android:id="@+id/activitybackgroundpictureAButton9" android:id="@+id/activitybackgroundsettingsAButton5"/>
android:onClick="onNetworkBackgroundDialog"/>
</LinearLayout> </LinearLayout>
@@ -99,7 +98,7 @@
android:text="[+]" android:text="[+]"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="5dp" android:layout_margin="5dp"
android:id="@+id/activitybackgroundpictureAButton3"/> android:id="@+id/activitybackgroundsettingsAButton6"/>
<cc.winboll.studio.libaes.views.AButton <cc.winboll.studio.libaes.views.AButton
android:layout_width="50dp" android:layout_width="50dp"
@@ -107,7 +106,7 @@
android:text="[+~]" android:text="[+~]"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="5dp" android:layout_margin="5dp"
android:id="@+id/activitybackgroundpictureAButton6"/> android:id="@+id/activitybackgroundsettingsAButton7"/>
<cc.winboll.studio.libaes.views.AButton <cc.winboll.studio.libaes.views.AButton
android:layout_width="50dp" android:layout_width="50dp"
@@ -115,7 +114,7 @@
android:text="[◐]" android:text="[◐]"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="5dp" android:layout_margin="5dp"
android:id="@+id/activitybackgroundpictureAButton7"/> android:id="@+id/activitybackgroundsettingsAButton8"/>
<cc.winboll.studio.libaes.views.AButton <cc.winboll.studio.libaes.views.AButton
android:layout_width="50dp" android:layout_width="50dp"
@@ -123,8 +122,8 @@
android:text="[©]" android:text="[©]"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="5dp" android:layout_margin="5dp"
android:id="@+id/activitybackgroundsettingsAButton1" android:onClick="onColorPaletteDialog"
android:onClick="onColorPaletteDialog"/> android:id="@+id/activitybackgroundsettingsAButton9"/>
<cc.winboll.studio.libaes.views.AButton <cc.winboll.studio.libaes.views.AButton
android:layout_width="50dp" android:layout_width="50dp"
@@ -132,7 +131,7 @@
android:text="[○]" android:text="[○]"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="5dp" android:layout_margin="5dp"
android:id="@+id/activitybackgroundpictureAButton8"/> android:id="@+id/activitybackgroundsettingsAButton10"/>
</LinearLayout> </LinearLayout>

View File

@@ -13,11 +13,20 @@
android:id="@+id/tv_dialog_title" android:id="@+id/tv_dialog_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="网络后台提示" android:text="网络图片资源下载"
android:textSize="18sp" android:textSize="18sp"
android:textColor="@android:color/black" android:textColor="@android:color/black"
android:textStyle="bold"/> android:textStyle="bold"/>
<TextView
android:id="@+id/tv_dialog_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="请输入网络图片地址:"
android:textSize="15sp"
android:textColor="@android:color/darker_gray"/>
<LinearLayout <LinearLayout
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -54,15 +63,6 @@
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/tv_dialog_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="应用正在后台使用网络,是否继续允许?"
android:textSize="15sp"
android:textColor="@android:color/darker_gray"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -74,7 +74,7 @@
android:id="@+id/btn_cancel" android:id="@+id/btn_cancel"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="取消" android:text="关闭返回"
android:textSize="14sp" android:textSize="14sp"
android:background="@android:drawable/btn_default_small" android:background="@android:drawable/btn_default_small"
android:layout_marginRight="8dp"/> android:layout_marginRight="8dp"/>
@@ -83,7 +83,7 @@
android:id="@+id/btn_confirm" android:id="@+id/btn_confirm"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="允许" android:text="使用图片"
android:textSize="14sp" android:textSize="14sp"
android:background="@android:drawable/btn_default_small"/> android:background="@android:drawable/btn_default_small"/>