clone https://gitea.winboll.cc/Studio/WinBoLL_Bck20251205_175044_440.git and fix build.gradle file maven key settings.
This commit is contained in:
1
powerbell/.gitignore
vendored
Normal file
1
powerbell/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
112
powerbell/README.md
Normal file
112
powerbell/README.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# PowerBell
|
||||
|
||||
#### 介绍
|
||||
一个接收手机电量信息的应用,当电量值达到设定范围时会提醒用户。
|
||||
|
||||
#### 软件架构
|
||||
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
|
||||
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
|
||||
|
||||
|
||||
#### Gradle 编译说明
|
||||
调试版编译命令 :gradle assembleBetaDebug
|
||||
阶段版编译命令 :gradle assembleStageRelease
|
||||
|
||||
#### 使用说明
|
||||
|
||||
在安卓系统中需要设置两个权限允许。
|
||||
1.自启动权限允许。
|
||||
2.省电策略-无限制权限允许。
|
||||
3.设置背景图片需要读写手机存储权限。
|
||||
4.要在锁屏充电的时候提醒,还需要设置允许锁屏通知权限。
|
||||
|
||||
#### 参与贡献
|
||||
|
||||
1. Fork 本仓库
|
||||
2. 新建 Feat_xxx 分支
|
||||
3. 提交代码 : ZhanGSKen(ZhanGSKen<zhangsken@qq.com>)
|
||||
4. 新建 Pull Request
|
||||
|
||||
|
||||
#### 特技
|
||||
|
||||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
|
||||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
|
||||
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
|
||||
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
|
||||
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
|
||||
#### 参考文档
|
||||
|
||||
AndroidManifest.xml详解
|
||||
https://www.jianshu.com/p/3b5b89d4e154
|
||||
|
||||
CrashHandler自定义异常处理
|
||||
https://www.jianshu.com/p/9a3d800a429a
|
||||
|
||||
Android用Intent启动Activity的方法
|
||||
https://blog.csdn.net/huangxiaohu_coder/article/details/7105457
|
||||
|
||||
Android开发:最详细的 Toolbar 开发实践总结
|
||||
https://www.jianshu.com/p/79604c3ddcae
|
||||
|
||||
Using the App Toolbar
|
||||
https://guides.codepath.com/android/using-the-app-toolbar
|
||||
|
||||
Android的onCreateOptionsMenu()创建菜单Menu详解
|
||||
https://www.cnblogs.com/spring87/p/4312538.html
|
||||
|
||||
Android通知栏-Notification(通知消息)
|
||||
https://blog.csdn.net/qq_35507234/article/details/90676587
|
||||
|
||||
android之PendingIntent的使用
|
||||
https://blog.csdn.net/qq_16628781/article/details/51548324
|
||||
|
||||
change seekbar color android” Code Answer
|
||||
https://www.codegrepper.com/code-examples/whatever/change+seekbar+color+android
|
||||
|
||||
如何选择开源项目许可证
|
||||
https://www.zhihu.com/question/28292322
|
||||
|
||||
Android最简单的自定义布局Notification
|
||||
https://blog.csdn.net/acesheep_911/article/details/81458784?utm_medium=distribute.wap_relevant.none-task-blog-2~default~baidujs_title~default-0.wap_blog_relevant_default&spm=1001.2101.3001.4242.1&utm_relevant_index=3
|
||||
|
||||
Android中通知栏Notification详解以及自定义Notification
|
||||
https://blog.csdn.net/daitu_liang/article/details/50246803
|
||||
|
||||
Android 图像系列: 将本地图片加载到Drawable
|
||||
https://blog.csdn.net/qzone123222/article/details/7930035
|
||||
|
||||
android 从相册选择,Android开发从相册中选取照片
|
||||
https://blog.csdn.net/weixin_42146086/article/details/117570917
|
||||
|
||||
Android 任务栈简介
|
||||
https://blog.csdn.net/qq_34368586/article/details/107653912
|
||||
|
||||
Android用Intent启动Activity的方法
|
||||
https://blog.csdn.net/huangxiaohu_coder/article/details/7105457
|
||||
|
||||
Android中使用dimen定义尺寸
|
||||
https://blog.csdn.net/yuzhiboyi/article/details/7696174
|
||||
|
||||
declare-styleable:自定义控件的属性
|
||||
https://blog.csdn.net/congqingbin/article/details/7869730
|
||||
|
||||
安卓自定义滑动解锁控件
|
||||
https://blog.csdn.net/lp506954774/article/details/72677018
|
||||
|
||||
Android 添加菜单和返回按钮
|
||||
https://blog.csdn.net/my_tiantian/article/details/77822173
|
||||
|
||||
Android Button的基本使用
|
||||
https://www.cnblogs.com/yishaochu/p/5783605.html
|
||||
|
||||
Android应用中实现系统“分享”接口
|
||||
https://blog.csdn.net/lowprofile_coding/article/details/37656255
|
||||
|
||||
Android 关于mimeType的使用
|
||||
https://blog.csdn.net/dorytmx/article/details/80972248
|
||||
|
||||
使用GitHub Actions实现Android自动打包apk
|
||||
https://blog.csdn.net/ZZL23333/article/details/115798615?app_version=6.0.0&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22115798615%22%2C%22source%22%3A%22weixin_38986226%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app
|
||||
1
powerbell/app_update_description.txt
Normal file
1
powerbell/app_update_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
85
powerbell/build.gradle
Normal file
85
powerbell/build.gradle
Normal file
@@ -0,0 +1,85 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply from: '../.winboll/winboll_app_build.gradle'
|
||||
apply from: '../.winboll/winboll_lint_build.gradle'
|
||||
|
||||
def genVersionName(def versionName){
|
||||
// 检查编译标志位配置
|
||||
assert (winbollBuildProps['stageCount'] != null)
|
||||
assert (winbollBuildProps['baseVersion'] != null)
|
||||
// 保存基础版本号
|
||||
winbollBuildProps.setProperty("baseVersion", "${versionName}");
|
||||
//保存编译标志配置
|
||||
FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
|
||||
winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
|
||||
fos.close();
|
||||
|
||||
// 返回编译版本号
|
||||
return "${versionName}." + winbollBuildProps['stageCount']
|
||||
}
|
||||
|
||||
android {
|
||||
|
||||
// 1. compileSdkVersion:必须 ≥ targetSdkVersion,建议直接等于 targetSdkVersion(30)
|
||||
compileSdkVersion 30
|
||||
|
||||
// 2. buildToolsVersion:需匹配 compileSdkVersion,建议使用 30.x.x 最新稳定版(无需高于 compileSdkVersion)
|
||||
buildToolsVersion "30.0.3" // 这是 30 对应的最新稳定版,避免使用 beta 版
|
||||
|
||||
defaultConfig {
|
||||
applicationId "cc.winboll.studio.powerbell"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 30
|
||||
versionCode 6
|
||||
// versionName 更新后需要手动设置
|
||||
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
||||
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||
versionName "15.11"
|
||||
if(true) {
|
||||
versionName = genVersionName("${versionName}")
|
||||
}
|
||||
}
|
||||
|
||||
// 米盟 SDK
|
||||
packagingOptions {
|
||||
doNotStrip "*/*/libmimo_1011.so"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
// 米盟
|
||||
api 'com.miui.zeus:mimo-ad-sdk:5.3.+'//请使用最新版sdk
|
||||
//注意:以下5个库必须要引入
|
||||
//api 'androidx.appcompat:appcompat:1.4.1'
|
||||
api 'androidx.recyclerview:recyclerview:1.0.0'
|
||||
api 'com.google.code.gson:gson:2.8.5'
|
||||
api 'com.github.bumptech.glide:glide:4.9.0'
|
||||
//annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
|
||||
|
||||
|
||||
// 应用介绍页类库
|
||||
api 'io.github.medyo:android-about-page:2.0.0'
|
||||
// SSH
|
||||
api 'com.jcraft:jsch:0.1.55'
|
||||
// Html 解析
|
||||
api 'org.jsoup:jsoup:1.13.1'
|
||||
// 二维码类库
|
||||
api 'com.google.zxing:core:3.4.1'
|
||||
api 'com.journeyapps:zxing-android-embedded:3.6.0'
|
||||
// 网络连接类库
|
||||
api 'com.squareup.okhttp3:okhttp:4.4.1'
|
||||
|
||||
// AndroidX 类库
|
||||
api 'androidx.appcompat:appcompat:1.1.0'
|
||||
api 'com.google.android.material:material:1.4.0'
|
||||
//api 'androidx.viewpager:viewpager:1.0.0'
|
||||
//api 'androidx.vectordrawable:vectordrawable:1.1.0'
|
||||
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
|
||||
//api 'androidx.fragment:fragment:1.1.0'
|
||||
|
||||
implementation 'cc.winboll.studio:libaes:15.11.6'
|
||||
implementation 'cc.winboll.studio:libappbase:15.11.0'
|
||||
|
||||
//api fileTree(dir: 'libs', include: ['*.aar'])
|
||||
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||
}
|
||||
8
powerbell/build.properties
Normal file
8
powerbell/build.properties
Normal file
@@ -0,0 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Wed Nov 26 16:27:33 HKT 2025
|
||||
stageCount=9
|
||||
libraryProject=
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.8
|
||||
buildCount=0
|
||||
baseBetaVersion=15.11.9
|
||||
143
powerbell/proguard-rules.pro
vendored
Normal file
143
powerbell/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# ============================== 基础通用规则 ==============================
|
||||
# 保留系统组件
|
||||
-keep public class * extends android.app.Activity
|
||||
-keep public class * extends android.app.Service
|
||||
-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 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
|
||||
|
||||
12
powerbell/src/beta/AndroidManifest.xml
Normal file
12
powerbell/src/beta/AndroidManifest.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application
|
||||
tools:replace="android:icon"
|
||||
android:icon="@drawable/ic_launcher_beta">
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
7
powerbell/src/beta/res/values-zh/string.xml
Normal file
7
powerbell/src/beta/res/values-zh/string.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name_cn1">能源钟★</string>
|
||||
<string name="app_name_cn2">泡额呗额☆</string>
|
||||
|
||||
</resources>
|
||||
6
powerbell/src/beta/res/values/strings.xml
Normal file
6
powerbell/src/beta/res/values/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">PowerBell+</string>
|
||||
|
||||
</resources>
|
||||
46
powerbell/src/beta/res/xml/shortcutsmaincn1.xml
Normal file
46
powerbell/src/beta/res/xml/shortcutsmaincn1.xml
Normal 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>
|
||||
46
powerbell/src/beta/res/xml/shortcutsmaincn2.xml
Normal file
46
powerbell/src/beta/res/xml/shortcutsmaincn2.xml
Normal 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>
|
||||
46
powerbell/src/beta/res/xml/shortcutsmainen1.xml
Normal file
46
powerbell/src/beta/res/xml/shortcutsmainen1.xml
Normal 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>
|
||||
235
powerbell/src/main/AndroidManifest.xml
Normal file
235
powerbell/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,235 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="cc.winboll.studio.powerbell">
|
||||
|
||||
<!-- 只能在前台获取精确的位置信息 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
|
||||
<!-- 只有在前台运行时才能获取大致位置信息 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
|
||||
<!-- 拍摄照片和视频 -->
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
|
||||
<!-- 运行前台服务 -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
|
||||
<!-- 读取您共享存储空间中的内容 -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
|
||||
<!-- 修改或删除您共享存储空间中的内容 -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<!-- 开机启动 -->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
|
||||
<!-- MANAGE_EXTERNAL_STORAGE -->
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<!-- 显示通知 -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
|
||||
<!-- PACKAGE_USAGE_STATS -->
|
||||
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
|
||||
|
||||
<!-- BATTERY_STATS -->
|
||||
<uses-permission android:name="android.permission.BATTERY_STATS"/>
|
||||
|
||||
<!-- 计算应用存储空间 -->
|
||||
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
|
||||
|
||||
<uses-feature android:name="android.hardware.camera"/>
|
||||
|
||||
<uses-feature android:name="android.hardware.camera.autofocus"/>
|
||||
|
||||
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission"/>
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.ACCESS_PACKAGE_USAGE_STATS"
|
||||
tools:ignore="ProtectedPermissions"/>
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme_Default"
|
||||
android:persistent="true"
|
||||
android:resizeableActivity="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
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>
|
||||
|
||||
<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/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
|
||||
android:name="cc.winboll.studio.powerbell.activities.ClearRecordActivity"
|
||||
android:parentActivityName="cc.winboll.studio.powerbell.MainActivity"
|
||||
android:launchMode="singleTask">
|
||||
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="cc.winboll.studio.powerbell.activities.BackgroundPictureActivity"
|
||||
android:parentActivityName="cc.winboll.studio.powerbell.MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask">
|
||||
|
||||
<intent-filter>
|
||||
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
|
||||
<data android:mimeType="image/jpeg"/>
|
||||
|
||||
<data android:mimeType="image/jpg"/>
|
||||
|
||||
<data android:mimeType="image/png"/>
|
||||
|
||||
<data android:mimeType="image/webp"/>
|
||||
|
||||
<data android:mimeType="image/*"/>
|
||||
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.MainReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:directBootAware="true">
|
||||
|
||||
<intent-filter>
|
||||
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
|
||||
</intent-filter>
|
||||
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name="cc.winboll.studio.powerbell.services.ControlCenterService"
|
||||
android:priority="1000"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:process=".controlcenterservice"/>
|
||||
|
||||
<service
|
||||
android:name="cc.winboll.studio.powerbell.services.AssistantService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:process=".assistantservice"/>
|
||||
|
||||
<meta-data
|
||||
android:name="android.max_aspect"
|
||||
android:value="4.0"/>
|
||||
|
||||
<activity android:name="cc.winboll.studio.powerbell.activities.BatteryReporterActivity"/>
|
||||
|
||||
<activity android:name="cc.winboll.studio.powerbell.activities.AboutActivity"/>
|
||||
|
||||
<activity android:name="cc.winboll.studio.powerbell.activities.PixelPickerActivity"/>
|
||||
|
||||
<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"/>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
97
powerbell/src/main/java/cc/winboll/studio/powerbell/App.java
Normal file
97
powerbell/src/main/java/cc/winboll/studio/powerbell/App.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package cc.winboll.studio.powerbell;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.view.Gravity;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.receivers.GlobalApplicationReceiver;
|
||||
import cc.winboll.studio.powerbell.utils.AppCacheUtils;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
import java.io.File;
|
||||
|
||||
public class App extends GlobalApplication {
|
||||
|
||||
public static final String TAG = "GlobalApplication";
|
||||
|
||||
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 AppCacheUtils _mAppCacheUtils;
|
||||
GlobalApplicationReceiver mReceiver;
|
||||
static String szTempDir = "";
|
||||
|
||||
public static String getTempDirPath() {
|
||||
return szTempDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
setIsDebugging(BuildConfig.DEBUG);
|
||||
|
||||
// 临时文件夹方案1
|
||||
// 获取Pictures文件夹路径(Android 10及以上推荐使用MediaStore,此处为传统方式)
|
||||
File picturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||
// 定义目标文件路径(在Pictures目录下创建"PowerBell"子文件夹及文件)
|
||||
File powerBellDir = new File(picturesDir, "PowerBell");
|
||||
|
||||
// 临时文件夹方案2 <图片保存失败>
|
||||
// 获取Pictures文件夹路径(Android 10及以上推荐使用MediaStore,此处为传统方式)
|
||||
//File powerBellDir = getExternalFilesDir("TempDir");
|
||||
|
||||
// 先创建文件夹(如果不存在)
|
||||
if (!powerBellDir.exists()) {
|
||||
powerBellDir.mkdirs();
|
||||
}
|
||||
szTempDir = powerBellDir.getAbsolutePath();
|
||||
|
||||
|
||||
// 初始化 Toast 框架
|
||||
ToastUtils.init(this);
|
||||
// 设置 Toast 布局样式
|
||||
//ToastUtils.setView(R.layout.toast_custom_view);
|
||||
//ToastUtils.setStyle(new WhiteToastStyle());
|
||||
//ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
|
||||
|
||||
// 设置数据配置存储工具
|
||||
_mAppConfigUtils = getAppConfigUtils(this);
|
||||
_mAppCacheUtils = getAppCacheUtils(this);
|
||||
|
||||
mReceiver = new GlobalApplicationReceiver(this);
|
||||
mReceiver.registerAction();
|
||||
}
|
||||
|
||||
public static AppConfigUtils getAppConfigUtils(Context context) {
|
||||
if (_mAppConfigUtils == null) {
|
||||
_mAppConfigUtils = AppConfigUtils.getInstance(context);
|
||||
}
|
||||
return _mAppConfigUtils;
|
||||
}
|
||||
|
||||
public static AppCacheUtils getAppCacheUtils(Context context) {
|
||||
if (_mAppCacheUtils == null) {
|
||||
_mAppCacheUtils = AppCacheUtils.getInstance(context);
|
||||
}
|
||||
return _mAppCacheUtils;
|
||||
}
|
||||
|
||||
public void clearBatteryHistory() {
|
||||
_mAppCacheUtils.clearBatteryHistory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
super.onTerminate();
|
||||
ToastUtils.release();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,621 @@
|
||||
package cc.winboll.studio.powerbell;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.RelativeLayout;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import cc.winboll.studio.libaes.views.ADsBannerView;
|
||||
import cc.winboll.studio.libappbase.LogActivity;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.activities.AboutActivity;
|
||||
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity;
|
||||
import cc.winboll.studio.powerbell.activities.BatteryReportActivity;
|
||||
import cc.winboll.studio.powerbell.activities.ClearRecordActivity;
|
||||
import cc.winboll.studio.powerbell.activities.WinBoLLActivity;
|
||||
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
|
||||
import cc.winboll.studio.powerbell.fragments.MainViewFragment;
|
||||
import cc.winboll.studio.powerbell.unittest.MainUnitTestActivity;
|
||||
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils;
|
||||
|
||||
/**
|
||||
* 主活动类(修复小米广告SDK空Context崩溃问题)
|
||||
* 核心修改点:
|
||||
* 1. 添加全局Context安全校验
|
||||
* 2. 优化广告请求的生命周期判断
|
||||
* 3. 确保广告操作在主线程执行
|
||||
* 4. 完善广告资源释放逻辑
|
||||
*/
|
||||
public class MainActivity extends WinBoLLActivity {
|
||||
|
||||
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_VALUE = "privacy_value";//0: 拒绝,1:赞同
|
||||
//
|
||||
// private SharedPreferences mSharedPreferences;
|
||||
//
|
||||
// private String BANNER_POS_ID = "802e356f1726f9ff39c69308bfd6f06a";
|
||||
// private String BANNER_POS_ID_WINBOLL_BETA = "d129ee5a263911f981a6dc7a9802e3e7";
|
||||
// private String BANNER_POS_ID_WINBOLL = "4ec30efdb32271765b9a4efac902828b";
|
||||
|
||||
// private BannerAd mBannerAd;
|
||||
// private List<BannerAd> mAllBanners = new ArrayList<>();
|
||||
//
|
||||
// private ViewGroup mContainer;
|
||||
//
|
||||
// private boolean mIsBiddingWin = true;
|
||||
//
|
||||
// public static final int BACKGROUND_PICTURE_REQUEST_CODE = 0;
|
||||
|
||||
public static MainActivity _mMainActivity;
|
||||
private App mApplication;
|
||||
private Menu mMenu;
|
||||
private Fragment mCurrentShowFragment;
|
||||
private MainViewFragment mMainViewFragment;
|
||||
private Toolbar mToolbar;
|
||||
// 新增:主线程Handler,确保广告操作在主线程执行
|
||||
//private Handler mMainHandler;
|
||||
ADsBannerView mADsBannerView;
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
LogUtils.d(TAG, "onCreate(...)");
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
mADsBannerView = findViewById(R.id.adsbanner);
|
||||
|
||||
// mContainer = findViewById(R.id.ads_container);
|
||||
//
|
||||
// // 初始化主线程Handler(关键:确保广告操作在主线程执行)
|
||||
// mMainHandler = new Handler(Looper.getMainLooper());
|
||||
//
|
||||
// // 米盟模块:隐私协议弹窗
|
||||
// showPrivacy();
|
||||
|
||||
_mMainActivity = this;
|
||||
mApplication = (App) getApplication();
|
||||
|
||||
// 初始化工具栏
|
||||
mToolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(mToolbar);
|
||||
mToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
|
||||
|
||||
// 初始化主Fragment
|
||||
if (mMainViewFragment == null) {
|
||||
FragmentTransaction tx = getFragmentManager().beginTransaction();
|
||||
mMainViewFragment = new MainViewFragment();
|
||||
tx.add(R.id.activitymainFrameLayout1, mMainViewFragment, MainViewFragment.TAG);
|
||||
tx.commit();
|
||||
}
|
||||
showFragment(mMainViewFragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
// // 修复:释放广告资源,避免内存泄漏
|
||||
// releaseAdResources();
|
||||
// 置空静态引用,避免内存泄漏
|
||||
_mMainActivity = null;
|
||||
// // 移除Handler回调
|
||||
// if (mMainHandler != null) {
|
||||
// mMainHandler.removeCallbacksAndMessages(null);
|
||||
// }
|
||||
if (mADsBannerView != null) {
|
||||
mADsBannerView.releaseAdResources();
|
||||
}
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 释放广告资源(关键:避免内存泄漏和空Context调用)
|
||||
// */
|
||||
// private void releaseAdResources() {
|
||||
// LogUtils.d(TAG, "releaseAdResources()");
|
||||
// // 销毁所有广告实例
|
||||
// if (mAllBanners != null && !mAllBanners.isEmpty()) {
|
||||
// for (BannerAd ad : mAllBanners) {
|
||||
// if (ad != null) {
|
||||
// ad.destroy();
|
||||
// }
|
||||
// }
|
||||
// mAllBanners.clear();
|
||||
// }
|
||||
// // 置空当前广告引用
|
||||
// mBannerAd = null;
|
||||
// // 移除广告容器中的视图
|
||||
// if (mContainer != null) {
|
||||
// mContainer.removeAllViews();
|
||||
// }
|
||||
// }
|
||||
|
||||
void showFragment(Fragment fragment) {
|
||||
FragmentTransaction tx = getFragmentManager().beginTransaction();
|
||||
for (Fragment item : getFragmentManager().getFragments()) {
|
||||
tx.hide(item);
|
||||
}
|
||||
tx.show(fragment);
|
||||
tx.commit();
|
||||
mCurrentShowFragment = fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
reloadBackground();
|
||||
}
|
||||
|
||||
public static void reloadBackground() {
|
||||
// 修复:添加非空校验,避免Activity已销毁时调用
|
||||
if (_mMainActivity != null && !_mMainActivity.isFinishing() && !_mMainActivity.isDestroyed()) {
|
||||
_mMainActivity.mMainViewFragment.reloadBackground();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过Uri获取文件在本地存储的真实路径
|
||||
*/
|
||||
private String getRealPathFromURI(Uri contentUri) {
|
||||
String[] proj = {MediaStore.MediaColumns.DATA};
|
||||
Cursor cursor = getContentResolver().query(contentUri, proj, null, null, null);
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
int nColumnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
|
||||
if (nColumnIndex > -1) {
|
||||
String path = cursor.getString(nColumnIndex);
|
||||
cursor.close();
|
||||
return path;
|
||||
} else {
|
||||
LogUtils.d(TAG, "getRealPathFromURI nColumnIndex is -1.");
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
reloadBackground();
|
||||
setBackgroundColor();
|
||||
if (mADsBannerView != null) {
|
||||
mADsBannerView.resumeADs();
|
||||
}
|
||||
|
||||
// // 修复:优化广告请求逻辑(添加生命周期判断 + 主线程执行)
|
||||
// if (!isFinishing() && !isDestroyed()) {
|
||||
// String privacyAgreeValue = getSharedPreferences().getString(PRIVACY_VALUE, null);
|
||||
// if (TextUtils.equals(privacyAgreeValue, String.valueOf(1))) {
|
||||
// LogUtils.i(TAG, "已同意隐私协议,开始播放米盟广告...");
|
||||
// mMainHandler.postDelayed(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// // 再次校验生命周期,避免延迟执行时Activity已销毁
|
||||
// if (!isFinishing() && !isDestroyed()) {
|
||||
// fetchAd();
|
||||
// }
|
||||
// }
|
||||
// }, 1000); // 延迟1秒请求广告,提升页面加载体验
|
||||
// }
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
mMenu = menu;
|
||||
getMenuInflater().inflate(R.menu.toolbar_main, mMenu);
|
||||
if (App.isDebugging()) {
|
||||
getMenuInflater().inflate(R.menu.toolbar_unittest, mMenu);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
int menuItemId = item.getItemId();
|
||||
if (menuItemId == R.id.action_about) {
|
||||
startActivity(new Intent(this, AboutActivity.class));
|
||||
} else if (menuItemId == R.id.action_battery_report) {
|
||||
startActivity(new Intent(this, BatteryReportActivity.class));
|
||||
} else if (menuItemId == R.id.action_clearrecord) {
|
||||
startActivity(new Intent(this, ClearRecordActivity.class));
|
||||
} else if (menuItemId == R.id.action_changepicture) {
|
||||
startActivity(new Intent(this, BackgroundPictureActivity.class));
|
||||
} else if (menuItemId == R.id.action_log) {
|
||||
LogActivity.startLogActivity(this);
|
||||
} else if (menuItemId == R.id.action_unittestactivity) {
|
||||
startActivity(new Intent(this, MainUnitTestActivity.class));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
// if (requestCode == BACKGROUND_PICTURE_REQUEST_CODE) {
|
||||
// if (resultCode == RESULT_OK) {
|
||||
// Toast.makeText(getApplicationContext(), "OK", Toast.LENGTH_SHORT).show();
|
||||
// }
|
||||
// } else {
|
||||
// String sz = "Unsolved requestCode = " + Integer.toString(requestCode);
|
||||
// Toast.makeText(getApplicationContext(), sz, Toast.LENGTH_SHORT).show();
|
||||
// LogUtils.d(TAG, sz);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mCurrentShowFragment != mMainViewFragment) {
|
||||
showFragment(mMainViewFragment);
|
||||
} else {
|
||||
moveTaskToBack(true);
|
||||
}
|
||||
}
|
||||
|
||||
void setBackgroundColor() {
|
||||
// 修复:添加Activity非空校验
|
||||
if (isFinishing() || isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(this);
|
||||
BackgroundPictureBean bean = utils.getBackgroundPictureBean();
|
||||
int nPixelColor = bean.getPixelColor();
|
||||
RelativeLayout mainLayout = findViewById(R.id.activitymainRelativeLayout1);
|
||||
if (mainLayout != null) {
|
||||
mainLayout.setBackgroundColor(nPixelColor);
|
||||
}
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 显示广告(核心修复:传递安全的Context + 生命周期校验)
|
||||
// */
|
||||
// private void showAd() {
|
||||
// LogUtils.d(TAG, "showAd()");
|
||||
// // 1. 生命周期校验:避免Activity已销毁时操作UI
|
||||
// if (isFinishing() || isDestroyed()) {
|
||||
// LogUtils.e(TAG, "showAd: Activity is finishing or destroyed");
|
||||
// return;
|
||||
// }
|
||||
// // 2. 非空校验:广告实例和容器
|
||||
// if (mBannerAd == null || mContainer == null) {
|
||||
// LogUtils.e(TAG, "showAd: BannerAd or Container is null");
|
||||
// return;
|
||||
// }
|
||||
// // 3. 创建广告容器(使用ApplicationContext避免内存泄漏)
|
||||
// final FrameLayout container = new FrameLayout(getApplicationContext());
|
||||
// container.setPadding(0, 0, 0, MimoUtils.dpToPx(this, 10));
|
||||
// mContainer.addView(container, new FrameLayout.LayoutParams(
|
||||
// FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
// FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
// ));
|
||||
//
|
||||
// if (mIsBiddingWin) {
|
||||
// mBannerAd.setPrice(getPrice());
|
||||
// }
|
||||
// // 4. 显示广告:传递ApplicationContext,避免Activity Context失效
|
||||
// mBannerAd.showAd(MainActivity.this, container, new BannerAd.BannerInteractionListener() {
|
||||
// @Override
|
||||
// public void onAdClick() {
|
||||
// LogUtils.d(TAG, "onAdClick");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onAdShow() {
|
||||
// LogUtils.d(TAG, "onAdShow");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onAdDismiss() {
|
||||
// LogUtils.d(TAG, "onAdDismiss");
|
||||
// // 修复:移除容器时校验Activity状态
|
||||
// if (!isFinishing() && !isDestroyed() && mContainer != null) {
|
||||
// mContainer.removeView(container);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onRenderSuccess() {
|
||||
// LogUtils.d(TAG, "onRenderSuccess");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onRenderFail(int code, String msg) {
|
||||
// LogUtils.e(TAG, "onRenderFail errorCode " + code + " errorMsg " + msg);
|
||||
// // 修复:渲染失败时移除容器
|
||||
// if (!isFinishing() && !isDestroyed() && mContainer != null) {
|
||||
// mContainer.removeView(container);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 请求广告(核心修复:Context安全校验 + 异常捕获 + 资源管理)
|
||||
// */
|
||||
// private void fetchAd() {
|
||||
// LogUtils.d(TAG, "fetchAd()");
|
||||
// // 1. 双重校验:Activity未销毁 + Context非空
|
||||
// if (isFinishing() || isDestroyed() || getApplicationContext() == null) {
|
||||
// LogUtils.e(TAG, "fetchAd: Invalid Context or Activity state");
|
||||
// return;
|
||||
// }
|
||||
// // 2. 释放之前的广告资源,避免内存泄漏
|
||||
// if (mBannerAd != null) {
|
||||
// mBannerAd.destroy();
|
||||
// }
|
||||
// // 3. 初始化广告(使用ApplicationContext,避免Activity Context失效)
|
||||
// try {
|
||||
// mBannerAd = new BannerAd();
|
||||
// mAllBanners.add(mBannerAd);
|
||||
// } catch (Exception e) {
|
||||
// LogUtils.e(TAG, "fetchAd: Init BannerAd failed", e);
|
||||
// return;
|
||||
// }
|
||||
// // 4. 设置下载监听
|
||||
// mBannerAd.setDownLoadListener(new BannerAd.BannerDownloadListener() {
|
||||
// @Override
|
||||
// public void onDownloadStarted() {
|
||||
// LogUtils.d(TAG, "onDownloadStarted");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onDownloadPaused() {
|
||||
// LogUtils.d(TAG, "onDownloadPaused");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onDownloadFailed(int errorCode) {
|
||||
// LogUtils.d(TAG, "onDownloadFailed, errorCode = " + errorCode);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onDownloadFinished() {
|
||||
// LogUtils.d(TAG, "onDownloadFinished");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onDownloadProgressUpdated(int progress) {
|
||||
// LogUtils.d(TAG, "onDownloadProgressUpdated " + progress + "%");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onInstallFailed(int errorCode) {
|
||||
// LogUtils.d(TAG, "onInstallFailed, errorCode = " + errorCode);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onInstallStart() {
|
||||
// LogUtils.d(TAG, "onInstallStart");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onInstallSuccess() {
|
||||
// LogUtils.d(TAG, "onInstallSuccess");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onDownloadCancel() {
|
||||
// LogUtils.d(TAG, "onDownloadCancel");
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// // 5. 构建广告参数并请求
|
||||
// String currentAD_ID = getAD_ID();
|
||||
// LogUtils.d(TAG, String.format("currentAD_ID = %s", currentAD_ID));
|
||||
// ADParams params = new ADParams.Builder().setUpId(currentAD_ID).build();
|
||||
// mBannerAd.loadAd(params, new BannerAd.BannerLoadListener() {
|
||||
// @Override
|
||||
// public void onBannerAdLoadSuccess() {
|
||||
// LogUtils.d(TAG, "onBannerAdLoadSuccess()");
|
||||
// // 修复:广告加载成功后校验Activity状态
|
||||
// if (!isFinishing() && !isDestroyed()) {
|
||||
// showAd();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onAdLoadFailed(int errorCode, String errorMsg) {
|
||||
// LogUtils.e(TAG, "onAdLoadFailed: errorCode = " + errorCode + ", errorMsg = " + errorMsg);
|
||||
// // 修复:加载失败时移除当前广告实例
|
||||
// if (mAllBanners.contains(mBannerAd)) {
|
||||
// mAllBanners.remove(mBannerAd);
|
||||
// }
|
||||
// mBannerAd.destroy();
|
||||
// mBannerAd = null;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 根据当前秒数获取广告ID(原逻辑保留)
|
||||
// */
|
||||
// private String getAD_ID() {
|
||||
// long currentSecond = System.currentTimeMillis() / 1000;
|
||||
// return (currentSecond % 2 == 0) ? BANNER_POS_ID :
|
||||
// (BuildConfig.DEBUG ? BANNER_POS_ID_WINBOLL_BETA : BANNER_POS_ID_WINBOLL);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取广告价格(原逻辑保留,添加空指针校验)
|
||||
// */
|
||||
// private long getPrice() {
|
||||
// if (mBannerAd == null) {
|
||||
// return 0;
|
||||
// }
|
||||
// Map<String, Object> map = mBannerAd.getMediaExtraInfo();
|
||||
// if (map == null || map.isEmpty() || !map.containsKey("price")) {
|
||||
// LogUtils.w(TAG, "getPrice: media extra info is null or no price key");
|
||||
// return 0;
|
||||
// }
|
||||
// Object priceObj = map.get("price");
|
||||
// if (priceObj instanceof Long) {
|
||||
// return (Long) priceObj;
|
||||
// } else if (priceObj instanceof Integer) {
|
||||
// return ((Integer) priceObj).longValue();
|
||||
// } else {
|
||||
// LogUtils.e(TAG, "getPrice: price type is invalid");
|
||||
// return 0;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 显示隐私协议弹窗(原逻辑保留,优化Context使用)
|
||||
// */
|
||||
// private void showPrivacy() {
|
||||
// // 校验Activity状态,避免弹窗泄露
|
||||
// if (isFinishing() || isDestroyed()) {
|
||||
// return;
|
||||
// }
|
||||
// String privacyAgreeValue = getSharedPreferences().getString(PRIVACY_VALUE, null);
|
||||
// if (TextUtils.equals(privacyAgreeValue, String.valueOf(0))) {
|
||||
// LogUtils.i(TAG, "已拒绝隐私协议,广告已处于不可用状态...");
|
||||
// Toast.makeText(getApplicationContext(), "已拒绝隐私协议,广告已处于不可用状态", Toast.LENGTH_SHORT).show();
|
||||
// return;
|
||||
// }
|
||||
// if (TextUtils.equals(privacyAgreeValue, String.valueOf(1))) {
|
||||
// LogUtils.i(TAG, "已同意隐私协议,开始初始化米盟SDK...");
|
||||
// initMimoSdk();
|
||||
// return;
|
||||
// }
|
||||
// LogUtils.i(TAG, "开始弹出隐私协议...");
|
||||
// AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
// builder.setTitle("用户须知");
|
||||
// builder.setMessage("小米广告SDK隐私政策: https://dev.mi.com/distribute/doc/details?pId=1688, 请复制到浏览器查看");
|
||||
// builder.setIcon(R.drawable.ic_launcher);
|
||||
// builder.setCancelable(false); // 点击对话框以外的区域不消失
|
||||
// builder.setPositiveButton("同意", new DialogInterface.OnClickListener() {
|
||||
// @Override
|
||||
// public void onClick(DialogInterface dialog, int which) {
|
||||
// getSharedPreferences().edit()
|
||||
// .putString(PRIVACY_VALUE, String.valueOf(1))
|
||||
// .apply();
|
||||
// initMimoSdk();
|
||||
// dialog.dismiss();
|
||||
// }
|
||||
// });
|
||||
// builder.setNegativeButton("拒绝", new DialogInterface.OnClickListener() {
|
||||
// @Override
|
||||
// public void onClick(DialogInterface dialog, int which) {
|
||||
// getSharedPreferences().edit()
|
||||
// .putString(PRIVACY_VALUE, String.valueOf(0))
|
||||
// .apply();
|
||||
// dialog.dismiss();
|
||||
// }
|
||||
// });
|
||||
// AlertDialog dialog = builder.create();
|
||||
//
|
||||
// // 配置弹窗位置(底部全屏)
|
||||
// Window window = dialog.getWindow();
|
||||
// if (window != null) {
|
||||
// window.setGravity(Gravity.BOTTOM);
|
||||
// WindowManager m = getWindowManager();
|
||||
// Display d = m.getDefaultDisplay();
|
||||
// WindowManager.LayoutParams p = window.getAttributes();
|
||||
// p.width = d.getWidth();
|
||||
// window.setAttributes(p);
|
||||
// }
|
||||
// dialog.show();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 初始化米盟SDK(核心修复:传递ApplicationContext + 异常捕获)
|
||||
// */
|
||||
// private void initMimoSdk() {
|
||||
// // 1. 安全获取ApplicationContext,避免Activity Context失效
|
||||
// Context appContext = getApplicationContext();
|
||||
// if (appContext == null) {
|
||||
// Log.e(TAG, "initMimoSdk: ApplicationContext is null");
|
||||
// return;
|
||||
// }
|
||||
// // 2. 初始化SDK,捕获异常避免崩溃
|
||||
// try {
|
||||
// MimoSdk.init(appContext, new MimoCustomController() {
|
||||
// @Override
|
||||
// public boolean isCanUseLocation() {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public MimoLocation getMimoLocation() {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean isCanUseWifiState() {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean alist() {
|
||||
// return true;
|
||||
// }
|
||||
// }, new MimoSdk.InitCallback() {
|
||||
// @Override
|
||||
// public void success() {
|
||||
// Log.d(TAG, "MimoSdk init success");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void fail(int code, String msg) {
|
||||
// Log.e(TAG, "MimoSdk init fail, code=" + code + ",msg=" + msg);
|
||||
// }
|
||||
// });
|
||||
// MimoSdk.setDebugOn(true);
|
||||
// } catch (Exception e) {
|
||||
// Log.e(TAG, "initMimoSdk: init failed", e);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupToolbar() {
|
||||
super.setupToolbar();
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
}
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 获取SharedPreferences实例(原逻辑保留,添加空指针校验)
|
||||
// */
|
||||
// public SharedPreferences getSharedPreferences() {
|
||||
// if (mSharedPreferences == null) {
|
||||
// // 修复:使用ApplicationContext获取SharedPreferences,避免Activity Context泄露
|
||||
// Context appContext = getApplicationContext();
|
||||
// if (appContext != null) {
|
||||
// mSharedPreferences = appContext.getSharedPreferences(PRIVACY_FILE, Context.MODE_PRIVATE);
|
||||
// } else {
|
||||
// Log.e(TAG, "getSharedPreferences: ApplicationContext is null");
|
||||
// // 降级方案:若ApplicationContext为空,使用Activity Context(仅作兼容)
|
||||
// mSharedPreferences = super.getSharedPreferences(PRIVACY_FILE, Context.MODE_PRIVATE);
|
||||
// }
|
||||
// }
|
||||
// return mSharedPreferences;
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package cc.winboll.studio.powerbell.activities;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/03/25 01:16:32
|
||||
* @Describe 应用介绍窗口
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import cc.winboll.studio.libaes.models.APPInfo;
|
||||
import cc.winboll.studio.libaes.views.AToolbar;
|
||||
import cc.winboll.studio.libaes.views.AboutView;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
|
||||
public class AboutActivity extends Activity {
|
||||
|
||||
Context mContext;
|
||||
|
||||
public static final String TAG = "AboutActivity";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_about);
|
||||
mContext = this;
|
||||
|
||||
// 初始化工具栏
|
||||
AToolbar mAToolbar = (AToolbar) findViewById(R.id.toolbar);
|
||||
setActionBar(mAToolbar);
|
||||
mAToolbar.setSubtitle(getString(R.string.text_about));
|
||||
//mAToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
AboutView aboutView = CreateAboutView();
|
||||
// 在 Activity 的 onCreate 或其他生命周期方法中调用
|
||||
LinearLayout llRoot = findViewById(R.id.root_ll);
|
||||
//layout.setOrientation(LinearLayout.VERTICAL);
|
||||
// 创建布局参数(宽度和高度)
|
||||
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
);
|
||||
llRoot.addView(aboutView, params);
|
||||
|
||||
}
|
||||
|
||||
public AboutView CreateAboutView() {
|
||||
String szBranchName = "powerbell";
|
||||
APPInfo appInfo = new APPInfo();
|
||||
appInfo.setAppName(getString(R.string.app_name));
|
||||
appInfo.setAppIcon(R.drawable.ic_launcher);
|
||||
appInfo.setAppDescription(getString(R.string.app_description));
|
||||
appInfo.setAppGitName("APPBase");
|
||||
appInfo.setAppGitOwner("Studio");
|
||||
appInfo.setAppGitAPPBranch(szBranchName);
|
||||
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
|
||||
appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=PowerBell");
|
||||
appInfo.setAppAPKName("PowerBell");
|
||||
appInfo.setAppAPKFolderName("PowerBell");
|
||||
return new AboutView(mContext, appInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,659 @@
|
||||
package cc.winboll.studio.powerbell.activities;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.RelativeLayout;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import cc.winboll.studio.libaes.dialogs.YesNoAlertDialog;
|
||||
import cc.winboll.studio.libaes.views.AToolbar;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
|
||||
import cc.winboll.studio.powerbell.dialogs.BackgroundPicturePreviewDialog;
|
||||
import cc.winboll.studio.powerbell.dialogs.NetworkBackgroundDialog;
|
||||
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils;
|
||||
import cc.winboll.studio.powerbell.utils.FileUtils;
|
||||
import cc.winboll.studio.powerbell.utils.UriUtil;
|
||||
import cc.winboll.studio.powerbell.views.BackgroundView;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class BackgroundPictureActivity extends WinBoLLActivity implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
|
||||
|
||||
public static final String TAG = "BackgroundPictureActivity";
|
||||
public BackgroundPictureUtils mBackgroundPictureUtils;
|
||||
|
||||
// 图片选择请求码
|
||||
public static final int REQUEST_SELECT_PICTURE = 0;
|
||||
public static final int REQUEST_TAKE_PHOTO = 1;
|
||||
public static final int REQUEST_CROP_IMAGE = 2;
|
||||
private static final int STORAGE_PERMISSION_REQUEST = 100;
|
||||
|
||||
private AToolbar mAToolbar;
|
||||
private File mfBackgroundDir; // 背景图片存储文件夹
|
||||
private File mfPictureDir; // 拍照与剪裁临时文件夹
|
||||
private File mfTakePhoto; // 拍照文件
|
||||
private File mfRecivedPicture; // 接收的图片文件
|
||||
private File mfTempCropPicture; // 剪裁临时文件
|
||||
private File mfRecivedCropPicture; // 剪裁后的目标文件
|
||||
|
||||
private String preViewFileBackgroundView = "";
|
||||
BackgroundView bvPreviewBackground;
|
||||
boolean isCommitSettings = false;
|
||||
|
||||
// 静态变量
|
||||
public static String _mszRecivedCropPicture = "RecivedCrop.jpg";
|
||||
private static String _mszCommonFileType = "jpeg";
|
||||
private int mnPictureCompress = 100;
|
||||
private static String _RecivedPictureFileName;
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_backgroundpicture);
|
||||
initEnv();
|
||||
|
||||
// 初始化工具类和文件夹
|
||||
mBackgroundPictureUtils = BackgroundPictureUtils.getInstance(this);
|
||||
mfBackgroundDir = new File(mBackgroundPictureUtils.getBackgroundDir());
|
||||
if (!mfBackgroundDir.exists()) {
|
||||
mfBackgroundDir.mkdirs();
|
||||
}
|
||||
|
||||
mfPictureDir = new File(App.getTempDirPath());
|
||||
if (!mfPictureDir.exists()) {
|
||||
mfPictureDir.mkdirs();
|
||||
}
|
||||
|
||||
// 初始化文件对象
|
||||
mfTakePhoto = new File(mfPictureDir, "TakePhoto.jpg");
|
||||
mfTempCropPicture = new File(mfPictureDir, "TempCrop.jpg");
|
||||
|
||||
mfRecivedPicture = getRecivedPictureFile(this);
|
||||
mfRecivedCropPicture = new File(mfBackgroundDir, _mszRecivedCropPicture);
|
||||
|
||||
// 初始化工具栏
|
||||
mAToolbar = (AToolbar) findViewById(R.id.toolbar);
|
||||
setActionBar(mAToolbar);
|
||||
mAToolbar.setSubtitle(R.string.subtitle_activity_backgroundpicture);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
mAToolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish(); // 点击导航栏返回按钮,触发 finish()
|
||||
}
|
||||
});
|
||||
|
||||
// 设置按钮点击事件
|
||||
findViewById(R.id.activitybackgroundpictureAButton5).setOnClickListener(onOriginNullClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton4).setOnClickListener(onReceivedPictureClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton1).setOnClickListener(onTakePhotoClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton2).setOnClickListener(onSelectPictureClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton3).setOnClickListener(onCropPictureClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton6).setOnClickListener(onCropFreePictureClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton7).setOnClickListener(onPixelPickerClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton8).setOnClickListener(onCleanPixelClickListener);
|
||||
|
||||
updatePreviewBackground();
|
||||
|
||||
// 处理分享的图片
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
String type = intent.getType();
|
||||
|
||||
if (Intent.ACTION_SEND.equals(action) && type != null && isImageType(type)) {
|
||||
BackgroundPicturePreviewDialog dlg = new BackgroundPicturePreviewDialog(this);
|
||||
dlg.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void initEnv() {
|
||||
LogUtils.d(TAG, "initEnv()");
|
||||
_RecivedPictureFileName = "Recived.data";
|
||||
}
|
||||
|
||||
public static String getBackgroundFileName() {
|
||||
return _mszRecivedCropPicture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAcceptRecivedPicture(String szPreRecivedPictureName) {
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(this);
|
||||
utils.getBackgroundPictureBean().setIsUseBackgroundFile(true);
|
||||
utils.saveData();
|
||||
|
||||
File sourceFile = new File(utils.getBackgroundDir(), szPreRecivedPictureName);
|
||||
if (FileUtils.copyFile(sourceFile, mfRecivedPicture)) {
|
||||
startCropImageActivity(false);
|
||||
} else {
|
||||
ToastUtils.show("图片复制失败,请重试");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新背景图片预览
|
||||
*/
|
||||
public void updatePreviewBackground() {
|
||||
LogUtils.d(TAG, "updatePreviewBackground");
|
||||
//ImageView ivPreviewBackground = (ImageView) findViewById(R.id.activitybackgroundpictureImageView1);
|
||||
bvPreviewBackground = (BackgroundView) findViewById(R.id.activitybackgroundpictureBackgroundView1);
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(this);
|
||||
utils.loadBackgroundPictureBean();
|
||||
|
||||
boolean isUseBackgroundFile = utils.getBackgroundPictureBean().isUseBackgroundFile();
|
||||
if (isUseBackgroundFile && mfRecivedCropPicture.exists()) {
|
||||
//try {
|
||||
String filePath = utils.getBackgroundDir() + getBackgroundFileName();
|
||||
preViewFileBackgroundView = filePath;
|
||||
bvPreviewBackground.previewBackgroundImage(preViewFileBackgroundView);
|
||||
/*Drawable drawable = FileUtils.getImageDrawable(filePath);
|
||||
if (drawable != null) {
|
||||
//drawable.setAlpha(120);
|
||||
//bvPreviewBackground.setImageDrawable(drawable);
|
||||
}*/
|
||||
//ToastUtils.show("背景图片已更新");
|
||||
// } catch (IOException e) {
|
||||
// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
// ToastUtils.show("背景图片加载失败");
|
||||
// }
|
||||
} else {
|
||||
ToastUtils.show("未使用背景图片");
|
||||
preViewFileBackgroundView = "";
|
||||
bvPreviewBackground.previewBackgroundImage(preViewFileBackgroundView);
|
||||
// Drawable drawable = getResources().getDrawable(R.drawable.blank10x10);
|
||||
// if (drawable != null) {
|
||||
// drawable.setAlpha(120);
|
||||
// bvPreviewBackground.setImageDrawable(drawable);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// 点击事件监听器
|
||||
private View.OnClickListener onOriginNullClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this);
|
||||
BackgroundPictureBean bean = utils.getBackgroundPictureBean();
|
||||
bean.setIsUseBackgroundFile(false);
|
||||
utils.saveData();
|
||||
updatePreviewBackground();
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener onSelectPictureClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (checkAndRequestStoragePermission()) {
|
||||
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||
startActivityForResult(intent, REQUEST_SELECT_PICTURE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener onCropPictureClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
File fCheck = new File(mfBackgroundDir, getBackgroundFileName());
|
||||
if (fCheck.exists()) {
|
||||
startCropImageActivity(false);
|
||||
} else {
|
||||
ToastUtils.show("没有可剪裁的图片");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener onCropFreePictureClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
File fCheck = new File(mfBackgroundDir, getBackgroundFileName());
|
||||
if (fCheck.exists()) {
|
||||
startCropImageActivity(true);
|
||||
} else {
|
||||
ToastUtils.show("没有可剪裁的图片");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener onTakePhotoClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "onTakePhotoClickListener");
|
||||
LogUtils.d(TAG, "mfTakePhoto : " + mfTakePhoto.getPath());
|
||||
|
||||
if (mfTakePhoto.exists()) {
|
||||
mfTakePhoto.delete();
|
||||
}
|
||||
try {
|
||||
mfTakePhoto.createNewFile();
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
ToastUtils.show("拍照文件创建失败");
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkAndRequestStoragePermission()) {
|
||||
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener onReceivedPictureClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this);
|
||||
utils.getBackgroundPictureBean().setIsUseBackgroundFile(true);
|
||||
utils.saveData();
|
||||
updatePreviewBackground();
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener onPixelPickerClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// 从文件路径启动像素拾取活动
|
||||
//String imagePath = "/storage/emulated/0/DCIM/Camera/sample.jpg";
|
||||
String imagePath = mfRecivedCropPicture.toString();
|
||||
Intent intent = new Intent(getApplicationContext(), PixelPickerActivity.class);
|
||||
intent.putExtra("imagePath", imagePath);
|
||||
startActivity(intent);
|
||||
//App.getWinBoLLActivityManager().startWinBoLLActivity(getActivity(), intent, PixelPickerActivity.class);
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener onCleanPixelClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this);
|
||||
BackgroundPictureBean bean = utils.getBackgroundPictureBean();
|
||||
bean.setPixelColor(0);
|
||||
utils.saveData();
|
||||
setBackgroundColor();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 压缩图片并保存到接收文件
|
||||
*/
|
||||
void compressQualityToRecivedPicture(Bitmap bitmap) {
|
||||
OutputStream outStream = null;
|
||||
try {
|
||||
mfRecivedPicture = getRecivedPictureFile(this);
|
||||
if (!mfRecivedPicture.exists()) {
|
||||
mfRecivedPicture.createNewFile();
|
||||
}
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(mfRecivedPicture);
|
||||
outStream = new BufferedOutputStream(fos);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
|
||||
outStream.flush();
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
ToastUtils.show("图片压缩失败");
|
||||
} finally {
|
||||
if (outStream != null) {
|
||||
try {
|
||||
outStream.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
if (bitmap != null && !bitmap.isRecycled()) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动图片裁剪活动
|
||||
* @param isCropFree 是否自由裁剪
|
||||
*/
|
||||
public void startCropImageActivity(boolean isCropFree) {
|
||||
LogUtils.d(TAG, "startCropImageActivity");
|
||||
BackgroundPictureBean bean = mBackgroundPictureUtils.loadBackgroundPictureBean();
|
||||
mfRecivedPicture = getRecivedPictureFile(this);
|
||||
Uri uri = UriUtil.getUriForFile(this, mfRecivedPicture);
|
||||
LogUtils.d(TAG, "uri : " + uri.toString());
|
||||
|
||||
if (mfTempCropPicture.exists()) {
|
||||
mfTempCropPicture.delete();
|
||||
}
|
||||
try {
|
||||
mfTempCropPicture.createNewFile();
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
ToastUtils.show("剪裁临时文件创建失败");
|
||||
return;
|
||||
}
|
||||
|
||||
Uri cropOutPutUri = Uri.fromFile(mfTempCropPicture);
|
||||
LogUtils.d(TAG, "mfTempCropPicture : " + mfTempCropPicture.getPath());
|
||||
|
||||
Intent intent = new Intent("com.android.camera.action.CROP");
|
||||
intent.setDataAndType(uri, "image/" + _mszCommonFileType);
|
||||
intent.putExtra("crop", "true");
|
||||
intent.putExtra("noFaceDetection", true);
|
||||
|
||||
if (!isCropFree) {
|
||||
intent.putExtra("aspectX", bean.getBackgroundWidth());
|
||||
intent.putExtra("aspectY", bean.getBackgroundHeight());
|
||||
}
|
||||
|
||||
intent.putExtra("return-data", true);
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, cropOutPutUri);
|
||||
intent.putExtra("scale", true);
|
||||
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
startActivityForResult(intent, REQUEST_CROP_IMAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存剪裁后的Bitmap(优化版)
|
||||
*/
|
||||
private void saveCropBitmap(Bitmap bitmap) {
|
||||
if (bitmap == null) {
|
||||
ToastUtils.show("剪裁图片为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 内存优化:大图片自动缩放
|
||||
Bitmap scaledBitmap = bitmap;
|
||||
if (bitmap.getByteCount() > 10 * 1024 * 1024) { // 超过10MB
|
||||
float scale = 1.0f;
|
||||
while (scaledBitmap.getByteCount() > 5 * 1024 * 1024) {
|
||||
scale -= 0.2f; // 每次缩小20%
|
||||
if (scale < 0.2f) break; // 最小缩放到20%
|
||||
scaledBitmap = scaleBitmap(scaledBitmap, scale);
|
||||
}
|
||||
if (scaledBitmap != bitmap) {
|
||||
bitmap.recycle(); // 回收原Bitmap
|
||||
}
|
||||
}
|
||||
|
||||
// 优化:创建保存目录
|
||||
File backgroundDir = new File(mBackgroundPictureUtils.getBackgroundDir());
|
||||
if (!backgroundDir.exists()) {
|
||||
if (!backgroundDir.mkdirs()) {
|
||||
ToastUtils.show("无法创建保存目录");
|
||||
if (scaledBitmap != bitmap) scaledBitmap.recycle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
File saveFile = new File(backgroundDir, getBackgroundFileName());
|
||||
|
||||
// 优化:检查文件是否可写
|
||||
if (saveFile.exists() && !saveFile.canWrite()) {
|
||||
if (!saveFile.delete()) {
|
||||
ToastUtils.show("无法删除旧文件");
|
||||
if (scaledBitmap != bitmap) scaledBitmap.recycle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(saveFile);
|
||||
boolean success = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
|
||||
fos.flush();
|
||||
if (success) {
|
||||
ToastUtils.show("保存成功");
|
||||
// 更新数据
|
||||
mBackgroundPictureUtils.getBackgroundPictureBean().setIsUseBackgroundFile(true);
|
||||
updatePreviewBackground();
|
||||
} else {
|
||||
ToastUtils.show("图片压缩保存失败");
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
LogUtils.e(TAG, "文件未找到" + e);
|
||||
ToastUtils.show("保存失败:文件路径错误");
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "写入异常" + e);
|
||||
ToastUtils.show("保存失败:磁盘可能已满或路径错误");
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "流关闭异常" + e);
|
||||
}
|
||||
}
|
||||
if (scaledBitmap != null && !scaledBitmap.isRecycled()) {
|
||||
scaledBitmap.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放Bitmap
|
||||
*/
|
||||
private Bitmap scaleBitmap(Bitmap original, float scale) {
|
||||
if (original == null) {
|
||||
return null;
|
||||
}
|
||||
int width = (int) (original.getWidth() * scale);
|
||||
int height = (int) (original.getHeight() * scale);
|
||||
return Bitmap.createScaledBitmap(original, width, height, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分享图片
|
||||
*/
|
||||
void sharePicture() {
|
||||
Uri uri = UriUtil.getUriForFile(this, mfRecivedPicture);
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
shareIntent.setType("image/" + _mszCommonFileType);
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
startActivity(Intent.createChooser(shareIntent, "Share Image"));
|
||||
}
|
||||
|
||||
public static File getRecivedPictureFile(Context context) {
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(context);
|
||||
utils.loadBackgroundPictureBean();
|
||||
return new File(utils.getBackgroundDir(), _RecivedPictureFileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_SELECT_PICTURE && resultCode == RESULT_OK) {
|
||||
try {
|
||||
Uri selectedImage = data.getData();
|
||||
LogUtils.d(TAG, "Uri is : " + selectedImage.toString());
|
||||
File fSrcImage = new File(UriUtil.getFilePathFromUri(this, selectedImage));
|
||||
mfRecivedPicture = getRecivedPictureFile(this);
|
||||
if (FileUtils.copyFile(fSrcImage, mfRecivedPicture)) {
|
||||
startCropImageActivity(false);
|
||||
} else {
|
||||
ToastUtils.show("图片复制失败,请重试");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "选择图片异常" + e);
|
||||
ToastUtils.show("选择图片失败:" + e.getMessage());
|
||||
}
|
||||
} else if (requestCode == REQUEST_TAKE_PHOTO && resultCode == RESULT_OK) {
|
||||
LogUtils.d(TAG, "REQUEST_TAKE_PHOTO");
|
||||
Bundle extras = data.getExtras();
|
||||
if (extras != null) {
|
||||
Bitmap imageBitmap = (Bitmap) extras.get("data");
|
||||
if (imageBitmap != null) {
|
||||
compressQualityToRecivedPicture(imageBitmap);
|
||||
startCropImageActivity(false);
|
||||
} else {
|
||||
ToastUtils.show("拍照图片为空");
|
||||
}
|
||||
} else {
|
||||
ToastUtils.show("拍照数据获取失败");
|
||||
}
|
||||
} else if (requestCode == REQUEST_CROP_IMAGE && resultCode == RESULT_OK) {
|
||||
LogUtils.d(TAG, "CROP_IMAGE_REQUEST_CODE");
|
||||
try {
|
||||
Bitmap cropBitmap = null;
|
||||
// 方案1:通过Intent获取剪裁后的Bitmap
|
||||
if (data != null && data.hasExtra("data")) {
|
||||
cropBitmap = data.getParcelableExtra("data");
|
||||
} else if (mfTempCropPicture.exists()) {
|
||||
cropBitmap = BitmapFactory.decodeFile(mfTempCropPicture.getPath());
|
||||
} else {
|
||||
ToastUtils.show("剪裁文件不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cropBitmap != null) {
|
||||
saveCropBitmap(cropBitmap);
|
||||
} else {
|
||||
ToastUtils.show("获取剪裁图片失败");
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
LogUtils.e(TAG, "内存溢出" + e);
|
||||
ToastUtils.show("保存失败:内存不足,请尝试裁剪更小的图片");
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "剪裁保存异常" + e);
|
||||
ToastUtils.show("保存失败:" + e.getMessage());
|
||||
}/* finally {
|
||||
// 安全删除临时文件
|
||||
if (mfTempCropPicture.exists()) {
|
||||
mfTempCropPicture.delete();
|
||||
}
|
||||
}*/
|
||||
} else if (resultCode != RESULT_OK) {
|
||||
LogUtils.d(TAG, "操作取消或失败,requestCode: " + requestCode);
|
||||
ToastUtils.show("操作已取消");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查类型是否为图片
|
||||
*/
|
||||
private boolean isImageType(String type) {
|
||||
return type.startsWith("image/") || "image/jpeg".equals(type) ||
|
||||
"image/jpg".equals(type) || "image/png".equals(type) ||
|
||||
"image/webp".equals(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查并申请存储权限
|
||||
*/
|
||||
private boolean checkAndRequestStoragePermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
STORAGE_PERMISSION_REQUEST);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == STORAGE_PERMISSION_REQUEST) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
ToastUtils.show("存储权限已获取");
|
||||
} else {
|
||||
ToastUtils.show("需要存储权限才能保存图片");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setBackgroundColor() {
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this);
|
||||
BackgroundPictureBean bean = utils.getBackgroundPictureBean();
|
||||
int nPixelColor = bean.getPixelColor();
|
||||
RelativeLayout mainLayout = findViewById(R.id.activitybackgroundpictureRelativeLayout1);
|
||||
mainLayout.setBackgroundColor(nPixelColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
setBackgroundColor();
|
||||
}
|
||||
|
||||
public void onNetworkBackgroundDialog(View view) {
|
||||
// 在需要显示对话框的地方(如网络状态监听回调中)
|
||||
NetworkBackgroundDialog dialog = new NetworkBackgroundDialog(this, new NetworkBackgroundDialog.OnDialogClickListener() {
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
ToastUtils.show("onConfirm");
|
||||
// 处理确认逻辑(如允许后台网络使用)
|
||||
LogUtils.d("MainActivity", "用户允许后台网络使用");
|
||||
// 执行具体业务:如开启后台网络请求服务
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
ToastUtils.show("onCancel");
|
||||
// 处理取消逻辑(如禁止后台网络使用)
|
||||
LogUtils.d("MainActivity", "用户禁止后台网络使用");
|
||||
// 执行具体业务:如关闭后台网络请求
|
||||
}
|
||||
});
|
||||
|
||||
// 可选:修改对话框标题和内容(适配自定义场景)
|
||||
dialog.setTitle("网络图片下载对话框");
|
||||
dialog.setContent("是否下载地址中的图片资源,作为应用背景图片?");
|
||||
|
||||
// 显示对话框
|
||||
dialog.show();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写finish方法,确保所有退出场景都触发Toast
|
||||
*/
|
||||
@Override
|
||||
public void finish() {
|
||||
if (!isCommitSettings) {
|
||||
YesNoAlertDialog.show(this, "应用背景更改提示:", "是否应用预览图片?", new YesNoAlertDialog.OnDialogResultListener(){
|
||||
|
||||
@Override
|
||||
public void onNo() {
|
||||
isCommitSettings = true;
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onYes() {
|
||||
bvPreviewBackground.saveToBackgroundSources(preViewFileBackgroundView);
|
||||
isCommitSettings = true;
|
||||
finish();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
super.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,516 @@
|
||||
package cc.winboll.studio.powerbell.activities;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/10/22 13:21
|
||||
* @Describe BatteryReportActivity
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
public class BatteryReportActivity extends Activity {
|
||||
public static final String TAG = "BatteryReportActivity";
|
||||
|
||||
private RecyclerView rvBatteryReport;
|
||||
private BatteryReportAdapter adapter;
|
||||
private List<AppBatteryModel> dataList = new ArrayList<AppBatteryModel>();
|
||||
private List<AppBatteryModel> filteredList = new ArrayList<AppBatteryModel>();
|
||||
private BroadcastReceiver batteryReceiver;
|
||||
private int batteryCapacity = 5400; // 电池容量(mAh)
|
||||
private float lastBatteryPercent = 100.0f;
|
||||
private long lastCheckTime = System.currentTimeMillis();
|
||||
private EditText etSearch;
|
||||
private Map<String, Long> appRunTimeCache = new HashMap<String, Long>();
|
||||
private Map<String, String> packageToAppNameCache = new HashMap<String, String>();
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_battery_report);
|
||||
mPackageManager = getPackageManager();
|
||||
|
||||
// 权限检查(Java7 传统条件判断)
|
||||
if (!hasUsageStatsPermission(this)) {
|
||||
Toast.makeText(this, "请进入设置-应用-权限-特殊访问权限-使用情况访问权限,开启本应用的权限", Toast.LENGTH_LONG).show();
|
||||
startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
|
||||
return;
|
||||
}
|
||||
|
||||
etSearch = (EditText) findViewById(R.id.et_search);
|
||||
rvBatteryReport = (RecyclerView) findViewById(R.id.rv_battery_report);
|
||||
rvBatteryReport.setLayoutManager(new LinearLayoutManager(this));
|
||||
|
||||
// 初始化流程:新增“加载24小时累计耗电”步骤
|
||||
loadAllAppPackage();
|
||||
preCacheAllAppNames();
|
||||
appRunTimeCache = getAppRunTime();
|
||||
updateAppRunTimeToModel();
|
||||
calculateInitial24hTotalConsumption(); // 初始化时计算24小时累计耗电
|
||||
filteredList.addAll(dataList);
|
||||
adapter = new BatteryReportAdapter(this, filteredList, mPackageManager, packageToAppNameCache);
|
||||
rvBatteryReport.setAdapter(adapter);
|
||||
|
||||
// 搜索监听(不变)
|
||||
etSearch.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
filterAppsByPackageAndName(s.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
|
||||
// 电池广播:调用修改后的“单次耗电计算+累计累加”方法
|
||||
batteryReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
int level = intent.getIntExtra("level", 100);
|
||||
int scale = intent.getIntExtra("scale", 100);
|
||||
float currentPercent = (float) level / scale * 100;
|
||||
LogUtils.d(TAG, "电池百分比变化:" + lastBatteryPercent + " -> " + currentPercent);
|
||||
|
||||
if (currentPercent < lastBatteryPercent) {
|
||||
float dropPercent = lastBatteryPercent - currentPercent;
|
||||
long duration = System.currentTimeMillis() - lastCheckTime;
|
||||
LogUtils.d(TAG, "电池消耗:" + dropPercent + "%,时长:" + duration + "ms");
|
||||
appRunTimeCache = getAppRunTime();
|
||||
updateAppRunTimeToModel();
|
||||
calculateSingleConsumptionAndAccumulate(dropPercent, appRunTimeCache); // 单次+累计逻辑
|
||||
}
|
||||
|
||||
lastBatteryPercent = currentPercent;
|
||||
lastCheckTime = System.currentTimeMillis();
|
||||
}
|
||||
};
|
||||
registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Java7 显式非空判断
|
||||
if (batteryReceiver != null) {
|
||||
unregisterReceiver(batteryReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载所有应用(仅获取包名,初始化模型时单次耗电、累计耗电均设为0)
|
||||
*/
|
||||
private void loadAllAppPackage() {
|
||||
List<ApplicationInfo> appList = mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA);
|
||||
dataList.clear();
|
||||
|
||||
LogUtils.d(TAG, "开始加载应用包名列表,共找到" + appList.size() + "个应用");
|
||||
|
||||
for (ApplicationInfo appInfo : appList) {
|
||||
String packageName = appInfo.packageName;
|
||||
// 初始化:单次耗电(consumption)=0,累计耗电(totalConsumption)=0,运行时长=0
|
||||
dataList.add(new AppBatteryModel(packageName, 0.0f, 0.0f, 0));
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, "应用包名列表加载完成,共添加" + dataList.size() + "个包名。");
|
||||
}
|
||||
|
||||
/**
|
||||
* 预缓存应用名称(逻辑不变)
|
||||
*/
|
||||
private void preCacheAllAppNames() {
|
||||
packageToAppNameCache.clear();
|
||||
LogUtils.d(TAG, "开始预缓存包名-应用名称映射");
|
||||
|
||||
for (AppBatteryModel model : dataList) {
|
||||
String packageName = model.getPackageName();
|
||||
String appName = getAppNameByPackage(packageName);
|
||||
packageToAppNameCache.put(packageName, appName);
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, "预缓存完成,共缓存" + packageToAppNameCache.size() + "个应用名称");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过包名获取应用名称(逻辑不变)
|
||||
*/
|
||||
private String getAppNameByPackage(String packageName) {
|
||||
try {
|
||||
ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
|
||||
return mPackageManager.getApplicationLabel(appInfo).toString();
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
LogUtils.e(TAG, "包名" + packageName + "对应的应用未找到:" + e.getMessage());
|
||||
return packageName;
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "查询应用名称失败(包名:" + packageName + "):" + e.getMessage());
|
||||
return packageName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新运行时长到模型(逻辑不变)
|
||||
*/
|
||||
private void updateAppRunTimeToModel() {
|
||||
int nCount = 0;
|
||||
for (AppBatteryModel model : dataList) {
|
||||
String packageName = model.getPackageName();
|
||||
Long runTime;
|
||||
if (appRunTimeCache.containsKey(packageName)) {
|
||||
runTime = appRunTimeCache.get(packageName);
|
||||
LogUtils.d(TAG, String.format("应用包 %s 运行时长已更新。", packageName));
|
||||
nCount++;
|
||||
} else {
|
||||
runTime = 0L;
|
||||
}
|
||||
model.setRunTime(runTime);
|
||||
}
|
||||
LogUtils.d(TAG, String.format("dataList.size() %d, appRunTimeCache.size() %d。", dataList.size(), appRunTimeCache.size()));
|
||||
LogUtils.d(TAG, String.format("updateAppRunTimeToModel() 更新的数据量为:%d", nCount));
|
||||
}
|
||||
|
||||
/**
|
||||
* 【新增】初始化时计算24小时累计耗电(赋值给totalConsumption)
|
||||
* 逻辑:基于24小时运行时长占比,分配当前电池容量的理论24小时消耗
|
||||
*/
|
||||
private void calculateInitial24hTotalConsumption() {
|
||||
long total24hRunTime = 0;
|
||||
// 1. 计算24小时内所有应用总运行时长
|
||||
for (Map.Entry<String, Long> entry : appRunTimeCache.entrySet()) {
|
||||
total24hRunTime += entry.getValue();
|
||||
}
|
||||
LogUtils.d(TAG, "24小时内所有应用总运行时长:" + formatRunTime(total24hRunTime));
|
||||
|
||||
// 2. 按运行时长占比分配24小时累计耗电(假设电池满电循环,用总容量近似24小时总消耗)
|
||||
for (AppBatteryModel model : dataList) {
|
||||
String packageName = model.getPackageName();
|
||||
Long app24hRunTime = appRunTimeCache.getOrDefault(packageName, 0L);
|
||||
|
||||
// 计算占比与累计耗电
|
||||
float ratio = (total24hRunTime > 0) ? (float) app24hRunTime / total24hRunTime : 0;
|
||||
float initialTotalConsumption = batteryCapacity * ratio; // 用电池容量近似24小时总消耗
|
||||
model.setTotalConsumption(initialTotalConsumption); // 初始化累计耗电
|
||||
LogUtils.d(TAG, String.format("应用包 %s 24小时累计耗电初始化:%.1f mAh", packageName, initialTotalConsumption));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 【核心修改】计算单次耗电(赋值给consumption)+ 累加至累计耗电(totalConsumption = totalConsumption + consumption)
|
||||
*/
|
||||
private void calculateSingleConsumptionAndAccumulate(float dropPercent, Map<String, Long> runTimeMap) {
|
||||
long totalSingleRunTime = 0;
|
||||
// 1. 计算本次电池下降期间的总运行时长
|
||||
for (Map.Entry<String, Long> entry : runTimeMap.entrySet()) {
|
||||
totalSingleRunTime += entry.getValue();
|
||||
}
|
||||
|
||||
// 2. 遍历计算每个应用的“单次耗电”并“累加至累计”
|
||||
for (AppBatteryModel model : dataList) {
|
||||
String packageName = model.getPackageName();
|
||||
Long appSingleRunTime = runTimeMap.getOrDefault(packageName, 0L);
|
||||
|
||||
// 步骤1:计算本次单次耗电(赋值给consumption)
|
||||
float ratio = (totalSingleRunTime > 0) ? (float) appSingleRunTime / totalSingleRunTime : 0;
|
||||
float singleConsumption = batteryCapacity * dropPercent / 100 * ratio; // 单次消耗
|
||||
model.setConsumption(singleConsumption); // 存储单次耗电
|
||||
|
||||
// 步骤2:累加单次耗电到累计耗电(totalConsumption = 原有累计 + 本次单次)
|
||||
float newTotalConsumption = model.getTotalConsumption() + singleConsumption;
|
||||
model.setTotalConsumption(newTotalConsumption); // 更新累计耗电
|
||||
|
||||
// 同步运行时长
|
||||
model.setRunTime(appSingleRunTime);
|
||||
|
||||
LogUtils.d(TAG, String.format("应用包 %s:单次耗电%.1f mAh,累计耗电%.1f mAh",
|
||||
packageName, singleConsumption, newTotalConsumption));
|
||||
}
|
||||
|
||||
// 3. 按累计耗电排序(从高到低)
|
||||
Collections.sort(dataList, new Comparator<AppBatteryModel>() {
|
||||
@Override
|
||||
public int compare(AppBatteryModel m1, AppBatteryModel m2) {
|
||||
return Float.compare(m2.getTotalConsumption(), m1.getTotalConsumption());
|
||||
}
|
||||
});
|
||||
|
||||
// 4. 重新应用过滤并刷新列表
|
||||
filterAppsByPackageAndName(etSearch.getText().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 双维度过滤(逻辑不变)
|
||||
*/
|
||||
private void filterAppsByPackageAndName(String keyword) {
|
||||
filteredList.clear();
|
||||
if (keyword == null || keyword.isEmpty()) {
|
||||
filteredList.addAll(dataList);
|
||||
} else {
|
||||
String lowerKeyword = keyword.toLowerCase();
|
||||
|
||||
for (AppBatteryModel model : dataList) {
|
||||
String packageName = model.getPackageName();
|
||||
String packageNameLower = packageName.toLowerCase();
|
||||
String appName = packageToAppNameCache.get(packageName);
|
||||
String appNameLower = appName.toLowerCase();
|
||||
|
||||
boolean isMatched = packageNameLower.contains(lowerKeyword)
|
||||
|| appNameLower.contains(lowerKeyword);
|
||||
|
||||
if (isMatched) {
|
||||
filteredList.add(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用运行时长(逻辑不变,返回24小时运行时长)
|
||||
*/
|
||||
private Map<String, Long> getAppRunTime() {
|
||||
Map<String, Long> runTimeMap = new HashMap<String, Long>();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
try {
|
||||
android.app.usage.UsageStatsManager manager =
|
||||
(android.app.usage.UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
|
||||
long endTime = System.currentTimeMillis();
|
||||
long startTime = endTime - 24 * 3600 * 1000; // 近24小时
|
||||
List<android.app.usage.UsageStats> statsList = manager.queryUsageStats(
|
||||
android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime);
|
||||
|
||||
for (android.app.usage.UsageStats stats : statsList) {
|
||||
long runTimeMs = stats.getTotalTimeInForeground();
|
||||
String packageName = stats.getPackageName();
|
||||
LogUtils.d(TAG, "包名" + packageName + "24小时运行时长:" + formatRunTime(runTimeMs));
|
||||
runTimeMap.put(packageName, runTimeMs);
|
||||
if (packageName.equals("aidepro.top")) {
|
||||
LogUtils.d(TAG, String.format("runTimeMap.put(packageName, runTimeMs) 特殊查询 %s 查询有结果。", packageName));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "获取应用运行时长失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, String.format("应用运行时长列表数量%d。", runTimeMap.size()));
|
||||
return runTimeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化运行时长(逻辑不变)
|
||||
*/
|
||||
private String formatRunTime(long runTimeMs) {
|
||||
if (runTimeMs <= 0) {
|
||||
return "0秒";
|
||||
}
|
||||
long seconds = runTimeMs / 1000;
|
||||
long hours = seconds / 3600;
|
||||
long minutes = (seconds % 3600) / 60;
|
||||
seconds = seconds % 60;
|
||||
|
||||
if (hours > 0) {
|
||||
return String.format("%d时%d分%d秒", hours, minutes, seconds);
|
||||
} else if (minutes > 0) {
|
||||
return String.format("%d分%d秒", minutes, seconds);
|
||||
} else {
|
||||
return String.format("%d秒", seconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限检查(逻辑不变)
|
||||
*/
|
||||
private boolean hasUsageStatsPermission(Context context) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
android.app.usage.UsageStatsManager manager =
|
||||
(android.app.usage.UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
|
||||
if (manager == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long startTime = endTime - 1000 * 60;
|
||||
List<android.app.usage.UsageStats> statsList = manager.queryUsageStats(
|
||||
android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime);
|
||||
|
||||
return statsList != null && !statsList.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 【核心修改】数据模型:明确字段含义
|
||||
* - consumption:单次耗电(两次电池广播间的消耗,float类型便于计算)
|
||||
* - totalConsumption:累计耗电(24小时初始化值+后续单次累加,显示用)
|
||||
*/
|
||||
public static class AppBatteryModel {
|
||||
private String packageName; // 应用包名(核心标识)
|
||||
private float consumption; // 单次耗电(mAh,float类型)
|
||||
private float totalConsumption;// 累计耗电(mAh,显示+排序用)
|
||||
private long runTime; // 运行时长(ms)
|
||||
|
||||
// Java7 显式构造:初始化单次耗电、累计耗电为0
|
||||
public AppBatteryModel(String packageName, float consumption, float totalConsumption, long runTime) {
|
||||
this.packageName = packageName;
|
||||
this.consumption = consumption; // 单次耗电初始为0
|
||||
this.totalConsumption = totalConsumption; // 累计耗电初始为0(后续初始化时赋值)
|
||||
this.runTime = runTime;
|
||||
}
|
||||
|
||||
// Getter/Setter:覆盖所有字段,确保数据操作正常
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public float getConsumption() {
|
||||
return consumption; // 获取单次耗电
|
||||
}
|
||||
|
||||
public void setConsumption(float consumption) {
|
||||
this.consumption = consumption; // 设置单次耗电
|
||||
}
|
||||
|
||||
public float getTotalConsumption() {
|
||||
return totalConsumption; // 获取累计耗电(显示用)
|
||||
}
|
||||
|
||||
public void setTotalConsumption(float totalConsumption) {
|
||||
this.totalConsumption = totalConsumption; // 设置累计耗电(初始化/累加用)
|
||||
}
|
||||
|
||||
public long getRunTime() {
|
||||
return runTime;
|
||||
}
|
||||
|
||||
public void setRunTime(long runTime) {
|
||||
this.runTime = runTime;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RecyclerView 适配器:仅显示累计耗电(totalConsumption),逻辑适配模型修改
|
||||
*/
|
||||
public static class BatteryReportAdapter extends RecyclerView.Adapter<BatteryReportAdapter.ViewHolder> {
|
||||
private Context mContext;
|
||||
private List<AppBatteryModel> mDataList;
|
||||
private PackageManager mPm;
|
||||
private Map<String, String> mPackageToNameCache;
|
||||
|
||||
// Java7 显式构造:接收名称缓存,确保显示时高效获取应用名
|
||||
public BatteryReportAdapter(Context context, List<AppBatteryModel> dataList,
|
||||
PackageManager pm, Map<String, String> packageToNameCache) {
|
||||
this.mContext = context;
|
||||
this.mDataList = dataList;
|
||||
this.mPm = pm;
|
||||
this.mPackageToNameCache = packageToNameCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
// 加载系统列表项布局(text1显示应用名,text2显示累计耗电+时长)
|
||||
View itemView = LayoutInflater.from(mContext)
|
||||
.inflate(android.R.layout.simple_list_item_2, parent, false);
|
||||
return new ViewHolder(itemView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
// Java7 显式非空判断:避免空指针异常
|
||||
if (mDataList == null || mDataList.isEmpty() || position >= mDataList.size()) {
|
||||
holder.tvAppName.setText("未知应用");
|
||||
holder.tvConsumption.setText("累计耗电:0.0 mAh | 运行时长:0秒");
|
||||
return;
|
||||
}
|
||||
|
||||
AppBatteryModel model = mDataList.get(position);
|
||||
String packageName = model.getPackageName();
|
||||
String appName = "";
|
||||
|
||||
// 优先从缓存获取应用名:减少PackageManager调用,提升性能
|
||||
if (mPackageToNameCache != null && mPackageToNameCache.containsKey(packageName)) {
|
||||
appName = mPackageToNameCache.get(packageName);
|
||||
} else {
|
||||
// 缓存无数据时兜底查询,并同步更新缓存
|
||||
try {
|
||||
ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0);
|
||||
appName = mPm.getApplicationLabel(appInfo).toString();
|
||||
if (mPackageToNameCache != null) {
|
||||
mPackageToNameCache.put(packageName, appName);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
appName = packageName; // 包名不存在时用包名兜底
|
||||
LogUtils.e("Adapter", "包名" + packageName + "对应的应用未找到:" + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
appName = packageName; // 其他异常时用包名兜底
|
||||
LogUtils.e("Adapter", "查询应用名称失败(包名:" + packageName + "):" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 显示逻辑:仅展示累计耗电(totalConsumption),隐藏单次耗电
|
||||
holder.tvAppName.setText(appName);
|
||||
// 格式化运行时长 + 累计耗电(保留1位小数,提升可读性)
|
||||
String runTimeStr = ((BatteryReportActivity) mContext).formatRunTime(model.getRunTime());
|
||||
String totalConsumptionText = String.format("累计耗电:%.1f mAh | 运行时长:%s",
|
||||
model.getTotalConsumption(), runTimeStr);
|
||||
holder.tvConsumption.setText(totalConsumptionText);
|
||||
|
||||
// 显示优化:文字颜色区分(避免所有应用均标蓝,仅示例可按需修改)
|
||||
holder.tvAppName.setTextColor(mContext.getResources().getColor(android.R.color.black));
|
||||
holder.tvConsumption.setTextColor(mContext.getResources().getColor(android.R.color.darker_gray));
|
||||
|
||||
// 调整文字大小:适配手机屏幕,提升可读性
|
||||
holder.tvAppName.setTextSize(16);
|
||||
holder.tvConsumption.setTextSize(14);
|
||||
}
|
||||
|
||||
// 获取列表长度:Java7 三元运算符判断空值,避免空指针
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mDataList == null ? 0 : mDataList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder:绑定系统布局控件,与显示逻辑对应
|
||||
*/
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvAppName; // 显示应用名称
|
||||
TextView tvConsumption; // 显示累计耗电 + 运行时长
|
||||
|
||||
// Java7 显式构造:绑定控件ID(系统布局固定ID:text1、text2)
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
tvAppName = (TextView) itemView.findViewById(android.R.id.text1);
|
||||
tvConsumption = (TextView) itemView.findViewById(android.R.id.text2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package cc.winboll.studio.powerbell.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
|
||||
import cc.winboll.studio.libaes.views.AToolbar;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.beans.BatteryInfoBean;
|
||||
import cc.winboll.studio.powerbell.receivers.ControlCenterServiceReceiver;
|
||||
import cc.winboll.studio.powerbell.utils.AppCacheUtils;
|
||||
import cc.winboll.studio.powerbell.utils.StringUtils;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ClearRecordActivity extends Activity {
|
||||
|
||||
public static final String TAG = "ClearRecordActivity";
|
||||
|
||||
AToolbar mAToolbar;
|
||||
TextView mtvRecordText;
|
||||
App mApplication;
|
||||
boolean mIsShowRecordWithEnter = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_clearrecord);
|
||||
mApplication = (App) getApplication();
|
||||
|
||||
// 初始化工具栏
|
||||
mAToolbar = (AToolbar) findViewById(R.id.toolbar);
|
||||
setActionBar(mAToolbar);
|
||||
//mAToolbar.setTitle(getTitle() + " - " + getString(R.string.subtitle_activity_clearrecord));
|
||||
mAToolbar.setSubtitle(R.string.subtitle_activity_clearrecord);
|
||||
//mAToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
|
||||
//mAToolbar.setSubtitleTextAppearance(this, R.style.Toolbar_SubTitleText);
|
||||
//mAToolbar.setBackgroundColor(getColor(R.color.colorPrimary));
|
||||
setActionBar(mAToolbar);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
mAToolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
// 设置滑动清理控件
|
||||
//
|
||||
// 初始化发送拉动控件
|
||||
final AOHPCTCSeekBar aOHPCTCSeekBar = findViewById(R.id.activityclearrecordAOHPCTCSeekBar1);
|
||||
aOHPCTCSeekBar.setThumb(getDrawable(R.drawable.cursor_pointer));
|
||||
aOHPCTCSeekBar.setThumbOffset(0);
|
||||
aOHPCTCSeekBar.setOnOHPCListener(
|
||||
new AOHPCTCSeekBar.OnOHPCListener(){
|
||||
|
||||
@Override
|
||||
public void onOHPCommit() {
|
||||
mApplication.clearBatteryHistory();
|
||||
sendBroadcast(new Intent(ControlCenterServiceReceiver.ACTION_UPDATE_SERVICENOTIFICATION));
|
||||
initRecordText();
|
||||
String szMSG = "The APP battery record is cleaned.";
|
||||
LogUtils.d(TAG, szMSG);
|
||||
ToastUtils.show(szMSG);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
// 初始化提示框
|
||||
TextView tvAOHPCTCSeekBarMSG = findViewById(R.id.activityclearrecordTextView1);
|
||||
tvAOHPCTCSeekBarMSG.setText(R.string.msg_AOHPCTCSeekBar_ClearRecord);
|
||||
mtvRecordText = findViewById(R.id.activityclearrecordTextView2);
|
||||
initRecordText();
|
||||
}
|
||||
|
||||
void initRecordText() {
|
||||
ArrayList<BatteryInfoBean> listBatteryInfo = AppCacheUtils.getInstance(this).getArrayListBatteryInfo();
|
||||
if (mIsShowRecordWithEnter) {
|
||||
String szRecordText = StringUtils.formatPCMListStringWithEnter(listBatteryInfo);
|
||||
mtvRecordText.setText(szRecordText);
|
||||
} else {
|
||||
String szRecordText = StringUtils.formatPCMListString(listBatteryInfo);
|
||||
mtvRecordText.setText(szRecordText);
|
||||
}
|
||||
}
|
||||
|
||||
public void onShowRecordWithEnter(View view) {
|
||||
Switch swShowRecordWithEnter = (Switch)view;
|
||||
mIsShowRecordWithEnter = swShowRecordWithEnter.isChecked();
|
||||
initRecordText();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
package cc.winboll.studio.powerbell.activities;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/06/22 14:15
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
|
||||
import cc.winboll.studio.libaes.views.AToolbar;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
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.utils.BackgroundPictureUtils;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
public class PixelPickerActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
||||
|
||||
public static final String TAG = "PixelPickerActivity";
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
private AToolbar mAToolbar;
|
||||
private ImageView imageView;
|
||||
private Bitmap originalBitmap;
|
||||
private TextView infoText;
|
||||
private ViewGroup imageContainer;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_pixelpicker);
|
||||
|
||||
|
||||
// 初始化工具栏
|
||||
mAToolbar = (AToolbar) findViewById(R.id.toolbar);
|
||||
setActionBar(mAToolbar);
|
||||
mAToolbar.setSubtitle(R.string.subtitle_activity_pixelpicker);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
mAToolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
imageView = findViewById(R.id.imageView);
|
||||
infoText = findViewById(R.id.infoText);
|
||||
imageContainer = findViewById(R.id.imageContainer);
|
||||
|
||||
// 从Intent获取图片路径并加载
|
||||
String imagePath = getIntent().getStringExtra("imagePath");
|
||||
if (imagePath != null) {
|
||||
loadImage(imagePath);
|
||||
} else {
|
||||
infoText.setText("未找到图片路径");
|
||||
}
|
||||
|
||||
// 设置图片点击事件
|
||||
imageContainer.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN && originalBitmap != null) {
|
||||
// 计算点击位置在图片上的实际坐标
|
||||
float touchX = event.getX();
|
||||
float touchY = event.getY();
|
||||
|
||||
int pixelX = -1, pixelY = -1;
|
||||
try {
|
||||
// 获取图片在容器中的实际位置和尺寸
|
||||
int[] imageLocation = new int[2];
|
||||
imageView.getLocationInWindow(imageLocation);
|
||||
int imageWidth = imageView.getWidth();
|
||||
int imageHeight = imageView.getHeight();
|
||||
|
||||
// 计算缩放比例
|
||||
float scaleX = (float) originalBitmap.getWidth() / imageWidth;
|
||||
float scaleY = (float) originalBitmap.getHeight() / imageHeight;
|
||||
|
||||
// 调整触摸坐标到图片坐标系
|
||||
float adjustedX = touchX - imageLocation[0];
|
||||
float adjustedY = touchY - imageLocation[1];
|
||||
|
||||
// 检查是否在图片范围内
|
||||
if (adjustedX >= 0 && adjustedX <= imageWidth && adjustedY >= 0 && adjustedY <= imageHeight) {
|
||||
// 计算实际像素坐标
|
||||
pixelX = (int) (adjustedX * scaleX);
|
||||
pixelY = (int) (adjustedY * scaleY);
|
||||
|
||||
// 再次检查像素坐标是否在有效范围内
|
||||
if (pixelX >= 0 && pixelX < originalBitmap.getWidth() &&
|
||||
pixelY >= 0 && pixelY < originalBitmap.getHeight()) {
|
||||
int pixelColor = originalBitmap.getPixel(pixelX, pixelY);
|
||||
showPixelDialog(pixelColor, pixelX, pixelY);
|
||||
} else {
|
||||
Toast.makeText(PixelPickerActivity.this, "像素坐标超出范围", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(PixelPickerActivity.this, "点击位置超出图片显示范围", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(PixelPickerActivity.this, "计算像素位置失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载图片
|
||||
*/
|
||||
private void loadImage(String imagePath) {
|
||||
try {
|
||||
File file = new File(imagePath);
|
||||
if (file.exists()) {
|
||||
// 解码图片
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inSampleSize = 1; // 加载原图
|
||||
originalBitmap = BitmapFactory.decodeStream(new FileInputStream(file), null, options);
|
||||
|
||||
if (originalBitmap != null) {
|
||||
imageView.setImageBitmap(originalBitmap);
|
||||
infoText.setText("图片已加载,点击获取像素值");
|
||||
} else {
|
||||
infoText.setText("图片加载失败");
|
||||
}
|
||||
} else {
|
||||
infoText.setText("图片文件不存在");
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
infoText.setText("图片文件未找到");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示像素对话框
|
||||
*/
|
||||
private void showPixelDialog(final int pixelColor, int x, int y) {
|
||||
final Dialog dialog = new Dialog(this);
|
||||
dialog.setContentView(R.layout.dialog_pixel);
|
||||
dialog.setCancelable(true);
|
||||
|
||||
// 设置像素颜色视图背景
|
||||
TextView colorView = dialog.findViewById(R.id.pixelColorView);
|
||||
colorView.setBackgroundColor(pixelColor);
|
||||
|
||||
// 显示颜色信息
|
||||
TextView infoText = dialog.findViewById(R.id.colorInfoText);
|
||||
String colorInfo = String.format(
|
||||
"RGB: (%d, %d, %d)\n" +
|
||||
"ARGB: #%08X\n" +
|
||||
"实际像素位置: (%d, %d)",
|
||||
Color.red(pixelColor),
|
||||
Color.green(pixelColor),
|
||||
Color.blue(pixelColor),
|
||||
pixelColor,
|
||||
x, y);
|
||||
infoText.setText(colorInfo);
|
||||
|
||||
// 设置确定按钮点击事件
|
||||
Button confirmButton = dialog.findViewById(R.id.confirmButton);
|
||||
confirmButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dialog.dismiss();
|
||||
// 可以在这里添加确定后的回调逻辑
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(PixelPickerActivity.this);
|
||||
BackgroundPictureBean bean = utils.getBackgroundPictureBean();
|
||||
bean.setPixelColor(pixelColor);
|
||||
utils.saveData();
|
||||
Toast.makeText(PixelPickerActivity.this, "已记录像素值", Toast.LENGTH_SHORT).show();
|
||||
setBackgroundColor();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
// 回收Bitmap资源
|
||||
if (originalBitmap != null && !originalBitmap.isRecycled()) {
|
||||
originalBitmap.recycle();
|
||||
originalBitmap = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setBackgroundColor() {
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(PixelPickerActivity.this);
|
||||
BackgroundPictureBean bean = utils.getBackgroundPictureBean();
|
||||
int nPixelColor = bean.getPixelColor();
|
||||
RelativeLayout mainLayout = findViewById(R.id.activitypixelpickerRelativeLayout1);
|
||||
mainLayout.setBackgroundColor(nPixelColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
setBackgroundColor();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
Intent intent = new Intent();
|
||||
intent.setClass(this, BackgroundPictureActivity.class);
|
||||
startActivity(intent);
|
||||
//GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), );
|
||||
return true;
|
||||
}
|
||||
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
Intent intent = new Intent();
|
||||
intent.setClass(this, BackgroundPictureActivity.class);
|
||||
startActivity(intent);
|
||||
//GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), BackgroundPictureActivity.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package cc.winboll.studio.powerbell.activities;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/06/19 20:35
|
||||
* @Describe 应用窗口基类
|
||||
*/
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
|
||||
import cc.winboll.studio.powerbell.BuildConfig;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
public abstract class WinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity {
|
||||
|
||||
public static final String TAG = "WinBoLLActivity";
|
||||
|
||||
protected TextView mTagView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
changeFullScreen(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
addVersionNameToContentView();
|
||||
}
|
||||
|
||||
protected void addVersionNameToContentView() {
|
||||
if (!isTagViewVisible()) {
|
||||
return;
|
||||
}
|
||||
if (mTagView == null) {
|
||||
mTagView = new TextView(this);
|
||||
mTagView.setTextColor(Color.GRAY);
|
||||
mTagView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
|
||||
mTagView.setText("MIMO SDK V" + BuildConfig.VERSION_NAME);
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
|
||||
FrameLayout frameLayout = findViewById(android.R.id.content);
|
||||
frameLayout.addView(mTagView, params);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isTagViewVisible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setupToolbar() {
|
||||
Toolbar mToolbar = findViewById(R.id.toolbar);
|
||||
if (mToolbar != null) {
|
||||
setSupportActionBar(mToolbar);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
//GlobalApplication.getWinBoLLActivityManager().add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
//GlobalApplication.getWinBoLLActivityManager().registeRemove(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
//GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class);
|
||||
return true;
|
||||
}
|
||||
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
//GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class);
|
||||
}
|
||||
|
||||
public void changeFullScreen(Activity activity) {
|
||||
Window window = activity.getWindow();
|
||||
if (window == null){
|
||||
return;
|
||||
}
|
||||
View decorView = window.getDecorView();
|
||||
if (decorView == null){
|
||||
return;
|
||||
}
|
||||
int flag = decorView.getSystemUiVisibility();
|
||||
flag |= View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
flag |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
|
||||
flag |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
|
||||
flag |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
|
||||
flag |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||
decorView.setSystemUiVisibility(flag);
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package cc.winboll.studio.powerbell.adapters;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/03/22 14:38:55
|
||||
* @Describe 电池报告数据适配器
|
||||
*/
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.adapters.BatteryAdapter;
|
||||
import cc.winboll.studio.powerbell.beans.BatteryData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BatteryAdapter extends RecyclerView.Adapter<BatteryAdapter.ViewHolder> {
|
||||
public static final String TAG = "BatteryAdapter";
|
||||
private List<BatteryData> dataList = new ArrayList<>();
|
||||
|
||||
public void updateData(List<BatteryData> newData) {
|
||||
dataList = newData;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_battery_report, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
BatteryData item = dataList.get(position);
|
||||
holder.tvLevel.setText(String.format("%d%%", item.getCurrentLevel()));
|
||||
holder.tvDischargeTime.setText("使用时间: " + item.getDischargeTime());
|
||||
holder.tvChargeTime.setText("充电时间: " + item.getChargeTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return dataList.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvLevel;
|
||||
TextView tvDischargeTime;
|
||||
TextView tvChargeTime;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
tvLevel = itemView.findViewById(R.id.tvLevel);
|
||||
tvDischargeTime = itemView.findViewById(R.id.tvDischargeTime);
|
||||
tvChargeTime = itemView.findViewById(R.id.tvChargeTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package cc.winboll.studio.powerbell.beans;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/04/29 17:24:53
|
||||
* @Describe 应用运行参数类
|
||||
*/
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class AppConfigBean extends BaseBean implements Serializable {
|
||||
|
||||
transient public static final String TAG = "AppConfigBean";
|
||||
|
||||
boolean isEnableUsegeReminder = false;
|
||||
int usegeReminderValue = 45;
|
||||
boolean isEnableChargeReminder = false;
|
||||
int chargeReminderValue = 100;
|
||||
// 铃声提醒间隔时间。.
|
||||
int reminderIntervalTime = 5000;
|
||||
// 电池是否正在充电。
|
||||
boolean isCharging = false;
|
||||
// 电池当前电量。.
|
||||
int currentValue = -1;
|
||||
|
||||
public AppConfigBean() {
|
||||
setChargeReminderValue(100);
|
||||
setIsEnableChargeReminder(false);
|
||||
setUsegeReminderValue(10);
|
||||
setIsEnableUsegeReminder(false);
|
||||
setReminderIntervalTime(5000);
|
||||
}
|
||||
|
||||
public void setReminderIntervalTime(int reminderIntervalTime) {
|
||||
this.reminderIntervalTime = reminderIntervalTime;
|
||||
}
|
||||
|
||||
public int getReminderIntervalTime() {
|
||||
return reminderIntervalTime;
|
||||
}
|
||||
|
||||
public void setIsCharging(boolean isCharging) {
|
||||
this.isCharging = isCharging;
|
||||
}
|
||||
|
||||
public boolean isCharging() {
|
||||
return isCharging;
|
||||
}
|
||||
|
||||
public void setCurrentValue(int currentValue) {
|
||||
this.currentValue = currentValue;
|
||||
}
|
||||
|
||||
public int getCurrentValue() {
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
public void setIsEnableUsegeReminder(boolean isEnableUsegeReminder) {
|
||||
this.isEnableUsegeReminder = isEnableUsegeReminder;
|
||||
}
|
||||
|
||||
public boolean isEnableUsegeReminder() {
|
||||
return isEnableUsegeReminder;
|
||||
}
|
||||
|
||||
public void setUsegeReminderValue(int usegeReminderValue) {
|
||||
this.usegeReminderValue = usegeReminderValue;
|
||||
}
|
||||
|
||||
public int getUsegeReminderValue() {
|
||||
return usegeReminderValue;
|
||||
}
|
||||
|
||||
public void setIsEnableChargeReminder(boolean isEnableChargeReminder) {
|
||||
this.isEnableChargeReminder = isEnableChargeReminder;
|
||||
}
|
||||
|
||||
public boolean isEnableChargeReminder() {
|
||||
return isEnableChargeReminder;
|
||||
}
|
||||
|
||||
public void setChargeReminderValue(int chargeReminderValue) {
|
||||
this.chargeReminderValue = chargeReminderValue;
|
||||
}
|
||||
|
||||
public int getChargeReminderValue() {
|
||||
return chargeReminderValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return AppConfigBean.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
AppConfigBean bean = this;
|
||||
jsonWriter.name("isEnableUsegeReminder").value(bean.isEnableUsegeReminder());
|
||||
jsonWriter.name("usegeReminderValue").value(bean.getUsegeReminderValue());
|
||||
jsonWriter.name("isEnableChargeReminder").value(bean.isEnableChargeReminder());
|
||||
jsonWriter.name("chargeReminderValue").value(bean.getChargeReminderValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
AppConfigBean bean = new AppConfigBean();
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (name.equals("isEnableUsegeReminder")) {
|
||||
bean.setIsEnableUsegeReminder(jsonReader.nextBoolean());
|
||||
} else if (name.equals("usegeReminderValue")) {
|
||||
bean.setUsegeReminderValue(jsonReader.nextInt());
|
||||
} else if (name.equals("isEnableChargeReminder")) {
|
||||
bean.setIsEnableChargeReminder(jsonReader.nextBoolean());
|
||||
} else if (name.equals("chargeReminderValue")) {
|
||||
bean.setChargeReminderValue(jsonReader.nextInt());
|
||||
} else {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package cc.winboll.studio.powerbell.beans;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/03/22 14:30:51
|
||||
* @Describe 电池报告数据模型
|
||||
*/
|
||||
public class BatteryData {
|
||||
|
||||
public static final String TAG = "BatteryData";
|
||||
|
||||
private int currentLevel;
|
||||
private String dischargeTime;
|
||||
private String chargeTime;
|
||||
|
||||
public BatteryData(int currentLevel, String dischargeTime, String chargeTime) {
|
||||
this.currentLevel = currentLevel;
|
||||
this.dischargeTime = dischargeTime;
|
||||
this.chargeTime = chargeTime;
|
||||
}
|
||||
|
||||
public int getCurrentLevel() { return currentLevel; }
|
||||
public String getDischargeTime() { return dischargeTime; }
|
||||
public String getChargeTime() { return chargeTime; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package cc.winboll.studio.powerbell.beans;
|
||||
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class BatteryInfoBean extends BaseBean implements Serializable {
|
||||
|
||||
public static final String TAG = "BatteryInfoBean";
|
||||
|
||||
// 记录电量的时间戳
|
||||
long timeStamp;
|
||||
// 电量值
|
||||
int battetyValue;
|
||||
|
||||
public BatteryInfoBean() {
|
||||
this.timeStamp = 0;
|
||||
this.battetyValue = 0;
|
||||
}
|
||||
|
||||
public BatteryInfoBean(long timeStamp, int battetyValue) {
|
||||
this.timeStamp = timeStamp;
|
||||
this.battetyValue = battetyValue;
|
||||
}
|
||||
|
||||
public void setTimeStamp(long timeStamp) {
|
||||
this.timeStamp = timeStamp;
|
||||
}
|
||||
|
||||
public long getTimeStamp() {
|
||||
return timeStamp;
|
||||
}
|
||||
|
||||
public void setBattetyValue(int battetyValue) {
|
||||
this.battetyValue = battetyValue;
|
||||
}
|
||||
|
||||
public int getBattetyValue() {
|
||||
return battetyValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return BatteryInfoBean.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
BatteryInfoBean bean = this;
|
||||
jsonWriter.name("timeStamp").value(bean.getTimeStamp());
|
||||
jsonWriter.name("battetyValue").value(bean.getBattetyValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
BatteryInfoBean bean = new BatteryInfoBean();
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (name.equals("timeStamp")) {
|
||||
bean.setTimeStamp(jsonReader.nextLong());
|
||||
} else if (name.equals("battetyValue")) {
|
||||
bean.setBattetyValue(jsonReader.nextInt());
|
||||
} else {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package cc.winboll.studio.powerbell.beans;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/07/18 07:06:07
|
||||
* @Describe 服务控制参数
|
||||
*/
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ControlCenterServiceBean extends BaseBean {
|
||||
|
||||
public static final String TAG = "ControlCenterServiceBean";
|
||||
|
||||
boolean isEnableService = false;
|
||||
|
||||
public ControlCenterServiceBean() {
|
||||
this.isEnableService = false;
|
||||
}
|
||||
|
||||
public ControlCenterServiceBean(boolean isEnableService) {
|
||||
this.isEnableService = isEnableService;
|
||||
}
|
||||
|
||||
public void setIsEnableService(boolean isEnableService) {
|
||||
this.isEnableService = isEnableService;
|
||||
}
|
||||
|
||||
public boolean isEnableService() {
|
||||
return isEnableService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return ControlCenterServiceBean.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
ControlCenterServiceBean bean = this;
|
||||
jsonWriter.name("isEnableService").value(bean.isEnableService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
ControlCenterServiceBean bean = new ControlCenterServiceBean();
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (name.equals("isEnableService")) {
|
||||
bean.setIsEnableService(jsonReader.nextBoolean());
|
||||
} else {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package cc.winboll.studio.powerbell.beans;
|
||||
|
||||
// 应用消息结构
|
||||
//
|
||||
public class NotificationMessage {
|
||||
|
||||
String Title;
|
||||
String Content;
|
||||
String RemindMSG;
|
||||
|
||||
public NotificationMessage(String title, String content) {
|
||||
Title = title;
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public void setRemindMSG(String remindMSG) {
|
||||
RemindMSG = remindMSG;
|
||||
}
|
||||
|
||||
public String getRemindMSG() {
|
||||
return RemindMSG;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
Title = title;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return Title;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return Content;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package cc.winboll.studio.powerbell.dialogs;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.MainActivity;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity;
|
||||
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils;
|
||||
import cc.winboll.studio.powerbell.utils.FileUtils;
|
||||
import cc.winboll.studio.powerbell.utils.UriUtil;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/04/25 16:27:53
|
||||
* @Describe 背景图片的接收分享文件后的预览对话框
|
||||
*/
|
||||
public class BackgroundPicturePreviewDialog extends Dialog {
|
||||
|
||||
public static final String TAG = "BackgroundPicturePreviewDialog";
|
||||
|
||||
Context mContext;
|
||||
BackgroundPictureUtils mBackgroundPictureUtils;
|
||||
Button dialogbackgroundpicturepreviewButton1;
|
||||
Button dialogbackgroundpicturepreviewButton2;
|
||||
String mszPreReceivedFileName;
|
||||
|
||||
public BackgroundPicturePreviewDialog(Context context) {
|
||||
super(context);
|
||||
setContentView(R.layout.dialog_backgroundpicturepreview);
|
||||
initEnv();
|
||||
|
||||
mContext = context;
|
||||
mBackgroundPictureUtils = ((BackgroundPictureActivity)context).mBackgroundPictureUtils;
|
||||
|
||||
ImageView imageView = findViewById(R.id.dialogbackgroundpicturepreviewImageView1);
|
||||
copyAndViewRecivePicture(imageView);
|
||||
|
||||
dialogbackgroundpicturepreviewButton1 = findViewById(R.id.dialogbackgroundpicturepreviewButton1);
|
||||
dialogbackgroundpicturepreviewButton1.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
// 不使用分享到的图片
|
||||
// 跳转到主窗口
|
||||
Intent i = new Intent(mContext, MainActivity.class);
|
||||
mContext.startActivity(i);
|
||||
}
|
||||
});
|
||||
|
||||
dialogbackgroundpicturepreviewButton2 = findViewById(R.id.dialogbackgroundpicturepreviewButton2);
|
||||
dialogbackgroundpicturepreviewButton2.setOnClickListener(new View.OnClickListener(){
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// 使用分享到的图片
|
||||
//
|
||||
//LogUtils.d(TAG, "mszReceivedFileName : " + mszReceivedFileName);
|
||||
((IOnRecivedPictureListener)mContext).onAcceptRecivedPicture(mszPreReceivedFileName);
|
||||
// 关闭对话框
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void initEnv() {
|
||||
LogUtils.d(TAG, "initEnv()");
|
||||
mszPreReceivedFileName = "PreReceived.data";
|
||||
}
|
||||
|
||||
void copyAndViewRecivePicture(ImageView imageView) {
|
||||
//AppConfigUtils appConfigUtils = AppConfigUtils.getInstance((GlobalApplication)mContext.getApplicationContext());
|
||||
BackgroundPictureActivity activity = ((BackgroundPictureActivity)mContext);
|
||||
|
||||
//取出文件uri
|
||||
Uri uri = activity.getIntent().getData();
|
||||
if (uri == null) {
|
||||
uri = activity.getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
}
|
||||
//获取文件真实地址
|
||||
String szSrcImage = UriUtil.getFilePathFromUri(mContext, uri);
|
||||
if (TextUtils.isEmpty(szSrcImage)) {
|
||||
Toast.makeText(mContext, "接收到的文件为空。", Toast.LENGTH_SHORT).show();
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
File fSrcImage = new File(szSrcImage);
|
||||
//mszPreReceivedFileName = DateUtils.getDateNowString() + "-" + fSrcImage.getName();
|
||||
File mfPreReceivedPhoto = new File(activity.mBackgroundPictureUtils.getBackgroundDir(), mszPreReceivedFileName);
|
||||
// 复制源图片到剪裁文件
|
||||
try {
|
||||
FileUtils.copyFileUsingFileChannels(fSrcImage, mfPreReceivedPhoto);
|
||||
LogUtils.d(TAG, "copyFileUsingFileChannels");
|
||||
Drawable drawable = Drawable.createFromPath(mfPreReceivedPhoto.getPath());
|
||||
imageView.setBackground(drawable);
|
||||
//LogUtils.d(TAG, "mszPreReceivedFileName : " + mszPreReceivedFileName);
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 创建图片背景图片目录
|
||||
//
|
||||
boolean createBackgroundFolder2(String szBackgroundFolder) {
|
||||
// 文件路径参数为空值或无效值时返回false.
|
||||
if (szBackgroundFolder == null | szBackgroundFolder.equals("")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, "Background Folder Is : " + szBackgroundFolder);
|
||||
File f = new File(szBackgroundFolder);
|
||||
if (f.exists()) {
|
||||
if (f.isDirectory()) {
|
||||
return true;
|
||||
} else {
|
||||
// 工作路径不是一个目录
|
||||
LogUtils.d(TAG, "createImageWorkFolder() error : szImageCacheFolder isDirectory return false. -->" + szBackgroundFolder);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return f.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
public interface IOnRecivedPictureListener {
|
||||
void onAcceptRecivedPicture(String szBackgroundFileName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
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.utils.PictureUtils;
|
||||
import cc.winboll.studio.powerbell.views.BackgroundView;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @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 previewFilePath;
|
||||
|
||||
// 按钮点击回调接口(Java7 接口实现)
|
||||
public interface OnDialogClickListener {
|
||||
void onConfirm(); // 确认按钮点击
|
||||
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:
|
||||
// 图片加载成功,获取文件路径并设置背景
|
||||
String filePath = (String) msg.obj;
|
||||
setBackgroundFromPath(filePath);
|
||||
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);
|
||||
|
||||
// 设置按钮点击事件
|
||||
setButtonClickListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮点击监听
|
||||
*/
|
||||
private void setButtonClickListeners() {
|
||||
// 取消按钮:关闭对话框 + 回调外部
|
||||
btnCancel.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d("NetworkBackgroundDialog", "取消按钮点击");
|
||||
dismiss(); // 关闭对话框
|
||||
if (listener != null) {
|
||||
listener.onCancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 确认按钮:关闭对话框 + 回调外部
|
||||
btnConfirm.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d("NetworkBackgroundDialog", "确认按钮点击");
|
||||
// 确定预览背景资源
|
||||
bvBackgroundPreview.saveToBackgroundSources(previewFilePath);
|
||||
|
||||
dismiss(); // 关闭对话框
|
||||
if (listener != null) {
|
||||
listener.onConfirm();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 图片预览按钮:预览输入框地址图片
|
||||
btnPreview.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d("NetworkBackgroundDialog", "确认预览点击");
|
||||
downloadImageToAlbumAndPreview();
|
||||
/*String url = etURL.getText().toString().trim();
|
||||
if (url.isEmpty()) {
|
||||
ToastUtils.show("请输入图片链接");
|
||||
return;
|
||||
}
|
||||
ImageDownloader.getInstance(mContext).downloadImage(url, mDownloadCallback);*/
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件路径设置 BackgroundView 背景(主线程调用)
|
||||
* @param filePath 图片文件路径
|
||||
*/
|
||||
private void setBackgroundFromPath(String filePath) {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
File imageFile = new File(filePath);
|
||||
if (!imageFile.exists()) {
|
||||
LogUtils.e(TAG, "图片文件不存在:" + filePath);
|
||||
ToastUtils.show("Test");
|
||||
//bvBackgroundPreview.setBackgroundResource(R.drawable.ic_launcher);
|
||||
return;
|
||||
}
|
||||
|
||||
// 预览背景
|
||||
previewFilePath = filePath;
|
||||
bvBackgroundPreview.previewBackgroundImage(previewFilePath);
|
||||
|
||||
LogUtils.d(TAG, "图片预览成功:" + filePath);
|
||||
|
||||
} 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;
|
||||
}
|
||||
|
||||
/*ImageDownloader.DownloadCallback mDownloadCallback = new ImageDownloader.DownloadCallback() {
|
||||
@Override
|
||||
public void onSuccess(String filePath) {
|
||||
ToastUtils.show("图片下载成功:" + filePath);
|
||||
LogUtils.d(TAG, filePath);
|
||||
// 发送消息到主线程,携带图片路径
|
||||
Message successMsg = mUiHandler.obtainMessage(MSG_IMAGE_LOAD_SUCCESS, filePath);
|
||||
mUiHandler.sendMessage(successMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String errorMsg) {
|
||||
ToastUtils.show("下载失败:" + errorMsg);
|
||||
LogUtils.e(TAG, errorMsg);
|
||||
// 发送图片加载失败消息
|
||||
mUiHandler.sendEmptyMessage(MSG_IMAGE_LOAD_FAILED);
|
||||
}
|
||||
};*/
|
||||
|
||||
void downloadImageToAlbumAndPreview() {
|
||||
//String imgUrl = "https://example.com/test.jpg";
|
||||
String imgUrl = etURL.getText().toString();
|
||||
PictureUtils.downloadImageToAlbum(mContext, imgUrl, new PictureUtils.DownloadCallback(){
|
||||
@Override
|
||||
public void onSuccess(String savePath) {
|
||||
ToastUtils.show("下载成功:" + savePath);
|
||||
// 发送消息到主线程,携带图片路径
|
||||
Message successMsg = mUiHandler.obtainMessage(MSG_IMAGE_LOAD_SUCCESS, savePath);
|
||||
mUiHandler.sendMessage(successMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
ToastUtils.show("下载失败:" + e.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package cc.winboll.studio.powerbell.dialogs;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/06/10 19:32:55
|
||||
* @Describe 用户确定与否选择框
|
||||
*/
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
public class YesNoAlertDialog {
|
||||
|
||||
public static final String TAG = "YesNoAlertDialog";
|
||||
|
||||
public static void show(Context context, String szTitle, String szMessage, final OnDialogResultListener listener) {
|
||||
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
|
||||
context);
|
||||
|
||||
// set title
|
||||
alertDialogBuilder.setTitle(szTitle);
|
||||
|
||||
// set dialog message
|
||||
alertDialogBuilder
|
||||
.setMessage(szMessage)
|
||||
.setCancelable(true)
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener(){
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
listener.onNo();
|
||||
}
|
||||
})
|
||||
.setPositiveButton("YES", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
// if this button is clicked, close
|
||||
// current activity
|
||||
listener.onYes();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("NO", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
// if this button is clicked, just close
|
||||
// the dialog box and do nothing
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
// create alert dialog
|
||||
AlertDialog alertDialog = alertDialogBuilder.create();
|
||||
|
||||
// show it
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
public interface OnDialogResultListener {
|
||||
abstract void onYes();
|
||||
abstract void onNo();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
package cc.winboll.studio.powerbell.fragments;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
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.VerticalSeekBar;
|
||||
|
||||
public class MainViewFragment extends Fragment {
|
||||
|
||||
public static final String TAG = "MainViewFragment";
|
||||
|
||||
public static final int MSG_RELOAD_APPCONFIG = 0;
|
||||
public static final int MSG_CURRENTVALUEBATTERY = 1;
|
||||
|
||||
static MainViewFragment _mMainViewFragment;
|
||||
AppConfigUtils mAppConfigUtils;
|
||||
View mView;
|
||||
Drawable mDrawableFrame;
|
||||
LinearLayout mllLeftSeekBar;
|
||||
LinearLayout mllRightSeekBar;
|
||||
CheckBox mcbIsEnableChargeReminder;
|
||||
CheckBox mcbIsEnableUsegeReminder;
|
||||
Switch mswIsEnableService;
|
||||
TextView mtvTips;
|
||||
|
||||
// 背景布局
|
||||
//LinearLayout mLinearLayoutloadBackground;
|
||||
|
||||
// 现在电量图示
|
||||
BatteryDrawable mCurrentValueBatteryDrawable;
|
||||
// 现在充电提醒电量图示
|
||||
BatteryDrawable mChargeReminderValueBatteryDrawable;
|
||||
// 现在耗电提醒电量图示
|
||||
BatteryDrawable mUsegeReminderValueBatteryDrawable;
|
||||
|
||||
ImageView mCurrentValueBatteryImageView;
|
||||
ImageView mChargeReminderValueBatteryImageView;
|
||||
ImageView mUsegeReminderValueBatteryImageView;
|
||||
|
||||
VerticalSeekBar mChargeReminderSeekBar;
|
||||
ChargeReminderSeekBarChangeListener mChargeReminderSeekBarChangeListener;
|
||||
TextView mtvChargeReminderValue;
|
||||
|
||||
|
||||
VerticalSeekBar mUsegeReminderSeekBar;
|
||||
UsegeReminderSeekBarChangeListener mUsegeReminderSeekBarChangeListener;
|
||||
TextView mtvUsegeReminderValue;
|
||||
CheckBox mcbUsegeReminderValue;
|
||||
TextView mtvCurrentValue;
|
||||
BackgroundView bvPreviewBackground;
|
||||
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
mView = inflater.inflate(R.layout.fragment_mainview, container, false);
|
||||
_mMainViewFragment = MainViewFragment.this;
|
||||
mAppConfigUtils = App.getAppConfigUtils(getActivity());
|
||||
|
||||
// 获取指定ID的View实例
|
||||
bvPreviewBackground = mView.findViewById(R.id.fragmentmainviewBackgroundView1);
|
||||
/*final View mainImageView = mView.findViewById(R.id.fragmentmainviewImageView1);
|
||||
|
||||
// 注册OnGlobalLayoutListener
|
||||
mainImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
// 获取宽度和高度
|
||||
int width = mainImageView.getMeasuredWidth();
|
||||
int height = mainImageView.getMeasuredHeight();
|
||||
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(getActivity());
|
||||
BackgroundPictureBean bean = utils.loadBackgroundPictureBean();
|
||||
bean.setBackgroundWidth(width);
|
||||
bean.setBackgroundHeight(height);
|
||||
utils.saveData();
|
||||
// 移除监听器以避免内存泄漏
|
||||
mainImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
}
|
||||
});*/
|
||||
|
||||
mDrawableFrame = getActivity().getDrawable(R.drawable.bg_frame);
|
||||
mllLeftSeekBar = (LinearLayout) mView.findViewById(R.id.fragmentmainviewLinearLayout1);
|
||||
mllRightSeekBar = (LinearLayout) mView.findViewById(R.id.fragmentmainviewLinearLayout2);
|
||||
|
||||
// 初始化充电电量提醒设置控件
|
||||
mtvChargeReminderValue = (TextView) mView.findViewById(R.id.fragmentandroidviewTextView2);
|
||||
mChargeReminderSeekBar = (VerticalSeekBar) mView.findViewById(R.id.fragmentandroidviewVerticalSeekBar1);
|
||||
mcbIsEnableChargeReminder = mView.findViewById(R.id.fragmentmainviewCheckBox1);
|
||||
|
||||
// 初始化耗电电量提醒设置控件
|
||||
mtvUsegeReminderValue = (TextView) mView.findViewById(R.id.fragmentandroidviewTextView3);
|
||||
mUsegeReminderSeekBar = (VerticalSeekBar) mView.findViewById(R.id.fragmentandroidviewVerticalSeekBar2);
|
||||
mcbIsEnableUsegeReminder = mView.findViewById(R.id.fragmentmainviewCheckBox2);
|
||||
|
||||
// 初始化现在电量显示控件
|
||||
mtvCurrentValue = (TextView) mView.findViewById(R.id.fragmentandroidviewTextView4);
|
||||
|
||||
// 初始化服务总开关
|
||||
mswIsEnableService = (Switch) mView.findViewById(R.id.fragmentandroidviewSwitch1);
|
||||
mtvTips = mView.findViewById(R.id.fragmentandroidviewTextView1);
|
||||
|
||||
// 设置视图显示数据
|
||||
setViewData();
|
||||
// 设置视图控件响应
|
||||
setViewListener();
|
||||
|
||||
// 注册一个广播接收
|
||||
//mMainActivityReceiver = new MainActivityReceiver(this);
|
||||
//mMainActivityReceiver.registerAction();
|
||||
|
||||
// 启动的时候检查一下服务
|
||||
if (mAppConfigUtils.getIsEnableService()
|
||||
&& ServiceUtils.isServiceAlive(getActivity(), ControlCenterService.class.getName()) == false) {
|
||||
// 如果配置了服务启动,服务没有启动
|
||||
// 就启动服务
|
||||
Intent intent = new Intent(getActivity(), ControlCenterService.class);
|
||||
getActivity().startForegroundService(intent);
|
||||
}
|
||||
|
||||
return mView;
|
||||
}
|
||||
|
||||
void setViewData() {
|
||||
int nChargeReminderValue = mAppConfigUtils.getChargeReminderValue();
|
||||
int nUsegeReminderValue = mAppConfigUtils.getUsegeReminderValue();
|
||||
int nCurrentValue = mAppConfigUtils.getCurrentValue();
|
||||
|
||||
mllLeftSeekBar.setBackground(mDrawableFrame);
|
||||
mllRightSeekBar.setBackground(mDrawableFrame);
|
||||
|
||||
// 初始化电量图
|
||||
mCurrentValueBatteryDrawable = new BatteryDrawable(getActivity().getColor(R.color.colorCurrent));
|
||||
mCurrentValueBatteryDrawable.setValue(mAppConfigUtils.getCurrentValue());
|
||||
mCurrentValueBatteryImageView = mView.findViewById(R.id.fragmentandroidviewImageView1);
|
||||
mCurrentValueBatteryImageView.setImageDrawable(mCurrentValueBatteryDrawable);
|
||||
|
||||
// 初始化充电电量提醒图
|
||||
mChargeReminderValueBatteryDrawable = new BatteryDrawable(getActivity().getColor(R.color.colorCharge));
|
||||
mChargeReminderValueBatteryDrawable.setValue(nChargeReminderValue);
|
||||
mChargeReminderValueBatteryImageView = mView.findViewById(R.id.fragmentandroidviewImageView3);
|
||||
mChargeReminderValueBatteryImageView.setImageDrawable(mChargeReminderValueBatteryDrawable);
|
||||
|
||||
// 初始化耗电电量提醒图
|
||||
mUsegeReminderValueBatteryDrawable = new BatteryDrawable(getActivity().getColor(R.color.colorUsege));
|
||||
mUsegeReminderValueBatteryDrawable.setValue(nUsegeReminderValue);
|
||||
mUsegeReminderValueBatteryImageView = mView.findViewById(R.id.fragmentandroidviewImageView2);
|
||||
mUsegeReminderValueBatteryImageView.setImageDrawable(mUsegeReminderValueBatteryDrawable);
|
||||
|
||||
// 初始化充电电量提醒设置控件
|
||||
mtvChargeReminderValue.setTextColor(getActivity().getColor(R.color.colorCharge));
|
||||
//LogUtils.d(TAG, "Color.YELLOW is " + Integer.toString(mApplication.getColor(R.color.colorCharge)));
|
||||
mtvChargeReminderValue.setText(Integer.toString(nChargeReminderValue) + "%");
|
||||
mChargeReminderSeekBar.setProgress(nChargeReminderValue);
|
||||
mcbIsEnableChargeReminder.setChecked(mAppConfigUtils.getIsEnableChargeReminder());
|
||||
|
||||
// 初始化耗电电量提醒设置控件
|
||||
mtvUsegeReminderValue.setTextColor(getActivity().getColor(R.color.colorUsege));
|
||||
mtvUsegeReminderValue.setText(Integer.toString(nUsegeReminderValue) + "%");
|
||||
mUsegeReminderSeekBar.setProgress(nUsegeReminderValue);
|
||||
mcbIsEnableUsegeReminder.setChecked(mAppConfigUtils.getIsEnableUsegeReminder());
|
||||
|
||||
// 初始化现在电量显示控件
|
||||
mtvCurrentValue.setTextColor(getActivity().getColor(R.color.colorCurrent));
|
||||
mtvCurrentValue.setText(Integer.toString(nCurrentValue) + "%");
|
||||
|
||||
// 初始化服务总开关
|
||||
mswIsEnableService.setChecked(mAppConfigUtils.getIsEnableService());
|
||||
if (mAppConfigUtils.getIsEnableService()) {
|
||||
//LogUtils.d(TAG, "mApplication.getIsEnableService() " + Boolean.toString(mAppConfigUtils.getIsEnableService()));
|
||||
ControlCenterService.startControlCenterService(getActivity());
|
||||
} else {
|
||||
//LogUtils.d(TAG, "mApplication.getIsEnableService() " + Boolean.toString(mAppConfigUtils.getIsEnableService()));
|
||||
ControlCenterService.stopControlCenterService(getActivity());
|
||||
}
|
||||
mswIsEnableService.setText(getString(R.string.txt_aboveswitch));
|
||||
mtvTips.setText(getString(R.string.txt_aboveswitchtips));
|
||||
|
||||
}
|
||||
|
||||
void setViewListener() {
|
||||
// 初始化充电电量提醒设置控件
|
||||
mChargeReminderSeekBarChangeListener = new ChargeReminderSeekBarChangeListener();
|
||||
mChargeReminderSeekBar.setOnSeekBarChangeListener(mChargeReminderSeekBarChangeListener);
|
||||
mcbIsEnableChargeReminder.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "setIsEnableChargeReminder");
|
||||
mAppConfigUtils.setIsEnableChargeReminder(mcbIsEnableChargeReminder.isChecked());
|
||||
//ControlCenterService.updateIsEnableChargeReminder(mcbIsEnableChargeReminder.isChecked());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 初始化耗电电量提醒设置控件
|
||||
mUsegeReminderSeekBarChangeListener = new UsegeReminderSeekBarChangeListener();
|
||||
mUsegeReminderSeekBar.setOnSeekBarChangeListener(mUsegeReminderSeekBarChangeListener);
|
||||
mcbIsEnableUsegeReminder.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "setIsEnableUsegeReminder");
|
||||
mAppConfigUtils.setIsEnableUsegeReminder(mcbIsEnableUsegeReminder.isChecked());
|
||||
//ControlCenterService.updateIsEnableUsegeReminder(mcbIsEnableUsegeReminder.isChecked());
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化服务总开关
|
||||
mswIsEnableService.setOnClickListener(new CompoundButton.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mAppConfigUtils.setIsEnableService(getActivity(), mswIsEnableService.isChecked());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void setCurrentValueBattery(int value) {
|
||||
//LogUtils.d(TAG, "setCurrentValueBattery");
|
||||
mtvCurrentValue.setText(Integer.toString(value) + "%");
|
||||
mCurrentValueBatteryDrawable.setValue(value);
|
||||
mCurrentValueBatteryDrawable.invalidateSelf();
|
||||
}
|
||||
|
||||
class ChargeReminderSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
//LogUtils.d(TAG, "call onProgressChanged");
|
||||
int nChargeReminderValue = progress;
|
||||
mtvChargeReminderValue.setText(Integer.toString(nChargeReminderValue) + "%");
|
||||
mChargeReminderValueBatteryDrawable.setValue(nChargeReminderValue);
|
||||
mChargeReminderValueBatteryDrawable.invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
//LogUtils.d(TAG, "call onStartTrackingTouch");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
//LogUtils.d(TAG, "call onStopTrackingTouch");
|
||||
//取得当前进度条的刻度
|
||||
int nChargeReminderValue = ((VerticalSeekBar)seekBar)._mnProgress;
|
||||
|
||||
mAppConfigUtils.setChargeReminderValue(nChargeReminderValue);
|
||||
mtvChargeReminderValue.setText(Integer.toString(nChargeReminderValue) + "%");
|
||||
//ControlCenterService.updateChargeReminderValue(nChargeReminderValue);
|
||||
}
|
||||
}
|
||||
|
||||
class UsegeReminderSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
//LogUtils.d(TAG, "call onProgressChanged");
|
||||
int nUsegeReminderValue = progress;
|
||||
mtvUsegeReminderValue.setText(Integer.toString(nUsegeReminderValue) + "%");
|
||||
mUsegeReminderValueBatteryDrawable.setValue(nUsegeReminderValue);
|
||||
mUsegeReminderValueBatteryDrawable.invalidateSelf();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
//LogUtils.d(TAG, "call onStartTrackingTouch");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
//LogUtils.d(TAG, "call onStopTrackingTouch");
|
||||
//取得当前进度条的刻度
|
||||
int nUsegeReminderValue = ((VerticalSeekBar)seekBar)._mnProgress;
|
||||
LogUtils.d(TAG, "nUsegeReminderValue is " + Integer.toString(nUsegeReminderValue));
|
||||
//LogUtils.d(TAG, "mPowerReminder is " + mApplication);
|
||||
mAppConfigUtils.setUsegeReminderValue(nUsegeReminderValue);
|
||||
//LogUtils.d(TAG, "opopopopopopopop");
|
||||
mtvUsegeReminderValue.setText(Integer.toString(nUsegeReminderValue) + "%");
|
||||
//ControlCenterService.updateUsegeReminderValue(nUsegeReminderValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void reloadBackground() {
|
||||
bvPreviewBackground.reloadBackgroundImage();
|
||||
// BackgroundPictureBean bean = BackgroundPictureUtils.getInstance(getActivity()).getBackgroundPictureBean();
|
||||
// ImageView imageView = mView.findViewById(R.id.fragmentmainviewImageView1);
|
||||
// String szBackgroundFilePath = BackgroundPictureUtils.getInstance(getActivity()).getBackgroundDir() + BackgroundPictureActivity.getBackgroundFileName();
|
||||
// File fBackgroundFilePath = new File(szBackgroundFilePath);
|
||||
// LogUtils.d(TAG, "szBackgroundFilePath : " + szBackgroundFilePath);
|
||||
// LogUtils.d(TAG, String.format("fBackgroundFilePath.exists() %s", fBackgroundFilePath.exists()));
|
||||
// if (bean.isUseBackgroundFile() && fBackgroundFilePath.exists()) {
|
||||
// Drawable drawableBackground = Drawable.createFromPath(szBackgroundFilePath);
|
||||
// //drawableBackground.setAlpha(120);
|
||||
// imageView.setImageDrawable(drawableBackground);
|
||||
// } else {
|
||||
// Drawable drawableBackground = getActivity().getDrawable(R.drawable.blank10x10);
|
||||
// //drawableBackground.setAlpha(120);
|
||||
// imageView.setImageDrawable(drawableBackground);
|
||||
// }
|
||||
}
|
||||
|
||||
Handler mHandler = new Handler(){
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_RELOAD_APPCONFIG : {
|
||||
setViewData();
|
||||
break;
|
||||
}
|
||||
case MSG_CURRENTVALUEBATTERY : {
|
||||
setCurrentValueBattery(msg.arg1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static void relaodAppConfigs() {
|
||||
if (_mMainViewFragment != null) {
|
||||
Handler handler = _mMainViewFragment.mHandler;
|
||||
handler.sendMessage(handler.obtainMessage(MSG_RELOAD_APPCONFIG));
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendMsgCurrentValueBattery(int value) {
|
||||
if (_mMainViewFragment != null) {
|
||||
Handler handler = _mMainViewFragment.mHandler;
|
||||
Message msg = handler.obtainMessage(MSG_CURRENTVALUEBATTERY);
|
||||
msg.arg1 = value;
|
||||
handler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package cc.winboll.studio.powerbell.handlers;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class ControlCenterServiceHandler extends Handler {
|
||||
public static final String TAG = ControlCenterServiceHandler.class.getSimpleName();
|
||||
|
||||
public static final int MSG_REMIND_TEXT = 0;
|
||||
|
||||
WeakReference<ControlCenterService> serviceWeakReference;
|
||||
public ControlCenterServiceHandler(ControlCenterService service) {
|
||||
serviceWeakReference = new WeakReference<ControlCenterService>(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_REMIND_TEXT: // 处理下载完成消息,更新UI
|
||||
{
|
||||
// 显示提醒消息
|
||||
//
|
||||
//LogUtils.d(TAG, "显示提醒消息");
|
||||
ControlCenterService controlCenterService = serviceWeakReference.get();
|
||||
if (controlCenterService != null) {
|
||||
//LogUtils.d(TAG, ((NotificationMessage)msg.obj).getTitle());
|
||||
//LogUtils.d(TAG, ((NotificationMessage)msg.obj).getContent());
|
||||
controlCenterService.appenRemindMSG((String)msg.obj);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package cc.winboll.studio.powerbell.receivers;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.beans.AppConfigBean;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
import cc.winboll.studio.powerbell.utils.BatteryUtils;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class ControlCenterServiceReceiver extends BroadcastReceiver {
|
||||
public static final String TAG = ControlCenterServiceReceiver.class.getSimpleName();
|
||||
|
||||
public static final String ACTION_UPDATE_SERVICENOTIFICATION = ControlCenterServiceReceiver.class.getName() + ".ACTION_UPDATE_NOTIFICATION";
|
||||
public static final String ACTION_START_REMINDTHREAD = ControlCenterServiceReceiver.class.getName() + ".ACTION_UPDATE_REMINDTHREAD";
|
||||
|
||||
WeakReference<ControlCenterService> mwrService;
|
||||
// 存储电量指示值,
|
||||
// 用于校验电量消息时的电量变化
|
||||
static volatile int _mnTheQuantityOfElectricityOld = -1;
|
||||
static volatile boolean _mIsCharging = false;
|
||||
|
||||
public ControlCenterServiceReceiver(ControlCenterService service) {
|
||||
mwrService = new WeakReference<ControlCenterService>(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(ACTION_UPDATE_SERVICENOTIFICATION)) {
|
||||
mwrService.get().updateServiceNotification();
|
||||
} else if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
|
||||
boolean isCharging = BatteryUtils.isCharging(intent);
|
||||
int nTheQuantityOfElectricity = BatteryUtils.getTheQuantityOfElectricity(intent);
|
||||
if (mwrService.get().getRemindThread() != null) {
|
||||
// 先设置提醒进程电池状态标志
|
||||
if (_mIsCharging != isCharging) {
|
||||
mwrService.get().getRemindThread().setIsCharging(isCharging);
|
||||
}
|
||||
if (_mnTheQuantityOfElectricityOld != nTheQuantityOfElectricity) {
|
||||
mwrService.get().getRemindThread().setQuantityOfElectricity(nTheQuantityOfElectricity);
|
||||
}
|
||||
}
|
||||
// 新电池状态标志某一个有变化就更新显示信息
|
||||
if (_mIsCharging != isCharging || _mnTheQuantityOfElectricityOld != nTheQuantityOfElectricity) {
|
||||
mwrService.get().updateServiceNotification();
|
||||
AppConfigUtils appConfigUtils = AppConfigUtils.getInstance(context);
|
||||
appConfigUtils.loadAppConfigBean();
|
||||
AppConfigBean appConfigBean = appConfigUtils.mAppConfigBean;
|
||||
appConfigBean.setCurrentValue(nTheQuantityOfElectricity);
|
||||
appConfigBean.setIsCharging(isCharging);
|
||||
mwrService.get().startRemindThread(appConfigBean);
|
||||
|
||||
// 保存电池报告
|
||||
// 示例数据更新逻辑
|
||||
// List<BatteryData> newData = new ArrayList<>(adapter.getDataList());
|
||||
// newData.add(0, new BatteryData(percentage, "00:00:00", "00:00:00"));
|
||||
// adapter.updateData(newData);
|
||||
|
||||
// 保存好新的电池状态标志
|
||||
_mIsCharging = isCharging;
|
||||
_mnTheQuantityOfElectricityOld = nTheQuantityOfElectricity;
|
||||
}
|
||||
} else if (intent.getAction().equals(ACTION_START_REMINDTHREAD)) {
|
||||
LogUtils.d(TAG, "ACTION_START_REMINDTHREAD");
|
||||
//AppConfigUtils appConfigUtils = AppConfigUtils.getInstance(context);
|
||||
//appConfigUtils.loadAppConfigBean();
|
||||
AppConfigBean appConfigBean = (AppConfigBean)intent.getSerializableExtra("appConfigBean");
|
||||
mwrService.get().startRemindThread(appConfigBean);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册 Receiver
|
||||
//
|
||||
public void registerAction(Context context) {
|
||||
IntentFilter filter=new IntentFilter();
|
||||
filter.addAction(ACTION_UPDATE_SERVICENOTIFICATION);
|
||||
filter.addAction(ACTION_START_REMINDTHREAD);
|
||||
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
|
||||
context.registerReceiver(this, filter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package cc.winboll.studio.powerbell.receivers;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.fragments.MainViewFragment;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
import cc.winboll.studio.powerbell.utils.BatteryUtils;
|
||||
import cc.winboll.studio.powerbell.utils.NotificationHelper;
|
||||
|
||||
public class GlobalApplicationReceiver extends BroadcastReceiver {
|
||||
|
||||
public static final String TAG = "GlobalApplicationReceiver";
|
||||
|
||||
AppConfigUtils mAppConfigUtils;
|
||||
App mGlobalApplication;
|
||||
// 存储电量指示值,
|
||||
// 用于校验电量消息时的电量变化
|
||||
static volatile int _mnTheQuantityOfElectricityOld = -1;
|
||||
static volatile boolean _mIsCharging = false;
|
||||
// 保存当前实例,
|
||||
// 便利封装 registerAction() 函数
|
||||
GlobalApplicationReceiver mReceiver;
|
||||
|
||||
public GlobalApplicationReceiver(App globalApplication) {
|
||||
mReceiver = this;
|
||||
mGlobalApplication = globalApplication;
|
||||
mAppConfigUtils = App.getAppConfigUtils(mGlobalApplication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
|
||||
// 先设置好新电池状态标志
|
||||
boolean isCharging = BatteryUtils.isCharging(intent);
|
||||
if (_mIsCharging != isCharging) {
|
||||
mAppConfigUtils.setIsCharging(isCharging);
|
||||
}
|
||||
int nTheQuantityOfElectricity = BatteryUtils.getTheQuantityOfElectricity(intent);
|
||||
if (_mnTheQuantityOfElectricityOld != nTheQuantityOfElectricity) {
|
||||
mAppConfigUtils.setCurrentValue(nTheQuantityOfElectricity);
|
||||
}
|
||||
// 新电池状态标志某一个有变化就更新显示信息
|
||||
if (_mIsCharging != isCharging || _mnTheQuantityOfElectricityOld != nTheQuantityOfElectricity) {
|
||||
// 电池状态改变先取消旧的提醒消息
|
||||
//NotificationHelper.cancelRemindNotification(context);
|
||||
|
||||
App.getAppCacheUtils(context).addChangingTime(nTheQuantityOfElectricity);
|
||||
MainViewFragment.sendMsgCurrentValueBattery(nTheQuantityOfElectricity);
|
||||
// 保存好新的电池状态标志
|
||||
_mIsCharging = isCharging;
|
||||
_mnTheQuantityOfElectricityOld = nTheQuantityOfElectricity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 注册 Receiver
|
||||
//
|
||||
public void registerAction() {
|
||||
IntentFilter filter=new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
|
||||
mGlobalApplication.registerReceiver(mReceiver, filter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package cc.winboll.studio.powerbell.receivers;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/06/06 15:01:39
|
||||
* @Describe 应用广播消息接收类
|
||||
*/
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
import cc.winboll.studio.powerbell.utils.ServiceUtils;
|
||||
|
||||
public class MainReceiver extends BroadcastReceiver {
|
||||
|
||||
public static final String TAG = "MainReceiver";
|
||||
|
||||
static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
|
||||
// 存储电量指示值,
|
||||
// 用于校验电量消息时的电量变化
|
||||
static volatile int _mnTheQuantityOfElectricityOld = -1;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String szAction = intent.getAction();
|
||||
if (szAction.equals(ACTION_BOOT_COMPLETED)) {
|
||||
boolean isEnableService = App.getAppConfigUtils(context).getIsEnableService();
|
||||
if (isEnableService) {
|
||||
if (ServiceUtils.isServiceAlive(context.getApplicationContext(), ControlCenterService.class.getName()) == false) {
|
||||
LogUtils.d(TAG, "wakeupAndBindMain() Wakeup... ControlCenterService");
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
context.startForegroundService(new Intent(context, ControlCenterService.class));
|
||||
} else {
|
||||
context.startService(new Intent(context, ControlCenterService.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package cc.winboll.studio.powerbell.services;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
import cc.winboll.studio.powerbell.utils.ServiceUtils;
|
||||
|
||||
public class AssistantService extends Service {
|
||||
private final static String TAG = "AssistantService";
|
||||
|
||||
//MyBinder mMyBinder;
|
||||
MyServiceConnection mMyServiceConnection;
|
||||
volatile boolean mIsThreadAlive;
|
||||
AppConfigUtils mAppConfigUtils;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
//return mMyBinder;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
//LogUtils.d(TAG, "onCreate");
|
||||
super.onCreate();
|
||||
mAppConfigUtils = App.getAppConfigUtils(this);
|
||||
|
||||
//mMyBinder = new MyBinder();
|
||||
if (mMyServiceConnection == null) {
|
||||
mMyServiceConnection = new MyServiceConnection();
|
||||
}
|
||||
// 设置运行参数
|
||||
mIsThreadAlive = false;
|
||||
run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
//LogUtils.d(TAG, "call onStartCommand(...)");
|
||||
run();
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
/*class MyBinder extends IMyAidlInterface.Stub {
|
||||
@Override
|
||||
public String getServiceName() {
|
||||
return AssistantService.class.getSimpleName();
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
//LogUtils.d(TAG, "onDestroy");
|
||||
mIsThreadAlive = false;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
// 运行服务内容
|
||||
//
|
||||
void run() {
|
||||
//LogUtils.d(TAG, "run");
|
||||
if (mAppConfigUtils.getIsEnableService()) {
|
||||
if (mIsThreadAlive == false) {
|
||||
// 设置运行状态
|
||||
mIsThreadAlive = true;
|
||||
// 唤醒和绑定主进程
|
||||
wakeupAndBindMain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 唤醒和绑定主进程
|
||||
//
|
||||
void wakeupAndBindMain() {
|
||||
if (ServiceUtils.isServiceAlive(getApplicationContext(), ControlCenterService.class.getName()) == false) {
|
||||
//LogUtils.d(TAG, "wakeupAndBindMain() Wakeup... ControlCenterService");
|
||||
startForegroundService(new Intent(AssistantService.this, ControlCenterService.class));
|
||||
}
|
||||
//LogUtils.d(TAG, "wakeupAndBindMain() Bind... ControlCenterService");
|
||||
bindService(new Intent(AssistantService.this, ControlCenterService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
|
||||
}
|
||||
|
||||
// 主进程与守护进程连接时需要用到此类
|
||||
//
|
||||
class MyServiceConnection implements ServiceConnection {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
//LogUtils.d(TAG, "call onServiceConnected(...)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
//LogUtils.d(TAG, "call onServiceDisconnected(...)");
|
||||
if (mAppConfigUtils.getIsEnableService()) {
|
||||
wakeupAndBindMain();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
package cc.winboll.studio.powerbell.services;
|
||||
|
||||
/*
|
||||
* PowerBy : ZhanGSKen(ZhangShaojian2018@163.com)
|
||||
* 参考:
|
||||
* 进程保活-双进程守护的正确姿势
|
||||
* https://blog.csdn.net/sinat_35159441/article/details/75267380
|
||||
* Android Service之onStartCommand方法研究
|
||||
* https://blog.csdn.net/cyp331203/article/details/38920491
|
||||
*/
|
||||
import android.app.Notification;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.widget.RemoteViews;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.MainActivity;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.beans.AppConfigBean;
|
||||
import cc.winboll.studio.powerbell.beans.NotificationMessage;
|
||||
import cc.winboll.studio.powerbell.handlers.ControlCenterServiceHandler;
|
||||
import cc.winboll.studio.powerbell.receivers.ControlCenterServiceReceiver;
|
||||
import cc.winboll.studio.powerbell.services.AssistantService;
|
||||
import cc.winboll.studio.powerbell.threads.RemindThread;
|
||||
import cc.winboll.studio.powerbell.utils.AppCacheUtils;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
import cc.winboll.studio.powerbell.utils.NotificationHelper;
|
||||
import cc.winboll.studio.powerbell.utils.ServiceUtils;
|
||||
import cc.winboll.studio.powerbell.utils.StringUtils;
|
||||
|
||||
public class ControlCenterService extends Service {
|
||||
|
||||
public static final String TAG = "ControlCenterService";
|
||||
|
||||
public static final int MSG_UPDATE_STATUS = 0;
|
||||
|
||||
static ControlCenterService _mControlCenterService;
|
||||
|
||||
volatile boolean isServiceRunning;
|
||||
|
||||
AppConfigUtils mAppConfigUtils;
|
||||
AppCacheUtils mAppCacheUtils;
|
||||
// 前台服务通知工具
|
||||
NotificationHelper mNotificationHelper;
|
||||
Notification notification;
|
||||
RemindThread mRemindThread;
|
||||
ControlCenterServiceHandler mControlCenterServiceHandler;
|
||||
MyServiceConnection mMyServiceConnection;
|
||||
ControlCenterServiceReceiver mControlCenterServiceReceiver;
|
||||
ControlCenterServiceReceiver mControlCenterServiceReceiverLocalBroadcast;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public RemindThread getRemindThread() {
|
||||
return mRemindThread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
_mControlCenterService = ControlCenterService.this;
|
||||
isServiceRunning = false;
|
||||
mAppConfigUtils = App.getAppConfigUtils(this);
|
||||
mAppCacheUtils = App.getAppCacheUtils(this);
|
||||
mNotificationHelper = new NotificationHelper(ControlCenterService.this);
|
||||
|
||||
|
||||
if (mMyServiceConnection == null) {
|
||||
mMyServiceConnection = new MyServiceConnection();
|
||||
}
|
||||
mControlCenterServiceHandler = new ControlCenterServiceHandler(this);
|
||||
|
||||
// 运行服务内容
|
||||
run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
// 运行服务内容
|
||||
run();
|
||||
return (mAppConfigUtils.getIsEnableService()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
// 运行服务内容
|
||||
//
|
||||
void run() {
|
||||
if (mAppConfigUtils.getIsEnableService() && isServiceRunning == false) {
|
||||
LogUtils.d(TAG, "run");
|
||||
isServiceRunning = true;
|
||||
// 唤醒守护进程
|
||||
wakeupAndBindAssistant();
|
||||
// 显示前台通知栏
|
||||
// 在Service中
|
||||
NotificationHelper helper = new NotificationHelper(this);
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
notification = helper.showForegroundNotification(intent, getString(R.string.app_name), "Service Running, Click to open app");
|
||||
startForeground(NotificationHelper.FOREGROUND_NOTIFICATION_ID, notification);
|
||||
|
||||
// NotificationMessage notificationMessage=createNotificationMessage();
|
||||
// //Toast.makeText(getApplication(), "", Toast.LENGTH_SHORT).show();
|
||||
// mNotificationUtils.createForegroundNotification(this, notificationMessage);
|
||||
// mNotificationUtils.createRemindNotification(this, notificationMessage);
|
||||
|
||||
if (mControlCenterServiceReceiver == null) {
|
||||
// 注册广播接收器
|
||||
mControlCenterServiceReceiver = new ControlCenterServiceReceiver(this);
|
||||
mControlCenterServiceReceiver.registerAction(this);
|
||||
}
|
||||
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable(){
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
startRemindThread(mAppConfigUtils.mAppConfigBean);
|
||||
ToastUtils.show("Service Is Start.");
|
||||
LogUtils.i(TAG, "Service Is Start.");
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
String getValuesString() {
|
||||
String szReturn = "Usege: ";
|
||||
szReturn += mAppConfigUtils.getIsEnableUsegeReminder() ? Integer.toString(mAppConfigUtils.getUsegeReminderValue()) : "?";
|
||||
szReturn += "% Charge: ";
|
||||
szReturn += mAppConfigUtils.getIsEnableChargeReminder() ? Integer.toString(mAppConfigUtils.getChargeReminderValue()) : "?";
|
||||
szReturn += "%\nCurrent: " + Integer.toString(mAppConfigUtils.getCurrentValue()) + "%";
|
||||
return szReturn;
|
||||
}
|
||||
|
||||
NotificationMessage createNotificationMessage() {
|
||||
String szTitle = ((App)getApplication()).getString(R.string.app_name);
|
||||
String szContent = getValuesString() + " {?} " + StringUtils.formatPCMListString(mAppCacheUtils.getArrayListBatteryInfo());
|
||||
return new NotificationMessage(szTitle, szContent);
|
||||
}
|
||||
|
||||
// 更新前台通知
|
||||
//
|
||||
public void updateServiceNotification() {
|
||||
//mNotificationUtils.updateForegroundNotification(ControlCenterService.this, createNotificationMessage());
|
||||
}
|
||||
|
||||
// 更新前台通知
|
||||
//
|
||||
public void updateServiceNotification(NotificationMessage notificationMessage) {
|
||||
//mNotificationUtils.updateForegroundNotification(ControlCenterService.this, notificationMessage);
|
||||
}
|
||||
|
||||
// 更新前台通知
|
||||
//
|
||||
public void updateRemindNotification(NotificationMessage notificationMessage) {
|
||||
//mNotificationUtils.updateRemindNotification(ControlCenterService.this, notificationMessage);
|
||||
}
|
||||
|
||||
// 唤醒和绑定守护进程
|
||||
//
|
||||
void wakeupAndBindAssistant() {
|
||||
if (ServiceUtils.isServiceAlive(getApplicationContext(), AssistantService.class.getName()) == false) {
|
||||
startService(new Intent(ControlCenterService.this, AssistantService.class));
|
||||
//LogUtils.d(TAG, "call wakeupAndBindAssistant() : Binding... AssistantService");
|
||||
bindService(new Intent(ControlCenterService.this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
|
||||
}
|
||||
}
|
||||
|
||||
// 开启提醒铃声线程
|
||||
//
|
||||
public void startRemindThread(AppConfigBean appConfigBean) {
|
||||
//LogUtils.d(TAG, "startRemindThread");
|
||||
if (mRemindThread == null) {
|
||||
mRemindThread = new RemindThread(this, mControlCenterServiceHandler);
|
||||
} else {
|
||||
if (mRemindThread.isExist() == true) {
|
||||
mRemindThread = new RemindThread(this, mControlCenterServiceHandler);
|
||||
} else {
|
||||
// 提醒进程正在进行中就更新状态后退出
|
||||
mRemindThread.setChargeReminderValue(appConfigBean.getChargeReminderValue());
|
||||
mRemindThread.setUsegeReminderValue(appConfigBean.getUsegeReminderValue());
|
||||
mRemindThread.setIsEnableChargeReminder(appConfigBean.isEnableChargeReminder());
|
||||
mRemindThread.setIsEnableUsegeReminder(appConfigBean.isEnableUsegeReminder());
|
||||
mRemindThread.setSleepTime(appConfigBean.getReminderIntervalTime());
|
||||
mRemindThread.setIsCharging(appConfigBean.isCharging());
|
||||
mRemindThread.setQuantityOfElectricity(appConfigBean.getCurrentValue());
|
||||
//LogUtils.d(TAG, "mRemindThread update.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
mRemindThread.setChargeReminderValue(appConfigBean.getChargeReminderValue());
|
||||
mRemindThread.setUsegeReminderValue(appConfigBean.getUsegeReminderValue());
|
||||
mRemindThread.setSleepTime(appConfigBean.getReminderIntervalTime());
|
||||
mRemindThread.setIsCharging(appConfigBean.isCharging());
|
||||
mRemindThread.setQuantityOfElectricity(appConfigBean.getCurrentValue());
|
||||
mRemindThread.setIsEnableChargeReminder(appConfigBean.isEnableChargeReminder());
|
||||
mRemindThread.setIsEnableUsegeReminder(appConfigBean.isEnableUsegeReminder());
|
||||
mRemindThread.start();
|
||||
//LogUtils.d(TAG, "mRemindThread.start()");
|
||||
}
|
||||
|
||||
public void stopRemindThread() {
|
||||
if (mRemindThread != null) {
|
||||
mRemindThread.setIsExist(true);
|
||||
mRemindThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
//LogUtils.d(TAG, "onDestroy");
|
||||
mAppConfigUtils.loadAppConfigBean();
|
||||
if (mAppConfigUtils.getIsEnableService() == false) {
|
||||
// 设置运行状态
|
||||
isServiceRunning = false;
|
||||
// 停止守护进程
|
||||
Intent intent = new Intent(this, AssistantService.class);
|
||||
stopService(intent);
|
||||
// 停止Receiver
|
||||
if (mControlCenterServiceReceiver != null) {
|
||||
unregisterReceiver(mControlCenterServiceReceiver);
|
||||
mControlCenterServiceReceiver = null;
|
||||
}
|
||||
// 停止前台通知栏
|
||||
stopForeground(true);
|
||||
// 停止消息提醒进程
|
||||
stopRemindThread();
|
||||
super.onDestroy();
|
||||
//LogUtils.d(TAG, "onDestroy done");
|
||||
}
|
||||
}
|
||||
|
||||
// 主进程与守护进程连接时需要用到此类
|
||||
//
|
||||
private class MyServiceConnection implements ServiceConnection {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
//LogUtils.d(TAG, "call onServiceConnected(...)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
//LogUtils.d(TAG, "call onServiceConnected(...)");
|
||||
if (mAppConfigUtils.getIsEnableService()) {
|
||||
// 唤醒守护进程
|
||||
wakeupAndBindAssistant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void appenRemindMSG(String szRemindMSG) {
|
||||
String msg = "";
|
||||
for (int i = 0; i < 20; i++) {
|
||||
msg += szRemindMSG;
|
||||
}
|
||||
NotificationHelper helper = new NotificationHelper(ControlCenterService.this);
|
||||
Intent intent = new Intent(ControlCenterService.this, MainActivity.class);
|
||||
helper.showTemporaryNotification(intent, getString(R.string.app_name), msg);
|
||||
|
||||
|
||||
|
||||
// NotificationMessage notificationMessage = createNotificationMessage();
|
||||
// notificationMessage.setRemindMSG(szRemindMSG);
|
||||
// //LogUtils.d(TAG, "notificationMessage : " + notificationMessage.getRemindMSG());
|
||||
// updateRemindNotification(notificationMessage);
|
||||
}
|
||||
|
||||
// 设置颜色背景
|
||||
public static RemoteViews setLinearLayoutColor(RemoteViews remoteViews, int viewId, int color) {
|
||||
remoteViews.setInt(viewId, "setBackgroundColor", color);
|
||||
return remoteViews;
|
||||
}
|
||||
|
||||
// 设置Drawable背景
|
||||
public static RemoteViews setLinearLayoutDrawable(RemoteViews remoteViews, int viewId, int drawableRes) {
|
||||
remoteViews.setInt(viewId, "setBackgroundResource", drawableRes);
|
||||
return remoteViews;
|
||||
}
|
||||
|
||||
//
|
||||
// 启动服务
|
||||
//
|
||||
public static void startControlCenterService(Context context) {
|
||||
Intent intent = new Intent(context, ControlCenterService.class);
|
||||
context.startForegroundService(intent);
|
||||
}
|
||||
|
||||
//
|
||||
// 停止服务
|
||||
//
|
||||
public static void stopControlCenterService(Context context) {
|
||||
Intent intent = new Intent(context, ControlCenterService.class);
|
||||
context.stopService(intent);
|
||||
}
|
||||
|
||||
public static void updateStatus(Context context, AppConfigBean appConfigBean) {
|
||||
//LogUtils.d(TAG, "updateStatus");
|
||||
// 创建一个Intent实例,定义广播的内容
|
||||
Intent intent = new Intent(ControlCenterServiceReceiver.ACTION_START_REMINDTHREAD);
|
||||
// 设置可选的Action数据,如额外信息
|
||||
intent.putExtra("appConfigBean", appConfigBean);
|
||||
// 发送广播
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
package cc.winboll.studio.powerbell.threads;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Message;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.handlers.ControlCenterServiceHandler;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class RemindThread extends Thread {
|
||||
|
||||
public static final String TAG = RemindThread.class.getSimpleName();
|
||||
|
||||
Context mContext;
|
||||
|
||||
// 控制线程是否退出的标志
|
||||
volatile boolean isExist = false;
|
||||
// 消息提醒开关
|
||||
static volatile boolean isReminding = false;
|
||||
// 充电提醒开关
|
||||
static volatile boolean isEnableUsegeReminder = false;
|
||||
// 耗电提醒开关
|
||||
static volatile boolean isEnableChargeReminder = false;
|
||||
// 电量比较停顿时间
|
||||
static volatile int sleepTime = 1000;
|
||||
// 充电提醒电量
|
||||
static volatile int chargeReminderValue = -1;
|
||||
// 耗电提醒电量
|
||||
static volatile int usegeReminderValue = -1;
|
||||
// 当前电量
|
||||
static volatile int quantityOfElectricity = -1;
|
||||
// 是否正在充电
|
||||
static volatile boolean isCharging = false;
|
||||
// 服务Handler, 用于线程发送消息使用
|
||||
WeakReference<ControlCenterServiceHandler> mwrControlCenterServiceHandler;
|
||||
|
||||
public void setIsExist(boolean isExist) {
|
||||
this.isExist = isExist;
|
||||
}
|
||||
|
||||
public boolean isExist() {
|
||||
return isExist;
|
||||
}
|
||||
|
||||
public static void setIsReminding(boolean isReminding) {
|
||||
RemindThread.isReminding = isReminding;
|
||||
}
|
||||
|
||||
public static boolean isReminding() {
|
||||
return isReminding;
|
||||
}
|
||||
|
||||
public static void setIsEnableUsegeReminder(boolean isEnableUsegeReminder) {
|
||||
RemindThread.isEnableUsegeReminder = isEnableUsegeReminder;
|
||||
}
|
||||
|
||||
public static boolean isEnableUsegeReminder() {
|
||||
return isEnableUsegeReminder;
|
||||
}
|
||||
|
||||
public static void setIsEnableChargeReminder(boolean isEnableChargeReminder) {
|
||||
RemindThread.isEnableChargeReminder = isEnableChargeReminder;
|
||||
}
|
||||
|
||||
public static boolean isEnableChargeReminder() {
|
||||
return isEnableChargeReminder;
|
||||
}
|
||||
|
||||
public static void setSleepTime(int sleepTime) {
|
||||
RemindThread.sleepTime = sleepTime;
|
||||
}
|
||||
|
||||
public static int getSleepTime() {
|
||||
return sleepTime;
|
||||
}
|
||||
|
||||
public static void setChargeReminderValue(int chargeReminderValue) {
|
||||
RemindThread.chargeReminderValue = chargeReminderValue;
|
||||
}
|
||||
|
||||
public static int getChargeReminderValue() {
|
||||
return chargeReminderValue;
|
||||
}
|
||||
|
||||
public static void setUsegeReminderValue(int usegeReminderValue) {
|
||||
RemindThread.usegeReminderValue = usegeReminderValue;
|
||||
}
|
||||
|
||||
public static int getUsegeReminderValue() {
|
||||
return usegeReminderValue;
|
||||
}
|
||||
|
||||
public static void setQuantityOfElectricity(int quantityOfElectricity) {
|
||||
RemindThread.quantityOfElectricity = quantityOfElectricity;
|
||||
}
|
||||
|
||||
public static int getQuantityOfElectricity() {
|
||||
return quantityOfElectricity;
|
||||
}
|
||||
|
||||
public static void setIsCharging(boolean isCharging) {
|
||||
RemindThread.isCharging = isCharging;
|
||||
}
|
||||
|
||||
public static boolean isCharging() {
|
||||
return isCharging;
|
||||
}
|
||||
|
||||
// 发送消息给用户
|
||||
//
|
||||
void sendNotificationMessage(String sz) {
|
||||
//LogUtils.d(TAG, "sz is " + sz);
|
||||
Message message = Message.obtain();
|
||||
message.what = ControlCenterServiceHandler.MSG_REMIND_TEXT;
|
||||
//message.obj = new NotificationMessage(mContext.getString(R.string.app_name), sz);
|
||||
message.obj = sz;
|
||||
ControlCenterServiceHandler handler = mwrControlCenterServiceHandler.get();
|
||||
if (isReminding && handler != null) {
|
||||
handler.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public RemindThread(Context context, ControlCenterServiceHandler handler) {
|
||||
mContext = context;
|
||||
mwrControlCenterServiceHandler = new WeakReference<ControlCenterServiceHandler>(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
//LogUtils.d(TAG, "call run()");
|
||||
if (isReminding == false) {
|
||||
isReminding = true;
|
||||
|
||||
// 等待些许时间,等所有数据初始化完成再执行下面的程序
|
||||
// 解决窗口移除后自动重启后会发送一个错误消息的问题
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {}
|
||||
|
||||
// 发送提醒线程开始的参数设置
|
||||
//sendMessageToUser(Integer.toString(_mnTheQuantityOfElectricity) + ">>>" + Integer.toString(_mnTargetNumber));
|
||||
//ToastUtils.show("Service Is Start.");
|
||||
//LogUtils.i(TAG, "Service Is Start.");
|
||||
while (!isExist()) {
|
||||
|
||||
/*
|
||||
LogUtils.d(TAG, "isCharging is " + Boolean.toString(isCharging));
|
||||
LogUtils.d(TAG, "usegeReminderValue is " + Integer.toString(usegeReminderValue));
|
||||
LogUtils.d(TAG, "quantityOfElectricity is " + Integer.toString(quantityOfElectricity));
|
||||
LogUtils.d(TAG, "chargeReminderValue is " + Integer.toString(chargeReminderValue));
|
||||
LogUtils.d(TAG, "isEnableChargeReminder is " + Boolean.toString(isEnableChargeReminder));
|
||||
LogUtils.d(TAG, "isEnableUsegeReminder is " + Boolean.toString(isEnableUsegeReminder));
|
||||
*/
|
||||
|
||||
try {
|
||||
if (isCharging) {
|
||||
if ((quantityOfElectricity >= chargeReminderValue)
|
||||
&& (isEnableChargeReminder)) {
|
||||
// 正在充电时电量大于指定电量发送提醒
|
||||
sendNotificationMessage("+");
|
||||
// 应用需要继续提醒,设置退出标志为否
|
||||
setIsExist(false);
|
||||
//sendNotificationMessage("I am ready! +");
|
||||
} else {
|
||||
// 设置退出标志,如果后续不需要继续提醒就退出当前进程,用于应用节能。
|
||||
setIsExist(true);
|
||||
isReminding = false;
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
if ((quantityOfElectricity <= usegeReminderValue)
|
||||
&& (isEnableUsegeReminder)) {
|
||||
// 正在放电时电量小于指定电量发送提醒
|
||||
sendNotificationMessage("-");
|
||||
// 应用需要继续提醒,设置退出标志为否
|
||||
setIsExist(false);
|
||||
//sendNotificationMessage("I am ready! -");
|
||||
} else {
|
||||
// 设置退出标志,如果后续不需要继续提醒就退出当前进程,用于应用节能。
|
||||
setIsExist(true);
|
||||
isReminding = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Thread.sleep(sleepTime);
|
||||
} catch (InterruptedException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
//ToastUtils.show("Service Is Stop.");
|
||||
//LogUtils.i(TAG, "Service Is Stop.");
|
||||
isReminding = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.beans.BatteryInfoBean;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class AppCacheUtils {
|
||||
public static final String TAG = "AppCacheUtils";
|
||||
|
||||
// 保存唯一配置实例
|
||||
static AppCacheUtils _mAppCacheUtils;
|
||||
// 配置实例引用的上下文环境
|
||||
Context mContext;
|
||||
// 配置实例的数据的存储文件路径
|
||||
//volatile String mAppCacheDataFilePath = null;
|
||||
ArrayList<BatteryInfoBean> mlBatteryInfo;
|
||||
|
||||
// 私有实例构造方法
|
||||
//
|
||||
AppCacheUtils(Context context) {
|
||||
mContext = context;
|
||||
//mAppCacheDataFilePath = context.getExternalFilesDir(TAG) + File.separator + "mlBatteryInfo.dat";
|
||||
mlBatteryInfo = new ArrayList<BatteryInfoBean>();
|
||||
loadAppCacheData();
|
||||
}
|
||||
|
||||
// 返回唯一实例
|
||||
//
|
||||
public static synchronized AppCacheUtils getInstance(Context context) {
|
||||
if (_mAppCacheUtils == null) {
|
||||
_mAppCacheUtils = new AppCacheUtils(context);
|
||||
}
|
||||
return _mAppCacheUtils;
|
||||
}
|
||||
|
||||
// 添加电量改变时间
|
||||
//
|
||||
public void addChangingTime(int nBattetyValue) {
|
||||
if (mlBatteryInfo.size() == 0) {
|
||||
addChangingTimeToList(nBattetyValue);
|
||||
//LogUtils.d(TAG, "nBattetyValue is "+Integer.toString(nBattetyValue));
|
||||
return;
|
||||
}
|
||||
if (mlBatteryInfo.get(mlBatteryInfo.size() - 1).getBattetyValue() != nBattetyValue) {
|
||||
addChangingTimeToList(nBattetyValue);
|
||||
//LogUtils.d(TAG, "nBattetyValue is "+Integer.toString(nBattetyValue));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void addChangingTimeToList(int nBattetyValue) {
|
||||
if (mlBatteryInfo.size() > 180) {
|
||||
mlBatteryInfo.remove(0);
|
||||
}
|
||||
BatteryInfoBean batteryInfo = new BatteryInfoBean(System.currentTimeMillis(), nBattetyValue);
|
||||
LogUtils.d(TAG, "getBattetyValue is " + Integer.toString(batteryInfo.getBattetyValue()));
|
||||
LogUtils.d(TAG, "getTimeStamp is " + Long.toString(batteryInfo.getTimeStamp()));
|
||||
mlBatteryInfo.add(batteryInfo);
|
||||
saveAppCacheData();
|
||||
}
|
||||
|
||||
public ArrayList<BatteryInfoBean> getArrayListBatteryInfo() {
|
||||
loadAppCacheData();
|
||||
return mlBatteryInfo;
|
||||
}
|
||||
|
||||
// 读取文件存储的数据
|
||||
//
|
||||
void saveAppCacheData() {
|
||||
BatteryInfoBean.saveBeanList(mContext, mlBatteryInfo, BatteryInfoBean.class);
|
||||
}
|
||||
|
||||
// 保存数据到文件
|
||||
//
|
||||
void loadAppCacheData() {
|
||||
mlBatteryInfo.clear();
|
||||
BatteryInfoBean.loadBeanList(mContext, mlBatteryInfo, BatteryInfoBean.class);
|
||||
}
|
||||
|
||||
public void clearBatteryHistory() {
|
||||
mlBatteryInfo.clear();
|
||||
saveAppCacheData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.MainActivity;
|
||||
import cc.winboll.studio.powerbell.beans.AppConfigBean;
|
||||
import cc.winboll.studio.powerbell.beans.ControlCenterServiceBean;
|
||||
import cc.winboll.studio.powerbell.dialogs.YesNoAlertDialog;
|
||||
import cc.winboll.studio.powerbell.fragments.MainViewFragment;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
import java.io.File;
|
||||
|
||||
// 应用配置工具类
|
||||
//
|
||||
public class AppConfigUtils {
|
||||
public static final String TAG = "AppConfigUtils";
|
||||
|
||||
public static final String BACKGROUND_DIR = "Background";
|
||||
|
||||
// 保存唯一配置实例
|
||||
static AppConfigUtils _mAppConfigUtils;
|
||||
// 应用环境上下文
|
||||
Context mContext;
|
||||
|
||||
// 是否启动铃声提醒服务
|
||||
volatile boolean mIsEnableService = false;
|
||||
|
||||
public volatile AppConfigBean mAppConfigBean;
|
||||
|
||||
// 电池充电提醒值。
|
||||
// Battery charge reminder value.
|
||||
volatile int mnChargeReminderValue = -1;
|
||||
volatile boolean mIsEnableChargeReminder = false;
|
||||
// 电池耗电量提醒值。
|
||||
// Battery power usege reminder value.
|
||||
volatile int mnUsegeReminderValue = -1;
|
||||
volatile boolean mIsEnableUsegeReminder = false;
|
||||
|
||||
volatile boolean mIsUseBackgroundFile = false;
|
||||
volatile String mszBackgroundFileName = "";
|
||||
|
||||
// 保存应用实例
|
||||
App mApplication;
|
||||
|
||||
AppConfigUtils(Context context) {
|
||||
mContext = context;
|
||||
String szExternalFilesDir = mContext.getExternalFilesDir(TAG) + File.separator;
|
||||
//mlistAppConfigBean = new ArrayList<AppConfigBean>();
|
||||
mAppConfigBean = new AppConfigBean();
|
||||
loadAppConfigBean();
|
||||
}
|
||||
|
||||
// 返回唯一实例
|
||||
//
|
||||
public static AppConfigUtils getInstance(Context context) {
|
||||
if (_mAppConfigUtils == null) {
|
||||
_mAppConfigUtils = new AppConfigUtils(context);
|
||||
}
|
||||
return _mAppConfigUtils;
|
||||
}
|
||||
|
||||
public void setIsEnableService(Activity activity, final boolean isEnableService) {
|
||||
YesNoAlertDialog.show(activity, "应用设置信息", "是否保存应用配置?", new YesNoAlertDialog.OnDialogResultListener(){
|
||||
@Override
|
||||
public void onYes() {
|
||||
mIsEnableService = isEnableService;
|
||||
ControlCenterServiceBean bean = new ControlCenterServiceBean(isEnableService);
|
||||
ControlCenterServiceBean.saveBean(mContext, bean);
|
||||
if (mIsEnableService) {
|
||||
LogUtils.d(TAG, "startControlCenterService");
|
||||
ControlCenterService.startControlCenterService(mContext);
|
||||
} else {
|
||||
LogUtils.d(TAG, "stopControlCenterService");
|
||||
ControlCenterService.stopControlCenterService(mContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNo() {
|
||||
MainViewFragment.relaodAppConfigs();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean getIsEnableService() {
|
||||
ControlCenterServiceBean bean = ControlCenterServiceBean.loadBean(mContext, ControlCenterServiceBean.class);
|
||||
if (bean == null) {
|
||||
ControlCenterServiceBean.saveBean(mContext, new ControlCenterServiceBean(false));
|
||||
return false;
|
||||
}
|
||||
return bean.isEnableService();
|
||||
}
|
||||
|
||||
public void setIsEnableChargeReminder(boolean isEnableChargeReminder) {
|
||||
mAppConfigBean.setIsEnableChargeReminder(isEnableChargeReminder);
|
||||
saveConfigData(MainActivity._mMainActivity);
|
||||
}
|
||||
|
||||
public boolean getIsEnableChargeReminder() {
|
||||
return mAppConfigBean.isEnableChargeReminder();
|
||||
}
|
||||
|
||||
public void setIsEnableUsegeReminder(boolean isEnableUsegeReminder) {
|
||||
mAppConfigBean.setIsEnableUsegeReminder(isEnableUsegeReminder);
|
||||
saveConfigData(MainActivity._mMainActivity);
|
||||
}
|
||||
|
||||
public boolean getIsEnableUsegeReminder() {
|
||||
return mAppConfigBean.isEnableUsegeReminder();
|
||||
}
|
||||
|
||||
public void setChargeReminderValue(int value) {
|
||||
mAppConfigBean.setChargeReminderValue(value);
|
||||
saveConfigData(MainActivity._mMainActivity);
|
||||
}
|
||||
|
||||
public int getChargeReminderValue() {
|
||||
return mAppConfigBean.getChargeReminderValue();
|
||||
}
|
||||
|
||||
public void setUsegeReminderValue(int value) {
|
||||
mAppConfigBean.setUsegeReminderValue(value);
|
||||
saveConfigData(MainActivity._mMainActivity);
|
||||
}
|
||||
|
||||
public int getUsegeReminderValue() {
|
||||
return mAppConfigBean.getUsegeReminderValue();
|
||||
}
|
||||
|
||||
public void setIsCharging(boolean isCharging) {
|
||||
mAppConfigBean.setIsCharging(isCharging);
|
||||
}
|
||||
|
||||
public boolean isCharging() {
|
||||
return mAppConfigBean.isCharging();
|
||||
}
|
||||
|
||||
public void setCurrentValue(int nCurrentValue) {
|
||||
mAppConfigBean.setCurrentValue(nCurrentValue);
|
||||
}
|
||||
|
||||
public int getCurrentValue() {
|
||||
return mAppConfigBean.getCurrentValue();
|
||||
}
|
||||
|
||||
public void setReminderIntervalTime(int reminderIntervalTime) {
|
||||
mAppConfigBean.setReminderIntervalTime(reminderIntervalTime);
|
||||
}
|
||||
|
||||
public int getReminderIntervalTime() {
|
||||
return mAppConfigBean.getReminderIntervalTime();
|
||||
}
|
||||
|
||||
//
|
||||
// 加载电池提醒配置数据
|
||||
//
|
||||
public void loadAppConfigBean() {
|
||||
AppConfigBean bean = AppConfigBean.loadBean(mContext, AppConfigBean.class);
|
||||
if (bean == null) {
|
||||
bean = new AppConfigBean();
|
||||
AppConfigBean.saveBean(mContext, mAppConfigBean);
|
||||
}
|
||||
mAppConfigBean.setIsEnableUsegeReminder(bean.isEnableUsegeReminder());
|
||||
mAppConfigBean.setUsegeReminderValue(bean.getUsegeReminderValue());
|
||||
mAppConfigBean.setIsEnableChargeReminder(bean.isEnableChargeReminder());
|
||||
mAppConfigBean.setChargeReminderValue(bean.getChargeReminderValue());
|
||||
}
|
||||
|
||||
public void saveConfigData(final MainActivity activity) {
|
||||
if (MainActivity._mMainActivity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
YesNoAlertDialog.show(activity, "应用设置信息", "是否保存应用配置?", new YesNoAlertDialog.OnDialogResultListener(){
|
||||
|
||||
@Override
|
||||
public void onYes() {
|
||||
saveConfigData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNo() {
|
||||
AppConfigUtils.getInstance(activity).loadAppConfigBean();
|
||||
MainViewFragment.relaodAppConfigs();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// 保存应用配置数据
|
||||
//
|
||||
void saveConfigData() {
|
||||
// 更新配置先取消一下旧的的提醒消息
|
||||
//NotificationHelper.cancelRemindNotification(mContext);
|
||||
|
||||
AppConfigBean.saveBean(mContext, mAppConfigBean);
|
||||
// 通知活动窗口和服务配置已更新
|
||||
ControlCenterService.updateStatus(mContext, mAppConfigBean);
|
||||
MainViewFragment.relaodAppConfigs();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/07/18 04:32:46
|
||||
* @Describe 电池工具类
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.BatteryManager;
|
||||
|
||||
public class BatteryUtils {
|
||||
|
||||
public static final String TAG = "BatteryUtils";
|
||||
|
||||
public static boolean isCharging(Intent intent) {
|
||||
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
|
||||
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
|
||||
status == BatteryManager.BATTERY_STATUS_FULL;
|
||||
return isCharging;
|
||||
}
|
||||
|
||||
public static int getTheQuantityOfElectricity(Intent intent) {
|
||||
int intLevel = intent.getIntExtra("level", 0);
|
||||
int intScale = intent.getIntExtra("scale", 100);
|
||||
return intLevel * 100 / intScale;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
public class DateUtils {
|
||||
|
||||
// 获取当前时间的格式化字符串
|
||||
public static String getDateNowString() {
|
||||
// 日期类转化成字符串类的工具
|
||||
SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("YYYYMMdd_HHmmssmmm", java.util.Locale.getDefault());
|
||||
// 读取当前时间
|
||||
long nTimeNow = System.currentTimeMillis();
|
||||
return mSimpleDateFormat.format(nTimeNow);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
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.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* 文件读取工具类
|
||||
*/
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
public static final String TAG = "FileUtils";
|
||||
|
||||
//
|
||||
// 读取文件内容,作为字符串返回
|
||||
//
|
||||
public static String readFileAsString(String filePath) throws IOException {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(filePath);
|
||||
}
|
||||
|
||||
if (file.length() > 1024 * 1024 * 1024) {
|
||||
throw new IOException("File is too large");
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder((int) (file.length()));
|
||||
// 创建字节输入流
|
||||
FileInputStream fis = new FileInputStream(filePath);
|
||||
// 创建一个长度为10240的Buffer
|
||||
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();
|
||||
}
|
||||
|
||||
//
|
||||
// 根据文件路径读取byte[] 数组
|
||||
//
|
||||
public static byte[] readFileByBytes(String filePath) throws IOException {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(filePath);
|
||||
} else {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
|
||||
BufferedInputStream in = null;
|
||||
|
||||
try {
|
||||
in = new BufferedInputStream(new FileInputStream(file));
|
||||
short bufSize = 1024;
|
||||
byte[] buffer = new byte[bufSize];
|
||||
int len1;
|
||||
while (-1 != (len1 = in.read(buffer, 0, bufSize))) {
|
||||
bos.write(buffer, 0, len1);
|
||||
}
|
||||
|
||||
byte[] var7 = bos.toByteArray();
|
||||
return var7;
|
||||
} finally {
|
||||
try {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException var14) {
|
||||
var14.printStackTrace();
|
||||
}
|
||||
|
||||
bos.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 文件复制函数
|
||||
//
|
||||
public static void copyFileUsingFileChannels(File source, File dest) throws IOException {
|
||||
FileChannel inputChannel = null;
|
||||
FileChannel outputChannel = null;
|
||||
try {
|
||||
inputChannel = new FileInputStream(source).getChannel();
|
||||
outputChannel = new FileOutputStream(dest).getChannel();
|
||||
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
|
||||
} finally {
|
||||
inputChannel.close();
|
||||
outputChannel.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件生成位图
|
||||
* @param path
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
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) {
|
||||
//String oldPath = "path/to/original/file.txt";
|
||||
//String newPath = "path/to/new-location/for/file.txt";
|
||||
|
||||
//File oldFile = new java.io.File(oldPath);
|
||||
//File newFile = new java.io.File(newPath);
|
||||
if (!newFile.getParentFile().exists()) {
|
||||
newFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
if (!oldFile.exists()) {
|
||||
//System.out.println("The original file does not exist.");
|
||||
LogUtils.d(TAG, "The original file does not exist.");
|
||||
} else {
|
||||
try {
|
||||
// 源文件路径
|
||||
Path sourcePath = Paths.get(oldFile.getPath());
|
||||
// 目标文件路径
|
||||
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());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class ImageUtils {
|
||||
|
||||
public static final String TAG = ImageUtils.class.getSimpleName();
|
||||
|
||||
// 这里我们生成了一个Pic文件夹,在下面放了我们质量压缩后的图片,用于和原图对比
|
||||
// 压缩图片使用Bitmap.compress(),这里是质量压缩
|
||||
// 参数:Context context :调用本函数函数引用的资源体系
|
||||
// String szSrcImagePath :要压缩的源文件路径
|
||||
// String szDstImagePath :压缩后文件要保存的路径
|
||||
// int nPictureCompress :图片压缩比例
|
||||
public static void bitmapCompress(Context context, String szSrcImagePath, String szDstImagePath, int nPictureCompress) {
|
||||
try {
|
||||
Bitmap bmpCompressImage;
|
||||
|
||||
//生成新的文件
|
||||
File fDstCompressImage = new File(szDstImagePath);
|
||||
|
||||
//裁剪后的图像转成BitMap
|
||||
//photoBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uriClipUri));
|
||||
bmpCompressImage = BitmapFactory.decodeFile(szSrcImagePath);
|
||||
|
||||
//创建输出流
|
||||
OutputStream out = null;
|
||||
|
||||
out = new FileOutputStream(fDstCompressImage.getPath());
|
||||
|
||||
//压缩文件,返回结果,参数分别是压缩的格式,压缩质量的百分比,输出流
|
||||
boolean bCompress = bmpCompressImage.compress(Bitmap.CompressFormat.JPEG, nPictureCompress, out);
|
||||
|
||||
// 复制压缩后的文件到源路径
|
||||
File fSrcImage = new File(szSrcImagePath);
|
||||
FileUtils.copyFileUsingFileChannels(fDstCompressImage, fSrcImage);
|
||||
LogUtils.d(TAG, Integer.toString(nPictureCompress) + "%压缩结束。");
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
LogUtils.d(TAG, "bitmapCompress FileNotFoundException : " + e.getMessage());
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, "bitmapCompress IOException : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/11/14 11:14
|
||||
* @Describe 米盟 MimoUtils
|
||||
*/
|
||||
public final class MimoUtils {
|
||||
public static final String TAG = "Utils";
|
||||
|
||||
public static int dpToPx(Context context, float dp) {
|
||||
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
|
||||
return (int) (dp * displayMetrics.density + 0.5f);
|
||||
}
|
||||
|
||||
public static int pxToDp(Context context, float px) {
|
||||
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
|
||||
return (int) (px / displayMetrics.density + 0.5f);
|
||||
}
|
||||
|
||||
public static int pxToSp(Context context, float pxValue) {
|
||||
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
|
||||
return (int) (pxValue / displayMetrics.scaledDensity + 0.5f);
|
||||
}
|
||||
|
||||
public static int spToPx(Context context, float spValue) {
|
||||
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
|
||||
return (int) (spValue * displayMetrics.scaledDensity + 0.5f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/03/22 04:39:40
|
||||
* @Describe 通知工具类
|
||||
*/
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.widget.RemoteViews;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
|
||||
public class NotificationHelper {
|
||||
public static final String TAG = "NotificationHelper";
|
||||
|
||||
// 渠道ID和名称
|
||||
private static final String CHANNEL_ID_FOREGROUND = "foreground_channel";
|
||||
private static final String CHANNEL_NAME_FOREGROUND = "Foreground Service";
|
||||
private static final String CHANNEL_ID_TEMPORARY = "temporary_channel";
|
||||
private static final String CHANNEL_NAME_TEMPORARY = "Temporary Notifications";
|
||||
|
||||
// 通知ID
|
||||
public static final int FOREGROUND_NOTIFICATION_ID = 1001;
|
||||
public static final int TEMPORARY_NOTIFICATION_ID = 2001;
|
||||
|
||||
private final Context mContext;
|
||||
private final NotificationManager mNotificationManager;
|
||||
|
||||
public NotificationHelper(Context context) {
|
||||
mContext = context;
|
||||
mNotificationManager = context.getSystemService(NotificationManager.class);
|
||||
createNotificationChannels();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createForegroundChannel();
|
||||
createTemporaryChannel();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createForegroundChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID_FOREGROUND,
|
||||
CHANNEL_NAME_FOREGROUND,
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
channel.setDescription("Persistent service notifications");
|
||||
channel.setSound(null, null);
|
||||
channel.enableVibration(false);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createTemporaryChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID_TEMPORARY,
|
||||
CHANNEL_NAME_TEMPORARY,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
);
|
||||
channel.setDescription("Temporary alert notifications");
|
||||
channel.setSound(null, null);
|
||||
channel.enableVibration(true);
|
||||
channel.setVibrationPattern(new long[]{100, 200, 300, 400});
|
||||
channel.setBypassDnd(true);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
// 显示常驻通知(通常用于前台服务)
|
||||
public Notification showForegroundNotification(Intent intent, String title, String content) {
|
||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_FOREGROUND)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
|
||||
//.setContentTitle(title + "\n" + content)
|
||||
.setContentTitle(content)
|
||||
//.setContentText(content)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setOngoing(true)
|
||||
.build();
|
||||
|
||||
mNotificationManager.notify(FOREGROUND_NOTIFICATION_ID, notification);
|
||||
return notification;
|
||||
}
|
||||
|
||||
// 显示临时通知(自动消失)
|
||||
public void showTemporaryNotification(Intent intent, String title, String content) {
|
||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMPORARY)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true)
|
||||
.setVibrate(new long[]{100, 200, 300, 400})
|
||||
.build();
|
||||
|
||||
mNotificationManager.notify(TEMPORARY_NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
// 创建自定义布局通知(可扩展)
|
||||
public void showCustomNotification(Intent intent, RemoteViews contentView, RemoteViews bigContentView) {
|
||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMPORARY)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContent(contentView)
|
||||
.setCustomBigContentView(bigContentView)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true)
|
||||
.build();
|
||||
|
||||
mNotificationManager.notify(TEMPORARY_NOTIFICATION_ID + 1, notification);
|
||||
}
|
||||
|
||||
// 取消所有通知
|
||||
public void cancelAllNotifications() {
|
||||
mNotificationManager.cancelAll();
|
||||
}
|
||||
|
||||
// 创建PendingIntent(兼容不同API版本)
|
||||
private PendingIntent createPendingIntent(Intent intent) {
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// flags |= PendingIntent.FLAG_IMMUTABLE;
|
||||
// }
|
||||
return PendingIntent.getActivity(
|
||||
mContext,
|
||||
0,
|
||||
intent,
|
||||
flags
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
/*
|
||||
* 参考:
|
||||
* https://blog.csdn.net/qq_35507234/article/details/90676587
|
||||
* https://blog.csdn.net/qq_16628781/article/details/51548324
|
||||
*/
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.media.RingtoneManager;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import cc.winboll.studio.powerbell.MainActivity;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.beans.NotificationMessage;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
|
||||
public class NotificationUtils2 {
|
||||
|
||||
public static final String TAG = NotificationHelper.class.getSimpleName();
|
||||
|
||||
Context mContext;
|
||||
NotificationManager mNotificationManager;
|
||||
|
||||
Notification mForegroundNotification;
|
||||
PendingIntent mForegroundPendingIntent;
|
||||
Notification mRemindNotification;
|
||||
PendingIntent mRemindPendingIntent;
|
||||
RemoteViews mrvServiceNotificationView;
|
||||
RemoteViews mrvRemindNotificationView;
|
||||
|
||||
static enum NotificationType { MIN, MAX };
|
||||
private static int _mnServiceNotificationID = 1;
|
||||
private static int _mnRemindNotificationID = 2;
|
||||
private static String _mszChannelIDService = "1";
|
||||
private static String _mszChannelNameService = "Service";
|
||||
private static String _mszChannelIDRemind = "2";
|
||||
private static String _mszChannelNameRemind = "Remind";
|
||||
|
||||
// public NotificationUtils(Context context) {
|
||||
// mContext = context;
|
||||
// mNotificationManager = (NotificationManager) context.getSystemService(
|
||||
// Context.NOTIFICATION_SERVICE);
|
||||
// }
|
||||
|
||||
public NotificationUtils2(Context context) {
|
||||
mContext = context;
|
||||
mNotificationManager = context.getSystemService(NotificationManager.class);
|
||||
//createNotificationChannels();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public void createNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createServiceChannel();
|
||||
createRemindChannel();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createServiceChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
_mszChannelIDService,
|
||||
_mszChannelNameService,
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
channel.setDescription("Background service updates");
|
||||
channel.setSound(null, null);
|
||||
channel.enableVibration(false);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createRemindChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
_mszChannelIDRemind,
|
||||
_mszChannelNameRemind,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
);
|
||||
channel.setDescription("Critical reminders");
|
||||
channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM), null);
|
||||
channel.enableVibration(true);
|
||||
channel.setVibrationPattern(new long[]{100, 200, 300, 400});
|
||||
channel.setBypassDnd(true);
|
||||
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
// 创建并发送服务通知
|
||||
//
|
||||
public void createForegroundNotification(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
//创建Notification,传入Context和channelId
|
||||
Intent intent = new Intent();//这个intent会传给目标,可以使用getIntent来获取
|
||||
intent.setPackage(service.getPackageName());
|
||||
//LogUtils.d(TAG, "mService.getPackageName() : " + service.getPackageName());
|
||||
intent.setClass(service, MainActivity.class);
|
||||
//LogUtils.d(TAG, "MainActivity.class.getName() : " + MainActivity.class.getName());
|
||||
//这里放一个count用来区分每一个通知
|
||||
//intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
|
||||
|
||||
//参数1:context 上下文对象
|
||||
//参数2:发送者私有的请求码(Private request code for the sender)
|
||||
//参数3:intent 意图对象
|
||||
//参数4:必须为FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,中的一个
|
||||
//mForegroundPendingIntent = PendingIntent.getActivity(mService, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
mForegroundPendingIntent = PendingIntent.getActivity(service,
|
||||
1, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
mForegroundPendingIntent = PendingIntent.getActivity(service,
|
||||
1, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
}
|
||||
|
||||
mForegroundNotification = new Notification.Builder(service, _mszChannelIDService)
|
||||
.setAutoCancel(true)
|
||||
.setContentTitle(notificationMessage.getTitle())
|
||||
.setContentText(notificationMessage.getContent())
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
//设置红色
|
||||
.setColor(Color.parseColor("#F00606"))
|
||||
.setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
|
||||
.setContentIntent(mForegroundPendingIntent)
|
||||
.build();
|
||||
|
||||
setForegroundNotificationRemoteViews(service, notificationMessage);
|
||||
service.startForeground(_mnServiceNotificationID, mForegroundNotification);
|
||||
}
|
||||
|
||||
void initmrvRemindNotificationView(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
mrvRemindNotificationView = new RemoteViews(service.getPackageName(), R.layout.view_remindnotification);
|
||||
mrvRemindNotificationView.setTextViewText(R.id.viewremindnotificationTextView1, notificationMessage.getTitle());
|
||||
String szRemindMSG = notificationMessage.getRemindMSG();
|
||||
//LogUtils.d(TAG, "szRemindMSG : " + szRemindMSG);
|
||||
//mrvRemindNotificationView.setTextViewText(R.id.remoteviewTextView2, szRemindMSG);
|
||||
if (szRemindMSG != null) {
|
||||
if (szRemindMSG.trim().equals("-")) {
|
||||
//LogUtils.d(TAG, "-");
|
||||
mrvRemindNotificationView.setViewVisibility(R.id.remoteviewCharge, View.GONE);
|
||||
mrvRemindNotificationView.setViewVisibility(R.id.remoteviewUsege, View.VISIBLE);
|
||||
} else if (szRemindMSG.trim().equals("+")) {
|
||||
//LogUtils.d(TAG, "+");
|
||||
mrvRemindNotificationView.setViewVisibility(R.id.remoteviewUsege, View.GONE);
|
||||
mrvRemindNotificationView.setViewVisibility(R.id.remoteviewCharge, View.VISIBLE);
|
||||
}
|
||||
mrvRemindNotificationView.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
|
||||
//给我remoteViews上的控件tv_content添加监听事件
|
||||
//remoteViews.setOnClickPendingIntent(R.id.remoteviewLinearLayout1, pi);
|
||||
//return mrvServiceNotificationView;
|
||||
}
|
||||
}
|
||||
|
||||
void initmrvServiceNotificationView(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
mrvServiceNotificationView = new RemoteViews(service.getPackageName(), R.layout.view_servicenotification);
|
||||
mrvServiceNotificationView.setTextViewText(R.id.remoteviewTextView1, notificationMessage.getTitle());
|
||||
//String szRemindMSG = notificationMessage.getRemindMSG();
|
||||
//mrvServiceNotificationView.setTextViewText(R.id.remoteviewTextView2, szRemindMSG);
|
||||
//rvServiceNotificationView.setTextViewText(R.id.remoteviewTextView3, notificationMessage.getContent() + Integer.toString(nTest));
|
||||
mrvServiceNotificationView.setTextViewText(R.id.remoteviewTextView3, notificationMessage.getContent());
|
||||
mrvServiceNotificationView.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
|
||||
//给我remoteViews上的控件tv_content添加监听事件
|
||||
//remoteViews.setOnClickPendingIntent(R.id.remoteviewLinearLayout1, pi);
|
||||
//return mrvServiceNotificationView;
|
||||
}
|
||||
|
||||
void setForegroundNotificationRemoteViews(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
initmrvServiceNotificationView(service, notificationMessage);
|
||||
mForegroundNotification.contentView = mrvServiceNotificationView;
|
||||
mForegroundNotification.bigContentView = mrvServiceNotificationView;
|
||||
}
|
||||
|
||||
void setRemindNotificationRemoteViews(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
initmrvRemindNotificationView(service, notificationMessage);
|
||||
mRemindNotification.contentView = mrvRemindNotificationView;
|
||||
mRemindNotification.bigContentView = mrvRemindNotificationView;
|
||||
}
|
||||
|
||||
// 更新服务通知
|
||||
//
|
||||
public void updateForegroundNotification(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
setForegroundNotificationRemoteViews(service, notificationMessage);
|
||||
mNotificationManager.notify(_mnServiceNotificationID, mForegroundNotification);
|
||||
|
||||
}
|
||||
|
||||
// 创建并发送电量提醒通知
|
||||
//
|
||||
public void updateRemindNotification(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
//LogUtils.d(TAG, "updateRemindNotification : " + notificationMessage.getRemindMSG());
|
||||
setRemindNotificationRemoteViews(service, notificationMessage);
|
||||
mNotificationManager.notify(_mnRemindNotificationID, mRemindNotification);
|
||||
}
|
||||
|
||||
public void createRemindNotification(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
//LogUtils.d(TAG, "notificationMessage : " + notificationMessage.getRemindMSG());
|
||||
//创建Notification,传入Context和channelId
|
||||
Intent intent = new Intent();//这个intent会传给目标,可以使用getIntent来获取
|
||||
intent.setPackage(service.getPackageName());
|
||||
intent.setClass(service, MainActivity.class);
|
||||
//这里放一个count用来区分每一个通知
|
||||
//intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
|
||||
|
||||
//参数1:context 上下文对象
|
||||
//参数2:发送者私有的请求码(Private request code for the sender)
|
||||
//参数3:intent 意图对象
|
||||
//参数4:必须为FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,中的一个
|
||||
//mRemindPendingIntent = PendingIntent.getActivity(mService, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
mRemindPendingIntent = PendingIntent.getActivity(service,
|
||||
1, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
mRemindPendingIntent = PendingIntent.getActivity(service,
|
||||
1, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
}
|
||||
|
||||
mRemindNotification = new Notification.Builder(service, _mszChannelIDRemind)
|
||||
.setAutoCancel(true)
|
||||
.setContentTitle(notificationMessage.getTitle())
|
||||
.setContentText(notificationMessage.getContent())
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
//设置红色
|
||||
.setColor(Color.parseColor("#F00606"))
|
||||
.setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
|
||||
.setContentIntent(mRemindPendingIntent)
|
||||
.build();
|
||||
setRemindNotificationRemoteViews(service, notificationMessage);
|
||||
}
|
||||
|
||||
public static void cancelRemindNotification(Context context){
|
||||
// 获取 NotificationManager 实例
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
// 撤回指定 ID 的通知栏消息
|
||||
notificationManager.cancel(_mnRemindNotificationID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/11/21 18:55
|
||||
* @Describe
|
||||
* 图片下载工具类(指定目录保存:Pictures/PowerBell/BackgroundHistory)
|
||||
*/
|
||||
public class PictureUtils {
|
||||
private static final String TAG = "PictureUtils";
|
||||
private static final String ROOT_DIR = "PowerBell/BackgroundHistory"; // 自定义目录结构
|
||||
private static OkHttpClient sOkHttpClient;
|
||||
|
||||
static {
|
||||
sOkHttpClient = new OkHttpClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载网络图片到指定目录(外部存储/Pictures/PowerBell/BackgroundHistory)
|
||||
* @param context 上下文(用于通知相册刷新)
|
||||
* @param imgUrl 图片网络URL
|
||||
* @param callback 下载结果回调(成功/失败)
|
||||
*/
|
||||
public static void downloadImageToAlbum(final Context context, final String imgUrl, final DownloadCallback callback) {
|
||||
// 检查参数合法性
|
||||
if (context == null) {
|
||||
if (callback != null) {
|
||||
callback.onFailure(new IllegalArgumentException("Context不能为空"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (imgUrl == null || imgUrl.isEmpty()) {
|
||||
if (callback != null) {
|
||||
callback.onFailure(new IllegalArgumentException("图片URL为空"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
startDownload(context, imgUrl, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行实际的下载逻辑
|
||||
*/
|
||||
private static void startDownload(final Context context, final String imgUrl, final DownloadCallback callback) {
|
||||
Request request = new Request.Builder().url(imgUrl).build();
|
||||
sOkHttpClient.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
if (!response.isSuccessful()) {
|
||||
if (callback != null) {
|
||||
callback.onFailure(new IOException("请求失败,响应码:" + response.code()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
InputStream inputStream = null;
|
||||
FileOutputStream outputStream = null;
|
||||
try {
|
||||
inputStream = response.body().byteStream();
|
||||
// 1. 获取并创建指定保存目录(外部存储/Pictures/PowerBell/BackgroundHistory)
|
||||
File saveDir = getTargetSaveDir(context);
|
||||
if (!saveDir.exists()) {
|
||||
boolean isDirCreated = saveDir.mkdirs(); // 递归创建多级目录
|
||||
if (!isDirCreated) {
|
||||
if (callback != null) {
|
||||
callback.onFailure(new IOException("创建目录失败:" + saveDir.getAbsolutePath()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 解析图片后缀
|
||||
String fileSuffix = getImageSuffix(imgUrl, response);
|
||||
// 3. 生成时间戳文件名
|
||||
String fileName = generateTimeFileName() + fileSuffix;
|
||||
// 4. 创建文件
|
||||
final File saveFile = new File(saveDir, fileName);
|
||||
|
||||
// 5. 写入文件
|
||||
outputStream = new FileOutputStream(saveFile);
|
||||
byte[] buffer = new byte[1024 * 4];
|
||||
int len;
|
||||
while ((len = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, len);
|
||||
}
|
||||
outputStream.flush();
|
||||
|
||||
// 6. 通知相册刷新(使图片显示在系统相册中)
|
||||
notifyAlbumRefresh(context, saveFile);
|
||||
|
||||
// 成功回调
|
||||
if (callback != null) {
|
||||
callback.onSuccess(saveFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "下载图片异常", e);
|
||||
if (callback != null) {
|
||||
callback.onFailure(e);
|
||||
}
|
||||
} finally {
|
||||
// 关闭资源
|
||||
if (inputStream != null) inputStream.close();
|
||||
if (outputStream != null) outputStream.close();
|
||||
if (response.body() != null) response.body().close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call call, final IOException e) {
|
||||
Log.e(TAG, "下载图片失败", e);
|
||||
if (callback != null) {
|
||||
callback.onFailure(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目标保存目录:外部存储/Pictures/PowerBell/BackgroundHistory
|
||||
*/
|
||||
private static File getTargetSaveDir(Context context) {
|
||||
// 优先使用公共Pictures目录(兼容多数设备)
|
||||
File publicPicturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||
if (publicPicturesDir.exists()) {
|
||||
return new File(publicPicturesDir, ROOT_DIR);
|
||||
}
|
||||
// 备选:应用私有Pictures目录(若公共目录不可用)
|
||||
File appPicturesDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
|
||||
if (appPicturesDir != null) {
|
||||
return new File(appPicturesDir, ROOT_DIR);
|
||||
}
|
||||
// 极端情况:外部存储根目录
|
||||
return new File(Environment.getExternalStorageDirectory(), ROOT_DIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析图片后缀名
|
||||
*/
|
||||
private static String getImageSuffix(String imgUrl, Response response) {
|
||||
// 优先从URL解析
|
||||
if (imgUrl.lastIndexOf('.') != -1) {
|
||||
String suffix = imgUrl.substring(imgUrl.lastIndexOf('.'));
|
||||
if (suffix.length() <= 5 && (suffix.contains("png") || suffix.contains("jpg") || suffix.contains("jpeg") || suffix.contains("gif"))) {
|
||||
return suffix.toLowerCase(Locale.getDefault());
|
||||
}
|
||||
}
|
||||
// 从响应头解析
|
||||
String contentType = response.header("Content-Type");
|
||||
if (contentType != null) {
|
||||
if (contentType.contains("png")) return ".png";
|
||||
if (contentType.contains("jpeg") || contentType.contains("jpg")) return ".jpg";
|
||||
if (contentType.contains("gif")) return ".gif";
|
||||
}
|
||||
return ".jpg"; // 默认后缀
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成时间戳文件名
|
||||
*/
|
||||
private static String generateTimeFileName() {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.getDefault());
|
||||
return sdf.format(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知相册刷新
|
||||
*/
|
||||
private static void notifyAlbumRefresh(Context context, File file) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
Uri uri = Uri.fromFile(file);
|
||||
intent.setData(uri);
|
||||
context.sendBroadcast(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "通知相册刷新失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载结果回调接口
|
||||
*/
|
||||
public interface DownloadCallback {
|
||||
void onSuccess(String savePath); // 下载成功(子线程回调)
|
||||
void onFailure(Exception e); // 下载失败(子线程回调)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import java.util.List;
|
||||
|
||||
public class ServiceUtils {
|
||||
|
||||
public static final String TAG = ServiceUtils.class.getSimpleName();
|
||||
|
||||
public static boolean isServiceAlive(Context context, String szServiceName) {
|
||||
// 获取Activity管理者对象
|
||||
ActivityManager manager = (ActivityManager) context
|
||||
.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
// 获取正在运行的服务(此处设置最多取1000个)
|
||||
List<ActivityManager.RunningServiceInfo> runningServices = manager
|
||||
.getRunningServices(1000);
|
||||
if (runningServices.size() <= 0) {
|
||||
return false;
|
||||
}
|
||||
// 遍历,若存在名字和传入的serviceName的一致则说明存在
|
||||
for (ActivityManager.RunningServiceInfo runningServiceInfo : runningServices) {
|
||||
if (runningServiceInfo.service.getClassName().equals(szServiceName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import cc.winboll.studio.powerbell.beans.BatteryInfoBean;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class StringUtils {
|
||||
public static final String TAG = StringUtils.class.getSimpleName();
|
||||
|
||||
// 电量改变使用分钟数列表
|
||||
// List of power-changing usage minutes
|
||||
//
|
||||
public static String formatPCMListString(ArrayList<BatteryInfoBean> arrayListBatteryInfo) {
|
||||
/* 调试数据
|
||||
Time t1 = new Time();
|
||||
//t.set(int second, int minute, int hour, int monthDay, int month, int year) {}
|
||||
t1.set(4, 8, 0, 27, 4, 2022);
|
||||
long ntime1 = t1.toMillis(true);
|
||||
Time t2 = new Time();
|
||||
//t.set(int second, int minute, int hour, int monthDay, int month, int year) {}
|
||||
t2.set(9, 12, 3, 29, 4, 2022);
|
||||
long ntime2 = t2.toMillis(true);
|
||||
LogUtils.d(TAG, "ntime1 is " + Long.toString(ntime1));
|
||||
LogUtils.d(TAG, "ntime2 is " + Long.toString(ntime2));
|
||||
LogUtils.d(TAG, "getTimespanDifference(ntime1, ntime2) is " + getTimespanDifference(ntime1, ntime2));
|
||||
*/
|
||||
|
||||
/*String sz = "";
|
||||
for (int i = 0; i < lnTime.size() - 1; i++) {
|
||||
sz += getTimespanDifference(lnTime.get(i), lnTime.get(i + 1));
|
||||
}
|
||||
return sz;*/
|
||||
|
||||
String sz = "";
|
||||
for (int i = 0; i < arrayListBatteryInfo.size() - 1; i++) {
|
||||
//LogUtils.d(TAG, "arrayListBatteryInfo.get(i).getBattetyValue() is "+ Integer.toString(arrayListBatteryInfo.get(i).getBattetyValue()));
|
||||
sz = arrayListBatteryInfo.get(i).getBattetyValue() + "% " + getTimespanDifference(arrayListBatteryInfo.get(i).getTimeStamp(), arrayListBatteryInfo.get(i + 1).getTimeStamp()) + " " + sz;
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
public static String formatPCMListStringWithEnter(ArrayList<BatteryInfoBean> arrayListBatteryInfo) {
|
||||
String sz = "";
|
||||
for (int i = 0; i < arrayListBatteryInfo.size() - 1; i++) {
|
||||
//LogUtils.d(TAG, "arrayListBatteryInfo.get(i).getBattetyValue() is "+ Integer.toString(arrayListBatteryInfo.get(i).getBattetyValue()));
|
||||
sz = "\n" + arrayListBatteryInfo.get(i).getBattetyValue() + "%\n " + getTimespanDifference(arrayListBatteryInfo.get(i).getTimeStamp(), arrayListBatteryInfo.get(i + 1).getTimeStamp()) + " " + sz;
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
// 获取时间之间的时间跨度字符串。
|
||||
// Get timespan string between times.
|
||||
// 返回值: {(几天/)(几小时/)(几分钟/)(几秒钟)}
|
||||
// 返回值: {(几小时/)(几分钟/)(几秒钟)}
|
||||
// 返回值: {(几分钟/)(几秒钟)}
|
||||
// 返回值: {(几秒钟)}
|
||||
// (注:start == end 时) 返回值: {0}
|
||||
public static String getTimespanDifference(long start, long end) {
|
||||
String szReturn = "{";
|
||||
long between = end - start;
|
||||
//LogUtils.d(TAG, "between is " + Long.toString(between));
|
||||
long day = between / (24 * 60 * 60 * 1000);
|
||||
long hour = (between / (60 * 60 * 1000) - day * 24);
|
||||
long min = ((between / (60 * 1000)) - day * 24 * 60 - hour * 60);
|
||||
long s = (between / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
|
||||
/* 调试数据
|
||||
day = 0;
|
||||
hour = 2;
|
||||
min = 0;
|
||||
s = 7;
|
||||
*/
|
||||
|
||||
//long ms = (between - day * 24 * 60 * 60 * 1000 - hour * 60 * 60 * 1000
|
||||
//- min * 60 * 1000 - s * 1000);
|
||||
|
||||
szReturn += day > 0 ? String.format(java.util.Locale.getDefault(), "%d☀", day) : "";
|
||||
szReturn += hour > 0 || day > 0 ? String.format(java.util.Locale.getDefault(), "%d★", hour) : "";
|
||||
szReturn += min > 0 || hour > 0 || day > 0 ? String.format(java.util.Locale.getDefault(), "%d✰", min) : "";
|
||||
szReturn += min > 0 || hour > 0 || day > 0 ? String.format(java.util.Locale.getDefault(), "%d}", s) : "☆}";
|
||||
|
||||
//String strmin = String.format("%02d", min);
|
||||
//String strs = String.format("%02d", s);
|
||||
//String strms = String.format("%03d",ms);
|
||||
//String timeDifference = day + "天" + hour + "小时" + strmin + "分" + strs + "秒" + strms + "毫秒";
|
||||
//String timeDifference = hour + getString(R.string.activity_main_msg_hour)
|
||||
// + strmin + getString(R.string.activity_main_msg_minute)
|
||||
// + strs + getString(R.string.activity_main_msg_second);
|
||||
//return timeDifference;
|
||||
|
||||
return szReturn;
|
||||
}
|
||||
|
||||
// 调试函数: 调试formatPCMListString(ArrayList<Long> lnTime)
|
||||
//
|
||||
/*public static String formatPCMListString_test() {
|
||||
// 调试数据
|
||||
ArrayList<Long> listTime = new ArrayList<Long>();
|
||||
Time t1 = new Time();
|
||||
//t.set(int second, int minute, int hour, int monthDay, int month, int year) {}
|
||||
t1.set(0, 8, 0, 27, 4, 2022);
|
||||
long ntime1 = t1.toMillis(true);
|
||||
listTime.add(ntime1);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Time t2 = new Time();
|
||||
//t.set(int second, int minute, int hour, int monthDay, int month, int year) {}
|
||||
t2.set(4, 8 + i + 1, 0, 27, 4, 2022);
|
||||
long ntime2 = t2.toMillis(true);
|
||||
listTime.add(ntime2);
|
||||
}
|
||||
|
||||
return formatPCMListString(listTime);
|
||||
//LogUtils.d(TAG, StringUtils.formatPCMListString(listTime));
|
||||
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/06/28 04:23:04
|
||||
* @Describe UriUtil
|
||||
*/
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore;
|
||||
import androidx.core.content.FileProvider;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class UriUtil {
|
||||
|
||||
public static final String TAG = "UriUtil";
|
||||
|
||||
/**
|
||||
* 获取真实路径
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public static String getFilePathFromUri(Context context, Uri uri) {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
switch (uri.getScheme()) {
|
||||
case ContentResolver.SCHEME_CONTENT:
|
||||
//Android7.0之后的uri content:// URI
|
||||
return getFilePathFromContentUri(context, uri);
|
||||
case ContentResolver.SCHEME_FILE:
|
||||
default:
|
||||
//Android7.0之前的uri file://
|
||||
return new File(uri.getPath()).getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从uri获取path
|
||||
*
|
||||
* @param uri content://media/external/file/109009
|
||||
* <p>
|
||||
* FileProvider适配
|
||||
* content://com.tencent.mobileqq.fileprovider/external_files/storage/emulated/0/Tencent/QQfile_recv/
|
||||
* content://com.tencent.mm.external.fileprovider/external/tencent/MicroMsg/Download/
|
||||
*/
|
||||
private static String getFilePathFromContentUri(Context context, Uri uri) {
|
||||
if (null == uri) return null;
|
||||
String data = null;
|
||||
|
||||
String[] filePathColumn = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME};
|
||||
Cursor cursor = context.getContentResolver().query(uri, filePathColumn, null, null, null);
|
||||
if (null != cursor) {
|
||||
if (cursor.moveToFirst()) {
|
||||
int index = cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
|
||||
if (index > -1) {
|
||||
data = cursor.getString(index);
|
||||
} else {
|
||||
int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||
String fileName = cursor.getString(nameIndex);
|
||||
data = getPathFromInputStreamUri(context, uri, fileName);
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用流拷贝文件一份到自己APP私有目录下
|
||||
*
|
||||
* @param context
|
||||
* @param uri
|
||||
* @param fileName
|
||||
*/
|
||||
private static String getPathFromInputStreamUri(Context context, Uri uri, String fileName) {
|
||||
InputStream inputStream = null;
|
||||
String filePath = null;
|
||||
|
||||
if (uri.getAuthority() != null) {
|
||||
try {
|
||||
inputStream = context.getContentResolver().openInputStream(uri);
|
||||
File file = createTemporalFileFrom(context, inputStream, fileName);
|
||||
filePath = file.getPath();
|
||||
|
||||
} catch (Exception e) {
|
||||
} finally {
|
||||
try {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public static Uri getUriForFile(Context context, File file) {
|
||||
//Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
|
||||
if (Build.VERSION.SDK_INT >= 24) {//android 7.0以上
|
||||
return FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
|
||||
}
|
||||
return Uri.fromFile(file);
|
||||
}
|
||||
|
||||
private static File createTemporalFileFrom(Context context, InputStream inputStream, String fileName)
|
||||
throws IOException {
|
||||
File targetFile = null;
|
||||
|
||||
if (inputStream != null) {
|
||||
int read;
|
||||
byte[] buffer = new byte[8 * 1024];
|
||||
//自己定义拷贝文件路径
|
||||
targetFile = new File(context.getExternalCacheDir(), fileName);
|
||||
if (targetFile.exists()) {
|
||||
targetFile.delete();
|
||||
}
|
||||
OutputStream outputStream = new FileOutputStream(targetFile);
|
||||
|
||||
while ((read = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, read);
|
||||
}
|
||||
outputStream.flush();
|
||||
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return targetFile;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
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.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;
|
||||
|
||||
/**
|
||||
* @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; // 默认 1:1
|
||||
// 标记当前是否处于预览状态
|
||||
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() {
|
||||
initBackgroundImageView();
|
||||
initBackgroundImagePath();
|
||||
loadAndSetImageViewBackground();
|
||||
}
|
||||
|
||||
private void initBackgroundImageView() {
|
||||
ivBackground = new ImageView(mContext);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
|
||||
ivBackground.setLayoutParams(layoutParams);
|
||||
ivBackground.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
this.addView(ivBackground);
|
||||
}
|
||||
|
||||
private void initBackgroundImagePath() {
|
||||
File externalFilesDir = mContext.getExternalFilesDir(null);
|
||||
if (externalFilesDir == null) {
|
||||
LogUtils.e(TAG, "外置存储不可用,无法初始化背景图片路径");
|
||||
return;
|
||||
}
|
||||
|
||||
File backgroundDir = new File(externalFilesDir, BACKGROUND_IMAGE_FOLDER);
|
||||
if (!backgroundDir.exists()) {
|
||||
backgroundDir.mkdirs();
|
||||
}
|
||||
backgroundSourceFilePath = new File(backgroundDir, BACKGROUND_IMAGE_FILENAME).getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝图片文件到背景资源目录(正式背景)
|
||||
*/
|
||||
public void saveToBackgroundSources(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, "预览图片路径为空");
|
||||
return;
|
||||
}
|
||||
|
||||
File previewFile = new File(previewImagePath);
|
||||
if (!previewFile.exists() || !previewFile.isFile()) {
|
||||
LogUtils.e(TAG, "预览图片不存在或不是文件:" + previewImagePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算预览图片宽高比
|
||||
if (!calculateImageAspectRatio(previewFile)) {
|
||||
LogUtils.e(TAG, "预览图片尺寸无效,无法预览");
|
||||
return;
|
||||
}
|
||||
|
||||
// 压缩加载预览图片
|
||||
Bitmap previewBitmap = decodeBitmapWithCompress(previewFile, 1080, 1920);
|
||||
if (previewBitmap == null) {
|
||||
LogUtils.e(TAG, "预览图片加载失败");
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 【新增公共函数】退出预览模式,恢复显示正式背景图片
|
||||
*/
|
||||
public void exitPreviewMode() {
|
||||
if (isPreviewMode) {
|
||||
loadAndSetImageViewBackground();
|
||||
isPreviewMode = false;
|
||||
LogUtils.d(TAG, "退出预览模式,恢复正式背景");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 公共函数:供外部类调用,重新加载正式背景图片(刷新显示)
|
||||
*/
|
||||
public void reloadBackgroundImage() {
|
||||
LogUtils.d(TAG, "外部调用重新加载背景图片");
|
||||
initBackgroundImagePath();
|
||||
loadAndSetImageViewBackground();
|
||||
// 若处于预览模式,退出预览
|
||||
if (isPreviewMode) {
|
||||
isPreviewMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载正式背景图片并设置到 ImageView
|
||||
*/
|
||||
private void loadAndSetImageViewBackground() {
|
||||
if (backgroundSourceFilePath == null) {
|
||||
setDefaultImageViewBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
File backgroundFile = new File(backgroundSourceFilePath);
|
||||
if (!backgroundFile.exists() || !backgroundFile.isFile()) {
|
||||
LogUtils.e(TAG, "背景图片不存在:" + backgroundSourceFilePath);
|
||||
setDefaultImageViewBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!calculateImageAspectRatio(backgroundFile)) {
|
||||
setDefaultImageViewBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
Bitmap bitmap = decodeBitmapWithCompress(backgroundFile, 1080, 1920);
|
||||
if (bitmap == null) {
|
||||
LogUtils.e(TAG, "图片加载失败,无法解析为 Bitmap");
|
||||
setDefaultImageViewBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 带压缩的 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.setBackgroundResource(R.drawable.default_background);
|
||||
imageAspectRatio = 1.0f;
|
||||
adjustImageViewSize();
|
||||
LogUtils.d(TAG, "已设置 ImageView 默认背景");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
adjustImageViewSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对外提供:判断当前是否处于预览模式
|
||||
*/
|
||||
public boolean isPreviewMode() {
|
||||
return isPreviewMode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package cc.winboll.studio.powerbell.views;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.PixelFormat;
|
||||
|
||||
public class BatteryDrawable extends Drawable {
|
||||
public static final String TAG = BatteryDrawable.class.getSimpleName();
|
||||
|
||||
// 电量颜色画笔
|
||||
final Paint mPaint;
|
||||
// 电量值
|
||||
int mnValue = 1;
|
||||
|
||||
// @int color : 电量颜色
|
||||
//
|
||||
public BatteryDrawable(int color) {
|
||||
mPaint = new Paint();
|
||||
mPaint.setColor(color);
|
||||
mPaint.setAlpha(210);
|
||||
}
|
||||
|
||||
// 设置电量值
|
||||
//
|
||||
public void setValue(int value) {
|
||||
mnValue = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
int nWidth = getBounds().width();
|
||||
int nHeight = getBounds().height();
|
||||
int mnDx = nHeight / 203;
|
||||
|
||||
// 绘制耗电电量提醒值电量
|
||||
// 能量绘图风格
|
||||
int nTop;
|
||||
int nLeft = 0;
|
||||
int nBottom;
|
||||
int nRight = nWidth;
|
||||
|
||||
//for (int i = 0; i < mnValue; i ++) {
|
||||
nBottom = nHeight;
|
||||
nTop = nHeight - (nHeight * mnValue / 100);
|
||||
canvas.drawRect(new Rect(nLeft, nTop, nRight, nBottom), mPaint);
|
||||
|
||||
// 绘制耗电电量提醒值电量
|
||||
// 意兴阑珊绘图风格
|
||||
/*int nTop;
|
||||
int nLeft = 0;
|
||||
int nBottom;
|
||||
int nRight = nWidth;
|
||||
|
||||
for (int i = 0; i < mnValue; i ++) {
|
||||
nBottom = (nHeight * (100-i)/100) - mnDx;
|
||||
nTop = nBottom + mnDx;
|
||||
canvas.drawRect(new Rect(nLeft, nTop, nRight, nBottom), mPaint);
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
// This method is required
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int p1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.UNKNOWN;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package cc.winboll.studio.powerbell.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
public class VerticalSeekBar extends SeekBar {
|
||||
public static final String TAG = VerticalSeekBar.class.getSimpleName();
|
||||
|
||||
public volatile int _mnProgress = -1;
|
||||
|
||||
public VerticalSeekBar(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
public VerticalSeekBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
// 去除冗余的水平阴影
|
||||
setBackgroundDrawable(null);
|
||||
|
||||
}
|
||||
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(h, w, oldh, oldw);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
|
||||
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
|
||||
}
|
||||
|
||||
protected void onDraw(Canvas c) {
|
||||
// 0--------100,顺时针旋转,小在上
|
||||
// c.rotate(+90);
|
||||
// c.translate(0, -getWidth());
|
||||
|
||||
// 0--------100,逆时针旋转,小在下
|
||||
c.rotate(-90);
|
||||
c.translate(-getHeight(), 0);
|
||||
|
||||
super.onDraw(c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// 调用基类的处理函数
|
||||
// 该方法可以使得
|
||||
// SeekBar.OnSeekBarChangeListener
|
||||
// 的 onStopTrackingTouch 和 onStartTrackingTouch 等函数有效。
|
||||
boolean handled = super.onTouchEvent(event);
|
||||
|
||||
if (handled) {
|
||||
int action = event.getAction();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// 0--------100,顺时针旋转,小在上
|
||||
//_mnProgress = (int)(getMax() * event.getY() / getHeight());
|
||||
// // 0--------100,逆时针旋转,小在下
|
||||
_mnProgress = getMax() - (int) (getMax() * event.getY() / getHeight());
|
||||
_mnProgress = _mnProgress > 100 ? 100 : _mnProgress ;
|
||||
//LogUtils.d(TAG, "_mnProgress is " + Integer.toString(_mnProgress));
|
||||
setProgress(_mnProgress);
|
||||
//onSizeChanged(getWidth(), getHeight(), 0, 0);
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
break;
|
||||
default :
|
||||
//LogUtils.d(TAG, "event.getAction() is " + event.getAction());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
// 解决调用setProgress()方法时滑块不跟随的bug
|
||||
@Override
|
||||
public synchronized void setProgress(int progress) {
|
||||
super.setProgress(progress);
|
||||
onSizeChanged(getWidth(), getHeight(), 0, 0);
|
||||
}
|
||||
}
|
||||
41
powerbell/src/main/res/drawable/bg_frame.xml
Normal file
41
powerbell/src/main/res/drawable/bg_frame.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<!-- 阴影部分 -->
|
||||
<!-- 个人觉得更形象的表达:top代表下边的阴影高度,left代表右边的阴影宽度。其实也就是相对应的offset,solid中的颜色是阴影的颜色,也可以设置角度等等 -->
|
||||
<item
|
||||
android:left="2dp"
|
||||
android:top="2dp"
|
||||
android:right="2dp"
|
||||
android:bottom="2dp">
|
||||
<shape android:shape="rectangle" >
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:endColor="#0F000000"
|
||||
android:startColor="#0F000000" />
|
||||
<corners
|
||||
android:bottomLeftRadius="6dip"
|
||||
android:bottomRightRadius="6dip"
|
||||
android:topLeftRadius="6dip"
|
||||
android:topRightRadius="6dip" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 背景部分 -->
|
||||
<!-- 形象的表达:bottom代表背景部分在上边缘超出阴影的高度,right代表背景部分在左边超出阴影的宽度(相对应的offset) -->
|
||||
<item
|
||||
android:left="3dp"
|
||||
android:top="3dp"
|
||||
android:right="3dp"
|
||||
android:bottom="5dp">
|
||||
<shape android:shape="rectangle" >
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:endColor="#0FFFFFFF"
|
||||
android:startColor="#FFFFFFFF" />
|
||||
<corners
|
||||
android:bottomLeftRadius="6dip"
|
||||
android:bottomRightRadius="6dip"
|
||||
android:topLeftRadius="6dip"
|
||||
android:topRightRadius="6dip" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
BIN
powerbell/src/main/res/drawable/blank10x10.png
Normal file
BIN
powerbell/src/main/res/drawable/blank10x10.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 B |
BIN
powerbell/src/main/res/drawable/charge.png
Normal file
BIN
powerbell/src/main/res/drawable/charge.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 642 B |
20
powerbell/src/main/res/drawable/cursor_pointer.xml
Normal file
20
powerbell/src/main/res/drawable/cursor_pointer.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#00FFFFFF"
|
||||
android:strokeColor="#FF000000"
|
||||
android:strokeWidth="2.0"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeMiterLimit="10"
|
||||
android:pathData="M24.17 5.23C34.61 5.23 43.16 13.78 43.16 24.22 43.16 34.66 34.61 43.2 24.17 43.2 13.73 43.2 5.18 34.66 5.18 24.22 5.18 13.78 13.73 5.23 24.17 5.23"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF000000"
|
||||
android:strokeWidth="2.0"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeMiterLimit="10"
|
||||
android:pathData="M25.8 20.4C25.8 20.4 26.6 20.6 26.6 20.6 26.6 20.6 31.92 23.77 31.92 23.77 32.81 24.12 33.39 25.17 33.32 26.3 33.32 26.3 33.32 26.5 33.32 26.5 33.32 26.5 33.32 26.7 33.32 26.7 33.32 26.7 32.17 36.04 32.17 36.04 32.09 36.69 31.85 37.3 31.41 37.73 31.01 38.19 30.49 38.42 29.94 38.42 29.94 38.42 21.18 38.42 21.18 38.42 20.55 38.42 19.98 38.14 19.56 37.61 19.56 37.61 11.93 28.5 11.93 28.5 11.93 28.5 13.07 26.97 13.07 26.97 13.38 26.59 13.86 26.38 14.32 26.41 14.32 26.41 14.69 26.41 14.69 26.41 14.69 26.41 19.75 27.74 19.75 27.74 19.75 27.74 19.75 11.72 19.75 11.72 19.75 10.03 20.89 8.66 22.3 8.66 23.7 8.66 24.85 10.03 24.85 11.72 24.85 11.72 24.85 20.4 24.85 20.4 24.85 20.4 25.8 20.4 25.8 20.4 25.8 20.4 25.8 20.4 25.8 20.4"/>
|
||||
</vector>
|
||||
170
powerbell/src/main/res/drawable/default_background.xml
Normal file
170
powerbell/src/main/res/drawable/default_background.xml
Normal 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>
|
||||
7
powerbell/src/main/res/drawable/divider_line.xml
Normal file
7
powerbell/src/main/res/drawable/divider_line.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="line">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#E0E0E0"/>
|
||||
</shape>
|
||||
27
powerbell/src/main/res/drawable/ic_launcher.xml
Normal file
27
powerbell/src/main/res/drawable/ic_launcher.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<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="#FF009600"
|
||||
android:strokeColor="#00009D00"
|
||||
android:strokeWidth="2.0"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeMiterLimit="10"
|
||||
android:pathData="M62.77 11.19C62.67 11.19 62.58 11.19 62.48 11.19 52.61 11.19 39.45 11.19 29.58 11.19 27.65 11.18 27.99 12.03 27.99 14.19 27.99 14.87 27.99 15.78 27.99 16.46 27.99 17.08 27.59 18.51 28.23 18.5 38.1 18.29 51.26 18.5 61.13 18.5 61.4 18.5 62.76 18.7 62.77 17.81 62.8 15.94 62.77 13.44 62.77 11.57 62.77 11.47 62.77 11.35 62.77 11.29 62.77 11.22 62.77 11.2 62.77 11.19M72.1 99.32C72.11 75.14 72.38 42.9 72.1 18.72 72.1 18.07 69.2 18.5 67.63 18.5 53.53 18.5 34.73 18.5 20.63 18.5 20.33 18.5 18.6 18.19 18.66 18.87 19.09 24.3 18.66 31.58 18.66 37.03 18.66 54.74 18.66 78.36 18.66 96.07 18.66 97.05 17.82 99.13 18.81 99.32 20.19 99.59 22.1 99.32 23.51 99.32 37.97 99.32 57.24 99.32 71.69 99.32 71.79 99.32 71.91 99.32 71.98 99.32 72.05 99.32 72.08 99.32 72.1 99.32"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FF000000"
|
||||
android:strokeWidth="2.0"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeMiterLimit="10"
|
||||
android:pathData="M95.69 46.15C95.69 46.06 95.69 45.96 95.68 45.87 95.62 42.18 93.47 32.14 79.53 27.34 78.29 26.91 75.48 25.94 70.99 25.58 57.33 24.5 41.98 31.96 40.58 44.42 40.29 47.08 40.58 50.91 43.06 54.84 43.67 55.81 42.82 56.28 41.94 58.72 40.03 64.07 45.17 66.64 54.03 67.28 60.63 67.77 64.55 67.28 71.29 66.69 73.45 66.5 76.23 66.12 79.77 64.87 86.98 62.33 95.34 56.16 95.68 46.7 95.68 46.6 95.68 46.51 95.68 46.42 95.69 46.33 95.69 46.24 95.69 46.15"/>
|
||||
<path
|
||||
android:fillColor="#00FFFFFF"
|
||||
android:strokeColor="#FF000000"
|
||||
android:strokeWidth="2.0"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeMiterLimit="10"
|
||||
android:pathData="M241.47 177.43C241.47 177.43 241.47 177.43 241.47 177.43 241.47 177.43 241.47 177.43 241.47 177.43"/>
|
||||
</vector>
|
||||
170
powerbell/src/main/res/drawable/ic_launcher_background.xml
Normal file
170
powerbell/src/main/res/drawable/ic_launcher_background.xml
Normal 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>
|
||||
11
powerbell/src/main/res/drawable/ic_launcher_beta.xml
Normal file
11
powerbell/src/main/res/drawable/ic_launcher_beta.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:clickable="true">
|
||||
<item android:drawable="@drawable/ic_launcher_background"/>
|
||||
<item
|
||||
android:left="0dp"
|
||||
android:top="0dp"
|
||||
android:right="0dp"
|
||||
android:bottom="0dp"
|
||||
android:drawable="@drawable/ic_launcher"/>
|
||||
</layer-list>
|
||||
BIN
powerbell/src/main/res/drawable/usege.png
Normal file
BIN
powerbell/src/main/res/drawable/usege.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 434 B |
BIN
powerbell/src/main/res/drawable/xiaobai.png
Normal file
BIN
powerbell/src/main/res/drawable/xiaobai.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
31
powerbell/src/main/res/layout/activity_about.xml
Normal file
31
powerbell/src/main/res/layout/activity_about.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<cc.winboll.studio.libaes.views.AToolbar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/toolbar_height"
|
||||
android:id="@+id/toolbar"
|
||||
android:gravity="center_vertical"
|
||||
style="@style/DefaultAToolbar"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0"
|
||||
android:id="@+id/root_ll"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
140
powerbell/src/main/res/layout/activity_backgroundpicture.xml
Normal file
140
powerbell/src/main/res/layout/activity_backgroundpicture.xml
Normal file
@@ -0,0 +1,140 @@
|
||||
<?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"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/activitybackgroundpictureRelativeLayout1"/>
|
||||
|
||||
<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_height="match_parent"
|
||||
android:background="#FF7381FF"
|
||||
android:id="@+id/activitybackgroundpictureBackgroundView1">
|
||||
|
||||
</cc.winboll.studio.powerbell.views.BackgroundView>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/toolbar">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<cc.winboll.studio.libaes.views.AButton
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="36dp"
|
||||
android:text="Origin BG"
|
||||
android:id="@+id/activitybackgroundpictureAButton5"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_margin="5dp"/>
|
||||
|
||||
<cc.winboll.studio.libaes.views.AButton
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="36dp"
|
||||
android:text="Received BG"
|
||||
android:id="@+id/activitybackgroundpictureAButton4"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_margin="5dp"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right">
|
||||
|
||||
<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/activitybackgroundpictureAButton1"/>
|
||||
|
||||
<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/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
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="36dp"
|
||||
android:text="[+]"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_margin="5dp"
|
||||
android:id="@+id/activitybackgroundpictureAButton3"/>
|
||||
|
||||
<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/activitybackgroundpictureAButton6"/>
|
||||
|
||||
<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/activitybackgroundpictureAButton7"/>
|
||||
|
||||
<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/activitybackgroundpictureAButton8"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
30
powerbell/src/main/res/layout/activity_battery_report.xml
Normal file
30
powerbell/src/main/res/layout/activity_battery_report.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<!-- 搜索框:提示文本改为“搜索应用名称或包名” -->
|
||||
<EditText
|
||||
android:id="@+id/et_search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:padding="12dp"
|
||||
android:hint="搜索应用名称或包名"
|
||||
android:background="@android:drawable/btn_default_small"
|
||||
android:inputType="text"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
<!-- 应用列表 -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_battery_report"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
94
powerbell/src/main/res/layout/activity_clearrecord.xml
Normal file
94
powerbell/src/main/res/layout/activity_clearrecord.xml
Normal file
@@ -0,0 +1,94 @@
|
||||
<?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"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="30dp">
|
||||
|
||||
<cc.winboll.studio.libaes.views.AOHPCTCSeekBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/activityclearrecordAOHPCTCSeekBar1"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/activityclearrecordTextView1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_weight="1.0"
|
||||
android:background="@drawable/bg_frame">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Record Text"
|
||||
android:textStyle="bold"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:background="#FFD5D5D5"/>
|
||||
|
||||
<Switch
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="<↲>"
|
||||
android:id="@+id/activityclearrecordSwitch1"
|
||||
android:onClick="onShowRecordWithEnter"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:textSize="@dimen/text_content_size"
|
||||
android:id="@+id/activityclearrecordTextView2"/>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
40
powerbell/src/main/res/layout/activity_main.xml
Normal file
40
powerbell/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<cc.winboll.studio.libaes.views.ASupportToolbar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/toolbar_height"
|
||||
android:id="@+id/toolbar"
|
||||
android:gravity="center_vertical"
|
||||
style="@style/DefaultAToolbar"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/activitymainRelativeLayout1"
|
||||
android:background="#FFB7B7B7"/>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/activitymainFrameLayout1"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<cc.winboll.studio.libaes.views.ADsBannerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/adsbanner"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
15
powerbell/src/main/res/layout/activity_mainunittest.xml
Normal file
15
powerbell/src/main/res/layout/activity_mainunittest.xml
Normal 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>
|
||||
|
||||
54
powerbell/src/main/res/layout/activity_pixelpicker.xml
Normal file
54
powerbell/src/main/res/layout/activity_pixelpicker.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<cc.winboll.studio.libaes.views.AToolbar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/toolbar_height"
|
||||
android:id="@+id/toolbar"
|
||||
android:gravity="center_vertical"
|
||||
style="@style/DefaultAToolbar"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#FFEE2121"
|
||||
android:id="@+id/activitypixelpickerRelativeLayout1"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/imageContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/ic_launcher"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/infoText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:padding="10dp"
|
||||
android:background="#80000000"
|
||||
android:textColor="#FFFFFF"
|
||||
android:text="点击图片获取像素值"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
23
powerbell/src/main/res/layout/activity_unbelievable.xml
Normal file
23
powerbell/src/main/res/layout/activity_unbelievable.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Text"
|
||||
android:id="@+id/activityunbelievableTextView1"
|
||||
android:padding="10dp"
|
||||
android:textIsSelectable="true"/>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
11
powerbell/src/main/res/layout/custom_notification.xml
Normal file
11
powerbell/src/main/res/layout/custom_notification.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?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"
|
||||
android:id="@+id/ll_notification">
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?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"
|
||||
android:id="@+id/ll_notification">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Text"
|
||||
android:id="@+id/info_tv"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?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="300dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#FF1FD3CE">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="是否使用下列图片作为应用背景图?"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical|center_horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="centerCrop"
|
||||
android:id="@+id/dialogbackgroundpicturepreviewImageView1"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right">
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Cancel"
|
||||
android:id="@+id/dialogbackgroundpicturepreviewButton1"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="OK"
|
||||
android:id="@+id/dialogbackgroundpicturepreviewButton2"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
93
powerbell/src/main/res/layout/dialog_networkbackground.xml
Normal file
93
powerbell/src/main/res/layout/dialog_networkbackground.xml
Normal 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>
|
||||
|
||||
32
powerbell/src/main/res/layout/dialog_pixel.xml
Normal file
32
powerbell/src/main/res/layout/dialog_pixel.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pixelColorView"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="200dp"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/colorInfoText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textColor="#333333"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/confirmButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@android:color/holo_blue_light"
|
||||
android:text="确定"
|
||||
android:textColor="@android:color/white" />
|
||||
</LinearLayout>
|
||||
|
||||
218
powerbell/src/main/res/layout/fragment_mainview.xml
Normal file
218
powerbell/src/main/res/layout/fragment_mainview.xml
Normal file
@@ -0,0 +1,218 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<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_height="match_parent"
|
||||
android:background="#FF7381FF"
|
||||
android:id="@+id/fragmentmainviewBackgroundView1"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentmainviewLinearLayout3"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@drawable/bg_frame">
|
||||
|
||||
<Switch
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewSwitch1"
|
||||
android:padding="10dp"
|
||||
android:layout_weight="1.0"
|
||||
android:textSize="@dimen/text_title_size"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:id="@+id/fragmentmainviewLinearLayout1">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:background="@drawable/usege"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"/>
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:id="@+id/fragmentmainviewCheckBox2"/>
|
||||
|
||||
<cc.winboll.studio.powerbell.views.VerticalSeekBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewVerticalSeekBar2"
|
||||
android:progressTint="@color/colorUsege"
|
||||
android:progressBackgroundTint="@color/colorUsege"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_margin="10dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="100%"
|
||||
android:textSize="@dimen/text_title_size"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/fragmentandroidviewTextView3"
|
||||
android:gravity="center_horizontal"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewImageView2"
|
||||
android:layout_weight="1.0"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="100%"
|
||||
android:textSize="@dimen/text_title_size"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal"
|
||||
android:id="@+id/fragmentandroidviewTextView4"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewImageView1"
|
||||
android:layout_weight="1.0"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="100%"
|
||||
android:textSize="@dimen/text_title_size"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/fragmentandroidviewTextView2"
|
||||
android:gravity="center_horizontal"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewImageView3"
|
||||
android:layout_weight="1.0"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:id="@+id/fragmentmainviewLinearLayout2">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:background="@drawable/charge"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"/>
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:id="@+id/fragmentmainviewCheckBox1"/>
|
||||
|
||||
<cc.winboll.studio.powerbell.views.VerticalSeekBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewVerticalSeekBar1"
|
||||
android:progressTint="@color/colorCharge"
|
||||
android:progressBackgroundTint="@color/colorCharge"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_margin="10dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Tips"
|
||||
android:textSize="@dimen/text_content_size"
|
||||
android:id="@+id/fragmentandroidviewTextView1"
|
||||
android:background="@drawable/bg_frame"
|
||||
android:padding="10dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -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>
|
||||
|
||||
34
powerbell/src/main/res/layout/item_battery_report.xml
Normal file
34
powerbell/src/main/res/layout/item_battery_report.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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:orientation="horizontal"
|
||||
android:padding="12dp"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvLevel"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="18sp"
|
||||
android:fontFamily="sans-serif-medium"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDischargeTime"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="16sp"
|
||||
android:paddingStart="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvChargeTime"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="16sp"
|
||||
android:paddingStart="16dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
48
powerbell/src/main/res/layout/view_remindnotification.xml
Normal file
48
powerbell/src/main/res/layout/view_remindnotification.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/remoteviewLinearLayout1">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:id="@+id/remoteviewImageView1"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Title"
|
||||
android:textSize="@dimen/text_title_size"
|
||||
android:textStyle="bold"
|
||||
android:id="@+id/viewremindnotificationTextView1"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/colorUsege"
|
||||
android:background="@color/colorUsege"
|
||||
android:id="@+id/remoteviewUsege"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:id="@+id/remoteviewCharge"
|
||||
android:textColor="@color/colorCharge"
|
||||
android:background="@color/colorCharge"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
39
powerbell/src/main/res/layout/view_servicenotification.xml
Normal file
39
powerbell/src/main/res/layout/view_servicenotification.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/remoteviewLinearLayout1">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:id="@+id/remoteviewImageView1"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Text"
|
||||
android:textSize="@dimen/text_title_size"
|
||||
android:id="@+id/remoteviewTextView1"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Text"
|
||||
android:textSize="@dimen/text_content_size"
|
||||
android:id="@+id/remoteviewTextView3"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
15
powerbell/src/main/res/menu/toolbar_main.xml
Normal file
15
powerbell/src/main/res/menu/toolbar_main.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_battery_report"
|
||||
android:title="@string/item_battery_report"/>
|
||||
<item
|
||||
android:id="@+id/action_clearrecord"
|
||||
android:title="@string/item_clearrecord"/>
|
||||
<item
|
||||
android:id="@+id/action_changepicture"
|
||||
android:title="@string/item_changepicture"/>
|
||||
<item
|
||||
android:id="@+id/action_about"
|
||||
android:title="@string/item_aboutview"/>
|
||||
</menu>
|
||||
12
powerbell/src/main/res/menu/toolbar_unittest.xml
Normal file
12
powerbell/src/main/res/menu/toolbar_unittest.xml
Normal 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>
|
||||
22
powerbell/src/main/res/values-zh/strings.xml
Normal file
22
powerbell/src/main/res/values-zh/strings.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name_cn1">能源钟</string>
|
||||
<string name="app_name_cn2">泡额呗额</string>
|
||||
<string name="app_description">一个接收手机电量信息的应用,当电量值达到设定范围时会提醒用户。</string>
|
||||
<string name="about_crashed">本应用崩溃了,作者水平有限,敬请谅解!</string>
|
||||
<string name="item_mainview">Main View</string>
|
||||
<string name="item_aboutview">About</string>
|
||||
<string name="item_clearrecord">Clear Record</string>
|
||||
<string name="item_changepicture">Change Picture</string>
|
||||
<string name="item_devoloperoptionsview">Developer View</string>
|
||||
<string name="item_logview">Log View</string>
|
||||
<string name="item_sourceview">Source View</string>
|
||||
<string name="txt_aboveswitch">消息总开关</string>
|
||||
<string name="txt_aboveswitchtips">当电量低于左边(放电状态)或高于右边(充电状态),就会发送一个提醒铃声。</string>
|
||||
<string name="texthint_CustomSlideToCleanRecord">Slide Right To Clean Up APP Record.</string>
|
||||
<string name="subtitle_activity_clearrecord">清理记录</string>
|
||||
<string name="subtitle_activity_backgroundpicture">更换背景图片</string>
|
||||
<string name="subtitle_activity_pixelpicker">背景像素拾取</string>
|
||||
<string name="subtitle_activity_about">关于应用</string>
|
||||
<string name="msg_AOHPCTCSeekBar_ClearRecord">>>>向右滑动100%可以清理电量记录。>>></string>
|
||||
</resources>
|
||||
3
powerbell/src/main/res/values/attrs.xml
Normal file
3
powerbell/src/main/res/values/attrs.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
</resources>
|
||||
66
powerbell/src/main/res/values/colors.xml
Normal file
66
powerbell/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!--
|
||||
<color name="colorPrimary">@color/colorShui</color>
|
||||
<color name="colorPrimaryDark">@color/colorXinling</color>
|
||||
<color name="colorAccent">@color/colorShui</color>
|
||||
-->
|
||||
<!-- 地球超人主题配置方案 -->
|
||||
<!--
|
||||
我们的大地神盖亚已经无法忍受世界的环境污染恐怕会毁了地球,
|
||||
她把五个拥有巨大魔力的戒指给了五大洲的五个特殊中学生:
|
||||
非洲的夸美,拥有土地的威力;
|
||||
北美的辉乐,拥有火的威力;
|
||||
苏联的琳卡,拥有风的威力;
|
||||
亚洲的姬伊,拥有水的威力;
|
||||
南美的玛狄,拥有心灵的威力。
|
||||
五个中学生的威力联合起来,就能唤来最伟大的地球超人!
|
||||
地球超人!力量是你们的!
|
||||
引用:(360百科) https://m.baike.so.com/doc/2093525-2214593.html
|
||||
-->
|
||||
<color name="colorTudi">#FF05CF00</color>
|
||||
<color name="colorHuo">#FFC61414</color>
|
||||
<color name="colorFeng">#FFDEDEDE</color>
|
||||
<color name="colorShui">#FF009ADE</color>
|
||||
<color name="colorShuiDark">#FF0072A4</color>
|
||||
<color name="colorShuiAccent">#FF33C1FF</color>
|
||||
<color name="colorXinling">#FFFCC500</color>
|
||||
<!--<color name="colorPrimary">@color/colorShuiDark</color>
|
||||
<color name="colorPrimaryDark">@color/colorFeng</color>
|
||||
<color name="colorAccent">@color/colorShui</color>
|
||||
-->
|
||||
<color name="colorYellow">#FFBEBE48</color>
|
||||
<color name="colorRed">#FFC85C5C</color>
|
||||
<color name="colorBlue">#FF2677C7</color>
|
||||
<color name="colorBlack">#FF000000</color>
|
||||
<color name="colorText">@color/colorBlack</color>
|
||||
<!-- 调试配置
|
||||
<color name="colorYellow">#FF630066</color>
|
||||
<color name="colorRed">#FF23244D</color>
|
||||
<color name="colorBlue">#FF00E2F7</color>
|
||||
<color name="colorBlack">#FF54FF00</color>
|
||||
-->
|
||||
<!-- 明色配置
|
||||
<color name="colorYellow">#FFFFBD00</color>
|
||||
<color name="colorRed">#FFE00000</color>
|
||||
<color name="colorBlue">#FF000DE5</color>
|
||||
<color name="colorBlack">#FF000000</color>
|
||||
-->
|
||||
<!-- 原色配置 -->
|
||||
<!--<color name="colorYellow">#FFFFFF00</color>
|
||||
<color name="colorRed">#FFFF0000</color>
|
||||
<color name="colorBlue">#FF000FFF</color>
|
||||
<color name="colorBlack">#FF000000</color>
|
||||
-->
|
||||
<color name="colorPrimary">@color/colorShui</color>
|
||||
<color name="colorPrimaryDark">@color/colorShuiDark</color>
|
||||
<color name="colorAccent">@color/colorShuiAccent</color>
|
||||
<color name="colorUsege">@color/colorRed</color>
|
||||
<color name="colorCurrent">@color/colorBlue</color>
|
||||
<color name="colorCharge">@color/colorYellow</color>
|
||||
|
||||
<!--CustomSlideToUnlockView控件配置-->
|
||||
<color name="colorCustomSlideToUnlockViewWhite">#FFFFFFFF</color>
|
||||
|
||||
<!---->
|
||||
</resources>
|
||||
8
powerbell/src/main/res/values/dimens.xml
Normal file
8
powerbell/src/main/res/values/dimens.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="toolbar_height">60dp</dimen>
|
||||
<dimen name="slide_width">76dp</dimen>
|
||||
<dimen name="text_content_size">18dp</dimen>
|
||||
<dimen name="text_title_size">24dp</dimen>
|
||||
<dimen name="text_subtitle_size">16dp</dimen>
|
||||
</resources>
|
||||
33
powerbell/src/main/res/values/strings.xml
Normal file
33
powerbell/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<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_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="switchto_en1">PowerBell</string>
|
||||
<string name="switchto_cn1">能源钟</string>
|
||||
<string name="switchto_cn2">泡额呗额</string>
|
||||
<string name="en1_switch_disabled">PowerBell</string>
|
||||
<string name="cn1_switch_disabled">能源钟</string>
|
||||
<string name="cn2_switch_disabled">泡额呗额</string>
|
||||
<string name="item_mainview">Main View</string>
|
||||
<string name="item_aboutview">About</string>
|
||||
<string name="item_battery_report">Battery Report</string>
|
||||
<string name="item_clearrecord">Clear Record</string>
|
||||
<string name="item_changepicture">Change Picture</string>
|
||||
<string name="item_devoloperoptionsview">Developer 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_sourceview">Source View</string>
|
||||
<string name="txt_aboveswitch">Message master switch</string>
|
||||
<string name="txt_aboveswitchtips">Send a remind ringtone, when power is lower than left (usage state) or higher than right (charged state).</string>
|
||||
<string name="texthint_CustomSlideToCleanRecord">Slide Right To Clean Up APP Record.</string>
|
||||
<string name="subtitle_activity_clearrecord">Clean Record</string>
|
||||
<string name="subtitle_activity_backgroundpicture">Background Picture</string>
|
||||
<string name="subtitle_activity_pixelpicker">Pixel Picker</string>
|
||||
<string name="subtitle_activity_about">About The APP</string>
|
||||
<string name="msg_AOHPCTCSeekBar_ClearRecord">>>>Seek 100% Right Is Clean Record.>>></string>
|
||||
</resources>
|
||||
29
powerbell/src/main/res/values/styles.xml
Normal file
29
powerbell/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="AppTheme_Base" parent="AESTheme">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
<style name="AppTheme_Default" parent="AESTheme">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="android:textSize">@dimen/text_content_size</item>
|
||||
</style>
|
||||
|
||||
<style name="DefaultAToolbar">
|
||||
<item name="attrAToolbarTitleTextColor">#FF000000</item>
|
||||
<item name="attrAToolbarStartColor">@color/colorShuiDark</item>
|
||||
<item name="attrAToolbarCenterColor">@color/colorShui</item>
|
||||
<item name="attrAToolbarEndColor">@color/colorShuiAccent</item>
|
||||
</style>
|
||||
|
||||
<!-- 设置Toolbar标题字体的大小 -->
|
||||
<style name="Toolbar.TitleText" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
|
||||
<item name="android:textSize">@dimen/text_title_size</item>
|
||||
</style>
|
||||
<style name="Toolbar.SubTitleText" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
|
||||
<item name="android:textSize">@dimen/text_subtitle_size</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
28
powerbell/src/main/res/xml/file_provider.xml
Normal file
28
powerbell/src/main/res/xml/file_provider.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<external-path
|
||||
name="external_storage_root"
|
||||
path="." />
|
||||
<files-path
|
||||
name="files_path"
|
||||
path="." />
|
||||
<cache-path
|
||||
name="cache_path"
|
||||
path="." />
|
||||
<!--/storage/emulated/0/Android/data/...-->
|
||||
<external-files-path
|
||||
name="external_file_path"
|
||||
path="." />
|
||||
<external-files-path
|
||||
name="files_root"
|
||||
path="mimoDownload" />
|
||||
<!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的目录-->
|
||||
<external-cache-path
|
||||
name="external_cache_path"
|
||||
path="." />
|
||||
<!--配置root-path。这样子可以读取到sd卡和一些应用分身的目录,否则微信分身保存的图片,就会导致 java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/999/tencent/MicroMsg/WeiXin/export1544062754693.jpg,在小米6的手机上微信分身有这个crash,华为没有
|
||||
-->
|
||||
<root-path
|
||||
name="root_path"
|
||||
path="" />
|
||||
</paths>
|
||||
46
powerbell/src/main/res/xml/shortcutsmaincn1.xml
Normal file
46
powerbell/src/main/res/xml/shortcutsmaincn1.xml
Normal 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>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user