Compare commits
	
		
			301 Commits
		
	
	
		
			apputils-v
			...
			midiplayer
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 3c36ed589a | ||
|   | 9768103741 | ||
|   | 787b8f0d77 | ||
|   | 1d58126fd8 | ||
|   | 7b5a3d2d71 | ||
|   | a988a9d4f6 | ||
|   | 04a67e666b | ||
|   | 348edc8aaf | ||
|   | 5970ae33c8 | ||
|   | 91a44f48ef | ||
|   | c66e9a090b | ||
|   | 7a14b55247 | ||
|   | fab68f16c8 | ||
|   | 3a97c6135f | ||
|   | cdad017d8c | ||
|   | bf84382963 | ||
|   | 34601fc5b1 | ||
|   | a40dbcfb61 | ||
|   | 4d344b299b | ||
|   | 37b0867d34 | ||
|   | cdfbb082d2 | ||
|   | 7e476894a7 | ||
|   | 0e8ae2e020 | ||
|   | 48623a2805 | ||
|   | b505156211 | ||
|   | 91b30fb576 | ||
|   | ab3ac72d54 | ||
|   | 73285c8779 | ||
|   | fa338ec8c7 | ||
|   | 7a3a1f4bcd | ||
|   | ea65810e7d | ||
|   | ad991e3da2 | ||
|   | 7e263447c8 | ||
|   | 80201e8370 | ||
|   | ea0473606a | ||
|   | 870e9a94fb | ||
|   | 2421ecb943 | ||
|   | 687fff7216 | ||
|   | 50d4cd830b | ||
|   | 2079822c00 | ||
|   | 297c76f328 | ||
|   | 43b18ee662 | ||
|   | 3ec3a4cfc2 | ||
|   | 2c10a9f38c | ||
|   | e62888636e | ||
|   | 364980dd02 | ||
|   | 0e155e4f3a | ||
|   | 89febba5a9 | ||
|   | 653330f8e1 | ||
|   | 386c73effc | ||
|   | b385583c5a | ||
|   | e7a9be2f56 | ||
|   | b27f7b0080 | ||
|   | 0a440419ff | ||
|   | 9e189ed5ac | ||
|   | 0fb6aadc72 | ||
|   | 46f3315b02 | ||
|   | c0ff228845 | ||
|   | a0fe8f17a8 | ||
|   | 16bd40fc59 | ||
|   | ffaf683c54 | ||
|   | 26f5f8d3db | ||
|   | 917e25cdc8 | ||
|   | 707bed52c7 | ||
|   | 3795cf8631 | ||
|   | b374f3117a | ||
|   | d581cd9842 | ||
|   | cef50d087d | ||
|   | 6d9adc124e | ||
|   | 52f738b45b | ||
|   | 9ece6778b7 | ||
|   | b7f8b76ace | ||
|   | 326e5fa68e | ||
|   | e9c8f9029e | ||
|   | c00bfa1292 | ||
|   | 11ee4dcf27 | ||
|   | 8974e24dce | ||
|   | 89142e379c | ||
|   | dabc671c27 | ||
|   | ed849e92d1 | ||
|   | 42d2522927 | ||
|   | 6a52b2a8c3 | ||
|   | 02ed5bd5d1 | ||
|   | b685665d0c | ||
|   | 1b030a2855 | ||
|   | dd577f1765 | ||
|   | 0f8350600d | ||
|   | 1777ebb8dc | ||
|   | 2b99f707e7 | ||
|   | 068c94e749 | ||
|   | 0b3bc7e296 | ||
|   | 6ef747bcf8 | ||
|   | a8e843c388 | ||
|   | d3fd593cb0 | ||
|   | 3aec176b8b | ||
|   | 5960f76238 | ||
|   | 1a118da827 | ||
|   | f73cad6f3e | ||
|   | 7130ecf023 | ||
|   | 953c8f08cd | ||
|   | 87b7557f72 | ||
|   | 77f5a156f8 | ||
|   | b34ea40536 | ||
|   | d202a3443d | ||
|   | 8c532c885f | ||
|   | 5fc4cb5f74 | ||
|   | 2a590a99fb | ||
|   | c6ad707ca2 | ||
|   | ee13a43fb6 | ||
|   | 5fbe1d8f71 | ||
|   | 38fe941a8b | ||
|   | e13c8e7af0 | ||
|   | a4988b5b68 | ||
|   | 04df902b6b | ||
|   | 33c71ea868 | ||
|   | 5507126f6b | ||
|   | d381c29452 | ||
|   | ba861d910e | ||
|   | f5d9aafe43 | ||
|   | e80d7e7b03 | ||
|   | 6e0a833fde | ||
|   | c596ee5fa4 | ||
|   | 5e99f1278e | ||
|   | b7158d1ebd | ||
|   | 9b51250ebf | ||
|   | eb61eb7306 | ||
|   | 31ad66685c | ||
|   | beb561ad6a | ||
|   | 8869265d60 | ||
|   | 2739627aff | ||
|   | 58e0be9cf4 | ||
|   | 9e9402f84e | ||
|   | ec18330022 | ||
|   | 8bb80ef575 | ||
|   | c1e6e32809 | ||
|   | 3e7722e2c0 | ||
|   | a1707e73b5 | ||
|   | 9dcbaa0d75 | ||
|   | 23920a7ff1 | ||
|   | 17c373c490 | ||
|   | 5f7c94b349 | ||
|   | c2b739d345 | ||
|   | 67a05cd457 | ||
|   | 554ab758bf | ||
|   | 20e118cd34 | ||
|   | f370ae8ffb | ||
|   | c92c874ea1 | ||
|   | 90a6116c0a | ||
|   | 45208ecbb1 | ||
|   | c28d655fe3 | ||
|   | 4b5905f74e | ||
|   | 6bd01780ec | ||
|   | a6699262f8 | ||
|   | 07b5e66875 | ||
|   | 91f5cf9051 | ||
|   | ea2d38defc | ||
|   | e430b7abe4 | ||
|   | 945eadb617 | ||
|   | c5bffc5eef | ||
|   | 88597fe407 | ||
|   | 53f985533a | ||
|   | a3950f13ad | ||
|   | c878e9dc02 | ||
|   | f2f7cab330 | ||
|   | 0e3b9dc760 | ||
|   | 6c8b0dcfa5 | ||
|   | 7de8a4f084 | ||
|   | 219c6614be | ||
|   | 0f5bb020b9 | ||
|   | 7794ff80ec | ||
|   | 7463ad3352 | ||
|   | 69187e3ed0 | ||
|   | 753032efed | ||
|   | 2b4c43c9af | ||
|   | 711c98d556 | ||
|   | 202205588a | ||
|   | 42c4978b44 | ||
|   | 1a2b7b862d | ||
|   | 8730f434dd | ||
|   | eb253b374f | ||
|   | c4e88e9593 | ||
|   | 08d9d92ae4 | ||
|   | 74841c08dc | ||
|   | 945bacb825 | ||
|   | 0e464495fd | ||
|   | f8944490f8 | ||
|   | 733af004f6 | ||
|   | c03568e1f5 | ||
|   | a0575a5e8b | ||
|   | 0e57ce679e | ||
|   | f9211a8eb4 | ||
|   | 4c31ff9b54 | ||
|   | 8cf610962e | ||
|   | 3071d186ec | ||
|   | df10306059 | ||
|   | ccdb9c5abd | ||
|   | f27209ab87 | ||
|   | e8682ce410 | ||
|   | 2e4003dae0 | ||
|   | 198b0975ce | ||
|   | 24a578a9d2 | ||
|   | 46de24447f | ||
|   | 2a819e94e4 | ||
|   | 6635358ec5 | ||
|   | ac1c008035 | ||
|   | b124487cb1 | ||
|   | 9621d35f79 | ||
|   | 17de0832a6 | ||
|   | f53b222b7f | ||
|   | 0c0cde8406 | ||
|   | 46967065c0 | ||
|   | 8edbff5ac1 | ||
|   | 434f8a8549 | ||
|   | c04be60b13 | ||
|   | 641098f8fb | ||
|   | dba54ac4b2 | ||
|   | c6cd779889 | ||
|   | dfb1692a04 | ||
|   | c83c8f66b3 | ||
|   | cd7b5f38bf | ||
|   | 0c2e73b82e | ||
|   | 7b1838ff8e | ||
|   | 73ff3d1726 | ||
|   | a69572e216 | ||
|   | fa79c3f807 | ||
|   | fde4b275f7 | ||
|   | d66d9373ff | ||
|   | f32ed94e4e | ||
|   | 1320984829 | ||
|   | abf1e5ba42 | ||
|   | 1cd2f88038 | ||
|   | 3f6e583d68 | ||
|   | 271456bfcd | ||
|   | ee5458d82c | ||
|   | 3a83367f71 | ||
|   | 74b9350a6a | ||
|   | d2858f23f7 | ||
|   | 40a5b9c339 | ||
|   | fd79113572 | ||
|   | 9b911b583c | ||
|   | 37817c3e8c | ||
|   | 0b5402f5f3 | ||
|   | bea22e3853 | ||
|   | 7e2ad0c01d | ||
|   | 476ce02fc8 | ||
|   | bc697279ad | ||
|   | dee01f1179 | ||
|   | a500decc7a | ||
|   | 5099d00050 | ||
|   | 515d14e896 | ||
|   | f630e27ed8 | ||
|   | cd7ed01216 | ||
|   | bb24bbfbd1 | ||
|   | 2ba2f88510 | ||
|   | db3a3644a8 | ||
|   | 556bfa7024 | ||
|   | 4842a1ec30 | ||
|   | 89dac91cc6 | ||
|   | 3809c1bcab | ||
|   | b0388a2972 | ||
|   | bd5a1f18ce | ||
|   | 99798b4816 | ||
|   | f93b6047a8 | ||
|   | daa3f858a0 | ||
|   | 3fded32426 | ||
|   | 8f85006040 | ||
|   | e28b0bd75e | ||
|   | af1d6d3439 | ||
|   | 470d1ffa1f | ||
|   | 49ae869df1 | ||
|   | 77e98bafe4 | ||
|   | ff14d0c0c3 | ||
|   | 950be3a182 | ||
|   | 1f20fca9be | ||
|   | 8d29d11078 | ||
|   | 7534881f50 | ||
|   | ef992dcd7c | ||
|   | 71c1baa4ba | ||
|   | 9e149037db | ||
|   | 89df24f736 | ||
|   | 2118495bc8 | ||
|   | 1dd614bd68 | ||
|   | b793c74e81 | ||
|   | 8d1872a893 | ||
|   | 5c58ee34e7 | ||
|   | e530403af7 | ||
|   | 264ab802c5 | ||
|   | 7c1832dc05 | ||
|   | 9a0ee889ba | ||
|   | c40066ca4d | ||
|   | 5348d1ef6d | ||
|   | a5083cc52f | ||
|   | 6cce9c4d3f | ||
|   | df18c34976 | ||
|   | 22ca83b5b7 | ||
|   | 98233ce148 | ||
|   | b61c63c426 | ||
|   | f02dc215ca | ||
|   | 1c27d0ccdc | ||
|   | 803745d12e | ||
|   | a66be9cd37 | 
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,6 @@ | ||||
| [submodule "libjc/jcc/libs"] | ||||
| 	path = libjc/jcc/libs | ||||
| 	url = https://gitea.winboll.cc/Studio/APP_libjc_jcc_libs.git | ||||
| [submodule "keystore"] | ||||
| 	path = keystore | ||||
| 	url = https://gitea.winboll.cc/Studio/keystore.git | ||||
|   | ||||
| @@ -1,3 +0,0 @@ | ||||
| #!/bin/usr/bash | ||||
| ## Change Back To Beta KeyStore in keystore module. | ||||
| cd keystore;git reset --hard f5bc75ff45fcb8894b5bd3f49b91bdd8fe3c317e;cd .. | ||||
| @@ -1,3 +0,0 @@ | ||||
| #!/bin/usr/bash | ||||
| ## Change Back To StageMG KeyStore in keystore module. | ||||
| cd keystore;git reset --hard d22519b11253f85f495400b01b6373e9657defb4;cd .. | ||||
| @@ -113,10 +113,10 @@ if [[ $? -eq 0 ]]; then | ||||
|     # 如果Git已经提交了所有代码就执行标签和应用发布操作 | ||||
|  | ||||
|     # 预先询问是否添加工作流标签 | ||||
|     echo "Add Github Workflows Tag? (yes/No)" | ||||
| 	result=$(askAddWorkflowsTag) | ||||
| 	nAskAddWorkflowsTag=$? | ||||
| 	echo $result | ||||
|     #echo "Add Github Workflows Tag? (yes/No)" | ||||
| 	#result=$(askAddWorkflowsTag) | ||||
| 	#nAskAddWorkflowsTag=$? | ||||
| 	#echo $result | ||||
|  | ||||
|     # 发布应用 | ||||
| 	echo "Publishing WinBoLL APK ..." | ||||
| @@ -138,17 +138,17 @@ if [[ $? -eq 0 ]]; then | ||||
| 	fi | ||||
|      | ||||
|     # 添加 GitHub 工作流标签 | ||||
| 	if [[ $nAskAddWorkflowsTag -eq 1 ]]; then | ||||
| 	#if [[ $nAskAddWorkflowsTag -eq 1 ]]; then | ||||
| 	    # 如果用户选择添加工作流标签 | ||||
|     	result=$(addWorkflowsTag $1) | ||||
| 		if [[ $? -eq 0 ]]; then | ||||
| 		    echo $result | ||||
|     	#result=$(addWorkflowsTag $1) | ||||
| 		#if [[ $? -eq 0 ]]; then | ||||
| 		#    echo $result | ||||
| 		    # 工作流标签添加成功 | ||||
| 		else | ||||
| 			echo -e "${0}: addWorkflowsTag $1\n${result}\nAdd workflows tag cancel." | ||||
| 			exit 1 # addWorkflowsTag 异常 | ||||
| 		fi | ||||
| 	fi | ||||
| 		#else | ||||
| 			#echo -e "${0}: addWorkflowsTag $1\n${result}\nAdd workflows tag cancel." | ||||
| 			#exit 1 # addWorkflowsTag 异常 | ||||
| 		#fi | ||||
| 	#fi | ||||
| 	 | ||||
| 	## 清理更新描述文件内容 | ||||
| 	echo "" > $1/app_update_description.txt | ||||
|   | ||||
							
								
								
									
										49
									
								
								GenKeyStore/gen_debug_keystore.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								GenKeyStore/gen_debug_keystore.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| #!/bin/bash | ||||
| # 应用秘钥创建脚本 | ||||
| # Linux 命令行创建JKS秘钥,alias和keyAlias可配置,文件名含时间戳 | ||||
|  | ||||
| # 可配置参数(按需修改) | ||||
| ALIAS="WinBoLL.CC_Debug"  # 别名(与keyAlias一致) | ||||
| STORE_PASS="androiddebugkey" | ||||
| KEY_PASS="androiddebugkey" | ||||
| COUNTRY="CN"        # 国家代码 | ||||
|  | ||||
| # 获取当前时间戳 | ||||
| TIMESTAMP=$(date +%Y%m%d%H%M%S) | ||||
| FILENAME="${ALIAS}_${TIMESTAMP}.jks" | ||||
| STORENAME="${ALIAS}_${TIMESTAMP}.keystore" | ||||
|  | ||||
| # 生成JKS文件(alias与keyAlias同步) | ||||
| keytool -genkeypair \ | ||||
|   -alias "${ALIAS}" \ | ||||
|   -keyalg RSA \ | ||||
|   -keysize 2048 \ | ||||
|   -validity 1 \ | ||||
|   -keystore "${FILENAME}" \ | ||||
|   -dname "CN=WBFans, OU=Studio, O=WinBoLL, L=Shanwei, ST=Guangdong, C=${COUNTRY}" \ | ||||
|   -storepass "${STORE_PASS}" \ | ||||
|   -keypass "${KEY_PASS}" | ||||
|  | ||||
| # 写入配置文件 | ||||
| cat <<EOF > ${STORENAME} | ||||
| keyAlias=${ALIAS} | ||||
| keyPassword=${KEY_PASS} | ||||
| storeFile=../appkey.jks | ||||
| storePassword=${STORE_PASS} | ||||
| EOF | ||||
|  | ||||
| echo "已生成秘钥:${FILENAME}" | ||||
| echo "配置已写入 ${STORENAME}(keyAlias=${ALIAS})" | ||||
|  | ||||
| # 询问是否复制文件 | ||||
| read -p "是否需要将文件复制为 appkey.jks 和 appkey.keystore?(y/n): " CONFIRM | ||||
|  | ||||
| if [[ $CONFIRM =~ ^[Yy]$ ]]; then | ||||
|   # 复制 jks 文件为 appkey.jks | ||||
|   cp -v ${FILENAME} ../appkey.jks | ||||
|   # 复制 keystore 文件为 appkey.keystore | ||||
|   cp -v ${STORENAME} ../appkey.keystore | ||||
|   echo "文件复制完成" | ||||
| else | ||||
|   echo "已取消文件复制" | ||||
| fi | ||||
| @@ -114,9 +114,12 @@ | ||||
|  | ||||
| # 本项目要实际运用需要注意以下几个步骤: | ||||
| # 在项目根目录下: | ||||
| ## 1. 项目模块编译环境设置(必须),settings.gradle-demo 要复制为 settings.gradle,并取消相应项目模块的注释。 | ||||
| ## 2. 项目 Android SDK 编译环境设置(可选),local.properties-demo 要复制为 local.properties,并按需要设置 Android SDK 目录。 | ||||
| ## 3. 类库型模块编译环境设置(可选),winboll.properties-demo 要复制为 winboll.properties,并按需要设置 WinBoLL Maven 库登录用户信息。 | ||||
| ## ★. 项目模块编译环境设置(必须),settings.gradle-demo 要复制为 settings.gradle,并取消相应项目模块的注释。 | ||||
| ## ★. 项目模块编译环境设置(必须) 在根目录拷贝 gradle.properties-androidx-demo 或者 gradle.properties-android-demo 文件为 gradle.properties。 | ||||
| ## ★. 项目 Android SDK 编译环境设置(可选),local.properties-demo 要复制为 local.properties,并按需要设置 Android SDK 目录。 | ||||
| ## ★. 应用签名密钥 keystore 设置问题。一般调试编译只需用【Termux】cd 进 GenKeyStore 目录执行 $ bash gen_debug_keystore.sh 命令即可完成设置。 | ||||
| ## ☆. 应用 WiBoLL 签名密钥配置问题<非必须考虑>。设置时需要 clone 【keystore】模块源码并拷贝模块目录的 appkey.jks 与 appkey.keystore 到项目根目录即可。 | ||||
| ## ☆. 类库型模块编译环境设置(可选),winboll.properties-demo 要复制为 winboll.properties,并按需要设置 WinBoLL Maven 库登录用户信息。 | ||||
|  | ||||
|  | ||||
| # ☆类库型项目编译方法 | ||||
|   | ||||
							
								
								
									
										35
									
								
								aes/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								aes/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| # AES | ||||
|  | ||||
| #### 介绍 | ||||
| 安卓视图元素类库 | ||||
|  | ||||
| #### 软件架构 | ||||
| 适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。 | ||||
| 也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。 | ||||
|  | ||||
|  | ||||
| #### Gradle 编译说明 | ||||
| 调试版编译命令 :gradle assembleBetaDebug | ||||
| 阶段版编译命令 :bash .winboll/bashPublishAPKAddTag.sh aes | ||||
| 阶段版类库发布命令 :git pull &&bash .winboll/bashPublishLIBAddTag.sh libaes | ||||
|  | ||||
| #### 使用说明 | ||||
|  | ||||
| #### 参与贡献 | ||||
|  | ||||
| 1.  Fork 本仓库 | ||||
| 2.  新建 Feat_xxx 分支 | ||||
| 3.  提交代码 : ZhanGSKen(ZhanGSKen<zhangsken@188.com>) | ||||
| 4.  新建 Pull Request | ||||
|  | ||||
|  | ||||
| #### 特技 | ||||
|  | ||||
| 1.  使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md | ||||
| 2.  Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) | ||||
| 3.  你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 | ||||
| 4.  [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 | ||||
| 5.  Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) | ||||
| 6.  Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) | ||||
|  | ||||
| #### 参考文档 | ||||
| @@ -29,7 +29,7 @@ android { | ||||
|         // versionName 更新后需要手动设置  | ||||
|         // 项目模块目录的 build.gradle 文件的 stageCount=0 | ||||
|         // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" | ||||
|         versionName "15.8"  | ||||
|         versionName "15.9"  | ||||
|         if(true) { | ||||
|             versionName = genVersionName("${versionName}") | ||||
|         } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Tue May 13 11:22:39 HKT 2025 | ||||
| stageCount=1 | ||||
| #Sun Aug 31 23:40:17 HKT 2025 | ||||
| stageCount=4 | ||||
| libraryProject=libaes | ||||
| baseVersion=15.8 | ||||
| publishVersion=15.8.0 | ||||
| baseVersion=15.9 | ||||
| publishVersion=15.9.3 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.8.1 | ||||
| baseBetaVersion=15.9.4 | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.aes; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/24 23:52:29 | ||||
|  * @Describe AES应用介绍窗口 | ||||
|  */ | ||||
| @@ -79,11 +79,11 @@ public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity | ||||
|         appInfo.setAppName("AES"); | ||||
|         appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll); | ||||
|         appInfo.setAppDescription("AES Description"); | ||||
|         appInfo.setAppGitName("APP"); | ||||
|         appInfo.setAppGitName("APPBase"); | ||||
|         appInfo.setAppGitOwner("Studio"); | ||||
|         appInfo.setAppGitAPPBranch(szBranchName); | ||||
|         appInfo.setAppGitAPPSubProjectFolder(szBranchName); | ||||
|         appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=AES"); | ||||
|         appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=3&extra=page%3D1"); | ||||
|         appInfo.setAppAPKName("AES"); | ||||
|         appInfo.setAppAPKFolderName("AES"); | ||||
|         //appInfo.setIsAddDebugTools(false); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.aes; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/06/13 19:03:58 | ||||
|  * @Describe AES应用类 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.aes; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/06/13 19:05:52 | ||||
|  * @Describe 应用主窗口 | ||||
|  */ | ||||
|   | ||||
| @@ -0,0 +1 @@ | ||||
|  | ||||
|   | ||||
| @@ -67,6 +67,6 @@ dependencies { | ||||
|     // https://mvnrepository.com/artifact/com.android.support/recyclerview-v7 | ||||
|     api 'com.android.support:recyclerview-v7:28.0.0' | ||||
|      | ||||
|     api 'cc.winboll.studio:libapputils:15.8.1' | ||||
|     api 'cc.winboll.studio:libappbase:15.8.1' | ||||
|     api 'cc.winboll.studio:libapputils:15.8.5' | ||||
|     api 'cc.winboll.studio:libappbase:15.9.5' | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Mon May 19 21:45:28 GMT 2025 | ||||
| stageCount=0 | ||||
| #Sun Aug 31 05:11:26 CST 2025 | ||||
| stageCount=1 | ||||
| libraryProject= | ||||
| baseVersion=15.0 | ||||
| publishVersion=15.0.0 | ||||
| buildCount=25 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.0.1 | ||||
|   | ||||
| @@ -0,0 +1 @@ | ||||
|  | ||||
|   | ||||
| @@ -29,7 +29,7 @@ android { | ||||
|         // versionName 更新后需要手动设置  | ||||
|         // .winboll/winbollBuildProps.properties 文件的 stageCount=0 | ||||
|         // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" | ||||
|         versionName "15.0" | ||||
|         versionName "15.1" | ||||
|         if(true) { | ||||
|             versionName = genVersionName("${versionName}") | ||||
|         } | ||||
| @@ -67,7 +67,7 @@ dependencies { | ||||
|     //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0' | ||||
|     //api 'androidx.fragment:fragment:1.1.0' | ||||
|      | ||||
|     api 'cc.winboll.studio:libaes:15.8.0' | ||||
|     api 'cc.winboll.studio:libapputils:15.8.1' | ||||
|     api 'cc.winboll.studio:libappbase:15.8.1' | ||||
|     api 'cc.winboll.studio:libaes:15.9.3' | ||||
|     api 'cc.winboll.studio:libapputils:15.8.5' | ||||
|     api 'cc.winboll.studio:libappbase:15.9.5' | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Mon May 19 21:43:40 GMT 2025 | ||||
| stageCount=0 | ||||
| #Sun Aug 31 05:42:50 CST 2025 | ||||
| stageCount=2 | ||||
| libraryProject= | ||||
| baseVersion=15.0 | ||||
| publishVersion=15.0.0 | ||||
| buildCount=22 | ||||
| baseBetaVersion=15.0.1 | ||||
| baseVersion=15.1 | ||||
| publishVersion=15.1.1 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.1.2 | ||||
|   | ||||
| @@ -30,7 +30,7 @@ android { | ||||
|         // versionName 更新后需要手动设置  | ||||
|         // .winboll/winbollBuildProps.properties 文件的 stageCount=0 | ||||
|         // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" | ||||
|         versionName "15.8" | ||||
|         versionName "15.9" | ||||
|         if(true) { | ||||
|             versionName = genVersionName("${versionName}") | ||||
|         } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Sun Jun 01 15:41:55 HKT 2025 | ||||
| stageCount=3 | ||||
| #Sun Aug 31 23:37:38 HKT 2025 | ||||
| stageCount=6 | ||||
| libraryProject=libappbase | ||||
| baseVersion=15.8 | ||||
| publishVersion=15.8.2 | ||||
| baseVersion=15.9 | ||||
| publishVersion=15.9.5 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.8.3 | ||||
| baseBetaVersion=15.9.6 | ||||
|   | ||||
| @@ -9,7 +9,8 @@ | ||||
|         android:label="@string/app_name" | ||||
|         android:theme="@style/MyAPPBaseTheme" | ||||
|         android:resizeableActivity="true" | ||||
|         android:process=":App"> | ||||
|         android:process=":App" | ||||
|         android:networkSecurityConfig="@xml/network_security_config"> | ||||
|  | ||||
|         <activity | ||||
|             android:name=".MainActivity" | ||||
| @@ -39,7 +40,8 @@ | ||||
|             android:resizeableActivity="true" | ||||
|             android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"/> | ||||
|  | ||||
|         <activity android:name=".activities.New2Activity" | ||||
|         <activity | ||||
|             android:name=".activities.New2Activity" | ||||
|             android:label="New2Activity" | ||||
|             android:exported="true" | ||||
|             android:resizeableActivity="true" | ||||
| @@ -74,7 +76,8 @@ | ||||
|  | ||||
|         <service android:name=".services.AssistantService"/> | ||||
|  | ||||
|         <receiver android:name="cc.winboll.studio.appbase.receivers.MainReceiver" | ||||
|         <receiver | ||||
|             android:name="cc.winboll.studio.appbase.receivers.MainReceiver" | ||||
|             android:exported="true"> | ||||
|  | ||||
|             <intent-filter> | ||||
| @@ -105,7 +108,8 @@ | ||||
|  | ||||
|         </receiver> | ||||
|  | ||||
|         <receiver android:name=".receivers.APPNewsWidgetClickListener" | ||||
|         <receiver | ||||
|             android:name=".receivers.APPNewsWidgetClickListener" | ||||
|             android:exported="true"> | ||||
|  | ||||
|             <intent-filter> | ||||
| @@ -122,7 +126,6 @@ | ||||
|             android:name="android.max_aspect" | ||||
|             android:value="4.0"/> | ||||
|  | ||||
|  | ||||
|     </application> | ||||
|  | ||||
| </manifest> | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/01/05 09:54:42 | ||||
|  * @Describe APPbase 应用类 | ||||
|  */ | ||||
|   | ||||
| @@ -62,6 +62,11 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity { | ||||
|  | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         if(item.getItemId() == R.id.item_yun) { | ||||
|             GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.YunActivity.class); | ||||
|         } else if(item.getItemId() == R.id.item_logon) { | ||||
|             GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.LogonActivity.class); | ||||
|         } | ||||
|         // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 | ||||
|         return super.onOptionsItemSelected(item); | ||||
|     } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/13 19:30:10 | ||||
|  */ | ||||
| import android.content.Context; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.activities; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/25 11:46:40 | ||||
|  * @Describe 测试窗口2 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.activities; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/25 05:04:22 | ||||
|  */ | ||||
| import android.app.Activity; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.activities; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/05/10 09:48 | ||||
|  * @Describe WinBoLL 窗口基础类 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.handlers; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/14 03:51:40 | ||||
|  */ | ||||
| import android.os.Handler; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.models; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/13 07:06:13 | ||||
|  */ | ||||
| import android.util.JsonReader; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.models; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/07 12:47:22 | ||||
|  * @Describe TestServiceBean | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.models; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/07 12:49:21 | ||||
|  * @Describe TestDemoServiceBean | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.receivers; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/24 07:11:44 | ||||
|  */ | ||||
| import android.content.BroadcastReceiver; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.receivers; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/13 06:58:04 | ||||
|  * @Describe 主要广播接收器 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.services; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/14 03:38:31 | ||||
|  * @Describe 守护进程服务 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.services; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/13 06:56:41 | ||||
|  * @Describe 拨号主服务 | ||||
|  * 参考: | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.services; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/07 12:45:49 | ||||
|  * @Describe 启动时申请绑定到APPBase主服务的服务示例 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.services; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/07 12:39:24 | ||||
|  * @Describe 普通服务示例 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.appbase.threads; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/14 03:46:44 | ||||
|  */ | ||||
| import android.content.Context; | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package cc.winboll.studio.appbase.widgets; | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/15 14:41:25 | ||||
|  * @Describe TimeWidget | ||||
|  */ | ||||
|   | ||||
| @@ -5,6 +5,14 @@ | ||||
|         android:id="@+id/item_home" | ||||
|         android:title="HOME" | ||||
|         android:icon="@drawable/ic_winboll"/> | ||||
|     <item | ||||
|         android:id="@+id/item_yun" | ||||
|         android:title="YUN" | ||||
|         android:icon="@drawable/ic_winboll"/> | ||||
|     <item | ||||
|         android:id="@+id/item_logon" | ||||
|         android:title="Logon" | ||||
|         android:icon="@drawable/ic_winboll"/> | ||||
|     <item | ||||
|         android:id="@+id/item_log" | ||||
|         android:title="LOG" | ||||
|   | ||||
							
								
								
									
										12
									
								
								appbase/src/main/res/xml/network_security_config.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								appbase/src/main/res/xml/network_security_config.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <network-security-config> | ||||
|     <!-- 允许访问 winboll.cc 及其子域名(原配置) --> | ||||
|     <domain-config cleartextTrafficPermitted="true"> | ||||
|         <domain includeSubdomains="true">winboll.cc</domain> | ||||
|     </domain-config> | ||||
|  | ||||
|     <!-- **新增:允许访问 IP 地址 10.8.0.250** --> | ||||
|     <domain-config cleartextTrafficPermitted="true"> | ||||
|         <domain includeSubdomains="false">10.8.0.250</domain> <!-- 不包含子域名 --> | ||||
|     </domain-config> | ||||
| </network-security-config> | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Sun Jun 01 16:01:19 HKT 2025 | ||||
| stageCount=3 | ||||
| #Mon Sep 01 07:56:33 HKT 2025 | ||||
| stageCount=7 | ||||
| libraryProject=libapputils | ||||
| baseVersion=15.8 | ||||
| publishVersion=15.8.2 | ||||
| publishVersion=15.8.6 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.8.3 | ||||
| baseBetaVersion=15.8.7 | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.apputils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/12/08 15:10:51 | ||||
|  * @Describe 全局应用类 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.apputils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/01/03 11:02:49 | ||||
|  * @Describe 一个可以浏览随 APP 附带的 Html 文档的窗口 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.apputils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/01/18 10:32:21 | ||||
|  * @Describe 二维码扫码解码窗口 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.apputils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/23 16:14:45 | ||||
|  */ | ||||
| import android.app.Activity; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.apputils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/05/13 11:15 | ||||
|  * @Describe WinBoLLActivity | ||||
|  */ | ||||
|   | ||||
							
								
								
									
										35
									
								
								autoinstaller/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								autoinstaller/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| # AutoInstaller | ||||
|  | ||||
| #### 介绍 | ||||
| APK 文件监控,监控指定文件路径的 APK 文件。 | ||||
| 在 APK 文件改变后自动调用系统安装命令安装,或者调用[应用信息查看器]打开。 | ||||
|  | ||||
| #### 软件架构 | ||||
| 适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。 | ||||
| 也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。 | ||||
|  | ||||
|  | ||||
| #### Gradle 编译说明 | ||||
| 调试版编译命令 :gradle assembleBetaDebug | ||||
| 阶段版编译命令 :bash .winboll/bashPublishAPKAddTag.sh autoinstaller | ||||
|  | ||||
| #### 使用说明 | ||||
|  | ||||
| #### 参与贡献 | ||||
|  | ||||
| 1.  Fork 本仓库 | ||||
| 2.  新建 Feat_xxx 分支 | ||||
| 3.  提交代码 : ZhanGSKen(ZhanGSKen<zhangsken@188.com>) | ||||
| 4.  新建 Pull Request | ||||
|  | ||||
|  | ||||
| #### 特技 | ||||
|  | ||||
| 1.  使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md | ||||
| 2.  Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) | ||||
| 3.  你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 | ||||
| 4.  [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 | ||||
| 5.  Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) | ||||
| 6.  Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) | ||||
|  | ||||
| #### 参考文档 | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Sun May 04 05:32:00 GMT 2025 | ||||
| stageCount=1 | ||||
| #Tue Jun 24 09:54:47 HKT 2025 | ||||
| stageCount=3 | ||||
| libraryProject= | ||||
| baseVersion=15.2 | ||||
| publishVersion=15.2.0 | ||||
| buildCount=74 | ||||
| baseBetaVersion=15.2.1 | ||||
| publishVersion=15.2.2 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.2.3 | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.autoinstaller; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/04/28 02:39:58 | ||||
|  * @Describe 全局应用类 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.autoinstaller; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/04/15 09:24:46 | ||||
|  * @Describe 磁贴工具服务类 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.autoinstaller.models; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/04/02 20:50:29 | ||||
|  * @Describe 监控的 APK 安装文件对应的应用信息数据模型 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.autoinstaller.models; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/05/27 17:36:01 | ||||
|  * @Describe 应用配置数据类 | ||||
|  */ | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import cc.winboll.studio.libappbase.BaseBean; | ||||
| import java.io.IOException; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/04/15 09:27:39 | ||||
|  * @Describe MainServiceBean | ||||
|  */ | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import cc.winboll.studio.autoinstaller.services.MainService; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/06/06 13:19:38 | ||||
|  * @Describe 应用消息接收类 | ||||
|  */ | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import cc.winboll.studio.autoinstaller.models.AppConfigs; | ||||
| import cc.winboll.studio.autoinstaller.utils.ServiceUtil; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/05/27 20:16:00 | ||||
|  * @Describe MainService 守护进程服务 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.autoinstaller.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/05/27 17:56:31 | ||||
|  * @Describe 文件管理类 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.autoinstaller.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/12/11 06:28:50 | ||||
|  * @Describe 一个获取安卓APK安装文件的应用包名的函数 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.autoinstaller.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/05/27 20:20:03 | ||||
|  * @Describe 服务类工具 | ||||
|  */ | ||||
|   | ||||
| @@ -72,7 +72,7 @@ allprojects { | ||||
|         bashCommitAppPublishBuildFlagInfoFilePath = ".winboll/bashCommitAppPublishBuildFlagInfo.sh" | ||||
|          | ||||
|         winbollFilePath = "winboll.properties" | ||||
|         keyPropsFilePath = "current.keystore" | ||||
|         keyPropsFilePath = "appkey.keystore" | ||||
|         // 定义 lint 输出文件 | ||||
|         lintXmlReportFilePath = "build/reports/lint-results.xml" | ||||
|         lintHTMLReportFilePath = "build/reports/lint-results.html" | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| https://github.com/aJIEw/PhoneCallApp.git | ||||
|  | ||||
| #### 介绍 | ||||
| 通讯录与拨号 | ||||
| 这是可以根据正则表达式匹配拦截骚扰电话的手机拨号应用。 | ||||
|  | ||||
| #### 软件架构 | ||||
| 适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。 | ||||
|   | ||||
| @@ -45,9 +45,9 @@ android { | ||||
|  | ||||
| dependencies { | ||||
|     api fileTree(dir: 'libs', include: ['*.jar']) | ||||
|     api 'cc.winboll.studio:libaes:15.8.0' | ||||
|     api 'cc.winboll.studio:libapputils:15.8.1' | ||||
|     api 'cc.winboll.studio:libappbase:15.8.1' | ||||
|     api 'cc.winboll.studio:libaes:15.9.3' | ||||
|     api 'cc.winboll.studio:libapputils:15.8.5' | ||||
|     api 'cc.winboll.studio:libappbase:15.9.5' | ||||
|      | ||||
|     // 权限请求框架:https://github.com/getActivity/XXPermissions | ||||
|     api 'com.github.getActivity:XXPermissions:18.63' | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Tue May 20 13:02:18 HKT 2025 | ||||
| stageCount=3 | ||||
| #Sun Aug 31 06:05:42 CST 2025 | ||||
| stageCount=17 | ||||
| libraryProject= | ||||
| baseVersion=15.3 | ||||
| publishVersion=15.3.2 | ||||
| publishVersion=15.3.16 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.3.3 | ||||
| baseBetaVersion=15.3.17 | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/12/08 15:10:51 | ||||
|  * @Describe 全局应用类 | ||||
|  */ | ||||
|   | ||||
| @@ -1,5 +1,10 @@ | ||||
| package cc.winboll.studio.contacts; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> | ||||
|  * @Date 2025/08/30 14:32 | ||||
|  * @Describe 主窗口 | ||||
|  */ | ||||
| import android.Manifest; | ||||
| import android.app.Activity; | ||||
| import android.app.ActivityManager; | ||||
| @@ -8,6 +13,7 @@ import android.content.Intent; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.os.Build; | ||||
| import android.os.Bundle; | ||||
| import android.os.Handler; | ||||
| import android.telecom.TelecomManager; | ||||
| import android.telephony.PhoneStateListener; | ||||
| import android.telephony.TelephonyManager; | ||||
| @@ -41,7 +47,7 @@ import java.util.List; | ||||
|  | ||||
| final public class MainActivity extends AppCompatActivity implements IWinBoLLActivity, ViewPager.OnPageChangeListener, View.OnClickListener { | ||||
|  | ||||
| 	public static final String TAG = "MainActivity"; | ||||
|     public static final String TAG = "MainActivity"; | ||||
|  | ||||
|     public static final int REQUEST_HOME_ACTIVITY = 0; | ||||
|     public static final int REQUEST_ABOUT_ACTIVITY = 1; | ||||
| @@ -55,13 +61,10 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | ||||
|     MainServiceBean mMainServiceBean; | ||||
|     private TabLayout tabLayout; | ||||
|     private ViewPager viewPager; | ||||
|     private List<View> views; //用来存放放进ViewPager里面的布局 | ||||
|     //实例化存储imageView(导航原点)的集合 | ||||
|     private List<View> views;  | ||||
|     ImageView[] imageViews; | ||||
|     //MyPagerAdapter adapter;//适配器 | ||||
|     MyPagerAdapter pagerAdapter; | ||||
|     LinearLayout linearLayout;//下标所在在LinearLayout布局里 | ||||
|     int currentPoint = 0;//当前被选中中页面的下标 | ||||
|     LinearLayout linearLayout; | ||||
|     int currentPoint = 0; | ||||
|  | ||||
|     private TelephonyManager telephonyManager; | ||||
|     private MyPhoneStateListener phoneStateListener; | ||||
| @@ -70,30 +73,6 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | ||||
|  | ||||
|     private static final int DIALER_REQUEST_CODE = 1; | ||||
|  | ||||
| //    @Override | ||||
| //    public Activity getActivity() { | ||||
| //        return this; | ||||
| //    } | ||||
|  | ||||
| //    @Override | ||||
| //    public APPInfo getAppInfo() { | ||||
| //        String szBranchName = "contacts"; | ||||
| // | ||||
| //        APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo(); | ||||
| //        appInfo.setAppName("Contacts"); | ||||
| //        appInfo.setAppIcon(cc.winboll.studio.libapputils.R.drawable.ic_winboll); | ||||
| //        appInfo.setAppDescription("Contacts Description"); | ||||
| //        appInfo.setAppGitName("APP"); | ||||
| //        appInfo.setAppGitOwner("Studio"); | ||||
| //        appInfo.setAppGitAPPBranch(szBranchName); | ||||
| //        appInfo.setAppGitAPPSubProjectFolder(szBranchName); | ||||
| //        appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts"); | ||||
| //        appInfo.setAppAPKName("Contacts"); | ||||
| //        appInfo.setAppAPKFolderName("Contacts"); | ||||
| //        return appInfo; | ||||
| //        return null; | ||||
| //    } | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     public Activity getActivity() { | ||||
| @@ -107,89 +86,62 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         // 接收并处理 Intent 数据,函数 Intent 处理接收就直接返回 | ||||
|         //if (prosessIntents(getIntent())) return; | ||||
|         // 以下正常创建主窗口 | ||||
|         super.onCreate(savedInstanceState); | ||||
|         _MainActivity = this; | ||||
|         setContentView(R.layout.activity_main); | ||||
|  | ||||
|         // 初始化工具栏 | ||||
|         mToolbar = findViewById(R.id.activitymainToolbar1); | ||||
|         // 初始化工具栏(仅加载基础UI) | ||||
|         mToolbar = (Toolbar) findViewById(R.id.activitymainToolbar1); | ||||
|         setSupportActionBar(mToolbar); | ||||
| //        if (isEnableDisplayHomeAsUp()) { | ||||
| //            // 显示后退按钮 | ||||
| //            getSupportActionBar().setDisplayHomeAsUpEnabled(true); | ||||
| //        } | ||||
|         getSupportActionBar().setSubtitle(TAG); | ||||
|  | ||||
|         tabLayout = findViewById(R.id.tabLayout); | ||||
|         viewPager = findViewById(R.id.viewPager); | ||||
|         tabLayout = (TabLayout) findViewById(R.id.tabLayout); | ||||
|         viewPager = (ViewPager) findViewById(R.id.viewPager); | ||||
|  | ||||
|         // 创建Fragment列表和标题列表 | ||||
|         fragmentList = new ArrayList<>(); | ||||
|         tabTitleList = new ArrayList<>(); | ||||
|         // 创建Fragment列表(仅实例化,不加载数据) | ||||
|         fragmentList = new ArrayList<Fragment>(); | ||||
|         tabTitleList = new ArrayList<String>(); | ||||
|         fragmentList.add(CallLogFragment.newInstance(0)); | ||||
|         fragmentList.add(ContactsFragment.newInstance(1)); | ||||
|         fragmentList.add(ContactsFragment.newInstance(1)); // 延迟加载联系人数据 | ||||
|         fragmentList.add(LogFragment.newInstance(2)); | ||||
|         tabTitleList.add("通话记录"); | ||||
|         tabTitleList.add("联系人"); | ||||
|         tabTitleList.add("应用日志"); | ||||
|  | ||||
|         // 设置ViewPager的适配器 | ||||
|         // 设置ViewPager适配器 | ||||
|         MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), fragmentList, tabTitleList); | ||||
|         viewPager.setAdapter(adapter); | ||||
|  | ||||
|         // 关键:关闭预加载,仅当前页初始化 | ||||
|         viewPager.setOffscreenPageLimit(0); | ||||
|  | ||||
|         // 关联TabLayout和ViewPager | ||||
|         tabLayout.setupWithViewPager(viewPager); | ||||
|  | ||||
|  | ||||
|  | ||||
| //        initData(); | ||||
| //        initView(); | ||||
| //        //initPoint();//调用初始化导航原点的方法 | ||||
| //        viewPager.addOnPageChangeListener(this);//滑动事件 | ||||
|  | ||||
|         //ViewPager viewPager = findViewById(R.id.activitymainViewPager1); | ||||
|         //MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager()); | ||||
|         //viewPager.setAdapter(pagerAdapter); | ||||
|         //TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1); | ||||
|         //tabLayout.setupWithViewPager(viewPager); | ||||
|  | ||||
| //        mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class); | ||||
| //        if (mMainServiceBean == null) { | ||||
| //            mMainServiceBean = new MainServiceBean(); | ||||
| //        } | ||||
| //        cbMainService = findViewById(R.id.activitymainCheckBox1); | ||||
| //        cbMainService.setChecked(mMainServiceBean.isEnable()); | ||||
| //        cbMainService.setOnClickListener(new View.OnClickListener(){ | ||||
| //                @Override | ||||
| //                public void onClick(View view) { | ||||
| //                    if (cbMainService.isChecked()) { | ||||
| //                        MainService.startMainService(MainActivity.this); | ||||
| //                    } else { | ||||
| //                        MainService.stopMainService(MainActivity.this); | ||||
| //                    } | ||||
| //                } | ||||
| //            }); | ||||
|  | ||||
|         // 初始化服务状态(延迟启动非核心服务) | ||||
|         MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class); | ||||
|         if (mMainServiceBean == null) { | ||||
|             mMainServiceBean = new MainServiceBean(); | ||||
|             MainServiceBean.saveBean(this, mMainServiceBean); | ||||
|         } | ||||
|         if (mMainServiceBean.isEnable()) { | ||||
|             MainService.startMainService(this); | ||||
|             // 延迟1秒启动服务,避免阻塞启动 | ||||
|             new Handler().postDelayed(new Runnable() { | ||||
| 					@Override | ||||
| 					public void run() { | ||||
| 						MainService.startMainService(MainActivity.this); | ||||
| 					} | ||||
| 				}, 1000); | ||||
|         } | ||||
|  | ||||
|         // 初始化TelephonyManager和PhoneStateListener | ||||
|         // 初始化电话状态监听(基础功能保留) | ||||
|         telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); | ||||
|         phoneStateListener = new MyPhoneStateListener(); | ||||
|         telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     // ViewPager的适配器 | ||||
|     // ViewPager适配器(Java 7语法) | ||||
|     private class MyPagerAdapter extends FragmentPagerAdapter { | ||||
|  | ||||
|         private List<Fragment> fragmentList; | ||||
| @@ -226,91 +178,22 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | ||||
|         _MainActivity.startActivity(intent); | ||||
|     } | ||||
|  | ||||
|     //初始化view,即显示的图片 | ||||
| //    void initView() { | ||||
| //        viewPager = findViewById(R.id.activitymainViewPager1); | ||||
| //        pagerAdapter = new MyPagerAdapter(getSupportFragmentManager()); | ||||
| //        viewPager.setAdapter(pagerAdapter); | ||||
| //        //adapter = new MyPagerAdapter(views); | ||||
| //        //viewPager = findViewById(R.id.activitymainViewPager1); | ||||
| //        //viewPager.setAdapter(adapter); | ||||
| //        //linearLayout = findViewById(R.id.activitymainLinearLayout1); | ||||
| //        //initPoint();//初始化页面下方的点 | ||||
| //        viewPager.setOnPageChangeListener(this); | ||||
| // | ||||
| //    } | ||||
|  | ||||
|     //初始化所要显示的布局 | ||||
| //    void initData() { | ||||
| //        LayoutInflater inflater = LayoutInflater.from(getActivity()); | ||||
| //        View view1 = inflater.inflate(R.layout.fragment_call_log, viewPager, false); | ||||
| //        View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false); | ||||
| //        View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false); | ||||
| // | ||||
| //        views = new ArrayList<>(); | ||||
| //        views.add(view1); | ||||
| //        views.add(view2); | ||||
| //        views.add(view3); | ||||
| //    } | ||||
|  | ||||
| //    void initPoint() { | ||||
| //        imageViews = new ImageView[5];//实例化5个图片 | ||||
| //        for (int i = 0; i < linearLayout.getChildCount(); i++) { | ||||
| //            imageViews[i] = (ImageView) linearLayout.getChildAt(i); | ||||
| //            imageViews[i].setImageResource(R.drawable.ic_launcher); | ||||
| //            imageViews[i].setOnClickListener(this);//点击导航点,即可跳转 | ||||
| //            imageViews[i].setTag(i);//重复利用实例化的对象 | ||||
| //        } | ||||
| //        currentPoint = 0;//默认第一个坐标 | ||||
| //        imageViews[currentPoint].setImageResource(R.drawable.ic_launcher); | ||||
| //    } | ||||
|  | ||||
|     //OnPageChangeListener接口要实现的三个方法 | ||||
|     /*    onPageScrollStateChanged(int state) | ||||
|      此方法是在状态改变的时候调用,其中state这个参数有三种状态: | ||||
|      SCROLL_STATE_DRAGGING(1)表示用户手指“按在屏幕上并且开始拖动”的状态 | ||||
|      (手指按下但是还没有拖动的时候还不是这个状态,只有按下并且手指开始拖动后log才打出。) | ||||
|      SCROLL_STATE_IDLE(0)滑动动画做完的状态。 | ||||
|      SCROLL_STATE_SETTLING(2)在“手指离开屏幕”的状态。*/ | ||||
|     // OnPageChangeListener接口实现 | ||||
|     @Override | ||||
|     public void onPageScrollStateChanged(int state) { | ||||
|     public void onPageScrollStateChanged(int state) {} | ||||
|  | ||||
|     } | ||||
|     /*    onPageScrolled(int position, float positionOffset, int positionOffsetPixels) | ||||
|      当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为: | ||||
|  | ||||
|      position :当前页面,即你点击滑动的页面(从A滑B,则是A页面的position。 | ||||
|      positionOffset:当前页面偏移的百分比 | ||||
|      positionOffsetPixels:当前页面偏移的像素位置*/ | ||||
|     @Override | ||||
|     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { | ||||
|     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {} | ||||
|  | ||||
|     } | ||||
|     /*    onPageSelected(int position) | ||||
|      此方法是页面滑动完后得到调用,position是你当前选中的页面的Position(位置编号) | ||||
|      (从A滑动到B,就是B的position)*/ | ||||
|     public void onPageSelected(int position) { | ||||
|  | ||||
| //        ImageView preView = imageViews[currentPoint]; | ||||
| //        preView.setImageResource(R.drawable.ic_launcher); | ||||
| //        ImageView currView = imageViews[position]; | ||||
| //        currView.setImageResource(R.drawable.ic_launcher); | ||||
| //        currentPoint = position; | ||||
|     } | ||||
|  | ||||
|     //小圆点点击事件 | ||||
|     @Override | ||||
|     public void onClick(View v) { | ||||
|         // TODO Auto-generated method stub | ||||
|         //通过getTag(),可以判断是哪个控件 | ||||
| //        int i = (Integer) v.getTag(); | ||||
| //        viewPager.setCurrentItem(i);//直接跳转到某一个页面的情况 | ||||
|     } | ||||
|     public void onPageSelected(int position) {} | ||||
|  | ||||
|     @Override | ||||
|     public void onClick(View v) {} | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostCreate(Bundle savedInstanceState) { | ||||
|         super.onPostCreate(savedInstanceState); | ||||
|         //setSubTitle(""); | ||||
|     } | ||||
|  | ||||
|     private class MyPhoneStateListener extends PhoneStateListener { | ||||
| @@ -336,109 +219,31 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | ||||
|         LogUtils.d(TAG, "onDestroy() SOS"); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     // | ||||
|     // 处理传入的 Intent 数据 | ||||
|     // | ||||
| //    boolean prosessIntents(Intent intent) { | ||||
| //        if (intent == null  | ||||
| //            || intent.getAction() == null | ||||
| //            || intent.getAction().equals("")) | ||||
| //            return false; | ||||
| // | ||||
| //        if (intent.getAction().equals(StringToQrCodeView.ACTION_UNITTEST_QRCODE)) { | ||||
| //            try { | ||||
| //                WinBoLLActivity clazzActivity = UnitTestActivity.class.newInstance(); | ||||
| //                String tag = clazzActivity.getTag(); | ||||
| //                LogUtils.d(TAG, "String tag = clazzActivity.getTag(); tag " + tag); | ||||
| //                Intent subIntent = new Intent(this, UnitTestActivity.class); | ||||
| //                subIntent.setAction(intent.getAction()); | ||||
| //                File file = new File(getCacheDir(), UUID.randomUUID().toString()); | ||||
| //                //取出文件uri | ||||
| //                Uri uri = intent.getData(); | ||||
| //                if (uri == null) { | ||||
| //                    uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); | ||||
| //                } | ||||
| //                //获取文件真实地址 | ||||
| //                String szSrcPath = UriUtils.getFileFromUri(getApplication(), uri); | ||||
| //                if (TextUtils.isEmpty(szSrcPath)) { | ||||
| //                    return false; | ||||
| //                } | ||||
| // | ||||
| //                Files.copy(Paths.get(szSrcPath), Paths.get(file.getPath())); | ||||
| //                //startWinBoLLActivity(subIntent, tag); | ||||
| //                WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, subIntent, UnitTestActivity.class); | ||||
| //            } catch (IllegalAccessException | InstantiationException | IOException e) { | ||||
| //                LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
| //                // 函数处理异常返回失败 | ||||
| //                return false; | ||||
| //            } | ||||
| //        } else { | ||||
| //            LogUtils.d(TAG, "prosessIntents|" + intent.getAction() + "|yet"); | ||||
| //            return false; | ||||
| //        } | ||||
| //        return true; | ||||
| //    } | ||||
|  | ||||
| //    @Override | ||||
| //    public String getTag() { | ||||
| //        return TAG; | ||||
| //    } | ||||
|  | ||||
| //    @Override | ||||
| //    public void onBackPressed() { | ||||
| //        exit(); | ||||
| //    } | ||||
| // | ||||
| //    void exit() { | ||||
| //        YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){ | ||||
| // | ||||
| //            @Override | ||||
| //            public void onYes() { | ||||
| //                WinBoLLActivityManager.getInstance(getApplicationContext()).finishAll(); | ||||
| //            } | ||||
| // | ||||
| //            @Override | ||||
| //            public void onNo() { | ||||
| //            } | ||||
| //        }; | ||||
| //        YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener); | ||||
| //    } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onCreateOptionsMenu(Menu menu) { | ||||
|         getMenuInflater().inflate(R.menu.toolbar_main, menu); | ||||
|         return super.onCreateOptionsMenu(menu); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         if (item.getItemId() == R.id.item_settings) { | ||||
|             Intent intent = new Intent(this, SettingsActivity.class); | ||||
|             startActivity(intent); | ||||
|             //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, CallActivity.class); | ||||
|         } | ||||
| //        } else  | ||||
| //        if (item.getItemId() == R.id.item_exit) { | ||||
| //            exit(); | ||||
| //            return true; | ||||
| //        } | ||||
|         return super.onOptionsItemSelected(item); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     protected void onResume() { | ||||
|         super.onResume(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Android M 及以上检查是否是系统默认电话应用 | ||||
|      * 检查是否是系统默认电话应用 | ||||
|      */ | ||||
|     public boolean isDefaultPhoneCallApp() { | ||||
|         if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||||
|             TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE); | ||||
|             if (manger != null && manger.getDefaultDialerPackage() != null) { | ||||
|                 return manger.getDefaultDialerPackage().equals(getPackageName()); | ||||
| @@ -452,35 +257,22 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | ||||
|         if (manager == null) return false; | ||||
|  | ||||
|         for (ActivityManager.RunningServiceInfo service : manager.getRunningServices( | ||||
|             Integer.MAX_VALUE)) { | ||||
| 			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(); | ||||
| 							   Toast.LENGTH_SHORT).show(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.activities; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/31 15:15:54 | ||||
|  * @Describe 应用介绍窗口 | ||||
|  */ | ||||
| @@ -79,12 +79,12 @@ public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity | ||||
|         APPInfo appInfo = new APPInfo(); | ||||
|         appInfo.setAppName("Contacts"); | ||||
|         appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll); | ||||
|         appInfo.setAppDescription("通讯录与拨号"); | ||||
|         appInfo.setAppGitName("APP"); | ||||
|         appInfo.setAppDescription("这是可以根据正则表达式匹配拦截骚扰电话的手机拨号应用。"); | ||||
|         appInfo.setAppGitName("APPBase"); | ||||
|         appInfo.setAppGitOwner("Studio"); | ||||
|         appInfo.setAppGitAPPBranch(szBranchName); | ||||
|         appInfo.setAppGitAPPSubProjectFolder(szBranchName); | ||||
|         appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts"); | ||||
|         appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=4&extra=page%3D1"); | ||||
|         appInfo.setAppAPKName("Contacts"); | ||||
|         appInfo.setAppAPKFolderName("Contacts"); | ||||
|         return new AboutView(mContext, appInfo); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.activities; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/20 17:15:46 | ||||
|  * @Describe 拨号窗口 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.activities; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/20 20:18:26 | ||||
|  */ | ||||
| import android.content.Intent; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.activities; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/21 05:37:42 | ||||
|  */ | ||||
| import android.content.Context; | ||||
| @@ -198,6 +198,9 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv | ||||
|             settingsModel.setDunTotalCount(Integer.parseInt(etDunTotalCount.getText().toString())); | ||||
|             settingsModel.setDunResumeSecondCount(Integer.parseInt(etDunResumeSecondCount.getText().toString())); | ||||
|             settingsModel.setDunResumeCount(Integer.parseInt(etDunResumeCount.getText().toString())); | ||||
| 			 | ||||
| 			// 应用效果提示 | ||||
| 			ToastUtils.show((settingsModel.getDunTotalCount() == 1)?"电话骚扰防御力几乎为0。":String.format("以下设置将在连拨%d次后接通电话。", settingsModel.getDunTotalCount())); | ||||
|         } | ||||
|         settingsModel.setIsEnableDun(isEnableDun); | ||||
|         Rules.getInstance(this).saveDun(); | ||||
| @@ -207,6 +210,7 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv | ||||
|         etDunTotalCount.setText(Integer.toString(settingsModel.getDunTotalCount())); | ||||
|         etDunResumeSecondCount.setText(Integer.toString(settingsModel.getDunResumeSecondCount())); | ||||
|         etDunResumeCount.setText(Integer.toString(settingsModel.getDunResumeCount())); | ||||
| 		 | ||||
|     } | ||||
|  | ||||
|     void updateStreamVolumeTextView() { | ||||
| @@ -243,6 +247,9 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv | ||||
|         Rules.getInstance(this).resetDefaultBoBullToonURL(); | ||||
|         EditText etBoBullToonURL = findViewById(R.id.bobulltoonurl_et); | ||||
|         etBoBullToonURL.setText(Rules.getInstance(this).getBoBullToonURL()); | ||||
| 		 | ||||
| 		final TomCat tomCat = TomCat.getInstance(this); | ||||
| 		tomCat.cleanBoBullToon(); | ||||
|     } | ||||
|  | ||||
|     public void onDownloadBoBullToon(View view) { | ||||
| @@ -330,4 +337,8 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv | ||||
|     public void onAbout(View view) { | ||||
|         App.getWinBoLLActivityManager().startWinBoLLActivity(this, AboutActivity.class); | ||||
|     } | ||||
| 	 | ||||
| 	public void onLogView(View view) { | ||||
|         App.getWinBoLLActivityManager().startLogActivity(this); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.LogView; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/02 16:07:04 | ||||
|  */ | ||||
| public class UnitTestActivity extends Activity { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.activities; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/31 15:16:45 | ||||
|  * @Describe 应用窗口基类 | ||||
|  */ | ||||
|   | ||||
| @@ -1,17 +1,22 @@ | ||||
| package cc.winboll.studio.contacts.adapters; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/26 13:09:32 | ||||
|  * @Describe CallLogAdapter | ||||
|  */ | ||||
| import android.content.ClipData; | ||||
| import android.content.ClipboardManager; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.Button; | ||||
| import android.widget.PopupMenu; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| @@ -35,6 +40,10 @@ public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogV | ||||
|         this.mContactUtils = ContactUtils.getInstance(mContext); | ||||
|         this.callLogList = callLogList; | ||||
|     } | ||||
| 	 | ||||
| 	public void relaodContacts() { | ||||
| 		this.mContactUtils.relaodContacts(); | ||||
| 	} | ||||
|  | ||||
|     @NonNull | ||||
|     @Override | ||||
| @@ -47,6 +56,38 @@ public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogV | ||||
|     public void onBindViewHolder(@NonNull CallLogViewHolder holder, int position) { | ||||
|         final CallLogModel callLog = callLogList.get(position); | ||||
|         holder.phoneNumber.setText(callLog.getPhoneNumber() + "☎" + mContactUtils.getContactsName(callLog.getPhoneNumber())); | ||||
| 		holder.phoneNumber.setOnLongClickListener(new View.OnLongClickListener() { | ||||
| 				@Override | ||||
| 				public boolean onLongClick(View p1) { | ||||
| 					// 弹出复制菜单 | ||||
| 					PopupMenu menu = new PopupMenu(mContext, holder.phoneNumber); | ||||
| 					//加载菜单资源 | ||||
| 					menu.getMenuInflater().inflate(R.menu.toolbar_calllog_phonenumber, menu.getMenu()); | ||||
| 					//设置点击事件的响应 | ||||
| 					menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { | ||||
| 							@Override | ||||
| 							public boolean onMenuItemClick(MenuItem menuItem) { | ||||
| 								int nItemId = menuItem.getItemId(); | ||||
| 								if (nItemId == R.id.item_calllog_phonenumber_copy) { | ||||
| 									// Gets a handle to the clipboard service. | ||||
| 									ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE); | ||||
| 									// Creates a new text clip to put on the clipboard | ||||
| 									ClipData clip = ClipData.newPlainText("simple text", callLog.getPhoneNumber()); | ||||
| 									// Set the clipboard's primary clip. | ||||
| 									clipboard.setPrimaryClip(clip); | ||||
| 									Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show(); | ||||
| 								} | ||||
|  | ||||
| 								return true; | ||||
| 							} | ||||
| 						}); | ||||
| 					//一定要调用show()来显示弹出式菜单 | ||||
| 					menu.show(); | ||||
|  | ||||
| 					return true; | ||||
| 				} | ||||
| 			}); | ||||
| 		 | ||||
|         holder.callStatus.setText(callLog.getCallStatus()); | ||||
|         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); | ||||
|         holder.callDate.setText(dateFormat.format(callLog.getCallDate())); | ||||
|   | ||||
| @@ -1,23 +1,29 @@ | ||||
| package cc.winboll.studio.contacts.adapters; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/26 13:35:44 | ||||
|  * @Describe ContactAdapter | ||||
|  */ | ||||
| import android.content.ClipData; | ||||
| import android.content.ClipboardManager; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.Button; | ||||
| import android.widget.LinearLayout; | ||||
| import android.widget.PopupMenu; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.contacts.beans.ContactModel; | ||||
| import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.util.List; | ||||
| import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | ||||
|  | ||||
| public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactViewHolder> { | ||||
|  | ||||
| @@ -26,8 +32,10 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV | ||||
|     private static final int REQUEST_CALL_PHONE = 1; | ||||
|  | ||||
|     private List<ContactModel> contactList; | ||||
| 	Context mContext; | ||||
|  | ||||
|     public ContactAdapter(List<ContactModel> contactList) { | ||||
|     public ContactAdapter(Context context, List<ContactModel> contactList) { | ||||
| 		mContext = context; | ||||
|         this.contactList = contactList; | ||||
|     } | ||||
|  | ||||
| @@ -41,6 +49,37 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV | ||||
|     @Override | ||||
|     public void onBindViewHolder(@NonNull ContactViewHolder holder, int position) { | ||||
|         final ContactModel contact = contactList.get(position); | ||||
| 		holder.llPhoneNumberMain.setOnLongClickListener(new View.OnLongClickListener() { | ||||
| 				@Override | ||||
| 				public boolean onLongClick(View p1) { | ||||
| 					// 弹出复制菜单 | ||||
| 					PopupMenu menu = new PopupMenu(mContext, holder.llPhoneNumberMain); | ||||
| 					//加载菜单资源 | ||||
| 					menu.getMenuInflater().inflate(R.menu.toolbar_contact_phonenumber, menu.getMenu()); | ||||
| 					//设置点击事件的响应 | ||||
| 					menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { | ||||
| 							@Override | ||||
| 							public boolean onMenuItemClick(MenuItem menuItem) { | ||||
| 								int nItemId = menuItem.getItemId(); | ||||
| 								if (nItemId == R.id.item_contact_phonenumber_copy) { | ||||
| 									// Gets a handle to the clipboard service. | ||||
| 									ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE); | ||||
| 									// Creates a new text clip to put on the clipboard | ||||
| 									ClipData clip = ClipData.newPlainText("simple text", contact.getNumber()); | ||||
| 									// Set the clipboard's primary clip. | ||||
| 									clipboard.setPrimaryClip(clip); | ||||
| 									Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show(); | ||||
| 								} | ||||
|  | ||||
| 								return true; | ||||
| 							} | ||||
| 						}); | ||||
| 					//一定要调用show()来显示弹出式菜单 | ||||
| 					menu.show(); | ||||
|  | ||||
| 					return true; | ||||
| 				} | ||||
| 			}); | ||||
|         holder.contactName.setText(contact.getName()); | ||||
|         holder.contactNumber.setText(contact.getNumber()); | ||||
|  | ||||
| @@ -69,12 +108,14 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV | ||||
|     } | ||||
|  | ||||
|     public class ContactViewHolder extends RecyclerView.ViewHolder { | ||||
| 		LinearLayout llPhoneNumberMain; | ||||
|         TextView contactName; | ||||
|         TextView contactNumber; | ||||
|         AOHPCTCSeekBar dialAOHPCTCSeekBar; | ||||
|          | ||||
|         public ContactViewHolder(@NonNull View itemView) { | ||||
|             super(itemView); | ||||
| 			llPhoneNumberMain = itemView.findViewById(R.id.itemcontactLinearLayout1); | ||||
|             contactName = itemView.findViewById(R.id.contact_name); | ||||
|             contactNumber = itemView.findViewById(R.id.contact_number); | ||||
|             dialAOHPCTCSeekBar = itemView.findViewById(R.id.aohpctcseekbar_dial); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.adapters; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/20 14:20:38 | ||||
|  * @Describe ImagePagerAdapter | ||||
|  */ | ||||
|   | ||||
| @@ -1,12 +1,13 @@ | ||||
| package cc.winboll.studio.contacts.adapters; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/02 17:27:41 | ||||
|  * @Describe PhoneConnectRuleAdapter | ||||
|  */ | ||||
| import android.content.Context; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.MotionEvent; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.Button; | ||||
| @@ -20,6 +21,7 @@ import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel; | ||||
| import cc.winboll.studio.contacts.dun.Rules; | ||||
| import cc.winboll.studio.contacts.views.LeftScrollView; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.util.ArrayList; | ||||
| @@ -60,6 +62,10 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V | ||||
|             final SimpleViewHolder simpleViewHolder = (SimpleViewHolder) holder; | ||||
|             String szView = model.getRuleText().trim().equals("") ?"[NULL]": model.getRuleText(); | ||||
|             simpleViewHolder.tvRuleText.setText(szView); | ||||
|             simpleViewHolder.checkBoxAllow.setChecked(model.isAllowConnection()); | ||||
| 			simpleViewHolder.checkBoxAllow.setEnabled(false); | ||||
|             simpleViewHolder.checkBoxEnable.setChecked(model.isEnable()); | ||||
| 			simpleViewHolder.checkBoxEnable.setEnabled(false); | ||||
|             simpleViewHolder.scrollView.setOnActionListener(new LeftScrollView.OnActionListener(){ | ||||
|  | ||||
|                     @Override | ||||
| @@ -215,16 +221,22 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V | ||||
|  | ||||
|         private final LeftScrollView scrollView; | ||||
|         private final TextView tvRuleText; | ||||
| 		CheckBox checkBoxAllow; | ||||
|         CheckBox checkBoxEnable; | ||||
|  | ||||
|  | ||||
|         public SimpleViewHolder(@NonNull ViewGroup parent, @NonNull View itemView) { | ||||
|             super(itemView); | ||||
|             scrollView = itemView.findViewById(R.id.scrollView); | ||||
|             //tvRuleText = itemView.findViewById(R.id.ruletext_tv); | ||||
|             tvRuleText = new TextView(itemView.getContext()); | ||||
| 			LayoutInflater inflater = LayoutInflater.from(itemView.getContext()); | ||||
| 		    View viewContent = inflater.inflate(R.layout.view_phone_connect_rule_simple_content, parent, false); | ||||
|             tvRuleText = viewContent.findViewById(R.id.ruletext_tv); | ||||
|             checkBoxAllow = viewContent.findViewById(R.id.checkbox_allow); | ||||
|             checkBoxEnable = viewContent.findViewById(R.id.checkbox_enable); | ||||
|             //tvRuleText = new TextView(itemView.getContext()); | ||||
|             scrollView.setContentWidth(parent.getWidth()); | ||||
|             //scrollView.setContentWidth(600); | ||||
|             scrollView.addContentLayout(tvRuleText); | ||||
|             scrollView.addContentLayout(viewContent); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| @@ -243,5 +255,9 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V | ||||
|             buttonConfirm = itemView.findViewById(R.id.button_confirm); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| 	private void setCheckBoxTouchListener(CheckBox checkBox) { | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.beans; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/26 13:10:57 | ||||
|  * @Describe CallLogModel | ||||
|  */ | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| package cc.winboll.studio.contacts.beans; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Date 2025/02/26 13:37:00 | ||||
|  * @Describe ContactModel | ||||
|  * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> | ||||
|  * @Date 2025/08/30 14:32 | ||||
|  * @Describe 联系人信息数据模型 | ||||
|  */ | ||||
| import net.sourceforge.pinyin4j.PinyinHelper; | ||||
| import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; | ||||
| @@ -18,13 +18,18 @@ public class ContactModel { | ||||
|     private String name; | ||||
|     private String number; | ||||
|     private String pinyin; | ||||
|     // 新增:存储姓名的拼音首字母(如"啊牛"→"an") | ||||
|     private String pinyinFirstLetter; | ||||
|  | ||||
|     public ContactModel(String name, String number) { | ||||
|         this.name = name; | ||||
|         this.number = number.replaceAll("\\s", ""); | ||||
|         this.pinyin = convertToPinyin(name); | ||||
|         // 初始化时生成拼音首字母 | ||||
|         this.pinyinFirstLetter = convertToPinyinFirstLetter(name); | ||||
|     } | ||||
|  | ||||
|     // 原方法:转换为全拼(如"啊牛"→"aniu") | ||||
|     private String convertToPinyin(String chinese) { | ||||
|         HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); | ||||
|         format.setCaseType(HanyuPinyinCaseType.LOWERCASE); | ||||
| @@ -33,22 +38,55 @@ public class ContactModel { | ||||
|         StringBuilder pinyin = new StringBuilder(); | ||||
|         for (int i = 0; i < chinese.length(); i++) { | ||||
|             char ch = chinese.charAt(i); | ||||
|             if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) { | ||||
|             if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) { // 仅处理汉字 | ||||
|                 try { | ||||
|                     String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format); | ||||
|                     if (pinyinArray != null) { | ||||
|                         pinyin.append(pinyinArray[0]); | ||||
|                     if (pinyinArray != null && pinyinArray.length > 0) { | ||||
|                         pinyin.append(pinyinArray[0]); // 取第一个拼音(多音字默认首选项) | ||||
|                     } | ||||
|                 } catch (BadHanyuPinyinOutputFormatCombination e) { | ||||
|                     e.printStackTrace(); | ||||
|                 } | ||||
|             } else { | ||||
|                 pinyin.append(ch); | ||||
|                 pinyin.append(ch); // 非汉字直接拼接(如字母、数字、符号) | ||||
|             } | ||||
|         } | ||||
|         return pinyin.toString(); | ||||
|     } | ||||
|  | ||||
|     // 新增:转换为拼音首字母(如"啊牛"→"an") | ||||
|     private String convertToPinyinFirstLetter(String chinese) { | ||||
|         HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); | ||||
|         format.setCaseType(HanyuPinyinCaseType.LOWERCASE); | ||||
|         format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); | ||||
|  | ||||
|         StringBuilder firstLetters = new StringBuilder(); | ||||
|         for (int i = 0; i < chinese.length(); i++) { | ||||
|             char ch = chinese.charAt(i); | ||||
|             if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) { // 仅处理汉字 | ||||
|                 try { | ||||
|                     String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format); | ||||
|                     if (pinyinArray != null && pinyinArray.length > 0) { | ||||
|                         // 取拼音的第一个字母(如"a"、"niu"→"a"、"n") | ||||
|                         firstLetters.append(pinyinArray[0].charAt(0)); | ||||
|                     } | ||||
|                 } catch (BadHanyuPinyinOutputFormatCombination e) { | ||||
|                     e.printStackTrace(); | ||||
|                 } | ||||
|             } else { | ||||
|                 // 非汉字可根据需求处理:此处保留原字符(如"李3"→"l3","张A"→"za") | ||||
|                 firstLetters.append(ch); | ||||
|             } | ||||
|         } | ||||
|         return firstLetters.toString(); | ||||
|     } | ||||
|  | ||||
|     // 新增:获取拼音首字母 | ||||
|     public String getPinyinFirstLetter() { | ||||
|         return pinyinFirstLetter; | ||||
|     } | ||||
|  | ||||
|     // 原有getter方法 | ||||
|     public String getName() { | ||||
|         return name; | ||||
|     } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.beans; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/13 07:06:13 | ||||
|  */ | ||||
| import android.util.JsonReader; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.beans; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/21 09:52:10 | ||||
|  * @Describe 电话黑名单规则 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.beans; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/24 18:47:11 | ||||
|  * @Describe 手机铃声设置参数类 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.beans; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/02 19:51:40 | ||||
|  * @Describe SettingsModel | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.bobulltoon; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/02 13:47:48 | ||||
|  * @Describe 汤姆猫管家 :使用 BoBullToon 项目,对通讯地址进行筛选判断的好朋友。 | ||||
|  */ | ||||
| @@ -11,6 +11,7 @@ import cc.winboll.studio.contacts.dun.Rules; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.io.File; | ||||
| import java.io.FileFilter; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| @@ -44,7 +45,7 @@ public class TomCat { | ||||
|         } | ||||
|         return _TomCat; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     public String getDefaultBobulltoonUrl() { | ||||
|         return mContext.getString(R.string.default_bobulltoon_url); | ||||
|     } | ||||
| @@ -123,7 +124,7 @@ public class TomCat { | ||||
|             } | ||||
|  | ||||
|             // 更新新文件 | ||||
|             if(downloadAndExtractZip(zipUrl, destinationFolder)) { | ||||
|             if (downloadAndExtractZip(zipUrl, destinationFolder)) { | ||||
|                 LogUtils.d(TAG, "ZIP 文件下载并解压成功。"); | ||||
|                 return true; | ||||
|             } | ||||
| @@ -154,12 +155,81 @@ public class TomCat { | ||||
|     File getWorkingFolder() { | ||||
|         return mContext.getExternalFilesDir(TAG); | ||||
|     } | ||||
| 	 | ||||
| 	public File getBoBullToonDataFolder() { | ||||
| 		File fCheckRoot = getWorkingFolder(); | ||||
| 		if (fCheckRoot == null || !fCheckRoot.exists()) { | ||||
| 			return fCheckRoot; | ||||
| 		} | ||||
|  | ||||
| 		// 递归查找符合条件的文件夹 | ||||
| 		File targetFolder = findTargetFolder(fCheckRoot); | ||||
| 		return targetFolder != null ? targetFolder : fCheckRoot; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * 递归查找同时包含LICENSE和README.md文件的文件夹 | ||||
| 	 */ | ||||
| 	private File findTargetFolder(File currentFolder) { | ||||
| 		// 检查当前文件夹是否符合条件 | ||||
| 		if (hasRequiredFiles(currentFolder)) { | ||||
| 			return currentFolder; | ||||
| 		} | ||||
|  | ||||
| 		// 查找子文件夹(Java 7不支持方法引用,用匿名内部类过滤) | ||||
| 		File[] subFolders = currentFolder.listFiles(new FileFilter() { | ||||
| 				@Override | ||||
| 				public boolean accept(File file) { | ||||
| 					return file.isDirectory(); // 仅保留子文件夹 | ||||
| 				} | ||||
| 			}); | ||||
|  | ||||
| 		if (subFolders != null) { | ||||
| 			for (File subFolder : subFolders) { | ||||
| 				File result = findTargetFolder(subFolder); | ||||
| 				if (result != null) { | ||||
| 					return result; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return null; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * 检查文件夹中是否同时存在LICENSE和README.md文件 | ||||
| 	 */ | ||||
| 	private boolean hasRequiredFiles(File folder) { | ||||
| 		if (folder == null || !folder.isDirectory()) { | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		// 检查两个文件是否同时存在且均为文件(非文件夹) | ||||
| 		File licenseFile = new File(folder, "LICENSE"); | ||||
| 		File readmeFile = new File(folder, "README.md"); | ||||
|  | ||||
| 		return licenseFile.exists() && licenseFile.isFile() | ||||
| 			&& readmeFile.exists() && readmeFile.isFile(); | ||||
| 	} | ||||
|  | ||||
| 	public void cleanBoBullToon() { | ||||
| 		String destinationFolder = getWorkingFolder().getPath(); // 替换为实际的目标文件夹路径 | ||||
| 		// 删除旧文件 | ||||
| 		File fOldFolder = new File(destinationFolder); | ||||
| 		if (fOldFolder.exists()) { | ||||
| 			deleteFolderRecursive(fOldFolder); | ||||
| 			fOldFolder.mkdirs(); | ||||
| 		} | ||||
|  | ||||
| 		ToastUtils.show("已清空 BoBullToon 数据!"); | ||||
| 		LogUtils.d(TAG, "已清空 BoBullToon 数据"); | ||||
| 	} | ||||
|  | ||||
|     public boolean loadPhoneBoBullToon() { | ||||
|         listPhoneBoBullToon.clear(); | ||||
|         File fBoBullToon = new File(getWorkingFolder(), "bobulltoon"); | ||||
|         File fBoBullToon = getBoBullToonDataFolder(); | ||||
|         if (fBoBullToon.exists()) { | ||||
|             LogUtils.d(TAG, String.format("getWorkingFolder() %s", getWorkingFolder())); | ||||
|             LogUtils.d(TAG, String.format("getBoBullToonDataFolder() %s", getWorkingFolder())); | ||||
|             for (File userFolder : fBoBullToon.listFiles()) { | ||||
|                 if (userFolder.isDirectory()) { | ||||
|                     for (File recordFile : userFolder.listFiles()) { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.dun; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/21 06:15:10 | ||||
|  * @Describe 云盾防御规则 | ||||
|  */ | ||||
| @@ -145,6 +145,14 @@ public class Rules { | ||||
|             LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect)); | ||||
|         } | ||||
|  | ||||
|         // 检验拨不通号码群 | ||||
|         if (!isDefend && MainService.isPhoneInBoBullToon(phoneNumber)) { | ||||
|             LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber)); | ||||
|             isDefend = true; | ||||
|             isConnect = false; | ||||
|             LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect)); | ||||
|         } | ||||
|  | ||||
|         // 查询通讯录是否有该联系人 | ||||
|         boolean isPhoneInContacts = ContactUtils.getInstance(mContext).isPhoneInContacts(mContext, phoneNumber); | ||||
|         if (!isDefend) { | ||||
| @@ -158,14 +166,6 @@ public class Rules { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 检验拨不通号码群 | ||||
|         if (!isDefend && MainService.isPhoneInBoBullToon(phoneNumber)) { | ||||
|             LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber)); | ||||
|             isDefend = true; | ||||
|             isConnect = false; | ||||
|             LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect)); | ||||
|         } | ||||
|  | ||||
|         // 正则匹配规则名单校验 | ||||
|         if (!isDefend) { | ||||
|             for (int i = 0; i < _PhoneConnectRuleModelList.size(); i++) { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.fragments; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/20 12:57:00 | ||||
|  * @Describe 拨号 | ||||
|  */ | ||||
| @@ -161,4 +161,12 @@ public class CallLogFragment extends Fragment { | ||||
|             _CallLogFragment.triggerUpdate(); | ||||
|         } | ||||
|     } | ||||
| 	 | ||||
| 	@Override | ||||
| 	public void onResume() { | ||||
| 		super.onResume(); | ||||
| 		//ToastUtils.show("onResume"); | ||||
| 		callLogAdapter.relaodContacts(); | ||||
| 		readCallLog();  // 窗口回显时更新通话记录 | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,15 +1,18 @@ | ||||
| package cc.winboll.studio.contacts.fragments; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Date 2025/02/20 12:57:50 | ||||
|  * @Describe 联系人 | ||||
|  * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> | ||||
|  * @Date 2025/08/30 14:32 | ||||
|  * @Describe 联系人视图 | ||||
|  */ | ||||
| import android.Manifest; | ||||
| import android.content.Intent; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.database.Cursor; | ||||
| import android.os.Bundle; | ||||
| import android.os.Handler; | ||||
| import android.os.Looper; | ||||
| import android.provider.ContactsContract; | ||||
| import android.text.Editable; | ||||
| import android.text.TextWatcher; | ||||
| @@ -27,24 +30,39 @@ import androidx.recyclerview.widget.RecyclerView; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.contacts.adapters.ContactAdapter; | ||||
| import cc.winboll.studio.contacts.beans.ContactModel; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import java.util.concurrent.ExecutorService; | ||||
| import java.util.concurrent.Executors; | ||||
|  | ||||
| public class ContactsFragment extends Fragment { | ||||
|  | ||||
|     public static final String TAG = "ContactsFragment"; | ||||
|  | ||||
|     private static final String ARG_PAGE = "ARG_PAGE"; | ||||
|     private int mPage; | ||||
|  | ||||
|     private static final int REQUEST_READ_CONTACTS = 1; | ||||
|  | ||||
|     private int mPage; | ||||
|     private RecyclerView recyclerView; | ||||
|     private ContactAdapter contactAdapter; | ||||
|     private List<ContactModel> contactList = new ArrayList<>(); | ||||
|     private List<ContactModel> originalContactList = new ArrayList<>(); | ||||
|     private EditText searchEditText; | ||||
|     private Button btnDial; | ||||
|     private boolean isViewInitialized = false; // 标记视图是否已初始化 | ||||
|  | ||||
|     // 静态缓存:全局复用联系人数据 | ||||
|     private static List<ContactModel> sCachedOriginalList = new ArrayList<ContactModel>(); | ||||
|     private static List<ContactModel> sCachedFilteredList = new ArrayList<ContactModel>(); | ||||
|  | ||||
|     // 当前页面数据容器 | ||||
|     private List<ContactModel> contactList = new ArrayList<ContactModel>(); | ||||
|     private List<ContactModel> originalContactList = new ArrayList<ContactModel>(); | ||||
|  | ||||
|     // 异步工具 | ||||
|     private final ExecutorService executor = Executors.newSingleThreadExecutor(); | ||||
|     private final Handler mainHandler = new Handler(Looper.getMainLooper()); | ||||
|     private boolean isDataLoaded = false; | ||||
|  | ||||
|  | ||||
|     public static ContactsFragment newInstance(int page) { | ||||
|         Bundle args = new Bundle(); | ||||
| @@ -65,103 +83,272 @@ public class ContactsFragment extends Fragment { | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { | ||||
|         return inflater.inflate(R.layout.fragment_contacts, container, false); | ||||
|         // 加载布局(已移除进度条相关代码) | ||||
|         View view = inflater.inflate(R.layout.fragment_contacts, container, false); | ||||
|         return view; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { | ||||
|         super.onViewCreated(view, savedInstanceState); | ||||
|         recyclerView = view.findViewById(R.id.contacts_recycler_view); | ||||
|         // 初始化RecyclerView | ||||
|         recyclerView = (RecyclerView) view.findViewById(R.id.contacts_recycler_view); | ||||
|         recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); | ||||
|         contactAdapter = new ContactAdapter(contactList); | ||||
|         contactList = new ArrayList<ContactModel>(); | ||||
|         contactAdapter = new ContactAdapter(getContext(), contactList); | ||||
|         recyclerView.setAdapter(contactAdapter); | ||||
|         // 初始隐藏列表,数据加载后显示 | ||||
|         recyclerView.setVisibility(View.GONE); | ||||
|  | ||||
|         searchEditText = view.findViewById(R.id.search_edit_text); | ||||
|         searchEditText.addTextChangedListener(new TextWatcher() { | ||||
|                 @Override | ||||
|                 public void beforeTextChanged(CharSequence s, int start, int count, int after) { | ||||
|                 } | ||||
|         // 绑定搜索框和拨号按钮 | ||||
|         searchEditText = (EditText) view.findViewById(R.id.search_edit_text); | ||||
|         btnDial = (Button) view.findViewById(R.id.btn_dial); | ||||
|         // 初始隐藏搜索相关控件,延迟到首次可见时显示 | ||||
|         searchEditText.setVisibility(View.GONE); | ||||
|         btnDial.setVisibility(View.GONE); | ||||
|     } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onTextChanged(CharSequence s, int start, int before, int count) { | ||||
|                     filterContacts(s.toString()); | ||||
|                 } | ||||
|     // 首次可见时初始化资源 | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
|         if (!isViewInitialized) { | ||||
|             initSearchAndDial(); // 初始化搜索和拨号功能 | ||||
|             checkContactPermission(); // 检查权限并加载数据 | ||||
|             isViewInitialized = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|                 @Override | ||||
|                 public void afterTextChanged(Editable s) { | ||||
|                 } | ||||
|             }); | ||||
|     // 初始化搜索框和拨号按钮 | ||||
|     private void initSearchAndDial() { | ||||
|         // 显示搜索相关控件 | ||||
|         searchEditText.setVisibility(View.VISIBLE); | ||||
|         btnDial.setVisibility(View.VISIBLE); | ||||
|  | ||||
|         // 搜索框防抖监听 | ||||
|         searchEditText.addTextChangedListener(new DebounceTextWatcher(300) { | ||||
| 				@Override | ||||
| 				public void onDebounceTextChanged(String query) { | ||||
| 					filterContacts(query); | ||||
| 				} | ||||
| 			}); | ||||
|  | ||||
|         // 拨号按钮点击事件 | ||||
|         btnDial.setOnClickListener(new View.OnClickListener() { | ||||
| 				@Override | ||||
| 				public void onClick(View v) { | ||||
| 					String phoneNumber = searchEditText.getText().toString().replaceAll("\\s", ""); | ||||
| 					if (phoneNumber.isEmpty()) { | ||||
| 						ToastUtils.show("请输入号码"); | ||||
| 						return; | ||||
| 					} | ||||
| 					Intent intent = new Intent(Intent.ACTION_CALL); | ||||
| 					intent.setData(android.net.Uri.parse("tel:" + phoneNumber)); | ||||
| 					intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
| 					startActivity(intent); | ||||
| 				} | ||||
| 			}); | ||||
|     } | ||||
|  | ||||
|     // 权限检查 | ||||
|     private void checkContactPermission() { | ||||
|         if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { | ||||
|             ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_READ_CONTACTS); | ||||
|         } else { | ||||
|             readContacts(); | ||||
|             loadContacts(); | ||||
|         } | ||||
|  | ||||
|         Button btnDial = view.findViewById(R.id.btn_dial); | ||||
|         btnDial.setOnClickListener(new View.OnClickListener(){ | ||||
|                 @Override | ||||
|                 public void onClick(View p1) { | ||||
|  | ||||
|                     String phoneNumber = searchEditText.getText().toString().replaceAll("\\s", ""); | ||||
|                     //phoneNumber = "+8616769764848"; | ||||
|                     ToastUtils.show(phoneNumber); | ||||
|                     Intent intent = new Intent(Intent.ACTION_CALL); | ||||
|                     intent.setData(android.net.Uri.parse("tel:" + phoneNumber)); | ||||
|                     // 添加 FLAG_ACTIVITY_NEW_TASK 标志 | ||||
|                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|                     startActivity(intent); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     // 加载联系人(延迟到首次可见时) | ||||
|     private void loadContacts() { | ||||
|         // 若有缓存,直接复用 | ||||
|         if (!sCachedOriginalList.isEmpty() && !sCachedFilteredList.isEmpty()) { | ||||
|             originalContactList.clear(); | ||||
|             originalContactList.addAll(sCachedOriginalList); | ||||
|             contactList.clear(); | ||||
|             contactList.addAll(sCachedFilteredList); | ||||
|             contactAdapter.notifyDataSetChanged(); | ||||
|             recyclerView.setVisibility(View.VISIBLE); // 显示列表 | ||||
|             isDataLoaded = true; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // 无缓存时异步加载 | ||||
|         if (!isDataLoaded) { | ||||
|             recyclerView.setVisibility(View.GONE); // 加载中隐藏列表 | ||||
|  | ||||
|             executor.execute(new Runnable() { | ||||
| 					@Override | ||||
| 					public void run() { | ||||
| 						// 子线程读取联系人 | ||||
| 						final List<ContactModel> tempList = readContactsInBackground(); | ||||
|  | ||||
| 						// 主线程更新UI | ||||
| 						mainHandler.post(new Runnable() { | ||||
| 								@Override | ||||
| 								public void run() { | ||||
| 									// 更新缓存 | ||||
| 									sCachedOriginalList.clear(); | ||||
| 									sCachedOriginalList.addAll(tempList); | ||||
| 									sCachedFilteredList.clear(); | ||||
| 									sCachedFilteredList.addAll(tempList); | ||||
|  | ||||
| 									// 更新当前列表 | ||||
| 									originalContactList.clear(); | ||||
| 									originalContactList.addAll(sCachedOriginalList); | ||||
| 									contactList.clear(); | ||||
| 									contactList.addAll(sCachedFilteredList); | ||||
| 									contactAdapter.notifyDataSetChanged(); | ||||
| 									LogUtils.d(TAG, String.format("联系人加载完成,共%d条数据", contactList.size())); | ||||
|  | ||||
| 									// 数据加载后显示列表 | ||||
| 									recyclerView.setVisibility(View.VISIBLE); | ||||
| 									isDataLoaded = true; | ||||
| 								} | ||||
| 							}); | ||||
| 					} | ||||
| 				}); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // 子线程读取联系人 | ||||
|     private List<ContactModel> readContactsInBackground() { | ||||
|         List<ContactModel> tempList = new ArrayList<ContactModel>(); | ||||
|         Cursor cursor = null; | ||||
|         try { | ||||
|             // 查询联系人姓名和号码 | ||||
|             cursor = requireContext().getContentResolver().query( | ||||
|                 ContactsContract.CommonDataKinds.Phone.CONTENT_URI, | ||||
|                 new String[]{ | ||||
|                     ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, | ||||
|                     ContactsContract.CommonDataKinds.Phone.NUMBER | ||||
|                 }, | ||||
|                 null, | ||||
|                 null, | ||||
|                 ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC" | ||||
|             ); | ||||
|  | ||||
|             if (cursor != null && cursor.moveToFirst()) { | ||||
|                 int nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME); | ||||
|                 int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER); | ||||
|  | ||||
|                 do { | ||||
|                     String name = cursor.getString(nameIndex); | ||||
|                     String number = cursor.getString(numberIndex).replaceAll("\\s", ""); // 去除空格 | ||||
|                     tempList.add(new ContactModel(name, number)); | ||||
|                 } while (cursor.moveToNext()); | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
|             LogUtils.d(TAG, "读取联系人失败:" + e); | ||||
|         } finally { | ||||
|             if (cursor != null) { | ||||
|                 cursor.close(); // 关闭游标,避免内存泄漏 | ||||
|             } | ||||
|         } | ||||
|         return tempList; | ||||
|     } | ||||
|  | ||||
|     // 过滤联系人 | ||||
|     private void filterContacts(String query) { | ||||
|         contactList.clear(); | ||||
|         if (query.isEmpty()) { | ||||
|             contactList.addAll(originalContactList); | ||||
|             sCachedFilteredList.clear(); | ||||
|             sCachedFilteredList.addAll(originalContactList); | ||||
|         } else { | ||||
|             String lowerQuery = query.toLowerCase(); | ||||
|             for (ContactModel contact : originalContactList) { | ||||
|                 // 匹配姓名、全拼、简拼、号码 | ||||
|                 boolean matchName = contact.getName().toLowerCase().contains(lowerQuery); | ||||
|                 boolean matchPinyin = contact.getPinyin().toLowerCase().contains(lowerQuery); | ||||
|                 boolean matchFirstLetter = contact.getPinyinFirstLetter().toLowerCase().contains(lowerQuery); | ||||
|                 boolean matchNumber = contact.getNumber().contains(lowerQuery); | ||||
|  | ||||
|                 if (matchName || matchPinyin || matchFirstLetter || matchNumber) { | ||||
|                     contactList.add(contact); | ||||
|                 } | ||||
|             } | ||||
|             sCachedFilteredList.clear(); | ||||
|             sCachedFilteredList.addAll(contactList); | ||||
|         } | ||||
|         contactAdapter.notifyDataSetChanged(); | ||||
|         // 过滤后确保列表可见 | ||||
|         recyclerView.setVisibility(View.VISIBLE); | ||||
|     } | ||||
|  | ||||
|     // 权限回调 | ||||
|     @Override | ||||
|     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | ||||
|         super.onRequestPermissionsResult(requestCode, permissions, grantResults); | ||||
|         if (requestCode == REQUEST_READ_CONTACTS) { | ||||
|             if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { | ||||
|                 readContacts(); | ||||
|                 loadContacts(); // 授权后加载联系人 | ||||
|             } else { | ||||
|                 ToastUtils.show("请授予联系人权限以查看联系人列表"); | ||||
|                 recyclerView.setVisibility(View.VISIBLE); // 显示空列表 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void readContacts() { | ||||
|         contactList.clear(); | ||||
|         originalContactList.clear(); | ||||
|         Cursor cursor = requireContext().getContentResolver().query( | ||||
|             ContactsContract.CommonDataKinds.Phone.CONTENT_URI, | ||||
|             null, | ||||
|             null, | ||||
|             null, | ||||
|             ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC"); | ||||
|     // 防抖TextWatcher(Java 7实现) | ||||
|     public abstract static class DebounceTextWatcher implements TextWatcher { | ||||
|         private final long debounceDelay; | ||||
|         private Handler handler = new Handler(Looper.getMainLooper()); | ||||
|         private Runnable pendingRunnable; | ||||
|  | ||||
|         if (cursor != null) { | ||||
|             while (cursor.moveToNext()) { | ||||
|                 String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); | ||||
|                 String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); | ||||
|                 ContactModel contact = new ContactModel(name, number); | ||||
|                 contactList.add(contact); | ||||
|                 originalContactList.add(contact); | ||||
|             } | ||||
|             cursor.close(); | ||||
|             contactAdapter.notifyDataSetChanged(); | ||||
|         public DebounceTextWatcher(long debounceDelay) { | ||||
|             this.debounceDelay = debounceDelay; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void filterContacts(String query) { | ||||
|         contactList.clear(); | ||||
|         if (query.isEmpty()) { | ||||
|             contactList.addAll(originalContactList); | ||||
|         } else { | ||||
|             for (ContactModel contact : originalContactList) { | ||||
|                 if (contact.getName().toLowerCase().contains(query.toLowerCase()) || | ||||
|                     contact.getPinyin().toLowerCase().contains(query.toLowerCase()) || | ||||
|                     contact.getNumber().toLowerCase().contains(query.toLowerCase())) { | ||||
|                     contactList.add(contact); | ||||
|         @Override | ||||
|         public void beforeTextChanged(CharSequence s, int start, int count, int after) { | ||||
|             // 无需处理 | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void onTextChanged(final CharSequence s, int start, int before, int count) { | ||||
|             // 移除之前的延迟任务 | ||||
|             if (pendingRunnable != null) { | ||||
|                 handler.removeCallbacks(pendingRunnable); | ||||
|             } | ||||
|             // 延迟执行过滤 | ||||
|             pendingRunnable = new Runnable() { | ||||
|                 @Override | ||||
|                 public void run() { | ||||
|                     onDebounceTextChanged(s.toString()); | ||||
|                 } | ||||
|             } | ||||
|             }; | ||||
|             handler.postDelayed(pendingRunnable, debounceDelay); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void afterTextChanged(Editable s) { | ||||
|             // 无需处理 | ||||
|         } | ||||
|  | ||||
|         // 抽象方法:防抖后的回调 | ||||
|         public abstract void onDebounceTextChanged(String query); | ||||
|     } | ||||
|  | ||||
|     // 资源释放 | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         executor.shutdown(); // 关闭线程池 | ||||
|         mainHandler.removeCallbacksAndMessages(null); // 清除未执行任务 | ||||
|     } | ||||
|  | ||||
|     // Fragment隐藏/显示时的处理 | ||||
|     @Override | ||||
|     public void onHiddenChanged(boolean hidden) { | ||||
|         super.onHiddenChanged(hidden); | ||||
|         if (!hidden && isDataLoaded) { | ||||
|             // 复用缓存数据并显示列表 | ||||
|             contactList.clear(); | ||||
|             contactList.addAll(sCachedFilteredList); | ||||
|             contactAdapter.notifyDataSetChanged(); | ||||
|             recyclerView.setVisibility(View.VISIBLE); | ||||
|         } | ||||
|         contactAdapter.notifyDataSetChanged(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.fragments; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/20 12:58:15 | ||||
|  * @Describe 应用日志 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.handlers; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/14 03:51:40 | ||||
|  */ | ||||
| import android.os.Handler; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.receivers; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/13 06:58:04 | ||||
|  * @Describe 主要广播接收器 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.services; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/14 03:38:31 | ||||
|  * @Describe 守护进程服务 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.services; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/13 06:56:41 | ||||
|  * @Describe 拨号主服务 | ||||
|  * 参考: | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.threads; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/14 03:46:44 | ||||
|  */ | ||||
| import android.content.Context; | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| package cc.winboll.studio.contacts.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Date 2025/03/06 21:08:16 | ||||
|  * @Describe ContactUtils | ||||
|  * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> | ||||
|  * @Date 2025/08/30 14:32 | ||||
|  * @Describe 联系人工具集 | ||||
|  */ | ||||
| import android.content.ContentResolver; | ||||
| import android.content.Context; | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import android.widget.EditText; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/04/13 00:59:13 | ||||
|  * @Describe Int类型数字输入框工具集 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/04/13 01:16:28 | ||||
|  * @Describe Int数字操作工具集 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/26 15:21:48 | ||||
|  * @Describe PhoneUtils | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2024/12/09 19:00:21 | ||||
|  * @Describe .* 前置预防针 | ||||
|  regex pointer preventive injection | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.views; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/02 21:11:03 | ||||
|  * @Describe 云盾防御信息 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.views; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/04 10:51:50 | ||||
|  * @Describe CustomHorizontalScrollView | ||||
|  */ | ||||
| @@ -47,8 +47,8 @@ public class LeftScrollView extends HorizontalScrollView { | ||||
|         init(); | ||||
|     } | ||||
|  | ||||
|     public void addContentLayout(TextView textView) { | ||||
|         contentLayout.addView(textView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); | ||||
|     public void addContentLayout(View viewContent) { | ||||
|         contentLayout.addView(viewContent, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); | ||||
|     } | ||||
|  | ||||
|     public void setContentWidth(int contentWidth) { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.views; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/03/19 14:04:20 | ||||
|  * @Describe 云盾滑视度热备控件 | ||||
|  */ | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package cc.winboll.studio.contacts.widgets; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Author ZhanGSKen<zhangsken@qq.com> | ||||
|  * @Date 2025/02/17 14:49:31 | ||||
|  * @Describe APPStatusWidget | ||||
|  */ | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user