Compare commits

...

314 Commits

Author SHA1 Message Date
ZhanGSKen
16f45aabe4 <aes>APK 15.6.3 release Publish. 2025-05-12 18:10:35 +08:00
ZhanGSKen
8190f4818f <aes>APK 15.6.2 release Publish. 2025-05-12 18:10:02 +08:00
ZhanGSKen
f339733d4b <aes>APK 15.6.1 release Publish. 2025-05-12 18:07:00 +08:00
ZhanGSKen
0a993ccb9b 设置 Gradle 编译时的本地 Maven 库路径 2025-05-12 18:03:14 +08:00
ZhanGSKen
9bd4c41b75 添加 Maven 库紧急备用通道 2025-05-12 15:19:02 +08:00
ZhanGSKen
42256fd0e4 更新 Maven 库引用方案 2025-05-12 03:34:18 +08:00
ZhanGSKen
2a11205468 添加 gradle 调试码 2025-05-07 20:00:44 +08:00
ZhanGSKen
bb9f46373a Merge remote-tracking branch 'origin/timestamp' into appbase 2025-05-07 19:23:19 +08:00
ZhanGSKen
af797743d4 <timestamp>APK 15.1.1 release Publish. 2025-05-07 15:07:02 +08:00
ZhanGSKen
1b37f1bd11 修改图标资源 2025-05-07 15:06:16 +08:00
ZhanGSKen
00177284c1 <timestamp>APK 15.1.0 release Publish. 2025-05-07 14:54:01 +08:00
ZhanGSKen
178e00f8b4 更新版本号 2025-05-07 14:53:32 +08:00
ZhanGSKen
743a5d5299 <timestamp>Start New Stage Version. 2025-05-07 14:51:24 +08:00
ZhanGSKen
30f75f2394 <timestamp>Start New Stage Version. 2025-05-07 14:51:14 +08:00
ZhanGSKen
9d66934694 图标资源修改 2025-05-07 14:48:50 +08:00
ZhanGSKen
c7177f5fa5 <timestamp>APK 15.0.16 release Publish. 2025-05-07 14:44:06 +08:00
ZhanGSKen
9e8263ae8f 分开应用图标和调试图标 2025-05-07 14:43:20 +08:00
ZhanGSKen
5ee9da1982 <timestamp>APK 15.0.15 release Publish. 2025-05-07 14:05:50 +08:00
ZhanGSKen
f59f7676b7 调整通知栏显示信息 2025-05-07 14:05:12 +08:00
ZhanGSKen
19b8251182 <timestamp>APK 15.0.14 release Publish. 2025-05-07 13:59:51 +08:00
ZhanGSKen
4dadb15c0c <timestamp>APK 15.0.13 release Publish. 2025-05-07 13:59:38 +08:00
ZhanGSKen
21b5487513 设置时间戳初始数据 2025-05-07 13:58:56 +08:00
ZhanGSKen
f49c8141a8 <timestamp>APK 15.0.12 release Publish. 2025-05-07 13:48:06 +08:00
ZhanGSKen
6f3ce092ca 调整调试版与阶段版的消息发送 2025-05-07 13:47:26 +08:00
ZhanGSKen
29785be88d 修复信息显示 2025-05-07 13:46:21 +08:00
ZhanGSKen
bf0a70e056 Merge remote-tracking branch 'origin/timestamp' into appbase 2025-05-07 13:34:00 +08:00
ZhanGSKen
3f809e4c7a <timestamp>APK 15.0.11 release Publish. 2025-05-07 13:33:12 +08:00
ZhanGSKen
9c3c485ecd 更新Maven库地址 2025-05-07 13:32:10 +08:00
ZhanGSKen
336511931d <timestamp>APK 15.0.10 release Publish. 2025-05-07 12:09:48 +08:00
ZhanGSKen
d3da3304ee 统一时间戳数据生成方法 2025-05-07 11:58:48 +08:00
ZhanGSKen
a5295a0744 更新Maven库地址 2025-05-07 11:58:14 +08:00
ZhanGSKen
9b761b35ce <timestamp>APK 15.0.9 release Publish. 2025-05-07 10:56:24 +08:00
ZhanGSKen
5206f8781e 初始数据 BugFix. 2025-05-07 10:55:46 +08:00
ZhanGSKen
2c0a5bfe65 更新默认时间戳显示格式 2025-05-07 10:51:48 +08:00
ZhanGSKen
49bce0f773 <timestamp>APK 15.0.8 release Publish. 2025-05-07 10:48:11 +08:00
ZhanGSKen
07c6778212 优化UI显示 2025-05-07 10:47:19 +08:00
ZhanGSKen
1cdad9ac7d <timestamp>APK 15.0.7 release Publish. 2025-05-07 02:58:13 +08:00
ZhanGSKen
986922c1fa 添加自定义应用内铃声 2025-05-07 02:57:08 +08:00
ZhanGSKen
bff40d2a64 重构通知栏消息,添加铃声。 2025-05-07 02:35:51 +08:00
ZhanGSKen
13e0ad3f03 <timestamp>APK 15.0.6 release Publish. 2025-05-06 20:37:48 +08:00
ZhanGSKen
be85ef923e 更新应用图标,优化状态栏UI。 2025-05-06 20:36:51 +08:00
ZhanGSKen
9a3383a43b <timestamp>APK 15.0.5 release Publish. 2025-05-06 18:33:59 +08:00
ZhanGSKen
d875b6965d <timestamp>APK 15.0.4 release Publish. 2025-05-06 18:33:32 +08:00
ZhanGSKen
bc873852c2 修复时间戳复制格式设置未保存问题 2025-05-06 18:32:51 +08:00
ZhanGSKen
b032de55dc 添加说明书 2025-05-06 18:14:34 +08:00
ZhanGSKen
8fa20b56ec <timestamp>APK 15.0.3 release Publish. 2025-05-06 18:01:46 +08:00
ZhanGSKen
1ca93a610e 优化常驻通知电量消耗 2025-05-06 17:59:57 +08:00
ZhanGSKen
6555346618 重构通知栏模块 2025-05-06 17:04:14 +08:00
ZhanGSKen
df6633046c <timestamp>APK 15.0.2 release Publish. 2025-05-06 11:34:02 +08:00
ZhanGSKen
7d35d5a9b8 添加通知栏打开主窗口功能 2025-05-06 11:33:28 +08:00
ZhanGSKen
ddce5646bd <timestamp>APK 15.0.1 release Publish. 2025-05-06 11:27:46 +08:00
ZhanGSKen
3607a99053 添加通知栏清理功能 2025-05-06 11:27:09 +08:00
ZhanGSKen
b44e5575b3 Merge remote-tracking branch 'origin/timestamp' into appbase 2025-05-06 11:18:33 +08:00
ZhanGSKen
6a101d8843 <timestamp>Start New Stage Version. 2025-05-06 11:17:47 +08:00
ZhanGSKen
c5e5e76bbf <timestamp>APK 15.0.0 release Publish. 2025-05-06 11:17:44 +08:00
ZhanGSKen
6d05808a2a 设置目标版本 2025-05-06 11:13:35 +08:00
ZhanGSKen
2625ffe6a6 通知栏优先级设置调试 2025-05-06 11:11:45 +08:00
ZhanGSKen
9c4c1320aa 添加拷贝时间戳到剪贴板功能 2025-05-06 11:07:28 +08:00
ZhanGSKen
551b424960 添加时间戳拷贝与显示的格式设置功能 2025-05-06 10:48:53 +08:00
ZhanGSKen
1062c7d2ee 重构常驻通知栏 2025-05-06 10:09:34 +08:00
ZhanGSKen
e958556073 0937 2025-05-05 21:37:22 +08:00
ZhanGSKen
466e061aa8 0343 2025-05-05 15:43:41 +08:00
ZhanGSKen
2f2512cd76 修复时间戳复制按钮响应 2025-05-05 12:39:45 +08:00
ZhanGSKen
d26de8f2f7 1153 2025-05-05 11:53:48 +08:00
ZhanGSKen
2e9b6ae263 添加通知栏时间戳显示 2025-05-05 11:30:41 +08:00
ZhanGSKen
f0a29dc7a9 添加常驻通知栏 2025-05-05 10:45:25 +08:00
ZhanGSKen
125c2b4431 初始化 TimeStamp 时间戳应用。 2025-05-04 22:03:06 +08:00
ZhanGSKen
4a4e20a6bd 常驻通知栏点击BugFix. 2025-05-04 13:33:15 +08:00
ZhanGSKen
4c0d825d7b Merge remote-tracking branch 'origin/appbase' into autoinstaller 2025-05-04 13:29:22 +08:00
ZhanGSKen
ae111b3a99 2025-05-04 13:27:55 +08:00
ZhanGSKen
03ceaf3e2a Merge remote-tracking branch 'origin/powerbell' into appbase 2025-05-04 13:23:10 +08:00
ZhanGSKen
7ff2cab615 源码整理 2025-05-03 19:36:03 +08:00
ZhanGSKen
954139f0c9 Merge remote-tracking branch 'origin/appbase' into aes 2025-05-03 18:34:10 +08:00
ZhanGSKen
027dc23060 更新Maven库地址设置 2025-05-03 18:33:41 +08:00
ZhanGSKen
640f0c39e9 常驻通知栏UI优化。 2025-05-03 16:44:32 +08:00
ZhanGSKen
9816167e87 优化通知栏显示文字 2025-05-03 14:22:27 +08:00
ZhanGSKen
512c321b76 优化常驻通知栏显示UI 2025-05-03 14:19:32 +08:00
ZhanGSKen
482c007481 应用服务启动后延迟2秒执行提醒任务 2025-05-03 14:01:56 +08:00
ZhanGSKen
609182f909 编译测试 2025-05-03 13:53:48 +08:00
ZhanGSKen
eb69a473a3 调整类库引用顺序 2025-05-03 13:53:32 +08:00
ZhanGSKen
38e4deb425 修复应用不能打开的Bug. 2025-05-03 13:46:46 +08:00
ZhanGSKen
d06023d023 (打开应用)的按钮修复 2025-05-03 13:21:32 +08:00
ZhanGSKen
cb486bb90c 更新类库,BugFix。 2025-05-03 11:52:00 +08:00
ZhanGSKen
2cbf9e5669 Merge remote-tracking branch 'origin/appbase' into autoinstaller 2025-05-03 10:09:42 +08:00
ZhanGSKen
e772563fc0 模块清理 2025-05-02 06:16:08 +08:00
ZhanGSKen
55ce968053 <powerbell>APK 15.3.0 release Publish. 2025-04-29 21:00:39 +08:00
ZhanGSKen
502d3ad23d 升级版本号,升级引用类库。设置适配的Android API为30. 2025-04-29 20:42:50 +08:00
ZhanGSKen
103c1101ce Merge remote-tracking branch 'origin/appbase' into powerbell 2025-04-29 20:08:31 +08:00
ZhanGSKen
03d4cf2efe Merge remote-tracking branch 'origin/apputils' into appbase 2025-04-29 15:15:58 +08:00
ZhanGSKen
15fa0ad506 Merge remote-tracking branch 'origin/aes' into appbase 2025-04-29 15:15:52 +08:00
ZhanGSKen
0dfc7df4b5 <libaes>Library Release 15.6.0 2025-04-29 15:14:45 +08:00
ZhanGSKen
f33956c872 <aes>APK 15.6.0 release Publish. 2025-04-29 15:14:21 +08:00
ZhanGSKen
9283ad709f 升级版本号 2025-04-29 15:13:53 +08:00
ZhanGSKen
459fff1531 <aes>APK 15.5.0 release Publish. 2025-04-29 15:11:54 +08:00
ZhanGSKen
b4fb680b8c 更新 Android API 目标版本,更新基础类库,把 WinBoll 改为 WinBoLL。 2025-04-29 15:09:25 +08:00
ZhanGSKen
cf933da3aa <libapputils>Library Release 15.3.4 2025-04-29 15:04:20 +08:00
ZhanGSKen
2bd8947af1 <apputils>APK 15.3.4 release Publish. 2025-04-29 15:03:54 +08:00
ZhanGSKen
28f2ada31a 把 WinBoll 改为 WinBoLL。 2025-04-29 15:03:10 +08:00
ZhanGSKen
81669f03b3 <libappbase>Library Release 15.7.6 2025-04-29 14:47:37 +08:00
ZhanGSKen
980eb533bf <appbase>APK 15.7.6 release Publish. 2025-04-29 14:47:16 +08:00
ZhanGSKen
4fcef89a10 编译测试 2025-04-29 14:46:07 +08:00
ZhanGSKen
3127a26056 文件清理 2025-04-29 14:45:53 +08:00
ZhanGSKen
897875b368 把 WinBoll 改为 WinBoLL 2025-04-29 14:39:39 +08:00
ZhanGSKen
e12dd5630b Merge remote-tracking branch 'origin/apputils' into appbase 2025-04-29 14:10:58 +08:00
ZhanGSKen
23519f1ec9 <libapputils>Library Release 15.3.3 2025-04-29 14:08:14 +08:00
ZhanGSKen
7dc2668d42 <apputils>APK 15.3.3 release Publish. 2025-04-29 14:07:52 +08:00
ZhanGSKen
63f424c149 Merge remote-tracking branch 'github/apputils' into appbase 2025-04-29 14:06:04 +08:00
ZhanGSKen
ff9f5b42b9 整理代码 2025-04-29 14:05:21 +08:00
ZhanGSKen
077a5fc616 更换主题基础类 2025-04-29 14:04:40 +08:00
ZhanGSKen
62fd671f6e 调整适配Android API 级别 2025-04-29 13:45:03 +08:00
ZhanGSKen
3dd66d387a Merge remote-tracking branch 'origin/appbase' into apputils 2025-04-29 13:42:04 +08:00
ZhanGSKen
dab1412bc4 <libappbase>Library Release 15.7.5 2025-04-29 13:40:47 +08:00
ZhanGSKen
aca23ca727 <appbase>APK 15.7.5 release Publish. 2025-04-29 13:40:21 +08:00
ZhanGSKen
5e8070ae01 调整类库依赖,改为基本Android API依赖。 2025-04-29 13:39:36 +08:00
ZhanGSKen
d737766459 Merge remote-tracking branch 'origin/appbase' into apputils 2025-04-29 13:16:20 +08:00
ZhanGSKen
f8e9afddd1 <libappbase>Library Release 15.7.4 2025-04-29 13:15:31 +08:00
ZhanGSKen
f8bb5a7852 <appbase>APK 15.7.4 release Publish. 2025-04-29 13:14:59 +08:00
ZhanGSKen
5a1f1f2d73 修改 WinBoll 为 WinBoLL 2025-04-29 13:04:45 +08:00
ZhanGSKen
0e4dc85dfa Merge remote-tracking branch 'origin/appbase' into aes 2025-04-29 12:48:41 +08:00
ZhanGSKen
2be7e2cbaf 编译测试 2025-04-29 12:21:17 +08:00
ZhanGSKen
24f598294e 编译调试 2025-04-29 12:19:32 +08:00
ZhanGSKen
b5e918b364 更新应用图标 2025-04-29 12:18:51 +08:00
ZhanGSKen
50561a2f67 更换Android适配版本 2025-04-29 12:18:32 +08:00
ZhanGSKen
204642a5cd <aes>Start New Stage Version. 2025-04-29 00:22:19 +08:00
ZhanGSKen
daf10dd727 <aes>Start New Stage Version. 2025-04-29 00:21:59 +08:00
ZhanGSKen
2005ba6aee 设置版本号 2025-04-29 00:21:37 +08:00
ZhanGSKen
6adc96449b <aes>Start New Stage Version. 2025-04-29 00:19:26 +08:00
ZhanGSKen
4d8675f1f7 编译测试 2025-04-29 00:10:58 +08:00
ZhanGSKen
2467f3b5a8 Merge remote-tracking branch 'origin/appbase' into aes 2025-04-29 00:08:31 +08:00
ZhanGSKen
33726a306f 编译测试 2025-04-29 00:07:26 +08:00
ZhanGSKen
99199d445e <apputils>APK 15.3.2 release Publish. 2025-04-28 21:34:43 +08:00
ZhanGSKen
a629941054 编译测试 2025-04-28 21:22:06 +08:00
ZhanGSKen
3a2274d7a2 Merge remote-tracking branch 'origin/appbase' into apputils 2025-04-28 21:20:38 +08:00
ZhanGSKen
32a0e47c87 <appbase>APK 15.7.3 release Publish. 2025-04-28 21:17:52 +08:00
ZhanGSKen
82af906fed <appbase>APK 15.7.2 release Publish. 2025-04-28 21:14:11 +08:00
ZhanGSKen
d13a8a445e <appbase>APK 15.7.1 release Publish. 2025-04-28 21:13:53 +08:00
ZhanGSKen
6d02c9fc49 <appbase>APK 15.7.0 release Publish. 2025-04-28 21:13:29 +08:00
ZhanGSKen
10a8c2c1ed <appbase>Start New Stage Version. 2025-04-28 21:08:18 +08:00
ZhanGSKen
bff0ec4e57 设定版本号 2025-04-28 21:07:08 +08:00
ZhanGSKen
aa101977f0 把 WinBoll 名称改为 WinBoLL 2025-04-28 21:04:17 +08:00
ZhanGSKen
a88e1b2d99 git repos fix 2025-04-28 20:12:28 +08:00
ZhanGSKen
363fa0c3b3 <appbase>Start New Stage Version. 2025-04-28 20:06:05 +08:00
ZhanGSKen
ebb0df2a74 <appbase>Start New Stage Version. 2025-04-28 20:05:46 +08:00
ZhanGSKen
ed193119f6 设定版本号(baseBetaVersion 版本要比publishVersion 版本加多 1 个版本) 2025-04-28 00:36:58 +08:00
ZhanGSKen
b8834627ae 编译测试 2025-04-27 21:10:23 +08:00
ZhanGSKen
b918b04068 编译检查 2025-04-27 21:07:46 +08:00
ZhanGSKen
f0326bc9a3 Merge remote-tracking branch 'origin/appbase' into apputils 2025-04-27 21:04:10 +08:00
ZhanGSKen
164c00ee0d Merge remote-tracking branch 'origin/appbase' into aes 2025-04-27 21:03:34 +08:00
ZhanGSKen
f4a054663e 设定版本号 2025-04-27 21:02:50 +08:00
ZhanGSKen
d17dd8073d 编译测试 2025-04-27 21:00:31 +08:00
ZhanGSKen
b34d777c9f 合并appbase 2025-04-27 20:55:34 +08:00
ZhanGSKen
455e38ac0c Merge remote-tracking branch 'origin/appbase' into apputils 2025-04-27 20:52:28 +08:00
ZhanGSKen
7150974d01 根目录 Gradle 配置回复,移除就的秘钥配置文件。 2025-04-27 20:42:01 +08:00
ZhanGSKen
c2794e7322 回复先前版本 2025-04-27 20:28:37 +08:00
ZhanGSKen
2ce07bbcf3 <libaes>Library Release 15.4.1 2025-04-27 17:40:14 +08:00
ZhanGSKen
7932e25def <aes>APK 15.4.1 release Publish. 2025-04-27 17:39:51 +08:00
ZhanGSKen
191de5c511 <aes>APK 15.4.0 release Publish. 2025-04-27 17:39:39 +08:00
ZhanGSKen
d520c9ae06 <aes>Start New Stage Version. 2025-04-27 17:36:17 +08:00
ZhanGSKen
933abbc261 <aes>Start New Stage Version. 2025-04-27 17:36:08 +08:00
ZhanGSKen
14a161ab3f 更新版本号 2025-04-27 17:35:16 +08:00
ZhanGSKen
248b8084dc Merge branch 'aes' of https://zhangsken.winboll.cc/ZhanGSKen/APPBase.git into aes 2025-04-27 17:31:08 +08:00
ZhanGSKen
006cefea2a <aes>Start New Stage Version. 2025-04-27 17:30:08 +08:00
ZhanGSKen
ad1b6ca3e9 编译测试 2025-04-27 17:26:24 +08:00
ZhanGSKen
37c93658b3 <aes>Start New Stage Version. 2025-04-27 14:03:25 +08:00
ZhanGSKen
65aae2146e 更新版本号 2025-04-27 14:03:03 +08:00
ZhanGSKen
58eba26eb5 <aes>Start New Stage Version. 2025-04-27 14:00:56 +08:00
ZhanGSKen
a32b10cdec 更新类库 2025-04-27 13:48:45 +08:00
ZhanGSKen
559d41e46c <libapputils>Library Release 15.3.1 2025-04-27 13:42:58 +08:00
ZhanGSKen
ef9c0f08ae <apputils>APK 15.3.1 release Publish. 2025-04-27 13:42:46 +08:00
ZhanGSKen
18b078c644 与AppCompatActivity引用一样,类继承层数不能是两层。 2025-04-27 13:41:49 +08:00
ZhanGSKen
b68d8a58e3 <libapputils>Library Release 15.3.0 2025-04-27 11:20:08 +08:00
ZhanGSKen
9eec7060b9 <apputils>APK 15.3.0 release Publish. 2025-04-27 11:19:49 +08:00
ZhanGSKen
e572486b05 设置版本号 2025-04-27 11:17:19 +08:00
ZhanGSKen
4737ce92e1 <apputils>Start New Stage Version. 2025-04-27 11:14:42 +08:00
ZhanGSKen
7abe096779 移除类库依赖 2025-04-27 11:11:48 +08:00
ZhanGSKen
839bf72eca <libappbase>Library Release 15.4.8 2025-04-27 11:03:44 +08:00
ZhanGSKen
3f2ec872f8 <appbase>APK 15.4.8 release Publish. 2025-04-27 11:03:35 +08:00
ZhanGSKen
b62cbc043b 移除类库依赖 2025-04-27 11:02:06 +08:00
ZhanGSKen
d0e7f1f56c <libappbase>Library Release 15.4.7 2025-04-27 10:06:01 +08:00
ZhanGSKen
39fe25f34f <appbase>APK 15.4.7 release Publish. 2025-04-27 10:05:40 +08:00
ZhanGSKen
94f444d6c7 更新版本号 2025-04-27 10:05:16 +08:00
ZhanGSKen
33952a90de <appbase>APK 15.2.6 release Publish. 2025-04-27 10:02:38 +08:00
ZhanGSKen
4c1fece9f0 更新秘钥配置方式 2025-04-27 09:59:15 +08:00
ZhanGSKen
b60de856ca Merge branch 'appbase' into winboll 2025-04-26 21:16:41 +08:00
ZhanGSKen
37d5d1efe0 更新秘钥配置方式,更新APK文件输出目录,更新说明书。 2025-04-26 20:29:31 +08:00
ZhanGSKen
c11102d729 开发者联系邮箱 2025-04-26 19:16:10 +08:00
ZhanGSKen
6cc04f8e6f 修正说明书标题部分 2025-04-26 17:52:58 +08:00
ZhanGSKen
2bc7db1dca 将appbase子项目拷贝为winboll子项目。 2025-04-24 20:19:19 +08:00
ZhanGSKen
96f31e252c 设置类库资源地址引用顺序。 2025-04-24 20:11:58 +08:00
ZhanGSKen
4612593d88 添加调试日志 2025-04-15 20:47:23 +08:00
ZhanGSKen
0460613aea 固定参数属性初始化方法综合 2025-04-15 20:43:18 +08:00
ZhanGSKen
e1dadfa41c 源码检查,辅助提交。 2025-04-15 19:45:34 +08:00
ZhanGSKen
1a7134711e 实现磁贴工具静态挂载... 2025-04-15 18:22:17 +08:00
ZhanGSKen
dabd1271fd 重构磁贴状态更新方法 2025-04-15 17:23:19 +08:00
ZhanGSKen
0d3d691d8a 添加应用图标文件 2025-04-15 17:10:01 +08:00
ZhanGSKen
f3198de003 源码整理 2025-04-15 16:18:26 +08:00
ZhanGSKen
df179242f0 清理冗余注释 2025-04-15 16:09:39 +08:00
ZhanGSKen
1ae3fdf2b7 取消磁贴状态更新 2025-04-15 16:07:49 +08:00
ZhanGSKen
7edc341f15 整理代码 2025-04-15 16:03:45 +08:00
ZhanGSKen
f1669f9e41 更新应用实例参数上下文 2025-04-15 16:00:42 +08:00
ZhanGSKen
63b1d2dece 添加磁贴工具... 2025-04-15 10:57:19 +08:00
ZhanGSKen
6a0510e76c 取消用应用外部的路径作为临时目录,转为应用内部缓存目录里的文件夹作为临时目录。 2025-04-14 21:01:46 +08:00
ZhanGSKen
f9bfdd3de8 Merge branch 'app_important_fix' into appbase 2025-04-13 03:04:52 +08:00
ZhanGSKen
0f787de4d9 <contacts>APK 15.2.7 release Publish. 2025-04-13 02:46:10 +08:00
ZhanGSKen
c476792d7c 定义云盾参数有效区域值 2025-04-13 02:04:48 +08:00
ZhanGSKen
ee421d1019 Merge branch 'contacts' into app_important_fix 2025-04-13 00:45:42 +08:00
ZhanGSKen
cd277497a3 Merge remote-tracking branch 'origin/contacts' into appbase 2025-04-13 00:45:11 +08:00
ZhanGSKen
8452680514 更换 BoBullToon 数据请求地址 2025-04-12 12:26:08 +08:00
ZhanGSKen
c302179960 <autoinstaller>APK 15.2.0 release Publish. 2025-04-02 21:25:51 +08:00
ZhanGSKen
df55608eb3 更新应用打开按钮的启动数据。 2025-04-02 21:23:49 +08:00
ZhanGSKen
b64a229ab1 数据模型命名空间重构 2025-04-02 20:48:37 +08:00
ZhanGSKen
d507d4ac06 调整全局吐司风格 2025-04-02 20:45:25 +08:00
ZhanGSKen
e815c08970 更新类库,去掉主窗口启动时的吐司提示。 2025-04-02 20:41:05 +08:00
ZhanGSKen
d780116031 Merge remote-tracking branch 'origin/appbase' into autoinstaller 2025-04-02 20:31:31 +08:00
ZhanGSKen
3ce6746e50 <contacts>APK 15.2.6 release Publish. 2025-04-01 17:48:43 +08:00
ZhanGSKen
c06b157332 更新应用介绍窗口标题栏风格 2025-04-01 17:47:31 +08:00
ZhanGSKen
8dd41a0c3a <powerbell>APK 15.2.2 release Publish. 2025-03-31 20:11:58 +08:00
ZhanGSKen
ca4814744f 更新类库,修复调试模式设置。 2025-03-31 20:11:08 +08:00
ZhanGSKen
1f8f7fd405 <contacts>APK 15.2.5 release Publish. 2025-03-31 19:51:50 +08:00
ZhanGSKen
60ddae0950 更新类库,修复应用简介窗口设置应用调试模式没有保存的问题。 2025-03-31 19:51:02 +08:00
ZhanGSKen
79b680f9f3 <contacts>APK 15.2.4 release Publish. 2025-03-31 15:43:06 +08:00
ZhanGSKen
f572c488e6 修改调试模式配置方法 2025-03-31 15:42:33 +08:00
ZhanGSKen
a018958899 <contacts>APK 15.2.3 release Publish. 2025-03-31 15:25:52 +08:00
ZhanGSKen
87dfa4872d <contacts>APK 15.2.2 release Publish. 2025-03-31 15:25:32 +08:00
ZhanGSKen
935a663cee 在设置窗口里添加应用介绍按钮 2025-03-31 15:23:41 +08:00
ZhanGSKen
5a9ee81c30 <powerbell>APK 15.2.1 release Publish. 2025-03-31 15:08:28 +08:00
ZhanGSKen
841d526b0d <powerbell>APK 15.2.0 release Publish. 2025-03-31 15:06:43 +08:00
ZhanGSKen
11d2db6bf9 更新应用简介。 2025-03-31 15:05:02 +08:00
ZhanGSKen
93d8fa15db 修改主题配置,修复日志菜单栏显示问题。 2025-03-31 14:59:54 +08:00
ZhanGSKen
99ff741a22 更新类库,设置版本号。 2025-03-31 14:53:33 +08:00
ZhanGSKen
5bc7581db1 Merge remote-tracking branch 'origin/androidxdemo' into powerbell 2025-03-31 14:39:31 +08:00
ZhanGSKen
fde0712439 Merge remote-tracking branch 'origin/appbase' into powerbell 2025-03-31 14:39:26 +08:00
ZhanGSKen
c47639484d <contacts>APK 15.2.1 release Publish. 2025-03-31 13:11:08 +08:00
ZhanGSKen
f053218932 添加通话记录数据更新功能。在长期打开主窗口后,通话记录可以实时更新。 2025-03-31 13:07:40 +08:00
ZhanGSKen
bec4041b87 修复日志视图再打开第二个日志后,第一个日志回显时停顿问题。 2025-03-31 12:09:46 +08:00
ZhanGSKen
0d540c422e 更新类库 2025-03-31 12:05:11 +08:00
ZhanGSKen
62b39988fd Merge remote-tracking branch 'origin/appbase' into contacts 2025-03-31 11:50:44 +08:00
ZhanGSKen
82d8cd7f17 <contacts>APK 15.2.0 release Publish. 2025-03-29 09:34:54 +08:00
ZhanGSKen
5465b8eb34 编译测试 2025-03-29 09:34:09 +08:00
ZhanGSKen
35d7716778 编译参数修复 2025-03-29 09:33:05 +08:00
ZhanGSKen
256b9ec99c 设置版本号 2025-03-29 09:22:32 +08:00
ZhanGSKen
2aacb0e70d <contacts>APK 15.2.22 release Publish. 2025-03-29 09:16:58 +08:00
ZhanGSKen
266009de85 Merge remote-tracking branch 'origin/appbase' into contacts 2025-03-29 09:15:52 +08:00
ZhanGSKen
1a571684ca 更新类库版本 2025-03-29 09:13:14 +08:00
ZhanGSKen
4ff54c785e <powerbell>APK 15.0.3 release Publish. 2025-03-25 02:43:07 +08:00
ZhanGSKen
b961468e1e <powerbell>APK 15.0.2 release Publish. 2025-03-25 02:42:42 +08:00
ZhanGSKen
575ef9aac0 内容提供配置调整 2025-03-25 02:42:10 +08:00
ZhanGSKen
16b118f83b <powerbell>APK 15.0.1 release Publish. 2025-03-25 02:36:56 +08:00
ZhanGSKen
1eeba4e4c6 <powerbell>APK 15.0.0 release Publish. 2025-03-25 02:34:56 +08:00
ZhanGSKen
66eb8e06ea 应用介绍页基本调试完成 2025-03-25 02:30:45 +08:00
ZhanGSKen
51775620db 更新类库,调试应用介绍... 2025-03-24 20:24:31 +08:00
ZhanGSKen
deaa9caadd Merge remote-tracking branch 'origin/appbase' into powerbell 2025-03-24 14:33:17 +08:00
ZhanGSKen
58af5ba074 <powerbell>APK 4.0.7 release Publish. 2025-03-22 16:06:33 +08:00
ZhanGSKen
19743d30ef 修复编译参数 2025-03-22 16:05:10 +08:00
ZhanGSKen
3c2b720e20 调整背景图片设置窗口按钮布局 2025-03-22 16:03:08 +08:00
ZhanGSKen
5a052e4b22 添加电池报告窗口雏形 2025-03-22 15:54:18 +08:00
ZhanGSKen
4ccf6824a6 <powerbell>APK 4.0.6 release Publish. 2025-03-22 06:01:46 +08:00
ZhanGSKen
508c8b0b97 适配小米15,修改通知模块。 2025-03-22 05:53:58 +08:00
ZhanGSKen
0d21994291 编译配置精简 2025-03-22 03:06:40 +08:00
ZhanGSKen
473dd627fe 添加云盾的防御功能临时暂停控件 2025-03-19 14:28:35 +08:00
ZhanGSKen
dc8dcc35dd <contacts>APK 1.0.21 release Publish. 2025-03-19 11:09:14 +08:00
ZhanGSKen
80c00a86b0 修正联系人信息提示语法问题。 2025-03-19 11:08:12 +08:00
ZhanGSKen
35c639e7c9 <contacts>APK 1.0.20 release Publish. 2025-03-19 08:51:20 +08:00
ZhanGSKen
05cc5c72f8 添加联系人查询,在联系人名单内就允许连接。 2025-03-19 08:48:03 +08:00
ZhanGSKen
3eaec5ed73 Merge branch 'appbase' into contacts 2025-03-17 09:54:20 +08:00
ZhanGSKen
5714aed761 <autoinstaller>APK 15.0.0 release Publish. 2025-03-15 15:38:45 +08:00
ZhanGSKen
f04f5cf77c 更新安卓适配版本 2025-03-15 15:33:36 +08:00
ZhanGSKen
6ccbaf8c32 更新类库 2025-03-15 15:21:41 +08:00
ZhanGSKen
486a17d4b2 <contacts>APK 1.0.19 release Publish. 2025-03-09 20:33:54 +08:00
ZhanGSKen
6cde3be042 更新类库 2025-03-09 20:31:51 +08:00
ZhanGSKen
445bf47e38 <contacts>APK 1.0.18 release Publish. 2025-03-09 20:06:59 +08:00
ZhanGSKen
322e1426d7 修复拉动拨号控件在列表滚动时图标位置不稳定的BUG. 2025-03-09 20:05:52 +08:00
ZhanGSKen
9ff752b652 <contacts>APK 1.0.17 release Publish. 2025-03-09 17:42:52 +08:00
ZhanGSKen
95cba7166e UI优化 2025-03-09 17:41:19 +08:00
ZhanGSKen
ba4354ad59 <contacts>APK 1.0.16 release Publish. 2025-03-09 14:02:13 +08:00
ZhanGSKen
69fd92f4a0 Merge branch 'appbase' into contacts 2025-03-09 14:01:10 +08:00
ZhanGSKen
73608ed6e6 更新拉动拨号控件 2025-03-09 14:00:36 +08:00
ZhanGSKen
5746ef4569 Merge branch 'contacts' into appbase 2025-03-09 11:43:12 +08:00
ZhanGSKen
ad745a9cc1 更新类库 2025-03-09 11:42:53 +08:00
ZhanGSKen
e3571ae39f <contacts>APK 1.0.15 release Publish. 2025-03-08 04:50:59 +08:00
ZhanGSKen
261c87a42d 修复应用日志时间显示BUG 2025-03-08 04:49:35 +08:00
ZhanGSKen
0db31c07ba <contacts>APK 1.0.14 release Publish. 2025-03-08 04:37:47 +08:00
ZhanGSKen
87dea88e36 <contacts>APK 1.0.13 release Publish. 2025-03-08 04:37:15 +08:00
ZhanGSKen
5779f195f8 设置UI为拖动拨号方式 2025-03-08 04:36:07 +08:00
ZhanGSKen
02f2d4f0bc <contacts>APK 1.0.12 release Publish. 2025-03-06 21:44:44 +08:00
ZhanGSKen
f6bece28ac 添加通信记录联系人显示 2025-03-06 21:43:50 +08:00
ZhanGSKen
559f1c58ba <contacts>APK 1.0.11 release Publish. 2025-03-06 20:00:52 +08:00
ZhanGSKen
4bb814308b 添加单号码单元测试 2025-03-06 19:59:11 +08:00
ZhanGSKen
05a9fc5275 <contacts>APK 1.0.10 release Publish. 2025-03-05 20:51:08 +08:00
ZhanGSKen
74d5239898 添加规则删除确认框。 2025-03-05 20:49:39 +08:00
ZhanGSKen
948141d5a9 规则编辑列表的项滑动逻辑完成。 2025-03-05 20:41:29 +08:00
ZhanGSKen
3535df8b3e <contacts>APK 1.0.9 release Publish. 2025-03-05 17:21:57 +08:00
ZhanGSKen
bd4ba7b291 连接规则记录操作接口完成 2025-03-05 17:20:00 +08:00
ZhanGSKen
9ff8b1ed80 1629 2025-03-05 16:29:55 +08:00
ZhanGSKen
859238a173 1852 2025-03-04 18:52:16 +08:00
ZhanGSKen
53eced104e <contacts>APK 1.0.8 release Publish. 2025-03-04 01:25:27 +08:00
ZhanGSKen
b45119b487 添加调试信息 2025-03-04 01:24:16 +08:00
ZhanGSKen
fdddde33b5 <contacts>APK 1.0.7 release Publish. 2025-03-03 20:51:18 +08:00
ZhanGSKen
f263733609 UI界面优化 2025-03-03 20:42:00 +08:00
ZhanGSKen
51a1cf1e26 重构为Java7语法 2025-03-03 20:23:00 +08:00
ZhanGSKen
093772c824 <contacts>APK 1.0.6 release Publish. 2025-03-03 17:39:16 +08:00
ZhanGSKen
bf3e9bdc91 云盾防御体系成型 2025-03-03 17:37:42 +08:00
ZhanGSKen
0f72817b50 <contacts>APK 1.0.5 release Publish. 2025-03-02 18:48:57 +08:00
ZhanGSKen
c827b2cbad 完成电话连接规则的编辑和保存 2025-03-02 18:26:45 +08:00
ZhanGSKen
02673d19dd <contacts>APK 1.0.4 release Publish. 2025-02-26 19:10:28 +08:00
ZhanGSKen
38884d3457 基本拨号功能完成 2025-02-26 19:07:55 +08:00
ZhanGSKen
ecb56df773 1821 2025-02-26 18:22:10 +08:00
ZhanGSKen
a15f6bad8f 1509 2025-02-26 15:09:29 +08:00
ZhanGSKen
2f0293103c 添加通话记录和联系人 2025-02-26 14:10:16 +08:00
ZhanGSKen
d4135f0104 <contacts>APK 1.0.3 release Publish. 2025-02-26 05:10:50 +08:00
ZhanGSKen
bdb9bc7637 性能优化 2025-02-26 05:08:56 +08:00
ZhanGSKen
e7633e53ed <contacts>APK 1.0.2 release Publish. 2025-02-26 04:37:18 +08:00
ZhanGSKen
3734a659ff 添加主要服务启动设置 2025-02-26 04:35:14 +08:00
ZhanGSKen
b9f740c386 添加系统设置静音时的权限异常处理,添加固定的应用铃声音量设置。忽略其他应用设置的铃声音量。 2025-02-26 03:40:41 +08:00
291 changed files with 10089 additions and 2704 deletions

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "winboll-x"]
path = winboll-x
url = https://gitea.winboll.cc/Studio/WinBollX.git
[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

View File

@@ -1,4 +1,4 @@
## WinBoll 主机编译事项提醒 ## WinBoLL 主机编译事项提醒
## 类库类型源码发布 ## 类库类型源码发布
# 类库发布使用以下面命令 # 类库发布使用以下面命令

View File

@@ -37,7 +37,7 @@ fi
# 使用grep找到包含"publishVersion="的那一行然后用awk提取其后的值 # 使用grep找到包含"publishVersion="的那一行然后用awk提取其后的值
PUBLISH_VERSION=$(grep -o "publishVersion=.*" $1/build.properties | awk -F '=' '{print $2}') PUBLISH_VERSION=$(grep -o "publishVersion=.*" $1/build.properties | awk -F '=' '{print $2}')
echo "< $1/build.properties publishVersion : ${PUBLISH_VERSION} >" echo "< $1/build.properties publishVersion : ${PUBLISH_VERSION} >"
## 设新的 WinBoll 标签 ## 设新的 WinBoLL 标签
# 脚本调试时使用 # 脚本调试时使用
#tag="v7.6.4-test1" #tag="v7.6.4-test1"
# 正式设置标签时使用 # 正式设置标签时使用

View File

@@ -38,24 +38,24 @@ function askAddWorkflowsTag {
fi fi
} }
function addWinBollTag { function addWinBoLLTag {
# 就读取脚本 .winboll/winboll_app_build.gradle 生成的 publishVersion。 # 就读取脚本 .winboll/winboll_app_build.gradle 生成的 publishVersion。
# 如果文件中有 publishVersion 这一项, # 如果文件中有 publishVersion 这一项,
# 使用grep找到包含"publishVersion="的那一行然后用awk提取其后的值 # 使用grep找到包含"publishVersion="的那一行然后用awk提取其后的值
PUBLISH_VERSION=$(grep -o "publishVersion=.*" $1/build.properties | awk -F '=' '{print $2}') PUBLISH_VERSION=$(grep -o "publishVersion=.*" $1/build.properties | awk -F '=' '{print $2}')
echo "< $1/build.properties publishVersion : ${PUBLISH_VERSION} >" echo "< $1/build.properties publishVersion : ${PUBLISH_VERSION} >"
## 设新的 WinBoll 标签 ## 设新的 WinBoLL 标签
# 脚本调试时使用 # 脚本调试时使用
#tag="projectname-v7.6.4-test1" #tag="projectname-v7.6.4-test1"
# 正式设置标签时使用 # 正式设置标签时使用
tag=$1"-v"${PUBLISH_VERSION} tag=$1"-v"${PUBLISH_VERSION}
echo "< WinBoll Tag To: $tag >"; echo "< WinBoLL Tag To: $tag >";
# 检查是否已经添加了 WinBoll Tag # 检查是否已经添加了 WinBoLL Tag
if [ "$(git tag -l ${tag})" == "${tag}" ]; then if [ "$(git tag -l ${tag})" == "${tag}" ]; then
echo -e "< WinBoll Tag ${tag} exist! >" echo -e "< WinBoLL Tag ${tag} exist! >"
return 1 # WinBoll标签重复 return 1 # WinBoLL标签重复
fi fi
# 添加WinBoll标签 # 添加WinBoLL标签
git tag -a ${tag} -F $1/app_update_description.txt git tag -a ${tag} -F $1/app_update_description.txt
return 0 return 0
} }
@@ -119,22 +119,22 @@ if [[ $? -eq 0 ]]; then
echo $result echo $result
# 发布应用 # 发布应用
echo "Publishing WinBoll APK ..." echo "Publishing WinBoLL APK ..."
# 脚本调试时使用 # 脚本调试时使用
#bash gradlew :$1:assembleBetaDebug #bash gradlew :$1:assembleBetaDebug
# 正式发布 # 正式发布
bash gradlew :$1:assembleStageRelease bash gradlew :$1:assembleStageRelease
echo "Publishing WinBoll APK OK." echo "Publishing WinBoLL APK OK."
# 添加 WinBoll 标签 # 添加 WinBoLL 标签
result=$(addWinBollTag $1) result=$(addWinBoLLTag $1)
echo $result echo $result
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
echo $result echo $result
# WinBoll 标签添加成功 # WinBoLL 标签添加成功
else else
echo -e "${0}: addWinBollTag $1\n${result}\nAdd WinBoll tag cancel." echo -e "${0}: addWinBoLLTag $1\n${result}\nAdd WinBoLL tag cancel."
exit 1 # addWinBollTag 异常 exit 1 # addWinBoLLTag 异常
fi fi
# 添加 GitHub 工作流标签 # 添加 GitHub 工作流标签

View File

@@ -38,24 +38,24 @@ function askAddWorkflowsTag {
fi fi
} }
function addWinBollTag { function addWinBoLLTag {
# 就读取脚本 .winboll/winboll_app_build.gradle 生成的 publishVersion。 # 就读取脚本 .winboll/winboll_app_build.gradle 生成的 publishVersion。
# 如果文件中有 publishVersion 这一项, # 如果文件中有 publishVersion 这一项,
# 使用grep找到包含"publishVersion="的那一行然后用awk提取其后的值 # 使用grep找到包含"publishVersion="的那一行然后用awk提取其后的值
PUBLISH_VERSION=$(grep -o "publishVersion=.*" $1/build.properties | awk -F '=' '{print $2}') PUBLISH_VERSION=$(grep -o "publishVersion=.*" $1/build.properties | awk -F '=' '{print $2}')
echo "< $1/build.properties publishVersion : ${PUBLISH_VERSION} >" echo "< $1/build.properties publishVersion : ${PUBLISH_VERSION} >"
## 设新的 WinBoll 标签 ## 设新的 WinBoLL 标签
# 脚本调试时使用 # 脚本调试时使用
#tag="v7.6.4-test1" #tag="v7.6.4-test1"
# 正式调试版设置标签时使用 # 正式调试版设置标签时使用
tag=$1"-v"${PUBLISH_VERSION}"-debug" tag=$1"-v"${PUBLISH_VERSION}"-debug"
echo "< WinBoll Tag To: $tag >"; echo "< WinBoLL Tag To: $tag >";
# 检查是否已经添加了 WinBoll Tag # 检查是否已经添加了 WinBoLL Tag
if [ "$(git tag -l ${tag})" == "${tag}" ]; then if [ "$(git tag -l ${tag})" == "${tag}" ]; then
echo -e "< WinBoll Tag ${tag} exist! >" echo -e "< WinBoLL Tag ${tag} exist! >"
return 1 # WinBoll标签重复 return 1 # WinBoLL标签重复
fi fi
# 添加WinBoll标签 # 添加WinBoLL标签
git tag -a ${tag} -F $1/app_update_description.txt git tag -a ${tag} -F $1/app_update_description.txt
return 0 return 0
} }
@@ -119,22 +119,22 @@ if [[ $? -eq 0 ]]; then
echo $result echo $result
# 发布应用 # 发布应用
echo "Publishing WinBoll Debug APK ..." echo "Publishing WinBoLL Debug APK ..."
# 脚本调试时使用 # 脚本调试时使用
#bash gradlew :$1:assembleBetaDebug #bash gradlew :$1:assembleBetaDebug
# 正式发布调试版 # 正式发布调试版
bash gradlew :$1:assembleStageDebug bash gradlew :$1:assembleStageDebug
echo "Publishing WinBoll Debug APK OK." echo "Publishing WinBoLL Debug APK OK."
# 添加 WinBoll 标签 # 添加 WinBoLL 标签
result=$(addWinBollTag $1) result=$(addWinBoLLTag $1)
echo $result echo $result
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
echo $result echo $result
# WinBoll 标签添加成功 # WinBoLL 标签添加成功
else else
echo -e "${0}: addWinBollTag $1\n${result}\nAdd WinBoll tag cancel." echo -e "${0}: addWinBoLLTag $1\n${result}\nAdd WinBoLL tag cancel."
exit 1 # addWinBollTag 异常 exit 1 # addWinBoLLTag 异常
fi fi
# 添加 GitHub 工作流标签 # 添加 GitHub 工作流标签

View File

@@ -8,7 +8,7 @@ if [ -z "$1" ]; then
fi fi
## 正式发布使用 ## 正式发布使用
git pull && bash gradlew :$1:publishReleasePublicationToWinBollReleaseRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1 git pull && bash gradlew :$1:publishReleasePublicationToWinBoLLReleaseRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1
## 调试使用 ## 调试使用
#bash gradlew :$1:publishSnapshotWinBollPublicationToWinBollSnapshotRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1 #bash gradlew :$1:publishSnapshotWinBoLLPublicationToWinBoLLSnapshotRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1

View File

@@ -1,4 +1,4 @@
// WinBoll 应用签名配置 // WinBoLL 应用签名配置
// //
android { android {
@@ -31,18 +31,18 @@ android {
} }
} }
flavorDimensions "WinBollApp" flavorDimensions "WinBoLLApp"
productFlavors { productFlavors {
beta { beta {
// 检查编译标志位配置 // 检查编译标志位配置
assert (winbollBuildProps['buildCount'] != null) assert (winbollBuildProps['buildCount'] != null)
dimension "WinBollApp" dimension "WinBoLLApp"
applicationIdSuffix ".beta" applicationIdSuffix ".beta"
LocalDateTime localDateTimeNow = LocalDateTime.now(ZoneId.of("Asia/Shanghai")); LocalDateTime localDateTimeNow = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
versionNameSuffix "-beta" + winbollBuildProps['buildCount'] + "_" + localDateTimeNow.format('mmss') versionNameSuffix "-beta" + winbollBuildProps['buildCount'] + "_" + localDateTimeNow.format('mmss')
} }
stage { stage {
dimension "WinBollApp" dimension "WinBoLLApp"
} }
} }
@@ -61,7 +61,7 @@ android {
} }
// //
// WinBoll 应用包输出配置 // WinBoLL 应用包输出配置
// 1. 配置 Stage Release 版应用包输出 // 1. 配置 Stage Release 版应用包输出
// 2. 配置 Beta Debug 版应用包输出 // 2. 配置 Beta Debug 版应用包输出
// //
@@ -74,13 +74,13 @@ android {
//def outputFileName="${rootProject.name}_${versionName}.apk" //def outputFileName="${rootProject.name}_${versionName}.apk"
def outputFileName=project.rootDir.name + "_${versionName}.apk" def outputFileName=project.rootDir.name + "_${versionName}.apk"
// 创建 WinBoll Studio 发布接口文件夹 // 创建 WinBoLL Studio 发布接口文件夹
File fWinBollStudioDir = file("/sdcard/WinBollStudio/APKs"); File fWinBoLLStudioDir = file("/sdcard/WinBoLLStudio/APKs");
if(!fWinBollStudioDir.exists()) { if(!fWinBoLLStudioDir.exists()) {
//fWinBollStudioDir.mkdirs(); //fWinBoLLStudioDir.mkdirs();
// 如果没有发布接口文件就不用进行APK发布和源码管理操作 // 如果没有发布接口文件就不用进行APK发布和源码管理操作
// 当前编译环境不是 WinBoll 主机, 以下将忽略APK发布和源码管理操作。 // 当前编译环境不是 WinBoLL 主机, 以下将忽略APK发布和源码管理操作。
println 'The current compilation environment is not in WinBoll host, and the following APK publishing and source management operations will be ignore.' println 'The current compilation environment is not in WinBoLL host, and the following APK publishing and source management operations will be ignore.'
} else { } else {
/// WINBOLL 主机的 APK 发布和源码管理操作 /// /// WINBOLL 主机的 APK 发布和源码管理操作 ///
variant.getAssembleProvider().get().doFirst { variant.getAssembleProvider().get().doFirst {
@@ -91,15 +91,15 @@ android {
// //
variant.getAssembleProvider().get().doLast { variant.getAssembleProvider().get().doLast {
variant.outputs.forEach{ file-> variant.outputs.forEach{ file->
// 如果正在调试,就拷贝到 WinBoll 备份管理文件夹 // 如果正在调试,就拷贝到 WinBoLL 备份管理文件夹
// //
if(variant.flavorName == "beta"&&variant.buildType.name == "debug"){ if(variant.flavorName == "beta"&&variant.buildType.name == "debug"){
//File outBuildBckDir = new File(fWinBollStudioDir, "/${rootProject.name}/${variant.buildType.name}") //File outBuildBckDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/${variant.buildType.name}")
File outBuildBckDir = new File(fWinBollStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}") File outBuildBckDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}")
// 创建目标路径目录 // 创建目标路径目录
if(!outBuildBckDir.exists()) { if(!outBuildBckDir.exists()) {
outBuildBckDir.mkdirs(); outBuildBckDir.mkdirs();
println "Output Folder Created.(WinBollStudio) : " + outBuildBckDir.getAbsolutePath() println "Output Folder Created.(WinBoLLStudio) : " + outBuildBckDir.getAbsolutePath()
} }
if(outBuildBckDir.exists()) { if(outBuildBckDir.exists()) {
copy{ copy{
@@ -108,7 +108,7 @@ android {
rename { rename {
String fileName -> "${outputFileName}" String fileName -> "${outputFileName}"
} }
println "Output APK (WinBollStudio): " + outBuildBckDir.getAbsolutePath() + "/${outputFileName}" println "Output APK (WinBoLLStudio): " + outBuildBckDir.getAbsolutePath() + "/${outputFileName}"
} }
// 检查编译标志位配置 // 检查编译标志位配置
assert (winbollBuildProps['buildCount'] != null) assert (winbollBuildProps['buildCount'] != null)
@@ -137,7 +137,7 @@ android {
} }
} }
// 如果正在发布,就拷贝到 WinBoll 标签管理文件夹 // 如果正在发布,就拷贝到 WinBoLL 标签管理文件夹
// //
if((variant.flavorName == "stage"&&variant.buildType.name == "debug") if((variant.flavorName == "stage"&&variant.buildType.name == "debug")
|| (variant.flavorName == "stage"&&variant.buildType.name == "release")){ || (variant.flavorName == "stage"&&variant.buildType.name == "release")){
@@ -151,8 +151,8 @@ android {
String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk" String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk"
println "CommonTagAPKName is : " + szCommonTagAPKName println "CommonTagAPKName is : " + szCommonTagAPKName
//File outTagDir = new File(fWinBollStudioDir, "/${rootProject.name}/tag/") //File outTagDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/tag/")
File outTagDir = new File(fWinBollStudioDir, "/" + project.rootDir.name + "/tag/") File outTagDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/tag/")
// 创建目标路径目录 // 创建目标路径目录
if(!outTagDir.exists()) { if(!outTagDir.exists()) {
outTagDir.mkdirs(); outTagDir.mkdirs();

View File

@@ -1,4 +1,4 @@
// 本机和 WinBoll Maven 仓库传输配置。 // 本机和 WinBoLL Maven 仓库传输配置。
// //
def getDefaultVersion(){ def getDefaultVersion(){
@@ -9,12 +9,12 @@ def getDefaultVersion(){
} }
def siteUrl = 'https://winboll.cc/?page=studio/details.php&app=${rootProject.name}' // 项目主页 def siteUrl = 'https://winboll.cc/?page=studio/details.php&app=${rootProject.name}' // 项目主页
def gitUrl = 'https://gitea.winboll.cc/WinBoll/${rootProject.name}' // 项目的git地址 def gitUrl = 'https://gitea.winboll.cc/WinBoLL/${rootProject.name}' // 项目的git地址
def DefaultGroupId = 'cc.winboll.studio' // 类库所有者groupId def DefaultGroupId = 'cc.winboll.studio' // 类库所有者groupId
def DefaultVersion = getDefaultVersion() // 版本号 def DefaultVersion = getDefaultVersion() // 版本号
def DeveloperId='zhangsken' // 开发者账号 def DeveloperId='zhangsken' // 开发者账号
def DeveloperName='ZhanGSKen' // 开发者名称 def DeveloperName='ZhanGSKen' // 开发者名称
def DeveloperEMail='ZhanGSKen@QQ.COM' // 开发者邮箱地址 def DeveloperEMail='zhangsken@188.com' // 开发者邮箱地址
def LicenseName='The Apache Software License, Version 2.0' def LicenseName='The Apache Software License, Version 2.0'
def LicenseUrl='http://www.apache.org/licenses/LICENSE-2.0.txt' def LicenseUrl='http://www.apache.org/licenses/LICENSE-2.0.txt'
@@ -27,10 +27,10 @@ afterEvaluate {
properties.load(file("${RootProjectDir}/${winbollFilePath}").newDataInputStream()) properties.load(file("${RootProjectDir}/${winbollFilePath}").newDataInputStream())
def NexusUserName = properties.getProperty("Nexus.name") def NexusUserName = properties.getProperty("Nexus.name")
def NexusPassword = properties.getProperty("Nexus.password") def NexusPassword = properties.getProperty("Nexus.password")
// WinBoll Release 仓库 // WinBoLL Release 仓库
maven{ maven{
//仓库的名字和地址 //仓库的名字和地址
name = "WinBollRelease" name = "WinBoLLRelease"
url="https://nexus.winboll.cc/repository/maven-releases/" url="https://nexus.winboll.cc/repository/maven-releases/"
// 仓库用户名密码 // 仓库用户名密码
credentials { credentials {
@@ -38,10 +38,10 @@ afterEvaluate {
password = NexusPassword password = NexusPassword
} }
} }
// WinBoll Snapshot 仓库 // WinBoLL Snapshot 仓库
maven{ maven{
//仓库的名字和地址 //仓库的名字和地址
name = "WinBollSnapshot" name = "WinBoLLSnapshot"
url="https://nexus.winboll.cc/repository/maven-snapshots/" url="https://nexus.winboll.cc/repository/maven-snapshots/"
// 仓库用户名密码 // 仓库用户名密码
credentials { credentials {
@@ -101,9 +101,9 @@ afterEvaluate {
} }
} }
// WinBoll Maven Release 仓库传输任务 // WinBoLL Maven Release 仓库传输任务
// //
releaseWinBoll(MavenPublication) { releaseWinBoLL(MavenPublication) {
// 需要使用的变体假设有free和pay两个变体可以选择一个 // 需要使用的变体假设有free和pay两个变体可以选择一个
//from components.free //from components.free
@@ -154,9 +154,9 @@ afterEvaluate {
} // 创建名为 release 的任务结束 } // 创建名为 release 的任务结束
// WinBoll Maven Snapshot 仓库传输任务 // WinBoLL Maven Snapshot 仓库传输任务
// //
snapshotWinBoll(MavenPublication) { snapshotWinBoLL(MavenPublication) {
// 需要使用的变体假设有free和pay两个变体可以选择一个 // 需要使用的变体假设有free和pay两个变体可以选择一个
//from components.free //from components.free

127
README.md
View File

@@ -1,23 +1,23 @@
# ☁ ☁ ☁ WinBoll APP ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ # ☁ ☁ ☁ WinBoLL APP ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁
# ☁ ☁ 这是 WinBoll 系列 APP 汇总项目。☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ # ☁ ☁ WinBoLL Studio Android 应用开源项目。☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁
# ☁ ☁ ☁ WinBoll 网站地址 https://www.winboll.cc/studio/app/ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ # ☁ ☁ ☁ WinBoLL 网站地址 https://www.winboll.cc/ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁
## WinBoll 提问 ## WinBoLL 提问
同样是 /sdcard 目录,在开发 Android 应用时, 同样是 /sdcard 目录,在开发 Android 应用时,
能否实现手机编译与电脑编译的源码同步。 能否实现手机编译与电脑编译的源码同步。
☁因而 WinBoll 项目组诞生了。 ☁因而 WinBoLL 项目组诞生了。
## WinBoll 项目组研发计划 ## WinBoLL 项目组研发计划
致力于把 WinBoll-APP 应用在手机端 Android 项目开发。 致力于把 WinBoLL-APP 应用在手机端 Android 项目开发。
也在探索 https://gitea.winboll.cc/<WinBoll 项目组>/APP.git 应用于 WinBoll-APP APK 分发。 也在探索 https://gitea.winboll.cc/<WinBoLL 项目组>/APP.git 应用于 WinBoLL-APP APK 分发。
更想进阶 https://github.com/<WinBoll 项目组>/APP.git 应用于 WinBoll-APP Beta APK 分发。 更想进阶 https://github.com/<WinBoLL 项目组>/APP.git 应用于 WinBoLL-APP Beta APK 分发。
## WinBoll-APP 汗下... ## WinBoLL-APP 汗下...
#### ☁应用何置如此呢。且观用户云云。 #### ☁应用何置如此呢。且观用户云云。
#### ☁ 正当下 ☁ ### #### ☁ 正当下 ☁ ###
#### ☁ 且容傻家叙说 ☁ WinBoll-APP 应用场景 #### ☁ 且容傻家叙说 ☁ WinBoLL-APP 应用场景
### ☁ WinBoll 设备资源概述 ### ☁ WinBoLL 设备资源概述
#### ☁ 1. Raid Disk. #### ☁ 1. Raid Disk.
概述:这是一个矩阵存储类设备。 概述:这是一个矩阵存储类设备。
优点:该设备具有数据容错存储功能, 优点:该设备具有数据容错存储功能,
@@ -40,74 +40,79 @@
设备位于操作系统内部文件系统。 设备位于操作系统内部文件系统。
数据持久性与操作系统挂钩。 数据持久性与操作系统挂钩。
#### ☁ 4. WinBoll 用户资源概述。 #### ☁ 4. WinBoLL 用户资源概述。
1> /home/<用户名> 位于 WinBoll 操作系统目录下。 1> /home/<用户名> 位于 WinBoLL 操作系统目录下。
2> /rdisk/<用户名> 挂载用户 Raid Disk. 2> /rdisk/<用户名> 挂载用户 Raid Disk.
3> /data/<用户名> 挂载用户 Data Disk. 3> /data/<用户名> 挂载用户 Data Disk.
4> /sdcard/<用户名> 挂载用户 SSD Disk. 4> /sdcard/<用户名> 挂载用户 SSD Disk.
#### ☁ 5. WinBoll-APP 用户资源概述。 #### ☁ 5. WinBoLL-APP 用户资源概述。
1> /sdcard 挂载用户手机 SD 存储/storage/emulated/0 1> /sdcard 挂载用户手机 SD 存储/storage/emulated/0
### ☁ 稍稍歇 ☁ ### ### ☁ 稍稍歇 ☁ ###
### ☁ 急急停 ☁ WinBoll 应用前置条件 ### ☁ 急急停 ☁ WinBoLL 应用前置条件
☁ WinBoll 主机建立 1Panel MySQL 应用。 ☁ WinBoLL 主机建立 1Panel MySQL 应用。
☁ WinBoll 主机建立 1Panel Gitea 应用。 ☁ WinBoLL 主机建立 1Panel Gitea 应用。
☁ WinBoll 主机设置 WinBoll 应用为非登录状态。 ☁ WinBoLL 主机设置 WinBoLL 应用为非登录状态。
☁ WinBoll 主机建立 WinBoll 账户与 WinBoll 用户组。 ☁ WinBoLL 主机建立 WinBoLL 账户与 WinBoLL 用户组。
☁ WinBoll 账户 User ID 为: J。 ☁ WinBoLL 账户 User ID 为: J。
☁ WinBoll 用户组 Group ID 为: Studio。 ☁ WinBoLL 用户组 Group ID 为: Studio。
☁ WinBoll 主机 WinBoll 1Panel Gitea 建立 WinBoll 工作组。 ☁ WinBoLL 主机 WinBoLL 1Panel Gitea 建立 WinBoLL 工作组。
☁ WinBoll 主机 WinBoll 1Panel Gitea 用户项目 APK 编译输出目录为 /sdcard/WinBollStudio/<用户名>/APKs/ ☁ WinBoLL 主机 WinBoLL 1Panel Gitea 用户项目 APK 编译输出目录为 /sdcard/WinBoLLStudio/<用户名>/APKs/
☁ WinBoll 项目配置文件示例为 "<WinBoll 项目根目录>/.winboll/winboll.properties-demo"(WinBoll 项目已设置) ☁ WinBoLL 项目配置文件示例为 "<WinBoLL 项目根目录>/.winboll/winboll.properties-demo"(WinBoLL 项目已设置)
☁ WinBoll 项目配置文件为 "<WinBoll 项目根目录>/.winboll/winboll.properties" ☁ WinBoLL 项目配置文件为 "<WinBoLL 项目根目录>/.winboll/winboll.properties"
☁ WinBoll 项目配置文件设定为源码提交时忽略。(WinBoll 项目已设置) ☁ WinBoLL 项目配置文件设定为源码提交时忽略。(WinBoLL 项目已设置)
☁ Gradle 项目配置文件示例为 "<WinBoll 项目根目录>/.winboll/local.properties-demo"(WinBoll 项目已设置) ☁ Gradle 项目配置文件示例为 "<WinBoLL 项目根目录>/.winboll/local.properties-demo"(WinBoLL 项目已设置)
☁ Gradle 项目配置文件为 "<WinBoll 项目根目录>/local.properties"(WinBoll 项目已设置) ☁ Gradle 项目配置文件为 "<WinBoLL 项目根目录>/local.properties"(WinBoLL 项目已设置)
☁ Gradle 项目配置文件设定为源码提交时忽略。(WinBoll 项目已设置) ☁ Gradle 项目配置文件设定为源码提交时忽略。(WinBoLL 项目已设置)
### ☁ 登高处 ☁ WinBoll 应用需求规划 ### ☁ 登高处 ☁ WinBoLL 应用需求规划
☁ WinBoll 主机建立 WinBoll 客户端用户数据库为 MySQL winbollclient 数据库。 ☁ WinBoLL 主机建立 WinBoLL 客户端用户数据库为 MySQL winbollclient 数据库。
☁ WinBoll 主机设置 WinBoll 客户端用户信息存储在 winbollclient 数据库中。 ☁ WinBoLL 主机设置 WinBoLL 客户端用户信息存储在 winbollclient 数据库中。
☁ MySQL winbollclient 数据库中 ☁ MySQL winbollclient 数据库中
WinBoll 客户端用户信息设定为: WinBoLL 客户端用户信息设定为:
<用户名, 验证密码, 验证邮箱, 验证手机, 唯一存储令牌Token, 备用验证邮箱>。 <用户名, 验证密码, 验证邮箱, 验证手机, 唯一存储令牌Token, 备用验证邮箱>。
☁ WinBoll 项目源码仓库托管在 WinBoll 1Panel Gitea 目录 /opt/1panel/apps/gitea/gitea/data/git/repositories/studio/app.git中。 ☁ WinBoLL 项目源码仓库托管在 WinBoLL 1Panel Gitea 目录 /opt/1panel/apps/gitea/gitea/data/git/repositories/studio/app.git中。
☁ WinBoll 主机提供 WinBoll 1Panel Gitea 应用的 WinBoll 项目源码仓库存取功能。Gitea 应用已提供) ☁ WinBoLL 主机提供 WinBoLL 1Panel Gitea 应用的 WinBoLL 项目源码仓库存取功能。Gitea 应用已提供)
☁ WinBoll 主机提供 WinBoll Gitea 项目仓库存档功能。Gitea 应用已提供) ☁ WinBoLL 主机提供 WinBoLL Gitea 项目仓库存档功能。Gitea 应用已提供)
☁ 提供 WinBoll 客户端用户登录功能。Gitea 应用已提供) ☁ 提供 WinBoLL 客户端用户登录功能。Gitea 应用已提供)
### ☁ 看远方 ☁ ### ### ☁ 看远方 ☁ ###
### ☁ 心忧虑 ☁ WinBoll-APP 应用前置需求 ### ☁ 心忧虑 ☁ WinBoLL-APP 应用前置需求
☁ WinBoll-APP WinBoll 项目根目录设定为手机的 /sdcard/WinBollStudio/Sources 目录。(需要用户手动建立文件夹) ☁ WinBoLL-APP WinBoLL 项目根目录设定为手机的 /sdcard/WinBoLLStudio/Sources 目录。(需要用户手动建立文件夹)
☁ WinBoll-APP 具有手机 /sdcard/WinBoll 目录的存储权限。(需要手机操作系统授权) ☁ WinBoLL-APP 具有手机 /sdcard/WinBoLL 目录的存储权限。(需要手机操作系统授权)
☁ WinBoll-APP WinBoll 项目仓库源码存储路径为 /sdcard/WinBollStudio/Sources/APP.git需要用户手动建立文件夹 ☁ WinBoLL-APP WinBoLL 项目仓库源码存储路径为 /sdcard/WinBoLLStudio/Sources/APP.git需要用户手动建立文件夹
☁ WinBoll-APP 项目 APK 编译输出目录为 /sdcard/WinBollStudio/APKs/ ☁ WinBoLL-APP 项目 APK 编译输出目录为 /sdcard/WinBoLLStudio/APKs/
☁ WinBoll-APP 应用签名验证可定制化。WinBoll 项目已提供) ☁ WinBoLL-APP 应用签名验证可定制化。WinBoLL 项目已提供)
☁ WinBoll-APP 与系列衍生 APP 应用共享 cc.winboll.studio 命名空间资源。WinBoll 项目已提供) ☁ WinBoLL-APP 与系列衍生 APP 应用共享 cc.winboll.studio 命名空间资源。WinBoLL 项目已提供)
☁ WinBoll-APP 用户客户端信息存储在命名空间为 WinBoll APP MySQLLite 应用的 winbollappclient 数据库中。 ☁ WinBoLL-APP 用户客户端信息存储在命名空间为 WinBoLL APP MySQLLite 应用的 winbollappclient 数据库中。
☁ WinBoll-APP MySQLLite 应用的 winbollappclient 数据库中, ☁ WinBoLL-APP MySQLLite 应用的 winbollappclient 数据库中,
WinBoll 用户客户端信息设定为: WinBoLL 用户客户端信息设定为:
<用户名, 唯一存储令牌Token>。 <用户名, 唯一存储令牌Token>。
### ☁ 云游四方 ☁ ### ### ☁ 云游四方 ☁ ###
### ☁ 呔! ☁ WinBoll-APP 应用需求规划 ### ☁ 呔! ☁ WinBoLL-APP 应用需求规划
WinBoll-APP 提供手机目录 /sdcard/WinBollStudio/Sources 的 WinBoll 项目源码管理功能。 如要使用 WinBoLL Android 项目的 Gradle 编译功能,则需要设置以下两个文件夹。
☁ 1. 则需要建立数据存储目录 /sdcard/WinBoLLStudio/APKs。
WinBoLL 项目源码编译出来的安装包会拷贝一份到 /sdcard/WinBoLLStudio/APKs 目录下。
☁ 2. 则需要建立数据存储目录 /sdcard/AppProjects。
WinBoLL 项目源码编译出来的安装包会拷贝一份并命名 "app.apk" 的安装文件为到 /sdcard/AppProjects 目录下。
### ☁ 吁! ☁ WinBoll-APP 共享计划前景
☁ WinBoll-APP 将会实现 https://winboll.cc/api 访问功能。
☁ WinBoll-APP 将会实现手机端 Android 应用的开发与管理功能。
## ☁ WinBoll ☁ WinBoll 主机忧虑 ###吁! ☁ WinBoLL-APP 共享计划前景
☁ WinBoll 将会提供 gitea.winboll.cc 域名用户注册登录功能。 ☁ WinBoLL-APP 将会实现 https://winboll.cc/api 访问功能。
☁ WinBoll 将会提供 WinBoll-APP 及其衍生应用的 Gitea 仓库管理服务 ☁ WinBoLL-APP 将会实现手机端 Android 应用的开发与管理功能
☁ WinBoll 将会提供 winboll.cc 域名 WinBoll 项目组注册登录功能。
## ☁ WinBoLL ☁ WinBoLL 主机忧虑
☁ WinBoLL 将会提供 gitea.winboll.cc 域名用户注册登录功能。
☁ WinBoLL 将会提供 WinBoLL-APP 及其衍生应用的 Gitea 仓库管理服务。
☁ WinBoLL 将会提供 winboll.cc 域名 WinBoLL 项目组注册登录功能。
# 本项目要实际运用需要注意以下几个步骤: # 本项目要实际运用需要注意以下几个步骤:
# 在项目根目录下: # 在项目根目录下:
## 1. 项目模块编译环境设置(必须)settings.gradle-demo 要复制为 settings.gradle并取消相应项目模块的注释。 ## 1. 项目模块编译环境设置(必须)settings.gradle-demo 要复制为 settings.gradle并取消相应项目模块的注释。
## 2. 项目 Android SDK 编译环境设置(可选)local.properties-demo 要复制为 local.properties并按需要设置 Android SDK 目录。 ## 2. 项目 Android SDK 编译环境设置(可选)local.properties-demo 要复制为 local.properties并按需要设置 Android SDK 目录。
## 3. 类库型模块编译环境设置(可选)winboll.properties-demo 要复制为 winboll.properties并按需要设置 WinBoll Maven 库登录用户信息。 ## 3. 类库型模块编译环境设置(可选)winboll.properties-demo 要复制为 winboll.properties并按需要设置 WinBoLL Maven 库登录用户信息。
# ☆类库型项目编译方法 # ☆类库型项目编译方法
@@ -116,12 +121,12 @@
设置属性 libraryProject=<类库项目模块文件夹名称> 设置属性 libraryProject=<类库项目模块文件夹名称>
### 再编译测试项目 ### 再编译测试项目
$ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称> $ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称>
#### 测试项目编译后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBollStudio/APKs/<项目根目录名称>/tag/" 文件夹。 #### 测试项目编译后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
### 最后编译类库项目 ### 最后编译类库项目
$ bash .winboll/bashPublishLIBAddTag.sh <类库项目模块文件夹名称> $ bash .winboll/bashPublishLIBAddTag.sh <类库项目模块文件夹名称>
#### 类库模块编译命令执行后,编译器会发布到 WinBoll Nexus Maven 库Maven 库地址可以参阅根项目目录配置 build.gradle 文件。 #### 类库模块编译命令执行后,编译器会发布到 WinBoLL Nexus Maven 库Maven 库地址可以参阅根项目目录配置 build.gradle 文件。
# ☆应用型项目编译方法 # ☆应用型项目编译方法
## 直接调用以下命令编译应用型项目 ## 直接调用以下命令编译应用型项目
$ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称> $ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称>
#### 应用模块编译命令执行后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBollStudio/APKs/<项目根目录名称>/tag/" 文件夹。 #### 应用模块编译命令执行后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。

View File

@@ -24,12 +24,12 @@ android {
defaultConfig { defaultConfig {
applicationId "cc.winboll.studio.aes" applicationId "cc.winboll.studio.aes"
minSdkVersion 24 minSdkVersion 24
targetSdkVersion 29 targetSdkVersion 30
versionCode 1 versionCode 1
// versionName 更新后需要手动设置 // versionName 更新后需要手动设置
// 项目模块目录的 build.gradle 文件的 stageCount=0 // 项目模块目录的 build.gradle 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "15.2" versionName "15.6"
if(true) { if(true) {
versionName = genVersionName("${versionName}") versionName = genVersionName("${versionName}")
} }

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Thu Apr 03 11:14:05 HKT 2025 #Mon May 12 18:10:35 HKT 2025
stageCount=7 stageCount=4
libraryProject=libaes libraryProject=libaes
baseVersion=15.2 baseVersion=15.6
publishVersion=15.2.6 publishVersion=15.6.3
buildCount=0 buildCount=0
baseBetaVersion=15.2.7 baseBetaVersion=15.6.4

View File

@@ -15,9 +15,9 @@ import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libaes.winboll.APPInfo; import cc.winboll.studio.libaes.winboll.APPInfo;
import cc.winboll.studio.libaes.winboll.AboutView; import cc.winboll.studio.libaes.winboll.AboutView;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity; import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
public class AboutActivity extends WinBollActivity implements IWinBollActivity { public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity {
public static final String TAG = "AboutActivity"; public static final String TAG = "AboutActivity";
@@ -64,13 +64,13 @@ public class AboutActivity extends WinBollActivity implements IWinBollActivity {
); );
layout.addView(aboutView, params); layout.addView(aboutView, params);
GlobalApplication.getWinBollActivityManager().add(this); GlobalApplication.getWinBoLLActivityManager().add(this);
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
GlobalApplication.getWinBollActivityManager().registeRemove(this); GlobalApplication.getWinBoLLActivityManager().registeRemove(this);
} }
public AboutView CreateAboutView() { public AboutView CreateAboutView() {

View File

@@ -8,6 +8,7 @@ package cc.winboll.studio.aes;
import android.view.Gravity; import android.view.Gravity;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
import com.hjq.toast.ToastUtils; import com.hjq.toast.ToastUtils;
import com.hjq.toast.style.WhiteToastStyle;
public class App extends GlobalApplication { public class App extends GlobalApplication {
@@ -21,8 +22,8 @@ public class App extends GlobalApplication {
// 初始化 Toast 框架 // 初始化 Toast 框架
ToastUtils.init(this); ToastUtils.init(this);
// 设置 Toast 布局样式 // 设置 Toast 布局样式
ToastUtils.setView(R.layout.view_toast); //ToastUtils.setView(R.layout.view_toast);
//ToastUtils.setStyle(new WhiteToastStyle()); ToastUtils.setStyle(new WhiteToastStyle());
ToastUtils.setGravity(Gravity.BOTTOM, 0, 200); ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
} }

View File

@@ -25,12 +25,12 @@ import cc.winboll.studio.libaes.unittests.TestAToolbarActivity;
import cc.winboll.studio.libaes.unittests.TestDrawerFragmentActivity; import cc.winboll.studio.libaes.unittests.TestDrawerFragmentActivity;
import cc.winboll.studio.libaes.unittests.TestViewPageFragment; import cc.winboll.studio.libaes.unittests.TestViewPageFragment;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity; import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
import com.a4455jkjh.colorpicker.ColorPickerDialog; import com.a4455jkjh.colorpicker.ColorPickerDialog;
import com.hjq.toast.ToastUtils; import com.hjq.toast.ToastUtils;
import java.util.ArrayList; import java.util.ArrayList;
public class MainActivity extends DrawerFragmentActivity implements IWinBollActivity { public class MainActivity extends DrawerFragmentActivity implements IWinBoLLActivity {
public static final String TAG = "MainActivity"; public static final String TAG = "MainActivity";
@@ -123,7 +123,7 @@ public class MainActivity extends DrawerFragmentActivity implements IWinBollActi
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
int nItemId = item.getItemId(); int nItemId = item.getItemId();
// if (item.getItemId() == R.id.item_log) { // if (item.getItemId() == R.id.item_log) {
// WinBollActivityManager.getInstance(this).startWinBollActivity(getApplicationContext(), LogActivity.class); // WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(getApplicationContext(), LogActivity.class);
// } else // } else
if (nItemId == R.id.item_atoast) { if (nItemId == R.id.item_atoast) {
Toast.makeText(getApplication(), "item_testatoast", Toast.LENGTH_SHORT).show(); Toast.makeText(getApplication(), "item_testatoast", Toast.LENGTH_SHORT).show();

View File

@@ -25,12 +25,12 @@ android {
defaultConfig { defaultConfig {
applicationId "cc.winboll.studio.appbase" applicationId "cc.winboll.studio.appbase"
minSdkVersion 24 minSdkVersion 24
targetSdkVersion 29 targetSdkVersion 30
versionCode 1 versionCode 1
// versionName 更新后需要手动设置 // versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0 // .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "15.2" versionName "15.7"
if(true) { if(true) {
versionName = genVersionName("${versionName}") versionName = genVersionName("${versionName}")
} }

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Sat Apr 12 15:06:52 HKT 2025 #Sat May 03 10:32:21 GMT 2025
stageCount=6 stageCount=7
libraryProject=libappbase libraryProject=libappbase
baseVersion=15.2 baseVersion=15.7
publishVersion=15.2.5 publishVersion=15.7.6
buildCount=0 buildCount=4
baseBetaVersion=15.2.6 baseBetaVersion=15.7.7

View File

@@ -5,7 +5,7 @@
<application <application
android:name=".App" android:name=".App"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_miapp"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/MyAPPBaseTheme" android:theme="@style/MyAPPBaseTheme"
android:resizeableActivity="true" android:resizeableActivity="true"

View File

@@ -4,11 +4,11 @@ import android.app.Activity;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.widget.Toolbar;
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.CheckBox;
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.appbase.activities.NewActivity;
import cc.winboll.studio.appbase.services.MainService; import cc.winboll.studio.appbase.services.MainService;
@@ -21,10 +21,9 @@ import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.sos.SOS; import cc.winboll.studio.libappbase.sos.SOS;
import cc.winboll.studio.libappbase.utils.ToastUtils; import cc.winboll.studio.libappbase.utils.ToastUtils;
import cc.winboll.studio.libappbase.widgets.StatusWidget; import cc.winboll.studio.libappbase.widgets.StatusWidget;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity; import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
public class MainActivity extends WinBollActivityBase implements IWinBollActivity { public class MainActivity extends WinBoLLActivityBase implements IWinBoLLActivity {
public static final String TAG = "MainActivity"; public static final String TAG = "MainActivity";
@@ -48,7 +47,7 @@ public class MainActivity extends WinBollActivityBase implements IWinBollActivit
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
mToolbar = findViewById(R.id.toolbar); mToolbar = findViewById(R.id.toolbar);
setSupportActionBar(mToolbar); setActionBar(mToolbar);
CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1); CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
cbIsDebugMode.setChecked(GlobalApplication.isDebuging()); cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
@@ -177,7 +176,7 @@ public class MainActivity extends WinBollActivityBase implements IWinBollActivit
} }
public void onTestOpenNewActivity(View view) { public void onTestOpenNewActivity(View view) {
GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, NewActivity.class); GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, NewActivity.class);
} }

View File

@@ -7,16 +7,16 @@ package cc.winboll.studio.appbase.activities;
*/ */
import android.app.Activity; import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.widget.Toolbar;
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.Toolbar;
import cc.winboll.studio.appbase.R; import cc.winboll.studio.appbase.R;
import cc.winboll.studio.appbase.WinBollActivityBase; import cc.winboll.studio.appbase.WinBoLLActivityBase;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity; import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
public class New2Activity extends WinBollActivityBase implements IWinBollActivity { public class New2Activity extends WinBoLLActivityBase implements IWinBoLLActivity {
public static final String TAG = "New2Activity"; public static final String TAG = "New2Activity";
@@ -41,7 +41,7 @@ public class New2Activity extends WinBollActivityBase implements IWinBollActivit
// mLogView = findViewById(R.id.logview); // mLogView = findViewById(R.id.logview);
// mLogView.start(); // mLogView.start();
mToolbar = findViewById(R.id.toolbar); mToolbar = findViewById(R.id.toolbar);
setSupportActionBar(mToolbar); setActionBar(mToolbar);
} }
@@ -52,15 +52,15 @@ public class New2Activity extends WinBollActivityBase implements IWinBollActivit
} }
public void onCloseThisActivity(View view) { public void onCloseThisActivity(View view) {
GlobalApplication.getWinBollActivityManager().finish(this); GlobalApplication.getWinBoLLActivityManager().finish(this);
} }
public void onCloseAllActivity(View view) { public void onCloseAllActivity(View view) {
GlobalApplication.getWinBollActivityManager().finishAll(); GlobalApplication.getWinBoLLActivityManager().finishAll();
} }
public void onNewActivity(View view) { public void onNewActivity(View view) {
GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, NewActivity.class); GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, NewActivity.class);
} }
@@ -74,7 +74,7 @@ public class New2Activity extends WinBollActivityBase implements IWinBollActivit
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) { if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
GlobalApplication.getWinBollActivityManager().startLogActivity(this); GlobalApplication.getWinBoLLActivityManager().startLogActivity(this);
return true; return true;
} }
// 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。 // 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。

View File

@@ -6,16 +6,16 @@ package cc.winboll.studio.appbase.activities;
*/ */
import android.app.Activity; import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.widget.Toolbar;
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.Toolbar;
import cc.winboll.studio.appbase.R; import cc.winboll.studio.appbase.R;
import cc.winboll.studio.appbase.WinBollActivityBase; import cc.winboll.studio.appbase.WinBoLLActivityBase;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity; import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
public class NewActivity extends WinBollActivityBase implements IWinBollActivity { public class NewActivity extends WinBoLLActivityBase implements IWinBoLLActivity {
public static final String TAG = "NewActivity"; public static final String TAG = "NewActivity";
@@ -39,7 +39,7 @@ public class NewActivity extends WinBollActivityBase implements IWinBollActivity
// mLogView = findViewById(R.id.logview); // mLogView = findViewById(R.id.logview);
// mLogView.start(); // mLogView.start();
mToolbar = findViewById(R.id.toolbar); mToolbar = findViewById(R.id.toolbar);
setSupportActionBar(mToolbar); setActionBar(mToolbar);
} }
@@ -50,15 +50,15 @@ public class NewActivity extends WinBollActivityBase implements IWinBollActivity
} }
public void onCloseThisActivity(View view) { public void onCloseThisActivity(View view) {
GlobalApplication.getWinBollActivityManager().finish(this); GlobalApplication.getWinBoLLActivityManager().finish(this);
} }
public void onCloseAllActivity(View view) { public void onCloseAllActivity(View view) {
GlobalApplication.getWinBollActivityManager().finishAll(); GlobalApplication.getWinBoLLActivityManager().finishAll();
} }
public void onNew2Activity(View view) { public void onNew2Activity(View view) {
GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, New2Activity.class); GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, New2Activity.class);
} }
@@ -72,7 +72,7 @@ public class NewActivity extends WinBollActivityBase implements IWinBollActivity
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) { if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
GlobalApplication.getWinBollActivityManager().startLogActivity(this); GlobalApplication.getWinBoLLActivityManager().startLogActivity(this);
return true; return true;
} }
// 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。 // 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。

View File

@@ -10,7 +10,7 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import cc.winboll.studio.appbase.models.WinBollNewsBean; import cc.winboll.studio.appbase.models.WinBoLLNewsBean;
import cc.winboll.studio.appbase.services.MainService; import cc.winboll.studio.appbase.services.MainService;
import cc.winboll.studio.appbase.widgets.APPNewsWidget; import cc.winboll.studio.appbase.widgets.APPNewsWidget;
import cc.winboll.studio.libappbase.AppUtils; import cc.winboll.studio.libappbase.AppUtils;
@@ -18,7 +18,7 @@ import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.sos.APPModel; import cc.winboll.studio.libappbase.sos.APPModel;
import cc.winboll.studio.libappbase.sos.SOS; import cc.winboll.studio.libappbase.sos.SOS;
import cc.winboll.studio.libappbase.sos.SOSObject; import cc.winboll.studio.libappbase.sos.SOSObject;
import cc.winboll.studio.libappbase.sos.WinBoll; import cc.winboll.studio.libappbase.sos.WinBoLL;
import cc.winboll.studio.libappbase.utils.ToastUtils; import cc.winboll.studio.libappbase.utils.ToastUtils;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@@ -42,11 +42,11 @@ public class MainReceiver extends BroadcastReceiver {
String szAction = intent.getAction(); String szAction = intent.getAction();
if (szAction.equals(ACTION_BOOT_COMPLETED)) { if (szAction.equals(ACTION_BOOT_COMPLETED)) {
ToastUtils.show("ACTION_BOOT_COMPLETED"); ToastUtils.show("ACTION_BOOT_COMPLETED");
} else if (szAction.equals(WinBoll.ACTION_BIND)) { } else if (szAction.equals(WinBoLL.ACTION_BIND)) {
LogUtils.d(TAG, "ACTION_BIND"); LogUtils.d(TAG, "ACTION_BIND");
LogUtils.d(TAG, String.format("context.getPackageName() %s", context.getPackageName())); LogUtils.d(TAG, String.format("context.getPackageName() %s", context.getPackageName()));
LogUtils.d(TAG, String.format("intent.getAction() %s", intent.getAction())); LogUtils.d(TAG, String.format("intent.getAction() %s", intent.getAction()));
String szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL); String szAPPModel = intent.getStringExtra(WinBoLL.EXTRA_APPMODEL);
LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel)); LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel));
if (szAPPModel != null && !szAPPModel.equals("")) { if (szAPPModel != null && !szAPPModel.equals("")) {
try { try {
@@ -80,7 +80,7 @@ public class MainReceiver extends BroadcastReceiver {
String appName = AppUtils.getAppNameByPackageName(context, szObjectPackageName); String appName = AppUtils.getAppNameByPackageName(context, szObjectPackageName);
LogUtils.d(TAG, String.format("appName %s", appName)); LogUtils.d(TAG, String.format("appName %s", appName));
WinBollNewsBean appWinBollNewsBean = new WinBollNewsBean(appName); WinBoLLNewsBean appWinBoLLNewsBean = new WinBoLLNewsBean(appName);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String currentTime = sdf.format(new Date()); String currentTime = sdf.format(new Date());
StringBuilder sbLine = new StringBuilder(); StringBuilder sbLine = new StringBuilder();
@@ -88,9 +88,9 @@ public class MainReceiver extends BroadcastReceiver {
sbLine.append(currentTime); sbLine.append(currentTime);
sbLine.append("] Power to "); sbLine.append("] Power to ");
sbLine.append(appName); sbLine.append(appName);
appWinBollNewsBean.setMessage(sbLine.toString()); appWinBoLLNewsBean.setMessage(sbLine.toString());
APPNewsWidget.addWinBollNewsBean(context, appWinBollNewsBean); APPNewsWidget.addWinBoLLNewsBean(context, appWinBoLLNewsBean);
Intent intentWidget = new Intent(context, APPNewsWidget.class); Intent intentWidget = new Intent(context, APPNewsWidget.class);
intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT); intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
@@ -110,7 +110,7 @@ public class MainReceiver extends BroadcastReceiver {
IntentFilter filter=new IntentFilter(); IntentFilter filter=new IntentFilter();
filter.addAction(ACTION_BOOT_COMPLETED); filter.addAction(ACTION_BOOT_COMPLETED);
filter.addAction(SOS.ACTION_SOS); filter.addAction(SOS.ACTION_SOS);
filter.addAction(WinBoll.ACTION_BIND); filter.addAction(WinBoLL.ACTION_BIND);
//filter.addAction(Intent.ACTION_BATTERY_CHANGED); //filter.addAction(Intent.ACTION_BATTERY_CHANGED);
service.registerReceiver(this, filter); service.registerReceiver(this, filter);
} }

View File

@@ -12,7 +12,7 @@ import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import cc.winboll.studio.appbase.models.TestDemoBindServiceBean; import cc.winboll.studio.appbase.models.TestDemoBindServiceBean;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.sos.WinBoll; import cc.winboll.studio.libappbase.sos.WinBoLL;
import cc.winboll.studio.appbase.App; import cc.winboll.studio.appbase.App;
import cc.winboll.studio.libappbase.sos.SOS; import cc.winboll.studio.libappbase.sos.SOS;
@@ -156,9 +156,9 @@ public class TestDemoBindService extends Service {
super.run(); super.run();
LogUtils.d(TAG, "run() start"); LogUtils.d(TAG, "run() start");
if (App.isDebuging()) { if (App.isDebuging()) {
WinBoll.bindToAPPBaseBeta(mContext, TestDemoBindService.class.getName()); WinBoLL.bindToAPPBaseBeta(mContext, TestDemoBindService.class.getName());
} else { } else {
WinBoll.bindToAPPBase(mContext, TestDemoBindService.class.getName()); WinBoLL.bindToAPPBase(mContext, TestDemoBindService.class.getName());
} }
while (!isExit()) { while (!isExit()) {

View File

@@ -12,7 +12,7 @@ import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import cc.winboll.studio.appbase.models.TestDemoServiceBean; import cc.winboll.studio.appbase.models.TestDemoServiceBean;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.sos.WinBoll; import cc.winboll.studio.libappbase.sos.WinBoLL;
public class TestDemoService extends Service { public class TestDemoService extends Service {

View File

@@ -12,12 +12,12 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import cc.winboll.studio.appbase.R; import cc.winboll.studio.appbase.R;
import cc.winboll.studio.appbase.models.WinBollNewsBean; import cc.winboll.studio.appbase.models.WinBoLLNewsBean;
import cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener; import cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener;
import cc.winboll.studio.libappbase.AppUtils; import cc.winboll.studio.libappbase.AppUtils;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.sos.APPModel; import cc.winboll.studio.libappbase.sos.APPModel;
import cc.winboll.studio.libappbase.sos.WinBoll; import cc.winboll.studio.libappbase.sos.WinBoLL;
import java.io.IOException; import java.io.IOException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
@@ -31,14 +31,14 @@ public class APPNewsWidget extends AppWidgetProvider {
public static final String ACTION_RELOAD_REPORT = APPNewsWidget.class.getName() + ".ACTION_RELOAD_REPORT"; public static final String ACTION_RELOAD_REPORT = APPNewsWidget.class.getName() + ".ACTION_RELOAD_REPORT";
volatile static ArrayList<WinBollNewsBean> _WinBollNewsBeanList; volatile static ArrayList<WinBoLLNewsBean> _WinBoLLNewsBeanList;
final static int _MAX_PAGES = 10; final static int _MAX_PAGES = 10;
final static int _OnePageLinesCount = 5; final static int _OnePageLinesCount = 5;
volatile static int _CurrentPageIndex = 0; volatile static int _CurrentPageIndex = 0;
@Override @Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
initWinBollNewsBeanList(context); initWinBoLLNewsBeanList(context);
for (int appWidgetId : appWidgetIds) { for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId); updateAppWidget(context, appWidgetManager, appWidgetId);
} }
@@ -47,7 +47,7 @@ public class APPNewsWidget extends AppWidgetProvider {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent); super.onReceive(context, intent);
initWinBollNewsBeanList(context); initWinBoLLNewsBeanList(context);
if (intent.getAction().equals(ACTION_RELOAD_REPORT)) { if (intent.getAction().equals(ACTION_RELOAD_REPORT)) {
LogUtils.d(TAG, "ACTION_RELOAD_REPORT"); LogUtils.d(TAG, "ACTION_RELOAD_REPORT");
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
@@ -57,7 +57,7 @@ public class APPNewsWidget extends AppWidgetProvider {
} }
}else if (intent.getAction().equals(ACTION_WAKEUP_SERVICE)) { }else if (intent.getAction().equals(ACTION_WAKEUP_SERVICE)) {
LogUtils.d(TAG, "ACTION_WAKEUP_SERVICE"); LogUtils.d(TAG, "ACTION_WAKEUP_SERVICE");
String szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL); String szAPPModel = intent.getStringExtra(WinBoLL.EXTRA_APPMODEL);
LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel)); LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel));
if (szAPPModel != null && !szAPPModel.equals("")) { if (szAPPModel != null && !szAPPModel.equals("")) {
try { try {
@@ -71,7 +71,7 @@ public class APPNewsWidget extends AppWidgetProvider {
String appName = AppUtils.getAppNameByPackageName(context, szAppPackageName); String appName = AppUtils.getAppNameByPackageName(context, szAppPackageName);
LogUtils.d(TAG, String.format("appName %s", appName)); LogUtils.d(TAG, String.format("appName %s", appName));
WinBollNewsBean winBollNewsBean = new WinBollNewsBean(appName); WinBoLLNewsBean winBollNewsBean = new WinBoLLNewsBean(appName);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String currentTime = sdf.format(new Date()); String currentTime = sdf.format(new Date());
StringBuilder sbLine = new StringBuilder(); StringBuilder sbLine = new StringBuilder();
@@ -81,7 +81,7 @@ public class APPNewsWidget extends AppWidgetProvider {
sbLine.append(appName); sbLine.append(appName);
winBollNewsBean.setMessage(sbLine.toString()); winBollNewsBean.setMessage(sbLine.toString());
addWinBollNewsBean(context, winBollNewsBean); addWinBoLLNewsBean(context, winBollNewsBean);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class)); int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class));
@@ -99,24 +99,24 @@ public class APPNewsWidget extends AppWidgetProvider {
// //
// 加入新报告信息 // 加入新报告信息
// //
public synchronized static void addWinBollNewsBean(Context context, WinBollNewsBean bean) { public synchronized static void addWinBoLLNewsBean(Context context, WinBoLLNewsBean bean) {
initWinBollNewsBeanList(context); initWinBoLLNewsBeanList(context);
_WinBollNewsBeanList.add(0, bean); _WinBoLLNewsBeanList.add(0, bean);
// 控制记录总数 // 控制记录总数
while (_WinBollNewsBeanList.size() > _MAX_PAGES * _OnePageLinesCount) { while (_WinBoLLNewsBeanList.size() > _MAX_PAGES * _OnePageLinesCount) {
_WinBollNewsBeanList.remove(_WinBollNewsBeanList.size() - 1); _WinBoLLNewsBeanList.remove(_WinBoLLNewsBeanList.size() - 1);
} }
WinBollNewsBean.saveBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class); WinBoLLNewsBean.saveBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class);
} }
synchronized static void initWinBollNewsBeanList(Context context) { synchronized static void initWinBoLLNewsBeanList(Context context) {
if (_WinBollNewsBeanList == null) { if (_WinBoLLNewsBeanList == null) {
_WinBollNewsBeanList = new ArrayList<WinBollNewsBean>(); _WinBoLLNewsBeanList = new ArrayList<WinBoLLNewsBean>();
WinBollNewsBean.loadBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class); WinBoLLNewsBean.loadBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class);
} }
if (_WinBollNewsBeanList == null) { if (_WinBoLLNewsBeanList == null) {
_WinBollNewsBeanList = new ArrayList<WinBollNewsBean>(); _WinBoLLNewsBeanList = new ArrayList<WinBoLLNewsBean>();
WinBollNewsBean.saveBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class); WinBoLLNewsBean.saveBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class);
} }
} }
@@ -141,11 +141,11 @@ public class APPNewsWidget extends AppWidgetProvider {
public static String getMessage() { public static String getMessage() {
ArrayList<String> msgTemp = new ArrayList<String>(); ArrayList<String> msgTemp = new ArrayList<String>();
if (_WinBollNewsBeanList != null) { if (_WinBoLLNewsBeanList != null) {
int start = _OnePageLinesCount * _CurrentPageIndex; int start = _OnePageLinesCount * _CurrentPageIndex;
start = _WinBollNewsBeanList.size() > start ? start : _WinBollNewsBeanList.size() - 1; start = _WinBoLLNewsBeanList.size() > start ? start : _WinBoLLNewsBeanList.size() - 1;
for (int i = start, j = 0; i < _WinBollNewsBeanList.size() && j < _OnePageLinesCount && start > -1; i++, j++) { for (int i = start, j = 0; i < _WinBoLLNewsBeanList.size() && j < _OnePageLinesCount && start > -1; i++, j++) {
msgTemp.add(_WinBollNewsBeanList.get(i).getMessage()); msgTemp.add(_WinBoLLNewsBeanList.get(i).getMessage());
} }
String message = String.join("\n", msgTemp); String message = String.join("\n", msgTemp);
return message; return message;
@@ -154,7 +154,7 @@ public class APPNewsWidget extends AppWidgetProvider {
} }
public static void prePage(Context context) { public static void prePage(Context context) {
if (_WinBollNewsBeanList != null) { if (_WinBoLLNewsBeanList != null) {
if (_CurrentPageIndex > 0) { if (_CurrentPageIndex > 0) {
_CurrentPageIndex = _CurrentPageIndex - 1; _CurrentPageIndex = _CurrentPageIndex - 1;
} }
@@ -165,8 +165,8 @@ public class APPNewsWidget extends AppWidgetProvider {
} }
public static void nextPage(Context context) { public static void nextPage(Context context) {
if (_WinBollNewsBeanList != null) { if (_WinBoLLNewsBeanList != null) {
if ((_CurrentPageIndex + 1) * _OnePageLinesCount < _WinBollNewsBeanList.size()) { if ((_CurrentPageIndex + 1) * _OnePageLinesCount < _WinBoLLNewsBeanList.size()) {
_CurrentPageIndex = _CurrentPageIndex + 1; _CurrentPageIndex = _CurrentPageIndex + 1;
} }
Intent intentWidget = new Intent(context, APPNewsWidget.class); Intent intentWidget = new Intent(context, APPNewsWidget.class);
@@ -176,11 +176,11 @@ public class APPNewsWidget extends AppWidgetProvider {
} }
String getPageInfo() { String getPageInfo() {
if (_WinBollNewsBeanList == null) { if (_WinBoLLNewsBeanList == null) {
return "0/0"; return "0/0";
} }
int leftCount = _WinBollNewsBeanList.size() % _OnePageLinesCount; int leftCount = _WinBoLLNewsBeanList.size() % _OnePageLinesCount;
int currentPageCount = _WinBollNewsBeanList.size() / _OnePageLinesCount + (leftCount == 0 ?0: 1); int currentPageCount = _WinBoLLNewsBeanList.size() / _OnePageLinesCount + (leftCount == 0 ?0: 1);
return String.format("%d/%d", _CurrentPageIndex + 1, currentPageCount); return String.format("%d/%d", _CurrentPageIndex + 1, currentPageCount);
} }
} }

View File

@@ -5,7 +5,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.support.v7.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"/>
@@ -32,7 +32,7 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Hello, WinBoll!"/> android:text="Hello, WinBoLL!"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.support.v7.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"/>

View File

@@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.support.v7.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"/>

View File

@@ -18,7 +18,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/tv_title" android:id="@+id/tv_title"
android:layout_weight="1.0" android:layout_weight="1.0"
android:text="WinBollNews" android:text="WinBoLLNews"
android:textStyle="bold" android:textStyle="bold"
android:textSize="16sp"/> android:textSize="16sp"/>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">AppBase</string> <string name="app_name">AppBase</string>
<string name="tileservice_name">WinBoll</string> <string name="tileservice_name">WinBoLL</string>
</resources> </resources>

View File

@@ -24,12 +24,12 @@ android {
defaultConfig { defaultConfig {
applicationId "cc.winboll.studio.apputils" applicationId "cc.winboll.studio.apputils"
minSdkVersion 26 minSdkVersion 26
targetSdkVersion 29 targetSdkVersion 30
versionCode 1 versionCode 1
// versionName 更新后需要手动设置 // versionName 更新后需要手动设置
// 项目模块目录的 build.gradle 文件的 stageCount=0 // 项目模块目录的 build.gradle 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "15.2" versionName "15.3"
if(true) { if(true) {
versionName = genVersionName("${versionName}") versionName = genVersionName("${versionName}")
} }

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Sat Mar 29 12:14:55 HKT 2025 #Tue Apr 29 15:04:17 HKT 2025
stageCount=2 stageCount=5
libraryProject=libapputils libraryProject=libapputils
baseVersion=15.2 baseVersion=15.3
publishVersion=15.2.1 publishVersion=15.3.4
buildCount=0 buildCount=0
baseBetaVersion=15.2.2 baseBetaVersion=15.3.5

View File

@@ -15,12 +15,12 @@ import android.view.MenuItem;
import android.widget.Toolbar; import android.widget.Toolbar;
import cc.winboll.studio.apputils.R; import cc.winboll.studio.apputils.R;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity; import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
import cc.winboll.studio.libapputils.views.SimpleWebView; import cc.winboll.studio.libapputils.views.SimpleWebView;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
public class AssetsHtmlActivity extends WinBollActivityBase implements IWinBollActivity { public class AssetsHtmlActivity extends WinBoLLActivityBase implements IWinBoLLActivity {
@Override @Override
public Activity getActivity() { public Activity getActivity() {
@@ -57,7 +57,7 @@ public class AssetsHtmlActivity extends WinBollActivityBase implements IWinBollA
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
// if (item.getItemId() == android.R.id.home) { // if (item.getItemId() == android.R.id.home) {
// WinBollActivityManager.getInstance(this).finish(this); // WinBoLLActivityManager.getInstance(this).finish(this);
// } // }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }

View File

@@ -60,9 +60,9 @@ final public class MainActivity extends Activity {
//if (prosessIntents(getIntent())) return; //if (prosessIntents(getIntent())) return;
// 以下正常创建主窗口 // 以下正常创建主窗口
// // 设置 WinBoll 应用 UI 类型 // // 设置 WinBoLL 应用 UI 类型
// WinBollApplication.setWinBollUI_TYPE(WinBollApplication.WinBollUI_TYPE.Aplication); // WinBoLLApplication.setWinBoLLUI_TYPE(WinBoLLApplication.WinBoLLUI_TYPE.Aplication);
// //ToastUtils.show("WinBollUI_TYPE " + WinBollApplication.getWinBollUI_TYPE()); // //ToastUtils.show("WinBoLLUI_TYPE " + WinBoLLApplication.getWinBoLLUI_TYPE());
// LogUtils.d(TAG, "BuildConfig.DEBUG : " + Boolean.toString(BuildConfig.DEBUG)); // LogUtils.d(TAG, "BuildConfig.DEBUG : " + Boolean.toString(BuildConfig.DEBUG));
} }
@@ -77,7 +77,7 @@ final public class MainActivity extends Activity {
if (intent.getAction() != null) { if (intent.getAction() != null) {
// if (intent.getAction().equals(cc.winboll.studio.libapputils.intent.action.DEBUGVIEW)) { // if (intent.getAction().equals(cc.winboll.studio.libapputils.intent.action.DEBUGVIEW)) {
// App.setIsDebug(true); // App.setIsDebug(true);
// //ToastUtils.show!("WinBollApplication.setIsDebug(true) by action : " + intent.getAction()); // //ToastUtils.show!("WinBoLLApplication.setIsDebug(true) by action : " + intent.getAction());
// //
// } // }
} }
@@ -130,12 +130,12 @@ final public class MainActivity extends Activity {
protected void onPostCreate(Bundle savedInstanceState) { protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState); super.onPostCreate(savedInstanceState);
// 缓存当前 activity // 缓存当前 activity
//WinBollActivityManager.getInstance(this).add(this); //WinBoLLActivityManager.getInstance(this).add(this);
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
//WinBollActivityManager.getInstance(this).registeRemove(this); //WinBoLLActivityManager.getInstance(this).registeRemove(this);
super.onDestroy(); super.onDestroy();
} }
@@ -150,8 +150,8 @@ final public class MainActivity extends Activity {
// intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); // intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
// startActivity(intent); // startActivity(intent);
//WinBollActivityManager.getInstance().printAvtivityListInfo(); //WinBoLLActivityManager.getInstance().printAvtivityListInfo();
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, LogActivity.class); //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, LogActivity.class);
} }
// //
@@ -165,7 +165,7 @@ final public class MainActivity extends Activity {
// if (intent.getAction().equals(StringToQrCodeView.ACTION_UNITTEST_QRCODE)) { // if (intent.getAction().equals(StringToQrCodeView.ACTION_UNITTEST_QRCODE)) {
// try { // try {
// WinBollActivity clazzActivity = UnitTestActivity.class.newInstance(); // WinBoLLActivity clazzActivity = UnitTestActivity.class.newInstance();
// String tag = clazzActivity.getTag(); // String tag = clazzActivity.getTag();
// LogUtils.d(TAG, "String tag = clazzActivity.getTag(); tag " + tag); // LogUtils.d(TAG, "String tag = clazzActivity.getTag(); tag " + tag);
// Intent subIntent = new Intent(this, UnitTestActivity.class); // Intent subIntent = new Intent(this, UnitTestActivity.class);
@@ -183,8 +183,8 @@ final public class MainActivity extends Activity {
// } // }
// //
// Files.copy(Paths.get(szSrcPath), Paths.get(file.getPath())); // Files.copy(Paths.get(szSrcPath), Paths.get(file.getPath()));
// //startWinBollActivity(subIntent, tag); // //startWinBoLLActivity(subIntent, tag);
// WinBollActivityManager.getInstance(this).startWinBollActivity(this, subIntent, UnitTestActivity.class); // WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, subIntent, UnitTestActivity.class);
// } catch (IllegalAccessException | InstantiationException | IOException e) { // } catch (IllegalAccessException | InstantiationException | IOException e) {
// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); // LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
// // 函数处理异常返回失败 // // 函数处理异常返回失败
@@ -201,8 +201,8 @@ final public class MainActivity extends Activity {
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
//ToastUtils.show("onCreateOptionsMenu"); //ToastUtils.show("onCreateOptionsMenu");
getMenuInflater().inflate(R.menu.toolbar_main, menu); getMenuInflater().inflate(R.menu.toolbar_main, menu);
// if (isAddWinBollToolBar()) { // if (isAddWinBoLLToolBar()) {
// //ToastUtils.show("mIWinBoll.isAddWinBollToolBar()"); // //ToastUtils.show("mIWinBoLL.isAddWinBoLLToolBar()");
// getMenuInflater().inflate(R.menu.toolbar_winboll_shared_main, menu); // getMenuInflater().inflate(R.menu.toolbar_winboll_shared_main, menu);
// } // }
if (App.isDebuging()) { if (App.isDebuging()) {
@@ -220,7 +220,7 @@ final public class MainActivity extends Activity {
} else if (item.getItemId() == R.id.item_teststringtoqrcodeview) { } else if (item.getItemId() == R.id.item_teststringtoqrcodeview) {
Intent intent = new Intent(this, TestStringToQRCodeViewActivity.class); Intent intent = new Intent(this, TestStringToQRCodeViewActivity.class);
startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY); startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY);
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, TestStringToQrCodeViewActivity.class); //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, TestStringToQrCodeViewActivity.class);
} else if (item.getItemId() == R.id.item_testqrcodedecodeactivity) { } else if (item.getItemId() == R.id.item_testqrcodedecodeactivity) {
Intent intent = new Intent(this, QRCodeDecodeActivity.class); Intent intent = new Intent(this, QRCodeDecodeActivity.class);
startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY); startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY);
@@ -230,13 +230,13 @@ final public class MainActivity extends Activity {
} }
return true; return true;
} else if (item.getItemId() == R.id.item_log) { } else if (item.getItemId() == R.id.item_log) {
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, LogActivity.class); //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, LogActivity.class);
return true; return true;
} else if (item.getItemId() == R.id.item_exitdebug) { } else if (item.getItemId() == R.id.item_exitdebug) {
//AboutView.setApp2NormalMode(this); //AboutView.setApp2NormalMode(this);
return true; return true;
} else if (item.getItemId() == android.R.id.home) { } else if (item.getItemId() == android.R.id.home) {
//WinBollActivityManager.getInstance(this).finish(this); //WinBoLLActivityManager.getInstance(this).finish(this);
return true; return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
@@ -247,7 +247,7 @@ final public class MainActivity extends Activity {
// //
// @Override // @Override
// public void onYes() { // public void onYes() {
// //WinBollActivityManager.getInstance(getApplicationContext()).finishAll(); // //WinBoLLActivityManager.getInstance(getApplicationContext()).finishAll();
// } // }
// //
// @Override // @Override
@@ -260,10 +260,10 @@ final public class MainActivity extends Activity {
@Override @Override
public void onBackPressed() { public void onBackPressed() {
// if (WinBollActivityManager.getInstance(getApplicationContext()).isFirstIWinBollActivity(this)) { // if (WinBoLLActivityManager.getInstance(getApplicationContext()).isFirstIWinBoLLActivity(this)) {
// exit(); // exit();
// } else { // } else {
// WinBollActivityManager.getInstance(this).finish(this); // WinBoLLActivityManager.getInstance(this).finish(this);
// super.onBackPressed(); // super.onBackPressed();
// } // }
} }
@@ -275,7 +275,7 @@ final public class MainActivity extends Activity {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
startActivity(intent); startActivity(intent);
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, intent, AssetsHtmlActivity.class); //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, intent, AssetsHtmlActivity.class);
} }
@Override @Override

View File

@@ -1,36 +0,0 @@
package cc.winboll.studio.apputils;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2025/01/17 19:50:46
*/
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toolbar;
import cc.winboll.studio.apputils.R;
import cc.winboll.studio.libapputils.views.StringToQrCodeView;
public class TestStringToQRCodeViewActivity extends Activity {
public static final String TAG = "TestStringToQrCodeViewActivity";
StringToQrCodeView mStringToQrCodeView;
//
// @Override
// public Activity getActivity() {
// return this;
// }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_teststringtoqrcodeview);
// 初始化工具栏
Toolbar mToolbar = findViewById(R.id.toolbar);
mToolbar.setSubtitle(TAG);
setActionBar(mToolbar);
mStringToQrCodeView = findViewById(R.id.activityteststringtoqrcodeviewStringToQrCodeView1);
}
}

View File

@@ -1,52 +0,0 @@
package cc.winboll.studio.apputils;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/28 17:11:37
* @Describe 应用活动窗口基类
*/
import android.app.Activity;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
import cc.winboll.studio.libappbase.winboll.WinBollActivityManager;
public class WinBollActivityBase extends AppCompatActivity implements IWinBollActivity {
public static final String TAG = "WinBollActivityBase";
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
WinBollActivityManager getWinBollActivityManager() {
return WinBollActivityManager.getInstance(GlobalApplication.getInstance());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWinBollActivityManager().add(this);
}
@Override
public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onPostCreate(savedInstanceState, persistentState);
}
@Override
protected void onDestroy() {
super.onDestroy();
getWinBollActivityManager().registeRemove(this);
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- WinBoll 默认方案 --> <!-- WinBoLL 默认方案 -->
<color name="colorPrimary">#FF196ABC</color> <color name="colorPrimary">#FF196ABC</color>
<color name="colorPrimaryDark">#FF002B57</color> <color name="colorPrimaryDark">#FF002B57</color>
<color name="colorAccent">#FF80BFFF</color> <color name="colorAccent">#FF80BFFF</color>

View File

@@ -19,7 +19,7 @@ def genVersionName(def versionName){
android { android {
compileSdkVersion 32 compileSdkVersion 32
buildToolsVersion "33.0.3" buildToolsVersion "32.0.0"
defaultConfig { defaultConfig {
applicationId "cc.winboll.studio.autoinstaller" applicationId "cc.winboll.studio.autoinstaller"
@@ -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 "5.0" versionName "15.2"
if(true) { if(true) {
versionName = genVersionName("${versionName}") versionName = genVersionName("${versionName}")
} }
@@ -41,30 +41,33 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
} }
dependencies { dependencies {
api 'cc.winboll.studio:winboll-shared:1.1.0'
api 'io.github.medyo:android-about-page:2.0.0'
api 'com.github.getActivity:ToastUtils:10.5'
api 'com.jcraft:jsch:0.1.55'
api 'org.jsoup:jsoup:1.13.1'
api 'com.squareup.okhttp3:okhttp:4.4.1'
api 'androidx.appcompat:appcompat:1.0.0'
api 'androidx.fragment:fragment:1.0.0'
api 'com.google.android.material:material:1.0.0'
api 'androidx.vectordrawable:vectordrawable-animated:1.0.0'
api 'androidx.lifecycle:lifecycle-livedata:1.0.0'
//api 'cc.winboll.studio:libaes:6.3.2'
//api 'cc.winboll.studio:libapputils:8.3.8'
api fileTree(dir: 'libs', include: ['*.jar']) api fileTree(dir: 'libs', include: ['*.jar'])
api 'cc.winboll.studio:libaes:15.6.0'
api 'cc.winboll.studio:libapputils:15.3.4'
api 'cc.winboll.studio:libappbase:15.7.6'
// SSH
api 'com.jcraft:jsch:0.1.55'
// Html 解析
api 'org.jsoup:jsoup:1.13.1'
// 二维码类库
api 'com.google.zxing:core:3.4.1'
api 'com.journeyapps:zxing-android-embedded:3.6.0'
// 应用介绍页类库
api 'io.github.medyo:android-about-page:2.0.0'
// 吐司类库
api 'com.github.getActivity:ToastUtils:10.5'
// 网络连接类库
api 'com.squareup.okhttp3:okhttp:4.4.1'
// AndroidX 类库
api 'androidx.appcompat:appcompat:1.1.0'
api 'com.google.android.material:material:1.4.0'
//api 'androidx.viewpager:viewpager:1.0.0'
//api 'androidx.vectordrawable:vectordrawable:1.1.0'
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
//api 'androidx.fragment:fragment:1.1.0'
} }

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Thu Jan 02 02:39:56 HKT 2025 #Sun May 04 05:32:00 GMT 2025
stageCount=4 stageCount=1
libraryProject= libraryProject=
baseVersion=5.0 baseVersion=15.2
publishVersion=5.0.3 publishVersion=15.2.0
buildCount=0 buildCount=74
baseBetaVersion=5.0.4 baseBetaVersion=15.2.1

View File

@@ -23,6 +23,8 @@
<!-- MANAGE_EXTERNAL_STORAGE --> <!-- MANAGE_EXTERNAL_STORAGE -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application <application
android:name=".App" android:name=".App"
@@ -33,7 +35,7 @@
android:persistent="true" android:persistent="true"
android:supportsRtl="true" android:supportsRtl="true"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:theme="@style/Theme.Application"> android:theme="@style/MyAppTheme">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
@@ -54,6 +56,21 @@
<service android:name=".services.AssistantService"/> <service android:name=".services.AssistantService"/>
<service
android:name=".AppBaseTileService"
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>
<receiver <receiver
android:name=".receivers.MainReceiver" android:name=".receivers.MainReceiver"
android:enabled="true" android:enabled="true"

View File

@@ -6,22 +6,23 @@ package cc.winboll.studio.autoinstaller;
* @Describe 全局应用类 * @Describe 全局应用类
*/ */
import android.view.Gravity; import android.view.Gravity;
import cc.winboll.studio.libappbase.GlobalApplication;
import com.hjq.toast.ToastUtils; import com.hjq.toast.ToastUtils;
import cc.winboll.studio.shared.app.WinBollApplication; import com.hjq.toast.style.WhiteToastStyle;
public class App extends WinBollApplication { public class App extends GlobalApplication {
public static final String TAG = "App"; public static final String TAG = "App";
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
setIsDebug(BuildConfig.DEBUG);
// 初始化 Toast 框架 // 初始化 Toast 框架
ToastUtils.init(this); ToastUtils.init(this);
// 设置 Toast 布局样式 // 设置 Toast 布局样式
ToastUtils.setView(R.layout.toast_custom_view); //ToastUtils.setView(R.layout.view_toast);
//ToastUtils.setStyle(new WhiteToastStyle()); ToastUtils.setStyle(new WhiteToastStyle());
ToastUtils.setGravity(Gravity.BOTTOM, 0, 200); ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
} }
} }

View File

@@ -0,0 +1,12 @@
package cc.winboll.studio.autoinstaller;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/04/15 09:24:46
* @Describe 磁贴工具服务类
*/
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import cc.winboll.studio.autoinstaller.models.AppConfigs;

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.autoinstaller; package cc.winboll.studio.autoinstaller;
import android.os.FileObserver; import android.os.FileObserver;
import cc.winboll.studio.shared.log.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
public class FileListener extends FileObserver { public class FileListener extends FileObserver {
public final static String TAG = "FileListener"; public final static String TAG = "FileListener";

View File

@@ -6,21 +6,24 @@ import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View; import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.RadioButton; import android.widget.RadioButton;
import android.widget.SimpleAdapter; import android.widget.SimpleAdapter;
import android.widget.Switch; import android.widget.Switch;
import android.widget.TextClock; import android.widget.TextClock;
import android.widget.Toast;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import cc.winboll.studio.shared.log.LogUtils;
import cc.winboll.studio.shared.log.LogView;
import cc.winboll.studio.autoinstaller.MainActivity; import cc.winboll.studio.autoinstaller.MainActivity;
import cc.winboll.studio.autoinstaller.beans.AppConfigs; import cc.winboll.studio.autoinstaller.models.APKModel;
import cc.winboll.studio.autoinstaller.models.AppConfigs;
import cc.winboll.studio.autoinstaller.services.MainService; import cc.winboll.studio.autoinstaller.services.MainService;
import cc.winboll.studio.autoinstaller.utils.NotificationUtil; import cc.winboll.studio.autoinstaller.utils.NotificationUtil;
import cc.winboll.studio.autoinstaller.utils.PackageUtil;
import cc.winboll.studio.autoinstaller.views.ListViewForScrollView; import cc.winboll.studio.autoinstaller.views.ListViewForScrollView;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import com.hjq.toast.ToastUtils; import com.hjq.toast.ToastUtils;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
@@ -30,8 +33,13 @@ import java.util.Map;
public class MainActivity extends Activity { public class MainActivity extends Activity {
public static final String TAG = "MainActivity"; public static final String TAG = "MainActivity";
public static final int MSG_UPDATE_STATUS = 0;
private static final int INSTALL_PERMISSION_CODE = 1; private static final int INSTALL_PERMISSION_CODE = 1;
static MainActivity _MainActivity;
ArrayList<APKModel> _APKModelList = new ArrayList<APKModel>();
LogView mLogView; LogView mLogView;
TextClock mTextClock; TextClock mTextClock;
EditText mEditText; EditText mEditText;
@@ -60,9 +68,11 @@ public class MainActivity extends Activity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
_MainActivity = this;
initView(); initView();
if (getIntent().getAction().equals(ACTION_NEW_INSTALLTASK)) { String action = getIntent().getAction();
if ((action != null) && action.equals(ACTION_NEW_INSTALLTASK)) {
mszInstalledPackageName = getIntent().getStringExtra(EXTRA_INSTALLED_PACKAGENAME); mszInstalledPackageName = getIntent().getStringExtra(EXTRA_INSTALLED_PACKAGENAME);
mszInstalledAPKFilePath = getIntent().getStringExtra(EXTRA_INSTALLED_APKFILEPATH); mszInstalledAPKFilePath = getIntent().getStringExtra(EXTRA_INSTALLED_APKFILEPATH);
installAPK(); installAPK();
@@ -76,11 +86,7 @@ public class MainActivity extends Activity {
mLogView = findViewById(R.id.logview); mLogView = findViewById(R.id.logview);
mLogView.start(); mLogView.start();
AppConfigs appConfigs = AppConfigs.loadAppConfigs(this); AppConfigs appConfigs = AppConfigs.getInstance(this).loadAppConfigs(this);
if (appConfigs == null) {
appConfigs = new AppConfigs();
AppConfigs.saveAppConfigs(this, appConfigs);
}
if (appConfigs.getSetupMode() == AppConfigs.SetupMode.WATCHOUTPUTINSTALLER) { if (appConfigs.getSetupMode() == AppConfigs.SetupMode.WATCHOUTPUTINSTALLER) {
((RadioButton)findViewById(R.id.activitymainRadioButton1)).setChecked(true); ((RadioButton)findViewById(R.id.activitymainRadioButton1)).setChecked(true);
@@ -131,23 +137,44 @@ public class MainActivity extends Activity {
} }
String getLastApkPackageName() {
APKModel.loadBeanList(this, _APKModelList, APKModel.class);
if (_APKModelList.size() > 0) {
return _APKModelList.get(_APKModelList.size() - 1).getApkPackageName();
}
return "";
}
public void onOpenAPP(View view) { public void onOpenAPP(View view) {
if (mszInstalledPackageName.trim().equals("")) { String szInstalledPackageName = getLastApkPackageName();
LogUtils.d(TAG, "szInstalledPackageName : " + szInstalledPackageName);
if (szInstalledPackageName.trim().equals("")) {
ToastUtils.show("Installed APP package name is null."); ToastUtils.show("Installed APP package name is null.");
return; return;
} }
Intent intent = getPackageManager().getLaunchIntentForPackage(mszInstalledPackageName); PackageUtil.openAPP(this, szInstalledPackageName);
if (intent != null) {
startActivity(intent);
} else {
// 若没能获取到启动意图,可进行相应提示等操作,比如跳转到应用商店让用户下载该应用(示例)
Intent marketIntent = new Intent(Intent.ACTION_VIEW);
marketIntent.setData(Uri.parse("market://details?id=" + mszInstalledPackageName));
startActivity(marketIntent);
}
} }
// public void onOpenAPP(View view) {
// String szInstalledPackageName = getLastApkPackageName();
// if (szInstalledPackageName.trim().equals("")) {
// ToastUtils.show("Installed APP package name is null.");
// return;
// }
//
// Intent intent = getPackageManager().getLaunchIntentForPackage(mszInstalledPackageName);
// if (intent != null) {
// //ToastUtils.show("startActivity");
// startActivity(intent);
// } else {
// // 若没能获取到启动意图,可进行相应提示等操作,比如跳转到应用商店让用户下载该应用(示例)
// Intent marketIntent = new Intent(Intent.ACTION_VIEW);
// marketIntent.setData(Uri.parse("market://details?id=" + mszInstalledPackageName));
// startActivity(marketIntent);
// }
// }
public void onInstallAPK(View view) { public void onInstallAPK(View view) {
installAPK(); installAPK();
} }
@@ -185,17 +212,22 @@ public class MainActivity extends Activity {
} }
public void onLockPath(View view) { public void onLockPath(View view) {
AppConfigs appConfigs = AppConfigs.loadAppConfigs(this);
Switch sw = (Switch)view; Switch sw = (Switch)view;
if (sw.isChecked()) { setMainServiceStatus(sw.isChecked());
}
public void setMainServiceStatus(boolean isEnable) {
AppConfigs appConfigs = AppConfigs.getInstance(this).loadAppConfigs(this);
Switch sw = (Switch)findViewById(R.id.activitymainSwitch1);
if (isEnable) {
String szFilePath = mEditText.getText().toString(); String szFilePath = mEditText.getText().toString();
// 设置空路径时退出 // 设置空路径时退出
// //
if (szFilePath.trim().equals("")) { if (szFilePath.trim().equals("")) {
sw.setChecked(false); sw.setChecked(false);
Toast.makeText(getApplication(), "监控路径为空。", Toast.LENGTH_SHORT).show(); ToastUtils.show("监控路径为空。");
return; return;
} }
@@ -236,7 +268,7 @@ public class MainActivity extends Activity {
stopWatchingFile(); stopWatchingFile();
} }
AppConfigs.saveAppConfigs(this, appConfigs); AppConfigs.getInstance(this).saveAppConfigs(this, appConfigs);
} }
void stopWatchingFile() { void stopWatchingFile() {
@@ -251,8 +283,6 @@ public class MainActivity extends Activity {
Intent intentService = new Intent(MainActivity.this, MainService.class); Intent intentService = new Intent(MainActivity.this, MainService.class);
//intentService.putExtra(MainService.EXTRA_APKFILEPATH, szAPKFilePath); //intentService.putExtra(MainService.EXTRA_APKFILEPATH, szAPKFilePath);
startService(intentService); startService(intentService);
} }
/* /*
@@ -298,7 +328,7 @@ public class MainActivity extends Activity {
}*/ }*/
public void onChangeSetupMode(View view) { public void onChangeSetupMode(View view) {
AppConfigs appConfigs = AppConfigs.loadAppConfigs(this); AppConfigs appConfigs = AppConfigs.getInstance(this).loadAppConfigs(this);
if (view.getId() == R.id.activitymainRadioButton1) { if (view.getId() == R.id.activitymainRadioButton1) {
appConfigs.setSetupMode(AppConfigs.SetupMode.WATCHOUTPUTINSTALLER); appConfigs.setSetupMode(AppConfigs.SetupMode.WATCHOUTPUTINSTALLER);
@@ -307,6 +337,42 @@ public class MainActivity extends Activity {
appConfigs.setSetupMode(AppConfigs.SetupMode.NEWAPKINFONEWAPKINFO); appConfigs.setSetupMode(AppConfigs.SetupMode.NEWAPKINFONEWAPKINFO);
((RadioButton)findViewById(R.id.activitymainRadioButton1)).setChecked(false); ((RadioButton)findViewById(R.id.activitymainRadioButton1)).setChecked(false);
} }
AppConfigs.saveAppConfigs(this, appConfigs); AppConfigs.getInstance(this).saveAppConfigs(this, appConfigs);
}
// 定义Handler
static Handler _Handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == MSG_UPDATE_STATUS) {
if (_MainActivity != null) {
boolean isEnableMainService = (boolean)msg.obj;
// 处理消息,这里更新 MainService 的状态
_MainActivity.setMainServiceStatus(isEnableMainService);
}
}
}
};
static void updateMainServiceStatus(boolean isEnable) {
if (_Handler != null) {
Message msg = new Message();
msg.obj = isEnable;
msg.what = MSG_UPDATE_STATUS;
_Handler.sendMessage(msg);
}
}
public static void stopMainService() {
if (_MainActivity != null && _Handler != null) {
updateMainServiceStatus(false);
}
}
public static void startMainService() {
if (_MainActivity != null && _Handler != null) {
updateMainServiceStatus(true);
}
} }
} }

View File

@@ -0,0 +1,75 @@
package cc.winboll.studio.autoinstaller.models;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/04/02 20:50:29
* @Describe 监控的 APK 安装文件对应的应用信息数据模型
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
public class APKModel extends BaseBean {
public static final String TAG = "APPModel";
// 每次更新的 APK 文件对应的应用包名称
String apkPackageName;
public APKModel() {
this.apkPackageName = "";
}
public APKModel(String apkPackageName) {
this.apkPackageName = apkPackageName;
}
public void setApkPackageName(String apkPackageName) {
this.apkPackageName = apkPackageName;
}
public String getApkPackageName() {
return apkPackageName;
}
@Override
public String getName() {
return APKModel.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("appPackageName").value(getApkPackageName());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("appPackageName")) {
setApkPackageName(jsonReader.nextString());
} 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;
}
}

View File

@@ -1,4 +1,4 @@
package cc.winboll.studio.autoinstaller.beans; package cc.winboll.studio.autoinstaller.models;
/** /**
* @Author ZhanGSKen@QQ.COM * @Author ZhanGSKen@QQ.COM
@@ -8,13 +8,14 @@ package cc.winboll.studio.autoinstaller.beans;
import android.content.Context; import android.content.Context;
import android.util.JsonReader; import android.util.JsonReader;
import android.util.JsonWriter; import android.util.JsonWriter;
import cc.winboll.studio.autoinstaller.beans.AppConfigs; import cc.winboll.studio.autoinstaller.models.AppConfigs;
import cc.winboll.studio.autoinstaller.utils.FileUtil; import cc.winboll.studio.autoinstaller.utils.FileUtil;
import cc.winboll.studio.libappbase.LogUtils;
import com.hjq.toast.ToastUtils;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.io.StringReader; import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import cc.winboll.studio.shared.log.LogUtils;
public class AppConfigs implements Serializable { public class AppConfigs implements Serializable {
@@ -26,6 +27,21 @@ public class AppConfigs implements Serializable {
NEWAPKINFONEWAPKINFO // 调用[应用信息查看器]打开应用包 NEWAPKINFONEWAPKINFO // 调用[应用信息查看器]打开应用包
}; };
static volatile AppConfigs _AppConfigs;
Context mContext;
AppConfigs(Context context) {
mContext = context.getApplicationContext();
}
public static synchronized AppConfigs getInstance(Context context) {
if (_AppConfigs == null) {
_AppConfigs = new AppConfigs(context);
_AppConfigs.loadAppConfigs(_AppConfigs.mContext);
}
return _AppConfigs;
}
// 监控文件路径 // 监控文件路径
private String watchingFilePath = ""; private String watchingFilePath = "";
@@ -85,8 +101,8 @@ public class AppConfigs implements Serializable {
return ""; return "";
} }
public static AppConfigs parseAppConfigs(String szAppConfigs) { public AppConfigs parseAppConfigs(String szAppConfigs) {
AppConfigs appConfigs = new AppConfigs(); AppConfigs appConfigs = new AppConfigs(mContext);
// 创建 JsonWriter 对象 // 创建 JsonWriter 对象
StringReader stringReader = new StringReader(szAppConfigs); StringReader stringReader = new StringReader(szAppConfigs);
JsonReader jsonReader = new JsonReader jsonReader = new
@@ -121,20 +137,35 @@ public class AppConfigs implements Serializable {
static String getDataPath(Context context) { static String getDataPath(Context context) {
return context.getExternalFilesDir(TAG) + "/" + TAG + ".json"; return context.getExternalFilesDir(TAG) + "/" + TAG + ".json";
} }
public static AppConfigs loadAppConfigs(Context context) { public AppConfigs loadAppConfigs() {
AppConfigs appConfigs = null; AppConfigs appConfigs = null;
try { try {
String szJson = FileUtil.readFile(getDataPath(context)); String szJson = FileUtil.readFile(getDataPath(mContext));
appConfigs = AppConfigs.parseAppConfigs(szJson); appConfigs = AppConfigs.getInstance(mContext).parseAppConfigs(szJson);
} catch (IOException e) { } catch (IOException e) {
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace()); LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
} }
return appConfigs; return appConfigs;
} }
public static void saveAppConfigs(Context context, AppConfigs appConfigs) { public AppConfigs loadAppConfigs(Context context) {
AppConfigs appConfigs = null;
try { try {
String szJson = FileUtil.readFile(getDataPath(context.getApplicationContext()));
appConfigs = AppConfigs.getInstance(mContext).parseAppConfigs(szJson);
if(appConfigs != null) {
_AppConfigs = appConfigs;
}
} catch (IOException e) {
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
}
return _AppConfigs;
}
public void saveAppConfigs(Context context, AppConfigs appConfigs) {
try {
ToastUtils.show(String.format("AppConfigs set enable service to %s", appConfigs.isEnableService()));
//LogUtils.d(TAG, "appConfigs is : " + appConfigs.toString()); //LogUtils.d(TAG, "appConfigs is : " + appConfigs.toString());
String szJson = appConfigs.toString(); String szJson = appConfigs.toString();
FileUtil.writeFile(getDataPath(context), szJson); FileUtil.writeFile(getDataPath(context), szJson);
@@ -142,4 +173,8 @@ public class AppConfigs implements Serializable {
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace()); LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
} }
} }
public void saveAppConfigs() {
saveAppConfigs(mContext, this);
}
} }

View File

@@ -0,0 +1,67 @@
package cc.winboll.studio.autoinstaller.models;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/04/15 09:27:39
* @Describe MainServiceBean
*/
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;
}
}

View File

@@ -4,9 +4,9 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import cc.winboll.studio.autoinstaller.beans.AppConfigs; import cc.winboll.studio.autoinstaller.models.AppConfigs;
import cc.winboll.studio.autoinstaller.services.MainService; import cc.winboll.studio.autoinstaller.services.MainService;
import cc.winboll.studio.shared.log.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
/** /**
* @Author ZhanGSKen@QQ.COM * @Author ZhanGSKen@QQ.COM
@@ -23,7 +23,7 @@ public class MainReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
String szAction = intent.getAction(); String szAction = intent.getAction();
if (szAction.equals(ACTION_BOOT_COMPLETED)) { if (szAction.equals(ACTION_BOOT_COMPLETED)) {
AppConfigs appConfigs = AppConfigs.loadAppConfigs(context); AppConfigs appConfigs = AppConfigs.getInstance(context).loadAppConfigs(context);
if (appConfigs.isEnableService()) { if (appConfigs.isEnableService()) {
Intent intentService = new Intent(context, MainService.class); Intent intentService = new Intent(context, MainService.class);
//intentService.putExtra(MainService.EXTRA_APKFILEPATH, appConfigs.getWatchingFilePath()); //intentService.putExtra(MainService.EXTRA_APKFILEPATH, appConfigs.getWatchingFilePath());

View File

@@ -6,7 +6,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.os.IBinder; import android.os.IBinder;
import cc.winboll.studio.autoinstaller.beans.AppConfigs; import cc.winboll.studio.autoinstaller.models.AppConfigs;
import cc.winboll.studio.autoinstaller.utils.ServiceUtil; import cc.winboll.studio.autoinstaller.utils.ServiceUtil;
/** /**
@@ -46,7 +46,7 @@ public class AssistantService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
//LogUtils.d(TAG, "call onStartCommand(...)"); //LogUtils.d(TAG, "call onStartCommand(...)");
run(); run();
AppConfigs appConfigs = AppConfigs.loadAppConfigs(AssistantService.this); AppConfigs appConfigs = AppConfigs.getInstance(AssistantService.this).loadAppConfigs(AssistantService.this);
return appConfigs.isEnableService() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId); return appConfigs.isEnableService() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId);
} }
@@ -68,7 +68,7 @@ public class AssistantService extends Service {
// //
void run() { void run() {
//LogUtils.d(TAG, "call run()"); //LogUtils.d(TAG, "call run()");
AppConfigs appConfigs = AppConfigs.loadAppConfigs(AssistantService.this); AppConfigs appConfigs = AppConfigs.getInstance(AssistantService.this).loadAppConfigs(AssistantService.this);
if (appConfigs.isEnableService()) { if (appConfigs.isEnableService()) {
if (mIsThreadAlive == false) { if (mIsThreadAlive == false) {
// 设置运行状态 // 设置运行状态
@@ -101,7 +101,7 @@ public class AssistantService extends Service {
@Override @Override
public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName name) {
//LogUtils.d(TAG, "call onServiceDisconnected(...)"); //LogUtils.d(TAG, "call onServiceDisconnected(...)");
AppConfigs appConfigs = AppConfigs.loadAppConfigs(AssistantService.this); AppConfigs appConfigs = AppConfigs.getInstance(AssistantService.this).loadAppConfigs(AssistantService.this);
if (appConfigs.isEnableService()) { if (appConfigs.isEnableService()) {
wakeupAndBindMain(); wakeupAndBindMain();
} }

View File

@@ -10,25 +10,32 @@ import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Message; import android.os.Message;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.widget.Toast; import android.widget.Toast;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import cc.winboll.studio.shared.log.LogUtils;
import cc.winboll.studio.autoinstaller.FileListener; import cc.winboll.studio.autoinstaller.FileListener;
import cc.winboll.studio.autoinstaller.MainActivity; import cc.winboll.studio.autoinstaller.MainActivity;
import cc.winboll.studio.autoinstaller.beans.AppConfigs; import cc.winboll.studio.autoinstaller.R;
import cc.winboll.studio.autoinstaller.models.APKModel;
import cc.winboll.studio.autoinstaller.models.AppConfigs;
import cc.winboll.studio.autoinstaller.services.AssistantService; import cc.winboll.studio.autoinstaller.services.AssistantService;
import cc.winboll.studio.autoinstaller.services.MainService; import cc.winboll.studio.autoinstaller.services.MainService;
import cc.winboll.studio.autoinstaller.utils.NotificationUtil; import cc.winboll.studio.autoinstaller.utils.NotificationUtil;
import cc.winboll.studio.autoinstaller.utils.PackageUtil; import cc.winboll.studio.autoinstaller.utils.PackageUtil;
import cc.winboll.studio.autoinstaller.utils.ServiceUtil; import cc.winboll.studio.autoinstaller.utils.ServiceUtil;
import com.hjq.toast.ToastUtils; import cc.winboll.studio.libappbase.LogUtils;
import java.io.File; import java.io.File;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList;
public class MainService extends Service { public class MainService extends Service {
public static String TAG = "MainService"; public static String TAG = "MainService";
Context mContext;
ArrayList<APKModel> _APKModelList = new ArrayList<APKModel>();
private static boolean _mIsServiceAlive; private static boolean _mIsServiceAlive;
//String mszAPKFilePath; //String mszAPKFilePath;
//String mszAPKFileName; //String mszAPKFileName;
@@ -47,6 +54,7 @@ public class MainService extends Service {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
mContext = this;
LogUtils.d(TAG, "onCreate()"); LogUtils.d(TAG, "onCreate()");
_mIsServiceAlive = false; _mIsServiceAlive = false;
mHandler = new MyHandler(MainService.this); mHandler = new MyHandler(MainService.this);
@@ -55,11 +63,14 @@ public class MainService extends Service {
} }
run(); run();
// 初始化磁贴工具服务
MainTileService mainTileService = new MainTileService(this);
} }
private void run() { private void run() {
AppConfigs appConfigs = AppConfigs.loadAppConfigs(MainService.this); AppConfigs appConfigs = AppConfigs.getInstance(MainService.this).loadAppConfigs(MainService.this);
if (appConfigs.isEnableService()) { if (appConfigs.isEnableService()) {
if (_mIsServiceAlive == false) { if (_mIsServiceAlive == false) {
// 设置运行状态 // 设置运行状态
@@ -74,7 +85,8 @@ public class MainService extends Service {
startWatchingFile(appConfigs.getWatchingFilePath()); startWatchingFile(appConfigs.getWatchingFilePath());
LogUtils.d(TAG, "running..."); //LogUtils.d(TAG, "running...");
//ToastUtils.show("running...");
} else { } else {
LogUtils.d(TAG, "_mIsServiceAlive is " + Boolean.toString(_mIsServiceAlive)); LogUtils.d(TAG, "_mIsServiceAlive is " + Boolean.toString(_mIsServiceAlive));
@@ -94,6 +106,7 @@ public class MainService extends Service {
} }
_mIsServiceAlive = false; _mIsServiceAlive = false;
LogUtils.d(TAG, "onDestroy()"); LogUtils.d(TAG, "onDestroy()");
mContext = null;
} }
@Override @Override
@@ -101,7 +114,7 @@ public class MainService extends Service {
LogUtils.d(TAG, "onStartCommand"); LogUtils.d(TAG, "onStartCommand");
run(); run();
AppConfigs appConfigs = AppConfigs.loadAppConfigs(MainService.this); AppConfigs appConfigs = AppConfigs.getInstance(MainService.this).loadAppConfigs(MainService.this);
return appConfigs.isEnableService() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId); return appConfigs.isEnableService() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId);
} }
@@ -118,7 +131,7 @@ public class MainService extends Service {
@Override @Override
public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName name) {
//LogUtils.d(TAG, "call onServiceConnected(...)"); //LogUtils.d(TAG, "call onServiceConnected(...)");
AppConfigs appConfigs = AppConfigs.loadAppConfigs(MainService.this); AppConfigs appConfigs = AppConfigs.getInstance(MainService.this).loadAppConfigs(MainService.this);
if (appConfigs.isEnableService()) { if (appConfigs.isEnableService()) {
// 唤醒守护进程 // 唤醒守护进程
wakeupAndBindAssistant(); wakeupAndBindAssistant();
@@ -160,7 +173,7 @@ public class MainService extends Service {
}); });
mFileListener.startWatching(); mFileListener.startWatching();
ToastUtils.show("Start watching."); //ToastUtils.show("Start watching.");
} else { } else {
// 父级文件夹不存在,就提示用户 // 父级文件夹不存在,就提示用户
Toast.makeText(getApplication(), fParentDir.toString() + " no exist.", Toast.LENGTH_SHORT).show(); Toast.makeText(getApplication(), fParentDir.toString() + " no exist.", Toast.LENGTH_SHORT).show();
@@ -176,6 +189,9 @@ public class MainService extends Service {
// 调用[应用信息查看器]打开应用包 // 调用[应用信息查看器]打开应用包
// //
private void installAPK(String szAPKFilePath) { private void installAPK(String szAPKFilePath) {
String szAPKPackageName = PackageUtil.getPackageNameFromApk(this, szAPKFilePath);
saveAPKInfo(szAPKPackageName);
long nTimeNow = System.currentTimeMillis(); long nTimeNow = System.currentTimeMillis();
/*SimpleDateFormat dateFormat = new SimpleDateFormat( /*SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()); "yyyy-MM-dd HH:mm:ss", Locale.getDefault());
@@ -210,15 +226,24 @@ public class MainService extends Service {
// //
void installAPK2(String szAPKFilePath) { void installAPK2(String szAPKFilePath) {
LogUtils.d(TAG, "installAPK2()"); LogUtils.d(TAG, "installAPK2()");
String szAPKPackageName = PackageUtil.getPackageNameFromApk(this, szAPKFilePath);
saveAPKInfo(szAPKPackageName);
Intent intent = new Intent(this, MainActivity.class); Intent intent = new Intent(this, MainActivity.class);
intent.setAction(MainActivity.ACTION_NEW_INSTALLTASK); intent.setAction(MainActivity.ACTION_NEW_INSTALLTASK);
intent.putExtra(MainActivity.EXTRA_INSTALLED_PACKAGENAME, PackageUtil.getPackageNameFromApk(this, szAPKFilePath)); intent.putExtra(MainActivity.EXTRA_INSTALLED_PACKAGENAME, szAPKPackageName);
intent.putExtra(MainActivity.EXTRA_INSTALLED_APKFILEPATH, szAPKFilePath); intent.putExtra(MainActivity.EXTRA_INSTALLED_APKFILEPATH, szAPKFilePath);
// Intent.ACTION_VIEW); // Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent); this.startActivity(intent);
} }
void saveAPKInfo(String szApkPackageName) {
APKModel.loadBeanList(this, _APKModelList, APKModel.class);
_APKModelList.add(new APKModel(szApkPackageName));
APKModel.saveBeanList(this, _APKModelList, APKModel.class);
}
// //
// //
static class MyHandler extends Handler { static class MyHandler extends Handler {
@@ -231,7 +256,7 @@ public class MainService extends Service {
switch (message.what) { switch (message.what) {
case MSG_INSTALL_APK: case MSG_INSTALL_APK:
{ {
AppConfigs appConfigs = AppConfigs.loadAppConfigs(theActivity); AppConfigs appConfigs = AppConfigs.getInstance(theActivity).loadAppConfigs(theActivity);
if (appConfigs.getSetupMode() == AppConfigs.SetupMode.WATCHOUTPUTINSTALLER) { if (appConfigs.getSetupMode() == AppConfigs.SetupMode.WATCHOUTPUTINSTALLER) {
theActivity.installAPK2((String)message.obj); theActivity.installAPK2((String)message.obj);
} else if (appConfigs.getSetupMode() == AppConfigs.SetupMode.NEWAPKINFONEWAPKINFO) { } else if (appConfigs.getSetupMode() == AppConfigs.SetupMode.NEWAPKINFONEWAPKINFO) {
@@ -245,4 +270,21 @@ public class MainService extends Service {
super.handleMessage(message); super.handleMessage(message);
} }
} }
static class MainTileService extends TileService {
Context mContext;
MainTileService(Context context) {
mContext = context;
}
@Override
public void onStartListening() {
super.onStartListening();
Tile tile = getQsTile();
tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_android));
// 更新磁贴状态
tile.setState((mContext == null || mContext.getApplicationContext() == null )?Tile.STATE_INACTIVE: Tile.STATE_ACTIVE);
tile.updateTile();
}
}
} }

View File

@@ -55,8 +55,9 @@ public class NotificationUtil {
Notification mForegroundNotification = new Notification.Builder(service, szServiceChannelID) Notification mForegroundNotification = new Notification.Builder(service, szServiceChannelID)
.setAutoCancel(true) .setAutoCancel(true)
.setContentTitle(service.getString(R.string.app_name)) //.setContentTitle(service.getString(R.string.app_name))
.setContentText(service.TAG + " is started.") .setContentTitle(service.TAG + " is started.")
//.setContentText(service.TAG + " is started.")
.setWhen(System.currentTimeMillis()) .setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.ic_launcher) .setSmallIcon(R.drawable.ic_launcher)
//设置红色 //设置红色

View File

@@ -6,9 +6,11 @@ package cc.winboll.studio.autoinstaller.utils;
* @Describe 一个获取安卓APK安装文件的应用包名的函数 * @Describe 一个获取安卓APK安装文件的应用包名的函数
*/ */
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import cc.winboll.studio.shared.log.LogUtils; import android.content.pm.ResolveInfo;
import cc.winboll.studio.libappbase.LogUtils;
public class PackageUtil { public class PackageUtil {
@@ -27,4 +29,20 @@ public class PackageUtil {
} }
return ""; return "";
} }
public static void openAPP(Context context, String packageName) {
// 这里假设要打开微信微信的包名是com.tencent.mm
//String packageName = "com.tencent.mm";
LogUtils.d(TAG, "packageName : " + packageName);
PackageManager packageManager = context.getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage(packageName);
if (intent != null) {
ResolveInfo resolveInfo = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfo != null) {
LogUtils.d(TAG, "startActivity...");
context.startActivity(intent);
}
}
}
} }

View File

@@ -0,0 +1,11 @@
<?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="M16.61,15.15C16.15,15.15 15.77,14.78 15.77,14.32S16.15,13.5 16.61,13.5H16.61C17.07,13.5 17.45,13.86 17.45,14.32C17.45,14.78 17.07,15.15 16.61,15.15M7.41,15.15C6.95,15.15 6.57,14.78 6.57,14.32C6.57,13.86 6.95,13.5 7.41,13.5H7.41C7.87,13.5 8.24,13.86 8.24,14.32C8.24,14.78 7.87,15.15 7.41,15.15M16.91,10.14L18.58,7.26C18.67,7.09 18.61,6.88 18.45,6.79C18.28,6.69 18.07,6.75 18,6.92L16.29,9.83C14.95,9.22 13.5,8.9 12,8.91C10.47,8.91 9,9.24 7.73,9.82L6.04,6.91C5.95,6.74 5.74,6.68 5.57,6.78C5.4,6.87 5.35,7.08 5.44,7.25L7.1,10.13C4.25,11.69 2.29,14.58 2,18H22C21.72,14.59 19.77,11.7 16.91,10.14H16.91Z"/>
</vector>

View File

@@ -0,0 +1,11 @@
<?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>

View File

@@ -0,0 +1,11 @@
<?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>

View File

@@ -132,7 +132,7 @@
</LinearLayout> </LinearLayout>
<cc.winboll.studio.shared.log.LogView <cc.winboll.studio.libappbase.LogView
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp" android:layout_height="200dp"

View File

@@ -1,3 +1,4 @@
<resources> <resources>
<string name="app_name">AutoInstaller</string> <string name="app_name">AutoInstaller</string>
<string name="tileservice_name">AutoInstaller</string>
</resources> </resources>

View File

@@ -0,0 +1,13 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="MyAppTheme" parent="APPBaseTheme">
<item name="attrColorPrimary">@color/colorPrimary</item>
<item name="themeGlobalCrashActivity">@style/MyGlobalCrashActivityTheme</item>
</style>
<style name="MyGlobalCrashActivityTheme" parent="GlobalCrashActivityTheme">
<item name="colorTittle">#FFFFFFFF</item>
<item name="colorTittleBackgound">#FF00A4B3</item>
<item name="colorText">#FFFFFFFF</item>
<item name="colorTextBackgound">#FF000000</item>
</style>
</resources>

View File

@@ -1,14 +0,0 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.Application" parent="android:Theme.Material.Light.DarkActionBar">
<!-- Primary brand color. -->
<item name="android:colorPrimary">@color/purple_500</item>
<!-- Secondary brand color. -->
<item name="android:colorSecondary">@color/teal_200</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">@color/purple_700</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -1,15 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
repositories { repositories {
// 本地 Maven 仓库(默认路径为 ~/.m2/repository mavenLocal {
mavenLocal() // 设置本地Maven仓库路径
// 或自定义本地仓库路径 url 'file:///sdcard/.m2/repository/'
maven { url "file:///sdcard/.m2/repository" } }
// Nexus Maven 库地址 // Nexus Maven 库地址
// "WinBoll Release" // "WinBoLL Release"
maven { url "https://nexus.winboll.cc/repository/maven-public/" } maven { url "https://nexus.winboll.cc/repository/maven-public/" }
// "WinBoll Snapshot" // "WinBoLL Snapshot"
maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" } maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" }
maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/public/' }
@@ -20,7 +19,13 @@ buildscript {
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
mavenCentral() mavenCentral()
google() google()
//println "mavenLocal : ==========="
//println mavenLocal().url
//println "mavenLocal : ==========="
//mavenLocal() //mavenLocal()
// WinBoLL.CC 紧急备用 Maven 仓库
maven { url 'https://spare-maven.winboll.cc/repository/' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.2.1' // 对应 compileSdkVersion 32 classpath 'com.android.tools.build:gradle:7.2.1' // 对应 compileSdkVersion 32
@@ -31,10 +36,15 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
mavenLocal {
// 设置本地Maven仓库路径
url 'file:///sdcard/.m2/repository/'
}
// Nexus Maven 库地址 // Nexus Maven 库地址
// "WinBoll Release" // "WinBoLL Release"
maven { url "https://nexus.winboll.cc/repository/maven-public/" } maven { url "https://nexus.winboll.cc/repository/maven-public/" }
// "WinBoll Snapshot" // "WinBoLL Snapshot"
maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" } maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" }
maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/public/' }
@@ -45,7 +55,13 @@ allprojects {
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
mavenCentral() mavenCentral()
google() google()
mavenLocal() //println "mavenLocal : ==========="
//println mavenLocal().url
//println "mavenLocal : ==========="
//mavenLocal()
// WinBoLL.CC 紧急备用 Maven 仓库
maven { url 'https://spare-maven.winboll.cc/repository/' }
} }
ext { ext {
// 定义全局变量,常用于版本管理 // 定义全局变量,常用于版本管理
@@ -56,7 +72,7 @@ allprojects {
bashCommitAppPublishBuildFlagInfoFilePath = ".winboll/bashCommitAppPublishBuildFlagInfo.sh" bashCommitAppPublishBuildFlagInfoFilePath = ".winboll/bashCommitAppPublishBuildFlagInfo.sh"
winbollFilePath = "winboll.properties" winbollFilePath = "winboll.properties"
keyPropsFilePath = "winboll-x/current.keystore" keyPropsFilePath = "current.keystore"
// 定义 lint 输出文件 // 定义 lint 输出文件
lintXmlReportFilePath = "build/reports/lint-results.xml" lintXmlReportFilePath = "build/reports/lint-results.xml"
lintHTMLReportFilePath = "build/reports/lint-results.html" lintHTMLReportFilePath = "build/reports/lint-results.html"

View File

@@ -19,17 +19,17 @@ def genVersionName(def versionName){
android { android {
compileSdkVersion 32 compileSdkVersion 32
buildToolsVersion "33.0.3" buildToolsVersion "32.0.0"
defaultConfig { defaultConfig {
applicationId "cc.winboll.studio.contacts" applicationId "cc.winboll.studio.contacts"
minSdkVersion 21 minSdkVersion 24
targetSdkVersion 30 targetSdkVersion 29
versionCode 1 versionCode 1
// versionName 更新后需要手动设置 // versionName 更新后需要手动设置
// 项目模块目录的 build.gradle 文件的 stageCount=0 // 项目模块目录的 build.gradle 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "1.0" versionName "15.2"
if(true) { if(true) {
versionName = genVersionName("${versionName}") versionName = genVersionName("${versionName}")
} }
@@ -41,31 +41,48 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
} }
dependencies { dependencies {
// 二维码使用的类库 api fileTree(dir: 'libs', include: ['*.jar'])
// 权限请求框架https://github.com/getActivity/XXPermissions
api 'com.github.getActivity:XXPermissions:18.63'
// 下拉控件
api 'com.baoyz.pullrefreshlayout:library:1.2.0'
// 拼音搜索
// https://mvnrepository.com/artifact/com.github.open-android/pinyin4j
api 'com.github.open-android:pinyin4j:2.5.0'
// SSH
api 'com.jcraft:jsch:0.1.55'
// Html 解析
api 'org.jsoup:jsoup:1.13.1'
// 二维码类库
api 'com.google.zxing:core:3.4.1' api 'com.google.zxing:core:3.4.1'
api 'com.journeyapps:zxing-android-embedded:3.6.0' api 'com.journeyapps:zxing-android-embedded:3.6.0'
// 应用介绍页类库
api 'io.github.medyo:android-about-page:2.0.0' api 'io.github.medyo:android-about-page:2.0.0'
// 吐司类库
api 'com.github.getActivity:ToastUtils:10.5' api 'com.github.getActivity:ToastUtils:10.5'
api 'com.jcraft:jsch:0.1.55' // 网络连接类库
api 'org.jsoup:jsoup:1.13.1'
api 'com.squareup.okhttp3:okhttp:4.4.1' api 'com.squareup.okhttp3:okhttp:4.4.1'
// AndroidX 类库
/*implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.viewpager:viewpager:1.0.0'
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
implementation 'androidx.fragment:fragment:1.1.0'
implementation 'com.google.android.material:material:1.4.0'
*/
api 'androidx.appcompat:appcompat:1.1.0' api 'androidx.appcompat:appcompat:1.1.0'
api 'androidx.viewpager:viewpager:1.0.0'
api 'androidx.fragment:fragment:1.1.0'
api 'com.google.android.material:material:1.4.0' api 'com.google.android.material:material:1.4.0'
//api 'androidx.viewpager:viewpager:1.0.0'
//api 'androidx.vectordrawable:vectordrawable:1.1.0'
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
//api 'androidx.fragment:fragment:1.1.0'
api 'cc.winboll.studio:libapputils:9.3.2' api 'cc.winboll.studio:libaes:15.2.4'
api 'cc.winboll.studio:libappbase:1.5.6' api 'cc.winboll.studio:libapputils:15.2.1'
api 'cc.winboll.studio:libappbase:15.2.2'
api fileTree(dir: 'libs', include: ['*.jar'])
} }

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Tue Feb 25 00:17:29 HKT 2025 #Sun Apr 13 02:46:09 HKT 2025
stageCount=2 stageCount=8
libraryProject= libraryProject=
baseVersion=1.0 baseVersion=15.2
publishVersion=1.0.1 publishVersion=15.2.7
buildCount=0 buildCount=0
baseBetaVersion=1.0.2 baseBetaVersion=15.2.8

View File

@@ -29,9 +29,13 @@
<!-- 更改您的音频设置 --> <!-- 更改您的音频设置 -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS" /> <!-- 读取通话记录 -->
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<!-- 录音 -->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<application <application
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"
@@ -65,7 +69,7 @@
<activity <activity
android:name=".phonecallui.PhoneCallActivity" android:name=".phonecallui.PhoneCallActivity"
android:launchMode="singleInstance" android:launchMode="singleTask"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
@@ -182,6 +186,10 @@
</provider> </provider>
<activity android:name="cc.winboll.studio.contacts.activities.UnitTestActivity"/>
<activity android:name="cc.winboll.studio.contacts.activities.AboutActivity"/>
</application> </application>
</manifest> </manifest>

View File

@@ -5,9 +5,10 @@ package cc.winboll.studio.contacts;
* @Date 2024/12/08 15:10:51 * @Date 2024/12/08 15:10:51
* @Describe 全局应用类 * @Describe 全局应用类
*/ */
import android.view.Gravity;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.winboll.WinBollActivityManager;
import cc.winboll.studio.libapputils.app.WinBollActivityManager; import com.hjq.toast.ToastUtils;
public class App extends GlobalApplication { public class App extends GlobalApplication {
@@ -17,12 +18,20 @@ public class App extends GlobalApplication {
public void onCreate() { public void onCreate() {
// 必须在调用基类前设置应用调试标志, // 必须在调用基类前设置应用调试标志,
// 这样可以预先设置日志与数据的存储根目录。 // 这样可以预先设置日志与数据的存储根目录。
setIsDebuging(this, BuildConfig.DEBUG); //setIsDebuging(BuildConfig.DEBUG);
super.onCreate(); super.onCreate();
// 设置 WinBoll 应用 UI 类型 // 设置 WinBoll 应用 UI 类型
WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Aplication); WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Aplication);
LogUtils.d(TAG, "onCreate"); //LogUtils.d(TAG, "onCreate");
// 初始化 Toast 框架
ToastUtils.init(this);
// 设置 Toast 布局样式
//ToastUtils.setView(R.layout.toast_custom_view);
//ToastUtils.setStyle(new WhiteToastStyle());
ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
} }
} }

View File

@@ -1,52 +1,44 @@
package cc.winboll.studio.contacts; package cc.winboll.studio.contacts;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.role.RoleManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings;
import android.telecom.TelecomManager; import android.telecom.TelecomManager;
import android.view.LayoutInflater; import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
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.view.WindowManager;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat; import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.activities.CallActivity; import cc.winboll.studio.contacts.activities.SettingsActivity;
import cc.winboll.studio.contacts.adapters.MyPagerAdapter;
import cc.winboll.studio.contacts.beans.MainServiceBean; import cc.winboll.studio.contacts.beans.MainServiceBean;
import cc.winboll.studio.contacts.fragments.CallLogFragment;
import cc.winboll.studio.contacts.fragments.ContactsFragment;
import cc.winboll.studio.contacts.fragments.LogFragment;
import cc.winboll.studio.contacts.services.MainService; import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.libaes.winboll.APPInfo;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView; import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libapputils.app.IWinBollActivity; import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
import cc.winboll.studio.libapputils.app.WinBollActivityManager;
import cc.winboll.studio.libapputils.bean.APPInfo;
import cc.winboll.studio.libapputils.view.YesNoAlertDialog;
import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import android.content.DialogInterface;
import cc.winboll.studio.contacts.activities.SettingsActivity;
final public class MainActivity extends AppCompatActivity implements IWinBollActivity, ViewPager.OnPageChangeListener, View.OnClickListener { final public class MainActivity extends AppCompatActivity implements IWinBollActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
@@ -57,11 +49,13 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoll.ACTION_SOS"; public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoll.ACTION_SOS";
static MainActivity _MainActivity;
LogView mLogView; LogView mLogView;
Toolbar mToolbar; Toolbar mToolbar;
CheckBox cbMainService; CheckBox cbMainService;
MainServiceBean mMainServiceBean; MainServiceBean mMainServiceBean;
ViewPager viewPager; private TabLayout tabLayout;
private ViewPager viewPager;
private List<View> views; //用来存放放进ViewPager里面的布局 private List<View> views; //用来存放放进ViewPager里面的布局
//实例化存储imageView导航原点的集合 //实例化存储imageView导航原点的集合
ImageView[] imageViews; ImageView[] imageViews;
@@ -70,15 +64,20 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
LinearLayout linearLayout;//下标所在在LinearLayout布局里 LinearLayout linearLayout;//下标所在在LinearLayout布局里
int currentPoint = 0;//当前被选中中页面的下标 int currentPoint = 0;//当前被选中中页面的下标
private TelephonyManager telephonyManager;
private MyPhoneStateListener phoneStateListener;
List<Fragment> fragmentList;
List<String> tabTitleList;
private static final int DIALER_REQUEST_CODE = 1; private static final int DIALER_REQUEST_CODE = 1;
@Override @Override
public AppCompatActivity getActivity() { public Activity getActivity() {
return this; return this;
} }
@Override // @Override
public APPInfo getAppInfo() { // public APPInfo getAppInfo() {
// String szBranchName = "contacts"; // String szBranchName = "contacts";
// //
// APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo(); // APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo();
@@ -93,8 +92,8 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
// appInfo.setAppAPKName("Contacts"); // appInfo.setAppAPKName("Contacts");
// appInfo.setAppAPKFolderName("Contacts"); // appInfo.setAppAPKFolderName("Contacts");
// return appInfo; // return appInfo;
return null; // return null;
} // }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -102,28 +101,51 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
//if (prosessIntents(getIntent())) return; //if (prosessIntents(getIntent())) return;
// 以下正常创建主窗口 // 以下正常创建主窗口
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
_MainActivity = this;
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
// 初始化工具栏 // 初始化工具栏
mToolbar = findViewById(R.id.activitymainToolbar1); mToolbar = findViewById(R.id.activitymainToolbar1);
setSupportActionBar(mToolbar); setSupportActionBar(mToolbar);
if (isEnableDisplayHomeAsUp()) { // if (isEnableDisplayHomeAsUp()) {
// 显示后退按钮 // // 显示后退按钮
getSupportActionBar().setDisplayHomeAsUpEnabled(true); // getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} // }
getSupportActionBar().setSubtitle(getTag()); getSupportActionBar().setSubtitle(getTag());
initData(); tabLayout = findViewById(R.id.tabLayout);
initView(); viewPager = findViewById(R.id.viewPager);
//initPoint();//调用初始化导航原点的方法
viewPager.addOnPageChangeListener(this);//滑动事件
ViewPager viewPager = findViewById(R.id.activitymainViewPager1); // 创建Fragment列表和标题列表
MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager()); fragmentList = new ArrayList<>();
viewPager.setAdapter(pagerAdapter); tabTitleList = new ArrayList<>();
TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1); fragmentList.add(CallLogFragment.newInstance(0));
fragmentList.add(ContactsFragment.newInstance(1));
fragmentList.add(LogFragment.newInstance(2));
tabTitleList.add("通话记录");
tabTitleList.add("联系人");
tabTitleList.add("应用日志");
// 设置ViewPager的适配器
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), fragmentList, tabTitleList);
viewPager.setAdapter(adapter);
// 关联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); // mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
// if (mMainServiceBean == null) { // if (mMainServiceBean == null) {
// mMainServiceBean = new MainServiceBean(); // mMainServiceBean = new MainServiceBean();
@@ -140,36 +162,86 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
// } // }
// } // }
// }); // });
MainService.startMainService(MainActivity.this);
MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
if (mMainServiceBean == null) {
mMainServiceBean = new MainServiceBean();
MainServiceBean.saveBean(this, mMainServiceBean);
}
if (mMainServiceBean.isEnable()) {
MainService.startMainService(this);
}
// 初始化TelephonyManager和PhoneStateListener
telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
phoneStateListener = new MyPhoneStateListener();
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
// ViewPager的适配器
private class MyPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragmentList;
private List<String> tabTitleList;
public MyPagerAdapter(FragmentManager fm, List<Fragment> fragmentList, List<String> tabTitleList) {
super(fm);
this.fragmentList = fragmentList;
this.tabTitleList = tabTitleList;
}
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return tabTitleList.get(position);
}
}
public static void dialPhoneNumber(String phoneNumber) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
if (ActivityCompat.checkSelfPermission(_MainActivity, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
_MainActivity.startActivity(intent);
} }
//初始化view即显示的图片 //初始化view即显示的图片
void initView() { // void initView() {
viewPager = findViewById(R.id.activitymainViewPager1); // viewPager = findViewById(R.id.activitymainViewPager1);
pagerAdapter = new MyPagerAdapter(getSupportFragmentManager()); // pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter); // viewPager.setAdapter(pagerAdapter);
//adapter = new MyPagerAdapter(views); // //adapter = new MyPagerAdapter(views);
//viewPager = findViewById(R.id.activitymainViewPager1); // //viewPager = findViewById(R.id.activitymainViewPager1);
//viewPager.setAdapter(adapter); // //viewPager.setAdapter(adapter);
//linearLayout = findViewById(R.id.activitymainLinearLayout1); // //linearLayout = findViewById(R.id.activitymainLinearLayout1);
//initPoint();//初始化页面下方的点 // //initPoint();//初始化页面下方的点
viewPager.setOnPageChangeListener(this); // viewPager.setOnPageChangeListener(this);
//
} // }
//初始化所要显示的布局 //初始化所要显示的布局
void initData() { // void initData() {
ViewPager viewPager = findViewById(R.id.activitymainViewPager1); // LayoutInflater inflater = LayoutInflater.from(getActivity());
LayoutInflater inflater = LayoutInflater.from(getActivity()); // View view1 = inflater.inflate(R.layout.fragment_call_log, viewPager, false);
View view1 = inflater.inflate(R.layout.fragment_call, viewPager, false); // View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false);
View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false); // View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false);
View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false); //
// views = new ArrayList<>();
views = new ArrayList<>(); // views.add(view1);
views.add(view1); // views.add(view2);
views.add(view2); // views.add(view3);
views.add(view3); // }
}
// void initPoint() { // void initPoint() {
// imageViews = new ImageView[5];//实例化5个图片 // imageViews = new ImageView[5];//实例化5个图片
@@ -231,6 +303,23 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
//setSubTitle(""); //setSubTitle("");
} }
private class MyPhoneStateListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
LogUtils.d(TAG, "电话已挂断");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
LogUtils.d(TAG, "正在通话中");
break;
case TelephonyManager.CALL_STATE_RINGING:
LogUtils.d(TAG, "来电: " + incomingNumber);
break;
}
}
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
@@ -287,40 +376,25 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
return TAG; return TAG;
} }
@Override // @Override
public Toolbar initToolBar() { // public void onBackPressed() {
return findViewById(R.id.activitymainToolbar1); // exit();
} // }
//
@Override // void exit() {
public boolean isAddWinBollToolBar() { // YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){
return true; //
} // @Override
// public void onYes() {
@Override // WinBollActivityManager.getInstance(getApplicationContext()).finishAll();
public boolean isEnableDisplayHomeAsUp() { // }
return false; //
} // @Override
// public void onNo() {
@Override // }
public void onBackPressed() { // };
exit(); // YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener);
} // }
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) {
@@ -331,11 +405,7 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.item_call) { if (item.getItemId() == R.id.item_settings) {
Intent intent = new Intent(this, CallActivity.class);
startActivity(intent);
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
} else 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); //WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);

View File

@@ -1,35 +0,0 @@
package cc.winboll.studio.contacts;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 21:14:52
* @Describe PhoneCallManager
*/
import android.telecom.Call;
import android.telecom.VideoProfile;
public class PhoneCallManager {
public static final String TAG = "PhoneCallManager";
public static Call call;
/**
* 接听电话
*/
public void answer() {
if (call != null) {
call.answer(VideoProfile.STATE_AUDIO_ONLY);
}
}
/**
* 断开电话,包括来电时的拒接以及接听后的挂断
*/
public void disconnect() {
if (call != null) {
call.disconnect();
}
}
}

View File

@@ -0,0 +1,91 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/31 15:15:54
* @Describe 应用介绍窗口
*/
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.libaes.winboll.APPInfo;
import cc.winboll.studio.libaes.winboll.AboutView;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
public class AboutActivity extends WinBollActivity implements IWinBollActivity {
public static final String TAG = "AboutActivity";
Context mContext;
Toolbar mToolbar;
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
setContentView(R.layout.activity_about);
mToolbar = findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
mToolbar.setSubtitle(TAG);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
AboutView aboutView = CreateAboutView();
// 在 Activity 的 onCreate 或其他生命周期方法中调用
// LinearLayout layout = new LinearLayout(this);
// layout.setOrientation(LinearLayout.VERTICAL);
// // 创建布局参数(宽度和高度)
// ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.MATCH_PARENT
// );
// addContentView(aboutView, params);
LinearLayout layout = findViewById(R.id.aboutviewroot_ll);
// 创建布局参数(宽度和高度)
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
);
layout.addView(aboutView, params);
GlobalApplication.getWinBollActivityManager().add(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
GlobalApplication.getWinBollActivityManager().registeRemove(this);
}
public AboutView CreateAboutView() {
String szBranchName = "contacts";
APPInfo appInfo = new APPInfo();
appInfo.setAppName("Contacts");
appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll);
appInfo.setAppDescription("通讯录与拨号");
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 new AboutView(mContext, appInfo);
}
}

View File

@@ -34,6 +34,7 @@ public class CallActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main); //setContentView(R.layout.activity_main);
setContentView(R.layout.activity_call); setContentView(R.layout.activity_call);

View File

@@ -4,7 +4,6 @@ package cc.winboll.studio.contacts.activities;
* @Author ZhanGSKen@AliYun.Com * @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/21 05:37:42 * @Date 2025/02/21 05:37:42
*/ */
import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
@@ -15,17 +14,33 @@ import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.Switch; import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.adapters.PhoneConnectRuleAdapter;
import cc.winboll.studio.contacts.beans.MainServiceBean;
import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel;
import cc.winboll.studio.contacts.beans.RingTongBean; import cc.winboll.studio.contacts.beans.RingTongBean;
import cc.winboll.studio.libappbase.IWinBollActivity; import cc.winboll.studio.contacts.beans.SettingsModel;
import cc.winboll.studio.libappbase.bean.APPInfo; import cc.winboll.studio.contacts.bobulltoon.TomCat;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.contacts.views.DuInfoTextView;
import cc.winboll.studio.libaes.winboll.APPInfo;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
import com.hjq.toast.ToastUtils; import com.hjq.toast.ToastUtils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.List;
import cc.winboll.studio.contacts.App;
public class SettingsActivity extends AppCompatActivity implements IWinBollActivity { public class SettingsActivity extends AppCompatActivity implements IWinBollActivity {
@@ -33,11 +48,25 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
Toolbar mToolbar; Toolbar mToolbar;
Switch swSilent; Switch swSilent;
SeekBar msbVolume;
TextView mtvVolume;
int mnStreamMaxVolume;
int mnStreamVolume;
Switch mswMainService;
static DuInfoTextView _DuInfoTextView;
@Override // 云盾防御层数量
public APPInfo getAppInfo() { EditText etDunTotalCount;
return null; // 防御层恢复时间间隔(秒钟)
} EditText etDunResumeSecondCount;
// 每次恢复防御层数
EditText etDunResumeCount;
// 是否启用云盾
Switch swIsEnableDun;
private RecyclerView recyclerView;
private PhoneConnectRuleAdapter adapter;
private List<PhoneConnectRuleModel> ruleList;
@Override @Override
public AppCompatActivity getActivity() { public AppCompatActivity getActivity() {
@@ -49,21 +78,6 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
return TAG; return TAG;
} }
@Override
public Toolbar initToolBar() {
return findViewById(R.id.activitymainToolbar1);
}
@Override
public boolean isAddWinBollToolBar() {
return true;
}
@Override
public boolean isEnableDisplayHomeAsUp() {
return false;
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -72,11 +86,136 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
// 初始化工具栏 // 初始化工具栏
mToolbar = findViewById(R.id.activitymainToolbar1); mToolbar = findViewById(R.id.activitymainToolbar1);
setSupportActionBar(mToolbar); setSupportActionBar(mToolbar);
if (isEnableDisplayHomeAsUp()) { // 显示后退按钮
// 显示后退按钮 getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
getSupportActionBar().setSubtitle(getTag()); getSupportActionBar().setSubtitle(getTag());
mswMainService = findViewById(R.id.sw_mainservice);
MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
mswMainService.setChecked(mMainServiceBean.isEnable());
mswMainService.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View arg0) {
LogUtils.d(TAG, "mswMainService onClick");
// TODO: Implement this method
if (mswMainService.isChecked()) {
//ToastUtils.show("Is Checked");
MainService.startMainServiceAndSaveStatus(SettingsActivity.this);
} else {
//ToastUtils.show("Not Checked");
MainService.stopMainServiceAndSaveStatus(SettingsActivity.this);
}
}
});
msbVolume = findViewById(R.id.bellvolume);
mtvVolume = findViewById(R.id.tv_volume);
final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
// 设置SeekBar的最大值为系统铃声音量的最大刻度
mnStreamMaxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_RING);
msbVolume.setMax(mnStreamMaxVolume);
// 获取当前铃声音量并设置为SeekBar的初始进度
mnStreamVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
msbVolume.setProgress(mnStreamVolume);
updateStreamVolumeTextView();
msbVolume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
// 设置铃声音量
audioManager.setStreamVolume(AudioManager.STREAM_RING, progress, 0);
RingTongBean bean = RingTongBean.loadBean(SettingsActivity.this, RingTongBean.class);
if (bean == null) {
bean = new RingTongBean();
}
bean.setStreamVolume(progress);
RingTongBean.saveBean(SettingsActivity.this, bean);
mnStreamVolume = progress;
updateStreamVolumeTextView();
//Toast.makeText(SettingsActivity.this, "音量设置为: " + progress, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// 当开始拖动SeekBar时的操作
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 当停止拖动SeekBar时的操作
}
});
recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
ruleList = Rules.getInstance(this).getPhoneBlacRuleBeanList();
adapter = new PhoneConnectRuleAdapter(this, ruleList);
recyclerView.setAdapter(adapter);
// 设置参数云盾
_DuInfoTextView = findViewById(R.id.tv_DunInfo);
etDunTotalCount = findViewById(R.id.et_DunTotalCount);
etDunResumeSecondCount = findViewById(R.id.et_DunResumeSecondCount);
etDunResumeCount = findViewById(R.id.et_DunResumeCount);
swIsEnableDun = findViewById(R.id.sw_IsEnableDun);
SettingsModel settingsModel = Rules.getInstance(this).getSettingsModel();
etDunTotalCount.setText(Integer.toString(settingsModel.getDunTotalCount()));
etDunResumeSecondCount.setText(Integer.toString(settingsModel.getDunResumeSecondCount()));
etDunResumeCount.setText(Integer.toString(settingsModel.getDunResumeCount()));
swIsEnableDun.setChecked(settingsModel.isEnableDun());
boolean isEnableDun = settingsModel.isEnableDun();
etDunTotalCount.setEnabled(!isEnableDun);
etDunResumeSecondCount.setEnabled(!isEnableDun);
etDunResumeCount.setEnabled(!isEnableDun);
}
public static void notifyDunInfoUpdate() {
if (_DuInfoTextView != null) {
_DuInfoTextView.notifyInfoUpdate();
}
}
public void onSW_IsEnableDun(View view) {
LogUtils.d(TAG, "onSW_IsEnableDun");
boolean isEnableDun = swIsEnableDun.isChecked();
etDunTotalCount.setEnabled(!isEnableDun);
etDunResumeSecondCount.setEnabled(!isEnableDun);
etDunResumeCount.setEnabled(!isEnableDun);
SettingsModel settingsModel = Rules.getInstance(this).getSettingsModel();
if (isEnableDun) {
settingsModel.setDunTotalCount(Integer.parseInt(etDunTotalCount.getText().toString()));
settingsModel.setDunResumeSecondCount(Integer.parseInt(etDunResumeSecondCount.getText().toString()));
settingsModel.setDunResumeCount(Integer.parseInt(etDunResumeCount.getText().toString()));
}
settingsModel.setIsEnableDun(isEnableDun);
Rules.getInstance(this).saveDun();
Rules.getInstance(this).reload();
}
void updateStreamVolumeTextView() {
mtvVolume.setText(String.format("%d/%d", mnStreamVolume, mnStreamMaxVolume));
}
public void onUnitTest(View view) {
Intent intent = new Intent(this, UnitTestActivity.class);
startActivity(intent);
}
public void onAddNewConnectionRule(View view) {
Rules.getInstance(this).getPhoneBlacRuleBeanList().add(new PhoneConnectRuleModel());
Rules.getInstance(this).saveRules();
adapter.notifyDataSetChanged();
} }
public void onDefaultPhone(View view) { public void onDefaultPhone(View view) {
@@ -94,6 +233,38 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
} }
} }
public void onDownloadBoBullToon(View view) {
final TomCat tomCat = TomCat.getInstance(this);
new Thread(new Runnable() {
@Override
public void run() {
if (tomCat.downloadBoBullToon()) {
ToastUtils.show("BoBullToon downlaod OK!");
MainService.restartMainService(SettingsActivity.this);
Rules.getInstance(SettingsActivity.this).reload();
}
}
}).start();
}
public void onSearchBoBullToonPhone(View view) {
TomCat tomCat = TomCat.getInstance(this);
EditText etPhone = findViewById(R.id.activitysettingsEditText1);
String phone = etPhone.getText().toString().trim();
if (tomCat.loadPhoneBoBullToon()) {
if (tomCat.isPhoneBoBullToon(phone)) {
ToastUtils.show("It is a BoBullToon Phone!");
} else {
ToastUtils.show("Not in BoBullToon.");
}
} else {
ToastUtils.show("没有下载 BoBullToon。");
}
}
private void askForDrawOverlay() { private void askForDrawOverlay() {
AlertDialog alertDialog = new AlertDialog.Builder(this) AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("允许显示悬浮框") .setTitle("允许显示悬浮框")
@@ -140,4 +311,8 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
} }
} }
} }
public void onAbout(View view) {
App.getWinBollActivityManager().startWinBollActivity(this, AboutActivity.class);
}
} }

View File

@@ -0,0 +1,94 @@
package cc.winboll.studio.contacts.activities;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.utils.IntUtils;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/02 16:07:04
*/
public class UnitTestActivity extends Activity {
public static final String TAG = "UnitTestActivity";
LogView logView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_unittest);
logView = findViewById(R.id.logview);
logView.start();
}
public void onTestPhone(View view) {
// 开始测试数据
EditText etPhone = findViewById(R.id.phone_et);
Rules rules = Rules.getInstance(this);
String phone = etPhone.getText().toString().trim();
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
}
public void onTestMain(View view) {
LogUtils.d(TAG, "IntUtils.unittest_getIntInRange();");
IntUtils.unittest_getIntInRange();
Rules rules = Rules.getInstance(this);
// 如果没有规则就添加测试规则
if (rules.getPhoneBlacRuleBeanList().size() == 0) {
// 手机号码允许
// 中国手机号码正则表达式以1开头第二位可以是3、4、5、6、7、8、9后面跟9位数字
String regex = "^1[3-9]\\d{9}$";
rules.add(regex, true, true);
// 指定区号号码允许
regex = "^0660\\d+$";
rules.add(regex, true, true);
// 指定区号号码允许
regex = "^020\\d+$";
rules.add(regex, true, true);
// 添加默认拒接规则
regex = ".*";
rules.add(regex, false, true);
// 保存规则到文件
rules.saveRules();
}
// 开始测试数据
String phone = "16769764848";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "16856582777";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "17519703124";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "0205658955";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "0108965253";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "+8616769764848";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "4005816769764848";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "95566";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
}
}

View File

@@ -1,18 +1,18 @@
package cc.winboll.studio.aes; package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/31 15:16:45
* @Describe 应用窗口基类
*/
import android.app.Activity; import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libaes.beans.AESThemeBean; import cc.winboll.studio.libaes.beans.AESThemeBean;
import cc.winboll.studio.libaes.utils.AESThemeUtil; import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity; import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
import android.view.MenuItem;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/30 00:34:02
* @Describe WinBoll 活动窗口通用基类
*/
public class WinBollActivity extends AppCompatActivity implements IWinBollActivity { public class WinBollActivity extends AppCompatActivity implements IWinBollActivity {
public static final String TAG = "WinBollActivity"; public static final String TAG = "WinBollActivity";

View File

@@ -0,0 +1,93 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/26 13:09:32
* @Describe CallLogAdapter
*/
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.beans.CallLogModel;
import cc.winboll.studio.contacts.utils.ContactUtils;
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
import com.hjq.toast.ToastUtils;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogViewHolder> {
public static final String TAG = "CallLogAdapter";
private List<CallLogModel> callLogList;
ContactUtils mContactUtils;
Context mContext;
public CallLogAdapter(Context context, List<CallLogModel> callLogList) {
mContext = context;
this.mContactUtils = ContactUtils.getInstance(mContext);
this.callLogList = callLogList;
}
@NonNull
@Override
public CallLogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_call_log, parent, false);
return new CallLogViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull CallLogViewHolder holder, int position) {
final CallLogModel callLog = callLogList.get(position);
holder.phoneNumber.setText(callLog.getPhoneNumber() + "" + mContactUtils.getContactsName(callLog.getPhoneNumber()));
holder.callStatus.setText(callLog.getCallStatus());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
holder.callDate.setText(dateFormat.format(callLog.getCallDate()));
// 初始化拉动后拨号控件
holder.dialAOHPCTCSeekBar.setThumb(holder.itemView.getContext().getDrawable(R.drawable.ic_call));
holder.dialAOHPCTCSeekBar.setBlurRightDP(80);
holder.dialAOHPCTCSeekBar.setThumbOffset(0);
holder.dialAOHPCTCSeekBar.setOnOHPCListener(
new AOHPCTCSeekBar.OnOHPCListener(){
@Override
public void onOHPCommit() {
String phoneNumber = callLog.getPhoneNumber().replaceAll("\\s", "");
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);
holder.itemView.getContext().startActivity(intent);
}
});
}
@Override
public int getItemCount() {
return callLogList.size();
}
public class CallLogViewHolder extends RecyclerView.ViewHolder {
TextView phoneNumber, callStatus, callDate;
Button dialButton;
AOHPCTCSeekBar dialAOHPCTCSeekBar;
public CallLogViewHolder(@NonNull View itemView) {
super(itemView);
phoneNumber = itemView.findViewById(R.id.phone_number);
callStatus = itemView.findViewById(R.id.call_status);
callDate = itemView.findViewById(R.id.call_date);
dialAOHPCTCSeekBar = itemView.findViewById(R.id.aohpctcseekbar_dial);
}
}
}

View File

@@ -0,0 +1,84 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/26 13:35:44
* @Describe ContactAdapter
*/
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.beans.ContactModel;
import com.hjq.toast.ToastUtils;
import java.util.List;
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactViewHolder> {
public static final String TAG = "ContactAdapter";
private static final int REQUEST_CALL_PHONE = 1;
private List<ContactModel> contactList;
public ContactAdapter(List<ContactModel> contactList) {
this.contactList = contactList;
}
@NonNull
@Override
public ContactViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_contact, parent, false);
return new ContactViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ContactViewHolder holder, int position) {
final ContactModel contact = contactList.get(position);
holder.contactName.setText(contact.getName());
holder.contactNumber.setText(contact.getNumber());
// 初始化拉动后拨号控件
holder.dialAOHPCTCSeekBar.setThumb(holder.itemView.getContext().getDrawable(R.drawable.ic_call));
holder.dialAOHPCTCSeekBar.setBlurRightDP(80);
holder.dialAOHPCTCSeekBar.setThumbOffset(0);
holder.dialAOHPCTCSeekBar.setOnOHPCListener(
new AOHPCTCSeekBar.OnOHPCListener(){
@Override
public void onOHPCommit() {
String phoneNumber = contact.getNumber().replaceAll("\\s", "");
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);
holder.itemView.getContext().startActivity(intent);
}
});
}
@Override
public int getItemCount() {
return contactList.size();
}
public class ContactViewHolder extends RecyclerView.ViewHolder {
TextView contactName;
TextView contactNumber;
AOHPCTCSeekBar dialAOHPCTCSeekBar;
public ContactViewHolder(@NonNull View itemView) {
super(itemView);
contactName = itemView.findViewById(R.id.contact_name);
contactNumber = itemView.findViewById(R.id.contact_number);
dialAOHPCTCSeekBar = itemView.findViewById(R.id.aohpctcseekbar_dial);
}
}
}

View File

@@ -1,42 +0,0 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 13:33:04
* @Describe MyPagerAdapter
*/
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import cc.winboll.studio.contacts.fragments.CallFragment;
import cc.winboll.studio.contacts.fragments.ContactsFragment;
import cc.winboll.studio.contacts.fragments.LogFragment;
public class MyPagerAdapter extends FragmentPagerAdapter {
public static final String TAG = "MyPagerAdapter";
private static final int PAGE_COUNT = 3;
public MyPagerAdapter(@NonNull FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@NonNull
@Override
public Fragment getItem(int position) {
if(position == 1) {
return ContactsFragment.newInstance(position);
} else if(position == 2) {
return LogFragment.newInstance(position);
} else {
return CallFragment.newInstance(position);
}
}
@Override
public int getCount() {
return PAGE_COUNT;
}
}

View File

@@ -0,0 +1,247 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/02 17:27:41
* @Describe PhoneConnectRuleAdapter
*/
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.views.LeftScrollView;
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
import com.hjq.toast.ToastUtils;
import java.util.ArrayList;
import java.util.List;
public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static final String TAG = "PhoneConnectRuleAdapter";
private static final int VIEW_TYPE_SIMPLE = 0;
private static final int VIEW_TYPE_EDIT = 1;
private Context context;
private List<PhoneConnectRuleModel> ruleList;
public PhoneConnectRuleAdapter(Context context, List<PhoneConnectRuleModel> ruleList) {
this.context = context;
this.ruleList = ruleList;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
if (viewType == VIEW_TYPE_SIMPLE) {
View view = inflater.inflate(R.layout.view_phone_connect_rule_simple, parent, false);
return new SimpleViewHolder(parent, view);
} else {
View view = inflater.inflate(R.layout.view_phone_connect_rule, parent, false);
return new EditViewHolder(parent, view);
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
final PhoneConnectRuleModel model = ruleList.get(position);
if (holder instanceof SimpleViewHolder) {
final SimpleViewHolder simpleViewHolder = (SimpleViewHolder) holder;
String szView = model.getRuleText().trim().equals("") ?"[NULL]": model.getRuleText();
simpleViewHolder.tvRuleText.setText(szView);
simpleViewHolder.scrollView.setOnActionListener(new LeftScrollView.OnActionListener(){
@Override
public void onUp() {
ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList();
if (position > 0) {
ToastUtils.show("onUp");
simpleViewHolder.scrollView.smoothScrollTo(0, 0);
// PhoneConnectRuleModel newBean = new PhoneConnectRuleModel();
// newBean.setRuleText(list.get(position).getRuleText());
// newBean.setIsAllowConnection(list.get(position).isAllowConnection());
// newBean.setIsEnable(list.get(position).isEnable());
// newBean.setIsSimpleView(list.get(position).isSimpleView());
list.add(position - 1, list.get(position));
list.remove(position + 1);
Rules.getInstance(context).saveRules();
notifyDataSetChanged();
}
}
@Override
public void onDown() {
ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList();
if (position < list.size() - 1) {
ToastUtils.show("onDown");
simpleViewHolder.scrollView.smoothScrollTo(0, 0);
// PhoneConnectRuleModel newBean = new PhoneConnectRuleModel();
// newBean.setRuleText(list.get(position).getRuleText());
// newBean.setIsAllowConnection(list.get(position).isAllowConnection());
// newBean.setIsEnable(list.get(position).isEnable());
// newBean.setIsSimpleView(list.get(position).isSimpleView());
list.add(position + 2, list.get(position));
list.remove(position);
Rules.getInstance(context).saveRules();
notifyDataSetChanged();
}
}
@Override
public void onEdit() {
simpleViewHolder.scrollView.smoothScrollTo(0, 0);
model.setIsSimpleView(false);
notifyDataSetChanged();
//notifyItemChanged(position);
}
@Override
public void onDelete() {
YesNoAlertDialog.show(simpleViewHolder.scrollView.getContext(), "删除确认", "是否删除该通话规则?", new YesNoAlertDialog.OnDialogResultListener(){
@Override
public void onYes() {
simpleViewHolder.scrollView.smoothScrollTo(0, 0);
model.setIsSimpleView(true);
ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList();
list.remove(position);
Rules.getInstance(context).saveRules();
notifyDataSetChanged();
//notifyItemChanged(position);
}
@Override
public void onNo() {
}
});
}
});
// simpleViewHolder.editButton.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// model.setIsSimpleView(false);
// notifyItemChanged(position);
// }
// });
// simpleViewHolder.deleteButton.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// model.setIsSimpleView(false);
// ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList();
// list.remove(position);
// Rules.getInstance(context).saveRules();
// notifyItemChanged(position);
// }
// });
// // 触摸事件处理
// simpleViewHolder.contentLayout.setOnTouchListener(new View.OnTouchListener() {
// @Override
// public boolean onTouch(View v, MotionEvent event) {
// switch (event.getAction()) {
// case MotionEvent.ACTION_DOWN:
// simpleViewHolder.startX = event.getX();
// simpleViewHolder.isSwiping = true;
// break;
// case MotionEvent.ACTION_MOVE:
// if (simpleViewHolder.isSwiping) {
// float deltaX = simpleViewHolder.startX - event.getX();
// if (deltaX > 0) { // 左滑
// float translationX = Math.max(-simpleViewHolder.actionLayout.getWidth(), -deltaX);
// simpleViewHolder.contentLayout.setTranslationX(translationX);
// simpleViewHolder.actionLayout.setVisibility(View.VISIBLE);
// }
// }
// break;
// case MotionEvent.ACTION_UP:
// simpleViewHolder.isSwiping = false;
// if (simpleViewHolder.contentLayout.getTranslationX() < -simpleViewHolder.actionLayout.getWidth() / 2) {
// // 保持按钮显示
// simpleViewHolder.contentLayout.setTranslationX(-actionLayout.getWidth());
// } else {
// // 恢复原状
// simpleViewHolder.contentLayout.animate().translationX(0).setDuration(200).start();
// simpleViewHolder.actionLayout.setVisibility(View.INVISIBLE);
// }
// break;
// }
// return true;
// }
// });
} else if (holder instanceof EditViewHolder) {
final EditViewHolder editViewHolder = (EditViewHolder) holder;
editViewHolder.editText.setText(model.getRuleText());
editViewHolder.checkBoxAllow.setChecked(model.isAllowConnection());
editViewHolder.checkBoxEnable.setChecked(model.isEnable());
editViewHolder.buttonConfirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
model.setRuleText(editViewHolder.editText.getText().toString());
model.setIsAllowConnection(editViewHolder.checkBoxAllow.isChecked());
model.setIsEnable(editViewHolder.checkBoxEnable.isChecked());
model.setIsSimpleView(true);
Rules.getInstance(context).saveRules();
notifyItemChanged(position);
Toast.makeText(context, "保存成功", Toast.LENGTH_SHORT).show();
}
});
}
}
@Override
public int getItemCount() {
return ruleList.size();
}
@Override
public int getItemViewType(int position) {
PhoneConnectRuleModel model = ruleList.get(position);
// 这里可以根据模型的状态来决定视图类型,简单起见,假设点击按钮后进入编辑视图
return model.isSimpleView() ? VIEW_TYPE_SIMPLE : VIEW_TYPE_EDIT;
}
static class SimpleViewHolder extends RecyclerView.ViewHolder {
private final LeftScrollView scrollView;
private final TextView tvRuleText;
public SimpleViewHolder(@NonNull ViewGroup parent, @NonNull View itemView) {
super(itemView);
scrollView = itemView.findViewById(R.id.scrollView);
//tvRuleText = itemView.findViewById(R.id.ruletext_tv);
tvRuleText = new TextView(itemView.getContext());
scrollView.setContentWidth(parent.getWidth());
//scrollView.setContentWidth(600);
scrollView.addContentLayout(tvRuleText);
}
}
static class EditViewHolder extends RecyclerView.ViewHolder {
EditText editText;
CheckBox checkBoxAllow;
CheckBox checkBoxEnable;
Button buttonConfirm;
public EditViewHolder(@NonNull ViewGroup parent, @NonNull View itemView) {
super(itemView);
editText = itemView.findViewById(R.id.edit_text);
checkBoxAllow = itemView.findViewById(R.id.checkbox_allow);
checkBoxEnable = itemView.findViewById(R.id.checkbox_enable);
buttonConfirm = itemView.findViewById(R.id.button_confirm);
}
}
}

View File

@@ -0,0 +1,36 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/26 13:10:57
* @Describe CallLogModel
*/
import java.util.Date;
public class CallLogModel {
public static final String TAG = "CallLogModel";
private String phoneNumber;
private String callStatus;
private Date callDate;
public CallLogModel(String phoneNumber, String callStatus, Date callDate) {
this.phoneNumber = phoneNumber.replaceAll("\\s", "");
this.callStatus = callStatus;
this.callDate = callDate;
}
public String getPhoneNumber() {
return phoneNumber;
}
public String getCallStatus() {
return callStatus;
}
public Date getCallDate() {
return callDate;
}
}

View File

@@ -0,0 +1,64 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/26 13:37:00
* @Describe ContactModel
*/
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
public class ContactModel {
public static final String TAG = "ContactModel";
private String name;
private String number;
private String pinyin;
public ContactModel(String name, String number) {
this.name = name;
this.number = number.replaceAll("\\s", "");
this.pinyin = convertToPinyin(name);
}
private String convertToPinyin(String chinese) {
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
StringBuilder pinyin = 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) {
pinyin.append(pinyinArray[0]);
}
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
pinyin.append(ch);
}
}
return pinyin.toString();
}
public String getName() {
return name;
}
public String getNumber() {
return number;
}
public String getPinyin() {
return pinyin;
}
}

View File

@@ -10,21 +10,35 @@ import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean; import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException; import java.io.IOException;
public class PhoneBlackRuleBean extends BaseBean { public class PhoneConnectRuleModel extends BaseBean {
public static final String TAG = "PhoneBlackRuleBean"; public static final String TAG = "PhoneConnectRuleModel";
String ruleText; String ruleText;
boolean isAllowConnection;
boolean isEnable; boolean isEnable;
boolean isSimpleView;
public PhoneBlackRuleBean() {
public PhoneConnectRuleModel() {
this.ruleText = ""; this.ruleText = "";
this.isAllowConnection = false;
this.isEnable = false; this.isEnable = false;
this.isSimpleView = true;
} }
public PhoneBlackRuleBean(String ruleText, boolean isEnable) { public PhoneConnectRuleModel(String ruleText, boolean isAllowConnection, boolean isEnable) {
this.ruleText = ruleText; this.ruleText = ruleText;
this.isAllowConnection = isAllowConnection;
this.isEnable = isEnable; this.isEnable = isEnable;
this.isSimpleView = true;
}
public void setIsSimpleView(boolean isSimpleView) {
this.isSimpleView = isSimpleView;
}
public boolean isSimpleView() {
return isSimpleView;
} }
public void setRuleText(String ruleText) { public void setRuleText(String ruleText) {
@@ -35,6 +49,14 @@ public class PhoneBlackRuleBean extends BaseBean {
return ruleText; return ruleText;
} }
public void setIsAllowConnection(boolean isAllowConnection) {
this.isAllowConnection = isAllowConnection;
}
public boolean isAllowConnection() {
return isAllowConnection;
}
public void setIsEnable(boolean isEnable) { public void setIsEnable(boolean isEnable) {
this.isEnable = isEnable; this.isEnable = isEnable;
} }
@@ -43,17 +65,19 @@ public class PhoneBlackRuleBean extends BaseBean {
return isEnable; return isEnable;
} }
@Override @Override
public String getName() { public String getName() {
return PhoneBlackRuleBean.class.getName(); return PhoneConnectRuleModel.class.getName();
} }
@Override @Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter); super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("ruleText").value(getRuleText()); jsonWriter.name("ruleText").value(getRuleText());
jsonWriter.name("isAllowConnection").value(isAllowConnection());
jsonWriter.name("isEnable").value(isEnable()); jsonWriter.name("isEnable").value(isEnable());
} }
@Override @Override
@@ -61,6 +85,8 @@ public class PhoneBlackRuleBean extends BaseBean {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("ruleText")) { if (name.equals("ruleText")) {
setRuleText(jsonReader.nextString()); setRuleText(jsonReader.nextString());
} else if (name.equals("isAllowConnection")) {
setIsAllowConnection(jsonReader.nextBoolean());
} else if (name.equals("isEnable")) { } else if (name.equals("isEnable")) {
setIsEnable(jsonReader.nextBoolean()); setIsEnable(jsonReader.nextBoolean());
} else { } else {
@@ -83,6 +109,6 @@ public class PhoneBlackRuleBean extends BaseBean {
jsonReader.endObject(); jsonReader.endObject();
return this; return this;
} }
} }

View File

@@ -12,26 +12,26 @@ import android.media.AudioManager;
import android.util.JsonReader; import android.util.JsonReader;
public class RingTongBean extends BaseBean { public class RingTongBean extends BaseBean {
public static final String TAG = "AudioRingTongBean"; public static final String TAG = "AudioRingTongBean";
// 模式 // 铃声音量
int ringerMode; int streamVolume;
public RingTongBean() { public RingTongBean() {
this.ringerMode = AudioManager.RINGER_MODE_NORMAL; this.streamVolume = 100;
} }
public RingTongBean(int ringerMode) { public RingTongBean(int streamVolume) {
this.ringerMode = ringerMode; this.streamVolume = streamVolume;
} }
public void setRingerMode(int ringerMode) { public void setStreamVolume(int streamVolume) {
this.ringerMode = ringerMode; this.streamVolume = streamVolume;
} }
public int getRingerMode() { public int getStreamVolume() {
return ringerMode; return streamVolume;
} }
@Override @Override
@@ -42,15 +42,15 @@ public class RingTongBean extends BaseBean {
@Override @Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter); super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("ringerMode").value(getRingerMode()); jsonWriter.name("streamVolume").value(getStreamVolume());
} }
@Override @Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException { public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("ringerMode")) { if (name.equals("streamVolume")) {
setRingerMode(jsonReader.nextInt()); setStreamVolume(jsonReader.nextInt());
} else { } else {
return false; return false;
} }

View File

@@ -0,0 +1,141 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/02 19:51:40
* @Describe SettingsModel
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
import cc.winboll.studio.contacts.utils.IntUtils;
public class SettingsModel extends BaseBean {
public static final String TAG = "SettingsModel";
public static final int MAX_INTRANGE = 666666;
public static final int MIN_INTRANGE = 1;
// 云盾防御层数量
int dunTotalCount;
// 当前云盾防御层
int dunCurrentCount;
// 防御层恢复时间间隔(秒钟)
int dunResumeSecondCount;
// 每次恢复防御层数
int dunResumeCount;
// 是否启用云盾
boolean isEnableDun;
public SettingsModel() {
this.dunTotalCount = 6;
this.dunCurrentCount = 6;
this.dunResumeSecondCount = 60;
this.dunResumeCount = 1;
this.isEnableDun = false;
}
public SettingsModel(int dunTotalCount, int dunCurrentCount, int dunResumeSecondCount, int dunResumeCount, boolean isEnableDun) {
this.dunTotalCount = getSettingsModelRangeInt(dunTotalCount);
this.dunCurrentCount = getSettingsModelRangeInt(dunCurrentCount);
this.dunResumeSecondCount = getSettingsModelRangeInt(dunResumeSecondCount);
this.dunResumeCount = getSettingsModelRangeInt(dunResumeCount);
this.isEnableDun = isEnableDun;
}
public void setDunTotalCount(int dunTotalCount) {
this.dunTotalCount = getSettingsModelRangeInt(dunTotalCount);
}
public int getDunTotalCount() {
return dunTotalCount;
}
public void setDunCurrentCount(int dunCurrentCount) {
this.dunCurrentCount = getSettingsModelRangeInt(dunCurrentCount);
}
public int getDunCurrentCount() {
return dunCurrentCount;
}
public void setDunResumeSecondCount(int dunResumeSecondCount) {
this.dunResumeSecondCount = getSettingsModelRangeInt(dunResumeSecondCount);
}
public int getDunResumeSecondCount() {
return dunResumeSecondCount;
}
public void setDunResumeCount(int dunResumeCount) {
this.dunResumeCount = getSettingsModelRangeInt(dunResumeCount);
}
public int getDunResumeCount() {
return dunResumeCount;
}
public void setIsEnableDun(boolean isEnableDun) {
this.isEnableDun = isEnableDun;
}
public boolean isEnableDun() {
return isEnableDun;
}
int getSettingsModelRangeInt(int origin) {
return IntUtils.getIntInRange(origin, MIN_INTRANGE, MAX_INTRANGE);
}
@Override
public String getName() {
return SettingsModel.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("dunTotalCount").value(getDunTotalCount());
jsonWriter.name("dunCurrentCount").value(getDunCurrentCount());
jsonWriter.name("dunResumeSecondCount").value(getDunResumeSecondCount());
jsonWriter.name("dunResumeCount").value(getDunResumeCount());
jsonWriter.name("isEnableDun").value(isEnableDun());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("dunTotalCount")) {
setDunTotalCount(getSettingsModelRangeInt(jsonReader.nextInt()));
} else if (name.equals("dunCurrentCount")) {
setDunCurrentCount(getSettingsModelRangeInt(jsonReader.nextInt()));
} else if (name.equals("dunResumeSecondCount")) {
setDunResumeSecondCount(getSettingsModelRangeInt(jsonReader.nextInt()));
} else if (name.equals("dunResumeCount")) {
setDunResumeCount(getSettingsModelRangeInt(jsonReader.nextInt()));
} else if (name.equals("isEnableDun")) {
setIsEnableDun(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;
}
}

View File

@@ -0,0 +1,178 @@
package cc.winboll.studio.contacts.bobulltoon;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/02 13:47:48
* @Describe 汤姆猫管家 :使用 BoBullToon 项目,对通讯地址进行筛选判断的好朋友。
*/
import android.content.Context;
import cc.winboll.studio.libappbase.LogUtils;
import com.hjq.toast.ToastUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class TomCat {
public static final String TAG = "TomCat";
List<String> listPhoneBoBullToon = new ArrayList<String>();
static volatile TomCat _TomCat;
Context mContext;
TomCat(Context context) {
mContext = context;
}
public static synchronized TomCat getInstance(Context context) {
if (_TomCat == null) {
_TomCat = new TomCat(context);
}
return _TomCat;
}
void downloadAndExtractZip(String zipUrl, String destinationFolder) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(zipUrl)
.build();
try {
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
// 下载 ZIP 文件到临时位置
File tempZipFile = File.createTempFile("temp", ".zip");
try {
InputStream inputStream = response.body().byteStream();
FileOutputStream outputStream = new FileOutputStream(tempZipFile);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
// 解压 ZIP 文件到指定文件夹
try {
ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(tempZipFile.toPath()));
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
Path targetFilePath = Paths.get(destinationFolder, zipEntry.getName());
if (zipEntry.isDirectory()) {
Files.createDirectories(targetFilePath);
} else {
Files.createDirectories(targetFilePath.getParent());
try (FileOutputStream fos = new FileOutputStream(targetFilePath.toFile())) {
byte[] buffer = new byte[1024];
int len;
while ((len = zipInputStream.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
}
}
zipInputStream.closeEntry();
}
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
// 删除临时 ZIP 文件
tempZipFile.delete();
LogUtils.d(TAG, "已更新 BoBullToon 数据");
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
public boolean downloadBoBullToon() {
String zipUrl = "http://10.8.0.12:3000/Studio/BoBullToon/archive/main.zip"; // 替换为实际的 ZIP 文件 URL
String destinationFolder = getWorkingFolder().getPath(); // 替换为实际的目标文件夹路径
try {
// 删除旧文件
File fOldFolder = new File(destinationFolder);
if (fOldFolder.exists()) {
deleteFolderRecursive(fOldFolder);
fOldFolder.mkdirs();
LogUtils.d(TAG, "已清空 BoBullToon 数据");
}
// 更新新文件
downloadAndExtractZip(zipUrl, destinationFolder);
LogUtils.d(TAG, "ZIP 文件下载并解压成功。");
return true;
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
return false;
}
// 递归删除文件夹及其内容的方法
public static void deleteFolderRecursive(File file) {
// 判断是否为文件夹
if (file.isDirectory()) {
// 列出文件夹中的所有文件和子文件夹
File[] files = file.listFiles();
if (files != null) {
// 遍历并递归删除每个文件和子文件夹
for (File f : files) {
deleteFolderRecursive(f);
}
}
}
// 删除文件或空文件夹
file.delete();
}
File getWorkingFolder() {
return mContext.getExternalFilesDir(TAG);
}
public boolean loadPhoneBoBullToon() {
listPhoneBoBullToon.clear();
File fBoBullToon = new File(getWorkingFolder(), "bobulltoon");
if (fBoBullToon.exists()) {
LogUtils.d(TAG, String.format("getWorkingFolder() %s", getWorkingFolder()));
for (File userFolder : fBoBullToon.listFiles()) {
if (userFolder.isDirectory()) {
for (File recordFile : userFolder.listFiles()) {
listPhoneBoBullToon.add(recordFile.getName());
}
}
}
for (int i = 0; i < listPhoneBoBullToon.size(); i++) {
LogUtils.d(TAG, String.format("listPhoneBoBullToon add : %s", listPhoneBoBullToon.get(i)));
}
return true;
} else {
LogUtils.d(TAG, "fBoBullToon not exists。");
}
return false;
}
public boolean isPhoneBoBullToon(String phone) {
for (int i = 0; i < listPhoneBoBullToon.size(); i++) {
LogUtils.d(TAG, String.format("isPhoneBoBullToon(...) get(i) phone : %s", listPhoneBoBullToon.get(i)));
if (listPhoneBoBullToon.get(i).equals(phone)) {
return true;
}
}
return false;
}
}

View File

@@ -5,25 +5,36 @@ package cc.winboll.studio.contacts.dun;
* @Date 2025/02/21 06:15:10 * @Date 2025/02/21 06:15:10
* @Describe 云盾防御规则 * @Describe 云盾防御规则
*/ */
import cc.winboll.studio.contacts.beans.PhoneBlackRuleBean;
import java.util.ArrayList;
import java.util.regex.Pattern;
import android.content.Context; import android.content.Context;
import cc.winboll.studio.contacts.activities.SettingsActivity;
import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel;
import cc.winboll.studio.contacts.beans.SettingsModel;
import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.contacts.utils.ContactUtils;
import cc.winboll.studio.contacts.utils.IntUtils;
import cc.winboll.studio.contacts.utils.RegexPPiUtils;
import cc.winboll.studio.libappbase.LogUtils;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Pattern;
public class Rules { public class Rules {
public static final String TAG = "Rules"; public static final String TAG = "Rules";
ArrayList<PhoneBlackRuleBean> _PhoneBlacRuleBeanList; ArrayList<PhoneConnectRuleModel> _PhoneConnectRuleModelList;
static volatile Rules _Rules; static volatile Rules _Rules;
Context mContext; Context mContext;
SettingsModel mSettingsModel;
Timer mDunResumeTimer;
Rules(Context context) { Rules(Context context) {
mContext = context; mContext = context;
_PhoneBlacRuleBeanList = new ArrayList<PhoneBlackRuleBean>(); _PhoneConnectRuleModelList = new ArrayList<PhoneConnectRuleModel>();
PhoneBlackRuleBean.loadBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class); reload();
} }
public static synchronized Rules getInstance(Context context) { public static synchronized Rules getInstance(Context context) {
if (_Rules == null) { if (_Rules == null) {
_Rules = new Rules(context); _Rules = new Rules(context);
@@ -31,46 +42,168 @@ public class Rules {
return _Rules; return _Rules;
} }
public void reload() {
LogUtils.d(TAG, "reload()");
loadRules();
loadDun();
setDunResumTimer();
}
public void setDunResumTimer() {
if (mDunResumeTimer != null) {
mDunResumeTimer.cancel();
}
// 盾牌恢复定时器
mDunResumeTimer = new Timer();
int ss = IntUtils.getIntInRange(mSettingsModel.getDunResumeSecondCount() * 1000, SettingsModel.MIN_INTRANGE, SettingsModel.MAX_INTRANGE);
mDunResumeTimer.schedule(new TimerTask() {
@Override
public void run() {
if (mSettingsModel.getDunCurrentCount() != mSettingsModel.getDunTotalCount()) {
LogUtils.d(TAG, String.format("当前防御值为%d最大防御值为%d", mSettingsModel.getDunCurrentCount(), mSettingsModel.getDunTotalCount()));
int newDunCount = mSettingsModel.getDunCurrentCount() + mSettingsModel.getDunResumeCount();
// 设置盾值在[0DunTotalCount]之内其他值一律重置为 DunTotalCount。
newDunCount = (newDunCount > mSettingsModel.getDunTotalCount()) ?mSettingsModel.getDunTotalCount(): newDunCount;
mSettingsModel.setDunCurrentCount(newDunCount);
LogUtils.d(TAG, String.format("设置防御值为%d", newDunCount));
saveDun();
SettingsActivity.notifyDunInfoUpdate();
}
}
}, 1000, ss);
}
public void loadRules() {
_PhoneConnectRuleModelList.clear();
PhoneConnectRuleModel.loadBeanList(mContext, _PhoneConnectRuleModelList, PhoneConnectRuleModel.class);
}
public void saveRules() {
LogUtils.d(TAG, String.format("saveRules()"));
PhoneConnectRuleModel.saveBeanList(mContext, _PhoneConnectRuleModelList, PhoneConnectRuleModel.class);
}
public void loadDun() {
mSettingsModel = SettingsModel.loadBean(mContext, SettingsModel.class);
if (mSettingsModel == null) {
mSettingsModel = new SettingsModel();
SettingsModel.saveBean(mContext, mSettingsModel);
}
}
public void saveDun() {
LogUtils.d(TAG, String.format("saveDun()"));
SettingsModel.saveBean(mContext, mSettingsModel);
}
public boolean isAllowed(String phoneNumber) { public boolean isAllowed(String phoneNumber) {
// 黑名单拒接 // 没有启用云盾,默认允许接通任何电话
for (int i = 0; i < _PhoneBlacRuleBeanList.size(); i++) { if (!mSettingsModel.isEnableDun()) {
if (_PhoneBlacRuleBeanList.get(i).isEnable()) { LogUtils.d(TAG, String.format("没有启用云盾默认允许接通任何电话。isAllowed(...) return true"));
String regex = _PhoneBlacRuleBeanList.get(i).getRuleText(); return true;
if (Pattern.matches(regex, phoneNumber)) { }
return false;
//
// 以下是云盾防御体系
boolean isDefend = false; // 盾牌是否生效
boolean isConnect = true; // 防御结果是否连接
// 如果盾值小于1则解除防御
if (!isDefend && mSettingsModel.getDunCurrentCount() < 1) {
// 盾层为1以下防御解除
LogUtils.d(TAG, "盾层为1以下防御解除");
isDefend = true;
isConnect = true;
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
}
// 正则运算预防针
if (!isDefend && !RegexPPiUtils.isPPiOK(phoneNumber)) {
LogUtils.d(TAG, "正则运算预防针生效。");
isDefend = true;
isConnect = false;
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
}
// 查询通讯录是否有该联系人
boolean isPhoneInContacts = ContactUtils.getInstance(mContext).isPhoneInContacts(mContext, phoneNumber);
if (!isDefend) {
if (isPhoneInContacts) {
LogUtils.d(TAG, String.format("Phone %s is in contacts.", phoneNumber));
isDefend = true;
isConnect = true;
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
} else {
LogUtils.d(TAG, String.format("Phone %s is not in contacts.", phoneNumber));
}
}
// 检验拨不通号码群
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) {
for (int i = 0; i < _PhoneConnectRuleModelList.size(); i++) {
if (_PhoneConnectRuleModelList.get(i).isEnable()) {
String regex = _PhoneConnectRuleModelList.get(i).getRuleText();
if (Pattern.matches(regex, phoneNumber)) {
LogUtils.d(TAG, String.format("Phone Number [%s] is matched by rule : %s", phoneNumber, _PhoneConnectRuleModelList.get(i)));
isDefend = true;
isConnect = _PhoneConnectRuleModelList.get(i).isAllowConnection();
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
break;
}
} }
} }
} }
// 手机号码允许 if (isConnect) {
// 中国手机号码正则表达式以1开头第二位可以是3、4、5、6、7、8、9后面跟9位数字 // 如果防御结果为连接,则恢复防御盾牌最大值层数
String regex = "^1[3-9]\\d{9}$"; mSettingsModel.setDunCurrentCount(mSettingsModel.getDunTotalCount());
if (Pattern.matches(regex, phoneNumber)) { LogUtils.d(TAG, String.format("防御结果为连接,恢复防御盾牌最大值层数 %d", mSettingsModel.getDunTotalCount()));
return true; saveDun();
} SettingsActivity.notifyDunInfoUpdate();
} else if (isDefend) {
// 指定区号号码允许 // 如果触发了以上某个防御模块,
regex = "^0660\\d+$"; // 就减少防御盾牌层数。
if (Pattern.matches(regex, phoneNumber)) { // 每校验一次规则云盾防御层数减1
return true; // 当云盾防御层数为0时再次进行以下程序段则恢复满值防御。
} int newDunCount = mSettingsModel.getDunCurrentCount() - 1;
LogUtils.d(TAG, String.format("新的防御层数预计为 %d", newDunCount));
// 指定区号号码允许
regex = "^020\\d+$"; // 保证盾值在[0DunTotalCount]之内其他值一律重置为 DunTotalCount。
if (Pattern.matches(regex, phoneNumber)) { if (newDunCount < 0 || newDunCount > mSettingsModel.getDunTotalCount()) {
return true; mSettingsModel.setDunCurrentCount(mSettingsModel.getDunTotalCount());
LogUtils.d(TAG, String.format("盾值不在[0%d]区间,恢复防御最大值%d", mSettingsModel.getDunTotalCount(), mSettingsModel.getDunTotalCount()));
} else {
mSettingsModel.setDunCurrentCount(newDunCount);
LogUtils.d(TAG, String.format("设置防御层数为 %d", newDunCount));
}
saveDun();
SettingsActivity.notifyDunInfoUpdate();
} }
// 其他拒接 // 返回校验结果
return false; LogUtils.d(TAG, String.format("返回校验结果 isConnect == %s", isConnect));
return isConnect;
} }
public void add(String phoneRuleBlack, boolean isEnable) { public void add(String szPhoneConnectRule, boolean isAllowConnection, boolean isEnable) {
_PhoneBlacRuleBeanList.add(new PhoneBlackRuleBean(phoneRuleBlack, isEnable)); _PhoneConnectRuleModelList.add(new PhoneConnectRuleModel(szPhoneConnectRule, isAllowConnection, isEnable));
PhoneBlackRuleBean.saveBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
} }
public ArrayList<PhoneBlackRuleBean> getPhoneBlacRuleBeanList() { public ArrayList<PhoneConnectRuleModel> getPhoneBlacRuleBeanList() {
return _PhoneBlacRuleBeanList; return _PhoneConnectRuleModelList;
}
public SettingsModel getSettingsModel() {
return mSettingsModel;
} }
} }

View File

@@ -1,51 +0,0 @@
package cc.winboll.studio.contacts.fragments;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 12:57:00
* @Describe 拨号
*/
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.libappbase.LogView;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import android.widget.TextView;
public class CallFragment extends Fragment {
public static final String TAG = "CallFragment";
private static final String ARG_PAGE = "ARG_PAGE";
private int mPage;
public static CallFragment newInstance(int page) {
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
CallFragment fragment = new CallFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments()!= null) {
mPage = getArguments().getInt(ARG_PAGE);
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_call, container, false);
TextView textView = view.findViewById(R.id.page_text);
textView.setText("这是第 " + mPage + "");
return view;
}
}

View File

@@ -0,0 +1,164 @@
package cc.winboll.studio.contacts.fragments;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 12:57:00
* @Describe 拨号
*/
import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.CallLog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.adapters.CallLogAdapter;
import cc.winboll.studio.contacts.beans.CallLogModel;
import com.hjq.toast.ToastUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class CallLogFragment extends Fragment {
public static final String TAG = "CallFragment";
static volatile CallLogFragment _CallLogFragment;
public static final int MSG_UPDATE = 1; // 添加消息常量
private static final String ARG_PAGE = "ARG_PAGE";
private int mPage;
private static final int REQUEST_READ_CALL_LOG = 1;
private RecyclerView recyclerView;
private CallLogAdapter callLogAdapter;
private List<CallLogModel> callLogList = new ArrayList<>();
// 添加Handler
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == MSG_UPDATE) {
readCallLog(); // 接收到消息时更新通话记录
}
}
};
CallLogFragment() {
super();
}
public static CallLogFragment newInstance(int page) {
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
CallLogFragment fragment = new CallLogFragment();
fragment.setArguments(args);
_CallLogFragment = fragment;
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_call_log, container, false);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mPage = getArguments().getInt(ARG_PAGE);
}
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
callLogAdapter = new CallLogAdapter(getContext(), callLogList);
recyclerView.setAdapter(callLogAdapter);
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CALL_LOG}, REQUEST_READ_CALL_LOG);
} else {
mHandler.sendEmptyMessage(MSG_UPDATE); // 通过Handler触发更新
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_READ_CALL_LOG) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mHandler.sendEmptyMessage(MSG_UPDATE); // 通过Handler触发更新
}
}
}
private void readCallLog() {
callLogList.clear(); // 清空原有数据
Cursor cursor = requireContext().getContentResolver().query(
CallLog.Calls.CONTENT_URI,
null,
null,
null,
CallLog.Calls.DATE + " DESC");
if (cursor != null) {
while (cursor.moveToNext()) {
String phoneNumber = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
int callType = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.TYPE));
long callDateLong = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE));
Date callDate = new Date(callDateLong);
String callStatus = getCallStatus(callType);
callLogList.add(new CallLogModel(phoneNumber, callStatus, callDate));
}
cursor.close();
callLogAdapter.notifyDataSetChanged();
}
}
private String getCallStatus(int callType) {
switch (callType) {
case CallLog.Calls.OUTGOING_TYPE:
return "Outgoing";
case CallLog.Calls.INCOMING_TYPE:
return "Incoming";
case CallLog.Calls.MISSED_TYPE:
return "Missed";
default:
return "Unknown";
}
}
@Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null); // 清理Handler防止内存泄漏
}
public void triggerUpdate() {
mHandler.sendEmptyMessage(MSG_UPDATE);
}
public static void updateCallLogFragment() {
if (_CallLogFragment != null) {
_CallLogFragment.triggerUpdate();
}
}
}

View File

@@ -5,23 +5,47 @@ package cc.winboll.studio.contacts.fragments;
* @Date 2025/02/20 12:57:50 * @Date 2025/02/20 12:57:50
* @Describe 联系人 * @Describe 联系人
*/ */
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.provider.ContactsContract;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.Button;
import android.widget.EditText;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
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.beans.ContactModel;
import com.hjq.toast.ToastUtils;
import java.util.ArrayList;
import java.util.List;
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 int mPage;
private static final int REQUEST_READ_CONTACTS = 1;
private RecyclerView recyclerView;
private ContactAdapter contactAdapter;
private List<ContactModel> contactList = new ArrayList<>();
private List<ContactModel> originalContactList = new ArrayList<>();
private EditText searchEditText;
public static ContactsFragment newInstance(int page) { public static ContactsFragment newInstance(int page) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putInt(ARG_PAGE, page); args.putInt(ARG_PAGE, page);
@@ -33,18 +57,111 @@ public class ContactsFragment extends Fragment {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (getArguments()!= null) { if (getArguments() != null) {
mPage = getArguments().getInt(ARG_PAGE); mPage = getArguments().getInt(ARG_PAGE);
} }
} }
@Nullable @Nullable
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_contacts, container, false);
View view = inflater.inflate(R.layout.fragment_contacts, container, false); }
TextView textView = view.findViewById(R.id.page_text);
textView.setText("这是第 " + mPage + ""); @Override
return view; public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.contacts_recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
contactAdapter = new ContactAdapter(contactList);
recyclerView.setAdapter(contactAdapter);
searchEditText = view.findViewById(R.id.search_edit_text);
searchEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
filterContacts(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_READ_CONTACTS);
} else {
readContacts();
}
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);
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_READ_CONTACTS) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
readContacts();
}
}
}
private void readContacts() {
contactList.clear();
originalContactList.clear();
Cursor cursor = requireContext().getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
null,
null,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC");
if (cursor != null) {
while (cursor.moveToNext()) {
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) {
contactList.clear();
if (query.isEmpty()) {
contactList.addAll(originalContactList);
} else {
for (ContactModel contact : originalContactList) {
if (contact.getName().toLowerCase().contains(query.toLowerCase()) ||
contact.getPinyin().toLowerCase().contains(query.toLowerCase()) ||
contact.getNumber().toLowerCase().contains(query.toLowerCase())) {
contactList.add(contact);
}
}
}
contactAdapter.notifyDataSetChanged();
} }
} }

View File

@@ -14,6 +14,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import cc.winboll.studio.libappbase.LogView; import cc.winboll.studio.libappbase.LogView;
import com.hjq.toast.ToastUtils;
public class LogFragment extends Fragment { public class LogFragment extends Fragment {
@@ -21,6 +22,8 @@ public class LogFragment extends Fragment {
private static final String ARG_PAGE = "ARG_PAGE"; private static final String ARG_PAGE = "ARG_PAGE";
private int mPage; private int mPage;
LogView mLogView;
public static LogFragment newInstance(int page) { public static LogFragment newInstance(int page) {
Bundle args = new Bundle(); Bundle args = new Bundle();
@@ -43,8 +46,17 @@ public class LogFragment extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_log, container, false); View view = inflater.inflate(R.layout.fragment_log, container, false);
LogView logView = view.findViewById(R.id.logview); mLogView = view.findViewById(R.id.logview);
logView.start(); mLogView.start();
return view; return view;
} }
@Override
public void onResume() {
super.onResume();
//ToastUtils.show("onResume");
mLogView.start();
}
} }

View File

@@ -21,6 +21,8 @@ import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import cc.winboll.studio.contacts.MainActivity; import cc.winboll.studio.contacts.MainActivity;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.phonecallui.PhoneCallActivity;
import cc.winboll.studio.contacts.phonecallui.PhoneCallService;
public class CallListenerService extends Service { public class CallListenerService extends Service {
@@ -152,9 +154,12 @@ public class CallListenerService extends Service {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
Intent intent = new Intent(getApplicationContext(), MainActivity.class); // Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
CallListenerService.this.startActivity(intent); // CallListenerService.this.startActivity(intent);
PhoneCallService.CallType callType = isCallingIn ? PhoneCallService.CallType.CALL_IN: PhoneCallService.CallType.CALL_OUT;
PhoneCallActivity.actionStart(CallListenerService.this, callNumber, callType);
} }
}); });
} }

View File

@@ -1,7 +1,5 @@
package cc.winboll.studio.contacts.phonecallui; package cc.winboll.studio.contacts.phonecallui;
import static cc.winboll.studio.contacts.listenphonecall.CallListenerService.formatPhoneNumber;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -10,16 +8,16 @@ import android.os.Bundle;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.contacts.ActivityStack; import cc.winboll.studio.contacts.ActivityStack;
import cc.winboll.studio.contacts.MainActivity;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import static cc.winboll.studio.contacts.listenphonecall.CallListenerService.formatPhoneNumber;
/** /**
* 提供接打电话的界面,仅支持 Android M (6.0, API 23) 及以上的系统 * 提供接打电话的界面,仅支持 Android M (6.0, API 23) 及以上的系统
@@ -57,10 +55,9 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
setContentView(R.layout.activity_phone_call); setContentView(R.layout.activity_phone_call);
ActivityStack.getInstance().addActivity(this); ActivityStack.getInstance().addActivity(this);
initData(); initData();
initView(); initView();
} }
private void initData() { private void initData() {
@@ -74,9 +71,9 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
private void initView() { private void initView() {
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE; | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
getWindow().getDecorView().setSystemUiVisibility(uiOptions); getWindow().getDecorView().setSystemUiVisibility(uiOptions);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
@@ -94,9 +91,7 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
if (callType == PhoneCallService.CallType.CALL_IN) { if (callType == PhoneCallService.CallType.CALL_IN) {
tvCallNumberLabel.setText("来电号码"); tvCallNumberLabel.setText("来电号码");
tvPickUp.setVisibility(View.VISIBLE); tvPickUp.setVisibility(View.VISIBLE);
} } else if (callType == PhoneCallService.CallType.CALL_OUT) {
// 打出的电话
else if (callType == PhoneCallService.CallType.CALL_OUT) {
tvCallNumberLabel.setText("呼叫号码"); tvCallNumberLabel.setText("呼叫号码");
tvPickUp.setVisibility(View.GONE); tvPickUp.setVisibility(View.GONE);
phoneCallManager.openSpeaker(); phoneCallManager.openSpeaker();
@@ -107,13 +102,13 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
public void showOnLockScreen() { public void showOnLockScreen() {
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON, WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
} }
@Override @Override
@@ -123,18 +118,18 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
tvPickUp.setVisibility(View.GONE); tvPickUp.setVisibility(View.GONE);
tvCallingTime.setVisibility(View.VISIBLE); tvCallingTime.setVisibility(View.VISIBLE);
onGoingCallTimer.schedule(new TimerTask() { onGoingCallTimer.schedule(new TimerTask() {
@Override @Override
public void run() { public void run() {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@Override @Override
public void run() { public void run() {
callingTime++; callingTime++;
tvCallingTime.setText("通话中:" + getCallingTime()); tvCallingTime.setText("通话中:" + getCallingTime());
} }
}); });
} }
}, 0, 1000); }, 0, 1000);
} else if (v.getId() == R.id.tv_phone_hang_up) { } else if (v.getId() == R.id.tv_phone_hang_up) {
phoneCallManager.disconnect(); phoneCallManager.disconnect();
stopTimer(); stopTimer();
@@ -145,8 +140,8 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
int minute = callingTime / 60; int minute = callingTime / 60;
int second = callingTime % 60; int second = callingTime % 60;
return (minute < 10 ? "0" + minute : minute) + return (minute < 10 ? "0" + minute : minute) +
":" + ":" +
(second < 10 ? "0" + second : second); (second < 10 ? "0" + second : second);
} }
private void stopTimer() { private void stopTimer() {
@@ -160,7 +155,7 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
//MainActivity.updateCallLogFragment();
phoneCallManager.destroy(); phoneCallManager.destroy();
} }
} }

View File

@@ -6,7 +6,7 @@ import android.os.Build;
import android.telecom.Call; import android.telecom.Call;
import android.telecom.VideoProfile; import android.telecom.VideoProfile;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import cc.winboll.studio.contacts.dun.Rules; import cc.winboll.studio.contacts.MainActivity;
@RequiresApi(api = Build.VERSION_CODES.M) @RequiresApi(api = Build.VERSION_CODES.M)

View File

@@ -7,28 +7,53 @@ package cc.winboll.studio.contacts.phonecallui;
* @see PhoneCallActivity * @see PhoneCallActivity
* @see android.telecom.InCallService * @see android.telecom.InCallService
*/ */
import android.content.ContentResolver;
import android.database.Cursor;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.provider.CallLog;
import android.telecom.Call; import android.telecom.Call;
import android.telecom.InCallService; import android.telecom.InCallService;
import android.telephony.TelephonyManager;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import cc.winboll.studio.contacts.ActivityStack; import cc.winboll.studio.contacts.ActivityStack;
import cc.winboll.studio.contacts.beans.RingTongBean; import cc.winboll.studio.contacts.beans.RingTongBean;
import cc.winboll.studio.contacts.dun.Rules; import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.fragments.CallLogFragment;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import java.io.File;
import java.io.IOException;
@RequiresApi(api = Build.VERSION_CODES.M) @RequiresApi(api = Build.VERSION_CODES.M)
public class PhoneCallService extends InCallService { public class PhoneCallService extends InCallService {
public static final String TAG = "PhoneCallService"; public static final String TAG = "PhoneCallService";
private volatile int originalRingVolume; MediaRecorder mediaRecorder;
private final Call.Callback callback = new Call.Callback() { private final Call.Callback callback = new Call.Callback() {
@Override @Override
public void onStateChanged(Call call, int state) { public void onStateChanged(Call call, int state) {
super.onStateChanged(call, state); super.onStateChanged(call, state);
switch (state) { switch (state) {
case TelephonyManager.CALL_STATE_OFFHOOK:
{
long callId = getCurrentCallId();
if (callId != -1) {
// 在这里可以对获取到的通话记录ID进行处理
//System.out.println("当前通话记录ID: " + callId);
// 电话接通,开始录音
startRecording(callId);
}
break;
}
case TelephonyManager.CALL_STATE_IDLE:
// 电话挂断,停止录音
stopRecording();
break;
case Call.STATE_ACTIVE: { case Call.STATE_ACTIVE: {
break; break;
} }
@@ -61,25 +86,58 @@ public class PhoneCallService extends InCallService {
String phoneNumber = details.getHandle().getSchemeSpecificPart(); String phoneNumber = details.getHandle().getSchemeSpecificPart();
// 记录原始铃声音量 // 记录原始铃声音量
//
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
originalRingVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING); int ringerVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
// 恢复铃声音量,预防其他意外条件导致的音量变化问题
//
// 读取应用配置,未配置就初始化配置文件
RingTongBean bean = RingTongBean.loadBean(this, RingTongBean.class);
if (bean == null) {
// 初始化配置
bean = new RingTongBean();
RingTongBean.saveBean(this, bean);
}
// 如果当前音量和应用保存的不一致就恢复为应用设定值
// 恢复铃声音量
try {
if (ringerVolume != bean.getStreamVolume()) {
audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0);
//audioManager.setMode(AudioManager.RINGER_MODE_NORMAL);
}
} catch (java.lang.SecurityException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
// 检查电话接收规则 // 检查电话接收规则
if (!Rules.getInstance(this).isAllowed(phoneNumber)) { if (!Rules.getInstance(this).isAllowed(phoneNumber)) {
// 预先静音 // 调低音量
audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0); try {
audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
//audioManager.setMode(AudioManager.RINGER_MODE_SILENT);
} catch (java.lang.SecurityException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
// 断开电话 // 断开电话
call.disconnect(); call.disconnect();
// 停顿1秒预防第一声铃声响动 // 停顿1秒预防第一声铃声响动
try { try {
Thread.sleep(1000); Thread.sleep(500);
} catch (InterruptedException e) { } catch (InterruptedException e) {
LogUtils.d(TAG, ""); LogUtils.d(TAG, "");
} }
// 恢复铃声音量 // 恢复铃声音量
audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0); try {
audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0);
//audioManager.setMode(AudioManager.RINGER_MODE_NORMAL);
} catch (java.lang.SecurityException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
// 屏蔽电话结束 // 屏蔽电话结束
return; return;
} }
// 正常接听电话 // 正常接听电话
PhoneCallActivity.actionStart(this, phoneNumber, callType); PhoneCallActivity.actionStart(this, phoneNumber, callType);
} }
@@ -88,16 +146,70 @@ public class PhoneCallService extends InCallService {
@Override @Override
public void onCallRemoved(Call call) { public void onCallRemoved(Call call) {
super.onCallRemoved(call); super.onCallRemoved(call);
call.unregisterCallback(callback); call.unregisterCallback(callback);
PhoneCallManager.call = null; PhoneCallManager.call = null;
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); }
// 恢复铃声音量
audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0); @Override
public void onDestroy() {
super.onDestroy();
CallLogFragment.updateCallLogFragment();
} }
public enum CallType { public enum CallType {
CALL_IN, CALL_IN,
CALL_OUT, CALL_OUT,
} }
private void startRecording(long callId) {
LogUtils.d(TAG, "startRecording(...)");
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mediaRecorder.setOutputFile(getOutputFilePath(callId));
try {
mediaRecorder.prepare();
mediaRecorder.start();
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
private String getOutputFilePath(long callId) {
LogUtils.d(TAG, "getOutputFilePath(...)");
// 设置录音文件的保存路径
File file = new File(getExternalFilesDir(TAG), String.format("call_%d.mp4", callId));
return file.getAbsolutePath();
}
private void stopRecording() {
LogUtils.d(TAG, "stopRecording()");
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
}
}
private long getCurrentCallId() {
LogUtils.d(TAG, "getCurrentCallId()");
ContentResolver contentResolver = getApplicationContext().getContentResolver();
Uri callLogUri = Uri.parse("content://call_log/calls");
String[] projection = {"_id", "number", "call_type", "date"};
String selection = "call_type = " + CallLog.Calls.OUTGOING_TYPE + " OR call_type = " + CallLog.Calls.INCOMING_TYPE;
String sortOrder = "date DESC";
try {
Cursor cursor = contentResolver.query(callLogUri, projection, selection, null, sortOrder);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndex("_id"));
}
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
return -1;
}
} }

View File

@@ -9,13 +9,9 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.media.RingtoneManager;
import android.net.Uri;
import android.util.Log;
import cc.winboll.studio.contacts.services.MainService; import cc.winboll.studio.contacts.services.MainService;
import com.hjq.toast.ToastUtils; import com.hjq.toast.ToastUtils;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import cc.winboll.studio.libappbase.LogUtils;
public class MainReceiver extends BroadcastReceiver { public class MainReceiver extends BroadcastReceiver {
@@ -43,7 +39,7 @@ public class MainReceiver extends BroadcastReceiver {
public void registerAction(Context context) { public void registerAction(Context context) {
IntentFilter filter=new IntentFilter(); IntentFilter filter=new IntentFilter();
filter.addAction(ACTION_BOOT_COMPLETED); filter.addAction(ACTION_BOOT_COMPLETED);
//filter.addAction(Intent.ACTION_BATTERY_CHANGED); //filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
context.registerReceiver(this, filter); context.registerReceiver(this, filter);
} }
} }

View File

@@ -15,8 +15,6 @@ import android.os.IBinder;
import cc.winboll.studio.contacts.beans.MainServiceBean; import cc.winboll.studio.contacts.beans.MainServiceBean;
import cc.winboll.studio.contacts.services.MainService; import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.SOS;
import cc.winboll.studio.libappbase.bean.APPSOSBean;
public class AssistantService extends Service { public class AssistantService extends Service {

View File

@@ -11,25 +11,29 @@ package cc.winboll.studio.contacts.services;
* https://blog.csdn.net/cyp331203/article/details/38920491 * https://blog.csdn.net/cyp331203/article/details/38920491
*/ */
import android.app.Service; import android.app.Service;
import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.media.AudioManager;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import cc.winboll.studio.contacts.beans.MainServiceBean; import cc.winboll.studio.contacts.beans.MainServiceBean;
import cc.winboll.studio.contacts.beans.RingTongBean;
import cc.winboll.studio.contacts.bobulltoon.TomCat;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.handlers.MainServiceHandler; import cc.winboll.studio.contacts.handlers.MainServiceHandler;
import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
import cc.winboll.studio.contacts.receivers.MainReceiver; import cc.winboll.studio.contacts.receivers.MainReceiver;
import cc.winboll.studio.contacts.services.MainService; import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.contacts.threads.MainServiceThread; import cc.winboll.studio.contacts.threads.MainServiceThread;
import cc.winboll.studio.contacts.widgets.APPStatusWidget;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.SOS; import cc.winboll.studio.libappbase.sos.SOS;
import cc.winboll.studio.libappbase.bean.APPSOSBean; import java.util.Timer;
import cc.winboll.studio.contacts.dun.Rules; import java.util.TimerTask;
import android.media.AudioManager; import cc.winboll.studio.libappbase.sos.WinBoll;
import com.hjq.toast.ToastUtils; import cc.winboll.studio.contacts.App;
import cc.winboll.studio.libappbase.sos.APPModel;
public class MainService extends Service { public class MainService extends Service {
@@ -48,8 +52,9 @@ public class MainService extends Service {
AssistantService mAssistantService; AssistantService mAssistantService;
boolean isBound = false; boolean isBound = false;
MainReceiver mMainReceiver; MainReceiver mMainReceiver;
Timer mStreamVolumeCheckTimer;
static volatile TomCat _TomCat;
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return new MyBinder(); return new MyBinder();
@@ -71,9 +76,37 @@ public class MainService extends Service {
mMyServiceConnection = new MyServiceConnection(); mMyServiceConnection = new MyServiceConnection();
} }
mMainServiceHandler = new MainServiceHandler(this); mMainServiceHandler = new MainServiceHandler(this);
// 铃声检查定时器
mStreamVolumeCheckTimer = new Timer();
mStreamVolumeCheckTimer.schedule(new TimerTask() {
@Override
public void run() {
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
int ringerVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
// 恢复铃声音量,预防其他意外条件导致的音量变化问题
//
// 读取应用配置,未配置就初始化配置文件
RingTongBean bean = RingTongBean.loadBean(MainService.this, RingTongBean.class);
if (bean == null) {
// 初始化配置
bean = new RingTongBean();
RingTongBean.saveBean(MainService.this, bean);
}
// 如果当前音量和应用保存的不一致就恢复为应用设定值
// 恢复铃声音量
try {
if (ringerVolume != bean.getStreamVolume()) {
audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0);
//audioManager.setMode(AudioManager.RINGER_MODE_NORMAL);
}
} catch (java.lang.SecurityException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
}, 1000, 60000);
// 运行服务内容 // 运行服务内容
mainService(); mainService();
} }
@@ -97,18 +130,26 @@ public class MainService extends Service {
// 唤醒守护进程 // 唤醒守护进程
wakeupAndBindAssistant(); wakeupAndBindAssistant();
// 召唤 WinBoll APP 绑定本服务 // 召唤 WinBoll APP 绑定本服务
SOS.bindToAPPService(this, new APPSOSBean(getPackageName(), MainService.class.getName())); if (App.isDebuging()) {
WinBoll.bindToAPPBaseBeta(this, MainService.class.getName());
} else {
WinBoll.bindToAPPBase(this, MainService.class.getName());
}
// 初始化服务运行参数
_TomCat = TomCat.getInstance(this);
if (!_TomCat.loadPhoneBoBullToon()) {
LogUtils.d(TAG, "没有下载 BoBullToon 数据。BoBullToon 参数无法加载。");
}
if (mMainReceiver == null) { if (mMainReceiver == null) {
// 注册广播接收器 // 注册广播接收器
mMainReceiver = new MainReceiver(this); mMainReceiver = new MainReceiver(this);
mMainReceiver.registerAction(this); mMainReceiver.registerAction(this);
} }
Rules.getInstance(this); Rules.getInstance(this).loadRules();
//Rules.getInstance(this).add("18888888888", true);
//Rules.getInstance(this).add("16769764848", true);
startPhoneCallListener(); startPhoneCallListener();
MainServiceThread.getInstance(this, mMainServiceHandler).start(); MainServiceThread.getInstance(this, mMainServiceHandler).start();
@@ -117,6 +158,14 @@ public class MainService extends Service {
} }
} }
public static boolean isPhoneInBoBullToon(String phone) {
if (_TomCat != null) {
return _TomCat.isPhoneBoBullToon(phone);
}
return false;
}
// 唤醒和绑定守护进程 // 唤醒和绑定守护进程
// //
void wakeupAndBindAssistant() { void wakeupAndBindAssistant() {
@@ -137,7 +186,7 @@ public class MainService extends Service {
// LogUtils.d(TAG, "startService(intent)"); // LogUtils.d(TAG, "startService(intent)");
// bindService(new Intent(this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT); // bindService(new Intent(this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
} }
void startPhoneCallListener() { void startPhoneCallListener() {
Intent callListener = new Intent(this, CallListenerService.class); Intent callListener = new Intent(this, CallListenerService.class);
startService(callListener); startService(callListener);
@@ -168,7 +217,7 @@ public class MainService extends Service {
// 停止主要进程 // 停止主要进程
MainServiceThread.getInstance(this, mMainServiceHandler).setIsExit(true); MainServiceThread.getInstance(this, mMainServiceHandler).setIsExit(true);
} }
super.onDestroy(); super.onDestroy();
@@ -191,7 +240,11 @@ public class MainService extends Service {
if (mMainServiceBean.isEnable()) { if (mMainServiceBean.isEnable()) {
// 唤醒守护进程 // 唤醒守护进程
wakeupAndBindAssistant(); wakeupAndBindAssistant();
SOS.sosWinBollService(getApplicationContext(), new APPSOSBean(getPackageName(), MainService.class.getName())); if (App.isDebuging()) {
SOS.sosToAppBase(getApplicationContext(), MainService.class.getName());
} else {
SOS.sosToAppBaseBeta(getApplicationContext(), MainService.class.getName());
}
} }
isBound = false; isBound = false;
mAssistantService = null; mAssistantService = null;
@@ -230,14 +283,40 @@ public class MainService extends Service {
public static void stopMainService(Context context) { public static void stopMainService(Context context) {
LogUtils.d(TAG, "stopMainService"); LogUtils.d(TAG, "stopMainService");
context.stopService(new Intent(context, MainService.class));
}
public static void startMainService(Context context) {
LogUtils.d(TAG, "startMainService");
context.startService(new Intent(context, MainService.class));
}
public static void restartMainService(Context context) {
LogUtils.d(TAG, "restartMainService");
MainServiceBean bean = MainServiceBean.loadBean(context, MainServiceBean.class);
if (bean != null && bean.isEnable()) {
context.stopService(new Intent(context, MainService.class));
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
// }
context.startService(new Intent(context, MainService.class));
LogUtils.d(TAG, "已重启 MainService");
}
}
public static void stopMainServiceAndSaveStatus(Context context) {
LogUtils.d(TAG, "stopMainServiceAndSaveStatus");
MainServiceBean bean = new MainServiceBean(); MainServiceBean bean = new MainServiceBean();
bean.setIsEnable(false); bean.setIsEnable(false);
MainServiceBean.saveBean(context, bean); MainServiceBean.saveBean(context, bean);
context.stopService(new Intent(context, MainService.class)); context.stopService(new Intent(context, MainService.class));
} }
public static void startMainService(Context context) { public static void startMainServiceAndSaveStatus(Context context) {
LogUtils.d(TAG, "startMainService"); LogUtils.d(TAG, "startMainServiceAndSaveStatus");
MainServiceBean bean = new MainServiceBean(); MainServiceBean bean = new MainServiceBean();
bean.setIsEnable(true); bean.setIsEnable(true);
MainServiceBean.saveBean(context, bean); MainServiceBean.saveBean(context, bean);

View File

@@ -6,11 +6,7 @@ package cc.winboll.studio.contacts.threads;
*/ */
import android.content.Context; import android.content.Context;
import cc.winboll.studio.contacts.handlers.MainServiceHandler; import cc.winboll.studio.contacts.handlers.MainServiceHandler;
import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.SOS;
import cc.winboll.studio.libappbase.bean.APPSOSBean;
import com.hjq.toast.ToastUtils;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
public class MainServiceThread extends Thread { public class MainServiceThread extends Thread {

View File

@@ -0,0 +1,123 @@
package cc.winboll.studio.contacts.utils;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/06 21:08:16
* @Describe ContactUtils
*/
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract;
import cc.winboll.studio.libappbase.LogUtils;
import java.util.HashMap;
import java.util.Map;
public class ContactUtils {
public static final String TAG = "ContactUtils";
Map<String, String> contactMap = new HashMap<>();
static volatile ContactUtils _ContactUtils;
Context mContext;
ContactUtils(Context context) {
mContext = context;
relaodContacts();
}
public synchronized static ContactUtils getInstance(Context context) {
if (_ContactUtils == null) {
_ContactUtils = new ContactUtils(context);
}
return _ContactUtils;
}
public void relaodContacts() {
readContacts();
}
private void readContacts() {
contactMap.clear();
ContentResolver contentResolver = mContext.getContentResolver();
Cursor cursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phoneNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
//Map<String, String> contactMap = new HashMap<>();
contactMap.put(formatToSimplePhoneNumber(phoneNumber), displayName);
}
cursor.close();
}
// 此时 contactList 就是存储联系人信息的 Map 列表
}
public String getContactsName(String phone) {
String result = contactMap.get(formatToSimplePhoneNumber(phone));
return result == null ? "[NotInContacts]" : result;
}
// static String getSimplePhone(String phone) {
// return phone.replaceAll("[+\\s]", "");
// }
public static String formatToSimplePhoneNumber(String number) {
// 去除所有空格和非数字字符
return number.replaceAll("[^0-9]", "");
}
public static String getDisplayNameByPhone(Context context, String phoneNumber) {
String displayName = null;
ContentResolver resolver = context.getContentResolver();
String[] projection = {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME};
Cursor cursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, ContactsContract.CommonDataKinds.Phone.NUMBER + "=?", new String[]{phoneNumber}, null);
if (cursor != null && cursor.moveToFirst()) {
displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
cursor.close();
}
return displayName;
}
public static String getDisplayNameByPhoneSimple(Context context, String phoneNumber) {
String displayName = null;
ContentResolver resolver = context.getContentResolver();
String[] projection = {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME};
Cursor cursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, ContactsContract.CommonDataKinds.Phone.NUMBER + "=?", new String[]{formatToSimplePhoneNumber(phoneNumber)}, null);
if (cursor != null && cursor.moveToFirst()) {
displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
cursor.close();
}
return displayName;
}
public static boolean isPhoneInContacts(Context context, String phoneNumber) {
String szPhoneNumber = formatToSimplePhoneNumber(phoneNumber);
String szDisplayName = getDisplayNameByPhone(context, szPhoneNumber);
if (szDisplayName == null) {
LogUtils.d(TAG, String.format("Phone %s is not in contacts.", szPhoneNumber));
szPhoneNumber = formatToSpacePhoneNumber(szPhoneNumber);
szDisplayName = getDisplayNameByPhone(context, szPhoneNumber);
if (szDisplayName == null) {
LogUtils.d(TAG, String.format("Phone %s is not in contacts.", szPhoneNumber));
return false;
}
}
LogUtils.d(TAG, String.format("Phone %s is found in contacts %s.", szPhoneNumber, szDisplayName));
return true;
}
public static String formatToSpacePhoneNumber(String simpleNumber) {
// 去除所有空格和非数字字符
StringBuilder sbSpaceNumber = new StringBuilder();
String regex = "^1[0-9]{10}$";
if (simpleNumber.matches(regex)) {
sbSpaceNumber.append(simpleNumber.substring(0, 3));
sbSpaceNumber.append(" ");
sbSpaceNumber.append(simpleNumber.substring(3, 7));
sbSpaceNumber.append(" ");
sbSpaceNumber.append(simpleNumber.substring(7, 11));
}
return sbSpaceNumber.toString();
}
}

View File

@@ -0,0 +1,24 @@
package cc.winboll.studio.contacts.utils;
import android.widget.EditText;
import cc.winboll.studio.libappbase.LogUtils;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/04/13 00:59:13
* @Describe Int类型数字输入框工具集
*/
public class EditTextIntUtils {
public static final String TAG = "EditTextIntUtils";
public static int getIntFromEditText(EditText editText) {
try {
String sz = editText.getText().toString().trim();
return Integer.parseInt(sz);
} catch (NumberFormatException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
return 0;
}
}
}

View File

@@ -0,0 +1,37 @@
package cc.winboll.studio.contacts.utils;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/04/13 01:16:28
* @Describe Int数字操作工具集
*/
import cc.winboll.studio.libappbase.LogUtils;
public class IntUtils {
public static final String TAG = "IntUtils";
public static int getIntInRange(int origin, int range_a, int range_b) {
int min = Math.min(range_a, range_b);
int max = Math.max(range_a, range_b);
int res = Math.min(origin, max);
res = Math.max(res, min);
return res;
}
public static void unittest_getIntInRange() {
LogUtils.d(TAG, String.format("getIntInRange(-100, 5, 10); %d", getIntInRange(-100, 5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(8, 5, 10); %d", getIntInRange(8, 5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(200, 5, 10); %d", getIntInRange(200, 5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(-100, -5, 10); %d", getIntInRange(-100, -5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(9, -5, 10); %d", getIntInRange(9, -5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(100, -5, 10); %d", getIntInRange(100, -5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(500, 5, -10); %d", getIntInRange(500, 5, -10)));
LogUtils.d(TAG, String.format("getIntInRange(4, 5, -10); %d", getIntInRange(4, 5, -10)));
LogUtils.d(TAG, String.format("getIntInRange(-20, 5, -10); %d", getIntInRange(-20, 5, -10)));
LogUtils.d(TAG, String.format("getIntInRange(500, 50, 10); %d", getIntInRange(500, 50, 10)));
LogUtils.d(TAG, String.format("getIntInRange(30, 50, 10); %d", getIntInRange(30, 50, 10)));
LogUtils.d(TAG, String.format("getIntInRange(6, 50, 10); %d", getIntInRange(6, 50, 10)));
}
}

View File

@@ -0,0 +1,27 @@
package cc.winboll.studio.contacts.utils;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/26 15:21:48
* @Describe PhoneUtils
*/
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
public class PhoneUtils {
public static final String TAG = "PhoneUtils";
public static void call(Context context, String phoneNumber) {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
context.startActivity(intent);
}
}

View File

@@ -0,0 +1,32 @@
package cc.winboll.studio.contacts.utils;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/12/09 19:00:21
* @Describe .* 前置预防针
regex pointer preventive injection
简称 RegexPPi
*/
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexPPiUtils {
public static final String TAG = "RegexPPiUtils";
//
// 检验文本是否满足适合正则表达式模式计算
//
public static boolean isPPiOK(String text) {
//String text = "这里是一些任意的文本内容";
String regex = ".*";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
/*if (matcher.matches()) {
System.out.println("文本满足该正则表达式模式");
} else {
System.out.println("文本不满足该正则表达式模式");
}*/
return matcher.matches();
}
}

View File

@@ -0,0 +1,68 @@
package cc.winboll.studio.contacts.views;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/02 21:11:03
* @Describe 云盾防御信息
*/
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
import cc.winboll.studio.contacts.beans.SettingsModel;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.libappbase.LogUtils;
public class DuInfoTextView extends TextView {
public static final String TAG = "DuInfoTextView";
public static final int MSG_NOTIFY_INFO_UPDATE = 0;
Context mContext;
public DuInfoTextView(android.content.Context context) {
super(context);
}
public DuInfoTextView(android.content.Context context, android.util.AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public DuInfoTextView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public DuInfoTextView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
void initView(android.content.Context context) {
mContext = context;
updateInfo();
}
void updateInfo() {
LogUtils.d(TAG, "updateInfo()");
SettingsModel settingsModel = Rules.getInstance(mContext).getSettingsModel();
String info = String.format("(云盾防御值【%d/%d】)", settingsModel.getDunCurrentCount(), settingsModel.getDunTotalCount());
setText(info);
}
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == MSG_NOTIFY_INFO_UPDATE) {
updateInfo();
}
}
};
public void notifyInfoUpdate() {
LogUtils.d(TAG, "notifyInfoUpdate()");
mHandler.sendMessage(mHandler.obtainMessage(MSG_NOTIFY_INFO_UPDATE));
}
}

View File

@@ -0,0 +1,220 @@
package cc.winboll.studio.contacts.views;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/04 10:51:50
* @Describe CustomHorizontalScrollView
*/
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.libappbase.LogUtils;
public class LeftScrollView extends HorizontalScrollView {
public static final String TAG = "LeftScrollView";
private LinearLayout contentLayout;
private LinearLayout toolLayout;
private TextView textView;
private Button editButton;
private Button deleteButton;
private Button upButton;
private Button downButton;
private float mStartX;
private float mEndX;
private boolean isScrolling = false;
private int nScrollAcceptSize;
public LeftScrollView(Context context) {
super(context);
init();
}
public LeftScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LeftScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void addContentLayout(TextView textView) {
contentLayout.addView(textView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
}
public void setContentWidth(int contentWidth) {
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) contentLayout.getLayoutParams();
layoutParams.width = contentWidth;
contentLayout.setLayoutParams(layoutParams);
}
private void init() {
View viewMain = inflate(getContext(), R.layout.view_left_scroll, null);
// 创建内容布局
contentLayout = viewMain.findViewById(R.id.content_layout);
toolLayout = viewMain.findViewById(R.id.action_layout);
//LogUtils.d(TAG, String.format("getWidth() %d", getWidth()));
addView(viewMain);
// 创建编辑按钮
editButton = viewMain.findViewById(R.id.edit_btn);
// 创建删除按钮
deleteButton = viewMain.findViewById(R.id.delete_btn);
// 向上按钮
upButton = viewMain.findViewById(R.id.up_btn);
// 向下按钮
downButton = viewMain.findViewById(R.id.down_btn);
// 编辑按钮点击事件
editButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onActionListener != null) {
onActionListener.onEdit();
}
}
});
// 删除按钮点击事件
deleteButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onActionListener != null) {
onActionListener.onDelete();
}
}
});
// 编辑按钮点击事件
upButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onActionListener != null) {
onActionListener.onUp();
}
}
});
// 删除按钮点击事件
downButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onActionListener != null) {
onActionListener.onDown();
}
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
LogUtils.d(TAG, "ACTION_DOWN");
mStartX = event.getX();
// isScrolling = false;
break;
case MotionEvent.ACTION_MOVE:
//LogUtils.d(TAG, "ACTION_MOVE");
// float currentX = event.getX();
// float deltaX = mStartX - currentX;
// //mLastX = currentX;
// if (Math.abs(deltaX) > 0) {
// isScrolling = true;
// }
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (getScrollX() > 0) {
LogUtils.d(TAG, "ACTION_UP");
mEndX = event.getX();
LogUtils.d(TAG, String.format("mStartX %f, mEndX %f", mStartX, mEndX));
if (mEndX < mStartX) {
LogUtils.d(TAG, String.format("mEndX >= mStartX \ngetScrollX() %d", getScrollX()));
//if (getScrollX() > editButton.getWidth()) {
if (Math.abs(mStartX - mEndX) > editButton.getWidth()) {
smoothScrollToRight();
} else {
smoothScrollToLeft();
}
} else {
LogUtils.d(TAG, String.format("mEndX >= mStartX \ngetScrollX() %d", getScrollX()));
//if (getScrollX() > deleteButton.getWidth()) {
if (Math.abs(mEndX - mStartX) > deleteButton.getWidth()) {
smoothScrollToLeft();
} else {
smoothScrollToRight();
}
}
}
break;
}
return super.onTouchEvent(event);
}
void smoothScrollToRight() {
mEndX = 0;
mStartX = 0;
View childView = getChildAt(0);
if (childView != null) {
// 计算需要滑动到最右边的距离
int scrollToX = childView.getWidth() - getWidth();
// 确保滑动距离不小于0
final int scrollToX2 = Math.max(0, scrollToX);
// 平滑滑动到最右边
post(new Runnable() {
@Override
public void run() {
smoothScrollTo(scrollToX2, 0);
LogUtils.d(TAG, "smoothScrollTo(0, 0);");
}
});
LogUtils.d(TAG, "smoothScrollTo(scrollToX, 0);");
}
}
void smoothScrollToLeft() {
mEndX = 0;
mStartX = 0;
// 在手指抬起时,使用 post 方法调用 smoothScrollTo(0, 0)
post(new Runnable() {
@Override
public void run() {
smoothScrollTo(0, 0);
LogUtils.d(TAG, "smoothScrollTo(0, 0);");
}
});
}
// 设置文本内容
public void setText(CharSequence text) {
textView.setText(text);
}
// 定义回调接口
public interface OnActionListener {
void onEdit();
void onDelete();
void onUp();
void onDown();
}
private OnActionListener onActionListener;
public void setOnActionListener(OnActionListener listener) {
this.onActionListener = listener;
}
}

Some files were not shown because too many files have changed in this diff Show More