Compare commits
	
		
			200 Commits
		
	
	
		
			apputils-v
			...
			4d344b299b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					4d344b299b | ||
| 
						 | 
					37b0867d34 | ||
| 
						 | 
					b505156211 | ||
| 
						 | 
					91b30fb576 | ||
| 
						 | 
					ab3ac72d54 | ||
| 
						 | 
					870e9a94fb | ||
| 
						 | 
					2421ecb943 | ||
| 
						 | 
					687fff7216 | ||
| 
						 | 
					3795cf8631 | ||
| 
						 | 
					b374f3117a | ||
| 
						 | 
					6d9adc124e | ||
| 
						 | 
					52f738b45b | ||
| 
						 | 
					9ece6778b7 | ||
| 
						 | 
					e13c8e7af0 | ||
| 
						 | 
					a4988b5b68 | ||
| 
						 | 
					5507126f6b | ||
| 
						 | 
					d381c29452 | ||
| 
						 | 
					945eadb617 | ||
| 
						 | 
					c5bffc5eef | ||
| 
						 | 
					88597fe407 | ||
| 
						 | 
					53f985533a | ||
| 
						 | 
					a3950f13ad | ||
| 
						 | 
					c878e9dc02 | ||
| 
						 | 
					f2f7cab330 | ||
| 
						 | 
					c4e88e9593 | ||
| 
						 | 
					08d9d92ae4 | ||
| 
						 | 
					74841c08dc | ||
| 
						 | 
					945bacb825 | ||
| 
						 | 
					0e464495fd | ||
| 
						 | 
					e8682ce410 | ||
| 
						 | 
					2e4003dae0 | ||
| 
						 | 
					198b0975ce | ||
| 
						 | 
					24a578a9d2 | ||
| 
						 | 
					46de24447f | ||
| 
						 | 
					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 | ||
| 
						 | 
					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 | ||
| 
						 | 
					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
 | 
			
		||||
#Thu Jun 19 20:42:40 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=libaes
 | 
			
		||||
baseVersion=15.6
 | 
			
		||||
publishVersion=15.6.3
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.6.4
 | 
			
		||||
baseBetaVersion=15.9.2
 | 
			
		||||
 
 | 
			
		||||
@@ -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=2&fromuid=1");
 | 
			
		||||
        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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue May 13 11:05:12 HKT 2025
 | 
			
		||||
stageCount=1
 | 
			
		||||
#Mon Jun 09 09:38:19 HKT 2025
 | 
			
		||||
stageCount=9
 | 
			
		||||
libraryProject=libappbase
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.0
 | 
			
		||||
publishVersion=15.8.8
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.8.1
 | 
			
		||||
baseBetaVersion=15.8.9
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue May 13 11:17:53 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
#Tue Jun 03 15:05:48 HKT 2025
 | 
			
		||||
stageCount=5
 | 
			
		||||
libraryProject=libapputils
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.1
 | 
			
		||||
publishVersion=15.8.4
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.8.2
 | 
			
		||||
baseBetaVersion=15.8.5
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -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.8.0'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.1'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.1'
 | 
			
		||||
    
 | 
			
		||||
    // 权限请求框架: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
 | 
			
		||||
#Tue May 20 13:02:18 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.2
 | 
			
		||||
publishVersion=15.2.7
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.2
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.2.8
 | 
			
		||||
baseBetaVersion=15.3.3
 | 
			
		||||
 
 | 
			
		||||
@@ -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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -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() {
 | 
			
		||||
@@ -201,6 +202,11 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
 | 
			
		||||
        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 +239,18 @@ 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());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 +264,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 +326,8 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public void onAbout(View view) {
 | 
			
		||||
        App.getWinBollActivityManager().startWinBollActivity(this, AboutActivity.class);
 | 
			
		||||
        App.getWinBoLLActivityManager().startWinBoLLActivity(this, AboutActivity.class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -41,8 +44,12 @@ public class TomCat {
 | 
			
		||||
        }
 | 
			
		||||
        return _TomCat;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public String getDefaultBobulltoonUrl() {
 | 
			
		||||
        return mContext.getString(R.string.default_bobulltoon_url);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void downloadAndExtractZip(String zipUrl, String destinationFolder) throws IOException {
 | 
			
		||||
    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());
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -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());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 初始化服务运行参数
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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">http://10.8.0.12:3000/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.2'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.2'
 | 
			
		||||
    
 | 
			
		||||
    // 吐司类库
 | 
			
		||||
    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
 | 
			
		||||
#Thu Jun 19 20:42:26 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=libaes
 | 
			
		||||
baseVersion=15.6
 | 
			
		||||
publishVersion=15.6.3
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.6.4
 | 
			
		||||
baseBetaVersion=15.9.2
 | 
			
		||||
 
 | 
			
		||||
@@ -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()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 11:05:02 HKT 2025
 | 
			
		||||
stageCount=1
 | 
			
		||||
#Mon Jun 09 09:38:19 HKT 2025
 | 
			
		||||
stageCount=9
 | 
			
		||||
libraryProject=libappbase
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.0
 | 
			
		||||
publishVersion=15.8.8
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.8.1
 | 
			
		||||
baseBetaVersion=15.8.9
 | 
			
		||||
 
 | 
			
		||||
@@ -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.8.0'
 | 
			
		||||
    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 May 13 11:17:53 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
#Tue Jun 03 15:05:42 HKT 2025
 | 
			
		||||
stageCount=5
 | 
			
		||||
libraryProject=libapputils
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.1
 | 
			
		||||
publishVersion=15.8.4
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.8.2
 | 
			
		||||
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,29 @@ 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.3'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.6'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.9.5'
 | 
			
		||||
    
 | 
			
		||||
	api 'io.github.medyo:android-about-page:2.0.0'
 | 
			
		||||
    api 'com.github.getActivity:ToastUtils:10.5'
 | 
			
		||||
    api 'com.jcraft:jsch:0.1.55'
 | 
			
		||||
    api 'org.jsoup:jsoup:1.13.1'
 | 
			
		||||
    api 'com.squareup.okhttp3:okhttp:4.4.1'
 | 
			
		||||
    
 | 
			
		||||
    api 'androidx.appcompat:appcompat:1.0.0'
 | 
			
		||||
    api 'androidx.fragment:fragment:1.0.0'
 | 
			
		||||
    api 'com.google.android.material:material:1.0.0'
 | 
			
		||||
	
 | 
			
		||||
	api 'com.belerweb:pinyin4j:2.5.1'
 | 
			
		||||
    
 | 
			
		||||
    // 权限请求框架: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
 | 
			
		||||
#Fri Sep 05 17:49:46 GMT 2025
 | 
			
		||||
stageCount=8
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=4.1
 | 
			
		||||
publishVersion=4.1.13
 | 
			
		||||
buildCount=5
 | 
			
		||||
baseBetaVersion=4.1.14
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.7
 | 
			
		||||
buildCount=16
 | 
			
		||||
baseBetaVersion=15.3.8
 | 
			
		||||
 
 | 
			
		||||
@@ -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));
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,10 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.activitys;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/08/30 14:32
 | 
			
		||||
 * @Describe 联系人查询与短信发送窗口
 | 
			
		||||
 */
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.text.Editable;
 | 
			
		||||
import android.text.TextWatcher;
 | 
			
		||||
@@ -10,15 +15,18 @@ 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 android.content.Intent;
 | 
			
		||||
import android.graphics.drawable.Drawable;
 | 
			
		||||
 | 
			
		||||
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.activitys.ComposeSMSActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.PhoneBean;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.PhoneUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSUtil;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -27,166 +35,331 @@ import java.util.Map;
 | 
			
		||||
public class ComposeSMSActivity extends BaseActivity {
 | 
			
		||||
 | 
			
		||||
    public static String TAG = "ComposeSMSActivity";
 | 
			
		||||
 | 
			
		||||
    public static String EXTRA_SMSBODY = "sms_body";
 | 
			
		||||
    private static final String MAP_NAME = "NAME";
 | 
			
		||||
    private static final String MAP_PHONE = "PHONE";
 | 
			
		||||
 | 
			
		||||
    static String MAP_NAME = "NAME";
 | 
			
		||||
    static String MAP_PHONE = "PHONE";
 | 
			
		||||
 | 
			
		||||
    String mszSMSBody;
 | 
			
		||||
    String mszScheme;
 | 
			
		||||
    String mszPhoneTo;
 | 
			
		||||
    EditText metTO;
 | 
			
		||||
    EditText metSMSBody;
 | 
			
		||||
    SimpleAdapter mSimpleAdapter;
 | 
			
		||||
    List<Map<String,Object>> mAdapterData = new ArrayList<>();
 | 
			
		||||
    ListView mlvContracts;
 | 
			
		||||
    List<PhoneBean> mListPhoneBeanContracts;
 | 
			
		||||
    Toolbar mToolbar;
 | 
			
		||||
    AOHPCTCSeekBar mAOHPCTCSeekBar;
 | 
			
		||||
    RelativeLayout mrlContracts;
 | 
			
		||||
    private String mszSMSBody;
 | 
			
		||||
    private String mszScheme;
 | 
			
		||||
    private String mszPhoneTo;
 | 
			
		||||
    private TextView mtvTOName;
 | 
			
		||||
    private EditText metTONameSearch;
 | 
			
		||||
    private EditText metTO;
 | 
			
		||||
    private EditText metSMSBody;
 | 
			
		||||
    private SimpleAdapter mSimpleAdapter;
 | 
			
		||||
    private List<Map<String, Object>> mAdapterData = new ArrayList<Map<String, Object>>();
 | 
			
		||||
    private ListView mlvContracts;
 | 
			
		||||
    private List<PhoneBean> mListPhoneBeanContracts;
 | 
			
		||||
    private Toolbar mToolbar;
 | 
			
		||||
    private AOHPCTCSeekBar mAOHPCTCSeekBar;
 | 
			
		||||
    private RelativeLayout mrlContracts;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        LogUtils.d(TAG, "onCreate");
 | 
			
		||||
        setContentView(R.layout.activity_composesms);
 | 
			
		||||
        mszSMSBody = getIntent().getStringExtra(EXTRA_SMSBODY);
 | 
			
		||||
        mszScheme = getIntent().getData().getScheme();
 | 
			
		||||
        mszPhoneTo = getIntent().getData().getSchemeSpecificPart();
 | 
			
		||||
        if (!mszScheme.equals("smsto")) {
 | 
			
		||||
            // 其他方式未支持就退出
 | 
			
		||||
            finish();
 | 
			
		||||
 | 
			
		||||
        // 初始化Intent数据(增加空判断,避免NullPointerException)
 | 
			
		||||
        Intent intent = getIntent();
 | 
			
		||||
        if (intent != null) {
 | 
			
		||||
            mszSMSBody = intent.getStringExtra(EXTRA_SMSBODY);
 | 
			
		||||
            if (intent.getData() != null) {
 | 
			
		||||
                mszScheme = intent.getData().getScheme();
 | 
			
		||||
                mszPhoneTo = intent.getData().getSchemeSpecificPart();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 初始化视图
 | 
			
		||||
 | 
			
		||||
        // 校验启动方式,非smsto则退出
 | 
			
		||||
        if (mszScheme == null || !"smsto".equals(mszScheme)) {
 | 
			
		||||
            ToastUtils.show("不支持的启动方式");
 | 
			
		||||
            finish();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        initView();
 | 
			
		||||
        // 设置适配器
 | 
			
		||||
        initAdapter();
 | 
			
		||||
        // 设置搜索到的匹配位置
 | 
			
		||||
        setListViewPrePosition();
 | 
			
		||||
        initAdapter(null); // 初始加载所有联系人
 | 
			
		||||
        setListViewPrePositionByPhone();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 初始化视图
 | 
			
		||||
    //
 | 
			
		||||
    void initView() {
 | 
			
		||||
        //Drawable drawableFrame = AppCompatResources.getDrawable(this, R.drawable.bg_frame);
 | 
			
		||||
 | 
			
		||||
    private void initView() {
 | 
			
		||||
        // 初始化标题栏
 | 
			
		||||
        mToolbar = findViewById(R.id.activitycomposesmsASupportToolbar1);
 | 
			
		||||
        mToolbar = (Toolbar) findViewById(R.id.activitycomposesmsASupportToolbar1);
 | 
			
		||||
        mToolbar.setSubtitle(getString(R.string.activity_name_composesms));
 | 
			
		||||
        setActionBar(mToolbar);
 | 
			
		||||
 | 
			
		||||
        // 初始化联系人栏目框
 | 
			
		||||
        mrlContracts = findViewById(R.id.activitycomposesmsRelativeLayout1);
 | 
			
		||||
        //mrlContracts.setBackground(drawableFrame);
 | 
			
		||||
        // 初始化联系人姓名显示和搜索栏
 | 
			
		||||
        mtvTOName = (TextView) findViewById(R.id.activitycomposesmsTextView2);
 | 
			
		||||
        mrlContracts = (RelativeLayout) findViewById(R.id.activitycomposesmsRelativeLayout1);
 | 
			
		||||
        metTONameSearch = (EditText) findViewById(R.id.activitycomposesmsEditText2);
 | 
			
		||||
 | 
			
		||||
        // 初始化联系人列表
 | 
			
		||||
        mlvContracts = findViewById(R.id.activitycomposesmsListView1);
 | 
			
		||||
        // 姓名搜索框文本变化监听
 | 
			
		||||
        metTONameSearch.addTextChangedListener(new TextWatcher() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onTextChanged(CharSequence s, int start, int before, int count) {
 | 
			
		||||
					metTO.setText(""); // 清空号码输入框,避免冲突
 | 
			
		||||
					String input = s == null ? "" : s.toString().trim();
 | 
			
		||||
					if (input.isEmpty()) {
 | 
			
		||||
						initAdapter(null); // 空搜索时显示所有联系人
 | 
			
		||||
					} else {
 | 
			
		||||
						setListViewPrePositionByName(); // 按姓名搜索
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
        // 初始化联系人输入框
 | 
			
		||||
        metTO = findViewById(R.id.activitycomposesmsEditText1);
 | 
			
		||||
        metTO.setText(mszPhoneTo);
 | 
			
		||||
        metTO.addTextChangedListener(new TextWatcher() {           
 | 
			
		||||
                @Override  
 | 
			
		||||
                public void onTextChanged(CharSequence s, int start, int before, int count) {
 | 
			
		||||
                    setListViewPrePosition();
 | 
			
		||||
                }
 | 
			
		||||
                @Override  
 | 
			
		||||
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
 | 
			
		||||
                }
 | 
			
		||||
				@Override
 | 
			
		||||
				public void beforeTextChanged(CharSequence s, int start, int count, int after) {
 | 
			
		||||
					// 无操作
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
                @Override  
 | 
			
		||||
                public void afterTextChanged(Editable s) {
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // 初始化发送拉动控件
 | 
			
		||||
        mAOHPCTCSeekBar = findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1);
 | 
			
		||||
        mAOHPCTCSeekBar.setThumb(getDrawable(R.drawable.ic_message));
 | 
			
		||||
        mAOHPCTCSeekBar.setThumbOffset(20);
 | 
			
		||||
        mAOHPCTCSeekBar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onOHPCommit() {
 | 
			
		||||
                    // 空号码不发送
 | 
			
		||||
                    mszPhoneTo = metTO.getText().toString();
 | 
			
		||||
                    if (mszPhoneTo.trim().equals("")) {
 | 
			
		||||
                        ToastUtils.show("没有设置接收号码。");
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    // 空消息不发送
 | 
			
		||||
                    mszSMSBody = metSMSBody.getText().toString();
 | 
			
		||||
                    if (mszSMSBody.equals("")) {
 | 
			
		||||
                        ToastUtils.show("没有消息内容可发送。");
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    // 发送消息
 | 
			
		||||
                    if (SMSUtil.sendMessageByInterface2(ComposeSMSActivity.this, mszPhoneTo, mszSMSBody)) {
 | 
			
		||||
                        ComposeSMSActivity.this.finish();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
				@Override
 | 
			
		||||
				public void afterTextChanged(Editable s) {
 | 
			
		||||
					// 无操作
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化提示框
 | 
			
		||||
        TextView tvAOHPCTCSeekBarMSG = findViewById(R.id.viewsmssendpart1TextView1);
 | 
			
		||||
        // 初始化联系人列表(关键:设置单选模式,确保选中状态生效)
 | 
			
		||||
        mlvContracts = (ListView) findViewById(R.id.activitycomposesmsListView1);
 | 
			
		||||
        mlvContracts.setChoiceMode(ListView.CHOICE_MODE_SINGLE); // 开启单选,与布局中一致
 | 
			
		||||
 | 
			
		||||
        // 初始化号码输入框(核心:优化文本变化监听逻辑)
 | 
			
		||||
        metTO = (EditText) findViewById(R.id.activitycomposesmsEditText1);
 | 
			
		||||
        if (mszPhoneTo != null) {
 | 
			
		||||
            metTO.setText(mszPhoneTo);
 | 
			
		||||
        }
 | 
			
		||||
        metTO.addTextChangedListener(new TextWatcher() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onTextChanged(CharSequence s, int start, int before, int count) {
 | 
			
		||||
					mtvTOName.setText(""); // 清空姓名显示
 | 
			
		||||
					String inputPhone = s == null ? "" : s.toString().trim();
 | 
			
		||||
 | 
			
		||||
					if (inputPhone.isEmpty()) {
 | 
			
		||||
						// 输入为空时,显示所有联系人
 | 
			
		||||
						initAdapter(null);
 | 
			
		||||
					} else {
 | 
			
		||||
						// 输入非空时,按号码搜索并更新列表(无结果则清空)
 | 
			
		||||
						filterListByPhone(inputPhone);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				@Override
 | 
			
		||||
				public void beforeTextChanged(CharSequence s, int start, int count, int after) {
 | 
			
		||||
					// 无操作
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				@Override
 | 
			
		||||
				public void afterTextChanged(Editable s) {
 | 
			
		||||
					// 无操作
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化发送控件
 | 
			
		||||
        mAOHPCTCSeekBar = (AOHPCTCSeekBar) findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1);
 | 
			
		||||
        Drawable thumbDrawable = getResources().getDrawable(R.drawable.ic_message); // Java 7兼容写法
 | 
			
		||||
        mAOHPCTCSeekBar.setThumb(thumbDrawable);
 | 
			
		||||
        mAOHPCTCSeekBar.setThumbOffset(20);
 | 
			
		||||
        mAOHPCTCSeekBar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onOHPCommit() {
 | 
			
		||||
					sendSMS();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化短信内容输入框
 | 
			
		||||
        TextView tvAOHPCTCSeekBarMSG = (TextView) findViewById(R.id.viewsmssendpart1TextView1);
 | 
			
		||||
        tvAOHPCTCSeekBarMSG.setText(R.string.msg_100sendmsg);
 | 
			
		||||
 | 
			
		||||
        // 初始化发送消息框
 | 
			
		||||
        metSMSBody = findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        //metSMSBody.setBackground(drawableFrame);
 | 
			
		||||
        metSMSBody.setText(mszSMSBody);
 | 
			
		||||
        metSMSBody = (EditText) findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        if (mszSMSBody != null) {
 | 
			
		||||
            metSMSBody.setText(mszSMSBody);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 设置搜索到的匹配位置
 | 
			
		||||
    //
 | 
			
		||||
    void setListViewPrePosition() {
 | 
			
		||||
        int nPrePosition = getContractsDataPrePosition(metTO.getText().toString());
 | 
			
		||||
        mlvContracts.setSelected(false);
 | 
			
		||||
        mlvContracts.setSelection(nPrePosition);
 | 
			
		||||
    // 核心优化:根据输入号码筛选列表(无结果则显示空列表,优化选中逻辑)
 | 
			
		||||
    private void filterListByPhone(String inputPhone) {
 | 
			
		||||
        PhoneUtil phoneUtil = new PhoneUtil(this);
 | 
			
		||||
        List<PhoneBean> allContacts = phoneUtil.getPhoneList();
 | 
			
		||||
        List<PhoneBean> matchedContacts = new ArrayList<PhoneBean>();
 | 
			
		||||
 | 
			
		||||
        // 遍历所有联系人,匹配包含输入号码的联系人
 | 
			
		||||
        for (PhoneBean contact : allContacts) {
 | 
			
		||||
            if (contact.getTelPhone().contains(inputPhone)
 | 
			
		||||
                || phoneUtil.isTheSamePhoneNumber(contact.getTelPhone(), inputPhone)) {
 | 
			
		||||
                matchedContacts.add(contact);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LogUtils.d(TAG, "号码搜索:输入'" + inputPhone + "', 匹配" + matchedContacts.size() + "个结果");
 | 
			
		||||
 | 
			
		||||
        // 用筛选结果更新列表(无结果则传入空列表)
 | 
			
		||||
        initAdapter(matchedContacts.isEmpty() ? new ArrayList<PhoneBean>() : matchedContacts);
 | 
			
		||||
 | 
			
		||||
        // 定位并选中匹配项(如果有)
 | 
			
		||||
        if (!matchedContacts.isEmpty()) {
 | 
			
		||||
            boolean isFound = false;
 | 
			
		||||
            for (int i = 0; i < matchedContacts.size(); i++) {
 | 
			
		||||
                PhoneBean item = matchedContacts.get(i);
 | 
			
		||||
                // 精确匹配号码(兼容区域码格式)
 | 
			
		||||
                if (phoneUtil.isTheSamePhoneNumber(item.getTelPhone(), inputPhone)) {
 | 
			
		||||
                    mtvTOName.setText(item.getName());
 | 
			
		||||
                    // 关键:先滚动到目标位置,再设置选中状态
 | 
			
		||||
                    mlvContracts.setSelection(i);
 | 
			
		||||
                    // 主动设置选中(确保样式生效,兼容部分系统)
 | 
			
		||||
                    mlvContracts.setItemChecked(i, true);
 | 
			
		||||
                    LogUtils.d(TAG, String.format("%s 匹配 %s,选中位置:%d", inputPhone, item.getTelPhone(), i));
 | 
			
		||||
                    isFound = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // 若未精确匹配,选中第一个结果
 | 
			
		||||
            /*if (!isFound) {
 | 
			
		||||
                mlvContracts.setSelection(0);
 | 
			
		||||
                mlvContracts.setItemChecked(0, true);
 | 
			
		||||
                mtvTOName.setText(matchedContacts.get(0).getName());
 | 
			
		||||
            }*/
 | 
			
		||||
        } else {
 | 
			
		||||
            mtvTOName.setText(""); // 无结果时清空姓名显示
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 返回搜索到的匹配位置
 | 
			
		||||
    //
 | 
			
		||||
    int getContractsDataPrePosition(String szPhone) {
 | 
			
		||||
    // 根据姓名搜索联系人
 | 
			
		||||
    private void setListViewPrePositionByName() {
 | 
			
		||||
        String searchName = metTONameSearch.getText().toString().trim();
 | 
			
		||||
        PhoneUtil phoneUtil = new PhoneUtil(this);
 | 
			
		||||
        List<PhoneBean> matchedContacts = phoneUtil.getPhonesByName(searchName);
 | 
			
		||||
        initAdapter(matchedContacts);
 | 
			
		||||
        if (!matchedContacts.isEmpty()) {
 | 
			
		||||
            // 选中第一个结果并设置样式
 | 
			
		||||
            mlvContracts.setSelection(0);
 | 
			
		||||
            mlvContracts.setItemChecked(0, true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 初始定位号码对应的联系人
 | 
			
		||||
    private void setListViewPrePositionByPhone() {
 | 
			
		||||
        String inputPhone = metTO.getText().toString().trim();
 | 
			
		||||
        if (inputPhone.isEmpty()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        filterListByPhone(inputPhone); // 复用筛选逻辑
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 获取号码匹配的位置(兼容旧逻辑)
 | 
			
		||||
    private int getContractsDataPrePositionByPhone(String szPhone) {
 | 
			
		||||
        if (mListPhoneBeanContracts == null || mListPhoneBeanContracts.isEmpty()) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 0; i < mListPhoneBeanContracts.size(); i++) {
 | 
			
		||||
            if (mListPhoneBeanContracts.get(i).getTelPhone().compareTo(szPhone) > -1) {
 | 
			
		||||
            PhoneBean bean = mListPhoneBeanContracts.get(i);
 | 
			
		||||
            if (bean.getTelPhone().compareTo(szPhone) >= 0) {
 | 
			
		||||
                return i;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 初始化适配器
 | 
			
		||||
    //
 | 
			
		||||
    void initAdapter() {
 | 
			
		||||
        // 初始化联系人数据适配器
 | 
			
		||||
        mAdapterData = new ArrayList<>();
 | 
			
		||||
        // 读取联系人数据
 | 
			
		||||
        PhoneUtil phoneUtils = new PhoneUtil(this);
 | 
			
		||||
        mListPhoneBeanContracts = phoneUtils.getPhoneList();
 | 
			
		||||
        // 映射联系人数据给适配器数据对象
 | 
			
		||||
        for (int i = 0;i < mListPhoneBeanContracts.size();i++) {
 | 
			
		||||
            Map<String,Object> map =new HashMap<>();
 | 
			
		||||
            map.put(MAP_NAME, mListPhoneBeanContracts.get(i).getName());
 | 
			
		||||
            map.put(MAP_PHONE, mListPhoneBeanContracts.get(i).getTelPhone());
 | 
			
		||||
            mAdapterData.add(map);
 | 
			
		||||
    // 获取姓名匹配的位置(兼容旧逻辑)
 | 
			
		||||
    private int getContractsDataPrePositionByName(String szName) {
 | 
			
		||||
        if (mListPhoneBeanContracts == null || mListPhoneBeanContracts.isEmpty()) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        // 绑定适配器与数据
 | 
			
		||||
        mSimpleAdapter = new SimpleAdapter(ComposeSMSActivity.this, mAdapterData, R.layout.listview_contracts
 | 
			
		||||
                                           , new String[]{MAP_NAME, MAP_PHONE}
 | 
			
		||||
                                           , new int[]{R.id.listviewcontractsTextView1, R.id.listviewcontractsTextView2});
 | 
			
		||||
        mSimpleAdapter.setDropDownViewResource(R.layout.listview_contracts);
 | 
			
		||||
        mlvContracts.setAdapter(mSimpleAdapter);
 | 
			
		||||
        mlvContracts.setOnItemClickListener(new ListView.OnItemClickListener() {
 | 
			
		||||
        for (int i = 0; i < mListPhoneBeanContracts.size(); i++) {
 | 
			
		||||
            if (mListPhoneBeanContracts.get(i).getName().startsWith(szName)) {
 | 
			
		||||
                return i;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 | 
			
		||||
                    metTO.setText(mAdapterData.get(position).get(MAP_PHONE).toString());
 | 
			
		||||
	// 初始化或更新列表适配器
 | 
			
		||||
    private void initAdapter(List<PhoneBean> initData) {
 | 
			
		||||
        mAdapterData.clear(); // 清空旧数据
 | 
			
		||||
        final PhoneUtil phoneUtil = new PhoneUtil(this);
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        // 确定数据源:传入的筛选数据或所有联系人
 | 
			
		||||
        if (initData != null) {
 | 
			
		||||
            mListPhoneBeanContracts = initData;
 | 
			
		||||
        } else {
 | 
			
		||||
            mListPhoneBeanContracts = phoneUtil.getPhoneList();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 转换数据为SimpleAdapter所需格式
 | 
			
		||||
        if (mListPhoneBeanContracts != null) {
 | 
			
		||||
            for (PhoneBean bean : mListPhoneBeanContracts) {
 | 
			
		||||
                Map<String, Object> map = new HashMap<String, Object>();
 | 
			
		||||
                map.put(MAP_NAME, bean.getName());
 | 
			
		||||
                map.put(MAP_PHONE, bean.getTelPhone());
 | 
			
		||||
                mAdapterData.add(map);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 初始化或更新适配器
 | 
			
		||||
        if (mSimpleAdapter == null) {
 | 
			
		||||
            mSimpleAdapter = new SimpleAdapter(
 | 
			
		||||
                ComposeSMSActivity.this,
 | 
			
		||||
                mAdapterData,
 | 
			
		||||
                R.layout.listview_contracts,
 | 
			
		||||
                new String[]{MAP_NAME, MAP_PHONE},
 | 
			
		||||
                new int[]{R.id.listviewcontractsTextView1, R.id.listviewcontractsTextView2}
 | 
			
		||||
            );
 | 
			
		||||
            mSimpleAdapter.setDropDownViewResource(R.layout.listview_contracts);
 | 
			
		||||
            mlvContracts.setAdapter(mSimpleAdapter);
 | 
			
		||||
 | 
			
		||||
            // 列表项点击事件:点击时主动设置选中状态,确保样式突显
 | 
			
		||||
            mlvContracts.setOnItemClickListener(new AdapterView.OnItemClickListener() {
 | 
			
		||||
					@Override
 | 
			
		||||
					public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 | 
			
		||||
						if (position < mAdapterData.size()) {
 | 
			
		||||
							// 1. 主动设置当前项为选中状态
 | 
			
		||||
							mlvContracts.setItemChecked(position, true);
 | 
			
		||||
							// 2. 更新号码输入框和姓名显示
 | 
			
		||||
							String phone = mAdapterData.get(position).get(MAP_PHONE).toString();
 | 
			
		||||
							metTO.setText(phone);
 | 
			
		||||
							mtvTOName.setText(phoneUtil.getNameByPhone(phone));
 | 
			
		||||
							// 3. 滚动到点击位置(确保可见)
 | 
			
		||||
							mlvContracts.setSelection(position);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
            // 列表项选中状态变化监听(可选,增强选中反馈)
 | 
			
		||||
            mlvContracts.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
 | 
			
		||||
					@Override
 | 
			
		||||
					public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
 | 
			
		||||
						// 选中时可添加额外反馈(如改变文本颜色,可选)
 | 
			
		||||
						if (view != null) {
 | 
			
		||||
							TextView tvName = (TextView) view.findViewById(R.id.listviewcontractsTextView1);
 | 
			
		||||
							TextView tvPhone = (TextView) view.findViewById(R.id.listviewcontractsTextView2);
 | 
			
		||||
							if (tvName != null) tvName.setTextColor(getResources().getColor(R.color.white));
 | 
			
		||||
							if (tvPhone != null) tvPhone.setTextColor(getResources().getColor(R.color.white));
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					@Override
 | 
			
		||||
					public void onNothingSelected(AdapterView<?> parent) {
 | 
			
		||||
						// 未选中时无操作
 | 
			
		||||
					}
 | 
			
		||||
				});
 | 
			
		||||
        } else {
 | 
			
		||||
            // 数据更新时,先取消所有旧选中状态,再通知适配器刷新
 | 
			
		||||
            mlvContracts.clearChoices();
 | 
			
		||||
            mSimpleAdapter.notifyDataSetChanged();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 发送短信逻辑
 | 
			
		||||
    private void sendSMS() {
 | 
			
		||||
        String phoneTo = metTO.getText().toString().trim();
 | 
			
		||||
        if (phoneTo.isEmpty()) {
 | 
			
		||||
            ToastUtils.show("没有设置接收号码。");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        String smsBody = metSMSBody.getText().toString().trim();
 | 
			
		||||
        if (smsBody.isEmpty()) {
 | 
			
		||||
            ToastUtils.show("没有消息内容可发送。");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (SMSUtil.sendMessageByInterface2(ComposeSMSActivity.this, phoneTo, smsBody)) {
 | 
			
		||||
            finish();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,16 @@ import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.IntentFilter;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.Message;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewTreeObserver;
 | 
			
		||||
import android.view.inputmethod.InputMethodManager;
 | 
			
		||||
import android.widget.AbsListView;
 | 
			
		||||
import android.widget.EditText;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.ScrollView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
@@ -16,19 +21,17 @@ import android.widget.Toolbar;
 | 
			
		||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 | 
			
		||||
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.BottomPositionFixedScrollView;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.views.SMSListViewForScrollView;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.views.SMSView;
 | 
			
		||||
import java.lang.ref.WeakReference;
 | 
			
		||||
 | 
			
		||||
public class SMSActivity extends BaseActivity {
 | 
			
		||||
    public static String TAG = "SMSActivity";
 | 
			
		||||
 | 
			
		||||
    public static final String ACTION_NOTIFY_SMS_CHANGED = "cc.winboll.studio.mymessagemanager.activitys.SMSActivity.ACTION_NOTIFY_SMS_CHANGED";
 | 
			
		||||
 | 
			
		||||
    public static final String EXTRA_PHONE = "Phone";
 | 
			
		||||
    final static int MSG_SET_FOCUS = 0;
 | 
			
		||||
 | 
			
		||||
@@ -36,10 +39,11 @@ public class SMSActivity extends BaseActivity {
 | 
			
		||||
    Toolbar mToolbar;
 | 
			
		||||
    String mszPhoneTo;
 | 
			
		||||
    SMSArrayAdapter mSMSArrayAdapter;
 | 
			
		||||
    ScrollView mScrollView;
 | 
			
		||||
    BottomPositionFixedScrollView mScrollView1;
 | 
			
		||||
    EditText metSMSBody;
 | 
			
		||||
    SMSActivityBroadcastReceiver mSMSActivityBroadcastReceiver;
 | 
			
		||||
    Handler mSetFocusHandler;
 | 
			
		||||
    private boolean isImeVisible = false;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
@@ -47,45 +51,90 @@ public class SMSActivity extends BaseActivity {
 | 
			
		||||
        setContentView(R.layout.activity_sms);
 | 
			
		||||
 | 
			
		||||
        initView();
 | 
			
		||||
		mSetFocusHandler = new MyHandler(SMSActivity.this);
 | 
			
		||||
        scrollScrollView();
 | 
			
		||||
        setupImeStatusListener();
 | 
			
		||||
 | 
			
		||||
        // 每隔一定时间设置输入框获得焦点
 | 
			
		||||
        //
 | 
			
		||||
        new Thread() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void run() {
 | 
			
		||||
                while (true) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        Thread.sleep(1500);
 | 
			
		||||
                    } catch (InterruptedException e) {}
 | 
			
		||||
                    Message message = mSetFocusHandler.obtainMessage(MSG_SET_FOCUS);
 | 
			
		||||
                    mSetFocusHandler.sendMessage(message);
 | 
			
		||||
                }
 | 
			
		||||
            }}.start();
 | 
			
		||||
        // 新增:监听窗口加载完成,触发mScrollView1滚动到底部
 | 
			
		||||
        setupScrollToBottomAfterWindowLoaded();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 设置输入框获得焦点的类
 | 
			
		||||
	//
 | 
			
		||||
	static class MyHandler extends Handler {
 | 
			
		||||
		WeakReference<SMSActivity> mActivity;  
 | 
			
		||||
		MyHandler(SMSActivity activity) {  
 | 
			
		||||
			mActivity = new WeakReference<SMSActivity>(activity);  
 | 
			
		||||
		}
 | 
			
		||||
        public void handleMessage(Message msg) {
 | 
			
		||||
			SMSActivity theActivity = mActivity.get();
 | 
			
		||||
            switch (msg.what) {
 | 
			
		||||
                case MSG_SET_FOCUS:
 | 
			
		||||
                    theActivity.metSMSBody.setFocusable(true);
 | 
			
		||||
                    theActivity.metSMSBody.requestFocus();
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            super.handleMessage(msg);
 | 
			
		||||
        }
 | 
			
		||||
	}
 | 
			
		||||
    // 新增:窗口加载完成后让mScrollView1滚动到底部
 | 
			
		||||
    private void setupScrollToBottomAfterWindowLoaded() {
 | 
			
		||||
        final View rootView = findViewById(android.R.id.content);
 | 
			
		||||
        // 监听根布局绘制完成(窗口加载完成的标志)
 | 
			
		||||
        rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onGlobalLayout() {
 | 
			
		||||
					// 滚动到底部
 | 
			
		||||
					mScrollView1.post(new Runnable() {
 | 
			
		||||
							@Override
 | 
			
		||||
							public void run() {
 | 
			
		||||
								mScrollView1.fullScroll(ScrollView.FOCUS_DOWN);
 | 
			
		||||
							}
 | 
			
		||||
						});
 | 
			
		||||
 | 
			
		||||
					// 移除监听,避免重复触发
 | 
			
		||||
					if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
 | 
			
		||||
						rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
 | 
			
		||||
					} else {
 | 
			
		||||
						rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setupImeStatusListener() {
 | 
			
		||||
        final View rootView = findViewById(android.R.id.content);
 | 
			
		||||
        rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onGlobalLayout() {
 | 
			
		||||
					int rootViewHeight = rootView.getHeight();
 | 
			
		||||
					int screenHeight = getResources().getDisplayMetrics().heightPixels;
 | 
			
		||||
					int imeThreshold = dp2px(200);
 | 
			
		||||
 | 
			
		||||
					boolean currentImeVisible = (screenHeight - rootViewHeight) > imeThreshold;
 | 
			
		||||
 | 
			
		||||
					if (currentImeVisible != isImeVisible) {
 | 
			
		||||
						isImeVisible = currentImeVisible;
 | 
			
		||||
						setupScrollView1Height();
 | 
			
		||||
						if (!isImeVisible) {
 | 
			
		||||
							metSMSBody.clearFocus();
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
 | 
			
		||||
						rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
 | 
			
		||||
					} else {
 | 
			
		||||
						rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
 | 
			
		||||
					}
 | 
			
		||||
					setupImeStatusListener();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private int dp2px(int dp) {
 | 
			
		||||
        return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*static class MyHandler extends Handler {
 | 
			
		||||
	 WeakReference<SMSActivity> mActivity;
 | 
			
		||||
	 MyHandler(SMSActivity activity) {
 | 
			
		||||
	 mActivity = new WeakReference<SMSActivity>(activity);
 | 
			
		||||
	 }
 | 
			
		||||
	 public void handleMessage(Message msg) {
 | 
			
		||||
	 SMSActivity theActivity = mActivity.get();
 | 
			
		||||
	 switch (msg.what) {
 | 
			
		||||
	 case MSG_SET_FOCUS:
 | 
			
		||||
	 theActivity.metSMSBody.setFocusable(true);
 | 
			
		||||
	 theActivity.metSMSBody.requestFocus();
 | 
			
		||||
	 theActivity.setupScrollView1Height();
 | 
			
		||||
	 break;
 | 
			
		||||
	 default:
 | 
			
		||||
	 break;
 | 
			
		||||
	 }
 | 
			
		||||
	 super.handleMessage(msg);
 | 
			
		||||
	 }
 | 
			
		||||
	 }*/
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onDestroy() {
 | 
			
		||||
@@ -94,135 +143,130 @@ public class SMSActivity extends BaseActivity {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void initView() {
 | 
			
		||||
        // 发送端空号码退出
 | 
			
		||||
        mszPhoneTo = getIntent().getStringExtra(EXTRA_PHONE);
 | 
			
		||||
        if (mszPhoneTo == null || mszPhoneTo.trim().equals("")) {
 | 
			
		||||
            finish();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 初始化标题栏
 | 
			
		||||
        mToolbar = findViewById(R.id.activitysmsASupportToolbar1);
 | 
			
		||||
        mToolbar.setSubtitle(getString(R.string.activity_name_smsinphone) + " < Phone : " + mszPhoneTo + " >");
 | 
			
		||||
        mToolbar = (Toolbar) findViewById(R.id.activitysmsASupportToolbar1);
 | 
			
		||||
        mToolbar.setSubtitle(getString(R.string.activity_name_smsinphone) + " < Phone : " + AddressUtils.getFormattedAddress(mszPhoneTo) + " >");
 | 
			
		||||
        setActionBar(mToolbar);
 | 
			
		||||
 | 
			
		||||
        // 初始化滚动窗口
 | 
			
		||||
        mScrollView = findViewById(R.id.activitysmsinphoneScrollView1);
 | 
			
		||||
        mScrollView1 = (BottomPositionFixedScrollView) findViewById(R.id.activitysmsScrollView1);
 | 
			
		||||
 | 
			
		||||
        // 初始化发送消息框
 | 
			
		||||
        //Drawable drawableFrame = AppCompatResources.getDrawable(this, R.drawable.bg_frame);
 | 
			
		||||
        metSMSBody = findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        //metSMSBody.setBackground(drawableFrame);
 | 
			
		||||
        metSMSBody = (EditText) findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        metSMSBody.setOnClickListener(new View.OnClickListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onClick(View v) {
 | 
			
		||||
					setupScrollView1Height();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
        metSMSBody.setOnFocusChangeListener(new View.OnFocusChangeListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onFocusChange(View v, boolean hasFocus) {
 | 
			
		||||
					setupScrollView1Height();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化发送拉动控件
 | 
			
		||||
        final AOHPCTCSeekBar aOHPCTCSeekBar = findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1);
 | 
			
		||||
        final AOHPCTCSeekBar aOHPCTCSeekBar = (AOHPCTCSeekBar) findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1);
 | 
			
		||||
        aOHPCTCSeekBar.setThumb(getDrawable(R.drawable.ic_message));
 | 
			
		||||
        aOHPCTCSeekBar.setThumbOffset(20);
 | 
			
		||||
        aOHPCTCSeekBar.setOnOHPCListener(
 | 
			
		||||
            new AOHPCTCSeekBar.OnOHPCListener(){
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onOHPCommit() {
 | 
			
		||||
                    //Toast.makeText(getApplication(), "Send", Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    sendSMS();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        aOHPCTCSeekBar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onOHPCommit() {
 | 
			
		||||
					sendSMS();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化提示框
 | 
			
		||||
        TextView tvAOHPCTCSeekBarMSG = findViewById(R.id.viewsmssendpart1TextView1);
 | 
			
		||||
        TextView tvAOHPCTCSeekBarMSG = (TextView) findViewById(R.id.viewsmssendpart1TextView1);
 | 
			
		||||
        tvAOHPCTCSeekBarMSG.setText(R.string.msg_100sendmsg);
 | 
			
		||||
 | 
			
		||||
        mlvSMS = (SMSListViewForScrollView) findViewById(R.id.activitysmsinphoneListView1);
 | 
			
		||||
 | 
			
		||||
        // 准备数据
 | 
			
		||||
        mlvSMS = (SMSListViewForScrollView) findViewById(R.id.activitysmsSMSListViewForScrollView1);
 | 
			
		||||
        mSMSArrayAdapter = new SMSArrayAdapter(SMSActivity.this, mszPhoneTo);
 | 
			
		||||
        mlvSMS.setAdapter(mSMSArrayAdapter);
 | 
			
		||||
 | 
			
		||||
        // 设置短信列表滚动到底部就取消已发送的通知消息
 | 
			
		||||
        //
 | 
			
		||||
        mlvSMS.setOnScrollListener(new AbsListView.OnScrollListener() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onScrollStateChanged(AbsListView view, int scrollState) {
 | 
			
		||||
                }
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onScrollStateChanged(AbsListView view, int scrollState) {}
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
 | 
			
		||||
                    if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {
 | 
			
		||||
                        // 滑动到了底部
 | 
			
		||||
                        mSMSArrayAdapter.cancelMessageNotification();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
 | 
			
		||||
					if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {
 | 
			
		||||
						mSMSArrayAdapter.cancelMessageNotification();
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        mSMSActivityBroadcastReceiver = new SMSActivityBroadcastReceiver();
 | 
			
		||||
        IntentFilter intentFilter = new IntentFilter();
 | 
			
		||||
        intentFilter.addAction(ACTION_NOTIFY_SMS_CHANGED);
 | 
			
		||||
        IntentFilter intentFilter = new IntentFilter(ACTION_NOTIFY_SMS_CHANGED);
 | 
			
		||||
        LocalBroadcastManager.getInstance(this).registerReceiver(mSMSActivityBroadcastReceiver, intentFilter);
 | 
			
		||||
 | 
			
		||||
        /*SMSView mSMSView = findViewById(R.id.viewsmssendSMSView1);
 | 
			
		||||
        mSMSView.setSMSType(SMSView.SMSType.SEND);*/
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 更新信息列表
 | 
			
		||||
    //
 | 
			
		||||
    private void setupScrollView1Height() {
 | 
			
		||||
        mScrollView1.postDelayed(new Runnable() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void run() {
 | 
			
		||||
					final ScrollView scrollView2 = (ScrollView) findViewById(R.id.activitysmsScrollView2);
 | 
			
		||||
					final BottomPositionFixedScrollView scrollView1 = (BottomPositionFixedScrollView) findViewById(R.id.activitysmsScrollView1);
 | 
			
		||||
					final View includeView = findViewById(R.id.activitysmsinclude1);
 | 
			
		||||
 | 
			
		||||
					scrollView2.post(new Runnable() {
 | 
			
		||||
							@Override
 | 
			
		||||
							public void run() {
 | 
			
		||||
								int scrollView2Height = scrollView2.getHeight();
 | 
			
		||||
								int includeHeight = includeView.getHeight();
 | 
			
		||||
								int targetHeight = Math.max(scrollView2Height - includeHeight, 0);
 | 
			
		||||
 | 
			
		||||
								LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) scrollView1.getLayoutParams();
 | 
			
		||||
								params.height = targetHeight;
 | 
			
		||||
								scrollView1.setLayoutParams(params);
 | 
			
		||||
							}
 | 
			
		||||
						});
 | 
			
		||||
				}
 | 
			
		||||
			}, 100);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void updateSMSView() {
 | 
			
		||||
        mSMSArrayAdapter.reLoadSMSList(SMSActivity.this, mszPhoneTo);
 | 
			
		||||
        mSMSArrayAdapter.notifyDataSetChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 滚动消息文本框
 | 
			
		||||
    //
 | 
			
		||||
    void scrollScrollView() {
 | 
			
		||||
 | 
			
		||||
        ViewUtil.scrollScrollView(mScrollView);
 | 
			
		||||
 | 
			
		||||
        ViewUtil.scrollScrollView(mScrollView1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 发送短信
 | 
			
		||||
    //
 | 
			
		||||
    void sendSMS() {
 | 
			
		||||
        // 空消息不发送
 | 
			
		||||
        String szSMSBody = metSMSBody.getText().toString();
 | 
			
		||||
        if (szSMSBody.equals("")) {
 | 
			
		||||
            Toast.makeText(getApplication(), "没有消息内容可发送。", Toast.LENGTH_SHORT).show();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 发送短信
 | 
			
		||||
        if (SMSUtil.sendMessageByInterface2(this, mszPhoneTo, szSMSBody)) {
 | 
			
		||||
            metSMSBody.setText("");
 | 
			
		||||
            new Handler().postDelayed(new Runnable(){
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void run() {
 | 
			
		||||
                        updateSMSView();
 | 
			
		||||
                        ViewUtil.scrollScrollView(mScrollView);
 | 
			
		||||
                    }
 | 
			
		||||
                }, 1000);
 | 
			
		||||
            metSMSBody.clearFocus();
 | 
			
		||||
            new Handler().postDelayed(new Runnable() {
 | 
			
		||||
					@Override
 | 
			
		||||
					public void run() {
 | 
			
		||||
						updateSMSView();
 | 
			
		||||
						ViewUtil.scrollScrollView(mScrollView1);
 | 
			
		||||
					}
 | 
			
		||||
				}, 1000);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class SMSActivityBroadcastReceiver extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
        public SMSActivityBroadcastReceiver() {
 | 
			
		||||
            //LogUtils.d(TAG, "SMSActivityBroadcastReceiver()");
 | 
			
		||||
        }
 | 
			
		||||
        public SMSActivityBroadcastReceiver() {}
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onReceive(Context context, Intent intent) {
 | 
			
		||||
            switch (intent.getAction()) {
 | 
			
		||||
                case ACTION_NOTIFY_SMS_CHANGED :
 | 
			
		||||
                    //Toast.makeText(context, "ACTION_NOTIFY_SMS_CHANGED", Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    updateSMSView();
 | 
			
		||||
                    ViewUtil.scrollScrollView(mScrollView);
 | 
			
		||||
                    //LogUtils.d(TAG, "ACTION_NOTIFY_SMS_CHANGED");
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    throw new IllegalStateException("Unexpected value: " + intent.getAction());
 | 
			
		||||
            if (ACTION_NOTIFY_SMS_CHANGED.equals(intent.getAction())) {
 | 
			
		||||
                updateSMSView();
 | 
			
		||||
                ViewUtil.scrollScrollView(mScrollView1);
 | 
			
		||||
            } else {
 | 
			
		||||
                throw new IllegalStateException("Unexpected value: " + intent.getAction());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ import android.widget.Toast;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
import androidx.recyclerview.widget.LinearLayoutManager;
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.App;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.activitys.SMSReceiveRuleActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.adapters.SMSAcceptRuleArrayAdapter;
 | 
			
		||||
@@ -114,7 +114,7 @@ public class SMSReceiveRuleActivity extends BaseActivity {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void initSMSAcceptRuleBeanAdd() {
 | 
			
		||||
        mSMSAcceptRuleBeanAdd = new SMSAcceptRuleBean(GlobalApplication.USER_ID, "", true, SMSAcceptRuleBean.RuleType.REFUSE, true);
 | 
			
		||||
        mSMSAcceptRuleBeanAdd = new SMSAcceptRuleBean(App.USER_ID, "", true, SMSAcceptRuleBean.RuleType.REFUSE, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -8,16 +8,16 @@ package cc.winboll.studio.mymessagemanager.activitys;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.Menu;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
import androidx.recyclerview.widget.LinearLayoutManager;
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView;
 | 
			
		||||
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.activitys.SMSRecycleActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.adapters.SMSRecycleAdapter;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSRecycleUtil;
 | 
			
		||||
import cc.winboll.studio.shared.view.YesNoAlertDialog;
 | 
			
		||||
import com.baoyz.widget.PullRefreshLayout;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
 | 
			
		||||
public class SMSRecycleActivity extends BaseActivity {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,60 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.activitys;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen@AliYun.Com
 | 
			
		||||
 * @Date 2025/03/31 01:31:17
 | 
			
		||||
 * @Describe 应用活动窗口基类
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
public class WinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "WinBoLLActivity";
 | 
			
		||||
 | 
			
		||||
    protected volatile AESThemeBean.ThemeType mThemeType;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        mThemeType = getThemeType();
 | 
			
		||||
        setThemeStyle();
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    AESThemeBean.ThemeType getThemeType() {
 | 
			
		||||
        /*SharedPreferences sharedPreferences = getSharedPreferences(
 | 
			
		||||
         SHAREDPREFERENCES_NAME, MODE_PRIVATE);
 | 
			
		||||
         return AESThemeBean.ThemeType.values()[((sharedPreferences.getInt(DRAWER_THEME_TYPE, AESThemeBean.ThemeType.DEFAULT.ordinal())))];
 | 
			
		||||
         */
 | 
			
		||||
        return AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setThemeStyle() {
 | 
			
		||||
        //setTheme(AESThemeBean.getThemeStyle(getThemeType()));
 | 
			
		||||
        setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext()));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onOptionsItemSelected(MenuItem item) {
 | 
			
		||||
        if(item.getItemId() == android.R.id.home) {
 | 
			
		||||
            finish();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return super.onOptionsItemSelected(item);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -9,13 +9,14 @@ import android.view.ViewGroup;
 | 
			
		||||
import android.widget.BaseAdapter;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.activitys.SMSActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.PhoneBean;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.SMSBean;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.AddressUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.PhoneUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSUtil;
 | 
			
		||||
import cc.winboll.studio.shared.log.LogUtils;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -74,9 +75,9 @@ public class PhoneArrayAdapter extends BaseAdapter {
 | 
			
		||||
            viewHolder = (ViewHolder) convertView.getTag();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		String szAddress = ((SMSBean)getItem(position)).getAddress();
 | 
			
		||||
		final String szAddress = ((SMSBean)getItem(position)).getAddress();
 | 
			
		||||
 | 
			
		||||
		viewHolder.tvAddress.setText(szAddress);
 | 
			
		||||
		viewHolder.tvAddress.setText(AddressUtils.getFormattedAddress(szAddress));
 | 
			
		||||
		viewHolder.tvName.setText(getName(szAddress));
 | 
			
		||||
 | 
			
		||||
        //Drawable drawableFrame = AppCompatResources.getDrawable(mContext, R.drawable.bg_frame);
 | 
			
		||||
@@ -87,7 +88,7 @@ public class PhoneArrayAdapter extends BaseAdapter {
 | 
			
		||||
 | 
			
		||||
                    //Toast.makeText(mContext, tv.getText(), Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    Intent intent = new Intent(mContext, SMSActivity.class);
 | 
			
		||||
                    intent.putExtra(SMSActivity.EXTRA_PHONE, viewHolder.tvAddress.getText());
 | 
			
		||||
                    intent.putExtra(SMSActivity.EXTRA_PHONE, szAddress);
 | 
			
		||||
                    mContext.startActivity(intent);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,6 @@ import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.activitys.TTSPlayRuleActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.SMSBean;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.dialogs.YesNoAlertDialog;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.NotificationUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSReceiveRuleUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSRecycleUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSUtil;
 | 
			
		||||
@@ -28,6 +27,7 @@ import cc.winboll.studio.mymessagemanager.views.SMSView;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.SMSAcceptRuleBean;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.NotificationHelper;
 | 
			
		||||
 | 
			
		||||
public class SMSArrayAdapter extends BaseAdapter {
 | 
			
		||||
 | 
			
		||||
@@ -54,7 +54,8 @@ public class SMSArrayAdapter extends BaseAdapter {
 | 
			
		||||
 | 
			
		||||
    public void cancelMessageNotification() {
 | 
			
		||||
        for (SMSBean bean : mData) {
 | 
			
		||||
            NotificationUtil.cancelNotification(mContext, bean.getId());
 | 
			
		||||
            NotificationHelper notificationHelper = new NotificationHelper(mContext);
 | 
			
		||||
            notificationHelper.cancelNotification(bean.getId());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ import cc.winboll.studio.mymessagemanager.views.DateAgoTextView;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.views.SMSView;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.AddressUtils;
 | 
			
		||||
 | 
			
		||||
public class SMSRecycleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 | 
			
		||||
 | 
			
		||||
@@ -154,7 +155,7 @@ public class SMSRecycleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
 | 
			
		||||
        final SMSRecycleBean item = mDataList.get(position);
 | 
			
		||||
        if (holder.getItemViewType() == 0) {
 | 
			
		||||
            SimpleViewHolder viewHolder = (SimpleViewHolder) holder;
 | 
			
		||||
            viewHolder.mtvAddress.setText(item.getAddress());
 | 
			
		||||
            viewHolder.mtvAddress.setText(AddressUtils.getFormattedAddress(item.getAddress()));
 | 
			
		||||
            viewHolder.mbtnViewBody.setOnClickListener(new View.OnClickListener(){
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void onClick(View v) {
 | 
			
		||||
@@ -179,7 +180,7 @@ public class SMSRecycleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
 | 
			
		||||
                viewHolder.mvRight.setVisibility(View.GONE);
 | 
			
		||||
                viewHolder.mSMSView.setSMSType(SMSView.SMSType.SEND);
 | 
			
		||||
            }
 | 
			
		||||
            viewHolder.mtvAddress.setText(item.getAddress());
 | 
			
		||||
            viewHolder.mtvAddress.setText(AddressUtils.getFormattedAddress(item.getAddress()));
 | 
			
		||||
            viewHolder.mdatvDeleteDate.setDate(item.getDeleteDate());
 | 
			
		||||
            viewHolder.mdatvDate.setDate(item.getDate());
 | 
			
		||||
            if(mAppConfigUtil.mAppConfigBean.isSMSRecycleProtectMode()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,7 @@ package cc.winboll.studio.mymessagemanager.beans;
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.ThemeUtil;
 | 
			
		||||
import cc.winboll.studio.shared.app.BaseBean;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class AppConfigBean extends BaseBean {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ package cc.winboll.studio.mymessagemanager.beans;
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.shared.app.BaseBean;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class SMSAcceptRuleBean extends BaseBean {
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ package cc.winboll.studio.mymessagemanager.beans;
 | 
			
		||||
import android.content.ContentValues;
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.shared.app.BaseBean;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.text.Collator;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ package cc.winboll.studio.mymessagemanager.beans;
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.shared.app.BaseBean;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.text.Collator;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ package cc.winboll.studio.mymessagemanager.beans;
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.shared.app.BaseBean;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class TTSPlayRuleBean extends BaseBean {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,8 @@ package cc.winboll.studio.mymessagemanager.beans;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.FileUtil;
 | 
			
		||||
import cc.winboll.studio.shared.log.LogUtils;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.StringReader;
 | 
			
		||||
import java.io.StringWriter;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,9 +10,9 @@ import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.services.MainService;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.AppConfigUtil;
 | 
			
		||||
import cc.winboll.studio.shared.log.LogUtils;
 | 
			
		||||
 | 
			
		||||
public class MainReceiver extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,17 +4,17 @@ import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.App;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.activitys.SMSActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.SMSBean;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.AppConfigUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.NotificationUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.PhoneUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSReceiveRuleUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSRecycleUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.TTSPlayRuleUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.RegexPPiUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.NotificationHelper;
 | 
			
		||||
 | 
			
		||||
public class SMSRecevier extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
@@ -36,34 +36,20 @@ public class SMSRecevier extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
        String szAction = intent.getAction();
 | 
			
		||||
        if (szAction.equals(ACTION_SMS_RECEIVED)) {
 | 
			
		||||
            //LogUtils.d(TAG, "ACTION_SMS_RECEIVED");
 | 
			
		||||
            LogUtils.d(TAG, "ACTION_SMS_RECEIVED");
 | 
			
		||||
            String szSmsBody = SMSUtil.getSmsBody(intent);
 | 
			
		||||
            String szSmsAddress = SMSUtil.getSmsAddress(intent);
 | 
			
		||||
            PhoneUtil phoneUtil = new PhoneUtil(context);
 | 
			
		||||
            boolean isPhoneInContacts = phoneUtil.isPhoneInContacts(szSmsAddress);
 | 
			
		||||
            AppConfigUtil configUtil = AppConfigUtil.getInstance(context);
 | 
			
		||||
            boolean isOnlyReceiveContacts = configUtil.mAppConfigBean.isEnableOnlyReceiveContacts();
 | 
			
		||||
            boolean isEnableTTS = configUtil.mAppConfigBean.isEnableTTS();
 | 
			
		||||
            boolean isEnableTTSAnalyzeMode = configUtil.mAppConfigBean.isEnableTTSRuleMode();
 | 
			
		||||
            boolean isInSMSAcceptRule = SMSReceiveRuleUtil.getInstance(context, false).checkIsSMSAcceptInRule(context, szSmsBody);
 | 
			
		||||
            //LogUtils.d(TAG, "isInSMSAcceptRule is : " + Boolean.toString(isInSMSAcceptRule));
 | 
			
		||||
 | 
			
		||||
            if (!isPhoneInContacts) {
 | 
			
		||||
                GlobalApplication.showApplicationMessage(" The phone number " + szSmsAddress + " is not in contacts.");
 | 
			
		||||
                if (isOnlyReceiveContacts) {
 | 
			
		||||
                    GlobalApplication.showApplicationMessage("Close the \"Only Receive Contacts\" switch will be receive The " + szSmsAddress + "'s message in future.");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ((!isOnlyReceiveContacts)
 | 
			
		||||
                || isPhoneInContacts
 | 
			
		||||
                || isInSMSAcceptRule) {
 | 
			
		||||
            if (checkIsSMSOK(context, szSmsBody, szSmsAddress)) {
 | 
			
		||||
                int nResultId = SMSUtil.saveReceiveSms(context, szSmsAddress, szSmsBody, "0", System.currentTimeMillis(), "inbox");
 | 
			
		||||
                if (nResultId >= 0) {
 | 
			
		||||
                    NotificationUtil nu = new NotificationUtil();
 | 
			
		||||
                    nu.sendSMSReceivedMessage(context, nResultId, szSmsAddress, szSmsBody);
 | 
			
		||||
                    NotificationHelper notificationHelper = new NotificationHelper(context);
 | 
			
		||||
                    notificationHelper.sendSMSReceivedMessage(nResultId, szSmsAddress, szSmsBody);
 | 
			
		||||
                    LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(SMSActivity.ACTION_NOTIFY_SMS_CHANGED));
 | 
			
		||||
                    GlobalApplication.showApplicationMessage("<" + szSmsAddress + "> : ( " + szSmsBody + " ) [SAVED]");
 | 
			
		||||
                    LogUtils.d(TAG, "<" + szSmsAddress + "> : ( " + szSmsBody + " ) [SAVED]");
 | 
			
		||||
                    if (isEnableTTS) {
 | 
			
		||||
                        if (isEnableTTSAnalyzeMode) {
 | 
			
		||||
                            TTSPlayRuleUtil ttsPlayRuleUtil = TTSPlayRuleUtil.getInstance(context);
 | 
			
		||||
@@ -81,12 +67,41 @@ public class SMSRecevier extends BroadcastReceiver {
 | 
			
		||||
                SMSRecycleUtil.addSMSRecycleItem(context, bean);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 检查短信是否在接收设定规则内
 | 
			
		||||
    //
 | 
			
		||||
    public static boolean checkIsSMSOK(Context context, String szSmsBody, String szSmsAddress) {
 | 
			
		||||
        PhoneUtil phoneUtil = new PhoneUtil(context);
 | 
			
		||||
        boolean isPhoneInContacts = phoneUtil.isPhoneInContacts(szSmsAddress);
 | 
			
		||||
        LogUtils.d(TAG, String.format("isPhoneInContacts %s", isPhoneInContacts));
 | 
			
		||||
 | 
			
		||||
        boolean isPhoneByDigit = phoneUtil.isPhoneByDigit(szSmsAddress);
 | 
			
		||||
        LogUtils.d(TAG, String.format("isPhoneByDigit %s", isPhoneByDigit));
 | 
			
		||||
 | 
			
		||||
        AppConfigUtil configUtil = AppConfigUtil.getInstance(context);
 | 
			
		||||
        boolean isOnlyReceiveContacts = configUtil.mAppConfigBean.isEnableOnlyReceiveContacts();
 | 
			
		||||
        LogUtils.d(TAG, String.format("isOnlyReceiveContacts %s", isOnlyReceiveContacts));
 | 
			
		||||
 | 
			
		||||
        boolean isInSMSAcceptRule = SMSReceiveRuleUtil.getInstance(context, false).checkIsSMSAcceptInRule(context, szSmsBody);
 | 
			
		||||
        LogUtils.d(TAG, String.format("isInSMSAcceptRule %s", isInSMSAcceptRule));
 | 
			
		||||
 | 
			
		||||
        // 启用了只接受通讯录,通讯录里有记录
 | 
			
		||||
        if (isOnlyReceiveContacts && isPhoneInContacts) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        // 如果不是数字通讯地址,但是在通讯录内
 | 
			
		||||
        if (!isPhoneByDigit && isPhoneInContacts) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        // 通讯地址是数字,并且在短信接收规则内。
 | 
			
		||||
        if (isPhoneByDigit && isInSMSAcceptRule) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ package cc.winboll.studio.mymessagemanager.services;
 | 
			
		||||
 * @Date 2024/07/19 14:30:57
 | 
			
		||||
 * @Describe 应用主要服务组件类
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Notification;
 | 
			
		||||
import android.app.Service;
 | 
			
		||||
import android.content.ComponentName;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
@@ -12,20 +13,24 @@ import android.content.Intent;
 | 
			
		||||
import android.content.IntentFilter;
 | 
			
		||||
import android.content.ServiceConnection;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.activitys.MainActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.MessageNotificationBean;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.receivers.SMSRecevier;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.services.MainService;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.AppConfigUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.NotificationUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.NotificationHelper;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.ServiceUtil;
 | 
			
		||||
import cc.winboll.studio.shared.log.LogUtils;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
 | 
			
		||||
public class MainService extends Service {
 | 
			
		||||
 | 
			
		||||
    public static String TAG = "ManagerService";
 | 
			
		||||
 | 
			
		||||
    // 前台服务通知工具
 | 
			
		||||
    NotificationHelper mNotificationHelper;
 | 
			
		||||
    Notification notification;
 | 
			
		||||
    AppConfigUtil mConfigUtil;
 | 
			
		||||
    //MyBinder mMyBinder;
 | 
			
		||||
    MyServiceConnection mMyServiceConnection;
 | 
			
		||||
@@ -73,17 +78,12 @@ public class MainService extends Service {
 | 
			
		||||
                mSMSRecevier = new SMSRecevier();
 | 
			
		||||
                registerReceiver(mSMSRecevier, localIntentFilter);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                // 显示前台通知栏
 | 
			
		||||
                MessageNotificationBean notificationMessage = createNotificationMessage();
 | 
			
		||||
                NotificationUtil nu = new NotificationUtil();
 | 
			
		||||
                nu.sendForegroundNotification(MainService.this, notificationMessage);
 | 
			
		||||
                NotificationHelper helper = new NotificationHelper(this);
 | 
			
		||||
                Intent intent = new Intent(this, MainActivity.class);
 | 
			
		||||
                notification = helper.showForegroundNotification(intent, getString(R.string.app_name), getString(R.string.text_aboutservernotification));
 | 
			
		||||
                startForeground(NotificationHelper.FOREGROUND_NOTIFICATION_ID, notification);
 | 
			
		||||
 | 
			
		||||
                /*if (mConfigUtil.isEnableTTS()) {
 | 
			
		||||
                 TTSPlayRuleUtil.speakText(ManagerService.this, getString(R.string.text_iamhere), 0);
 | 
			
		||||
                 GlobalApplication.showApplicationMessage(getString(R.string.text_iamhere));
 | 
			
		||||
                 }*/
 | 
			
		||||
                 
 | 
			
		||||
                ToastUtils.show("Service is start.");
 | 
			
		||||
                LogUtils.i(TAG, "Service is start.");
 | 
			
		||||
            }
 | 
			
		||||
@@ -101,13 +101,6 @@ public class MainService extends Service {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private MessageNotificationBean createNotificationMessage() {
 | 
			
		||||
        String szTitle = getApplicationContext().getString(R.string.app_name);
 | 
			
		||||
        String szContent = getString(R.string.text_aboutservernotification);
 | 
			
		||||
        return new MessageNotificationBean(NotificationUtil.ID_MSG_SERVICE, "", szTitle, szContent);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int onStartCommand(Intent intent, int flags, int startId) {
 | 
			
		||||
        //return super.onStartCommand(intent, flags, startId);
 | 
			
		||||
@@ -116,12 +109,12 @@ public class MainService extends Service {
 | 
			
		||||
        return mConfigUtil.mAppConfigBean.isEnableService() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /*private class MyBinder extends IMyAidlInterface.Stub {
 | 
			
		||||
        @Override
 | 
			
		||||
        public String getServiceName() {
 | 
			
		||||
            return MainService.class.getSimpleName();
 | 
			
		||||
        }
 | 
			
		||||
    }*/
 | 
			
		||||
    /*private class MyBinder extends IMyAidlInterface.Stub {
 | 
			
		||||
     @Override
 | 
			
		||||
     public String getServiceName() {
 | 
			
		||||
     return MainService.class.getSimpleName();
 | 
			
		||||
     }
 | 
			
		||||
     }*/
 | 
			
		||||
 | 
			
		||||
    // 主进程与守护进程连接时需要用到此类
 | 
			
		||||
    //
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,28 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.unittest;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.AddressUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen@AliYun.Com
 | 
			
		||||
 * @Date 2025/03/01 13:07:32
 | 
			
		||||
 * @Describe AddressUtils Test
 | 
			
		||||
 */
 | 
			
		||||
public class AddressUtils_Test {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "AddressUtils_Test";
 | 
			
		||||
 | 
			
		||||
    public static void main(Context context) {
 | 
			
		||||
        String szSmsBody = "无影无迹";
 | 
			
		||||
        String szSmsAddress = "无名小辈";
 | 
			
		||||
        LogUtils.d(TAG, String.format("szSmsAddress %s\n getFormattedAddress : %s", szSmsAddress, AddressUtils.getFormattedAddress(szSmsAddress)));
 | 
			
		||||
        szSmsAddress = "13172887736";
 | 
			
		||||
        LogUtils.d(TAG, String.format("szSmsAddress %s\n getFormattedAddress : %s", szSmsAddress, AddressUtils.getFormattedAddress(szSmsAddress)));
 | 
			
		||||
        szSmsAddress = "+8613172887736";
 | 
			
		||||
        LogUtils.d(TAG, String.format("szSmsAddress %s\n getFormattedAddress : %s", szSmsAddress, AddressUtils.getFormattedAddress(szSmsAddress)));
 | 
			
		||||
        szSmsAddress = "8613172887736";
 | 
			
		||||
        LogUtils.d(TAG, String.format("szSmsAddress %s\n getFormattedAddress : %s", szSmsAddress, AddressUtils.getFormattedAddress(szSmsAddress)));
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,56 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.unittest;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen@AliYun.Com
 | 
			
		||||
 * @Date 2025/02/25 19:02:15
 | 
			
		||||
 * @Describe SMSRecevier 测试类
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.receivers.SMSRecevier;
 | 
			
		||||
 | 
			
		||||
public class SMSRecevier_Test {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "SMSRecevier_Test";
 | 
			
		||||
 | 
			
		||||
    public static void main(Context context) {
 | 
			
		||||
        String szSmsBody = "无影无迹";
 | 
			
		||||
        String szSmsAddress = "无名小辈";
 | 
			
		||||
        test1(context, szSmsBody, szSmsAddress);
 | 
			
		||||
        
 | 
			
		||||
        szSmsBody = "无影无迹";
 | 
			
		||||
        szSmsAddress = "淘宝物流";
 | 
			
		||||
        test1(context, szSmsBody, szSmsAddress);
 | 
			
		||||
        
 | 
			
		||||
        szSmsBody = "无影无迹";
 | 
			
		||||
        szSmsAddress = "1?0";
 | 
			
		||||
        test1(context, szSmsBody, szSmsAddress);
 | 
			
		||||
        
 | 
			
		||||
        szSmsBody = "无影无迹";
 | 
			
		||||
        szSmsAddress = "10000";
 | 
			
		||||
        test1(context, szSmsBody, szSmsAddress);
 | 
			
		||||
        
 | 
			
		||||
        szSmsBody = "【UC】无影无迹";
 | 
			
		||||
        szSmsAddress = "无名小辈";
 | 
			
		||||
        test1(context, szSmsBody, szSmsAddress);
 | 
			
		||||
        
 | 
			
		||||
        szSmsBody = "【UC】无影无迹";
 | 
			
		||||
        szSmsAddress = "10000";
 | 
			
		||||
        test1(context, szSmsBody, szSmsAddress);
 | 
			
		||||
        
 | 
			
		||||
        szSmsBody = "【UC】无影无迹";
 | 
			
		||||
        szSmsAddress = "13172887736";
 | 
			
		||||
        test1(context, szSmsBody, szSmsAddress);
 | 
			
		||||
        
 | 
			
		||||
        szSmsBody = "【UC】无影无迹";
 | 
			
		||||
        szSmsAddress = "+8613172887736";
 | 
			
		||||
        test1(context, szSmsBody, szSmsAddress);
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public static void test1(Context context, String szSmsBody, String szSmsAddress) {
 | 
			
		||||
        
 | 
			
		||||
        boolean isSMSOK = SMSRecevier.checkIsSMSOK(context, szSmsBody, szSmsAddress);
 | 
			
		||||
        LogUtils.d(TAG, String.format("szSmsBody : %s\nszSmsAddress : %s\nisSMSOK : %s", szSmsBody, szSmsAddress, isSMSOK));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,36 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.unittest;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen@AliYun.Com
 | 
			
		||||
 * @Date 2025/02/25 19:00:10
 | 
			
		||||
 * @Describe 应用单元测试窗口
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogView;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
 | 
			
		||||
public class UnitTestActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "UnitTestActivity";
 | 
			
		||||
 | 
			
		||||
    LogView mLogView;
 | 
			
		||||
    
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        setContentView(R.layout.activity_unittest);
 | 
			
		||||
        
 | 
			
		||||
        mLogView = findViewById(R.id.logview);
 | 
			
		||||
        mLogView.start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void onMain(View view) {
 | 
			
		||||
        LogUtils.d(TAG, "SMSRecevier_Test");
 | 
			
		||||
        SMSRecevier_Test.main(this);
 | 
			
		||||
        LogUtils.d(TAG, "AddressUtils_Test");
 | 
			
		||||
        AddressUtils_Test.main(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user