Compare commits

...

262 Commits

Author SHA1 Message Date
3eeb808e07 <libapputils>Library Release 15.10.2 2025-09-29 01:16:07 +08:00
56a3e12521 <apputils>APK 15.10.2 release Publish. 2025-09-29 01:15:55 +08:00
ZhanGSKen
6ce48c8881 更新类库 2025-09-29 00:22:22 +08:00
eb0881ae6b <libapputils>Library Release 15.10.1 2025-09-29 00:19:53 +08:00
c88dcd7316 <apputils>APK 15.10.1 release Publish. 2025-09-29 00:19:37 +08:00
ZhanGSKen
0ab2fdea66 精简模块,转移到WinBoLL项目。 2025-09-29 00:18:20 +08:00
ce83a08eb8 <libapputils>Library Release 15.10.0 2025-09-26 21:10:29 +08:00
7679c9a0d9 <apputils>APK 15.10.0 release Publish. 2025-09-26 21:10:11 +08:00
ZhanGSKen
23eca69a3c 设置次级版本号与APPBase同步 2025-09-26 21:08:50 +08:00
1362d7a5cf <apputils>APK 15.9.1 release Publish. 2025-09-26 19:55:26 +08:00
ZhanGSKen
8963d2a5df 修复日志初始分屏比例无法调节问题。原因是两个窗口都存在LogView控件时,会引起未知原因的无法调节问题。只要启动窗口把LogView控件移除就可以了。 2025-09-26 19:43:06 +08:00
ZhanGSKen
f2726ddc7a 添加说明书 2025-09-22 08:34:36 +08:00
ZhanGSKen
fa09da4e56 Merge remote-tracking branch 'origin/appbase' into apputils 2025-09-22 08:29:10 +08:00
ZhanGSKen
edc63c750b 整理git管理文件 2025-09-22 08:25:44 +08:00
52185ed7da <libapputils>Library Release 15.9.0 2025-09-22 08:19:46 +08:00
355a1c70e5 移除keystore 2025-09-22 08:19:22 +08:00
a808310a7c <apputils>APK 15.9.0 release Publish. 2025-09-22 08:17:49 +08:00
ZhanGSKen
839a9e2054 更新类库。处理从APPBase转移过来的模块,修整一下二维码功能窗口。 2025-09-22 08:11:30 +08:00
ZhanGSKen
471ca23585 Merge remote-tracking branch 'origin/appbase' into apputils 2025-09-22 06:35:18 +08:00
ZhanGSKen
56be1767bb 移除keystore 2025-09-22 06:34:23 +08:00
ZhanGSKen
b66d53da1b 加入APPBase递交模块 2025-09-22 06:31:03 +08:00
b170085482 <libappbase>Library Release 15.10.7 2025-09-22 06:01:13 +08:00
6e8ba3394d <appbase>APK 15.10.7 release Publish. 2025-09-22 02:57:06 +08:00
b91fe3c16b <appbase>APK 15.10.6 release Publish. 2025-09-22 02:50:34 +08:00
e30b7acfdb 清理文件夹 2025-09-22 02:49:41 +08:00
6221555dfe <appbase>APK 15.10.5 release Publish. 2025-09-22 02:46:13 +08:00
ZhanGSKen
c60eb29f4f 编译测试 2025-09-22 02:43:04 +08:00
ZhanGSKen
3efcf40025 添加Stage调试包编译配置 2025-09-22 02:40:36 +08:00
ZhanGSKen
27e948b86d <appbase>APK 15.10.4 release Publish. 2025-09-22 02:03:38 +08:00
ZhanGSKen
ae4742f3e2 精简资源文件 2025-09-22 01:44:00 +08:00
ZhanGSKen
d3312cbb29 项目模块精简 2025-09-22 01:30:18 +08:00
ZhanGSKen
58f3778dff 20250922_005701_514 2025-09-22 00:57:07 +08:00
ZhanGSKen
f5e2961445 移除秘钥模块加载 2025-09-21 21:25:52 +08:00
ZhanGSKen
e4e9c31f02 添加APP应用版本识别,使用时间戳标识方法。 2025-09-15 18:06:53 +08:00
ZhanGSKen
a02a7efe43 更新APK路径输出说明 2025-09-15 17:46:42 +08:00
ZhanGSKen
2220efd009 优化APK输出路径设置,改为配置形式。 2025-09-15 17:33:11 +08:00
ZhanGSKen
0bc0fdf8c2 仅仅升级调试版本号 2025-09-15 02:09:02 +08:00
ZhanGSKen
6b7b07a6fd 20250914_205447_378 2025-09-14 20:54:50 +08:00
ZhanGSKen
8f3417818d 添加Gradle调试信息 2025-09-14 20:15:03 +08:00
ZhanGSKen
ca280c7334 20250914_200011_033 2025-09-14 20:00:14 +08:00
ZhanGSKen
8172cf7b6f 更新起始窗口,备注调整。 2025-09-14 19:51:33 +08:00
ZhanGSKen
8014f4149c 更新秘钥库 2025-09-12 10:55:43 +08:00
ZhanGSKen
3c36ed589a 更换联系邮箱 2025-09-10 02:26:59 +08:00
ZhanGSKen
9768103741 添加WebPageSources项目 2025-09-10 02:09:47 +08:00
ZhanGSKen
787b8f0d77 Merge remote-tracking branch 'origin/timestamp' into appbase 2025-09-10 02:02:52 +08:00
ZhanGSKen
1d58126fd8 Merge remote-tracking branch 'origin/powerbell' into appbase 2025-09-10 02:02:42 +08:00
ZhanGSKen
7b5a3d2d71 Merge remote-tracking branch 'origin/mymessagemanager' into appbase 2025-09-10 02:02:24 +08:00
ZhanGSKen
a988a9d4f6 Merge remote-tracking branch 'origin/midiplayer' into appbase 2025-09-10 02:01:57 +08:00
ZhanGSKen
04a67e666b Merge remote-tracking branch 'origin/apputils' into appbase 2025-09-10 02:01:15 +08:00
ZhanGSKen
348edc8aaf Merge remote-tracking branch 'gitee/timestamp' into appbase 2025-09-10 01:58:15 +08:00
ZhanGSKen
5970ae33c8 Merge remote-tracking branch 'gitee/powerbell' into appbase 2025-09-10 01:58:08 +08:00
ZhanGSKen
91a44f48ef Merge remote-tracking branch 'gitee/numtable' into appbase 2025-09-10 01:58:01 +08:00
ZhanGSKen
c66e9a090b Merge remote-tracking branch 'gitee/mymessagemanager' into appbase 2025-09-10 01:57:54 +08:00
ZhanGSKen
7a14b55247 Merge remote-tracking branch 'gitee/contacts' into appbase 2025-09-10 01:57:47 +08:00
ZhanGSKen
fab68f16c8 Merge remote-tracking branch 'gitee/autoinstaller' into appbase 2025-09-10 01:57:40 +08:00
ZhanGSKen
3a97c6135f Merge remote-tracking branch 'gitee/apputils' into appbase 2025-09-10 01:57:35 +08:00
ZhanGSKen
cdad017d8c Merge remote-tracking branch 'gitee/androidxdemo' into appbase 2025-09-10 01:57:24 +08:00
ZhanGSKen
bf84382963 Merge remote-tracking branch 'gitee/androiddemo' into appbase 2025-09-10 01:57:17 +08:00
ZhanGSKen
34601fc5b1 Merge remote-tracking branch 'gitee/aes' into appbase 2025-09-10 01:57:11 +08:00
ZhanGSKen
a40dbcfb61 <mymessagemanager>APK 15.3.8 release Publish. 2025-09-06 01:57:20 +08:00
ZhanGSKen
4d344b299b 修改联系人查询发送的窗口,设置输入框号码完全匹配某个联系人时,才显示号码对应的联系人名称。 2025-09-06 01:52:37 +08:00
ZhanGSKen
37b0867d34 20250906_012327_326 2025-09-06 01:23:42 +08:00
ZhanGSKen
cdfbb082d2 <powerbell>APK 15.4.12 release Publish. 2025-09-03 20:59:53 +08:00
ZhanGSKen
7e476894a7 电量记录表里添加换行显示功能。 2025-09-03 20:54:48 +08:00
ZhanGSKen
0e8ae2e020 修复MidiPlayer项目文件夹命名错误问题。 2025-09-02 21:06:49 +08:00
ZhanGSKen
48623a2805 更新说明书 2025-09-01 23:23:18 +08:00
ZhanGSKen
b505156211 <mymessagemanager>APK 15.3.7 release Publish. 2025-09-01 08:07:48 +08:00
ZhanGSKen
91b30fb576 <mymessagemanager>Start New Stage Version. 2025-09-01 08:07:13 +08:00
ZhanGSKen
ab3ac72d54 RegexPPiUtils 入选 APPUtils 类库。 2025-09-01 08:04:36 +08:00
ZhanGSKen
73285c8779 <libapputils>Library Release 15.8.6 2025-09-01 07:56:35 +08:00
ZhanGSKen
fa338ec8c7 <apputils>APK 15.8.6 release Publish. 2025-09-01 07:56:11 +08:00
ZhanGSKen
7a3a1f4bcd 添加正则表达式前置预防针工具类 2025-09-01 07:51:20 +08:00
ZhanGSKen
ea65810e7d <libaes>Library Release 15.9.3 2025-08-31 23:40:22 +08:00
ZhanGSKen
ad991e3da2 <libapputils>Library Release 15.8.5 2025-08-31 23:39:23 +08:00
ZhanGSKen
7e263447c8 <libappbase>Library Release 15.9.5 2025-08-31 23:37:44 +08:00
ZhanGSKen
80201e8370 <powerbell>APK 15.4.11 release Publish. 2025-08-31 06:21:48 +08:00
ZhanGSKen
ea0473606a 更新类库 2025-08-31 06:17:41 +08:00
ZhanGSKen
870e9a94fb <mymessagemanager>APK 15.3.6 release Publish. 2025-08-31 06:13:45 +08:00
ZhanGSKen
2421ecb943 更新类库 2025-08-31 06:12:49 +08:00
ZhanGSKen
687fff7216 <mymessagemanager>Start New Stage Version. 2025-08-31 06:10:17 +08:00
ZhanGSKen
50d4cd830b <contacts>APK 15.3.16 release Publish. 2025-08-31 06:05:42 +08:00
ZhanGSKen
2079822c00 更新类库 2025-08-31 06:04:54 +08:00
ZhanGSKen
297c76f328 <contacts>Start New Stage Version. 2025-08-31 06:02:02 +08:00
ZhanGSKen
43b18ee662 <contacts>Start New Stage Version. 2025-08-31 06:01:23 +08:00
ZhanGSKen
3ec3a4cfc2 <androidxdemo>APK 15.1.1 release Publish. 2025-08-31 05:42:50 +08:00
ZhanGSKen
2c10a9f38c 设置初始版本号 2025-08-31 05:42:19 +08:00
ZhanGSKen
e62888636e <androidxdemo>APK 15.0.0 release Publish. 2025-08-31 05:39:39 +08:00
ZhanGSKen
364980dd02 更新类库 2025-08-31 05:38:28 +08:00
ZhanGSKen
0e155e4f3a <androidxdemo>Start New Stage Version. 2025-08-31 05:29:49 +08:00
ZhanGSKen
89febba5a9 <androidxdemo>Start New Stage Version. 2025-08-31 05:29:33 +08:00
ZhanGSKen
653330f8e1 <androidxdemo>Start New Stage Version. 2025-08-31 05:26:36 +08:00
ZhanGSKen
386c73effc <androidxdemo>Start New Stage Version. 2025-08-31 05:19:41 +08:00
ZhanGSKen
b385583c5a <androiddemo>Start New Stage Version. 2025-08-31 05:11:29 +08:00
ZhanGSKen
e7a9be2f56 <androiddemo>APK 15.0.0 release Publish. 2025-08-31 05:11:26 +08:00
ZhanGSKen
b27f7b0080 更新类库 2025-08-31 05:06:06 +08:00
ZhanGSKen
0a440419ff <libaes>Library Release 15.9.3 2025-08-31 05:01:04 +08:00
ZhanGSKen
9e189ed5ac <aes>APK 15.9.3 release Publish. 2025-08-31 05:00:43 +08:00
ZhanGSKen
0fb6aadc72 更新类库版本 2025-08-31 04:55:30 +08:00
ZhanGSKen
46f3315b02 Merge remote-tracking branch 'gitee/appbase' into aes 2025-08-31 04:54:49 +08:00
ZhanGSKen
c0ff228845 <libapputils>Library Release 15.8.5 2025-08-31 04:53:26 +08:00
ZhanGSKen
a0fe8f17a8 <apputils>APK 15.8.5 release Publish. 2025-08-31 04:53:04 +08:00
ZhanGSKen
16bd40fc59 Merge remote-tracking branch 'gitee/appbase' into apputils 2025-08-31 04:51:48 +08:00
ZhanGSKen
ffaf683c54 <libapputils>Library Release 15.8.4 2025-08-31 04:46:19 +08:00
ZhanGSKen
26f5f8d3db 编译参数修复 2025-08-31 04:45:24 +08:00
ZhanGSKen
917e25cdc8 更新基础类库版本 2025-08-31 04:41:42 +08:00
ZhanGSKen
707bed52c7 <libappbase>Library Release 15.9.5 2025-08-31 04:33:11 +08:00
ZhanGSKen
3795cf8631 <mymessagemanager>APK 15.3.5 release Publish. 2025-08-31 01:10:33 +08:00
ZhanGSKen
b374f3117a 更新联系人查询与短信发送框,在搜索到空数据时的显示逻辑。 2025-08-30 21:15:01 +08:00
ZhanGSKen
d581cd9842 <contacts>APK 15.3.15 release Publish. 2025-08-30 15:04:01 +08:00
ZhanGSKen
cef50d087d 联系人搜索框添加拼音首字母简单搜索功能,如“周星星”输入“zxx”即可搜索到。 2025-08-30 15:02:37 +08:00
ZhanGSKen
6d9adc124e <mymessagemanager>APK 15.3.4 release Publish. 2025-08-30 11:42:36 +08:00
ZhanGSKen
52f738b45b 修正选项开关提示 2025-08-30 11:40:55 +08:00
ZhanGSKen
9ece6778b7 <mymessagemanager>APK 15.3.3 release Publish. 2025-08-28 21:13:38 +08:00
ZhanGSKen
b7f8b76ace <libapputils>Library Release 15.8.4 2025-08-28 21:11:05 +08:00
ZhanGSKen
326e5fa68e Merge remote-tracking branch 'studio/aes' into aes 2025-08-28 21:08:37 +08:00
ZhanGSKen
e9c8f9029e <appbase>APK 15.9.1 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
c00bfa1292 <appbase>APK 15.9.0 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
11ee4dcf27 提升版本 2025-08-28 21:04:59 +08:00
ZhanGSKen
8974e24dce <appbase>Start New Stage Version. 2025-08-28 21:04:59 +08:00
ZhanGSKen
89142e379c 编译测试 2025-08-28 21:04:59 +08:00
ZhanGSKen
dabc671c27 <contacts>APK 15.3.11 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
ed849e92d1 区分防御层数量级差异,区分量级给出相应提示。 2025-08-28 21:04:59 +08:00
ZhanGSKen
42d2522927 <contacts>APK 15.3.10 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
6a52b2a8c3 添加应用效果提示 2025-08-28 21:04:59 +08:00
ZhanGSKen
02ed5bd5d1 <contacts>APK 15.3.9 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
b685665d0c 命名重构 2025-08-28 21:04:59 +08:00
ZhanGSKen
1b030a2855 联系人号码添加复制功能 2025-08-28 21:04:59 +08:00
ZhanGSKen
dd577f1765 通话记录号码添加复制功能 2025-08-28 21:04:59 +08:00
ZhanGSKen
0f8350600d <contacts>APK 15.3.8 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
1777ebb8dc 添加清空 BoBullToon 数据功能,更新默认 BoBullToon 数据地址。 2025-08-28 21:04:59 +08:00
ZhanGSKen
2b99f707e7 <contacts>APK 15.3.7 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
068c94e749 规则编辑列表显示优化 2025-08-28 21:04:59 +08:00
ZhanGSKen
0b3bc7e296 <contacts>APK 15.3.6 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
6ef747bcf8 <contacts/>Start New Stage Version. 2025-08-28 21:04:59 +08:00
ZhanGSKen
a8e843c388 更新应用描述 2025-08-28 21:04:59 +08:00
ZhanGSKen
d3fd593cb0 更新应用介绍页 2025-08-28 21:04:59 +08:00
ZhanGSKen
3aec176b8b 更新按钮文字描述 2025-08-28 21:04:59 +08:00
ZhanGSKen
5960f76238 添加应用使用方法提示 2025-08-28 21:04:59 +08:00
ZhanGSKen
1a118da827 更新说明书 2025-08-28 21:04:59 +08:00
ZhanGSKen
f73cad6f3e <contacts>APK 15.3.5 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
7130ecf023 <contacts>APK 15.3.4 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
953c8f08cd 检验拨不通号码群排在查询通讯录联系人前面 2025-08-28 21:04:59 +08:00
ZhanGSKen
87b7557f72 <contacts>APK 15.3.3 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
77f5a156f8 编译调试 2025-08-28 21:04:59 +08:00
ZhanGSKen
b34ea40536 <mymessagemanager>APK 15.3.1 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
d202a3443d <mymessagemanager>APK 15.3.0 release Publish. 2025-08-28 21:04:59 +08:00
ZhanGSKen
8c532c885f 介于UI界面有调整,版本号升级以区分。 2025-08-28 21:04:59 +08:00
ZhanGSKen
5fc4cb5f74 应用介绍页链接更新 2025-08-28 21:04:59 +08:00
ZhanGSKen
2a590a99fb 菜单功能BugFix 2025-08-28 21:04:59 +08:00
ZhanGSKen
c6ad707ca2 应用菜单排列调整 2025-08-28 21:04:59 +08:00
ZhanGSKen
ee13a43fb6 <libaes>Library Release 15.9.2 2025-08-28 21:04:18 +08:00
ZhanGSKen
5fbe1d8f71 <libaes>Library Release 15.9.2 2025-08-28 20:57:31 +08:00
ZhanGSKen
38fe941a8b <libappbase>Library Release 15.9.5 2025-08-28 20:56:48 +08:00
ZhanGSKen
e13c8e7af0 <mymessagemanager>Start New Stage Version. 2025-08-28 16:03:47 +08:00
ZhanGSKen
a4988b5b68 联系人查询发送窗口添加拼音查询功能。 2025-08-28 16:02:41 +08:00
ZhanGSKen
04df902b6b <contacts>APK 15.3.14 release Publish. 2025-08-26 01:37:58 +08:00
ZhanGSKen
33c71ea868 添加手机端BoBullToon数据文件夹查找模块。 2025-08-25 20:47:49 +08:00
ZhanGSKen
5507126f6b <mymessagemanager>APK 15.3.2 release Publish. 2025-08-23 13:40:21 +08:00
ZhanGSKen
d381c29452 短信发送窗口UI优化 2025-08-23 13:38:20 +08:00
ZhanGSKen
ba861d910e <contacts>APK 15.3.13 release Publish. 2025-08-20 20:19:22 +08:00
ZhanGSKen
f5d9aafe43 更新默认BoBullToon数据源URL 2025-08-20 20:17:53 +08:00
ZhanGSKen
e80d7e7b03 <appbase>APK 15.9.5 release Publish. 2025-08-18 03:56:26 +08:00
ZhanGSKen
6e0a833fde <appbase>APK 15.9.4 release Publish. 2025-08-17 21:30:42 +08:00
ZhanGSKen
c596ee5fa4 <appbase>APK 15.9.3 release Publish. 2025-08-17 21:10:44 +08:00
ZhanGSKen
5e99f1278e <appbase>APK 15.9.2 release Publish. 2025-08-17 19:23:49 +08:00
ZhanGSKen
b7158d1ebd 添加项目关键配置说明 2025-08-15 21:39:22 +08:00
ZhanGSKen
9b51250ebf Merge remote-tracking branch 'origin/androidxdemo' into appbase 2025-08-15 20:53:02 +08:00
ZhanGSKen
eb61eb7306 <powerbell>APK 15.4.10 release Publish. 2025-08-13 02:46:25 +08:00
ZhanGSKen
31ad66685c 修复信息提示错误问题。 2025-08-13 02:45:05 +08:00
ZhanGSKen
beb561ad6a <powerbell>APK 15.4.9 release Publish. 2025-08-13 02:37:14 +08:00
ZhanGSKen
8869265d60 优化电量清理活动窗口UI显示。 2025-08-13 02:35:36 +08:00
ZhanGSKen
2739627aff <timestamp>APK 15.1.3 release Publish. 2025-07-28 11:36:51 +08:00
ZhanGSKen
58e0be9cf4 添加开机启动功能。 2025-07-28 11:35:30 +08:00
ZhanGSKen
9e9402f84e <contacts>APK 15.3.12 release Publish. 2025-07-27 15:39:49 +08:00
ZhanGSKen
ec18330022 窗口回显时刷新到最新数据。 2025-07-27 15:38:16 +08:00
ZhanGSKen
8bb80ef575 <timestamp>APK 15.1.2 release Publish. 2025-07-24 15:25:52 +08:00
ZhanGSKen
c1e6e32809 在主界面添加时间戳截取按钮 2025-07-24 15:22:18 +08:00
ZhanGSKen
3e7722e2c0 <numtable>APK 15.1.1 release Publish. 2025-07-24 10:16:27 +08:00
ZhanGSKen
a1707e73b5 添加页面方向指示图标创建功能 2025-07-24 10:12:30 +08:00
ZhanGSKen
9dcbaa0d75 添加桌面快捷方式创建功能 2025-07-24 09:00:19 +08:00
ZhanGSKen
23920a7ff1 Merge remote-tracking branch 'origin/aes' into appbase 2025-07-24 08:25:21 +08:00
ZhanGSKen
17c373c490 <appbase>APK 15.9.1 release Publish. 2025-07-17 11:39:14 +08:00
ZhanGSKen
5f7c94b349 <appbase>APK 15.9.0 release Publish. 2025-07-17 11:24:17 +08:00
ZhanGSKen
c2b739d345 提升版本 2025-07-17 11:23:30 +08:00
ZhanGSKen
67a05cd457 <appbase>Start New Stage Version. 2025-07-17 11:21:04 +08:00
ZhanGSKen
554ab758bf 编译测试 2025-07-17 11:17:58 +08:00
ZhanGSKen
20e118cd34 Merge remote-tracking branch 'origin/contacts' into appbase 2025-07-17 11:15:51 +08:00
ZhanGSKen
f370ae8ffb <contacts>APK 15.3.11 release Publish. 2025-07-17 09:57:24 +08:00
ZhanGSKen
c92c874ea1 区分防御层数量级差异,区分量级给出相应提示。 2025-07-17 09:54:47 +08:00
ZhanGSKen
90a6116c0a <contacts>APK 15.3.10 release Publish. 2025-07-17 04:06:42 +08:00
ZhanGSKen
45208ecbb1 添加应用效果提示 2025-07-17 04:05:01 +08:00
ZhanGSKen
c28d655fe3 <contacts>APK 15.3.9 release Publish. 2025-07-06 16:18:59 +08:00
ZhanGSKen
4b5905f74e 命名重构 2025-07-06 16:17:09 +08:00
ZhanGSKen
6bd01780ec 联系人号码添加复制功能 2025-07-06 16:14:16 +08:00
ZhanGSKen
a6699262f8 通话记录号码添加复制功能 2025-07-06 16:02:45 +08:00
ZhanGSKen
07b5e66875 <powerbell>APK 15.4.8 release Publish. 2025-07-06 12:53:58 +08:00
ZhanGSKen
91f5cf9051 更新应用介绍页 2025-07-06 12:51:50 +08:00
ZhanGSKen
ea2d38defc <contacts>APK 15.3.8 release Publish. 2025-07-05 12:43:15 +08:00
ZhanGSKen
e430b7abe4 添加清空 BoBullToon 数据功能,更新默认 BoBullToon 数据地址。 2025-07-05 12:41:14 +08:00
ZhanGSKen
945eadb617 <mymessagemanager>APK 15.3.1 release Publish. 2025-07-03 13:50:15 +08:00
ZhanGSKen
c5bffc5eef <mymessagemanager>APK 15.3.0 release Publish. 2025-07-03 13:47:44 +08:00
ZhanGSKen
88597fe407 介于UI界面有调整,版本号升级以区分。 2025-07-03 13:28:17 +08:00
ZhanGSKen
53f985533a Merge branch 'mymessagemanager' into appbase 2025-07-03 13:00:51 +08:00
ZhanGSKen
a3950f13ad 应用介绍页链接更新 2025-07-03 12:56:57 +08:00
ZhanGSKen
c878e9dc02 菜单功能BugFix 2025-07-03 12:53:33 +08:00
ZhanGSKen
f2f7cab330 应用菜单排列调整 2025-07-03 12:38:09 +08:00
ZhanGSKen
0e3b9dc760 20250629_120423_103 2025-06-29 12:04:31 +08:00
ZhanGSKen
6c8b0dcfa5 <contacts>APK 15.3.7 release Publish. 2025-06-28 19:57:09 +08:00
ZhanGSKen
7de8a4f084 规则编辑列表显示优化 2025-06-28 19:54:58 +08:00
ZhanGSKen
219c6614be <contacts>APK 15.3.6 release Publish. 2025-06-28 13:20:38 +08:00
ZhanGSKen
0f5bb020b9 <contacts/>Start New Stage Version. 2025-06-28 13:17:42 +08:00
ZhanGSKen
7794ff80ec 更新应用描述 2025-06-28 13:16:18 +08:00
ZhanGSKen
7463ad3352 更新应用介绍页 2025-06-28 13:08:24 +08:00
ZhanGSKen
69187e3ed0 更新类库 2025-06-28 13:04:03 +08:00
ZhanGSKen
753032efed <libaes>Library Release 15.9.2 2025-06-28 12:59:55 +08:00
ZhanGSKen
2b4c43c9af <aes>APK 15.9.2 release Publish. 2025-06-28 12:59:30 +08:00
ZhanGSKen
711c98d556 应用介绍页更新 2025-06-28 12:42:52 +08:00
ZhanGSKen
202205588a 更新按钮文字描述 2025-06-28 12:21:55 +08:00
ZhanGSKen
42c4978b44 添加应用使用方法提示 2025-06-28 12:11:26 +08:00
ZhanGSKen
1a2b7b862d Merge remote-tracking branch 'origin/appbase' into contacts 2025-06-28 12:04:27 +08:00
ZhanGSKen
8730f434dd 更新说明书 2025-06-28 01:18:20 +08:00
ZhanGSKen
eb253b374f 更新说明书 2025-06-28 01:03:45 +08:00
ZhanGSKen
c4e88e9593 编译配置初始化 2025-06-24 19:18:23 +08:00
ZhanGSKen
08d9d92ae4 <autoinstaller>APK 15.2.2 release Publish. 2025-06-24 09:54:47 +08:00
ZhanGSKen
74841c08dc 编译参数修复 2025-06-24 09:52:39 +08:00
ZhanGSKen
945bacb825 <autoinstaller>APK 15.2.1 release Publish. 2025-06-24 09:49:39 +08:00
ZhanGSKen
0e464495fd 编译调试 2025-06-24 09:45:47 +08:00
ZhanGSKen
f8944490f8 <powerbell>APK 15.4.7 release Publish. 2025-06-23 20:07:50 +08:00
ZhanGSKen
733af004f6 编译参数修复 2025-06-23 20:07:12 +08:00
ZhanGSKen
c03568e1f5 <powerbell>APK 15.4.6 release Publish. 2025-06-23 20:04:59 +08:00
ZhanGSKen
a0575a5e8b 调整像素拾取窗口、背景图片设置窗口与主窗口的切换逻辑。 2025-06-23 20:03:32 +08:00
ZhanGSKen
0e57ce679e <powerbell>APK 15.4.5 release Publish. 2025-06-23 14:49:46 +08:00
ZhanGSKen
f9211a8eb4 优化背景像素拾取UI 2025-06-23 14:48:24 +08:00
ZhanGSKen
4c31ff9b54 调整窗口切换模式 2025-06-23 14:29:59 +08:00
ZhanGSKen
8cf610962e <powerbell>APK 15.4.4 release Publish. 2025-06-22 16:21:12 +08:00
ZhanGSKen
3071d186ec 添加图片像素拾取并可以设置像素为图片背景 2025-06-22 16:19:24 +08:00
ZhanGSKen
ac1c008035 <contacts>APK 15.3.5 release Publish. 2025-06-14 18:43:41 +08:00
ZhanGSKen
b124487cb1 Merge remote-tracking branch 'origin/appbase' into contacts 2025-06-14 17:59:39 +08:00
ZhanGSKen
9621d35f79 <contacts>APK 15.3.4 release Publish. 2025-06-14 17:58:55 +08:00
ZhanGSKen
17de0832a6 检验拨不通号码群排在查询通讯录联系人前面 2025-06-14 17:49:16 +08:00
ZhanGSKen
f53b222b7f <webpagesources>APK 15.0.6 release Publish. 2025-06-13 10:04:49 +08:00
ZhanGSKen
0c0cde8406 编译参数冲突修复 2025-06-13 09:41:59 +08:00
ZhanGSKen
46967065c0 修复dev网站证书问题,添加证书配置。 2025-06-13 09:37:45 +08:00
ZhanGSKen
8edbff5ac1 <webpagesources>APK 15.0.5 release Publish. 2025-06-12 02:46:36 +08:00
ZhanGSKen
434f8a8549 精简信息 2025-06-12 02:44:57 +08:00
ZhanGSKen
c04be60b13 修复外部应用传入view action时的处理方法Bug 2025-06-12 02:43:51 +08:00
ZhanGSKen
641098f8fb <webpagesources>APK 15.0.4 release Publish. 2025-06-11 13:59:15 +08:00
ZhanGSKen
dba54ac4b2 编译测试 2025-06-11 13:57:48 +08:00
ZhanGSKen
c6cd779889 <webpagesources>APK 15.0.3 release Publish. 2025-06-11 13:46:01 +08:00
ZhanGSKen
dfb1692a04 <webpagesources>APK 15.0.2 release Publish. 2025-06-11 13:42:18 +08:00
ZhanGSKen
c83c8f66b3 <webpagesources>APK 15.0.1 release Publish. 2025-06-11 05:46:14 +08:00
ZhanGSKen
cd7b5f38bf <webpagesources>APK 15.0.0 release Publish. 2025-06-11 05:44:31 +08:00
ZhanGSKen
0c2e73b82e <webpagesources>Start New Stage Version. 2025-06-11 05:26:10 +08:00
ZhanGSKen
7b1838ff8e 添加Log窗口调用 2025-06-11 05:15:25 +08:00
ZhanGSKen
73ff3d1726 移除LogView.添加消息状态栏。 2025-06-11 05:11:56 +08:00
ZhanGSKen
a69572e216 清理冗余代码 2025-06-11 04:09:09 +08:00
ZhanGSKen
fa79c3f807 请豆包优化BaseWebView的WebSettings部分后,再修复点击网页链接无反应的Bug。 2025-06-11 03:56:10 +08:00
ZhanGSKen
fde4b275f7 设置默认浏览器类型接口 2025-06-10 15:16:58 +08:00
ZhanGSKen
d66d9373ff 继承https://archives-git.winboll.cc/git/repositories_old/repositories_Bck20250428/webpagesource.git源码 2025-06-10 14:42:17 +08:00
ZhanGSKen
f32ed94e4e 添加 WinBoLL 浏览器 2025-06-10 12:35:45 +08:00
ZhanGSKen
89dac91cc6 <contacts>APK 15.3.3 release Publish. 2025-06-04 00:08:54 +08:00
ZhanGSKen
3809c1bcab 编译调试 2025-06-04 00:04:20 +08:00
515 changed files with 10867 additions and 6728 deletions

16
.gitignore vendored
View File

@@ -87,19 +87,15 @@ lint/tmp/
# Android Profiling
*.hprof
# Custom
.androidide
# 忽略 Lint 输出文件
lint-results.xml
lint-results.html
winboll.properties
local.properties
## 忽略 AndroidIDE 临时文件夹
.androidide
## 忽略模块应用编译配置
/settings.gradle
/gradle.properties
## 忽略 srv 纠结问题
/srv/
## 忽略 winboll-x 文件夹
/winboll-x/
/winboll.properties
/local.properties

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "libjc/jcc/libs"]
path = libjc/jcc/libs
url = https://gitea.winboll.cc/Studio/APP_libjc_jcc_libs.git
[submodule "keystore"]
path = keystore
url = https://gitea.winboll.cc/Studio/keystore.git

View File

@@ -2,6 +2,11 @@
//
android {
def winbollProps = new Properties()
def winbollPropsFile = rootProject.file("${winbollFilePath}")
assert(winbollPropsFile.exists())
winbollProps.load(new FileInputStream(winbollPropsFile))
// 读取秘钥配置文件
//
def keyProps = new Properties()
@@ -122,6 +127,7 @@ android {
FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
fos.close();
println "\n\n>>> Project build.properties saved.\n\n";
if(winbollBuildProps['libraryProject'] != "") {
// 如果应用 build.properties 文件设置了类库模块项目文件名
@@ -133,14 +139,15 @@ android {
java.nio.file.Path targetFilePath = libraryProjectBuildPropsFile.toPath();
// 使用copyTo()方法复制文件,如果目标文件存在会被覆盖,可选参数可以选择不覆盖
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
println "\n\n>>> Library Project build.properties saved.\n\n";
}
}
}
// 如果正在发布,就拷贝到 WinBoLL 标签管理文件夹
// 如果正在发布,就拷贝到 WinBoLL 标签管理文件夹,和处理 Git 仓库管理任务。
//
if((variant.flavorName == "stage"&&variant.buildType.name == "debug")
|| (variant.flavorName == "stage"&&variant.buildType.name == "release")){
if(variant.flavorName == "stage"&&variant.buildType.name == "release"){
// 发布 APK 文件
//
// 截取版本号的版本字段为短版本名
@@ -221,6 +228,7 @@ android {
}
// 提交新的编译标志配置
println 'exec bashCommitAppPublishBuildFlagInfoFilePath ...'
def resultCommitBuildFlag = exec {
commandLine 'bash', '--', "${RootProjectDir}/${bashCommitAppPublishBuildFlagInfoFilePath}", "${RootProjectDir}", "${versionName}", variant.buildType.name , rootProject.name
}
@@ -229,10 +237,65 @@ android {
}
} // if(variant.buildType.name == "release"){
// 如果公共目录存在就拷贝到公共目录并重命名为app.apk
// 如果正在调试发布版就只生成和输出APK文件不处理 Git 仓库提交与更新问题。
//
File outCommonDir = new File("/sdcard/AppProjects")
String commandAPKName = "app.apk"
if(variant.flavorName == "stage"&&variant.buildType.name == "debug"){
// 发布 APK 文件
//
// 截取版本号的版本字段为短版本名
String szVersionName = "${versionName}"
String[] szlistTemp = szVersionName.split("-")
String szShortVersionName = szlistTemp[0]
//String szCommonTagAPKName = "${rootProject.name}_" + szShortVersionName + ".apk"
String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk"
println "CommonTagAPKName is : " + szCommonTagAPKName
//File outTagDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/tag/")
File outTagDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}/")
// 创建目标路径目录
if(!outTagDir.exists()) {
outTagDir.mkdirs();
println "Output Folder Created.(Tags) : " + outTagDir.getAbsolutePath()
}
if(outTagDir.exists()) {
File targetAPK = new File(outTagDir, "${szCommonTagAPKName}")
if(targetAPK.exists()) {
// 标签版本APK文件已经存在构建拷贝任务停止
println '如果是在调试 Stage 版应用包构建,请删除(注在debug目录)现有的 Stage 应用包('+targetAPK.getAbsolutePath()+')。再编译一次。'
assert (!targetAPK.exists())
// 可选择删除并继续输出APK文件
//delete targetAPK
}
// 复制一个备份
copy{
from file.outputFile
into outTagDir
rename {
String fileName -> "${outputFileName}"
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${outputFileName}"
}
// 复制一个并重命名为短版本名
copy{
from file.outputFile
into outTagDir
rename {
String fileName -> "${szCommonTagAPKName}"
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${szCommonTagAPKName}"
}
//不保存编译标志配置
}
}
// 如果配置了APK额外输出路径就复制一份拷贝到额外路径。
//
if(winbollProps['ExtraAPKOutputPath'] != null ) {
File apkFile = new File(winbollProps['ExtraAPKOutputPath'])
File outCommonDir = apkFile.getParentFile();
String commandAPKName = apkFile.getName();
if(outCommonDir.exists()) {
copy{
from file.outputFile
@@ -243,6 +306,7 @@ android {
println "Output APK (Common): " + outCommonDir.getAbsolutePath() + "/${commandAPKName}"
}
}
}
}

View File

@@ -115,10 +115,11 @@
# 本项目要实际运用需要注意以下几个步骤:
# 在项目根目录下:
## ★. 项目模块编译环境设置(必须)settings.gradle-demo 要复制为 settings.gradle并取消相应项目模块的注释。
## ★. 项目模块编译环境设置(必须) 在根目录拷贝 gradle.properties-androidx-demo 或者 gradle.properties-android-demo 文件为 gradle.properties。
## ★. 项目 Android SDK 编译环境设置(可选)local.properties-demo 要复制为 local.properties并按需要设置 Android SDK 目录。
## ★. 应用签名密钥 keystore 设置问题。一般调试编译只需用【Termux】cd 进 GenKeyStore 目录执行 $ bash gen_debug_keystore.sh 命令即可完成设置。
## ☆. 应用 WiBoLL 签名密钥配置问题<非必须考虑>。设置时需要 clone 【keystore】模块源码并拷贝模块目录的 appkey.jks 与 appkey.keystore 到项目根目录即可。
## ☆. 类库型模块编译环境设置(可选)winboll.properties-demo 要复制为 winboll.properties并按需要设置 WinBoLL Maven 库登录用户信息。
## ☆. 类库型模块编译环境设置(可选)winboll.properties-demo 要复制为 winboll.properties并按需要设置 WinBoLL Maven 库登录用户信息 和 APK 文件额外输出路径
# ☆类库型项目编译方法
@@ -127,7 +128,8 @@
设置属性 libraryProject=<类库项目模块文件夹名称>
### 再编译测试项目
$ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称>
#### 测试项目编译后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
#### 测试项目编译后,编译器会复制一份 APK 到 路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
#### 若是 winboll.properties 文件的 [ExtraAPKOutputPath] 属性设置了路径。编译器也会复制一份 APK 到这个路径。
### 最后编译类库项目
$ bash .winboll/bashPublishLIBAddTag.sh <类库项目模块文件夹名称>
#### 类库模块编译命令执行后,编译器会发布到 WinBoLL Nexus Maven 库Maven 库地址可以参阅根项目目录配置 build.gradle 文件。
@@ -135,4 +137,17 @@ $ bash .winboll/bashPublishLIBAddTag.sh <类库项目模块文件夹名称>
# ☆应用型项目编译方法
## 直接调用以下命令编译应用型项目
$ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称>
#### 应用模块编译命令执行后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
#### 应用模块编译命令执行后,编译器会复制一份 APK 到
#### 测试项目编译后,编译器会复制一份 APK 到 路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
#### 若是 winboll.properties 文件的 [ExtraAPKOutputPath] 属性设置了路径。编译器也会复制一份 APK 到这个路径。
## ☆应用调试编译方法
使用以下命令编译调试:
### Beta调试使用
$ bash gradlew assembleBetaDebug
### Stage调试使用
$ bash gradlew assembleStageDebug
### 若是 winboll.properties 文件的 [ExtraAPKOutputPath] 属性设置了路径。编译器也会复制一份 APK 到这个路径。

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Thu Jun 19 20:42:40 HKT 2025
stageCount=2
#Sun Aug 31 23:40:17 HKT 2025
stageCount=4
libraryProject=libaes
baseVersion=15.9
publishVersion=15.9.1
publishVersion=15.9.3
buildCount=0
baseBetaVersion=15.9.2
baseBetaVersion=15.9.4

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.aes;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/24 23:52:29
* @Describe AES应用介绍窗口
*/
@@ -83,7 +83,7 @@ public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(szBranchName);
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=2&fromuid=1");
appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=3&extra=page%3D1");
appInfo.setAppAPKName("AES");
appInfo.setAppAPKFolderName("AES");
//appInfo.setIsAddDebugTools(false);

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.aes;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/13 19:03:58
* @Describe AES应用类
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.aes;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/13 19:05:52
* @Describe 应用主窗口
*/

View File

@@ -0,0 +1 @@

View File

@@ -67,6 +67,6 @@ dependencies {
// https://mvnrepository.com/artifact/com.android.support/recyclerview-v7
api 'com.android.support:recyclerview-v7:28.0.0'
api 'cc.winboll.studio:libapputils:15.8.2'
api 'cc.winboll.studio:libappbase:15.8.2'
api 'cc.winboll.studio:libapputils:15.8.5'
api 'cc.winboll.studio:libappbase:15.9.5'
}

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Sun Jun 01 08:02:46 GMT 2025
stageCount=0
#Sun Aug 31 05:11:26 CST 2025
stageCount=1
libraryProject=
baseVersion=15.0
publishVersion=15.0.0
buildCount=27
buildCount=0
baseBetaVersion=15.0.1

View File

@@ -0,0 +1 @@

View File

@@ -29,7 +29,7 @@ android {
// versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "15.0"
versionName "15.1"
if(true) {
versionName = genVersionName("${versionName}")
}
@@ -67,7 +67,7 @@ dependencies {
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
//api 'androidx.fragment:fragment:1.1.0'
api 'cc.winboll.studio:libaes:15.8.0'
api 'cc.winboll.studio:libapputils:15.8.2'
api 'cc.winboll.studio:libappbase:15.8.2'
api 'cc.winboll.studio:libaes:15.9.3'
api 'cc.winboll.studio:libapputils:15.8.5'
api 'cc.winboll.studio:libappbase:15.9.5'
}

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Thu Jun 19 12:49:47 GMT 2025
stageCount=0
#Sun Aug 31 05:42:50 CST 2025
stageCount=2
libraryProject=
baseVersion=15.0
publishVersion=15.0.0
buildCount=26
baseBetaVersion=15.0.1
baseVersion=15.1
publishVersion=15.1.1
buildCount=0
baseBetaVersion=15.1.2

View File

@@ -30,7 +30,7 @@ android {
// versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "15.8"
versionName "15.10"
if(true) {
versionName = genVersionName("${versionName}")
}

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Mon Jun 09 09:38:19 HKT 2025
stageCount=9
#Mon Sep 22 06:01:08 HKT 2025
stageCount=8
libraryProject=libappbase
baseVersion=15.8
publishVersion=15.8.8
baseVersion=15.10
publishVersion=15.10.7
buildCount=0
baseBetaVersion=15.8.9
baseBetaVersion=15.10.8

View File

@@ -2,7 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<application
tools:replace="android:icon,android:roundIcon"
android:icon="@drawable/ic_winboll_beta"
android:roundIcon="@drawable/ic_winboll_beta">
<!-- Put flavor specific code here -->

View File

@@ -3,14 +3,16 @@
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.appbase">
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".App"
android:icon="@drawable/ic_miapp"
android:icon="@drawable/ic_winboll"
android:label="@string/app_name"
android:theme="@style/MyAPPBaseTheme"
android:resizeableActivity="true"
android:process=":App"
android:networkSecurityConfig="@xml/network_security_config">
android:process=":App">
<activity
android:name=".MainActivity"
@@ -25,102 +27,13 @@
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name=".activities.NewActivity"
android:label="NewActivity"
android:exported="true"
android:resizeableActivity="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"/>
<activity
android:name=".activities.New2Activity"
android:label="New2Activity"
android:exported="true"
android:resizeableActivity="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"/>
<service
android:name=".MyTileService"
android:exported="true"
android:label="@string/tileservice_name"
android:icon="@drawable/ic_launcher"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE"/>
</intent-filter>
</service>
<service
android:name=".services.MainService"
android:exported="true"/>
<service
android:name="cc.winboll.studio.appbase.services.TestDemoBindService"
android:exported="true"/>
<service
android:name="cc.winboll.studio.appbase.services.TestDemoService"
android:exported="true"/>
<service android:name=".services.AssistantService"/>
<receiver
android:name="cc.winboll.studio.appbase.receivers.MainReceiver"
android:exported="true">
<intent-filter>
<action android:name="cc.winboll.studio.appbase.receivers.MainReceiver"/>
</intent-filter>
</receiver>
<receiver
android:name=".widgets.APPNewsWidget"
android:exported="true">
<intent-filter>
<action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidget.ACTION_WAKEUP_SERVICE"/>
<action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidget.ACTION_RELOAD_REPORT"/>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_provider_info_sos"/>
</receiver>
<receiver
android:name=".receivers.APPNewsWidgetClickListener"
android:exported="true">
<intent-filter>
<action android:name="cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener.ACTION_PRE"/>
<action android:name="cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener.ACTION_NEXT"/>
</intent-filter>
</receiver>
<activity android:name=".GlobalApplication$CrashActivity"/>
<meta-data
android:name="android.max_aspect"

View File

@@ -1,27 +1,19 @@
package cc.winboll.studio.appbase;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/01/05 09:54:42
* @Describe APPbase 应用类
*/
import cc.winboll.studio.libappbase.GlobalApplication;
import android.content.IntentFilter;
import cc.winboll.studio.libappbase.sos.SOSCenterServiceReceiver;
import cc.winboll.studio.libappbase.sos.SOS;
import cc.winboll.studio.libappbase.GlobalApplication;
public class App extends GlobalApplication {
public static final String TAG = "App";
SOSCenterServiceReceiver mSOSCenterServiceReceiver;
@Override
public void onCreate() {
super.onCreate();
mSOSCenterServiceReceiver = new SOSCenterServiceReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(SOS.ACTION_SOS);
registerReceiver(mSOSCenterServiceReceiver, intentFilter);
}
}

View File

@@ -1,44 +1,23 @@
package cc.winboll.studio.appbase;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.Toast;
import android.widget.Toolbar;
import cc.winboll.studio.appbase.R;
import cc.winboll.studio.appbase.activities.NewActivity;
import cc.winboll.studio.appbase.activities.WinBoLLActivity;
import cc.winboll.studio.appbase.services.MainService;
import cc.winboll.studio.appbase.services.TestDemoBindService;
import cc.winboll.studio.appbase.services.TestDemoService;
import cc.winboll.studio.libappbase.CrashHandler;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.GlobalCrashActivity;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libappbase.sos.SOS;
import cc.winboll.studio.libappbase.utils.ToastUtils;
import cc.winboll.studio.libappbase.widgets.StatusWidget;
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
import cc.winboll.studio.libappbase.LogActivity;
import cc.winboll.studio.libappbase.ToastUtils;
public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
public class MainActivity extends Activity {
public static final String TAG = "MainActivity";
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
Toolbar mToolbar;
@Override
@@ -49,9 +28,6 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
mToolbar = findViewById(R.id.toolbar);
setActionBar(mToolbar);
CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
}
@Override
@@ -62,122 +38,37 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == R.id.item_yun) {
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.YunActivity.class);
} else if(item.getItemId() == R.id.item_logon) {
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.LogonActivity.class);
switch (item.getItemId()) {
case R.id.item_home : {
openWebsiteInBrowser(this);
}
}
// 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。
return super.onOptionsItemSelected(item);
}
@Override
protected void onDestroy() {
super.onDestroy();
Intent intentAPPWidget = new Intent(this, StatusWidget.class);
intentAPPWidget.setAction(StatusWidget.ACTION_STATUS_UPDATE);
sendBroadcast(intentAPPWidget);
}
public void onSwitchDebugMode(View view) {
boolean isDebuging = ((CheckBox)view).isChecked();
GlobalApplication.setIsDebuging(isDebuging);
GlobalApplication.saveDebugStatus();
}
public void onPreviewGlobalCrashActivity(View view) {
Intent intent = new Intent(this, GlobalCrashActivity.class);
intent.putExtra(CrashHandler.EXTRA_CRASH_INFO, "Demo log...");
startActivity(intent);
}
public void onStartCenter(View view) {
MainService.startMainService(this);
}
public void onStopCenter(View view) {
MainService.stopMainService(this);
}
public void onTestStopMainServiceWithoutSettingEnable(View view) {
LogUtils.d(TAG, "onTestStopMainServiceWithoutSettingEnable");
stopService(new Intent(this, MainService.class));
}
public void onTestUseComponentStartService(View view) {
LogUtils.d(TAG, "onTestUseComponentStartService");
// 目标服务的包名和类名
String packageName = this.getPackageName();
String serviceClassName = TestDemoService.class.getName();
// 构建Intent
Intent intentService = new Intent();
intentService.setComponent(new ComponentName(packageName, serviceClassName));
startService(intentService);
}
public void onTestDemoServiceSOS(View view) {
Intent intent = new Intent(this, TestDemoService.class);
stopService(intent);
if (App.isDebuging()) {
SOS.sosToAppBaseBeta(this, TestDemoService.class.getName());
} else {
SOS.sosToAppBase(this, TestDemoService.class.getName());
public void onCrashTest(View view) {
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
getString(i);
}
}
public void onSartTestDemoService(View view) {
Intent intent = new Intent(this, TestDemoService.class);
intent.setAction(TestDemoService.ACTION_ENABLE);
startService(intent);
public void onLogTest(View view) {
LogActivity.startLogActivity(this);
}
public void onStopTestDemoService(View view) {
Intent intent = new Intent(this, TestDemoService.class);
intent.setAction(TestDemoService.ACTION_DISABLE);
startService(intent);
Intent intentStop = new Intent(this, TestDemoService.class);
stopService(intentStop);
}
public void onStopTestDemoServiceNoSettings(View view) {
Intent intent = new Intent(this, TestDemoService.class);
stopService(intent);
}
public void onSartTestDemoBindService(View view) {
Intent intent = new Intent(this, TestDemoBindService.class);
intent.setAction(TestDemoBindService.ACTION_ENABLE);
startService(intent);
/**
* 唤起默认浏览器打开指定网站
* @param context 上下文(如 Activity.this
*/
public void openWebsiteInBrowser(Context context) {
// 目标网站地址
String url = "https://www.winboll.cc";
// 构建打开浏览器的意图
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
// 设置标志:避免创建新的任务栈(可选,按需求调整)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
public void onStopTestDemoBindService(View view) {
Intent intent = new Intent(this, TestDemoBindService.class);
intent.setAction(TestDemoBindService.ACTION_DISABLE);
startService(intent);
Intent intentStop = new Intent(this, TestDemoBindService.class);
stopService(intentStop);
}
public void onStopTestDemoBindServiceNoSettings(View view) {
Intent intent = new Intent(this, TestDemoBindService.class);
stopService(intent);
}
public void onTestOpenNewActivity(View view) {
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, NewActivity.class);
}
@Override
protected void onResume() {
super.onResume();
}
}

View File

@@ -1,79 +0,0 @@
package cc.winboll.studio.appbase;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/02/13 19:30:10
*/
import android.content.Context;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import cc.winboll.studio.appbase.models.MainServiceBean;
import cc.winboll.studio.appbase.services.MainService;
public class MyTileService extends TileService {
public static final String TAG = "MyTileService";
volatile static MyTileService _MyTileService;
@Override
public void onStartListening() {
super.onStartListening();
_MyTileService = this;
Tile tile = getQsTile();
MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
if (bean != null && bean.isEnable()) {
//MainService.startMainService(context);
tile.setState(Tile.STATE_ACTIVE);
tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud));
} else {
//MainService.stopMainService(context);
tile.setState(Tile.STATE_INACTIVE);
tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
}
tile.updateTile();
// Tile tile = getQsTile();
// tile.setState(Tile.STATE_INACTIVE);
// tile.setLabel(getString(R.string.tileservice_name));
// tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
// tile.updateTile();
}
@Override
public void onClick() {
super.onClick();
Tile tile = getQsTile();
MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
if (bean == null) {
bean = new MainServiceBean();
}
if (tile.getState() == Tile.STATE_ACTIVE) {
bean.setIsEnable(false);
MainServiceBean.saveBean(this, bean);
MainService.stopMainService(this);
} else if (tile.getState() == Tile.STATE_INACTIVE) {
bean.setIsEnable(true);
MainServiceBean.saveBean(this, bean);
MainService.startMainService(this);
}
updateServiceIconStatus(this);
}
public static void updateServiceIconStatus(Context context) {
if (_MyTileService == null) {
return;
}
Tile tile = _MyTileService.getQsTile();
MainServiceBean bean = MainServiceBean.loadBean(context, MainServiceBean.class);
if (bean != null && bean.isEnable()) {
tile.setState(Tile.STATE_ACTIVE);
tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud));
} else {
tile.setState(Tile.STATE_INACTIVE);
tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud_outline));
}
tile.updateTile();
}
}

View File

@@ -1,77 +0,0 @@
package cc.winboll.studio.appbase.activities;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/03/25 11:46:40
* @Describe 测试窗口2
*/
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toolbar;
import cc.winboll.studio.appbase.R;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
public class New2Activity extends WinBoLLActivity implements IWinBoLLActivity {
public static final String TAG = "New2Activity";
Toolbar mToolbar;
//LogView mLogView;
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new2);
// mLogView = findViewById(R.id.logview);
// mLogView.start();
mToolbar = findViewById(R.id.toolbar);
setActionBar(mToolbar);
}
@Override
protected void onResume() {
super.onResume();
//mLogView.start();
}
public void onCloseThisActivity(View view) {
GlobalApplication.getWinBoLLActivityManager().finish(this);
}
public void onCloseAllActivity(View view) {
GlobalApplication.getWinBoLLActivityManager().finishAll();
}
public void onNewActivity(View view) {
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, NewActivity.class);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。
return super.onOptionsItemSelected(item);
}
}

View File

@@ -1,75 +0,0 @@
package cc.winboll.studio.appbase.activities;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/03/25 05:04:22
*/
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toolbar;
import cc.winboll.studio.appbase.R;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
public class NewActivity extends WinBoLLActivity implements IWinBoLLActivity {
public static final String TAG = "NewActivity";
Toolbar mToolbar;
//LogView mLogView;
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new);
// mLogView = findViewById(R.id.logview);
// mLogView.start();
mToolbar = findViewById(R.id.toolbar);
setActionBar(mToolbar);
}
@Override
protected void onResume() {
super.onResume();
//mLogView.start();
}
public void onCloseThisActivity(View view) {
GlobalApplication.getWinBoLLActivityManager().finish(this);
}
public void onCloseAllActivity(View view) {
GlobalApplication.getWinBoLLActivityManager().finishAll();
}
public void onNew2Activity(View view) {
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, New2Activity.class);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。
return super.onOptionsItemSelected(item);
}
}

View File

@@ -1,38 +0,0 @@
package cc.winboll.studio.appbase.handlers;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/02/14 03:51:40
*/
import android.os.Handler;
import android.os.Message;
import cc.winboll.studio.appbase.services.MainService;
import java.lang.ref.WeakReference;
public class MainServiceHandler extends Handler {
public static final String TAG = "MainServiceHandler";
public static final int MSG_REMINDTHREAD = 0;
WeakReference<MainService> serviceWeakReference;
public MainServiceHandler(MainService service) {
serviceWeakReference = new WeakReference<MainService>(service);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REMINDTHREAD: // 处理下载完成消息更新UI
{
// 显示提醒消息
//
//LogUtils.d(TAG, "显示提醒消息");
MainService mainService = serviceWeakReference.get();
if (mainService != null) {
mainService.appenMessage((String)msg.obj);
}
break;
}
}
}
}

View File

@@ -1,67 +0,0 @@
package cc.winboll.studio.appbase.models;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/02/13 07:06:13
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
public class MainServiceBean extends BaseBean {
public static final String TAG = "MainServiceBean";
boolean isEnable;
public MainServiceBean() {
this.isEnable = false;
}
public void setIsEnable(boolean isEnable) {
this.isEnable = isEnable;
}
public boolean isEnable() {
return isEnable;
}
@Override
public String getName() {
return MainServiceBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("isEnable").value(isEnable());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("isEnable")) {
setIsEnable(jsonReader.nextBoolean());
} else {
return false;
}
}
return true;
}
@Override
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (!initObjectsFromJsonReader(jsonReader, name)) {
jsonReader.skipValue();
}
}
// 结束 JSON 对象
jsonReader.endObject();
return this;
}
}

View File

@@ -1,67 +0,0 @@
package cc.winboll.studio.appbase.models;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/03/07 12:47:22
* @Describe TestServiceBean
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
public class TestDemoBindServiceBean extends BaseBean {
public static final String TAG = "TestServiceBean";
boolean isEnable;
public TestDemoBindServiceBean() {
this.isEnable = false;
}
public void setIsEnable(boolean isEnable) {
this.isEnable = isEnable;
}
public boolean isEnable() {
return isEnable;
}
@Override
public String getName() {
return TestDemoBindServiceBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("isEnable").value(isEnable());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("isEnable")) {
setIsEnable(jsonReader.nextBoolean());
} else {
return false;
}
}
return true;
}
@Override
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (!initObjectsFromJsonReader(jsonReader, name)) {
jsonReader.skipValue();
}
}
// 结束 JSON 对象
jsonReader.endObject();
return this;
}
}

View File

@@ -1,68 +0,0 @@
package cc.winboll.studio.appbase.models;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/03/07 12:49:21
* @Describe TestDemoServiceBean
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
public class TestDemoServiceBean extends BaseBean {
public static final String TAG = "TestDemoServiceBean";
boolean isEnable;
public TestDemoServiceBean() {
this.isEnable = false;
}
public void setIsEnable(boolean isEnable) {
this.isEnable = isEnable;
}
public boolean isEnable() {
return isEnable;
}
@Override
public String getName() {
return TestDemoServiceBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("isEnable").value(isEnable());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("isEnable")) {
setIsEnable(jsonReader.nextBoolean());
} else {
return false;
}
}
return true;
}
@Override
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (!initObjectsFromJsonReader(jsonReader, name)) {
jsonReader.skipValue();
}
}
// 结束 JSON 对象
jsonReader.endObject();
return this;
}
}

View File

@@ -1,36 +0,0 @@
package cc.winboll.studio.appbase.receivers;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/03/24 07:11:44
*/
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
import cc.winboll.studio.libappbase.LogUtils;
public class APPNewsWidgetClickListener extends BroadcastReceiver {
public static final String TAG = "APPNewsWidgetClickListener";
public static final String ACTION_PRE = APPNewsWidgetClickListener.class.getName() + ".ACTION_PRE";
public static final String ACTION_NEXT = APPNewsWidgetClickListener.class.getName() + ".ACTION_NEXT";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null) {
LogUtils.d(TAG, String.format("action %s", action));
return;
}
if (action.equals(ACTION_PRE)) {
LogUtils.d(TAG, "ACTION_PRE");
APPNewsWidget.prePage(context);
} else if (action.equals(ACTION_NEXT)) {
LogUtils.d(TAG, "ACTION_NEXT");
APPNewsWidget.nextPage(context);
} else {
LogUtils.d(TAG, String.format("action %s", action));
}
}
}

View File

@@ -1,119 +0,0 @@
package cc.winboll.studio.appbase.receivers;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/02/13 06:58:04
* @Describe 主要广播接收器
*/
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import cc.winboll.studio.appbase.services.MainService;
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
import cc.winboll.studio.libappbase.AppUtils;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.models.APPModel;
import cc.winboll.studio.libappbase.models.WinBoLLModel;
import cc.winboll.studio.libappbase.models.WinBoLLNewsBean;
import cc.winboll.studio.libappbase.sos.SOS;
import cc.winboll.studio.libappbase.sos.SOSObject;
import cc.winboll.studio.libappbase.utils.ToastUtils;
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
import cc.winboll.studio.libappbase.winboll.WinBoLL;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainReceiver extends BroadcastReceiver {
public static final String TAG = "MainReceiver";
public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
WeakReference<MainService> mwrService;
public MainReceiver(MainService service) {
mwrService = new WeakReference<MainService>(service);
}
@Override
public void onReceive(Context context, Intent intent) {
String szAction = intent.getAction();
if (szAction.equals(ACTION_BOOT_COMPLETED)) {
ToastUtils.show("ACTION_BOOT_COMPLETED");
} else if (szAction.equals(IWinBoLLActivity.ACTION_BIND)) {
LogUtils.d(TAG, "ACTION_BIND");
LogUtils.d(TAG, String.format("context.getPackageName() %s", context.getPackageName()));
LogUtils.d(TAG, String.format("intent.getAction() %s", intent.getAction()));
String szWinBoLLModel = intent.getStringExtra(WinBoLL.EXTRA_WINBOLLMODEL);
LogUtils.d(TAG, String.format("szAPPModel %s", szWinBoLLModel));
if (szWinBoLLModel != null && !szWinBoLLModel.equals("")) {
try {
WinBoLLModel bean = WinBoLLModel.parseStringToBean(szWinBoLLModel, WinBoLLModel.class);
if (bean != null) {
String szAppPackageName = bean.getAppPackageName();
LogUtils.d(TAG, String.format("szAppPackageName %s", szAppPackageName));
String szAppMainServiveName = bean.getAppMainServiveName();
LogUtils.d(TAG, String.format("szAppMainServiveName %s", szAppMainServiveName));
mwrService.get().bindWinBoLLModelConnection(bean);
}
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
} else if (intent.getAction().equals(SOS.ACTION_SOS)) {
LogUtils.d(TAG, "ACTION_SOS");
String sos = intent.getStringExtra(SOS.EXTRA_OBJECT);
LogUtils.d(TAG, String.format("SOS %s", sos));
if (sos != null && !sos.equals("")) {
SOSObject bean = SOS.parseSOSObject(sos);
if (bean != null) {
String szObjectPackageName = bean.getObjectPackageName();
LogUtils.d(TAG, String.format("szObjectPackageName %s", szObjectPackageName));
String szObjectServiveName = bean.getObjectServiveName();
LogUtils.d(TAG, String.format("szObjectServiveName %s", szObjectServiveName));
Intent intentService = new Intent();
intentService.setComponent(new ComponentName(szObjectPackageName, szObjectServiveName));
context.startService(intentService);
String appName = AppUtils.getAppNameByPackageName(context, szObjectPackageName);
LogUtils.d(TAG, String.format("appName %s", appName));
WinBoLLNewsBean appWinBoLLNewsBean = new WinBoLLNewsBean(appName);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String currentTime = sdf.format(new Date());
StringBuilder sbLine = new StringBuilder();
sbLine.append("[");
sbLine.append(currentTime);
sbLine.append("] Power to ");
sbLine.append(appName);
appWinBoLLNewsBean.setMessage(sbLine.toString());
APPNewsWidget.addWinBoLLNewsBean(context, appWinBoLLNewsBean);
Intent intentWidget = new Intent(context, APPNewsWidget.class);
intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
context.sendBroadcast(intentWidget);
}
}
} else {
ToastUtils.show(szAction);
}
}
// 注册 Receiver
//
public void registerAction(MainService service) {
IntentFilter filter=new IntentFilter();
filter.addAction(ACTION_BOOT_COMPLETED);
filter.addAction(SOS.ACTION_SOS);
filter.addAction(WinBoLL.ACTION_BIND);
//filter.addAction(Intent.ACTION_BATTERY_CHANGED);
service.registerReceiver(this, filter);
}
}

View File

@@ -1,138 +0,0 @@
package cc.winboll.studio.appbase.services;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/02/14 03:38:31
* @Describe 守护进程服务
*/
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import cc.winboll.studio.appbase.models.MainServiceBean;
import cc.winboll.studio.appbase.services.AssistantService;
import cc.winboll.studio.appbase.services.MainService;
import cc.winboll.studio.libappbase.LogUtils;
import android.os.Binder;
public class AssistantService extends Service {
public static final String TAG = "AssistantService";
MainServiceBean mMainServiceBean;
MyServiceConnection mMyServiceConnection;
MainService mMainService;
boolean isBound = false;
volatile boolean isThreadAlive = false;
public synchronized void setIsThreadAlive(boolean isThreadAlive) {
LogUtils.d(TAG, "setIsThreadAlive(...)");
LogUtils.d(TAG, String.format("isThreadAlive %s", isThreadAlive));
this.isThreadAlive = isThreadAlive;
}
public boolean isThreadAlive() {
return isThreadAlive;
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
@Override
public void onCreate() {
LogUtils.d(TAG, "onCreate");
super.onCreate();
//mMyBinder = new MyBinder();
if (mMyServiceConnection == null) {
mMyServiceConnection = new MyServiceConnection();
}
// 设置运行参数
setIsThreadAlive(false);
assistantService();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "call onStartCommand(...)");
assistantService();
return START_STICKY;
}
@Override
public void onDestroy() {
//LogUtils.d(TAG, "onDestroy");
setIsThreadAlive(false);
// 解除绑定
if (isBound) {
unbindService(mMyServiceConnection);
isBound = false;
}
super.onDestroy();
}
// 运行服务内容
//
void assistantService() {
LogUtils.d(TAG, "assistantService()");
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
LogUtils.d(TAG, String.format("mMainServiceBean.isEnable() %s", mMainServiceBean.isEnable()));
if (mMainServiceBean.isEnable()) {
LogUtils.d(TAG, String.format("mIsThreadAlive %s", isThreadAlive()));
if (isThreadAlive() == false) {
// 设置运行状态
setIsThreadAlive(true);
// 唤醒和绑定主进程
wakeupAndBindMain();
}
}
}
// 唤醒和绑定主进程
//
void wakeupAndBindMain() {
LogUtils.d(TAG, "wakeupAndBindMain()");
// 绑定服务的Intent
Intent intent = new Intent(this, MainService.class);
startService(new Intent(this, MainService.class));
bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
// startService(new Intent(this, MainService.class));
// bindService(new Intent(AssistantService.this, MainService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
}
// 主进程与守护进程连接时需要用到此类
//
class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtils.d(TAG, "onServiceConnected(...)");
MainService.MyBinder binder = (MainService.MyBinder) service;
mMainService = binder.getService();
isBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
LogUtils.d(TAG, "onServiceDisconnected(...)");
mMainServiceBean = MainServiceBean.loadBean(AssistantService.this, MainServiceBean.class);
if (mMainServiceBean.isEnable()) {
wakeupAndBindMain();
}
isBound = false;
mMainService = null;
}
}
// 用于返回服务实例的Binder
public class MyBinder extends Binder {
AssistantService getService() {
LogUtils.d(TAG, "AssistantService MyBinder getService()");
return AssistantService.this;
}
}
}

View File

@@ -1,317 +0,0 @@
package cc.winboll.studio.appbase.services;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/02/13 06:56:41
* @Describe 拨号主服务
* 参考:
* 进程保活-双进程守护的正确姿势
* https://blog.csdn.net/sinat_35159441/article/details/75267380
* Android Service之onStartCommand方法研究
* https://blog.csdn.net/cyp331203/article/details/38920491
*/
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import cc.winboll.studio.appbase.MyTileService;
import cc.winboll.studio.appbase.handlers.MainServiceHandler;
import cc.winboll.studio.appbase.models.MainServiceBean;
import cc.winboll.studio.appbase.receivers.MainReceiver;
import cc.winboll.studio.appbase.services.AssistantService;
import cc.winboll.studio.appbase.threads.MainServiceThread;
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.models.APPModel;
import cc.winboll.studio.libappbase.models.WinBoLLModel;
import java.util.ArrayList;
public class MainService extends Service {
public static final String TAG = "MainService";
public static final int MSG_UPDATE_STATUS = 0;
static MainService _mControlCenterService;
volatile boolean isServiceRunning;
MainServiceBean mMainServiceBean;
MainServiceThread mMainServiceThread;
MainServiceHandler mMainServiceHandler;
MyServiceConnection mMyServiceConnection;
AssistantService mAssistantService;
boolean isBound = false;
MainReceiver mMainReceiver;
ArrayList<APPConnection> mAPPModelConnectionList;
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
public MainServiceThread getRemindThread() {
return mMainServiceThread;
}
@Override
public void onCreate() {
super.onCreate();
LogUtils.d(TAG, "onCreate()");
mAPPModelConnectionList = new ArrayList<APPConnection>();
_mControlCenterService = MainService.this;
isServiceRunning = false;
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
if (mMyServiceConnection == null) {
mMyServiceConnection = new MyServiceConnection();
}
mMainServiceHandler = new MainServiceHandler(this);
// 运行服务内容
mainService();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "onStartCommand(...)");
// 运行服务内容
mainService();
return (mMainServiceBean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
}
// 运行服务内容
//
void mainService() {
LogUtils.d(TAG, "mainService()");
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
if (mMainServiceBean.isEnable() && isServiceRunning == false) {
LogUtils.d(TAG, "mainService() start running");
isServiceRunning = true;
// 唤醒守护进程
wakeupAndBindAssistant();
if (mMainReceiver == null) {
// 注册广播接收器
mMainReceiver = new MainReceiver(this);
mMainReceiver.registerAction(this);
}
// 启动小部件
Intent intentTimeWidget = new Intent(this, APPNewsWidget.class);
intentTimeWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
this.sendBroadcast(intentTimeWidget);
startMainServiceThread();
MyTileService.updateServiceIconStatus(this);
LogUtils.i(TAG, "Main Service Is Start.");
}
}
// 唤醒和绑定守护进程
//
void wakeupAndBindAssistant() {
LogUtils.d(TAG, "wakeupAndBindAssistant()");
Intent intent = new Intent(this, AssistantService.class);
startService(intent);
// 绑定服务的Intent
bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
}
// 开启提醒铃声线程
//
public void startMainServiceThread() {
LogUtils.d(TAG, "startMainServiceThread");
if (mMainServiceThread == null) {
mMainServiceThread = new MainServiceThread(this, mMainServiceHandler);
LogUtils.d(TAG, "new MainServiceThread");
} else {
if (mMainServiceThread.isExist() == true) {
mMainServiceThread = new MainServiceThread(this, mMainServiceHandler);
LogUtils.d(TAG, "renew MainServiceThread");
} else {
// 提醒进程正在进行中就更新状态后退出
LogUtils.d(TAG, "A mMainServiceThread running.");
return;
}
}
mMainServiceThread.start();
}
public void stopRemindThread() {
if (mMainServiceThread != null) {
mMainServiceThread.setIsExist(true);
mMainServiceThread = null;
}
}
@Override
public void onDestroy() {
//LogUtils.d(TAG, "onDestroy");
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
if (mMainServiceBean.isEnable() == false) {
// 设置运行状态
isServiceRunning = false;// 解除绑定
if (isBound) {
unbindService(mMyServiceConnection);
isBound = false;
}
// 停止守护进程
Intent intent = new Intent(this, AssistantService.class);
stopService(intent);
// 停止Receiver
if (mMainReceiver != null) {
unregisterReceiver(mMainReceiver);
mMainReceiver = null;
}
// 停止前台通知栏
stopForeground(true);
// 停止消息提醒进程
stopRemindThread();
MyTileService.updateServiceIconStatus(this);
super.onDestroy();
//LogUtils.d(TAG, "onDestroy done");
}
}
public void bindWinBoLLModelConnection(WinBoLLModel bean) {
LogUtils.d(TAG, "bindAPPModelConnection(...)");
// 清理旧的绑定链接
for (int i = mAPPModelConnectionList.size() - 1; i > -1; i--) {
APPConnection item = mAPPModelConnectionList.get(i);
if (item.isBindToAPP(bean)) {
LogUtils.d(TAG, "Bind Servive exist.");
unbindService(item);
mAPPModelConnectionList.remove(i);
}
}
// 绑定服务
APPConnection appConnection = new APPConnection();
Intent intentService = new Intent();
intentService.setComponent(new ComponentName(bean.getAppPackageName(), bean.getAppMainServiveName()));
bindService(intentService, appConnection, Context.BIND_IMPORTANT);
mAPPModelConnectionList.add(appConnection);
Intent intentWidget = new Intent(this, APPNewsWidget.class);
intentWidget.setAction(APPNewsWidget.ACTION_WAKEUP_SERVICE);
WinBoLLModel appSOSBean = new WinBoLLModel(bean.getAppPackageName(), bean.getAppMainServiveName());
intentWidget.putExtra("APPSOSBean", appSOSBean.toString());
sendBroadcast(intentWidget);
}
public class APPConnection implements ServiceConnection {
ComponentName mComponentName;
boolean isBindToAPP(WinBoLLModel bean) {
return mComponentName != null
&& mComponentName.getClassName().equals(bean.getAppMainServiveName())
&& mComponentName.getPackageName().equals(bean.getAppPackageName());
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtils.d(TAG, "onServiceConnected(...)");
mComponentName = name;
LogUtils.d(TAG, String.format("onServiceConnected : \ngetClassName %s\ngetPackageName %s", name.getClassName(), name.getPackageName()));
}
@Override
public void onServiceDisconnected(ComponentName name) {
LogUtils.d(TAG, "onServiceDisconnected(...)");
LogUtils.d(TAG, String.format("onServiceDisconnected : \ngetClassName %s\ngetPackageName %s", name.getClassName(), name.getPackageName()));
// 尝试无参数启动一下服务
String appPackage = mComponentName.getPackageName();
LogUtils.d(TAG, String.format("appPackage %s", appPackage));
String appMainServiceClassName = mComponentName.getClassName();
LogUtils.d(TAG, String.format("appMainServiceClassName %s", appMainServiceClassName));
Intent intentService = new Intent();
intentService.setComponent(new ComponentName(appPackage, appMainServiceClassName));
startService(intentService);
}
}
// 主进程与守护进程连接时需要用到此类
//
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtils.d(TAG, "onServiceConnected(...)");
AssistantService.MyBinder binder = (AssistantService.MyBinder) service;
mAssistantService = binder.getService();
isBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
LogUtils.d(TAG, "onServiceDisconnected(...)");
if (mMainServiceBean.isEnable()) {
// 唤醒守护进程
wakeupAndBindAssistant();
}
isBound = false;
mAssistantService = null;
}
}
// 用于返回服务实例的Binder
public class MyBinder extends Binder {
MainService getService() {
LogUtils.d(TAG, "MainService MyBinder getService()");
return MainService.this;
}
}
// //
// // 启动服务
// //
// public static void startControlCenterService(Context context) {
// Intent intent = new Intent(context, MainService.class);
// context.startForegroundService(intent);
// }
//
// //
// // 停止服务
// //
// public static void stopControlCenterService(Context context) {
// Intent intent = new Intent(context, MainService.class);
// context.stopService(intent);
// }
public void appenMessage(String message) {
LogUtils.d(TAG, String.format("Message : %s", message));
}
public static void stopMainService(Context context) {
LogUtils.d(TAG, "stopMainService");
MainServiceBean bean = new MainServiceBean();
bean.setIsEnable(false);
MainServiceBean.saveBean(context, bean);
context.stopService(new Intent(context, MainService.class));
}
public static void startMainService(Context context) {
LogUtils.d(TAG, "startMainService");
MainServiceBean bean = new MainServiceBean();
bean.setIsEnable(true);
MainServiceBean.saveBean(context, bean);
context.startService(new Intent(context, MainService.class));
}
}

View File

@@ -1,179 +0,0 @@
package cc.winboll.studio.appbase.services;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/03/07 12:45:49
* @Describe 启动时申请绑定到APPBase主服务的服务示例
*/
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import cc.winboll.studio.appbase.App;
import cc.winboll.studio.appbase.models.TestDemoBindServiceBean;
import cc.winboll.studio.appbase.services.TestDemoBindService;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.sos.SOS;
import cc.winboll.studio.libappbase.winboll.WinBoLL;
public class TestDemoBindService extends Service {
public static final String TAG = "TestDemoBindService";
public static final String ACTION_ENABLE = TestDemoBindService.class.getName() + ".ACTION_ENABLE";
public static final String ACTION_DISABLE = TestDemoBindService.class.getName() + ".ACTION_DISABLE";
volatile static TestThread _TestThread;
volatile static boolean _IsRunning;
public synchronized static void setIsRunning(boolean isRunning) {
_IsRunning = isRunning;
}
public static boolean isRunning() {
return _IsRunning;
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
public class MyBinder extends Binder {
public TestDemoBindService getService() {
return TestDemoBindService.this;
}
}
@Override
public void onCreate() {
super.onCreate();
LogUtils.d(TAG, "onCreate()");
run();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "onStartCommand(...)");
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
if (bean == null) {
bean = new TestDemoBindServiceBean();
}
if (intent.getAction() != null) {
if (intent.getAction().equals(ACTION_ENABLE)) {
bean.setIsEnable(true);
LogUtils.d(TAG, "setIsEnable(true);");
TestDemoBindServiceBean.saveBean(this, bean);
} else if (intent.getAction().equals(ACTION_DISABLE)) {
bean.setIsEnable(false);
LogUtils.d(TAG, "setIsEnable(false);");
TestDemoBindServiceBean.saveBean(this, bean);
}
}
run();
return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
//return super.onStartCommand(intent, flags, startId);
}
void run() {
LogUtils.d(TAG, "run()");
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
if (bean == null) {
bean = new TestDemoBindServiceBean();
TestDemoBindServiceBean.saveBean(this, bean);
}
if (bean.isEnable()) {
LogUtils.d(TAG, "run() bean.isEnable()");
TestThread.getInstance(this).start();
LogUtils.d(TAG, "_TestThread.start()");
}
}
@Override
public void onDestroy() {
super.onDestroy();
LogUtils.d(TAG, "onDestroy()");
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
if (bean == null) {
bean = new TestDemoBindServiceBean();
}
TestThread.getInstance(this).setIsExit(true);
// 预防 APPBase 应用重启绑定失效。
// 所以退出时检查本服务是否配置启用,如果启用就发送一个 SOS 信号。
// 这样 APPBase 就会用组件方式启动本服务。
if (bean.isEnable()) {
if (App.isDebuging()) {
SOS.sosToAppBaseBeta(this, TestDemoBindService.class.getName());
} else {
SOS.sosToAppBase(this, TestDemoBindService.class.getName());
}
}
_IsRunning = false;
}
static class TestThread extends Thread {
volatile static TestThread _TestThread;
Context mContext;
volatile boolean isStarted = false;
volatile boolean isExit = false;
TestThread(Context context) {
super();
mContext = context;
}
public static synchronized TestThread getInstance(Context context) {
if (_TestThread != null) {
_TestThread.setIsExit(true);
}
_TestThread = new TestThread(context);
return _TestThread;
}
public synchronized void setIsExit(boolean isExit) {
this.isExit = isExit;
}
public boolean isExit() {
return isExit;
}
@Override
public void run() {
if (isStarted == false) {
isStarted = true;
super.run();
LogUtils.d(TAG, "run() start");
if (App.isDebuging()) {
WinBoLL.bindToAPPBaseBeta(mContext, TestDemoBindService.class.getName());
} else {
WinBoLL.bindToAPPBase(mContext, TestDemoBindService.class.getName());
}
while (!isExit()) {
LogUtils.d(TAG, "run()");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
LogUtils.d(TAG, "run() exit");
}
}
}
}

View File

@@ -1,155 +0,0 @@
package cc.winboll.studio.appbase.services;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/03/07 12:39:24
* @Describe 普通服务示例
*/
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import cc.winboll.studio.appbase.models.TestDemoServiceBean;
import cc.winboll.studio.libappbase.LogUtils;
public class TestDemoService extends Service {
public static final String TAG = "TestDemoService";
public static final String ACTION_ENABLE = TestDemoService.class.getName() + ".ACTION_ENABLE";
public static final String ACTION_DISABLE = TestDemoService.class.getName() + ".ACTION_DISABLE";
volatile static TestThread _TestThread;
volatile static boolean _IsRunning;
public synchronized static void setIsRunning(boolean isRunning) {
_IsRunning = isRunning;
}
public static boolean isRunning() {
return _IsRunning;
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
public class MyBinder extends Binder {
public TestDemoService getService() {
return TestDemoService.this;
}
}
@Override
public void onCreate() {
super.onCreate();
LogUtils.d(TAG, "onCreate()");
run();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "onStartCommand(...)");
TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
if (bean == null) {
bean = new TestDemoServiceBean();
}
if (intent.getAction() != null) {
if (intent.getAction().equals(ACTION_ENABLE)) {
bean.setIsEnable(true);
LogUtils.d(TAG, "setIsEnable(true);");
TestDemoServiceBean.saveBean(this, bean);
} else if (intent.getAction().equals(ACTION_DISABLE)) {
bean.setIsEnable(false);
LogUtils.d(TAG, "setIsEnable(false);");
TestDemoServiceBean.saveBean(this, bean);
}
}
run();
return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
//return super.onStartCommand(intent, flags, startId);
}
void run() {
LogUtils.d(TAG, "run()");
TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
if (bean == null) {
bean = new TestDemoServiceBean();
TestDemoServiceBean.saveBean(this, bean);
}
if (bean.isEnable()) {
LogUtils.d(TAG, "run() bean.isEnable()");
TestThread.getInstance(this).start();
LogUtils.d(TAG, "_TestThread.start()");
}
}
@Override
public void onDestroy() {
super.onDestroy();
LogUtils.d(TAG, "onDestroy()");
TestThread.getInstance(this).setIsExit(true);
_IsRunning = false;
}
static class TestThread extends Thread {
volatile static TestThread _TestThread;
Context mContext;
volatile boolean isStarted = false;
volatile boolean isExit = false;
TestThread(Context context) {
super();
mContext = context;
}
public static synchronized TestThread getInstance(Context context) {
if (_TestThread != null) {
_TestThread.setIsExit(true);
}
_TestThread = new TestThread(context);
return _TestThread;
}
public synchronized void setIsExit(boolean isExit) {
this.isExit = isExit;
}
public boolean isExit() {
return isExit;
}
@Override
public void run() {
if (isStarted == false) {
isStarted = true;
super.run();
LogUtils.d(TAG, "run() start");
while (!isExit()) {
LogUtils.d(TAG, "run()");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
LogUtils.d(TAG, "run() exit");
}
}
}
}

View File

@@ -1,54 +0,0 @@
package cc.winboll.studio.appbase.threads;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/02/14 03:46:44
*/
import android.content.Context;
import cc.winboll.studio.appbase.handlers.MainServiceHandler;
import cc.winboll.studio.libappbase.LogUtils;
import java.lang.ref.WeakReference;
public class MainServiceThread extends Thread {
public static final String TAG = "MainServiceThread";
Context mContext;
// 控制线程是否退出的标志
volatile boolean isExist = false;
// 服务Handler, 用于线程发送消息使用
WeakReference<MainServiceHandler> mwrMainServiceHandler;
public void setIsExist(boolean isExist) {
this.isExist = isExist;
}
public boolean isExist() {
return isExist;
}
public MainServiceThread(Context context, MainServiceHandler handler) {
mContext = context;
mwrMainServiceHandler = new WeakReference<MainServiceHandler>(handler);
}
@Override
public void run() {
LogUtils.d(TAG, "run()");
while (!isExist()) {
//ToastUtils.show("run()");
//LogUtils.d(TAG, "run()");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
LogUtils.d(TAG, "run() exit.");
}
}

View File

@@ -1,187 +0,0 @@
package cc.winboll.studio.appbase.widgets;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/02/15 14:41:25
* @Describe TimeWidget
*/
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
import cc.winboll.studio.appbase.R;
import cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener;
import cc.winboll.studio.libappbase.AppUtils;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.models.APPModel;
import cc.winboll.studio.libappbase.models.WinBoLLNewsBean;
import cc.winboll.studio.libappbase.winboll.WinBoLL;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import cc.winboll.studio.libappbase.models.WinBoLLModel;
public class APPNewsWidget extends AppWidgetProvider {
public static final String TAG = "APPNewsWidget";
public static final String ACTION_WAKEUP_SERVICE = APPNewsWidget.class.getName() + ".ACTION_WAKEUP_SERVICE";
public static final String ACTION_RELOAD_REPORT = APPNewsWidget.class.getName() + ".ACTION_RELOAD_REPORT";
volatile static ArrayList<WinBoLLNewsBean> _WinBoLLNewsBeanList;
final static int _MAX_PAGES = 10;
final static int _OnePageLinesCount = 5;
volatile static int _CurrentPageIndex = 0;
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
initWinBoLLNewsBeanList(context);
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
initWinBoLLNewsBeanList(context);
if (intent.getAction().equals(ACTION_RELOAD_REPORT)) {
LogUtils.d(TAG, "ACTION_RELOAD_REPORT");
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class));
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}else if (intent.getAction().equals(ACTION_WAKEUP_SERVICE)) {
LogUtils.d(TAG, "ACTION_WAKEUP_SERVICE");
String szWinBoLLModel = intent.getStringExtra(WinBoLL.EXTRA_WINBOLLMODEL);
LogUtils.d(TAG, String.format("szWinBoLLModel %s", szWinBoLLModel));
if (szWinBoLLModel != null && !szWinBoLLModel.equals("")) {
try {
WinBoLLModel bean = WinBoLLModel.parseStringToBean(szWinBoLLModel, WinBoLLModel.class);
if (bean != null) {
String szAppPackageName = bean.getAppPackageName();
LogUtils.d(TAG, String.format("szAppPackageName %s", szAppPackageName));
String szAppMainServiveName = bean.getAppMainServiveName();
LogUtils.d(TAG, String.format("szAppMainServiveName %s", szAppMainServiveName));
String appName = AppUtils.getAppNameByPackageName(context, szAppPackageName);
LogUtils.d(TAG, String.format("appName %s", appName));
WinBoLLNewsBean winBollNewsBean = new WinBoLLNewsBean(appName);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String currentTime = sdf.format(new Date());
StringBuilder sbLine = new StringBuilder();
sbLine.append("[");
sbLine.append(currentTime);
sbLine.append("] Wake up ");
sbLine.append(appName);
winBollNewsBean.setMessage(sbLine.toString());
addWinBoLLNewsBean(context, winBollNewsBean);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class));
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
}
}
//
// 加入新报告信息
//
public synchronized static void addWinBoLLNewsBean(Context context, WinBoLLNewsBean bean) {
initWinBoLLNewsBeanList(context);
_WinBoLLNewsBeanList.add(0, bean);
// 控制记录总数
while (_WinBoLLNewsBeanList.size() > _MAX_PAGES * _OnePageLinesCount) {
_WinBoLLNewsBeanList.remove(_WinBoLLNewsBeanList.size() - 1);
}
WinBoLLNewsBean.saveBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class);
}
synchronized static void initWinBoLLNewsBeanList(Context context) {
if (_WinBoLLNewsBeanList == null) {
_WinBoLLNewsBeanList = new ArrayList<WinBoLLNewsBean>();
WinBoLLNewsBean.loadBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class);
}
if (_WinBoLLNewsBeanList == null) {
_WinBoLLNewsBeanList = new ArrayList<WinBoLLNewsBean>();
WinBoLLNewsBean.saveBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class);
}
}
private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
LogUtils.d(TAG, "updateAppWidget(...)");
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_news);
//设置按钮点击事件
Intent intentPre = new Intent(context, APPNewsWidgetClickListener.class);
intentPre.setAction(APPNewsWidgetClickListener.ACTION_PRE);
PendingIntent pendingIntentPre = PendingIntent.getBroadcast(context, 0, intentPre, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.widget_button_pre, pendingIntentPre);
Intent intentNext = new Intent(context, APPNewsWidgetClickListener.class);
intentNext.setAction(APPNewsWidgetClickListener.ACTION_NEXT);
PendingIntent pendingIntentNext = PendingIntent.getBroadcast(context, 0, intentNext, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.widget_button_next, pendingIntentNext);
views.setTextViewText(R.id.tv_msg, getPageInfo());
views.setTextViewText(R.id.tv_news, getMessage());
appWidgetManager.updateAppWidget(appWidgetId, views);
}
public static String getMessage() {
ArrayList<String> msgTemp = new ArrayList<String>();
if (_WinBoLLNewsBeanList != null) {
int start = _OnePageLinesCount * _CurrentPageIndex;
start = _WinBoLLNewsBeanList.size() > start ? start : _WinBoLLNewsBeanList.size() - 1;
for (int i = start, j = 0; i < _WinBoLLNewsBeanList.size() && j < _OnePageLinesCount && start > -1; i++, j++) {
msgTemp.add(_WinBoLLNewsBeanList.get(i).getMessage());
}
String message = String.join("\n", msgTemp);
return message;
}
return "";
}
public static void prePage(Context context) {
if (_WinBoLLNewsBeanList != null) {
if (_CurrentPageIndex > 0) {
_CurrentPageIndex = _CurrentPageIndex - 1;
}
Intent intentWidget = new Intent(context, APPNewsWidget.class);
intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
context.sendBroadcast(intentWidget);
}
}
public static void nextPage(Context context) {
if (_WinBoLLNewsBeanList != null) {
if ((_CurrentPageIndex + 1) * _OnePageLinesCount < _WinBoLLNewsBeanList.size()) {
_CurrentPageIndex = _CurrentPageIndex + 1;
}
Intent intentWidget = new Intent(context, APPNewsWidget.class);
intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
context.sendBroadcast(intentWidget);
}
}
String getPageInfo() {
if (_WinBoLLNewsBeanList == null) {
return "0/0";
}
int leftCount = _WinBoLLNewsBeanList.size() % _OnePageLinesCount;
int currentPageCount = _WinBoLLNewsBeanList.size() / _OnePageLinesCount + (leftCount == 0 ?0: 1);
return String.format("%d/%d", _CurrentPageIndex + 1, currentPageCount);
}
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#81C7F5"/> <!-- 浅蓝色填充 -->
<corners android:radius="8dp"/> <!-- 8dp 圆角 -->
</shape>

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#ff000000"
android:pathData="M6.5,20Q4.22,20 2.61,18.43 1,16.85 1,14.58 1,12.63 2.17,11.1 3.35,9.57 5.25,9.15 5.88,6.85 7.75,5.43 9.63,4 12,4 14.93,4 16.96,6.04 19,8.07 19,11 20.73,11.2 21.86,12.5 23,13.78 23,15.5 23,17.38 21.69,18.69 20.38,20 18.5,20Z"/>
</vector>

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#ff000000"
android:pathData="M6.5,20Q4.22,20 2.61,18.43 1,16.85 1,14.58 1,12.63 2.17,11.1 3.35,9.57 5.25,9.15 5.88,6.85 7.75,5.43 9.63,4 12,4 14.93,4 16.96,6.04 19,8.07 19,11 20.73,11.2 21.86,12.5 23,13.78 23,15.5 23,17.38 21.69,18.69 20.38,20 18.5,20M6.5,18H18.5Q19.55,18 20.27,17.27 21,16.55 21,15.5 21,14.45 20.27,13.73 19.55,13 18.5,13H17V11Q17,8.93 15.54,7.46 14.08,6 12,6 9.93,6 8.46,7.46 7,8.93 7,11H6.5Q5.05,11 4.03,12.03 3,13.05 3,14.5 3,15.95 4.03,17 5.05,18 6.5,18M12,12Z"/>
</vector>

View File

@@ -3,219 +3,45 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:padding="16dp">
<android.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/toolbar"/>
<ScrollView
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0">
<LinearLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:gravity="center">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:gravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="安卓R对应的是Android 11其API级别是30。以下是Android 11中一些重要的API相关特性
\n- 隐私保护方面:引入单次授权,让用户可以选择授予应用对位置信息、麦克风和摄像头的临时访问权限。还增加了数据访问审核功能,能让开发者深入了解应用在何处访问私密数据。
\n- 系统功能方面提供了ControlsProviderService API用于向连接的外部设备提供控件这些控件显示于Android电源菜单中的设备控件下。媒体控件也得到更新显示于快捷设置旁来自多个应用的会话排列在一个可滑动的轮播界面中。
\n- 硬件支持方面提供了一些API以支持瀑布屏通过将窗口布局属性  layoutInDisplayCutoutMode  设为  LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS 可允许窗口延伸到屏幕各个边缘上的刘海和瀑布区域。对于合页式屏幕配置的设备提供了具有  TYPE_HINGE_ANGLE  的新传感器以及新的  SensorEvent 用于监控合页角度。
\n- 安全方面:对生物识别身份验证机制进行了更新,引入了  BiometricManager.Authenticators  接口,定义了  BIOMETRIC_STRONG  BIOMETRIC_WEAK  DEVICE_CREDENTIAL  等身份验证类型。还在  BiometricPrompt  类中提供了对“每次使用时进行身份验证”密钥的更多支持。
\n- 性能和质量方面支持无线调试通过Android调试桥adb从工作站以无线方式部署和调试应用避免了常见的USB连接问题。"/>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_height="wrap_content"
android:gravity="right|center_vertical"
android:layout_width="wrap_content">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Debug Mode"
android:layout_weight="1.0"
android:onClick="onSwitchDebugMode"
android:id="@+id/activitymainCheckBox1"/>
android:gravity="center_vertical"
android:spacing="12dp">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test Application CrashReport"
android:textAllCaps="false"
android:onClick="onTestApplicationCrashReport"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PreviewGlobalCrashActivity"
android:textAllCaps="false"
android:onClick="onPreviewGlobalCrashActivity"/>
</LinearLayout>
</HorizontalScrollView>
<ScrollView
android:layout_width="match_parent"
android:layout_height="400dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:text="应用崩溃测试"
android:textSize="16sp"
android:textColor="@android:color/white"
android:background="#81C7F5"
android:paddingVertical="12dp"
android:layout_marginHorizontal="24dp"
android:onClick="onCrashTest"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StartCenter"
android:textAllCaps="false"
android:onClick="onStartCenter"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StopCenter"
android:textAllCaps="false"
android:onClick="onStopCenter"/>
</LinearLayout>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SartTestDemoService"
android:textAllCaps="false"
android:onClick="onSartTestDemoService"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StopTestDemoService"
android:textAllCaps="false"
android:onClick="onStopTestDemoService"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StopTestDemoServiceNoSettings"
android:textAllCaps="false"
android:onClick="onStopTestDemoServiceNoSettings"/>
android:text="应用日志测试"
android:textSize="16sp"
android:textColor="@android:color/white"
android:background="#81C7F5"
android:paddingVertical="12dp"
android:layout_marginHorizontal="24dp"
android:onClick="onLogTest"/>
</LinearLayout>
</HorizontalScrollView>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SartTestDemoBindService"
android:textAllCaps="false"
android:onClick="onSartTestDemoBindService"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StopTestDemoBindService"
android:textAllCaps="false"
android:onClick="onStopTestDemoBindService"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StopTestDemoBindServiceNoSettings"
android:textAllCaps="false"
android:onClick="onStopTestDemoBindServiceNoSettings"/>
</LinearLayout>
</HorizontalScrollView>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TestStopMainServiceWithoutSettingEnable"
android:textAllCaps="false"
android:onClick="onTestStopMainServiceWithoutSettingEnable"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TestUseComponentStartService"
android:textAllCaps="false"
android:onClick="onTestUseComponentStartService"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TestDemoServiceSOS"
android:textAllCaps="false"
android:onClick="onTestDemoServiceSOS"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TestOpenNewActivity"
android:textAllCaps="false"
android:onClick="onTestOpenNewActivity"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/toolbar"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="NewActivity"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CloseThisActivity"
android:textAllCaps="false"
android:onClick="onCloseThisActivity"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CloseAllActivity"
android:textAllCaps="false"
android:onClick="onCloseAllActivity"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New2Activity"
android:textAllCaps="false"
android:onClick="onNew2Activity"/>
</LinearLayout>

View File

@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/toolbar"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New2Activity"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CloseThisActivity"
android:textAllCaps="false"
android:onClick="onCloseThisActivity"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CloseAllActivity"
android:textAllCaps="false"
android:onClick="onCloseAllActivity"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="NewActivity"
android:textAllCaps="false"
android:onClick="onNewActivity"/>
</LinearLayout>

View File

@@ -1,51 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FFFFFFFF">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right|center_vertical">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/tv_title"
android:layout_weight="1.0"
android:text="WinBoLLNews"
android:textStyle="bold"
android:textSize="16sp"/>
<Button
android:layout_width="48dp"
android:layout_height="48dp"
android:text="⇦"
android:id="@+id/widget_button_pre"/>
<Button
android:layout_width="48dp"
android:layout_height="48dp"
android:text="⇨"
android:id="@+id/widget_button_next"/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tv_msg"/>
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/tv_news"
android:layout_weight="1.0"/>
</LinearLayout>

View File

@@ -3,18 +3,6 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/item_home"
android:title="HOME"
android:title="WinBoLL Home"
android:icon="@drawable/ic_winboll"/>
<item
android:id="@+id/item_yun"
android:title="YUN"
android:icon="@drawable/ic_winboll"/>
<item
android:id="@+id/item_logon"
android:title="Logon"
android:icon="@drawable/ic_winboll"/>
<item
android:id="@+id/item_log"
android:title="LOG"
android:icon="@drawable/ic_winboll_log"/>
</menu>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!-- 允许访问 winboll.cc 及其子域名(原配置) -->
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">winboll.cc</domain>
</domain-config>
<!-- **新增:允许访问 IP 地址 10.8.0.250** -->
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">10.8.0.250</domain> <!-- 不包含子域名 -->
</domain-config>
</network-security-config>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="200dp"
android:minHeight="100dp"
android:updatePeriodMillis="1000"
android:initialLayout="@layout/widget_news">
</appwidget-provider>

35
apputils/README.md Normal file
View File

@@ -0,0 +1,35 @@
# APPUtils
#### 介绍
应用开发工具套件类
#### 软件架构
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
#### Gradle 编译说明
调试版编译命令 gradle assembleBetaDebug
阶段版编译命令 git pull && bash .winboll/bashPublishAPKAddTag.sh apputils
阶段版类库发布命令 git pull &&bash .winboll/bashPublishLIBAddTag.sh libapputils
#### 使用说明
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码 : ZhanGSKen(ZhanGSKen<zhangsken@188.com>)
4. 新建 Pull Request
#### 特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
#### 参考文档

View File

@@ -29,7 +29,7 @@ android {
// versionName 更新后需要手动设置
// 项目模块目录的 build.gradle 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "15.8"
versionName "15.10"
if(true) {
versionName = genVersionName("${versionName}")
}

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Tue Jun 03 15:05:48 HKT 2025
stageCount=5
#Mon Sep 29 01:16:05 HKT 2025
stageCount=3
libraryProject=libapputils
baseVersion=15.8
publishVersion=15.8.4
baseVersion=15.10
publishVersion=15.10.2
buildCount=0
baseBetaVersion=15.8.5
baseBetaVersion=15.10.3

View File

@@ -3,13 +3,19 @@
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.apputils">
<!-- 读取外部存储权限Android 10 以下) -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name=".App"
android:allowBackup="true"
android:icon="@drawable/ic_winboll"
android:label="@string/app_name"
android:theme="@style/MyUtilsTheme"
android:supportsRtl="true">
android:supportsRtl="true"
android:resizeableActivity="true"
android:screenOrientation="unspecified"
android:requestLegacyExternalStorage="true">
<activity
android:name=".MainActivity"
@@ -35,6 +41,8 @@
<activity android:name=".QRCodeDecodeActivity"/>
<activity android:name=".QRGeneratorActivity"/>
</application>
</manifest>

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.apputils;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/12/08 15:10:51
* @Describe 全局应用类
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.apputils;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/01/03 11:02:49
* @Describe 一个可以浏览随 APP 附带的 Html 文档的窗口
*/
@@ -15,12 +15,11 @@ import android.view.MenuItem;
import android.widget.Toolbar;
import cc.winboll.studio.apputils.R;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
import cc.winboll.studio.libapputils.views.SimpleWebView;
import java.io.IOException;
import java.io.InputStream;
public class AssetsHtmlActivity extends WinBoLLActivity implements IWinBoLLActivity {
public class AssetsHtmlActivity extends Activity {
public static final String TAG = "AssetsHtmlActivity";
@@ -33,16 +32,6 @@ public class AssetsHtmlActivity extends WinBoLLActivity implements IWinBoLLActiv
// Assets 文件夹里的 Html 文件的名称
String mszHtmlFileName;
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return super.onCreateOptionsMenu(menu);

View File

@@ -15,9 +15,10 @@ import android.widget.Toolbar;
import cc.winboll.studio.apputils.R;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libappbase.utils.ToastUtils;
import cc.winboll.studio.libappbase.ToastUtils;
import java.util.List;
import java.util.Set;
import cc.winboll.studio.libappbase.LogActivity;
final public class MainActivity extends Activity {
@@ -26,7 +27,7 @@ final public class MainActivity extends Activity {
public static final int REQUEST_QRCODEDECODE_ACTIVITY = 0;
Toolbar mToolbar;
LogView mLogView;
//LogView mLogView;
//
// @Override
// public Activity getActivity() {
@@ -39,8 +40,8 @@ final public class MainActivity extends Activity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLogView = findViewById(R.id.logview);
mLogView.start();
// mLogView = findViewById(R.id.logview);
// mLogView.start();
// 初始化工具栏
mToolbar = findViewById(R.id.toolbar);
@@ -145,13 +146,21 @@ final public class MainActivity extends Activity {
}
public void onTestLogActivity(View view) {
// Intent intent = new Intent(this, LogActivity.class);
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
// intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
// startActivity(intent);
/* 分屏代码有效
// 1. 创建启动 SecondActivity 的 Intent
Intent splitIntent = new Intent(MainActivity.this, LogActivity.class);
//WinBoLLActivityManager.getInstance().printAvtivityListInfo();
//WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, LogActivity.class);
// 2. 添加分屏启动必需的两个标志API 30 兼容)
// FLAG_ACTIVITY_LAUNCH_ADJACENT相邻分屏显示
// FLAG_ACTIVITY_NEW_TASK分屏需要新任务栈
splitIntent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
| Intent.FLAG_ACTIVITY_NEW_TASK);
// 3. 启动分屏活动(若设备不支持分屏,会默认全屏启动)
startActivity(splitIntent);
*/
LogActivity.startLogActivity(this);
}
//
@@ -217,10 +226,9 @@ final public class MainActivity extends Activity {
if (item.getItemId() == R.id.item_exit) {
//exit();
return true;
// } else if (item.getItemId() == R.id.item_teststringtoqrcodeview) {
// Intent intent = new Intent(this, TestStringToQRCodeViewActivity.class);
// startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY);
// //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, TestStringToQrCodeViewActivity.class);
} else if (item.getItemId() == R.id.item_testqrgeneratoractivity) {
Intent intent = new Intent(this, QRGeneratorActivity.class);
startActivity(intent);
} else if (item.getItemId() == R.id.item_testqrcodedecodeactivity) {
Intent intent = new Intent(this, QRCodeDecodeActivity.class);
startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY);
@@ -281,7 +289,7 @@ final public class MainActivity extends Activity {
@Override
protected void onResume() {
super.onResume();
mLogView.start();
//mLogView.start();
}
/*@Override

View File

@@ -1,89 +1,323 @@
package cc.winboll.studio.apputils;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/01/18 10:32:21
* @Describe 二维码扫码解码窗口
* @Describe 二维码解码窗口
*/
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toolbar;
import cc.winboll.studio.apputils.R;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.HybridBinarizer;
import com.journeyapps.barcodescanner.BarcodeCallback;
import com.journeyapps.barcodescanner.BarcodeResult;
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
import com.journeyapps.barcodescanner.DefaultDecoderFactory;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
public class QRCodeDecodeActivity extends Activity {
public static final String TAG = "QRCodeDecodeActivity";
public static final String EXTRA_RESULT = "EXTRA_RESULT";
private static final int REQUEST_CAMERA_PERMISSION = 1;
private static final int REQUEST_PICK_IMAGE = 2;
private static final int REQUEST_READ_STORAGE_PERMISSION = 3;
// 图片压缩阈值超过1000px时压缩避免内存溢出和解码效率低
private static final int MAX_BITMAP_SIZE = 1000;
TextView resultTextView;
DecoratedBarcodeView barcodeView;
// @Override
// public Activity getActivity() {
// return this;
// }
private TextView resultTextView;
private DecoratedBarcodeView barcodeView;
private Button btnDecodeFromAlbum;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qrcodedecode);
initToolbar();
initViews();
checkCameraPermission();
setAlbumDecodeClickListener();
}
// 初始化工具栏
// Toolbar mToolbar = findViewById(R.id.toolbar);
// setActionBar(mToolbar);
private void initToolbar() {
Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar != null) {
setActionBar(mToolbar);
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
}
}
//resultTextView = findViewById(R.id.activityqrcodedecodeTextView1);
barcodeView = findViewById(R.id.activityqrcodedecodeDecoratedBarcodeView1);
// 请求相机权限
// if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA)
// != PackageManager.PERMISSION_GRANTED) {
// ActivityCompat.requestPermissions(this,
// new String[]{android.Manifest.permission.CAMERA},
// REQUEST_CAMERA_PERMISSION);
// } else {
// startScanning();
// }
private void initViews() {
resultTextView = (TextView) findViewById(R.id.activityqrcodedecodeTextView1);
barcodeView = (DecoratedBarcodeView) findViewById(R.id.activityqrcodedecodeDecoratedBarcodeView1);
btnDecodeFromAlbum = (Button) findViewById(R.id.btn_decode_from_album);
// 初始化扫码解码器支持所有常见码制避免仅支持QR_CODE的局限
List<BarcodeFormat> formats = new ArrayList<BarcodeFormat>();
formats.add(BarcodeFormat.QR_CODE);
formats.add(BarcodeFormat.CODE_128);
formats.add(BarcodeFormat.EAN_13);
barcodeView.getBarcodeView().setDecoderFactory(new DefaultDecoderFactory(formats));
}
private void setAlbumDecodeClickListener() {
btnDecodeFromAlbum.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (checkSelfPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_READ_STORAGE_PERMISSION);
} else {
openAlbum();
}
}
});
}
private void openAlbum() {
Intent pickImageIntent = new Intent(Intent.ACTION_PICK);
pickImageIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(pickImageIntent, REQUEST_PICK_IMAGE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_PICK_IMAGE && resultCode == RESULT_OK && data != null) {
Uri selectedImageUri = data.getData();
if (selectedImageUri != null) {
try {
// 1. 读取图片并压缩(关键优化:避免大图片解码失败)
InputStream imageStream = getContentResolver().openInputStream(selectedImageUri);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 先获取图片尺寸,不加载像素
BitmapFactory.decodeStream(imageStream, null, options);
imageStream.close(); // 关闭流,重新读取
// 计算压缩比例超过MAX_BITMAP_SIZE时按比例压缩
options.inSampleSize = calculateInSampleSize(options, MAX_BITMAP_SIZE, MAX_BITMAP_SIZE);
options.inJustDecodeBounds = false; // 开始加载压缩后的像素
imageStream = getContentResolver().openInputStream(selectedImageUri);
Bitmap originalBitmap = BitmapFactory.decodeStream(imageStream, null, options);
imageStream.close();
if (originalBitmap == null) {
showToast("图片损坏,无法解析");
return;
}
// 2. 图片预处理:转为灰度图+提高对比度(解决模糊/低对比度图片识别问题)
Bitmap processedBitmap = processBitmap(originalBitmap);
// 3. 解码预处理后的图片
String decodeResult = decodeQrFromBitmap(processedBitmap);
// 4. 结果处理
if (decodeResult != null && !decodeResult.isEmpty()) {
resultTextView.setText("图片解码结果:" + decodeResult);
showDecodeResultDialog(decodeResult);
returnResultToPreviousPage(decodeResult);
} else {
// 尝试直接解码原图(防止预处理过度导致识别失败)
String originalResult = decodeQrFromBitmap(originalBitmap);
if (originalResult != null && !originalResult.isEmpty()) {
resultTextView.setText("图片解码结果:" + originalResult);
showDecodeResultDialog(originalResult);
returnResultToPreviousPage(originalResult);
} else {
new AlertDialog.Builder(this)
.setTitle("解码失败")
.setMessage("图片中未识别到二维码/条码,建议选择清晰、完整的图片")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.show();
}
}
// 回收Bitmap避免内存泄漏
if (!originalBitmap.isRecycled()) originalBitmap.recycle();
if (!processedBitmap.isRecycled() && processedBitmap != originalBitmap) {
processedBitmap.recycle();
}
} catch (Exception e) {
e.printStackTrace();
showToast("图片处理失败:" + e.getMessage());
}
} else {
showToast("未选择图片");
}
}
}
/**
* 核心优化1计算图片压缩比例
*/
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// 找到最接近reqWidth/reqHeight的压缩比例2的倍数保证图片质量
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
/**
* 核心优化2图片预处理灰度化+提高对比度)
* 解决模糊、低亮度、低对比度图片识别率低的问题
*/
private Bitmap processBitmap(Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
// 创建灰度图
Bitmap grayBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(grayBitmap);
Paint paint = new Paint();
// 1. 灰度化矩阵
ColorMatrix grayMatrix = new ColorMatrix();
grayMatrix.setSaturation(0); // 饱和度设为0转为灰度
// 2. 提高对比度矩阵alpha=1.5,亮度=0可根据需求调整
ColorMatrix contrastMatrix = new ColorMatrix();
contrastMatrix.set(new float[]{
1.5f, 0, 0, 0, 0, // 红通道对比度
0, 1.5f, 0, 0, 0, // 绿通道对比度
0, 0, 1.5f, 0, 0, // 蓝通道对比度
0, 0, 0, 1f, 0 // alpha通道不变
});
// 合并灰度+对比度矩阵
ColorMatrix combinedMatrix = new ColorMatrix();
combinedMatrix.postConcat(grayMatrix);
combinedMatrix.postConcat(contrastMatrix);
paint.setColorFilter(new ColorMatrixColorFilter(combinedMatrix));
canvas.drawBitmap(bitmap, 0, 0, paint);
return grayBitmap;
}
/**
* 核心优化3修复解码参数支持更多场景
*/
private String decodeQrFromBitmap(Bitmap bitmap) {
if (bitmap == null) return null;
try {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
// 修复1使用RGBLuminanceSource避免YUV格式导致的颜色偏差
LuminanceSource source = new RGBLuminanceSource(width, height, pixels);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
// 修复2完善解码参数解决模糊、变形二维码识别问题
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
// 支持所有常见码制不仅限于QR_CODE
List<BarcodeFormat> formats = new ArrayList<BarcodeFormat>();
formats.add(BarcodeFormat.QR_CODE);
formats.add(BarcodeFormat.CODE_128);
formats.add(BarcodeFormat.EAN_8);
formats.add(BarcodeFormat.EAN_13);
hints.put(DecodeHintType.POSSIBLE_FORMATS, formats);
// 字符编码:支持中文等多语言
hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
// 容错模式:允许二维码有一定损坏(关键!解决轻微变形/污染的二维码)
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
// 不使用纯条码模式兼容带logo的二维码
hints.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE);
MultiFormatReader reader = new MultiFormatReader();
reader.setHints(hints);
Result result = reader.decode(binaryBitmap);
return result.getText();
} catch (NotFoundException e) {
// 正常未识别到,不打印异常(避免日志冗余)
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// ==================== 原有逻辑(不变) ====================
private void checkCameraPermission() {
if (checkSelfPermission(android.Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{android.Manifest.permission.CAMERA},
REQUEST_CAMERA_PERMISSION);
} else {
startScanning();
}
}
private void startScanning() {
barcodeView.getBarcodeView().setDecoderFactory(null);
barcodeView.decodeContinuous(barcodeCallback);
}
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startScanning();
} else {
// 权限被拒绝的处理
}
}
}
BarcodeCallback barcodeCallback = new BarcodeCallback(){
private BarcodeCallback barcodeCallback = new BarcodeCallback() {
@Override
public void barcodeResult(BarcodeResult result) {
if (result.getText() != null) {
//Toast.makeText(MainActivity.this, "Scanned: " + result.getText(), Toast.LENGTH_SHORT).show();
//ToastUtils.show("Scanned: " + result.getText());
if (result != null && result.getText() != null) {
barcodeView.pause();
Intent intent = new Intent();
intent.putExtra(EXTRA_RESULT, result.getText());
setResult(RESULT_OK, intent);
finish();
String decodeResult = result.getText();
resultTextView.setText("扫码结果:" + decodeResult);
showDecodeResultDialog(decodeResult);
returnResultToPreviousPage(decodeResult);
}
}
@@ -92,16 +326,111 @@ public class QRCodeDecodeActivity extends Activity {
}
};
private void showDecodeResultDialog(String result) {
ScrollView scrollView = new ScrollView(this);
scrollView.setPadding(dip2px(16), dip2px(16), dip2px(16), dip2px(16));
TextView dialogTv = new TextView(this);
dialogTv.setTextSize(16);
dialogTv.setTextColor(getResources().getColor(android.R.color.black));
dialogTv.setText(result);
scrollView.addView(dialogTv);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("解码结果");
builder.setView(scrollView);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
barcodeView.resume();
}
});
builder.setCancelable(false);
builder.show();
}
private void returnResultToPreviousPage(String result) {
Intent intent = new Intent();
intent.putExtra(EXTRA_RESULT, result);
setResult(RESULT_OK, intent);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults != null && grantResults.length > 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startScanning();
} else {
new AlertDialog.Builder(this)
.setTitle("权限申请")
.setMessage("扫码需要相机权限,请在设置中开启")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
finish();
}
})
.setCancelable(false)
.show();
}
}
} else if (requestCode == REQUEST_READ_STORAGE_PERMISSION) {
if (grantResults != null && grantResults.length > 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openAlbum();
} else {
new AlertDialog.Builder(this)
.setTitle("权限申请")
.setMessage("从相册解码需要存储权限,请在设置中开启")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setCancelable(false)
.show();
}
}
}
}
@Override
protected void onResume() {
super.onResume();
if (barcodeView != null) {
barcodeView.resume();
}
}
@Override
protected void onPause() {
super.onPause();
if (barcodeView != null) {
barcodeView.pause();
}
}
private int dip2px(float dpValue) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
public boolean onOptionsItemSelected(android.view.MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
private void showToast(String message) {
android.widget.Toast.makeText(this, message, android.widget.Toast.LENGTH_SHORT).show();
}
}

View File

@@ -0,0 +1,97 @@
package cc.winboll.studio.apputils;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/09/22 07:09
* @Describe 二维码生成窗口
*/
import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.journeyapps.barcodescanner.BarcodeEncoder;
public class QRGeneratorActivity extends Activity {
public static final String TAG = "QrGeneratorActivity";
// 控件引用
private EditText etInputText;
private ImageView ivQrPreview;
private Button btnGenerateQr;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qrgenerator);
// 初始化控件
initViews();
// 设置按钮点击事件
setGenerateClickListener();
}
/**
* 初始化布局控件
*/
private void initViews() {
etInputText = findViewById(R.id.et_input_text);
ivQrPreview = findViewById(R.id.iv_qr_preview);
btnGenerateQr = findViewById(R.id.btn_generate_qr);
}
/**
* 设置生成按钮点击事件:获取输入文字 → 生成二维码 → 显示到 ImageView
*/
private void setGenerateClickListener() {
btnGenerateQr.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1. 获取输入框文字(去除前后空格)
String inputText = etInputText.getText().toString().trim();
// 2. 空输入判断
if (inputText.isEmpty()) {
Toast.makeText(QRGeneratorActivity.this, "请先输入要生成二维码的文字", Toast.LENGTH_SHORT).show();
return;
}
// 3. 生成二维码 Bitmap宽高 500px可调整
Bitmap qrBitmap = generateQrCodeBitmap(inputText, 500, 500);
// 4. 显示二维码到 ImageView
if (qrBitmap != null) {
ivQrPreview.setImageBitmap(qrBitmap);
} else {
Toast.makeText(QRGeneratorActivity.this, "二维码生成失败,请重试", Toast.LENGTH_SHORT).show();
}
}
});
}
/**
* 核心方法:生成二维码 Bitmap
* @param content 二维码内容(输入的文字)
* @param width 二维码宽度px
* @param height 二维码高度px
* @return 生成的二维码 Bitmap失败返回 null
*/
private Bitmap generateQrCodeBitmap(String content, int width, int height) {
try {
// 初始化二维码编码器(指定格式为 QR_CODE
BarcodeEncoder encoder = new BarcodeEncoder();
// 生成二维码 Bitmap参数内容、格式、宽、高
return encoder.encodeBitmap(content, BarcodeFormat.QR_CODE, width, height);
} catch (WriterException e) {
// 生成失败(如内容过长、宽高非法),打印异常信息
e.printStackTrace();
return null;
}
}
}

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.apputils;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/23 16:14:45
*/
import android.app.Activity;

View File

@@ -1,24 +1,14 @@
package cc.winboll.studio.apputils;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/05/13 11:15
* @Describe WinBoLLActivity
*/
import android.app.Activity;
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
public class WinBoLLActivity extends Activity implements IWinBoLLActivity {
public class WinBoLLActivity extends Activity {
public static final String TAG = "WinBoLLActivity";
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
}

View File

@@ -54,13 +54,6 @@
</LinearLayout>
<cc.winboll.studio.libappbase.LogView
android:layout_width="match_parent"
android:layout_height="0dp"
android:text="Button"
android:id="@+id/logview"
android:layout_weight="1.0"/>
</LinearLayout>
</LinearLayout>

View File

@@ -1,20 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="QRCodeDecodeActivity"/>
<!-- 工具栏(原有) -->
<Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<!-- 扫码控件(原有) -->
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:layout_width="wrap_content"
android:id="@+id/activityqrcodedecodeDecoratedBarcodeView1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<!-- 新增:从相册解码按钮 -->
<Button
android:id="@+id/btn_decode_from_album"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/activityqrcodedecodeDecoratedBarcodeView1"/>
android:text="从相册选图解码"
android:textSize="16sp"
android:textColor="@android:color/white"
android:background="#2196F3"
android:paddingVertical="12dp"
android:layout_margin="16dp"/>
<!-- 结果显示TextView原有 -->
<TextView
android:id="@+id/activityqrcodedecodeTextView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="等待扫码或选择图片..."
android:textSize="16sp"/>
</LinearLayout>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp"
android:gravity="top|center_horizontal">
<!-- 文字输入框 -->
<EditText
android:id="@+id/et_input_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入要生成二维码的文字"
android:inputType="textMultiLine"
android:minLines="3"
android:maxLines="5"
android:padding="12dp"
android:background="@android:drawable/editbox_background_normal"
android:layout_marginBottom="16dp"/>
<!-- 生成二维码按钮 -->
<Button
android:id="@+id/btn_generate_qr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="生成二维码"
android:textSize="16sp"
android:background="#4CAF50"
android:textColor="@android:color/white"
android:paddingVertical="12dp"
android:layout_marginBottom="24dp"/>
<!-- 二维码预览图片 -->
<ImageView
android:id="@+id/iv_qr_preview"
android:layout_width="280dp"
android:layout_height="280dp"
android:scaleType="fitCenter"
android:background="@android:drawable/dialog_holo_light_frame"
android:contentDescription="二维码预览"/>
</LinearLayout>

View File

@@ -2,8 +2,8 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/item_teststringtoqrcodeview"
android:title="TestStringToQRCodeViewActivity"/>
android:id="@+id/item_testqrgeneratoractivity"
android:title="TestQRGeneratorActivity"/>
<item
android:id="@+id/item_testqrcodedecodeactivity"
android:title="TestQRCodeDecodeActivity"/>

35
autoinstaller/README.md Normal file
View File

@@ -0,0 +1,35 @@
# AutoInstaller
#### 介绍
APK 文件监控,监控指定文件路径的 APK 文件。
在 APK 文件改变后自动调用系统安装命令安装,或者调用[应用信息查看器]打开。
#### 软件架构
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
#### Gradle 编译说明
调试版编译命令 gradle assembleBetaDebug
阶段版编译命令 bash .winboll/bashPublishAPKAddTag.sh autoinstaller
#### 使用说明
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码 : ZhanGSKen(ZhanGSKen<zhangsken@188.com>)
4. 新建 Pull Request
#### 特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
#### 参考文档

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Sun May 04 05:32:00 GMT 2025
stageCount=1
#Tue Jun 24 09:54:47 HKT 2025
stageCount=3
libraryProject=
baseVersion=15.2
publishVersion=15.2.0
buildCount=74
baseBetaVersion=15.2.1
publishVersion=15.2.2
buildCount=0
baseBetaVersion=15.2.3

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.autoinstaller;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/04/28 02:39:58
* @Describe 全局应用类
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.autoinstaller;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/04/15 09:24:46
* @Describe 磁贴工具服务类
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.autoinstaller.models;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/04/02 20:50:29
* @Describe 监控的 APK 安装文件对应的应用信息数据模型
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.autoinstaller.models;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/05/27 17:36:01
* @Describe 应用配置数据类
*/

View File

@@ -5,7 +5,7 @@ import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/04/15 09:27:39
* @Describe MainServiceBean
*/

View File

@@ -9,7 +9,7 @@ import cc.winboll.studio.autoinstaller.services.MainService;
import cc.winboll.studio.libappbase.LogUtils;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/06 13:19:38
* @Describe 应用消息接收类
*/

View File

@@ -10,7 +10,7 @@ import cc.winboll.studio.autoinstaller.models.AppConfigs;
import cc.winboll.studio.autoinstaller.utils.ServiceUtil;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/05/27 20:16:00
* @Describe MainService 守护进程服务
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.autoinstaller.utils;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/05/27 17:56:31
* @Describe 文件管理类
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.autoinstaller.utils;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/12/11 06:28:50
* @Describe 一个获取安卓APK安装文件的应用包名的函数
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.autoinstaller.utils;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/05/27 20:20:03
* @Describe 服务类工具
*/

View File

@@ -3,7 +3,7 @@
https://github.com/aJIEw/PhoneCallApp.git
#### 介绍
通讯录与拨号
这是可以根据正则表达式匹配拦截骚扰电话的手机拨号应用。
#### 软件架构
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。

View File

@@ -45,9 +45,9 @@ android {
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
api 'cc.winboll.studio:libaes:15.8.0'
api 'cc.winboll.studio:libapputils:15.8.1'
api 'cc.winboll.studio:libappbase:15.8.1'
api 'cc.winboll.studio:libaes:15.9.3'
api 'cc.winboll.studio:libapputils:15.8.5'
api 'cc.winboll.studio:libappbase:15.9.5'
// 权限请求框架https://github.com/getActivity/XXPermissions
api 'com.github.getActivity:XXPermissions:18.63'

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Tue May 20 13:02:18 HKT 2025
stageCount=3
#Sun Aug 31 06:05:42 CST 2025
stageCount=17
libraryProject=
baseVersion=15.3
publishVersion=15.3.2
publishVersion=15.3.16
buildCount=0
baseBetaVersion=15.3.3
baseBetaVersion=15.3.17

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/12/08 15:10:51
* @Describe 全局应用类
*/

View File

@@ -1,5 +1,10 @@
package cc.winboll.studio.contacts;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/08/30 14:32
* @Describe 主窗口
*/
import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
@@ -8,6 +13,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.telecom.TelecomManager;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
@@ -55,13 +61,10 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
MainServiceBean mMainServiceBean;
private TabLayout tabLayout;
private ViewPager viewPager;
private List<View> views; //用来存放放进ViewPager里面的布局
//实例化存储imageView导航原点的集合
private List<View> views;
ImageView[] imageViews;
//MyPagerAdapter adapter;//适配器
MyPagerAdapter pagerAdapter;
LinearLayout linearLayout;//下标所在在LinearLayout布局里
int currentPoint = 0;//当前被选中中页面的下标
LinearLayout linearLayout;
int currentPoint = 0;
private TelephonyManager telephonyManager;
private MyPhoneStateListener phoneStateListener;
@@ -70,30 +73,6 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
private static final int DIALER_REQUEST_CODE = 1;
// @Override
// public Activity getActivity() {
// return this;
// }
// @Override
// public APPInfo getAppInfo() {
// String szBranchName = "contacts";
//
// APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo();
// appInfo.setAppName("Contacts");
// appInfo.setAppIcon(cc.winboll.studio.libapputils.R.drawable.ic_winboll);
// appInfo.setAppDescription("Contacts Description");
// appInfo.setAppGitName("APP");
// appInfo.setAppGitOwner("Studio");
// appInfo.setAppGitAPPBranch(szBranchName);
// appInfo.setAppGitAPPSubProjectFolder(szBranchName);
// appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts");
// appInfo.setAppAPKName("Contacts");
// appInfo.setAppAPKFolderName("Contacts");
// return appInfo;
// return null;
// }
@Override
public Activity getActivity() {
@@ -107,89 +86,62 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
@Override
protected void onCreate(Bundle savedInstanceState) {
// 接收并处理 Intent 数据,函数 Intent 处理接收就直接返回
//if (prosessIntents(getIntent())) return;
// 以下正常创建主窗口
super.onCreate(savedInstanceState);
_MainActivity = this;
setContentView(R.layout.activity_main);
// 初始化工具栏
mToolbar = findViewById(R.id.activitymainToolbar1);
// 初始化工具栏仅加载基础UI
mToolbar = (Toolbar) findViewById(R.id.activitymainToolbar1);
setSupportActionBar(mToolbar);
// if (isEnableDisplayHomeAsUp()) {
// // 显示后退按钮
// getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// }
getSupportActionBar().setSubtitle(TAG);
tabLayout = findViewById(R.id.tabLayout);
viewPager = findViewById(R.id.viewPager);
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
viewPager = (ViewPager) findViewById(R.id.viewPager);
// 创建Fragment列表和标题列表
fragmentList = new ArrayList<>();
tabTitleList = new ArrayList<>();
// 创建Fragment列表(仅实例化,不加载数据)
fragmentList = new ArrayList<Fragment>();
tabTitleList = new ArrayList<String>();
fragmentList.add(CallLogFragment.newInstance(0));
fragmentList.add(ContactsFragment.newInstance(1));
fragmentList.add(ContactsFragment.newInstance(1)); // 延迟加载联系人数据
fragmentList.add(LogFragment.newInstance(2));
tabTitleList.add("通话记录");
tabTitleList.add("联系人");
tabTitleList.add("应用日志");
// 设置ViewPager适配器
// 设置ViewPager适配器
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), fragmentList, tabTitleList);
viewPager.setAdapter(adapter);
// 关键:关闭预加载,仅当前页初始化
viewPager.setOffscreenPageLimit(0);
// 关联TabLayout和ViewPager
tabLayout.setupWithViewPager(viewPager);
// initData();
// initView();
// //initPoint();//调用初始化导航原点的方法
// viewPager.addOnPageChangeListener(this);//滑动事件
//ViewPager viewPager = findViewById(R.id.activitymainViewPager1);
//MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
//viewPager.setAdapter(pagerAdapter);
//TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1);
//tabLayout.setupWithViewPager(viewPager);
// mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
// if (mMainServiceBean == null) {
// mMainServiceBean = new MainServiceBean();
// }
// cbMainService = findViewById(R.id.activitymainCheckBox1);
// cbMainService.setChecked(mMainServiceBean.isEnable());
// cbMainService.setOnClickListener(new View.OnClickListener(){
// @Override
// public void onClick(View view) {
// if (cbMainService.isChecked()) {
// MainService.startMainService(MainActivity.this);
// } else {
// MainService.stopMainService(MainActivity.this);
// }
// }
// });
// 初始化服务状态(延迟启动非核心服务)
MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
if (mMainServiceBean == null) {
mMainServiceBean = new MainServiceBean();
MainServiceBean.saveBean(this, mMainServiceBean);
}
if (mMainServiceBean.isEnable()) {
MainService.startMainService(this);
// 延迟1秒启动服务避免阻塞启动
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
MainService.startMainService(MainActivity.this);
}
}, 1000);
}
// 初始化TelephonyManager和PhoneStateListener
// 初始化电话状态监听(基础功能保留)
telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
phoneStateListener = new MyPhoneStateListener();
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
// ViewPager适配器
// ViewPager适配器Java 7语法
private class MyPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragmentList;
@@ -226,91 +178,22 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
_MainActivity.startActivity(intent);
}
//初始化view即显示的图片
// void initView() {
// viewPager = findViewById(R.id.activitymainViewPager1);
// pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
// viewPager.setAdapter(pagerAdapter);
// //adapter = new MyPagerAdapter(views);
// //viewPager = findViewById(R.id.activitymainViewPager1);
// //viewPager.setAdapter(adapter);
// //linearLayout = findViewById(R.id.activitymainLinearLayout1);
// //initPoint();//初始化页面下方的点
// viewPager.setOnPageChangeListener(this);
//
// }
//初始化所要显示的布局
// void initData() {
// LayoutInflater inflater = LayoutInflater.from(getActivity());
// View view1 = inflater.inflate(R.layout.fragment_call_log, viewPager, false);
// View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false);
// View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false);
//
// views = new ArrayList<>();
// views.add(view1);
// views.add(view2);
// views.add(view3);
// }
// void initPoint() {
// imageViews = new ImageView[5];//实例化5个图片
// for (int i = 0; i < linearLayout.getChildCount(); i++) {
// imageViews[i] = (ImageView) linearLayout.getChildAt(i);
// imageViews[i].setImageResource(R.drawable.ic_launcher);
// imageViews[i].setOnClickListener(this);//点击导航点,即可跳转
// imageViews[i].setTag(i);//重复利用实例化的对象
// }
// currentPoint = 0;//默认第一个坐标
// imageViews[currentPoint].setImageResource(R.drawable.ic_launcher);
// }
//OnPageChangeListener接口要实现的三个方法
/* onPageScrollStateChanged(int state)
此方法是在状态改变的时候调用其中state这个参数有三种状态
SCROLL_STATE_DRAGGING1表示用户手指“按在屏幕上并且开始拖动”的状态
手指按下但是还没有拖动的时候还不是这个状态只有按下并且手指开始拖动后log才打出。
SCROLL_STATE_IDLE0滑动动画做完的状态。
SCROLL_STATE_SETTLING2在“手指离开屏幕”的状态。*/
// OnPageChangeListener接口实现
@Override
public void onPageScrollStateChanged(int state) {
public void onPageScrollStateChanged(int state) {}
}
/* onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为:
position :当前页面即你点击滑动的页面从A滑B则是A页面的position。
positionOffset:当前页面偏移的百分比
positionOffsetPixels:当前页面偏移的像素位置*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
}
/* onPageSelected(int position)
此方法是页面滑动完后得到调用position是你当前选中的页面的Position位置编号
(从A滑动到B就是B的position)*/
public void onPageSelected(int position) {
// ImageView preView = imageViews[currentPoint];
// preView.setImageResource(R.drawable.ic_launcher);
// ImageView currView = imageViews[position];
// currView.setImageResource(R.drawable.ic_launcher);
// currentPoint = position;
}
//小圆点点击事件
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
//通过getTag(),可以判断是哪个控件
// int i = (Integer) v.getTag();
// viewPager.setCurrentItem(i);//直接跳转到某一个页面的情况
}
public void onPageSelected(int position) {}
@Override
public void onClick(View v) {}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
//setSubTitle("");
}
private class MyPhoneStateListener extends PhoneStateListener {
@@ -336,109 +219,31 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
LogUtils.d(TAG, "onDestroy() SOS");
}
//
// 处理传入的 Intent 数据
//
// boolean prosessIntents(Intent intent) {
// if (intent == null
// || intent.getAction() == null
// || intent.getAction().equals(""))
// return false;
//
// if (intent.getAction().equals(StringToQrCodeView.ACTION_UNITTEST_QRCODE)) {
// try {
// WinBoLLActivity clazzActivity = UnitTestActivity.class.newInstance();
// String tag = clazzActivity.getTag();
// LogUtils.d(TAG, "String tag = clazzActivity.getTag(); tag " + tag);
// Intent subIntent = new Intent(this, UnitTestActivity.class);
// subIntent.setAction(intent.getAction());
// File file = new File(getCacheDir(), UUID.randomUUID().toString());
// //取出文件uri
// Uri uri = intent.getData();
// if (uri == null) {
// uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
// }
// //获取文件真实地址
// String szSrcPath = UriUtils.getFileFromUri(getApplication(), uri);
// if (TextUtils.isEmpty(szSrcPath)) {
// return false;
// }
//
// Files.copy(Paths.get(szSrcPath), Paths.get(file.getPath()));
// //startWinBoLLActivity(subIntent, tag);
// WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, subIntent, UnitTestActivity.class);
// } catch (IllegalAccessException | InstantiationException | IOException e) {
// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
// // 函数处理异常返回失败
// return false;
// }
// } else {
// LogUtils.d(TAG, "prosessIntents|" + intent.getAction() + "|yet");
// return false;
// }
// return true;
// }
// @Override
// public String getTag() {
// return TAG;
// }
// @Override
// public void onBackPressed() {
// exit();
// }
//
// void exit() {
// YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){
//
// @Override
// public void onYes() {
// WinBoLLActivityManager.getInstance(getApplicationContext()).finishAll();
// }
//
// @Override
// public void onNo() {
// }
// };
// YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener);
// }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.item_settings) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
//WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, CallActivity.class);
}
// } else
// if (item.getItemId() == R.id.item_exit) {
// exit();
// return true;
// }
return super.onOptionsItemSelected(item);
}
@Override
protected void onResume() {
super.onResume();
}
/**
* Android M 及以上检查是否是系统默认电话应用
* 检查是否是系统默认电话应用
*/
public boolean isDefaultPhoneCallApp() {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);
if (manger != null && manger.getDefaultDialerPackage() != null) {
return manger.getDefaultDialerPackage().equals(getPackageName());
@@ -457,25 +262,11 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
return true;
}
}
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// switch (resultCode) {
// case REQUEST_HOME_ACTIVITY : {
// LogUtils.d(TAG, "REQUEST_HOME_ACTIVITY");
// break;
// }
// case REQUEST_ABOUT_ACTIVITY : {
// LogUtils.d(TAG, "REQUEST_ABOUT_ACTIVITY");
// break;
// }
// default : {
// super.onActivityResult(requestCode, resultCode, data);
// }
// }
if (requestCode == DIALER_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用",
@@ -484,3 +275,4 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
}
}
}

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/31 15:15:54
* @Describe 应用介绍窗口
*/
@@ -79,12 +79,12 @@ public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity
APPInfo appInfo = new APPInfo();
appInfo.setAppName("Contacts");
appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll);
appInfo.setAppDescription("通讯录与拨号");
appInfo.setAppGitName("APP");
appInfo.setAppDescription("这是可以根据正则表达式匹配拦截骚扰电话的手机拨号应用。");
appInfo.setAppGitName("APPBase");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(szBranchName);
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts");
appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=4&extra=page%3D1");
appInfo.setAppAPKName("Contacts");
appInfo.setAppAPKFolderName("Contacts");
return new AboutView(mContext, appInfo);

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/20 17:15:46
* @Describe 拨号窗口
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/20 20:18:26
*/
import android.content.Intent;

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/21 05:37:42
*/
import android.content.Context;
@@ -198,6 +198,9 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
settingsModel.setDunTotalCount(Integer.parseInt(etDunTotalCount.getText().toString()));
settingsModel.setDunResumeSecondCount(Integer.parseInt(etDunResumeSecondCount.getText().toString()));
settingsModel.setDunResumeCount(Integer.parseInt(etDunResumeCount.getText().toString()));
// 应用效果提示
ToastUtils.show((settingsModel.getDunTotalCount() == 1)?"电话骚扰防御力几乎为0。":String.format("以下设置将在连拨%d次后接通电话。", settingsModel.getDunTotalCount()));
}
settingsModel.setIsEnableDun(isEnableDun);
Rules.getInstance(this).saveDun();
@@ -207,6 +210,7 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
etDunTotalCount.setText(Integer.toString(settingsModel.getDunTotalCount()));
etDunResumeSecondCount.setText(Integer.toString(settingsModel.getDunResumeSecondCount()));
etDunResumeCount.setText(Integer.toString(settingsModel.getDunResumeCount()));
}
void updateStreamVolumeTextView() {
@@ -243,6 +247,9 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
Rules.getInstance(this).resetDefaultBoBullToonURL();
EditText etBoBullToonURL = findViewById(R.id.bobulltoonurl_et);
etBoBullToonURL.setText(Rules.getInstance(this).getBoBullToonURL());
final TomCat tomCat = TomCat.getInstance(this);
tomCat.cleanBoBullToon();
}
public void onDownloadBoBullToon(View view) {
@@ -330,4 +337,8 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
public void onAbout(View view) {
App.getWinBoLLActivityManager().startWinBoLLActivity(this, AboutActivity.class);
}
public void onLogView(View view) {
App.getWinBoLLActivityManager().startLogActivity(this);
}
}

View File

@@ -11,7 +11,7 @@ import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/02 16:07:04
*/
public class UnitTestActivity extends Activity {

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/31 15:16:45
* @Describe 应用窗口基类
*/

View File

@@ -1,17 +1,22 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/26 13:09:32
* @Describe CallLogAdapter
*/
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R;
@@ -36,6 +41,10 @@ public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogV
this.callLogList = callLogList;
}
public void relaodContacts() {
this.mContactUtils.relaodContacts();
}
@NonNull
@Override
public CallLogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -47,6 +56,38 @@ public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogV
public void onBindViewHolder(@NonNull CallLogViewHolder holder, int position) {
final CallLogModel callLog = callLogList.get(position);
holder.phoneNumber.setText(callLog.getPhoneNumber() + "" + mContactUtils.getContactsName(callLog.getPhoneNumber()));
holder.phoneNumber.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View p1) {
// 弹出复制菜单
PopupMenu menu = new PopupMenu(mContext, holder.phoneNumber);
//加载菜单资源
menu.getMenuInflater().inflate(R.menu.toolbar_calllog_phonenumber, menu.getMenu());
//设置点击事件的响应
menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
int nItemId = menuItem.getItemId();
if (nItemId == R.id.item_calllog_phonenumber_copy) {
// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
// Creates a new text clip to put on the clipboard
ClipData clip = ClipData.newPlainText("simple text", callLog.getPhoneNumber());
// Set the clipboard's primary clip.
clipboard.setPrimaryClip(clip);
Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show();
}
return true;
}
});
//一定要调用show()来显示弹出式菜单
menu.show();
return true;
}
});
holder.callStatus.setText(callLog.getCallStatus());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
holder.callDate.setText(dateFormat.format(callLog.getCallDate()));

View File

@@ -1,23 +1,29 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/26 13:35:44
* @Describe ContactAdapter
*/
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
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.ContactModel;
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
import com.hjq.toast.ToastUtils;
import java.util.List;
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactViewHolder> {
@@ -26,8 +32,10 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV
private static final int REQUEST_CALL_PHONE = 1;
private List<ContactModel> contactList;
Context mContext;
public ContactAdapter(List<ContactModel> contactList) {
public ContactAdapter(Context context, List<ContactModel> contactList) {
mContext = context;
this.contactList = contactList;
}
@@ -41,6 +49,37 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV
@Override
public void onBindViewHolder(@NonNull ContactViewHolder holder, int position) {
final ContactModel contact = contactList.get(position);
holder.llPhoneNumberMain.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View p1) {
// 弹出复制菜单
PopupMenu menu = new PopupMenu(mContext, holder.llPhoneNumberMain);
//加载菜单资源
menu.getMenuInflater().inflate(R.menu.toolbar_contact_phonenumber, menu.getMenu());
//设置点击事件的响应
menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
int nItemId = menuItem.getItemId();
if (nItemId == R.id.item_contact_phonenumber_copy) {
// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
// Creates a new text clip to put on the clipboard
ClipData clip = ClipData.newPlainText("simple text", contact.getNumber());
// Set the clipboard's primary clip.
clipboard.setPrimaryClip(clip);
Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show();
}
return true;
}
});
//一定要调用show()来显示弹出式菜单
menu.show();
return true;
}
});
holder.contactName.setText(contact.getName());
holder.contactNumber.setText(contact.getNumber());
@@ -69,12 +108,14 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV
}
public class ContactViewHolder extends RecyclerView.ViewHolder {
LinearLayout llPhoneNumberMain;
TextView contactName;
TextView contactNumber;
AOHPCTCSeekBar dialAOHPCTCSeekBar;
public ContactViewHolder(@NonNull View itemView) {
super(itemView);
llPhoneNumberMain = itemView.findViewById(R.id.itemcontactLinearLayout1);
contactName = itemView.findViewById(R.id.contact_name);
contactNumber = itemView.findViewById(R.id.contact_number);
dialAOHPCTCSeekBar = itemView.findViewById(R.id.aohpctcseekbar_dial);

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/20 14:20:38
* @Describe ImagePagerAdapter
*/

View File

@@ -1,12 +1,13 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/02 17:27:41
* @Describe PhoneConnectRuleAdapter
*/
import android.content.Context;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -20,6 +21,7 @@ import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.views.LeftScrollView;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
import com.hjq.toast.ToastUtils;
import java.util.ArrayList;
@@ -60,6 +62,10 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V
final SimpleViewHolder simpleViewHolder = (SimpleViewHolder) holder;
String szView = model.getRuleText().trim().equals("") ?"[NULL]": model.getRuleText();
simpleViewHolder.tvRuleText.setText(szView);
simpleViewHolder.checkBoxAllow.setChecked(model.isAllowConnection());
simpleViewHolder.checkBoxAllow.setEnabled(false);
simpleViewHolder.checkBoxEnable.setChecked(model.isEnable());
simpleViewHolder.checkBoxEnable.setEnabled(false);
simpleViewHolder.scrollView.setOnActionListener(new LeftScrollView.OnActionListener(){
@Override
@@ -215,16 +221,22 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V
private final LeftScrollView scrollView;
private final TextView tvRuleText;
CheckBox checkBoxAllow;
CheckBox checkBoxEnable;
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());
LayoutInflater inflater = LayoutInflater.from(itemView.getContext());
View viewContent = inflater.inflate(R.layout.view_phone_connect_rule_simple_content, parent, false);
tvRuleText = viewContent.findViewById(R.id.ruletext_tv);
checkBoxAllow = viewContent.findViewById(R.id.checkbox_allow);
checkBoxEnable = viewContent.findViewById(R.id.checkbox_enable);
//tvRuleText = new TextView(itemView.getContext());
scrollView.setContentWidth(parent.getWidth());
//scrollView.setContentWidth(600);
scrollView.addContentLayout(tvRuleText);
scrollView.addContentLayout(viewContent);
}
}
@@ -243,5 +255,9 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V
buttonConfirm = itemView.findViewById(R.id.button_confirm);
}
}
private void setCheckBoxTouchListener(CheckBox checkBox) {
}
}

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/26 13:10:57
* @Describe CallLogModel
*/

View File

@@ -1,9 +1,9 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/02/26 13:37:00
* @Describe ContactModel
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/08/30 14:32
* @Describe 联系人信息数据模型
*/
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
@@ -18,13 +18,18 @@ public class ContactModel {
private String name;
private String number;
private String pinyin;
// 新增:存储姓名的拼音首字母(如"啊牛"→"an"
private String pinyinFirstLetter;
public ContactModel(String name, String number) {
this.name = name;
this.number = number.replaceAll("\\s", "");
this.pinyin = convertToPinyin(name);
// 初始化时生成拼音首字母
this.pinyinFirstLetter = convertToPinyinFirstLetter(name);
}
// 原方法:转换为全拼(如"啊牛"→"aniu"
private String convertToPinyin(String chinese) {
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
@@ -33,22 +38,55 @@ public class ContactModel {
StringBuilder pinyin = new StringBuilder();
for (int i = 0; i < chinese.length(); i++) {
char ch = chinese.charAt(i);
if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) {
if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) { // 仅处理汉字
try {
String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format);
if (pinyinArray != null) {
pinyin.append(pinyinArray[0]);
if (pinyinArray != null && pinyinArray.length > 0) {
pinyin.append(pinyinArray[0]); // 取第一个拼音(多音字默认首选项)
}
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
pinyin.append(ch);
pinyin.append(ch); // 非汉字直接拼接(如字母、数字、符号)
}
}
return pinyin.toString();
}
// 新增:转换为拼音首字母(如"啊牛"→"an"
private String convertToPinyinFirstLetter(String chinese) {
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
StringBuilder firstLetters = new StringBuilder();
for (int i = 0; i < chinese.length(); i++) {
char ch = chinese.charAt(i);
if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) { // 仅处理汉字
try {
String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format);
if (pinyinArray != null && pinyinArray.length > 0) {
// 取拼音的第一个字母(如"a"、"niu"→"a"、"n"
firstLetters.append(pinyinArray[0].charAt(0));
}
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
// 非汉字可根据需求处理:此处保留原字符(如"李3"→"l3""张A"→"za"
firstLetters.append(ch);
}
}
return firstLetters.toString();
}
// 新增:获取拼音首字母
public String getPinyinFirstLetter() {
return pinyinFirstLetter;
}
// 原有getter方法
public String getName() {
return name;
}

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/13 07:06:13
*/
import android.util.JsonReader;

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/21 09:52:10
* @Describe 电话黑名单规则
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/24 18:47:11
* @Describe 手机铃声设置参数类
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/02 19:51:40
* @Describe SettingsModel
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.bobulltoon;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/02 13:47:48
* @Describe 汤姆猫管家 :使用 BoBullToon 项目,对通讯地址进行筛选判断的好朋友。
*/
@@ -11,6 +11,7 @@ import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.libappbase.LogUtils;
import com.hjq.toast.ToastUtils;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -123,7 +124,7 @@ public class TomCat {
}
// 更新新文件
if(downloadAndExtractZip(zipUrl, destinationFolder)) {
if (downloadAndExtractZip(zipUrl, destinationFolder)) {
LogUtils.d(TAG, "ZIP 文件下载并解压成功。");
return true;
}
@@ -155,11 +156,80 @@ public class TomCat {
return mContext.getExternalFilesDir(TAG);
}
public File getBoBullToonDataFolder() {
File fCheckRoot = getWorkingFolder();
if (fCheckRoot == null || !fCheckRoot.exists()) {
return fCheckRoot;
}
// 递归查找符合条件的文件夹
File targetFolder = findTargetFolder(fCheckRoot);
return targetFolder != null ? targetFolder : fCheckRoot;
}
/**
* 递归查找同时包含LICENSE和README.md文件的文件夹
*/
private File findTargetFolder(File currentFolder) {
// 检查当前文件夹是否符合条件
if (hasRequiredFiles(currentFolder)) {
return currentFolder;
}
// 查找子文件夹Java 7不支持方法引用用匿名内部类过滤
File[] subFolders = currentFolder.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory(); // 仅保留子文件夹
}
});
if (subFolders != null) {
for (File subFolder : subFolders) {
File result = findTargetFolder(subFolder);
if (result != null) {
return result;
}
}
}
return null;
}
/**
* 检查文件夹中是否同时存在LICENSE和README.md文件
*/
private boolean hasRequiredFiles(File folder) {
if (folder == null || !folder.isDirectory()) {
return false;
}
// 检查两个文件是否同时存在且均为文件(非文件夹)
File licenseFile = new File(folder, "LICENSE");
File readmeFile = new File(folder, "README.md");
return licenseFile.exists() && licenseFile.isFile()
&& readmeFile.exists() && readmeFile.isFile();
}
public void cleanBoBullToon() {
String destinationFolder = getWorkingFolder().getPath(); // 替换为实际的目标文件夹路径
// 删除旧文件
File fOldFolder = new File(destinationFolder);
if (fOldFolder.exists()) {
deleteFolderRecursive(fOldFolder);
fOldFolder.mkdirs();
}
ToastUtils.show("已清空 BoBullToon 数据!");
LogUtils.d(TAG, "已清空 BoBullToon 数据");
}
public boolean loadPhoneBoBullToon() {
listPhoneBoBullToon.clear();
File fBoBullToon = new File(getWorkingFolder(), "bobulltoon");
File fBoBullToon = getBoBullToonDataFolder();
if (fBoBullToon.exists()) {
LogUtils.d(TAG, String.format("getWorkingFolder() %s", getWorkingFolder()));
LogUtils.d(TAG, String.format("getBoBullToonDataFolder() %s", getWorkingFolder()));
for (File userFolder : fBoBullToon.listFiles()) {
if (userFolder.isDirectory()) {
for (File recordFile : userFolder.listFiles()) {

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.dun;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/21 06:15:10
* @Describe 云盾防御规则
*/
@@ -145,6 +145,14 @@ public class Rules {
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
}
// 检验拨不通号码群
if (!isDefend && MainService.isPhoneInBoBullToon(phoneNumber)) {
LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber));
isDefend = true;
isConnect = false;
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
}
// 查询通讯录是否有该联系人
boolean isPhoneInContacts = ContactUtils.getInstance(mContext).isPhoneInContacts(mContext, phoneNumber);
if (!isDefend) {
@@ -158,14 +166,6 @@ public class Rules {
}
}
// 检验拨不通号码群
if (!isDefend && MainService.isPhoneInBoBullToon(phoneNumber)) {
LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber));
isDefend = true;
isConnect = false;
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
}
// 正则匹配规则名单校验
if (!isDefend) {
for (int i = 0; i < _PhoneConnectRuleModelList.size(); i++) {

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.fragments;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/20 12:57:00
* @Describe 拨号
*/
@@ -161,4 +161,12 @@ public class CallLogFragment extends Fragment {
_CallLogFragment.triggerUpdate();
}
}
@Override
public void onResume() {
super.onResume();
//ToastUtils.show("onResume");
callLogAdapter.relaodContacts();
readCallLog(); // 窗口回显时更新通话记录
}
}

View File

@@ -1,15 +1,18 @@
package cc.winboll.studio.contacts.fragments;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/02/20 12:57:50
* @Describe 联系人
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/08/30 14:32
* @Describe 联系人视图
*/
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.provider.ContactsContract;
import android.text.Editable;
import android.text.TextWatcher;
@@ -27,24 +30,39 @@ import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.adapters.ContactAdapter;
import cc.winboll.studio.contacts.beans.ContactModel;
import cc.winboll.studio.libappbase.LogUtils;
import com.hjq.toast.ToastUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ContactsFragment extends Fragment {
public static final String TAG = "ContactsFragment";
private static final String ARG_PAGE = "ARG_PAGE";
private int mPage;
private static final int REQUEST_READ_CONTACTS = 1;
private int mPage;
private RecyclerView recyclerView;
private ContactAdapter contactAdapter;
private List<ContactModel> contactList = new ArrayList<>();
private List<ContactModel> originalContactList = new ArrayList<>();
private EditText searchEditText;
private Button btnDial;
private boolean isViewInitialized = false; // 标记视图是否已初始化
// 静态缓存:全局复用联系人数据
private static List<ContactModel> sCachedOriginalList = new ArrayList<ContactModel>();
private static List<ContactModel> sCachedFilteredList = new ArrayList<ContactModel>();
// 当前页面数据容器
private List<ContactModel> contactList = new ArrayList<ContactModel>();
private List<ContactModel> originalContactList = new ArrayList<ContactModel>();
// 异步工具
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private boolean isDataLoaded = false;
public static ContactsFragment newInstance(int page) {
Bundle args = new Bundle();
@@ -65,103 +83,272 @@ public class ContactsFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_contacts, container, false);
// 加载布局(已移除进度条相关代码)
View view = inflater.inflate(R.layout.fragment_contacts, container, false);
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.contacts_recycler_view);
// 初始化RecyclerView
recyclerView = (RecyclerView) view.findViewById(R.id.contacts_recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
contactAdapter = new ContactAdapter(contactList);
contactList = new ArrayList<ContactModel>();
contactAdapter = new ContactAdapter(getContext(), contactList);
recyclerView.setAdapter(contactAdapter);
// 初始隐藏列表,数据加载后显示
recyclerView.setVisibility(View.GONE);
searchEditText = view.findViewById(R.id.search_edit_text);
searchEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// 绑定搜索框和拨号按钮
searchEditText = (EditText) view.findViewById(R.id.search_edit_text);
btnDial = (Button) view.findViewById(R.id.btn_dial);
// 初始隐藏搜索相关控件,延迟到首次可见时显示
searchEditText.setVisibility(View.GONE);
btnDial.setVisibility(View.GONE);
}
// 首次可见时初始化资源
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
filterContacts(s.toString());
public void onResume() {
super.onResume();
if (!isViewInitialized) {
initSearchAndDial(); // 初始化搜索和拨号功能
checkContactPermission(); // 检查权限并加载数据
isViewInitialized = true;
}
}
// 初始化搜索框和拨号按钮
private void initSearchAndDial() {
// 显示搜索相关控件
searchEditText.setVisibility(View.VISIBLE);
btnDial.setVisibility(View.VISIBLE);
// 搜索框防抖监听
searchEditText.addTextChangedListener(new DebounceTextWatcher(300) {
@Override
public void afterTextChanged(Editable s) {
public void onDebounceTextChanged(String query) {
filterContacts(query);
}
});
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(){
// 拨号按钮点击事件
btnDial.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View p1) {
public void onClick(View v) {
String phoneNumber = searchEditText.getText().toString().replaceAll("\\s", "");
//phoneNumber = "+8616769764848";
ToastUtils.show(phoneNumber);
if (phoneNumber.isEmpty()) {
ToastUtils.show("请输入号码");
return;
}
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
// 添加 FLAG_ACTIVITY_NEW_TASK 标志
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});
}
// 权限检查
private void checkContactPermission() {
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_READ_CONTACTS);
} else {
loadContacts();
}
}
// 加载联系人(延迟到首次可见时)
private void loadContacts() {
// 若有缓存,直接复用
if (!sCachedOriginalList.isEmpty() && !sCachedFilteredList.isEmpty()) {
originalContactList.clear();
originalContactList.addAll(sCachedOriginalList);
contactList.clear();
contactList.addAll(sCachedFilteredList);
contactAdapter.notifyDataSetChanged();
recyclerView.setVisibility(View.VISIBLE); // 显示列表
isDataLoaded = true;
return;
}
// 无缓存时异步加载
if (!isDataLoaded) {
recyclerView.setVisibility(View.GONE); // 加载中隐藏列表
executor.execute(new Runnable() {
@Override
public void run() {
// 子线程读取联系人
final List<ContactModel> tempList = readContactsInBackground();
// 主线程更新UI
mainHandler.post(new Runnable() {
@Override
public void run() {
// 更新缓存
sCachedOriginalList.clear();
sCachedOriginalList.addAll(tempList);
sCachedFilteredList.clear();
sCachedFilteredList.addAll(tempList);
// 更新当前列表
originalContactList.clear();
originalContactList.addAll(sCachedOriginalList);
contactList.clear();
contactList.addAll(sCachedFilteredList);
contactAdapter.notifyDataSetChanged();
LogUtils.d(TAG, String.format("联系人加载完成,共%d条数据", contactList.size()));
// 数据加载后显示列表
recyclerView.setVisibility(View.VISIBLE);
isDataLoaded = true;
}
});
}
});
}
}
// 子线程读取联系人
private List<ContactModel> readContactsInBackground() {
List<ContactModel> tempList = new ArrayList<ContactModel>();
Cursor cursor = null;
try {
// 查询联系人姓名和号码
cursor = requireContext().getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[]{
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER
},
null,
null,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC"
);
if (cursor != null && cursor.moveToFirst()) {
int nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
do {
String name = cursor.getString(nameIndex);
String number = cursor.getString(numberIndex).replaceAll("\\s", ""); // 去除空格
tempList.add(new ContactModel(name, number));
} while (cursor.moveToNext());
}
} catch (Exception e) {
LogUtils.d(TAG, "读取联系人失败:" + e);
} finally {
if (cursor != null) {
cursor.close(); // 关闭游标,避免内存泄漏
}
}
return tempList;
}
// 过滤联系人
private void filterContacts(String query) {
contactList.clear();
if (query.isEmpty()) {
contactList.addAll(originalContactList);
sCachedFilteredList.clear();
sCachedFilteredList.addAll(originalContactList);
} else {
String lowerQuery = query.toLowerCase();
for (ContactModel contact : originalContactList) {
// 匹配姓名、全拼、简拼、号码
boolean matchName = contact.getName().toLowerCase().contains(lowerQuery);
boolean matchPinyin = contact.getPinyin().toLowerCase().contains(lowerQuery);
boolean matchFirstLetter = contact.getPinyinFirstLetter().toLowerCase().contains(lowerQuery);
boolean matchNumber = contact.getNumber().contains(lowerQuery);
if (matchName || matchPinyin || matchFirstLetter || matchNumber) {
contactList.add(contact);
}
}
sCachedFilteredList.clear();
sCachedFilteredList.addAll(contactList);
}
contactAdapter.notifyDataSetChanged();
// 过滤后确保列表可见
recyclerView.setVisibility(View.VISIBLE);
}
// 权限回调
@Override
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);
loadContacts(); // 授权后加载联系人
} 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);
ToastUtils.show("请授予联系人权限以查看联系人列表");
recyclerView.setVisibility(View.VISIBLE); // 显示空列表
}
}
}
// 防抖TextWatcherJava 7实现
public abstract static class DebounceTextWatcher implements TextWatcher {
private final long debounceDelay;
private Handler handler = new Handler(Looper.getMainLooper());
private Runnable pendingRunnable;
public DebounceTextWatcher(long debounceDelay) {
this.debounceDelay = debounceDelay;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// 无需处理
}
@Override
public void onTextChanged(final CharSequence s, int start, int before, int count) {
// 移除之前的延迟任务
if (pendingRunnable != null) {
handler.removeCallbacks(pendingRunnable);
}
// 延迟执行过滤
pendingRunnable = new Runnable() {
@Override
public void run() {
onDebounceTextChanged(s.toString());
}
};
handler.postDelayed(pendingRunnable, debounceDelay);
}
@Override
public void afterTextChanged(Editable s) {
// 无需处理
}
// 抽象方法:防抖后的回调
public abstract void onDebounceTextChanged(String query);
}
// 资源释放
@Override
public void onDestroy() {
super.onDestroy();
executor.shutdown(); // 关闭线程池
mainHandler.removeCallbacksAndMessages(null); // 清除未执行任务
}
// Fragment隐藏/显示时的处理
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (!hidden && isDataLoaded) {
// 复用缓存数据并显示列表
contactList.clear();
contactList.addAll(sCachedFilteredList);
contactAdapter.notifyDataSetChanged();
recyclerView.setVisibility(View.VISIBLE);
}
}
}

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.fragments;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/20 12:58:15
* @Describe 应用日志
*/

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.handlers;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/14 03:51:40
*/
import android.os.Handler;

View File

@@ -1,7 +1,7 @@
package cc.winboll.studio.contacts.receivers;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/13 06:58:04
* @Describe 主要广播接收器
*/

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