Compare commits
	
		
			231 Commits
		
	
	
		
			appbase-v1
			...
			23920a7ff1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					23920a7ff1 | ||
| 
						 | 
					17c373c490 | ||
| 
						 | 
					5f7c94b349 | ||
| 
						 | 
					c2b739d345 | ||
| 
						 | 
					67a05cd457 | ||
| 
						 | 
					554ab758bf | ||
| 
						 | 
					20e118cd34 | ||
| 
						 | 
					f370ae8ffb | ||
| 
						 | 
					c92c874ea1 | ||
| 
						 | 
					90a6116c0a | ||
| 
						 | 
					45208ecbb1 | ||
| 
						 | 
					c28d655fe3 | ||
| 
						 | 
					4b5905f74e | ||
| 
						 | 
					6bd01780ec | ||
| 
						 | 
					a6699262f8 | ||
| 
						 | 
					ea2d38defc | ||
| 
						 | 
					e430b7abe4 | ||
| 
						 | 
					945eadb617 | ||
| 
						 | 
					c5bffc5eef | ||
| 
						 | 
					88597fe407 | ||
| 
						 | 
					53f985533a | ||
| 
						 | 
					a3950f13ad | ||
| 
						 | 
					c878e9dc02 | ||
| 
						 | 
					f2f7cab330 | ||
| 
						 | 
					6c8b0dcfa5 | ||
| 
						 | 
					7de8a4f084 | ||
| 
						 | 
					219c6614be | ||
| 
						 | 
					0f5bb020b9 | ||
| 
						 | 
					7794ff80ec | ||
| 
						 | 
					7463ad3352 | ||
| 
						 | 
					753032efed | ||
| 
						 | 
					2b4c43c9af | ||
| 
						 | 
					711c98d556 | ||
| 
						 | 
					202205588a | ||
| 
						 | 
					42c4978b44 | ||
| 
						 | 
					1a2b7b862d | ||
| 
						 | 
					eb253b374f | ||
| 
						 | 
					c4e88e9593 | ||
| 
						 | 
					08d9d92ae4 | ||
| 
						 | 
					74841c08dc | ||
| 
						 | 
					945bacb825 | ||
| 
						 | 
					0e464495fd | ||
| 
						 | 
					e8682ce410 | ||
| 
						 | 
					2e4003dae0 | ||
| 
						 | 
					198b0975ce | ||
| 
						 | 
					24a578a9d2 | ||
| 
						 | 
					46de24447f | ||
| 
						 | 
					ac1c008035 | ||
| 
						 | 
					b124487cb1 | ||
| 
						 | 
					9621d35f79 | ||
| 
						 | 
					17de0832a6 | ||
| 
						 | 
					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 | ||
| 
						 | 
					063c997bbb | ||
| 
						 | 
					1376ca7ebb | ||
| 
						 | 
					92e271b569 | ||
| 
						 | 
					a5083cc52f | ||
| 
						 | 
					0692d4efb2 | ||
| 
						 | 
					f10438d3d3 | ||
| 
						 | 
					1e697bc12d | ||
| 
						 | 
					d5a3c626b3 | ||
| 
						 | 
					56692db142 | ||
| 
						 | 
					934d54963a | ||
| 
						 | 
					c105123e7b | ||
| 
						 | 
					d8c534bbc8 | ||
| 
						 | 
					6cce9c4d3f | ||
| 
						 | 
					df18c34976 | ||
| 
						 | 
					22ca83b5b7 | ||
| 
						 | 
					98233ce148 | ||
| 
						 | 
					41fd9b171b | ||
| 
						 | 
					330e5032df | ||
| 
						 | 
					76420ff5f4 | ||
| 
						 | 
					ee3553716c | ||
| 
						 | 
					02da4c3db9 | ||
| 
						 | 
					d689db61ff | ||
| 
						 | 
					2b090e5c8e | ||
| 
						 | 
					309214ecf3 | ||
| 
						 | 
					d29c9de3b4 | ||
| 
						 | 
					d7efdb6c02 | ||
| 
						 | 
					20b36dd63b | ||
| 
						 | 
					a40450c085 | ||
| 
						 | 
					e7a7a4438c | ||
| 
						 | 
					3e6de13fa3 | ||
| 
						 | 
					af848809e9 | ||
| 
						 | 
					2c7026a635 | ||
| 
						 | 
					6278c3f945 | ||
| 
						 | 
					bc3a320433 | ||
| 
						 | 
					e03b8d4745 | ||
| 
						 | 
					d5fb348134 | ||
| 
						 | 
					5c7437b19c | ||
| 
						 | 
					bf09def10b | ||
| 
						 | 
					8431d7dfa9 | ||
| 
						 | 
					6aadaf87af | ||
| 
						 | 
					6a2943420f | ||
| 
						 | 
					dc704e292a | ||
| 
						 | 
					1087a7837c | ||
| 
						 | 
					59876a2353 | ||
| 
						 | 
					053b45e87e | ||
| 
						 | 
					58e49ed371 | ||
| 
						 | 
					90a6eb266b | ||
| 
						 | 
					f0a564c9bb | ||
| 
						 | 
					46b21331af | ||
| 
						 | 
					6221cc30ac | ||
| 
						 | 
					8489ee6447 | ||
| 
						 | 
					9205af4aa9 | ||
| 
						 | 
					780e730a38 | ||
| 
						 | 
					429499885b | ||
| 
						 | 
					fcf4ad2fc0 | ||
| 
						 | 
					7635f6f084 | ||
| 
						 | 
					e49935f772 | ||
| 
						 | 
					c687b5513d | ||
| 
						 | 
					7c7554133b | ||
| 
						 | 
					98f6d0019b | ||
| 
						 | 
					fc9c82921d | ||
| 
						 | 
					1ac0ad96e1 | ||
| 
						 | 
					9060cb51ff | ||
| 
						 | 
					7951d0a8d6 | ||
| 
						 | 
					ded775ae01 | ||
| 
						 | 
					82258f1763 | ||
| 
						 | 
					99bf53f27d | ||
| 
						 | 
					ddb6b53189 | ||
| 
						 | 
					ae8da772c0 | ||
| 
						 | 
					b2555ebfd3 | ||
| 
						 | 
					82d9aabf33 | ||
| 
						 | 
					e045bbb71c | ||
| 
						 | 
					d5782da040 | ||
| 
						 | 
					840da14438 | ||
| 
						 | 
					31595fe1ae | ||
| 
						 | 
					2c91d32e41 | ||
| 
						 | 
					58dabb7138 | ||
| 
						 | 
					3432575aa7 | ||
| 
						 | 
					fa43d43110 | ||
| 
						 | 
					8f8e7d98c7 | ||
| 
						 | 
					af39f98a51 | ||
| 
						 | 
					5a1d557683 | ||
| 
						 | 
					39116f0912 | ||
| 
						 | 
					a6b711b38b | ||
| 
						 | 
					d1703551f5 | ||
| 
						 | 
					d63f7d3d83 | ||
| 
						 | 
					06b399846d | ||
| 
						 | 
					e06efea08e | ||
| 
						 | 
					890ff6eda9 | ||
| 
						 | 
					e5a5eda9b6 | ||
| 
						 | 
					174a052088 | ||
| 
						 | 
					fbd8441264 | ||
| 
						 | 
					fb92e9f673 | ||
| 
						 | 
					d34d1e2796 | ||
| 
						 | 
					36fb8b41a4 | ||
| 
						 | 
					fe70a18547 | ||
| 
						 | 
					b61c63c426 | ||
| 
						 | 
					f02dc215ca | ||
| 
						 | 
					1c27d0ccdc | ||
| 
						 | 
					803745d12e | ||
| 
						 | 
					a66be9cd37 | ||
| 
						 | 
					46e4ee7fb7 | ||
| 
						 | 
					b2b959232c | ||
| 
						 | 
					b11f814c41 | ||
| 
						 | 
					1e991aed7e | ||
| 
						 | 
					f56125f82a | ||
| 
						 | 
					33b7b65239 | ||
| 
						 | 
					664d14ad84 | ||
| 
						 | 
					47cb393f76 | ||
| 
						 | 
					6495f1c66e | ||
| 
						 | 
					f0c52d1e02 | ||
| 
						 | 
					d948f31331 | ||
| 
						 | 
					e1b3087020 | ||
| 
						 | 
					03e21ab81c | ||
| 
						 | 
					ac72132969 | ||
| 
						 | 
					cedb5f521b | ||
| 
						 | 
					396df6713c | 
							
								
								
									
										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,11 @@
 | 
			
		||||
 | 
			
		||||
# 本项目要实际运用需要注意以下几个步骤:
 | 
			
		||||
# 在项目根目录下:
 | 
			
		||||
## 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,并取消相应项目模块的注释。
 | 
			
		||||
## ★. 项目 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.6" 
 | 
			
		||||
        versionName "15.9" 
 | 
			
		||||
        if(true) {
 | 
			
		||||
            versionName = genVersionName("${versionName}")
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Mon May 12 18:10:35 HKT 2025
 | 
			
		||||
stageCount=4
 | 
			
		||||
#Sat Jun 28 12:59:51 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
libraryProject=libaes
 | 
			
		||||
baseVersion=15.6
 | 
			
		||||
publishVersion=15.6.3
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.2
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.6.4
 | 
			
		||||
baseBetaVersion=15.9.3
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ android {
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId "cc.winboll.studio.androiddemo"
 | 
			
		||||
        minSdkVersion 24
 | 
			
		||||
        targetSdkVersion 29
 | 
			
		||||
        targetSdkVersion 30
 | 
			
		||||
        versionCode 1
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // .winboll/winbollBuildProps.properties 文件的 stageCount=0
 | 
			
		||||
@@ -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.2.2'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.2.2'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.2'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.2'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Thu Apr 03 03:17:18 GMT 2025
 | 
			
		||||
#Sun Jun 01 08:02:46 GMT 2025
 | 
			
		||||
stageCount=0
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.0
 | 
			
		||||
publishVersion=15.0.0
 | 
			
		||||
buildCount=21
 | 
			
		||||
buildCount=27
 | 
			
		||||
baseBetaVersion=15.0.1
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ android {
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId "cc.winboll.studio.androidxdemo"
 | 
			
		||||
        minSdkVersion 24
 | 
			
		||||
        targetSdkVersion 29
 | 
			
		||||
        targetSdkVersion 30
 | 
			
		||||
        versionCode 1
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // .winboll/winbollBuildProps.properties 文件的 stageCount=0
 | 
			
		||||
@@ -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.2.6'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.2.2'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.2.2'
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.8.0'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.2'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.2'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Thu Apr 03 03:15:55 GMT 2025
 | 
			
		||||
#Thu Jun 19 12:49:47 GMT 2025
 | 
			
		||||
stageCount=0
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.0
 | 
			
		||||
publishVersion=15.0.0
 | 
			
		||||
buildCount=18
 | 
			
		||||
buildCount=26
 | 
			
		||||
baseBetaVersion=15.0.1
 | 
			
		||||
 
 | 
			
		||||
@@ -22,9 +22,9 @@ import android.widget.HorizontalScrollView;
 | 
			
		||||
import android.widget.ScrollView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
import cc.winboll.studio.androidxdemo.R;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import com.hjq.toast.style.WhiteToastStyle;
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.io.Closeable;
 | 
			
		||||
@@ -53,8 +53,8 @@ public class App extends GlobalApplication {
 | 
			
		||||
        // 初始化 Toast 框架
 | 
			
		||||
        ToastUtils.init(this);
 | 
			
		||||
        // 设置 Toast 布局样式
 | 
			
		||||
        ToastUtils.setView(R.layout.view_toast);
 | 
			
		||||
        //ToastUtils.setStyle(new WhiteToastStyle());
 | 
			
		||||
        //ToastUtils.setView(R.layout.view_toast);
 | 
			
		||||
        ToastUtils.setStyle(new WhiteToastStyle());
 | 
			
		||||
        ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
 | 
			
		||||
        
 | 
			
		||||
        //CrashHandler.getInstance().registerGlobal(this);
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ android {
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // .winboll/winbollBuildProps.properties 文件的 stageCount=0
 | 
			
		||||
        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
			
		||||
        versionName "15.7"
 | 
			
		||||
        versionName "15.9"
 | 
			
		||||
        if(true) {
 | 
			
		||||
            versionName = genVersionName("${versionName}")
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue May 13 10:28:56 HKT 2025
 | 
			
		||||
stageCount=8
 | 
			
		||||
#Thu Jul 17 11:39:14 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=libappbase
 | 
			
		||||
baseVersion=15.7
 | 
			
		||||
publishVersion=15.7.7
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.7.8
 | 
			
		||||
baseBetaVersion=15.9.2
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,6 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Toolbar mToolbar;
 | 
			
		||||
    LogView mLogView;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
@@ -53,19 +52,21 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
        CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
 | 
			
		||||
        cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
 | 
			
		||||
        mLogView = findViewById(R.id.logview);
 | 
			
		||||
        mLogView.start(); 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onCreateOptionsMenu(Menu menu) {
 | 
			
		||||
        getMenuInflater().inflate(R.menu.toolbar_main, menu);
 | 
			
		||||
        getMenuInflater().inflate(R.menu.toolbar_appbase, menu);
 | 
			
		||||
        return super.onCreateOptionsMenu(menu);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @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);
 | 
			
		||||
    }
 | 
			
		||||
@@ -176,8 +177,7 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onResume() {
 | 
			
		||||
        super.onResume();
 | 
			
		||||
        mLogView.start();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -66,16 +66,11 @@ public class New2Activity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onCreateOptionsMenu(Menu menu) {
 | 
			
		||||
        getMenuInflater().inflate(R.menu.toolbar_main, menu);
 | 
			
		||||
        getMenuInflater().inflate(R.menu.toolbar_appbase, menu);
 | 
			
		||||
        return super.onCreateOptionsMenu(menu);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onOptionsItemSelected(MenuItem item) {
 | 
			
		||||
        if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
 | 
			
		||||
            //GlobalApplication.getWinBoLLActivityManager().startLogActivity(this);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
 | 
			
		||||
        return super.onOptionsItemSelected(item);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -64,16 +64,11 @@ public class NewActivity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onCreateOptionsMenu(Menu menu) {
 | 
			
		||||
        getMenuInflater().inflate(R.menu.toolbar_main, menu);
 | 
			
		||||
        getMenuInflater().inflate(R.menu.toolbar_appbase, menu);
 | 
			
		||||
        return super.onCreateOptionsMenu(menu);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onOptionsItemSelected(MenuItem item) {
 | 
			
		||||
        if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
 | 
			
		||||
            //GlobalApplication.getWinBoLLActivityManager().startLogActivity(this);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
 | 
			
		||||
        return super.onOptionsItemSelected(item);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,12 @@ package cc.winboll.studio.appbase.activities;
 | 
			
		||||
 * @Describe WinBoLL 窗口基础类
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
import cc.winboll.studio.appbase.MainActivity;
 | 
			
		||||
import cc.winboll.studio.appbase.R;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
 | 
			
		||||
public class WinBoLLActivity extends Activity implements IWinBoLLActivity {
 | 
			
		||||
@@ -21,4 +27,35 @@ public class WinBoLLActivity extends Activity implements IWinBoLLActivity {
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onResume() {
 | 
			
		||||
        super.onResume();
 | 
			
		||||
        LogUtils.d(TAG, String.format("onResume %s", getTag()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onOptionsItemSelected(MenuItem item) {
 | 
			
		||||
        if (item.getItemId() == R.id.item_log) {
 | 
			
		||||
            GlobalApplication.getWinBoLLActivityManager().startLogActivity(this);
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (item.getItemId() == R.id.item_home) {
 | 
			
		||||
            GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
 | 
			
		||||
        return super.onOptionsItemSelected(item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onPostCreate(savedInstanceState);
 | 
			
		||||
        GlobalApplication.getWinBoLLActivityManager().add(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onDestroy() {
 | 
			
		||||
        super.onDestroy();
 | 
			
		||||
        GlobalApplication.getWinBoLLActivityManager().registeRemove(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,13 +32,19 @@
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="Hello, WinBoLL!"/>
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="Android版本10的代号是“Q”,API级别是29。   Android 10开始谷歌不再公开使用甜品作为版本代号,但内部仍保留了大量与“Q”相关的元素。Android 10本身并没有严格对应某个特定的Java版本,但在开发Android 10应用时,通常可以使用Java 8或更高版本。   Java 8为Android开发带来了诸如Lambda表达式、方法引用等新特性,能提高开发效率和代码可读性,与Android 10开发适配良好。Java 9及更高版本也可用于Android 10开发,能使用一些新的语言特性和API,但可能需要注意兼容性和配置问题。"/>
 | 
			
		||||
					android:text="安卓R对应的是Android 11,其API级别是30。以下是Android 11中一些重要的API相关特性:
 | 
			
		||||
 | 
			
		||||
                    \n- 隐私保护方面:引入单次授权,让用户可以选择授予应用对位置信息、麦克风和摄像头的临时访问权限。还增加了数据访问审核功能,能让开发者深入了解应用在何处访问私密数据。
 | 
			
		||||
                    
 | 
			
		||||
                    \n- 系统功能方面:提供了ControlsProviderService API,用于向连接的外部设备提供控件,这些控件显示于Android电源菜单中的设备控件下。媒体控件也得到更新,显示于快捷设置旁,来自多个应用的会话排列在一个可滑动的轮播界面中。
 | 
			
		||||
                    
 | 
			
		||||
                    \n- 硬件支持方面:提供了一些API以支持瀑布屏,通过将窗口布局属性  layoutInDisplayCutoutMode  设为  LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS ,可允许窗口延伸到屏幕各个边缘上的刘海和瀑布区域。对于合页式屏幕配置的设备,提供了具有  TYPE_HINGE_ANGLE  的新传感器以及新的  SensorEvent ,用于监控合页角度。
 | 
			
		||||
                    
 | 
			
		||||
                    \n- 安全方面:对生物识别身份验证机制进行了更新,引入了  BiometricManager.Authenticators  接口,定义了  BIOMETRIC_STRONG 、 BIOMETRIC_WEAK 、 DEVICE_CREDENTIAL  等身份验证类型。还在  BiometricPrompt  类中提供了对“每次使用时进行身份验证”密钥的更多支持。
 | 
			
		||||
                    
 | 
			
		||||
                    \n- 性能和质量方面:支持无线调试,通过Android调试桥(adb)从工作站以无线方式部署和调试应用,避免了常见的USB连接问题。"/>
 | 
			
		||||
                
 | 
			
		||||
                    
 | 
			
		||||
				<HorizontalScrollView
 | 
			
		||||
					android:layout_width="match_parent"
 | 
			
		||||
					android:layout_height="wrap_content">
 | 
			
		||||
@@ -211,11 +217,5 @@
 | 
			
		||||
		</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	</ScrollView>
 | 
			
		||||
 | 
			
		||||
	<cc.winboll.studio.libappbase.LogView
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="300dp"
 | 
			
		||||
		android:id="@+id/logview"/>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,20 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_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"
 | 
			
		||||
        android:icon="@drawable/ic_winboll_log"/>
 | 
			
		||||
</menu>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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>
 | 
			
		||||
@@ -29,7 +29,7 @@ android {
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // 项目模块目录的 build.gradle 文件的 stageCount=0
 | 
			
		||||
        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
			
		||||
        versionName "15.3" 
 | 
			
		||||
        versionName "15.8" 
 | 
			
		||||
        if(true) {
 | 
			
		||||
            versionName = genVersionName("${versionName}")
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue Apr 29 15:04:17 HKT 2025
 | 
			
		||||
#Tue Jun 03 15:05:48 HKT 2025
 | 
			
		||||
stageCount=5
 | 
			
		||||
libraryProject=libapputils
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.4
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.4
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.3.5
 | 
			
		||||
baseBetaVersion=15.8.5
 | 
			
		||||
 
 | 
			
		||||
@@ -20,18 +20,7 @@ import cc.winboll.studio.libapputils.views.SimpleWebView;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
 | 
			
		||||
public class AssetsHtmlActivity extends WinBoLLActivityBase implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return super.getActivity();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
public class AssetsHtmlActivity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "AssetsHtmlActivity";
 | 
			
		||||
 | 
			
		||||
@@ -43,12 +32,17 @@ public class AssetsHtmlActivity extends WinBoLLActivityBase implements IWinBoLLA
 | 
			
		||||
 | 
			
		||||
    // Assets 文件夹里的 Html 文件的名称
 | 
			
		||||
    String mszHtmlFileName;
 | 
			
		||||
//
 | 
			
		||||
//    @Override
 | 
			
		||||
//    public Activity getActivity() {
 | 
			
		||||
//        return this;
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onCreateOptionsMenu(Menu menu) {
 | 
			
		||||
        return super.onCreateOptionsMenu(menu);
 | 
			
		||||
 
 | 
			
		||||
@@ -217,10 +217,10 @@ final public class MainActivity extends Activity {
 | 
			
		||||
        if (item.getItemId() == R.id.item_exit) {
 | 
			
		||||
            //exit();
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (item.getItemId() == R.id.item_teststringtoqrcodeview) {
 | 
			
		||||
            Intent intent = new Intent(this, TestStringToQRCodeViewActivity.class);
 | 
			
		||||
            startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY);
 | 
			
		||||
            //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, TestStringToQrCodeViewActivity.class);
 | 
			
		||||
//        } else if (item.getItemId() == R.id.item_teststringtoqrcodeview) {
 | 
			
		||||
//            Intent intent = new Intent(this, TestStringToQRCodeViewActivity.class);
 | 
			
		||||
//            startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY);
 | 
			
		||||
//            //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, TestStringToQrCodeViewActivity.class);
 | 
			
		||||
        } else if (item.getItemId() == R.id.item_testqrcodedecodeactivity) {
 | 
			
		||||
            Intent intent = new Intent(this, QRCodeDecodeActivity.class);
 | 
			
		||||
            startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY);
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
package cc.winboll.studio.apputils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/05/13 11:15
 | 
			
		||||
 * @Describe WinBoLLActivity
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
 | 
			
		||||
public class WinBoLLActivity extends Activity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "WinBoLLActivity";
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 编译结构。
 | 
			
		||||
 
 | 
			
		||||
@@ -24,12 +24,12 @@ android {
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId "cc.winboll.studio.contacts"
 | 
			
		||||
        minSdkVersion 24
 | 
			
		||||
        targetSdkVersion 29
 | 
			
		||||
        targetSdkVersion 30
 | 
			
		||||
        versionCode 1
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // 项目模块目录的 build.gradle 文件的 stageCount=0
 | 
			
		||||
        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
			
		||||
        versionName "15.2" 
 | 
			
		||||
        versionName "15.3" 
 | 
			
		||||
        if(true) {
 | 
			
		||||
            versionName = genVersionName("${versionName}")
 | 
			
		||||
        }
 | 
			
		||||
@@ -45,6 +45,9 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.2'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
    
 | 
			
		||||
    // 权限请求框架:https://github.com/getActivity/XXPermissions
 | 
			
		||||
    api 'com.github.getActivity:XXPermissions:18.63'
 | 
			
		||||
@@ -81,8 +84,4 @@ dependencies {
 | 
			
		||||
    //api 'androidx.vectordrawable:vectordrawable:1.1.0'
 | 
			
		||||
    //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
 | 
			
		||||
    //api 'androidx.fragment:fragment:1.1.0'
 | 
			
		||||
    
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.2.4'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.2.1'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.2.2'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sun Apr 13 02:46:09 HKT 2025
 | 
			
		||||
stageCount=8
 | 
			
		||||
#Thu Jul 17 09:57:24 HKT 2025
 | 
			
		||||
stageCount=12
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.2
 | 
			
		||||
publishVersion=15.2.7
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.11
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.2.8
 | 
			
		||||
baseBetaVersion=15.3.12
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,8 @@
 | 
			
		||||
        android:icon="@drawable/ic_winboll"
 | 
			
		||||
        android:label="@string/app_name"
 | 
			
		||||
        android:theme="@style/MyAppTheme"
 | 
			
		||||
        android:supportsRtl="true">
 | 
			
		||||
        android:supportsRtl="true"
 | 
			
		||||
        android:networkSecurityConfig="@xml/network_security_config">
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".MainActivity"
 | 
			
		||||
@@ -192,4 +193,4 @@
 | 
			
		||||
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
</manifest>
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ package cc.winboll.studio.contacts;
 | 
			
		||||
 */
 | 
			
		||||
import android.view.Gravity;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.WinBollActivityManager;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.WinBoLLActivityManager;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
 | 
			
		||||
public class App extends GlobalApplication {
 | 
			
		||||
@@ -20,8 +20,8 @@ public class App extends GlobalApplication {
 | 
			
		||||
        // 这样可以预先设置日志与数据的存储根目录。
 | 
			
		||||
        //setIsDebuging(BuildConfig.DEBUG);
 | 
			
		||||
        super.onCreate();
 | 
			
		||||
        // 设置 WinBoll 应用 UI 类型
 | 
			
		||||
        WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Aplication);
 | 
			
		||||
        // 设置 WinBoLL 应用 UI 类型
 | 
			
		||||
        getWinBoLLActivityManager().setWinBoLLUI_TYPE(WinBoLLActivityManager.WinBoLLUI_TYPE.Aplication);
 | 
			
		||||
        
 | 
			
		||||
        //LogUtils.d(TAG, "onCreate");
 | 
			
		||||
        
 | 
			
		||||
 
 | 
			
		||||
@@ -32,22 +32,21 @@ import cc.winboll.studio.contacts.fragments.CallLogFragment;
 | 
			
		||||
import cc.winboll.studio.contacts.fragments.ContactsFragment;
 | 
			
		||||
import cc.winboll.studio.contacts.fragments.LogFragment;
 | 
			
		||||
import cc.winboll.studio.contacts.services.MainService;
 | 
			
		||||
import cc.winboll.studio.libaes.winboll.APPInfo;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogView;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
import com.google.android.material.tabs.TabLayout;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
final public class MainActivity extends AppCompatActivity implements IWinBollActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
 | 
			
		||||
final public class MainActivity extends AppCompatActivity implements IWinBoLLActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
 | 
			
		||||
 | 
			
		||||
	public static final String TAG = "MainActivity";
 | 
			
		||||
 | 
			
		||||
    public static final int REQUEST_HOME_ACTIVITY = 0;
 | 
			
		||||
    public static final int REQUEST_ABOUT_ACTIVITY = 1;
 | 
			
		||||
 | 
			
		||||
    public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoll.ACTION_SOS";
 | 
			
		||||
    public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoLL.ACTION_SOS";
 | 
			
		||||
 | 
			
		||||
    static MainActivity _MainActivity;
 | 
			
		||||
    LogView mLogView;
 | 
			
		||||
@@ -71,10 +70,10 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
 | 
			
		||||
 | 
			
		||||
    private static final int DIALER_REQUEST_CODE = 1;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
//    @Override
 | 
			
		||||
//    public Activity getActivity() {
 | 
			
		||||
//        return this;
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
//    @Override
 | 
			
		||||
//    public APPInfo getAppInfo() {
 | 
			
		||||
@@ -95,6 +94,17 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
 | 
			
		||||
//        return null;
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        // 接收并处理 Intent 数据,函数 Intent 处理接收就直接返回
 | 
			
		||||
@@ -111,7 +121,7 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
 | 
			
		||||
//            // 显示后退按钮
 | 
			
		||||
//            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 | 
			
		||||
//        }
 | 
			
		||||
        getSupportActionBar().setSubtitle(getTag());
 | 
			
		||||
        getSupportActionBar().setSubtitle(TAG);
 | 
			
		||||
 | 
			
		||||
        tabLayout = findViewById(R.id.tabLayout);
 | 
			
		||||
        viewPager = findViewById(R.id.viewPager);
 | 
			
		||||
@@ -339,7 +349,7 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
 | 
			
		||||
//
 | 
			
		||||
//        if (intent.getAction().equals(StringToQrCodeView.ACTION_UNITTEST_QRCODE)) {
 | 
			
		||||
//            try {
 | 
			
		||||
//                WinBollActivity clazzActivity = UnitTestActivity.class.newInstance();
 | 
			
		||||
//                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);
 | 
			
		||||
@@ -357,8 +367,8 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
 | 
			
		||||
//                }
 | 
			
		||||
//
 | 
			
		||||
//                Files.copy(Paths.get(szSrcPath), Paths.get(file.getPath()));
 | 
			
		||||
//                //startWinBollActivity(subIntent, tag);
 | 
			
		||||
//                WinBollActivityManager.getInstance(this).startWinBollActivity(this, subIntent, UnitTestActivity.class);
 | 
			
		||||
//                //startWinBoLLActivity(subIntent, tag);
 | 
			
		||||
//                WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, subIntent, UnitTestActivity.class);
 | 
			
		||||
//            } catch (IllegalAccessException | InstantiationException | IOException e) {
 | 
			
		||||
//                LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
//                // 函数处理异常返回失败
 | 
			
		||||
@@ -371,10 +381,10 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
 | 
			
		||||
//        return true;
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
//    @Override
 | 
			
		||||
//    public String getTag() {
 | 
			
		||||
//        return TAG;
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
//    @Override
 | 
			
		||||
//    public void onBackPressed() {
 | 
			
		||||
@@ -386,7 +396,7 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
 | 
			
		||||
//
 | 
			
		||||
//            @Override
 | 
			
		||||
//            public void onYes() {
 | 
			
		||||
//                WinBollActivityManager.getInstance(getApplicationContext()).finishAll();
 | 
			
		||||
//                WinBoLLActivityManager.getInstance(getApplicationContext()).finishAll();
 | 
			
		||||
//            }
 | 
			
		||||
//
 | 
			
		||||
//            @Override
 | 
			
		||||
@@ -408,7 +418,7 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
 | 
			
		||||
        if (item.getItemId() == R.id.item_settings) {
 | 
			
		||||
            Intent intent = new Intent(this, SettingsActivity.class);
 | 
			
		||||
            startActivity(intent);
 | 
			
		||||
            //WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
 | 
			
		||||
            //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, CallActivity.class);
 | 
			
		||||
        }
 | 
			
		||||
//        } else 
 | 
			
		||||
//        if (item.getItemId() == R.id.item_exit) {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,14 +10,15 @@ import android.content.Context;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
import cc.winboll.studio.contacts.R;
 | 
			
		||||
import cc.winboll.studio.libaes.winboll.APPInfo;
 | 
			
		||||
import cc.winboll.studio.libaes.winboll.AboutView;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
 | 
			
		||||
public class AboutActivity extends WinBollActivity implements IWinBollActivity {
 | 
			
		||||
public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "AboutActivity";
 | 
			
		||||
 | 
			
		||||
@@ -64,13 +65,13 @@ public class AboutActivity extends WinBollActivity implements IWinBollActivity {
 | 
			
		||||
        );
 | 
			
		||||
        layout.addView(aboutView, params);
 | 
			
		||||
 | 
			
		||||
        GlobalApplication.getWinBollActivityManager().add(this);
 | 
			
		||||
        GlobalApplication.getWinBoLLActivityManager().add(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onDestroy() {
 | 
			
		||||
        super.onDestroy();
 | 
			
		||||
        GlobalApplication.getWinBollActivityManager().registeRemove(this);
 | 
			
		||||
        GlobalApplication.getWinBoLLActivityManager().registeRemove(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AboutView CreateAboutView() {
 | 
			
		||||
@@ -78,12 +79,12 @@ public class AboutActivity extends WinBollActivity 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);
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import androidx.appcompat.app.AppCompatActivity;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
import androidx.recyclerview.widget.LinearLayoutManager;
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView;
 | 
			
		||||
import cc.winboll.studio.contacts.App;
 | 
			
		||||
import cc.winboll.studio.contacts.R;
 | 
			
		||||
import cc.winboll.studio.contacts.adapters.PhoneConnectRuleAdapter;
 | 
			
		||||
import cc.winboll.studio.contacts.beans.MainServiceBean;
 | 
			
		||||
@@ -34,15 +35,13 @@ import cc.winboll.studio.contacts.bobulltoon.TomCat;
 | 
			
		||||
import cc.winboll.studio.contacts.dun.Rules;
 | 
			
		||||
import cc.winboll.studio.contacts.services.MainService;
 | 
			
		||||
import cc.winboll.studio.contacts.views.DuInfoTextView;
 | 
			
		||||
import cc.winboll.studio.libaes.winboll.APPInfo;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.lang.reflect.Field;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import cc.winboll.studio.contacts.App;
 | 
			
		||||
 | 
			
		||||
public class SettingsActivity extends AppCompatActivity implements IWinBollActivity {
 | 
			
		||||
public class SettingsActivity extends AppCompatActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "SettingsActivity";
 | 
			
		||||
 | 
			
		||||
@@ -177,6 +176,8 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
 | 
			
		||||
        etDunResumeSecondCount.setEnabled(!isEnableDun);
 | 
			
		||||
        etDunResumeCount.setEnabled(!isEnableDun);
 | 
			
		||||
 | 
			
		||||
        EditText etBoBullToonURL = findViewById(R.id.bobulltoonurl_et);
 | 
			
		||||
        etBoBullToonURL.setText(Rules.getInstance(this).getBoBullToonURL());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void notifyDunInfoUpdate() {
 | 
			
		||||
@@ -197,10 +198,19 @@ 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();
 | 
			
		||||
        Rules.getInstance(this).reload();
 | 
			
		||||
        
 | 
			
		||||
        // 重新加载盾牌参数
 | 
			
		||||
        etDunTotalCount.setText(Integer.toString(settingsModel.getDunTotalCount()));
 | 
			
		||||
        etDunResumeSecondCount.setText(Integer.toString(settingsModel.getDunResumeSecondCount()));
 | 
			
		||||
        etDunResumeCount.setText(Integer.toString(settingsModel.getDunResumeCount()));
 | 
			
		||||
		
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateStreamVolumeTextView() {
 | 
			
		||||
@@ -233,7 +243,21 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void onResetBoBullToonURL(View view) {
 | 
			
		||||
        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) {
 | 
			
		||||
        EditText etBoBullToonURL = findViewById(R.id.bobulltoonurl_et);
 | 
			
		||||
        if (!etBoBullToonURL.getText().toString().trim().equals(Rules.getInstance(this).getBoBullToonURL())) {
 | 
			
		||||
            Rules.getInstance(this).setBoBullToonURL(etBoBullToonURL.getText().toString().trim());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final TomCat tomCat = TomCat.getInstance(this);
 | 
			
		||||
        new Thread(new Runnable() {
 | 
			
		||||
                @Override
 | 
			
		||||
@@ -247,8 +271,6 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
 | 
			
		||||
            }).start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void onSearchBoBullToonPhone(View view) {
 | 
			
		||||
        TomCat tomCat = TomCat.getInstance(this);
 | 
			
		||||
        EditText etPhone = findViewById(R.id.activitysettingsEditText1);
 | 
			
		||||
@@ -311,8 +333,12 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public void onAbout(View view) {
 | 
			
		||||
        App.getWinBollActivityManager().startWinBollActivity(this, AboutActivity.class);
 | 
			
		||||
        App.getWinBoLLActivityManager().startWinBoLLActivity(this, AboutActivity.class);
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
	public void onLogView(View view) {
 | 
			
		||||
        App.getWinBoLLActivityManager().startLogActivity(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,9 +11,9 @@ import android.view.MenuItem;
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity;
 | 
			
		||||
import cc.winboll.studio.libaes.beans.AESThemeBean;
 | 
			
		||||
import cc.winboll.studio.libaes.utils.AESThemeUtil;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
 | 
			
		||||
public class WinBollActivity extends AppCompatActivity implements IWinBollActivity {
 | 
			
		||||
public class WinBollActivity extends AppCompatActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "WinBollActivity";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,18 @@ package cc.winboll.studio.contacts.adapters;
 | 
			
		||||
 * @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;
 | 
			
		||||
@@ -47,6 +52,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()));
 | 
			
		||||
 
 | 
			
		||||
@@ -5,19 +5,25 @@ package cc.winboll.studio.contacts.adapters;
 | 
			
		||||
 * @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);
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ package cc.winboll.studio.contacts.adapters;
 | 
			
		||||
 */
 | 
			
		||||
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) {
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ import cc.winboll.studio.contacts.utils.IntUtils;
 | 
			
		||||
public class SettingsModel extends BaseBean {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "SettingsModel";
 | 
			
		||||
    
 | 
			
		||||
    public static final int MAX_INTRANGE = 666666;
 | 
			
		||||
    public static final int MIN_INTRANGE = 1;
 | 
			
		||||
    
 | 
			
		||||
@@ -27,6 +28,8 @@ public class SettingsModel extends BaseBean {
 | 
			
		||||
    int dunResumeCount;
 | 
			
		||||
    // 是否启用云盾
 | 
			
		||||
    boolean isEnableDun;
 | 
			
		||||
    // BoBullToon 应用模块数据请求地址
 | 
			
		||||
    String szBoBullToon_URL;
 | 
			
		||||
 | 
			
		||||
    public SettingsModel() {
 | 
			
		||||
        this.dunTotalCount = 6;
 | 
			
		||||
@@ -34,14 +37,24 @@ public class SettingsModel extends BaseBean {
 | 
			
		||||
        this.dunResumeSecondCount = 60;
 | 
			
		||||
        this.dunResumeCount = 1;
 | 
			
		||||
        this.isEnableDun = false;
 | 
			
		||||
        this.szBoBullToon_URL = "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SettingsModel(int dunTotalCount, int dunCurrentCount, int dunResumeSecondCount, int dunResumeCount, boolean isEnableDun) {
 | 
			
		||||
    public SettingsModel(int dunTotalCount, int dunCurrentCount, int dunResumeSecondCount, int dunResumeCount, boolean isEnableDun, String szBoBullToon_URL) {
 | 
			
		||||
        this.dunTotalCount = getSettingsModelRangeInt(dunTotalCount);
 | 
			
		||||
        this.dunCurrentCount = getSettingsModelRangeInt(dunCurrentCount);
 | 
			
		||||
        this.dunResumeSecondCount = getSettingsModelRangeInt(dunResumeSecondCount);
 | 
			
		||||
        this.dunResumeCount = getSettingsModelRangeInt(dunResumeCount);
 | 
			
		||||
        this.isEnableDun = isEnableDun;
 | 
			
		||||
        this.szBoBullToon_URL = szBoBullToon_URL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setBoBullToon_URL(String boBullToon_URL) {
 | 
			
		||||
        this.szBoBullToon_URL = boBullToon_URL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getBoBullToon_URL() {
 | 
			
		||||
        return szBoBullToon_URL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setDunTotalCount(int dunTotalCount) {
 | 
			
		||||
@@ -102,7 +115,8 @@ public class SettingsModel extends BaseBean {
 | 
			
		||||
        jsonWriter.name("dunResumeSecondCount").value(getDunResumeSecondCount());
 | 
			
		||||
        jsonWriter.name("dunResumeCount").value(getDunResumeCount());
 | 
			
		||||
        jsonWriter.name("isEnableDun").value(isEnableDun());
 | 
			
		||||
 | 
			
		||||
        jsonWriter.name("szBoBullToon_URL").value(getBoBullToon_URL());
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -118,6 +132,8 @@ public class SettingsModel extends BaseBean {
 | 
			
		||||
                setDunResumeCount(getSettingsModelRangeInt(jsonReader.nextInt()));
 | 
			
		||||
            } else if (name.equals("isEnableDun")) {
 | 
			
		||||
                setIsEnableDun(jsonReader.nextBoolean());
 | 
			
		||||
            } else if (name.equals("szBoBullToon_URL")) {
 | 
			
		||||
                setBoBullToon_URL(jsonReader.nextString());
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@ package cc.winboll.studio.contacts.bobulltoon;
 | 
			
		||||
 * @Describe 汤姆猫管家 :使用 BoBullToon 项目,对通讯地址进行筛选判断的好朋友。
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import cc.winboll.studio.contacts.R;
 | 
			
		||||
import cc.winboll.studio.contacts.dun.Rules;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
@@ -28,6 +30,7 @@ public class TomCat {
 | 
			
		||||
    public static final String TAG = "TomCat";
 | 
			
		||||
 | 
			
		||||
    List<String> listPhoneBoBullToon = new ArrayList<String>();
 | 
			
		||||
    String mszBoBullToon_URL;
 | 
			
		||||
 | 
			
		||||
    static volatile TomCat _TomCat;
 | 
			
		||||
    Context mContext;
 | 
			
		||||
@@ -42,7 +45,11 @@ public class TomCat {
 | 
			
		||||
        return _TomCat;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void downloadAndExtractZip(String zipUrl, String destinationFolder) throws IOException {
 | 
			
		||||
    public String getDefaultBobulltoonUrl() {
 | 
			
		||||
        return mContext.getString(R.string.default_bobulltoon_url);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    boolean downloadAndExtractZip(String zipUrl, String destinationFolder) throws IOException {
 | 
			
		||||
        OkHttpClient client = new OkHttpClient();
 | 
			
		||||
        Request request = new Request.Builder()
 | 
			
		||||
            .url(zipUrl)
 | 
			
		||||
@@ -95,13 +102,16 @@ public class TomCat {
 | 
			
		||||
            // 删除临时 ZIP 文件
 | 
			
		||||
            tempZipFile.delete();
 | 
			
		||||
            LogUtils.d(TAG, "已更新 BoBullToon 数据");
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            ToastUtils.show(e.getMessage());
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean downloadBoBullToon() {
 | 
			
		||||
        String zipUrl = "http://10.8.0.12:3000/Studio/BoBullToon/archive/main.zip"; // 替换为实际的 ZIP 文件 URL
 | 
			
		||||
        String zipUrl = Rules.getInstance(mContext).getBoBullToonURL(); // 替换为实际的 ZIP 文件 URL
 | 
			
		||||
        String destinationFolder = getWorkingFolder().getPath(); // 替换为实际的目标文件夹路径
 | 
			
		||||
        try {
 | 
			
		||||
            // 删除旧文件
 | 
			
		||||
@@ -113,9 +123,11 @@ public class TomCat {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 更新新文件
 | 
			
		||||
            downloadAndExtractZip(zipUrl, destinationFolder);
 | 
			
		||||
            LogUtils.d(TAG, "ZIP 文件下载并解压成功。");
 | 
			
		||||
            return true;
 | 
			
		||||
            if (downloadAndExtractZip(zipUrl, destinationFolder)) {
 | 
			
		||||
                LogUtils.d(TAG, "ZIP 文件下载并解压成功。");
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
        }
 | 
			
		||||
@@ -143,6 +155,19 @@ public class TomCat {
 | 
			
		||||
        return mContext.getExternalFilesDir(TAG);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	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");
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ import java.util.ArrayList;
 | 
			
		||||
import java.util.Timer;
 | 
			
		||||
import java.util.TimerTask;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
import cc.winboll.studio.contacts.bobulltoon.TomCat;
 | 
			
		||||
 | 
			
		||||
public class Rules {
 | 
			
		||||
 | 
			
		||||
@@ -84,6 +85,20 @@ public class Rules {
 | 
			
		||||
        PhoneConnectRuleModel.saveBeanList(mContext, _PhoneConnectRuleModelList, PhoneConnectRuleModel.class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void resetDefaultBoBullToonURL() {
 | 
			
		||||
        mSettingsModel.setBoBullToon_URL(TomCat.getInstance(mContext).getDefaultBobulltoonUrl());
 | 
			
		||||
        saveDun();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setBoBullToonURL(String szUrl) {
 | 
			
		||||
        mSettingsModel.setBoBullToon_URL(szUrl);
 | 
			
		||||
        saveDun();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getBoBullToonURL() {
 | 
			
		||||
        return mSettingsModel.getBoBullToon_URL();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void loadDun() {
 | 
			
		||||
        mSettingsModel = SettingsModel.loadBean(mContext, SettingsModel.class);
 | 
			
		||||
        if (mSettingsModel == null) {
 | 
			
		||||
@@ -109,8 +124,12 @@ public class Rules {
 | 
			
		||||
        boolean isDefend = false; // 盾牌是否生效
 | 
			
		||||
        boolean isConnect = true; // 防御结果是否连接
 | 
			
		||||
 | 
			
		||||
        // 进行盾牌层数预计缩减计算
 | 
			
		||||
        int nDunCurrentCount = mSettingsModel.getDunCurrentCount() - 1;
 | 
			
		||||
        LogUtils.d(TAG, String.format("nDunCurrentCount : %d", nDunCurrentCount));
 | 
			
		||||
 | 
			
		||||
        // 如果盾值小于1,则解除防御
 | 
			
		||||
        if (!isDefend && mSettingsModel.getDunCurrentCount() < 1) {
 | 
			
		||||
        if (!isDefend && nDunCurrentCount < 1) {
 | 
			
		||||
            // 盾层为1以下,防御解除
 | 
			
		||||
            LogUtils.d(TAG, "盾层为1以下,防御解除");
 | 
			
		||||
            isDefend = true;
 | 
			
		||||
@@ -126,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) {
 | 
			
		||||
@@ -139,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++) {
 | 
			
		||||
@@ -174,17 +193,17 @@ public class Rules {
 | 
			
		||||
            // 就减少防御盾牌层数。
 | 
			
		||||
            // 每校验一次规则,云盾防御层数减1
 | 
			
		||||
            // 当云盾防御层数为0时,再次进行以下程序段则恢复满值防御。
 | 
			
		||||
            int newDunCount = mSettingsModel.getDunCurrentCount() - 1;
 | 
			
		||||
            int newDunCount = nDunCurrentCount;
 | 
			
		||||
            LogUtils.d(TAG, String.format("新的防御层数预计为 %d", newDunCount));
 | 
			
		||||
 | 
			
		||||
            // 保证盾值在[0,DunTotalCount]之内其他值一律重置为 DunTotalCount。
 | 
			
		||||
            if (newDunCount < 0 || newDunCount > mSettingsModel.getDunTotalCount()) {
 | 
			
		||||
                mSettingsModel.setDunCurrentCount(mSettingsModel.getDunTotalCount());
 | 
			
		||||
                LogUtils.d(TAG, String.format("盾值不在[0,%d]区间,恢复防御最大值%d", mSettingsModel.getDunTotalCount(), mSettingsModel.getDunTotalCount()));
 | 
			
		||||
            } else {
 | 
			
		||||
            // 保证盾值在[1,DunTotalCount]之内其他值一律重置为 DunTotalCount。
 | 
			
		||||
            if (newDunCount > 0 && newDunCount < mSettingsModel.getDunTotalCount()) {
 | 
			
		||||
                mSettingsModel.setDunCurrentCount(newDunCount);
 | 
			
		||||
                LogUtils.d(TAG, String.format("设置防御层数为 %d", newDunCount));
 | 
			
		||||
            }
 | 
			
		||||
            } else {
 | 
			
		||||
                mSettingsModel.setDunCurrentCount(mSettingsModel.getDunTotalCount());
 | 
			
		||||
                LogUtils.d(TAG, String.format("盾值不在[0,%d]区间,恢复防御最大值%d", mSettingsModel.getDunTotalCount(), mSettingsModel.getDunTotalCount()));
 | 
			
		||||
            } 
 | 
			
		||||
 | 
			
		||||
            saveDun();
 | 
			
		||||
            SettingsActivity.notifyDunInfoUpdate();
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,7 @@ public class ContactsFragment extends Fragment {
 | 
			
		||||
        super.onViewCreated(view, savedInstanceState);
 | 
			
		||||
        recyclerView = view.findViewById(R.id.contacts_recycler_view);
 | 
			
		||||
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
 | 
			
		||||
        contactAdapter = new ContactAdapter(contactList);
 | 
			
		||||
        contactAdapter = new ContactAdapter(getContext(), contactList);
 | 
			
		||||
        recyclerView.setAdapter(contactAdapter);
 | 
			
		||||
 | 
			
		||||
        searchEditText = view.findViewById(R.id.search_edit_text);
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ import android.content.ServiceConnection;
 | 
			
		||||
import android.media.AudioManager;
 | 
			
		||||
import android.os.Binder;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import cc.winboll.studio.contacts.App;
 | 
			
		||||
import cc.winboll.studio.contacts.beans.MainServiceBean;
 | 
			
		||||
import cc.winboll.studio.contacts.beans.RingTongBean;
 | 
			
		||||
import cc.winboll.studio.contacts.bobulltoon.TomCat;
 | 
			
		||||
@@ -29,11 +30,9 @@ import cc.winboll.studio.contacts.services.MainService;
 | 
			
		||||
import cc.winboll.studio.contacts.threads.MainServiceThread;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.sos.SOS;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.WinBoLL;
 | 
			
		||||
import java.util.Timer;
 | 
			
		||||
import java.util.TimerTask;
 | 
			
		||||
import cc.winboll.studio.libappbase.sos.WinBoll;
 | 
			
		||||
import cc.winboll.studio.contacts.App;
 | 
			
		||||
import cc.winboll.studio.libappbase.sos.APPModel;
 | 
			
		||||
 | 
			
		||||
public class MainService extends Service {
 | 
			
		||||
 | 
			
		||||
@@ -129,11 +128,11 @@ public class MainService extends Service {
 | 
			
		||||
            isServiceRunning = true;
 | 
			
		||||
            // 唤醒守护进程
 | 
			
		||||
            wakeupAndBindAssistant();
 | 
			
		||||
            // 召唤 WinBoll APP 绑定本服务
 | 
			
		||||
            // 召唤 WinBoLL APP 绑定本服务
 | 
			
		||||
            if (App.isDebuging()) {
 | 
			
		||||
                WinBoll.bindToAPPBaseBeta(this, MainService.class.getName());
 | 
			
		||||
                WinBoLL.bindToAPPBaseBeta(this, MainService.class.getName());
 | 
			
		||||
            } else {
 | 
			
		||||
                WinBoll.bindToAPPBase(this, MainService.class.getName());
 | 
			
		||||
                WinBoLL.bindToAPPBase(this, MainService.class.getName());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 初始化服务运行参数
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -195,16 +195,29 @@
 | 
			
		||||
				android:text="拨不通电话记录查询:"/>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="vertical"
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:gravity="right"
 | 
			
		||||
				android:layout_margin="10dp">
 | 
			
		||||
 | 
			
		||||
				<EditText
 | 
			
		||||
					android:layout_width="0dp"
 | 
			
		||||
					android:ems="10"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:layout_weight="1.0"
 | 
			
		||||
					android:id="@+id/bobulltoonurl_et"/>
 | 
			
		||||
 | 
			
		||||
				<Button
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="下载 BoBullToon"
 | 
			
		||||
					android:text="重置地址"
 | 
			
		||||
					android:onClick="onResetBoBullToonURL"/>
 | 
			
		||||
 | 
			
		||||
				<Button
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="下载数据"
 | 
			
		||||
					android:onClick="onDownloadBoBullToon"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
@@ -256,6 +269,19 @@
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:gravity="center_horizontal">
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="<<==向左拉动列表项可编辑内容"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
				android:id="@+id/recycler_view"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
@@ -274,6 +300,12 @@
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:gravity="right">
 | 
			
		||||
 | 
			
		||||
				<Button
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="LogView"
 | 
			
		||||
					android:onClick="onLogView"/>
 | 
			
		||||
 | 
			
		||||
				<Button
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
 
 | 
			
		||||
@@ -15,8 +15,10 @@
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Test Main"
 | 
			
		||||
			android:onClick="onTestMain"/>
 | 
			
		||||
			android:text="Add Demo Rules(While size is 0) and Test"
 | 
			
		||||
			android:onClick="onTestMain"
 | 
			
		||||
			android:textSize="10sp"
 | 
			
		||||
			android:textAllCaps="false"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -43,7 +45,8 @@
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Test Phone"
 | 
			
		||||
			android:onClick="onTestPhone"/>
 | 
			
		||||
			android:onClick="onTestPhone"
 | 
			
		||||
			android:textAllCaps="false"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,8 @@
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content">
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:id="@+id/itemcontactLinearLayout1">
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:id="@+id/contact_number"
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
			android:id="@+id/checkbox_allow"
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="允许连接"/>
 | 
			
		||||
			android:text="连接"/>
 | 
			
		||||
 | 
			
		||||
        <CheckBox
 | 
			
		||||
            android:id="@+id/checkbox_enable"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,55 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<HorizontalScrollView
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="wrap_content"
 | 
			
		||||
    android:scrollbars="none"
 | 
			
		||||
    android:id="@+id/scrollView">
 | 
			
		||||
 | 
			
		||||
    <LinearLayout
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:orientation="horizontal">
 | 
			
		||||
 | 
			
		||||
        <!-- 内容区域 -->
 | 
			
		||||
        <LinearLayout
 | 
			
		||||
            android:id="@+id/content_layout"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:padding="16dp"
 | 
			
		||||
            android:background="@color/white">
 | 
			
		||||
            <!-- 这里放置你的列表项内容 -->
 | 
			
		||||
 | 
			
		||||
            <TextView
 | 
			
		||||
                android:id="@+id/text_view"
 | 
			
		||||
                android:layout_width="0dp"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:layout_weight="1"
 | 
			
		||||
                android:textSize="16sp"/>
 | 
			
		||||
        </LinearLayout>
 | 
			
		||||
 | 
			
		||||
        <!-- 操作按钮 -->
 | 
			
		||||
        <LinearLayout
 | 
			
		||||
            android:id="@+id/action_layout"
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="match_parent"
 | 
			
		||||
            android:orientation="horizontal"
 | 
			
		||||
            android:background="@color/lightgray">
 | 
			
		||||
 | 
			
		||||
            <Button
 | 
			
		||||
                android:id="@+id/edit_btn"
 | 
			
		||||
                android:layout_width="80dp"
 | 
			
		||||
                android:layout_height="match_parent"
 | 
			
		||||
                android:text="编辑"
 | 
			
		||||
                android:background="@color/blue" />
 | 
			
		||||
 | 
			
		||||
            <Button
 | 
			
		||||
                android:id="@+id/delete_btn"
 | 
			
		||||
                android:layout_width="80dp"
 | 
			
		||||
                android:layout_height="match_parent"
 | 
			
		||||
                android:text="删除"
 | 
			
		||||
                android:background="@color/red" />
 | 
			
		||||
        </LinearLayout>
 | 
			
		||||
    </LinearLayout>
 | 
			
		||||
</HorizontalScrollView>
 | 
			
		||||
@@ -0,0 +1,35 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:orientation="horizontal"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="wrap_content"
 | 
			
		||||
	android:layout_gravity="center_vertical"
 | 
			
		||||
	android:gravity="center_vertical">
 | 
			
		||||
 | 
			
		||||
	<TextView
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:text="Text"
 | 
			
		||||
		android:layout_weight="1.0"
 | 
			
		||||
		android:id="@+id/ruletext_tv"/>
 | 
			
		||||
 | 
			
		||||
	<CheckBox
 | 
			
		||||
		android:id="@+id/checkbox_allow"
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:text="连接"
 | 
			
		||||
		android:clickable="false"
 | 
			
		||||
		android:focusable="false"/>
 | 
			
		||||
 | 
			
		||||
	<CheckBox
 | 
			
		||||
		android:id="@+id/checkbox_enable"
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:text="启用"
 | 
			
		||||
		android:clickable="false"
 | 
			
		||||
		android:focusable="false"/>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -2,5 +2,8 @@
 | 
			
		||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
    
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_calllog_phonenumber_copy"
 | 
			
		||||
        android:title="Copy"/>
 | 
			
		||||
    
 | 
			
		||||
</menu>
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_contact_phonenumber_copy"
 | 
			
		||||
        android:title="Copy"/>
 | 
			
		||||
    
 | 
			
		||||
</menu>
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <!-- WinBoll 默认方案 -->
 | 
			
		||||
    <!-- WinBoLL 默认方案 -->
 | 
			
		||||
    <color name="colorPrimary">#FF196ABC</color>
 | 
			
		||||
    <color name="colorPrimaryDark">#FF002B57</color>
 | 
			
		||||
    <color name="colorAccent">#FF80BFFF</color>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,5 +2,6 @@
 | 
			
		||||
<resources>
 | 
			
		||||
 | 
			
		||||
    <string name="app_name">Contacts</string>
 | 
			
		||||
    <string name="default_bobulltoon_url">https://gitea.winboll.cc/Studio/BoBullToon/archive/main.zip</string>
 | 
			
		||||
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <style name="MyAppTheme" parent="APPBaseTheme">
 | 
			
		||||
    <style name="MyAppTheme" parent="AESTheme">
 | 
			
		||||
        <item name="colorPrimary">@color/colorPrimary</item>
 | 
			
		||||
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
 | 
			
		||||
        <item name="colorAccent">@color/colorAccent</item>
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
        <item name="android:windowContentOverlay">@null</item>
 | 
			
		||||
    </style>
 | 
			
		||||
 | 
			
		||||
    <style name="GlobalCrashActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
 | 
			
		||||
    <style name="GlobalCrashActivityTheme" parent="AESTheme">
 | 
			
		||||
        <item name="colorTittle">@color/colorAccent</item>
 | 
			
		||||
        <item name="colorTittleBackgound">@color/colorPrimary</item>
 | 
			
		||||
        <item name="colorText">@color/colorAccent</item>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								contacts/src/main/res/xml/network_security_config.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								contacts/src/main/res/xml/network_security_config.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<network-security-config>
 | 
			
		||||
    <domain-config cleartextTrafficPermitted="true">
 | 
			
		||||
        <domain includeSubdomains="true">winboll.cc</domain>
 | 
			
		||||
    </domain-config>
 | 
			
		||||
    <domain-config cleartextTrafficPermitted="true">
 | 
			
		||||
        <domain includeSubdomains="false">10.8.0.12</domain>
 | 
			
		||||
    </domain-config>
 | 
			
		||||
</network-security-config>
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
keyAlias=WinBoLL.CC
 | 
			
		||||
keyPassword=androiddebugkey
 | 
			
		||||
storeFile=../WinBoLL.CC.jks
 | 
			
		||||
storePassword=androiddebugkey
 | 
			
		||||
@@ -42,23 +42,24 @@ android {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    compileOptions {
 | 
			
		||||
    /*compileOptions {
 | 
			
		||||
        sourceCompatibility JavaVersion.VERSION_11
 | 
			
		||||
        targetCompatibility JavaVersion.VERSION_11
 | 
			
		||||
    }
 | 
			
		||||
    }*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
	api project(':libjc')
 | 
			
		||||
	api 'cc.winboll.studio:libaes:15.9.1'
 | 
			
		||||
	api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
	api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
	
 | 
			
		||||
    // https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
 | 
			
		||||
    //implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
 | 
			
		||||
    implementation 'org.bouncycastle:bcprov-jdk15to18:1.69'
 | 
			
		||||
    implementation 'org.bouncycastle:bcpkix-jdk15to18:1.69'
 | 
			
		||||
 | 
			
		||||
	api project(':libjc')
 | 
			
		||||
    api 'androidx.appcompat:appcompat:1.0.0'
 | 
			
		||||
	api 'com.google.android.material:material:1.0.0'
 | 
			
		||||
    
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:9.1.0'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:1.0.3'
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Fri Jan 10 22:03:57 GMT 2025
 | 
			
		||||
#Tue Jun 24 11:17:30 GMT 2025
 | 
			
		||||
stageCount=0
 | 
			
		||||
libraryProject=libjc
 | 
			
		||||
baseVersion=1.0
 | 
			
		||||
publishVersion=1.0.0
 | 
			
		||||
buildCount=133
 | 
			
		||||
buildCount=135
 | 
			
		||||
baseBetaVersion=1.0.1
 | 
			
		||||
 
 | 
			
		||||
@@ -15,10 +15,10 @@ import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.ScrollView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import cc.winboll.studio.jc.R;
 | 
			
		||||
import cc.winboll.studio.libapputils.log.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libjc.JAR_RUNNING_MODE;
 | 
			
		||||
import cc.winboll.studio.libjc.JCMainThread;
 | 
			
		||||
import cc.winboll.studio.libjc.net.JCSocketClient;
 | 
			
		||||
import cc.winboll.studio.libjc.util.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libjc.Main;
 | 
			
		||||
 | 
			
		||||
final public class MainActivity extends Activity implements JCMainThread.OnMessageListener {
 | 
			
		||||
 | 
			
		||||
@@ -77,7 +77,7 @@ final public class MainActivity extends Activity implements JCMainThread.OnMessa
 | 
			
		||||
        // 启动主线程
 | 
			
		||||
        _JCMainThread = JCMainThread.getInstance(getPackageName());
 | 
			
		||||
        _JCMainThread.setOnLogListener(this);
 | 
			
		||||
        _JCMainThread.setRunningMode(JAR_RUNNING_MODE.JC);
 | 
			
		||||
        //_JCMainThread.setRunningMode(Main.JAR_RUNNING_MODE.JC);
 | 
			
		||||
        _JCMainThread.start();
 | 
			
		||||
 | 
			
		||||
        // 设置 WinBoll 应用 UI 类型
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								keystore
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								keystore
									
									
									
									
									
										Submodule
									
								
							 Submodule keystore added at e7f70226c1
									
								
							@@ -21,9 +21,8 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    //api 'cc.winboll.studio:libaes:15.6.0'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.3.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.7.6'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
    
 | 
			
		||||
    // 吐司类库
 | 
			
		||||
    api 'com.github.getActivity:ToastUtils:10.5'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Mon May 12 18:10:35 HKT 2025
 | 
			
		||||
stageCount=4
 | 
			
		||||
#Sat Jun 28 12:59:30 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
libraryProject=libaes
 | 
			
		||||
baseVersion=15.6
 | 
			
		||||
publishVersion=15.6.3
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.2
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.6.4
 | 
			
		||||
baseBetaVersion=15.9.3
 | 
			
		||||
 
 | 
			
		||||
@@ -176,8 +176,8 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
 | 
			
		||||
            for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
 | 
			
		||||
                getString(i);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (R.id.item_log == item.getItemId()) {
 | 
			
		||||
            GlobalApplication.getWinBoLLActivityManager().startLogActivity(this);
 | 
			
		||||
//        } else if (R.id.item_log == item.getItemId()) {
 | 
			
		||||
//            GlobalApplication.getWinBoLLActivityManager().startLogActivity(this);
 | 
			
		||||
        } else if (R.id.item_about == item.getItemId()) {
 | 
			
		||||
            LogUtils.d(TAG, "onAbout");
 | 
			
		||||
        } else if (android.R.id.home == item.getItemId()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -107,7 +107,7 @@ public class AboutView extends LinearLayout {
 | 
			
		||||
        mszAppDescription = mAPPInfo.getAppDescription();
 | 
			
		||||
        mnAppIcon = mAPPInfo.getAppIcon();
 | 
			
		||||
 | 
			
		||||
        mszWinBoLLServerHost = GlobalApplication.isDebuging() ?  "https://dev.winboll.cc": "https://www.winboll.cc";
 | 
			
		||||
        mszWinBoLLServerHost = GlobalApplication.isDebuging() ?  "https://yun-preivew.winboll.cc": "https://yun.winboll.cc";
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            mszAppVersionName = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionName;
 | 
			
		||||
@@ -115,7 +115,8 @@ public class AboutView extends LinearLayout {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
        }
 | 
			
		||||
        mszCurrentAppPackageName = mszAppAPKName + "_" + mszAppVersionName + ".apk";
 | 
			
		||||
        mszHomePage = mszWinBoLLServerHost + "/studio/details.php?app=" + mszAppAPKFolderName;
 | 
			
		||||
        mszHomePage = mAPPInfo.getAppHomePage();
 | 
			
		||||
        //mszHomePage = mszWinBoLLServerHost + "/studio/details.php?app=" + mszAppAPKFolderName;
 | 
			
		||||
        if (mAPPInfo.getAppGitAPPBranch().equals("")) {
 | 
			
		||||
            mszGitea = "https://gitea.winboll.cc/" + mAPPInfo.getAppGitOwner() + "/" + mszAppGitName;
 | 
			
		||||
        } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,4 +22,9 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    // 网络连接类库
 | 
			
		||||
    api 'com.squareup.okhttp3:okhttp:4.4.1'
 | 
			
		||||
    // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
 | 
			
		||||
    
 | 
			
		||||
    api 'com.google.code.gson:gson:2.10.1'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue May 13 10:28:56 HKT 2025
 | 
			
		||||
stageCount=8
 | 
			
		||||
#Thu Jul 17 11:39:14 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=libappbase
 | 
			
		||||
baseVersion=15.7
 | 
			
		||||
publishVersion=15.7.7
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.7.8
 | 
			
		||||
baseBetaVersion=15.9.2
 | 
			
		||||
 
 | 
			
		||||
@@ -103,6 +103,10 @@
 | 
			
		||||
 | 
			
		||||
        </receiver>
 | 
			
		||||
 | 
			
		||||
        <activity android:name="cc.winboll.studio.libappbase.activities.YunActivity"/>
 | 
			
		||||
 | 
			
		||||
        <activity android:name="cc.winboll.studio.libappbase.activities.LogonActivity"/>
 | 
			
		||||
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ public class GlobalApplication extends Application {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static WinBoLLActivityManager getWinBoLLActivityManager() {
 | 
			
		||||
        return WinBoLLActivityManager.getInstance(_GlobalApplication);
 | 
			
		||||
        return WinBoLLActivityManager.getInstance();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,150 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.activities;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.widget.RadioButton;
 | 
			
		||||
import cc.winboll.studio.libappbase.BuildConfig;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogView;
 | 
			
		||||
import cc.winboll.studio.libappbase.R;
 | 
			
		||||
import cc.winboll.studio.libappbase.models.UserInfoModel;
 | 
			
		||||
import cc.winboll.studio.libappbase.utils.RSAUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.utils.YunUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
import java.security.KeyPair;
 | 
			
		||||
import java.security.PrivateKey;
 | 
			
		||||
import java.security.PublicKey;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/06/04 13:29
 | 
			
		||||
 * @Describe 用户登录框
 | 
			
		||||
 */
 | 
			
		||||
public class LogonActivity extends Activity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "LogonActivity";
 | 
			
		||||
 | 
			
		||||
    public static final String DEBUG_HOST = "http://10.8.0.250:456";
 | 
			
		||||
    public static final String YUN_HOST = "https://yun.winboll.cc";
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    String mHost = "";
 | 
			
		||||
    RadioButton mrbYunHost;
 | 
			
		||||
    RadioButton mrbDebugHost;
 | 
			
		||||
    LogView mLogView;
 | 
			
		||||
    
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        setContentView(R.layout.activity_logon);
 | 
			
		||||
        mLogView = findViewById(R.id.logview);
 | 
			
		||||
        mLogView.start();
 | 
			
		||||
 | 
			
		||||
        mHost = BuildConfig.DEBUG ? DEBUG_HOST: YUN_HOST;
 | 
			
		||||
        if (BuildConfig.DEBUG) {
 | 
			
		||||
            mrbYunHost = findViewById(R.id.rb_yunhost);
 | 
			
		||||
            mrbDebugHost = findViewById(R.id.rb_debughost);
 | 
			
		||||
            mrbYunHost.setChecked(!BuildConfig.DEBUG);
 | 
			
		||||
            mrbDebugHost.setChecked(BuildConfig.DEBUG);
 | 
			
		||||
        } else {
 | 
			
		||||
            findViewById(R.id.ll_hostbar).setVisibility(View.GONE);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public void onSwitchHost(View view) {
 | 
			
		||||
        if (view.getId() == R.id.rb_yunhost) {
 | 
			
		||||
            mrbDebugHost.setChecked(false);
 | 
			
		||||
            mHost = YUN_HOST;
 | 
			
		||||
        } else if (view.getId() == R.id.rb_debughost) {
 | 
			
		||||
            mrbYunHost.setChecked(false);
 | 
			
		||||
            mHost = DEBUG_HOST;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onResume() {
 | 
			
		||||
        super.onResume();
 | 
			
		||||
        mLogView.start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void onTestLogin(View view) {
 | 
			
		||||
        LogUtils.d(TAG, "onTestLogin");
 | 
			
		||||
        final YunUtils yunUtils = YunUtils.getInstance(this);
 | 
			
		||||
        
 | 
			
		||||
        UserInfoModel userInfoModel = new UserInfoModel();
 | 
			
		||||
        userInfoModel.setUsername("jian");
 | 
			
		||||
        userInfoModel.setPassword("kkiio");
 | 
			
		||||
        userInfoModel.setToken("aaa111");
 | 
			
		||||
        yunUtils.login(mHost, userInfoModel);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void onTestRSA(View view) {
 | 
			
		||||
        LogUtils.d(TAG, "onTestRSA");
 | 
			
		||||
        RSAUtils utils = RSAUtils.getInstance(this);
 | 
			
		||||
        
 | 
			
		||||
        try {
 | 
			
		||||
            // 测试 1:首次生成密钥对
 | 
			
		||||
            LogUtils.d(TAG, "==== 首次生成密钥对 ====");
 | 
			
		||||
            if (utils.keysExist()) {
 | 
			
		||||
                LogUtils.d(TAG, "密钥对已生成");
 | 
			
		||||
            } else {
 | 
			
		||||
                utils.generateAndSaveKeys();
 | 
			
		||||
                LogUtils.d(TAG, "密钥对生成成功。");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 测试 2:获取密钥对(自动读取已生成的文件)
 | 
			
		||||
            KeyPair keyPair = utils.getOrGenerateKeys();
 | 
			
		||||
            PublicKey publicKey = keyPair.getPublic();
 | 
			
		||||
            PrivateKey privateKey = keyPair.getPrivate();
 | 
			
		||||
 | 
			
		||||
            // 打印密钥信息
 | 
			
		||||
            LogUtils.d(TAG, "\n==== 密钥信息 ====");
 | 
			
		||||
            LogUtils.d(TAG, "公钥算法:" + publicKey.getAlgorithm());
 | 
			
		||||
            LogUtils.d(TAG, "公钥编码长度:" + publicKey.getEncoded().length + "字节");
 | 
			
		||||
            LogUtils.d(TAG, "私钥算法:" + privateKey.getAlgorithm());
 | 
			
		||||
            LogUtils.d(TAG, "私钥编码长度:" + privateKey.getEncoded().length + "字节");
 | 
			
		||||
 | 
			
		||||
            // 测试 3:重复调用时检查是否复用文件
 | 
			
		||||
            LogUtils.d(TAG, "\n==== 二次调用 ====");
 | 
			
		||||
            KeyPair reusedPair = utils.getOrGenerateKeys();
 | 
			
		||||
            LogUtils.d(TAG, "是否为同一公钥:" + (publicKey.equals(reusedPair.getPublic()))); // true(单例引用)
 | 
			
		||||
            LogUtils.d(TAG, "操作完成");
 | 
			
		||||
 | 
			
		||||
            String testMessage = "Hello, RSA Encryption!";
 | 
			
		||||
 | 
			
		||||
            // 1. 获取或生成密钥对
 | 
			
		||||
            PublicKey publicKeyReused = reusedPair.getPublic();
 | 
			
		||||
            PrivateKey privateKeyReused = reusedPair.getPrivate();
 | 
			
		||||
 | 
			
		||||
            // 2. 公钥加密
 | 
			
		||||
            byte[] encryptedData = utils.encryptWithPublicKey(testMessage, publicKeyReused);
 | 
			
		||||
            LogUtils.d(TAG, "加密后数据(字节长度):" + encryptedData.length);
 | 
			
		||||
 | 
			
		||||
            // 3. 私钥解密
 | 
			
		||||
            String decryptedMessage = utils.decryptWithPrivateKey(encryptedData, privateKeyReused);
 | 
			
		||||
            LogUtils.d(TAG, "解密结果: " + decryptedMessage);
 | 
			
		||||
            
 | 
			
		||||
            // 4. 验证解密是否成功
 | 
			
		||||
            if (testMessage.equals(decryptedMessage)) {
 | 
			
		||||
                LogUtils.d(TAG, "加密解密测试通过!");
 | 
			
		||||
            } else {
 | 
			
		||||
                LogUtils.d(TAG, "测试失败:内容不一致");
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,126 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.activities;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import cc.winboll.studio.libappbase.BuildConfig;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.R;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import okhttp3.OkHttpClient;
 | 
			
		||||
import okhttp3.Request;
 | 
			
		||||
import okhttp3.Response;
 | 
			
		||||
import android.widget.RadioButton;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogView;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/06/04 11:06
 | 
			
		||||
 * @Describe 云宝云
 | 
			
		||||
 */
 | 
			
		||||
public class YunActivity extends Activity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "YunActivity";
 | 
			
		||||
 | 
			
		||||
    public static final String DEBUG_HOST = "http://10.8.0.250:456";
 | 
			
		||||
    public static final String YUN_HOST = "https://yun.winboll.cc";
 | 
			
		||||
 | 
			
		||||
    String mHost = "";
 | 
			
		||||
    RadioButton mrbYunHost;
 | 
			
		||||
    RadioButton mrbDebugHost;
 | 
			
		||||
    LogView mLogView;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        setContentView(R.layout.activity_yun);
 | 
			
		||||
        mLogView = findViewById(R.id.logview);
 | 
			
		||||
        mLogView.start();
 | 
			
		||||
 | 
			
		||||
        mHost = BuildConfig.DEBUG ? DEBUG_HOST: YUN_HOST;
 | 
			
		||||
        if (BuildConfig.DEBUG) {
 | 
			
		||||
            mrbYunHost = findViewById(R.id.rb_yunhost);
 | 
			
		||||
            mrbDebugHost = findViewById(R.id.rb_debughost);
 | 
			
		||||
            mrbYunHost.setChecked(!BuildConfig.DEBUG);
 | 
			
		||||
            mrbDebugHost.setChecked(BuildConfig.DEBUG);
 | 
			
		||||
        } else {
 | 
			
		||||
            findViewById(R.id.ll_hostbar).setVisibility(View.GONE);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void onSwitchHost(View view) {
 | 
			
		||||
        if (view.getId() == R.id.rb_yunhost) {
 | 
			
		||||
            mrbDebugHost.setChecked(false);
 | 
			
		||||
            mHost = YUN_HOST;
 | 
			
		||||
        } else if (view.getId() == R.id.rb_debughost) {
 | 
			
		||||
            mrbYunHost.setChecked(false);
 | 
			
		||||
            mHost = DEBUG_HOST;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onResume() {
 | 
			
		||||
        super.onResume();
 | 
			
		||||
        mLogView.start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void onTestYun(View view) {
 | 
			
		||||
        LogUtils.d(TAG, "onTestYun");
 | 
			
		||||
        (new Thread(new Runnable(){
 | 
			
		||||
                @Override
 | 
			
		||||
                public void run() {
 | 
			
		||||
                    testYun();
 | 
			
		||||
                }
 | 
			
		||||
            })).start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void testYun() {
 | 
			
		||||
        OkHttpClient client = new OkHttpClient();
 | 
			
		||||
        Request request = new Request.Builder()
 | 
			
		||||
            .url(mHost + "/backups/")
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
        Response response = null;
 | 
			
		||||
        try {
 | 
			
		||||
            response = client.newCall(request).execute();
 | 
			
		||||
            if (response.isSuccessful()) {
 | 
			
		||||
                String responseBody = "";
 | 
			
		||||
                if (response.body() != null) {
 | 
			
		||||
                    responseBody = response.body().string();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 正则匹配:任意主机名 -> Test OK(主机名部分匹配非空字符)
 | 
			
		||||
                boolean isMatch = responseBody.matches(".+? -> Test OK");
 | 
			
		||||
 | 
			
		||||
                if (isMatch) {
 | 
			
		||||
                    LogUtils.d(TAG, responseBody);
 | 
			
		||||
                } else {
 | 
			
		||||
                    LogUtils.d(TAG, "响应内容不匹配,内容:" + responseBody);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                LogUtils.d(TAG, "请求失败,状态码:" + response.code());
 | 
			
		||||
            }
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            LogUtils.d(TAG, "读取响应体失败:" + e.getMessage());
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            LogUtils.d(TAG, "异常:" + e.getMessage());
 | 
			
		||||
            e.printStackTrace(); // Java 7 需显式打印堆栈
 | 
			
		||||
        } finally {
 | 
			
		||||
            // 手动关闭 Response(Java 7 不支持 try-with-resources)
 | 
			
		||||
            if (response != null && response.body() != null) {
 | 
			
		||||
                response.body().close();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,53 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.models;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/06/05 11:26
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
public class ResponseData {
 | 
			
		||||
    
 | 
			
		||||
    public static final String STATUS_SUCCESS = "success";
 | 
			
		||||
    public static final String STATUS_ERROR = "error";
 | 
			
		||||
    
 | 
			
		||||
    private String status;
 | 
			
		||||
    private String message;
 | 
			
		||||
    private UserInfoModel data;
 | 
			
		||||
    
 | 
			
		||||
    public ResponseData() {
 | 
			
		||||
        this.status = "";
 | 
			
		||||
        this.message = "";
 | 
			
		||||
        this.data = new UserInfoModel();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public ResponseData(String status, String message, UserInfoModel data) {
 | 
			
		||||
        this.status = status;
 | 
			
		||||
        this.message = message;
 | 
			
		||||
        this.data = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setStatus(String status) {
 | 
			
		||||
        this.status = status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getStatus() {
 | 
			
		||||
        return status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setMessage(String message) {
 | 
			
		||||
        this.message = message;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getMessage() {
 | 
			
		||||
        return message;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setData(UserInfoModel data) {
 | 
			
		||||
        this.data = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public UserInfoModel getData() {
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,92 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.models;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/06/04 19:14
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class UserInfoModel extends BaseBean {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "UserInfoModel";
 | 
			
		||||
 | 
			
		||||
    String username;
 | 
			
		||||
    String password;
 | 
			
		||||
    String token;
 | 
			
		||||
 | 
			
		||||
    public UserInfoModel() {
 | 
			
		||||
        this.username = "";
 | 
			
		||||
        this.password = "";
 | 
			
		||||
        this.token = "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setUsername(String username) {
 | 
			
		||||
        this.username = username;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getUsername() {
 | 
			
		||||
        return username;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setPassword(String password) {
 | 
			
		||||
        this.password = password;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getPassword() {
 | 
			
		||||
        return password;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setToken(String token) {
 | 
			
		||||
        this.token = token;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getToken() {
 | 
			
		||||
        return token;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return UserInfoModel.class.getName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
			
		||||
        super.writeThisToJsonWriter(jsonWriter);
 | 
			
		||||
        jsonWriter.name("username").value(getUsername());
 | 
			
		||||
        jsonWriter.name("password").value(getPassword());
 | 
			
		||||
        jsonWriter.name("token").value(getToken());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
 | 
			
		||||
        if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
 | 
			
		||||
            if (name.equals("username")) {
 | 
			
		||||
                setUsername(jsonReader.nextString());
 | 
			
		||||
            } else if (name.equals("password")) {
 | 
			
		||||
                setPassword(jsonReader.nextString());
 | 
			
		||||
            } else if (name.equals("token")) {
 | 
			
		||||
                setToken(jsonReader.nextString());
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
 | 
			
		||||
        jsonReader.beginObject();
 | 
			
		||||
        while (jsonReader.hasNext()) {
 | 
			
		||||
            String name = jsonReader.nextName();
 | 
			
		||||
            if (!initObjectsFromJsonReader(jsonReader, name)) {
 | 
			
		||||
                jsonReader.skipValue();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 结束 JSON 对象
 | 
			
		||||
        jsonReader.endObject();
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,128 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/06/04 20:15
 | 
			
		||||
 * @Describe 文件操作类
 | 
			
		||||
 */
 | 
			
		||||
import java.io.BufferedReader;
 | 
			
		||||
import java.io.BufferedWriter;
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.FileReader;
 | 
			
		||||
import java.io.FileWriter;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class FileUtils {
 | 
			
		||||
    public static final String TAG = "FileUtils";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 读取文件为字节数组(Java 7 语法)
 | 
			
		||||
     */
 | 
			
		||||
    public static byte[] readFileToByteArray(String filePath) {
 | 
			
		||||
        FileInputStream fis = null;
 | 
			
		||||
        ByteArrayOutputStream bos = null;
 | 
			
		||||
        try {
 | 
			
		||||
            fis = new FileInputStream(filePath);
 | 
			
		||||
            bos = new ByteArrayOutputStream();
 | 
			
		||||
            byte[] buffer = new byte[4096];
 | 
			
		||||
            int bytesRead;
 | 
			
		||||
            while ((bytesRead = fis.read(buffer)) != -1) {
 | 
			
		||||
                bos.write(buffer, 0, bytesRead);
 | 
			
		||||
            }
 | 
			
		||||
            return bos.toByteArray();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return null;
 | 
			
		||||
        } finally {
 | 
			
		||||
            // 手动关闭流(Java 7 不支持 try-with-resources)
 | 
			
		||||
            if (fis != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    fis.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (bos != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    bos.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 写入字节数组到文件(Java 7 语法)
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean writeByteArrayToFile(byte[] data, String filePath) {
 | 
			
		||||
        FileOutputStream fos = null;
 | 
			
		||||
        try {
 | 
			
		||||
            fos = new FileOutputStream(filePath);
 | 
			
		||||
            fos.write(data);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return false;
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (fos != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    fos.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 原字符串读写方法(适配 Java 7)
 | 
			
		||||
    public static String readFileToString(String filePath) {
 | 
			
		||||
        BufferedReader reader = null;
 | 
			
		||||
        try {
 | 
			
		||||
            reader = new BufferedReader(new FileReader(filePath));
 | 
			
		||||
            StringBuilder content = new StringBuilder();
 | 
			
		||||
            String line;
 | 
			
		||||
            while ((line = reader.readLine()) != null) {
 | 
			
		||||
                content.append(line).append(System.getProperty("line.separator"));
 | 
			
		||||
            }
 | 
			
		||||
            // 去除最后一个换行符(可选)
 | 
			
		||||
            if (content.length() > 0) {
 | 
			
		||||
                content.deleteCharAt(content.length() - 1);
 | 
			
		||||
            }
 | 
			
		||||
            return content.toString();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return null;
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (reader != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    reader.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean writeStringToFile(String content, String filePath, boolean append) {
 | 
			
		||||
        BufferedWriter writer = null;
 | 
			
		||||
        try {
 | 
			
		||||
            writer = new BufferedWriter(new FileWriter(filePath, append));
 | 
			
		||||
            writer.write(content);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return false;
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (writer != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    writer.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,222 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/06/04 13:36
 | 
			
		||||
 * @Describe RSA加密工具
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.util.Base64;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.security.KeyFactory;
 | 
			
		||||
import java.security.KeyPair;
 | 
			
		||||
import java.security.KeyPairGenerator;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
import java.security.PrivateKey;
 | 
			
		||||
import java.security.PublicKey;
 | 
			
		||||
import java.security.spec.InvalidKeySpecException;
 | 
			
		||||
import java.security.spec.PKCS8EncodedKeySpec;
 | 
			
		||||
import java.security.spec.X509EncodedKeySpec;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import javax.crypto.Cipher;
 | 
			
		||||
 | 
			
		||||
public class RSAUtils {
 | 
			
		||||
    private static final String TAG = "RSAUtils";
 | 
			
		||||
    private static final int KEY_SIZE = 2048;
 | 
			
		||||
    private static final String KEY_ALGORITHM = "RSA";
 | 
			
		||||
    private static final String PUBLIC_KEY_FILE = "public.key";
 | 
			
		||||
    private static final String PRIVATE_KEY_FILE = "private.key";
 | 
			
		||||
    private static final String CIPHER_ALGORITHM = KEY_ALGORITHM + "/ECB/PKCS1Padding"; // 保留原加密方式
 | 
			
		||||
 | 
			
		||||
    private final String keyPath;
 | 
			
		||||
    private static volatile RSAUtils INSTANCE;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 构造方法:初始化密钥存储路径(内部存储)
 | 
			
		||||
     */
 | 
			
		||||
    private RSAUtils(Context context) {
 | 
			
		||||
        keyPath = context.getFilesDir() + File.separator + "keys" + File.separator; // 修正路径格式
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取单例实例
 | 
			
		||||
     */
 | 
			
		||||
    public static synchronized RSAUtils getInstance(Context context) {
 | 
			
		||||
        if (INSTANCE == null) {
 | 
			
		||||
            INSTANCE = new RSAUtils(context);
 | 
			
		||||
        }
 | 
			
		||||
        return INSTANCE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查密钥文件是否存在
 | 
			
		||||
     */
 | 
			
		||||
    public boolean keysExist() {
 | 
			
		||||
        File publicKeyFile = new File(keyPath + PUBLIC_KEY_FILE);
 | 
			
		||||
        File privateKeyFile = new File(keyPath + PRIVATE_KEY_FILE);
 | 
			
		||||
        return publicKeyFile.exists() && privateKeyFile.exists();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成密钥对并保存到文件
 | 
			
		||||
     */
 | 
			
		||||
    public void generateAndSaveKeys() throws Exception {
 | 
			
		||||
        LogUtils.d(TAG, "开始生成 RSA 密钥对(2048位)");
 | 
			
		||||
        KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
 | 
			
		||||
        generator.initialize(KEY_SIZE);
 | 
			
		||||
        KeyPair keyPair = generator.generateKeyPair();
 | 
			
		||||
 | 
			
		||||
        saveKey(PUBLIC_KEY_FILE, keyPair.getPublic().getEncoded());
 | 
			
		||||
        saveKey(PRIVATE_KEY_FILE, keyPair.getPrivate().getEncoded());
 | 
			
		||||
        LogUtils.d(TAG, "密钥对生成并保存成功");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取或生成密钥对(线程安全)
 | 
			
		||||
     */
 | 
			
		||||
    public KeyPair getOrGenerateKeys() throws Exception {
 | 
			
		||||
        if (!keysExist()) {
 | 
			
		||||
            synchronized (RSAUtils.class) { // 双重检查锁,避免多线程重复生成
 | 
			
		||||
                if (!keysExist()) {
 | 
			
		||||
                    generateAndSaveKeys();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return readKeysFromFile();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 从文件读取密钥对
 | 
			
		||||
     */
 | 
			
		||||
    private KeyPair readKeysFromFile() throws Exception {
 | 
			
		||||
        LogUtils.d(TAG, "读取密钥对文件");
 | 
			
		||||
        try {
 | 
			
		||||
            byte[] publicKeyBytes = readFileToBytes(keyPath + PUBLIC_KEY_FILE);
 | 
			
		||||
            byte[] privateKeyBytes = readFileToBytes(keyPath + PRIVATE_KEY_FILE);
 | 
			
		||||
 | 
			
		||||
            X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(publicKeyBytes);
 | 
			
		||||
            PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(privateKeyBytes);
 | 
			
		||||
 | 
			
		||||
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
 | 
			
		||||
            PublicKey publicKey = factory.generatePublic(publicSpec);
 | 
			
		||||
            PrivateKey privateKey = factory.generatePrivate(privateSpec);
 | 
			
		||||
 | 
			
		||||
            return new KeyPair(publicKey, privateKey);
 | 
			
		||||
        } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
 | 
			
		||||
            LogUtils.e(TAG, "密钥文件读取失败:" + e.getMessage());
 | 
			
		||||
            throw new Exception("密钥文件损坏或格式错误", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 保存密钥到文件(通用方法)
 | 
			
		||||
     */
 | 
			
		||||
    private void saveKey(String fileName, byte[] keyBytes) throws IOException {
 | 
			
		||||
        Objects.requireNonNull(keyBytes, "密钥字节数据不可为空");
 | 
			
		||||
        File dir = new File(keyPath);
 | 
			
		||||
        if (!dir.exists() && !dir.mkdirs()) {
 | 
			
		||||
            throw new IOException("创建密钥目录失败:" + keyPath);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        FileOutputStream fos = null;
 | 
			
		||||
        try {
 | 
			
		||||
            fos = new FileOutputStream(keyPath + fileName);
 | 
			
		||||
            fos.write(keyBytes);
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (fos != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    fos.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    LogUtils.e(TAG, "关闭文件流失败:" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 读取文件为字节数组(Java 7 兼容)
 | 
			
		||||
     */
 | 
			
		||||
    private byte[] readFileToBytes(String filePath) throws IOException {
 | 
			
		||||
        File file = new File(filePath);
 | 
			
		||||
        if (!file.exists() || file.isDirectory()) {
 | 
			
		||||
            throw new IOException("文件不存在或为目录:" + filePath);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        FileInputStream fis = null;
 | 
			
		||||
        try {
 | 
			
		||||
            fis = new FileInputStream(file);
 | 
			
		||||
            byte[] data = new byte[(int) file.length()];
 | 
			
		||||
            int bytesRead = fis.read(data);
 | 
			
		||||
            if (bytesRead != data.length) {
 | 
			
		||||
                throw new IOException("文件读取不完整");
 | 
			
		||||
            }
 | 
			
		||||
            return data;
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (fis != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    fis.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    LogUtils.e(TAG, "关闭文件流失败:" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 公钥加密(带参数校验)
 | 
			
		||||
     */
 | 
			
		||||
    public byte[] encryptWithPublicKey(String plainText, PublicKey publicKey) throws Exception {
 | 
			
		||||
        Objects.requireNonNull(plainText, "明文不可为空");
 | 
			
		||||
        Objects.requireNonNull(publicKey, "公钥不可为空");
 | 
			
		||||
 | 
			
		||||
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
 | 
			
		||||
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
 | 
			
		||||
 | 
			
		||||
        // 检查数据长度是否超过 RSA 限制(2048位密钥最大明文为 214字节,PKCS1Padding)
 | 
			
		||||
        int maxPlainTextSize = cipher.getBlockSize() - 11; // PKCS1Padding 固定填充长度
 | 
			
		||||
        if (plainText.getBytes("UTF-8").length > maxPlainTextSize) {
 | 
			
		||||
            throw new IllegalArgumentException("明文过长,最大支持 " + maxPlainTextSize + " 字节");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return cipher.doFinal(plainText.getBytes("UTF-8"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 私钥解密(带参数校验)
 | 
			
		||||
     */
 | 
			
		||||
    public String decryptWithPrivateKey(byte[] encryptedData, PrivateKey privateKey) throws Exception {
 | 
			
		||||
        Objects.requireNonNull(encryptedData, "密文不可为空");
 | 
			
		||||
        Objects.requireNonNull(privateKey, "私钥不可为空");
 | 
			
		||||
 | 
			
		||||
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
 | 
			
		||||
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
 | 
			
		||||
        byte[] decryptedBytes = cipher.doFinal(encryptedData);
 | 
			
		||||
        return new String(decryptedBytes, "UTF-8");
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 将 HTTP 传输的 Base64 字符串还原为加密字节数组(Java 7 兼容)
 | 
			
		||||
     * @param httpString Base64 字符串(非 null)
 | 
			
		||||
     * @return 加密字节数组
 | 
			
		||||
     * @throws IllegalArgumentException 解码失败时抛出
 | 
			
		||||
     */
 | 
			
		||||
    public byte[] httpStringToEncryptBytes(String httpString) {
 | 
			
		||||
        Objects.requireNonNull(httpString, "HTTP 字符串不可为空");
 | 
			
		||||
 | 
			
		||||
        // 计算缺失的填充符数量(Java 7 不支持 repeat(),手动拼接)
 | 
			
		||||
        int pad = httpString.length() % 4;
 | 
			
		||||
        StringBuilder paddedString = new StringBuilder(httpString);
 | 
			
		||||
        if (pad != 0) {
 | 
			
		||||
            for (int i = 0; i < pad; i++) {
 | 
			
		||||
                paddedString.append('='); // 补全 '='
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 使用 Base64 解码(Android 原生 Base64 类兼容 Java 7)
 | 
			
		||||
        return Base64.decode(paddedString.toString(), Base64.URL_SAFE);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,281 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/06/04 17:21
 | 
			
		||||
 * @Describe 应用登录与接口工具
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.Looper;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.models.ResponseData;
 | 
			
		||||
import cc.winboll.studio.libappbase.models.UserInfoModel;
 | 
			
		||||
import com.google.gson.Gson;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.net.URLDecoder;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.security.KeyPair;
 | 
			
		||||
import java.security.PrivateKey;
 | 
			
		||||
import java.security.PublicKey;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import okhttp3.Call;
 | 
			
		||||
import okhttp3.Callback;
 | 
			
		||||
import okhttp3.MediaType;
 | 
			
		||||
import okhttp3.OkHttpClient;
 | 
			
		||||
import okhttp3.Request;
 | 
			
		||||
import okhttp3.RequestBody;
 | 
			
		||||
import okhttp3.Response;
 | 
			
		||||
import java.io.UnsupportedEncodingException;
 | 
			
		||||
 | 
			
		||||
public class YunUtils {
 | 
			
		||||
    public static final String TAG = "YunUtils";
 | 
			
		||||
    // 私有静态实例,类加载时创建
 | 
			
		||||
    private static volatile YunUtils INSTANCE;
 | 
			
		||||
    Context mContext;
 | 
			
		||||
    UserInfoModel mUserInfoModel;
 | 
			
		||||
    String token = "";
 | 
			
		||||
    String mDataFolderPath = "";
 | 
			
		||||
    String mUserInfoModelPath = "";
 | 
			
		||||
 | 
			
		||||
    private static final int CONNECT_TIMEOUT = 15; // 连接超时时间(秒)
 | 
			
		||||
    private static final int READ_TIMEOUT = 20;    // 读取超时时间(秒)
 | 
			
		||||
    private static volatile YunUtils instance;
 | 
			
		||||
    private OkHttpClient okHttpClient;
 | 
			
		||||
    private Handler mainHandler; // 主线程 Handler
 | 
			
		||||
 | 
			
		||||
    // 私有构造方法,防止外部实例化
 | 
			
		||||
    private YunUtils(Context context) {
 | 
			
		||||
        LogUtils.d(TAG, "YunUtils");
 | 
			
		||||
        mContext = context;
 | 
			
		||||
        mDataFolderPath = mContext.getExternalFilesDir(TAG).toString();
 | 
			
		||||
        File fTest = new File(mDataFolderPath);
 | 
			
		||||
        if (!fTest.exists()) {
 | 
			
		||||
            fTest.mkdirs();
 | 
			
		||||
        }
 | 
			
		||||
        mUserInfoModelPath = mDataFolderPath + File.separator + "UserInfoModel.rsajson";
 | 
			
		||||
 | 
			
		||||
        okHttpClient = new OkHttpClient.Builder()
 | 
			
		||||
            .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
 | 
			
		||||
            .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
 | 
			
		||||
            .build();
 | 
			
		||||
        mainHandler = new Handler(Looper.getMainLooper()); // 获取主线程 Looper
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 公共静态方法,返回唯一实例
 | 
			
		||||
    public static synchronized YunUtils getInstance(Context context) {
 | 
			
		||||
        LogUtils.d(TAG, "getInstance");
 | 
			
		||||
        if (INSTANCE == null) {
 | 
			
		||||
            INSTANCE = new YunUtils(context);
 | 
			
		||||
        }
 | 
			
		||||
        return INSTANCE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void checkLoginStatus() {
 | 
			
		||||
        String token = getLocalToken();
 | 
			
		||||
        LogUtils.d(TAG, String.format("checkLoginStatus token is %s", token));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    String getLocalToken() {
 | 
			
		||||
        UserInfoModel userInfoModel = loadUserInfoModel();
 | 
			
		||||
        return (userInfoModel == null) ?"": userInfoModel.getToken();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void login(String host, UserInfoModel userInfoModel) {
 | 
			
		||||
        LogUtils.d(TAG, "login");
 | 
			
		||||
 | 
			
		||||
        // 发送 POST 请求
 | 
			
		||||
        String apiUrl = host + "/login/index.php";
 | 
			
		||||
        // 序列化对象为JSON
 | 
			
		||||
        Gson gson = new Gson();
 | 
			
		||||
        String jsonData = gson.toJson(userInfoModel); // 自动生成标准JSON
 | 
			
		||||
        //String jsonData = userInfoModel.toString();
 | 
			
		||||
        LogUtils.d(TAG, "要发送的数据 : " + jsonData);
 | 
			
		||||
 | 
			
		||||
        sendPostRequest(apiUrl, jsonData, new OnResponseListener() {
 | 
			
		||||
                // 成功回调(主线程)
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onSuccess(String responseBody) {
 | 
			
		||||
                    LogUtils.d(TAG, "onSuccess");
 | 
			
		||||
                    LogUtils.d(TAG, String.format("responseBody %s", responseBody));
 | 
			
		||||
                    Gson gson = new Gson();
 | 
			
		||||
                    ResponseData result = gson.fromJson(responseBody, ResponseData.class); // 转为 Result 实例
 | 
			
		||||
                    if(result.getStatus().equals(ResponseData.STATUS_SUCCESS)) {
 | 
			
		||||
                        
 | 
			
		||||
                            UserInfoModel userInfoModel = result.getData();
 | 
			
		||||
                            if (userInfoModel != null) {
 | 
			
		||||
                                LogUtils.d(TAG, "收到网站 UserInfoModel");
 | 
			
		||||
                                String token = userInfoModel.getToken();
 | 
			
		||||
                                saveLocalToken(token);
 | 
			
		||||
                                checkLoginStatus();
 | 
			
		||||
                            }
 | 
			
		||||
                       
 | 
			
		||||
                    } else if(result.getStatus().equals(ResponseData.STATUS_ERROR)) {
 | 
			
		||||
                        try {
 | 
			
		||||
                            String decodedMessage = URLDecoder.decode(result.getMessage(), "UTF-8");
 | 
			
		||||
                            LogUtils.d(TAG, "服务器返回信息: " + decodedMessage);
 | 
			
		||||
                        } catch (UnsupportedEncodingException e) {
 | 
			
		||||
                            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 失败回调(主线程)
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onFailure(String errorMsg) {
 | 
			
		||||
                    LogUtils.d(TAG, errorMsg);
 | 
			
		||||
                    // 处理错误
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void saveLocalToken(String token) {
 | 
			
		||||
        UserInfoModel userInfoModel = new UserInfoModel();
 | 
			
		||||
        userInfoModel.setToken(token);
 | 
			
		||||
        saveUserInfoModel(userInfoModel);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    UserInfoModel loadUserInfoModel() {
 | 
			
		||||
        LogUtils.d(TAG, "loadUserInfoModel");
 | 
			
		||||
        if (new File(mUserInfoModelPath).exists()) {
 | 
			
		||||
            try {
 | 
			
		||||
                // 加载加密后的模型数据
 | 
			
		||||
                byte[] encryptedData = FileUtils.readFileToByteArray(mUserInfoModelPath);
 | 
			
		||||
                // 加载 RSA 工具
 | 
			
		||||
                RSAUtils utils = RSAUtils.getInstance(mContext);
 | 
			
		||||
                KeyPair keyPair = utils.getOrGenerateKeys();
 | 
			
		||||
                //PublicKey publicKey = keyPair.getPublic();
 | 
			
		||||
                PrivateKey privateKey = keyPair.getPrivate();
 | 
			
		||||
                // 私钥解密模型数据
 | 
			
		||||
                String szInfo = utils.decryptWithPrivateKey(encryptedData, keyPair.getPrivate());
 | 
			
		||||
                LogUtils.d(TAG, String.format("szInfo %s", szInfo));
 | 
			
		||||
                mUserInfoModel = UserInfoModel.parseStringToBean(szInfo, UserInfoModel.class);
 | 
			
		||||
                if (mUserInfoModel == null) {
 | 
			
		||||
                    LogUtils.d(TAG, "模型数据解析为空数据。");
 | 
			
		||||
                }
 | 
			
		||||
                LogUtils.d(TAG, "UserInfoModel 解密加载结束。");
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            LogUtils.d(TAG, "云服务登录信息不存在。");
 | 
			
		||||
            mUserInfoModel = null;
 | 
			
		||||
        }
 | 
			
		||||
        return mUserInfoModel;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void saveUserInfoModel(UserInfoModel userInfoModel) {
 | 
			
		||||
        LogUtils.d(TAG, "saveUserInfoModel");
 | 
			
		||||
        try {
 | 
			
		||||
            String szInfo = userInfoModel.toString();
 | 
			
		||||
            LogUtils.d(TAG, "原始数据: " + szInfo);
 | 
			
		||||
 | 
			
		||||
            RSAUtils utils = RSAUtils.getInstance(mContext);
 | 
			
		||||
            KeyPair keyPair = utils.getOrGenerateKeys();
 | 
			
		||||
            PublicKey publicKey = keyPair.getPublic();
 | 
			
		||||
 | 
			
		||||
            // 公钥加密(传入字节数组,避免中间字符串转换)
 | 
			
		||||
            byte[] encryptedData = utils.encryptWithPublicKey(szInfo, publicKey);
 | 
			
		||||
 | 
			
		||||
            // 保存加密字节数组到文件(直接操作字节,无需转字符串)
 | 
			
		||||
            FileUtils.writeByteArrayToFile(encryptedData, mUserInfoModelPath);
 | 
			
		||||
            LogUtils.d(TAG, "加密数据已保存");
 | 
			
		||||
 | 
			
		||||
            // 测试解密(仅调试用)
 | 
			
		||||
            String szInfo2 = utils.decryptWithPrivateKey(encryptedData, keyPair.getPrivate());
 | 
			
		||||
            LogUtils.d(TAG, "解密结果: " + szInfo2);
 | 
			
		||||
 | 
			
		||||
            mUserInfoModel = UserInfoModel.parseStringToBean(szInfo2, UserInfoModel.class);
 | 
			
		||||
            if (mUserInfoModel == null) {
 | 
			
		||||
                LogUtils.d(TAG, "模型解析失败");
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            LogUtils.d(TAG, "加密/解密失败: " + e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 发送 POST 请求(JSON 数据)
 | 
			
		||||
    public void sendPostRequest(String url, String data, OnResponseListener listener) {
 | 
			
		||||
        RequestBody requestBody = RequestBody.create(
 | 
			
		||||
            MediaType.parse("application/json; charset=utf-8"), // 关键头信息
 | 
			
		||||
            data.getBytes(StandardCharsets.UTF_8)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        Request request = new Request.Builder()
 | 
			
		||||
            .url(url)
 | 
			
		||||
            .post(requestBody)
 | 
			
		||||
            .addHeader("Content-Type", "application/json") // 显式添加头
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
        executeRequest(request, listener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 发送 GET 请求
 | 
			
		||||
    public void sendGetRequest(String url, OnResponseListener listener) {
 | 
			
		||||
        Request request = new Request.Builder()
 | 
			
		||||
            .url(url)
 | 
			
		||||
            .get()
 | 
			
		||||
            .build();
 | 
			
		||||
        executeRequest(request, listener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 执行请求(子线程处理)
 | 
			
		||||
    private void executeRequest(final Request request, final OnResponseListener listener) {
 | 
			
		||||
        okHttpClient.newCall(request).enqueue(new Callback() {
 | 
			
		||||
                // 响应成功(子线程)
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onResponse(Call call, Response response) throws IOException {
 | 
			
		||||
                    try {
 | 
			
		||||
                        if (!response.isSuccessful()) {
 | 
			
		||||
                            postFailure(listener, "响应码错误:" + response.code());
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                        String responseBody = response.body().string();
 | 
			
		||||
                        postSuccess(listener, responseBody);
 | 
			
		||||
                    } catch (Exception e) {
 | 
			
		||||
                        postFailure(listener, "解析失败:" + e.getMessage());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 响应失败(子线程)
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onFailure(Call call, IOException e) {
 | 
			
		||||
                    postFailure(listener, "网络失败:" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 主线程回调(使用 Handler)
 | 
			
		||||
                private void postSuccess(final OnResponseListener listener, final String msg) {
 | 
			
		||||
                    mainHandler.post(new Runnable() {
 | 
			
		||||
                            @Override
 | 
			
		||||
                            public void run() {
 | 
			
		||||
                                listener.onSuccess(msg);
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                private void postFailure(final OnResponseListener listener, final String msg) {
 | 
			
		||||
                    mainHandler.post(new Runnable() {
 | 
			
		||||
                            @Override
 | 
			
		||||
                            public void run() {
 | 
			
		||||
                                listener.onFailure(msg);
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public interface OnResponseListener {
 | 
			
		||||
        /**
 | 
			
		||||
         * 成功响应(主线程回调)
 | 
			
		||||
         * @param responseBody 响应体字符串
 | 
			
		||||
         */
 | 
			
		||||
        void onSuccess(String responseBody);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 失败回调(包含错误信息)
 | 
			
		||||
         * @param errorMsg 错误描述
 | 
			
		||||
         */
 | 
			
		||||
        void onFailure(String errorMsg);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,7 +22,9 @@ import java.util.Map;
 | 
			
		||||
 | 
			
		||||
public class WinBoLLActivityManager {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "IWinBoLLActivityManager";
 | 
			
		||||
    public static final String TAG = "WinBoLLActivityManager";
 | 
			
		||||
 | 
			
		||||
    public static final String EXTRA_TAG = "EXTRA_TAG";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public enum WinBoLLUI_TYPE { Aplication, Service }
 | 
			
		||||
@@ -39,14 +41,14 @@ public class WinBoLLActivityManager {
 | 
			
		||||
    public static WinBoLLUI_TYPE getWinBoLLUI_TYPE() {
 | 
			
		||||
        return _WinBoLLUI_TYPE;
 | 
			
		||||
    }
 | 
			
		||||
    WinBoLLActivityManager(Context context) {
 | 
			
		||||
        mContext = context;
 | 
			
		||||
    WinBoLLActivityManager() {
 | 
			
		||||
        mContext = GlobalApplication.getInstance();
 | 
			
		||||
        mActivityListMap = new HashMap<String, IWinBoLLActivity>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static synchronized WinBoLLActivityManager getInstance(Context context) {
 | 
			
		||||
    public static synchronized WinBoLLActivityManager getInstance() {
 | 
			
		||||
        if (_mIWinBoLLActivityManager == null) {
 | 
			
		||||
            _mIWinBoLLActivityManager = new WinBoLLActivityManager(context);
 | 
			
		||||
            _mIWinBoLLActivityManager = new WinBoLLActivityManager();
 | 
			
		||||
        }
 | 
			
		||||
        return _mIWinBoLLActivityManager;
 | 
			
		||||
    }
 | 
			
		||||
@@ -55,7 +57,7 @@ public class WinBoLLActivityManager {
 | 
			
		||||
     * 把Activity添加到管理中
 | 
			
		||||
     */
 | 
			
		||||
    public <T extends IWinBoLLActivity> void add(T activity) {
 | 
			
		||||
        if (isActive(activity.getTag())) {
 | 
			
		||||
        if (isActivityActive(activity.getTag())) {
 | 
			
		||||
            LogUtils.d(TAG, String.format("add(...) %s is active.", activity.getTag()));
 | 
			
		||||
        } else {
 | 
			
		||||
            mActivityListMap.put(activity.getTag(), activity);
 | 
			
		||||
@@ -70,104 +72,107 @@ public class WinBoLLActivityManager {
 | 
			
		||||
    // intent.putExtra 函数 "tag" 参数为 activity.getTag()
 | 
			
		||||
    //
 | 
			
		||||
    public <T extends IWinBoLLActivity> void startWinBoLLActivity(Context context, Class<T> clazz) {
 | 
			
		||||
        try {
 | 
			
		||||
            // 如果窗口已存在就重启窗口
 | 
			
		||||
            String tag = clazz.newInstance().getTag();
 | 
			
		||||
            if (isActive(tag)) {
 | 
			
		||||
                resumeActivity(context, tag);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        // 如果窗口已存在就重启窗口
 | 
			
		||||
        if (!resumeActivity(clazz)) {
 | 
			
		||||
            // 新建一个任务窗口
 | 
			
		||||
            Intent intent = new Intent(context, clazz);
 | 
			
		||||
            //打开多任务窗口 flags
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
 | 
			
		||||
            intent.putExtra("tag", tag);
 | 
			
		||||
            mContext.startActivity(intent);
 | 
			
		||||
        } catch (InstantiationException | IllegalAccessException e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
            //intent.putExtra("tag", tag);
 | 
			
		||||
            context.startActivity(intent);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public <T extends IWinBoLLActivity> void startWinBoLLActivity(Context context, Intent intent, Class<T> clazz) {
 | 
			
		||||
        try {
 | 
			
		||||
            // 如果窗口已存在就重启窗口
 | 
			
		||||
            String tag = clazz.newInstance().getTag();
 | 
			
		||||
            if (isActive(tag)) {
 | 
			
		||||
                resumeActivity(context, tag);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        // 如果窗口已存在就重启窗口
 | 
			
		||||
        if (!resumeActivity(clazz)) {
 | 
			
		||||
            // 新建一个任务窗口
 | 
			
		||||
            //Intent intent = new Intent(context, clazz);
 | 
			
		||||
            //打开多任务窗口 flags
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
 | 
			
		||||
            intent.putExtra("tag", tag);
 | 
			
		||||
            mContext.startActivity(intent);
 | 
			
		||||
            //intent.putExtra("tag", tag);
 | 
			
		||||
            context.startActivity(intent);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public <T extends IWinBoLLActivity> void startLogActivity(Context context) {
 | 
			
		||||
        // 如果窗口已存在就重启窗口
 | 
			
		||||
        //if (!resumeActivity(LogActivity.class)) {
 | 
			
		||||
        // 新建一个任务窗口
 | 
			
		||||
        Intent intent = new Intent(context, LogActivity.class);
 | 
			
		||||
        //打开多任务窗口 flags
 | 
			
		||||
        // Define the bounds.
 | 
			
		||||
//        Rect bounds = new Rect(0, 0, 800, 200);
 | 
			
		||||
//        // Set the bounds as an activity option.
 | 
			
		||||
//        ActivityOptions options = ActivityOptions.makeBasic();
 | 
			
		||||
//        options.setLaunchBounds(bounds);
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
 | 
			
		||||
 | 
			
		||||
        //intent.putExtra(EXTRA_TAG, tag);
 | 
			
		||||
 | 
			
		||||
        //context.startActivity(intent, options.toBundle());
 | 
			
		||||
        context.startActivity(intent);
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 判断 tag 绑定的 Activity 是否已经创建
 | 
			
		||||
    //
 | 
			
		||||
    public boolean isActivityActive(String tag) {
 | 
			
		||||
        return mActivityListMap.get(tag) != null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Activity getActivityByTag(String tag) {
 | 
			
		||||
        return (mActivityListMap.get(tag) == null) ?null: mActivityListMap.get(tag).getActivity();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
 | 
			
		||||
    //
 | 
			
		||||
    public <T extends IWinBoLLActivity> boolean resumeActivity(Class<T> clazz) {
 | 
			
		||||
        try {
 | 
			
		||||
            Activity activity = getActivityByTag(clazz.newInstance().getTag());
 | 
			
		||||
            if (activity != null) {
 | 
			
		||||
                return resumeActivity(activity);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (InstantiationException | IllegalAccessException e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 判断 tag绑定的 MyActivity是否存在
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isActive(String tag) {
 | 
			
		||||
        //printAvtivityListInfo();
 | 
			
		||||
        IWinBoLLActivity iWinBoLLActivity = getIWinBoLLActivity(tag);
 | 
			
		||||
        if (iWinBoLLActivity != null) {
 | 
			
		||||
            Activity activity = iWinBoLLActivity.getActivity();
 | 
			
		||||
            if (activity != null) {
 | 
			
		||||
                LogUtils.d(TAG, "isActive(...) activity != null tag " + tag);
 | 
			
		||||
                //ToastUtils.show("activity != null tag " + tag);
 | 
			
		||||
                //判断是否为 BaseActivity,如果已经销毁,则移除
 | 
			
		||||
                if (activity.isFinishing() || activity.isDestroyed()) {
 | 
			
		||||
                    mActivityListMap.remove(iWinBoLLActivity.getTag());
 | 
			
		||||
                    //_mIWinBoLLActivityList.remove(activity);
 | 
			
		||||
                    LogUtils.d(TAG, String.format("isActive(...) remove activity.\ntag : %s", tag));
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else {
 | 
			
		||||
                    LogUtils.d(TAG, String.format("isActive(...) activity is exist.\ntag : %s", tag));
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
    //
 | 
			
		||||
    // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
 | 
			
		||||
    //
 | 
			
		||||
    public <T extends IWinBoLLActivity> boolean resumeActivity(String tag) {
 | 
			
		||||
        Activity activity = getActivityByTag(tag);
 | 
			
		||||
        if (activity != null) {
 | 
			
		||||
            return resumeActivity(activity);
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IWinBoLLActivity getIWinBoLLActivity(String tag) {
 | 
			
		||||
        return mActivityListMap.get(tag);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
 | 
			
		||||
     */
 | 
			
		||||
    public <T extends IWinBoLLActivity> void resumeActivity(Context context, String tag) {
 | 
			
		||||
        LogUtils.d(TAG, "resumeActivty");
 | 
			
		||||
        T iWinBoLLActivity = (T)getIWinBoLLActivity(tag);
 | 
			
		||||
        LogUtils.d(TAG, "activity " + iWinBoLLActivity.getTag());
 | 
			
		||||
        if (iWinBoLLActivity != null && iWinBoLLActivity.getActivity() != null && !iWinBoLLActivity.getActivity().isFinishing() && !iWinBoLLActivity.getActivity().isDestroyed()) {
 | 
			
		||||
            resumeActivity(context, iWinBoLLActivity);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
 | 
			
		||||
     */
 | 
			
		||||
    public <T extends IWinBoLLActivity> void resumeActivity(Context context, T activity) {
 | 
			
		||||
        ActivityManager am = (ActivityManager) activity.getActivity().getSystemService(Context.ACTIVITY_SERVICE);
 | 
			
		||||
    //
 | 
			
		||||
    // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
 | 
			
		||||
    //
 | 
			
		||||
    public <T extends IWinBoLLActivity> boolean resumeActivity(Activity activity) {
 | 
			
		||||
        ActivityManager am = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
 | 
			
		||||
        //返回启动它的根任务(home 或者 MainActivity)
 | 
			
		||||
        Intent intent = new Intent(context, activity.getClass());
 | 
			
		||||
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
 | 
			
		||||
        stackBuilder.addNextIntentWithParentStack(intent);
 | 
			
		||||
        stackBuilder.startActivities();
 | 
			
		||||
        //moveTaskToFront(YourTaskId, 0);
 | 
			
		||||
        LogUtils.d(TAG, "am.moveTaskToFront");
 | 
			
		||||
        //ToastUtils.show("resumeActivity am.moveTaskToFront");
 | 
			
		||||
        am.moveTaskToFront(activity.getActivity().getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION);
 | 
			
		||||
        //Intent intent = new Intent(mContext, activity.getClass());
 | 
			
		||||
        //TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
 | 
			
		||||
        //stackBuilder.addNextIntentWithParentStack(intent);
 | 
			
		||||
        //stackBuilder.startActivities();
 | 
			
		||||
        am.moveTaskToFront(activity.getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION);
 | 
			
		||||
        //ToastUtils.show("resumeActivity");
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -219,10 +224,10 @@ public class WinBoLLActivityManager {
 | 
			
		||||
                // ★:0 1 2 [3] 4 >> 2
 | 
			
		||||
                // ★:0 1 2 3 [4] >> 3
 | 
			
		||||
                // ★:[0] >> 直接关闭当前窗口
 | 
			
		||||
                IWinBoLLActivity preActivity = getPreActivity(iWinBoLLActivity);
 | 
			
		||||
                Activity preActivity = getPreActivity(iWinBoLLActivity);
 | 
			
		||||
                iWinBoLLActivity.getActivity().finish();
 | 
			
		||||
                if (preActivity != null) {
 | 
			
		||||
                    resumeActivity(mContext, preActivity);
 | 
			
		||||
                    resumeActivity(preActivity);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -231,7 +236,7 @@ public class WinBoLLActivityManager {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IWinBoLLActivity getPreActivity(IWinBoLLActivity iWinBoLLActivity) {
 | 
			
		||||
    Activity getPreActivity(IWinBoLLActivity iWinBoLLActivity) {
 | 
			
		||||
        try {
 | 
			
		||||
            boolean bingo = false;
 | 
			
		||||
            IWinBoLLActivity preIWinBoLLActivity = null;
 | 
			
		||||
@@ -245,7 +250,7 @@ public class WinBoLLActivityManager {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (bingo) {
 | 
			
		||||
                return preIWinBoLLActivity;
 | 
			
		||||
                return preIWinBoLLActivity.getActivity();
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								libappbase/src/main/res/layout/activity_logon.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								libappbase/src/main/res/layout/activity_logon.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent">
 | 
			
		||||
 | 
			
		||||
    <LinearLayout
 | 
			
		||||
        android:orientation="horizontal"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_gravity="right"
 | 
			
		||||
        android:gravity="right"
 | 
			
		||||
        android:padding="10dp"
 | 
			
		||||
        android:id="@+id/ll_hostbar">
 | 
			
		||||
 | 
			
		||||
        <RadioButton
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:text="10.8.0.250:456"
 | 
			
		||||
            android:id="@+id/rb_debughost"
 | 
			
		||||
            android:onClick="onSwitchHost"/>
 | 
			
		||||
 | 
			
		||||
        <RadioButton
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:text="yun.winboll.cc"
 | 
			
		||||
            android:id="@+id/rb_yunhost"
 | 
			
		||||
            android:onClick="onSwitchHost"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
    
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:gravity="right">
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Test RSA"
 | 
			
		||||
            android:onClick="onTestRSA"/>
 | 
			
		||||
 | 
			
		||||
        <Button
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:text="Test Login"
 | 
			
		||||
            android:onClick="onTestLogin"/>
 | 
			
		||||
        
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0">
 | 
			
		||||
 | 
			
		||||
		<cc.winboll.studio.libappbase.LogView
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:id="@+id/logview"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										63
									
								
								libappbase/src/main/res/layout/activity_yun.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								libappbase/src/main/res/layout/activity_yun.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent">
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:layout_gravity="right"
 | 
			
		||||
		android:gravity="right"
 | 
			
		||||
		android:padding="10dp"
 | 
			
		||||
		android:id="@+id/ll_hostbar">
 | 
			
		||||
 | 
			
		||||
		<RadioButton
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="10.8.0.250:456"
 | 
			
		||||
			android:id="@+id/rb_debughost"
 | 
			
		||||
			android:onClick="onSwitchHost"/>
 | 
			
		||||
 | 
			
		||||
		<RadioButton
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="yun.winboll.cc"
 | 
			
		||||
			android:id="@+id/rb_yunhost"
 | 
			
		||||
			android:onClick="onSwitchHost"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:layout_gravity="right"
 | 
			
		||||
		android:gravity="right">
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="TestYun"
 | 
			
		||||
			android:onClick="onTestYun"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0">
 | 
			
		||||
 | 
			
		||||
		<cc.winboll.studio.libappbase.LogView
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:id="@+id/logview"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_minimal"
 | 
			
		||||
        android:title="MINIMAL"
 | 
			
		||||
        android:icon="@drawable/ic_winboll_point"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_about"
 | 
			
		||||
        android:title="ABOUT"
 | 
			
		||||
        android:icon="@drawable/ic_winboll_logo"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_help"
 | 
			
		||||
        android:title="HELP"
 | 
			
		||||
        android:icon="@drawable/ic_winboll_help"/>
 | 
			
		||||
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_log"
 | 
			
		||||
        android:title="LOG"
 | 
			
		||||
        android:icon="@drawable/ic_winboll_log"/>
 | 
			
		||||
</menu>
 | 
			
		||||
@@ -21,7 +21,7 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.7.6'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.2'
 | 
			
		||||
    
 | 
			
		||||
    // 二维码类库
 | 
			
		||||
    api 'com.google.zxing:core:3.4.1'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue Apr 29 15:03:54 HKT 2025
 | 
			
		||||
#Tue Jun 03 15:05:42 HKT 2025
 | 
			
		||||
stageCount=5
 | 
			
		||||
libraryProject=libapputils
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.4
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.4
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.3.5
 | 
			
		||||
baseBetaVersion=15.8.5
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Fri Jan 10 22:03:57 GMT 2025
 | 
			
		||||
#Tue Jun 24 11:17:30 GMT 2025
 | 
			
		||||
stageCount=0
 | 
			
		||||
libraryProject=libjc
 | 
			
		||||
baseVersion=1.0
 | 
			
		||||
publishVersion=1.0.0
 | 
			
		||||
buildCount=133
 | 
			
		||||
buildCount=135
 | 
			
		||||
baseBetaVersion=1.0.1
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ public class Main {
 | 
			
		||||
    public final static int JAR_RUNNING_MODE_JCNDK_DEBUG = 4;
 | 
			
		||||
    public final static int JAR_RUNNING_MODE_JC = 5;
 | 
			
		||||
    public final static int JAR_RUNNING_MODE_JC_DEBUG = 6;
 | 
			
		||||
    public enum JAR_RUNNING_MODE {
 | 
			
		||||
    public static enum JAR_RUNNING_MODE {
 | 
			
		||||
        UNKNOWN(JAR_RUNNING_MODE_UNKNOWN),
 | 
			
		||||
        CONSOLE(JAR_RUNNING_MODE_CONSOLE),
 | 
			
		||||
        CONSOLE_DEBUG(JAR_RUNNING_MODE_CONSOLE_DEBUG),
 | 
			
		||||
 
 | 
			
		||||
@@ -18,18 +18,18 @@ def genVersionName(def versionName){
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
    compileSdkVersion 30
 | 
			
		||||
    buildToolsVersion "30.0.3"
 | 
			
		||||
    compileSdkVersion 32
 | 
			
		||||
    buildToolsVersion "32.0.0"
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId "cc.winboll.studio.mymessagemanager"
 | 
			
		||||
        minSdkVersion 26
 | 
			
		||||
        targetSdkVersion 29
 | 
			
		||||
        minSdkVersion 24
 | 
			
		||||
        targetSdkVersion 30
 | 
			
		||||
        versionCode 8
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // .winboll/winbollBuildProps.properties 文件的 stageCount=0
 | 
			
		||||
        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
			
		||||
        versionName "4.1" 
 | 
			
		||||
        versionName "15.3" 
 | 
			
		||||
        if(true) {
 | 
			
		||||
            versionName = genVersionName("${versionName}")
 | 
			
		||||
        }
 | 
			
		||||
@@ -44,26 +44,27 @@ android {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
	api 'cc.winboll.studio:winboll-shared:1.6.4'
 | 
			
		||||
    api 'io.github.medyo:android-about-page:2.0.0'
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.2'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
    
 | 
			
		||||
	api 'io.github.medyo:android-about-page:2.0.0'
 | 
			
		||||
    api 'com.github.getActivity:ToastUtils:10.5'
 | 
			
		||||
    api 'com.jcraft:jsch:0.1.55'
 | 
			
		||||
    api 'org.jsoup:jsoup:1.13.1'
 | 
			
		||||
    api 'com.squareup.okhttp3:okhttp:4.4.1'
 | 
			
		||||
    
 | 
			
		||||
    api 'androidx.appcompat:appcompat:1.0.0'
 | 
			
		||||
    api 'androidx.fragment:fragment:1.0.0'
 | 
			
		||||
    api 'com.google.android.material:material:1.0.0'
 | 
			
		||||
    
 | 
			
		||||
    // 权限请求框架:https://github.com/getActivity/XXPermissions
 | 
			
		||||
    api 'com.github.getActivity:XXPermissions:18.63'
 | 
			
		||||
    api 'com.baoyz.pullrefreshlayout:library:1.2.0'
 | 
			
		||||
    
 | 
			
		||||
    api 'androidx.appcompat:appcompat:1.0.0'
 | 
			
		||||
    api 'androidx.fragment:fragment:1.0.0'
 | 
			
		||||
    // AndroidX 类库
 | 
			
		||||
    api 'androidx.appcompat:appcompat:1.1.0'
 | 
			
		||||
    api 'com.google.android.material:material:1.4.0'
 | 
			
		||||
    //api 'androidx.viewpager:viewpager:1.0.0'
 | 
			
		||||
    //api 'androidx.vectordrawable:vectordrawable:1.1.0'
 | 
			
		||||
    //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
 | 
			
		||||
    //api 'androidx.fragment:fragment:1.1.0'
 | 
			
		||||
    api 'com.google.android.material:material:1.0.0'
 | 
			
		||||
    
 | 
			
		||||
    api 'cc.winboll.studio:libaes:7.6.0'
 | 
			
		||||
    
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue Feb 25 10:52:41 GMT 2025
 | 
			
		||||
stageCount=14
 | 
			
		||||
#Thu Jul 03 13:50:15 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=4.1
 | 
			
		||||
publishVersion=4.1.13
 | 
			
		||||
buildCount=5
 | 
			
		||||
baseBetaVersion=4.1.14
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.3.2
 | 
			
		||||
 
 | 
			
		||||
@@ -53,30 +53,28 @@
 | 
			
		||||
    </queries>
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        android:name=".GlobalApplication"
 | 
			
		||||
        android:name=".App"
 | 
			
		||||
        android:allowBackup="true"
 | 
			
		||||
        android:icon="@drawable/ic_launcher"
 | 
			
		||||
        android:roundIcon="@drawable/ic_launcher"
 | 
			
		||||
        android:label="@string/app_name"
 | 
			
		||||
        android:theme="@style/WinBoll.SupportThemeNoActionBar"
 | 
			
		||||
        android:theme="@style/MyAppTheme"
 | 
			
		||||
        android:persistent="true"
 | 
			
		||||
        android:resizeableActivity="true"
 | 
			
		||||
        android:supportsRtl="true"
 | 
			
		||||
        android:requestLegacyExternalStorage="true">
 | 
			
		||||
        android:requestLegacyExternalStorage="true"
 | 
			
		||||
        android:networkSecurityConfig="@xml/network_security_config">
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".activitys.SMSActivity"
 | 
			
		||||
            android:process=":sms"/>
 | 
			
		||||
            android:name=".activitys.SMSActivity"/>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".activitys.SMSReceiveRuleActivity"
 | 
			
		||||
            android:process=":smsars">
 | 
			
		||||
            android:name=".activitys.SMSReceiveRuleActivity">
 | 
			
		||||
 | 
			
		||||
        </activity>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".activitys.SharedJSONReceiveActivity"
 | 
			
		||||
            android:process=":smssjr"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
@@ -98,17 +96,14 @@
 | 
			
		||||
        </activity>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".activitys.TTSPlayRuleActivity"
 | 
			
		||||
            android:process=":ttsrule"/>
 | 
			
		||||
            android:name=".activitys.TTSPlayRuleActivity"/>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".activitys.AboutActivity"
 | 
			
		||||
            android:process=":about"/>
 | 
			
		||||
            android:name=".activitys.AboutActivity"/>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".activitys.MainActivity"
 | 
			
		||||
            android:exported="true"
 | 
			
		||||
            android:process=":main">
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
@@ -122,8 +117,7 @@
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".activitys.ComposeSMSActivity"
 | 
			
		||||
            android:exported="true"
 | 
			
		||||
            android:process=":csms">
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
@@ -226,6 +220,8 @@
 | 
			
		||||
 | 
			
		||||
        <activity android:name="cc.winboll.studio.mymessagemanager.activitys.SMSRecycleActivity"/>
 | 
			
		||||
 | 
			
		||||
        <activity android:name="cc.winboll.studio.mymessagemanager.unittest.UnitTestActivity"/>
 | 
			
		||||
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,17 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Author ZhanGSKen@QQ.COM
 | 
			
		||||
 * @Date 2023/07/24 01:46:59
 | 
			
		||||
 * @Describe 全局应用类
 | 
			
		||||
 */
 | 
			
		||||
import android.view.Gravity;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.shared.app.WinBollApplication;
 | 
			
		||||
import cc.winboll.studio.shared.log.LogUtils;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
 | 
			
		||||
public class GlobalApplication extends WinBollApplication {
 | 
			
		||||
public class App extends GlobalApplication {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "GlobalApplication";
 | 
			
		||||
 | 
			
		||||
@@ -30,7 +29,6 @@ public class GlobalApplication extends WinBollApplication {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate() {
 | 
			
		||||
        super.onCreate();
 | 
			
		||||
        //setIsDebug(cc.winboll.studio.mymessagemanager.BuildConfig.DEBUG);
 | 
			
		||||
 | 
			
		||||
        // 初始化 Toast 框架
 | 
			
		||||
        ToastUtils.init(this);
 | 
			
		||||
@@ -45,8 +43,4 @@ public class GlobalApplication extends WinBollApplication {
 | 
			
		||||
        _mszConfigUtilPath = _mszAppExternalFilesDir + File.separator + _mszConfigUtilFileName;
 | 
			
		||||
        _mszSMSReceiveRuleUtilPath = _mszAppExternalFilesDir + File.separator + _mszSMSReceiveRuleUtilFileName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void showApplicationMessage(String szMessage) {
 | 
			
		||||
        LogUtils.i(TAG, szMessage);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,72 +3,89 @@ package cc.winboll.studio.mymessagemanager.activitys;
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2024/07/14 13:20:33
 | 
			
		||||
 * @Describe 应用关于对话窗口
 | 
			
		||||
 * @Describe 应用介绍窗口
 | 
			
		||||
 */
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.Menu;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.shared.app.WinBollActivity;
 | 
			
		||||
import cc.winboll.studio.shared.app.WinBollActivityManager;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import cc.winboll.studio.libaes.utils.AESThemeUtil;
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
import cc.winboll.studio.libaes.winboll.APPInfo;
 | 
			
		||||
import cc.winboll.studio.libaes.winboll.AboutView;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.App;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
 | 
			
		||||
final public class AboutActivity extends WinBollActivity {
 | 
			
		||||
public class AboutActivity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "AboutActivity";
 | 
			
		||||
 | 
			
		||||
    Context mContext;
 | 
			
		||||
    
 | 
			
		||||
    Toolbar mToolbar;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected boolean isEnableDisplayHomeAsUp() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext()));
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        mContext = this;
 | 
			
		||||
        setContentView(R.layout.activity_about);
 | 
			
		||||
        mContext = getApplicationContext();
 | 
			
		||||
 | 
			
		||||
        mToolbar = findViewById(R.id.toolbar);
 | 
			
		||||
        setSupportActionBar(mToolbar);
 | 
			
		||||
        mToolbar.setSubtitle(TAG);
 | 
			
		||||
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 | 
			
		||||
 | 
			
		||||
        AboutView aboutView = CreateAboutView();
 | 
			
		||||
        // 在 Activity 的 onCreate 或其他生命周期方法中调用
 | 
			
		||||
//        LinearLayout layout = new LinearLayout(this);
 | 
			
		||||
//        layout.setOrientation(LinearLayout.VERTICAL);
 | 
			
		||||
//        // 创建布局参数(宽度和高度)
 | 
			
		||||
//        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
 | 
			
		||||
//            ViewGroup.LayoutParams.MATCH_PARENT,
 | 
			
		||||
//            ViewGroup.LayoutParams.MATCH_PARENT
 | 
			
		||||
//        );
 | 
			
		||||
//        addContentView(aboutView, params);
 | 
			
		||||
 | 
			
		||||
        LinearLayout layout = findViewById(R.id.aboutviewroot_ll);
 | 
			
		||||
        // 创建布局参数(宽度和高度)
 | 
			
		||||
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
 | 
			
		||||
            ViewGroup.LayoutParams.MATCH_PARENT,
 | 
			
		||||
            ViewGroup.LayoutParams.MATCH_PARENT
 | 
			
		||||
        );
 | 
			
		||||
        layout.addView(aboutView, params);
 | 
			
		||||
 | 
			
		||||
        App.getWinBoLLActivityManager().add(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onPostCreate(savedInstanceState);
 | 
			
		||||
        setTitle(mContext.getString(R.string.text_about) + mContext.getString(R.string.app_name));
 | 
			
		||||
    protected void onDestroy() {
 | 
			
		||||
        super.onDestroy();
 | 
			
		||||
        App.getWinBoLLActivityManager().registeRemove(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected boolean isAddWinBollToolBar() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Toolbar initToolBar() {
 | 
			
		||||
        return findViewById(R.id.activityaboutASupportToolbar1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onCreateOptionsMenu(Menu menu) {
 | 
			
		||||
        getMenuInflater().inflate(R.menu.toolbar_about, menu);
 | 
			
		||||
        return super.onCreateOptionsMenu(menu);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onOptionsItemSelected(MenuItem item) {
 | 
			
		||||
        /*if (item.getItemId() == R.id.item_help) {
 | 
			
		||||
            ToastUtils.show("R.id.item_help");
 | 
			
		||||
        } else */if (item.getItemId() == android.R.id.home) {
 | 
			
		||||
            WinBollActivityManager.getInstance(getApplicationContext()).finish(this);
 | 
			
		||||
        }
 | 
			
		||||
        return super.onOptionsItemSelected(item);
 | 
			
		||||
    public AboutView CreateAboutView() {
 | 
			
		||||
        String szBranchName = "mymessagemanager";
 | 
			
		||||
        APPInfo appInfo = new APPInfo();
 | 
			
		||||
        appInfo.setAppName(getString(R.string.app_name));
 | 
			
		||||
        appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll);
 | 
			
		||||
        appInfo.setAppDescription(getString(R.string.app_description));
 | 
			
		||||
        appInfo.setAppGitName("APPBase");
 | 
			
		||||
        appInfo.setAppGitOwner("Studio");
 | 
			
		||||
        appInfo.setAppGitAPPBranch(szBranchName);
 | 
			
		||||
        appInfo.setAppGitAPPSubProjectFolder(szBranchName);
 | 
			
		||||
        appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=5&extra=page%3D1");
 | 
			
		||||
        appInfo.setAppAPKName("MyMessageManager");
 | 
			
		||||
        appInfo.setAppAPKFolderName("MyMessageManager");
 | 
			
		||||
        return new AboutView(mContext, appInfo);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,8 @@ import cc.winboll.studio.libaes.views.AToolbar;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.AppConfigUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.PermissionUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.NotificationHelper;
 | 
			
		||||
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
 | 
			
		||||
 | 
			
		||||
public class AppSettingsActivity extends BaseActivity {
 | 
			
		||||
 | 
			
		||||
@@ -96,4 +98,18 @@ public class AppSettingsActivity extends BaseActivity {
 | 
			
		||||
            Toast.makeText(getApplication(), "应用已获得所需权限。", Toast.LENGTH_SHORT).show();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public void onCleanOldChannels(View view) {
 | 
			
		||||
        YesNoAlertDialog.show(this, "通知设置清理", "是否清理旧的通知设置?", new YesNoAlertDialog.OnDialogResultListener(){
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onNo() {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onYes() {
 | 
			
		||||
                    NotificationHelper notificationHelper = new NotificationHelper(AppSettingsActivity.this);
 | 
			
		||||
                    notificationHelper.cleanOldChannels();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.activitys;
 | 
			
		||||
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.Message;
 | 
			
		||||
@@ -8,9 +7,8 @@ import android.view.Menu;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity;
 | 
			
		||||
import cc.winboll.studio.libaes.utils.AESThemeUtil;
 | 
			
		||||
import cc.winboll.studio.shared.log.LogUtils;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import cc.winboll.studio.libaes.beans.AESThemeBean;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
 | 
			
		||||
abstract public class BaseActivity extends AppCompatActivity {
 | 
			
		||||
 | 
			
		||||
@@ -103,7 +101,10 @@ abstract public class BaseActivity extends AppCompatActivity {
 | 
			
		||||
            AESThemeUtil.saveThemeStyleID(this, R.style.MyTaoAESTheme);
 | 
			
		||||
            recreate();
 | 
			
		||||
        } else if (R.id.item_defaulttheme == item.getItemId()) {
 | 
			
		||||
            AESThemeUtil.saveThemeStyleID(this, R.style.MyDefaultAESTheme);
 | 
			
		||||
            AESThemeUtil.saveThemeStyleID(this, R.style.MyAppTheme);
 | 
			
		||||
            recreate();
 | 
			
		||||
        } else if (R.id.item_defaulttheme == item.getItemId()) {
 | 
			
		||||
            AESThemeUtil.saveThemeStyleID(this, R.style.MyAppTheme);
 | 
			
		||||
            recreate();
 | 
			
		||||
        }
 | 
			
		||||
        //ToastUtils.show("nThemeStyleID " + Integer.toString(nThemeStyleID));
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ import android.widget.ListView;
 | 
			
		||||
import android.widget.RelativeLayout;
 | 
			
		||||
import android.widget.SimpleAdapter;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
import android.widget.Toolbar;
 | 
			
		||||
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,28 +10,25 @@ import android.view.MenuItem;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.widget.Button;
 | 
			
		||||
import android.widget.ScrollView;
 | 
			
		||||
import cc.winboll.studio.libaes.views.AToolbar;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogView;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.App;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.BuildConfig;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.activitys.MainActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.adapters.PhoneArrayAdapter;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.services.MainService;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.unittest.UnitTestActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.AppConfigUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.AppGoToSettingsUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.NotificationUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.PermissionUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.ThemeUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.ViewUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.views.ConfirmSwitchView;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.views.PhoneListViewForScrollView;
 | 
			
		||||
import cc.winboll.studio.shared.log.LogUtils;
 | 
			
		||||
import cc.winboll.studio.shared.log.LogView;
 | 
			
		||||
import com.baoyz.widget.PullRefreshLayout;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import cc.winboll.studio.libaes.utils.AESThemeUtil;
 | 
			
		||||
import cc.winboll.studio.libaes.views.ASupportToolbar;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
 | 
			
		||||
public class MainActivity extends BaseActivity {
 | 
			
		||||
 | 
			
		||||
@@ -45,7 +42,7 @@ public class MainActivity extends BaseActivity {
 | 
			
		||||
    public static final int MY_PERMISSIONS_REQUEST = 0;
 | 
			
		||||
 | 
			
		||||
    static MainActivity _mMainActivity;
 | 
			
		||||
    LogView mLogView;
 | 
			
		||||
    //LogView mLogView;
 | 
			
		||||
    AppConfigUtil mAppConfigUtil;
 | 
			
		||||
    ConfirmSwitchView msvEnableService;
 | 
			
		||||
    ConfirmSwitchView msvOnlyReceiveContacts;
 | 
			
		||||
@@ -121,8 +118,8 @@ public class MainActivity extends BaseActivity {
 | 
			
		||||
    //
 | 
			
		||||
    void initView() {
 | 
			
		||||
        // 设置调试日志
 | 
			
		||||
        mLogView = findViewById(R.id.logview);
 | 
			
		||||
        mLogView.start();
 | 
			
		||||
//        mLogView = findViewById(R.id.logview);
 | 
			
		||||
//        mLogView.start();
 | 
			
		||||
 | 
			
		||||
        // 设置消息处理函数
 | 
			
		||||
        setOnActivityMessageReceived(mIOnActivityMessageReceived);
 | 
			
		||||
@@ -132,11 +129,6 @@ public class MainActivity extends BaseActivity {
 | 
			
		||||
        mToolbar.setSubtitle(getString(R.string.activity_name_main));
 | 
			
		||||
        setSupportActionBar(mToolbar);
 | 
			
		||||
 | 
			
		||||
        // 创建通知频道
 | 
			
		||||
        NotificationUtil nu = new NotificationUtil();
 | 
			
		||||
        nu.createServiceNotificationChannel(MainActivity.this);
 | 
			
		||||
        nu.createSMSNotificationChannel(MainActivity.this);
 | 
			
		||||
 | 
			
		||||
        boolean isEnableService = mAppConfigUtil.mAppConfigBean.isEnableService();
 | 
			
		||||
        msvEnableService = findViewById(R.id.activitymainSwitchView1);
 | 
			
		||||
        msvEnableService.setChecked(isEnableService);
 | 
			
		||||
@@ -270,24 +262,16 @@ public class MainActivity extends BaseActivity {
 | 
			
		||||
    protected void onResume() {
 | 
			
		||||
        super.onResume();
 | 
			
		||||
        reloadSMS();
 | 
			
		||||
        mLogView.start();
 | 
			
		||||
        //mLogView.start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onCreateOptionsMenu(Menu menu) {
 | 
			
		||||
        //return super.onCreateOptionsMenu(menu);
 | 
			
		||||
        getMenuInflater().inflate(R.menu.toolbar_main, menu);
 | 
			
		||||
 | 
			
		||||
        /*ThemeUtil.BaseTheme baseTheme = ThemeUtil.getTheme(mAppConfigUtil.mAppConfigBean.getAppThemeID());
 | 
			
		||||
        if (baseTheme == ThemeUtil.BaseTheme.DEFAULT) {
 | 
			
		||||
            menu.findItem(R.id.app_defaulttheme).setChecked(true);
 | 
			
		||||
        } else if (baseTheme == ThemeUtil.BaseTheme.SKY) {
 | 
			
		||||
            menu.findItem(R.id.app_skytheme).setChecked(true);
 | 
			
		||||
        } else if (baseTheme == ThemeUtil.BaseTheme.GOLDEN) {
 | 
			
		||||
            menu.findItem(R.id.app_goldentheme).setChecked(true);
 | 
			
		||||
        }*/
 | 
			
		||||
 | 
			
		||||
        return super.onCreateOptionsMenu(menu);
 | 
			
		||||
        super.onCreateOptionsMenu(menu);
 | 
			
		||||
		getMenuInflater().inflate(R.menu.toolbar_main2, menu);
 | 
			
		||||
		return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void reloadSMS() {
 | 
			
		||||
@@ -303,30 +287,36 @@ public class MainActivity extends BaseActivity {
 | 
			
		||||
		int nItemId = item.getItemId();
 | 
			
		||||
        if (nItemId == R.id.app_ttsrule) {
 | 
			
		||||
			Intent i = new Intent(MainActivity.this, TTSPlayRuleActivity.class);
 | 
			
		||||
			i.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
			i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
			startActivity(i);
 | 
			
		||||
		} else if (nItemId == R.id.app_smsrule) {
 | 
			
		||||
			Intent i = new Intent(MainActivity.this, SMSReceiveRuleActivity.class);
 | 
			
		||||
			i.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
			i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
			startActivity(i);
 | 
			
		||||
		} else if (nItemId == R.id.app_appsettings) {
 | 
			
		||||
            Intent i = new Intent(MainActivity.this, AppSettingsActivity.class);
 | 
			
		||||
            i.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            startActivity(i);
 | 
			
		||||
		} else if (nItemId ==  R.id.app_crashtest) {
 | 
			
		||||
		} else if (nItemId ==  R.id.app_log) {
 | 
			
		||||
            App.getWinBoLLActivityManager().startLogActivity(this);
 | 
			
		||||
        } else if (nItemId ==  R.id.app_unittest) {
 | 
			
		||||
            Intent i = new Intent(MainActivity.this, UnitTestActivity.class);
 | 
			
		||||
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
			startActivity(i);
 | 
			
		||||
        } else if (nItemId ==  R.id.app_crashtest) {
 | 
			
		||||
            for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
 | 
			
		||||
                getString(i);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (nItemId ==  R.id.app_about) {
 | 
			
		||||
            Intent i = new Intent(MainActivity.this, AboutActivity.class);
 | 
			
		||||
            i.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            startActivity(i);
 | 
			
		||||
        } else if (nItemId ==  R.id.app_smsrecycle) {
 | 
			
		||||
            Intent i = new Intent(MainActivity.this, SMSRecycleActivity.class);
 | 
			
		||||
            i.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            startActivity(i);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return super.onOptionsItemSelected(item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,10 +18,10 @@ import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.activitys.SMSActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.adapters.SMSArrayAdapter;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.AddressUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.ViewUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.views.SMSListViewForScrollView;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.views.SMSView;
 | 
			
		||||
import java.lang.ref.WeakReference;
 | 
			
		||||
 | 
			
		||||
public class SMSActivity extends BaseActivity {
 | 
			
		||||
@@ -102,7 +102,7 @@ public class SMSActivity extends BaseActivity {
 | 
			
		||||
 | 
			
		||||
        // 初始化标题栏
 | 
			
		||||
        mToolbar = findViewById(R.id.activitysmsASupportToolbar1);
 | 
			
		||||
        mToolbar.setSubtitle(getString(R.string.activity_name_smsinphone) + " < Phone : " + mszPhoneTo + " >");
 | 
			
		||||
        mToolbar.setSubtitle(getString(R.string.activity_name_smsinphone) + " < Phone : " + AddressUtils.getFormattedAddress(mszPhoneTo) + " >");
 | 
			
		||||
        setActionBar(mToolbar);
 | 
			
		||||
 | 
			
		||||
        // 初始化滚动窗口
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user