Compare commits
262 Commits
appbase-v1
...
mymessagem
| Author | SHA1 | Date | |
|---|---|---|---|
| b59073247c | |||
|
|
8271f663a1 | ||
| e9bb789daa | |||
| dbff19e7f4 | |||
|
|
44679d0c8a | ||
| 6656161903 | |||
|
|
edc63c750b | ||
| b170085482 | |||
| 6e8ba3394d | |||
| b91fe3c16b | |||
| e30b7acfdb | |||
| 6221555dfe | |||
|
|
c60eb29f4f | ||
|
|
3efcf40025 | ||
|
|
27e948b86d | ||
|
|
ae4742f3e2 | ||
|
|
d3312cbb29 | ||
|
|
58f3778dff | ||
|
|
f5e2961445 | ||
|
|
e4e9c31f02 | ||
|
|
a02a7efe43 | ||
|
|
2220efd009 | ||
|
|
0bc0fdf8c2 | ||
|
|
6b7b07a6fd | ||
|
|
8f3417818d | ||
|
|
ca280c7334 | ||
|
|
8172cf7b6f | ||
|
|
8014f4149c | ||
|
|
3c36ed589a | ||
|
|
9768103741 | ||
|
|
787b8f0d77 | ||
|
|
1d58126fd8 | ||
|
|
7b5a3d2d71 | ||
|
|
a988a9d4f6 | ||
|
|
04a67e666b | ||
|
|
348edc8aaf | ||
|
|
5970ae33c8 | ||
|
|
91a44f48ef | ||
|
|
c66e9a090b | ||
|
|
7a14b55247 | ||
|
|
fab68f16c8 | ||
|
|
3a97c6135f | ||
|
|
cdad017d8c | ||
|
|
bf84382963 | ||
|
|
34601fc5b1 | ||
|
|
a40dbcfb61 | ||
|
|
4d344b299b | ||
|
|
37b0867d34 | ||
|
|
cdfbb082d2 | ||
|
|
7e476894a7 | ||
|
|
0e8ae2e020 | ||
|
|
48623a2805 | ||
|
|
b505156211 | ||
|
|
91b30fb576 | ||
|
|
ab3ac72d54 | ||
|
|
73285c8779 | ||
|
|
fa338ec8c7 | ||
|
|
7a3a1f4bcd | ||
|
|
ea65810e7d | ||
|
|
ad991e3da2 | ||
|
|
7e263447c8 | ||
|
|
80201e8370 | ||
|
|
ea0473606a | ||
|
|
870e9a94fb | ||
|
|
2421ecb943 | ||
|
|
687fff7216 | ||
|
|
50d4cd830b | ||
|
|
2079822c00 | ||
|
|
297c76f328 | ||
|
|
43b18ee662 | ||
|
|
3ec3a4cfc2 | ||
|
|
2c10a9f38c | ||
|
|
e62888636e | ||
|
|
364980dd02 | ||
|
|
0e155e4f3a | ||
|
|
89febba5a9 | ||
|
|
653330f8e1 | ||
|
|
386c73effc | ||
|
|
b385583c5a | ||
|
|
e7a9be2f56 | ||
|
|
b27f7b0080 | ||
|
|
0a440419ff | ||
|
|
9e189ed5ac | ||
|
|
0fb6aadc72 | ||
|
|
46f3315b02 | ||
|
|
c0ff228845 | ||
|
|
a0fe8f17a8 | ||
|
|
16bd40fc59 | ||
|
|
ffaf683c54 | ||
|
|
26f5f8d3db | ||
|
|
917e25cdc8 | ||
|
|
707bed52c7 | ||
|
|
3795cf8631 | ||
|
|
b374f3117a | ||
|
|
d581cd9842 | ||
|
|
cef50d087d | ||
|
|
6d9adc124e | ||
|
|
52f738b45b | ||
|
|
9ece6778b7 | ||
|
|
b7f8b76ace | ||
|
|
326e5fa68e | ||
|
|
e9c8f9029e | ||
|
|
c00bfa1292 | ||
|
|
11ee4dcf27 | ||
|
|
8974e24dce | ||
|
|
89142e379c | ||
|
|
dabc671c27 | ||
|
|
ed849e92d1 | ||
|
|
42d2522927 | ||
|
|
6a52b2a8c3 | ||
|
|
02ed5bd5d1 | ||
|
|
b685665d0c | ||
|
|
1b030a2855 | ||
|
|
dd577f1765 | ||
|
|
0f8350600d | ||
|
|
1777ebb8dc | ||
|
|
2b99f707e7 | ||
|
|
068c94e749 | ||
|
|
0b3bc7e296 | ||
|
|
6ef747bcf8 | ||
|
|
a8e843c388 | ||
|
|
d3fd593cb0 | ||
|
|
3aec176b8b | ||
|
|
5960f76238 | ||
|
|
1a118da827 | ||
|
|
f73cad6f3e | ||
|
|
7130ecf023 | ||
|
|
953c8f08cd | ||
|
|
87b7557f72 | ||
|
|
77f5a156f8 | ||
|
|
b34ea40536 | ||
|
|
d202a3443d | ||
|
|
8c532c885f | ||
|
|
5fc4cb5f74 | ||
|
|
2a590a99fb | ||
|
|
c6ad707ca2 | ||
|
|
ee13a43fb6 | ||
|
|
5fbe1d8f71 | ||
|
|
38fe941a8b | ||
|
|
e13c8e7af0 | ||
|
|
a4988b5b68 | ||
|
|
04df902b6b | ||
|
|
33c71ea868 | ||
|
|
5507126f6b | ||
|
|
d381c29452 | ||
|
|
ba861d910e | ||
|
|
f5d9aafe43 | ||
|
|
e80d7e7b03 | ||
|
|
6e0a833fde | ||
|
|
c596ee5fa4 | ||
|
|
5e99f1278e | ||
|
|
b7158d1ebd | ||
|
|
9b51250ebf | ||
|
|
eb61eb7306 | ||
|
|
31ad66685c | ||
|
|
beb561ad6a | ||
|
|
8869265d60 | ||
|
|
2739627aff | ||
|
|
58e0be9cf4 | ||
|
|
9e9402f84e | ||
|
|
ec18330022 | ||
|
|
8bb80ef575 | ||
|
|
c1e6e32809 | ||
|
|
3e7722e2c0 | ||
|
|
a1707e73b5 | ||
|
|
9dcbaa0d75 | ||
|
|
23920a7ff1 | ||
|
|
17c373c490 | ||
|
|
5f7c94b349 | ||
|
|
c2b739d345 | ||
|
|
67a05cd457 | ||
|
|
554ab758bf | ||
|
|
20e118cd34 | ||
|
|
f370ae8ffb | ||
|
|
c92c874ea1 | ||
|
|
90a6116c0a | ||
|
|
45208ecbb1 | ||
|
|
c28d655fe3 | ||
|
|
4b5905f74e | ||
|
|
6bd01780ec | ||
|
|
a6699262f8 | ||
|
|
07b5e66875 | ||
|
|
91f5cf9051 | ||
|
|
ea2d38defc | ||
|
|
e430b7abe4 | ||
|
|
945eadb617 | ||
|
|
c5bffc5eef | ||
|
|
88597fe407 | ||
|
|
53f985533a | ||
|
|
a3950f13ad | ||
|
|
c878e9dc02 | ||
|
|
f2f7cab330 | ||
|
|
0e3b9dc760 | ||
|
|
6c8b0dcfa5 | ||
|
|
7de8a4f084 | ||
|
|
219c6614be | ||
|
|
0f5bb020b9 | ||
|
|
7794ff80ec | ||
|
|
7463ad3352 | ||
|
|
69187e3ed0 | ||
|
|
753032efed | ||
|
|
2b4c43c9af | ||
|
|
711c98d556 | ||
|
|
202205588a | ||
|
|
42c4978b44 | ||
|
|
1a2b7b862d | ||
|
|
8730f434dd | ||
|
|
eb253b374f | ||
|
|
c4e88e9593 | ||
|
|
08d9d92ae4 | ||
|
|
74841c08dc | ||
|
|
945bacb825 | ||
|
|
0e464495fd | ||
|
|
f8944490f8 | ||
|
|
733af004f6 | ||
|
|
c03568e1f5 | ||
|
|
a0575a5e8b | ||
|
|
0e57ce679e | ||
|
|
f9211a8eb4 | ||
|
|
4c31ff9b54 | ||
|
|
8cf610962e | ||
|
|
3071d186ec | ||
|
|
df10306059 | ||
|
|
ccdb9c5abd | ||
|
|
f27209ab87 | ||
|
|
e8682ce410 | ||
|
|
2e4003dae0 | ||
|
|
198b0975ce | ||
|
|
24a578a9d2 | ||
|
|
46de24447f | ||
|
|
2a819e94e4 | ||
|
|
6635358ec5 | ||
|
|
ac1c008035 | ||
|
|
b124487cb1 | ||
|
|
9621d35f79 | ||
|
|
17de0832a6 | ||
|
|
f53b222b7f | ||
|
|
0c0cde8406 | ||
|
|
46967065c0 | ||
|
|
8edbff5ac1 | ||
|
|
434f8a8549 | ||
|
|
c04be60b13 | ||
|
|
641098f8fb | ||
|
|
dba54ac4b2 | ||
|
|
c6cd779889 | ||
|
|
dfb1692a04 | ||
|
|
c83c8f66b3 | ||
|
|
cd7b5f38bf | ||
|
|
0c2e73b82e | ||
|
|
7b1838ff8e | ||
|
|
73ff3d1726 | ||
|
|
a69572e216 | ||
|
|
fa79c3f807 | ||
|
|
fde4b275f7 | ||
|
|
d66d9373ff | ||
|
|
f32ed94e4e | ||
|
|
1320984829 | ||
|
|
abf1e5ba42 | ||
|
|
1cd2f88038 | ||
|
|
3f6e583d68 | ||
|
|
89dac91cc6 | ||
|
|
3809c1bcab |
16
.gitignore
vendored
16
.gitignore
vendored
@@ -87,19 +87,15 @@ lint/tmp/
|
|||||||
# Android Profiling
|
# Android Profiling
|
||||||
*.hprof
|
*.hprof
|
||||||
|
|
||||||
# Custom
|
# 忽略 Lint 输出文件
|
||||||
.androidide
|
|
||||||
lint-results.xml
|
lint-results.xml
|
||||||
lint-results.html
|
lint-results.html
|
||||||
winboll.properties
|
|
||||||
local.properties
|
## 忽略 AndroidIDE 临时文件夹
|
||||||
|
.androidide
|
||||||
|
|
||||||
## 忽略模块应用编译配置
|
## 忽略模块应用编译配置
|
||||||
/settings.gradle
|
/settings.gradle
|
||||||
/gradle.properties
|
/gradle.properties
|
||||||
|
/winboll.properties
|
||||||
## 忽略 srv 纠结问题
|
/local.properties
|
||||||
/srv/
|
|
||||||
|
|
||||||
## 忽略 winboll-x 文件夹
|
|
||||||
/winboll-x/
|
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,6 +1,3 @@
|
|||||||
[submodule "libjc/jcc/libs"]
|
[submodule "libjc/jcc/libs"]
|
||||||
path = libjc/jcc/libs
|
path = libjc/jcc/libs
|
||||||
url = https://gitea.winboll.cc/Studio/APP_libjc_jcc_libs.git
|
url = https://gitea.winboll.cc/Studio/APP_libjc_jcc_libs.git
|
||||||
[submodule "keystore"]
|
|
||||||
path = keystore
|
|
||||||
url = https://gitea.winboll.cc/Studio/keystore.git
|
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
def winbollProps = new Properties()
|
||||||
|
def winbollPropsFile = rootProject.file("${winbollFilePath}")
|
||||||
|
assert(winbollPropsFile.exists())
|
||||||
|
winbollProps.load(new FileInputStream(winbollPropsFile))
|
||||||
|
|
||||||
// 读取秘钥配置文件
|
// 读取秘钥配置文件
|
||||||
//
|
//
|
||||||
def keyProps = new Properties()
|
def keyProps = new Properties()
|
||||||
@@ -122,6 +127,7 @@ android {
|
|||||||
FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
|
FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
|
||||||
winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
|
winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
|
||||||
fos.close();
|
fos.close();
|
||||||
|
println "\n\n>>> Project build.properties saved.\n\n";
|
||||||
|
|
||||||
if(winbollBuildProps['libraryProject'] != "") {
|
if(winbollBuildProps['libraryProject'] != "") {
|
||||||
// 如果应用 build.properties 文件设置了类库模块项目文件名
|
// 如果应用 build.properties 文件设置了类库模块项目文件名
|
||||||
@@ -133,14 +139,15 @@ android {
|
|||||||
java.nio.file.Path targetFilePath = libraryProjectBuildPropsFile.toPath();
|
java.nio.file.Path targetFilePath = libraryProjectBuildPropsFile.toPath();
|
||||||
// 使用copyTo()方法复制文件,如果目标文件存在会被覆盖,可选参数可以选择不覆盖
|
// 使用copyTo()方法复制文件,如果目标文件存在会被覆盖,可选参数可以选择不覆盖
|
||||||
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
|
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
|
||||||
|
println "\n\n>>> Library Project build.properties saved.\n\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果正在发布,就拷贝到 WinBoLL 标签管理文件夹
|
// 如果正在发布,就拷贝到 WinBoLL 标签管理文件夹,和处理 Git 仓库管理任务。
|
||||||
//
|
//
|
||||||
if((variant.flavorName == "stage"&&variant.buildType.name == "debug")
|
if(variant.flavorName == "stage"&&variant.buildType.name == "release"){
|
||||||
|| (variant.flavorName == "stage"&&variant.buildType.name == "release")){
|
|
||||||
// 发布 APK 文件
|
// 发布 APK 文件
|
||||||
//
|
//
|
||||||
// 截取版本号的版本字段为短版本名
|
// 截取版本号的版本字段为短版本名
|
||||||
@@ -221,6 +228,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 提交新的编译标志配置
|
// 提交新的编译标志配置
|
||||||
|
println 'exec bashCommitAppPublishBuildFlagInfoFilePath ...'
|
||||||
def resultCommitBuildFlag = exec {
|
def resultCommitBuildFlag = exec {
|
||||||
commandLine 'bash', '--', "${RootProjectDir}/${bashCommitAppPublishBuildFlagInfoFilePath}", "${RootProjectDir}", "${versionName}", variant.buildType.name , rootProject.name
|
commandLine 'bash', '--', "${RootProjectDir}/${bashCommitAppPublishBuildFlagInfoFilePath}", "${RootProjectDir}", "${versionName}", variant.buildType.name , rootProject.name
|
||||||
}
|
}
|
||||||
@@ -228,22 +236,78 @@ android {
|
|||||||
assert(resultCommitBuildFlag.getExitValue() == 0)
|
assert(resultCommitBuildFlag.getExitValue() == 0)
|
||||||
}
|
}
|
||||||
} // if(variant.buildType.name == "release"){
|
} // if(variant.buildType.name == "release"){
|
||||||
|
|
||||||
// 如果公共目录存在就拷贝到公共目录并重命名为app.apk
|
// 如果正在调试发布版,就只生成和输出APK文件,不处理 Git 仓库提交与更新问题。
|
||||||
//
|
//
|
||||||
File outCommonDir = new File("/sdcard/AppProjects")
|
if(variant.flavorName == "stage"&&variant.buildType.name == "debug"){
|
||||||
String commandAPKName = "app.apk"
|
// 发布 APK 文件
|
||||||
if(outCommonDir.exists()) {
|
//
|
||||||
copy{
|
// 截取版本号的版本字段为短版本名
|
||||||
from file.outputFile
|
String szVersionName = "${versionName}"
|
||||||
into outCommonDir
|
String[] szlistTemp = szVersionName.split("-")
|
||||||
rename {
|
String szShortVersionName = szlistTemp[0]
|
||||||
String fileName -> "${commandAPKName}"
|
//String szCommonTagAPKName = "${rootProject.name}_" + szShortVersionName + ".apk"
|
||||||
|
String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk"
|
||||||
|
println "CommonTagAPKName is : " + szCommonTagAPKName
|
||||||
|
|
||||||
|
//File outTagDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/tag/")
|
||||||
|
File outTagDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}/")
|
||||||
|
// 创建目标路径目录
|
||||||
|
if(!outTagDir.exists()) {
|
||||||
|
outTagDir.mkdirs();
|
||||||
|
println "Output Folder Created.(Tags) : " + outTagDir.getAbsolutePath()
|
||||||
|
}
|
||||||
|
|
||||||
|
if(outTagDir.exists()) {
|
||||||
|
File targetAPK = new File(outTagDir, "${szCommonTagAPKName}")
|
||||||
|
if(targetAPK.exists()) {
|
||||||
|
// 标签版本APK文件已经存在,构建拷贝任务停止
|
||||||
|
println '如果是在调试 Stage 版应用包构建,请删除(注:在debug目录)现有的 Stage 应用包('+targetAPK.getAbsolutePath()+')。再编译一次。'
|
||||||
|
assert (!targetAPK.exists())
|
||||||
|
// 可选择删除并继续输出APK文件
|
||||||
|
//delete targetAPK
|
||||||
}
|
}
|
||||||
println "Output APK (Common): " + outCommonDir.getAbsolutePath() + "/${commandAPKName}"
|
// 复制一个备份
|
||||||
|
copy{
|
||||||
|
from file.outputFile
|
||||||
|
into outTagDir
|
||||||
|
rename {
|
||||||
|
String fileName -> "${outputFileName}"
|
||||||
|
}
|
||||||
|
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${outputFileName}"
|
||||||
|
}
|
||||||
|
// 复制一个并重命名为短版本名
|
||||||
|
copy{
|
||||||
|
from file.outputFile
|
||||||
|
into outTagDir
|
||||||
|
rename {
|
||||||
|
String fileName -> "${szCommonTagAPKName}"
|
||||||
|
}
|
||||||
|
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${szCommonTagAPKName}"
|
||||||
|
}
|
||||||
|
|
||||||
|
//不保存编译标志配置
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果配置了APK额外输出路径,就复制一份拷贝到额外路径。
|
||||||
|
//
|
||||||
|
if(winbollProps['ExtraAPKOutputPath'] != null ) {
|
||||||
|
File apkFile = new File(winbollProps['ExtraAPKOutputPath'])
|
||||||
|
File outCommonDir = apkFile.getParentFile();
|
||||||
|
String commandAPKName = apkFile.getName();
|
||||||
|
if(outCommonDir.exists()) {
|
||||||
|
copy{
|
||||||
|
from file.outputFile
|
||||||
|
into outCommonDir
|
||||||
|
rename {
|
||||||
|
String fileName -> "${commandAPKName}"
|
||||||
|
}
|
||||||
|
println "Output APK (Common): " + outCommonDir.getAbsolutePath() + "/${commandAPKName}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}// End of (variant.getAssembleProvider().get().doLast {)
|
}// End of (variant.getAssembleProvider().get().doLast {)
|
||||||
|
|||||||
27
README.md
27
README.md
@@ -114,9 +114,12 @@
|
|||||||
|
|
||||||
# 本项目要实际运用需要注意以下几个步骤:
|
# 本项目要实际运用需要注意以下几个步骤:
|
||||||
# 在项目根目录下:
|
# 在项目根目录下:
|
||||||
## 1. 项目模块编译环境设置(必须),settings.gradle-demo 要复制为 settings.gradle,并取消相应项目模块的注释。
|
## ★. 项目模块编译环境设置(必须),settings.gradle-demo 要复制为 settings.gradle,并取消相应项目模块的注释。
|
||||||
## 2. 项目 Android SDK 编译环境设置(可选),local.properties-demo 要复制为 local.properties,并按需要设置 Android SDK 目录。
|
## ★. 项目模块编译环境设置(必须) 在根目录拷贝 gradle.properties-androidx-demo 或者 gradle.properties-android-demo 文件为 gradle.properties。
|
||||||
## 3. 类库型模块编译环境设置(可选),winboll.properties-demo 要复制为 winboll.properties,并按需要设置 WinBoLL Maven 库登录用户信息。
|
## ★. 项目 Android SDK 编译环境设置(可选),local.properties-demo 要复制为 local.properties,并按需要设置 Android SDK 目录。
|
||||||
|
## ★. 应用签名密钥 keystore 设置问题。一般调试编译只需用【Termux】cd 进 GenKeyStore 目录执行 $ bash gen_debug_keystore.sh 命令即可完成设置。
|
||||||
|
## ☆. 应用 WiBoLL 签名密钥配置问题<非必须考虑>。设置时需要 clone 【keystore】模块源码并拷贝模块目录的 appkey.jks 与 appkey.keystore 到项目根目录即可。
|
||||||
|
## ☆. 类库型模块编译环境设置(可选),winboll.properties-demo 要复制为 winboll.properties,并按需要设置 WinBoLL Maven 库登录用户信息, 和 APK 文件额外输出路径。
|
||||||
|
|
||||||
|
|
||||||
# ☆类库型项目编译方法
|
# ☆类库型项目编译方法
|
||||||
@@ -125,7 +128,8 @@
|
|||||||
设置属性 libraryProject=<类库项目模块文件夹名称>
|
设置属性 libraryProject=<类库项目模块文件夹名称>
|
||||||
### 再编译测试项目
|
### 再编译测试项目
|
||||||
$ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称>
|
$ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称>
|
||||||
#### 测试项目编译后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
|
#### 测试项目编译后,编译器会复制一份 APK 到 路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
|
||||||
|
#### 若是 winboll.properties 文件的 [ExtraAPKOutputPath] 属性设置了路径。编译器也会复制一份 APK 到这个路径。
|
||||||
### 最后编译类库项目
|
### 最后编译类库项目
|
||||||
$ bash .winboll/bashPublishLIBAddTag.sh <类库项目模块文件夹名称>
|
$ bash .winboll/bashPublishLIBAddTag.sh <类库项目模块文件夹名称>
|
||||||
#### 类库模块编译命令执行后,编译器会发布到 WinBoLL Nexus Maven 库:Maven 库地址可以参阅根项目目录配置 build.gradle 文件。
|
#### 类库模块编译命令执行后,编译器会发布到 WinBoLL Nexus Maven 库:Maven 库地址可以参阅根项目目录配置 build.gradle 文件。
|
||||||
@@ -133,4 +137,17 @@ $ bash .winboll/bashPublishLIBAddTag.sh <类库项目模块文件夹名称>
|
|||||||
# ☆应用型项目编译方法
|
# ☆应用型项目编译方法
|
||||||
## 直接调用以下命令编译应用型项目
|
## 直接调用以下命令编译应用型项目
|
||||||
$ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称>
|
$ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称>
|
||||||
#### 应用模块编译命令执行后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
|
#### 应用模块编译命令执行后,编译器会复制一份 APK 到
|
||||||
|
#### 测试项目编译后,编译器会复制一份 APK 到 路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
|
||||||
|
#### 若是 winboll.properties 文件的 [ExtraAPKOutputPath] 属性设置了路径。编译器也会复制一份 APK 到这个路径。
|
||||||
|
|
||||||
|
## ☆应用调试编译方法
|
||||||
|
使用以下命令编译调试:
|
||||||
|
|
||||||
|
### Beta调试使用:
|
||||||
|
$ bash gradlew assembleBetaDebug
|
||||||
|
|
||||||
|
### Stage调试使用:
|
||||||
|
$ bash gradlew assembleStageDebug
|
||||||
|
|
||||||
|
### 若是 winboll.properties 文件的 [ExtraAPKOutputPath] 属性设置了路径。编译器也会复制一份 APK 到这个路径。
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Mon Jun 09 01:44:28 HKT 2025
|
#Sun Aug 31 23:40:17 HKT 2025
|
||||||
stageCount=1
|
stageCount=4
|
||||||
libraryProject=libaes
|
libraryProject=libaes
|
||||||
baseVersion=15.9
|
baseVersion=15.9
|
||||||
publishVersion=15.9.0
|
publishVersion=15.9.3
|
||||||
buildCount=0
|
buildCount=0
|
||||||
baseBetaVersion=15.9.1
|
baseBetaVersion=15.9.4
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.aes;
|
package cc.winboll.studio.aes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/03/24 23:52:29
|
* @Date 2025/03/24 23:52:29
|
||||||
* @Describe AES应用介绍窗口
|
* @Describe AES应用介绍窗口
|
||||||
*/
|
*/
|
||||||
@@ -83,7 +83,7 @@ public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity
|
|||||||
appInfo.setAppGitOwner("Studio");
|
appInfo.setAppGitOwner("Studio");
|
||||||
appInfo.setAppGitAPPBranch(szBranchName);
|
appInfo.setAppGitAPPBranch(szBranchName);
|
||||||
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
|
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
|
||||||
appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=2&fromuid=1");
|
appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=3&extra=page%3D1");
|
||||||
appInfo.setAppAPKName("AES");
|
appInfo.setAppAPKName("AES");
|
||||||
appInfo.setAppAPKFolderName("AES");
|
appInfo.setAppAPKFolderName("AES");
|
||||||
//appInfo.setIsAddDebugTools(false);
|
//appInfo.setIsAddDebugTools(false);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.aes;
|
package cc.winboll.studio.aes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/06/13 19:03:58
|
* @Date 2024/06/13 19:03:58
|
||||||
* @Describe AES应用类
|
* @Describe AES应用类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.aes;
|
package cc.winboll.studio.aes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/06/13 19:05:52
|
* @Date 2024/06/13 19:05:52
|
||||||
* @Describe 应用主窗口
|
* @Describe 应用主窗口
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,6 @@ dependencies {
|
|||||||
// https://mvnrepository.com/artifact/com.android.support/recyclerview-v7
|
// https://mvnrepository.com/artifact/com.android.support/recyclerview-v7
|
||||||
api 'com.android.support:recyclerview-v7:28.0.0'
|
api 'com.android.support:recyclerview-v7:28.0.0'
|
||||||
|
|
||||||
api 'cc.winboll.studio:libapputils:15.8.2'
|
api 'cc.winboll.studio:libapputils:15.8.5'
|
||||||
api 'cc.winboll.studio:libappbase:15.8.2'
|
api 'cc.winboll.studio:libappbase:15.9.5'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Sun Jun 01 08:02:46 GMT 2025
|
#Sun Aug 31 05:11:26 CST 2025
|
||||||
stageCount=0
|
stageCount=1
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.0
|
baseVersion=15.0
|
||||||
publishVersion=15.0.0
|
publishVersion=15.0.0
|
||||||
buildCount=27
|
buildCount=0
|
||||||
baseBetaVersion=15.0.1
|
baseBetaVersion=15.0.1
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ android {
|
|||||||
// versionName 更新后需要手动设置
|
// versionName 更新后需要手动设置
|
||||||
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
||||||
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||||
versionName "15.0"
|
versionName "15.1"
|
||||||
if(true) {
|
if(true) {
|
||||||
versionName = genVersionName("${versionName}")
|
versionName = genVersionName("${versionName}")
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ dependencies {
|
|||||||
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
|
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
|
||||||
//api 'androidx.fragment:fragment:1.1.0'
|
//api 'androidx.fragment:fragment:1.1.0'
|
||||||
|
|
||||||
api 'cc.winboll.studio:libaes:15.8.0'
|
api 'cc.winboll.studio:libaes:15.9.3'
|
||||||
api 'cc.winboll.studio:libapputils:15.8.2'
|
api 'cc.winboll.studio:libapputils:15.8.5'
|
||||||
api 'cc.winboll.studio:libappbase:15.8.2'
|
api 'cc.winboll.studio:libappbase:15.9.5'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Sun Jun 01 08:03:56 GMT 2025
|
#Sun Aug 31 05:42:50 CST 2025
|
||||||
stageCount=0
|
stageCount=2
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.0
|
baseVersion=15.1
|
||||||
publishVersion=15.0.0
|
publishVersion=15.1.1
|
||||||
buildCount=24
|
buildCount=0
|
||||||
baseBetaVersion=15.0.1
|
baseBetaVersion=15.1.2
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ android {
|
|||||||
// versionName 更新后需要手动设置
|
// versionName 更新后需要手动设置
|
||||||
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
||||||
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||||
versionName "15.8"
|
versionName "15.10"
|
||||||
if(true) {
|
if(true) {
|
||||||
versionName = genVersionName("${versionName}")
|
versionName = genVersionName("${versionName}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Mon Jun 09 09:38:19 HKT 2025
|
#Sat Sep 27 21:03:20 HKT 2025
|
||||||
stageCount=9
|
stageCount=10
|
||||||
libraryProject=libappbase
|
libraryProject=libappbase
|
||||||
baseVersion=15.8
|
baseVersion=15.10
|
||||||
publishVersion=15.8.8
|
publishVersion=15.10.9
|
||||||
buildCount=0
|
buildCount=0
|
||||||
baseBetaVersion=15.8.9
|
baseBetaVersion=15.10.10
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools" >
|
xmlns:tools="http://schemas.android.com/tools" >
|
||||||
|
|
||||||
<application>
|
<application
|
||||||
|
tools:replace="android:icon,android:roundIcon"
|
||||||
|
android:icon="@drawable/ic_winboll_beta"
|
||||||
|
android:roundIcon="@drawable/ic_winboll_beta">
|
||||||
|
|
||||||
<!-- Put flavor specific code here -->
|
<!-- Put flavor specific code here -->
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,19 @@
|
|||||||
<manifest
|
<manifest
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="cc.winboll.studio.appbase">
|
package="cc.winboll.studio.appbase">
|
||||||
|
|
||||||
|
<!-- 网络权限 -->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:icon="@drawable/ic_miapp"
|
android:icon="@drawable/ic_winboll"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/MyAPPBaseTheme"
|
android:theme="@style/MyAPPBaseTheme"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:process=":App"
|
android:process=":App">
|
||||||
android:networkSecurityConfig="@xml/network_security_config">
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@@ -25,103 +27,14 @@
|
|||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
|
||||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES"/>
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity android:name=".GlobalApplication$CrashActivity"/>
|
||||||
android:name=".activities.NewActivity"
|
|
||||||
android:label="NewActivity"
|
|
||||||
android:exported="true"
|
|
||||||
android:resizeableActivity="true"
|
|
||||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"/>
|
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".activities.New2Activity"
|
|
||||||
android:label="New2Activity"
|
|
||||||
android:exported="true"
|
|
||||||
android:resizeableActivity="true"
|
|
||||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"/>
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".MyTileService"
|
|
||||||
android:exported="true"
|
|
||||||
android:label="@string/tileservice_name"
|
|
||||||
android:icon="@drawable/ic_launcher"
|
|
||||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
|
||||||
|
|
||||||
<intent-filter>
|
|
||||||
|
|
||||||
<action android:name="android.service.quicksettings.action.QS_TILE"/>
|
|
||||||
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".services.MainService"
|
|
||||||
android:exported="true"/>
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name="cc.winboll.studio.appbase.services.TestDemoBindService"
|
|
||||||
android:exported="true"/>
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name="cc.winboll.studio.appbase.services.TestDemoService"
|
|
||||||
android:exported="true"/>
|
|
||||||
|
|
||||||
<service android:name=".services.AssistantService"/>
|
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name="cc.winboll.studio.appbase.receivers.MainReceiver"
|
|
||||||
android:exported="true">
|
|
||||||
|
|
||||||
<intent-filter>
|
|
||||||
|
|
||||||
<action android:name="cc.winboll.studio.appbase.receivers.MainReceiver"/>
|
|
||||||
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name=".widgets.APPNewsWidget"
|
|
||||||
android:exported="true">
|
|
||||||
|
|
||||||
<intent-filter>
|
|
||||||
|
|
||||||
<action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidget.ACTION_WAKEUP_SERVICE"/>
|
|
||||||
|
|
||||||
<action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidget.ACTION_RELOAD_REPORT"/>
|
|
||||||
|
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
|
||||||
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="android.appwidget.provider"
|
|
||||||
android:resource="@xml/widget_provider_info_sos"/>
|
|
||||||
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name=".receivers.APPNewsWidgetClickListener"
|
|
||||||
android:exported="true">
|
|
||||||
|
|
||||||
<intent-filter>
|
|
||||||
|
|
||||||
<action android:name="cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener.ACTION_PRE"/>
|
|
||||||
|
|
||||||
<action android:name="cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener.ACTION_NEXT"/>
|
|
||||||
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.max_aspect"
|
android:name="android.max_aspect"
|
||||||
android:value="4.0"/>
|
android:value="4.0"/>
|
||||||
|
|||||||
@@ -1,27 +1,19 @@
|
|||||||
package cc.winboll.studio.appbase;
|
package cc.winboll.studio.appbase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/01/05 09:54:42
|
* @Date 2025/01/05 09:54:42
|
||||||
* @Describe APPbase 应用类
|
* @Describe APPbase 应用类
|
||||||
*/
|
*/
|
||||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import cc.winboll.studio.libappbase.sos.SOSCenterServiceReceiver;
|
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||||
import cc.winboll.studio.libappbase.sos.SOS;
|
|
||||||
|
|
||||||
public class App extends GlobalApplication {
|
public class App extends GlobalApplication {
|
||||||
|
|
||||||
public static final String TAG = "App";
|
public static final String TAG = "App";
|
||||||
|
|
||||||
SOSCenterServiceReceiver mSOSCenterServiceReceiver;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
mSOSCenterServiceReceiver = new SOSCenterServiceReceiver();
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
intentFilter.addAction(SOS.ACTION_SOS);
|
|
||||||
registerReceiver(mSOSCenterServiceReceiver, intentFilter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,23 @@
|
|||||||
package cc.winboll.studio.appbase;
|
package cc.winboll.studio.appbase;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ComponentName;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.CheckBox;
|
import android.widget.Toast;
|
||||||
import android.widget.Toolbar;
|
import android.widget.Toolbar;
|
||||||
import cc.winboll.studio.appbase.R;
|
import cc.winboll.studio.appbase.R;
|
||||||
import cc.winboll.studio.appbase.activities.NewActivity;
|
import cc.winboll.studio.libappbase.LogActivity;
|
||||||
import cc.winboll.studio.appbase.activities.WinBoLLActivity;
|
import cc.winboll.studio.libappbase.ToastUtils;
|
||||||
import cc.winboll.studio.appbase.services.MainService;
|
|
||||||
import cc.winboll.studio.appbase.services.TestDemoBindService;
|
|
||||||
import cc.winboll.studio.appbase.services.TestDemoService;
|
|
||||||
import cc.winboll.studio.libappbase.CrashHandler;
|
|
||||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
|
||||||
import cc.winboll.studio.libappbase.GlobalCrashActivity;
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
|
||||||
import cc.winboll.studio.libappbase.LogView;
|
|
||||||
import cc.winboll.studio.libappbase.sos.SOS;
|
|
||||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
|
|
||||||
import cc.winboll.studio.libappbase.widgets.StatusWidget;
|
|
||||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
|
||||||
|
|
||||||
public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
public class MainActivity extends Activity {
|
||||||
|
|
||||||
public static final String TAG = "MainActivity";
|
public static final String TAG = "MainActivity";
|
||||||
|
|
||||||
@Override
|
|
||||||
public Activity getActivity() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTag() {
|
|
||||||
return TAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toolbar mToolbar;
|
Toolbar mToolbar;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -49,9 +28,6 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
|||||||
|
|
||||||
mToolbar = findViewById(R.id.toolbar);
|
mToolbar = findViewById(R.id.toolbar);
|
||||||
setActionBar(mToolbar);
|
setActionBar(mToolbar);
|
||||||
|
|
||||||
CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
|
|
||||||
cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -62,122 +38,37 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
if(item.getItemId() == R.id.item_yun) {
|
switch (item.getItemId()) {
|
||||||
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.YunActivity.class);
|
case R.id.item_home : {
|
||||||
} else if(item.getItemId() == R.id.item_logon) {
|
openWebsiteInBrowser(this);
|
||||||
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.LogonActivity.class);
|
}
|
||||||
}
|
}
|
||||||
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void onCrashTest(View view) {
|
||||||
protected void onDestroy() {
|
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
|
||||||
super.onDestroy();
|
getString(i);
|
||||||
Intent intentAPPWidget = new Intent(this, StatusWidget.class);
|
}
|
||||||
intentAPPWidget.setAction(StatusWidget.ACTION_STATUS_UPDATE);
|
|
||||||
sendBroadcast(intentAPPWidget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSwitchDebugMode(View view) {
|
public void onLogTest(View view) {
|
||||||
boolean isDebuging = ((CheckBox)view).isChecked();
|
LogActivity.startLogActivity(this);
|
||||||
GlobalApplication.setIsDebuging(isDebuging);
|
|
||||||
GlobalApplication.saveDebugStatus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onPreviewGlobalCrashActivity(View view) {
|
/**
|
||||||
Intent intent = new Intent(this, GlobalCrashActivity.class);
|
* 唤起默认浏览器打开指定网站
|
||||||
intent.putExtra(CrashHandler.EXTRA_CRASH_INFO, "Demo log...");
|
* @param context 上下文(如 Activity.this)
|
||||||
startActivity(intent);
|
*/
|
||||||
}
|
public void openWebsiteInBrowser(Context context) {
|
||||||
|
// 目标网站地址
|
||||||
public void onStartCenter(View view) {
|
String url = "https://www.winboll.cc";
|
||||||
MainService.startMainService(this);
|
// 构建打开浏览器的意图
|
||||||
}
|
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||||
|
// 设置标志:避免创建新的任务栈(可选,按需求调整)
|
||||||
public void onStopCenter(View view) {
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
MainService.stopMainService(this);
|
context.startActivity(intent);
|
||||||
}
|
|
||||||
|
}
|
||||||
public void onTestStopMainServiceWithoutSettingEnable(View view) {
|
|
||||||
LogUtils.d(TAG, "onTestStopMainServiceWithoutSettingEnable");
|
|
||||||
stopService(new Intent(this, MainService.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTestUseComponentStartService(View view) {
|
|
||||||
LogUtils.d(TAG, "onTestUseComponentStartService");
|
|
||||||
|
|
||||||
// 目标服务的包名和类名
|
|
||||||
String packageName = this.getPackageName();
|
|
||||||
String serviceClassName = TestDemoService.class.getName();
|
|
||||||
|
|
||||||
// 构建Intent
|
|
||||||
Intent intentService = new Intent();
|
|
||||||
intentService.setComponent(new ComponentName(packageName, serviceClassName));
|
|
||||||
|
|
||||||
startService(intentService);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTestDemoServiceSOS(View view) {
|
|
||||||
Intent intent = new Intent(this, TestDemoService.class);
|
|
||||||
stopService(intent);
|
|
||||||
if (App.isDebuging()) {
|
|
||||||
SOS.sosToAppBaseBeta(this, TestDemoService.class.getName());
|
|
||||||
} else {
|
|
||||||
SOS.sosToAppBase(this, TestDemoService.class.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onSartTestDemoService(View view) {
|
|
||||||
Intent intent = new Intent(this, TestDemoService.class);
|
|
||||||
intent.setAction(TestDemoService.ACTION_ENABLE);
|
|
||||||
startService(intent);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStopTestDemoService(View view) {
|
|
||||||
Intent intent = new Intent(this, TestDemoService.class);
|
|
||||||
intent.setAction(TestDemoService.ACTION_DISABLE);
|
|
||||||
startService(intent);
|
|
||||||
|
|
||||||
Intent intentStop = new Intent(this, TestDemoService.class);
|
|
||||||
stopService(intentStop);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStopTestDemoServiceNoSettings(View view) {
|
|
||||||
Intent intent = new Intent(this, TestDemoService.class);
|
|
||||||
stopService(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onSartTestDemoBindService(View view) {
|
|
||||||
Intent intent = new Intent(this, TestDemoBindService.class);
|
|
||||||
intent.setAction(TestDemoBindService.ACTION_ENABLE);
|
|
||||||
startService(intent);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStopTestDemoBindService(View view) {
|
|
||||||
Intent intent = new Intent(this, TestDemoBindService.class);
|
|
||||||
intent.setAction(TestDemoBindService.ACTION_DISABLE);
|
|
||||||
startService(intent);
|
|
||||||
|
|
||||||
Intent intentStop = new Intent(this, TestDemoBindService.class);
|
|
||||||
stopService(intentStop);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStopTestDemoBindServiceNoSettings(View view) {
|
|
||||||
Intent intent = new Intent(this, TestDemoBindService.class);
|
|
||||||
stopService(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTestOpenNewActivity(View view) {
|
|
||||||
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, NewActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/02/13 19:30:10
|
|
||||||
*/
|
|
||||||
import android.content.Context;
|
|
||||||
import android.service.quicksettings.Tile;
|
|
||||||
import android.service.quicksettings.TileService;
|
|
||||||
import cc.winboll.studio.appbase.models.MainServiceBean;
|
|
||||||
import cc.winboll.studio.appbase.services.MainService;
|
|
||||||
|
|
||||||
public class MyTileService extends TileService {
|
|
||||||
public static final String TAG = "MyTileService";
|
|
||||||
|
|
||||||
volatile static MyTileService _MyTileService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStartListening() {
|
|
||||||
super.onStartListening();
|
|
||||||
_MyTileService = this;
|
|
||||||
Tile tile = getQsTile();
|
|
||||||
MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
|
||||||
if (bean != null && bean.isEnable()) {
|
|
||||||
//MainService.startMainService(context);
|
|
||||||
tile.setState(Tile.STATE_ACTIVE);
|
|
||||||
tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud));
|
|
||||||
} else {
|
|
||||||
//MainService.stopMainService(context);
|
|
||||||
tile.setState(Tile.STATE_INACTIVE);
|
|
||||||
tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
|
|
||||||
}
|
|
||||||
tile.updateTile();
|
|
||||||
// Tile tile = getQsTile();
|
|
||||||
// tile.setState(Tile.STATE_INACTIVE);
|
|
||||||
// tile.setLabel(getString(R.string.tileservice_name));
|
|
||||||
// tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
|
|
||||||
// tile.updateTile();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick() {
|
|
||||||
super.onClick();
|
|
||||||
Tile tile = getQsTile();
|
|
||||||
MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
|
||||||
if (bean == null) {
|
|
||||||
bean = new MainServiceBean();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tile.getState() == Tile.STATE_ACTIVE) {
|
|
||||||
bean.setIsEnable(false);
|
|
||||||
MainServiceBean.saveBean(this, bean);
|
|
||||||
MainService.stopMainService(this);
|
|
||||||
} else if (tile.getState() == Tile.STATE_INACTIVE) {
|
|
||||||
bean.setIsEnable(true);
|
|
||||||
MainServiceBean.saveBean(this, bean);
|
|
||||||
MainService.startMainService(this);
|
|
||||||
}
|
|
||||||
updateServiceIconStatus(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void updateServiceIconStatus(Context context) {
|
|
||||||
if (_MyTileService == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Tile tile = _MyTileService.getQsTile();
|
|
||||||
MainServiceBean bean = MainServiceBean.loadBean(context, MainServiceBean.class);
|
|
||||||
if (bean != null && bean.isEnable()) {
|
|
||||||
tile.setState(Tile.STATE_ACTIVE);
|
|
||||||
tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud));
|
|
||||||
} else {
|
|
||||||
tile.setState(Tile.STATE_INACTIVE);
|
|
||||||
tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud_outline));
|
|
||||||
}
|
|
||||||
tile.updateTile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.activities;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/03/25 11:46:40
|
|
||||||
* @Describe 测试窗口2
|
|
||||||
*/
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Toolbar;
|
|
||||||
import cc.winboll.studio.appbase.R;
|
|
||||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
|
||||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
|
||||||
|
|
||||||
public class New2Activity extends WinBoLLActivity implements IWinBoLLActivity {
|
|
||||||
|
|
||||||
public static final String TAG = "New2Activity";
|
|
||||||
|
|
||||||
Toolbar mToolbar;
|
|
||||||
//LogView mLogView;
|
|
||||||
|
|
||||||
@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_new2);
|
|
||||||
|
|
||||||
// mLogView = findViewById(R.id.logview);
|
|
||||||
// mLogView.start();
|
|
||||||
mToolbar = findViewById(R.id.toolbar);
|
|
||||||
setActionBar(mToolbar);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
//mLogView.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onCloseThisActivity(View view) {
|
|
||||||
GlobalApplication.getWinBoLLActivityManager().finish(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onCloseAllActivity(View view) {
|
|
||||||
GlobalApplication.getWinBoLLActivityManager().finishAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onNewActivity(View view) {
|
|
||||||
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, NewActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.activities;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/03/25 05:04:22
|
|
||||||
*/
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Toolbar;
|
|
||||||
import cc.winboll.studio.appbase.R;
|
|
||||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
|
||||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
|
||||||
|
|
||||||
public class NewActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
|
||||||
|
|
||||||
public static final String TAG = "NewActivity";
|
|
||||||
|
|
||||||
Toolbar mToolbar;
|
|
||||||
//LogView mLogView;
|
|
||||||
|
|
||||||
@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_new);
|
|
||||||
// mLogView = findViewById(R.id.logview);
|
|
||||||
// mLogView.start();
|
|
||||||
mToolbar = findViewById(R.id.toolbar);
|
|
||||||
setActionBar(mToolbar);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
//mLogView.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onCloseThisActivity(View view) {
|
|
||||||
GlobalApplication.getWinBoLLActivityManager().finish(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onCloseAllActivity(View view) {
|
|
||||||
GlobalApplication.getWinBoLLActivityManager().finishAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onNew2Activity(View view) {
|
|
||||||
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, New2Activity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.handlers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/02/14 03:51:40
|
|
||||||
*/
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import cc.winboll.studio.appbase.services.MainService;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
|
|
||||||
public class MainServiceHandler extends Handler {
|
|
||||||
public static final String TAG = "MainServiceHandler";
|
|
||||||
|
|
||||||
public static final int MSG_REMINDTHREAD = 0;
|
|
||||||
|
|
||||||
WeakReference<MainService> serviceWeakReference;
|
|
||||||
public MainServiceHandler(MainService service) {
|
|
||||||
serviceWeakReference = new WeakReference<MainService>(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case MSG_REMINDTHREAD: // 处理下载完成消息,更新UI
|
|
||||||
{
|
|
||||||
// 显示提醒消息
|
|
||||||
//
|
|
||||||
//LogUtils.d(TAG, "显示提醒消息");
|
|
||||||
MainService mainService = serviceWeakReference.get();
|
|
||||||
if (mainService != null) {
|
|
||||||
mainService.appenMessage((String)msg.obj);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.models;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/02/13 07:06:13
|
|
||||||
*/
|
|
||||||
import android.util.JsonReader;
|
|
||||||
import android.util.JsonWriter;
|
|
||||||
import cc.winboll.studio.libappbase.BaseBean;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class MainServiceBean extends BaseBean {
|
|
||||||
|
|
||||||
public static final String TAG = "MainServiceBean";
|
|
||||||
|
|
||||||
boolean isEnable;
|
|
||||||
|
|
||||||
public MainServiceBean() {
|
|
||||||
this.isEnable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIsEnable(boolean isEnable) {
|
|
||||||
this.isEnable = isEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnable() {
|
|
||||||
return isEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return MainServiceBean.class.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
|
||||||
super.writeThisToJsonWriter(jsonWriter);
|
|
||||||
jsonWriter.name("isEnable").value(isEnable());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
|
||||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
|
||||||
if (name.equals("isEnable")) {
|
|
||||||
setIsEnable(jsonReader.nextBoolean());
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
|
||||||
jsonReader.beginObject();
|
|
||||||
while (jsonReader.hasNext()) {
|
|
||||||
String name = jsonReader.nextName();
|
|
||||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
|
||||||
jsonReader.skipValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 结束 JSON 对象
|
|
||||||
jsonReader.endObject();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.models;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/03/07 12:47:22
|
|
||||||
* @Describe TestServiceBean
|
|
||||||
*/
|
|
||||||
import android.util.JsonReader;
|
|
||||||
import android.util.JsonWriter;
|
|
||||||
import cc.winboll.studio.libappbase.BaseBean;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class TestDemoBindServiceBean extends BaseBean {
|
|
||||||
|
|
||||||
public static final String TAG = "TestServiceBean";
|
|
||||||
|
|
||||||
boolean isEnable;
|
|
||||||
|
|
||||||
public TestDemoBindServiceBean() {
|
|
||||||
this.isEnable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIsEnable(boolean isEnable) {
|
|
||||||
this.isEnable = isEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnable() {
|
|
||||||
return isEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return TestDemoBindServiceBean.class.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
|
||||||
super.writeThisToJsonWriter(jsonWriter);
|
|
||||||
jsonWriter.name("isEnable").value(isEnable());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
|
||||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
|
||||||
if (name.equals("isEnable")) {
|
|
||||||
setIsEnable(jsonReader.nextBoolean());
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
|
||||||
jsonReader.beginObject();
|
|
||||||
while (jsonReader.hasNext()) {
|
|
||||||
String name = jsonReader.nextName();
|
|
||||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
|
||||||
jsonReader.skipValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 结束 JSON 对象
|
|
||||||
jsonReader.endObject();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.models;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/03/07 12:49:21
|
|
||||||
* @Describe TestDemoServiceBean
|
|
||||||
*/
|
|
||||||
import android.util.JsonReader;
|
|
||||||
import android.util.JsonWriter;
|
|
||||||
import cc.winboll.studio.libappbase.BaseBean;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class TestDemoServiceBean extends BaseBean {
|
|
||||||
|
|
||||||
public static final String TAG = "TestDemoServiceBean";
|
|
||||||
|
|
||||||
boolean isEnable;
|
|
||||||
|
|
||||||
public TestDemoServiceBean() {
|
|
||||||
this.isEnable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIsEnable(boolean isEnable) {
|
|
||||||
this.isEnable = isEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnable() {
|
|
||||||
return isEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return TestDemoServiceBean.class.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
|
||||||
super.writeThisToJsonWriter(jsonWriter);
|
|
||||||
jsonWriter.name("isEnable").value(isEnable());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
|
||||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
|
||||||
if (name.equals("isEnable")) {
|
|
||||||
setIsEnable(jsonReader.nextBoolean());
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
|
||||||
jsonReader.beginObject();
|
|
||||||
while (jsonReader.hasNext()) {
|
|
||||||
String name = jsonReader.nextName();
|
|
||||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
|
||||||
jsonReader.skipValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 结束 JSON 对象
|
|
||||||
jsonReader.endObject();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.receivers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/03/24 07:11:44
|
|
||||||
*/
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
|
||||||
|
|
||||||
public class APPNewsWidgetClickListener extends BroadcastReceiver {
|
|
||||||
|
|
||||||
public static final String TAG = "APPNewsWidgetClickListener";
|
|
||||||
public static final String ACTION_PRE = APPNewsWidgetClickListener.class.getName() + ".ACTION_PRE";
|
|
||||||
public static final String ACTION_NEXT = APPNewsWidgetClickListener.class.getName() + ".ACTION_NEXT";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
String action = intent.getAction();
|
|
||||||
if (action == null) {
|
|
||||||
LogUtils.d(TAG, String.format("action %s", action));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (action.equals(ACTION_PRE)) {
|
|
||||||
LogUtils.d(TAG, "ACTION_PRE");
|
|
||||||
APPNewsWidget.prePage(context);
|
|
||||||
} else if (action.equals(ACTION_NEXT)) {
|
|
||||||
LogUtils.d(TAG, "ACTION_NEXT");
|
|
||||||
APPNewsWidget.nextPage(context);
|
|
||||||
} else {
|
|
||||||
LogUtils.d(TAG, String.format("action %s", action));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.receivers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/02/13 06:58:04
|
|
||||||
* @Describe 主要广播接收器
|
|
||||||
*/
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import cc.winboll.studio.appbase.services.MainService;
|
|
||||||
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
|
|
||||||
import cc.winboll.studio.libappbase.AppUtils;
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
|
||||||
import cc.winboll.studio.libappbase.models.APPModel;
|
|
||||||
import cc.winboll.studio.libappbase.models.WinBoLLModel;
|
|
||||||
import cc.winboll.studio.libappbase.models.WinBoLLNewsBean;
|
|
||||||
import cc.winboll.studio.libappbase.sos.SOS;
|
|
||||||
import cc.winboll.studio.libappbase.sos.SOSObject;
|
|
||||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
|
|
||||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
|
||||||
import cc.winboll.studio.libappbase.winboll.WinBoLL;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public class MainReceiver extends BroadcastReceiver {
|
|
||||||
|
|
||||||
public static final String TAG = "MainReceiver";
|
|
||||||
|
|
||||||
public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
|
|
||||||
|
|
||||||
WeakReference<MainService> mwrService;
|
|
||||||
|
|
||||||
public MainReceiver(MainService service) {
|
|
||||||
mwrService = new WeakReference<MainService>(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
String szAction = intent.getAction();
|
|
||||||
if (szAction.equals(ACTION_BOOT_COMPLETED)) {
|
|
||||||
ToastUtils.show("ACTION_BOOT_COMPLETED");
|
|
||||||
} else if (szAction.equals(IWinBoLLActivity.ACTION_BIND)) {
|
|
||||||
LogUtils.d(TAG, "ACTION_BIND");
|
|
||||||
LogUtils.d(TAG, String.format("context.getPackageName() %s", context.getPackageName()));
|
|
||||||
LogUtils.d(TAG, String.format("intent.getAction() %s", intent.getAction()));
|
|
||||||
String szWinBoLLModel = intent.getStringExtra(WinBoLL.EXTRA_WINBOLLMODEL);
|
|
||||||
LogUtils.d(TAG, String.format("szAPPModel %s", szWinBoLLModel));
|
|
||||||
if (szWinBoLLModel != null && !szWinBoLLModel.equals("")) {
|
|
||||||
try {
|
|
||||||
WinBoLLModel bean = WinBoLLModel.parseStringToBean(szWinBoLLModel, WinBoLLModel.class);
|
|
||||||
if (bean != null) {
|
|
||||||
String szAppPackageName = bean.getAppPackageName();
|
|
||||||
LogUtils.d(TAG, String.format("szAppPackageName %s", szAppPackageName));
|
|
||||||
String szAppMainServiveName = bean.getAppMainServiveName();
|
|
||||||
LogUtils.d(TAG, String.format("szAppMainServiveName %s", szAppMainServiveName));
|
|
||||||
mwrService.get().bindWinBoLLModelConnection(bean);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (intent.getAction().equals(SOS.ACTION_SOS)) {
|
|
||||||
LogUtils.d(TAG, "ACTION_SOS");
|
|
||||||
String sos = intent.getStringExtra(SOS.EXTRA_OBJECT);
|
|
||||||
LogUtils.d(TAG, String.format("SOS %s", sos));
|
|
||||||
if (sos != null && !sos.equals("")) {
|
|
||||||
SOSObject bean = SOS.parseSOSObject(sos);
|
|
||||||
if (bean != null) {
|
|
||||||
String szObjectPackageName = bean.getObjectPackageName();
|
|
||||||
LogUtils.d(TAG, String.format("szObjectPackageName %s", szObjectPackageName));
|
|
||||||
String szObjectServiveName = bean.getObjectServiveName();
|
|
||||||
LogUtils.d(TAG, String.format("szObjectServiveName %s", szObjectServiveName));
|
|
||||||
|
|
||||||
Intent intentService = new Intent();
|
|
||||||
intentService.setComponent(new ComponentName(szObjectPackageName, szObjectServiveName));
|
|
||||||
context.startService(intentService);
|
|
||||||
|
|
||||||
String appName = AppUtils.getAppNameByPackageName(context, szObjectPackageName);
|
|
||||||
LogUtils.d(TAG, String.format("appName %s", appName));
|
|
||||||
WinBoLLNewsBean appWinBoLLNewsBean = new WinBoLLNewsBean(appName);
|
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
|
|
||||||
String currentTime = sdf.format(new Date());
|
|
||||||
StringBuilder sbLine = new StringBuilder();
|
|
||||||
sbLine.append("[");
|
|
||||||
sbLine.append(currentTime);
|
|
||||||
sbLine.append("] Power to ");
|
|
||||||
sbLine.append(appName);
|
|
||||||
appWinBoLLNewsBean.setMessage(sbLine.toString());
|
|
||||||
|
|
||||||
APPNewsWidget.addWinBoLLNewsBean(context, appWinBoLLNewsBean);
|
|
||||||
|
|
||||||
Intent intentWidget = new Intent(context, APPNewsWidget.class);
|
|
||||||
intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
|
|
||||||
context.sendBroadcast(intentWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ToastUtils.show(szAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注册 Receiver
|
|
||||||
//
|
|
||||||
public void registerAction(MainService service) {
|
|
||||||
IntentFilter filter=new IntentFilter();
|
|
||||||
filter.addAction(ACTION_BOOT_COMPLETED);
|
|
||||||
filter.addAction(SOS.ACTION_SOS);
|
|
||||||
filter.addAction(WinBoLL.ACTION_BIND);
|
|
||||||
//filter.addAction(Intent.ACTION_BATTERY_CHANGED);
|
|
||||||
service.registerReceiver(this, filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.services;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/02/14 03:38:31
|
|
||||||
* @Describe 守护进程服务
|
|
||||||
*/
|
|
||||||
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.appbase.models.MainServiceBean;
|
|
||||||
import cc.winboll.studio.appbase.services.AssistantService;
|
|
||||||
import cc.winboll.studio.appbase.services.MainService;
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
|
||||||
import android.os.Binder;
|
|
||||||
|
|
||||||
public class AssistantService extends Service {
|
|
||||||
|
|
||||||
public static final String TAG = "AssistantService";
|
|
||||||
|
|
||||||
MainServiceBean mMainServiceBean;
|
|
||||||
MyServiceConnection mMyServiceConnection;
|
|
||||||
MainService mMainService;
|
|
||||||
boolean isBound = false;
|
|
||||||
volatile boolean isThreadAlive = false;
|
|
||||||
|
|
||||||
public synchronized void setIsThreadAlive(boolean isThreadAlive) {
|
|
||||||
LogUtils.d(TAG, "setIsThreadAlive(...)");
|
|
||||||
LogUtils.d(TAG, String.format("isThreadAlive %s", isThreadAlive));
|
|
||||||
this.isThreadAlive = isThreadAlive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isThreadAlive() {
|
|
||||||
return isThreadAlive;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBinder onBind(Intent intent) {
|
|
||||||
return new MyBinder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
LogUtils.d(TAG, "onCreate");
|
|
||||||
super.onCreate();
|
|
||||||
|
|
||||||
//mMyBinder = new MyBinder();
|
|
||||||
if (mMyServiceConnection == null) {
|
|
||||||
mMyServiceConnection = new MyServiceConnection();
|
|
||||||
}
|
|
||||||
// 设置运行参数
|
|
||||||
setIsThreadAlive(false);
|
|
||||||
assistantService();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
||||||
LogUtils.d(TAG, "call onStartCommand(...)");
|
|
||||||
assistantService();
|
|
||||||
return START_STICKY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
//LogUtils.d(TAG, "onDestroy");
|
|
||||||
setIsThreadAlive(false);
|
|
||||||
// 解除绑定
|
|
||||||
if (isBound) {
|
|
||||||
unbindService(mMyServiceConnection);
|
|
||||||
isBound = false;
|
|
||||||
}
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运行服务内容
|
|
||||||
//
|
|
||||||
void assistantService() {
|
|
||||||
LogUtils.d(TAG, "assistantService()");
|
|
||||||
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
|
||||||
LogUtils.d(TAG, String.format("mMainServiceBean.isEnable() %s", mMainServiceBean.isEnable()));
|
|
||||||
if (mMainServiceBean.isEnable()) {
|
|
||||||
LogUtils.d(TAG, String.format("mIsThreadAlive %s", isThreadAlive()));
|
|
||||||
if (isThreadAlive() == false) {
|
|
||||||
// 设置运行状态
|
|
||||||
setIsThreadAlive(true);
|
|
||||||
// 唤醒和绑定主进程
|
|
||||||
wakeupAndBindMain();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 唤醒和绑定主进程
|
|
||||||
//
|
|
||||||
void wakeupAndBindMain() {
|
|
||||||
LogUtils.d(TAG, "wakeupAndBindMain()");
|
|
||||||
// 绑定服务的Intent
|
|
||||||
Intent intent = new Intent(this, MainService.class);
|
|
||||||
startService(new Intent(this, MainService.class));
|
|
||||||
bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
|
|
||||||
|
|
||||||
// startService(new Intent(this, MainService.class));
|
|
||||||
// bindService(new Intent(AssistantService.this, MainService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 主进程与守护进程连接时需要用到此类
|
|
||||||
//
|
|
||||||
class MyServiceConnection implements ServiceConnection {
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
||||||
LogUtils.d(TAG, "onServiceConnected(...)");
|
|
||||||
MainService.MyBinder binder = (MainService.MyBinder) service;
|
|
||||||
mMainService = binder.getService();
|
|
||||||
isBound = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
|
||||||
LogUtils.d(TAG, "onServiceDisconnected(...)");
|
|
||||||
mMainServiceBean = MainServiceBean.loadBean(AssistantService.this, MainServiceBean.class);
|
|
||||||
if (mMainServiceBean.isEnable()) {
|
|
||||||
wakeupAndBindMain();
|
|
||||||
}
|
|
||||||
isBound = false;
|
|
||||||
mMainService = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 用于返回服务实例的Binder
|
|
||||||
public class MyBinder extends Binder {
|
|
||||||
AssistantService getService() {
|
|
||||||
LogUtils.d(TAG, "AssistantService MyBinder getService()");
|
|
||||||
return AssistantService.this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,317 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.services;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/02/13 06:56:41
|
|
||||||
* @Describe 拨号主服务
|
|
||||||
* 参考:
|
|
||||||
* 进程保活-双进程守护的正确姿势
|
|
||||||
* https://blog.csdn.net/sinat_35159441/article/details/75267380
|
|
||||||
* Android Service之onStartCommand方法研究
|
|
||||||
* https://blog.csdn.net/cyp331203/article/details/38920491
|
|
||||||
*/
|
|
||||||
import android.app.Service;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import cc.winboll.studio.appbase.MyTileService;
|
|
||||||
import cc.winboll.studio.appbase.handlers.MainServiceHandler;
|
|
||||||
import cc.winboll.studio.appbase.models.MainServiceBean;
|
|
||||||
import cc.winboll.studio.appbase.receivers.MainReceiver;
|
|
||||||
import cc.winboll.studio.appbase.services.AssistantService;
|
|
||||||
import cc.winboll.studio.appbase.threads.MainServiceThread;
|
|
||||||
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
|
||||||
import cc.winboll.studio.libappbase.models.APPModel;
|
|
||||||
import cc.winboll.studio.libappbase.models.WinBoLLModel;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class MainService extends Service {
|
|
||||||
|
|
||||||
public static final String TAG = "MainService";
|
|
||||||
|
|
||||||
public static final int MSG_UPDATE_STATUS = 0;
|
|
||||||
|
|
||||||
static MainService _mControlCenterService;
|
|
||||||
|
|
||||||
volatile boolean isServiceRunning;
|
|
||||||
|
|
||||||
MainServiceBean mMainServiceBean;
|
|
||||||
MainServiceThread mMainServiceThread;
|
|
||||||
MainServiceHandler mMainServiceHandler;
|
|
||||||
MyServiceConnection mMyServiceConnection;
|
|
||||||
AssistantService mAssistantService;
|
|
||||||
boolean isBound = false;
|
|
||||||
MainReceiver mMainReceiver;
|
|
||||||
ArrayList<APPConnection> mAPPModelConnectionList;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBinder onBind(Intent intent) {
|
|
||||||
return new MyBinder();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MainServiceThread getRemindThread() {
|
|
||||||
return mMainServiceThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
LogUtils.d(TAG, "onCreate()");
|
|
||||||
mAPPModelConnectionList = new ArrayList<APPConnection>();
|
|
||||||
|
|
||||||
_mControlCenterService = MainService.this;
|
|
||||||
isServiceRunning = false;
|
|
||||||
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
|
||||||
|
|
||||||
if (mMyServiceConnection == null) {
|
|
||||||
mMyServiceConnection = new MyServiceConnection();
|
|
||||||
}
|
|
||||||
mMainServiceHandler = new MainServiceHandler(this);
|
|
||||||
|
|
||||||
// 运行服务内容
|
|
||||||
mainService();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
||||||
LogUtils.d(TAG, "onStartCommand(...)");
|
|
||||||
// 运行服务内容
|
|
||||||
mainService();
|
|
||||||
return (mMainServiceBean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运行服务内容
|
|
||||||
//
|
|
||||||
void mainService() {
|
|
||||||
LogUtils.d(TAG, "mainService()");
|
|
||||||
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
|
||||||
if (mMainServiceBean.isEnable() && isServiceRunning == false) {
|
|
||||||
LogUtils.d(TAG, "mainService() start running");
|
|
||||||
isServiceRunning = true;
|
|
||||||
// 唤醒守护进程
|
|
||||||
wakeupAndBindAssistant();
|
|
||||||
|
|
||||||
if (mMainReceiver == null) {
|
|
||||||
// 注册广播接收器
|
|
||||||
mMainReceiver = new MainReceiver(this);
|
|
||||||
mMainReceiver.registerAction(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启动小部件
|
|
||||||
Intent intentTimeWidget = new Intent(this, APPNewsWidget.class);
|
|
||||||
intentTimeWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
|
|
||||||
this.sendBroadcast(intentTimeWidget);
|
|
||||||
|
|
||||||
startMainServiceThread();
|
|
||||||
|
|
||||||
MyTileService.updateServiceIconStatus(this);
|
|
||||||
|
|
||||||
LogUtils.i(TAG, "Main Service Is Start.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 唤醒和绑定守护进程
|
|
||||||
//
|
|
||||||
void wakeupAndBindAssistant() {
|
|
||||||
LogUtils.d(TAG, "wakeupAndBindAssistant()");
|
|
||||||
|
|
||||||
Intent intent = new Intent(this, AssistantService.class);
|
|
||||||
startService(intent);
|
|
||||||
// 绑定服务的Intent
|
|
||||||
bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开启提醒铃声线程
|
|
||||||
//
|
|
||||||
public void startMainServiceThread() {
|
|
||||||
LogUtils.d(TAG, "startMainServiceThread");
|
|
||||||
if (mMainServiceThread == null) {
|
|
||||||
mMainServiceThread = new MainServiceThread(this, mMainServiceHandler);
|
|
||||||
LogUtils.d(TAG, "new MainServiceThread");
|
|
||||||
} else {
|
|
||||||
if (mMainServiceThread.isExist() == true) {
|
|
||||||
mMainServiceThread = new MainServiceThread(this, mMainServiceHandler);
|
|
||||||
LogUtils.d(TAG, "renew MainServiceThread");
|
|
||||||
} else {
|
|
||||||
// 提醒进程正在进行中就更新状态后退出
|
|
||||||
LogUtils.d(TAG, "A mMainServiceThread running.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mMainServiceThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stopRemindThread() {
|
|
||||||
if (mMainServiceThread != null) {
|
|
||||||
mMainServiceThread.setIsExist(true);
|
|
||||||
mMainServiceThread = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
//LogUtils.d(TAG, "onDestroy");
|
|
||||||
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
|
||||||
if (mMainServiceBean.isEnable() == false) {
|
|
||||||
// 设置运行状态
|
|
||||||
isServiceRunning = false;// 解除绑定
|
|
||||||
if (isBound) {
|
|
||||||
unbindService(mMyServiceConnection);
|
|
||||||
isBound = false;
|
|
||||||
}
|
|
||||||
// 停止守护进程
|
|
||||||
Intent intent = new Intent(this, AssistantService.class);
|
|
||||||
stopService(intent);
|
|
||||||
// 停止Receiver
|
|
||||||
if (mMainReceiver != null) {
|
|
||||||
unregisterReceiver(mMainReceiver);
|
|
||||||
mMainReceiver = null;
|
|
||||||
}
|
|
||||||
// 停止前台通知栏
|
|
||||||
stopForeground(true);
|
|
||||||
// 停止消息提醒进程
|
|
||||||
stopRemindThread();
|
|
||||||
|
|
||||||
MyTileService.updateServiceIconStatus(this);
|
|
||||||
|
|
||||||
super.onDestroy();
|
|
||||||
//LogUtils.d(TAG, "onDestroy done");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bindWinBoLLModelConnection(WinBoLLModel bean) {
|
|
||||||
LogUtils.d(TAG, "bindAPPModelConnection(...)");
|
|
||||||
// 清理旧的绑定链接
|
|
||||||
for (int i = mAPPModelConnectionList.size() - 1; i > -1; i--) {
|
|
||||||
APPConnection item = mAPPModelConnectionList.get(i);
|
|
||||||
if (item.isBindToAPP(bean)) {
|
|
||||||
LogUtils.d(TAG, "Bind Servive exist.");
|
|
||||||
unbindService(item);
|
|
||||||
mAPPModelConnectionList.remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绑定服务
|
|
||||||
APPConnection appConnection = new APPConnection();
|
|
||||||
Intent intentService = new Intent();
|
|
||||||
intentService.setComponent(new ComponentName(bean.getAppPackageName(), bean.getAppMainServiveName()));
|
|
||||||
bindService(intentService, appConnection, Context.BIND_IMPORTANT);
|
|
||||||
mAPPModelConnectionList.add(appConnection);
|
|
||||||
|
|
||||||
Intent intentWidget = new Intent(this, APPNewsWidget.class);
|
|
||||||
intentWidget.setAction(APPNewsWidget.ACTION_WAKEUP_SERVICE);
|
|
||||||
WinBoLLModel appSOSBean = new WinBoLLModel(bean.getAppPackageName(), bean.getAppMainServiveName());
|
|
||||||
intentWidget.putExtra("APPSOSBean", appSOSBean.toString());
|
|
||||||
sendBroadcast(intentWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class APPConnection implements ServiceConnection {
|
|
||||||
|
|
||||||
ComponentName mComponentName;
|
|
||||||
|
|
||||||
boolean isBindToAPP(WinBoLLModel bean) {
|
|
||||||
return mComponentName != null
|
|
||||||
&& mComponentName.getClassName().equals(bean.getAppMainServiveName())
|
|
||||||
&& mComponentName.getPackageName().equals(bean.getAppPackageName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
||||||
LogUtils.d(TAG, "onServiceConnected(...)");
|
|
||||||
mComponentName = name;
|
|
||||||
LogUtils.d(TAG, String.format("onServiceConnected : \ngetClassName %s\ngetPackageName %s", name.getClassName(), name.getPackageName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
|
||||||
LogUtils.d(TAG, "onServiceDisconnected(...)");
|
|
||||||
LogUtils.d(TAG, String.format("onServiceDisconnected : \ngetClassName %s\ngetPackageName %s", name.getClassName(), name.getPackageName()));
|
|
||||||
|
|
||||||
// 尝试无参数启动一下服务
|
|
||||||
String appPackage = mComponentName.getPackageName();
|
|
||||||
LogUtils.d(TAG, String.format("appPackage %s", appPackage));
|
|
||||||
String appMainServiceClassName = mComponentName.getClassName();
|
|
||||||
LogUtils.d(TAG, String.format("appMainServiceClassName %s", appMainServiceClassName));
|
|
||||||
|
|
||||||
Intent intentService = new Intent();
|
|
||||||
intentService.setComponent(new ComponentName(appPackage, appMainServiceClassName));
|
|
||||||
startService(intentService);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 主进程与守护进程连接时需要用到此类
|
|
||||||
//
|
|
||||||
private class MyServiceConnection implements ServiceConnection {
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
||||||
LogUtils.d(TAG, "onServiceConnected(...)");
|
|
||||||
AssistantService.MyBinder binder = (AssistantService.MyBinder) service;
|
|
||||||
mAssistantService = binder.getService();
|
|
||||||
isBound = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
|
||||||
LogUtils.d(TAG, "onServiceDisconnected(...)");
|
|
||||||
|
|
||||||
if (mMainServiceBean.isEnable()) {
|
|
||||||
// 唤醒守护进程
|
|
||||||
wakeupAndBindAssistant();
|
|
||||||
}
|
|
||||||
isBound = false;
|
|
||||||
mAssistantService = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 用于返回服务实例的Binder
|
|
||||||
public class MyBinder extends Binder {
|
|
||||||
MainService getService() {
|
|
||||||
LogUtils.d(TAG, "MainService MyBinder getService()");
|
|
||||||
return MainService.this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// //
|
|
||||||
// // 启动服务
|
|
||||||
// //
|
|
||||||
// public static void startControlCenterService(Context context) {
|
|
||||||
// Intent intent = new Intent(context, MainService.class);
|
|
||||||
// context.startForegroundService(intent);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// //
|
|
||||||
// // 停止服务
|
|
||||||
// //
|
|
||||||
// public static void stopControlCenterService(Context context) {
|
|
||||||
// Intent intent = new Intent(context, MainService.class);
|
|
||||||
// context.stopService(intent);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public void appenMessage(String message) {
|
|
||||||
LogUtils.d(TAG, String.format("Message : %s", message));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void stopMainService(Context context) {
|
|
||||||
LogUtils.d(TAG, "stopMainService");
|
|
||||||
MainServiceBean bean = new MainServiceBean();
|
|
||||||
bean.setIsEnable(false);
|
|
||||||
MainServiceBean.saveBean(context, bean);
|
|
||||||
context.stopService(new Intent(context, MainService.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startMainService(Context context) {
|
|
||||||
LogUtils.d(TAG, "startMainService");
|
|
||||||
MainServiceBean bean = new MainServiceBean();
|
|
||||||
bean.setIsEnable(true);
|
|
||||||
MainServiceBean.saveBean(context, bean);
|
|
||||||
context.startService(new Intent(context, MainService.class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.services;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/03/07 12:45:49
|
|
||||||
* @Describe 启动时申请绑定到APPBase主服务的服务示例
|
|
||||||
*/
|
|
||||||
import android.app.Service;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import cc.winboll.studio.appbase.App;
|
|
||||||
import cc.winboll.studio.appbase.models.TestDemoBindServiceBean;
|
|
||||||
import cc.winboll.studio.appbase.services.TestDemoBindService;
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
|
||||||
import cc.winboll.studio.libappbase.sos.SOS;
|
|
||||||
import cc.winboll.studio.libappbase.winboll.WinBoLL;
|
|
||||||
|
|
||||||
public class TestDemoBindService extends Service {
|
|
||||||
|
|
||||||
public static final String TAG = "TestDemoBindService";
|
|
||||||
|
|
||||||
public static final String ACTION_ENABLE = TestDemoBindService.class.getName() + ".ACTION_ENABLE";
|
|
||||||
public static final String ACTION_DISABLE = TestDemoBindService.class.getName() + ".ACTION_DISABLE";
|
|
||||||
|
|
||||||
volatile static TestThread _TestThread;
|
|
||||||
|
|
||||||
volatile static boolean _IsRunning;
|
|
||||||
|
|
||||||
public synchronized static void setIsRunning(boolean isRunning) {
|
|
||||||
_IsRunning = isRunning;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isRunning() {
|
|
||||||
return _IsRunning;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBinder onBind(Intent intent) {
|
|
||||||
return new MyBinder();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MyBinder extends Binder {
|
|
||||||
public TestDemoBindService getService() {
|
|
||||||
return TestDemoBindService.this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
LogUtils.d(TAG, "onCreate()");
|
|
||||||
|
|
||||||
run();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
||||||
LogUtils.d(TAG, "onStartCommand(...)");
|
|
||||||
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
|
|
||||||
if (bean == null) {
|
|
||||||
bean = new TestDemoBindServiceBean();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intent.getAction() != null) {
|
|
||||||
if (intent.getAction().equals(ACTION_ENABLE)) {
|
|
||||||
bean.setIsEnable(true);
|
|
||||||
LogUtils.d(TAG, "setIsEnable(true);");
|
|
||||||
TestDemoBindServiceBean.saveBean(this, bean);
|
|
||||||
} else if (intent.getAction().equals(ACTION_DISABLE)) {
|
|
||||||
bean.setIsEnable(false);
|
|
||||||
LogUtils.d(TAG, "setIsEnable(false);");
|
|
||||||
TestDemoBindServiceBean.saveBean(this, bean);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run();
|
|
||||||
|
|
||||||
return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
|
|
||||||
//return super.onStartCommand(intent, flags, startId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run() {
|
|
||||||
LogUtils.d(TAG, "run()");
|
|
||||||
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
|
|
||||||
if (bean == null) {
|
|
||||||
bean = new TestDemoBindServiceBean();
|
|
||||||
TestDemoBindServiceBean.saveBean(this, bean);
|
|
||||||
}
|
|
||||||
if (bean.isEnable()) {
|
|
||||||
LogUtils.d(TAG, "run() bean.isEnable()");
|
|
||||||
TestThread.getInstance(this).start();
|
|
||||||
LogUtils.d(TAG, "_TestThread.start()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
LogUtils.d(TAG, "onDestroy()");
|
|
||||||
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
|
|
||||||
if (bean == null) {
|
|
||||||
bean = new TestDemoBindServiceBean();
|
|
||||||
}
|
|
||||||
|
|
||||||
TestThread.getInstance(this).setIsExit(true);
|
|
||||||
|
|
||||||
// 预防 APPBase 应用重启绑定失效。
|
|
||||||
// 所以退出时检查本服务是否配置启用,如果启用就发送一个 SOS 信号。
|
|
||||||
// 这样 APPBase 就会用组件方式启动本服务。
|
|
||||||
if (bean.isEnable()) {
|
|
||||||
if (App.isDebuging()) {
|
|
||||||
SOS.sosToAppBaseBeta(this, TestDemoBindService.class.getName());
|
|
||||||
} else {
|
|
||||||
SOS.sosToAppBase(this, TestDemoBindService.class.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_IsRunning = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class TestThread extends Thread {
|
|
||||||
|
|
||||||
volatile static TestThread _TestThread;
|
|
||||||
Context mContext;
|
|
||||||
volatile boolean isStarted = false;
|
|
||||||
volatile boolean isExit = false;
|
|
||||||
|
|
||||||
TestThread(Context context) {
|
|
||||||
super();
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized TestThread getInstance(Context context) {
|
|
||||||
if (_TestThread != null) {
|
|
||||||
_TestThread.setIsExit(true);
|
|
||||||
}
|
|
||||||
_TestThread = new TestThread(context);
|
|
||||||
|
|
||||||
return _TestThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void setIsExit(boolean isExit) {
|
|
||||||
this.isExit = isExit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isExit() {
|
|
||||||
return isExit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isStarted == false) {
|
|
||||||
isStarted = true;
|
|
||||||
super.run();
|
|
||||||
LogUtils.d(TAG, "run() start");
|
|
||||||
if (App.isDebuging()) {
|
|
||||||
WinBoLL.bindToAPPBaseBeta(mContext, TestDemoBindService.class.getName());
|
|
||||||
} else {
|
|
||||||
WinBoLL.bindToAPPBase(mContext, TestDemoBindService.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!isExit()) {
|
|
||||||
LogUtils.d(TAG, "run()");
|
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LogUtils.d(TAG, "run() exit");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.services;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/03/07 12:39:24
|
|
||||||
* @Describe 普通服务示例
|
|
||||||
*/
|
|
||||||
import android.app.Service;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import cc.winboll.studio.appbase.models.TestDemoServiceBean;
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
|
||||||
|
|
||||||
public class TestDemoService extends Service {
|
|
||||||
|
|
||||||
public static final String TAG = "TestDemoService";
|
|
||||||
|
|
||||||
public static final String ACTION_ENABLE = TestDemoService.class.getName() + ".ACTION_ENABLE";
|
|
||||||
public static final String ACTION_DISABLE = TestDemoService.class.getName() + ".ACTION_DISABLE";
|
|
||||||
|
|
||||||
volatile static TestThread _TestThread;
|
|
||||||
|
|
||||||
volatile static boolean _IsRunning;
|
|
||||||
|
|
||||||
public synchronized static void setIsRunning(boolean isRunning) {
|
|
||||||
_IsRunning = isRunning;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isRunning() {
|
|
||||||
return _IsRunning;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBinder onBind(Intent intent) {
|
|
||||||
return new MyBinder();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MyBinder extends Binder {
|
|
||||||
public TestDemoService getService() {
|
|
||||||
return TestDemoService.this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
LogUtils.d(TAG, "onCreate()");
|
|
||||||
|
|
||||||
|
|
||||||
run();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
||||||
LogUtils.d(TAG, "onStartCommand(...)");
|
|
||||||
TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
|
|
||||||
if (bean == null) {
|
|
||||||
bean = new TestDemoServiceBean();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intent.getAction() != null) {
|
|
||||||
if (intent.getAction().equals(ACTION_ENABLE)) {
|
|
||||||
bean.setIsEnable(true);
|
|
||||||
LogUtils.d(TAG, "setIsEnable(true);");
|
|
||||||
TestDemoServiceBean.saveBean(this, bean);
|
|
||||||
} else if (intent.getAction().equals(ACTION_DISABLE)) {
|
|
||||||
bean.setIsEnable(false);
|
|
||||||
LogUtils.d(TAG, "setIsEnable(false);");
|
|
||||||
TestDemoServiceBean.saveBean(this, bean);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run();
|
|
||||||
|
|
||||||
return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
|
|
||||||
//return super.onStartCommand(intent, flags, startId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run() {
|
|
||||||
LogUtils.d(TAG, "run()");
|
|
||||||
TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
|
|
||||||
if (bean == null) {
|
|
||||||
bean = new TestDemoServiceBean();
|
|
||||||
TestDemoServiceBean.saveBean(this, bean);
|
|
||||||
}
|
|
||||||
if (bean.isEnable()) {
|
|
||||||
LogUtils.d(TAG, "run() bean.isEnable()");
|
|
||||||
TestThread.getInstance(this).start();
|
|
||||||
LogUtils.d(TAG, "_TestThread.start()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
LogUtils.d(TAG, "onDestroy()");
|
|
||||||
TestThread.getInstance(this).setIsExit(true);
|
|
||||||
|
|
||||||
_IsRunning = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class TestThread extends Thread {
|
|
||||||
|
|
||||||
volatile static TestThread _TestThread;
|
|
||||||
Context mContext;
|
|
||||||
volatile boolean isStarted = false;
|
|
||||||
volatile boolean isExit = false;
|
|
||||||
|
|
||||||
TestThread(Context context) {
|
|
||||||
super();
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized TestThread getInstance(Context context) {
|
|
||||||
if (_TestThread != null) {
|
|
||||||
_TestThread.setIsExit(true);
|
|
||||||
}
|
|
||||||
_TestThread = new TestThread(context);
|
|
||||||
|
|
||||||
return _TestThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void setIsExit(boolean isExit) {
|
|
||||||
this.isExit = isExit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isExit() {
|
|
||||||
return isExit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isStarted == false) {
|
|
||||||
isStarted = true;
|
|
||||||
super.run();
|
|
||||||
LogUtils.d(TAG, "run() start");
|
|
||||||
|
|
||||||
while (!isExit()) {
|
|
||||||
LogUtils.d(TAG, "run()");
|
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LogUtils.d(TAG, "run() exit");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.threads;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/02/14 03:46:44
|
|
||||||
*/
|
|
||||||
import android.content.Context;
|
|
||||||
import cc.winboll.studio.appbase.handlers.MainServiceHandler;
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
|
|
||||||
public class MainServiceThread extends Thread {
|
|
||||||
|
|
||||||
public static final String TAG = "MainServiceThread";
|
|
||||||
|
|
||||||
Context mContext;
|
|
||||||
|
|
||||||
// 控制线程是否退出的标志
|
|
||||||
volatile boolean isExist = false;
|
|
||||||
|
|
||||||
// 服务Handler, 用于线程发送消息使用
|
|
||||||
WeakReference<MainServiceHandler> mwrMainServiceHandler;
|
|
||||||
|
|
||||||
public void setIsExist(boolean isExist) {
|
|
||||||
this.isExist = isExist;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isExist() {
|
|
||||||
return isExist;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MainServiceThread(Context context, MainServiceHandler handler) {
|
|
||||||
mContext = context;
|
|
||||||
mwrMainServiceHandler = new WeakReference<MainServiceHandler>(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
LogUtils.d(TAG, "run()");
|
|
||||||
|
|
||||||
while (!isExist()) {
|
|
||||||
//ToastUtils.show("run()");
|
|
||||||
//LogUtils.d(TAG, "run()");
|
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogUtils.d(TAG, "run() exit.");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
package cc.winboll.studio.appbase.widgets;
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
|
||||||
* @Date 2025/02/15 14:41:25
|
|
||||||
* @Describe TimeWidget
|
|
||||||
*/
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.appwidget.AppWidgetManager;
|
|
||||||
import android.appwidget.AppWidgetProvider;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.widget.RemoteViews;
|
|
||||||
import cc.winboll.studio.appbase.R;
|
|
||||||
import cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener;
|
|
||||||
import cc.winboll.studio.libappbase.AppUtils;
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
|
||||||
import cc.winboll.studio.libappbase.models.APPModel;
|
|
||||||
import cc.winboll.studio.libappbase.models.WinBoLLNewsBean;
|
|
||||||
import cc.winboll.studio.libappbase.winboll.WinBoLL;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import cc.winboll.studio.libappbase.models.WinBoLLModel;
|
|
||||||
|
|
||||||
public class APPNewsWidget extends AppWidgetProvider {
|
|
||||||
|
|
||||||
public static final String TAG = "APPNewsWidget";
|
|
||||||
|
|
||||||
public static final String ACTION_WAKEUP_SERVICE = APPNewsWidget.class.getName() + ".ACTION_WAKEUP_SERVICE";
|
|
||||||
public static final String ACTION_RELOAD_REPORT = APPNewsWidget.class.getName() + ".ACTION_RELOAD_REPORT";
|
|
||||||
|
|
||||||
|
|
||||||
volatile static ArrayList<WinBoLLNewsBean> _WinBoLLNewsBeanList;
|
|
||||||
final static int _MAX_PAGES = 10;
|
|
||||||
final static int _OnePageLinesCount = 5;
|
|
||||||
volatile static int _CurrentPageIndex = 0;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
|
||||||
initWinBoLLNewsBeanList(context);
|
|
||||||
for (int appWidgetId : appWidgetIds) {
|
|
||||||
updateAppWidget(context, appWidgetManager, appWidgetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
super.onReceive(context, intent);
|
|
||||||
initWinBoLLNewsBeanList(context);
|
|
||||||
if (intent.getAction().equals(ACTION_RELOAD_REPORT)) {
|
|
||||||
LogUtils.d(TAG, "ACTION_RELOAD_REPORT");
|
|
||||||
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
|
||||||
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class));
|
|
||||||
for (int appWidgetId : appWidgetIds) {
|
|
||||||
updateAppWidget(context, appWidgetManager, appWidgetId);
|
|
||||||
}
|
|
||||||
}else if (intent.getAction().equals(ACTION_WAKEUP_SERVICE)) {
|
|
||||||
LogUtils.d(TAG, "ACTION_WAKEUP_SERVICE");
|
|
||||||
String szWinBoLLModel = intent.getStringExtra(WinBoLL.EXTRA_WINBOLLMODEL);
|
|
||||||
LogUtils.d(TAG, String.format("szWinBoLLModel %s", szWinBoLLModel));
|
|
||||||
if (szWinBoLLModel != null && !szWinBoLLModel.equals("")) {
|
|
||||||
try {
|
|
||||||
WinBoLLModel bean = WinBoLLModel.parseStringToBean(szWinBoLLModel, WinBoLLModel.class);
|
|
||||||
if (bean != null) {
|
|
||||||
String szAppPackageName = bean.getAppPackageName();
|
|
||||||
LogUtils.d(TAG, String.format("szAppPackageName %s", szAppPackageName));
|
|
||||||
String szAppMainServiveName = bean.getAppMainServiveName();
|
|
||||||
LogUtils.d(TAG, String.format("szAppMainServiveName %s", szAppMainServiveName));
|
|
||||||
|
|
||||||
|
|
||||||
String appName = AppUtils.getAppNameByPackageName(context, szAppPackageName);
|
|
||||||
LogUtils.d(TAG, String.format("appName %s", appName));
|
|
||||||
WinBoLLNewsBean winBollNewsBean = new WinBoLLNewsBean(appName);
|
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
|
|
||||||
String currentTime = sdf.format(new Date());
|
|
||||||
StringBuilder sbLine = new StringBuilder();
|
|
||||||
sbLine.append("[");
|
|
||||||
sbLine.append(currentTime);
|
|
||||||
sbLine.append("] Wake up ");
|
|
||||||
sbLine.append(appName);
|
|
||||||
winBollNewsBean.setMessage(sbLine.toString());
|
|
||||||
|
|
||||||
addWinBoLLNewsBean(context, winBollNewsBean);
|
|
||||||
|
|
||||||
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
|
||||||
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class));
|
|
||||||
for (int appWidgetId : appWidgetIds) {
|
|
||||||
updateAppWidget(context, appWidgetManager, appWidgetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// 加入新报告信息
|
|
||||||
//
|
|
||||||
public synchronized static void addWinBoLLNewsBean(Context context, WinBoLLNewsBean bean) {
|
|
||||||
initWinBoLLNewsBeanList(context);
|
|
||||||
_WinBoLLNewsBeanList.add(0, bean);
|
|
||||||
// 控制记录总数
|
|
||||||
while (_WinBoLLNewsBeanList.size() > _MAX_PAGES * _OnePageLinesCount) {
|
|
||||||
_WinBoLLNewsBeanList.remove(_WinBoLLNewsBeanList.size() - 1);
|
|
||||||
}
|
|
||||||
WinBoLLNewsBean.saveBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized static void initWinBoLLNewsBeanList(Context context) {
|
|
||||||
if (_WinBoLLNewsBeanList == null) {
|
|
||||||
_WinBoLLNewsBeanList = new ArrayList<WinBoLLNewsBean>();
|
|
||||||
WinBoLLNewsBean.loadBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class);
|
|
||||||
}
|
|
||||||
if (_WinBoLLNewsBeanList == null) {
|
|
||||||
_WinBoLLNewsBeanList = new ArrayList<WinBoLLNewsBean>();
|
|
||||||
WinBoLLNewsBean.saveBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
|
|
||||||
LogUtils.d(TAG, "updateAppWidget(...)");
|
|
||||||
|
|
||||||
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_news);
|
|
||||||
//设置按钮点击事件
|
|
||||||
Intent intentPre = new Intent(context, APPNewsWidgetClickListener.class);
|
|
||||||
intentPre.setAction(APPNewsWidgetClickListener.ACTION_PRE);
|
|
||||||
PendingIntent pendingIntentPre = PendingIntent.getBroadcast(context, 0, intentPre, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
views.setOnClickPendingIntent(R.id.widget_button_pre, pendingIntentPre);
|
|
||||||
Intent intentNext = new Intent(context, APPNewsWidgetClickListener.class);
|
|
||||||
intentNext.setAction(APPNewsWidgetClickListener.ACTION_NEXT);
|
|
||||||
PendingIntent pendingIntentNext = PendingIntent.getBroadcast(context, 0, intentNext, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
views.setOnClickPendingIntent(R.id.widget_button_next, pendingIntentNext);
|
|
||||||
|
|
||||||
views.setTextViewText(R.id.tv_msg, getPageInfo());
|
|
||||||
views.setTextViewText(R.id.tv_news, getMessage());
|
|
||||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getMessage() {
|
|
||||||
ArrayList<String> msgTemp = new ArrayList<String>();
|
|
||||||
if (_WinBoLLNewsBeanList != null) {
|
|
||||||
int start = _OnePageLinesCount * _CurrentPageIndex;
|
|
||||||
start = _WinBoLLNewsBeanList.size() > start ? start : _WinBoLLNewsBeanList.size() - 1;
|
|
||||||
for (int i = start, j = 0; i < _WinBoLLNewsBeanList.size() && j < _OnePageLinesCount && start > -1; i++, j++) {
|
|
||||||
msgTemp.add(_WinBoLLNewsBeanList.get(i).getMessage());
|
|
||||||
}
|
|
||||||
String message = String.join("\n", msgTemp);
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void prePage(Context context) {
|
|
||||||
if (_WinBoLLNewsBeanList != null) {
|
|
||||||
if (_CurrentPageIndex > 0) {
|
|
||||||
_CurrentPageIndex = _CurrentPageIndex - 1;
|
|
||||||
}
|
|
||||||
Intent intentWidget = new Intent(context, APPNewsWidget.class);
|
|
||||||
intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
|
|
||||||
context.sendBroadcast(intentWidget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void nextPage(Context context) {
|
|
||||||
if (_WinBoLLNewsBeanList != null) {
|
|
||||||
if ((_CurrentPageIndex + 1) * _OnePageLinesCount < _WinBoLLNewsBeanList.size()) {
|
|
||||||
_CurrentPageIndex = _CurrentPageIndex + 1;
|
|
||||||
}
|
|
||||||
Intent intentWidget = new Intent(context, APPNewsWidget.class);
|
|
||||||
intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
|
|
||||||
context.sendBroadcast(intentWidget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String getPageInfo() {
|
|
||||||
if (_WinBoLLNewsBeanList == null) {
|
|
||||||
return "0/0";
|
|
||||||
}
|
|
||||||
int leftCount = _WinBoLLNewsBeanList.size() % _OnePageLinesCount;
|
|
||||||
int currentPageCount = _WinBoLLNewsBeanList.size() / _OnePageLinesCount + (leftCount == 0 ?0: 1);
|
|
||||||
return String.format("%d/%d", _CurrentPageIndex + 1, currentPageCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
appbase/src/main/res/drawable/btn_light_blue.xml
Normal file
7
appbase/src/main/res/drawable/btn_light_blue.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="rectangle">
|
||||||
|
<solid android:color="#81C7F5"/> <!-- 浅蓝色填充 -->
|
||||||
|
<corners android:radius="8dp"/> <!-- 8dp 圆角 -->
|
||||||
|
</shape>
|
||||||
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:viewportWidth="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="#ff000000"
|
|
||||||
android:pathData="M6.5,20Q4.22,20 2.61,18.43 1,16.85 1,14.58 1,12.63 2.17,11.1 3.35,9.57 5.25,9.15 5.88,6.85 7.75,5.43 9.63,4 12,4 14.93,4 16.96,6.04 19,8.07 19,11 20.73,11.2 21.86,12.5 23,13.78 23,15.5 23,17.38 21.69,18.69 20.38,20 18.5,20Z"/>
|
|
||||||
|
|
||||||
</vector>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:viewportWidth="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="#ff000000"
|
|
||||||
android:pathData="M6.5,20Q4.22,20 2.61,18.43 1,16.85 1,14.58 1,12.63 2.17,11.1 3.35,9.57 5.25,9.15 5.88,6.85 7.75,5.43 9.63,4 12,4 14.93,4 16.96,6.04 19,8.07 19,11 20.73,11.2 21.86,12.5 23,13.78 23,15.5 23,17.38 21.69,18.69 20.38,20 18.5,20M6.5,18H18.5Q19.55,18 20.27,17.27 21,16.55 21,15.5 21,14.45 20.27,13.73 19.55,13 18.5,13H17V11Q17,8.93 15.54,7.46 14.08,6 12,6 9.93,6 8.46,7.46 7,8.93 7,11H6.5Q5.05,11 4.03,12.03 3,13.05 3,14.5 3,15.95 4.03,17 5.05,18 6.5,18M12,12Z"/>
|
|
||||||
|
|
||||||
</vector>
|
|
||||||
@@ -3,219 +3,45 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
<android.widget.Toolbar
|
<android.widget.Toolbar
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/toolbar"/>
|
android:id="@+id/toolbar"/>
|
||||||
|
|
||||||
<ScrollView
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1.0">
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:spacing="12dp">
|
||||||
|
|
||||||
<LinearLayout
|
<Button
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:text="应用崩溃测试"
|
||||||
android:gravity="center">
|
android:textSize="16sp"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:background="#81C7F5"
|
||||||
|
android:paddingVertical="12dp"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:onClick="onCrashTest"/>
|
||||||
|
|
||||||
<LinearLayout
|
<Button
|
||||||
android:orientation="vertical"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="0dp"
|
android:text="应用日志测试"
|
||||||
android:layout_weight="1.0"
|
android:textSize="16sp"
|
||||||
android:gravity="center_horizontal">
|
android:textColor="@android:color/white"
|
||||||
|
android:background="#81C7F5"
|
||||||
|
android:paddingVertical="12dp"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:onClick="onLogTest"/>
|
||||||
|
|
||||||
<TextView
|
</LinearLayout>
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="安卓R对应的是Android 11,其API级别是30。以下是Android 11中一些重要的API相关特性:
|
|
||||||
|
|
||||||
\n- 隐私保护方面:引入单次授权,让用户可以选择授予应用对位置信息、麦克风和摄像头的临时访问权限。还增加了数据访问审核功能,能让开发者深入了解应用在何处访问私密数据。
|
|
||||||
|
|
||||||
\n- 系统功能方面:提供了ControlsProviderService API,用于向连接的外部设备提供控件,这些控件显示于Android电源菜单中的设备控件下。媒体控件也得到更新,显示于快捷设置旁,来自多个应用的会话排列在一个可滑动的轮播界面中。
|
|
||||||
|
|
||||||
\n- 硬件支持方面:提供了一些API以支持瀑布屏,通过将窗口布局属性 layoutInDisplayCutoutMode 设为 LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS ,可允许窗口延伸到屏幕各个边缘上的刘海和瀑布区域。对于合页式屏幕配置的设备,提供了具有 TYPE_HINGE_ANGLE 的新传感器以及新的 SensorEvent ,用于监控合页角度。
|
|
||||||
|
|
||||||
\n- 安全方面:对生物识别身份验证机制进行了更新,引入了 BiometricManager.Authenticators 接口,定义了 BIOMETRIC_STRONG 、 BIOMETRIC_WEAK 、 DEVICE_CREDENTIAL 等身份验证类型。还在 BiometricPrompt 类中提供了对“每次使用时进行身份验证”密钥的更多支持。
|
|
||||||
|
|
||||||
\n- 性能和质量方面:支持无线调试,通过Android调试桥(adb)从工作站以无线方式部署和调试应用,避免了常见的USB连接问题。"/>
|
|
||||||
|
|
||||||
|
|
||||||
<HorizontalScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="right|center_vertical"
|
|
||||||
android:layout_width="wrap_content">
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Debug Mode"
|
|
||||||
android:layout_weight="1.0"
|
|
||||||
android:onClick="onSwitchDebugMode"
|
|
||||||
android:id="@+id/activitymainCheckBox1"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Test Application CrashReport"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onTestApplicationCrashReport"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="PreviewGlobalCrashActivity"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onPreviewGlobalCrashActivity"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</HorizontalScrollView>
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="400dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="right">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="StartCenter"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onStartCenter"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="StopCenter"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onStopCenter"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<HorizontalScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="SartTestDemoService"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onSartTestDemoService"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="StopTestDemoService"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onStopTestDemoService"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="StopTestDemoServiceNoSettings"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onStopTestDemoServiceNoSettings"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</HorizontalScrollView>
|
|
||||||
|
|
||||||
<HorizontalScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="SartTestDemoBindService"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onSartTestDemoBindService"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="StopTestDemoBindService"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onStopTestDemoBindService"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="StopTestDemoBindServiceNoSettings"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onStopTestDemoBindServiceNoSettings"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</HorizontalScrollView>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="TestStopMainServiceWithoutSettingEnable"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onTestStopMainServiceWithoutSettingEnable"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="TestUseComponentStartService"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onTestUseComponentStartService"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="TestDemoServiceSOS"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onTestDemoServiceSOS"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="TestOpenNewActivity"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onTestOpenNewActivity"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
<?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.widget.Toolbar
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/toolbar"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="NewActivity"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="CloseThisActivity"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onCloseThisActivity"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="CloseAllActivity"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onCloseAllActivity"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="New2Activity"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onNew2Activity"/>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
<?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.widget.Toolbar
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/toolbar"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="New2Activity"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="CloseThisActivity"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onCloseThisActivity"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="CloseAllActivity"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onCloseAllActivity"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="NewActivity"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onNewActivity"/>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
<?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"
|
|
||||||
android:background="#FFFFFFFF">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="right|center_vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/tv_title"
|
|
||||||
android:layout_weight="1.0"
|
|
||||||
android:text="WinBoLLNews"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textSize="16sp"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:text="⇦"
|
|
||||||
android:id="@+id/widget_button_pre"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:text="⇨"
|
|
||||||
android:id="@+id/widget_button_next"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/tv_msg"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:id="@+id/tv_news"
|
|
||||||
android:layout_weight="1.0"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
@@ -3,18 +3,6 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/item_home"
|
android:id="@+id/item_home"
|
||||||
android:title="HOME"
|
android:title="WinBoLL Home"
|
||||||
android:icon="@drawable/ic_winboll"/>
|
android:icon="@drawable/ic_winboll"/>
|
||||||
<item
|
|
||||||
android:id="@+id/item_yun"
|
|
||||||
android:title="YUN"
|
|
||||||
android:icon="@drawable/ic_winboll"/>
|
|
||||||
<item
|
|
||||||
android:id="@+id/item_logon"
|
|
||||||
android:title="Logon"
|
|
||||||
android:icon="@drawable/ic_winboll"/>
|
|
||||||
<item
|
|
||||||
android:id="@+id/item_log"
|
|
||||||
android:title="LOG"
|
|
||||||
android:icon="@drawable/ic_winboll_log"/>
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<network-security-config>
|
|
||||||
<!-- 允许访问 winboll.cc 及其子域名(原配置) -->
|
|
||||||
<domain-config cleartextTrafficPermitted="true">
|
|
||||||
<domain includeSubdomains="true">winboll.cc</domain>
|
|
||||||
</domain-config>
|
|
||||||
|
|
||||||
<!-- **新增:允许访问 IP 地址 10.8.0.250** -->
|
|
||||||
<domain-config cleartextTrafficPermitted="true">
|
|
||||||
<domain includeSubdomains="false">10.8.0.250</domain> <!-- 不包含子域名 -->
|
|
||||||
</domain-config>
|
|
||||||
</network-security-config>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:minWidth="200dp"
|
|
||||||
android:minHeight="100dp"
|
|
||||||
android:updatePeriodMillis="1000"
|
|
||||||
android:initialLayout="@layout/widget_news">
|
|
||||||
</appwidget-provider>
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Tue Jun 03 15:05:48 HKT 2025
|
#Mon Sep 01 07:56:33 HKT 2025
|
||||||
stageCount=5
|
stageCount=7
|
||||||
libraryProject=libapputils
|
libraryProject=libapputils
|
||||||
baseVersion=15.8
|
baseVersion=15.8
|
||||||
publishVersion=15.8.4
|
publishVersion=15.8.6
|
||||||
buildCount=0
|
buildCount=0
|
||||||
baseBetaVersion=15.8.5
|
baseBetaVersion=15.8.7
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.apputils;
|
package cc.winboll.studio.apputils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/12/08 15:10:51
|
* @Date 2024/12/08 15:10:51
|
||||||
* @Describe 全局应用类
|
* @Describe 全局应用类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.apputils;
|
package cc.winboll.studio.apputils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/01/03 11:02:49
|
* @Date 2025/01/03 11:02:49
|
||||||
* @Describe 一个可以浏览随 APP 附带的 Html 文档的窗口
|
* @Describe 一个可以浏览随 APP 附带的 Html 文档的窗口
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.apputils;
|
package cc.winboll.studio.apputils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/01/18 10:32:21
|
* @Date 2025/01/18 10:32:21
|
||||||
* @Describe 二维码扫码解码窗口
|
* @Describe 二维码扫码解码窗口
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.apputils;
|
package cc.winboll.studio.apputils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/03/23 16:14:45
|
* @Date 2025/03/23 16:14:45
|
||||||
*/
|
*/
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.apputils;
|
package cc.winboll.studio.apputils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/05/13 11:15
|
* @Date 2025/05/13 11:15
|
||||||
* @Describe WinBoLLActivity
|
* @Describe WinBoLLActivity
|
||||||
*/
|
*/
|
||||||
|
|||||||
35
autoinstaller/README.md
Normal file
35
autoinstaller/README.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# AutoInstaller
|
||||||
|
|
||||||
|
#### 介绍
|
||||||
|
APK 文件监控,监控指定文件路径的 APK 文件。
|
||||||
|
在 APK 文件改变后自动调用系统安装命令安装,或者调用[应用信息查看器]打开。
|
||||||
|
|
||||||
|
#### 软件架构
|
||||||
|
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
|
||||||
|
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
|
||||||
|
|
||||||
|
|
||||||
|
#### Gradle 编译说明
|
||||||
|
调试版编译命令 :gradle assembleBetaDebug
|
||||||
|
阶段版编译命令 :bash .winboll/bashPublishAPKAddTag.sh autoinstaller
|
||||||
|
|
||||||
|
#### 使用说明
|
||||||
|
|
||||||
|
#### 参与贡献
|
||||||
|
|
||||||
|
1. Fork 本仓库
|
||||||
|
2. 新建 Feat_xxx 分支
|
||||||
|
3. 提交代码 : ZhanGSKen(ZhanGSKen<zhangsken@188.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/)
|
||||||
|
|
||||||
|
#### 参考文档
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Sun May 04 05:32:00 GMT 2025
|
#Tue Jun 24 09:54:47 HKT 2025
|
||||||
stageCount=1
|
stageCount=3
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.2
|
baseVersion=15.2
|
||||||
publishVersion=15.2.0
|
publishVersion=15.2.2
|
||||||
buildCount=74
|
buildCount=0
|
||||||
baseBetaVersion=15.2.1
|
baseBetaVersion=15.2.3
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.autoinstaller;
|
package cc.winboll.studio.autoinstaller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/04/28 02:39:58
|
* @Date 2024/04/28 02:39:58
|
||||||
* @Describe 全局应用类
|
* @Describe 全局应用类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.autoinstaller;
|
package cc.winboll.studio.autoinstaller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/04/15 09:24:46
|
* @Date 2025/04/15 09:24:46
|
||||||
* @Describe 磁贴工具服务类
|
* @Describe 磁贴工具服务类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.autoinstaller.models;
|
package cc.winboll.studio.autoinstaller.models;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/04/02 20:50:29
|
* @Date 2025/04/02 20:50:29
|
||||||
* @Describe 监控的 APK 安装文件对应的应用信息数据模型
|
* @Describe 监控的 APK 安装文件对应的应用信息数据模型
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.autoinstaller.models;
|
package cc.winboll.studio.autoinstaller.models;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/05/27 17:36:01
|
* @Date 2024/05/27 17:36:01
|
||||||
* @Describe 应用配置数据类
|
* @Describe 应用配置数据类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import cc.winboll.studio.libappbase.BaseBean;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/04/15 09:27:39
|
* @Date 2025/04/15 09:27:39
|
||||||
* @Describe MainServiceBean
|
* @Describe MainServiceBean
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import cc.winboll.studio.autoinstaller.services.MainService;
|
|||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/06/06 13:19:38
|
* @Date 2024/06/06 13:19:38
|
||||||
* @Describe 应用消息接收类
|
* @Describe 应用消息接收类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import cc.winboll.studio.autoinstaller.models.AppConfigs;
|
|||||||
import cc.winboll.studio.autoinstaller.utils.ServiceUtil;
|
import cc.winboll.studio.autoinstaller.utils.ServiceUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/05/27 20:16:00
|
* @Date 2024/05/27 20:16:00
|
||||||
* @Describe MainService 守护进程服务
|
* @Describe MainService 守护进程服务
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.autoinstaller.utils;
|
package cc.winboll.studio.autoinstaller.utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/05/27 17:56:31
|
* @Date 2024/05/27 17:56:31
|
||||||
* @Describe 文件管理类
|
* @Describe 文件管理类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.autoinstaller.utils;
|
package cc.winboll.studio.autoinstaller.utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/12/11 06:28:50
|
* @Date 2024/12/11 06:28:50
|
||||||
* @Describe 一个获取安卓APK安装文件的应用包名的函数
|
* @Describe 一个获取安卓APK安装文件的应用包名的函数
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.autoinstaller.utils;
|
package cc.winboll.studio.autoinstaller.utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/05/27 20:20:03
|
* @Date 2024/05/27 20:20:03
|
||||||
* @Describe 服务类工具
|
* @Describe 服务类工具
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
https://github.com/aJIEw/PhoneCallApp.git
|
https://github.com/aJIEw/PhoneCallApp.git
|
||||||
|
|
||||||
#### 介绍
|
#### 介绍
|
||||||
通讯录与拨号
|
这是可以根据正则表达式匹配拦截骚扰电话的手机拨号应用。
|
||||||
|
|
||||||
#### 软件架构
|
#### 软件架构
|
||||||
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
|
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api fileTree(dir: 'libs', include: ['*.jar'])
|
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
api 'cc.winboll.studio:libaes:15.8.0'
|
api 'cc.winboll.studio:libaes:15.9.3'
|
||||||
api 'cc.winboll.studio:libapputils:15.8.1'
|
api 'cc.winboll.studio:libapputils:15.8.5'
|
||||||
api 'cc.winboll.studio:libappbase:15.8.1'
|
api 'cc.winboll.studio:libappbase:15.9.5'
|
||||||
|
|
||||||
// 权限请求框架:https://github.com/getActivity/XXPermissions
|
// 权限请求框架:https://github.com/getActivity/XXPermissions
|
||||||
api 'com.github.getActivity:XXPermissions:18.63'
|
api 'com.github.getActivity:XXPermissions:18.63'
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Tue May 20 13:02:18 HKT 2025
|
#Sun Aug 31 06:05:42 CST 2025
|
||||||
stageCount=3
|
stageCount=17
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.3
|
baseVersion=15.3
|
||||||
publishVersion=15.3.2
|
publishVersion=15.3.16
|
||||||
buildCount=0
|
buildCount=0
|
||||||
baseBetaVersion=15.3.3
|
baseBetaVersion=15.3.17
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts;
|
package cc.winboll.studio.contacts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/12/08 15:10:51
|
* @Date 2024/12/08 15:10:51
|
||||||
* @Describe 全局应用类
|
* @Describe 全局应用类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
package cc.winboll.studio.contacts;
|
package cc.winboll.studio.contacts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
|
* @Date 2025/08/30 14:32
|
||||||
|
* @Describe 主窗口
|
||||||
|
*/
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
@@ -8,6 +13,7 @@ import android.content.Intent;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.telecom.TelecomManager;
|
import android.telecom.TelecomManager;
|
||||||
import android.telephony.PhoneStateListener;
|
import android.telephony.PhoneStateListener;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
@@ -41,7 +47,7 @@ import java.util.List;
|
|||||||
|
|
||||||
final public class MainActivity extends AppCompatActivity implements IWinBoLLActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
|
final public class MainActivity extends AppCompatActivity implements IWinBoLLActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
|
||||||
|
|
||||||
public static final String TAG = "MainActivity";
|
public static final String TAG = "MainActivity";
|
||||||
|
|
||||||
public static final int REQUEST_HOME_ACTIVITY = 0;
|
public static final int REQUEST_HOME_ACTIVITY = 0;
|
||||||
public static final int REQUEST_ABOUT_ACTIVITY = 1;
|
public static final int REQUEST_ABOUT_ACTIVITY = 1;
|
||||||
@@ -55,13 +61,10 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
|
|||||||
MainServiceBean mMainServiceBean;
|
MainServiceBean mMainServiceBean;
|
||||||
private TabLayout tabLayout;
|
private TabLayout tabLayout;
|
||||||
private ViewPager viewPager;
|
private ViewPager viewPager;
|
||||||
private List<View> views; //用来存放放进ViewPager里面的布局
|
private List<View> views;
|
||||||
//实例化存储imageView(导航原点)的集合
|
|
||||||
ImageView[] imageViews;
|
ImageView[] imageViews;
|
||||||
//MyPagerAdapter adapter;//适配器
|
LinearLayout linearLayout;
|
||||||
MyPagerAdapter pagerAdapter;
|
int currentPoint = 0;
|
||||||
LinearLayout linearLayout;//下标所在在LinearLayout布局里
|
|
||||||
int currentPoint = 0;//当前被选中中页面的下标
|
|
||||||
|
|
||||||
private TelephonyManager telephonyManager;
|
private TelephonyManager telephonyManager;
|
||||||
private MyPhoneStateListener phoneStateListener;
|
private MyPhoneStateListener phoneStateListener;
|
||||||
@@ -70,30 +73,6 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
|
|||||||
|
|
||||||
private static final int DIALER_REQUEST_CODE = 1;
|
private static final int DIALER_REQUEST_CODE = 1;
|
||||||
|
|
||||||
// @Override
|
|
||||||
// public Activity getActivity() {
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Override
|
|
||||||
// public APPInfo getAppInfo() {
|
|
||||||
// String szBranchName = "contacts";
|
|
||||||
//
|
|
||||||
// APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo();
|
|
||||||
// appInfo.setAppName("Contacts");
|
|
||||||
// appInfo.setAppIcon(cc.winboll.studio.libapputils.R.drawable.ic_winboll);
|
|
||||||
// appInfo.setAppDescription("Contacts Description");
|
|
||||||
// appInfo.setAppGitName("APP");
|
|
||||||
// appInfo.setAppGitOwner("Studio");
|
|
||||||
// appInfo.setAppGitAPPBranch(szBranchName);
|
|
||||||
// appInfo.setAppGitAPPSubProjectFolder(szBranchName);
|
|
||||||
// appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts");
|
|
||||||
// appInfo.setAppAPKName("Contacts");
|
|
||||||
// appInfo.setAppAPKFolderName("Contacts");
|
|
||||||
// return appInfo;
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Activity getActivity() {
|
public Activity getActivity() {
|
||||||
@@ -107,89 +86,62 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
// 接收并处理 Intent 数据,函数 Intent 处理接收就直接返回
|
|
||||||
//if (prosessIntents(getIntent())) return;
|
|
||||||
// 以下正常创建主窗口
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
_MainActivity = this;
|
_MainActivity = this;
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
// 初始化工具栏
|
// 初始化工具栏(仅加载基础UI)
|
||||||
mToolbar = findViewById(R.id.activitymainToolbar1);
|
mToolbar = (Toolbar) findViewById(R.id.activitymainToolbar1);
|
||||||
setSupportActionBar(mToolbar);
|
setSupportActionBar(mToolbar);
|
||||||
// if (isEnableDisplayHomeAsUp()) {
|
|
||||||
// // 显示后退按钮
|
|
||||||
// getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
// }
|
|
||||||
getSupportActionBar().setSubtitle(TAG);
|
getSupportActionBar().setSubtitle(TAG);
|
||||||
|
|
||||||
tabLayout = findViewById(R.id.tabLayout);
|
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
|
||||||
viewPager = findViewById(R.id.viewPager);
|
viewPager = (ViewPager) findViewById(R.id.viewPager);
|
||||||
|
|
||||||
// 创建Fragment列表和标题列表
|
// 创建Fragment列表(仅实例化,不加载数据)
|
||||||
fragmentList = new ArrayList<>();
|
fragmentList = new ArrayList<Fragment>();
|
||||||
tabTitleList = new ArrayList<>();
|
tabTitleList = new ArrayList<String>();
|
||||||
fragmentList.add(CallLogFragment.newInstance(0));
|
fragmentList.add(CallLogFragment.newInstance(0));
|
||||||
fragmentList.add(ContactsFragment.newInstance(1));
|
fragmentList.add(ContactsFragment.newInstance(1)); // 延迟加载联系人数据
|
||||||
fragmentList.add(LogFragment.newInstance(2));
|
fragmentList.add(LogFragment.newInstance(2));
|
||||||
tabTitleList.add("通话记录");
|
tabTitleList.add("通话记录");
|
||||||
tabTitleList.add("联系人");
|
tabTitleList.add("联系人");
|
||||||
tabTitleList.add("应用日志");
|
tabTitleList.add("应用日志");
|
||||||
|
|
||||||
// 设置ViewPager的适配器
|
// 设置ViewPager适配器
|
||||||
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), fragmentList, tabTitleList);
|
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), fragmentList, tabTitleList);
|
||||||
viewPager.setAdapter(adapter);
|
viewPager.setAdapter(adapter);
|
||||||
|
|
||||||
|
// 关键:关闭预加载,仅当前页初始化
|
||||||
|
viewPager.setOffscreenPageLimit(0);
|
||||||
|
|
||||||
// 关联TabLayout和ViewPager
|
// 关联TabLayout和ViewPager
|
||||||
tabLayout.setupWithViewPager(viewPager);
|
tabLayout.setupWithViewPager(viewPager);
|
||||||
|
|
||||||
|
// 初始化服务状态(延迟启动非核心服务)
|
||||||
|
|
||||||
// initData();
|
|
||||||
// initView();
|
|
||||||
// //initPoint();//调用初始化导航原点的方法
|
|
||||||
// viewPager.addOnPageChangeListener(this);//滑动事件
|
|
||||||
|
|
||||||
//ViewPager viewPager = findViewById(R.id.activitymainViewPager1);
|
|
||||||
//MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
|
|
||||||
//viewPager.setAdapter(pagerAdapter);
|
|
||||||
//TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1);
|
|
||||||
//tabLayout.setupWithViewPager(viewPager);
|
|
||||||
|
|
||||||
// mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
|
||||||
// if (mMainServiceBean == null) {
|
|
||||||
// mMainServiceBean = new MainServiceBean();
|
|
||||||
// }
|
|
||||||
// cbMainService = findViewById(R.id.activitymainCheckBox1);
|
|
||||||
// cbMainService.setChecked(mMainServiceBean.isEnable());
|
|
||||||
// cbMainService.setOnClickListener(new View.OnClickListener(){
|
|
||||||
// @Override
|
|
||||||
// public void onClick(View view) {
|
|
||||||
// if (cbMainService.isChecked()) {
|
|
||||||
// MainService.startMainService(MainActivity.this);
|
|
||||||
// } else {
|
|
||||||
// MainService.stopMainService(MainActivity.this);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||||
if (mMainServiceBean == null) {
|
if (mMainServiceBean == null) {
|
||||||
mMainServiceBean = new MainServiceBean();
|
mMainServiceBean = new MainServiceBean();
|
||||||
MainServiceBean.saveBean(this, mMainServiceBean);
|
MainServiceBean.saveBean(this, mMainServiceBean);
|
||||||
}
|
}
|
||||||
if (mMainServiceBean.isEnable()) {
|
if (mMainServiceBean.isEnable()) {
|
||||||
MainService.startMainService(this);
|
// 延迟1秒启动服务,避免阻塞启动
|
||||||
|
new Handler().postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
MainService.startMainService(MainActivity.this);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化TelephonyManager和PhoneStateListener
|
// 初始化电话状态监听(基础功能保留)
|
||||||
telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
|
telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
|
||||||
phoneStateListener = new MyPhoneStateListener();
|
phoneStateListener = new MyPhoneStateListener();
|
||||||
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
|
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ViewPager的适配器
|
// ViewPager适配器(Java 7语法)
|
||||||
private class MyPagerAdapter extends FragmentPagerAdapter {
|
private class MyPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
|
||||||
private List<Fragment> fragmentList;
|
private List<Fragment> fragmentList;
|
||||||
@@ -226,91 +178,22 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
|
|||||||
_MainActivity.startActivity(intent);
|
_MainActivity.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
//初始化view,即显示的图片
|
// OnPageChangeListener接口实现
|
||||||
// void initView() {
|
|
||||||
// viewPager = findViewById(R.id.activitymainViewPager1);
|
|
||||||
// pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
|
|
||||||
// viewPager.setAdapter(pagerAdapter);
|
|
||||||
// //adapter = new MyPagerAdapter(views);
|
|
||||||
// //viewPager = findViewById(R.id.activitymainViewPager1);
|
|
||||||
// //viewPager.setAdapter(adapter);
|
|
||||||
// //linearLayout = findViewById(R.id.activitymainLinearLayout1);
|
|
||||||
// //initPoint();//初始化页面下方的点
|
|
||||||
// viewPager.setOnPageChangeListener(this);
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
//初始化所要显示的布局
|
|
||||||
// void initData() {
|
|
||||||
// LayoutInflater inflater = LayoutInflater.from(getActivity());
|
|
||||||
// View view1 = inflater.inflate(R.layout.fragment_call_log, viewPager, false);
|
|
||||||
// View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false);
|
|
||||||
// View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false);
|
|
||||||
//
|
|
||||||
// views = new ArrayList<>();
|
|
||||||
// views.add(view1);
|
|
||||||
// views.add(view2);
|
|
||||||
// views.add(view3);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void initPoint() {
|
|
||||||
// imageViews = new ImageView[5];//实例化5个图片
|
|
||||||
// for (int i = 0; i < linearLayout.getChildCount(); i++) {
|
|
||||||
// imageViews[i] = (ImageView) linearLayout.getChildAt(i);
|
|
||||||
// imageViews[i].setImageResource(R.drawable.ic_launcher);
|
|
||||||
// imageViews[i].setOnClickListener(this);//点击导航点,即可跳转
|
|
||||||
// imageViews[i].setTag(i);//重复利用实例化的对象
|
|
||||||
// }
|
|
||||||
// currentPoint = 0;//默认第一个坐标
|
|
||||||
// imageViews[currentPoint].setImageResource(R.drawable.ic_launcher);
|
|
||||||
// }
|
|
||||||
|
|
||||||
//OnPageChangeListener接口要实现的三个方法
|
|
||||||
/* onPageScrollStateChanged(int state)
|
|
||||||
此方法是在状态改变的时候调用,其中state这个参数有三种状态:
|
|
||||||
SCROLL_STATE_DRAGGING(1)表示用户手指“按在屏幕上并且开始拖动”的状态
|
|
||||||
(手指按下但是还没有拖动的时候还不是这个状态,只有按下并且手指开始拖动后log才打出。)
|
|
||||||
SCROLL_STATE_IDLE(0)滑动动画做完的状态。
|
|
||||||
SCROLL_STATE_SETTLING(2)在“手指离开屏幕”的状态。*/
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageScrollStateChanged(int state) {
|
public void onPageScrollStateChanged(int state) {}
|
||||||
|
|
||||||
}
|
|
||||||
/* onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
|
|
||||||
当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为:
|
|
||||||
|
|
||||||
position :当前页面,即你点击滑动的页面(从A滑B,则是A页面的position。
|
|
||||||
positionOffset:当前页面偏移的百分比
|
|
||||||
positionOffsetPixels:当前页面偏移的像素位置*/
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
|
||||||
|
|
||||||
}
|
|
||||||
/* onPageSelected(int position)
|
|
||||||
此方法是页面滑动完后得到调用,position是你当前选中的页面的Position(位置编号)
|
|
||||||
(从A滑动到B,就是B的position)*/
|
|
||||||
public void onPageSelected(int position) {
|
|
||||||
|
|
||||||
// ImageView preView = imageViews[currentPoint];
|
|
||||||
// preView.setImageResource(R.drawable.ic_launcher);
|
|
||||||
// ImageView currView = imageViews[position];
|
|
||||||
// currView.setImageResource(R.drawable.ic_launcher);
|
|
||||||
// currentPoint = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
//小圆点点击事件
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onPageSelected(int position) {}
|
||||||
// TODO Auto-generated method stub
|
|
||||||
//通过getTag(),可以判断是哪个控件
|
@Override
|
||||||
// int i = (Integer) v.getTag();
|
public void onClick(View v) {}
|
||||||
// viewPager.setCurrentItem(i);//直接跳转到某一个页面的情况
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostCreate(Bundle savedInstanceState) {
|
protected void onPostCreate(Bundle savedInstanceState) {
|
||||||
super.onPostCreate(savedInstanceState);
|
super.onPostCreate(savedInstanceState);
|
||||||
//setSubTitle("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MyPhoneStateListener extends PhoneStateListener {
|
private class MyPhoneStateListener extends PhoneStateListener {
|
||||||
@@ -336,109 +219,31 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
|
|||||||
LogUtils.d(TAG, "onDestroy() SOS");
|
LogUtils.d(TAG, "onDestroy() SOS");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// 处理传入的 Intent 数据
|
|
||||||
//
|
|
||||||
// boolean prosessIntents(Intent intent) {
|
|
||||||
// if (intent == null
|
|
||||||
// || intent.getAction() == null
|
|
||||||
// || intent.getAction().equals(""))
|
|
||||||
// return false;
|
|
||||||
//
|
|
||||||
// if (intent.getAction().equals(StringToQrCodeView.ACTION_UNITTEST_QRCODE)) {
|
|
||||||
// try {
|
|
||||||
// WinBoLLActivity clazzActivity = UnitTestActivity.class.newInstance();
|
|
||||||
// String tag = clazzActivity.getTag();
|
|
||||||
// LogUtils.d(TAG, "String tag = clazzActivity.getTag(); tag " + tag);
|
|
||||||
// Intent subIntent = new Intent(this, UnitTestActivity.class);
|
|
||||||
// subIntent.setAction(intent.getAction());
|
|
||||||
// File file = new File(getCacheDir(), UUID.randomUUID().toString());
|
|
||||||
// //取出文件uri
|
|
||||||
// Uri uri = intent.getData();
|
|
||||||
// if (uri == null) {
|
|
||||||
// uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
|
||||||
// }
|
|
||||||
// //获取文件真实地址
|
|
||||||
// String szSrcPath = UriUtils.getFileFromUri(getApplication(), uri);
|
|
||||||
// if (TextUtils.isEmpty(szSrcPath)) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Files.copy(Paths.get(szSrcPath), Paths.get(file.getPath()));
|
|
||||||
// //startWinBoLLActivity(subIntent, tag);
|
|
||||||
// WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, subIntent, UnitTestActivity.class);
|
|
||||||
// } catch (IllegalAccessException | InstantiationException | IOException e) {
|
|
||||||
// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
|
||||||
// // 函数处理异常返回失败
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// LogUtils.d(TAG, "prosessIntents|" + intent.getAction() + "|yet");
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Override
|
|
||||||
// public String getTag() {
|
|
||||||
// return TAG;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Override
|
|
||||||
// public void onBackPressed() {
|
|
||||||
// exit();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void exit() {
|
|
||||||
// YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onYes() {
|
|
||||||
// WinBoLLActivityManager.getInstance(getApplicationContext()).finishAll();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onNo() {
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener);
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
||||||
return super.onCreateOptionsMenu(menu);
|
return super.onCreateOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
if (item.getItemId() == R.id.item_settings) {
|
if (item.getItemId() == R.id.item_settings) {
|
||||||
Intent intent = new Intent(this, SettingsActivity.class);
|
Intent intent = new Intent(this, SettingsActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
//WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, CallActivity.class);
|
|
||||||
}
|
}
|
||||||
// } else
|
|
||||||
// if (item.getItemId() == R.id.item_exit) {
|
|
||||||
// exit();
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Android M 及以上检查是否是系统默认电话应用
|
* 检查是否是系统默认电话应用
|
||||||
*/
|
*/
|
||||||
public boolean isDefaultPhoneCallApp() {
|
public boolean isDefaultPhoneCallApp() {
|
||||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);
|
TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);
|
||||||
if (manger != null && manger.getDefaultDialerPackage() != null) {
|
if (manger != null && manger.getDefaultDialerPackage() != null) {
|
||||||
return manger.getDefaultDialerPackage().equals(getPackageName());
|
return manger.getDefaultDialerPackage().equals(getPackageName());
|
||||||
@@ -452,35 +257,22 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
|
|||||||
if (manager == null) return false;
|
if (manager == null) return false;
|
||||||
|
|
||||||
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
|
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
|
||||||
Integer.MAX_VALUE)) {
|
Integer.MAX_VALUE)) {
|
||||||
if (serviceClass.getName().equals(service.service.getClassName())) {
|
if (serviceClass.getName().equals(service.service.getClassName())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
// switch (resultCode) {
|
|
||||||
// case REQUEST_HOME_ACTIVITY : {
|
|
||||||
// LogUtils.d(TAG, "REQUEST_HOME_ACTIVITY");
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case REQUEST_ABOUT_ACTIVITY : {
|
|
||||||
// LogUtils.d(TAG, "REQUEST_ABOUT_ACTIVITY");
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// default : {
|
|
||||||
// super.onActivityResult(requestCode, resultCode, data);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
if (requestCode == DIALER_REQUEST_CODE) {
|
if (requestCode == DIALER_REQUEST_CODE) {
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用",
|
Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用",
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.activities;
|
package cc.winboll.studio.contacts.activities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/03/31 15:15:54
|
* @Date 2025/03/31 15:15:54
|
||||||
* @Describe 应用介绍窗口
|
* @Describe 应用介绍窗口
|
||||||
*/
|
*/
|
||||||
@@ -79,12 +79,12 @@ public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity
|
|||||||
APPInfo appInfo = new APPInfo();
|
APPInfo appInfo = new APPInfo();
|
||||||
appInfo.setAppName("Contacts");
|
appInfo.setAppName("Contacts");
|
||||||
appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll);
|
appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll);
|
||||||
appInfo.setAppDescription("通讯录与拨号");
|
appInfo.setAppDescription("这是可以根据正则表达式匹配拦截骚扰电话的手机拨号应用。");
|
||||||
appInfo.setAppGitName("APP");
|
appInfo.setAppGitName("APPBase");
|
||||||
appInfo.setAppGitOwner("Studio");
|
appInfo.setAppGitOwner("Studio");
|
||||||
appInfo.setAppGitAPPBranch(szBranchName);
|
appInfo.setAppGitAPPBranch(szBranchName);
|
||||||
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
|
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
|
||||||
appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts");
|
appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=4&extra=page%3D1");
|
||||||
appInfo.setAppAPKName("Contacts");
|
appInfo.setAppAPKName("Contacts");
|
||||||
appInfo.setAppAPKFolderName("Contacts");
|
appInfo.setAppAPKFolderName("Contacts");
|
||||||
return new AboutView(mContext, appInfo);
|
return new AboutView(mContext, appInfo);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.activities;
|
package cc.winboll.studio.contacts.activities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/20 17:15:46
|
* @Date 2025/02/20 17:15:46
|
||||||
* @Describe 拨号窗口
|
* @Describe 拨号窗口
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.activities;
|
package cc.winboll.studio.contacts.activities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/20 20:18:26
|
* @Date 2025/02/20 20:18:26
|
||||||
*/
|
*/
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.activities;
|
package cc.winboll.studio.contacts.activities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/21 05:37:42
|
* @Date 2025/02/21 05:37:42
|
||||||
*/
|
*/
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -198,6 +198,9 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
|
|||||||
settingsModel.setDunTotalCount(Integer.parseInt(etDunTotalCount.getText().toString()));
|
settingsModel.setDunTotalCount(Integer.parseInt(etDunTotalCount.getText().toString()));
|
||||||
settingsModel.setDunResumeSecondCount(Integer.parseInt(etDunResumeSecondCount.getText().toString()));
|
settingsModel.setDunResumeSecondCount(Integer.parseInt(etDunResumeSecondCount.getText().toString()));
|
||||||
settingsModel.setDunResumeCount(Integer.parseInt(etDunResumeCount.getText().toString()));
|
settingsModel.setDunResumeCount(Integer.parseInt(etDunResumeCount.getText().toString()));
|
||||||
|
|
||||||
|
// 应用效果提示
|
||||||
|
ToastUtils.show((settingsModel.getDunTotalCount() == 1)?"电话骚扰防御力几乎为0。":String.format("以下设置将在连拨%d次后接通电话。", settingsModel.getDunTotalCount()));
|
||||||
}
|
}
|
||||||
settingsModel.setIsEnableDun(isEnableDun);
|
settingsModel.setIsEnableDun(isEnableDun);
|
||||||
Rules.getInstance(this).saveDun();
|
Rules.getInstance(this).saveDun();
|
||||||
@@ -207,6 +210,7 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
|
|||||||
etDunTotalCount.setText(Integer.toString(settingsModel.getDunTotalCount()));
|
etDunTotalCount.setText(Integer.toString(settingsModel.getDunTotalCount()));
|
||||||
etDunResumeSecondCount.setText(Integer.toString(settingsModel.getDunResumeSecondCount()));
|
etDunResumeSecondCount.setText(Integer.toString(settingsModel.getDunResumeSecondCount()));
|
||||||
etDunResumeCount.setText(Integer.toString(settingsModel.getDunResumeCount()));
|
etDunResumeCount.setText(Integer.toString(settingsModel.getDunResumeCount()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateStreamVolumeTextView() {
|
void updateStreamVolumeTextView() {
|
||||||
@@ -243,6 +247,9 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
|
|||||||
Rules.getInstance(this).resetDefaultBoBullToonURL();
|
Rules.getInstance(this).resetDefaultBoBullToonURL();
|
||||||
EditText etBoBullToonURL = findViewById(R.id.bobulltoonurl_et);
|
EditText etBoBullToonURL = findViewById(R.id.bobulltoonurl_et);
|
||||||
etBoBullToonURL.setText(Rules.getInstance(this).getBoBullToonURL());
|
etBoBullToonURL.setText(Rules.getInstance(this).getBoBullToonURL());
|
||||||
|
|
||||||
|
final TomCat tomCat = TomCat.getInstance(this);
|
||||||
|
tomCat.cleanBoBullToon();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDownloadBoBullToon(View view) {
|
public void onDownloadBoBullToon(View view) {
|
||||||
@@ -330,4 +337,8 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
|
|||||||
public void onAbout(View view) {
|
public void onAbout(View view) {
|
||||||
App.getWinBoLLActivityManager().startWinBoLLActivity(this, AboutActivity.class);
|
App.getWinBoLLActivityManager().startWinBoLLActivity(this, AboutActivity.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onLogView(View view) {
|
||||||
|
App.getWinBoLLActivityManager().startLogActivity(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import cc.winboll.studio.libappbase.LogUtils;
|
|||||||
import cc.winboll.studio.libappbase.LogView;
|
import cc.winboll.studio.libappbase.LogView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/03/02 16:07:04
|
* @Date 2025/03/02 16:07:04
|
||||||
*/
|
*/
|
||||||
public class UnitTestActivity extends Activity {
|
public class UnitTestActivity extends Activity {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.activities;
|
package cc.winboll.studio.contacts.activities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/03/31 15:16:45
|
* @Date 2025/03/31 15:16:45
|
||||||
* @Describe 应用窗口基类
|
* @Describe 应用窗口基类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
package cc.winboll.studio.contacts.adapters;
|
package cc.winboll.studio.contacts.adapters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/26 13:09:32
|
* @Date 2025/02/26 13:09:32
|
||||||
* @Describe CallLogAdapter
|
* @Describe CallLogAdapter
|
||||||
*/
|
*/
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import cc.winboll.studio.contacts.R;
|
import cc.winboll.studio.contacts.R;
|
||||||
@@ -35,6 +40,10 @@ public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogV
|
|||||||
this.mContactUtils = ContactUtils.getInstance(mContext);
|
this.mContactUtils = ContactUtils.getInstance(mContext);
|
||||||
this.callLogList = callLogList;
|
this.callLogList = callLogList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void relaodContacts() {
|
||||||
|
this.mContactUtils.relaodContacts();
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
@@ -47,6 +56,38 @@ public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogV
|
|||||||
public void onBindViewHolder(@NonNull CallLogViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull CallLogViewHolder holder, int position) {
|
||||||
final CallLogModel callLog = callLogList.get(position);
|
final CallLogModel callLog = callLogList.get(position);
|
||||||
holder.phoneNumber.setText(callLog.getPhoneNumber() + "☎" + mContactUtils.getContactsName(callLog.getPhoneNumber()));
|
holder.phoneNumber.setText(callLog.getPhoneNumber() + "☎" + mContactUtils.getContactsName(callLog.getPhoneNumber()));
|
||||||
|
holder.phoneNumber.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View p1) {
|
||||||
|
// 弹出复制菜单
|
||||||
|
PopupMenu menu = new PopupMenu(mContext, holder.phoneNumber);
|
||||||
|
//加载菜单资源
|
||||||
|
menu.getMenuInflater().inflate(R.menu.toolbar_calllog_phonenumber, menu.getMenu());
|
||||||
|
//设置点击事件的响应
|
||||||
|
menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||||
|
int nItemId = menuItem.getItemId();
|
||||||
|
if (nItemId == R.id.item_calllog_phonenumber_copy) {
|
||||||
|
// Gets a handle to the clipboard service.
|
||||||
|
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
// Creates a new text clip to put on the clipboard
|
||||||
|
ClipData clip = ClipData.newPlainText("simple text", callLog.getPhoneNumber());
|
||||||
|
// Set the clipboard's primary clip.
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//一定要调用show()来显示弹出式菜单
|
||||||
|
menu.show();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
holder.callStatus.setText(callLog.getCallStatus());
|
holder.callStatus.setText(callLog.getCallStatus());
|
||||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
||||||
holder.callDate.setText(dateFormat.format(callLog.getCallDate()));
|
holder.callDate.setText(dateFormat.format(callLog.getCallDate()));
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
package cc.winboll.studio.contacts.adapters;
|
package cc.winboll.studio.contacts.adapters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/26 13:35:44
|
* @Date 2025/02/26 13:35:44
|
||||||
* @Describe ContactAdapter
|
* @Describe ContactAdapter
|
||||||
*/
|
*/
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import cc.winboll.studio.contacts.R;
|
import cc.winboll.studio.contacts.R;
|
||||||
import cc.winboll.studio.contacts.beans.ContactModel;
|
import cc.winboll.studio.contacts.beans.ContactModel;
|
||||||
|
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
|
||||||
import com.hjq.toast.ToastUtils;
|
import com.hjq.toast.ToastUtils;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
|
|
||||||
|
|
||||||
public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactViewHolder> {
|
public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactViewHolder> {
|
||||||
|
|
||||||
@@ -26,8 +32,10 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV
|
|||||||
private static final int REQUEST_CALL_PHONE = 1;
|
private static final int REQUEST_CALL_PHONE = 1;
|
||||||
|
|
||||||
private List<ContactModel> contactList;
|
private List<ContactModel> contactList;
|
||||||
|
Context mContext;
|
||||||
|
|
||||||
public ContactAdapter(List<ContactModel> contactList) {
|
public ContactAdapter(Context context, List<ContactModel> contactList) {
|
||||||
|
mContext = context;
|
||||||
this.contactList = contactList;
|
this.contactList = contactList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +49,37 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV
|
|||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull ContactViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull ContactViewHolder holder, int position) {
|
||||||
final ContactModel contact = contactList.get(position);
|
final ContactModel contact = contactList.get(position);
|
||||||
|
holder.llPhoneNumberMain.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View p1) {
|
||||||
|
// 弹出复制菜单
|
||||||
|
PopupMenu menu = new PopupMenu(mContext, holder.llPhoneNumberMain);
|
||||||
|
//加载菜单资源
|
||||||
|
menu.getMenuInflater().inflate(R.menu.toolbar_contact_phonenumber, menu.getMenu());
|
||||||
|
//设置点击事件的响应
|
||||||
|
menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||||
|
int nItemId = menuItem.getItemId();
|
||||||
|
if (nItemId == R.id.item_contact_phonenumber_copy) {
|
||||||
|
// Gets a handle to the clipboard service.
|
||||||
|
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
// Creates a new text clip to put on the clipboard
|
||||||
|
ClipData clip = ClipData.newPlainText("simple text", contact.getNumber());
|
||||||
|
// Set the clipboard's primary clip.
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//一定要调用show()来显示弹出式菜单
|
||||||
|
menu.show();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
holder.contactName.setText(contact.getName());
|
holder.contactName.setText(contact.getName());
|
||||||
holder.contactNumber.setText(contact.getNumber());
|
holder.contactNumber.setText(contact.getNumber());
|
||||||
|
|
||||||
@@ -69,12 +108,14 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class ContactViewHolder extends RecyclerView.ViewHolder {
|
public class ContactViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
LinearLayout llPhoneNumberMain;
|
||||||
TextView contactName;
|
TextView contactName;
|
||||||
TextView contactNumber;
|
TextView contactNumber;
|
||||||
AOHPCTCSeekBar dialAOHPCTCSeekBar;
|
AOHPCTCSeekBar dialAOHPCTCSeekBar;
|
||||||
|
|
||||||
public ContactViewHolder(@NonNull View itemView) {
|
public ContactViewHolder(@NonNull View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
|
llPhoneNumberMain = itemView.findViewById(R.id.itemcontactLinearLayout1);
|
||||||
contactName = itemView.findViewById(R.id.contact_name);
|
contactName = itemView.findViewById(R.id.contact_name);
|
||||||
contactNumber = itemView.findViewById(R.id.contact_number);
|
contactNumber = itemView.findViewById(R.id.contact_number);
|
||||||
dialAOHPCTCSeekBar = itemView.findViewById(R.id.aohpctcseekbar_dial);
|
dialAOHPCTCSeekBar = itemView.findViewById(R.id.aohpctcseekbar_dial);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.adapters;
|
package cc.winboll.studio.contacts.adapters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/20 14:20:38
|
* @Date 2025/02/20 14:20:38
|
||||||
* @Describe ImagePagerAdapter
|
* @Describe ImagePagerAdapter
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package cc.winboll.studio.contacts.adapters;
|
package cc.winboll.studio.contacts.adapters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/03/02 17:27:41
|
* @Date 2025/03/02 17:27:41
|
||||||
* @Describe PhoneConnectRuleAdapter
|
* @Describe PhoneConnectRuleAdapter
|
||||||
*/
|
*/
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@@ -20,6 +21,7 @@ import cc.winboll.studio.contacts.R;
|
|||||||
import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel;
|
import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel;
|
||||||
import cc.winboll.studio.contacts.dun.Rules;
|
import cc.winboll.studio.contacts.dun.Rules;
|
||||||
import cc.winboll.studio.contacts.views.LeftScrollView;
|
import cc.winboll.studio.contacts.views.LeftScrollView;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
|
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
|
||||||
import com.hjq.toast.ToastUtils;
|
import com.hjq.toast.ToastUtils;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -60,6 +62,10 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V
|
|||||||
final SimpleViewHolder simpleViewHolder = (SimpleViewHolder) holder;
|
final SimpleViewHolder simpleViewHolder = (SimpleViewHolder) holder;
|
||||||
String szView = model.getRuleText().trim().equals("") ?"[NULL]": model.getRuleText();
|
String szView = model.getRuleText().trim().equals("") ?"[NULL]": model.getRuleText();
|
||||||
simpleViewHolder.tvRuleText.setText(szView);
|
simpleViewHolder.tvRuleText.setText(szView);
|
||||||
|
simpleViewHolder.checkBoxAllow.setChecked(model.isAllowConnection());
|
||||||
|
simpleViewHolder.checkBoxAllow.setEnabled(false);
|
||||||
|
simpleViewHolder.checkBoxEnable.setChecked(model.isEnable());
|
||||||
|
simpleViewHolder.checkBoxEnable.setEnabled(false);
|
||||||
simpleViewHolder.scrollView.setOnActionListener(new LeftScrollView.OnActionListener(){
|
simpleViewHolder.scrollView.setOnActionListener(new LeftScrollView.OnActionListener(){
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -215,16 +221,22 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V
|
|||||||
|
|
||||||
private final LeftScrollView scrollView;
|
private final LeftScrollView scrollView;
|
||||||
private final TextView tvRuleText;
|
private final TextView tvRuleText;
|
||||||
|
CheckBox checkBoxAllow;
|
||||||
|
CheckBox checkBoxEnable;
|
||||||
|
|
||||||
|
|
||||||
public SimpleViewHolder(@NonNull ViewGroup parent, @NonNull View itemView) {
|
public SimpleViewHolder(@NonNull ViewGroup parent, @NonNull View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
scrollView = itemView.findViewById(R.id.scrollView);
|
scrollView = itemView.findViewById(R.id.scrollView);
|
||||||
//tvRuleText = itemView.findViewById(R.id.ruletext_tv);
|
LayoutInflater inflater = LayoutInflater.from(itemView.getContext());
|
||||||
tvRuleText = new TextView(itemView.getContext());
|
View viewContent = inflater.inflate(R.layout.view_phone_connect_rule_simple_content, parent, false);
|
||||||
|
tvRuleText = viewContent.findViewById(R.id.ruletext_tv);
|
||||||
|
checkBoxAllow = viewContent.findViewById(R.id.checkbox_allow);
|
||||||
|
checkBoxEnable = viewContent.findViewById(R.id.checkbox_enable);
|
||||||
|
//tvRuleText = new TextView(itemView.getContext());
|
||||||
scrollView.setContentWidth(parent.getWidth());
|
scrollView.setContentWidth(parent.getWidth());
|
||||||
//scrollView.setContentWidth(600);
|
//scrollView.setContentWidth(600);
|
||||||
scrollView.addContentLayout(tvRuleText);
|
scrollView.addContentLayout(viewContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -243,5 +255,9 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V
|
|||||||
buttonConfirm = itemView.findViewById(R.id.button_confirm);
|
buttonConfirm = itemView.findViewById(R.id.button_confirm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setCheckBoxTouchListener(CheckBox checkBox) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.beans;
|
package cc.winboll.studio.contacts.beans;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/26 13:10:57
|
* @Date 2025/02/26 13:10:57
|
||||||
* @Describe CallLogModel
|
* @Describe CallLogModel
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package cc.winboll.studio.contacts.beans;
|
package cc.winboll.studio.contacts.beans;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
* @Date 2025/02/26 13:37:00
|
* @Date 2025/08/30 14:32
|
||||||
* @Describe ContactModel
|
* @Describe 联系人信息数据模型
|
||||||
*/
|
*/
|
||||||
import net.sourceforge.pinyin4j.PinyinHelper;
|
import net.sourceforge.pinyin4j.PinyinHelper;
|
||||||
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
|
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
|
||||||
@@ -18,13 +18,18 @@ public class ContactModel {
|
|||||||
private String name;
|
private String name;
|
||||||
private String number;
|
private String number;
|
||||||
private String pinyin;
|
private String pinyin;
|
||||||
|
// 新增:存储姓名的拼音首字母(如"啊牛"→"an")
|
||||||
|
private String pinyinFirstLetter;
|
||||||
|
|
||||||
public ContactModel(String name, String number) {
|
public ContactModel(String name, String number) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.number = number.replaceAll("\\s", "");
|
this.number = number.replaceAll("\\s", "");
|
||||||
this.pinyin = convertToPinyin(name);
|
this.pinyin = convertToPinyin(name);
|
||||||
|
// 初始化时生成拼音首字母
|
||||||
|
this.pinyinFirstLetter = convertToPinyinFirstLetter(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 原方法:转换为全拼(如"啊牛"→"aniu")
|
||||||
private String convertToPinyin(String chinese) {
|
private String convertToPinyin(String chinese) {
|
||||||
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
|
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
|
||||||
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
|
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
|
||||||
@@ -33,22 +38,55 @@ public class ContactModel {
|
|||||||
StringBuilder pinyin = new StringBuilder();
|
StringBuilder pinyin = new StringBuilder();
|
||||||
for (int i = 0; i < chinese.length(); i++) {
|
for (int i = 0; i < chinese.length(); i++) {
|
||||||
char ch = chinese.charAt(i);
|
char ch = chinese.charAt(i);
|
||||||
if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) {
|
if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) { // 仅处理汉字
|
||||||
try {
|
try {
|
||||||
String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format);
|
String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format);
|
||||||
if (pinyinArray != null) {
|
if (pinyinArray != null && pinyinArray.length > 0) {
|
||||||
pinyin.append(pinyinArray[0]);
|
pinyin.append(pinyinArray[0]); // 取第一个拼音(多音字默认首选项)
|
||||||
}
|
}
|
||||||
} catch (BadHanyuPinyinOutputFormatCombination e) {
|
} catch (BadHanyuPinyinOutputFormatCombination e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pinyin.append(ch);
|
pinyin.append(ch); // 非汉字直接拼接(如字母、数字、符号)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pinyin.toString();
|
return pinyin.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增:转换为拼音首字母(如"啊牛"→"an")
|
||||||
|
private String convertToPinyinFirstLetter(String chinese) {
|
||||||
|
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
|
||||||
|
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
|
||||||
|
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
|
||||||
|
|
||||||
|
StringBuilder firstLetters = new StringBuilder();
|
||||||
|
for (int i = 0; i < chinese.length(); i++) {
|
||||||
|
char ch = chinese.charAt(i);
|
||||||
|
if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) { // 仅处理汉字
|
||||||
|
try {
|
||||||
|
String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format);
|
||||||
|
if (pinyinArray != null && pinyinArray.length > 0) {
|
||||||
|
// 取拼音的第一个字母(如"a"、"niu"→"a"、"n")
|
||||||
|
firstLetters.append(pinyinArray[0].charAt(0));
|
||||||
|
}
|
||||||
|
} catch (BadHanyuPinyinOutputFormatCombination e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 非汉字可根据需求处理:此处保留原字符(如"李3"→"l3","张A"→"za")
|
||||||
|
firstLetters.append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstLetters.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:获取拼音首字母
|
||||||
|
public String getPinyinFirstLetter() {
|
||||||
|
return pinyinFirstLetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原有getter方法
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.beans;
|
package cc.winboll.studio.contacts.beans;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/13 07:06:13
|
* @Date 2025/02/13 07:06:13
|
||||||
*/
|
*/
|
||||||
import android.util.JsonReader;
|
import android.util.JsonReader;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.beans;
|
package cc.winboll.studio.contacts.beans;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/21 09:52:10
|
* @Date 2025/02/21 09:52:10
|
||||||
* @Describe 电话黑名单规则
|
* @Describe 电话黑名单规则
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.beans;
|
package cc.winboll.studio.contacts.beans;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/24 18:47:11
|
* @Date 2025/02/24 18:47:11
|
||||||
* @Describe 手机铃声设置参数类
|
* @Describe 手机铃声设置参数类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.beans;
|
package cc.winboll.studio.contacts.beans;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/03/02 19:51:40
|
* @Date 2025/03/02 19:51:40
|
||||||
* @Describe SettingsModel
|
* @Describe SettingsModel
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.bobulltoon;
|
package cc.winboll.studio.contacts.bobulltoon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/03/02 13:47:48
|
* @Date 2025/03/02 13:47:48
|
||||||
* @Describe 汤姆猫管家 :使用 BoBullToon 项目,对通讯地址进行筛选判断的好朋友。
|
* @Describe 汤姆猫管家 :使用 BoBullToon 项目,对通讯地址进行筛选判断的好朋友。
|
||||||
*/
|
*/
|
||||||
@@ -11,6 +11,7 @@ import cc.winboll.studio.contacts.dun.Rules;
|
|||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
import com.hjq.toast.ToastUtils;
|
import com.hjq.toast.ToastUtils;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -44,7 +45,7 @@ public class TomCat {
|
|||||||
}
|
}
|
||||||
return _TomCat;
|
return _TomCat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDefaultBobulltoonUrl() {
|
public String getDefaultBobulltoonUrl() {
|
||||||
return mContext.getString(R.string.default_bobulltoon_url);
|
return mContext.getString(R.string.default_bobulltoon_url);
|
||||||
}
|
}
|
||||||
@@ -123,7 +124,7 @@ public class TomCat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新新文件
|
// 更新新文件
|
||||||
if(downloadAndExtractZip(zipUrl, destinationFolder)) {
|
if (downloadAndExtractZip(zipUrl, destinationFolder)) {
|
||||||
LogUtils.d(TAG, "ZIP 文件下载并解压成功。");
|
LogUtils.d(TAG, "ZIP 文件下载并解压成功。");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -154,12 +155,81 @@ public class TomCat {
|
|||||||
File getWorkingFolder() {
|
File getWorkingFolder() {
|
||||||
return mContext.getExternalFilesDir(TAG);
|
return mContext.getExternalFilesDir(TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public File getBoBullToonDataFolder() {
|
||||||
|
File fCheckRoot = getWorkingFolder();
|
||||||
|
if (fCheckRoot == null || !fCheckRoot.exists()) {
|
||||||
|
return fCheckRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归查找符合条件的文件夹
|
||||||
|
File targetFolder = findTargetFolder(fCheckRoot);
|
||||||
|
return targetFolder != null ? targetFolder : fCheckRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归查找同时包含LICENSE和README.md文件的文件夹
|
||||||
|
*/
|
||||||
|
private File findTargetFolder(File currentFolder) {
|
||||||
|
// 检查当前文件夹是否符合条件
|
||||||
|
if (hasRequiredFiles(currentFolder)) {
|
||||||
|
return currentFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找子文件夹(Java 7不支持方法引用,用匿名内部类过滤)
|
||||||
|
File[] subFolders = currentFolder.listFiles(new FileFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File file) {
|
||||||
|
return file.isDirectory(); // 仅保留子文件夹
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (subFolders != null) {
|
||||||
|
for (File subFolder : subFolders) {
|
||||||
|
File result = findTargetFolder(subFolder);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件夹中是否同时存在LICENSE和README.md文件
|
||||||
|
*/
|
||||||
|
private boolean hasRequiredFiles(File folder) {
|
||||||
|
if (folder == null || !folder.isDirectory()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查两个文件是否同时存在且均为文件(非文件夹)
|
||||||
|
File licenseFile = new File(folder, "LICENSE");
|
||||||
|
File readmeFile = new File(folder, "README.md");
|
||||||
|
|
||||||
|
return licenseFile.exists() && licenseFile.isFile()
|
||||||
|
&& readmeFile.exists() && readmeFile.isFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanBoBullToon() {
|
||||||
|
String destinationFolder = getWorkingFolder().getPath(); // 替换为实际的目标文件夹路径
|
||||||
|
// 删除旧文件
|
||||||
|
File fOldFolder = new File(destinationFolder);
|
||||||
|
if (fOldFolder.exists()) {
|
||||||
|
deleteFolderRecursive(fOldFolder);
|
||||||
|
fOldFolder.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
ToastUtils.show("已清空 BoBullToon 数据!");
|
||||||
|
LogUtils.d(TAG, "已清空 BoBullToon 数据");
|
||||||
|
}
|
||||||
|
|
||||||
public boolean loadPhoneBoBullToon() {
|
public boolean loadPhoneBoBullToon() {
|
||||||
listPhoneBoBullToon.clear();
|
listPhoneBoBullToon.clear();
|
||||||
File fBoBullToon = new File(getWorkingFolder(), "bobulltoon");
|
File fBoBullToon = getBoBullToonDataFolder();
|
||||||
if (fBoBullToon.exists()) {
|
if (fBoBullToon.exists()) {
|
||||||
LogUtils.d(TAG, String.format("getWorkingFolder() %s", getWorkingFolder()));
|
LogUtils.d(TAG, String.format("getBoBullToonDataFolder() %s", getWorkingFolder()));
|
||||||
for (File userFolder : fBoBullToon.listFiles()) {
|
for (File userFolder : fBoBullToon.listFiles()) {
|
||||||
if (userFolder.isDirectory()) {
|
if (userFolder.isDirectory()) {
|
||||||
for (File recordFile : userFolder.listFiles()) {
|
for (File recordFile : userFolder.listFiles()) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.dun;
|
package cc.winboll.studio.contacts.dun;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/21 06:15:10
|
* @Date 2025/02/21 06:15:10
|
||||||
* @Describe 云盾防御规则
|
* @Describe 云盾防御规则
|
||||||
*/
|
*/
|
||||||
@@ -145,6 +145,14 @@ public class Rules {
|
|||||||
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
|
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检验拨不通号码群
|
||||||
|
if (!isDefend && MainService.isPhoneInBoBullToon(phoneNumber)) {
|
||||||
|
LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber));
|
||||||
|
isDefend = true;
|
||||||
|
isConnect = false;
|
||||||
|
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
|
||||||
|
}
|
||||||
|
|
||||||
// 查询通讯录是否有该联系人
|
// 查询通讯录是否有该联系人
|
||||||
boolean isPhoneInContacts = ContactUtils.getInstance(mContext).isPhoneInContacts(mContext, phoneNumber);
|
boolean isPhoneInContacts = ContactUtils.getInstance(mContext).isPhoneInContacts(mContext, phoneNumber);
|
||||||
if (!isDefend) {
|
if (!isDefend) {
|
||||||
@@ -158,14 +166,6 @@ public class Rules {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检验拨不通号码群
|
|
||||||
if (!isDefend && MainService.isPhoneInBoBullToon(phoneNumber)) {
|
|
||||||
LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber));
|
|
||||||
isDefend = true;
|
|
||||||
isConnect = false;
|
|
||||||
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 正则匹配规则名单校验
|
// 正则匹配规则名单校验
|
||||||
if (!isDefend) {
|
if (!isDefend) {
|
||||||
for (int i = 0; i < _PhoneConnectRuleModelList.size(); i++) {
|
for (int i = 0; i < _PhoneConnectRuleModelList.size(); i++) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.fragments;
|
package cc.winboll.studio.contacts.fragments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/20 12:57:00
|
* @Date 2025/02/20 12:57:00
|
||||||
* @Describe 拨号
|
* @Describe 拨号
|
||||||
*/
|
*/
|
||||||
@@ -161,4 +161,12 @@ public class CallLogFragment extends Fragment {
|
|||||||
_CallLogFragment.triggerUpdate();
|
_CallLogFragment.triggerUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
//ToastUtils.show("onResume");
|
||||||
|
callLogAdapter.relaodContacts();
|
||||||
|
readCallLog(); // 窗口回显时更新通话记录
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
package cc.winboll.studio.contacts.fragments;
|
package cc.winboll.studio.contacts.fragments;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
* @Date 2025/02/20 12:57:50
|
* @Date 2025/08/30 14:32
|
||||||
* @Describe 联系人
|
* @Describe 联系人视图
|
||||||
*/
|
*/
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
@@ -27,24 +30,39 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import cc.winboll.studio.contacts.R;
|
import cc.winboll.studio.contacts.R;
|
||||||
import cc.winboll.studio.contacts.adapters.ContactAdapter;
|
import cc.winboll.studio.contacts.adapters.ContactAdapter;
|
||||||
import cc.winboll.studio.contacts.beans.ContactModel;
|
import cc.winboll.studio.contacts.beans.ContactModel;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
import com.hjq.toast.ToastUtils;
|
import com.hjq.toast.ToastUtils;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class ContactsFragment extends Fragment {
|
public class ContactsFragment extends Fragment {
|
||||||
|
|
||||||
public static final String TAG = "ContactsFragment";
|
public static final String TAG = "ContactsFragment";
|
||||||
|
|
||||||
private static final String ARG_PAGE = "ARG_PAGE";
|
private static final String ARG_PAGE = "ARG_PAGE";
|
||||||
private int mPage;
|
|
||||||
|
|
||||||
private static final int REQUEST_READ_CONTACTS = 1;
|
private static final int REQUEST_READ_CONTACTS = 1;
|
||||||
|
|
||||||
|
private int mPage;
|
||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
private ContactAdapter contactAdapter;
|
private ContactAdapter contactAdapter;
|
||||||
private List<ContactModel> contactList = new ArrayList<>();
|
|
||||||
private List<ContactModel> originalContactList = new ArrayList<>();
|
|
||||||
private EditText searchEditText;
|
private EditText searchEditText;
|
||||||
|
private Button btnDial;
|
||||||
|
private boolean isViewInitialized = false; // 标记视图是否已初始化
|
||||||
|
|
||||||
|
// 静态缓存:全局复用联系人数据
|
||||||
|
private static List<ContactModel> sCachedOriginalList = new ArrayList<ContactModel>();
|
||||||
|
private static List<ContactModel> sCachedFilteredList = new ArrayList<ContactModel>();
|
||||||
|
|
||||||
|
// 当前页面数据容器
|
||||||
|
private List<ContactModel> contactList = new ArrayList<ContactModel>();
|
||||||
|
private List<ContactModel> originalContactList = new ArrayList<ContactModel>();
|
||||||
|
|
||||||
|
// 异步工具
|
||||||
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
private boolean isDataLoaded = false;
|
||||||
|
|
||||||
|
|
||||||
public static ContactsFragment newInstance(int page) {
|
public static ContactsFragment newInstance(int page) {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
@@ -65,103 +83,272 @@ public class ContactsFragment extends Fragment {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
return inflater.inflate(R.layout.fragment_contacts, container, false);
|
// 加载布局(已移除进度条相关代码)
|
||||||
|
View view = inflater.inflate(R.layout.fragment_contacts, container, false);
|
||||||
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
recyclerView = view.findViewById(R.id.contacts_recycler_view);
|
// 初始化RecyclerView
|
||||||
|
recyclerView = (RecyclerView) view.findViewById(R.id.contacts_recycler_view);
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
contactAdapter = new ContactAdapter(contactList);
|
contactList = new ArrayList<ContactModel>();
|
||||||
|
contactAdapter = new ContactAdapter(getContext(), contactList);
|
||||||
recyclerView.setAdapter(contactAdapter);
|
recyclerView.setAdapter(contactAdapter);
|
||||||
|
// 初始隐藏列表,数据加载后显示
|
||||||
|
recyclerView.setVisibility(View.GONE);
|
||||||
|
|
||||||
searchEditText = view.findViewById(R.id.search_edit_text);
|
// 绑定搜索框和拨号按钮
|
||||||
searchEditText.addTextChangedListener(new TextWatcher() {
|
searchEditText = (EditText) view.findViewById(R.id.search_edit_text);
|
||||||
@Override
|
btnDial = (Button) view.findViewById(R.id.btn_dial);
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
// 初始隐藏搜索相关控件,延迟到首次可见时显示
|
||||||
}
|
searchEditText.setVisibility(View.GONE);
|
||||||
|
btnDial.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
// 首次可见时初始化资源
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
@Override
|
||||||
filterContacts(s.toString());
|
public void onResume() {
|
||||||
}
|
super.onResume();
|
||||||
|
if (!isViewInitialized) {
|
||||||
|
initSearchAndDial(); // 初始化搜索和拨号功能
|
||||||
|
checkContactPermission(); // 检查权限并加载数据
|
||||||
|
isViewInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
// 初始化搜索框和拨号按钮
|
||||||
public void afterTextChanged(Editable s) {
|
private void initSearchAndDial() {
|
||||||
}
|
// 显示搜索相关控件
|
||||||
});
|
searchEditText.setVisibility(View.VISIBLE);
|
||||||
|
btnDial.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
// 搜索框防抖监听
|
||||||
|
searchEditText.addTextChangedListener(new DebounceTextWatcher(300) {
|
||||||
|
@Override
|
||||||
|
public void onDebounceTextChanged(String query) {
|
||||||
|
filterContacts(query);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 拨号按钮点击事件
|
||||||
|
btnDial.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
String phoneNumber = searchEditText.getText().toString().replaceAll("\\s", "");
|
||||||
|
if (phoneNumber.isEmpty()) {
|
||||||
|
ToastUtils.show("请输入号码");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Intent intent = new Intent(Intent.ACTION_CALL);
|
||||||
|
intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 权限检查
|
||||||
|
private void checkContactPermission() {
|
||||||
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
|
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_READ_CONTACTS);
|
ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_READ_CONTACTS);
|
||||||
} else {
|
} else {
|
||||||
readContacts();
|
loadContacts();
|
||||||
}
|
}
|
||||||
|
|
||||||
Button btnDial = view.findViewById(R.id.btn_dial);
|
|
||||||
btnDial.setOnClickListener(new View.OnClickListener(){
|
|
||||||
@Override
|
|
||||||
public void onClick(View p1) {
|
|
||||||
|
|
||||||
String phoneNumber = searchEditText.getText().toString().replaceAll("\\s", "");
|
|
||||||
//phoneNumber = "+8616769764848";
|
|
||||||
ToastUtils.show(phoneNumber);
|
|
||||||
Intent intent = new Intent(Intent.ACTION_CALL);
|
|
||||||
intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
|
|
||||||
// 添加 FLAG_ACTIVITY_NEW_TASK 标志
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载联系人(延迟到首次可见时)
|
||||||
|
private void loadContacts() {
|
||||||
|
// 若有缓存,直接复用
|
||||||
|
if (!sCachedOriginalList.isEmpty() && !sCachedFilteredList.isEmpty()) {
|
||||||
|
originalContactList.clear();
|
||||||
|
originalContactList.addAll(sCachedOriginalList);
|
||||||
|
contactList.clear();
|
||||||
|
contactList.addAll(sCachedFilteredList);
|
||||||
|
contactAdapter.notifyDataSetChanged();
|
||||||
|
recyclerView.setVisibility(View.VISIBLE); // 显示列表
|
||||||
|
isDataLoaded = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无缓存时异步加载
|
||||||
|
if (!isDataLoaded) {
|
||||||
|
recyclerView.setVisibility(View.GONE); // 加载中隐藏列表
|
||||||
|
|
||||||
|
executor.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// 子线程读取联系人
|
||||||
|
final List<ContactModel> tempList = readContactsInBackground();
|
||||||
|
|
||||||
|
// 主线程更新UI
|
||||||
|
mainHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// 更新缓存
|
||||||
|
sCachedOriginalList.clear();
|
||||||
|
sCachedOriginalList.addAll(tempList);
|
||||||
|
sCachedFilteredList.clear();
|
||||||
|
sCachedFilteredList.addAll(tempList);
|
||||||
|
|
||||||
|
// 更新当前列表
|
||||||
|
originalContactList.clear();
|
||||||
|
originalContactList.addAll(sCachedOriginalList);
|
||||||
|
contactList.clear();
|
||||||
|
contactList.addAll(sCachedFilteredList);
|
||||||
|
contactAdapter.notifyDataSetChanged();
|
||||||
|
LogUtils.d(TAG, String.format("联系人加载完成,共%d条数据", contactList.size()));
|
||||||
|
|
||||||
|
// 数据加载后显示列表
|
||||||
|
recyclerView.setVisibility(View.VISIBLE);
|
||||||
|
isDataLoaded = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子线程读取联系人
|
||||||
|
private List<ContactModel> readContactsInBackground() {
|
||||||
|
List<ContactModel> tempList = new ArrayList<ContactModel>();
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
// 查询联系人姓名和号码
|
||||||
|
cursor = requireContext().getContentResolver().query(
|
||||||
|
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
|
||||||
|
new String[]{
|
||||||
|
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
|
||||||
|
ContactsContract.CommonDataKinds.Phone.NUMBER
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
int nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
|
||||||
|
int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
|
||||||
|
|
||||||
|
do {
|
||||||
|
String name = cursor.getString(nameIndex);
|
||||||
|
String number = cursor.getString(numberIndex).replaceAll("\\s", ""); // 去除空格
|
||||||
|
tempList.add(new ContactModel(name, number));
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.d(TAG, "读取联系人失败:" + e);
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close(); // 关闭游标,避免内存泄漏
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tempList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤联系人
|
||||||
|
private void filterContacts(String query) {
|
||||||
|
contactList.clear();
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
contactList.addAll(originalContactList);
|
||||||
|
sCachedFilteredList.clear();
|
||||||
|
sCachedFilteredList.addAll(originalContactList);
|
||||||
|
} else {
|
||||||
|
String lowerQuery = query.toLowerCase();
|
||||||
|
for (ContactModel contact : originalContactList) {
|
||||||
|
// 匹配姓名、全拼、简拼、号码
|
||||||
|
boolean matchName = contact.getName().toLowerCase().contains(lowerQuery);
|
||||||
|
boolean matchPinyin = contact.getPinyin().toLowerCase().contains(lowerQuery);
|
||||||
|
boolean matchFirstLetter = contact.getPinyinFirstLetter().toLowerCase().contains(lowerQuery);
|
||||||
|
boolean matchNumber = contact.getNumber().contains(lowerQuery);
|
||||||
|
|
||||||
|
if (matchName || matchPinyin || matchFirstLetter || matchNumber) {
|
||||||
|
contactList.add(contact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sCachedFilteredList.clear();
|
||||||
|
sCachedFilteredList.addAll(contactList);
|
||||||
|
}
|
||||||
|
contactAdapter.notifyDataSetChanged();
|
||||||
|
// 过滤后确保列表可见
|
||||||
|
recyclerView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 权限回调
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
if (requestCode == REQUEST_READ_CONTACTS) {
|
if (requestCode == REQUEST_READ_CONTACTS) {
|
||||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
readContacts();
|
loadContacts(); // 授权后加载联系人
|
||||||
|
} else {
|
||||||
|
ToastUtils.show("请授予联系人权限以查看联系人列表");
|
||||||
|
recyclerView.setVisibility(View.VISIBLE); // 显示空列表
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readContacts() {
|
// 防抖TextWatcher(Java 7实现)
|
||||||
contactList.clear();
|
public abstract static class DebounceTextWatcher implements TextWatcher {
|
||||||
originalContactList.clear();
|
private final long debounceDelay;
|
||||||
Cursor cursor = requireContext().getContentResolver().query(
|
private Handler handler = new Handler(Looper.getMainLooper());
|
||||||
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
|
private Runnable pendingRunnable;
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC");
|
|
||||||
|
|
||||||
if (cursor != null) {
|
public DebounceTextWatcher(long debounceDelay) {
|
||||||
while (cursor.moveToNext()) {
|
this.debounceDelay = debounceDelay;
|
||||||
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
|
|
||||||
String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
|
|
||||||
ContactModel contact = new ContactModel(name, number);
|
|
||||||
contactList.add(contact);
|
|
||||||
originalContactList.add(contact);
|
|
||||||
}
|
|
||||||
cursor.close();
|
|
||||||
contactAdapter.notifyDataSetChanged();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void filterContacts(String query) {
|
@Override
|
||||||
contactList.clear();
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
if (query.isEmpty()) {
|
// 无需处理
|
||||||
contactList.addAll(originalContactList);
|
}
|
||||||
} else {
|
|
||||||
for (ContactModel contact : originalContactList) {
|
@Override
|
||||||
if (contact.getName().toLowerCase().contains(query.toLowerCase()) ||
|
public void onTextChanged(final CharSequence s, int start, int before, int count) {
|
||||||
contact.getPinyin().toLowerCase().contains(query.toLowerCase()) ||
|
// 移除之前的延迟任务
|
||||||
contact.getNumber().toLowerCase().contains(query.toLowerCase())) {
|
if (pendingRunnable != null) {
|
||||||
contactList.add(contact);
|
handler.removeCallbacks(pendingRunnable);
|
||||||
|
}
|
||||||
|
// 延迟执行过滤
|
||||||
|
pendingRunnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
onDebounceTextChanged(s.toString());
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
handler.postDelayed(pendingRunnable, debounceDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
// 无需处理
|
||||||
|
}
|
||||||
|
|
||||||
|
// 抽象方法:防抖后的回调
|
||||||
|
public abstract void onDebounceTextChanged(String query);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 资源释放
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
executor.shutdown(); // 关闭线程池
|
||||||
|
mainHandler.removeCallbacksAndMessages(null); // 清除未执行任务
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fragment隐藏/显示时的处理
|
||||||
|
@Override
|
||||||
|
public void onHiddenChanged(boolean hidden) {
|
||||||
|
super.onHiddenChanged(hidden);
|
||||||
|
if (!hidden && isDataLoaded) {
|
||||||
|
// 复用缓存数据并显示列表
|
||||||
|
contactList.clear();
|
||||||
|
contactList.addAll(sCachedFilteredList);
|
||||||
|
contactAdapter.notifyDataSetChanged();
|
||||||
|
recyclerView.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
contactAdapter.notifyDataSetChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.fragments;
|
package cc.winboll.studio.contacts.fragments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/20 12:58:15
|
* @Date 2025/02/20 12:58:15
|
||||||
* @Describe 应用日志
|
* @Describe 应用日志
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.handlers;
|
package cc.winboll.studio.contacts.handlers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/14 03:51:40
|
* @Date 2025/02/14 03:51:40
|
||||||
*/
|
*/
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.receivers;
|
package cc.winboll.studio.contacts.receivers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/13 06:58:04
|
* @Date 2025/02/13 06:58:04
|
||||||
* @Describe 主要广播接收器
|
* @Describe 主要广播接收器
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.services;
|
package cc.winboll.studio.contacts.services;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/14 03:38:31
|
* @Date 2025/02/14 03:38:31
|
||||||
* @Describe 守护进程服务
|
* @Describe 守护进程服务
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.services;
|
package cc.winboll.studio.contacts.services;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/13 06:56:41
|
* @Date 2025/02/13 06:56:41
|
||||||
* @Describe 拨号主服务
|
* @Describe 拨号主服务
|
||||||
* 参考:
|
* 参考:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.threads;
|
package cc.winboll.studio.contacts.threads;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/14 03:46:44
|
* @Date 2025/02/14 03:46:44
|
||||||
*/
|
*/
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package cc.winboll.studio.contacts.utils;
|
package cc.winboll.studio.contacts.utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
* @Date 2025/03/06 21:08:16
|
* @Date 2025/08/30 14:32
|
||||||
* @Describe ContactUtils
|
* @Describe 联系人工具集
|
||||||
*/
|
*/
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import android.widget.EditText;
|
|||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/04/13 00:59:13
|
* @Date 2025/04/13 00:59:13
|
||||||
* @Describe Int类型数字输入框工具集
|
* @Describe Int类型数字输入框工具集
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.utils;
|
package cc.winboll.studio.contacts.utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/04/13 01:16:28
|
* @Date 2025/04/13 01:16:28
|
||||||
* @Describe Int数字操作工具集
|
* @Describe Int数字操作工具集
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.utils;
|
package cc.winboll.studio.contacts.utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/02/26 15:21:48
|
* @Date 2025/02/26 15:21:48
|
||||||
* @Describe PhoneUtils
|
* @Describe PhoneUtils
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.utils;
|
package cc.winboll.studio.contacts.utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2024/12/09 19:00:21
|
* @Date 2024/12/09 19:00:21
|
||||||
* @Describe .* 前置预防针
|
* @Describe .* 前置预防针
|
||||||
regex pointer preventive injection
|
regex pointer preventive injection
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.winboll.studio.contacts.views;
|
package cc.winboll.studio.contacts.views;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen<zhangsken@188.com>
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2025/03/02 21:11:03
|
* @Date 2025/03/02 21:11:03
|
||||||
* @Describe 云盾防御信息
|
* @Describe 云盾防御信息
|
||||||
*/
|
*/
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user