Compare commits

...

39 Commits

Author SHA1 Message Date
13b6af6921 <powerbell>APK 15.11.11 release Publish. 2025-11-30 03:50:23 +08:00
e2703495ae 更新基础类库以改进应用崩溃处理方法 2025-11-30 03:47:36 +08:00
c9bc5c88d8 20251129_170510_937 2025-11-29 17:05:15 +08:00
34356b8116 20251129_163631_757 2025-11-29 16:36:37 +08:00
de189c3fb0 20251129_161627_420整体函数重构完成,待调试。。。 2025-11-29 16:17:12 +08:00
c4b2ecaecb 调试到下载图片,未调试图片存储管理。 2025-11-29 11:25:41 +08:00
1b53594086 更正基础类库,方便调试,继续调试。。。 2025-11-29 02:51:29 +08:00
6a2d011ceb 20251127_214905_683 Debugging ... 2025-11-27 21:49:29 +08:00
5aa54091e5 20251127_212052_680正在调试图片文件存储问题。 2025-11-27 21:21:25 +08:00
bc8a63867c <powerbell>APK 15.11.10 release Publish. 2025-11-27 19:24:29 +08:00
2a10cb493d 更新AES类库,更新广告控制UI界面。 2025-11-27 19:22:05 +08:00
ea7e2f8366 <powerbell>APK 15.11.9 release Publish. 2025-11-27 14:46:03 +08:00
d073a86b9b 添加应用设置与米盟广告SDK设置控件。 2025-11-27 14:43:53 +08:00
e76427eac8 修复背景图片右边有缝隙,以及像素背景设置的问题。 2025-11-27 14:25:37 +08:00
af53216af3 添加中文资源字符串 2025-11-27 13:34:55 +08:00
3ae56bb202 升级基础类库 2025-11-27 13:27:49 +08:00
09f1974c8e 更新类标签 2025-11-27 08:58:41 +08:00
721c93c4b2 更新应用快捷菜单提示 2025-11-27 08:51:06 +08:00
5a9a138463 <powerbell>APK 15.11.8 release Publish. 2025-11-26 16:27:33 +08:00
ae601c1445 <powerbell>Start New Stage Version. 2025-11-26 16:24:41 +08:00
c5cd274b0f 添加多主题图标切换方案 2025-11-26 16:23:04 +08:00
07a53da918 更新应用中文名称 2025-11-25 21:29:07 +08:00
2aaf18f29f <powerbell>APK 15.11.7 release Publish. 2025-11-21 21:24:51 +08:00
9892f3de2d <powerbell>Start New Stage Version. 2025-11-21 21:23:06 +08:00
ZhanGSKen
c06a325c42 基本实现背景图层更换,操作流程与图片资源管理部分未完善。 2025-11-21 21:22:01 +08:00
ZhanGSKen
7897100659 添加历史背景图片保存功能 2025-11-21 20:27:35 +08:00
ZhanGSKen
51793077bd 添加图片设置预览功能与一些调试入口。 2025-11-21 18:24:22 +08:00
ada29fb2b4 <powerbell>APK 15.11.6 release Publish. 2025-11-21 14:24:01 +08:00
ZhanGSKen
306f62f7ca 添加应用资源混淆配置 2025-11-21 14:20:50 +08:00
ZhanGSKen
50e2bd375d Merge remote-tracking branch 'origin/appbase' into powerbell 2025-11-21 13:58:02 +08:00
2480c8c1f0 <powerbell>APK 15.11.5 release Publish. 2025-11-21 03:39:38 +08:00
ZhanGSKen
81950699b3 改进网络图片下载与预览 2025-11-21 03:38:42 +08:00
47ea47cddc <powerbell>APK 15.11.4 release Publish. 2025-11-21 03:21:01 +08:00
ZhanGSKen
2404a9c532 更新应用介绍页 2025-11-21 03:19:29 +08:00
ZhanGSKen
82518af2d6 完成示例图片控件的引用与存储数据存取功能。 2025-11-20 20:16:40 +08:00
ZhanGSKen
bb98d6bb1b 设置beta版与stage版不同的调试入口。 2025-11-20 11:24:24 +08:00
ZhanGSKen
230038f6f3 添加下载图片预览模块(未调试) 2025-11-19 21:24:35 +08:00
ZhanGSKen
f8980446a8 添加网络图片资源下载对话框 2025-11-19 20:25:48 +08:00
ZhanGSKen
643b84aece 添加应用背景调试模块 2025-11-19 19:21:40 +08:00
58 changed files with 3055 additions and 589 deletions

View File

@@ -39,14 +39,7 @@ android {
} }
} }
buildTypes { // 米盟 SDK
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// 米盟
packagingOptions { packagingOptions {
doNotStrip "*/*/libmimo_1011.so" doNotStrip "*/*/libmimo_1011.so"
} }
@@ -55,14 +48,15 @@ android {
dependencies { dependencies {
// 米盟 // 米盟
implementation 'com.miui.zeus:mimo-ad-sdk:5.3.+'//请使用最新版sdk api 'com.miui.zeus:mimo-ad-sdk:5.3.+'//请使用最新版sdk
//注意以下5个库必须要引入 //注意以下5个库必须要引入
//implementation 'androidx.appcompat:appcompat:1.4.1' //api 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.recyclerview:recyclerview:1.0.0' api 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.google.code.gson:gson:2.8.5' api 'com.google.code.gson:gson:2.8.5'
implementation 'com.github.bumptech.glide:glide:4.9.0' api 'com.github.bumptech.glide:glide:4.9.0'
//annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' //annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
// 应用介绍页类库 // 应用介绍页类库
api 'io.github.medyo:android-about-page:2.0.0' api 'io.github.medyo:android-about-page:2.0.0'
// SSH // SSH
@@ -83,8 +77,8 @@ dependencies {
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0' //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
//api 'androidx.fragment:fragment:1.1.0' //api 'androidx.fragment:fragment:1.1.0'
implementation 'cc.winboll.studio:libaes:15.11.4' implementation 'cc.winboll.studio:libaes:15.11.8'
implementation 'cc.winboll.studio:libappbase:15.11.0' implementation 'cc.winboll.studio:libappbase:15.11.4'
//api fileTree(dir: 'libs', include: ['*.aar']) //api fileTree(dir: 'libs', include: ['*.aar'])
api fileTree(dir: 'libs', include: ['*.jar']) api fileTree(dir: 'libs', include: ['*.jar'])

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Wed Nov 19 09:09:24 HKT 2025 #Sun Nov 30 03:50:22 HKT 2025
stageCount=4 stageCount=12
libraryProject= libraryProject=
baseVersion=15.11 baseVersion=15.11
publishVersion=15.11.3 publishVersion=15.11.11
buildCount=0 buildCount=0
baseBetaVersion=15.11.4 baseBetaVersion=15.11.12

View File

@@ -9,12 +9,135 @@
# Add any project specific keep options here: # Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following # ============================== 基础通用规则 ==============================
# and specify the fully qualified class name to the JavaScript interface # 保留系统组件
# class: -keep public class * extends android.app.Activity
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -keep public class * extends android.app.Service
# public *; -keep public class * extends android.content.BroadcastReceiver
#} -keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
## 米盟 # 保留 WinBoLL 核心包及子类(合并简化规则)
-keep class cc.winboll.studio.** { *; }
-keepclassmembers class cc.winboll.studio.** { *; }
# 保留所有类中的 public static final String TAG 字段(便于日志定位)
-keepclassmembers class * {
public static final java.lang.String TAG;
}
# 保留序列化类避免Parcelable/Gson解析异常
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# 保留 R 文件避免资源ID混淆
-keepclassmembers class **.R$* {
public static <fields>;
}
# 保留 native 方法避免JNI调用失败
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留注解和泛型(避免反射/序列化异常)
-keepattributes *Annotation*
-keepattributes Signature
# 屏蔽 Java 8+ 警告(适配 Java 7 语法)
-dontwarn java.lang.invoke.*
-dontwarn android.support.v8.renderscript.*
-dontwarn java.util.function.**
# ============================== 第三方框架专项规则 ==============================
# OkHttp 4.4.1米盟广告请求依赖完善Lambda兼容
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-keep class okhttp3.internal.** { *; }
-keep class okio.** { *; }
-dontwarn okhttp3.internal.platform.**
-dontwarn okio.**
# ============================== 必要补充规则 ==============================
# OkHttp 4.4.1 补充规则Java 7 兼容)
-keep class okhttp3.internal.concurrent.** { *; }
-keep class okhttp3.internal.connection.** { *; }
-dontwarn okhttp3.internal.concurrent.TaskRunner
-dontwarn okhttp3.internal.connection.RealCall
# Glide 4.9.0(米盟广告图片加载依赖)
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$ImageType {
**[] $VALUES;
public *;
}
-keepclassmembers class * implements com.bumptech.glide.module.AppGlideModule {
<init>();
}
-dontwarn com.bumptech.glide.**
# Gson 2.8.5(米盟广告数据序列化依赖)
-keep class com.google.gson.** { *; }
-keep interface com.google.gson.** { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
# 米盟 SDK(核心广告组件,完整保留避免加载失败)
-keep class com.miui.zeus.** { *; } -keep class com.miui.zeus.** { *; }
-keep interface com.miui.zeus.** { *; }
# 保留米盟日志字段(便于广告加载失败排查)
-keepclassmembers class com.miui.zeus.mimo.sdk.** {
public static final java.lang.String TAG;
}
# RecyclerView 1.0.0(米盟广告布局渲染依赖)
-keep class androidx.recyclerview.** { *; }
-keep interface androidx.recyclerview.** { *; }
-keepclassmembers class androidx.recyclerview.widget.RecyclerView$Adapter {
public *;
}
# 其他第三方框架(按引入依赖保留,无则可删除)
# XXPermissions 18.63
-keep class com.hjq.permissions.** { *; }
-keep interface com.hjq.permissions.** { *; }
# ZXing 二维码(核心解析组件)
-keep class com.google.zxing.** { *; }
-keep class com.journeyapps.zxing.** { *; }
# Jsoup HTML解析
-keep class org.jsoup.** { *; }
# Pinyin4j 拼音搜索
-keep class net.sourceforge.pinyin4j.** { *; }
# JSch SSH组件
-keep class com.jcraft.jsch.** { *; }
# AndroidX 基础组件
-keep class androidx.appcompat.** { *; }
-keep interface androidx.appcompat.** { *; }
# ============================== 优化与调试配置 ==============================
# 优化级别(平衡混淆效果与性能)
-optimizationpasses 5
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
# 调试辅助(保留行号便于崩溃定位)
-verbose
-dontpreverify
-dontusemixedcaseclassnames
-keepattributes SourceFile,LineNumberTable

View File

@@ -6,18 +6,6 @@
tools:replace="android:icon" tools:replace="android:icon"
android:icon="@drawable/ic_launcher_beta"> android:icon="@drawable/ic_launcher_beta">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="cc.winboll.studio.powerbell.beta.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/>
</provider>
</application> </application>
</manifest> </manifest>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">PowerBell☆</string> <string name="app_name_cn1">能源钟★</string>
<string name="app_name_cn2">泡额呗额☆</string>
</resources> </resources>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<!--<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<!--<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
</shortcuts>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<!--<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>

View File

@@ -1,13 +1,14 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<manifest <manifest
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="cc.winboll.studio.powerbell"> package="cc.winboll.studio.powerbell">
<!-- 通过GPS得到精确位置 --> <!-- 只能在前台获取精确位置信息 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- 通过网络得到粗略位置 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 只有在前台运行时才能获取大致位置信息 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!-- 拍摄照片和视频 --> <!-- 拍摄照片和视频 -->
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.CAMERA"/>
@@ -36,22 +37,22 @@
<!-- BATTERY_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.POST_NOTIFICATIONS"/> <!-- 计算应用存储空间 -->
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
<uses-feature android:name="android.hardware.camera"/> <uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/> <uses-feature android:name="android.hardware.camera.autofocus"/>
<!-- 1. 基础应用信息读取权限Android 11 及以下) --> <uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
<!-- 2. Android 11+ 应用列表读取权限(必须声明,否则无法获取全部应用) --> <uses-permission
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" /> tools:ignore="QueryAllPackagesPermission"/>
<!-- 3. 可选:若需读取系统应用,添加此权限(部分机型需要) --> <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"/>
<application <application
android:name=".App" android:name=".App"
@@ -67,8 +68,21 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:launchMode="singleTask" android:label="@string/app_name"
android:exported="true"> android:exported="true"
android:launchMode="singleTask">
</activity>
<activity android:name=".activities.CrashActivity"/>
<activity-alias
android:name=".MainActivityEN1"
android:targetActivity=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:enabled="true">
<intent-filter> <intent-filter>
@@ -78,7 +92,55 @@
</intent-filter> </intent-filter>
</activity> <meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmainen1"/>
</activity-alias>
<activity-alias
android:name=".MainActivityCN1"
android:targetActivity=".MainActivity"
android:exported="true"
android:label="@string/app_name_cn1"
android:icon="@drawable/ic_launcher"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmaincn1"/>
</activity-alias>
<activity-alias
android:name=".MainActivityCN2"
android:targetActivity=".MainActivity"
android:exported="true"
android:label="@string/app_name_cn2"
android:icon="@drawable/ic_launcher"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmaincn2"/>
</activity-alias>
<activity <activity
android:name="cc.winboll.studio.powerbell.activities.ClearRecordActivity" android:name="cc.winboll.studio.powerbell.activities.ClearRecordActivity"
@@ -88,7 +150,7 @@
</activity> </activity>
<activity <activity
android:name="cc.winboll.studio.powerbell.activities.BackgroundPictureActivity" android:name="cc.winboll.studio.powerbell.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">
@@ -152,6 +214,24 @@
<activity android:name="cc.winboll.studio.powerbell.activities.BatteryReportActivity"/> <activity android:name="cc.winboll.studio.powerbell.activities.BatteryReportActivity"/>
<activity android:name="cc.winboll.studio.powerbell.unittest.MainUnitTestActivity"/>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/>
</provider>
<activity android:name="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"/>
<activity android:name="cc.winboll.studio.powerbell.activities.SettingsActivity"/>
</application> </application>
</manifest> </manifest>

View File

@@ -12,8 +12,15 @@ import java.io.File;
public class App extends GlobalApplication { public class App extends GlobalApplication {
public static final String TAG = "GlobalApplication"; public static final String TAG = "App";
public static final String COMPONENT_EN1 = "cc.winboll.studio.powerbell.MainActivityEN1";
public static final String COMPONENT_CN1 = "cc.winboll.studio.powerbell.MainActivityCN1";
public static final String COMPONENT_CN2 = "cc.winboll.studio.powerbell.MainActivityCN2";
public static final String ACTION_SWITCHTO_EN1 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1";
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";
// 数据配置存储工具 // 数据配置存储工具
static AppConfigUtils _mAppConfigUtils; static AppConfigUtils _mAppConfigUtils;
static AppCacheUtils _mAppCacheUtils; static AppCacheUtils _mAppCacheUtils;

View File

@@ -17,13 +17,15 @@ import cc.winboll.studio.libaes.views.ADsBannerView;
import cc.winboll.studio.libappbase.LogActivity; import cc.winboll.studio.libappbase.LogActivity;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.activities.AboutActivity; import cc.winboll.studio.powerbell.activities.AboutActivity;
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity; import cc.winboll.studio.powerbell.activities.BackgroundSettingsActivity;
import cc.winboll.studio.powerbell.activities.BatteryReportActivity; import cc.winboll.studio.powerbell.activities.BatteryReportActivity;
import cc.winboll.studio.powerbell.activities.ClearRecordActivity; import cc.winboll.studio.powerbell.activities.ClearRecordActivity;
import cc.winboll.studio.powerbell.activities.SettingsActivity;
import cc.winboll.studio.powerbell.activities.WinBoLLActivity; import cc.winboll.studio.powerbell.activities.WinBoLLActivity;
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean; import cc.winboll.studio.powerbell.model.BackgroundBean;
import cc.winboll.studio.powerbell.fragments.MainViewFragment; import cc.winboll.studio.powerbell.fragments.MainViewFragment;
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils; import cc.winboll.studio.powerbell.unittest.MainUnitTestActivity;
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
/** /**
* 主活动类修复小米广告SDK空Context崩溃问题 * 主活动类修复小米广告SDK空Context崩溃问题
@@ -37,6 +39,8 @@ public class MainActivity extends WinBoLLActivity {
public static final String TAG = "MainActivity"; public static final String TAG = "MainActivity";
private static final int REQUEST_WRITE_STORAGE_PERMISSION = 1001;
// private static final String PRIVACY_FILE = "privacy_pfs"; // private static final String PRIVACY_FILE = "privacy_pfs";
// private static final String PRIVACY_VALUE = "privacy_value";//0: 拒绝1赞同 // private static final String PRIVACY_VALUE = "privacy_value";//0: 拒绝1赞同
// //
@@ -81,7 +85,7 @@ public class MainActivity extends WinBoLLActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
mADsBannerView = findViewById(R.id.adsbanner); mADsBannerView = findViewById(R.id.adsbanner);
// mContainer = findViewById(R.id.ads_container); // mContainer = findViewById(R.id.ads_container);
// //
// // 初始化主线程Handler关键确保广告操作在主线程执行 // // 初始化主线程Handler关键确保广告操作在主线程执行
@@ -119,7 +123,7 @@ public class MainActivity extends WinBoLLActivity {
// if (mMainHandler != null) { // if (mMainHandler != null) {
// mMainHandler.removeCallbacksAndMessages(null); // mMainHandler.removeCallbacksAndMessages(null);
// } // }
if(mADsBannerView != null) { if (mADsBannerView != null) {
mADsBannerView.releaseAdResources(); mADsBannerView.releaseAdResources();
} }
} }
@@ -165,7 +169,7 @@ public class MainActivity extends WinBoLLActivity {
public static void reloadBackground() { public static void reloadBackground() {
// 修复添加非空校验避免Activity已销毁时调用 // 修复添加非空校验避免Activity已销毁时调用
if (_mMainActivity != null && !_mMainActivity.isFinishing() && !_mMainActivity.isDestroyed()) { if (_mMainActivity != null && !_mMainActivity.isFinishing() && !_mMainActivity.isDestroyed()) {
_mMainActivity.mMainViewFragment.loadBackground(); _mMainActivity.mMainViewFragment.reloadBackground();
} }
} }
@@ -194,8 +198,8 @@ public class MainActivity extends WinBoLLActivity {
super.onResume(); super.onResume();
reloadBackground(); reloadBackground();
setBackgroundColor(); setBackgroundColor();
if(mADsBannerView != null) { if (mADsBannerView != null) {
mADsBannerView.resumeADs(); mADsBannerView.resumeADs(MainActivity.this);
} }
// // 修复:优化广告请求逻辑(添加生命周期判断 + 主线程执行) // // 修复:优化广告请求逻辑(添加生命周期判断 + 主线程执行)
@@ -221,6 +225,9 @@ public class MainActivity extends WinBoLLActivity {
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
mMenu = menu; mMenu = menu;
getMenuInflater().inflate(R.menu.toolbar_main, mMenu); getMenuInflater().inflate(R.menu.toolbar_main, mMenu);
if (App.isDebugging()) {
getMenuInflater().inflate(R.menu.toolbar_unittest, mMenu);
}
return true; return true;
} }
@@ -228,16 +235,20 @@ public class MainActivity extends WinBoLLActivity {
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item); super.onOptionsItemSelected(item);
int menuItemId = item.getItemId(); int menuItemId = item.getItemId();
if (menuItemId == R.id.action_about) { if (menuItemId == R.id.action_settings) {
startActivity(new Intent(this, SettingsActivity.class));
} else if (menuItemId == R.id.action_about) {
startActivity(new Intent(this, AboutActivity.class)); startActivity(new Intent(this, AboutActivity.class));
} else if (menuItemId == R.id.action_battery_report) { } else if (menuItemId == R.id.action_battery_report) {
startActivity(new Intent(this, BatteryReportActivity.class)); startActivity(new Intent(this, BatteryReportActivity.class));
} else if (menuItemId == R.id.action_clearrecord) { } else if (menuItemId == R.id.action_clearrecord) {
startActivity(new Intent(this, ClearRecordActivity.class)); startActivity(new Intent(this, ClearRecordActivity.class));
} else if (menuItemId == R.id.action_changepicture) { } else if (menuItemId == R.id.action_changepicture) {
startActivity(new Intent(this, BackgroundPictureActivity.class)); startActivity(new Intent(this, BackgroundSettingsActivity.class));
} else if (menuItemId == R.id.action_log) { } else if (menuItemId == R.id.action_log) {
LogActivity.startLogActivity(this); LogActivity.startLogActivity(this);
} else if (menuItemId == R.id.action_unittestactivity) {
startActivity(new Intent(this, MainUnitTestActivity.class));
} }
return true; return true;
} }
@@ -270,8 +281,8 @@ public class MainActivity extends WinBoLLActivity {
if (isFinishing() || isDestroyed()) { if (isFinishing() || isDestroyed()) {
return; return;
} }
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(this); BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
BackgroundPictureBean bean = utils.getBackgroundPictureBean(); BackgroundBean bean = utils.getCurrentBackgroundBean();
int nPixelColor = bean.getPixelColor(); int nPixelColor = bean.getPixelColor();
RelativeLayout mainLayout = findViewById(R.id.activitymainRelativeLayout1); RelativeLayout mainLayout = findViewById(R.id.activitymainRelativeLayout1);
if (mainLayout != null) { if (mainLayout != null) {

View File

@@ -57,7 +57,7 @@ public class AboutActivity extends Activity {
appInfo.setAppGitOwner("Studio"); appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(szBranchName); appInfo.setAppGitAPPBranch(szBranchName);
appInfo.setAppGitAPPSubProjectFolder(szBranchName); appInfo.setAppGitAPPSubProjectFolder(szBranchName);
appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=1"); appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=PowerBell");
appInfo.setAppAPKName("PowerBell"); appInfo.setAppAPKName("PowerBell");
appInfo.setAppAPKFolderName("PowerBell"); appInfo.setAppAPKFolderName("PowerBell");
return new AboutView(mContext, appInfo); return new AboutView(mContext, appInfo);

View File

@@ -7,37 +7,40 @@ import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.text.TextUtils;
import android.view.View; import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import cc.winboll.studio.libaes.dialogs.YesNoAlertDialog;
import cc.winboll.studio.libaes.views.AToolbar; import cc.winboll.studio.libaes.views.AToolbar;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.App; import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean; import cc.winboll.studio.powerbell.model.BackgroundBean;
import cc.winboll.studio.powerbell.dialogs.BackgroundPicturePreviewDialog; import cc.winboll.studio.powerbell.dialogs.BackgroundPicturePreviewDialog;
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils; import cc.winboll.studio.powerbell.dialogs.NetworkBackgroundDialog;
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
import cc.winboll.studio.powerbell.utils.FileUtils; import cc.winboll.studio.powerbell.utils.FileUtils;
import cc.winboll.studio.powerbell.utils.UriUtil; import cc.winboll.studio.powerbell.utils.UriUtil;
import cc.winboll.studio.powerbell.views.BackgroundView;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import androidx.core.util.Preconditions;
public class BackgroundPictureActivity extends WinBoLLActivity implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener { public class BackgroundSettingsActivity extends WinBoLLActivity implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
public static final String TAG = "BackgroundPictureActivity"; public static final String TAG = "BackgroundSettingsActivity";
public BackgroundPictureUtils mBackgroundPictureUtils; public BackgroundSourceUtils mBackgroundSourceUtils;
// 图片选择请求码 // 图片选择请求码
public static final int REQUEST_SELECT_PICTURE = 0; public static final int REQUEST_SELECT_PICTURE = 0;
@@ -49,15 +52,28 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
private File mfBackgroundDir; // 背景图片存储文件夹 private File mfBackgroundDir; // 背景图片存储文件夹
private File mfPictureDir; // 拍照与剪裁临时文件夹 private File mfPictureDir; // 拍照与剪裁临时文件夹
private File mfTakePhoto; // 拍照文件 private File mfTakePhoto; // 拍照文件
private File mfRecivedPicture; // 接收的图片文件 //private File mfRecivedPicture; // 接收的图片文件
private File mfTempCropPicture; // 剪裁临时文件
private File mfRecivedCropPicture; // 剪裁后的目标文件 // 背景视图预览图片的文件
private String preViewFilePath = "";
private String preViewFileUrl = "";
BackgroundView bvPreviewBackground;
boolean isCommitSettings = false;
// 静态变量 // 静态变量
public static String _mszRecivedCropPicture = "RecivedCrop.jpg"; // 源文件的临时剪裁图片保存名称
private static String _mSourceCropTempFileName = "SourceCropTemp.jpg";
// 源文件的临时剪裁图片保存文件对象
private static File _mSourceCropTempFile;
// 源文件的剪裁图片保存名称
private static String _mSourceCroppedFileName = "SourceCropped.jpg";
// 源文件的剪裁图片保存文件对象
private static File _mSourceCroppedFile;
// 源文件的剪裁图片保存路径
private static String _mSourceCroppedFilePath;
private static String _mszCommonFileType = "jpeg"; private static String _mszCommonFileType = "jpeg";
private int mnPictureCompress = 100; private int mnPictureCompress = 100;
private static String _RecivedPictureFileName; //private static String _RecivedBackgroundFileName;
@Override @Override
public Activity getActivity() { public Activity getActivity() {
@@ -73,11 +89,13 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_backgroundpicture); setContentView(R.layout.activity_backgroundpicture);
initEnv(); bvPreviewBackground = (BackgroundView) findViewById(R.id.activitybackgroundpictureBackgroundView1);
//initEnv();
// 初始化工具类和文件夹 // 初始化工具类和文件夹
mBackgroundPictureUtils = BackgroundPictureUtils.getInstance(this); mBackgroundSourceUtils = BackgroundSourceUtils.getInstance(this);
mfBackgroundDir = new File(mBackgroundPictureUtils.getBackgroundDir()); mfBackgroundDir = new File(mBackgroundSourceUtils.getBackgroundSourceDirPath());
if (!mfBackgroundDir.exists()) { if (!mfBackgroundDir.exists()) {
mfBackgroundDir.mkdirs(); mfBackgroundDir.mkdirs();
} }
@@ -89,10 +107,13 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
// 初始化文件对象 // 初始化文件对象
mfTakePhoto = new File(mfPictureDir, "TakePhoto.jpg"); mfTakePhoto = new File(mfPictureDir, "TakePhoto.jpg");
mfTempCropPicture = new File(mfPictureDir, "TempCrop.jpg"); //mfTempCropPicture = new File(mfPictureDir, "TempCrop.jpg");
mfRecivedPicture = getRecivedPictureFile(this); //mfRecivedPicture = getRecivedPictureFile();
mfRecivedCropPicture = new File(mfBackgroundDir, _mszRecivedCropPicture); _mSourceCropTempFile = new File(mfBackgroundDir, _mSourceCropTempFileName);
_mSourceCroppedFile = new File(mfBackgroundDir, _mSourceCroppedFileName);
_mSourceCroppedFilePath = _mSourceCroppedFile.getAbsolutePath().toString();
LogUtils.d(TAG, String.format("_mSourceCroppedFilePath : %s", _mSourceCroppedFilePath));
// 初始化工具栏 // 初始化工具栏
mAToolbar = (AToolbar) findViewById(R.id.toolbar); mAToolbar = (AToolbar) findViewById(R.id.toolbar);
@@ -102,7 +123,7 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
mAToolbar.setNavigationOnClickListener(new View.OnClickListener() { mAToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
finish(); finish(); // 点击导航栏返回按钮触发 finish()
} }
}); });
@@ -116,7 +137,9 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
findViewById(R.id.activitybackgroundpictureAButton7).setOnClickListener(onPixelPickerClickListener); findViewById(R.id.activitybackgroundpictureAButton7).setOnClickListener(onPixelPickerClickListener);
findViewById(R.id.activitybackgroundpictureAButton8).setOnClickListener(onCleanPixelClickListener); findViewById(R.id.activitybackgroundpictureAButton8).setOnClickListener(onCleanPixelClickListener);
updatePreviewBackground(); BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
utils.setCurrentSourceToPreview();
bvPreviewBackground.reloadPreviewBackground();
// 处理分享的图片 // 处理分享的图片
Intent intent = getIntent(); Intent intent = getIntent();
@@ -129,71 +152,85 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
} }
} }
private void initEnv() { // private void initEnv() {
LogUtils.d(TAG, "initEnv()"); // LogUtils.d(TAG, "initEnv()");
_RecivedPictureFileName = "Recived.data"; // _RecivedBackgroundFileName = "SourcePicture.data";
} // }
public static String getBackgroundFileName() { public static String getBackgroundFileName() {
return _mszRecivedCropPicture; return _mSourceCroppedFileName;
} }
@Override @Override
public void onAcceptRecivedPicture(String szPreRecivedPictureName) { public void onAcceptRecivedPicture(String szPreRecivedPictureName) {
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(this); ToastUtils.show("onAcceptRecivedPicture not yet.");
utils.getBackgroundPictureBean().setIsUseBackgroundFile(true); // BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
utils.saveData(); // utils.getCurrentBackgroundBean().setIsUseBackgroundFile(true);
// utils.saveSettings();
File sourceFile = new File(utils.getBackgroundDir(), szPreRecivedPictureName); //
if (FileUtils.copyFile(sourceFile, mfRecivedPicture)) { // File sourceFile = new File(utils.getBackgroundSourceDirPath(), szPreRecivedPictureName);
startCropImageActivity(false); // if (FileUtils.copyFile(sourceFile, mfRecivedPicture)) {
} else { // startCropImageActivity(false);
ToastUtils.show("图片复制失败,请重试"); // } else {
} // ToastUtils.show("图片复制失败,请重试");
// }
} }
/** /**
* 更新背景图片预览 * 更新背景图片预览
* 如果sourceFile参数为空则加载旧的背景图片资源
*/ */
public void updatePreviewBackground() { public void updateBackgroundView(File sourceFile, String sourceFileInfo) {
LogUtils.d(TAG, "updatePreviewBackground"); LogUtils.d(TAG, "updatePreviewBackground");
ImageView ivPreviewBackground = (ImageView) findViewById(R.id.activitybackgroundpictureImageView1); BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(this); if (sourceFile == null) {
utils.loadBackgroundPictureBean(); bvPreviewBackground.reloadCurrentBackground();
} else {
utils.saveFileToPreviewBean(sourceFile, sourceFileInfo);
bvPreviewBackground.reloadPreviewBackground();
}
boolean isUseBackgroundFile = utils.getBackgroundPictureBean().isUseBackgroundFile(); // boolean isUseBackgroundFile = utils.getCurrentBackgroundBean().isUseBackgroundFile();
if (isUseBackgroundFile && mfRecivedCropPicture.exists()) { // LogUtils.d(TAG, String.format("isUseBackgroundFile is %s, _mSourceCroppedFile.exists() is %s ", isUseBackgroundFile, _mSourceCroppedFile.exists()));
try { //
String filePath = utils.getBackgroundDir() + getBackgroundFileName(); // //if (isUseBackgroundFile && _mSourceCroppedFile.exists()) {
Drawable drawable = FileUtils.getImageDrawable(filePath); // if (_mSourceCroppedFile.exists()) {
if (drawable != null) { // //try {
//drawable.setAlpha(120); // //String filePath = utils.getBackgroundDir() + getBackgroundFileName();
ivPreviewBackground.setImageDrawable(drawable); // preViewFilePath = _mSourceCroppedFilePath;
} // LogUtils.d(TAG, String.format("preViewFilePathBackgroundView : %s", preViewFilePath));
//ToastUtils.show("背景图片已更新"); // bvPreviewBackground.previewBackgroundImage(preViewFilePath);
} catch (IOException e) { // /*Drawable drawable = FileUtils.getImageDrawable(filePath);
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); // if (drawable != null) {
ToastUtils.show("背景图片加载失败"); // //drawable.setAlpha(120);
} // //bvPreviewBackground.setImageDrawable(drawable);
} else { // }*/
ToastUtils.show("未使用背景图片"); // //ToastUtils.show("背景图片已更新");
Drawable drawable = getResources().getDrawable(R.drawable.blank10x10); //// } catch (IOException e) {
if (drawable != null) { //// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
drawable.setAlpha(120); //// ToastUtils.show("背景图片加载失败");
ivPreviewBackground.setImageDrawable(drawable); //// }
} // } else {
} // ToastUtils.show("未使用背景图片");
// preViewFilePath = "";
// bvPreviewBackground.previewBackgroundImage(preViewFilePath);
//// Drawable drawable = getResources().getDrawable(R.drawable.blank10x10);
//// if (drawable != null) {
//// drawable.setAlpha(120);
//// bvPreviewBackground.setImageDrawable(drawable);
//// }
// }
} }
// 点击事件监听器 // 点击事件监听器
private View.OnClickListener onOriginNullClickListener = new View.OnClickListener() { private View.OnClickListener onOriginNullClickListener = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this); BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
BackgroundPictureBean bean = utils.getBackgroundPictureBean(); BackgroundBean bean = utils.getCurrentBackgroundBean();
bean.setIsUseBackgroundFile(false); bean.setIsUseBackgroundFile(false);
utils.saveData(); utils.saveSettings();
updatePreviewBackground(); bvPreviewBackground.reloadPreviewBackground();
} }
}; };
@@ -258,10 +295,11 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
private View.OnClickListener onReceivedPictureClickListener = new View.OnClickListener() { private View.OnClickListener onReceivedPictureClickListener = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this); ToastUtils.show("onReceivedPictureClickListener not yet.");
utils.getBackgroundPictureBean().setIsUseBackgroundFile(true); // BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
utils.saveData(); // utils.getCurrentBackgroundBean().setIsUseBackgroundFile(true);
updatePreviewBackground(); // utils.saveSettings();
// updateBackgroundView();
} }
}; };
@@ -270,7 +308,7 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
public void onClick(View v) { public void onClick(View v) {
// 从文件路径启动像素拾取活动 // 从文件路径启动像素拾取活动
//String imagePath = "/storage/emulated/0/DCIM/Camera/sample.jpg"; //String imagePath = "/storage/emulated/0/DCIM/Camera/sample.jpg";
String imagePath = mfRecivedCropPicture.toString(); String imagePath = _mSourceCroppedFile.toString();
Intent intent = new Intent(getApplicationContext(), PixelPickerActivity.class); Intent intent = new Intent(getApplicationContext(), PixelPickerActivity.class);
intent.putExtra("imagePath", imagePath); intent.putExtra("imagePath", imagePath);
startActivity(intent); startActivity(intent);
@@ -281,10 +319,10 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
private View.OnClickListener onCleanPixelClickListener = new View.OnClickListener() { private View.OnClickListener onCleanPixelClickListener = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this); BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
BackgroundPictureBean bean = utils.getBackgroundPictureBean(); BackgroundBean bean = utils.getCurrentBackgroundBean();
bean.setPixelColor(0); bean.setPixelColor(0);
utils.saveData(); utils.saveSettings();
setBackgroundColor(); setBackgroundColor();
} }
}; };
@@ -295,12 +333,13 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
void compressQualityToRecivedPicture(Bitmap bitmap) { void compressQualityToRecivedPicture(Bitmap bitmap) {
OutputStream outStream = null; OutputStream outStream = null;
try { try {
mfRecivedPicture = getRecivedPictureFile(this); BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(this);
if (!mfRecivedPicture.exists()) { File fRecivedPicture = new File(utils.getPreviewBackgroundScaledCompressFilePath());
mfRecivedPicture.createNewFile(); if (!fRecivedPicture.exists()) {
fRecivedPicture.createNewFile();
} }
FileOutputStream fos = new FileOutputStream(mfRecivedPicture); FileOutputStream fos = new FileOutputStream(fRecivedPicture);
outStream = new BufferedOutputStream(fos); outStream = new BufferedOutputStream(fos);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
outStream.flush(); outStream.flush();
@@ -327,24 +366,30 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
*/ */
public void startCropImageActivity(boolean isCropFree) { public void startCropImageActivity(boolean isCropFree) {
LogUtils.d(TAG, "startCropImageActivity"); LogUtils.d(TAG, "startCropImageActivity");
BackgroundPictureBean bean = mBackgroundPictureUtils.loadBackgroundPictureBean(); BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(this);
mfRecivedPicture = getRecivedPictureFile(this); BackgroundBean bean = utils.getPreviewBackgroundBean();
Uri uri = UriUtil.getUriForFile(this, mfRecivedPicture); bean.setIsUseScaledCompress(true);
utils.saveSettings();
File fRecivedPicture = new File(utils.getPreviewBackgroundFilePath());
Uri uri = UriUtil.getUriForFile(this, fRecivedPicture);
LogUtils.d(TAG, "uri : " + uri.toString()); LogUtils.d(TAG, "uri : " + uri.toString());
if (mfTempCropPicture.exists()) { if (_mSourceCropTempFile.exists()) {
mfTempCropPicture.delete(); _mSourceCropTempFile.delete();
} }
try { try {
mfTempCropPicture.createNewFile(); _mSourceCropTempFile.createNewFile();
} catch (IOException e) { } catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
ToastUtils.show("剪裁临时文件创建失败"); ToastUtils.show("剪裁临时文件创建失败");
return; return;
} }
Uri cropOutPutUri = Uri.fromFile(mfTempCropPicture); Uri cropOutPutUri = Uri.fromFile(_mSourceCropTempFile);
LogUtils.d(TAG, "mfTempCropPicture : " + mfTempCropPicture.getPath()); LogUtils.d(TAG, "mfTempCropPicture : " + _mSourceCropTempFile.getPath());
Intent intent = new Intent("com.android.camera.action.CROP"); Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/" + _mszCommonFileType); intent.setDataAndType(uri, "image/" + _mszCommonFileType);
@@ -361,6 +406,7 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
intent.putExtra("scale", true); intent.putExtra("scale", true);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent, REQUEST_CROP_IMAGE); startActivityForResult(intent, REQUEST_CROP_IMAGE);
} }
@@ -388,7 +434,7 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
} }
// 优化创建保存目录 // 优化创建保存目录
File backgroundDir = new File(mBackgroundPictureUtils.getBackgroundDir()); File backgroundDir = new File(mBackgroundSourceUtils.getBackgroundSourceDirPath());
if (!backgroundDir.exists()) { if (!backgroundDir.exists()) {
if (!backgroundDir.mkdirs()) { if (!backgroundDir.mkdirs()) {
ToastUtils.show("无法创建保存目录"); ToastUtils.show("无法创建保存目录");
@@ -397,11 +443,12 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
} }
} }
File saveFile = new File(backgroundDir, getBackgroundFileName()); // 剪裁的图片的保存地址
File fScaledCompressBitmapFile = new File(backgroundDir, BackgroundSourceUtils.getInstance(this).getPreviewBackgroundScaledCompressFilePath());
// 优化检查文件是否可写 // 优化检查文件是否可写
if (saveFile.exists() && !saveFile.canWrite()) { if (fScaledCompressBitmapFile.exists() && !fScaledCompressBitmapFile.canWrite()) {
if (!saveFile.delete()) { if (!fScaledCompressBitmapFile.delete()) {
ToastUtils.show("无法删除旧文件"); ToastUtils.show("无法删除旧文件");
if (scaledBitmap != bitmap) scaledBitmap.recycle(); if (scaledBitmap != bitmap) scaledBitmap.recycle();
return; return;
@@ -410,36 +457,41 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
fos = new FileOutputStream(saveFile); fos = new FileOutputStream(fScaledCompressBitmapFile);
boolean success = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos); boolean success = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
fos.flush(); fos.flush();
if (success) { if (success) {
ToastUtils.show("保存成功"); ToastUtils.show("图片压缩保存成功");
// 更新数据 BackgroundSourceUtils.getInstance(this).getPreviewBackgroundBean().setIsUseScaledCompress(true);
mBackgroundPictureUtils.getBackgroundPictureBean().setIsUseBackgroundFile(true); BackgroundSourceUtils.getInstance(this).saveSettings();
updatePreviewBackground(); bvPreviewBackground.reloadPreviewBackground();
} else { } else {
ToastUtils.show("图片压缩保存失败"); ToastUtils.show("图片压缩保存失败");
BackgroundSourceUtils.getInstance(this).getPreviewBackgroundBean().setIsUseScaledCompress(false);
BackgroundSourceUtils.getInstance(this).saveSettings();
bvPreviewBackground.reloadPreviewBackground();
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
LogUtils.e(TAG, "文件未找到" + e); LogUtils.e(TAG, "文件未找到" + e);
ToastUtils.show("保存失败:文件路径错误"); ToastUtils.show("文件未找到" + e);
} catch (IOException e) { } catch (IOException e) {
LogUtils.e(TAG, "写入异常" + e); LogUtils.e(TAG, "写入异常" + e);
ToastUtils.show("保存失败:磁盘可能已满或路径错误"); ToastUtils.show("写入异常" + e);
} finally { } finally {
if (fos != null) { if (fos != null) {
try { try {
fos.close(); fos.close();
} catch (IOException e) { } catch (IOException e) {
LogUtils.e(TAG, "流关闭异常" + e); LogUtils.e(TAG, "流关闭异常" + e);
} ToastUtils.show("流关闭异常" + e);
} }
if (scaledBitmap != null && !scaledBitmap.isRecycled()) { }
scaledBitmap.recycle(); if (scaledBitmap != null && !scaledBitmap.isRecycled()) {
} scaledBitmap.recycle();
} }
} }
}
/** /**
* 缩放Bitmap * 缩放Bitmap
@@ -457,7 +509,9 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
* 分享图片 * 分享图片
*/ */
void sharePicture() { void sharePicture() {
Uri uri = UriUtil.getUriForFile(this, mfRecivedPicture); BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(this);
File fRecivedPicture = new File(utils.getCurrentBackgroundFilePath());
Uri uri = UriUtil.getUriForFile(this, fRecivedPicture);
Intent shareIntent = new Intent(Intent.ACTION_SEND); Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uri); shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
shareIntent.setType("image/" + _mszCommonFileType); shareIntent.setType("image/" + _mszCommonFileType);
@@ -465,11 +519,20 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
startActivity(Intent.createChooser(shareIntent, "Share Image")); startActivity(Intent.createChooser(shareIntent, "Share Image"));
} }
public static File getRecivedPictureFile(Context context) { // public File getRecivedPictureFile() {
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(context); // BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
utils.loadBackgroundPictureBean(); // utils.loadSettings();
return new File(utils.getBackgroundDir(), _RecivedPictureFileName); // return new File(utils.getBackgroundSourceDirPath(), _RecivedBackgroundFileName);
} // }
// public void saveToRecivedBackground(String srcFilePath, String srcFillSourcePath) {
// BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
// utils.loadSettings();
// File dstFile = new File(utils.getBackgroundSourceDirPath(), _RecivedBackgroundFileName);
// //compressQualityToRecivedPicture(srcFilePath);
// ToastUtils.show("compressQualityToRecivedPicture not yet.");
// FileUtils.copyFile(new File(srcFilePath), dstFile);
// }
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
@@ -479,12 +542,17 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
Uri selectedImage = data.getData(); Uri selectedImage = data.getData();
LogUtils.d(TAG, "Uri is : " + selectedImage.toString()); LogUtils.d(TAG, "Uri is : " + selectedImage.toString());
File fSrcImage = new File(UriUtil.getFilePathFromUri(this, selectedImage)); File fSrcImage = new File(UriUtil.getFilePathFromUri(this, selectedImage));
mfRecivedPicture = getRecivedPictureFile(this); BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(this);
if (FileUtils.copyFile(fSrcImage, mfRecivedPicture)) { utils.saveFileToPreviewBean(fSrcImage, selectedImage.toString());
startCropImageActivity(false); startCropImageActivity(false);
} else { //mfRecivedPicture = getRecivedPictureFile();
ToastUtils.show("图片复制失败,请重试"); // BackgroundBean bean = utils.getPreviewBackgroundBean();
} // mfRecivedPicture = getRecivedPictureFile();
// if (FileUtils.copyFile(fSrcImage, mfRecivedPicture)) {
// startCropImageActivity(false);
// } else {
// ToastUtils.show("图片复制失败,请重试");
// }
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "选择图片异常" + e); LogUtils.e(TAG, "选择图片异常" + e);
ToastUtils.show("选择图片失败:" + e.getMessage()); ToastUtils.show("选择图片失败:" + e.getMessage());
@@ -510,8 +578,9 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
// 方案1通过Intent获取剪裁后的Bitmap // 方案1通过Intent获取剪裁后的Bitmap
if (data != null && data.hasExtra("data")) { if (data != null && data.hasExtra("data")) {
cropBitmap = data.getParcelableExtra("data"); cropBitmap = data.getParcelableExtra("data");
} else if (mfTempCropPicture.exists()) { } else if (_mSourceCropTempFile.exists()) {
cropBitmap = BitmapFactory.decodeFile(mfTempCropPicture.getPath()); LogUtils.d(TAG, String.format("_mSourceCropTempFile Exists, Path is %s ", _mSourceCropTempFile.getPath()));
cropBitmap = BitmapFactory.decodeFile(_mSourceCropTempFile.getPath());
} else { } else {
ToastUtils.show("剪裁文件不存在"); ToastUtils.show("剪裁文件不存在");
return; return;
@@ -577,8 +646,8 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
} }
void setBackgroundColor() { void setBackgroundColor() {
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this); BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
BackgroundPictureBean bean = utils.getBackgroundPictureBean(); BackgroundBean bean = utils.getCurrentBackgroundBean();
int nPixelColor = bean.getPixelColor(); int nPixelColor = bean.getPixelColor();
RelativeLayout mainLayout = findViewById(R.id.activitybackgroundpictureRelativeLayout1); RelativeLayout mainLayout = findViewById(R.id.activitybackgroundpictureRelativeLayout1);
mainLayout.setBackgroundColor(nPixelColor); mainLayout.setBackgroundColor(nPixelColor);
@@ -589,5 +658,76 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
super.onResume(); super.onResume();
setBackgroundColor(); setBackgroundColor();
} }
public void onNetworkBackgroundDialog(View view) {
// 在需要显示对话框的地方如网络状态监听回调中
NetworkBackgroundDialog dialog = new NetworkBackgroundDialog(this, new NetworkBackgroundDialog.OnDialogClickListener() {
@Override
public void onConfirm(String szConfirmFilePath, String szConfirmFileUrl) {
//ToastUtils.show("onConfirm");
// 保存预览资源信息
preViewFilePath = szConfirmFilePath;
preViewFileUrl = szConfirmFileUrl;
onRecivedPictureListener.onRecivedPicture(preViewFilePath, preViewFileUrl);
}
@Override
public void onCancel() {
//ToastUtils.show("onCancel");
}
});
// 可选修改对话框标题和内容适配自定义场景
dialog.setTitle("网络图片下载对话框");
dialog.setContent("是否下载地址中的图片资源,作为应用背景图片?");
// 显示对话框
dialog.show();
}
interface OnRecivedPictureListener {
void onRecivedPicture(String srcFilePath, String srcFileUrl);
}
OnRecivedPictureListener onRecivedPictureListener = new OnRecivedPictureListener(){
@Override
public void onRecivedPicture(String srcFilePath, String srcFileUrl) {
BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
utils.saveFileToPreviewBean(new File(srcFilePath), srcFileUrl);
startCropImageActivity(true);
}
};
/**
* 重写finish方法确保所有退出场景都触发Toast
*/
@Override
public void finish() {
if (!isCommitSettings) {
YesNoAlertDialog.show(this, "应用背景更改提示:", "是否应用预览图片?", new YesNoAlertDialog.OnDialogResultListener(){
@Override
public void onNo() {
isCommitSettings = true;
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
BackgroundBean bean = utils.getCurrentBackgroundBean();
bean.setIsUseBackgroundFile(!preViewFilePath.equals(""));
utils.saveSettings();
finish();
}
@Override
public void onYes() {
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
utils.commitPreviewSourceToCurrent();
isCommitSettings = true;
finish();
}
});
} else {
super.finish();
}
}
} }

View File

@@ -12,7 +12,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.R; import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.beans.BatteryInfoBean; import cc.winboll.studio.powerbell.model.BatteryInfoBean;
import cc.winboll.studio.powerbell.receivers.ControlCenterServiceReceiver; import cc.winboll.studio.powerbell.receivers.ControlCenterServiceReceiver;
import cc.winboll.studio.powerbell.utils.AppCacheUtils; import cc.winboll.studio.powerbell.utils.AppCacheUtils;
import cc.winboll.studio.powerbell.utils.StringUtils; import cc.winboll.studio.powerbell.utils.StringUtils;

View File

@@ -24,10 +24,10 @@ import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.views.AToolbar; import cc.winboll.studio.libaes.views.AToolbar;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity; import cc.winboll.studio.powerbell.activities.BackgroundSettingsActivity;
import cc.winboll.studio.powerbell.activities.PixelPickerActivity; import cc.winboll.studio.powerbell.activities.PixelPickerActivity;
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean; import cc.winboll.studio.powerbell.model.BackgroundBean;
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils; import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@@ -193,10 +193,10 @@ public class PixelPickerActivity extends WinBoLLActivity implements IWinBoLLActi
public void onClick(View v) { public void onClick(View v) {
dialog.dismiss(); dialog.dismiss();
// 可以在这里添加确定后的回调逻辑 // 可以在这里添加确定后的回调逻辑
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(PixelPickerActivity.this); BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(PixelPickerActivity.this);
BackgroundPictureBean bean = utils.getBackgroundPictureBean(); BackgroundBean bean = utils.getCurrentBackgroundBean();
bean.setPixelColor(pixelColor); bean.setPixelColor(pixelColor);
utils.saveData(); utils.saveSettings();
Toast.makeText(PixelPickerActivity.this, "已记录像素值", Toast.LENGTH_SHORT).show(); Toast.makeText(PixelPickerActivity.this, "已记录像素值", Toast.LENGTH_SHORT).show();
setBackgroundColor(); setBackgroundColor();
} }
@@ -217,8 +217,8 @@ public class PixelPickerActivity extends WinBoLLActivity implements IWinBoLLActi
void setBackgroundColor() { void setBackgroundColor() {
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(PixelPickerActivity.this); BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(PixelPickerActivity.this);
BackgroundPictureBean bean = utils.getBackgroundPictureBean(); BackgroundBean bean = utils.getCurrentBackgroundBean();
int nPixelColor = bean.getPixelColor(); int nPixelColor = bean.getPixelColor();
RelativeLayout mainLayout = findViewById(R.id.activitypixelpickerRelativeLayout1); RelativeLayout mainLayout = findViewById(R.id.activitypixelpickerRelativeLayout1);
mainLayout.setBackgroundColor(nPixelColor); mainLayout.setBackgroundColor(nPixelColor);
@@ -235,7 +235,7 @@ public class PixelPickerActivity extends WinBoLLActivity implements IWinBoLLActi
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setClass(this, BackgroundPictureActivity.class); intent.setClass(this, BackgroundSettingsActivity.class);
startActivity(intent); startActivity(intent);
//GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), ); //GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), );
return true; return true;
@@ -248,7 +248,7 @@ public class PixelPickerActivity extends WinBoLLActivity implements IWinBoLLActi
public void onBackPressed() { public void onBackPressed() {
super.onBackPressed(); super.onBackPressed();
Intent intent = new Intent(); Intent intent = new Intent();
intent.setClass(this, BackgroundPictureActivity.class); intent.setClass(this, BackgroundSettingsActivity.class);
startActivity(intent); startActivity(intent);
//GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), BackgroundPictureActivity.class); //GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), BackgroundPictureActivity.class);
} }

View File

@@ -0,0 +1,21 @@
package cc.winboll.studio.powerbell.activities;
import android.app.Activity;
import android.os.Bundle;
import cc.winboll.studio.powerbell.R;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/27 14:26
* @Describe 应用设置窗口
*/
public class SettingsActivity extends Activity {
public static final String TAG = "SettingsActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
}
}

View File

@@ -0,0 +1,49 @@
package cc.winboll.studio.powerbell.activities;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.utils.APPPlusUtils;
import cc.winboll.studio.powerbell.App;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/15 13:45
* @Describe 应用快捷方式活动类
*/
public class ShortcutActionActivity extends Activity {
public static final String TAG = "ShortcutActionActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 处理应用级别的切换请求
handleSwitchRequest();
finish();
}
/**
* 处理应用图标快捷菜单的请求
*/
private void handleSwitchRequest() {
Intent intent = getIntent();
if (intent != null && "switchto_en1".equals(intent.getDataString())) {
APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_EN1);
ToastUtils.show("切换至" + getString(R.string.app_name) + "图标");
//moveTaskToBack(true);
}
if (intent != null && "switchto_cn1".equals(intent.getDataString())) {
APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_CN1);
ToastUtils.show("切换至" + getString(R.string.app_name_cn1) + "图标");
//moveTaskToBack(true);
}
if (intent != null && "switchto_cn2".equals(intent.getDataString())) {
APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_CN2);
ToastUtils.show("切换至" + getString(R.string.app_name_cn2) + "图标");
//moveTaskToBack(true);
}
}
}

View File

@@ -12,7 +12,7 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.adapters.BatteryAdapter; import cc.winboll.studio.powerbell.adapters.BatteryAdapter;
import cc.winboll.studio.powerbell.beans.BatteryData; import cc.winboll.studio.powerbell.model.BatteryData;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@@ -1,99 +0,0 @@
package cc.winboll.studio.powerbell.beans;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/18 11:52:28
* @Describe 应用背景图片数据类
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
public class BackgroundPictureBean extends BaseBean {
public static final String TAG = "BackgroundPictureBean";
int backgroundWidth = 100;
int backgroundHeight = 100;
boolean isUseBackgroundFile = false;
// 图片拾取像素颜色
int pixelColor = 0;
public BackgroundPictureBean() {
}
public BackgroundPictureBean(String recivedFileName, boolean isUseBackgroundFile) {
this.isUseBackgroundFile = isUseBackgroundFile;
}
public void setPixelColor(int pixelColor) {
this.pixelColor = pixelColor;
}
public int getPixelColor() {
return pixelColor;
}
public void setBackgroundWidth(int backgroundWidth) {
this.backgroundWidth = backgroundWidth;
}
public int getBackgroundWidth() {
return backgroundWidth;
}
public void setBackgroundHeight(int backgroundHeight) {
this.backgroundHeight = backgroundHeight;
}
public int getBackgroundHeight() {
return backgroundHeight;
}
public void setIsUseBackgroundFile(boolean isUseBackgroundFile) {
this.isUseBackgroundFile = isUseBackgroundFile;
}
public boolean isUseBackgroundFile() {
return isUseBackgroundFile;
}
@Override
public String getName() {
return BackgroundPictureBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
BackgroundPictureBean bean = this;
jsonWriter.name("backgroundWidth").value(bean.getBackgroundWidth());
jsonWriter.name("backgroundHeight").value(bean.getBackgroundHeight());
jsonWriter.name("isUseBackgroundFile").value(bean.isUseBackgroundFile());
jsonWriter.name("pixelColor").value(bean.getPixelColor());
}
@Override
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
BackgroundPictureBean bean = new BackgroundPictureBean();
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (name.equals("backgroundWidth")) {
bean.setBackgroundWidth(jsonReader.nextInt());
} else if (name.equals("backgroundHeight")) {
bean.setBackgroundHeight(jsonReader.nextInt());
} else if (name.equals("isUseBackgroundFile")) {
bean.setIsUseBackgroundFile(jsonReader.nextBoolean());
} else if (name.equals("pixelColor")) {
bean.setPixelColor(jsonReader.nextInt());
} else {
jsonReader.skipValue();
}
}
// 结束 JSON 对象
jsonReader.endObject();
return bean;
}
}

View File

@@ -12,8 +12,8 @@ import android.widget.Toast;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.MainActivity; import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity; import cc.winboll.studio.powerbell.activities.BackgroundSettingsActivity;
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils; import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
import cc.winboll.studio.powerbell.utils.FileUtils; import cc.winboll.studio.powerbell.utils.FileUtils;
import cc.winboll.studio.powerbell.utils.UriUtil; import cc.winboll.studio.powerbell.utils.UriUtil;
import java.io.File; import java.io.File;
@@ -29,7 +29,7 @@ public class BackgroundPicturePreviewDialog extends Dialog {
public static final String TAG = "BackgroundPicturePreviewDialog"; public static final String TAG = "BackgroundPicturePreviewDialog";
Context mContext; Context mContext;
BackgroundPictureUtils mBackgroundPictureUtils; BackgroundSourceUtils mBackgroundPictureUtils;
Button dialogbackgroundpicturepreviewButton1; Button dialogbackgroundpicturepreviewButton1;
Button dialogbackgroundpicturepreviewButton2; Button dialogbackgroundpicturepreviewButton2;
String mszPreReceivedFileName; String mszPreReceivedFileName;
@@ -40,7 +40,7 @@ public class BackgroundPicturePreviewDialog extends Dialog {
initEnv(); initEnv();
mContext = context; mContext = context;
mBackgroundPictureUtils = ((BackgroundPictureActivity)context).mBackgroundPictureUtils; mBackgroundPictureUtils = ((BackgroundSettingsActivity)context).mBackgroundSourceUtils;
ImageView imageView = findViewById(R.id.dialogbackgroundpicturepreviewImageView1); ImageView imageView = findViewById(R.id.dialogbackgroundpicturepreviewImageView1);
copyAndViewRecivePicture(imageView); copyAndViewRecivePicture(imageView);
@@ -78,7 +78,7 @@ public class BackgroundPicturePreviewDialog extends Dialog {
void copyAndViewRecivePicture(ImageView imageView) { void copyAndViewRecivePicture(ImageView imageView) {
//AppConfigUtils appConfigUtils = AppConfigUtils.getInstance((GlobalApplication)mContext.getApplicationContext()); //AppConfigUtils appConfigUtils = AppConfigUtils.getInstance((GlobalApplication)mContext.getApplicationContext());
BackgroundPictureActivity activity = ((BackgroundPictureActivity)mContext); BackgroundSettingsActivity activity = ((BackgroundSettingsActivity)mContext);
//取出文件uri //取出文件uri
Uri uri = activity.getIntent().getData(); Uri uri = activity.getIntent().getData();
@@ -95,7 +95,7 @@ public class BackgroundPicturePreviewDialog extends Dialog {
File fSrcImage = new File(szSrcImage); File fSrcImage = new File(szSrcImage);
//mszPreReceivedFileName = DateUtils.getDateNowString() + "-" + fSrcImage.getName(); //mszPreReceivedFileName = DateUtils.getDateNowString() + "-" + fSrcImage.getName();
File mfPreReceivedPhoto = new File(activity.mBackgroundPictureUtils.getBackgroundDir(), mszPreReceivedFileName); File mfPreReceivedPhoto = new File(activity.mBackgroundSourceUtils.getBackgroundSourceDirPath(), mszPreReceivedFileName);
// 复制源图片到剪裁文件 // 复制源图片到剪裁文件
try { try {
FileUtils.copyFileUsingFileChannels(fSrcImage, mfPreReceivedPhoto); FileUtils.copyFileUsingFileChannels(fSrcImage, mfPreReceivedPhoto);

View File

@@ -0,0 +1,273 @@
package cc.winboll.studio.powerbell.dialogs;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.views.BackgroundView;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import cc.winboll.studio.powerbell.utils.ImageDownloader;
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/19 20:11
* @Describe 网络后台使用提示对话框
* 继承 AndroidX AlertDialog绑定自定义布局 dialog_networkbackground.xml
*/
public class NetworkBackgroundDialog extends AlertDialog {
public static final String TAG = "NetworkBackgroundDialog";
// 消息标识:图片加载成功
private static final int MSG_IMAGE_LOAD_SUCCESS = 1001;
// 消息标识:图片加载失败
private static final int MSG_IMAGE_LOAD_FAILED = 1002;
// 控件引用
private TextView tvTitle;
private TextView tvContent;
private Button btnCancel;
private Button btnConfirm;
private Button btnPreview;
private EditText etURL;
BackgroundView bvBackgroundPreview;
Context mContext;
// 主线程 Handler用于接收子线程消息并更新 UI
private Handler mUiHandler;
String mPreviewFilePath;
String mPreviewFileUrl;
String mDownloadSavedPath;
// 按钮点击回调接口Java7 接口实现)
public interface OnDialogClickListener {
void onConfirm(String szConfirmFilePath, String previewFileUrl); // 确认按钮点击
void onCancel(); // 取消按钮点击
}
private OnDialogClickListener listener;
// Java7 显式构造(必须传入 Context
public NetworkBackgroundDialog(@NonNull Context context) {
super(context);
initHandler(); // 初始化 Handler
initView(); // 初始化布局和控件
setDismissListener(); // 设置对话框消失监听
}
// 带回调的构造(便于外部处理点击事件)
public NetworkBackgroundDialog(@NonNull Context context, OnDialogClickListener listener) {
super(context);
this.listener = listener;
initHandler(); // 初始化 Handler
initView();
setDismissListener(); // 设置对话框消失监听
}
/**
* 初始化主线程 Handler用于更新 UI
*/
private void initHandler() {
mUiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 对话框已消失时,不再处理 UI 消息
if (!isShowing()) {
return;
}
switch (msg.what) {
case MSG_IMAGE_LOAD_SUCCESS:
// 图片加载成功,获取文件路径并设置背景
mDownloadSavedPath = (String) msg.obj;
previewBackground(mDownloadSavedPath);
break;
case MSG_IMAGE_LOAD_FAILED:
// 图片加载失败,设置默认背景
bvBackgroundPreview.setBackgroundResource(R.drawable.ic_launcher);
ToastUtils.show("图片预览失败,请检查链接");
break;
}
}
};
}
/**
* 设置对话框消失监听:移除 Handler 消息,避免内存泄漏
*/
private void setDismissListener() {
this.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
// 对话框消失时,移除所有未处理的消息和回调
if (mUiHandler != null) {
mUiHandler.removeCallbacksAndMessages(null);
}
LogUtils.d(TAG, "对话框已消失Handler 消息已清理");
}
});
}
/**
* 初始化布局和控件
*/
private void initView() {
mContext = this.getContext();
// 加载自定义布局
View dialogView = LayoutInflater.from(getContext())
.inflate(R.layout.dialog_networkbackground, null);
// 设置对话框内容视图
setView(dialogView);
// 绑定控件
tvTitle = (TextView) dialogView.findViewById(R.id.tv_dialog_title);
tvContent = (TextView) dialogView.findViewById(R.id.tv_dialog_content);
btnCancel = (Button) dialogView.findViewById(R.id.btn_cancel);
btnConfirm = (Button) dialogView.findViewById(R.id.btn_confirm);
btnPreview = (Button) dialogView.findViewById(R.id.btn_preview);
etURL = (EditText) dialogView.findViewById(R.id.et_url);
bvBackgroundPreview = (BackgroundView) dialogView.findViewById(R.id.bv_background_preview);
// 加载初始图片
bvBackgroundPreview.setBackgroundResource(R.drawable.ic_launcher);
// 设置按钮点击事件
setButtonClickListeners();
}
/**
* 设置按钮点击监听
*/
private void setButtonClickListeners() {
// 取消按钮:关闭对话框 + 回调外部
btnCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d("NetworkBackgroundDialog", "取消按钮点击");
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(mContext);
utils.setCurrentSourceToPreview();
dismiss(); // 关闭对话框
if (listener != null) {
listener.onCancel();
}
}
});
// 确认按钮:关闭对话框 + 回调外部
btnConfirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d("NetworkBackgroundDialog", "确认按钮点击");
// 确定预览背景资源
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(mContext);
utils.saveFileToPreviewBean(new File(mPreviewFilePath), mPreviewFileUrl);
dismiss(); // 关闭对话框
if (listener != null) {
listener.onConfirm(mPreviewFilePath, mPreviewFileUrl);
}
}
});
// 图片预览按钮:预览输入框地址图片
btnPreview.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
downloadImageToAlbumAndPreview();
}
});
}
/**
* 根据文件路径设置 BackgroundView 背景(主线程调用)
* @param filePath 图片文件路径
*/
private void previewBackground(String previewFilePath) {
FileInputStream fis = null;
try {
File imageFile = new File(previewFilePath);
if (!imageFile.exists()) {
ToastUtils.show("图片文件不存在:" + previewFilePath);
LogUtils.e(TAG, "图片文件不存在:" + previewFilePath);
bvBackgroundPreview.setBackgroundResource(R.drawable.ic_launcher);
return;
}
// 预览背景
mPreviewFilePath = previewFilePath;
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(mContext);
utils.saveFileToPreviewBean(new File(mPreviewFilePath), mPreviewFileUrl);
bvBackgroundPreview.reloadPreviewBackground();
//ToastUtils.show("预览背景中。。。");
} catch (Exception e) {
e.printStackTrace();
bvBackgroundPreview.setBackgroundResource(R.drawable.ic_launcher);
LogUtils.e(TAG, "图片预览失败:" + e.getMessage());
} finally {
// Java7 手动关闭流,避免资源泄漏
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 对外提供方法:修改对话框标题(灵活适配不同场景)
*/
public void setTitle(String title) {
if (tvTitle != null) {
tvTitle.setText(title);
}
}
/**
* 对外提供方法:修改对话框内容(灵活适配不同场景)
*/
public void setContent(String content) {
if (tvContent != null) {
tvContent.setText(content);
}
}
/**
* 对外提供方法:设置按钮点击回调(替代带参构造)
*/
public void setOnDialogClickListener(OnDialogClickListener listener) {
this.listener = listener;
}
void downloadImageToAlbumAndPreview() {
//String previewFileUrl = "https://example.com/test.jpg";
mPreviewFileUrl = etURL.getText().toString();
ImageDownloader.getInstance(mContext).downloadImage(mPreviewFileUrl, new ImageDownloader.DownloadCallback(){
@Override
public void onSuccess(String savePath) {
// 发送消息到主线程,携带图片路径
Message successMsg = mUiHandler.obtainMessage(MSG_IMAGE_LOAD_SUCCESS, savePath);
mUiHandler.sendMessage(successMsg);
}
@Override
public void onFailure(String errorMsg) {
ToastUtils.show("下载失败:" + errorMsg);
}
});
}
}

View File

@@ -9,7 +9,6 @@ import android.os.Message;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.ImageView; import android.widget.ImageView;
@@ -20,15 +19,15 @@ import android.widget.TextView;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App; import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity; import cc.winboll.studio.powerbell.activities.PixelPickerActivity;
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean; import cc.winboll.studio.powerbell.model.BackgroundBean;
import cc.winboll.studio.powerbell.services.ControlCenterService; import cc.winboll.studio.powerbell.services.ControlCenterService;
import cc.winboll.studio.powerbell.utils.AppConfigUtils; import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils; import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
import cc.winboll.studio.powerbell.utils.ServiceUtils; import cc.winboll.studio.powerbell.utils.ServiceUtils;
import cc.winboll.studio.powerbell.views.BackgroundView;
import cc.winboll.studio.powerbell.views.BatteryDrawable; import cc.winboll.studio.powerbell.views.BatteryDrawable;
import cc.winboll.studio.powerbell.views.VerticalSeekBar; import cc.winboll.studio.powerbell.views.VerticalSeekBar;
import java.io.File;
public class MainViewFragment extends Fragment { public class MainViewFragment extends Fragment {
@@ -72,6 +71,7 @@ public class MainViewFragment extends Fragment {
TextView mtvUsegeReminderValue; TextView mtvUsegeReminderValue;
CheckBox mcbUsegeReminderValue; CheckBox mcbUsegeReminderValue;
TextView mtvCurrentValue; TextView mtvCurrentValue;
BackgroundView bvPreviewBackground;
@Override @Override
@@ -79,27 +79,33 @@ public class MainViewFragment extends Fragment {
mView = inflater.inflate(R.layout.fragment_mainview, container, false); mView = inflater.inflate(R.layout.fragment_mainview, container, false);
_mMainViewFragment = MainViewFragment.this; _mMainViewFragment = MainViewFragment.this;
mAppConfigUtils = App.getAppConfigUtils(getActivity()); mAppConfigUtils = App.getAppConfigUtils(getActivity());
// 获取指定ID的View实例 // 获取指定ID的View实例
final View mainImageView = mView.findViewById(R.id.fragmentmainviewImageView1); bvPreviewBackground = mView.findViewById(R.id.fragmentmainviewBackgroundView1);
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(getActivity());
BackgroundBean bean = utils.getCurrentBackgroundBean();
int nPixelColor = bean.getPixelColor();
bvPreviewBackground.setBackgroundColor(nPixelColor);
/*final View mainImageView = mView.findViewById(R.id.fragmentmainviewImageView1);
// 注册OnGlobalLayoutListener // 注册OnGlobalLayoutListener
mainImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { mainImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override @Override
public void onGlobalLayout() { public void onGlobalLayout() {
// 获取宽度和高度 // 获取宽度和高度
int width = mainImageView.getMeasuredWidth(); int width = mainImageView.getMeasuredWidth();
int height = mainImageView.getMeasuredHeight(); int height = mainImageView.getMeasuredHeight();
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(getActivity()); BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(getActivity());
BackgroundPictureBean bean = utils.loadBackgroundPictureBean(); BackgroundPictureBean bean = utils.loadBackgroundPictureBean();
bean.setBackgroundWidth(width); bean.setBackgroundWidth(width);
bean.setBackgroundHeight(height); bean.setBackgroundHeight(height);
utils.saveData(); utils.saveData();
// 移除监听器以避免内存泄漏 // 移除监听器以避免内存泄漏
mainImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this); mainImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} }
}); });*/
mDrawableFrame = getActivity().getDrawable(R.drawable.bg_frame); mDrawableFrame = getActivity().getDrawable(R.drawable.bg_frame);
mllLeftSeekBar = (LinearLayout) mView.findViewById(R.id.fragmentmainviewLinearLayout1); mllLeftSeekBar = (LinearLayout) mView.findViewById(R.id.fragmentmainviewLinearLayout1);
@@ -143,6 +149,17 @@ public class MainViewFragment extends Fragment {
return mView; return mView;
} }
@Override
public void onResume() {
super.onResume();
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(getActivity());
BackgroundBean bean = utils.getCurrentBackgroundBean();
int nPixelColor = bean.getPixelColor();
bvPreviewBackground.setBackgroundColor(nPixelColor);
}
void setViewData() { void setViewData() {
int nChargeReminderValue = mAppConfigUtils.getChargeReminderValue(); int nChargeReminderValue = mAppConfigUtils.getChargeReminderValue();
int nUsegeReminderValue = mAppConfigUtils.getUsegeReminderValue(); int nUsegeReminderValue = mAppConfigUtils.getUsegeReminderValue();
@@ -302,22 +319,23 @@ public class MainViewFragment extends Fragment {
} }
} }
public void loadBackground() { public void reloadBackground() {
BackgroundPictureBean bean = BackgroundPictureUtils.getInstance(getActivity()).getBackgroundPictureBean(); bvPreviewBackground.reloadCurrentBackground();
ImageView imageView = mView.findViewById(R.id.fragmentmainviewImageView1); // BackgroundPictureBean bean = BackgroundPictureUtils.getInstance(getActivity()).getBackgroundPictureBean();
String szBackgroundFilePath = BackgroundPictureUtils.getInstance(getActivity()).getBackgroundDir() + BackgroundPictureActivity.getBackgroundFileName(); // ImageView imageView = mView.findViewById(R.id.fragmentmainviewImageView1);
File fBackgroundFilePath = new File(szBackgroundFilePath); // String szBackgroundFilePath = BackgroundPictureUtils.getInstance(getActivity()).getBackgroundDir() + BackgroundPictureActivity.getBackgroundFileName();
LogUtils.d(TAG, "szBackgroundFilePath : " + szBackgroundFilePath); // File fBackgroundFilePath = new File(szBackgroundFilePath);
LogUtils.d(TAG, String.format("fBackgroundFilePath.exists() %s", fBackgroundFilePath.exists())); // LogUtils.d(TAG, "szBackgroundFilePath : " + szBackgroundFilePath);
if (bean.isUseBackgroundFile() && fBackgroundFilePath.exists()) { // LogUtils.d(TAG, String.format("fBackgroundFilePath.exists() %s", fBackgroundFilePath.exists()));
Drawable drawableBackground = Drawable.createFromPath(szBackgroundFilePath); // if (bean.isUseBackgroundFile() && fBackgroundFilePath.exists()) {
//drawableBackground.setAlpha(120); // Drawable drawableBackground = Drawable.createFromPath(szBackgroundFilePath);
imageView.setImageDrawable(drawableBackground); // //drawableBackground.setAlpha(120);
} else { // imageView.setImageDrawable(drawableBackground);
Drawable drawableBackground = getActivity().getDrawable(R.drawable.blank10x10); // } else {
//drawableBackground.setAlpha(120); // Drawable drawableBackground = getActivity().getDrawable(R.drawable.blank10x10);
imageView.setImageDrawable(drawableBackground); // //drawableBackground.setAlpha(120);
} // imageView.setImageDrawable(drawableBackground);
// }
} }
Handler mHandler = new Handler(){ Handler mHandler = new Handler(){

View File

@@ -1,4 +1,4 @@
package cc.winboll.studio.powerbell.beans; package cc.winboll.studio.powerbell.model;
/** /**
* @Author ZhanGSKen<zhangsken@qq.com> * @Author ZhanGSKen<zhangsken@qq.com>

View File

@@ -0,0 +1,143 @@
package cc.winboll.studio.powerbell.model;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/18 11:52:28
* @Describe 应用背景图片数据类
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
public class BackgroundBean extends BaseBean {
public static final String TAG = "BackgroundPictureBean";
String backgroundFileName = "";
String backgroundFileInfo = "";
boolean isUseBackgroundFile = false;
String backgroundScaledCompressFileName = "";
boolean isUseScaledCompress = false;
int backgroundWidth = 100;
int backgroundHeight = 100;
// 图片拾取像素颜色
int pixelColor = 0;
public BackgroundBean() {
}
public void setBackgroundScaledCompressFileName(String backgroundScaledCompressFileName) {
this.backgroundScaledCompressFileName = backgroundScaledCompressFileName;
}
public String getBackgroundScaledCompressFileName() {
return backgroundScaledCompressFileName;
}
public void setIsUseScaledCompress(boolean isUseScaledCompress) {
this.isUseScaledCompress = isUseScaledCompress;
}
public boolean isUseScaledCompress() {
return isUseScaledCompress;
}
public void setIsUseBackgroundFile(boolean isUseBackgroundFile) {
this.isUseBackgroundFile = isUseBackgroundFile;
}
public boolean isUseBackgroundFile() {
return isUseBackgroundFile;
}
public void setBackgroundFileInfo(String backgroundFileInfo) {
this.backgroundFileInfo = backgroundFileInfo;
}
public String getBackgroundFileInfo() {
return backgroundFileInfo;
}
public void setBackgroundFileName(String backgroundFileName) {
this.backgroundFileName = backgroundFileName;
}
public String getBackgroundFileName() {
return backgroundFileName;
}
public void setPixelColor(int pixelColor) {
this.pixelColor = pixelColor;
}
public int getPixelColor() {
return pixelColor;
}
public void setBackgroundWidth(int backgroundWidth) {
this.backgroundWidth = backgroundWidth;
}
public int getBackgroundWidth() {
return backgroundWidth;
}
public void setBackgroundHeight(int backgroundHeight) {
this.backgroundHeight = backgroundHeight;
}
public int getBackgroundHeight() {
return backgroundHeight;
}
@Override
public String getName() {
return BackgroundBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
BackgroundBean bean = this;
jsonWriter.name("backgroundFileName").value(bean.getBackgroundFileName());
jsonWriter.name("backgroundFileInfo").value(bean.getBackgroundFileInfo());
jsonWriter.name("isUseBackgroundFile").value(bean.isUseBackgroundFile());
jsonWriter.name("backgroundScaledCompressFileName").value(bean.getBackgroundScaledCompressFileName());
jsonWriter.name("isUseScaledCompress").value(bean.isUseScaledCompress());
jsonWriter.name("backgroundWidth").value(bean.getBackgroundWidth());
jsonWriter.name("backgroundHeight").value(bean.getBackgroundHeight());
jsonWriter.name("pixelColor").value(bean.getPixelColor());
}
@Override
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
BackgroundBean bean = new BackgroundBean();
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (name.equals("backgroundFileName")) {
bean.setBackgroundFileName(jsonReader.nextString());
} else if (name.equals("backgroundFileInfo")) {
bean.setBackgroundFileInfo(jsonReader.nextString());
} else if (name.equals("isUseBackgroundFile")) {
bean.setIsUseBackgroundFile(jsonReader.nextBoolean());
} else if (name.equals("backgroundScaledCompressFileName")) {
bean.setBackgroundScaledCompressFileName(jsonReader.nextString());
} else if (name.equals("isUseScaledCompress")) {
bean.setIsUseScaledCompress(jsonReader.nextBoolean());
} else if (name.equals("backgroundWidth")) {
bean.setBackgroundWidth(jsonReader.nextInt());
} else if (name.equals("backgroundHeight")) {
bean.setBackgroundHeight(jsonReader.nextInt());
} else if (name.equals("pixelColor")) {
bean.setPixelColor(jsonReader.nextInt());
} else {
jsonReader.skipValue();
}
}
// 结束 JSON 对象
jsonReader.endObject();
return bean;
}
}

View File

@@ -1,4 +1,4 @@
package cc.winboll.studio.powerbell.beans; package cc.winboll.studio.powerbell.model;
/** /**
* @Author ZhanGSKen<zhangsken@qq.com> * @Author ZhanGSKen<zhangsken@qq.com>

View File

@@ -1,4 +1,4 @@
package cc.winboll.studio.powerbell.beans; package cc.winboll.studio.powerbell.model;
import android.util.JsonReader; import android.util.JsonReader;
import android.util.JsonWriter; import android.util.JsonWriter;

View File

@@ -1,4 +1,4 @@
package cc.winboll.studio.powerbell.beans; package cc.winboll.studio.powerbell.model;
/** /**
* @Author ZhanGSKen<zhangsken@qq.com> * @Author ZhanGSKen<zhangsken@qq.com>

View File

@@ -1,4 +1,4 @@
package cc.winboll.studio.powerbell.beans; package cc.winboll.studio.powerbell.model;
// 应用消息结构 // 应用消息结构
// //

View File

@@ -5,7 +5,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.beans.AppConfigBean; import cc.winboll.studio.powerbell.model.AppConfigBean;
import cc.winboll.studio.powerbell.services.ControlCenterService; import cc.winboll.studio.powerbell.services.ControlCenterService;
import cc.winboll.studio.powerbell.utils.AppConfigUtils; import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import cc.winboll.studio.powerbell.utils.BatteryUtils; import cc.winboll.studio.powerbell.utils.BatteryUtils;

View File

@@ -23,8 +23,8 @@ import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.App; import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.MainActivity; import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.beans.AppConfigBean; import cc.winboll.studio.powerbell.model.AppConfigBean;
import cc.winboll.studio.powerbell.beans.NotificationMessage; import cc.winboll.studio.powerbell.model.NotificationMessage;
import cc.winboll.studio.powerbell.handlers.ControlCenterServiceHandler; import cc.winboll.studio.powerbell.handlers.ControlCenterServiceHandler;
import cc.winboll.studio.powerbell.receivers.ControlCenterServiceReceiver; import cc.winboll.studio.powerbell.receivers.ControlCenterServiceReceiver;
import cc.winboll.studio.powerbell.services.AssistantService; import cc.winboll.studio.powerbell.services.AssistantService;

View File

@@ -0,0 +1,48 @@
package cc.winboll.studio.powerbell.unittest;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.R;
import android.widget.Button;
import cc.winboll.studio.powerbell.MainActivity;
import android.content.Intent;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/19 18:16
* @Describe BackgroundViewTestFragment
*/
public class BackgroundViewTestFragment extends Fragment {
public static final String TAG = "BackgroundViewTestFragment";
View mainView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//super.onCreateView(inflater, container, savedInstanceState);
// 非调试状态就结束本线程
if (!GlobalApplication.isDebugging()) {
Thread.currentThread().destroy();
}
mainView = inflater.inflate(R.layout.fragment_test_backgroundview, container, false);
((Button)mainView.findViewById(R.id.btn_main_activity)).setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
getActivity().startActivity(new Intent(getActivity(), MainActivity.class));
}
});
ToastUtils.show(String.format("%s onCreate", TAG));
return mainView;
}
}

View File

@@ -0,0 +1,39 @@
package cc.winboll.studio.powerbell.unittest;
import android.os.Bundle;
import android.widget.FrameLayout;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.powerbell.R;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import android.nfc.tech.TagTechnology;
import cc.winboll.studio.libappbase.ToastUtils;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/19 18:04
* @Describe 单元测试启动主页窗口
*/
public class MainUnitTestActivity extends AppCompatActivity {
public static final String TAG = "MainUnitTestActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 非调试状态就退出
if (!GlobalApplication.isDebugging()) {
finish();
}
setContentView(R.layout.activity_mainunittest);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.activitymainunittestFrameLayout1, new BackgroundViewTestFragment(), BackgroundViewTestFragment.TAG);
fragmentTransaction.commit();
ToastUtils.show(String.format("%s onCreate", TAG));
}
}

View File

@@ -0,0 +1,164 @@
package cc.winboll.studio.powerbell.utils;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/26 15:54
* @Describe 应用图标切换工具类(启用组件时创建对应快捷方式)
*/
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.widget.Toast;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.R;
public class APPPlusUtils {
public static final String TAG = "APPPlusUtils";
// 快捷方式配置(名称+图标,需与实际资源匹配)
// private static final String PLUS_SHORTCUT_NAME = "位置服务-Laojun";
// private static final int PLUS_SHORTCUT_ICON = R.mipmap.ic_launcher; // Laojun 图标资源
/**
* 添加Plus组件与图标
*/
public static boolean switchAppLauncherToComponent(Context context, String componentName) {
if (context == null) {
LogUtils.d(TAG, "切换失败:上下文为空");
Toast.makeText(context, context.getString(R.string.app_name) + "图标切换失败", Toast.LENGTH_SHORT).show();
return false;
}
PackageManager pm = context.getPackageManager();
ComponentName plusComponentSwitchTo = new ComponentName(context, componentName);
ComponentName plusComponentEN1 = new ComponentName(context, App.COMPONENT_EN1);
ComponentName plusComponentCN1 = new ComponentName(context, App.COMPONENT_CN1);
ComponentName plusComponentCN2 = new ComponentName(context, App.COMPONENT_CN2);
try {
disableComponent(pm, plusComponentEN1);
disableComponent(pm, plusComponentCN1);
disableComponent(pm, plusComponentCN2);
enableComponent(pm, plusComponentSwitchTo);
return true;
} catch (Exception e) {
LogUtils.e(TAG, "图标切换失败:" + e.getMessage());
Toast.makeText(context, context.getString(R.string.app_name) + "图标切换失败" + e.getMessage(), Toast.LENGTH_SHORT).show();
return false;
}
}
/**
* 创建指定组件的桌面快捷方式(自动去重,兼容 Android 8.0+
* @param component 目标组件(如 LAOJUN_ACTIVITY
* @param name 快捷方式名称
* @param iconRes 快捷方式图标资源ID
* @return 是否创建成功
*/
private static boolean createComponentShortcut(Context context, ComponentName component, String name, int iconRes) {
if (context == null || component == null || name == null || iconRes == 0) {
LogUtils.d(TAG, "快捷方式创建失败:参数为空");
return false;
}
// Android 8.0+API 26+):使用 ShortcutManager系统推荐
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
PackageManager pm = context.getPackageManager();
android.content.pm.ShortcutManager shortcutManager = context.getSystemService(android.content.pm.ShortcutManager.class);
if (shortcutManager == null || !shortcutManager.isRequestPinShortcutSupported()) {
LogUtils.d(TAG, "系统不支持创建快捷方式");
return false;
}
// 检查是否已存在该组件的快捷方式(去重)
for (android.content.pm.ShortcutInfo info : shortcutManager.getPinnedShortcuts()) {
if (component.getClassName().equals(info.getIntent().getComponent().getClassName())) {
LogUtils.d(TAG, "快捷方式已存在:" + component.getClassName());
return true;
}
}
// 构建启动目标组件的意图
Intent launchIntent = new Intent(Intent.ACTION_MAIN)
.setComponent(component)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 构建快捷方式信息
android.content.pm.ShortcutInfo shortcutInfo = new android.content.pm.ShortcutInfo.Builder(context, component.getClassName())
.setShortLabel(name)
.setLongLabel(name)
.setIcon(android.graphics.drawable.Icon.createWithResource(context, iconRes))
.setIntent(launchIntent)
.build();
// 请求创建快捷方式(需用户确认)
shortcutManager.requestPinShortcut(shortcutInfo, null);
return true;
} catch (Exception e) {
LogUtils.d(TAG, "Android O+ 快捷方式创建失败:" + e.getMessage());
return false;
}
} else {
// Android 8.0 以下:使用广播(兼容旧机型)
try {
// 构建启动目标组件的意图
Intent launchIntent = new Intent(Intent.ACTION_MAIN)
.setComponent(component)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 构建创建快捷方式的广播意图
Intent installIntent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
installIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent);
installIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
installIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(context, iconRes));
installIntent.putExtra("duplicate", false); // 禁止重复创建
context.sendBroadcast(installIntent);
return true;
} catch (Exception e) {
LogUtils.d(TAG, "Android O- 快捷方式创建失败:" + e.getMessage());
return false;
}
}
}
/**
* 启用组件(带状态检查,避免重复操作)
*/
private static void enableComponent(PackageManager pm, ComponentName component) {
if (pm.getComponentEnabledSetting(component) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
pm.setComponentEnabledSetting(
component,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS
);
}
}
/**
* 禁用组件(带状态检查,避免重复操作)
*/
private static void disableComponent(PackageManager pm, ComponentName component) {
if (pm.getComponentEnabledSetting(component) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
pm.setComponentEnabledSetting(
component,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS
);
}
}
}

View File

@@ -2,7 +2,7 @@ package cc.winboll.studio.powerbell.utils;
import android.content.Context; import android.content.Context;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.beans.BatteryInfoBean; import cc.winboll.studio.powerbell.model.BatteryInfoBean;
import java.util.ArrayList; import java.util.ArrayList;
public class AppCacheUtils { public class AppCacheUtils {

View File

@@ -5,8 +5,8 @@ import android.content.Context;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App; import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.MainActivity; import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.beans.AppConfigBean; import cc.winboll.studio.powerbell.model.AppConfigBean;
import cc.winboll.studio.powerbell.beans.ControlCenterServiceBean; import cc.winboll.studio.powerbell.model.ControlCenterServiceBean;
import cc.winboll.studio.powerbell.dialogs.YesNoAlertDialog; import cc.winboll.studio.powerbell.dialogs.YesNoAlertDialog;
import cc.winboll.studio.powerbell.fragments.MainViewFragment; import cc.winboll.studio.powerbell.fragments.MainViewFragment;
import cc.winboll.studio.powerbell.services.ControlCenterService; import cc.winboll.studio.powerbell.services.ControlCenterService;

View File

@@ -1,64 +0,0 @@
package cc.winboll.studio.powerbell.utils;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/18 12:07:20
* @Describe 背景图片工具集
*/
import android.content.Context;
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
import java.io.File;
public class BackgroundPictureUtils {
public static final String TAG = "BackgroundPictureUtils";
static BackgroundPictureUtils _mBackgroundPictureUtils;
Context mContext;
BackgroundPictureBean mBackgroundPictureBean;
// 背景图片目录
String mszBackgroundDir;
BackgroundPictureUtils(Context context) {
mContext = context;
String szExternalFilesDir = mContext.getExternalFilesDir(TAG) + File.separator;
setBackgroundDir(szExternalFilesDir + "Background" + File.separator);
loadBackgroundPictureBean();
}
public static BackgroundPictureUtils getInstance(Context context) {
if (_mBackgroundPictureUtils == null) {
_mBackgroundPictureUtils = new BackgroundPictureUtils(context);
}
return _mBackgroundPictureUtils;
}
//
// 加载应用背景图片配置数据
//
public BackgroundPictureBean loadBackgroundPictureBean() {
mBackgroundPictureBean = BackgroundPictureBean.loadBean(mContext, BackgroundPictureBean.class);
if (mBackgroundPictureBean == null) {
mBackgroundPictureBean = new BackgroundPictureBean();
BackgroundPictureBean.saveBean(mContext, mBackgroundPictureBean);
}
return mBackgroundPictureBean;
}
void setBackgroundDir(String mszBackgroundDir) {
this.mszBackgroundDir = mszBackgroundDir;
}
public String getBackgroundDir() {
return mszBackgroundDir;
}
public BackgroundPictureBean getBackgroundPictureBean() {
return mBackgroundPictureBean;
}
public void saveData() {
BackgroundPictureBean.saveBean(mContext, mBackgroundPictureBean);
}
}

View File

@@ -0,0 +1,144 @@
package cc.winboll.studio.powerbell.utils;
import android.content.Context;
import cc.winboll.studio.powerbell.model.BackgroundBean;
import java.io.File;
import java.util.UUID;
import cc.winboll.studio.libappbase.ToastUtils;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/18 12:07:20
* @Describe 背景图片工具集(修复单例模式,线程安全)
*/
public class BackgroundSourceUtils {
public static final String TAG = "BackgroundPictureUtils";
// 1. 静态实例加volatile禁止指令重排保证可见性
private static volatile BackgroundSourceUtils sInstance;
private Context mContext;
private File currentBackgroundBeanFile;
private BackgroundBean currentBackgroundBean;
private File previewBackgroundBeanFile;
private BackgroundBean previewBackgroundBean;
// 应用外部存储文件夹路径
private File fUtilsDir;
private File fModelDir;
// 背景图片目录
private File fBackgroundSourceDir;
// 2. 私有构造器(加防反射逻辑)
private BackgroundSourceUtils(Context context) {
// 防反射破坏:若已有实例,抛异常阻止创建
if (sInstance != null) {
throw new RuntimeException("BackgroundSourceUtils 是单例类,禁止重复创建!");
}
// 上下文建议用Application Context避免内存泄漏
this.mContext = context.getApplicationContext();
fUtilsDir = this.mContext.getExternalFilesDir(TAG);
fModelDir = new File(fUtilsDir, "ModelDir");
currentBackgroundBeanFile = new File(fModelDir, "currentBackgroundBean.json");
previewBackgroundBeanFile = new File(fModelDir, "previewBackgroundBean.json");
fBackgroundSourceDir = new File(fUtilsDir, "BackgroundSource");
// 加载配置
loadSettings();
}
// 3. 双重校验锁单例(线程安全,高效)
public static BackgroundSourceUtils getInstance(Context context) {
// 第一重校验:避免每次调用都加锁(提高效率)
if (sInstance == null) {
// 同步锁:保证同一时刻只有一个线程进入创建逻辑
synchronized (BackgroundSourceUtils.class) {
// 第二重校验:防止多线程并发时重复创建(核心)
if (sInstance == null) {
sInstance = new BackgroundSourceUtils(context);
}
}
}
return sInstance;
}
/*
* 加载背景图片配置数据
*/
void loadSettings() {
currentBackgroundBean = BackgroundBean.loadBeanFromFile(currentBackgroundBeanFile.getAbsolutePath(), BackgroundBean.class);
if (currentBackgroundBean == null) {
currentBackgroundBean = new BackgroundBean();
BackgroundBean.saveBeanToFile(currentBackgroundBeanFile.getAbsolutePath(), currentBackgroundBean);
}
previewBackgroundBean = BackgroundBean.loadBeanFromFile(previewBackgroundBeanFile.getAbsolutePath(), BackgroundBean.class);
if (previewBackgroundBean == null) {
previewBackgroundBean = new BackgroundBean();
BackgroundBean.saveBeanToFile(previewBackgroundBeanFile.getAbsolutePath(), previewBackgroundBean);
}
}
public BackgroundBean getCurrentBackgroundBean() {
return currentBackgroundBean;
}
public BackgroundBean getPreviewBackgroundBean() {
return previewBackgroundBean;
}
public String getCurrentBackgroundFilePath() {
loadSettings();
File file = new File(fBackgroundSourceDir, currentBackgroundBean.getBackgroundFileName());
return file.getAbsolutePath();
}
public String getPreviewBackgroundFilePath() {
loadSettings();
File file = new File(fBackgroundSourceDir, previewBackgroundBean.getBackgroundFileName());
return file.getAbsolutePath();
}
public String getPreviewBackgroundScaledCompressFilePath() {
loadSettings();
File file = new File(fBackgroundSourceDir, previewBackgroundBean.getBackgroundScaledCompressFileName());
return file.getAbsolutePath();
}
public void saveSettings() {
BackgroundBean.saveBeanToFile(currentBackgroundBeanFile.getAbsolutePath(), currentBackgroundBean);
BackgroundBean.saveBeanToFile(previewBackgroundBeanFile.getAbsolutePath(), previewBackgroundBean);
}
public String getBackgroundSourceDirPath() {
return fBackgroundSourceDir.getAbsolutePath();
}
/*
* 保存图片到预览模型, 并返回预览模型数据
*/
public BackgroundBean saveFileToPreviewBean(File sourceFile, String fileInfo) {
File previewBackgroundFile = new File(fBackgroundSourceDir, FileUtils.createUniqueFileName(sourceFile));
//ToastUtils.show(String.format("saveFileToPreviewBean previewBackgroundFile : %s", previewBackgroundFile.getAbsolutePath()));
FileUtils.copyFile(sourceFile, previewBackgroundFile);
previewBackgroundBean = new BackgroundBean();
previewBackgroundBean.setBackgroundFileName(previewBackgroundFile.getName());
previewBackgroundBean.setBackgroundScaledCompressFileName("ScaledCompress_"+previewBackgroundFile.getName());
previewBackgroundBean.setBackgroundFileName(fileInfo);
saveSettings();
ToastUtils.show(String.format("saveFileToPreviewBean getPreviewBackgroundFilePath() : %s", getPreviewBackgroundFilePath()));
return previewBackgroundBean;
}
public void commitPreviewSourceToCurrent() {
currentBackgroundBean = previewBackgroundBean;
saveSettings();
}
public void setCurrentSourceToPreview() {
previewBackgroundBean = currentBackgroundBean;
saveSettings();
}
}

View File

@@ -1,176 +1,236 @@
package cc.winboll.studio.powerbell.utils; package cc.winboll.studio.powerbell.utils;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream; import java.io.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.UUID;
/** /**
* 文件读取工具类 * 文件操作工具类
* 功能:文件读写、复制、图片转换、文件名处理等常用文件操作
* 适配Java 7+支持Android全版本
* 注意调用文件操作前需确保已获取存储权限Android 6.0+ 需动态申请)
*/ */
public class FileUtils { public class FileUtils {
/** 日志标签 */
public static final String TAG = "FileUtils"; public static final String TAG = "FileUtils";
/** 读取文件默认缓冲区大小10KB */
private static final int BUFFER_SIZE = 10240;
/** 最大读取文件大小1GB防止OOM */
private static final long MAX_READ_FILE_SIZE = 1024 * 1024 * 1024;
// // ====================================== 文件读取相关 ======================================
// 读取文件内容,作为字符串返回
// /**
* 读取文件内容并转为字符串
* @param filePath 文件绝对路径(非空)
* @return 文件内容字符串
* @throws IOException 异常:文件不存在、文件过大、读取失败等
*/
public static String readFileAsString(String filePath) throws IOException { public static String readFileAsString(String filePath) throws IOException {
// 1. 校验文件合法性
File file = new File(filePath); File file = new File(filePath);
if (!file.exists()) { if (!file.exists()) {
throw new FileNotFoundException(filePath); throw new FileNotFoundException("文件不存在:" + filePath);
} }
if (file.length() > MAX_READ_FILE_SIZE) {
throw new IOException("文件过大超过1GB禁止读取" + filePath);
}
if (file.length() > 1024 * 1024 * 1024) { // 2. 读取文件内容使用StringBuilder高效拼接
throw new IOException("File is too large"); StringBuilder sb = new StringBuilder((int) file.length());
} try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[BUFFER_SIZE];
StringBuilder sb = new StringBuilder((int) (file.length())); int readLen;
// 创建字节输入流 // 循环读取缓冲区避免一次性读取大文件导致OOM
FileInputStream fis = new FileInputStream(filePath); while ((readLen = fis.read(buffer)) > 0) {
// 创建一个长度为10240的Buffer sb.append(new String(buffer, 0, readLen));
byte[] bbuf = new byte[10240]; }
// 用于保存实际读取的字节数 }
int hasRead = 0;
while ((hasRead = fis.read(bbuf)) > 0) {
sb.append(new String(bbuf, 0, hasRead));
}
fis.close();
return sb.toString(); return sb.toString();
} }
// /**
// 根据文件路径读取byte[] 数组 * 读取文件内容并转为byte数组适用于二进制文件图片、音频等
// * @param filePath 文件绝对路径(非空)
* @return 文件内容byte数组
* @throws IOException 异常:文件不存在、读取失败等
*/
public static byte[] readFileByBytes(String filePath) throws IOException { public static byte[] readFileByBytes(String filePath) throws IOException {
// 1. 校验文件合法性
File file = new File(filePath); File file = new File(filePath);
if (!file.exists()) { if (!file.exists()) {
throw new FileNotFoundException(filePath); throw new FileNotFoundException("文件不存在:" + filePath);
} else { }
ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
BufferedInputStream in = null;
try { // 2. 缓冲流读取高效减少IO次数
in = new BufferedInputStream(new FileInputStream(file)); try (ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
short bufSize = 1024; BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
byte[] buffer = new byte[bufSize];
int len1;
while (-1 != (len1 = in.read(buffer, 0, bufSize))) {
bos.write(buffer, 0, len1);
}
byte[] var7 = bos.toByteArray(); byte[] buffer = new byte[BUFFER_SIZE];
return var7; int readLen;
} finally { while ((readLen = bis.read(buffer)) != -1) {
try { bos.write(buffer, 0, readLen);
if (in != null) {
in.close();
}
} catch (IOException var14) {
var14.printStackTrace();
}
bos.close();
} }
bos.flush();
return bos.toByteArray();
} }
} }
// // ====================================== 文件复制相关 ======================================
// 文件复制函数
// /**
* 基于FileChannel复制文件高效适用于大文件复制
* @param source 源文件(非空,必须存在)
* @param dest 目标文件(非空,父目录会自动创建)
* @throws IOException 异常:源文件不存在、复制失败等
*/
public static void copyFileUsingFileChannels(File source, File dest) throws IOException { public static void copyFileUsingFileChannels(File source, File dest) throws IOException {
FileChannel inputChannel = null; // 1. 校验源文件合法性
FileChannel outputChannel = null; if (!source.exists() || !source.isFile()) {
try { throw new FileNotFoundException("源文件不存在或不是文件:" + source.getAbsolutePath());
inputChannel = new FileInputStream(source).getChannel(); }
outputChannel = new FileOutputStream(dest).getChannel();
// 2. 创建目标文件父目录
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}
// 3. 通道复制try-with-resources 自动关闭通道,无需手动关闭)
try (FileChannel inputChannel = new FileInputStream(source).getChannel();
FileChannel outputChannel = new FileOutputStream(dest).getChannel()) {
// 从输入通道复制到输出通道(高效,底层优化)
outputChannel.transferFrom(inputChannel, 0, inputChannel.size()); outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
} finally { LogUtils.d(TAG, "文件复制成功FileChannel" + source.getAbsolutePath() + "" + dest.getAbsolutePath());
inputChannel.close();
outputChannel.close();
} }
} }
/** /**
* 将文件生成位图 * 简化版文件复制基于NIO Files工具类代码简洁适用于中小文件
* @param path * @param oldFile 源文件(非空,必须存在)
* @return * @param newFile 目标文件(非空,父目录会自动创建)
* @throws IOException * @return 复制结果true-成功false-失败
*/ */
public static BitmapDrawable getImageDrawable(String path)
throws IOException {
//打开文件
File file = new File(path);
if (!file.exists()) {
return null;
}
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
int BUFFER_SIZE = 1000;
byte[] bt = new byte[BUFFER_SIZE];
//得到文件的输入流
InputStream in = new FileInputStream(file);
//将文件读出到输出流中
int readLength = in.read(bt);
while (readLength != -1) {
outStream.write(bt, 0, readLength);
readLength = in.read(bt);
}
//转换成byte 后 再格式化成位图
byte[] data = outStream.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);// 生成位图
BitmapDrawable bd = new BitmapDrawable(bitmap);
return bd;
}
public static boolean copyFile(File oldFile, File newFile) { public static boolean copyFile(File oldFile, File newFile) {
//String oldPath = "path/to/original/file.txt"; // 1. 校验源文件合法性
//String newPath = "path/to/new-location/for/file.txt"; if (oldFile == null || !oldFile.exists() || !oldFile.isFile()) {
LogUtils.e(TAG, "源文件无效:" + (oldFile != null ? oldFile.getAbsolutePath() : "null"));
return false;
}
//File oldFile = new java.io.File(oldPath); // 2. 创建目标文件父目录
//File newFile = new java.io.File(newPath);
if (!newFile.getParentFile().exists()) { if (!newFile.getParentFile().exists()) {
newFile.getParentFile().mkdirs(); newFile.getParentFile().mkdirs();
} }
if (!oldFile.exists()) { // 3. 复制文件(覆盖已有目标文件)
//System.out.println("The original file does not exist."); try {
LogUtils.d(TAG, "The original file does not exist."); Path sourcePath = Paths.get(oldFile.getPath());
} else { Path destPath = Paths.get(newFile.getPath());
try { // 先删除已有目标文件(避免覆盖失败)
// 源文件路径 if (newFile.exists()) {
Path sourcePath = Paths.get(oldFile.getPath()); newFile.delete();
// 目标文件路径
Path destPath = Paths.get(newFile.getPath());
if(newFile.exists()) {
newFile.delete();
}
Files.copy(sourcePath, destPath);
LogUtils.d(TAG, "File copy successfully.");
//System.out.println("File moved successfully.");
return true;
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
//System.err.println("An error occurred while moving the file: " + e.getMessage());
} }
Files.copy(sourcePath, destPath);
LogUtils.d(TAG, "文件复制成功Files" + oldFile.getAbsolutePath() + "" + newFile.getAbsolutePath());
return true;
} catch (Exception e) {
LogUtils.e(TAG, "文件复制失败:" + e.getMessage(), e);
return false;
} }
return false;
} }
// ====================================== 图片文件相关 ======================================
/**
* 从文件路径获取BitmapDrawable适用于Android图片显示
* @param path 图片文件绝对路径(非空)
* @return BitmapDrawable 图片对象(文件不存在/读取失败返回null
* @throws IOException 异常文件读取IO错误
*/
public static BitmapDrawable getImageDrawable(String path) throws IOException {
// 1. 校验文件合法性
File file = new File(path);
if (!file.exists() || !file.isFile()) {
LogUtils.e(TAG, "图片文件不存在:" + path);
return null;
}
// 2. 读取文件并转为BitmapDrawable缓冲流读取减少内存占用
try (InputStream is = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[BUFFER_SIZE];
int readLen;
while ((readLen = is.read(buffer)) != -1) {
bos.write(buffer, 0, readLen);
}
// 3. 生成Bitmap并包装为BitmapDrawable
byte[] imageBytes = bos.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
return new BitmapDrawable(bitmap);
}
}
// ====================================== 文件名处理相关 ======================================
/**
* 截取文件后缀名(兼容多 "." 场景,如"image.2025.png" → ".png"
* @param file 目标文件可为null
* @return 文件后缀名:带点(如".jpg"),无后缀/文件无效返回空字符串
*/
public static String getFileSuffixWithMultiDot(File file) {
// 1. 校验文件合法性
if (file == null || !file.isFile()) {
return "";
}
// 2. 提取文件名并查找最后一个 "."
String fileName = file.getName();
int lastDotIndex = fileName.lastIndexOf(".");
// 3. 校验后缀合法性(排除无后缀、以点结尾、后缀过长的异常文件)
if (lastDotIndex == -1 // 无 "."
|| lastDotIndex == fileName.length() - 1 // 以 "." 结尾(如".gitignore"
|| (fileName.length() - lastDotIndex) > 5) { // 后缀长度超过5异常文件名
return "";
}
// 4. 返回小写后缀(统一格式,避免大小写不一致问题)
return fileName.substring(lastDotIndex).toLowerCase();
}
/**
* 生成唯一文件名(优化版:唯一、合法、简洁)
* 生成规则UUID(去掉"-") + "_" + 时间戳 + 原文件后缀
* @param refFile 参考文件用于提取后缀名可为null
* @return 唯一文件名(如"a1b2c3d4e5f6_1730000000000.jpg",无后缀则不带点)
*/
public static String createUniqueFileName(File refFile) {
// 1. 获取参考文件的后缀名自动容错null/无效文件)
String suffix = getFileSuffixWithMultiDot(refFile);
// 2. 生成唯一标识UUID确保全局唯一时间戳进一步降低重复概率
String uniqueId = UUID.randomUUID().toString().replace("-", ""); // 去掉"-"简化文件名
long timeStamp = System.currentTimeMillis();
// 3. 拼接文件名(分场景处理,避免多余点)
if (suffix.isEmpty()) {
// 无后缀唯一ID + 时间戳
return String.format("%s_%d", uniqueId, timeStamp);
} else {
// 有后缀唯一ID + 时间戳 + 后缀(无多余点)
return String.format("%s_%d%s", uniqueId, timeStamp, suffix);
}
}
} }

View File

@@ -0,0 +1,294 @@
package cc.winboll.studio.powerbell.utils;
import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import cc.winboll.studio.libappbase.LogUtils;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/19 20:52
* @Describe 图片下载工具类(单例模式)
* 功能:下载网络图片到缓存目录、清理过期文件、获取最新下载文件
*/
public class ImageDownloader {
public static final String TAG = "ImageDownloader";
// 单例实例
private static ImageDownloader sInstance;
// OkHttp 客户端(全局复用,提升性能)
private OkHttpClient mOkHttpClient;
// 缓存目录:/data/data/应用包名/cache/networkdownload
private File mCacheDir;
// 过期时间7天单位毫秒可按需调整
private static final long EXPIRE_TIME = 7 * 24 * 3600 * 1000;
/**
* 私有构造(单例模式禁止外部实例化)
* @param context 上下文(用于获取缓存目录)
*/
private ImageDownloader(Context context) {
// 初始化 OkHttp 客户端(设置超时时间)
mOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.build();
// 初始化缓存目录networkdownload
initCacheDir(context);
// 初始化时清理过期文件
clearExpiredFiles();
}
/**
* 单例获取方法(线程安全)
* @param context 上下文(建议使用 Application 上下文避免内存泄漏)
* @return 单例实例
*/
public static synchronized ImageDownloader getInstance(Context context) {
if (sInstance == null) {
// 使用 Application 上下文,防止 Activity 销毁导致的内存泄漏
sInstance = new ImageDownloader(context.getApplicationContext());
}
return sInstance;
}
/**
* 初始化缓存目录:若不存在则创建
*/
private void initCacheDir(Context context) {
// 获取应用内置缓存目录(无需权限)
File cacheRoot = context.getCacheDir();
mCacheDir = new File(cacheRoot, "networkdownload");
// 若目录不存在则创建(包括父目录)
if (!mCacheDir.exists()) {
boolean isCreated = mCacheDir.mkdirs();
if (isCreated) {
LogUtils.d("ImageDownloader", "networkdownload 缓存目录创建成功:" + mCacheDir.getAbsolutePath());
} else {
LogUtils.e("ImageDownloader", "networkdownload 缓存目录创建失败");
}
} else {
LogUtils.d("ImageDownloader", "networkdownload 缓存目录已存在:" + mCacheDir.getAbsolutePath());
}
}
/**
* 清理过期文件(最后修改时间超过 EXPIRE_TIME 的文件)
*/
private void clearExpiredFiles() {
if (mCacheDir == null || !mCacheDir.exists()) {
return;
}
File[] files = mCacheDir.listFiles();
if (files == null || files.length == 0) {
LogUtils.d("ImageDownloader", "缓存目录无文件,无需清理");
return;
}
long currentTime = System.currentTimeMillis();
int deleteCount = 0;
// 遍历所有文件,删除过期文件
for (File file : files) {
long lastModifyTime = file.lastModified();
if (currentTime - lastModifyTime > EXPIRE_TIME) {
if (file.delete()) {
deleteCount++;
LogUtils.d("ImageDownloader", "删除过期文件:" + file.getName());
} else {
LogUtils.e("ImageDownloader", "删除过期文件失败:" + file.getName());
}
}
}
LogUtils.d("ImageDownloader", "过期文件清理完成,共删除 " + deleteCount + " 个文件");
}
/**
* 下载网络图片到缓存目录
* @param imageUrl 图片网络链接
* @param callback 下载结果回调(成功/失败)
*/
public void downloadImage(final String imageUrl, final DownloadCallback callback) {
// 校验参数
if (TextUtils.isEmpty(imageUrl)) {
if (callback != null) {
callback.onFailure("图片链接为空");
}
return;
}
if (mCacheDir == null || !mCacheDir.exists()) {
if (callback != null) {
callback.onFailure("缓存目录不存在");
}
return;
}
// 构建 OkHttp 请求
Request request = new Request.Builder()
.url(imageUrl)
.build();
// 异步下载(避免阻塞主线程)
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 下载失败,回调主线程
if (callback != null) {
callback.onFailure("下载失败:" + e.getMessage());
}
LogUtils.e("ImageDownloader", "图片下载失败:" + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
// 响应失败(如 404、500
if (callback != null) {
callback.onFailure("响应失败:" + response.code());
}
LogUtils.e("ImageDownloader", "图片响应失败,状态码:" + response.code());
return;
}
// 响应成功,写入文件
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = response.body().byteStream();
// 生成 UUID 唯一文件名(保留原文件后缀)
String fileExtension = getFileExtension(imageUrl);
String fileName = UUID.randomUUID().toString() + fileExtension;
File imageFile = new File(mCacheDir, fileName);
// 写入文件
outputStream = new FileOutputStream(imageFile);
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
outputStream.flush();
// 下载成功,回调主线程并返回文件路径
if (callback != null) {
callback.onSuccess(imageFile.getAbsolutePath());
}
LogUtils.d("ImageDownloader", "图片下载成功:" + imageFile.getAbsolutePath());
} catch (IOException e) {
if (callback != null) {
callback.onFailure("文件写入失败:" + e.getMessage());
}
LogUtils.e("ImageDownloader", "图片写入失败:" + e.getMessage());
} finally {
// 关闭流Java7 手动关闭,避免资源泄漏)
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 关闭响应体
if (response.body() != null) {
response.body().close();
}
}
}
});
}
/**
* 获取 networkdownload 目录中最后下载的文件(按修改时间排序)
* @return 最后下载的文件路径null 表示无文件)
*/
public String getLastDownloadedFile() {
if (mCacheDir == null || !mCacheDir.exists()) {
LogUtils.e("ImageDownloader", "缓存目录不存在");
return null;
}
File[] files = mCacheDir.listFiles();
if (files == null || files.length == 0) {
LogUtils.d("ImageDownloader", "缓存目录无文件");
return null;
}
// 按最后修改时间降序排序,取第一个即为最新文件
File lastFile = files[0];
for (File file : files) {
if (file.lastModified() > lastFile.lastModified()) {
lastFile = file;
}
}
LogUtils.d("ImageDownloader", "最后下载的文件:" + lastFile.getAbsolutePath());
return lastFile.getAbsolutePath();
}
/**
* 工具方法:从图片链接中提取文件后缀(如 .png、.jpg
* @param imageUrl 图片链接
* @return 文件后缀(含点号,若无法提取则返回 .jpg
*/
private String getFileExtension(String imageUrl) {
if (TextUtils.isEmpty(imageUrl)) {
return ".jpg";
}
int lastDotIndex = imageUrl.lastIndexOf(".");
int lastSlashIndex = imageUrl.lastIndexOf("/");
// 确保后缀在最后一个斜杠之后且长度合理1-5 个字符)
if (lastDotIndex > lastSlashIndex && lastDotIndex < imageUrl.length() - 1) {
String extension = imageUrl.substring(lastDotIndex);
if (extension.length() <= 5) {
return extension.toLowerCase(); // 统一转为小写
}
}
// 无法提取后缀时,默认使用 .jpg
return ".jpg";
}
/**
* 下载结果回调接口Java7 接口实现)
*/
public interface DownloadCallback {
/**
* 下载成功
* @param filePath 图片保存路径
*/
void onSuccess(String filePath);
/**
* 下载失败
* @param errorMsg 失败原因
*/
void onFailure(String errorMsg);
}
}

View File

@@ -21,7 +21,7 @@ import android.widget.RemoteViews;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import cc.winboll.studio.powerbell.MainActivity; import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.beans.NotificationMessage; import cc.winboll.studio.powerbell.model.NotificationMessage;
import cc.winboll.studio.powerbell.services.ControlCenterService; import cc.winboll.studio.powerbell.services.ControlCenterService;
public class NotificationUtils2 { public class NotificationUtils2 {

View File

@@ -1,6 +1,6 @@
package cc.winboll.studio.powerbell.utils; package cc.winboll.studio.powerbell.utils;
import cc.winboll.studio.powerbell.beans.BatteryInfoBean; import cc.winboll.studio.powerbell.model.BatteryInfoBean;
import java.util.ArrayList; import java.util.ArrayList;
public class StringUtils { public class StringUtils {

View File

@@ -0,0 +1,403 @@
package cc.winboll.studio.powerbell.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.R;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
import cc.winboll.studio.libappbase.ToastUtils;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/19 18:01
* @Describe 背景图片视图控件(全透明背景 + 不拉伸居中平铺 + 完全填充父视图)
*/
public class BackgroundView extends RelativeLayout {
public static final String TAG = "BackgroundView";
Context mContext;
private ImageView ivBackground;
private static String BACKGROUND_IMAGE_FOLDER = "Background";
private static String BACKGROUND_IMAGE_FILENAME = "current.data";
private static String BACKGROUND_IMAGE_PREVIEW_FILENAME = "current_preview.data";
private static String backgroundSourceFilePath;
private float imageAspectRatio = 1.0f; // 图片原始宽高比(控制不拉伸)
// 标记当前是否处于预览模式
private boolean isPreviewMode = false;
public BackgroundView(Context context) {
super(context);
this.mContext = context;
initView();
}
public BackgroundView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
initView();
}
public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
initView();
}
public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.mContext = context;
initView();
}
void initView() {
// 1. 控件本身:完全填充父视图 + 全透明背景 + 无内边距
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
setPadding(0, 0, 0, 0); // 取消自身内边距
setBackgroundColor(0x00000000); // 全透明背景(#00000000
setBackground(new ColorDrawable(0x00000000)); // 双重保障同时设置Background为透明兼容低版本
initBackgroundImageView(); // 初始化内部ImageView全透明 + 不拉伸居中平铺)
backgroundSourceFilePath = BackgroundSourceUtils.getInstance(this.mContext).getCurrentBackgroundFilePath();
loadAndSetImageViewBackground();
}
private void initBackgroundImageView() {
ivBackground = new ImageView(mContext);
// 2. ImageView初始宽高WRAP_CONTENT + 居中 + 无内边距 + 全透明背景
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); // 居中显示
layoutParams.setMargins(0, 0, 0, 0); // 取消边距
ivBackground.setLayoutParams(layoutParams);
// 3. 缩放模式FIT_CENTER不拉伸按比例显示
ivBackground.setScaleType(ImageView.ScaleType.FIT_CENTER);
ivBackground.setPadding(0, 0, 0, 0); // 取消内部padding
ivBackground.setBackgroundColor(0x00000000); // ImageView背景全透明
ivBackground.setBackground(new ColorDrawable(0x00000000)); // 双重保障(兼容低版本)
this.addView(ivBackground);
}
// private void initBackgroundImagePath() {
// if (isPreviewMode) {
// backgroundSourceFilePath = BackgroundSourceUtils.getInstance(this.mContext).getPreviewBackgroundFilePath();
// } else {
// backgroundSourceFilePath = BackgroundSourceUtils.getInstance(this.mContext).getCurrentBackgroundFilePath();
// }
// }
/**
* 拷贝图片文件到背景资源目录(正式背景)
*/
// public void setImageViewSource(String srcBackgroundPath) {
// initBackgroundImagePath();
// if (backgroundSourceFilePath == null) {
// LogUtils.e(TAG, "目标路径初始化失败,无法保存背景图片");
// return;
// }
//
// File srcFile = new File(srcBackgroundPath);
// if (!srcFile.exists() || !srcFile.isFile()) {
// LogUtils.e(TAG, String.format("源文件不存在或不是文件:%s", srcBackgroundPath));
// return;
// }
//
// File destFile = new File(backgroundSourceFilePath);
// File destDir = destFile.getParentFile();
// if (destDir != null && !destDir.exists()) {
// boolean isDirCreated = destDir.mkdirs();
// if (!isDirCreated) {
// LogUtils.e(TAG, "目标目录创建失败:" + destDir.getAbsolutePath());
// return;
// }
// }
//
// FileInputStream fis = null;
// FileOutputStream fos = null;
// try {
// fis = new FileInputStream(srcFile);
// fos = new FileOutputStream(destFile);
//
// byte[] buffer = new byte[4096];
// int len;
// while ((len = fis.read(buffer)) != -1) {
// fos.write(buffer, 0, len);
// }
// fos.flush();
//
// LogUtils.d(TAG, String.format("文件拷贝成功:%s -> %s", srcBackgroundPath, backgroundSourceFilePath));
// // 拷贝成功后,若处于预览模式则退出预览,加载正式背景
// if (isPreviewMode) {
// exitPreviewMode();
// } else {
// loadAndSetImageViewBackground();
// }
//
// } catch (Exception e) {
// LogUtils.e(TAG, String.format("文件拷贝失败:%s", e.getMessage()), e);
// if (destFile.exists()) {
// destFile.delete();
// LogUtils.d(TAG, "已删除损坏的目标文件");
// }
// } finally {
// if (fis != null) {
// try {
// fis.close();
// } catch (Exception e) {
// LogUtils.e(TAG, "输入流关闭失败:" + e.getMessage());
// }
// }
// if (fos != null) {
// try {
// fos.close();
// } catch (Exception e) {
// LogUtils.e(TAG, "输出流关闭失败:" + e.getMessage());
// }
// }
// }
// }
/**
* 预览临时图片(全透明背景 + 不拉伸居中平铺)
* @param previewImagePath 临时预览图片的路径
*/
/*public void previewBackgroundImage(String previewImagePath) {
if (previewImagePath == null || previewImagePath.isEmpty()) {
LogUtils.e(TAG, "预览图片路径为空");
setDefaultImageViewBackground();
return;
}
File previewFile = new File(previewImagePath);
if (!previewFile.exists() || !previewFile.isFile()) {
LogUtils.e(TAG, "预览图片不存在或不是文件:" + previewImagePath);
setDefaultImageViewBackground();
return;
}
// 计算图片原始宽高比
if (!calculateImageAspectRatio(previewFile)) {
LogUtils.e(TAG, "预览图片尺寸无效,无法预览");
setDefaultImageViewBackground();
return;
}
// 压缩加载预览图片(保持比例)
Bitmap previewBitmap = decodeBitmapWithCompress(previewFile, 1080, 1920);
if (previewBitmap == null) {
LogUtils.e(TAG, "预览图片加载失败");
setDefaultImageViewBackground();
return;
}
// 设置预览图片保持ImageView透明背景
Drawable previewDrawable = new BitmapDrawable(mContext.getResources(), previewBitmap);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
ivBackground.setBackground(previewDrawable);
} else {
ivBackground.setBackgroundDrawable(previewDrawable);
}
// 调整ImageView尺寸居中平铺不拉伸
adjustImageViewSize();
isPreviewMode = true;
LogUtils.d(TAG, "进入预览模式,预览图片路径:" + previewImagePath + ",宽高比:" + imageAspectRatio);
}
*/
/**
* 退出预览模式,恢复显示正式背景图片
*/
/*public void exitPreviewMode() {
if (isPreviewMode) {
loadAndSetImageViewBackground();
isPreviewMode = false;
LogUtils.d(TAG, "退出预览模式,恢复正式背景");
}
}*/
/**
* 公共函数:供外部类调用,重新加载正式背景图片(刷新显示)
*/
public void reloadCurrentBackground() {
LogUtils.d(TAG, "外部调用重新加载背景图片");
backgroundSourceFilePath = BackgroundSourceUtils.getInstance(this.mContext).getCurrentBackgroundFilePath();
loadAndSetImageViewBackground();
}
public void reloadPreviewBackground() {
LogUtils.d(TAG, "外部调用重新加载背景图片");
backgroundSourceFilePath = BackgroundSourceUtils.getInstance(this.mContext).getPreviewBackgroundFilePath();
loadAndSetImageViewBackground();
}
/**
* 加载正式背景图片并设置到 ImageView全透明背景 + 不拉伸居中平铺)
*/
private void loadAndSetImageViewBackground() {
if (backgroundSourceFilePath == null) {
LogUtils.d(TAG, "backgroundSourceFilePath == null");
setDefaultImageViewBackground();
return;
}
//ToastUtils.show(String.format("backgroundSourceFilePath : %s", backgroundSourceFilePath));
File backgroundFile = new File(backgroundSourceFilePath);
if (!backgroundFile.exists() || !backgroundFile.isFile()) {
LogUtils.e(TAG, "背景图片不存在:" + backgroundSourceFilePath);
setDefaultImageViewBackground();
return;
}
// 计算图片原始宽高比
if (!calculateImageAspectRatio(backgroundFile)) {
setDefaultImageViewBackground();
return;
}
// 压缩加载 Bitmap保持比例
Bitmap bitmap = decodeBitmapWithCompress(backgroundFile, 1080, 1920);
if (bitmap == null) {
LogUtils.e(TAG, "图片加载失败,无法解析为 Bitmap");
setDefaultImageViewBackground();
return;
}
// 设置图片保持ImageView透明背景
Drawable backgroundDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
ivBackground.setBackground(backgroundDrawable);
} else {
ivBackground.setBackgroundDrawable(backgroundDrawable);
}
// 调整ImageView尺寸居中平铺不拉伸
adjustImageViewSize();
LogUtils.d(TAG, "ImageView 背景加载成功(全透明+不拉伸),宽高比:" + imageAspectRatio);
}
/**
* 计算图片原始宽高比(宽/高)
*/
private boolean calculateImageAspectRatio(File file) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int imageWidth = options.outWidth;
int imageHeight = options.outHeight;
if (imageWidth <= 0 || imageHeight <= 0) {
LogUtils.e(TAG, "图片尺寸无效:宽=" + imageWidth + ", 高=" + imageHeight);
return false;
}
imageAspectRatio = (float) imageWidth / imageHeight;
return true;
} catch (Exception e) {
LogUtils.e(TAG, "计算图片宽高比失败:" + e.getMessage());
return false;
}
}
/**
* 调整ImageView尺寸不拉伸居中平铺
*/
private void adjustImageViewSize() {
int parentWidth = getWidth(); // 控件宽度(已填充父视图)
int parentHeight = getHeight(); // 控件高度(已填充父视图)
if (parentWidth == 0 || parentHeight == 0) {
post(new Runnable() {
@Override
public void run() {
adjustImageViewSize();
}
});
return;
}
int imageViewWidth, imageViewHeight;
if (imageAspectRatio >= 1.0f) { // 横图
imageViewWidth = Math.min(parentWidth, (int) (parentHeight * imageAspectRatio));
imageViewHeight = (int) (imageViewWidth / imageAspectRatio);
} else { // 竖图
imageViewHeight = Math.min(parentHeight, (int) (parentWidth / imageAspectRatio));
imageViewWidth = (int) (imageViewHeight * imageAspectRatio);
}
// 应用尺寸
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) ivBackground.getLayoutParams();
layoutParams.width = imageViewWidth;
layoutParams.height = imageViewHeight;
ivBackground.setLayoutParams(layoutParams);
LogUtils.d(TAG, "ImageView 尺寸调整完成:宽=" + imageViewWidth + ", 高=" + imageViewHeight);
}
/**
* 带压缩的 Bitmap 解码(保持比例,避免 OOM
*/
private Bitmap decodeBitmapWithCompress(File file, int maxWidth, int maxHeight) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int scaleX = options.outWidth / maxWidth;
int scaleY = options.outHeight / maxHeight;
int inSampleSize = Math.max(scaleX, scaleY);
if (inSampleSize <= 0) {
inSampleSize = 1;
}
options.inJustDecodeBounds = false;
options.inSampleSize = inSampleSize;
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
} catch (Exception e) {
LogUtils.e(TAG, "图片压缩加载失败:" + e.getMessage());
return null;
}
}
/**
* 设置默认背景(全透明兜底,避免空白)
*/
private void setDefaultImageViewBackground() {
// 关键:默认背景设为全透明(而非默认图,避免遮挡下层视图)
ivBackground.setBackground(new ColorDrawable(0x00000000)); // 全透明背景
imageAspectRatio = 1.0f;
adjustImageViewSize();
LogUtils.d(TAG, "已设置默认透明背景");
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
adjustImageViewSize();
}
/**
* 对外提供:判断当前是否处于预览模式
*/
public boolean isPreviewMode() {
return isPreviewMode;
}
}

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#FF009DCB"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -21,14 +21,15 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/activitybackgroundpictureRelativeLayout1"/> android:id="@+id/activitybackgroundpictureRelativeLayout1"/>
<cc.winboll.studio.powerbell.views.BackgroundView
<ImageView xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/activitybackgroundpictureImageView1" android:background="#FF7381FF"
android:layout_below="@id/toolbar"> android:id="@+id/activitybackgroundpictureBackgroundView1">
</ImageView> </cc.winboll.studio.powerbell.views.BackgroundView>
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"
@@ -80,6 +81,23 @@
android:layout_margin="5dp" android:layout_margin="5dp"
android:id="@+id/activitybackgroundpictureAButton2"/> android:id="@+id/activitybackgroundpictureAButton2"/>
<cc.winboll.studio.libaes.views.AButton
android:layout_width="50dp"
android:layout_height="36dp"
android:text="♾"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:id="@+id/activitybackgroundpictureAButton9"
android:onClick="onNetworkBackgroundDialog"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<cc.winboll.studio.libaes.views.AButton <cc.winboll.studio.libaes.views.AButton
android:layout_width="50dp" android:layout_width="50dp"
android:layout_height="36dp" android:layout_height="36dp"
@@ -103,7 +121,7 @@
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/activitybackgroundpictureAButton7"/>
<cc.winboll.studio.libaes.views.AButton <cc.winboll.studio.libaes.views.AButton
android:layout_width="50dp" android:layout_width="50dp"
android:layout_height="36dp" android:layout_height="36dp"
@@ -111,11 +129,12 @@
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/activitybackgroundpictureAButton8"/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>

View File

@@ -21,8 +21,7 @@
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/activitymainRelativeLayout1" android:id="@+id/activitymainRelativeLayout1"/>
android:background="#FFB7B7B7"/>
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/activitymainunittestFrameLayout1"/>
</LinearLayout>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<cc.winboll.studio.libaes.views.AToolbar
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_height"
android:id="@+id/toolbar"
style="@style/DefaultAToolbar"/>
<cc.winboll.studio.libaes.views.ADsControlView
android:id="@+id/ads_control_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:orientation="vertical"
android:background="@android:color/white"
android:padding="20dp"
android:radius="12dp">
<TextView
android:id="@+id/tv_dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="网络后台提示"
android:textSize="18sp"
android:textColor="@android:color/black"
android:textStyle="bold"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp">
<EditText
android:layout_width="0dp"
android:ems="10"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:id="@+id/et_url"
android:singleLine="true"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="↻"
android:id="@+id/btn_preview"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginTop="12dp"
android:layout_gravity="center_vertical">
<cc.winboll.studio.powerbell.views.BackgroundView
android:layout_width="100dp"
android:layout_height="100dp"
android:id="@+id/bv_background_preview"/>
</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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="horizontal"
android:gravity="end">
<Button
android:id="@+id/btn_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="取消"
android:textSize="14sp"
android:background="@android:drawable/btn_default_small"
android:layout_marginRight="8dp"/>
<Button
android:id="@+id/btn_confirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="允许"
android:textSize="14sp"
android:background="@android:drawable/btn_default_small"/>
</LinearLayout>
</LinearLayout>

View File

@@ -5,12 +5,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ImageView <cc.winboll.studio.powerbell.views.BackgroundView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/fragmentmainviewImageView1"> android:background="#FF7381FF"
android:id="@+id/fragmentmainviewBackgroundView1"/>
</ImageView>
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<cc.winboll.studio.powerbell.views.BackgroundView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF7381FF">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Main"
android:id="@+id/btn_main_activity"/>
</HorizontalScrollView>
</cc.winboll.studio.powerbell.views.BackgroundView>

View File

@@ -10,9 +10,9 @@
android:id="@+id/action_changepicture" android:id="@+id/action_changepicture"
android:title="@string/item_changepicture"/> android:title="@string/item_changepicture"/>
<item <item
android:id="@+id/action_log" android:id="@+id/action_settings"
android:title="@string/item_logview"/> android:title="@string/item_settings"/>
<item <item
android:id="@+id/action_about" android:id="@+id/action_about"
android:title="@string/item_aboutview"/> android:title="@string/item_about"/>
</menu> </menu>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_log"
android:title="@string/item_logview"/>
<item
android:id="@+id/action_unittestactivity"
android:title="@string/item_mainunittestactivity"/>
</menu>

View File

@@ -1,15 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">PowerBell</string> <string name="app_name_cn1">能源钟</string>
<string name="app_name_cn2">泡额呗额</string>
<string name="app_description">一个接收手机电量信息的应用,当电量值达到设定范围时会提醒用户。</string> <string name="app_description">一个接收手机电量信息的应用,当电量值达到设定范围时会提醒用户。</string>
<string name="about_crashed">本应用崩溃了,作者水平有限,敬请谅解!</string> <string name="about_crashed">本应用崩溃了,作者水平有限,敬请谅解!</string>
<string name="item_mainview">Main View</string> <string name="item_main">主窗口</string>
<string name="item_aboutview">About</string> <string name="item_about">关于</string>
<string name="item_clearrecord">Clear Record</string> <string name="item_settings">应用设置</string>
<string name="item_changepicture">Change Picture</string> <string name="item_battery_report">应用耗电记录</string>
<string name="item_devoloperoptionsview">Developer View</string> <string name="item_mainunittestactivity">开发调试窗口</string>
<string name="item_logview">Log View</string> <string name="item_clearrecord">清理电量记录</string>
<string name="item_sourceview">Source View</string> <string name="item_changepicture">更换背景图片</string>
<string name="item_devoloperoptionsview">开发调试窗口</string>
<string name="item_logview">日志窗口</string>
<string name="item_sourceview">源码窗口</string>
<string name="txt_aboveswitch">消息总开关</string> <string name="txt_aboveswitch">消息总开关</string>
<string name="txt_aboveswitchtips">当电量低于左边(放电状态)或高于右边(充电状态),就会发送一个提醒铃声。</string> <string name="txt_aboveswitchtips">当电量低于左边(放电状态)或高于右边(充电状态),就会发送一个提醒铃声。</string>
<string name="texthint_CustomSlideToCleanRecord">Slide Right To Clean Up APP Record.</string> <string name="texthint_CustomSlideToCleanRecord">Slide Right To Clean Up APP Record.</string>

View File

@@ -1,16 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">PowerBell</string> <string name="app_name">PowerBell</string>
<string name="app_name_cn1">能源钟</string>
<string name="app_name_cn2">泡额呗额</string>
<string name="app_projectname">PowerBell</string> <string name="app_projectname">PowerBell</string>
<string name="app_description">A mobile app that receives battery level information from a phone and alerts the user when the battery level reaches a predefined range.</string> <string name="app_description">A mobile app that receives battery level information from a phone and alerts the user when the battery level reaches a predefined range.</string>
<string name="about_crashed">This application has crashed, the author level is limited, please understand!</string> <string name="about_crashed">This application has crashed, the author level is limited, please understand!</string>
<string name="switchto_en1">PowerBell</string>
<string name="switchto_cn1">能源钟</string>
<string name="switchto_cn2">泡额呗额</string>
<string name="en1_switch_disabled">PowerBell X</string>
<string name="cn1_switch_disabled">能源钟 X</string>
<string name="cn2_switch_disabled">泡额呗额 X</string>
<string name="item_mainview">Main View</string> <string name="item_mainview">Main View</string>
<string name="item_aboutview">About</string> <string name="item_about">About</string>
<string name="item_settings">Settings</string>
<string name="item_battery_report">Battery Report</string> <string name="item_battery_report">Battery Report</string>
<string name="item_clearrecord">Clear Record</string> <string name="item_clearrecord">Clear Record</string>
<string name="item_changepicture">Change Picture</string> <string name="item_changepicture">Change Picture</string>
<string name="item_devoloperoptionsview">Developer View</string> <string name="item_devoloperoptionsview">Developer View</string>
<string name="item_logview">Log View</string> <string name="item_logview">Log View</string>
<string name="item_mainunittestactivity">Debug Activity</string>
<string name="item_cleanlog">Clean Log</string> <string name="item_cleanlog">Clean Log</string>
<string name="item_sourceview">Source View</string> <string name="item_sourceview">Source View</string>
<string name="txt_aboveswitch">Message master switch</string> <string name="txt_aboveswitch">Message master switch</string>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<!--<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<!--<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
</shortcuts>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<!--<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>

View File

@@ -4,18 +4,6 @@
<application> <application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="cc.winboll.studio.powerbell.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/>
</provider>
</application> </application>
</manifest> </manifest>