Compare commits

..

179 Commits

Author SHA1 Message Date
ZhanGSKen
00b619ee99 <appbase>APK 15.0.9 release Publish. 2025-03-24 14:06:10 +08:00
ZhanGSKen
3a6fb3e17c 更新日志标题栏风格 2025-03-24 14:04:59 +08:00
ZhanGSKen
5411d5e590 <libappbase>Library Release 15.0.8 2025-03-24 08:04:42 +08:00
ZhanGSKen
facf0b001d <appbase>APK 15.0.8 release Publish. 2025-03-24 08:04:27 +08:00
ZhanGSKen
df58c0355c 更新编译工具版本 2025-03-24 08:03:17 +08:00
ZhanGSKen
e657e45218 <libappbase>Library Release 15.0.7 2025-03-24 07:45:34 +08:00
ZhanGSKen
c22946f0a5 <appbase>APK 15.0.7 release Publish. 2025-03-24 07:44:12 +08:00
ZhanGSKen
1363a7dcce <appbase>APK 15.0.6 release Publish. 2025-03-24 07:43:28 +08:00
ZhanGSKen
e0ca5725f1 保持与旧版Gradle插件的兼容 2025-03-24 07:39:20 +08:00
ZhanGSKen
3c4988532f 函数命名更新 2025-03-24 07:30:59 +08:00
ZhanGSKen
b0a34579fa 命名空间重构 2025-03-24 07:17:22 +08:00
ZhanGSKen
dd08747edb 更新WinBoll maven库下载顺序 2025-03-21 00:42:41 +08:00
ZhanGSKen
603640b3cb <libappbase>Library Release 15.0.5 2025-03-18 10:15:14 +08:00
ZhanGSKen
5b06ce9699 <appbase>APK 15.0.5 release Publish. 2025-03-18 10:14:53 +08:00
ZhanGSKen
363fc6c7c1 修复编译参数 2025-03-18 10:13:01 +08:00
ZhanGSKen
7f43f8b5c5 <libappbase>Library Release 15.0.4 2025-03-18 10:09:49 +08:00
ZhanGSKen
a117e6e110 <appbase>APK 15.0.4 release Publish. 2025-03-18 10:09:21 +08:00
ZhanGSKen
c1f576e343 分开APP应用主题设置与类库主题预定设置。 2025-03-18 10:08:31 +08:00
ZhanGSKen
1dad84b65e <libappbase>Library Release 15.0.3 2025-03-17 09:51:28 +08:00
ZhanGSKen
1ae377e665 <appbase>APK 15.0.3 release Publish. 2025-03-17 09:51:04 +08:00
ZhanGSKen
02fa0a4134 优化调试窗口 2025-03-17 09:40:03 +08:00
ZhanGSKen
a730aa3f92 优化日志UI 2025-03-17 09:37:12 +08:00
ZhanGSKen
befea40f61 <libappbase>Library Release 15.0.2 2025-03-15 15:30:08 +08:00
ZhanGSKen
cc1ca5b092 <appbase>APK 15.0.2 release Publish. 2025-03-15 15:29:07 +08:00
ZhanGSKen
af77c09415 <libappbase>Library Release 15.0.1 2025-03-15 15:27:57 +08:00
ZhanGSKen
2968d1dbfa <appbase>APK 15.0.1 release Publish. 2025-03-15 15:27:43 +08:00
ZhanGSKen
db96ece15f 修复吐司模块不显示字幕的BUG 2025-03-15 15:26:59 +08:00
ZhanGSKen
c0347ed706 <libappbase>Library Release 15.0.0 2025-03-15 14:46:58 +08:00
ZhanGSKen
ad408dfb53 <appbase>APK 15.0.0 release Publish. 2025-03-15 14:46:32 +08:00
ZhanGSKen
953da400be 设置APP主板本号对应Android版本 2025-03-15 12:34:22 +08:00
ZhanGSKen
165765e0eb APPBase去除 Support 依赖 2025-03-12 14:32:48 +08:00
ZhanGSKen
f5f88a61b0 AndroidX Demo 调试完成 2025-03-12 02:27:08 +08:00
ZhanGSKen
2f33574d5c Android Demo 调试完成 2025-03-12 02:03:29 +08:00
ZhanGSKen
afd93d6871 添加两个示例项目 2025-03-11 20:52:40 +08:00
ZhanGSKen
b7affa477f 忽略项目编译类型配置文件 2025-03-11 20:19:33 +08:00
ZhanGSKen
4f317322fc 根项目Gradle配置文件设置两种编译类型 2025-03-11 20:18:07 +08:00
ZhanGSKen
5623d2d341 <libappbase>Library Release 2.1.5 2025-03-09 09:15:47 +08:00
ZhanGSKen
3d9b203760 <appbase>APK 2.1.5 release Publish. 2025-03-09 09:15:24 +08:00
ZhanGSKen
498a8ae458 更新Gradle配置 2025-03-09 09:13:51 +08:00
ZhanGSKen
9b8d6ad46b <libappbase>Library Release 2.1.4 2025-03-08 04:46:49 +08:00
ZhanGSKen
482f289df3 <appbase>APK 2.1.4 release Publish. 2025-03-08 04:46:23 +08:00
ZhanGSKen
28ab4e2859 修复日志时间显示BUG 2025-03-08 04:45:18 +08:00
ZhanGSKen
fd0cb05812 <libappbase>Library Release 2.1.3 2025-03-08 03:55:19 +08:00
ZhanGSKen
39b6c0ec89 <appbase>APK 2.1.3 release Publish. 2025-03-08 03:54:56 +08:00
ZhanGSKen
3b3ddbc8c9 更改类库引用方式。 2025-03-08 03:54:03 +08:00
ZhanGSKen
38aee09f87 <libappbase>Library Release 2.1.2 2025-03-08 03:38:53 +08:00
ZhanGSKen
89c4d00091 <appbase>APK 2.1.2 release Publish. 2025-03-08 03:38:29 +08:00
ZhanGSKen
4f57678012 更改类库引用方式 2025-03-08 03:37:30 +08:00
ZhanGSKen
b31e55d8ca <libappbase>Library Release 2.1.1 2025-03-08 02:31:18 +08:00
ZhanGSKen
80129470d7 <appbase>APK 2.1.1 release Publish. 2025-03-08 02:31:02 +08:00
ZhanGSKen
7b03e8302a 精简结构 2025-03-08 02:29:40 +08:00
ZhanGSKen
4d321dde84 添加类库引用 2025-03-07 17:11:17 +08:00
ZhanGSKen
b3a495ff6a <libappbase>Library Release 2.1.0 2025-03-07 14:55:07 +08:00
ZhanGSKen
a49b6cbfe0 <appbase>APK 2.1.0 release Publish. 2025-03-07 14:54:46 +08:00
ZhanGSKen
274a7135e7 应用命名重构调试完成 2025-03-07 14:51:00 +08:00
ZhanGSKen
00d0871809 更新UI视图 2025-03-07 12:37:13 +08:00
ZhanGSKen
bc252041d3 1115 2025-03-07 11:15:22 +08:00
ZhanGSKen
bca31f9079 WinBoll 应用体系重构 2025-03-02 10:55:32 +08:00
ZhanGSKen
fb7017a857 命名空间重构 2025-02-27 11:05:15 +08:00
ZhanGSKen
820574a4ba 2025-02-25 23:47:18 +08:00
ZhanGSKen
36368bd28e 1133 2025-02-25 23:33:40 +08:00
ZhanGSKen
90e66889a9 添加MyMessageManager项目 2025-02-25 18:53:54 +08:00
ZhanGSKen
3031c9bfe0 <libappbase>Library Release 2.0.2 2025-02-25 16:51:23 +08:00
ZhanGSKen
51acbb1f54 <appbase>APK 2.0.2 release Publish. 2025-02-25 16:51:09 +08:00
ZhanGSKen
26fc0b4684 <libappbase>Library Release 2.0.1 2025-02-25 16:51:01 +08:00
ZhanGSKen
ecb67edec1 <appbase>APK 2.0.1 release Publish. 2025-02-25 16:50:23 +08:00
ZhanGSKen
3b30acfdec <appbase>APK 2.0.0 release Publish. 2025-02-25 16:48:35 +08:00
ZhanGSKen
c633e1663b 2025-02-25 16:44:26 +08:00
ZhanGSKen
40589ad879 1641 2025-02-25 16:41:51 +08:00
ZhanGSKen
0357fcf6b3 0225_1615 2025-02-25 16:15:54 +08:00
ZhanGSKen
c56fdaf14d <libappbase>Library Release 2.0.0 2025-02-25 16:11:50 +08:00
ZhanGSKen
9b1264234e 设置版本号 2025-02-25 16:02:55 +08:00
ZhanGSKen
d207a9ea9b 更正com.android.tools.build:gradle:7.2.1'对应的Gradle版本设置 2025-02-25 15:59:48 +08:00
ZhanGSKen
404239de23 更新编译配置已对应Android10开发,API级别29。Java 使用版本为 Java 8。 2025-02-25 15:51:54 +08:00
ZhanGSKen
c16b80ffae 地图标记保存完成 2025-02-25 06:43:50 +08:00
ZhanGSKen
0bf0a1497a <contacts>APK 1.0.1 release Publish. 2025-02-25 00:17:29 +08:00
ZhanGSKen
2cc3850c95 优化铃声设置 2025-02-25 00:15:48 +08:00
ZhanGSKen
ca16a8677a 添加地图定位功能 2025-02-24 15:20:51 +08:00
ZhanGSKen
bb089a4eb1 整合SDK成功 2025-02-24 08:06:04 +08:00
ZhanGSKen
0c00ace869 注释问题代码 2025-02-24 04:30:26 +08:00
ZhanGSKen
e06b642b70 添加demo 2025-02-24 03:51:22 +08:00
ZhanGSKen
5e107028de 绘图与缩略完成 2025-02-22 19:43:11 +08:00
ZhanGSKen
3bcfc5a5da 添加Positions项目 2025-02-22 01:50:07 +08:00
ZhanGSKen
63c10e6dc9 添加溯源地址 2025-02-22 00:02:48 +08:00
ZhanGSKen
539afd0861 <contacts>APK 1.0.0 release Publish. 2025-02-21 12:18:00 +08:00
ZhanGSKen
234d2d57de 添加黑名单规则和区号规则设定 2025-02-21 11:37:22 +08:00
ZhanGSKen
e849e54960 <libappbase>Library Release 1.5.6 2025-02-21 11:03:53 +08:00
ZhanGSKen
8b2866419c <appbase>APK 1.5.6 release Publish. 2025-02-21 10:58:04 +08:00
ZhanGSKen
22a4a71203 更新bean基础类 2025-02-21 10:56:13 +08:00
ZhanGSKen
c16c6bba74 接收电话时预先静音模式调试完成 2025-02-21 09:47:43 +08:00
ZhanGSKen
c590c96fa6 基本电话接听与拨打功能完成 2025-02-21 06:13:51 +08:00
ZhanGSKen
64a9712294 添加电话拨打功能 2025-02-20 17:29:33 +08:00
ZhanGSKen
e5dfd5b975 拨号基本框架设计完成 2025-02-20 14:59:09 +08:00
ZhanGSKen
ffac543f6f 精简代码 2025-02-20 11:28:57 +08:00
ZhanGSKen
292f7c553f <libappbase>Library Release 1.5.5 2025-02-20 11:24:38 +08:00
ZhanGSKen
df9970c6df <appbase>APK 1.5.5 release Publish. 2025-02-20 11:24:23 +08:00
ZhanGSKen
0312ca50d2 添加绑定服务的APP的唤醒记录在部件的提示 2025-02-20 11:22:34 +08:00
ZhanGSKen
56187b588f <libappbase>Library Release 1.5.4 2025-02-19 19:57:16 +08:00
ZhanGSKen
d91372af52 <appbase>APK 1.5.4 release Publish. 2025-02-19 19:57:00 +08:00
ZhanGSKen
288725eca0 不同应用包服务绑定逻辑完成 2025-02-19 19:55:03 +08:00
ZhanGSKen
bd728b83a3 <libappbase>Library Release 1.5.3 2025-02-19 03:19:17 +08:00
ZhanGSKen
d4f9fc3561 <appbase>APK 1.5.3 release Publish. 2025-02-19 03:18:52 +08:00
ZhanGSKen
e7c614ebec 重构主应用架构 2025-02-19 03:17:49 +08:00
ZhanGSKen
c226a92ffe <libappbase>Library Release 1.5.2 2025-02-17 12:10:10 +08:00
ZhanGSKen
358e8b3522 <appbase>APK 1.5.2 release Publish. 2025-02-17 12:09:50 +08:00
ZhanGSKen
83ab4f9417 添加用磁贴启动小部件功能 2025-02-17 12:07:52 +08:00
ZhanGSKen
26cdacf1c6 BugFix 2025-02-17 11:44:15 +08:00
ZhanGSKen
6378433424 sos报告部件重构 2025-02-17 11:13:09 +08:00
ZhanGSKen
7b05d613e4 完成小部件分页功能 2025-02-16 20:54:33 +08:00
ZhanGSKen
691f9bbd1c 基本救援设施铺设完成 2025-02-15 21:37:33 +08:00
ZhanGSKen
7a8d3329d4 完成摘要事件监控小部件 2025-02-15 19:53:24 +08:00
ZhanGSKen
bbac0c7306 添加WinBoll类 2025-02-15 12:46:23 +08:00
ZhanGSKen
8d2b325172 实现磁贴启动服务功能 2025-02-15 11:30:20 +08:00
ZhanGSKen
825dfb944e AssistantService 启动成功 2025-02-14 21:18:43 +08:00
ZhanGSKen
419244b12a 放弃使用aidl 2025-02-14 07:30:47 +08:00
ZhanGSKen
39fda1b5da Use android studio add aidl file. 2025-02-14 07:04:21 +08:00
ZhanGSKen
464f2da89f test 2025-02-14 06:25:35 +08:00
ZhanGSKen
4f37b6f0e3 test 2025-02-14 06:08:50 +08:00
ZhanGSKen
f91cd0c9c3 <appbase>Start New Stage Version. 2025-02-14 06:02:07 +08:00
ZhanGSKen
d09f0783a4 <appbase>Start New Stage Version. 2025-02-14 06:01:54 +08:00
ZhanGSKen
accca716d4 Test aidl 2025-02-14 06:01:16 +08:00
ZhanGSKen
1baa0c5a61 更新类库,不同应用包之间广播消息接收调试通过。 2025-02-14 03:12:43 +08:00
ZhanGSKen
dfd09eb647 <libappbase>Library Release 1.5.1 2025-02-14 03:01:05 +08:00
ZhanGSKen
79547adcb3 <appbase>APK 1.5.1 release Publish. 2025-02-14 03:00:46 +08:00
ZhanGSKen
be39eef6e7 添加调试信息 2025-02-14 02:59:52 +08:00
ZhanGSKen
50490096b4 SOS广播消息发送与接收完成 2025-02-13 21:31:33 +08:00
ZhanGSKen
3dd4c84602 添加磁贴模块 2025-02-13 20:42:58 +08:00
ZhanGSKen
b0e81fc960 添加主服务 2025-02-13 07:29:48 +08:00
ZhanGSKen
311bc8e339 <libappbase>Library Release 1.5.0 2025-02-13 06:53:58 +08:00
ZhanGSKen
6467bcd675 <appbase>APK 1.5.0 release Publish. 2025-02-13 06:53:34 +08:00
ZhanGSKen
206c804b4d SOS 服务架构基本完成 2025-02-13 06:51:41 +08:00
ZhanGSKen
7a4df0444e 添加SOS广播接收 2025-02-13 03:46:55 +08:00
ZhanGSKen
75b2245061 添加初始 Contacts 项目 2025-02-12 23:28:42 +08:00
ZhanGSKen
3c827a56cf <libappbase>Library Release 1.4.1 2025-02-12 18:04:10 +08:00
ZhanGSKen
36af7947e8 <appbase>APK 1.4.1 release Publish. 2025-02-12 18:03:55 +08:00
ZhanGSKen
805edcf9c4 添加吐司控件 2025-02-12 16:43:57 +08:00
ZhanGSKen
11005e3356 <libappbase>Library Release 1.4.0 2025-02-12 15:01:51 +08:00
ZhanGSKen
75361b2313 <appbase>APK 1.4.0 release Publish. 2025-02-12 15:01:33 +08:00
ZhanGSKen
1d29ce81e8 编译测试 2025-02-12 14:59:38 +08:00
ZhanGSKen
0297e81090 精简配置 2025-02-12 14:59:00 +08:00
ZhanGSKen
df7203e985 更新类库 2025-02-12 14:57:50 +08:00
ZhanGSKen
8c4e8b8e23 <appbase>Start New Stage Version. 2025-02-12 14:14:36 +08:00
ZhanGSKen
d9c02f55e3 更新版本号 2025-02-12 14:14:15 +08:00
ZhanGSKen
a9cb6b471b <appbase>Start New Stage Version. 2025-02-12 14:10:53 +08:00
ZhanGSKen
e694063867 <appbase>Start New Stage Version. 2025-02-12 14:09:34 +08:00
ZhanGSKen
0ee13986dc <appbase>Start New Stage Version. 2025-02-12 14:09:05 +08:00
ZhanGSKen
3239778922 更新测试APP风格配置 2025-02-12 14:06:02 +08:00
ZhanGSKen
13510f45cf 应用风格架构基本完成 2025-02-12 13:28:58 +08:00
ZhanGSKen
49d300dd33 添加信号处理中心 2025-02-12 11:28:39 +08:00
ZhanGSKen
c42f677d48 <libappbase>Library Release 1.3.0 2025-02-11 13:11:35 +08:00
ZhanGSKen
9052a3924b <appbase>APK 1.3.0 release Publish. 2025-02-11 13:11:17 +08:00
ZhanGSKen
9642ad1966 精简代码 2025-02-11 13:09:33 +08:00
ZhanGSKen
6686da0e8f 调色架构1 2025-02-11 12:42:07 +08:00
ZhanGSKen
9e7aff09d1 <libappbase>Library Release 1.2.4 2025-02-11 00:32:09 +08:00
ZhanGSKen
494b2b7fbc <appbase>APK 1.2.4 release Publish. 2025-02-11 00:31:50 +08:00
ZhanGSKen
b1f9b74e28 源码重构整理,精简代码。 2025-02-11 00:27:54 +08:00
ZhanGSKen
32287e17c0 注释冗余代码 2025-02-09 19:05:27 +08:00
ZhanGSKen
8a0605c12e <libappbase>Library Release 1.2.3 2025-02-09 15:09:48 +08:00
ZhanGSKen
5768881a47 <appbase>APK 1.2.3 release Publish. 2025-02-09 15:09:34 +08:00
ZhanGSKen
6e02c130b2 类名重构,简化代码 2025-02-09 15:08:23 +08:00
ZhanGSKen
fb9443bbc7 <libappbase>Library Release 1.2.2 2025-02-09 13:09:48 +08:00
ZhanGSKen
bfc1180822 <appbase>APK 1.2.2 release Publish. 2025-02-09 13:09:33 +08:00
ZhanGSKen
ee52a6c28c 完成应用报告显示逻辑 2025-02-09 13:08:34 +08:00
ZhanGSKen
2f72f1795e <libappbase>Library Release 1.2.1 2025-02-09 11:41:09 +08:00
ZhanGSKen
db28447c0f <appbase>APK 1.2.1 release Publish. 2025-02-09 11:40:48 +08:00
ZhanGSKen
ad2c29d8ff 修复外部崩溃与内部崩溃的保险丝恢复逻辑 2025-02-09 11:39:24 +08:00
ZhanGSKen
b0e3bfe243 添加应用崩溃源APP名称显示 2025-02-09 11:33:27 +08:00
ZhanGSKen
7b1aeeae8b 统一内外部报告风格 2025-02-09 11:18:15 +08:00
ZhanGSKen
54cceeed3b 应用崩溃报告高级视图调试完成 2025-02-08 14:27:38 +08:00
ZhanGSKen
a72c5e1e6e 应用保险丝熔断测试通过 2025-02-08 12:26:59 +08:00
ZhanGSKen
26fcd9b584 日志模块初步调试完成 2025-02-07 19:39:25 +08:00
ZhanGSKen
ea7a0bda14 添加日志,文件,BaseBean类,未调试。 2025-02-07 17:06:16 +08:00
ZhanGSKen
b26b9dbbc4 <libappbase>Library Release 1.2.0 2025-02-06 07:12:51 +08:00
ZhanGSKen
8a405d33a7 <appbase>APK 1.2.0 release Publish. 2025-02-06 07:12:30 +08:00
ZhanGSKen
2b92da3e01 添加调试模式设置功能 2025-02-06 07:11:04 +08:00
ZhanGSKen
ac1c9fb9f5 <libappbase>Library Release 1.1.3 2025-02-04 08:46:57 +08:00
ZhanGSKen
16dd559c5b <appbase>APK 1.1.3 release Publish. 2025-02-04 08:46:36 +08:00
ZhanGSKen
3793ae3252 调整命名空间,精简代码 2025-02-04 08:45:31 +08:00
ZhanGSKen
dc8f127a8d <libappbase>Library Release 1.1.2 2025-01-23 14:42:57 +08:00
564 changed files with 62090 additions and 450 deletions

1
.gitignore vendored
View File

@@ -96,6 +96,7 @@ local.properties
## 忽略模块应用编译配置
/settings.gradle
/gradle.properties
## 忽略 srv 纠结问题
/srv/

3
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

1
.idea/.name generated Normal file
View File

@@ -0,0 +1 @@
appbase

6
.idea/compiler.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

10
.idea/deploymentTargetDropDown.xml generated Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<value>
<entry key="appbase">
<State />
</entry>
</value>
</component>
</project>

10
.idea/migrations.xml generated Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

10
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

1
androiddemo/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

74
androiddemo/build.gradle Normal file
View File

@@ -0,0 +1,74 @@
apply plugin: 'com.android.application'
apply from: '../.winboll/winboll_app_build.gradle'
apply from: '../.winboll/winboll_lint_build.gradle'
def genVersionName(def versionName){
// 检查编译标志位配置
assert (winbollBuildProps['stageCount'] != null)
assert (winbollBuildProps['baseVersion'] != null)
// 保存基础版本号
winbollBuildProps.setProperty("baseVersion", "${versionName}");
//保存编译标志配置
FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
fos.close();
// 返回编译版本号
return "${versionName}." + winbollBuildProps['stageCount']
}
android {
productFlavors {
beta {
}
stage {
}
}
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "cc.winboll.studio.androiddemo"
minSdkVersion 26
targetSdkVersion 29
versionCode 1
// versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "1.0"
if(true) {
versionName = genVersionName("${versionName}")
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
// 吐司类库
implementation 'com.github.getActivity:ToastUtils:10.5'
// Android 类库
// https://mvnrepository.com/artifact/com.android.support/support-v4
implementation 'com.android.support:support-v4:28.0.0'
// https://mvnrepository.com/artifact/com.android.support/support-compat
implementation 'com.android.support:support-compat:28.0.0'
// https://mvnrepository.com/artifact/com.android.support/support-media-compat
implementation 'com.android.support:support-media-compat:28.0.0'
// https://mvnrepository.com/artifact/com.android.support/support-core-utils
implementation 'com.android.support:support-core-utils:28.0.0'
// https://mvnrepository.com/artifact/com.android.support/support-core-ui
implementation 'com.android.support:support-core-ui:28.0.0'
// https://mvnrepository.com/artifact/com.android.support/support-fragment
implementation 'com.android.support:support-fragment:28.0.0'
// https://mvnrepository.com/artifact/com.android.support/recyclerview-v7
implementation 'com.android.support:recyclerview-v7:28.0.0'
}

View File

@@ -0,0 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Tue Mar 11 18:02:14 GMT 2025
stageCount=0
libraryProject=
baseVersion=1.0
publishVersion=1.0.0
buildCount=1
baseBetaVersion=1.0.1

21
androiddemo/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<!-- Put flavor specific code here -->
</application>
</manifest>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Android Demo +</string>
</resources>

View File

@@ -0,0 +1,39 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="cc.winboll.studio.androiddemo">
<application
tools:replace="android:appComponentFactory"
android:appComponentFactory="android.support.v4.app.CoreComponentFactory"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:resizeableActivity="true"
android:name=".GlobalApplication">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="android.max_aspect"
android:value="4.0"/>
<activity android:name=".GlobalApplication$CrashActivity"/>
</application>
</manifest>

View File

@@ -0,0 +1,334 @@
package cc.winboll.studio.androiddemo;
import android.app.Activity;
import android.app.Application;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class GlobalApplication extends Application {
private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
@Override
public void onCreate() {
super.onCreate();
CrashHandler.getInstance().registerGlobal(this);
CrashHandler.getInstance().registerPart(this);
}
public static void write(InputStream input, OutputStream output) throws IOException {
byte[] buf = new byte[1024 * 8];
int len;
while ((len = input.read(buf)) != -1) {
output.write(buf, 0, len);
}
}
public static void write(File file, byte[] data) throws IOException {
File parent = file.getParentFile();
if (parent != null && !parent.exists()) parent.mkdirs();
ByteArrayInputStream input = new ByteArrayInputStream(data);
FileOutputStream output = new FileOutputStream(file);
try {
write(input, output);
} finally {
closeIO(input, output);
}
}
public static String toString(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
write(input, output);
try {
return output.toString("UTF-8");
} finally {
closeIO(input, output);
}
}
public static void closeIO(Closeable... closeables) {
for (Closeable closeable : closeables) {
try {
if (closeable != null) closeable.close();
} catch (IOException ignored) {}
}
}
public static class CrashHandler {
public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
private static CrashHandler sInstance;
private PartCrashHandler mPartCrashHandler;
public static CrashHandler getInstance() {
if (sInstance == null) {
sInstance = new CrashHandler();
}
return sInstance;
}
public void registerGlobal(Context context) {
registerGlobal(context, null);
}
public void registerGlobal(Context context, String crashDir) {
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl(context.getApplicationContext(), crashDir));
}
public void unregister() {
Thread.setDefaultUncaughtExceptionHandler(DEFAULT_UNCAUGHT_EXCEPTION_HANDLER);
}
public void registerPart(Context context) {
unregisterPart(context);
mPartCrashHandler = new PartCrashHandler(context.getApplicationContext());
MAIN_HANDLER.postAtFrontOfQueue(mPartCrashHandler);
}
public void unregisterPart(Context context) {
if (mPartCrashHandler != null) {
mPartCrashHandler.isRunning.set(false);
mPartCrashHandler = null;
}
}
private static class PartCrashHandler implements Runnable {
private final Context mContext;
public AtomicBoolean isRunning = new AtomicBoolean(true);
public PartCrashHandler(Context context) {
this.mContext = context;
}
@Override
public void run() {
while (isRunning.get()) {
try {
Looper.loop();
} catch (final Throwable e) {
e.printStackTrace();
if (isRunning.get()) {
MAIN_HANDLER.post(new Runnable(){
@Override
public void run() {
Toast.makeText(mContext, e.toString(), Toast.LENGTH_LONG).show();
}
});
} else {
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
} else {
throw new RuntimeException(e);
}
}
}
}
}
}
private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler {
private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss");
private final Context mContext;
private final File mCrashDir;
public UncaughtExceptionHandlerImpl(Context context, String crashDir) {
this.mContext = context;
this.mCrashDir = TextUtils.isEmpty(crashDir) ? new File(mContext.getExternalCacheDir(), "crash") : new File(crashDir);
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
try {
String log = buildLog(throwable);
writeLog(log);
try {
Intent intent = new Intent(mContext, CrashActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_TEXT, log);
mContext.startActivity(intent);
} catch (Throwable e) {
e.printStackTrace();
writeLog(e.toString());
}
throwable.printStackTrace();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
} catch (Throwable e) {
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
}
}
private String buildLog(Throwable throwable) {
String time = DATE_FORMAT.format(new Date());
String versionName = "unknown";
long versionCode = 0;
try {
PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
versionName = packageInfo.versionName;
versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode() : packageInfo.versionCode;
} catch (Throwable ignored) {}
LinkedHashMap<String, String> head = new LinkedHashMap<String, String>();
head.put("Time Of Crash", time);
head.put("Device", String.format("%s, %s", Build.MANUFACTURER, Build.MODEL));
head.put("Android Version", String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT));
head.put("App Version", String.format("%s (%d)", versionName, versionCode));
head.put("Kernel", getKernel());
head.put("Support Abis", Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS != null ? Arrays.toString(Build.SUPPORTED_ABIS): "unknown");
head.put("Fingerprint", Build.FINGERPRINT);
StringBuilder builder = new StringBuilder();
for (String key : head.keySet()) {
if (builder.length() != 0) builder.append("\n");
builder.append(key);
builder.append(" : ");
builder.append(head.get(key));
}
builder.append("\n\n");
builder.append(Log.getStackTraceString(throwable));
return builder.toString();
}
private void writeLog(String log) {
String time = DATE_FORMAT.format(new Date());
File file = new File(mCrashDir, "crash_" + time + ".txt");
try {
write(file, log.getBytes("UTF-8"));
} catch (Throwable e) {
e.printStackTrace();
}
}
private static String getKernel() {
try {
return GlobalApplication.toString(new FileInputStream("/proc/version")).trim();
} catch (Throwable e) {
return e.getMessage();
}
}
}
}
public static final class CrashActivity extends Activity {
private String mLog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(android.R.style.Theme_DeviceDefault);
setTitle("App Crash");
mLog = getIntent().getStringExtra(Intent.EXTRA_TEXT);
ScrollView contentView = new ScrollView(this);
contentView.setFillViewport(true);
HorizontalScrollView horizontalScrollView = new HorizontalScrollView(this);
TextView textView = new TextView(this);
int padding = dp2px(16);
textView.setPadding(padding, padding, padding, padding);
textView.setText(mLog);
textView.setTextIsSelectable(true);
textView.setTypeface(Typeface.DEFAULT);
textView.setLinksClickable(true);
horizontalScrollView.addView(textView);
contentView.addView(horizontalScrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
setContentView(contentView);
}
private void restart() {
Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
finish();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
private static int dp2px(float dpValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, android.R.id.copy, 0, android.R.string.copy)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.copy:
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
restart();
}
}
}

View File

@@ -0,0 +1,15 @@
package cc.winboll.studio.androiddemo;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@@ -0,0 +1,16 @@
<?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:gravity="center_vertical|center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android Demo"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#009688</color>
<color name="colorPrimaryDark">#00796B</color>
<color name="colorAccent">#FF9800</color>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Android Demo</string>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
</style>
</resources>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<!-- Put flavor specific code here -->
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Put flavor specific strings here -->
</resources>

1
androidxdemo/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

76
androidxdemo/build.gradle Normal file
View File

@@ -0,0 +1,76 @@
apply plugin: 'com.android.application'
apply from: '../.winboll/winboll_app_build.gradle'
apply from: '../.winboll/winboll_lint_build.gradle'
def genVersionName(def versionName){
// 检查编译标志位配置
assert (winbollBuildProps['stageCount'] != null)
assert (winbollBuildProps['baseVersion'] != null)
// 保存基础版本号
winbollBuildProps.setProperty("baseVersion", "${versionName}");
//保存编译标志配置
FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
fos.close();
// 返回编译版本号
return "${versionName}." + winbollBuildProps['stageCount']
}
android {
productFlavors {
beta {
}
stage {
}
}
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "cc.winboll.studio.androidxdemo"
minSdkVersion 26
targetSdkVersion 29
versionCode 1
// versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "1.0"
if(true) {
versionName = genVersionName("${versionName}")
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
// SSH
implementation 'com.jcraft:jsch:0.1.55'
// Html 解析
implementation 'org.jsoup:jsoup:1.13.1'
// 二维码类库
implementation 'com.google.zxing:core:3.4.1'
implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
// 应用介绍页类库
implementation 'io.github.medyo:android-about-page:2.0.0'
// 吐司类库
implementation 'com.github.getActivity:ToastUtils:10.5'
// 网络连接类库
implementation 'com.squareup.okhttp3:okhttp:4.4.1'
// Android 类库
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.viewpager:viewpager:1.0.0'
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
implementation 'androidx.fragment:fragment:1.1.0'
implementation 'com.google.android.material:material:1.4.0'
}

View File

@@ -0,0 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Tue Mar 11 18:25:43 GMT 2025
stageCount=0
libraryProject=
baseVersion=1.0
publishVersion=1.0.0
buildCount=4
baseBetaVersion=1.0.1

21
androidxdemo/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<!-- Put flavor specific code here -->
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">AndroidX Demo +</string>
</resources>

View File

@@ -0,0 +1,37 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.androidxdemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:resizeableActivity="true"
android:name=".GlobalApplication">
<activity
android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="android.max_aspect"
android:value="4.0"/>
<activity android:name=".GlobalApplication$CrashActivity"/>
</application>
</manifest>

View File

@@ -0,0 +1,334 @@
package cc.winboll.studio.androidxdemo;
import android.app.Activity;
import android.app.Application;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class GlobalApplication extends Application {
private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
@Override
public void onCreate() {
super.onCreate();
CrashHandler.getInstance().registerGlobal(this);
CrashHandler.getInstance().registerPart(this);
}
public static void write(InputStream input, OutputStream output) throws IOException {
byte[] buf = new byte[1024 * 8];
int len;
while ((len = input.read(buf)) != -1) {
output.write(buf, 0, len);
}
}
public static void write(File file, byte[] data) throws IOException {
File parent = file.getParentFile();
if (parent != null && !parent.exists()) parent.mkdirs();
ByteArrayInputStream input = new ByteArrayInputStream(data);
FileOutputStream output = new FileOutputStream(file);
try {
write(input, output);
} finally {
closeIO(input, output);
}
}
public static String toString(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
write(input, output);
try {
return output.toString("UTF-8");
} finally {
closeIO(input, output);
}
}
public static void closeIO(Closeable... closeables) {
for (Closeable closeable : closeables) {
try {
if (closeable != null) closeable.close();
} catch (IOException ignored) {}
}
}
public static class CrashHandler {
public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
private static CrashHandler sInstance;
private PartCrashHandler mPartCrashHandler;
public static CrashHandler getInstance() {
if (sInstance == null) {
sInstance = new CrashHandler();
}
return sInstance;
}
public void registerGlobal(Context context) {
registerGlobal(context, null);
}
public void registerGlobal(Context context, String crashDir) {
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl(context.getApplicationContext(), crashDir));
}
public void unregister() {
Thread.setDefaultUncaughtExceptionHandler(DEFAULT_UNCAUGHT_EXCEPTION_HANDLER);
}
public void registerPart(Context context) {
unregisterPart(context);
mPartCrashHandler = new PartCrashHandler(context.getApplicationContext());
MAIN_HANDLER.postAtFrontOfQueue(mPartCrashHandler);
}
public void unregisterPart(Context context) {
if (mPartCrashHandler != null) {
mPartCrashHandler.isRunning.set(false);
mPartCrashHandler = null;
}
}
private static class PartCrashHandler implements Runnable {
private final Context mContext;
public AtomicBoolean isRunning = new AtomicBoolean(true);
public PartCrashHandler(Context context) {
this.mContext = context;
}
@Override
public void run() {
while (isRunning.get()) {
try {
Looper.loop();
} catch (final Throwable e) {
e.printStackTrace();
if (isRunning.get()) {
MAIN_HANDLER.post(new Runnable(){
@Override
public void run() {
Toast.makeText(mContext, e.toString(), Toast.LENGTH_LONG).show();
}
});
} else {
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
} else {
throw new RuntimeException(e);
}
}
}
}
}
}
private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler {
private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss");
private final Context mContext;
private final File mCrashDir;
public UncaughtExceptionHandlerImpl(Context context, String crashDir) {
this.mContext = context;
this.mCrashDir = TextUtils.isEmpty(crashDir) ? new File(mContext.getExternalCacheDir(), "crash") : new File(crashDir);
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
try {
String log = buildLog(throwable);
writeLog(log);
try {
Intent intent = new Intent(mContext, CrashActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_TEXT, log);
mContext.startActivity(intent);
} catch (Throwable e) {
e.printStackTrace();
writeLog(e.toString());
}
throwable.printStackTrace();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
} catch (Throwable e) {
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
}
}
private String buildLog(Throwable throwable) {
String time = DATE_FORMAT.format(new Date());
String versionName = "unknown";
long versionCode = 0;
try {
PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
versionName = packageInfo.versionName;
versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode() : packageInfo.versionCode;
} catch (Throwable ignored) {}
LinkedHashMap<String, String> head = new LinkedHashMap<String, String>();
head.put("Time Of Crash", time);
head.put("Device", String.format("%s, %s", Build.MANUFACTURER, Build.MODEL));
head.put("Android Version", String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT));
head.put("App Version", String.format("%s (%d)", versionName, versionCode));
head.put("Kernel", getKernel());
head.put("Support Abis", Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS != null ? Arrays.toString(Build.SUPPORTED_ABIS): "unknown");
head.put("Fingerprint", Build.FINGERPRINT);
StringBuilder builder = new StringBuilder();
for (String key : head.keySet()) {
if (builder.length() != 0) builder.append("\n");
builder.append(key);
builder.append(" : ");
builder.append(head.get(key));
}
builder.append("\n\n");
builder.append(Log.getStackTraceString(throwable));
return builder.toString();
}
private void writeLog(String log) {
String time = DATE_FORMAT.format(new Date());
File file = new File(mCrashDir, "crash_" + time + ".txt");
try {
write(file, log.getBytes("UTF-8"));
} catch (Throwable e) {
e.printStackTrace();
}
}
private static String getKernel() {
try {
return GlobalApplication.toString(new FileInputStream("/proc/version")).trim();
} catch (Throwable e) {
return e.getMessage();
}
}
}
}
public static final class CrashActivity extends Activity {
private String mLog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(android.R.style.Theme_DeviceDefault);
setTitle("App Crash");
mLog = getIntent().getStringExtra(Intent.EXTRA_TEXT);
ScrollView contentView = new ScrollView(this);
contentView.setFillViewport(true);
HorizontalScrollView horizontalScrollView = new HorizontalScrollView(this);
TextView textView = new TextView(this);
int padding = dp2px(16);
textView.setPadding(padding, padding, padding, padding);
textView.setText(mLog);
textView.setTextIsSelectable(true);
textView.setTypeface(Typeface.DEFAULT);
textView.setLinksClickable(true);
horizontalScrollView.addView(textView);
contentView.addView(horizontalScrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
setContentView(contentView);
}
private void restart() {
Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
finish();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
private static int dp2px(float dpValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, android.R.id.copy, 0, android.R.string.copy)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.copy:
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
restart();
}
}
}

View File

@@ -0,0 +1,19 @@
package cc.winboll.studio.androidxdemo;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
}

View File

@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>

View File

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

View File

@@ -0,0 +1,38 @@
<?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">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:gravity="center_vertical|center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AndroidX Demo"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#009688</color>
<color name="colorPrimaryDark">#00796B</color>
<color name="colorAccent">#FF9800</color>
</resources>

View File

@@ -0,0 +1,4 @@
<resources>
<string name="app_name">AndroidX Demo</string>
</resources>

View File

@@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<!-- Put flavor specific code here -->
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Put flavor specific strings here -->
</resources>

View File

@@ -19,17 +19,17 @@ def genVersionName(def versionName){
android {
compileSdkVersion 32
buildToolsVersion "33.0.3"
buildToolsVersion "32.0.0"
defaultConfig {
applicationId "cc.winboll.studio.appbase"
minSdkVersion 21
targetSdkVersion 30
minSdkVersion 26
targetSdkVersion 29
versionCode 1
// versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "1.1"
versionName "15.0"
if(true) {
versionName = genVersionName("${versionName}")
}
@@ -41,14 +41,9 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
dependencies {
api project(':libappbase')
api fileTree(dir: 'libs', include: ['*.jar'])
api fileTree(dir: 'libs', include: ['*.jar'])
}

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Thu Jan 23 14:42:39 HKT 2025
stageCount=3
#Mon Mar 24 14:06:10 HKT 2025
stageCount=10
libraryProject=libappbase
baseVersion=1.1
publishVersion=1.1.2
baseVersion=15.0
publishVersion=15.0.9
buildCount=0
baseBetaVersion=1.1.3
baseBetaVersion=15.0.10

View File

@@ -7,7 +7,7 @@
android:name=".App"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:theme="@style/MyAPPBaseTheme"
android:resizeableActivity="true">
<activity
@@ -21,16 +21,88 @@
<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=".GlobalApplication$CrashActivity"/>
<service
android:name=".MyTileService"
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">
<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">
<intent-filter>
<action android:name="cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener.ACTION_PRE"/>
<action android:name="cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener.ACTION_NEXT"/>
</intent-filter>
</receiver>
<meta-data
android:name="android.max_aspect"
android:value="4.0"/>
<activity android:name=".GlobalApplication$CrashActivity"/>
</application>
</manifest>

View File

@@ -5,12 +5,24 @@ package cc.winboll.studio.appbase;
* @Date 2025/01/05 09:54:42
* @Describe APPbase 应用类
*/
import cc.winboll.studio.GlobalApplication;
import cc.winboll.studio.libappbase.GlobalApplication;
import android.content.IntentFilter;
import cc.winboll.studio.libappbase.sos.SOSCenterServiceReceiver;
import cc.winboll.studio.libappbase.sos.SOS;
public class App extends GlobalApplication {
public static final String TAG = "App";
SOSCenterServiceReceiver mSOSCenterServiceReceiver;
@Override
public void onCreate() {
super.onCreate();
GlobalApplication.setIsDebuging(this, BuildConfig.DEBUG);
mSOSCenterServiceReceiver = new SOSCenterServiceReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(SOS.ACTION_SOS);
registerReceiver(mSOSCenterServiceReceiver, intentFilter);
}
}

View File

@@ -1,15 +1,143 @@
package cc.winboll.studio.appbase;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
package cc.winboll.studio.appbase;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
import android.widget.Toolbar;
import cc.winboll.studio.appbase.R;
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.GlobalApplication;
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;
public class MainActivity extends Activity {
public static final String TAG = "MainActivity";
LogView mLogView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ToastUtils.show("onCreate");
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.activitymainToolbar1);
setActionBar(toolbar);
CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
mLogView = findViewById(R.id.activitymainLogView1);
if (GlobalApplication.isDebuging()) {
mLogView.start();
ToastUtils.show("LogView start.");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Intent intentAPPWidget = new Intent(this, StatusWidget.class);
intentAPPWidget.setAction(StatusWidget.ACTION_STATUS_UPDATE);
sendBroadcast(intentAPPWidget);
}
@Override
protected void onResume() {
LogUtils.d(TAG, "onResume");
super.onResume();
mLogView.start();
}
public void onSwitchDebugMode(View view) {
GlobalApplication.setIsDebuging(this, ((CheckBox)view).isChecked());
}
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 onSartTestDemoService(View view) {
Intent intent = new Intent(this, TestDemoService.class);
intent.setAction(TestDemoService.ACTION_ENABLE);
startService(intent);
}
public void onStopTestDemoService(View view) {
Intent intent = new Intent(this, TestDemoService.class);
intent.setAction(TestDemoService.ACTION_DISABLE);
startService(intent);
Intent intentStop = new Intent(this, TestDemoService.class);
stopService(intentStop);
}
public void onStopTestDemoServiceNoSettings(View view) {
Intent intent = new Intent(this, TestDemoService.class);
stopService(intent);
}
public void onSartTestDemoBindService(View view) {
Intent intent = new Intent(this, TestDemoBindService.class);
intent.setAction(TestDemoBindService.ACTION_ENABLE);
startService(intent);
}
public void onStopTestDemoBindService(View view) {
Intent intent = new Intent(this, TestDemoBindService.class);
intent.setAction(TestDemoBindService.ACTION_DISABLE);
startService(intent);
Intent intentStop = new Intent(this, TestDemoBindService.class);
stopService(intentStop);
}
public void onStopTestDemoBindServiceNoSettings(View view) {
Intent intent = new Intent(this, TestDemoBindService.class);
stopService(intent);
}
}

View File

@@ -0,0 +1,79 @@
package cc.winboll.studio.appbase;
/**
* @Author ZhanGSKen@AliYun.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

@@ -0,0 +1,38 @@
package cc.winboll.studio.appbase.handlers;
/**
* @Author ZhanGSKen@AliYun.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

@@ -0,0 +1,67 @@
package cc.winboll.studio.appbase.models;
/**
* @Author ZhanGSKen@AliYun.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

@@ -0,0 +1,67 @@
package cc.winboll.studio.appbase.models;
/**
* @Author ZhanGSKen@AliYun.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

@@ -0,0 +1,68 @@
package cc.winboll.studio.appbase.models;
/**
* @Author ZhanGSKen@AliYun.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

@@ -0,0 +1,72 @@
package cc.winboll.studio.appbase.models;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/17 10:05:09
* @Describe APPSOSReportBean
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
public class WinBollNewsBean extends BaseBean {
public static final String TAG = "WinBollNewsBean";
protected String message;
public WinBollNewsBean() {
this.message = "";
}
public WinBollNewsBean(String message) {
this.message = message;
}
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
@Override
public String getName() {
return WinBollNewsBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("message").value(getMessage());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("message")) {
setMessage(jsonReader.nextString());
} else {
return false;
}
}
return true;
}
@Override
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (!initObjectsFromJsonReader(jsonReader, name)) {
jsonReader.skipValue();
}
}
// 结束 JSON 对象
jsonReader.endObject();
return this;
}
}

View File

@@ -0,0 +1,36 @@
package cc.winboll.studio.appbase.receivers;
/**
* @Author ZhanGSKen@AliYun.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

@@ -0,0 +1,117 @@
package cc.winboll.studio.appbase.receivers;
/**
* @Author ZhanGSKen@AliYun.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.models.WinBollNewsBean;
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.sos.APPModel;
import cc.winboll.studio.libappbase.sos.SOS;
import cc.winboll.studio.libappbase.sos.SOSObject;
import cc.winboll.studio.libappbase.sos.WinBoll;
import cc.winboll.studio.libappbase.utils.ToastUtils;
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(WinBoll.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 szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL);
LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel));
if (szAPPModel != null && !szAPPModel.equals("")) {
try {
APPModel bean = APPModel.parseStringToBean(szAPPModel, APPModel.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().bindAPPModelConnection(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

@@ -0,0 +1,138 @@
package cc.winboll.studio.appbase.services;
/**
* @Author ZhanGSKen@AliYun.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

@@ -0,0 +1,316 @@
package cc.winboll.studio.appbase.services;
/**
* @Author ZhanGSKen@AliYun.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.models.MainServiceBean;
import cc.winboll.studio.appbase.handlers.MainServiceHandler;
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 java.util.ArrayList;
import cc.winboll.studio.libappbase.sos.APPModel;
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 bindAPPModelConnection(APPModel 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);
APPModel appSOSBean = new APPModel(bean.getAppPackageName(), bean.getAppMainServiveName());
intentWidget.putExtra("APPSOSBean", appSOSBean.toString());
sendBroadcast(intentWidget);
}
public class APPConnection implements ServiceConnection {
ComponentName mComponentName;
boolean isBindToAPP(APPModel 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

@@ -0,0 +1,178 @@
package cc.winboll.studio.appbase.services;
/**
* @Author ZhanGSKen@AliYun.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.models.TestDemoBindServiceBean;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.sos.WinBoll;
import cc.winboll.studio.appbase.App;
import cc.winboll.studio.libappbase.sos.SOS;
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

@@ -0,0 +1,156 @@
package cc.winboll.studio.appbase.services;
/**
* @Author ZhanGSKen@AliYun.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;
import cc.winboll.studio.libappbase.sos.WinBoll;
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

@@ -0,0 +1,54 @@
package cc.winboll.studio.appbase.threads;
/**
* @Author ZhanGSKen@AliYun.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

@@ -0,0 +1,186 @@
package cc.winboll.studio.appbase.widgets;
/**
* @Author ZhanGSKen@AliYun.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.models.WinBollNewsBean;
import cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener;
import cc.winboll.studio.libappbase.AppUtils;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.sos.APPModel;
import cc.winboll.studio.libappbase.sos.WinBoll;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
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 szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL);
LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel));
if (szAPPModel != null && !szAPPModel.equals("")) {
try {
APPModel bean = APPModel.parseStringToBean(szAPPModel, APPModel.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,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#ff000000"
android:pathData="M6.5,20Q4.22,20 2.61,18.43 1,16.85 1,14.58 1,12.63 2.17,11.1 3.35,9.57 5.25,9.15 5.88,6.85 7.75,5.43 9.63,4 12,4 14.93,4 16.96,6.04 19,8.07 19,11 20.73,11.2 21.86,12.5 23,13.78 23,15.5 23,17.38 21.69,18.69 20.38,20 18.5,20Z"/>
</vector>

View File

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

View File

@@ -1,16 +1,201 @@
<?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:gravity="center">
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
<android.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello, WinBoll!"
android:onClick="onHello"/>
android:id="@+id/activitymainToolbar1"/>
<ScrollView
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: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="Hello, WinBoll!"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android版本10的代号是“Q”API级别是29。 Android 10开始谷歌不再公开使用甜品作为版本代号但内部仍保留了大量与“Q”相关的元素。Android 10本身并没有严格对应某个特定的Java版本但在开发Android 10应用时通常可以使用Java 8或更高版本。 Java 8为Android开发带来了诸如Lambda表达式、方法引用等新特性能提高开发效率和代码可读性与Android 10开发适配良好。Java 9及更高版本也可用于Android 10开发能使用一些新的语言特性和API但可能需要注意兼容性和配置问题。"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right|center_vertical">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Debug Mode"
android:layout_weight="1.0"
android:onClick="onSwitchDebugMode"
android:id="@+id/activitymainCheckBox1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test Application CrashReport"
android:textAllCaps="false"
android:onClick="onTestApplicationCrashReport"/>
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="400dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StartCenter"
android:textAllCaps="false"
android:onClick="onStartCenter"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StopCenter"
android:textAllCaps="false"
android:onClick="onStopCenter"/>
</LinearLayout>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SartTestDemoService"
android:textAllCaps="false"
android:onClick="onSartTestDemoService"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StopTestDemoService"
android:textAllCaps="false"
android:onClick="onStopTestDemoService"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StopTestDemoServiceNoSettings"
android:textAllCaps="false"
android:onClick="onStopTestDemoServiceNoSettings"/>
</LinearLayout>
</HorizontalScrollView>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SartTestDemoBindService"
android:textAllCaps="false"
android:onClick="onSartTestDemoBindService"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StopTestDemoBindService"
android:textAllCaps="false"
android:onClick="onStopTestDemoBindService"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="StopTestDemoBindServiceNoSettings"
android:textAllCaps="false"
android:onClick="onStopTestDemoBindServiceNoSettings"/>
</LinearLayout>
</HorizontalScrollView>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TestStopMainServiceWithoutSettingEnable"
android:textAllCaps="false"
android:onClick="onTestStopMainServiceWithoutSettingEnable"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TestUseComponentStartService"
android:textAllCaps="false"
android:onClick="onTestUseComponentStartService"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TestDemoServiceSOS"
android:textAllCaps="false"
android:onClick="onTestDemoServiceSOS"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
</LinearLayout>
</ScrollView>
<cc.winboll.studio.libappbase.LogView
android:layout_height="300dp"
android:layout_width="match_parent"
android:id="@+id/activitymainLogView1"/>
</LinearLayout>

View File

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

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#009688</color>
<color name="colorPrimaryDark">#00796B</color>
<color name="colorAccent">#FF9800</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#FF00B322</color>
<color name="colorPrimaryDark">#FF005C12</color>
<color name="colorAccent">#FF8DFFA2</color>
<color name="colorText">#FFFFFB8D</color>
</resources>

View File

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

View File

@@ -1,5 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyAPPBaseTheme" parent="APPBaseTheme">
<item name="attrColorPrimary">@color/colorPrimary</item>
<item name="themeGlobalCrashActivity">@style/MyGlobalCrashActivityTheme</item>
</style>
<style name="MyGlobalCrashActivityTheme" parent="GlobalCrashActivityTheme">
<item name="colorTittle">#FFFFFFFF</item>
<item name="colorTittleBackgound">#FF00A4B3</item>
<item name="colorText">#FFFFFFFF</item>
<item name="colorTextBackgound">#FF000000</item>
</style>
</resources>

View File

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

View File

@@ -1,8 +1,21 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
// Nexus Maven 库地址
// "WinBoll Release"
maven { url "https://nexus.winboll.cc/repository/maven-public/" }
// "WinBoll Snapshot"
maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" }
maven { url 'https://maven.aliyun.com/repository/public/' }
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
maven { url 'https://dl.bintray.com/ppartisan/maven/' }
maven { url "https://clojars.org/repo/" }
maven { url "https://jitpack.io" }
mavenCentral()
google()
mavenLocal()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.1'
@@ -69,6 +82,14 @@ allprojects {
}
}
}
subprojects {
tasks.withType(JavaCompile) {
options.compilerArgs << "-parameters"
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
}
task clean(type: Delete) {

40
contacts/README.md Normal file
View File

@@ -0,0 +1,40 @@
# Contacts
源码参考自:
https://github.com/aJIEw/PhoneCallApp.git
#### 介绍
通讯录与拨号
#### 软件架构
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
#### Gradle 编译说明
调试版编译命令 gradle assembleBetaDebug
阶段版编译命令 gradle assembleStageRelease
#### 使用说明
在安卓系统中需要设置两个权限允许。
1.自启动权限允许。
2.省电策略-无限制权限允许。
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码 : ZhanGSKen(ZhanGSKen@AliYun.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

@@ -0,0 +1 @@

71
contacts/build.gradle Normal file
View File

@@ -0,0 +1,71 @@
apply plugin: 'com.android.application'
apply from: '../.winboll/winboll_app_build.gradle'
apply from: '../.winboll/winboll_lint_build.gradle'
def genVersionName(def versionName){
// 检查编译标志位配置
assert (winbollBuildProps['stageCount'] != null)
assert (winbollBuildProps['baseVersion'] != null)
// 保存基础版本号
winbollBuildProps.setProperty("baseVersion", "${versionName}");
//保存编译标志配置
FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
fos.close();
// 返回编译版本号
return "${versionName}." + winbollBuildProps['stageCount']
}
android {
compileSdkVersion 32
buildToolsVersion "33.0.3"
defaultConfig {
applicationId "cc.winboll.studio.contacts"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
// versionName 更新后需要手动设置
// 项目模块目录的 build.gradle 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "1.0"
if(true) {
versionName = genVersionName("${versionName}")
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
dependencies {
// 二维码使用的类库
api 'com.google.zxing:core:3.4.1'
api 'com.journeyapps:zxing-android-embedded:3.6.0'
api 'io.github.medyo:android-about-page:2.0.0'
api 'com.github.getActivity:ToastUtils:10.5'
api 'com.jcraft:jsch:0.1.55'
api 'org.jsoup:jsoup:1.13.1'
api 'com.squareup.okhttp3:okhttp:4.4.1'
api 'androidx.appcompat:appcompat:1.1.0'
api 'androidx.viewpager:viewpager:1.0.0'
api 'androidx.fragment:fragment:1.1.0'
api 'com.google.android.material:material:1.4.0'
api 'cc.winboll.studio:libapputils:9.3.2'
api 'cc.winboll.studio:libappbase:1.5.6'
api fileTree(dir: 'libs', include: ['*.jar'])
}

View File

@@ -0,0 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Tue Feb 25 00:17:29 HKT 2025
stageCount=2
libraryProject=
baseVersion=1.0
publishVersion=1.0.1
buildCount=0
baseBetaVersion=1.0.2

17
contacts/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application
tools:replace="android:icon"
android:icon="@drawable/ic_winbollbeta">
<!-- Put flavor specific code here -->
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Contacts+</string>
</resources>

View File

@@ -0,0 +1,187 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.contacts">
<!-- BIND_AUTOFILL_SERVICE -->
<uses-permission android:name="android.permission.BIND_AUTOFILL_SERVICE"/>
<!-- 拨打电话 -->
<uses-permission android:name="android.permission.CALL_PHONE"/>
<!-- 读取手机状态和身份 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<!-- 修改系统设置 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<!-- 重新设置外拨电话的路径 -->
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<!-- 读取联系人 -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<!-- 修改您的通讯录 -->
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<!-- 此应用可显示在其他应用上方 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<!-- 更改您的音频设置 -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<application
android:name=".App"
android:allowBackup="true"
android:icon="@drawable/ic_winboll"
android:label="@string/app_name"
android:theme="@style/MyAppTheme"
android:supportsRtl="true">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".activities.CallActivity"
android:label="CallActivity"
android:launchMode="singleTask"
android:exported="true">
</activity>
<activity
android:name=".phonecallui.PhoneCallActivity"
android:launchMode="singleInstance"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.DIAL"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="tel"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DIAL"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name="cc.winboll.studio.contacts.activities.SettingsActivity"/>
<service
android:name="cc.winboll.studio.contacts.services.MainService"
android:exported="true"/>
<service android:name="cc.winboll.studio.contacts.services.AssistantService"/>
<service
android:name=".phonecallui.PhoneCallService"
android:permission="android.permission.BIND_INCALL_SERVICE"
android:exported="false">
<meta-data
android:name="android.telecom.IN_CALL_SERVICE_UI"
android:value="true"/>
<intent-filter>
<action android:name="android.telecom.InCallService"/>
</intent-filter>
</service>
<service
android:name=".listenphonecall.CallListenerService"
android:enabled="true"
android:exported="false">
<intent-filter android:priority="1000">
<action android:name=".service.CallShowService"/>
</intent-filter>
</service>
<receiver android:name=".receivers.MainReceiver">
<intent-filter>
<action android:name="cc.winboll.studio.contacts.receivers.MainReceiver"/>
</intent-filter>
</receiver>
<receiver
android:name=".widgets.APPStatusWidget"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_ACTIVE"/>
<action android:name="cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_NOACTIVE"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_provider_info"/>
</receiver>
<receiver android:name=".widgets.APPStatusWidgetClickListener">
<intent-filter>
<action android:name="cc.winboll.studio.contacts.widgets.APPStatusWidgetClickListener.ACTION_APPICON_CLICK"/>
</intent-filter>
</receiver>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/studio_provider"/>
</provider>
</application>
</manifest>

View File

@@ -0,0 +1,59 @@
package cc.winboll.studio.contacts;
import android.app.Activity;
import java.util.ArrayList;
import java.util.List;
public class ActivityStack {
private static final ActivityStack INSTANCE = new ActivityStack();
private List<Activity> activities = new ArrayList<>();
public static ActivityStack getInstance() {
return INSTANCE;
}
public void addActivity(Activity activity) {
activities.add(activity);
}
public Activity getTopActivity() {
if (activities.isEmpty()) {
return null;
}
return activities.get(activities.size() - 1);
}
public void finishTopActivity() {
if (!activities.isEmpty()) {
activities.remove(activities.size() - 1).finish();
}
}
public void finishActivity(Activity activity) {
if (activity != null) {
activities.remove(activity);
activity.finish();
}
}
public void finishActivity(Class activityClass) {
for (Activity activity : activities) {
if (activity.getClass().equals(activityClass)) {
finishActivity(activity);
}
}
}
public void finishAllActivity() {
if (!activities.isEmpty()) {
for (Activity activity : activities) {
activity.finish();
activities.remove(activity);
}
}
}
}

View File

@@ -0,0 +1,28 @@
package cc.winboll.studio.contacts;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/12/08 15:10:51
* @Describe 全局应用类
*/
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libapputils.app.WinBollActivityManager;
public class App extends GlobalApplication {
public static final String TAG = "App";
@Override
public void onCreate() {
// 必须在调用基类前设置应用调试标志,
// 这样可以预先设置日志与数据的存储根目录。
setIsDebuging(this, BuildConfig.DEBUG);
super.onCreate();
// 设置 WinBoll 应用 UI 类型
WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Aplication);
LogUtils.d(TAG, "onCreate");
}
}

View File

@@ -0,0 +1,406 @@
package cc.winboll.studio.contacts;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.role.RoleManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.telecom.TelecomManager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.viewpager.widget.ViewPager;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.activities.CallActivity;
import cc.winboll.studio.contacts.adapters.MyPagerAdapter;
import cc.winboll.studio.contacts.beans.MainServiceBean;
import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libapputils.app.IWinBollActivity;
import cc.winboll.studio.libapputils.app.WinBollActivityManager;
import cc.winboll.studio.libapputils.bean.APPInfo;
import cc.winboll.studio.libapputils.view.YesNoAlertDialog;
import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
import com.google.android.material.tabs.TabLayout;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import android.content.DialogInterface;
import cc.winboll.studio.contacts.activities.SettingsActivity;
final public class MainActivity extends AppCompatActivity implements IWinBollActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
public static final String TAG = "MainActivity";
public static final int REQUEST_HOME_ACTIVITY = 0;
public static final int REQUEST_ABOUT_ACTIVITY = 1;
public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoll.ACTION_SOS";
LogView mLogView;
Toolbar mToolbar;
CheckBox cbMainService;
MainServiceBean mMainServiceBean;
ViewPager viewPager;
private List<View> views; //用来存放放进ViewPager里面的布局
//实例化存储imageView导航原点的集合
ImageView[] imageViews;
//MyPagerAdapter adapter;//适配器
MyPagerAdapter pagerAdapter;
LinearLayout linearLayout;//下标所在在LinearLayout布局里
int currentPoint = 0;//当前被选中中页面的下标
private static final int DIALER_REQUEST_CODE = 1;
@Override
public AppCompatActivity 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
protected void onCreate(Bundle savedInstanceState) {
// 接收并处理 Intent 数据,函数 Intent 处理接收就直接返回
//if (prosessIntents(getIntent())) return;
// 以下正常创建主窗口
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化工具栏
mToolbar = findViewById(R.id.activitymainToolbar1);
setSupportActionBar(mToolbar);
if (isEnableDisplayHomeAsUp()) {
// 显示后退按钮
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
getSupportActionBar().setSubtitle(getTag());
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);
// }
// }
// });
MainService.startMainService(MainActivity.this);
}
//初始化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() {
ViewPager viewPager = findViewById(R.id.activitymainViewPager1);
LayoutInflater inflater = LayoutInflater.from(getActivity());
View view1 = inflater.inflate(R.layout.fragment_call, 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在“手指离开屏幕”的状态。*/
@Override
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) {
}
/* 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);//直接跳转到某一个页面的情况
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
//setSubTitle("");
}
@Override
protected void onDestroy() {
super.onDestroy();
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 Toolbar initToolBar() {
return findViewById(R.id.activitymainToolbar1);
}
@Override
public boolean isAddWinBollToolBar() {
return true;
}
@Override
public boolean isEnableDisplayHomeAsUp() {
return false;
}
@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_call) {
Intent intent = new Intent(this, CallActivity.class);
startActivity(intent);
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
} else if (item.getItemId() == R.id.item_settings) {
Intent intent = new Intent(this, SettingsActivity.class);
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) {
TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);
if (manger != null && manger.getDefaultDialerPackage() != null) {
return manger.getDefaultDialerPackage().equals(getPackageName());
}
}
return false;
}
public boolean isServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
if (manager == null) return false;
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
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) + " 已成为默认电话应用",
Toast.LENGTH_SHORT).show();
}
}
}
}

View File

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

View File

@@ -0,0 +1,118 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 17:15:46
* @Describe 拨号窗口
*/
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import cc.winboll.studio.contacts.MainActivity;
import cc.winboll.studio.contacts.R;
public class CallActivity extends AppCompatActivity {
public static final String TAG = "CallActivity";
private static final int REQUEST_CALL_PHONE = 1;
private EditText phoneNumberEditText;
private TextView callStatusTextView;
private TelephonyManager telephonyManager;
private MyPhoneStateListener phoneStateListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
setContentView(R.layout.activity_call);
phoneNumberEditText = findViewById(R.id.phone_number);
Button dialButton = findViewById(R.id.dial_button);
callStatusTextView = findViewById(R.id.call_status);
dialButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String phoneNumber = phoneNumberEditText.getText().toString().trim();
if (!phoneNumber.isEmpty()) {
if (ContextCompat.checkSelfPermission(CallActivity.this, Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(CallActivity.this,
new String[]{Manifest.permission.CALL_PHONE},
REQUEST_CALL_PHONE);
} else {
dialPhoneNumber(phoneNumber);
}
} else {
Toast.makeText(CallActivity.this, "请输入电话号码", Toast.LENGTH_SHORT).show();
}
}
});
// 初始化TelephonyManager和PhoneStateListener
telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
phoneStateListener = new MyPhoneStateListener();
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CALL_PHONE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
String phoneNumber = phoneNumberEditText.getText().toString().trim();
dialPhoneNumber(phoneNumber);
} else {
Toast.makeText(this, "未授予拨打电话权限", Toast.LENGTH_SHORT).show();
}
}
}
private void dialPhoneNumber(String phoneNumber) {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
startActivity(intent);
}
private class MyPhoneStateListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
callStatusTextView.setText("电话已挂断");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
callStatusTextView.setText("正在通话中");
break;
case TelephonyManager.CALL_STATE_RINGING:
callStatusTextView.setText("来电: " + incomingNumber);
break;
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 取消监听
if (telephonyManager != null) {
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
}
}
}

View File

@@ -0,0 +1,40 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 20:18:26
*/
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.contacts.R;
public class DialerActivity extends AppCompatActivity {
public static final String TAG = "DialerActivity";
private EditText phoneNumberEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dialer);
phoneNumberEditText = findViewById(R.id.phone_number_edit_text);
Button dialButton = findViewById(R.id.dial_button);
dialButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String phoneNumber = phoneNumberEditText.getText().toString();
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber));
startActivity(intent);
}
});
}
}

View File

@@ -0,0 +1,143 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/21 05:37:42
*/
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.view.WindowManager;
import android.widget.Switch;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.beans.RingTongBean;
import cc.winboll.studio.libappbase.IWinBollActivity;
import cc.winboll.studio.libappbase.bean.APPInfo;
import com.hjq.toast.ToastUtils;
import java.lang.reflect.Field;
public class SettingsActivity extends AppCompatActivity implements IWinBollActivity {
public static final String TAG = "SettingsActivity";
Toolbar mToolbar;
Switch swSilent;
@Override
public APPInfo getAppInfo() {
return null;
}
@Override
public AppCompatActivity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
public Toolbar initToolBar() {
return findViewById(R.id.activitymainToolbar1);
}
@Override
public boolean isAddWinBollToolBar() {
return true;
}
@Override
public boolean isEnableDisplayHomeAsUp() {
return false;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
// 初始化工具栏
mToolbar = findViewById(R.id.activitymainToolbar1);
setSupportActionBar(mToolbar);
if (isEnableDisplayHomeAsUp()) {
// 显示后退按钮
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
getSupportActionBar().setSubtitle(getTag());
}
public void onDefaultPhone(View view) {
Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
startActivity(intent);
}
public void onCanDrawOverlays(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !Settings.canDrawOverlays(this)) {
// 请求 悬浮框 权限
askForDrawOverlay();
} else {
ToastUtils.show("悬浮窗已开启");
}
}
private void askForDrawOverlay() {
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("允许显示悬浮框")
.setMessage("为了使电话监听服务正常工作,请允许这项权限")
.setPositiveButton("去设置", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
openDrawOverlaySettings();
dialog.dismiss();
}
})
.setNegativeButton("稍后再说", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.create();
//noinspection ConstantConditions
alertDialog.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
alertDialog.show();
}
/**
* 跳转悬浮窗管理设置界面
*/
private void openDrawOverlaySettings() {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Android M 以上引导用户去系统设置中打开允许悬浮窗
// 使用反射是为了用尽可能少的代码保证在大部分机型上都可用
try {
Context context = this;
Class clazz = Settings.class;
Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION");
Intent intent = new Intent(field.get(null).toString());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse("package:" + context.getPackageName()));
context.startActivity(intent);
} catch (Exception e) {
Toast.makeText(this, "请在悬浮窗管理中打开权限", Toast.LENGTH_LONG).show();
}
}
}
}

View File

@@ -0,0 +1,14 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 14:20:38
* @Describe ImagePagerAdapter
*/
public class ImagePagerAdapter {
public static final String TAG = "ImagePagerAdapter";
}

View File

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

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