Merge remote-tracking branch 'origin/appbase' into autoinstaller
This commit is contained in:
		| @@ -1,4 +1,4 @@ | ||||
| ## WinBoll 主机编译事项提醒 | ||||
| ## WinBoLL 主机编译事项提醒 | ||||
|  | ||||
| ## 类库类型源码发布 | ||||
| # 类库发布使用以下面命令 | ||||
|   | ||||
| @@ -37,7 +37,7 @@ fi | ||||
| # 使用grep找到包含"publishVersion="的那一行,然后用awk提取其后的值 | ||||
| PUBLISH_VERSION=$(grep -o "publishVersion=.*" $1/build.properties | awk -F '=' '{print $2}') | ||||
| echo "< $1/build.properties publishVersion : ${PUBLISH_VERSION} >" | ||||
| ## 设新的 WinBoll 标签 | ||||
| ## 设新的 WinBoLL 标签 | ||||
| # 脚本调试时使用 | ||||
| #tag="v7.6.4-test1" | ||||
| # 正式设置标签时使用 | ||||
|   | ||||
| @@ -38,24 +38,24 @@ function askAddWorkflowsTag { | ||||
| 	fi | ||||
| } | ||||
|  | ||||
| function addWinBollTag { | ||||
| function addWinBoLLTag { | ||||
| 	# 就读取脚本 .winboll/winboll_app_build.gradle 生成的 publishVersion。 | ||||
|     # 如果文件中有 publishVersion 这一项, | ||||
| 	# 使用grep找到包含"publishVersion="的那一行,然后用awk提取其后的值 | ||||
| 	PUBLISH_VERSION=$(grep -o "publishVersion=.*" $1/build.properties | awk -F '=' '{print $2}') | ||||
| 	echo "< $1/build.properties publishVersion : ${PUBLISH_VERSION} >" | ||||
| 	## 设新的 WinBoll 标签 | ||||
| 	## 设新的 WinBoLL 标签 | ||||
| 	# 脚本调试时使用 | ||||
| 	#tag="projectname-v7.6.4-test1" | ||||
| 	# 正式设置标签时使用 | ||||
| 	tag=$1"-v"${PUBLISH_VERSION} | ||||
| 	echo "< WinBoll Tag To: $tag >"; | ||||
| 	# 检查是否已经添加了 WinBoll Tag | ||||
| 	echo "< WinBoLL Tag To: $tag >"; | ||||
| 	# 检查是否已经添加了 WinBoLL Tag | ||||
| 	if [ "$(git tag -l ${tag})" == "${tag}" ]; then | ||||
|         echo -e "< WinBoll Tag ${tag} exist! >" | ||||
|         return 1 # WinBoll标签重复 | ||||
|         echo -e "< WinBoLL Tag ${tag} exist! >" | ||||
|         return 1 # WinBoLL标签重复 | ||||
|     fi | ||||
|     # 添加WinBoll标签 | ||||
|     # 添加WinBoLL标签 | ||||
| 	git tag -a ${tag} -F $1/app_update_description.txt | ||||
|     return 0 | ||||
| } | ||||
| @@ -119,22 +119,22 @@ if [[ $? -eq 0 ]]; then | ||||
| 	echo $result | ||||
|  | ||||
|     # 发布应用 | ||||
| 	echo "Publishing WinBoll APK ..." | ||||
| 	echo "Publishing WinBoLL APK ..." | ||||
| 	# 脚本调试时使用 | ||||
| 	#bash gradlew :$1:assembleBetaDebug | ||||
| 	# 正式发布 | ||||
|     bash gradlew :$1:assembleStageRelease | ||||
|     echo "Publishing WinBoll APK OK." | ||||
|     echo "Publishing WinBoLL APK OK." | ||||
|      | ||||
|     # 添加 WinBoll 标签 | ||||
|     result=$(addWinBollTag $1) | ||||
|     # 添加 WinBoLL 标签 | ||||
|     result=$(addWinBoLLTag $1) | ||||
|     echo $result | ||||
| 	if [[ $? -eq 0 ]]; then | ||||
| 	    echo $result | ||||
|     	# WinBoll 标签添加成功 | ||||
|     	# WinBoLL 标签添加成功 | ||||
| 	else | ||||
|     	echo -e "${0}: addWinBollTag $1\n${result}\nAdd WinBoll tag cancel." | ||||
|         exit 1 # addWinBollTag 异常 | ||||
|     	echo -e "${0}: addWinBoLLTag $1\n${result}\nAdd WinBoLL tag cancel." | ||||
|         exit 1 # addWinBoLLTag 异常 | ||||
| 	fi | ||||
|      | ||||
|     # 添加 GitHub 工作流标签 | ||||
|   | ||||
| @@ -38,24 +38,24 @@ function askAddWorkflowsTag { | ||||
|     fi | ||||
| } | ||||
|  | ||||
| function addWinBollTag { | ||||
| function addWinBoLLTag { | ||||
|     # 就读取脚本 .winboll/winboll_app_build.gradle 生成的 publishVersion。 | ||||
|     # 如果文件中有 publishVersion 这一项, | ||||
|     # 使用grep找到包含"publishVersion="的那一行,然后用awk提取其后的值 | ||||
|     PUBLISH_VERSION=$(grep -o "publishVersion=.*" $1/build.properties | awk -F '=' '{print $2}') | ||||
|     echo "< $1/build.properties publishVersion : ${PUBLISH_VERSION} >" | ||||
|     ## 设新的 WinBoll 标签 | ||||
|     ## 设新的 WinBoLL 标签 | ||||
|     # 脚本调试时使用 | ||||
|     #tag="v7.6.4-test1" | ||||
|     # 正式调试版设置标签时使用 | ||||
|     tag=$1"-v"${PUBLISH_VERSION}"-debug" | ||||
|     echo "< WinBoll Tag To: $tag >"; | ||||
|     # 检查是否已经添加了 WinBoll Tag | ||||
|     echo "< WinBoLL Tag To: $tag >"; | ||||
|     # 检查是否已经添加了 WinBoLL Tag | ||||
|     if [ "$(git tag -l ${tag})" == "${tag}" ]; then | ||||
|         echo -e "< WinBoll Tag ${tag} exist! >" | ||||
|         return 1 # WinBoll标签重复 | ||||
|         echo -e "< WinBoLL Tag ${tag} exist! >" | ||||
|         return 1 # WinBoLL标签重复 | ||||
|     fi | ||||
|     # 添加WinBoll标签 | ||||
|     # 添加WinBoLL标签 | ||||
|     git tag -a ${tag} -F $1/app_update_description.txt | ||||
|     return 0 | ||||
| } | ||||
| @@ -119,22 +119,22 @@ if [[ $? -eq 0 ]]; then | ||||
|     echo $result | ||||
|  | ||||
|     # 发布应用 | ||||
|     echo "Publishing WinBoll Debug APK ..." | ||||
|     echo "Publishing WinBoLL Debug APK ..." | ||||
|     # 脚本调试时使用 | ||||
|     #bash gradlew :$1:assembleBetaDebug | ||||
|     # 正式发布调试版 | ||||
|     bash gradlew :$1:assembleStageDebug | ||||
|     echo "Publishing WinBoll Debug APK OK." | ||||
|     echo "Publishing WinBoLL Debug APK OK." | ||||
|      | ||||
|     # 添加 WinBoll 标签 | ||||
|     result=$(addWinBollTag $1) | ||||
|     # 添加 WinBoLL 标签 | ||||
|     result=$(addWinBoLLTag $1) | ||||
|     echo $result | ||||
|     if [[ $? -eq 0 ]]; then | ||||
|         echo $result | ||||
|         # WinBoll 标签添加成功 | ||||
|         # WinBoLL 标签添加成功 | ||||
|     else | ||||
|         echo -e "${0}: addWinBollTag $1\n${result}\nAdd WinBoll tag cancel." | ||||
|         exit 1 # addWinBollTag 异常 | ||||
|         echo -e "${0}: addWinBoLLTag $1\n${result}\nAdd WinBoLL tag cancel." | ||||
|         exit 1 # addWinBoLLTag 异常 | ||||
|     fi | ||||
|      | ||||
|     # 添加 GitHub 工作流标签 | ||||
|   | ||||
| @@ -8,7 +8,7 @@ if [ -z "$1" ]; then | ||||
| fi | ||||
|  | ||||
| ## 正式发布使用 | ||||
| git pull && bash gradlew :$1:publishReleasePublicationToWinBollReleaseRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1 | ||||
| git pull && bash gradlew :$1:publishReleasePublicationToWinBoLLReleaseRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1 | ||||
|  | ||||
| ## 调试使用 | ||||
| #bash gradlew :$1:publishSnapshotWinBollPublicationToWinBollSnapshotRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1 | ||||
| #bash gradlew :$1:publishSnapshotWinBoLLPublicationToWinBoLLSnapshotRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1 | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| // WinBoll 应用签名配置 | ||||
| // WinBoLL 应用签名配置 | ||||
| // | ||||
|  | ||||
| android { | ||||
| @@ -31,18 +31,18 @@ android { | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     flavorDimensions "WinBollApp" | ||||
|     flavorDimensions "WinBoLLApp" | ||||
|     productFlavors { | ||||
|         beta { | ||||
|             // 检查编译标志位配置 | ||||
|             assert (winbollBuildProps['buildCount'] != null) | ||||
|             dimension "WinBollApp" | ||||
|             dimension "WinBoLLApp" | ||||
|             applicationIdSuffix ".beta" | ||||
|             LocalDateTime localDateTimeNow = LocalDateTime.now(ZoneId.of("Asia/Shanghai")); | ||||
|             versionNameSuffix "-beta" + winbollBuildProps['buildCount'] + "_" + localDateTimeNow.format('mmss') | ||||
|         } | ||||
|         stage { | ||||
|             dimension "WinBollApp" | ||||
|             dimension "WinBoLLApp" | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -61,7 +61,7 @@ android { | ||||
|         } | ||||
|          | ||||
|         // | ||||
|         // WinBoll 应用包输出配置 | ||||
|         // WinBoLL 应用包输出配置 | ||||
|         // 1. 配置 Stage Release 版应用包输出 | ||||
|         // 2. 配置 Beta Debug 版应用包输出 | ||||
| 	    // | ||||
| @@ -74,13 +74,13 @@ android { | ||||
|             //def outputFileName="${rootProject.name}_${versionName}.apk" | ||||
|             def outputFileName=project.rootDir.name + "_${versionName}.apk" | ||||
|              | ||||
|             // 创建 WinBoll Studio 发布接口文件夹 | ||||
|             File fWinBollStudioDir = file("/sdcard/WinBollStudio/APKs"); | ||||
|             if(!fWinBollStudioDir.exists()) { | ||||
|                 //fWinBollStudioDir.mkdirs(); | ||||
|             // 创建 WinBoLL Studio 发布接口文件夹 | ||||
|             File fWinBoLLStudioDir = file("/sdcard/WinBoLLStudio/APKs"); | ||||
|             if(!fWinBoLLStudioDir.exists()) { | ||||
|                 //fWinBoLLStudioDir.mkdirs(); | ||||
|                 // 如果没有发布接口文件就不用进行APK发布和源码管理操作 | ||||
|                 // 当前编译环境不是 WinBoll 主机, 以下将忽略APK发布和源码管理操作。 | ||||
|                 println 'The current compilation environment is not in WinBoll host, and the following APK publishing and source management operations will be ignore.' | ||||
|                 // 当前编译环境不是 WinBoLL 主机, 以下将忽略APK发布和源码管理操作。 | ||||
|                 println 'The current compilation environment is not in WinBoLL host, and the following APK publishing and source management operations will be ignore.' | ||||
|             } else { | ||||
|                 /// WINBOLL 主机的 APK 发布和源码管理操作 /// | ||||
|                 variant.getAssembleProvider().get().doFirst { | ||||
| @@ -91,15 +91,15 @@ android { | ||||
|                 // | ||||
|                 variant.getAssembleProvider().get().doLast { | ||||
|                         variant.outputs.forEach{ file-> | ||||
|                             // 如果正在调试,就拷贝到 WinBoll 备份管理文件夹 | ||||
|                             // 如果正在调试,就拷贝到 WinBoLL 备份管理文件夹 | ||||
|                             //  | ||||
|                             if(variant.flavorName == "beta"&&variant.buildType.name == "debug"){ | ||||
|                                 //File outBuildBckDir = new File(fWinBollStudioDir, "/${rootProject.name}/${variant.buildType.name}") | ||||
|                                 File outBuildBckDir = new File(fWinBollStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}") | ||||
|                                 //File outBuildBckDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/${variant.buildType.name}") | ||||
|                                 File outBuildBckDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}") | ||||
|                                 // 创建目标路径目录 | ||||
|                                 if(!outBuildBckDir.exists()) { | ||||
|                                     outBuildBckDir.mkdirs(); | ||||
|                                     println "Output Folder Created.(WinBollStudio) : " + outBuildBckDir.getAbsolutePath() | ||||
|                                     println "Output Folder Created.(WinBoLLStudio) : " + outBuildBckDir.getAbsolutePath() | ||||
|                                 } | ||||
|                                 if(outBuildBckDir.exists()) { | ||||
|                                     copy{ | ||||
| @@ -108,7 +108,7 @@ android { | ||||
|                                         rename { | ||||
|                                             String fileName -> "${outputFileName}" | ||||
|                                         } | ||||
|                                         println "Output APK (WinBollStudio): " + outBuildBckDir.getAbsolutePath() + "/${outputFileName}" | ||||
|                                         println "Output APK (WinBoLLStudio): " + outBuildBckDir.getAbsolutePath() + "/${outputFileName}" | ||||
|                                     } | ||||
|                                     // 检查编译标志位配置 | ||||
|                                     assert (winbollBuildProps['buildCount'] != null) | ||||
| @@ -137,7 +137,7 @@ android { | ||||
|                                 } | ||||
|                             } | ||||
|                              | ||||
|                             // 如果正在发布,就拷贝到 WinBoll 标签管理文件夹 | ||||
|                             // 如果正在发布,就拷贝到 WinBoLL 标签管理文件夹 | ||||
|                             // | ||||
|                             if((variant.flavorName == "stage"&&variant.buildType.name == "debug") | ||||
|                                 || (variant.flavorName == "stage"&&variant.buildType.name == "release")){ | ||||
| @@ -151,8 +151,8 @@ android { | ||||
|                                 String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk" | ||||
|                                 println "CommonTagAPKName is : " + szCommonTagAPKName | ||||
|                                  | ||||
|                                 //File outTagDir = new File(fWinBollStudioDir, "/${rootProject.name}/tag/") | ||||
|                                 File outTagDir = new File(fWinBollStudioDir, "/" + project.rootDir.name + "/tag/") | ||||
|                                 //File outTagDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/tag/") | ||||
|                                 File outTagDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/tag/") | ||||
|                                 // 创建目标路径目录 | ||||
|                                 if(!outTagDir.exists()) { | ||||
|                                     outTagDir.mkdirs(); | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| // 本机和 WinBoll Maven 仓库传输配置。 | ||||
| // 本机和 WinBoLL Maven 仓库传输配置。 | ||||
| // | ||||
|  | ||||
| def getDefaultVersion(){ | ||||
| @@ -9,12 +9,12 @@ def getDefaultVersion(){ | ||||
| } | ||||
|  | ||||
| def siteUrl = 'https://winboll.cc/?page=studio/details.php&app=${rootProject.name}' // 项目主页 | ||||
| def gitUrl = 'https://gitea.winboll.cc/WinBoll/${rootProject.name}' // 项目的git地址 | ||||
| def gitUrl = 'https://gitea.winboll.cc/WinBoLL/${rootProject.name}' // 项目的git地址 | ||||
| def DefaultGroupId = 'cc.winboll.studio' // 类库所有者groupId | ||||
| def DefaultVersion = getDefaultVersion() // 版本号 | ||||
| def DeveloperId='zhangsken' // 开发者账号 | ||||
| def DeveloperName='ZhanGSKen' // 开发者名称 | ||||
| def DeveloperEMail='ZhanGSKen@QQ.COM' // 开发者邮箱地址 | ||||
| def DeveloperEMail='zhangsken@188.com' // 开发者邮箱地址 | ||||
| def LicenseName='The Apache Software License, Version 2.0' | ||||
| def LicenseUrl='http://www.apache.org/licenses/LICENSE-2.0.txt' | ||||
|  | ||||
| @@ -27,10 +27,10 @@ afterEvaluate { | ||||
|                 properties.load(file("${RootProjectDir}/${winbollFilePath}").newDataInputStream()) | ||||
|                 def NexusUserName = properties.getProperty("Nexus.name") | ||||
|                 def NexusPassword = properties.getProperty("Nexus.password") | ||||
|                 // WinBoll Release 仓库 | ||||
|                 // WinBoLL Release 仓库 | ||||
|                 maven{ | ||||
|                     //仓库的名字和地址 | ||||
|                     name = "WinBollRelease" | ||||
|                     name = "WinBoLLRelease" | ||||
|                     url="https://nexus.winboll.cc/repository/maven-releases/" | ||||
|                     // 仓库用户名密码 | ||||
|                     credentials { | ||||
| @@ -38,10 +38,10 @@ afterEvaluate { | ||||
|                         password = NexusPassword | ||||
|                     } | ||||
|                 } | ||||
|                 // WinBoll Snapshot 仓库 | ||||
|                 // WinBoLL Snapshot 仓库 | ||||
|                 maven{ | ||||
|                     //仓库的名字和地址 | ||||
|                     name = "WinBollSnapshot" | ||||
|                     name = "WinBoLLSnapshot" | ||||
|                     url="https://nexus.winboll.cc/repository/maven-snapshots/" | ||||
|                     // 仓库用户名密码 | ||||
|                     credentials { | ||||
| @@ -101,9 +101,9 @@ afterEvaluate { | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             // WinBoll Maven Release 仓库传输任务 | ||||
|             // WinBoLL Maven Release 仓库传输任务 | ||||
|             // | ||||
|             releaseWinBoll(MavenPublication) { | ||||
|             releaseWinBoLL(MavenPublication) { | ||||
|                 // 需要使用的变体,假设有free和pay两个变体,可以选择一个 | ||||
|                 //from components.free | ||||
|                  | ||||
| @@ -154,9 +154,9 @@ afterEvaluate { | ||||
|                  | ||||
|             } // 创建名为 release 的任务结束 | ||||
|              | ||||
|             // WinBoll Maven Snapshot 仓库传输任务 | ||||
|             // WinBoLL Maven Snapshot 仓库传输任务 | ||||
|             // | ||||
|             snapshotWinBoll(MavenPublication) { | ||||
|             snapshotWinBoLL(MavenPublication) { | ||||
|                 // 需要使用的变体,假设有free和pay两个变体,可以选择一个 | ||||
|                 //from components.free | ||||
|                  | ||||
|   | ||||
							
								
								
									
										127
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,23 +1,23 @@ | ||||
| # ☁ ☁ ☁ WinBoll APP ☁ ☁ ☁ ☁ ☁ ☁   ☁ ☁   ☁  ☁ ☁ ☁ ☁  ☁ ☁ ☁ ☁ ☁ ☁  | ||||
| # ☁    ☁ 这是 WinBoll 系列 APP 汇总项目。☁ ☁   ☁ ☁ ☁ ☁  ☁ ☁ ☁ ☁ ☁  ☁ ☁ ☁  | ||||
| # ☁ ☁ ☁ WinBoll 网站地址 https://www.winboll.cc/studio/app/ ☁ ☁ ☁ ☁  ☁ ☁ ☁ ☁ | ||||
| # ☁ ☁ ☁ WinBoLL APP ☁ ☁ ☁ ☁ ☁ ☁   ☁ ☁   ☁  ☁ ☁ ☁ ☁  ☁ ☁ ☁ ☁ ☁ ☁  | ||||
| # ☁    ☁ WinBoLL Studio Android 应用开源项目。☁ ☁   ☁ ☁ ☁ ☁  ☁ ☁ ☁ ☁ ☁  ☁ ☁ ☁  | ||||
| # ☁ ☁ ☁ WinBoLL 网站地址 https://www.winboll.cc/ ☁ ☁ ☁ ☁  ☁ ☁ ☁ ☁ | ||||
|  | ||||
| ## WinBoll 提问 | ||||
| ## WinBoLL 提问 | ||||
| 同样是 /sdcard 目录,在开发 Android 应用时, | ||||
| 能否实现手机编译与电脑编译的源码同步。 | ||||
| ☁因而 WinBoll 项目组诞生了。 | ||||
| ☁因而 WinBoLL 项目组诞生了。 | ||||
|  | ||||
| ## WinBoll 项目组研发计划 | ||||
| 致力于把 WinBoll-APP 应用在手机端 Android 项目开发。 | ||||
| 也在探索 https://gitea.winboll.cc/<WinBoll 项目组>/APP.git 应用于 WinBoll-APP APK 分发。 | ||||
| 更想进阶 https://github.com/<WinBoll 项目组>/APP.git 应用于 WinBoll-APP Beta APK 分发。 | ||||
| ## WinBoLL 项目组研发计划 | ||||
| 致力于把 WinBoLL-APP 应用在手机端 Android 项目开发。 | ||||
| 也在探索 https://gitea.winboll.cc/<WinBoLL 项目组>/APP.git 应用于 WinBoLL-APP APK 分发。 | ||||
| 更想进阶 https://github.com/<WinBoLL 项目组>/APP.git 应用于 WinBoLL-APP Beta APK 分发。 | ||||
|  | ||||
| ## WinBoll-APP 汗下... | ||||
| ## WinBoLL-APP 汗下... | ||||
| #### ☁应用何置如此呢。且观用户云云。 | ||||
|  | ||||
| #### ☁ 正当下 ☁ ### | ||||
| #### ☁ 且容傻家叙说 ☁ WinBoll-APP 应用场景 | ||||
| ### ☁ WinBoll 设备资源概述 | ||||
| #### ☁ 且容傻家叙说 ☁ WinBoLL-APP 应用场景 | ||||
| ### ☁ WinBoLL 设备资源概述 | ||||
| #### ☁ 1. Raid Disk. | ||||
| 概述:这是一个矩阵存储类设备。 | ||||
| 优点:该设备具有数据容错存储功能, | ||||
| @@ -40,74 +40,79 @@ | ||||
|      设备位于操作系统内部文件系统。 | ||||
|      数据持久性与操作系统挂钩。 | ||||
|       | ||||
| #### ☁ 4. WinBoll 用户资源概述。 | ||||
| 1> /home/<用户名> 位于 WinBoll 操作系统目录下。 | ||||
| #### ☁ 4. WinBoLL 用户资源概述。 | ||||
| 1> /home/<用户名> 位于 WinBoLL 操作系统目录下。 | ||||
| 2> /rdisk/<用户名> 挂载用户 Raid Disk. | ||||
| 3> /data/<用户名> 挂载用户 Data Disk. | ||||
| 4> /sdcard/<用户名> 挂载用户 SSD Disk. | ||||
|  | ||||
| #### ☁ 5. WinBoll-APP 用户资源概述。 | ||||
| #### ☁ 5. WinBoLL-APP 用户资源概述。 | ||||
| 1> /sdcard 挂载用户手机 SD 存储/storage/emulated/0 | ||||
|  | ||||
| ### ☁ 稍稍歇 ☁ ### | ||||
| ### ☁ 急急停 ☁ WinBoll 应用前置条件 | ||||
| ☁ WinBoll 主机建立 1Panel MySQL 应用。 | ||||
| ☁ WinBoll 主机建立 1Panel Gitea 应用。 | ||||
| ☁ WinBoll 主机设置 WinBoll 应用为非登录状态。 | ||||
| ☁ WinBoll 主机建立 WinBoll 账户与 WinBoll 用户组。 | ||||
| ☁ WinBoll 账户 User ID 为: J。 | ||||
| ☁ WinBoll 用户组 Group ID 为: Studio。 | ||||
| ☁ WinBoll 主机 WinBoll 1Panel Gitea 建立 WinBoll 工作组。 | ||||
| ☁ WinBoll 主机 WinBoll 1Panel Gitea 用户项目 APK 编译输出目录为 /sdcard/WinBollStudio/<用户名>/APKs/ | ||||
| ☁ WinBoll 项目配置文件示例为 "<WinBoll 项目根目录>/.winboll/winboll.properties-demo"(WinBoll 项目已设置) | ||||
| ☁ WinBoll 项目配置文件为 "<WinBoll 项目根目录>/.winboll/winboll.properties" | ||||
| ☁ WinBoll 项目配置文件设定为源码提交时忽略。(WinBoll 项目已设置) | ||||
| ☁ Gradle 项目配置文件示例为 "<WinBoll 项目根目录>/.winboll/local.properties-demo"(WinBoll 项目已设置) | ||||
| ☁ Gradle 项目配置文件为 "<WinBoll 项目根目录>/local.properties"(WinBoll 项目已设置) | ||||
| ☁ Gradle 项目配置文件设定为源码提交时忽略。(WinBoll 项目已设置) | ||||
| ### ☁ 急急停 ☁ WinBoLL 应用前置条件 | ||||
| ☁ WinBoLL 主机建立 1Panel MySQL 应用。 | ||||
| ☁ WinBoLL 主机建立 1Panel Gitea 应用。 | ||||
| ☁ WinBoLL 主机设置 WinBoLL 应用为非登录状态。 | ||||
| ☁ WinBoLL 主机建立 WinBoLL 账户与 WinBoLL 用户组。 | ||||
| ☁ WinBoLL 账户 User ID 为: J。 | ||||
| ☁ WinBoLL 用户组 Group ID 为: Studio。 | ||||
| ☁ WinBoLL 主机 WinBoLL 1Panel Gitea 建立 WinBoLL 工作组。 | ||||
| ☁ WinBoLL 主机 WinBoLL 1Panel Gitea 用户项目 APK 编译输出目录为 /sdcard/WinBoLLStudio/<用户名>/APKs/ | ||||
| ☁ WinBoLL 项目配置文件示例为 "<WinBoLL 项目根目录>/.winboll/winboll.properties-demo"(WinBoLL 项目已设置) | ||||
| ☁ WinBoLL 项目配置文件为 "<WinBoLL 项目根目录>/.winboll/winboll.properties" | ||||
| ☁ WinBoLL 项目配置文件设定为源码提交时忽略。(WinBoLL 项目已设置) | ||||
| ☁ Gradle 项目配置文件示例为 "<WinBoLL 项目根目录>/.winboll/local.properties-demo"(WinBoLL 项目已设置) | ||||
| ☁ Gradle 项目配置文件为 "<WinBoLL 项目根目录>/local.properties"(WinBoLL 项目已设置) | ||||
| ☁ Gradle 项目配置文件设定为源码提交时忽略。(WinBoLL 项目已设置) | ||||
|  | ||||
| ### ☁ 登高处 ☁ WinBoll 应用需求规划 | ||||
| ☁ WinBoll 主机建立 WinBoll 客户端用户数据库为 MySQL winbollclient 数据库。 | ||||
| ☁ WinBoll 主机设置 WinBoll 客户端用户信息存储在 winbollclient 数据库中。 | ||||
| ### ☁ 登高处 ☁ WinBoLL 应用需求规划 | ||||
| ☁ WinBoLL 主机建立 WinBoLL 客户端用户数据库为 MySQL winbollclient 数据库。 | ||||
| ☁ WinBoLL 主机设置 WinBoLL 客户端用户信息存储在 winbollclient 数据库中。 | ||||
| ☁ MySQL winbollclient 数据库中 | ||||
|    WinBoll 客户端用户信息设定为: | ||||
|    WinBoLL 客户端用户信息设定为: | ||||
|    <用户名, 验证密码, 验证邮箱, 验证手机, 唯一存储令牌Token, 备用验证邮箱>。 | ||||
| ☁ WinBoll 项目源码仓库托管在 WinBoll 1Panel Gitea 目录 /opt/1panel/apps/gitea/gitea/data/git/repositories/studio/app.git中。 | ||||
| ☁ WinBoll 主机提供 WinBoll 1Panel Gitea 应用的 WinBoll 项目源码仓库存取功能。(Gitea 应用已提供) | ||||
| ☁ WinBoll 主机提供 WinBoll Gitea 项目仓库存档功能。(Gitea 应用已提供) | ||||
| ☁ 提供 WinBoll 客户端用户登录功能。(Gitea 应用已提供) | ||||
| ☁ WinBoLL 项目源码仓库托管在 WinBoLL 1Panel Gitea 目录 /opt/1panel/apps/gitea/gitea/data/git/repositories/studio/app.git中。 | ||||
| ☁ WinBoLL 主机提供 WinBoLL 1Panel Gitea 应用的 WinBoLL 项目源码仓库存取功能。(Gitea 应用已提供) | ||||
| ☁ WinBoLL 主机提供 WinBoLL Gitea 项目仓库存档功能。(Gitea 应用已提供) | ||||
| ☁ 提供 WinBoLL 客户端用户登录功能。(Gitea 应用已提供) | ||||
|  | ||||
| ### ☁ 看远方 ☁ ### | ||||
| ### ☁ 心忧虑 ☁ WinBoll-APP 应用前置需求 | ||||
| ☁ WinBoll-APP WinBoll 项目根目录设定为手机的 /sdcard/WinBollStudio/Sources 目录。(需要用户手动建立文件夹) | ||||
| ☁ WinBoll-APP 具有手机 /sdcard/WinBoll 目录的存储权限。(需要手机操作系统授权) | ||||
| ☁ WinBoll-APP WinBoll 项目仓库源码存储路径为 /sdcard/WinBollStudio/Sources/APP.git(需要用户手动建立文件夹) | ||||
| ☁ WinBoll-APP 项目 APK 编译输出目录为 /sdcard/WinBollStudio/APKs/ | ||||
| ☁ WinBoll-APP 应用签名验证可定制化。(WinBoll 项目已提供) | ||||
| ☁ WinBoll-APP 与系列衍生 APP 应用共享 cc.winboll.studio 命名空间资源。(WinBoll 项目已提供) | ||||
| ☁ WinBoll-APP 用户客户端信息存储在命名空间为 WinBoll APP MySQLLite 应用的 winbollappclient 数据库中。 | ||||
| ☁ WinBoll-APP MySQLLite 应用的 winbollappclient 数据库中,  | ||||
|    WinBoll 用户客户端信息设定为: | ||||
| ### ☁ 心忧虑 ☁ WinBoLL-APP 应用前置需求 | ||||
| ☁ WinBoLL-APP WinBoLL 项目根目录设定为手机的 /sdcard/WinBoLLStudio/Sources 目录。(需要用户手动建立文件夹) | ||||
| ☁ WinBoLL-APP 具有手机 /sdcard/WinBoLL 目录的存储权限。(需要手机操作系统授权) | ||||
| ☁ WinBoLL-APP WinBoLL 项目仓库源码存储路径为 /sdcard/WinBoLLStudio/Sources/APP.git(需要用户手动建立文件夹) | ||||
| ☁ WinBoLL-APP 项目 APK 编译输出目录为 /sdcard/WinBoLLStudio/APKs/ | ||||
| ☁ WinBoLL-APP 应用签名验证可定制化。(WinBoLL 项目已提供) | ||||
| ☁ WinBoLL-APP 与系列衍生 APP 应用共享 cc.winboll.studio 命名空间资源。(WinBoLL 项目已提供) | ||||
| ☁ WinBoLL-APP 用户客户端信息存储在命名空间为 WinBoLL APP MySQLLite 应用的 winbollappclient 数据库中。 | ||||
| ☁ WinBoLL-APP MySQLLite 应用的 winbollappclient 数据库中,  | ||||
|    WinBoLL 用户客户端信息设定为: | ||||
|    <用户名, 唯一存储令牌Token>。 | ||||
|  | ||||
| ### ☁ 云游四方 ☁ ### | ||||
| ### ☁ 呔! ☁ WinBoll-APP 应用需求规划 | ||||
| ☁ WinBoll-APP 提供手机目录 /sdcard/WinBollStudio/Sources 的 WinBoll 项目源码管理功能。  | ||||
| ### ☁ 呔! ☁ WinBoLL-APP 应用需求规划 | ||||
| ☁ 如要使用 WinBoLL Android 项目的 Gradle 编译功能,则需要设置以下两个文件夹。 | ||||
| ☁ 1. 则需要建立数据存储目录 /sdcard/WinBoLLStudio/APKs。 | ||||
|    WinBoLL 项目源码编译出来的安装包会拷贝一份到 /sdcard/WinBoLLStudio/APKs 目录下。  | ||||
| ☁ 2. 则需要建立数据存储目录 /sdcard/AppProjects。 | ||||
|    WinBoLL 项目源码编译出来的安装包会拷贝一份并命名 "app.apk" 的安装文件为到 /sdcard/AppProjects 目录下。  | ||||
|  | ||||
| ### ☁ 吁! ☁ WinBoll-APP 共享计划前景 | ||||
| ☁ WinBoll-APP 将会实现 https://winboll.cc/api 访问功能。 | ||||
| ☁ WinBoll-APP 将会实现手机端 Android 应用的开发与管理功能。 | ||||
|  | ||||
| ## ☁ WinBoll ☁ WinBoll 主机忧虑 | ||||
| ☁ WinBoll 将会提供 gitea.winboll.cc 域名用户注册登录功能。 | ||||
| ☁ WinBoll 将会提供 WinBoll-APP 及其衍生应用的 Gitea 仓库管理服务。 | ||||
| ☁ WinBoll 将会提供 winboll.cc 域名 WinBoll 项目组注册登录功能。 | ||||
| ### ☁ 吁! ☁ WinBoLL-APP 共享计划前景 | ||||
| ☁ WinBoLL-APP 将会实现 https://winboll.cc/api 访问功能。 | ||||
| ☁ WinBoLL-APP 将会实现手机端 Android 应用的开发与管理功能。 | ||||
|  | ||||
| ## ☁ WinBoLL ☁ WinBoLL 主机忧虑 | ||||
| ☁ WinBoLL 将会提供 gitea.winboll.cc 域名用户注册登录功能。 | ||||
| ☁ WinBoLL 将会提供 WinBoLL-APP 及其衍生应用的 Gitea 仓库管理服务。 | ||||
| ☁ WinBoLL 将会提供 winboll.cc 域名 WinBoLL 项目组注册登录功能。 | ||||
|  | ||||
| # 本项目要实际运用需要注意以下几个步骤: | ||||
| # 在项目根目录下: | ||||
| ## 1. 项目模块编译环境设置(必须),settings.gradle-demo 要复制为 settings.gradle,并取消相应项目模块的注释。 | ||||
| ## 2. 项目 Android SDK 编译环境设置(可选),local.properties-demo 要复制为 local.properties,并按需要设置 Android SDK 目录。 | ||||
| ## 3. 类库型模块编译环境设置(可选),winboll.properties-demo 要复制为 winboll.properties,并按需要设置 WinBoll Maven 库登录用户信息。 | ||||
| ## 3. 类库型模块编译环境设置(可选),winboll.properties-demo 要复制为 winboll.properties,并按需要设置 WinBoLL Maven 库登录用户信息。 | ||||
|  | ||||
|  | ||||
| # ☆类库型项目编译方法 | ||||
| @@ -116,12 +121,12 @@ | ||||
| 设置属性 libraryProject=<类库项目模块文件夹名称> | ||||
| ### 再编译测试项目 | ||||
| $ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称> | ||||
| #### 测试项目编译后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBollStudio/APKs/<项目根目录名称>/tag/" 文件夹。 | ||||
| #### 测试项目编译后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。 | ||||
| ### 最后编译类库项目 | ||||
| $ bash .winboll/bashPublishLIBAddTag.sh <类库项目模块文件夹名称> | ||||
| #### 类库模块编译命令执行后,编译器会发布到 WinBoll Nexus Maven 库:Maven 库地址可以参阅根项目目录配置 build.gradle 文件。 | ||||
| #### 类库模块编译命令执行后,编译器会发布到 WinBoLL Nexus Maven 库:Maven 库地址可以参阅根项目目录配置 build.gradle 文件。 | ||||
|               | ||||
| # ☆应用型项目编译方法 | ||||
| ## 直接调用以下命令编译应用型项目 | ||||
| $ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称> | ||||
| #### 应用模块编译命令执行后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBollStudio/APKs/<项目根目录名称>/tag/" 文件夹。 | ||||
| #### 应用模块编译命令执行后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。 | ||||
|   | ||||
| @@ -24,12 +24,12 @@ android { | ||||
|     defaultConfig { | ||||
|         applicationId "cc.winboll.studio.aes" | ||||
|         minSdkVersion 24 | ||||
|         targetSdkVersion 29 | ||||
|         targetSdkVersion 30 | ||||
|         versionCode 1 | ||||
|         // versionName 更新后需要手动设置  | ||||
|         // 项目模块目录的 build.gradle 文件的 stageCount=0 | ||||
|         // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" | ||||
|         versionName "15.2"  | ||||
|         versionName "15.6"  | ||||
|         if(true) { | ||||
|             versionName = genVersionName("${versionName}") | ||||
|         } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Wed Apr 02 20:09:04 HKT 2025 | ||||
| stageCount=6 | ||||
| #Tue Apr 29 15:14:41 HKT 2025 | ||||
| stageCount=1 | ||||
| libraryProject=libaes | ||||
| baseVersion=15.2 | ||||
| publishVersion=15.2.5 | ||||
| baseVersion=15.6 | ||||
| publishVersion=15.6.0 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.2.6 | ||||
| baseBetaVersion=15.6.1 | ||||
|   | ||||
| @@ -10,14 +10,13 @@ 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.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 WinBoLLActivity implements IWinBoLLActivity { | ||||
|  | ||||
|     public static final String TAG = "AboutActivity"; | ||||
|  | ||||
| @@ -64,13 +63,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() { | ||||
| @@ -86,6 +85,8 @@ public class AboutActivity extends WinBollActivity implements IWinBollActivity { | ||||
|         appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=AES"); | ||||
|         appInfo.setAppAPKName("AES"); | ||||
|         appInfo.setAppAPKFolderName("AES"); | ||||
|         //appInfo.setIsAddDebugTools(false); | ||||
|         appInfo.setIsAddDebugTools(BuildConfig.DEBUG); | ||||
|         return new AboutView(mContext, appInfo); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ package cc.winboll.studio.aes; | ||||
| import android.view.Gravity; | ||||
| import cc.winboll.studio.libappbase.GlobalApplication; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import com.hjq.toast.style.WhiteToastStyle; | ||||
|  | ||||
|  | ||||
| public class App extends GlobalApplication { | ||||
| @@ -21,8 +22,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); | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -25,12 +25,12 @@ import cc.winboll.studio.libaes.unittests.TestAToolbarActivity; | ||||
| import cc.winboll.studio.libaes.unittests.TestDrawerFragmentActivity; | ||||
| import cc.winboll.studio.libaes.unittests.TestViewPageFragment; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBollActivity; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; | ||||
| import com.a4455jkjh.colorpicker.ColorPickerDialog; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.util.ArrayList; | ||||
|  | ||||
| public class MainActivity extends DrawerFragmentActivity implements IWinBollActivity { | ||||
| public class MainActivity extends DrawerFragmentActivity implements IWinBoLLActivity { | ||||
|  | ||||
|  | ||||
|     public static final String TAG = "MainActivity"; | ||||
| @@ -123,7 +123,7 @@ public class MainActivity extends DrawerFragmentActivity implements IWinBollActi | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         int nItemId = item.getItemId(); | ||||
| //        if (item.getItemId() == R.id.item_log) { | ||||
| //            WinBollActivityManager.getInstance(this).startWinBollActivity(getApplicationContext(), LogActivity.class); | ||||
| //            WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(getApplicationContext(), LogActivity.class); | ||||
| //        } else  | ||||
|         if (nItemId == R.id.item_atoast) { | ||||
|             Toast.makeText(getApplication(), "item_testatoast", Toast.LENGTH_SHORT).show(); | ||||
|   | ||||
| @@ -1,21 +1,21 @@ | ||||
| package cc.winboll.studio.app; | ||||
| package cc.winboll.studio.aes; | ||||
| 
 | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/04/01 12:55:32 | ||||
|  * @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; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; | ||||
| import android.view.MenuItem; | ||||
| 
 | ||||
| public class WinBollActivity extends AppCompatActivity implements IWinBollActivity { | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/30 00:34:02 | ||||
|  * @Describe WinBoLL 活动窗口通用基类 | ||||
|  */ | ||||
| public class WinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity { | ||||
| 
 | ||||
|     public static final String TAG = "WinBollActivity"; | ||||
|     public static final String TAG = "WinBoLLActivity"; | ||||
| 
 | ||||
|     protected volatile AESThemeBean.ThemeType mThemeType; | ||||
| 
 | ||||
| @@ -32,16 +32,10 @@ public class WinBollActivity extends AppCompatActivity implements IWinBollActivi | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         mThemeType = getThemeType(); | ||||
|         setThemeStyle(); | ||||
|         super.onCreate(savedInstanceState); | ||||
|         App.getWinBollActivityManager().add(this); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         App.getWinBollActivityManager().registeRemove(this); | ||||
|     } | ||||
|      | ||||
|     AESThemeBean.ThemeType getThemeType() { | ||||
|         /*SharedPreferences sharedPreferences = getSharedPreferences( | ||||
|          SHAREDPREFERENCES_NAME, MODE_PRIVATE); | ||||
| @@ -57,7 +51,7 @@ public class WinBollActivity extends AppCompatActivity implements IWinBollActivi | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         if (item.getItemId() == android.R.id.home) { | ||||
|         if(item.getItemId() == android.R.id.home) { | ||||
|             finish(); | ||||
|             return true; | ||||
|         } | ||||
| @@ -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.1' | ||||
|     api 'cc.winboll.studio:libapputils:15.2.2' | ||||
|     api 'cc.winboll.studio:libappbase:15.2.2' | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Wed Apr 02 12:22:30 GMT 2025 | ||||
| #Thu Apr 03 03:17:18 GMT 2025 | ||||
| stageCount=0 | ||||
| libraryProject= | ||||
| baseVersion=15.0 | ||||
| publishVersion=15.0.0 | ||||
| buildCount=20 | ||||
| buildCount=21 | ||||
| baseBetaVersion=15.0.1 | ||||
|   | ||||
| @@ -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.5' | ||||
|     api 'cc.winboll.studio:libapputils:15.2.1' | ||||
|     api 'cc.winboll.studio:libaes:15.2.6' | ||||
|     api 'cc.winboll.studio:libapputils:15.2.2' | ||||
|     api 'cc.winboll.studio:libappbase:15.2.2' | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Wed Apr 02 12:13:40 GMT 2025 | ||||
| #Thu Apr 03 03:15:55 GMT 2025 | ||||
| stageCount=0 | ||||
| libraryProject= | ||||
| baseVersion=15.0 | ||||
| publishVersion=15.0.0 | ||||
| buildCount=16 | ||||
| buildCount=18 | ||||
| baseBetaVersion=15.0.1 | ||||
|   | ||||
| @@ -1,8 +0,0 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Tue Apr 01 13:50:28 HKT 2025 | ||||
| stageCount=2 | ||||
| libraryProject= | ||||
| baseVersion=15.0 | ||||
| publishVersion=15.0.1 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.0.2 | ||||
| @@ -1,13 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" > | ||||
|  | ||||
|     <application | ||||
|         tools:replace="android:icon" | ||||
|         android:icon="@drawable/ic_winbollbeta"> | ||||
|  | ||||
|         <!-- Put flavor specific code here --> | ||||
|     </application> | ||||
|  | ||||
| </manifest> | ||||
|  | ||||
| @@ -1,49 +0,0 @@ | ||||
| <?xml version='1.0' encoding='utf-8'?> | ||||
| <manifest | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     package="cc.winboll.studio.app"> | ||||
|  | ||||
|     <!-- BIND_AUTOFILL_SERVICE --> | ||||
|     <uses-permission android:name="android.permission.BIND_AUTOFILL_SERVICE"/> | ||||
|  | ||||
|     <application | ||||
|         android:name=".App" | ||||
|         android:allowBackup="true" | ||||
|         android:icon="@drawable/ic_winboll" | ||||
|         android:label="@string/app_name" | ||||
|         android:theme="@style/AESTheme" | ||||
|         android:supportsRtl="true"> | ||||
|  | ||||
|         <activity | ||||
|             android:name=".MainActivity" | ||||
|             android:label="@string/app_name" | ||||
|             android:launchMode="singleTask" | ||||
|             android:exported="true"> | ||||
|  | ||||
|             <intent-filter> | ||||
|  | ||||
|                 <action android:name="android.intent.action.MAIN"/> | ||||
|  | ||||
|                 <category android:name="android.intent.category.LAUNCHER"/> | ||||
|  | ||||
|             </intent-filter> | ||||
|  | ||||
|         </activity> | ||||
|  | ||||
|         <provider | ||||
|             android:name="androidx.core.content.FileProvider" | ||||
|             android:authorities="${applicationId}.fileprovider" | ||||
|             android:exported="false" | ||||
|             android:grantUriPermissions="true"> | ||||
|  | ||||
|             <meta-data | ||||
|                 android:name="android.support.FILE_PROVIDER_PATHS" | ||||
|                 android:resource="@xml/studio_provider"/> | ||||
|  | ||||
|         </provider> | ||||
|  | ||||
|         <activity android:name=".AboutActivity"/> | ||||
|  | ||||
|     </application> | ||||
|  | ||||
| </manifest> | ||||
| @@ -1,30 +0,0 @@ | ||||
| package cc.winboll.studio.app; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@QQ.COM | ||||
|  * @Date 2024/12/08 15:10:51 | ||||
|  * @Describe 全局应用类 | ||||
|  */ | ||||
| import android.view.Gravity; | ||||
| import cc.winboll.studio.libappbase.GlobalApplication; | ||||
| import cc.winboll.studio.libappbase.winboll.WinBollActivityManager; | ||||
| import com.hjq.toast.ToastUtils; | ||||
|  | ||||
| public class App extends GlobalApplication { | ||||
|  | ||||
|     public static final String TAG = "App"; | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate() { | ||||
|         super.onCreate(); | ||||
|  | ||||
|         // 初始化 Toast 框架 | ||||
|         ToastUtils.init(this); | ||||
|         // 设置 Toast 布局样式 | ||||
|         //ToastUtils.setView(R.layout.toast_custom_view); | ||||
|         //ToastUtils.setStyle(new WhiteToastStyle()); | ||||
|         ToastUtils.setGravity(Gravity.BOTTOM, 0, 200); | ||||
|  | ||||
|         getWinBollActivityManager().setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Service); | ||||
|     } | ||||
| } | ||||
| @@ -1,91 +0,0 @@ | ||||
| package cc.winboll.studio.app; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuItem; | ||||
| import androidx.appcompat.widget.Toolbar; | ||||
| import cc.winboll.studio.app.R; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBollActivity; | ||||
| import cc.winboll.studio.libappbase.winboll.WinBollActivityManager; | ||||
|  | ||||
| final public class MainActivity extends WinBollActivity implements IWinBollActivity { | ||||
|  | ||||
| 	public static final String TAG = "MainActivity"; | ||||
|  | ||||
|     Toolbar mToolbar; | ||||
|  | ||||
|     @Override | ||||
|     public Activity getActivity() { | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getTag() { | ||||
|         return TAG; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         LogUtils.d(TAG, "onCreate(Bundle savedInstanceState)"); | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setContentView(R.layout.activity_main); | ||||
|  | ||||
|         mToolbar = findViewById(R.id.toolbar); | ||||
|         setSupportActionBar(mToolbar); | ||||
|         mToolbar.setSubtitle(TAG); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostCreate(Bundle savedInstanceState) { | ||||
|         super.onPostCreate(savedInstanceState); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBackPressed() { | ||||
|         exit(); | ||||
|     } | ||||
|  | ||||
|     void exit() { | ||||
|         YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){ | ||||
|  | ||||
|             @Override | ||||
|             public void onYes() { | ||||
|                 App.getWinBollActivityManager().finishAll(); | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public void onNo() { | ||||
|             } | ||||
|         }; | ||||
|         YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onCreateOptionsMenu(Menu menu) { | ||||
|         getMenuInflater().inflate(R.menu.toolbar_main, menu); | ||||
|         return super.onCreateOptionsMenu(menu); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         if (item.getItemId() == R.id.item_log) { | ||||
|             App.getWinBollActivityManager().startLogActivity(this); | ||||
|         } else if (item.getItemId() == R.id.item_about) { | ||||
|             App.getWinBollActivityManager().startWinBollActivity(this, AboutActivity.class); | ||||
|         } else if (item.getItemId() == R.id.item_exit) { | ||||
|             exit(); | ||||
|             return true; | ||||
|         } | ||||
|         return super.onOptionsItemSelected(item); | ||||
|     } | ||||
| } | ||||
| @@ -1,11 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <layer-list xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:clickable="true"> | ||||
|     <item android:drawable="@drawable/ic_launcher_background"/>  | ||||
|     <item  | ||||
|         android:left="15dp"  | ||||
|         android:top="15dp"  | ||||
|         android:right="15dp"  | ||||
|         android:bottom="15dp" | ||||
|         android:drawable="@drawable/ic_launcher_foreground"/> | ||||
| </layer-list> | ||||
| @@ -1,170 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:width="108dp" | ||||
|     android:height="108dp" | ||||
|     android:viewportWidth="108" | ||||
|     android:viewportHeight="108"> | ||||
|     <path | ||||
|         android:fillColor="@color/colorPrimary" | ||||
|         android:pathData="M0,0h108v108h-108z" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M9,0L9,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,0L19,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M29,0L29,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M39,0L39,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M49,0L49,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M59,0L59,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M69,0L69,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M79,0L79,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M89,0L89,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M99,0L99,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,9L108,9" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,19L108,19" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,29L108,29" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,39L108,39" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,49L108,49" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,59L108,59" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,69L108,69" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,79L108,79" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,89L108,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,99L108,99" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,29L89,29" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,39L89,39" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,49L89,49" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,59L89,59" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,69L89,69" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,79L89,79" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M29,19L29,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M39,19L39,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M49,19L49,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M59,19L59,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M69,19L69,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M79,19L79,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
| </vector> | ||||
| @@ -1,10 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:width="24dp" | ||||
|     android:height="24dp" | ||||
|     android:viewportHeight="24" | ||||
|     android:viewportWidth="24"> | ||||
|     <path | ||||
|         android:fillColor="#FFFFFFFF" | ||||
|         android:pathData="M16.61,15.15C16.15,15.15 15.77,14.78 15.77,14.32S16.15,13.5 16.61,13.5H16.61C17.07,13.5 17.45,13.86 17.45,14.32C17.45,14.78 17.07,15.15 16.61,15.15M7.41,15.15C6.95,15.15 6.57,14.78 6.57,14.32C6.57,13.86 6.95,13.5 7.41,13.5H7.41C7.87,13.5 8.24,13.86 8.24,14.32C8.24,14.78 7.87,15.15 7.41,15.15M16.91,10.14L18.58,7.26C18.67,7.09 18.61,6.88 18.45,6.79C18.28,6.69 18.07,6.75 18,6.92L16.29,9.83C14.95,9.22 13.5,8.9 12,8.91C10.47,8.91 9,9.24 7.73,9.82L6.04,6.91C5.95,6.74 5.74,6.68 5.57,6.78C5.4,6.87 5.35,7.08 5.44,7.25L7.1,10.13C4.25,11.69 2.29,14.58 2,18H22C21.72,14.59 19.77,11.7 16.91,10.14H16.91Z"/> | ||||
| </vector> | ||||
| @@ -1,10 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <shape xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <gradient | ||||
|         android:angle="180" | ||||
|         android:endColor="#FFFFFFFF" | ||||
|         android:startColor="#FFFFFFFF" | ||||
|         android:type="linear" /> | ||||
|  | ||||
|     <corners android:radius="10dp" /> | ||||
| </shape> | ||||
| @@ -1,29 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout | ||||
| 	xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| 	android:orientation="vertical" | ||||
| 	android:layout_width="match_parent" | ||||
| 	android:layout_height="match_parent"> | ||||
|  | ||||
|     <cc.winboll.studio.libaes.views.ASupportToolbar | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:id="@+id/toolbar"/> | ||||
|  | ||||
| 	<LinearLayout | ||||
| 		xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
| 		android:orientation="vertical" | ||||
| 		android:layout_width="match_parent" | ||||
| 		android:layout_height="0dp" | ||||
| 		android:gravity="center" | ||||
| 		android:layout_weight="1.0"> | ||||
|  | ||||
| 		<TextView | ||||
| 			android:layout_width="wrap_content" | ||||
| 			android:layout_height="wrap_content" | ||||
| 			android:text="Hello, WinBoll!"/> | ||||
|  | ||||
| 	</LinearLayout> | ||||
|  | ||||
| </LinearLayout> | ||||
|  | ||||
| @@ -1,32 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:layout_width="wrap_content" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:background="@drawable/shape_gradient" | ||||
|     android:gravity="center" | ||||
|     android:orientation="vertical" | ||||
|     android:padding="10dp"> | ||||
|      | ||||
|     <LinearLayout | ||||
|         android:orientation="horizontal" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content"> | ||||
|  | ||||
|         <ImageView | ||||
|             android:layout_width="40dp" | ||||
|             android:layout_height="40dp" | ||||
|             android:src="@drawable/ic_launcher"/> | ||||
|  | ||||
|         <TextView | ||||
|             android:id="@android:id/message" | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_margin="10dp" | ||||
|             android:textColor="#FF000000" | ||||
|             android:textSize="16sp"/> | ||||
|  | ||||
|     </LinearLayout> | ||||
|  | ||||
| </LinearLayout> | ||||
| @@ -1,10 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <!-- WinBoll 默认方案 --> | ||||
|     <color name="colorPrimary">#FF196ABC</color> | ||||
|     <color name="colorPrimaryDark">#FF002B57</color> | ||||
|     <color name="colorAccent">#FF80BFFF</color> | ||||
|     <color name="colorToastFrame">#FFA9A9A9</color> | ||||
|     <color name="colorToastShadow">#FF000000</color> | ||||
|     <color name="colorToastBackgroung">#FFFFFFFF</color> | ||||
| </resources> | ||||
| @@ -1,6 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|  | ||||
|     <string name="app_name">APP</string> | ||||
|  | ||||
| </resources> | ||||
| @@ -1,3 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
| </resources> | ||||
| @@ -1,25 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <paths> | ||||
|     <external-path | ||||
|         name="external_storage_root" | ||||
|         path="." /> | ||||
|     <files-path | ||||
|         name="files_path" | ||||
|         path="." /> | ||||
|     <cache-path | ||||
|         name="cache_path" | ||||
|         path="." /> | ||||
|     <!--/storage/emulated/0/Android/data/...--> | ||||
|     <external-files-path | ||||
|         name="external_file_path" | ||||
|         path="." /> | ||||
|     <!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的目录--> | ||||
|     <external-cache-path | ||||
|         name="external_cache_path" | ||||
|         path="." /> | ||||
|     <!--配置root-path。这样子可以读取到sd卡和一些应用分身的目录,否则微信分身保存的图片,就会导致 java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/999/tencent/MicroMsg/WeiXin/export1544062754693.jpg,在小米6的手机上微信分身有这个crash,华为没有 | ||||
|     --> | ||||
|     <root-path | ||||
|         name="root_path" | ||||
|         path="" /> | ||||
| </paths> | ||||
| @@ -25,12 +25,12 @@ android { | ||||
|     defaultConfig { | ||||
|         applicationId "cc.winboll.studio.appbase" | ||||
|         minSdkVersion 24 | ||||
|         targetSdkVersion 29 | ||||
|         targetSdkVersion 30 | ||||
|         versionCode 1 | ||||
|         // versionName 更新后需要手动设置  | ||||
|         // .winboll/winbollBuildProps.properties 文件的 stageCount=0 | ||||
|         // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" | ||||
|         versionName "15.2" | ||||
|         versionName "15.7" | ||||
|         if(true) { | ||||
|             versionName = genVersionName("${versionName}") | ||||
|         } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Sat Mar 29 11:28:02 HKT 2025 | ||||
| stageCount=3 | ||||
| #Tue Apr 29 14:47:34 HKT 2025 | ||||
| stageCount=7 | ||||
| libraryProject=libappbase | ||||
| baseVersion=15.2 | ||||
| publishVersion=15.2.2 | ||||
| baseVersion=15.7 | ||||
| publishVersion=15.7.6 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.2.3 | ||||
| baseBetaVersion=15.7.7 | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
|  | ||||
|     <application | ||||
|         android:name=".App" | ||||
|         android:icon="@drawable/ic_launcher" | ||||
|         android:icon="@drawable/ic_miapp" | ||||
|         android:label="@string/app_name" | ||||
|         android:theme="@style/MyAPPBaseTheme" | ||||
|         android:resizeableActivity="true" | ||||
|   | ||||
| @@ -4,11 +4,11 @@ import android.app.Activity; | ||||
| import android.content.ComponentName; | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
| import android.support.v7.widget.Toolbar; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.widget.CheckBox; | ||||
| import android.widget.Toolbar; | ||||
| import cc.winboll.studio.appbase.R; | ||||
| import cc.winboll.studio.appbase.activities.NewActivity; | ||||
| import cc.winboll.studio.appbase.services.MainService; | ||||
| @@ -21,10 +21,9 @@ import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.sos.SOS; | ||||
| import cc.winboll.studio.libappbase.utils.ToastUtils; | ||||
| import cc.winboll.studio.libappbase.widgets.StatusWidget; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBollActivity; | ||||
| import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; | ||||
|  | ||||
| public class MainActivity extends WinBollActivityBase implements IWinBollActivity { | ||||
| public class MainActivity extends WinBoLLActivityBase implements IWinBoLLActivity { | ||||
|  | ||||
|     public static final String TAG = "MainActivity"; | ||||
|  | ||||
| @@ -48,7 +47,7 @@ public class MainActivity extends WinBollActivityBase implements IWinBollActivit | ||||
|         setContentView(R.layout.activity_main); | ||||
|  | ||||
|         mToolbar = findViewById(R.id.toolbar); | ||||
|         setSupportActionBar(mToolbar); | ||||
|         setActionBar(mToolbar); | ||||
|  | ||||
|         CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1); | ||||
|         cbIsDebugMode.setChecked(GlobalApplication.isDebuging()); | ||||
| @@ -177,7 +176,7 @@ public class MainActivity extends WinBollActivityBase implements IWinBollActivit | ||||
|     } | ||||
|  | ||||
|     public void onTestOpenNewActivity(View view) { | ||||
|         GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, NewActivity.class); | ||||
|         GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, NewActivity.class); | ||||
|     } | ||||
|  | ||||
|      | ||||
|   | ||||
| @@ -0,0 +1,81 @@ | ||||
| package cc.winboll.studio.appbase; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/28 15:34:16 | ||||
|  * @Describe 应用活动窗口基类 | ||||
|  */ | ||||
| import android.app.Activity; | ||||
| import android.os.Bundle; | ||||
| import android.os.PersistableBundle; | ||||
| import android.view.MenuItem; | ||||
| import cc.winboll.studio.appbase.App; | ||||
| import cc.winboll.studio.appbase.R; | ||||
| import cc.winboll.studio.libappbase.GlobalApplication; | ||||
| import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; | ||||
| import cc.winboll.studio.libappbase.winboll.WinBoLLActivityManager; | ||||
|  | ||||
| public class WinBoLLActivityBase extends Activity implements IWinBoLLActivity { | ||||
|  | ||||
|     public static final String TAG = "WinBoLLActivityBase"; | ||||
|  | ||||
|     @Override | ||||
|     public Activity getActivity() { | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getTag() { | ||||
|         return TAG; | ||||
|     } | ||||
|  | ||||
|     WinBoLLActivityManager getWinBoLLActivityManager() { | ||||
|         return WinBoLLActivityManager.getInstance(GlobalApplication.getInstance()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         getWinBoLLActivityManager().add(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) { | ||||
|         super.onPostCreate(savedInstanceState, persistentState); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) { | ||||
|             GlobalApplication.getWinBoLLActivityManager().startLogActivity(this); | ||||
|             return true; | ||||
|         } else if(item.getItemId() == cc.winboll.studio.appbase.R.id.item_minimal) { | ||||
|             //moveTaskToBack(true); | ||||
|             exit(); | ||||
|         } | ||||
|         // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 | ||||
|         return super.onOptionsItemSelected(item); | ||||
|     } | ||||
|  | ||||
|     void exit() { | ||||
|         YesNoAlertDialog.show(this, "Exit " + getString(R.string.app_name), "Close all activity and exit?", new YesNoAlertDialog.OnDialogResultListener(){ | ||||
|  | ||||
|                 @Override | ||||
|                 public void onYes() { | ||||
|                     App.getWinBoLLActivityManager().finishAll(); | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onNo() { | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         getWinBoLLActivityManager().registeRemove(this); | ||||
|     } | ||||
| } | ||||
| @@ -7,16 +7,16 @@ package cc.winboll.studio.appbase.activities; | ||||
|  */ | ||||
| import android.app.Activity; | ||||
| import android.os.Bundle; | ||||
| import android.support.v7.widget.Toolbar; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.widget.Toolbar; | ||||
| import cc.winboll.studio.appbase.R; | ||||
| import cc.winboll.studio.appbase.WinBollActivityBase; | ||||
| import cc.winboll.studio.appbase.WinBoLLActivityBase; | ||||
| import cc.winboll.studio.libappbase.GlobalApplication; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBollActivity; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; | ||||
|  | ||||
| public class New2Activity extends WinBollActivityBase implements IWinBollActivity { | ||||
| public class New2Activity extends WinBoLLActivityBase implements IWinBoLLActivity { | ||||
|  | ||||
|     public static final String TAG = "New2Activity"; | ||||
|  | ||||
| @@ -41,7 +41,7 @@ public class New2Activity extends WinBollActivityBase implements IWinBollActivit | ||||
| //        mLogView = findViewById(R.id.logview); | ||||
| //        mLogView.start(); | ||||
|         mToolbar = findViewById(R.id.toolbar); | ||||
|         setSupportActionBar(mToolbar); | ||||
|         setActionBar(mToolbar); | ||||
|          | ||||
|     } | ||||
|  | ||||
| @@ -52,15 +52,15 @@ public class New2Activity extends WinBollActivityBase implements IWinBollActivit | ||||
|     } | ||||
|  | ||||
|     public void onCloseThisActivity(View view) { | ||||
|         GlobalApplication.getWinBollActivityManager().finish(this); | ||||
|         GlobalApplication.getWinBoLLActivityManager().finish(this); | ||||
|     } | ||||
|  | ||||
|     public void onCloseAllActivity(View view) { | ||||
|         GlobalApplication.getWinBollActivityManager().finishAll(); | ||||
|         GlobalApplication.getWinBoLLActivityManager().finishAll(); | ||||
|     } | ||||
|  | ||||
|     public void onNewActivity(View view) { | ||||
|         GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, NewActivity.class); | ||||
|         GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, NewActivity.class); | ||||
|     } | ||||
|      | ||||
|  | ||||
| @@ -74,7 +74,7 @@ public class New2Activity extends WinBollActivityBase implements IWinBollActivit | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) { | ||||
|             GlobalApplication.getWinBollActivityManager().startLogActivity(this); | ||||
|             GlobalApplication.getWinBoLLActivityManager().startLogActivity(this); | ||||
|             return true; | ||||
|         } | ||||
|         // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 | ||||
|   | ||||
| @@ -6,16 +6,16 @@ package cc.winboll.studio.appbase.activities; | ||||
|  */ | ||||
| import android.app.Activity; | ||||
| import android.os.Bundle; | ||||
| import android.support.v7.widget.Toolbar; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.widget.Toolbar; | ||||
| import cc.winboll.studio.appbase.R; | ||||
| import cc.winboll.studio.appbase.WinBollActivityBase; | ||||
| import cc.winboll.studio.appbase.WinBoLLActivityBase; | ||||
| import cc.winboll.studio.libappbase.GlobalApplication; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBollActivity; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; | ||||
|  | ||||
| public class NewActivity extends WinBollActivityBase implements IWinBollActivity { | ||||
| public class NewActivity extends WinBoLLActivityBase implements IWinBoLLActivity { | ||||
|  | ||||
|     public static final String TAG = "NewActivity"; | ||||
|  | ||||
| @@ -39,7 +39,7 @@ public class NewActivity extends WinBollActivityBase implements IWinBollActivity | ||||
| //        mLogView = findViewById(R.id.logview); | ||||
| //        mLogView.start(); | ||||
|         mToolbar = findViewById(R.id.toolbar); | ||||
|         setSupportActionBar(mToolbar); | ||||
|         setActionBar(mToolbar); | ||||
|          | ||||
|     } | ||||
|  | ||||
| @@ -50,15 +50,15 @@ public class NewActivity extends WinBollActivityBase implements IWinBollActivity | ||||
|     } | ||||
|  | ||||
|     public void onCloseThisActivity(View view) { | ||||
|         GlobalApplication.getWinBollActivityManager().finish(this); | ||||
|         GlobalApplication.getWinBoLLActivityManager().finish(this); | ||||
|     } | ||||
|  | ||||
|     public void onCloseAllActivity(View view) { | ||||
|         GlobalApplication.getWinBollActivityManager().finishAll(); | ||||
|         GlobalApplication.getWinBoLLActivityManager().finishAll(); | ||||
|     } | ||||
|  | ||||
|     public void onNew2Activity(View view) { | ||||
|         GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, New2Activity.class); | ||||
|         GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, New2Activity.class); | ||||
|     } | ||||
|      | ||||
|  | ||||
| @@ -72,7 +72,7 @@ public class NewActivity extends WinBollActivityBase implements IWinBollActivity | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) { | ||||
|             GlobalApplication.getWinBollActivityManager().startLogActivity(this); | ||||
|             GlobalApplication.getWinBoLLActivityManager().startLogActivity(this); | ||||
|             return true; | ||||
|         } | ||||
|         // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 | ||||
|   | ||||
| @@ -0,0 +1,72 @@ | ||||
| package cc.winboll.studio.appbase.models; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/02/17 10:05:09 | ||||
|  * @Describe APPSOSReportBean | ||||
|  */ | ||||
| import android.util.JsonReader; | ||||
| import android.util.JsonWriter; | ||||
| import cc.winboll.studio.libappbase.BaseBean; | ||||
| import java.io.IOException; | ||||
|  | ||||
| public class WinBoLLNewsBean extends BaseBean { | ||||
|      | ||||
|     public static final String TAG = "WinBoLLNewsBean"; | ||||
|      | ||||
|     protected String message; | ||||
|      | ||||
|     public WinBoLLNewsBean() { | ||||
|         this.message = ""; | ||||
|     } | ||||
|  | ||||
|     public WinBoLLNewsBean(String message) { | ||||
|         this.message = message; | ||||
|     } | ||||
|  | ||||
|     public void setMessage(String message) { | ||||
|         this.message = message; | ||||
|     } | ||||
|  | ||||
|     public String getMessage() { | ||||
|         return message; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getName() { | ||||
|         return WinBoLLNewsBean.class.getName(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { | ||||
|         super.writeThisToJsonWriter(jsonWriter); | ||||
|         jsonWriter.name("message").value(getMessage()); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException { | ||||
|         if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { | ||||
|             if (name.equals("message")) { | ||||
|                 setMessage(jsonReader.nextString()); | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { | ||||
|         jsonReader.beginObject(); | ||||
|         while (jsonReader.hasNext()) { | ||||
|             String name = jsonReader.nextName(); | ||||
|             if (!initObjectsFromJsonReader(jsonReader, name)) { | ||||
|                 jsonReader.skipValue(); | ||||
|             } | ||||
|         } | ||||
|         // 结束 JSON 对象 | ||||
|         jsonReader.endObject(); | ||||
|         return this; | ||||
|     } | ||||
| } | ||||
| @@ -10,7 +10,7 @@ import android.content.ComponentName; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.IntentFilter; | ||||
| import cc.winboll.studio.appbase.models.WinBollNewsBean; | ||||
| import cc.winboll.studio.appbase.models.WinBoLLNewsBean; | ||||
| import cc.winboll.studio.appbase.services.MainService; | ||||
| import cc.winboll.studio.appbase.widgets.APPNewsWidget; | ||||
| import cc.winboll.studio.libappbase.AppUtils; | ||||
| @@ -18,7 +18,7 @@ import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.sos.APPModel; | ||||
| import cc.winboll.studio.libappbase.sos.SOS; | ||||
| import cc.winboll.studio.libappbase.sos.SOSObject; | ||||
| import cc.winboll.studio.libappbase.sos.WinBoll; | ||||
| import cc.winboll.studio.libappbase.sos.WinBoLL; | ||||
| import cc.winboll.studio.libappbase.utils.ToastUtils; | ||||
| import java.io.IOException; | ||||
| import java.lang.ref.WeakReference; | ||||
| @@ -42,11 +42,11 @@ public class MainReceiver extends BroadcastReceiver { | ||||
|         String szAction = intent.getAction(); | ||||
|         if (szAction.equals(ACTION_BOOT_COMPLETED)) { | ||||
|             ToastUtils.show("ACTION_BOOT_COMPLETED"); | ||||
|         } else if (szAction.equals(WinBoll.ACTION_BIND)) { | ||||
|         } else if (szAction.equals(WinBoLL.ACTION_BIND)) { | ||||
|             LogUtils.d(TAG, "ACTION_BIND"); | ||||
|             LogUtils.d(TAG, String.format("context.getPackageName() %s", context.getPackageName())); | ||||
|             LogUtils.d(TAG, String.format("intent.getAction() %s", intent.getAction())); | ||||
|             String szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL); | ||||
|             String szAPPModel = intent.getStringExtra(WinBoLL.EXTRA_APPMODEL); | ||||
|             LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel)); | ||||
|             if (szAPPModel != null && !szAPPModel.equals("")) { | ||||
|                 try { | ||||
| @@ -80,7 +80,7 @@ public class MainReceiver extends BroadcastReceiver { | ||||
|  | ||||
|                     String appName = AppUtils.getAppNameByPackageName(context, szObjectPackageName); | ||||
|                     LogUtils.d(TAG, String.format("appName %s", appName)); | ||||
|                     WinBollNewsBean appWinBollNewsBean = new WinBollNewsBean(appName); | ||||
|                     WinBoLLNewsBean appWinBoLLNewsBean = new WinBoLLNewsBean(appName); | ||||
|                     SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); | ||||
|                     String currentTime = sdf.format(new Date()); | ||||
|                     StringBuilder sbLine = new StringBuilder(); | ||||
| @@ -88,9 +88,9 @@ public class MainReceiver extends BroadcastReceiver { | ||||
|                     sbLine.append(currentTime); | ||||
|                     sbLine.append("] Power to "); | ||||
|                     sbLine.append(appName); | ||||
|                     appWinBollNewsBean.setMessage(sbLine.toString()); | ||||
|                     appWinBoLLNewsBean.setMessage(sbLine.toString()); | ||||
|  | ||||
|                     APPNewsWidget.addWinBollNewsBean(context, appWinBollNewsBean); | ||||
|                     APPNewsWidget.addWinBoLLNewsBean(context, appWinBoLLNewsBean); | ||||
|  | ||||
|                     Intent intentWidget = new Intent(context, APPNewsWidget.class); | ||||
|                     intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT); | ||||
| @@ -110,7 +110,7 @@ public class MainReceiver extends BroadcastReceiver { | ||||
|         IntentFilter filter=new IntentFilter(); | ||||
|         filter.addAction(ACTION_BOOT_COMPLETED); | ||||
|         filter.addAction(SOS.ACTION_SOS); | ||||
|         filter.addAction(WinBoll.ACTION_BIND); | ||||
|         filter.addAction(WinBoLL.ACTION_BIND); | ||||
|         //filter.addAction(Intent.ACTION_BATTERY_CHANGED); | ||||
|         service.registerReceiver(this, filter); | ||||
|     } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import android.os.Binder; | ||||
| import android.os.IBinder; | ||||
| import cc.winboll.studio.appbase.models.TestDemoBindServiceBean; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.sos.WinBoll; | ||||
| import cc.winboll.studio.libappbase.sos.WinBoLL; | ||||
| import cc.winboll.studio.appbase.App; | ||||
| import cc.winboll.studio.libappbase.sos.SOS; | ||||
|  | ||||
| @@ -156,9 +156,9 @@ public class TestDemoBindService extends Service { | ||||
|                 super.run(); | ||||
|                 LogUtils.d(TAG, "run() start"); | ||||
|                 if (App.isDebuging()) { | ||||
|                     WinBoll.bindToAPPBaseBeta(mContext, TestDemoBindService.class.getName()); | ||||
|                     WinBoLL.bindToAPPBaseBeta(mContext, TestDemoBindService.class.getName()); | ||||
|                 } else { | ||||
|                     WinBoll.bindToAPPBase(mContext, TestDemoBindService.class.getName()); | ||||
|                     WinBoLL.bindToAPPBase(mContext, TestDemoBindService.class.getName()); | ||||
|                 } | ||||
|  | ||||
|                 while (!isExit()) { | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import android.os.Binder; | ||||
| import android.os.IBinder; | ||||
| import cc.winboll.studio.appbase.models.TestDemoServiceBean; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.sos.WinBoll; | ||||
| import cc.winboll.studio.libappbase.sos.WinBoLL; | ||||
|  | ||||
| public class TestDemoService extends Service { | ||||
|  | ||||
|   | ||||
| @@ -12,12 +12,12 @@ import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.widget.RemoteViews; | ||||
| import cc.winboll.studio.appbase.R; | ||||
| import cc.winboll.studio.appbase.models.WinBollNewsBean; | ||||
| import cc.winboll.studio.appbase.models.WinBoLLNewsBean; | ||||
| import cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener; | ||||
| import cc.winboll.studio.libappbase.AppUtils; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.sos.APPModel; | ||||
| import cc.winboll.studio.libappbase.sos.WinBoll; | ||||
| import cc.winboll.studio.libappbase.sos.WinBoLL; | ||||
| import java.io.IOException; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.ArrayList; | ||||
| @@ -31,14 +31,14 @@ public class APPNewsWidget extends AppWidgetProvider { | ||||
|     public static final String ACTION_RELOAD_REPORT = APPNewsWidget.class.getName() + ".ACTION_RELOAD_REPORT"; | ||||
|  | ||||
|  | ||||
|     volatile static ArrayList<WinBollNewsBean> _WinBollNewsBeanList; | ||||
|     volatile static ArrayList<WinBoLLNewsBean> _WinBoLLNewsBeanList; | ||||
|     final static int _MAX_PAGES = 10; | ||||
|     final static int _OnePageLinesCount = 5; | ||||
|     volatile static int _CurrentPageIndex = 0; | ||||
|  | ||||
|     @Override | ||||
|     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { | ||||
|         initWinBollNewsBeanList(context); | ||||
|         initWinBoLLNewsBeanList(context); | ||||
|         for (int appWidgetId : appWidgetIds) { | ||||
|             updateAppWidget(context, appWidgetManager, appWidgetId); | ||||
|         } | ||||
| @@ -47,7 +47,7 @@ public class APPNewsWidget extends AppWidgetProvider { | ||||
|     @Override | ||||
|     public void onReceive(Context context, Intent intent) { | ||||
|         super.onReceive(context, intent); | ||||
|         initWinBollNewsBeanList(context); | ||||
|         initWinBoLLNewsBeanList(context); | ||||
|         if (intent.getAction().equals(ACTION_RELOAD_REPORT)) { | ||||
|             LogUtils.d(TAG, "ACTION_RELOAD_REPORT"); | ||||
|             AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); | ||||
| @@ -57,7 +57,7 @@ public class APPNewsWidget extends AppWidgetProvider { | ||||
|             } | ||||
|         }else if (intent.getAction().equals(ACTION_WAKEUP_SERVICE)) { | ||||
|             LogUtils.d(TAG, "ACTION_WAKEUP_SERVICE"); | ||||
|             String szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL); | ||||
|             String szAPPModel = intent.getStringExtra(WinBoLL.EXTRA_APPMODEL); | ||||
|             LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel)); | ||||
|             if (szAPPModel != null && !szAPPModel.equals("")) { | ||||
|                 try { | ||||
| @@ -71,7 +71,7 @@ public class APPNewsWidget extends AppWidgetProvider { | ||||
|                          | ||||
|                         String appName = AppUtils.getAppNameByPackageName(context, szAppPackageName); | ||||
|                         LogUtils.d(TAG, String.format("appName %s", appName)); | ||||
|                         WinBollNewsBean winBollNewsBean = new WinBollNewsBean(appName); | ||||
|                         WinBoLLNewsBean winBollNewsBean = new WinBoLLNewsBean(appName); | ||||
|                         SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); | ||||
|                         String currentTime = sdf.format(new Date()); | ||||
|                         StringBuilder sbLine = new StringBuilder(); | ||||
| @@ -81,7 +81,7 @@ public class APPNewsWidget extends AppWidgetProvider { | ||||
|                         sbLine.append(appName); | ||||
|                         winBollNewsBean.setMessage(sbLine.toString()); | ||||
|                          | ||||
|                         addWinBollNewsBean(context, winBollNewsBean); | ||||
|                         addWinBoLLNewsBean(context, winBollNewsBean); | ||||
|  | ||||
|                         AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); | ||||
|                         int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class)); | ||||
| @@ -99,24 +99,24 @@ public class APPNewsWidget extends AppWidgetProvider { | ||||
|     // | ||||
|     // 加入新报告信息 | ||||
|     // | ||||
|     public synchronized static void addWinBollNewsBean(Context context, WinBollNewsBean bean) { | ||||
|         initWinBollNewsBeanList(context); | ||||
|         _WinBollNewsBeanList.add(0, bean); | ||||
|     public synchronized static void addWinBoLLNewsBean(Context context, WinBoLLNewsBean bean) { | ||||
|         initWinBoLLNewsBeanList(context); | ||||
|         _WinBoLLNewsBeanList.add(0, bean); | ||||
|         // 控制记录总数 | ||||
|         while (_WinBollNewsBeanList.size() > _MAX_PAGES * _OnePageLinesCount) { | ||||
|             _WinBollNewsBeanList.remove(_WinBollNewsBeanList.size() - 1); | ||||
|         while (_WinBoLLNewsBeanList.size() > _MAX_PAGES * _OnePageLinesCount) { | ||||
|             _WinBoLLNewsBeanList.remove(_WinBoLLNewsBeanList.size() - 1); | ||||
|         } | ||||
|         WinBollNewsBean.saveBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class); | ||||
|         WinBoLLNewsBean.saveBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class); | ||||
|     } | ||||
|  | ||||
|     synchronized static void initWinBollNewsBeanList(Context context) { | ||||
|         if (_WinBollNewsBeanList == null) { | ||||
|             _WinBollNewsBeanList = new ArrayList<WinBollNewsBean>(); | ||||
|             WinBollNewsBean.loadBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class); | ||||
|     synchronized static void initWinBoLLNewsBeanList(Context context) { | ||||
|         if (_WinBoLLNewsBeanList == null) { | ||||
|             _WinBoLLNewsBeanList = new ArrayList<WinBoLLNewsBean>(); | ||||
|             WinBoLLNewsBean.loadBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class); | ||||
|         } | ||||
|         if (_WinBollNewsBeanList == null) { | ||||
|             _WinBollNewsBeanList = new ArrayList<WinBollNewsBean>(); | ||||
|             WinBollNewsBean.saveBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class); | ||||
|         if (_WinBoLLNewsBeanList == null) { | ||||
|             _WinBoLLNewsBeanList = new ArrayList<WinBoLLNewsBean>(); | ||||
|             WinBoLLNewsBean.saveBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -141,11 +141,11 @@ public class APPNewsWidget extends AppWidgetProvider { | ||||
|  | ||||
|     public static String getMessage() { | ||||
|         ArrayList<String> msgTemp = new ArrayList<String>(); | ||||
|         if (_WinBollNewsBeanList != null) { | ||||
|         if (_WinBoLLNewsBeanList != null) { | ||||
|             int start = _OnePageLinesCount * _CurrentPageIndex; | ||||
|             start = _WinBollNewsBeanList.size() > start ? start : _WinBollNewsBeanList.size() - 1; | ||||
|             for (int i = start, j = 0; i < _WinBollNewsBeanList.size() && j < _OnePageLinesCount && start > -1; i++, j++) { | ||||
|                 msgTemp.add(_WinBollNewsBeanList.get(i).getMessage()); | ||||
|             start = _WinBoLLNewsBeanList.size() > start ? start : _WinBoLLNewsBeanList.size() - 1; | ||||
|             for (int i = start, j = 0; i < _WinBoLLNewsBeanList.size() && j < _OnePageLinesCount && start > -1; i++, j++) { | ||||
|                 msgTemp.add(_WinBoLLNewsBeanList.get(i).getMessage()); | ||||
|             } | ||||
|             String message = String.join("\n", msgTemp); | ||||
|             return message; | ||||
| @@ -154,7 +154,7 @@ public class APPNewsWidget extends AppWidgetProvider { | ||||
|     } | ||||
|  | ||||
|     public static void prePage(Context context) { | ||||
|         if (_WinBollNewsBeanList != null) { | ||||
|         if (_WinBoLLNewsBeanList != null) { | ||||
|             if (_CurrentPageIndex > 0) { | ||||
|                 _CurrentPageIndex = _CurrentPageIndex - 1; | ||||
|             } | ||||
| @@ -165,8 +165,8 @@ public class APPNewsWidget extends AppWidgetProvider { | ||||
|     } | ||||
|  | ||||
|     public static void nextPage(Context context) { | ||||
|         if (_WinBollNewsBeanList != null) { | ||||
|             if ((_CurrentPageIndex + 1) * _OnePageLinesCount < _WinBollNewsBeanList.size()) { | ||||
|         if (_WinBoLLNewsBeanList != null) { | ||||
|             if ((_CurrentPageIndex + 1) * _OnePageLinesCount < _WinBoLLNewsBeanList.size()) { | ||||
|                 _CurrentPageIndex = _CurrentPageIndex + 1; | ||||
|             } | ||||
|             Intent intentWidget = new Intent(context, APPNewsWidget.class); | ||||
| @@ -176,11 +176,11 @@ public class APPNewsWidget extends AppWidgetProvider { | ||||
|     } | ||||
|  | ||||
|     String getPageInfo() { | ||||
|         if (_WinBollNewsBeanList == null) { | ||||
|         if (_WinBoLLNewsBeanList == null) { | ||||
|             return "0/0"; | ||||
|         } | ||||
|         int leftCount = _WinBollNewsBeanList.size() % _OnePageLinesCount; | ||||
|         int currentPageCount = _WinBollNewsBeanList.size() / _OnePageLinesCount + (leftCount == 0 ?0: 1); | ||||
|         int leftCount = _WinBoLLNewsBeanList.size() % _OnePageLinesCount; | ||||
|         int currentPageCount = _WinBoLLNewsBeanList.size() / _OnePageLinesCount + (leftCount == 0 ?0: 1); | ||||
|         return String.format("%d/%d", _CurrentPageIndex + 1, currentPageCount); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| 	android:layout_width="match_parent" | ||||
| 	android:layout_height="match_parent"> | ||||
|  | ||||
| 	<android.support.v7.widget.Toolbar | ||||
| 	<android.widget.Toolbar | ||||
| 		android:layout_width="match_parent" | ||||
| 		android:layout_height="wrap_content" | ||||
| 		android:id="@+id/toolbar"/> | ||||
| @@ -32,7 +32,7 @@ | ||||
| 				<TextView | ||||
| 					android:layout_width="wrap_content" | ||||
| 					android:layout_height="wrap_content" | ||||
| 					android:text="Hello, WinBoll!"/> | ||||
| 					android:text="Hello, WinBoLL!"/> | ||||
|  | ||||
| 				<TextView | ||||
| 					android:layout_width="wrap_content" | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| 	android:layout_width="match_parent" | ||||
| 	android:layout_height="match_parent"> | ||||
|  | ||||
|     <android.support.v7.widget.Toolbar | ||||
|     <android.widget.Toolbar | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
| 		android:id="@+id/toolbar"/> | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| 	android:layout_width="match_parent" | ||||
| 	android:layout_height="match_parent"> | ||||
|  | ||||
|     <android.support.v7.widget.Toolbar | ||||
|     <android.widget.Toolbar | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
| 		android:id="@+id/toolbar"/> | ||||
|   | ||||
| @@ -18,7 +18,7 @@ | ||||
| 			android:layout_height="wrap_content" | ||||
| 			android:id="@+id/tv_title" | ||||
| 			android:layout_weight="1.0" | ||||
| 			android:text="WinBollNews" | ||||
| 			android:text="WinBoLLNews" | ||||
| 			android:textStyle="bold" | ||||
| 			android:textSize="16sp"/> | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <string name="app_name">AppBase</string> | ||||
|     <string name="tileservice_name">WinBoll</string> | ||||
|     <string name="tileservice_name">WinBoLL</string> | ||||
| </resources> | ||||
|   | ||||
| @@ -24,12 +24,12 @@ android { | ||||
|     defaultConfig { | ||||
|         applicationId "cc.winboll.studio.apputils" | ||||
|         minSdkVersion 26 | ||||
|         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}") | ||||
|         } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Sat Mar 29 12:14:55 HKT 2025 | ||||
| stageCount=2 | ||||
| #Tue Apr 29 15:04:17 HKT 2025 | ||||
| stageCount=5 | ||||
| libraryProject=libapputils | ||||
| baseVersion=15.2 | ||||
| publishVersion=15.2.1 | ||||
| baseVersion=15.3 | ||||
| publishVersion=15.3.4 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.2.2 | ||||
| baseBetaVersion=15.3.5 | ||||
|   | ||||
| @@ -15,12 +15,12 @@ import android.view.MenuItem; | ||||
| import android.widget.Toolbar; | ||||
| import cc.winboll.studio.apputils.R; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBollActivity; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; | ||||
| import cc.winboll.studio.libapputils.views.SimpleWebView; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
|  | ||||
| public class AssetsHtmlActivity extends WinBollActivityBase implements IWinBollActivity { | ||||
| public class AssetsHtmlActivity extends WinBoLLActivityBase implements IWinBoLLActivity { | ||||
|  | ||||
|     @Override | ||||
|     public Activity getActivity() { | ||||
| @@ -57,7 +57,7 @@ public class AssetsHtmlActivity extends WinBollActivityBase implements IWinBollA | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
| //        if (item.getItemId() == android.R.id.home) { | ||||
| //            WinBollActivityManager.getInstance(this).finish(this); | ||||
| //            WinBoLLActivityManager.getInstance(this).finish(this); | ||||
| //        } | ||||
|         return super.onOptionsItemSelected(item); | ||||
|     } | ||||
|   | ||||
| @@ -60,9 +60,9 @@ final public class MainActivity extends Activity { | ||||
|         //if (prosessIntents(getIntent())) return; | ||||
|         // 以下正常创建主窗口 | ||||
|  | ||||
| //        // 设置 WinBoll 应用 UI 类型 | ||||
| //        WinBollApplication.setWinBollUI_TYPE(WinBollApplication.WinBollUI_TYPE.Aplication); | ||||
| //        //ToastUtils.show("WinBollUI_TYPE " + WinBollApplication.getWinBollUI_TYPE()); | ||||
| //        // 设置 WinBoLL 应用 UI 类型 | ||||
| //        WinBoLLApplication.setWinBoLLUI_TYPE(WinBoLLApplication.WinBoLLUI_TYPE.Aplication); | ||||
| //        //ToastUtils.show("WinBoLLUI_TYPE " + WinBoLLApplication.getWinBoLLUI_TYPE()); | ||||
| //        LogUtils.d(TAG, "BuildConfig.DEBUG : " + Boolean.toString(BuildConfig.DEBUG)); | ||||
|     } | ||||
|  | ||||
| @@ -77,7 +77,7 @@ final public class MainActivity extends Activity { | ||||
|                 if (intent.getAction() != null) { | ||||
| //                    if (intent.getAction().equals(cc.winboll.studio.libapputils.intent.action.DEBUGVIEW)) { | ||||
| //                        App.setIsDebug(true); | ||||
| //                        //ToastUtils.show!("WinBollApplication.setIsDebug(true) by action : " + intent.getAction()); | ||||
| //                        //ToastUtils.show!("WinBoLLApplication.setIsDebug(true) by action : " + intent.getAction()); | ||||
| // | ||||
| //                    } | ||||
|                 } | ||||
| @@ -130,12 +130,12 @@ final public class MainActivity extends Activity { | ||||
|     protected void onPostCreate(Bundle savedInstanceState) { | ||||
|         super.onPostCreate(savedInstanceState); | ||||
|         // 缓存当前 activity | ||||
|         //WinBollActivityManager.getInstance(this).add(this); | ||||
|         //WinBoLLActivityManager.getInstance(this).add(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         //WinBollActivityManager.getInstance(this).registeRemove(this); | ||||
|         //WinBoLLActivityManager.getInstance(this).registeRemove(this); | ||||
|         super.onDestroy(); | ||||
|     } | ||||
|  | ||||
| @@ -150,8 +150,8 @@ final public class MainActivity extends Activity { | ||||
| //        intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); | ||||
| //        startActivity(intent); | ||||
|  | ||||
|         //WinBollActivityManager.getInstance().printAvtivityListInfo(); | ||||
|         //WinBollActivityManager.getInstance(this).startWinBollActivity(this, LogActivity.class); | ||||
|         //WinBoLLActivityManager.getInstance().printAvtivityListInfo(); | ||||
|         //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, LogActivity.class); | ||||
|     } | ||||
|  | ||||
|     // | ||||
| @@ -165,7 +165,7 @@ final public class MainActivity extends Activity { | ||||
|  | ||||
| //        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); | ||||
| @@ -183,8 +183,8 @@ final public class MainActivity extends Activity { | ||||
| //                } | ||||
| // | ||||
| //                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()); | ||||
| //                // 函数处理异常返回失败 | ||||
| @@ -201,8 +201,8 @@ final public class MainActivity extends Activity { | ||||
|     public boolean onCreateOptionsMenu(Menu menu) { | ||||
|         //ToastUtils.show("onCreateOptionsMenu"); | ||||
|         getMenuInflater().inflate(R.menu.toolbar_main, menu); | ||||
| //        if (isAddWinBollToolBar()) { | ||||
| //            //ToastUtils.show("mIWinBoll.isAddWinBollToolBar()"); | ||||
| //        if (isAddWinBoLLToolBar()) { | ||||
| //            //ToastUtils.show("mIWinBoLL.isAddWinBoLLToolBar()"); | ||||
| //            getMenuInflater().inflate(R.menu.toolbar_winboll_shared_main, menu); | ||||
| //        } | ||||
|         if (App.isDebuging()) { | ||||
| @@ -220,7 +220,7 @@ final public class MainActivity extends Activity { | ||||
|         } else if (item.getItemId() == R.id.item_teststringtoqrcodeview) { | ||||
|             Intent intent = new Intent(this, TestStringToQRCodeViewActivity.class); | ||||
|             startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY); | ||||
|             //WinBollActivityManager.getInstance(this).startWinBollActivity(this, TestStringToQrCodeViewActivity.class); | ||||
|             //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, TestStringToQrCodeViewActivity.class); | ||||
|         } else if (item.getItemId() == R.id.item_testqrcodedecodeactivity) { | ||||
|             Intent intent = new Intent(this, QRCodeDecodeActivity.class); | ||||
|             startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY); | ||||
| @@ -230,13 +230,13 @@ final public class MainActivity extends Activity { | ||||
|             } | ||||
|             return true; | ||||
|         } else if (item.getItemId() == R.id.item_log) { | ||||
|             //WinBollActivityManager.getInstance(this).startWinBollActivity(this, LogActivity.class); | ||||
|             //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, LogActivity.class); | ||||
|             return true; | ||||
|         } else if (item.getItemId() == R.id.item_exitdebug) { | ||||
|             //AboutView.setApp2NormalMode(this); | ||||
|             return true; | ||||
|         } else if (item.getItemId() == android.R.id.home) { | ||||
|             //WinBollActivityManager.getInstance(this).finish(this); | ||||
|             //WinBoLLActivityManager.getInstance(this).finish(this); | ||||
|             return true; | ||||
|         } | ||||
|         return super.onOptionsItemSelected(item); | ||||
| @@ -247,7 +247,7 @@ final public class MainActivity extends Activity { | ||||
| // | ||||
| //            @Override | ||||
| //            public void onYes() { | ||||
| //                //WinBollActivityManager.getInstance(getApplicationContext()).finishAll(); | ||||
| //                //WinBoLLActivityManager.getInstance(getApplicationContext()).finishAll(); | ||||
| //            } | ||||
| // | ||||
| //            @Override | ||||
| @@ -260,10 +260,10 @@ final public class MainActivity extends Activity { | ||||
|  | ||||
|     @Override | ||||
|     public void onBackPressed() { | ||||
| //        if (WinBollActivityManager.getInstance(getApplicationContext()).isFirstIWinBollActivity(this)) { | ||||
| //        if (WinBoLLActivityManager.getInstance(getApplicationContext()).isFirstIWinBoLLActivity(this)) { | ||||
| //            exit(); | ||||
| //        } else { | ||||
| //            WinBollActivityManager.getInstance(this).finish(this); | ||||
| //            WinBoLLActivityManager.getInstance(this).finish(this); | ||||
| //            super.onBackPressed(); | ||||
| //        } | ||||
|     } | ||||
| @@ -275,7 +275,7 @@ final public class MainActivity extends Activity { | ||||
|         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); | ||||
|         intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); | ||||
|         startActivity(intent); | ||||
|         //WinBollActivityManager.getInstance(this).startWinBollActivity(this, intent, AssetsHtmlActivity.class); | ||||
|         //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, intent, AssetsHtmlActivity.class); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -8,14 +8,13 @@ package cc.winboll.studio.apputils; | ||||
| import android.app.Activity; | ||||
| import android.os.Bundle; | ||||
| import android.os.PersistableBundle; | ||||
| import android.support.v7.app.AppCompatActivity; | ||||
| import cc.winboll.studio.libappbase.GlobalApplication; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBollActivity; | ||||
| import cc.winboll.studio.libappbase.winboll.WinBollActivityManager; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; | ||||
| import cc.winboll.studio.libappbase.winboll.WinBoLLActivityManager; | ||||
| 
 | ||||
| public class WinBollActivityBase extends AppCompatActivity implements IWinBollActivity { | ||||
| public class WinBoLLActivityBase extends Activity implements IWinBoLLActivity { | ||||
| 
 | ||||
|     public static final String TAG = "WinBollActivityBase"; | ||||
|     public static final String TAG = "WinBoLLActivityBase"; | ||||
| 
 | ||||
|     @Override | ||||
|     public Activity getActivity() { | ||||
| @@ -27,14 +26,14 @@ public class WinBollActivityBase extends AppCompatActivity implements IWinBollAc | ||||
|         return TAG; | ||||
|     } | ||||
| 
 | ||||
|     WinBollActivityManager getWinBollActivityManager() { | ||||
|         return WinBollActivityManager.getInstance(GlobalApplication.getInstance()); | ||||
|     WinBoLLActivityManager getWinBoLLActivityManager() { | ||||
|         return WinBoLLActivityManager.getInstance(GlobalApplication.getInstance()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         getWinBollActivityManager().add(this); | ||||
|         getWinBoLLActivityManager().add(this); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
| @@ -47,6 +46,6 @@ public class WinBollActivityBase extends AppCompatActivity implements IWinBollAc | ||||
|     @Override | ||||
|     protected void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         getWinBollActivityManager().registeRemove(this); | ||||
|         getWinBoLLActivityManager().registeRemove(this); | ||||
|     } | ||||
| } | ||||
| @@ -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> | ||||
|   | ||||
							
								
								
									
										17
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								build.gradle
									
									
									
									
									
								
							| @@ -1,10 +1,15 @@ | ||||
| // Top-level build file where you can add configuration options common to all sub-projects/modules. | ||||
| buildscript { | ||||
|     repositories { | ||||
|         // 本地 Maven 仓库(默认路径为 ~/.m2/repository) | ||||
|         mavenLocal() | ||||
|         // 或自定义本地仓库路径 | ||||
|         maven { url "file:///sdcard/.m2/repository" } | ||||
|          | ||||
|         // Nexus Maven 库地址 | ||||
|         // "WinBoll Release" | ||||
|         // "WinBoLL Release" | ||||
|         maven { url "https://nexus.winboll.cc/repository/maven-public/" } | ||||
|         // "WinBoll Snapshot" | ||||
|         // "WinBoLL Snapshot" | ||||
|         maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" } | ||||
|          | ||||
|         maven { url 'https://maven.aliyun.com/repository/public/' }  | ||||
| @@ -15,7 +20,7 @@ buildscript { | ||||
|         maven { url "https://jitpack.io" } | ||||
|         mavenCentral() | ||||
|         google() | ||||
|         mavenLocal() | ||||
|         //mavenLocal() | ||||
|     } | ||||
|     dependencies { | ||||
|         classpath 'com.android.tools.build:gradle:7.2.1' // 对应 compileSdkVersion 32 | ||||
| @@ -27,9 +32,9 @@ buildscript { | ||||
| allprojects { | ||||
|     repositories { | ||||
|         // Nexus Maven 库地址 | ||||
|         // "WinBoll Release" | ||||
|         // "WinBoLL Release" | ||||
|         maven { url "https://nexus.winboll.cc/repository/maven-public/" } | ||||
|         // "WinBoll Snapshot" | ||||
|         // "WinBoLL Snapshot" | ||||
|         maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" } | ||||
|          | ||||
|         maven { url 'https://maven.aliyun.com/repository/public/' }  | ||||
| @@ -51,7 +56,7 @@ allprojects { | ||||
|         bashCommitAppPublishBuildFlagInfoFilePath = ".winboll/bashCommitAppPublishBuildFlagInfo.sh" | ||||
|          | ||||
|         winbollFilePath = "winboll.properties" | ||||
|         keyPropsFilePath = "winboll-x/current.keystore" | ||||
|         keyPropsFilePath = "current.keystore" | ||||
|         // 定义 lint 输出文件 | ||||
|         lintXmlReportFilePath = "build/reports/lint-results.xml" | ||||
|         lintHTMLReportFilePath = "build/reports/lint-results.html" | ||||
|   | ||||
| @@ -19,17 +19,17 @@ def genVersionName(def versionName){ | ||||
|  | ||||
| android { | ||||
|     compileSdkVersion 32 | ||||
|     buildToolsVersion "33.0.3" | ||||
|     buildToolsVersion "32.0.0" | ||||
|  | ||||
|     defaultConfig { | ||||
|         applicationId "cc.winboll.studio.contacts" | ||||
|         minSdkVersion 21 | ||||
|         targetSdkVersion 30 | ||||
|         minSdkVersion 24 | ||||
|         targetSdkVersion 29 | ||||
|         versionCode 1 | ||||
|         // versionName 更新后需要手动设置  | ||||
|         // 项目模块目录的 build.gradle 文件的 stageCount=0 | ||||
|         // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" | ||||
|         versionName "1.0"  | ||||
|         versionName "15.2"  | ||||
|         if(true) { | ||||
|             versionName = genVersionName("${versionName}") | ||||
|         } | ||||
| @@ -41,31 +41,48 @@ android { | ||||
|             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     compileOptions { | ||||
|         sourceCompatibility JavaVersion.VERSION_17 | ||||
|         targetCompatibility JavaVersion.VERSION_17 | ||||
|     } | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|         // 二维码使用的类库 | ||||
|     api fileTree(dir: 'libs', include: ['*.jar']) | ||||
|      | ||||
|     // 权限请求框架:https://github.com/getActivity/XXPermissions | ||||
|     api 'com.github.getActivity:XXPermissions:18.63' | ||||
|     // 下拉控件 | ||||
|     api 'com.baoyz.pullrefreshlayout:library:1.2.0' | ||||
|     // 拼音搜索 | ||||
|     // https://mvnrepository.com/artifact/com.github.open-android/pinyin4j | ||||
|     api 'com.github.open-android:pinyin4j:2.5.0' | ||||
|     // SSH | ||||
|     api 'com.jcraft:jsch:0.1.55' | ||||
|     // Html 解析 | ||||
|     api 'org.jsoup:jsoup:1.13.1' | ||||
|     // 二维码类库 | ||||
|     api 'com.google.zxing:core:3.4.1' | ||||
|     api 'com.journeyapps:zxing-android-embedded:3.6.0' | ||||
|      | ||||
|     // 应用介绍页类库 | ||||
|     api 'io.github.medyo:android-about-page:2.0.0' | ||||
|     // 吐司类库 | ||||
|     api 'com.github.getActivity:ToastUtils:10.5' | ||||
|     api 'com.jcraft:jsch:0.1.55' | ||||
|     api 'org.jsoup:jsoup:1.13.1' | ||||
|     // 网络连接类库 | ||||
|     api 'com.squareup.okhttp3:okhttp:4.4.1' | ||||
|      | ||||
|     // AndroidX 类库 | ||||
|     /*implementation 'androidx.appcompat:appcompat:1.1.0' | ||||
|     implementation 'androidx.viewpager:viewpager:1.0.0' | ||||
|     implementation 'androidx.vectordrawable:vectordrawable:1.1.0' | ||||
|     implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0' | ||||
|     implementation 'androidx.fragment:fragment:1.1.0' | ||||
|     implementation 'com.google.android.material:material:1.4.0' | ||||
|     */ | ||||
|     api 'androidx.appcompat:appcompat:1.1.0' | ||||
|     api 'androidx.viewpager:viewpager:1.0.0' | ||||
|     api 'androidx.fragment:fragment:1.1.0' | ||||
|     api 'com.google.android.material:material:1.4.0' | ||||
|     //api '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 'cc.winboll.studio:libapputils:9.3.2' | ||||
|     api 'cc.winboll.studio:libappbase:1.5.6' | ||||
|      | ||||
|     api fileTree(dir: 'libs', include: ['*.jar']) | ||||
|     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 | ||||
| #Tue Feb 25 00:17:29 HKT 2025 | ||||
| stageCount=2 | ||||
| #Sun Apr 13 02:46:09 HKT 2025 | ||||
| stageCount=8 | ||||
| libraryProject= | ||||
| baseVersion=1.0 | ||||
| publishVersion=1.0.1 | ||||
| baseVersion=15.2 | ||||
| publishVersion=15.2.7 | ||||
| buildCount=0 | ||||
| baseBetaVersion=1.0.2 | ||||
| baseBetaVersion=15.2.8 | ||||
|   | ||||
| @@ -29,9 +29,13 @@ | ||||
|  | ||||
|     <!-- 更改您的音频设置 --> | ||||
|     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> | ||||
|          | ||||
|     <uses-permission android:name="android.permission.WRITE_SETTINGS" /> | ||||
|          | ||||
|  | ||||
|     <!-- 读取通话记录 --> | ||||
|     <uses-permission android:name="android.permission.READ_CALL_LOG"/> | ||||
|  | ||||
|     <!-- 录音 --> | ||||
|     <uses-permission android:name="android.permission.RECORD_AUDIO"/> | ||||
|  | ||||
|     <application | ||||
|         android:name=".App" | ||||
|         android:allowBackup="true" | ||||
| @@ -65,7 +69,7 @@ | ||||
|  | ||||
|         <activity | ||||
|             android:name=".phonecallui.PhoneCallActivity" | ||||
|             android:launchMode="singleInstance" | ||||
|             android:launchMode="singleTask" | ||||
|             android:exported="true"> | ||||
|  | ||||
|             <intent-filter> | ||||
| @@ -182,6 +186,10 @@ | ||||
|  | ||||
|         </provider> | ||||
|  | ||||
|         <activity android:name="cc.winboll.studio.contacts.activities.UnitTestActivity"/> | ||||
|  | ||||
|         <activity android:name="cc.winboll.studio.contacts.activities.AboutActivity"/> | ||||
|  | ||||
|     </application> | ||||
|  | ||||
| </manifest> | ||||
| </manifest> | ||||
| @@ -5,9 +5,10 @@ package cc.winboll.studio.contacts; | ||||
|  * @Date 2024/12/08 15:10:51 | ||||
|  * @Describe 全局应用类 | ||||
|  */ | ||||
| import android.view.Gravity; | ||||
| import cc.winboll.studio.libappbase.GlobalApplication; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libapputils.app.WinBollActivityManager; | ||||
| import cc.winboll.studio.libappbase.winboll.WinBollActivityManager; | ||||
| import com.hjq.toast.ToastUtils; | ||||
|  | ||||
| public class App extends GlobalApplication { | ||||
|  | ||||
| @@ -17,12 +18,20 @@ public class App extends GlobalApplication { | ||||
|     public void onCreate() { | ||||
|         // 必须在调用基类前设置应用调试标志, | ||||
|         // 这样可以预先设置日志与数据的存储根目录。 | ||||
|         setIsDebuging(this, BuildConfig.DEBUG); | ||||
|         //setIsDebuging(BuildConfig.DEBUG); | ||||
|         super.onCreate(); | ||||
|         // 设置 WinBoll 应用 UI 类型 | ||||
|         WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Aplication); | ||||
|          | ||||
|         LogUtils.d(TAG, "onCreate"); | ||||
|         //LogUtils.d(TAG, "onCreate"); | ||||
|          | ||||
|         // 初始化 Toast 框架 | ||||
|         ToastUtils.init(this); | ||||
|         // 设置 Toast 布局样式 | ||||
|         //ToastUtils.setView(R.layout.toast_custom_view); | ||||
|         //ToastUtils.setStyle(new WhiteToastStyle()); | ||||
|         ToastUtils.setGravity(Gravity.BOTTOM, 0, 200); | ||||
|          | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,52 +1,44 @@ | ||||
| package cc.winboll.studio.contacts; | ||||
|  | ||||
| import android.Manifest; | ||||
| import android.annotation.SuppressLint; | ||||
| import android.app.Activity; | ||||
| import android.app.ActivityManager; | ||||
| import android.app.role.RoleManager; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.net.Uri; | ||||
| import android.os.Build; | ||||
| import android.os.Bundle; | ||||
| import android.provider.Settings; | ||||
| import android.telecom.TelecomManager; | ||||
| import android.view.LayoutInflater; | ||||
| import android.telephony.PhoneStateListener; | ||||
| import android.telephony.TelephonyManager; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.view.WindowManager; | ||||
| import android.widget.CheckBox; | ||||
| import android.widget.CompoundButton; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.LinearLayout; | ||||
| import android.widget.Switch; | ||||
| import android.widget.Toast; | ||||
| import androidx.appcompat.app.AlertDialog; | ||||
| import androidx.appcompat.app.AppCompatActivity; | ||||
| import androidx.appcompat.widget.Toolbar; | ||||
| import androidx.core.content.ContextCompat; | ||||
| import androidx.core.app.ActivityCompat; | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.fragment.app.FragmentManager; | ||||
| import androidx.fragment.app.FragmentPagerAdapter; | ||||
| import androidx.viewpager.widget.ViewPager; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.contacts.activities.CallActivity; | ||||
| import cc.winboll.studio.contacts.adapters.MyPagerAdapter; | ||||
| import cc.winboll.studio.contacts.activities.SettingsActivity; | ||||
| import cc.winboll.studio.contacts.beans.MainServiceBean; | ||||
| 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.libapputils.app.IWinBollActivity; | ||||
| import cc.winboll.studio.libapputils.app.WinBollActivityManager; | ||||
| import cc.winboll.studio.libapputils.bean.APPInfo; | ||||
| import cc.winboll.studio.libapputils.view.YesNoAlertDialog; | ||||
| import cc.winboll.studio.contacts.listenphonecall.CallListenerService; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBollActivity; | ||||
| import com.google.android.material.tabs.TabLayout; | ||||
| import java.lang.reflect.Field; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import android.content.DialogInterface; | ||||
| import cc.winboll.studio.contacts.activities.SettingsActivity; | ||||
|  | ||||
| final public class MainActivity extends AppCompatActivity implements IWinBollActivity, ViewPager.OnPageChangeListener, View.OnClickListener { | ||||
|  | ||||
| @@ -57,11 +49,13 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct | ||||
|  | ||||
|     public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoll.ACTION_SOS"; | ||||
|  | ||||
|     static MainActivity _MainActivity; | ||||
|     LogView mLogView; | ||||
|     Toolbar mToolbar; | ||||
|     CheckBox cbMainService; | ||||
|     MainServiceBean mMainServiceBean; | ||||
|     ViewPager viewPager; | ||||
|     private TabLayout tabLayout; | ||||
|     private ViewPager viewPager; | ||||
|     private List<View> views; //用来存放放进ViewPager里面的布局 | ||||
|     //实例化存储imageView(导航原点)的集合 | ||||
|     ImageView[] imageViews; | ||||
| @@ -70,15 +64,20 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct | ||||
|     LinearLayout linearLayout;//下标所在在LinearLayout布局里 | ||||
|     int currentPoint = 0;//当前被选中中页面的下标 | ||||
|  | ||||
|     private TelephonyManager telephonyManager; | ||||
|     private MyPhoneStateListener phoneStateListener; | ||||
|     List<Fragment> fragmentList; | ||||
|     List<String> tabTitleList; | ||||
|  | ||||
|     private static final int DIALER_REQUEST_CODE = 1; | ||||
|  | ||||
|     @Override | ||||
|     public AppCompatActivity getActivity() { | ||||
|     public Activity getActivity() { | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public APPInfo getAppInfo() { | ||||
| //    @Override | ||||
| //    public APPInfo getAppInfo() { | ||||
| //        String szBranchName = "contacts"; | ||||
| // | ||||
| //        APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo(); | ||||
| @@ -93,8 +92,8 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct | ||||
| //        appInfo.setAppAPKName("Contacts"); | ||||
| //        appInfo.setAppAPKFolderName("Contacts"); | ||||
| //        return appInfo; | ||||
|         return null; | ||||
|     } | ||||
| //        return null; | ||||
| //    } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
| @@ -102,28 +101,51 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct | ||||
|         //if (prosessIntents(getIntent())) return; | ||||
|         // 以下正常创建主窗口 | ||||
|         super.onCreate(savedInstanceState); | ||||
|         _MainActivity = this; | ||||
|         setContentView(R.layout.activity_main); | ||||
|  | ||||
|         // 初始化工具栏 | ||||
|         mToolbar = findViewById(R.id.activitymainToolbar1); | ||||
|         setSupportActionBar(mToolbar); | ||||
|         if (isEnableDisplayHomeAsUp()) { | ||||
|             // 显示后退按钮 | ||||
|             getSupportActionBar().setDisplayHomeAsUpEnabled(true); | ||||
|         } | ||||
| //        if (isEnableDisplayHomeAsUp()) { | ||||
| //            // 显示后退按钮 | ||||
| //            getSupportActionBar().setDisplayHomeAsUpEnabled(true); | ||||
| //        } | ||||
|         getSupportActionBar().setSubtitle(getTag()); | ||||
|  | ||||
|         initData(); | ||||
|         initView(); | ||||
|         //initPoint();//调用初始化导航原点的方法 | ||||
|         viewPager.addOnPageChangeListener(this);//滑动事件 | ||||
|         tabLayout = findViewById(R.id.tabLayout); | ||||
|         viewPager = findViewById(R.id.viewPager); | ||||
|  | ||||
|         ViewPager viewPager = findViewById(R.id.activitymainViewPager1); | ||||
|         MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager()); | ||||
|         viewPager.setAdapter(pagerAdapter); | ||||
|         TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1); | ||||
|         // 创建Fragment列表和标题列表 | ||||
|         fragmentList = new ArrayList<>(); | ||||
|         tabTitleList = new ArrayList<>(); | ||||
|         fragmentList.add(CallLogFragment.newInstance(0)); | ||||
|         fragmentList.add(ContactsFragment.newInstance(1)); | ||||
|         fragmentList.add(LogFragment.newInstance(2)); | ||||
|         tabTitleList.add("通话记录"); | ||||
|         tabTitleList.add("联系人"); | ||||
|         tabTitleList.add("应用日志"); | ||||
|  | ||||
|         // 设置ViewPager的适配器 | ||||
|         MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), fragmentList, tabTitleList); | ||||
|         viewPager.setAdapter(adapter); | ||||
|  | ||||
|         // 关联TabLayout和ViewPager | ||||
|         tabLayout.setupWithViewPager(viewPager); | ||||
|  | ||||
|  | ||||
|  | ||||
| //        initData(); | ||||
| //        initView(); | ||||
| //        //initPoint();//调用初始化导航原点的方法 | ||||
| //        viewPager.addOnPageChangeListener(this);//滑动事件 | ||||
|  | ||||
|         //ViewPager viewPager = findViewById(R.id.activitymainViewPager1); | ||||
|         //MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager()); | ||||
|         //viewPager.setAdapter(pagerAdapter); | ||||
|         //TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1); | ||||
|         //tabLayout.setupWithViewPager(viewPager); | ||||
|  | ||||
| //        mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class); | ||||
| //        if (mMainServiceBean == null) { | ||||
| //            mMainServiceBean = new MainServiceBean(); | ||||
| @@ -140,36 +162,86 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct | ||||
| //                    } | ||||
| //                } | ||||
| //            }); | ||||
|         MainService.startMainService(MainActivity.this); | ||||
|  | ||||
|         MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class); | ||||
|         if (mMainServiceBean == null) { | ||||
|             mMainServiceBean = new MainServiceBean(); | ||||
|             MainServiceBean.saveBean(this, mMainServiceBean); | ||||
|         } | ||||
|         if (mMainServiceBean.isEnable()) { | ||||
|             MainService.startMainService(this); | ||||
|         } | ||||
|  | ||||
|         // 初始化TelephonyManager和PhoneStateListener | ||||
|         telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); | ||||
|         phoneStateListener = new MyPhoneStateListener(); | ||||
|         telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     // ViewPager的适配器 | ||||
|     private class MyPagerAdapter extends FragmentPagerAdapter { | ||||
|  | ||||
|         private List<Fragment> fragmentList; | ||||
|         private List<String> tabTitleList; | ||||
|  | ||||
|         public MyPagerAdapter(FragmentManager fm, List<Fragment> fragmentList, List<String> tabTitleList) { | ||||
|             super(fm); | ||||
|             this.fragmentList = fragmentList; | ||||
|             this.tabTitleList = tabTitleList; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public Fragment getItem(int position) { | ||||
|             return fragmentList.get(position); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int getCount() { | ||||
|             return fragmentList.size(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public CharSequence getPageTitle(int position) { | ||||
|             return tabTitleList.get(position); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static void dialPhoneNumber(String phoneNumber) { | ||||
|         Intent intent = new Intent(Intent.ACTION_DIAL); | ||||
|         intent.setData(android.net.Uri.parse("tel:" + phoneNumber)); | ||||
|         if (ActivityCompat.checkSelfPermission(_MainActivity, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { | ||||
|             return; | ||||
|         } | ||||
|         _MainActivity.startActivity(intent); | ||||
|     } | ||||
|  | ||||
|     //初始化view,即显示的图片 | ||||
|     void initView() { | ||||
|         viewPager = findViewById(R.id.activitymainViewPager1); | ||||
|         pagerAdapter = new MyPagerAdapter(getSupportFragmentManager()); | ||||
|         viewPager.setAdapter(pagerAdapter); | ||||
|         //adapter = new MyPagerAdapter(views); | ||||
|         //viewPager = findViewById(R.id.activitymainViewPager1); | ||||
|         //viewPager.setAdapter(adapter); | ||||
|         //linearLayout = findViewById(R.id.activitymainLinearLayout1); | ||||
|         //initPoint();//初始化页面下方的点 | ||||
|         viewPager.setOnPageChangeListener(this); | ||||
|  | ||||
|     } | ||||
| //    void initView() { | ||||
| //        viewPager = findViewById(R.id.activitymainViewPager1); | ||||
| //        pagerAdapter = new MyPagerAdapter(getSupportFragmentManager()); | ||||
| //        viewPager.setAdapter(pagerAdapter); | ||||
| //        //adapter = new MyPagerAdapter(views); | ||||
| //        //viewPager = findViewById(R.id.activitymainViewPager1); | ||||
| //        //viewPager.setAdapter(adapter); | ||||
| //        //linearLayout = findViewById(R.id.activitymainLinearLayout1); | ||||
| //        //initPoint();//初始化页面下方的点 | ||||
| //        viewPager.setOnPageChangeListener(this); | ||||
| // | ||||
| //    } | ||||
|  | ||||
|     //初始化所要显示的布局 | ||||
|     void initData() { | ||||
|         ViewPager viewPager = findViewById(R.id.activitymainViewPager1); | ||||
|         LayoutInflater inflater = LayoutInflater.from(getActivity()); | ||||
|         View view1 = inflater.inflate(R.layout.fragment_call, viewPager, false); | ||||
|         View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false); | ||||
|         View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false); | ||||
|  | ||||
|         views = new ArrayList<>(); | ||||
|         views.add(view1); | ||||
|         views.add(view2); | ||||
|         views.add(view3); | ||||
|     } | ||||
| //    void initData() { | ||||
| //        LayoutInflater inflater = LayoutInflater.from(getActivity()); | ||||
| //        View view1 = inflater.inflate(R.layout.fragment_call_log, viewPager, false); | ||||
| //        View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false); | ||||
| //        View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false); | ||||
| // | ||||
| //        views = new ArrayList<>(); | ||||
| //        views.add(view1); | ||||
| //        views.add(view2); | ||||
| //        views.add(view3); | ||||
| //    } | ||||
|  | ||||
| //    void initPoint() { | ||||
| //        imageViews = new ImageView[5];//实例化5个图片 | ||||
| @@ -231,6 +303,23 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct | ||||
|         //setSubTitle(""); | ||||
|     } | ||||
|  | ||||
|     private class MyPhoneStateListener extends PhoneStateListener { | ||||
|         @Override | ||||
|         public void onCallStateChanged(int state, String incomingNumber) { | ||||
|             switch (state) { | ||||
|                 case TelephonyManager.CALL_STATE_IDLE: | ||||
|                     LogUtils.d(TAG, "电话已挂断"); | ||||
|                     break; | ||||
|                 case TelephonyManager.CALL_STATE_OFFHOOK: | ||||
|                     LogUtils.d(TAG, "正在通话中"); | ||||
|                     break; | ||||
|                 case TelephonyManager.CALL_STATE_RINGING: | ||||
|                     LogUtils.d(TAG, "来电: " + incomingNumber); | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onDestroy() { | ||||
|         super.onDestroy(); | ||||
| @@ -287,40 +376,25 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct | ||||
|         return TAG; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Toolbar initToolBar() { | ||||
|         return findViewById(R.id.activitymainToolbar1); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isAddWinBollToolBar() { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isEnableDisplayHomeAsUp() { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBackPressed() { | ||||
|         exit(); | ||||
|     } | ||||
|  | ||||
|     void exit() { | ||||
|         YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){ | ||||
|  | ||||
|             @Override | ||||
|             public void onYes() { | ||||
|                 WinBollActivityManager.getInstance(getApplicationContext()).finishAll(); | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public void onNo() { | ||||
|             } | ||||
|         }; | ||||
|         YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener); | ||||
|     } | ||||
| //    @Override | ||||
| //    public void onBackPressed() { | ||||
| //        exit(); | ||||
| //    } | ||||
| // | ||||
| //    void exit() { | ||||
| //        YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){ | ||||
| // | ||||
| //            @Override | ||||
| //            public void onYes() { | ||||
| //                WinBollActivityManager.getInstance(getApplicationContext()).finishAll(); | ||||
| //            } | ||||
| // | ||||
| //            @Override | ||||
| //            public void onNo() { | ||||
| //            } | ||||
| //        }; | ||||
| //        YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener); | ||||
| //    } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onCreateOptionsMenu(Menu menu) { | ||||
| @@ -331,11 +405,7 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct | ||||
|  | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         if (item.getItemId() == R.id.item_call) { | ||||
|             Intent intent = new Intent(this, CallActivity.class); | ||||
|             startActivity(intent); | ||||
|             //WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class); | ||||
|         } else if (item.getItemId() == R.id.item_settings) { | ||||
|         if (item.getItemId() == R.id.item_settings) { | ||||
|             Intent intent = new Intent(this, SettingsActivity.class); | ||||
|             startActivity(intent); | ||||
|             //WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class); | ||||
|   | ||||
| @@ -1,35 +0,0 @@ | ||||
| package cc.winboll.studio.contacts; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/02/20 21:14:52 | ||||
|  * @Describe PhoneCallManager | ||||
|  */ | ||||
|  | ||||
| import android.telecom.Call; | ||||
| import android.telecom.VideoProfile; | ||||
|  | ||||
| public class PhoneCallManager { | ||||
|      | ||||
|     public static final String TAG = "PhoneCallManager"; | ||||
|      | ||||
|     public static Call call; | ||||
|  | ||||
|     /** | ||||
|      * 接听电话 | ||||
|      */ | ||||
|     public void answer() { | ||||
|         if (call != null) { | ||||
|             call.answer(VideoProfile.STATE_AUDIO_ONLY); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 断开电话,包括来电时的拒接以及接听后的挂断 | ||||
|      */ | ||||
|     public void disconnect() { | ||||
|         if (call != null) { | ||||
|             call.disconnect(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,8 +1,8 @@ | ||||
| package cc.winboll.studio.app; | ||||
| package cc.winboll.studio.contacts.activities; | ||||
| 
 | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/24 23:52:29 | ||||
|  * @Date 2025/03/31 15:15:54 | ||||
|  * @Describe 应用介绍窗口 | ||||
|  */ | ||||
| import android.app.Activity; | ||||
| @@ -11,7 +11,7 @@ import android.os.Bundle; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.LinearLayout; | ||||
| import androidx.appcompat.widget.Toolbar; | ||||
| import cc.winboll.studio.app.R; | ||||
| 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; | ||||
| @@ -63,26 +63,29 @@ public class AboutActivity extends WinBollActivity implements IWinBollActivity { | ||||
|             ViewGroup.LayoutParams.MATCH_PARENT | ||||
|         ); | ||||
|         layout.addView(aboutView, params); | ||||
| 
 | ||||
|         GlobalApplication.getWinBollActivityManager().add(this); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         GlobalApplication.getWinBollActivityManager().registeRemove(this); | ||||
|     } | ||||
| 
 | ||||
|     public AboutView CreateAboutView() { | ||||
|         String szBranchName = "app"; | ||||
|         String szBranchName = "contacts"; | ||||
|         APPInfo appInfo = new APPInfo(); | ||||
|         appInfo.setAppName("APP"); | ||||
|         appInfo.setAppName("Contacts"); | ||||
|         appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll); | ||||
|         appInfo.setAppDescription("WinBoll APP"); | ||||
|         appInfo.setAppDescription("通讯录与拨号"); | ||||
|         appInfo.setAppGitName("APP"); | ||||
|         appInfo.setAppGitOwner("Studio"); | ||||
|         appInfo.setAppGitAPPBranch(szBranchName); | ||||
|         appInfo.setAppGitAPPSubProjectFolder(szBranchName); | ||||
|         appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=APP"); | ||||
|         appInfo.setAppAPKName("APP"); | ||||
|         appInfo.setAppAPKFolderName("APP"); | ||||
|         appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts"); | ||||
|         appInfo.setAppAPKName("Contacts"); | ||||
|         appInfo.setAppAPKFolderName("Contacts"); | ||||
|         return new AboutView(mContext, appInfo); | ||||
|     } | ||||
| } | ||||
| @@ -34,6 +34,7 @@ public class CallActivity extends AppCompatActivity { | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|          | ||||
|         super.onCreate(savedInstanceState); | ||||
|         //setContentView(R.layout.activity_main); | ||||
|         setContentView(R.layout.activity_call); | ||||
|   | ||||
| @@ -4,7 +4,6 @@ package cc.winboll.studio.contacts.activities; | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/02/21 05:37:42 | ||||
|  */ | ||||
| import android.app.NotificationManager; | ||||
| import android.content.Context; | ||||
| import android.content.DialogInterface; | ||||
| import android.content.Intent; | ||||
| @@ -15,17 +14,33 @@ import android.os.Bundle; | ||||
| import android.provider.Settings; | ||||
| import android.view.View; | ||||
| import android.view.WindowManager; | ||||
| import android.widget.EditText; | ||||
| import android.widget.SeekBar; | ||||
| import android.widget.Switch; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
| import androidx.appcompat.app.AlertDialog; | ||||
| 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.R; | ||||
| import cc.winboll.studio.contacts.adapters.PhoneConnectRuleAdapter; | ||||
| import cc.winboll.studio.contacts.beans.MainServiceBean; | ||||
| import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel; | ||||
| import cc.winboll.studio.contacts.beans.RingTongBean; | ||||
| import cc.winboll.studio.libappbase.IWinBollActivity; | ||||
| import cc.winboll.studio.libappbase.bean.APPInfo; | ||||
| import cc.winboll.studio.contacts.beans.SettingsModel; | ||||
| 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 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 { | ||||
|  | ||||
| @@ -33,11 +48,25 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv | ||||
|  | ||||
|     Toolbar mToolbar; | ||||
|     Switch swSilent; | ||||
|     SeekBar msbVolume; | ||||
|     TextView mtvVolume; | ||||
|     int mnStreamMaxVolume; | ||||
|     int mnStreamVolume; | ||||
|     Switch mswMainService; | ||||
|     static DuInfoTextView _DuInfoTextView; | ||||
|  | ||||
|     @Override | ||||
|     public APPInfo getAppInfo() { | ||||
|         return null; | ||||
|     } | ||||
|     // 云盾防御层数量 | ||||
|     EditText etDunTotalCount; | ||||
|     // 防御层恢复时间间隔(秒钟) | ||||
|     EditText etDunResumeSecondCount; | ||||
|     // 每次恢复防御层数 | ||||
|     EditText etDunResumeCount; | ||||
|     // 是否启用云盾 | ||||
|     Switch swIsEnableDun; | ||||
|  | ||||
|     private RecyclerView recyclerView; | ||||
|     private PhoneConnectRuleAdapter adapter; | ||||
|     private List<PhoneConnectRuleModel> ruleList; | ||||
|  | ||||
|     @Override | ||||
|     public AppCompatActivity getActivity() { | ||||
| @@ -49,21 +78,6 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv | ||||
|         return TAG; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Toolbar initToolBar() { | ||||
|         return findViewById(R.id.activitymainToolbar1); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isAddWinBollToolBar() { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isEnableDisplayHomeAsUp() { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
| @@ -72,11 +86,136 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv | ||||
|         // 初始化工具栏 | ||||
|         mToolbar = findViewById(R.id.activitymainToolbar1); | ||||
|         setSupportActionBar(mToolbar); | ||||
|         if (isEnableDisplayHomeAsUp()) { | ||||
|             // 显示后退按钮 | ||||
|             getSupportActionBar().setDisplayHomeAsUpEnabled(true); | ||||
|         } | ||||
|         // 显示后退按钮 | ||||
|         getSupportActionBar().setDisplayHomeAsUpEnabled(true); | ||||
|         getSupportActionBar().setSubtitle(getTag()); | ||||
|  | ||||
|         mswMainService = findViewById(R.id.sw_mainservice); | ||||
|         MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class); | ||||
|         mswMainService.setChecked(mMainServiceBean.isEnable()); | ||||
|         mswMainService.setOnClickListener(new View.OnClickListener(){ | ||||
|                 @Override | ||||
|                 public void onClick(View arg0) { | ||||
|                     LogUtils.d(TAG, "mswMainService onClick"); | ||||
|                     // TODO: Implement this method | ||||
|                     if (mswMainService.isChecked()) { | ||||
|                         //ToastUtils.show("Is Checked"); | ||||
|                         MainService.startMainServiceAndSaveStatus(SettingsActivity.this); | ||||
|                     } else { | ||||
|                         //ToastUtils.show("Not Checked"); | ||||
|                         MainService.stopMainServiceAndSaveStatus(SettingsActivity.this); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         msbVolume = findViewById(R.id.bellvolume); | ||||
|         mtvVolume = findViewById(R.id.tv_volume); | ||||
|         final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); | ||||
|  | ||||
|         // 设置SeekBar的最大值为系统铃声音量的最大刻度 | ||||
|         mnStreamMaxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_RING); | ||||
|         msbVolume.setMax(mnStreamMaxVolume); | ||||
|         // 获取当前铃声音量并设置为SeekBar的初始进度 | ||||
|         mnStreamVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING); | ||||
|         msbVolume.setProgress(mnStreamVolume); | ||||
|  | ||||
|         updateStreamVolumeTextView(); | ||||
|  | ||||
|         msbVolume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { | ||||
|                 @Override | ||||
|                 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { | ||||
|                     if (fromUser) { | ||||
|                         // 设置铃声音量 | ||||
|                         audioManager.setStreamVolume(AudioManager.STREAM_RING, progress, 0); | ||||
|                         RingTongBean bean = RingTongBean.loadBean(SettingsActivity.this, RingTongBean.class); | ||||
|                         if (bean == null) { | ||||
|                             bean = new RingTongBean(); | ||||
|                         } | ||||
|                         bean.setStreamVolume(progress); | ||||
|                         RingTongBean.saveBean(SettingsActivity.this, bean); | ||||
|                         mnStreamVolume = progress; | ||||
|                         updateStreamVolumeTextView(); | ||||
|                         //Toast.makeText(SettingsActivity.this, "音量设置为: " + progress, Toast.LENGTH_SHORT).show(); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onStartTrackingTouch(SeekBar seekBar) { | ||||
|                     // 当开始拖动SeekBar时的操作 | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onStopTrackingTouch(SeekBar seekBar) { | ||||
|                     // 当停止拖动SeekBar时的操作 | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|  | ||||
|         recyclerView = findViewById(R.id.recycler_view); | ||||
|         recyclerView.setLayoutManager(new LinearLayoutManager(this)); | ||||
|  | ||||
|         ruleList = Rules.getInstance(this).getPhoneBlacRuleBeanList(); | ||||
|  | ||||
|         adapter = new PhoneConnectRuleAdapter(this, ruleList); | ||||
|         recyclerView.setAdapter(adapter); | ||||
|  | ||||
|         // 设置参数云盾 | ||||
|         _DuInfoTextView = findViewById(R.id.tv_DunInfo); | ||||
|         etDunTotalCount = findViewById(R.id.et_DunTotalCount); | ||||
|         etDunResumeSecondCount = findViewById(R.id.et_DunResumeSecondCount); | ||||
|         etDunResumeCount = findViewById(R.id.et_DunResumeCount); | ||||
|         swIsEnableDun = findViewById(R.id.sw_IsEnableDun); | ||||
|         SettingsModel settingsModel = Rules.getInstance(this).getSettingsModel(); | ||||
|  | ||||
|         etDunTotalCount.setText(Integer.toString(settingsModel.getDunTotalCount())); | ||||
|         etDunResumeSecondCount.setText(Integer.toString(settingsModel.getDunResumeSecondCount())); | ||||
|         etDunResumeCount.setText(Integer.toString(settingsModel.getDunResumeCount())); | ||||
|         swIsEnableDun.setChecked(settingsModel.isEnableDun()); | ||||
|  | ||||
|         boolean isEnableDun = settingsModel.isEnableDun(); | ||||
|         etDunTotalCount.setEnabled(!isEnableDun); | ||||
|         etDunResumeSecondCount.setEnabled(!isEnableDun); | ||||
|         etDunResumeCount.setEnabled(!isEnableDun); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public static void notifyDunInfoUpdate() { | ||||
|         if (_DuInfoTextView != null) { | ||||
|             _DuInfoTextView.notifyInfoUpdate(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void onSW_IsEnableDun(View view) { | ||||
|         LogUtils.d(TAG, "onSW_IsEnableDun"); | ||||
|         boolean isEnableDun = swIsEnableDun.isChecked(); | ||||
|         etDunTotalCount.setEnabled(!isEnableDun); | ||||
|         etDunResumeSecondCount.setEnabled(!isEnableDun); | ||||
|         etDunResumeCount.setEnabled(!isEnableDun); | ||||
|  | ||||
|         SettingsModel settingsModel = Rules.getInstance(this).getSettingsModel(); | ||||
|         if (isEnableDun) { | ||||
|             settingsModel.setDunTotalCount(Integer.parseInt(etDunTotalCount.getText().toString())); | ||||
|             settingsModel.setDunResumeSecondCount(Integer.parseInt(etDunResumeSecondCount.getText().toString())); | ||||
|             settingsModel.setDunResumeCount(Integer.parseInt(etDunResumeCount.getText().toString())); | ||||
|         } | ||||
|         settingsModel.setIsEnableDun(isEnableDun); | ||||
|         Rules.getInstance(this).saveDun(); | ||||
|         Rules.getInstance(this).reload(); | ||||
|     } | ||||
|  | ||||
|     void updateStreamVolumeTextView() { | ||||
|         mtvVolume.setText(String.format("%d/%d", mnStreamVolume, mnStreamMaxVolume)); | ||||
|     } | ||||
|  | ||||
|     public void onUnitTest(View view) { | ||||
|         Intent intent = new Intent(this, UnitTestActivity.class); | ||||
|         startActivity(intent); | ||||
|     } | ||||
|  | ||||
|     public void onAddNewConnectionRule(View view) { | ||||
|         Rules.getInstance(this).getPhoneBlacRuleBeanList().add(new PhoneConnectRuleModel()); | ||||
|         Rules.getInstance(this).saveRules(); | ||||
|         adapter.notifyDataSetChanged(); | ||||
|     } | ||||
|  | ||||
|     public void onDefaultPhone(View view) { | ||||
| @@ -94,6 +233,38 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void onDownloadBoBullToon(View view) { | ||||
|         final TomCat tomCat = TomCat.getInstance(this); | ||||
|         new Thread(new Runnable() { | ||||
|                 @Override | ||||
|                 public void run() { | ||||
|                     if (tomCat.downloadBoBullToon()) { | ||||
|                         ToastUtils.show("BoBullToon downlaod OK!"); | ||||
|                         MainService.restartMainService(SettingsActivity.this); | ||||
|                         Rules.getInstance(SettingsActivity.this).reload(); | ||||
|                     } | ||||
|                 } | ||||
|             }).start(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public void onSearchBoBullToonPhone(View view) { | ||||
|         TomCat tomCat = TomCat.getInstance(this); | ||||
|         EditText etPhone = findViewById(R.id.activitysettingsEditText1); | ||||
|         String phone = etPhone.getText().toString().trim(); | ||||
|         if (tomCat.loadPhoneBoBullToon()) { | ||||
|             if (tomCat.isPhoneBoBullToon(phone)) { | ||||
|                 ToastUtils.show("It is a BoBullToon Phone!"); | ||||
|             } else { | ||||
|                 ToastUtils.show("Not in BoBullToon."); | ||||
|             } | ||||
|         } else { | ||||
|             ToastUtils.show("没有下载 BoBullToon。"); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void askForDrawOverlay() { | ||||
|         AlertDialog alertDialog = new AlertDialog.Builder(this) | ||||
|             .setTitle("允许显示悬浮框") | ||||
| @@ -140,4 +311,8 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     public void onAbout(View view) { | ||||
|         App.getWinBollActivityManager().startWinBollActivity(this, AboutActivity.class); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,94 @@ | ||||
| package cc.winboll.studio.contacts.activities; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.os.Bundle; | ||||
| import android.view.View; | ||||
| import android.widget.EditText; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.contacts.dun.Rules; | ||||
| import cc.winboll.studio.contacts.utils.IntUtils; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.LogView; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/02 16:07:04 | ||||
|  */ | ||||
| public class UnitTestActivity extends Activity { | ||||
|  | ||||
|     public static final String TAG = "UnitTestActivity"; | ||||
|  | ||||
|     LogView logView; | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setContentView(R.layout.activity_unittest); | ||||
|         logView = findViewById(R.id.logview); | ||||
|         logView.start(); | ||||
|     } | ||||
|  | ||||
|     public void onTestPhone(View view) { | ||||
|         // 开始测试数据 | ||||
|         EditText etPhone = findViewById(R.id.phone_et); | ||||
|         Rules rules = Rules.getInstance(this); | ||||
|         String phone = etPhone.getText().toString().trim(); | ||||
|         LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone))); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public void onTestMain(View view) { | ||||
|         LogUtils.d(TAG, "IntUtils.unittest_getIntInRange();"); | ||||
|         IntUtils.unittest_getIntInRange(); | ||||
|          | ||||
|         Rules rules = Rules.getInstance(this); | ||||
|  | ||||
|         // 如果没有规则就添加测试规则 | ||||
|         if (rules.getPhoneBlacRuleBeanList().size() == 0) { | ||||
|             // 手机号码允许 | ||||
|             // 中国手机号码正则表达式,以1开头,第二位可以是3、4、5、6、7、8、9,后面跟9位数字 | ||||
|             String regex = "^1[3-9]\\d{9}$"; | ||||
|             rules.add(regex, true, true); | ||||
|  | ||||
|             // 指定区号号码允许 | ||||
|             regex = "^0660\\d+$"; | ||||
|             rules.add(regex, true, true); | ||||
|  | ||||
|             // 指定区号号码允许 | ||||
|             regex = "^020\\d+$"; | ||||
|             rules.add(regex, true, true); | ||||
|  | ||||
|             // 添加默认拒接规则 | ||||
|             regex = ".*"; | ||||
|             rules.add(regex, false, true); | ||||
|  | ||||
|             // 保存规则到文件 | ||||
|             rules.saveRules(); | ||||
|         } | ||||
|  | ||||
|         // 开始测试数据 | ||||
|         String phone = "16769764848"; | ||||
|         LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone))); | ||||
|  | ||||
|         phone = "16856582777"; | ||||
|         LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone))); | ||||
|  | ||||
|         phone = "17519703124"; | ||||
|         LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone))); | ||||
|  | ||||
|         phone = "0205658955"; | ||||
|         LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone))); | ||||
|  | ||||
|         phone = "0108965253"; | ||||
|         LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone))); | ||||
|  | ||||
|         phone = "+8616769764848"; | ||||
|         LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone))); | ||||
|  | ||||
|         phone = "4005816769764848"; | ||||
|         LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone))); | ||||
|  | ||||
|         phone = "95566"; | ||||
|         LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone))); | ||||
|     } | ||||
| } | ||||
| @@ -1,18 +1,18 @@ | ||||
| package cc.winboll.studio.aes; | ||||
| package cc.winboll.studio.contacts.activities; | ||||
| 
 | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/31 15:16:45 | ||||
|  * @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; | ||||
| import android.view.MenuItem; | ||||
| 
 | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/30 00:34:02 | ||||
|  * @Describe WinBoll 活动窗口通用基类 | ||||
|  */ | ||||
| public class WinBollActivity extends AppCompatActivity implements IWinBollActivity { | ||||
| 
 | ||||
|     public static final String TAG = "WinBollActivity"; | ||||
| @@ -0,0 +1,93 @@ | ||||
| package cc.winboll.studio.contacts.adapters; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/02/26 13:09:32 | ||||
|  * @Describe CallLogAdapter | ||||
|  */ | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.Button; | ||||
| import android.widget.TextView; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.contacts.beans.CallLogModel; | ||||
| import cc.winboll.studio.contacts.utils.ContactUtils; | ||||
| import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
|  | ||||
| public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogViewHolder> { | ||||
|     public static final String TAG = "CallLogAdapter"; | ||||
|  | ||||
|     private List<CallLogModel> callLogList; | ||||
|     ContactUtils mContactUtils; | ||||
|     Context mContext; | ||||
|  | ||||
|     public CallLogAdapter(Context context, List<CallLogModel> callLogList) { | ||||
|         mContext = context; | ||||
|         this.mContactUtils = ContactUtils.getInstance(mContext); | ||||
|         this.callLogList = callLogList; | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public CallLogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||
|         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_call_log, parent, false); | ||||
|         return new CallLogViewHolder(view); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindViewHolder(@NonNull CallLogViewHolder holder, int position) { | ||||
|         final CallLogModel callLog = callLogList.get(position); | ||||
|         holder.phoneNumber.setText(callLog.getPhoneNumber() + "☎" + mContactUtils.getContactsName(callLog.getPhoneNumber())); | ||||
|         holder.callStatus.setText(callLog.getCallStatus()); | ||||
|         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); | ||||
|         holder.callDate.setText(dateFormat.format(callLog.getCallDate())); | ||||
|  | ||||
|         // 初始化拉动后拨号控件  | ||||
|         holder.dialAOHPCTCSeekBar.setThumb(holder.itemView.getContext().getDrawable(R.drawable.ic_call)); | ||||
|         holder.dialAOHPCTCSeekBar.setBlurRightDP(80); | ||||
|         holder.dialAOHPCTCSeekBar.setThumbOffset(0); | ||||
|         holder.dialAOHPCTCSeekBar.setOnOHPCListener( | ||||
|             new AOHPCTCSeekBar.OnOHPCListener(){ | ||||
|                 @Override | ||||
|                 public void onOHPCommit() { | ||||
|                     String phoneNumber = callLog.getPhoneNumber().replaceAll("\\s", ""); | ||||
|                     ToastUtils.show(phoneNumber); | ||||
|                     Intent intent = new Intent(Intent.ACTION_CALL); | ||||
|                     intent.setData(android.net.Uri.parse("tel:" + phoneNumber)); | ||||
|                     // 添加 FLAG_ACTIVITY_NEW_TASK 标志 | ||||
|                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|                     holder.itemView.getContext().startActivity(intent); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemCount() { | ||||
|         return callLogList.size(); | ||||
|     } | ||||
|  | ||||
|     public class CallLogViewHolder extends RecyclerView.ViewHolder { | ||||
|         TextView phoneNumber, callStatus, callDate; | ||||
|         Button dialButton; | ||||
|         AOHPCTCSeekBar dialAOHPCTCSeekBar; | ||||
|  | ||||
|         public CallLogViewHolder(@NonNull View itemView) { | ||||
|             super(itemView); | ||||
|             phoneNumber = itemView.findViewById(R.id.phone_number); | ||||
|             callStatus = itemView.findViewById(R.id.call_status); | ||||
|             callDate = itemView.findViewById(R.id.call_date); | ||||
|             dialAOHPCTCSeekBar = itemView.findViewById(R.id.aohpctcseekbar_dial); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,84 @@ | ||||
| package cc.winboll.studio.contacts.adapters; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/02/26 13:35:44 | ||||
|  * @Describe ContactAdapter | ||||
|  */ | ||||
| import android.content.Intent; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.Button; | ||||
| import android.widget.TextView; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.contacts.beans.ContactModel; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.util.List; | ||||
| import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | ||||
|  | ||||
| public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactViewHolder> { | ||||
|  | ||||
|     public static final String TAG = "ContactAdapter"; | ||||
|  | ||||
|     private static final int REQUEST_CALL_PHONE = 1; | ||||
|  | ||||
|     private List<ContactModel> contactList; | ||||
|  | ||||
|     public ContactAdapter(List<ContactModel> contactList) { | ||||
|         this.contactList = contactList; | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public ContactViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||
|         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_contact, parent, false); | ||||
|         return new ContactViewHolder(view); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindViewHolder(@NonNull ContactViewHolder holder, int position) { | ||||
|         final ContactModel contact = contactList.get(position); | ||||
|         holder.contactName.setText(contact.getName()); | ||||
|         holder.contactNumber.setText(contact.getNumber()); | ||||
|  | ||||
|         // 初始化拉动后拨号控件  | ||||
|         holder.dialAOHPCTCSeekBar.setThumb(holder.itemView.getContext().getDrawable(R.drawable.ic_call)); | ||||
|         holder.dialAOHPCTCSeekBar.setBlurRightDP(80); | ||||
|         holder.dialAOHPCTCSeekBar.setThumbOffset(0); | ||||
|         holder.dialAOHPCTCSeekBar.setOnOHPCListener( | ||||
|             new AOHPCTCSeekBar.OnOHPCListener(){ | ||||
|                 @Override | ||||
|                 public void onOHPCommit() { | ||||
|                     String phoneNumber = contact.getNumber().replaceAll("\\s", ""); | ||||
|                     ToastUtils.show(phoneNumber); | ||||
|                     Intent intent = new Intent(Intent.ACTION_CALL); | ||||
|                     intent.setData(android.net.Uri.parse("tel:" + phoneNumber)); | ||||
|                     // 添加 FLAG_ACTIVITY_NEW_TASK 标志 | ||||
|                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|                     holder.itemView.getContext().startActivity(intent); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemCount() { | ||||
|         return contactList.size(); | ||||
|     } | ||||
|  | ||||
|     public class ContactViewHolder extends RecyclerView.ViewHolder { | ||||
|         TextView contactName; | ||||
|         TextView contactNumber; | ||||
|         AOHPCTCSeekBar dialAOHPCTCSeekBar; | ||||
|          | ||||
|         public ContactViewHolder(@NonNull View itemView) { | ||||
|             super(itemView); | ||||
|             contactName = itemView.findViewById(R.id.contact_name); | ||||
|             contactNumber = itemView.findViewById(R.id.contact_number); | ||||
|             dialAOHPCTCSeekBar = itemView.findViewById(R.id.aohpctcseekbar_dial); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -1,42 +0,0 @@ | ||||
| package cc.winboll.studio.contacts.adapters; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/02/20 13:33:04 | ||||
|  * @Describe MyPagerAdapter | ||||
|  */ | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.fragment.app.FragmentManager; | ||||
| import androidx.fragment.app.FragmentPagerAdapter; | ||||
| import cc.winboll.studio.contacts.fragments.CallFragment; | ||||
| import cc.winboll.studio.contacts.fragments.ContactsFragment; | ||||
| import cc.winboll.studio.contacts.fragments.LogFragment; | ||||
|  | ||||
| public class MyPagerAdapter extends FragmentPagerAdapter { | ||||
|     public static final String TAG = "MyPagerAdapter"; | ||||
|  | ||||
|     private static final int PAGE_COUNT = 3; | ||||
|  | ||||
|     public MyPagerAdapter(@NonNull FragmentManager fm) { | ||||
|         super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Fragment getItem(int position) { | ||||
|         if(position == 1) { | ||||
|             return ContactsFragment.newInstance(position); | ||||
|         } else if(position == 2) { | ||||
|             return LogFragment.newInstance(position); | ||||
|         } else { | ||||
|             return CallFragment.newInstance(position); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getCount() { | ||||
|         return PAGE_COUNT; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,247 @@ | ||||
| package cc.winboll.studio.contacts.adapters; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/02 17:27:41 | ||||
|  * @Describe PhoneConnectRuleAdapter | ||||
|  */ | ||||
| import android.content.Context; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.Button; | ||||
| import android.widget.CheckBox; | ||||
| import android.widget.EditText; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel; | ||||
| import cc.winboll.studio.contacts.dun.Rules; | ||||
| import cc.winboll.studio.contacts.views.LeftScrollView; | ||||
| import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { | ||||
|  | ||||
|     public static final String TAG = "PhoneConnectRuleAdapter"; | ||||
|  | ||||
|     private static final int VIEW_TYPE_SIMPLE = 0; | ||||
|     private static final int VIEW_TYPE_EDIT = 1; | ||||
|  | ||||
|     private Context context; | ||||
|     private List<PhoneConnectRuleModel> ruleList; | ||||
|  | ||||
|     public PhoneConnectRuleAdapter(Context context, List<PhoneConnectRuleModel> ruleList) { | ||||
|         this.context = context; | ||||
|         this.ruleList = ruleList; | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||
|         LayoutInflater inflater = LayoutInflater.from(context); | ||||
|         if (viewType == VIEW_TYPE_SIMPLE) { | ||||
|             View view = inflater.inflate(R.layout.view_phone_connect_rule_simple, parent, false); | ||||
|             return new SimpleViewHolder(parent, view); | ||||
|         } else { | ||||
|             View view = inflater.inflate(R.layout.view_phone_connect_rule, parent, false); | ||||
|             return new EditViewHolder(parent, view); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) { | ||||
|         final PhoneConnectRuleModel model = ruleList.get(position); | ||||
|         if (holder instanceof SimpleViewHolder) { | ||||
|             final SimpleViewHolder simpleViewHolder = (SimpleViewHolder) holder; | ||||
|             String szView = model.getRuleText().trim().equals("") ?"[NULL]": model.getRuleText(); | ||||
|             simpleViewHolder.tvRuleText.setText(szView); | ||||
|             simpleViewHolder.scrollView.setOnActionListener(new LeftScrollView.OnActionListener(){ | ||||
|  | ||||
|                     @Override | ||||
|                     public void onUp() { | ||||
|                         ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList(); | ||||
|                         if (position > 0) { | ||||
|                             ToastUtils.show("onUp"); | ||||
|                             simpleViewHolder.scrollView.smoothScrollTo(0, 0); | ||||
| //                            PhoneConnectRuleModel newBean = new PhoneConnectRuleModel(); | ||||
| //                            newBean.setRuleText(list.get(position).getRuleText()); | ||||
| //                            newBean.setIsAllowConnection(list.get(position).isAllowConnection()); | ||||
| //                            newBean.setIsEnable(list.get(position).isEnable()); | ||||
| //                            newBean.setIsSimpleView(list.get(position).isSimpleView()); | ||||
|                             list.add(position - 1, list.get(position)); | ||||
|                             list.remove(position + 1); | ||||
|                             Rules.getInstance(context).saveRules(); | ||||
|                             notifyDataSetChanged(); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     public void onDown() { | ||||
|                         ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList(); | ||||
|                         if (position < list.size() - 1) { | ||||
|                             ToastUtils.show("onDown"); | ||||
|                             simpleViewHolder.scrollView.smoothScrollTo(0, 0); | ||||
| //                            PhoneConnectRuleModel newBean = new PhoneConnectRuleModel(); | ||||
| //                            newBean.setRuleText(list.get(position).getRuleText()); | ||||
| //                            newBean.setIsAllowConnection(list.get(position).isAllowConnection()); | ||||
| //                            newBean.setIsEnable(list.get(position).isEnable()); | ||||
| //                            newBean.setIsSimpleView(list.get(position).isSimpleView()); | ||||
|                             list.add(position + 2, list.get(position)); | ||||
|                             list.remove(position); | ||||
|                             Rules.getInstance(context).saveRules(); | ||||
|                             notifyDataSetChanged(); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     public void onEdit() { | ||||
|                         simpleViewHolder.scrollView.smoothScrollTo(0, 0); | ||||
|                         model.setIsSimpleView(false); | ||||
|                         notifyDataSetChanged(); | ||||
|                         //notifyItemChanged(position); | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     public void onDelete() { | ||||
|                         YesNoAlertDialog.show(simpleViewHolder.scrollView.getContext(), "删除确认", "是否删除该通话规则?", new YesNoAlertDialog.OnDialogResultListener(){ | ||||
|  | ||||
|                                 @Override | ||||
|                                 public void onYes() { | ||||
|                                     simpleViewHolder.scrollView.smoothScrollTo(0, 0); | ||||
|                                     model.setIsSimpleView(true); | ||||
|                                     ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList(); | ||||
|                                     list.remove(position); | ||||
|                                     Rules.getInstance(context).saveRules(); | ||||
|                                     notifyDataSetChanged(); | ||||
|                                     //notifyItemChanged(position); | ||||
|                                 } | ||||
|  | ||||
|                                 @Override | ||||
|                                 public void onNo() { | ||||
|                                 } | ||||
|                             }); | ||||
|  | ||||
|                     } | ||||
|                 }); | ||||
| //            simpleViewHolder.editButton.setOnClickListener(new View.OnClickListener() { | ||||
| //                    @Override | ||||
| //                    public void onClick(View v) { | ||||
| //                        model.setIsSimpleView(false); | ||||
| //                        notifyItemChanged(position); | ||||
| //                    } | ||||
| //                }); | ||||
| //            simpleViewHolder.deleteButton.setOnClickListener(new View.OnClickListener() { | ||||
| //                    @Override | ||||
| //                    public void onClick(View v) { | ||||
| //                        model.setIsSimpleView(false); | ||||
| //                        ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList(); | ||||
| //                        list.remove(position); | ||||
| //                        Rules.getInstance(context).saveRules(); | ||||
| //                        notifyItemChanged(position); | ||||
| //                    } | ||||
| //                }); | ||||
| //            // 触摸事件处理 | ||||
| //            simpleViewHolder.contentLayout.setOnTouchListener(new View.OnTouchListener() { | ||||
| //                    @Override | ||||
| //                    public boolean onTouch(View v, MotionEvent event) { | ||||
| //                        switch (event.getAction()) { | ||||
| //                            case MotionEvent.ACTION_DOWN: | ||||
| //                                simpleViewHolder.startX = event.getX(); | ||||
| //                                simpleViewHolder.isSwiping = true; | ||||
| //                                break; | ||||
| //                            case MotionEvent.ACTION_MOVE: | ||||
| //                                if (simpleViewHolder.isSwiping) { | ||||
| //                                    float deltaX = simpleViewHolder.startX - event.getX(); | ||||
| //                                    if (deltaX > 0) { // 左滑 | ||||
| //                                        float translationX = Math.max(-simpleViewHolder.actionLayout.getWidth(), -deltaX); | ||||
| //                                        simpleViewHolder.contentLayout.setTranslationX(translationX); | ||||
| //                                        simpleViewHolder.actionLayout.setVisibility(View.VISIBLE); | ||||
| //                                    } | ||||
| //                                } | ||||
| //                                break; | ||||
| //                            case MotionEvent.ACTION_UP: | ||||
| //                                simpleViewHolder.isSwiping = false; | ||||
| //                                if (simpleViewHolder.contentLayout.getTranslationX() < -simpleViewHolder.actionLayout.getWidth() / 2) { | ||||
| //                                    // 保持按钮显示 | ||||
| //                                    simpleViewHolder.contentLayout.setTranslationX(-actionLayout.getWidth()); | ||||
| //                                } else { | ||||
| //                                    // 恢复原状 | ||||
| //                                    simpleViewHolder.contentLayout.animate().translationX(0).setDuration(200).start(); | ||||
| //                                    simpleViewHolder.actionLayout.setVisibility(View.INVISIBLE); | ||||
| //                                } | ||||
| //                                break; | ||||
| //                        } | ||||
| //                        return true; | ||||
| //                    } | ||||
| //                }); | ||||
|         } else if (holder instanceof EditViewHolder) { | ||||
|             final EditViewHolder editViewHolder = (EditViewHolder) holder; | ||||
|             editViewHolder.editText.setText(model.getRuleText()); | ||||
|             editViewHolder.checkBoxAllow.setChecked(model.isAllowConnection()); | ||||
|             editViewHolder.checkBoxEnable.setChecked(model.isEnable()); | ||||
|             editViewHolder.buttonConfirm.setOnClickListener(new View.OnClickListener() { | ||||
|                     @Override | ||||
|                     public void onClick(View v) { | ||||
|                         model.setRuleText(editViewHolder.editText.getText().toString()); | ||||
|                         model.setIsAllowConnection(editViewHolder.checkBoxAllow.isChecked()); | ||||
|                         model.setIsEnable(editViewHolder.checkBoxEnable.isChecked()); | ||||
|                         model.setIsSimpleView(true); | ||||
|                         Rules.getInstance(context).saveRules(); | ||||
|                         notifyItemChanged(position); | ||||
|                         Toast.makeText(context, "保存成功", Toast.LENGTH_SHORT).show(); | ||||
|                     } | ||||
|                 }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemCount() { | ||||
|         return ruleList.size(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemViewType(int position) { | ||||
|         PhoneConnectRuleModel model = ruleList.get(position); | ||||
|         // 这里可以根据模型的状态来决定视图类型,简单起见,假设点击按钮后进入编辑视图 | ||||
|         return model.isSimpleView() ? VIEW_TYPE_SIMPLE : VIEW_TYPE_EDIT; | ||||
|     } | ||||
|  | ||||
|     static class SimpleViewHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         private final LeftScrollView scrollView; | ||||
|         private final TextView tvRuleText; | ||||
|  | ||||
|  | ||||
|         public SimpleViewHolder(@NonNull ViewGroup parent, @NonNull View itemView) { | ||||
|             super(itemView); | ||||
|             scrollView = itemView.findViewById(R.id.scrollView); | ||||
|             //tvRuleText = itemView.findViewById(R.id.ruletext_tv); | ||||
|             tvRuleText = new TextView(itemView.getContext()); | ||||
|             scrollView.setContentWidth(parent.getWidth()); | ||||
|             //scrollView.setContentWidth(600); | ||||
|             scrollView.addContentLayout(tvRuleText); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     static class EditViewHolder extends RecyclerView.ViewHolder { | ||||
|         EditText editText; | ||||
|         CheckBox checkBoxAllow; | ||||
|         CheckBox checkBoxEnable; | ||||
|         Button buttonConfirm; | ||||
|  | ||||
|         public EditViewHolder(@NonNull ViewGroup parent, @NonNull View itemView) { | ||||
|             super(itemView); | ||||
|             editText = itemView.findViewById(R.id.edit_text); | ||||
|             checkBoxAllow = itemView.findViewById(R.id.checkbox_allow); | ||||
|             checkBoxEnable = itemView.findViewById(R.id.checkbox_enable); | ||||
|             buttonConfirm = itemView.findViewById(R.id.button_confirm); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,36 @@ | ||||
| package cc.winboll.studio.contacts.beans; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/02/26 13:10:57 | ||||
|  * @Describe CallLogModel | ||||
|  */ | ||||
|  | ||||
| import java.util.Date; | ||||
|  | ||||
| public class CallLogModel { | ||||
|     public static final String TAG = "CallLogModel"; | ||||
|  | ||||
|     private String phoneNumber; | ||||
|     private String callStatus; | ||||
|     private Date callDate; | ||||
|  | ||||
|     public CallLogModel(String phoneNumber, String callStatus, Date callDate) { | ||||
|         this.phoneNumber = phoneNumber.replaceAll("\\s", ""); | ||||
|         this.callStatus = callStatus; | ||||
|         this.callDate = callDate; | ||||
|     } | ||||
|  | ||||
|     public String getPhoneNumber() { | ||||
|         return phoneNumber; | ||||
|     } | ||||
|  | ||||
|     public String getCallStatus() { | ||||
|         return callStatus; | ||||
|     } | ||||
|  | ||||
|     public Date getCallDate() { | ||||
|         return callDate; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,64 @@ | ||||
| package cc.winboll.studio.contacts.beans; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/02/26 13:37:00 | ||||
|  * @Describe ContactModel | ||||
|  */ | ||||
| import net.sourceforge.pinyin4j.PinyinHelper; | ||||
| import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; | ||||
| import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; | ||||
| import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; | ||||
| import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; | ||||
|  | ||||
| public class ContactModel { | ||||
|  | ||||
|     public static final String TAG = "ContactModel"; | ||||
|  | ||||
|     private String name; | ||||
|     private String number; | ||||
|     private String pinyin; | ||||
|  | ||||
|     public ContactModel(String name, String number) { | ||||
|         this.name = name; | ||||
|         this.number = number.replaceAll("\\s", ""); | ||||
|         this.pinyin = convertToPinyin(name); | ||||
|     } | ||||
|  | ||||
|     private String convertToPinyin(String chinese) { | ||||
|         HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); | ||||
|         format.setCaseType(HanyuPinyinCaseType.LOWERCASE); | ||||
|         format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); | ||||
|  | ||||
|         StringBuilder pinyin = new StringBuilder(); | ||||
|         for (int i = 0; i < chinese.length(); i++) { | ||||
|             char ch = chinese.charAt(i); | ||||
|             if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) { | ||||
|                 try { | ||||
|                     String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format); | ||||
|                     if (pinyinArray != null) { | ||||
|                         pinyin.append(pinyinArray[0]); | ||||
|                     } | ||||
|                 } catch (BadHanyuPinyinOutputFormatCombination e) { | ||||
|                     e.printStackTrace(); | ||||
|                 } | ||||
|             } else { | ||||
|                 pinyin.append(ch); | ||||
|             } | ||||
|         } | ||||
|         return pinyin.toString(); | ||||
|     } | ||||
|  | ||||
|     public String getName() { | ||||
|         return name; | ||||
|     } | ||||
|  | ||||
|     public String getNumber() { | ||||
|         return number; | ||||
|     } | ||||
|  | ||||
|     public String getPinyin() { | ||||
|         return pinyin; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -10,21 +10,35 @@ import android.util.JsonWriter; | ||||
| import cc.winboll.studio.libappbase.BaseBean; | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| public class PhoneBlackRuleBean extends BaseBean { | ||||
|      | ||||
|     public static final String TAG = "PhoneBlackRuleBean"; | ||||
|      | ||||
| public class PhoneConnectRuleModel extends BaseBean { | ||||
| 
 | ||||
|     public static final String TAG = "PhoneConnectRuleModel"; | ||||
| 
 | ||||
|     String ruleText; | ||||
|     boolean isAllowConnection; | ||||
|     boolean isEnable; | ||||
|      | ||||
|     public PhoneBlackRuleBean() { | ||||
|     boolean isSimpleView; | ||||
| 
 | ||||
|     public PhoneConnectRuleModel() { | ||||
|         this.ruleText = ""; | ||||
|         this.isAllowConnection = false; | ||||
|         this.isEnable = false; | ||||
|         this.isSimpleView = true; | ||||
|     } | ||||
| 
 | ||||
|     public PhoneBlackRuleBean(String ruleText, boolean isEnable) { | ||||
|     public PhoneConnectRuleModel(String ruleText, boolean isAllowConnection, boolean isEnable) { | ||||
|         this.ruleText = ruleText; | ||||
|         this.isAllowConnection = isAllowConnection; | ||||
|         this.isEnable = isEnable; | ||||
|         this.isSimpleView = true; | ||||
|     } | ||||
| 
 | ||||
|     public void setIsSimpleView(boolean isSimpleView) { | ||||
|         this.isSimpleView = isSimpleView; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isSimpleView() { | ||||
|         return isSimpleView; | ||||
|     } | ||||
| 
 | ||||
|     public void setRuleText(String ruleText) { | ||||
| @@ -35,6 +49,14 @@ public class PhoneBlackRuleBean extends BaseBean { | ||||
|         return ruleText; | ||||
|     } | ||||
| 
 | ||||
|     public void setIsAllowConnection(boolean isAllowConnection) { | ||||
|         this.isAllowConnection = isAllowConnection; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isAllowConnection() { | ||||
|         return isAllowConnection; | ||||
|     } | ||||
| 
 | ||||
|     public void setIsEnable(boolean isEnable) { | ||||
|         this.isEnable = isEnable; | ||||
|     } | ||||
| @@ -43,17 +65,19 @@ public class PhoneBlackRuleBean extends BaseBean { | ||||
|         return isEnable; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     @Override | ||||
|     public String getName() { | ||||
|         return PhoneBlackRuleBean.class.getName(); | ||||
|         return PhoneConnectRuleModel.class.getName(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { | ||||
|         super.writeThisToJsonWriter(jsonWriter); | ||||
|         jsonWriter.name("ruleText").value(getRuleText()); | ||||
|         jsonWriter.name("isAllowConnection").value(isAllowConnection()); | ||||
|         jsonWriter.name("isEnable").value(isEnable()); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
| @@ -61,6 +85,8 @@ public class PhoneBlackRuleBean extends BaseBean { | ||||
|         if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { | ||||
|             if (name.equals("ruleText")) { | ||||
|                 setRuleText(jsonReader.nextString()); | ||||
|             } else if (name.equals("isAllowConnection")) { | ||||
|                 setIsAllowConnection(jsonReader.nextBoolean()); | ||||
|             } else if (name.equals("isEnable")) { | ||||
|                 setIsEnable(jsonReader.nextBoolean()); | ||||
|             } else { | ||||
| @@ -83,6 +109,6 @@ public class PhoneBlackRuleBean extends BaseBean { | ||||
|         jsonReader.endObject(); | ||||
|         return this; | ||||
|     } | ||||
|      | ||||
|      | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @@ -12,26 +12,26 @@ import android.media.AudioManager; | ||||
| import android.util.JsonReader; | ||||
|  | ||||
| public class RingTongBean extends BaseBean { | ||||
|      | ||||
|  | ||||
|     public static final String TAG = "AudioRingTongBean"; | ||||
|      | ||||
|     // 模式 | ||||
|     int ringerMode; | ||||
|  | ||||
|     // 铃声音量 | ||||
|     int streamVolume; | ||||
|      | ||||
|     public RingTongBean() { | ||||
|         this.ringerMode = AudioManager.RINGER_MODE_NORMAL; | ||||
|         this.streamVolume = 100; | ||||
|     } | ||||
|  | ||||
|     public RingTongBean(int ringerMode) { | ||||
|         this.ringerMode = ringerMode; | ||||
|     public RingTongBean(int streamVolume) { | ||||
|         this.streamVolume = streamVolume; | ||||
|     } | ||||
|  | ||||
|     public void setRingerMode(int ringerMode) { | ||||
|         this.ringerMode = ringerMode; | ||||
|     public void setStreamVolume(int streamVolume) { | ||||
|         this.streamVolume = streamVolume; | ||||
|     } | ||||
|  | ||||
|     public int getRingerMode() { | ||||
|         return ringerMode; | ||||
|     public int getStreamVolume() { | ||||
|         return streamVolume; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -42,15 +42,15 @@ public class RingTongBean extends BaseBean { | ||||
|     @Override | ||||
|     public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { | ||||
|         super.writeThisToJsonWriter(jsonWriter); | ||||
|         jsonWriter.name("ringerMode").value(getRingerMode()); | ||||
|         jsonWriter.name("streamVolume").value(getStreamVolume()); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException { | ||||
|         if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { | ||||
|             if (name.equals("ringerMode")) { | ||||
|                 setRingerMode(jsonReader.nextInt()); | ||||
|             if (name.equals("streamVolume")) { | ||||
|                 setStreamVolume(jsonReader.nextInt()); | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|   | ||||
| @@ -0,0 +1,141 @@ | ||||
| package cc.winboll.studio.contacts.beans; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/02 19:51:40 | ||||
|  * @Describe SettingsModel | ||||
|  */ | ||||
| import android.util.JsonReader; | ||||
| import android.util.JsonWriter; | ||||
| import cc.winboll.studio.libappbase.BaseBean; | ||||
| import java.io.IOException; | ||||
| 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; | ||||
|      | ||||
|     // 云盾防御层数量 | ||||
|     int dunTotalCount; | ||||
|     // 当前云盾防御层 | ||||
|     int dunCurrentCount; | ||||
|     // 防御层恢复时间间隔(秒钟) | ||||
|     int dunResumeSecondCount; | ||||
|     // 每次恢复防御层数 | ||||
|     int dunResumeCount; | ||||
|     // 是否启用云盾 | ||||
|     boolean isEnableDun; | ||||
|  | ||||
|     public SettingsModel() { | ||||
|         this.dunTotalCount = 6; | ||||
|         this.dunCurrentCount = 6; | ||||
|         this.dunResumeSecondCount = 60; | ||||
|         this.dunResumeCount = 1; | ||||
|         this.isEnableDun = false; | ||||
|     } | ||||
|  | ||||
|     public SettingsModel(int dunTotalCount, int dunCurrentCount, int dunResumeSecondCount, int dunResumeCount, boolean isEnableDun) { | ||||
|         this.dunTotalCount = getSettingsModelRangeInt(dunTotalCount); | ||||
|         this.dunCurrentCount = getSettingsModelRangeInt(dunCurrentCount); | ||||
|         this.dunResumeSecondCount = getSettingsModelRangeInt(dunResumeSecondCount); | ||||
|         this.dunResumeCount = getSettingsModelRangeInt(dunResumeCount); | ||||
|         this.isEnableDun = isEnableDun; | ||||
|     } | ||||
|  | ||||
|     public void setDunTotalCount(int dunTotalCount) { | ||||
|         this.dunTotalCount = getSettingsModelRangeInt(dunTotalCount); | ||||
|     } | ||||
|  | ||||
|     public int getDunTotalCount() { | ||||
|         return dunTotalCount; | ||||
|     } | ||||
|  | ||||
|     public void setDunCurrentCount(int dunCurrentCount) { | ||||
|         this.dunCurrentCount = getSettingsModelRangeInt(dunCurrentCount); | ||||
|     } | ||||
|  | ||||
|     public int getDunCurrentCount() { | ||||
|         return dunCurrentCount; | ||||
|     } | ||||
|  | ||||
|     public void setDunResumeSecondCount(int dunResumeSecondCount) { | ||||
|         this.dunResumeSecondCount = getSettingsModelRangeInt(dunResumeSecondCount); | ||||
|     } | ||||
|  | ||||
|     public int getDunResumeSecondCount() { | ||||
|         return dunResumeSecondCount; | ||||
|     } | ||||
|  | ||||
|     public void setDunResumeCount(int dunResumeCount) { | ||||
|         this.dunResumeCount = getSettingsModelRangeInt(dunResumeCount); | ||||
|     } | ||||
|  | ||||
|     public int getDunResumeCount() { | ||||
|         return dunResumeCount; | ||||
|     } | ||||
|  | ||||
|     public void setIsEnableDun(boolean isEnableDun) { | ||||
|         this.isEnableDun = isEnableDun; | ||||
|     } | ||||
|  | ||||
|     public boolean isEnableDun() { | ||||
|         return isEnableDun; | ||||
|     } | ||||
|      | ||||
|     int getSettingsModelRangeInt(int origin) { | ||||
|         return IntUtils.getIntInRange(origin, MIN_INTRANGE, MAX_INTRANGE); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     public String getName() { | ||||
|         return SettingsModel.class.getName(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { | ||||
|         super.writeThisToJsonWriter(jsonWriter); | ||||
|         jsonWriter.name("dunTotalCount").value(getDunTotalCount()); | ||||
|         jsonWriter.name("dunCurrentCount").value(getDunCurrentCount()); | ||||
|         jsonWriter.name("dunResumeSecondCount").value(getDunResumeSecondCount()); | ||||
|         jsonWriter.name("dunResumeCount").value(getDunResumeCount()); | ||||
|         jsonWriter.name("isEnableDun").value(isEnableDun()); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException { | ||||
|         if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { | ||||
|             if (name.equals("dunTotalCount")) { | ||||
|                 setDunTotalCount(getSettingsModelRangeInt(jsonReader.nextInt())); | ||||
|             } else if (name.equals("dunCurrentCount")) { | ||||
|                 setDunCurrentCount(getSettingsModelRangeInt(jsonReader.nextInt())); | ||||
|             } else if (name.equals("dunResumeSecondCount")) { | ||||
|                 setDunResumeSecondCount(getSettingsModelRangeInt(jsonReader.nextInt())); | ||||
|             } else if (name.equals("dunResumeCount")) { | ||||
|                 setDunResumeCount(getSettingsModelRangeInt(jsonReader.nextInt())); | ||||
|             } else if (name.equals("isEnableDun")) { | ||||
|                 setIsEnableDun(jsonReader.nextBoolean()); | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { | ||||
|         jsonReader.beginObject(); | ||||
|         while (jsonReader.hasNext()) { | ||||
|             String name = jsonReader.nextName(); | ||||
|             if (!initObjectsFromJsonReader(jsonReader, name)) { | ||||
|                 jsonReader.skipValue(); | ||||
|             } | ||||
|         } | ||||
|         // 结束 JSON 对象 | ||||
|         jsonReader.endObject(); | ||||
|         return this; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,178 @@ | ||||
| package cc.winboll.studio.contacts.bobulltoon; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/02 13:47:48 | ||||
|  * @Describe 汤姆猫管家 :使用 BoBullToon 项目,对通讯地址进行筛选判断的好朋友。 | ||||
|  */ | ||||
| import android.content.Context; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.Paths; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.zip.ZipEntry; | ||||
| import java.util.zip.ZipInputStream; | ||||
| import okhttp3.OkHttpClient; | ||||
| import okhttp3.Request; | ||||
| import okhttp3.Response; | ||||
|  | ||||
| public class TomCat { | ||||
|  | ||||
|     public static final String TAG = "TomCat"; | ||||
|  | ||||
|     List<String> listPhoneBoBullToon = new ArrayList<String>(); | ||||
|  | ||||
|     static volatile TomCat _TomCat; | ||||
|     Context mContext; | ||||
|     TomCat(Context context) { | ||||
|         mContext = context; | ||||
|     } | ||||
|  | ||||
|     public static synchronized TomCat getInstance(Context context) { | ||||
|         if (_TomCat == null) { | ||||
|             _TomCat = new TomCat(context); | ||||
|         } | ||||
|         return _TomCat; | ||||
|     } | ||||
|  | ||||
|     void downloadAndExtractZip(String zipUrl, String destinationFolder) throws IOException { | ||||
|         OkHttpClient client = new OkHttpClient(); | ||||
|         Request request = new Request.Builder() | ||||
|             .url(zipUrl) | ||||
|             .build(); | ||||
|  | ||||
|         try { | ||||
|             Response response = client.newCall(request).execute(); | ||||
|             if (!response.isSuccessful()) { | ||||
|                 throw new IOException("Unexpected code " + response); | ||||
|             } | ||||
|  | ||||
|             // 下载 ZIP 文件到临时位置 | ||||
|             File tempZipFile = File.createTempFile("temp", ".zip"); | ||||
|             try { | ||||
|                 InputStream inputStream = response.body().byteStream(); | ||||
|                 FileOutputStream outputStream = new FileOutputStream(tempZipFile); | ||||
|                 byte[] buffer = new byte[1024]; | ||||
|                 int length; | ||||
|                 while ((length = inputStream.read(buffer)) > 0) { | ||||
|                     outputStream.write(buffer, 0, length); | ||||
|                 } | ||||
|             } catch (Exception e) { | ||||
|                 LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|             } | ||||
|  | ||||
|             // 解压 ZIP 文件到指定文件夹 | ||||
|             try { | ||||
|                 ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(tempZipFile.toPath())); | ||||
|                 ZipEntry zipEntry; | ||||
|                 while ((zipEntry = zipInputStream.getNextEntry()) != null) { | ||||
|                     Path targetFilePath = Paths.get(destinationFolder, zipEntry.getName()); | ||||
|                     if (zipEntry.isDirectory()) { | ||||
|                         Files.createDirectories(targetFilePath); | ||||
|                     } else { | ||||
|                         Files.createDirectories(targetFilePath.getParent()); | ||||
|                         try (FileOutputStream fos = new FileOutputStream(targetFilePath.toFile())) { | ||||
|                             byte[] buffer = new byte[1024]; | ||||
|                             int len; | ||||
|                             while ((len = zipInputStream.read(buffer)) > 0) { | ||||
|                                 fos.write(buffer, 0, len); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     zipInputStream.closeEntry(); | ||||
|                 } | ||||
|             } catch (Exception e) { | ||||
|                 LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|             } | ||||
|  | ||||
|             // 删除临时 ZIP 文件 | ||||
|             tempZipFile.delete(); | ||||
|             LogUtils.d(TAG, "已更新 BoBullToon 数据"); | ||||
|         } catch (Exception e) { | ||||
|             LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public boolean downloadBoBullToon() { | ||||
|         String zipUrl = "http://10.8.0.12:3000/Studio/BoBullToon/archive/main.zip"; // 替换为实际的 ZIP 文件 URL | ||||
|         String destinationFolder = getWorkingFolder().getPath(); // 替换为实际的目标文件夹路径 | ||||
|         try { | ||||
|             // 删除旧文件 | ||||
|             File fOldFolder = new File(destinationFolder); | ||||
|             if (fOldFolder.exists()) { | ||||
|                 deleteFolderRecursive(fOldFolder); | ||||
|                 fOldFolder.mkdirs(); | ||||
|                 LogUtils.d(TAG, "已清空 BoBullToon 数据"); | ||||
|             } | ||||
|  | ||||
|             // 更新新文件 | ||||
|             downloadAndExtractZip(zipUrl, destinationFolder); | ||||
|             LogUtils.d(TAG, "ZIP 文件下载并解压成功。"); | ||||
|             return true; | ||||
|         } catch (IOException e) { | ||||
|             LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // 递归删除文件夹及其内容的方法 | ||||
|     public static void deleteFolderRecursive(File file) { | ||||
|         // 判断是否为文件夹 | ||||
|         if (file.isDirectory()) { | ||||
|             // 列出文件夹中的所有文件和子文件夹 | ||||
|             File[] files = file.listFiles(); | ||||
|             if (files != null) { | ||||
|                 // 遍历并递归删除每个文件和子文件夹 | ||||
|                 for (File f : files) { | ||||
|                     deleteFolderRecursive(f); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // 删除文件或空文件夹 | ||||
|         file.delete(); | ||||
|     } | ||||
|  | ||||
|     File getWorkingFolder() { | ||||
|         return mContext.getExternalFilesDir(TAG); | ||||
|     } | ||||
|  | ||||
|     public boolean loadPhoneBoBullToon() { | ||||
|         listPhoneBoBullToon.clear(); | ||||
|         File fBoBullToon = new File(getWorkingFolder(), "bobulltoon"); | ||||
|         if (fBoBullToon.exists()) { | ||||
|             LogUtils.d(TAG, String.format("getWorkingFolder() %s", getWorkingFolder())); | ||||
|             for (File userFolder : fBoBullToon.listFiles()) { | ||||
|                 if (userFolder.isDirectory()) { | ||||
|                     for (File recordFile : userFolder.listFiles()) { | ||||
|                         listPhoneBoBullToon.add(recordFile.getName()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             for (int i = 0; i < listPhoneBoBullToon.size(); i++) { | ||||
|                 LogUtils.d(TAG, String.format("listPhoneBoBullToon add : %s", listPhoneBoBullToon.get(i))); | ||||
|             } | ||||
|             return true; | ||||
|         } else { | ||||
|             LogUtils.d(TAG, "fBoBullToon not exists。"); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public boolean isPhoneBoBullToon(String phone) { | ||||
|         for (int i = 0; i < listPhoneBoBullToon.size(); i++) { | ||||
|             LogUtils.d(TAG, String.format("isPhoneBoBullToon(...) get(i) phone : %s", listPhoneBoBullToon.get(i))); | ||||
|             if (listPhoneBoBullToon.get(i).equals(phone)) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| @@ -5,25 +5,36 @@ package cc.winboll.studio.contacts.dun; | ||||
|  * @Date 2025/02/21 06:15:10 | ||||
|  * @Describe 云盾防御规则 | ||||
|  */ | ||||
| import cc.winboll.studio.contacts.beans.PhoneBlackRuleBean; | ||||
| import java.util.ArrayList; | ||||
| import java.util.regex.Pattern; | ||||
| import android.content.Context; | ||||
| import cc.winboll.studio.contacts.activities.SettingsActivity; | ||||
| import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel; | ||||
| import cc.winboll.studio.contacts.beans.SettingsModel; | ||||
| import cc.winboll.studio.contacts.services.MainService; | ||||
| import cc.winboll.studio.contacts.utils.ContactUtils; | ||||
| import cc.winboll.studio.contacts.utils.IntUtils; | ||||
| import cc.winboll.studio.contacts.utils.RegexPPiUtils; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Timer; | ||||
| import java.util.TimerTask; | ||||
| import java.util.regex.Pattern; | ||||
|  | ||||
| public class Rules { | ||||
|  | ||||
|     public static final String TAG = "Rules"; | ||||
|  | ||||
|     ArrayList<PhoneBlackRuleBean> _PhoneBlacRuleBeanList; | ||||
|     ArrayList<PhoneConnectRuleModel> _PhoneConnectRuleModelList; | ||||
|     static volatile Rules _Rules; | ||||
|     Context mContext; | ||||
|     SettingsModel mSettingsModel; | ||||
|     Timer mDunResumeTimer; | ||||
|  | ||||
|     Rules(Context context) { | ||||
|         mContext = context; | ||||
|         _PhoneBlacRuleBeanList = new ArrayList<PhoneBlackRuleBean>(); | ||||
|         PhoneBlackRuleBean.loadBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class); | ||||
|  | ||||
|         _PhoneConnectRuleModelList = new ArrayList<PhoneConnectRuleModel>(); | ||||
|         reload(); | ||||
|     } | ||||
|  | ||||
|     public static synchronized Rules getInstance(Context context) { | ||||
|         if (_Rules == null) { | ||||
|             _Rules = new Rules(context); | ||||
| @@ -31,46 +42,168 @@ public class Rules { | ||||
|         return _Rules; | ||||
|     } | ||||
|  | ||||
|     public void reload() { | ||||
|         LogUtils.d(TAG, "reload()"); | ||||
|         loadRules(); | ||||
|         loadDun(); | ||||
|         setDunResumTimer(); | ||||
|     } | ||||
|  | ||||
|     public void setDunResumTimer() { | ||||
|         if (mDunResumeTimer != null) { | ||||
|             mDunResumeTimer.cancel(); | ||||
|         } | ||||
|  | ||||
|         // 盾牌恢复定时器 | ||||
|         mDunResumeTimer = new Timer(); | ||||
|         int ss = IntUtils.getIntInRange(mSettingsModel.getDunResumeSecondCount() * 1000, SettingsModel.MIN_INTRANGE, SettingsModel.MAX_INTRANGE); | ||||
|         mDunResumeTimer.schedule(new TimerTask() { | ||||
|                 @Override | ||||
|                 public void run() { | ||||
|                     if (mSettingsModel.getDunCurrentCount() != mSettingsModel.getDunTotalCount()) { | ||||
|                         LogUtils.d(TAG, String.format("当前防御值为%d,最大防御值为%d", mSettingsModel.getDunCurrentCount(), mSettingsModel.getDunTotalCount())); | ||||
|                         int newDunCount = mSettingsModel.getDunCurrentCount() + mSettingsModel.getDunResumeCount(); | ||||
|                         // 设置盾值在[0,DunTotalCount]之内其他值一律重置为 DunTotalCount。 | ||||
|                         newDunCount = (newDunCount > mSettingsModel.getDunTotalCount()) ?mSettingsModel.getDunTotalCount(): newDunCount; | ||||
|                         mSettingsModel.setDunCurrentCount(newDunCount); | ||||
|                         LogUtils.d(TAG, String.format("设置防御值为%d", newDunCount)); | ||||
|                         saveDun(); | ||||
|                         SettingsActivity.notifyDunInfoUpdate(); | ||||
|                     } | ||||
|                 } | ||||
|             }, 1000, ss); | ||||
|     } | ||||
|  | ||||
|     public void loadRules() { | ||||
|         _PhoneConnectRuleModelList.clear(); | ||||
|         PhoneConnectRuleModel.loadBeanList(mContext, _PhoneConnectRuleModelList, PhoneConnectRuleModel.class); | ||||
|     } | ||||
|  | ||||
|     public void saveRules() { | ||||
|         LogUtils.d(TAG, String.format("saveRules()")); | ||||
|         PhoneConnectRuleModel.saveBeanList(mContext, _PhoneConnectRuleModelList, PhoneConnectRuleModel.class); | ||||
|     } | ||||
|  | ||||
|     public void loadDun() { | ||||
|         mSettingsModel = SettingsModel.loadBean(mContext, SettingsModel.class); | ||||
|         if (mSettingsModel == null) { | ||||
|             mSettingsModel = new SettingsModel(); | ||||
|             SettingsModel.saveBean(mContext, mSettingsModel); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void saveDun() { | ||||
|         LogUtils.d(TAG, String.format("saveDun()")); | ||||
|         SettingsModel.saveBean(mContext, mSettingsModel); | ||||
|     } | ||||
|  | ||||
|     public boolean isAllowed(String phoneNumber) { | ||||
|         // 黑名单拒接 | ||||
|         for (int i = 0; i < _PhoneBlacRuleBeanList.size(); i++) { | ||||
|             if (_PhoneBlacRuleBeanList.get(i).isEnable()) { | ||||
|                 String regex = _PhoneBlacRuleBeanList.get(i).getRuleText(); | ||||
|                 if (Pattern.matches(regex, phoneNumber)) { | ||||
|                     return false; | ||||
|         // 没有启用云盾,默认允许接通任何电话 | ||||
|         if (!mSettingsModel.isEnableDun()) { | ||||
|             LogUtils.d(TAG, String.format("没有启用云盾,默认允许接通任何电话。isAllowed(...) return true")); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // | ||||
|         // 以下是云盾防御体系 | ||||
|         boolean isDefend = false; // 盾牌是否生效 | ||||
|         boolean isConnect = true; // 防御结果是否连接 | ||||
|  | ||||
|         // 如果盾值小于1,则解除防御 | ||||
|         if (!isDefend && mSettingsModel.getDunCurrentCount() < 1) { | ||||
|             // 盾层为1以下,防御解除 | ||||
|             LogUtils.d(TAG, "盾层为1以下,防御解除"); | ||||
|             isDefend = true; | ||||
|             isConnect = true; | ||||
|             LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect)); | ||||
|         } | ||||
|  | ||||
|         // 正则运算预防针 | ||||
|         if (!isDefend && !RegexPPiUtils.isPPiOK(phoneNumber)) { | ||||
|             LogUtils.d(TAG, "正则运算预防针生效。"); | ||||
|             isDefend = true; | ||||
|             isConnect = false; | ||||
|             LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect)); | ||||
|         } | ||||
|  | ||||
|         // 查询通讯录是否有该联系人 | ||||
|         boolean isPhoneInContacts = ContactUtils.getInstance(mContext).isPhoneInContacts(mContext, phoneNumber); | ||||
|         if (!isDefend) { | ||||
|             if (isPhoneInContacts) { | ||||
|                 LogUtils.d(TAG, String.format("Phone %s is in contacts.", phoneNumber)); | ||||
|                 isDefend = true; | ||||
|                 isConnect = true; | ||||
|                 LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect)); | ||||
|             } else { | ||||
|                 LogUtils.d(TAG, String.format("Phone %s is not in contacts.", phoneNumber)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 检验拨不通号码群 | ||||
|         if (!isDefend && MainService.isPhoneInBoBullToon(phoneNumber)) { | ||||
|             LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber)); | ||||
|             isDefend = true; | ||||
|             isConnect = false; | ||||
|             LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect)); | ||||
|         } | ||||
|  | ||||
|         // 正则匹配规则名单校验 | ||||
|         if (!isDefend) { | ||||
|             for (int i = 0; i < _PhoneConnectRuleModelList.size(); i++) { | ||||
|                 if (_PhoneConnectRuleModelList.get(i).isEnable()) { | ||||
|                     String regex = _PhoneConnectRuleModelList.get(i).getRuleText(); | ||||
|                     if (Pattern.matches(regex, phoneNumber)) { | ||||
|                         LogUtils.d(TAG, String.format("Phone Number [%s] is matched by rule : %s", phoneNumber, _PhoneConnectRuleModelList.get(i))); | ||||
|                         isDefend = true; | ||||
|                         isConnect = _PhoneConnectRuleModelList.get(i).isAllowConnection(); | ||||
|                         LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect)); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 手机号码允许 | ||||
|         // 中国手机号码正则表达式,以1开头,第二位可以是3、4、5、6、7、8、9,后面跟9位数字 | ||||
|         String regex = "^1[3-9]\\d{9}$"; | ||||
|         if (Pattern.matches(regex, phoneNumber)) { | ||||
|             return true; | ||||
|         } | ||||
|          | ||||
|         // 指定区号号码允许 | ||||
|         regex = "^0660\\d+$"; | ||||
|         if (Pattern.matches(regex, phoneNumber)) { | ||||
|             return true; | ||||
|         } | ||||
|          | ||||
|         // 指定区号号码允许 | ||||
|         regex = "^020\\d+$"; | ||||
|         if (Pattern.matches(regex, phoneNumber)) { | ||||
|             return true; | ||||
|         if (isConnect) { | ||||
|             // 如果防御结果为连接,则恢复防御盾牌最大值层数 | ||||
|             mSettingsModel.setDunCurrentCount(mSettingsModel.getDunTotalCount()); | ||||
|             LogUtils.d(TAG, String.format("防御结果为连接,恢复防御盾牌最大值层数 %d", mSettingsModel.getDunTotalCount())); | ||||
|             saveDun(); | ||||
|             SettingsActivity.notifyDunInfoUpdate(); | ||||
|         } else if (isDefend) { | ||||
|             // 如果触发了以上某个防御模块, | ||||
|             // 就减少防御盾牌层数。 | ||||
|             // 每校验一次规则,云盾防御层数减1 | ||||
|             // 当云盾防御层数为0时,再次进行以下程序段则恢复满值防御。 | ||||
|             int newDunCount = mSettingsModel.getDunCurrentCount() - 1; | ||||
|             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 { | ||||
|                 mSettingsModel.setDunCurrentCount(newDunCount); | ||||
|                 LogUtils.d(TAG, String.format("设置防御层数为 %d", newDunCount)); | ||||
|             } | ||||
|  | ||||
|             saveDun(); | ||||
|             SettingsActivity.notifyDunInfoUpdate(); | ||||
|         } | ||||
|  | ||||
|         // 其他拒接 | ||||
|         return false; | ||||
|         // 返回校验结果 | ||||
|         LogUtils.d(TAG, String.format("返回校验结果 isConnect == %s", isConnect)); | ||||
|         return isConnect; | ||||
|     } | ||||
|  | ||||
|     public void add(String phoneRuleBlack, boolean isEnable) { | ||||
|         _PhoneBlacRuleBeanList.add(new PhoneBlackRuleBean(phoneRuleBlack, isEnable)); | ||||
|         PhoneBlackRuleBean.saveBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class); | ||||
|     public void add(String szPhoneConnectRule, boolean isAllowConnection, boolean isEnable) { | ||||
|         _PhoneConnectRuleModelList.add(new PhoneConnectRuleModel(szPhoneConnectRule, isAllowConnection, isEnable)); | ||||
|     } | ||||
|  | ||||
|     public ArrayList<PhoneBlackRuleBean> getPhoneBlacRuleBeanList() { | ||||
|         return _PhoneBlacRuleBeanList; | ||||
|     public ArrayList<PhoneConnectRuleModel> getPhoneBlacRuleBeanList() { | ||||
|         return _PhoneConnectRuleModelList; | ||||
|     } | ||||
|  | ||||
|     public SettingsModel getSettingsModel() { | ||||
|         return mSettingsModel; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,51 +0,0 @@ | ||||
| package cc.winboll.studio.contacts.fragments; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/02/20 12:57:00 | ||||
|  * @Describe 拨号 | ||||
|  */ | ||||
| import android.os.Bundle; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import androidx.fragment.app.Fragment; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.libappbase.LogView; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.annotation.NonNull; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| public class CallFragment extends Fragment { | ||||
|  | ||||
|     public static final String TAG = "CallFragment"; | ||||
|  | ||||
|     private static final String ARG_PAGE = "ARG_PAGE"; | ||||
|     private int mPage; | ||||
|  | ||||
|     public static CallFragment newInstance(int page) { | ||||
|         Bundle args = new Bundle(); | ||||
|         args.putInt(ARG_PAGE, page); | ||||
|         CallFragment fragment = new CallFragment(); | ||||
|         fragment.setArguments(args); | ||||
|         return fragment; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         if (getArguments()!= null) { | ||||
|             mPage = getArguments().getInt(ARG_PAGE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, | ||||
|                              @Nullable Bundle savedInstanceState) { | ||||
|         View view = inflater.inflate(R.layout.fragment_call, container, false); | ||||
|         TextView textView = view.findViewById(R.id.page_text); | ||||
|         textView.setText("这是第 " + mPage + " 页"); | ||||
|         return view; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,164 @@ | ||||
| package cc.winboll.studio.contacts.fragments; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/02/20 12:57:00 | ||||
|  * @Describe 拨号 | ||||
|  */ | ||||
| import android.Manifest; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.database.Cursor; | ||||
| import android.os.Bundle; | ||||
| import android.os.Handler; | ||||
| import android.os.Looper; | ||||
| import android.os.Message; | ||||
| import android.provider.CallLog; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.core.app.ActivityCompat; | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.recyclerview.widget.LinearLayoutManager; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.contacts.adapters.CallLogAdapter; | ||||
| import cc.winboll.studio.contacts.beans.CallLogModel; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
|  | ||||
| public class CallLogFragment extends Fragment { | ||||
|  | ||||
|     public static final String TAG = "CallFragment"; | ||||
|  | ||||
|     static volatile CallLogFragment _CallLogFragment; | ||||
|  | ||||
|     public static final int MSG_UPDATE = 1;  // 添加消息常量 | ||||
|  | ||||
|     private static final String ARG_PAGE = "ARG_PAGE"; | ||||
|     private int mPage; | ||||
|  | ||||
|     private static final int REQUEST_READ_CALL_LOG = 1; | ||||
|     private RecyclerView recyclerView; | ||||
|     private CallLogAdapter callLogAdapter; | ||||
|     private List<CallLogModel> callLogList = new ArrayList<>(); | ||||
|  | ||||
|     // 添加Handler | ||||
|     private final Handler mHandler = new Handler(Looper.getMainLooper()) { | ||||
|         @Override | ||||
|         public void handleMessage(@NonNull Message msg) { | ||||
|             if (msg.what == MSG_UPDATE) { | ||||
|                 readCallLog();  // 接收到消息时更新通话记录 | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     CallLogFragment() { | ||||
|         super(); | ||||
|     } | ||||
|  | ||||
|     public static CallLogFragment newInstance(int page) { | ||||
|         Bundle args = new Bundle(); | ||||
|         args.putInt(ARG_PAGE, page); | ||||
|         CallLogFragment fragment = new CallLogFragment(); | ||||
|         fragment.setArguments(args); | ||||
|         _CallLogFragment = fragment; | ||||
|         return fragment; | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { | ||||
|         return inflater.inflate(R.layout.fragment_call_log, container, false); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         if (getArguments() != null) { | ||||
|             mPage = getArguments().getInt(ARG_PAGE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { | ||||
|         super.onViewCreated(view, savedInstanceState); | ||||
|         recyclerView = view.findViewById(R.id.recyclerView); | ||||
|         recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); | ||||
|         callLogAdapter = new CallLogAdapter(getContext(), callLogList); | ||||
|         recyclerView.setAdapter(callLogAdapter); | ||||
|  | ||||
|         if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) { | ||||
|             ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CALL_LOG}, REQUEST_READ_CALL_LOG); | ||||
|         } else { | ||||
|             mHandler.sendEmptyMessage(MSG_UPDATE);  // 通过Handler触发更新 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | ||||
|         super.onRequestPermissionsResult(requestCode, permissions, grantResults); | ||||
|         if (requestCode == REQUEST_READ_CALL_LOG) { | ||||
|             if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { | ||||
|                 mHandler.sendEmptyMessage(MSG_UPDATE);  // 通过Handler触发更新 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void readCallLog() { | ||||
|         callLogList.clear();  // 清空原有数据 | ||||
|         Cursor cursor = requireContext().getContentResolver().query( | ||||
|             CallLog.Calls.CONTENT_URI, | ||||
|             null, | ||||
|             null, | ||||
|             null, | ||||
|             CallLog.Calls.DATE + " DESC"); | ||||
|  | ||||
|         if (cursor != null) { | ||||
|             while (cursor.moveToNext()) { | ||||
|                 String phoneNumber = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER)); | ||||
|                 int callType = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.TYPE)); | ||||
|                 long callDateLong = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE)); | ||||
|                 Date callDate = new Date(callDateLong); | ||||
|  | ||||
|                 String callStatus = getCallStatus(callType); | ||||
|  | ||||
|                 callLogList.add(new CallLogModel(phoneNumber, callStatus, callDate)); | ||||
|             } | ||||
|             cursor.close(); | ||||
|             callLogAdapter.notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private String getCallStatus(int callType) { | ||||
|         switch (callType) { | ||||
|             case CallLog.Calls.OUTGOING_TYPE: | ||||
|                 return "Outgoing"; | ||||
|             case CallLog.Calls.INCOMING_TYPE: | ||||
|                 return "Incoming"; | ||||
|             case CallLog.Calls.MISSED_TYPE: | ||||
|                 return "Missed"; | ||||
|             default: | ||||
|                 return "Unknown"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         mHandler.removeCallbacksAndMessages(null);  // 清理Handler防止内存泄漏 | ||||
|     } | ||||
|  | ||||
|     public void triggerUpdate() { | ||||
|         mHandler.sendEmptyMessage(MSG_UPDATE); | ||||
|     } | ||||
|  | ||||
|     public static void updateCallLogFragment() { | ||||
|         if (_CallLogFragment != null) { | ||||
|             _CallLogFragment.triggerUpdate(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -5,23 +5,47 @@ package cc.winboll.studio.contacts.fragments; | ||||
|  * @Date 2025/02/20 12:57:50 | ||||
|  * @Describe 联系人 | ||||
|  */ | ||||
| import android.Manifest; | ||||
| import android.content.Intent; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.database.Cursor; | ||||
| import android.os.Bundle; | ||||
| import android.provider.ContactsContract; | ||||
| import android.text.Editable; | ||||
| import android.text.TextWatcher; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Button; | ||||
| import android.widget.EditText; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.core.app.ActivityCompat; | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.recyclerview.widget.LinearLayoutManager; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.contacts.adapters.ContactAdapter; | ||||
| import cc.winboll.studio.contacts.beans.ContactModel; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
|  | ||||
| public class ContactsFragment extends Fragment { | ||||
|      | ||||
|  | ||||
|     public static final String TAG = "ContactsFragment"; | ||||
|      | ||||
|  | ||||
|     private static final String ARG_PAGE = "ARG_PAGE"; | ||||
|     private int mPage; | ||||
|  | ||||
|     private static final int REQUEST_READ_CONTACTS = 1; | ||||
|     private RecyclerView recyclerView; | ||||
|     private ContactAdapter contactAdapter; | ||||
|     private List<ContactModel> contactList = new ArrayList<>(); | ||||
|     private List<ContactModel> originalContactList = new ArrayList<>(); | ||||
|     private EditText searchEditText; | ||||
|  | ||||
|     public static ContactsFragment newInstance(int page) { | ||||
|         Bundle args = new Bundle(); | ||||
|         args.putInt(ARG_PAGE, page); | ||||
| @@ -33,18 +57,111 @@ public class ContactsFragment extends Fragment { | ||||
|     @Override | ||||
|     public void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         if (getArguments()!= null) { | ||||
|         if (getArguments() != null) { | ||||
|             mPage = getArguments().getInt(ARG_PAGE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, | ||||
|                              @Nullable Bundle savedInstanceState) { | ||||
|         View view = inflater.inflate(R.layout.fragment_contacts, container, false); | ||||
|         TextView textView = view.findViewById(R.id.page_text); | ||||
|         textView.setText("这是第 " + mPage + " 页"); | ||||
|         return view; | ||||
|     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { | ||||
|         return inflater.inflate(R.layout.fragment_contacts, container, false); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { | ||||
|         super.onViewCreated(view, savedInstanceState); | ||||
|         recyclerView = view.findViewById(R.id.contacts_recycler_view); | ||||
|         recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); | ||||
|         contactAdapter = new ContactAdapter(contactList); | ||||
|         recyclerView.setAdapter(contactAdapter); | ||||
|  | ||||
|         searchEditText = view.findViewById(R.id.search_edit_text); | ||||
|         searchEditText.addTextChangedListener(new TextWatcher() { | ||||
|                 @Override | ||||
|                 public void beforeTextChanged(CharSequence s, int start, int count, int after) { | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onTextChanged(CharSequence s, int start, int before, int count) { | ||||
|                     filterContacts(s.toString()); | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void afterTextChanged(Editable s) { | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { | ||||
|             ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_READ_CONTACTS); | ||||
|         } else { | ||||
|             readContacts(); | ||||
|         } | ||||
|  | ||||
|         Button btnDial = view.findViewById(R.id.btn_dial); | ||||
|         btnDial.setOnClickListener(new View.OnClickListener(){ | ||||
|                 @Override | ||||
|                 public void onClick(View p1) { | ||||
|  | ||||
|                     String phoneNumber = searchEditText.getText().toString().replaceAll("\\s", ""); | ||||
|                     //phoneNumber = "+8616769764848"; | ||||
|                     ToastUtils.show(phoneNumber); | ||||
|                     Intent intent = new Intent(Intent.ACTION_CALL); | ||||
|                     intent.setData(android.net.Uri.parse("tel:" + phoneNumber)); | ||||
|                     // 添加 FLAG_ACTIVITY_NEW_TASK 标志 | ||||
|                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|                     startActivity(intent); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | ||||
|         super.onRequestPermissionsResult(requestCode, permissions, grantResults); | ||||
|         if (requestCode == REQUEST_READ_CONTACTS) { | ||||
|             if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { | ||||
|                 readContacts(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void readContacts() { | ||||
|         contactList.clear(); | ||||
|         originalContactList.clear(); | ||||
|         Cursor cursor = requireContext().getContentResolver().query( | ||||
|             ContactsContract.CommonDataKinds.Phone.CONTENT_URI, | ||||
|             null, | ||||
|             null, | ||||
|             null, | ||||
|             ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC"); | ||||
|  | ||||
|         if (cursor != null) { | ||||
|             while (cursor.moveToNext()) { | ||||
|                 String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); | ||||
|                 String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); | ||||
|                 ContactModel contact = new ContactModel(name, number); | ||||
|                 contactList.add(contact); | ||||
|                 originalContactList.add(contact); | ||||
|             } | ||||
|             cursor.close(); | ||||
|             contactAdapter.notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void filterContacts(String query) { | ||||
|         contactList.clear(); | ||||
|         if (query.isEmpty()) { | ||||
|             contactList.addAll(originalContactList); | ||||
|         } else { | ||||
|             for (ContactModel contact : originalContactList) { | ||||
|                 if (contact.getName().toLowerCase().contains(query.toLowerCase()) || | ||||
|                     contact.getPinyin().toLowerCase().contains(query.toLowerCase()) || | ||||
|                     contact.getNumber().toLowerCase().contains(query.toLowerCase())) { | ||||
|                     contactList.add(contact); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         contactAdapter.notifyDataSetChanged(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import androidx.annotation.Nullable; | ||||
| import androidx.fragment.app.Fragment; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.libappbase.LogView; | ||||
| import com.hjq.toast.ToastUtils; | ||||
|  | ||||
| public class LogFragment extends Fragment { | ||||
|  | ||||
| @@ -21,6 +22,8 @@ public class LogFragment extends Fragment { | ||||
|  | ||||
|     private static final String ARG_PAGE = "ARG_PAGE"; | ||||
|     private int mPage; | ||||
|      | ||||
|     LogView mLogView; | ||||
|  | ||||
|     public static LogFragment newInstance(int page) { | ||||
|         Bundle args = new Bundle(); | ||||
| @@ -43,8 +46,17 @@ public class LogFragment extends Fragment { | ||||
|     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, | ||||
|                              @Nullable Bundle savedInstanceState) { | ||||
|         View view = inflater.inflate(R.layout.fragment_log, container, false); | ||||
|         LogView logView = view.findViewById(R.id.logview); | ||||
|         logView.start(); | ||||
|         mLogView = view.findViewById(R.id.logview); | ||||
|         mLogView.start(); | ||||
|         return view; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
|         //ToastUtils.show("onResume"); | ||||
|         mLogView.start(); | ||||
|     } | ||||
|      | ||||
|      | ||||
| } | ||||
|   | ||||
| @@ -21,6 +21,8 @@ import android.widget.TextView; | ||||
| import androidx.annotation.Nullable; | ||||
| import cc.winboll.studio.contacts.MainActivity; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.contacts.phonecallui.PhoneCallActivity; | ||||
| import cc.winboll.studio.contacts.phonecallui.PhoneCallService; | ||||
|  | ||||
|  | ||||
| public class CallListenerService extends Service { | ||||
| @@ -152,9 +154,12 @@ public class CallListenerService extends Service { | ||||
|  | ||||
|                 @Override | ||||
|                 public void onClick(View view) { | ||||
|                     Intent intent = new Intent(getApplicationContext(), MainActivity.class); | ||||
|                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|                     CallListenerService.this.startActivity(intent); | ||||
| //                    Intent intent = new Intent(getApplicationContext(), MainActivity.class); | ||||
| //                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
| //                    CallListenerService.this.startActivity(intent); | ||||
|  | ||||
|                     PhoneCallService.CallType callType = isCallingIn ? PhoneCallService.CallType.CALL_IN: PhoneCallService.CallType.CALL_OUT; | ||||
|                     PhoneCallActivity.actionStart(CallListenerService.this, callNumber, callType); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| package cc.winboll.studio.contacts.phonecallui; | ||||
|  | ||||
| import static cc.winboll.studio.contacts.listenphonecall.CallListenerService.formatPhoneNumber; | ||||
|  | ||||
| import android.annotation.SuppressLint; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| @@ -10,16 +8,16 @@ import android.os.Bundle; | ||||
| import android.view.View; | ||||
| import android.view.WindowManager; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import androidx.annotation.RequiresApi; | ||||
| import androidx.appcompat.app.AppCompatActivity; | ||||
|  | ||||
| import cc.winboll.studio.contacts.ActivityStack; | ||||
| import cc.winboll.studio.contacts.MainActivity; | ||||
| import cc.winboll.studio.contacts.R; | ||||
|  | ||||
| import java.util.Timer; | ||||
| import java.util.TimerTask; | ||||
|  | ||||
| import static cc.winboll.studio.contacts.listenphonecall.CallListenerService.formatPhoneNumber; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * 提供接打电话的界面,仅支持 Android M (6.0, API 23) 及以上的系统 | ||||
| @@ -57,10 +55,9 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick | ||||
|         setContentView(R.layout.activity_phone_call); | ||||
|  | ||||
|         ActivityStack.getInstance().addActivity(this); | ||||
|  | ||||
|         initData(); | ||||
|  | ||||
|         initView(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void initData() { | ||||
| @@ -74,9 +71,9 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick | ||||
|  | ||||
|     private void initView() { | ||||
|         int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | ||||
|                 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar | ||||
|                 | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | ||||
|                 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; | ||||
|             | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar | ||||
|             | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | ||||
|             | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; | ||||
|         getWindow().getDecorView().setSystemUiVisibility(uiOptions); | ||||
|         getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); | ||||
|  | ||||
| @@ -94,9 +91,7 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick | ||||
|         if (callType == PhoneCallService.CallType.CALL_IN) { | ||||
|             tvCallNumberLabel.setText("来电号码"); | ||||
|             tvPickUp.setVisibility(View.VISIBLE); | ||||
|         } | ||||
|         // 打出的电话 | ||||
|         else if (callType == PhoneCallService.CallType.CALL_OUT) { | ||||
|         } else if (callType == PhoneCallService.CallType.CALL_OUT) { | ||||
|             tvCallNumberLabel.setText("呼叫号码"); | ||||
|             tvPickUp.setVisibility(View.GONE); | ||||
|             phoneCallManager.openSpeaker(); | ||||
| @@ -107,13 +102,13 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick | ||||
|  | ||||
|     public void showOnLockScreen() { | ||||
|         this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | | ||||
|                         WindowManager.LayoutParams.FLAG_FULLSCREEN | | ||||
|                         WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | | ||||
|                         WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON, | ||||
|                 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | | ||||
|                         WindowManager.LayoutParams.FLAG_FULLSCREEN | | ||||
|                         WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | | ||||
|                         WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); | ||||
|                                   WindowManager.LayoutParams.FLAG_FULLSCREEN | | ||||
|                                   WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | | ||||
|                                   WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON, | ||||
|                                   WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | | ||||
|                                   WindowManager.LayoutParams.FLAG_FULLSCREEN | | ||||
|                                   WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | | ||||
|                                   WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -123,18 +118,18 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick | ||||
|             tvPickUp.setVisibility(View.GONE); | ||||
|             tvCallingTime.setVisibility(View.VISIBLE); | ||||
|             onGoingCallTimer.schedule(new TimerTask() { | ||||
|                 @Override | ||||
|                 public void run() { | ||||
|                     runOnUiThread(new Runnable() { | ||||
|                         @SuppressLint("SetTextI18n") | ||||
|                         @Override | ||||
|                         public void run() { | ||||
|                             callingTime++; | ||||
|                             tvCallingTime.setText("通话中:" + getCallingTime()); | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
|             }, 0, 1000); | ||||
|                     @Override | ||||
|                     public void run() { | ||||
|                         runOnUiThread(new Runnable() { | ||||
|                                 @SuppressLint("SetTextI18n") | ||||
|                                 @Override | ||||
|                                 public void run() { | ||||
|                                     callingTime++; | ||||
|                                     tvCallingTime.setText("通话中:" + getCallingTime()); | ||||
|                                 } | ||||
|                             }); | ||||
|                     } | ||||
|                 }, 0, 1000); | ||||
|         } else if (v.getId() == R.id.tv_phone_hang_up) { | ||||
|             phoneCallManager.disconnect(); | ||||
|             stopTimer(); | ||||
| @@ -145,8 +140,8 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick | ||||
|         int minute = callingTime / 60; | ||||
|         int second = callingTime % 60; | ||||
|         return (minute < 10 ? "0" + minute : minute) + | ||||
|                 ":" + | ||||
|                 (second < 10 ? "0" + second : second); | ||||
|             ":" + | ||||
|             (second < 10 ? "0" + second : second); | ||||
|     } | ||||
|  | ||||
|     private void stopTimer() { | ||||
| @@ -160,7 +155,7 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick | ||||
|     @Override | ||||
|     protected void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|  | ||||
|         //MainActivity.updateCallLogFragment(); | ||||
|         phoneCallManager.destroy(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import android.os.Build; | ||||
| import android.telecom.Call; | ||||
| import android.telecom.VideoProfile; | ||||
| import androidx.annotation.RequiresApi; | ||||
| import cc.winboll.studio.contacts.dun.Rules; | ||||
| import cc.winboll.studio.contacts.MainActivity; | ||||
|  | ||||
|  | ||||
| @RequiresApi(api = Build.VERSION_CODES.M) | ||||
|   | ||||
| @@ -7,28 +7,53 @@ package cc.winboll.studio.contacts.phonecallui; | ||||
|  * @see PhoneCallActivity | ||||
|  * @see android.telecom.InCallService | ||||
|  */ | ||||
| import android.content.ContentResolver; | ||||
| import android.database.Cursor; | ||||
| import android.media.AudioManager; | ||||
| import android.media.MediaRecorder; | ||||
| import android.net.Uri; | ||||
| import android.os.Build; | ||||
| import android.provider.CallLog; | ||||
| import android.telecom.Call; | ||||
| import android.telecom.InCallService; | ||||
| import android.telephony.TelephonyManager; | ||||
| import androidx.annotation.RequiresApi; | ||||
| import cc.winboll.studio.contacts.ActivityStack; | ||||
| import cc.winboll.studio.contacts.beans.RingTongBean; | ||||
| import cc.winboll.studio.contacts.dun.Rules; | ||||
| import cc.winboll.studio.contacts.fragments.CallLogFragment; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
|  | ||||
| @RequiresApi(api = Build.VERSION_CODES.M) | ||||
| public class PhoneCallService extends InCallService { | ||||
|  | ||||
|     public static final String TAG = "PhoneCallService"; | ||||
|      | ||||
|     private volatile int originalRingVolume; | ||||
|  | ||||
|     MediaRecorder mediaRecorder; | ||||
|  | ||||
|     private final Call.Callback callback = new Call.Callback() { | ||||
|         @Override | ||||
|         public void onStateChanged(Call call, int state) { | ||||
|             super.onStateChanged(call, state); | ||||
|             switch (state) { | ||||
|                 case TelephonyManager.CALL_STATE_OFFHOOK: | ||||
|                     { | ||||
|                         long callId = getCurrentCallId(); | ||||
|                         if (callId != -1) { | ||||
|                             // 在这里可以对获取到的通话记录ID进行处理 | ||||
|                             //System.out.println("当前通话记录ID: " + callId); | ||||
|  | ||||
|                             // 电话接通,开始录音 | ||||
|                             startRecording(callId); | ||||
|                         } | ||||
|                         break; | ||||
|                     } | ||||
|                 case TelephonyManager.CALL_STATE_IDLE: | ||||
|                     // 电话挂断,停止录音 | ||||
|                     stopRecording(); | ||||
|                     break; | ||||
|                 case Call.STATE_ACTIVE: { | ||||
|                         break; | ||||
|                     } | ||||
| @@ -61,25 +86,58 @@ public class PhoneCallService extends InCallService { | ||||
|             String phoneNumber = details.getHandle().getSchemeSpecificPart(); | ||||
|  | ||||
|             // 记录原始铃声音量 | ||||
|             // | ||||
|             AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); | ||||
|             originalRingVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING); | ||||
|             int ringerVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING); | ||||
|             // 恢复铃声音量,预防其他意外条件导致的音量变化问题 | ||||
|             // | ||||
|  | ||||
|             // 读取应用配置,未配置就初始化配置文件 | ||||
|             RingTongBean bean = RingTongBean.loadBean(this, RingTongBean.class); | ||||
|             if (bean == null) { | ||||
|                 // 初始化配置 | ||||
|                 bean = new RingTongBean(); | ||||
|                 RingTongBean.saveBean(this, bean); | ||||
|             } | ||||
|             // 如果当前音量和应用保存的不一致就恢复为应用设定值 | ||||
|             // 恢复铃声音量 | ||||
|             try { | ||||
|                 if (ringerVolume != bean.getStreamVolume()) { | ||||
|                     audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0); | ||||
|                     //audioManager.setMode(AudioManager.RINGER_MODE_NORMAL); | ||||
|                 } | ||||
|             } catch (java.lang.SecurityException e) { | ||||
|                 LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|             } | ||||
|  | ||||
|             // 检查电话接收规则 | ||||
|             if (!Rules.getInstance(this).isAllowed(phoneNumber)) { | ||||
|                 // 预先静音 | ||||
|                 audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0); | ||||
|                 // 调低音量 | ||||
|                 try { | ||||
|                     audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0); | ||||
|                     //audioManager.setMode(AudioManager.RINGER_MODE_SILENT); | ||||
|                 } catch (java.lang.SecurityException e) { | ||||
|                     LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|                 } | ||||
|                 // 断开电话 | ||||
|                 call.disconnect(); | ||||
|                 // 停顿1秒,预防第一声铃声响动 | ||||
|                 try { | ||||
|                     Thread.sleep(1000); | ||||
|                     Thread.sleep(500); | ||||
|                 } catch (InterruptedException e) { | ||||
|                     LogUtils.d(TAG, ""); | ||||
|                 } | ||||
|                 // 恢复铃声音量 | ||||
|                 audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0); | ||||
|                 try { | ||||
|                     audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0); | ||||
|                     //audioManager.setMode(AudioManager.RINGER_MODE_NORMAL); | ||||
|                 } catch (java.lang.SecurityException e) { | ||||
|                     LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|                 } | ||||
|                 // 屏蔽电话结束 | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             // 正常接听电话 | ||||
|             PhoneCallActivity.actionStart(this, phoneNumber, callType); | ||||
|         } | ||||
| @@ -88,16 +146,70 @@ public class PhoneCallService extends InCallService { | ||||
|     @Override | ||||
|     public void onCallRemoved(Call call) { | ||||
|         super.onCallRemoved(call); | ||||
|  | ||||
|         call.unregisterCallback(callback); | ||||
|         PhoneCallManager.call = null; | ||||
|         AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); | ||||
|         // 恢复铃声音量 | ||||
|         audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         CallLogFragment.updateCallLogFragment(); | ||||
|     } | ||||
|  | ||||
|     public enum CallType { | ||||
|         CALL_IN, | ||||
|         CALL_OUT, | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private void startRecording(long callId) { | ||||
|         LogUtils.d(TAG, "startRecording(...)"); | ||||
|         mediaRecorder = new MediaRecorder(); | ||||
|         mediaRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL); | ||||
|         mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); | ||||
|         mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); | ||||
|         mediaRecorder.setOutputFile(getOutputFilePath(callId)); | ||||
|         try { | ||||
|             mediaRecorder.prepare(); | ||||
|             mediaRecorder.start(); | ||||
|         } catch (IOException e) { | ||||
|             LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private String getOutputFilePath(long callId) { | ||||
|         LogUtils.d(TAG, "getOutputFilePath(...)"); | ||||
|         // 设置录音文件的保存路径 | ||||
|         File file = new File(getExternalFilesDir(TAG), String.format("call_%d.mp4", callId)); | ||||
|         return file.getAbsolutePath(); | ||||
|     } | ||||
|  | ||||
|     private void stopRecording() { | ||||
|         LogUtils.d(TAG, "stopRecording()"); | ||||
|         if (mediaRecorder != null) { | ||||
|             mediaRecorder.stop(); | ||||
|             mediaRecorder.release(); | ||||
|             mediaRecorder = null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private long getCurrentCallId() { | ||||
|         LogUtils.d(TAG, "getCurrentCallId()"); | ||||
|         ContentResolver contentResolver = getApplicationContext().getContentResolver(); | ||||
|         Uri callLogUri = Uri.parse("content://call_log/calls"); | ||||
|         String[] projection = {"_id", "number", "call_type", "date"}; | ||||
|         String selection = "call_type = " + CallLog.Calls.OUTGOING_TYPE + " OR call_type = " + CallLog.Calls.INCOMING_TYPE; | ||||
|         String sortOrder = "date DESC"; | ||||
|  | ||||
|         try { | ||||
|             Cursor cursor = contentResolver.query(callLogUri, projection, selection, null, sortOrder); | ||||
|             if (cursor != null && cursor.moveToFirst()) { | ||||
|                 return cursor.getLong(cursor.getColumnIndex("_id")); | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
|             LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|         } | ||||
|  | ||||
|         return -1; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -9,13 +9,9 @@ import android.content.BroadcastReceiver; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.IntentFilter; | ||||
| import android.media.RingtoneManager; | ||||
| import android.net.Uri; | ||||
| import android.util.Log; | ||||
| import cc.winboll.studio.contacts.services.MainService; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.lang.ref.WeakReference; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
|  | ||||
| public class MainReceiver extends BroadcastReceiver { | ||||
|  | ||||
| @@ -43,7 +39,7 @@ public class MainReceiver extends BroadcastReceiver { | ||||
|     public void registerAction(Context context) { | ||||
|         IntentFilter filter=new IntentFilter(); | ||||
|         filter.addAction(ACTION_BOOT_COMPLETED); | ||||
|         //filter.addAction(Intent.ACTION_BATTERY_CHANGED); | ||||
|         //filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); | ||||
|         context.registerReceiver(this, filter); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -15,8 +15,6 @@ import android.os.IBinder; | ||||
| import cc.winboll.studio.contacts.beans.MainServiceBean; | ||||
| import cc.winboll.studio.contacts.services.MainService; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.SOS; | ||||
| import cc.winboll.studio.libappbase.bean.APPSOSBean; | ||||
|  | ||||
| public class AssistantService extends Service { | ||||
|  | ||||
|   | ||||
| @@ -11,25 +11,29 @@ package cc.winboll.studio.contacts.services; | ||||
|  * https://blog.csdn.net/cyp331203/article/details/38920491 | ||||
|  */ | ||||
| import android.app.Service; | ||||
| import cc.winboll.studio.contacts.listenphonecall.CallListenerService; | ||||
| import android.content.ComponentName; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.ServiceConnection; | ||||
| import android.media.AudioManager; | ||||
| import android.os.Binder; | ||||
| import android.os.IBinder; | ||||
| import cc.winboll.studio.contacts.beans.MainServiceBean; | ||||
| import cc.winboll.studio.contacts.beans.RingTongBean; | ||||
| import cc.winboll.studio.contacts.bobulltoon.TomCat; | ||||
| import cc.winboll.studio.contacts.dun.Rules; | ||||
| import cc.winboll.studio.contacts.handlers.MainServiceHandler; | ||||
| import cc.winboll.studio.contacts.listenphonecall.CallListenerService; | ||||
| import cc.winboll.studio.contacts.receivers.MainReceiver; | ||||
| import cc.winboll.studio.contacts.services.MainService; | ||||
| import cc.winboll.studio.contacts.threads.MainServiceThread; | ||||
| import cc.winboll.studio.contacts.widgets.APPStatusWidget; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.SOS; | ||||
| import cc.winboll.studio.libappbase.bean.APPSOSBean; | ||||
| import cc.winboll.studio.contacts.dun.Rules; | ||||
| import android.media.AudioManager; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import cc.winboll.studio.libappbase.sos.SOS; | ||||
| 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 { | ||||
|  | ||||
| @@ -48,8 +52,9 @@ public class MainService extends Service { | ||||
|     AssistantService mAssistantService; | ||||
|     boolean isBound = false; | ||||
|     MainReceiver mMainReceiver; | ||||
|      | ||||
|      | ||||
|     Timer mStreamVolumeCheckTimer; | ||||
|     static volatile TomCat _TomCat; | ||||
|  | ||||
|     @Override | ||||
|     public IBinder onBind(Intent intent) { | ||||
|         return new MyBinder(); | ||||
| @@ -71,9 +76,37 @@ public class MainService extends Service { | ||||
|             mMyServiceConnection = new MyServiceConnection(); | ||||
|         } | ||||
|         mMainServiceHandler = new MainServiceHandler(this); | ||||
|          | ||||
|          | ||||
|      | ||||
|  | ||||
|         // 铃声检查定时器 | ||||
|         mStreamVolumeCheckTimer = new Timer(); | ||||
|         mStreamVolumeCheckTimer.schedule(new TimerTask() { | ||||
|                 @Override | ||||
|                 public void run() { | ||||
|                     AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); | ||||
|                     int ringerVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING); | ||||
|                     // 恢复铃声音量,预防其他意外条件导致的音量变化问题 | ||||
|                     // | ||||
|  | ||||
|                     // 读取应用配置,未配置就初始化配置文件 | ||||
|                     RingTongBean bean = RingTongBean.loadBean(MainService.this, RingTongBean.class); | ||||
|                     if (bean == null) { | ||||
|                         // 初始化配置 | ||||
|                         bean = new RingTongBean(); | ||||
|                         RingTongBean.saveBean(MainService.this, bean); | ||||
|                     } | ||||
|                     // 如果当前音量和应用保存的不一致就恢复为应用设定值 | ||||
|                     // 恢复铃声音量 | ||||
|                     try { | ||||
|                         if (ringerVolume != bean.getStreamVolume()) { | ||||
|                             audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0); | ||||
|                             //audioManager.setMode(AudioManager.RINGER_MODE_NORMAL); | ||||
|                         } | ||||
|                     } catch (java.lang.SecurityException e) { | ||||
|                         LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|                     } | ||||
|                 } | ||||
|             }, 1000, 60000); | ||||
|  | ||||
|         // 运行服务内容 | ||||
|         mainService(); | ||||
|     } | ||||
| @@ -97,18 +130,26 @@ public class MainService extends Service { | ||||
|             // 唤醒守护进程 | ||||
|             wakeupAndBindAssistant(); | ||||
|             // 召唤 WinBoll APP 绑定本服务 | ||||
|             SOS.bindToAPPService(this, new APPSOSBean(getPackageName(), MainService.class.getName())); | ||||
|             if (App.isDebuging()) { | ||||
|                 WinBoll.bindToAPPBaseBeta(this, MainService.class.getName()); | ||||
|             } else { | ||||
|                 WinBoll.bindToAPPBase(this, MainService.class.getName()); | ||||
|             } | ||||
|  | ||||
|             // 初始化服务运行参数 | ||||
|             _TomCat = TomCat.getInstance(this); | ||||
|             if (!_TomCat.loadPhoneBoBullToon()) { | ||||
|                 LogUtils.d(TAG, "没有下载 BoBullToon 数据。BoBullToon 参数无法加载。"); | ||||
|             } | ||||
|  | ||||
|             if (mMainReceiver == null) { | ||||
|                 // 注册广播接收器 | ||||
|                 mMainReceiver = new MainReceiver(this); | ||||
|                 mMainReceiver.registerAction(this); | ||||
|             } | ||||
|              | ||||
|             Rules.getInstance(this); | ||||
|             //Rules.getInstance(this).add("18888888888", true); | ||||
|             //Rules.getInstance(this).add("16769764848", true); | ||||
|              | ||||
|  | ||||
|             Rules.getInstance(this).loadRules(); | ||||
|  | ||||
|             startPhoneCallListener(); | ||||
|  | ||||
|             MainServiceThread.getInstance(this, mMainServiceHandler).start(); | ||||
| @@ -117,6 +158,14 @@ public class MainService extends Service { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static boolean isPhoneInBoBullToon(String phone) { | ||||
|         if (_TomCat != null) { | ||||
|             return _TomCat.isPhoneBoBullToon(phone); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     // 唤醒和绑定守护进程 | ||||
|     // | ||||
|     void wakeupAndBindAssistant() { | ||||
| @@ -137,7 +186,7 @@ public class MainService extends Service { | ||||
| //        LogUtils.d(TAG, "startService(intent)"); | ||||
| //        bindService(new Intent(this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     void startPhoneCallListener() { | ||||
|         Intent callListener = new Intent(this, CallListenerService.class); | ||||
|         startService(callListener); | ||||
| @@ -168,7 +217,7 @@ public class MainService extends Service { | ||||
|  | ||||
|             // 停止主要进程 | ||||
|             MainServiceThread.getInstance(this, mMainServiceHandler).setIsExit(true); | ||||
|              | ||||
|  | ||||
|         } | ||||
|  | ||||
|         super.onDestroy(); | ||||
| @@ -191,7 +240,11 @@ public class MainService extends Service { | ||||
|             if (mMainServiceBean.isEnable()) { | ||||
|                 // 唤醒守护进程 | ||||
|                 wakeupAndBindAssistant(); | ||||
|                 SOS.sosWinBollService(getApplicationContext(), new APPSOSBean(getPackageName(), MainService.class.getName())); | ||||
|                 if (App.isDebuging()) { | ||||
|                     SOS.sosToAppBase(getApplicationContext(), MainService.class.getName()); | ||||
|                 } else { | ||||
|                     SOS.sosToAppBaseBeta(getApplicationContext(), MainService.class.getName()); | ||||
|                 } | ||||
|             } | ||||
|             isBound = false; | ||||
|             mAssistantService = null; | ||||
| @@ -230,14 +283,40 @@ public class MainService extends Service { | ||||
|  | ||||
|     public static void stopMainService(Context context) { | ||||
|         LogUtils.d(TAG, "stopMainService"); | ||||
|         context.stopService(new Intent(context, MainService.class)); | ||||
|     } | ||||
|  | ||||
|     public static void startMainService(Context context) { | ||||
|         LogUtils.d(TAG, "startMainService"); | ||||
|         context.startService(new Intent(context, MainService.class)); | ||||
|     } | ||||
|  | ||||
|     public static void restartMainService(Context context) { | ||||
|         LogUtils.d(TAG, "restartMainService"); | ||||
|  | ||||
|         MainServiceBean bean = MainServiceBean.loadBean(context, MainServiceBean.class); | ||||
|         if (bean != null && bean.isEnable()) { | ||||
|             context.stopService(new Intent(context, MainService.class)); | ||||
| //            try { | ||||
| //                Thread.sleep(1000); | ||||
| //            } catch (InterruptedException e) { | ||||
| //                LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
| //            } | ||||
|             context.startService(new Intent(context, MainService.class)); | ||||
|             LogUtils.d(TAG, "已重启 MainService"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static void stopMainServiceAndSaveStatus(Context context) { | ||||
|         LogUtils.d(TAG, "stopMainServiceAndSaveStatus"); | ||||
|         MainServiceBean bean = new MainServiceBean(); | ||||
|         bean.setIsEnable(false); | ||||
|         MainServiceBean.saveBean(context, bean); | ||||
|         context.stopService(new Intent(context, MainService.class)); | ||||
|     } | ||||
|  | ||||
|     public static void startMainService(Context context) { | ||||
|         LogUtils.d(TAG, "startMainService"); | ||||
|     public static void startMainServiceAndSaveStatus(Context context) { | ||||
|         LogUtils.d(TAG, "startMainServiceAndSaveStatus"); | ||||
|         MainServiceBean bean = new MainServiceBean(); | ||||
|         bean.setIsEnable(true); | ||||
|         MainServiceBean.saveBean(context, bean); | ||||
|   | ||||
| @@ -6,11 +6,7 @@ package cc.winboll.studio.contacts.threads; | ||||
|  */ | ||||
| import android.content.Context; | ||||
| import cc.winboll.studio.contacts.handlers.MainServiceHandler; | ||||
| import cc.winboll.studio.contacts.services.MainService; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.SOS; | ||||
| import cc.winboll.studio.libappbase.bean.APPSOSBean; | ||||
| import com.hjq.toast.ToastUtils; | ||||
| import java.lang.ref.WeakReference; | ||||
|  | ||||
| public class MainServiceThread extends Thread { | ||||
|   | ||||
| @@ -0,0 +1,123 @@ | ||||
| package cc.winboll.studio.contacts.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/06 21:08:16 | ||||
|  * @Describe ContactUtils | ||||
|  */ | ||||
| import android.content.ContentResolver; | ||||
| import android.content.Context; | ||||
| import android.database.Cursor; | ||||
| import android.provider.ContactsContract; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| public class ContactUtils { | ||||
|  | ||||
|     public static final String TAG = "ContactUtils"; | ||||
|  | ||||
|     Map<String, String> contactMap = new HashMap<>(); | ||||
|  | ||||
|     static volatile ContactUtils _ContactUtils; | ||||
|     Context mContext; | ||||
|     ContactUtils(Context context) { | ||||
|         mContext = context; | ||||
|         relaodContacts(); | ||||
|     } | ||||
|     public synchronized static ContactUtils getInstance(Context context) { | ||||
|         if (_ContactUtils == null) { | ||||
|             _ContactUtils = new ContactUtils(context); | ||||
|         } | ||||
|         return _ContactUtils; | ||||
|     } | ||||
|  | ||||
|     public void relaodContacts() { | ||||
|         readContacts(); | ||||
|     } | ||||
|  | ||||
|     private void readContacts() { | ||||
|         contactMap.clear(); | ||||
|         ContentResolver contentResolver = mContext.getContentResolver(); | ||||
|         Cursor cursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, | ||||
|                                               null, null, null, null); | ||||
|         if (cursor != null) { | ||||
|             while (cursor.moveToNext()) { | ||||
|                 String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); | ||||
|                 String phoneNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); | ||||
|                 //Map<String, String> contactMap = new HashMap<>(); | ||||
|                 contactMap.put(formatToSimplePhoneNumber(phoneNumber), displayName); | ||||
|             } | ||||
|             cursor.close(); | ||||
|         } | ||||
|         // 此时 contactList 就是存储联系人信息的 Map 列表 | ||||
|     } | ||||
|  | ||||
|     public String getContactsName(String phone) { | ||||
|         String result = contactMap.get(formatToSimplePhoneNumber(phone)); | ||||
|         return result == null ? "[NotInContacts]" : result; | ||||
|     } | ||||
|  | ||||
| //    static String getSimplePhone(String phone) { | ||||
| //        return phone.replaceAll("[+\\s]", ""); | ||||
| //    } | ||||
|  | ||||
|     public static String formatToSimplePhoneNumber(String number) { | ||||
|         // 去除所有空格和非数字字符 | ||||
|         return number.replaceAll("[^0-9]", ""); | ||||
|     } | ||||
|  | ||||
|     public static String getDisplayNameByPhone(Context context, String phoneNumber) { | ||||
|         String displayName = null; | ||||
|         ContentResolver resolver = context.getContentResolver(); | ||||
|         String[] projection = {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME}; | ||||
|         Cursor cursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, ContactsContract.CommonDataKinds.Phone.NUMBER + "=?", new String[]{phoneNumber}, null); | ||||
|         if (cursor != null && cursor.moveToFirst()) { | ||||
|             displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); | ||||
|             cursor.close(); | ||||
|         } | ||||
|         return displayName; | ||||
|     } | ||||
|  | ||||
|     public static String getDisplayNameByPhoneSimple(Context context, String phoneNumber) { | ||||
|         String displayName = null; | ||||
|         ContentResolver resolver = context.getContentResolver(); | ||||
|         String[] projection = {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME}; | ||||
|         Cursor cursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, ContactsContract.CommonDataKinds.Phone.NUMBER + "=?", new String[]{formatToSimplePhoneNumber(phoneNumber)}, null); | ||||
|         if (cursor != null && cursor.moveToFirst()) { | ||||
|             displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); | ||||
|             cursor.close(); | ||||
|         } | ||||
|         return displayName; | ||||
|     } | ||||
|  | ||||
|     public static boolean isPhoneInContacts(Context context, String phoneNumber) { | ||||
|         String szPhoneNumber = formatToSimplePhoneNumber(phoneNumber); | ||||
|         String szDisplayName = getDisplayNameByPhone(context, szPhoneNumber); | ||||
|         if (szDisplayName == null) { | ||||
|             LogUtils.d(TAG, String.format("Phone %s is not in contacts.", szPhoneNumber)); | ||||
|             szPhoneNumber = formatToSpacePhoneNumber(szPhoneNumber); | ||||
|             szDisplayName = getDisplayNameByPhone(context, szPhoneNumber); | ||||
|             if (szDisplayName == null) { | ||||
|                 LogUtils.d(TAG, String.format("Phone %s is not in contacts.", szPhoneNumber)); | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         LogUtils.d(TAG, String.format("Phone %s is found in contacts %s.", szPhoneNumber, szDisplayName)); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public static String formatToSpacePhoneNumber(String simpleNumber) { | ||||
|         // 去除所有空格和非数字字符 | ||||
|         StringBuilder sbSpaceNumber = new StringBuilder(); | ||||
|         String regex = "^1[0-9]{10}$";  | ||||
|         if (simpleNumber.matches(regex)) { | ||||
|             sbSpaceNumber.append(simpleNumber.substring(0, 3)); | ||||
|             sbSpaceNumber.append(" "); | ||||
|             sbSpaceNumber.append(simpleNumber.substring(3, 7)); | ||||
|             sbSpaceNumber.append(" "); | ||||
|             sbSpaceNumber.append(simpleNumber.substring(7, 11)); | ||||
|         } | ||||
|         return sbSpaceNumber.toString(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,24 @@ | ||||
| package cc.winboll.studio.contacts.utils; | ||||
| import android.widget.EditText; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/04/13 00:59:13 | ||||
|  * @Describe Int类型数字输入框工具集 | ||||
|  */ | ||||
| public class EditTextIntUtils { | ||||
|  | ||||
|     public static final String TAG = "EditTextIntUtils"; | ||||
|  | ||||
|     public static int getIntFromEditText(EditText editText) { | ||||
|         try { | ||||
|             String sz = editText.getText().toString().trim(); | ||||
|             return Integer.parseInt(sz); | ||||
|         } catch (NumberFormatException e) { | ||||
|             LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,37 @@ | ||||
| package cc.winboll.studio.contacts.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/04/13 01:16:28 | ||||
|  * @Describe Int数字操作工具集 | ||||
|  */ | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
|  | ||||
| public class IntUtils { | ||||
|  | ||||
|     public static final String TAG = "IntUtils"; | ||||
|  | ||||
|     public static int getIntInRange(int origin, int range_a, int range_b) { | ||||
|         int min = Math.min(range_a, range_b); | ||||
|         int max = Math.max(range_a, range_b); | ||||
|         int res = Math.min(origin, max); | ||||
|         res = Math.max(res, min); | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     public static void unittest_getIntInRange() { | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(-100, 5, 10); %d", getIntInRange(-100, 5, 10))); | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(8, 5, 10); %d", getIntInRange(8, 5, 10))); | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(200, 5, 10); %d", getIntInRange(200, 5, 10))); | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(-100, -5, 10); %d", getIntInRange(-100, -5, 10))); | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(9, -5, 10); %d", getIntInRange(9, -5, 10))); | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(100, -5, 10); %d", getIntInRange(100, -5, 10))); | ||||
|  | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(500, 5, -10); %d", getIntInRange(500, 5, -10))); | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(4, 5, -10); %d", getIntInRange(4, 5, -10))); | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(-20, 5, -10); %d", getIntInRange(-20, 5, -10))); | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(500, 50, 10); %d", getIntInRange(500, 50, 10))); | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(30, 50, 10); %d", getIntInRange(30, 50, 10))); | ||||
|         LogUtils.d(TAG, String.format("getIntInRange(6, 50, 10); %d", getIntInRange(6, 50, 10))); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| package cc.winboll.studio.contacts.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/02/26 15:21:48 | ||||
|  * @Describe PhoneUtils | ||||
|  */ | ||||
| import android.Manifest; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.pm.PackageManager; | ||||
| import androidx.core.app.ActivityCompat; | ||||
|  | ||||
| public class PhoneUtils { | ||||
|      | ||||
|     public static final String TAG = "PhoneUtils"; | ||||
|      | ||||
|     public static void call(Context context, String phoneNumber) { | ||||
|         Intent intent = new Intent(Intent.ACTION_CALL); | ||||
|         intent.setData(android.net.Uri.parse("tel:" + phoneNumber)); | ||||
|         if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { | ||||
|             return; | ||||
|         } | ||||
|         context.startActivity(intent); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -0,0 +1,32 @@ | ||||
| package cc.winboll.studio.contacts.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@QQ.COM | ||||
|  * @Date 2024/12/09 19:00:21 | ||||
|  * @Describe .* 前置预防针 | ||||
|  regex pointer preventive injection | ||||
|  简称 RegexPPi | ||||
|  */ | ||||
| import java.util.regex.Matcher; | ||||
| import java.util.regex.Pattern; | ||||
|  | ||||
| public class RegexPPiUtils { | ||||
|  | ||||
|     public static final String TAG = "RegexPPiUtils"; | ||||
|  | ||||
|     // | ||||
|     // 检验文本是否满足适合正则表达式模式计算 | ||||
|     // | ||||
|     public static boolean isPPiOK(String text) { | ||||
|         //String text = "这里是一些任意的文本内容"; | ||||
|         String regex = ".*"; | ||||
|         Pattern pattern = Pattern.compile(regex); | ||||
|         Matcher matcher = pattern.matcher(text); | ||||
|         /*if (matcher.matches()) { | ||||
|          System.out.println("文本满足该正则表达式模式"); | ||||
|          } else { | ||||
|          System.out.println("文本不满足该正则表达式模式"); | ||||
|          }*/ | ||||
|         return matcher.matches(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,68 @@ | ||||
| package cc.winboll.studio.contacts.views; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/02 21:11:03 | ||||
|  * @Describe 云盾防御信息 | ||||
|  */ | ||||
| import android.content.Context; | ||||
| import android.os.Handler; | ||||
| import android.os.Message; | ||||
| import android.widget.TextView; | ||||
| import cc.winboll.studio.contacts.beans.SettingsModel; | ||||
| import cc.winboll.studio.contacts.dun.Rules; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
|  | ||||
| public class DuInfoTextView extends TextView { | ||||
|      | ||||
|     public static final String TAG = "DuInfoTextView"; | ||||
|      | ||||
|     public static final int MSG_NOTIFY_INFO_UPDATE = 0; | ||||
|      | ||||
|     Context mContext; | ||||
|      | ||||
|     public DuInfoTextView(android.content.Context context) { | ||||
|         super(context); | ||||
|     } | ||||
|  | ||||
|     public DuInfoTextView(android.content.Context context, android.util.AttributeSet attrs) { | ||||
|         super(context, attrs); | ||||
|         initView(context); | ||||
|     } | ||||
|  | ||||
|     public DuInfoTextView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr) { | ||||
|         super(context, attrs, defStyleAttr); | ||||
|     } | ||||
|  | ||||
|     public DuInfoTextView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes) { | ||||
|         super(context, attrs, defStyleAttr, defStyleRes); | ||||
|     } | ||||
|  | ||||
|     void initView(android.content.Context context) { | ||||
|         mContext = context; | ||||
|         updateInfo(); | ||||
|     } | ||||
|      | ||||
|     void updateInfo() { | ||||
|         LogUtils.d(TAG, "updateInfo()"); | ||||
|         SettingsModel settingsModel = Rules.getInstance(mContext).getSettingsModel(); | ||||
|         String info = String.format("(云盾防御值【%d/%d】)", settingsModel.getDunCurrentCount(), settingsModel.getDunTotalCount()); | ||||
|         setText(info); | ||||
|     } | ||||
|      | ||||
|     Handler mHandler = new Handler(){ | ||||
|         @Override | ||||
|         public void handleMessage(Message msg) { | ||||
|             super.handleMessage(msg); | ||||
|             if(msg.what == MSG_NOTIFY_INFO_UPDATE) { | ||||
|                 updateInfo(); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|     }; | ||||
|      | ||||
|     public void notifyInfoUpdate() { | ||||
|         LogUtils.d(TAG, "notifyInfoUpdate()"); | ||||
|         mHandler.sendMessage(mHandler.obtainMessage(MSG_NOTIFY_INFO_UPDATE)); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,220 @@ | ||||
| package cc.winboll.studio.contacts.views; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/04 10:51:50 | ||||
|  * @Describe CustomHorizontalScrollView | ||||
|  */ | ||||
| import android.content.Context; | ||||
| import android.util.AttributeSet; | ||||
| import android.view.MotionEvent; | ||||
| import android.view.View; | ||||
| import android.widget.Button; | ||||
| import android.widget.HorizontalScrollView; | ||||
| import android.widget.LinearLayout; | ||||
| import android.widget.TextView; | ||||
| import cc.winboll.studio.contacts.R; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
|  | ||||
| public class LeftScrollView extends HorizontalScrollView { | ||||
|  | ||||
|     public static final String TAG = "LeftScrollView"; | ||||
|  | ||||
|     private LinearLayout contentLayout; | ||||
|     private LinearLayout toolLayout; | ||||
|     private TextView textView; | ||||
|     private Button editButton; | ||||
|     private Button deleteButton; | ||||
|     private Button upButton; | ||||
|     private Button downButton; | ||||
|     private float mStartX; | ||||
|     private float mEndX; | ||||
|     private boolean isScrolling = false; | ||||
|     private int nScrollAcceptSize; | ||||
|  | ||||
|     public LeftScrollView(Context context) { | ||||
|         super(context); | ||||
|         init(); | ||||
|     } | ||||
|  | ||||
|     public LeftScrollView(Context context, AttributeSet attrs) { | ||||
|         super(context, attrs); | ||||
|         init(); | ||||
|     } | ||||
|  | ||||
|     public LeftScrollView(Context context, AttributeSet attrs, int defStyleAttr) { | ||||
|         super(context, attrs, defStyleAttr); | ||||
|         init(); | ||||
|     } | ||||
|  | ||||
|     public void addContentLayout(TextView textView) { | ||||
|         contentLayout.addView(textView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); | ||||
|     } | ||||
|  | ||||
|     public void setContentWidth(int contentWidth) { | ||||
|         LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) contentLayout.getLayoutParams(); | ||||
|         layoutParams.width = contentWidth; | ||||
|         contentLayout.setLayoutParams(layoutParams); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void init() { | ||||
|         View viewMain = inflate(getContext(), R.layout.view_left_scroll, null); | ||||
|  | ||||
|         // 创建内容布局 | ||||
|         contentLayout = viewMain.findViewById(R.id.content_layout); | ||||
|         toolLayout = viewMain.findViewById(R.id.action_layout); | ||||
|  | ||||
|         //LogUtils.d(TAG, String.format("getWidth() %d", getWidth())); | ||||
|  | ||||
|         addView(viewMain); | ||||
|  | ||||
|         // 创建编辑按钮 | ||||
|         editButton = viewMain.findViewById(R.id.edit_btn); | ||||
|         // 创建删除按钮 | ||||
|         deleteButton = viewMain.findViewById(R.id.delete_btn); | ||||
|         // 向上按钮 | ||||
|         upButton = viewMain.findViewById(R.id.up_btn); | ||||
|         // 向下按钮 | ||||
|         downButton = viewMain.findViewById(R.id.down_btn); | ||||
|  | ||||
|         // 编辑按钮点击事件 | ||||
|         editButton.setOnClickListener(new OnClickListener() { | ||||
|                 @Override | ||||
|                 public void onClick(View v) { | ||||
|                     if (onActionListener != null) { | ||||
|                         onActionListener.onEdit(); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         // 删除按钮点击事件 | ||||
|         deleteButton.setOnClickListener(new OnClickListener() { | ||||
|                 @Override | ||||
|                 public void onClick(View v) { | ||||
|                     if (onActionListener != null) { | ||||
|                         onActionListener.onDelete(); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|         // 编辑按钮点击事件 | ||||
|         upButton.setOnClickListener(new OnClickListener() { | ||||
|                 @Override | ||||
|                 public void onClick(View v) { | ||||
|                     if (onActionListener != null) { | ||||
|                         onActionListener.onUp(); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         // 删除按钮点击事件 | ||||
|         downButton.setOnClickListener(new OnClickListener() { | ||||
|                 @Override | ||||
|                 public void onClick(View v) { | ||||
|                     if (onActionListener != null) { | ||||
|                         onActionListener.onDown(); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onTouchEvent(MotionEvent event) { | ||||
|         switch (event.getAction()) { | ||||
|             case MotionEvent.ACTION_DOWN: | ||||
|                 LogUtils.d(TAG, "ACTION_DOWN"); | ||||
|                 mStartX = event.getX(); | ||||
| //                isScrolling = false; | ||||
|                 break; | ||||
|             case MotionEvent.ACTION_MOVE: | ||||
|                 //LogUtils.d(TAG, "ACTION_MOVE"); | ||||
| //                float currentX = event.getX(); | ||||
| //                float deltaX = mStartX - currentX; | ||||
| //                //mLastX = currentX; | ||||
| //                if (Math.abs(deltaX) > 0) { | ||||
| //                    isScrolling = true; | ||||
| //                } | ||||
|                 break; | ||||
|             case MotionEvent.ACTION_UP: | ||||
|             case MotionEvent.ACTION_CANCEL: | ||||
|                 if (getScrollX() > 0) { | ||||
|                     LogUtils.d(TAG, "ACTION_UP"); | ||||
|                     mEndX = event.getX(); | ||||
|                     LogUtils.d(TAG, String.format("mStartX %f, mEndX %f", mStartX, mEndX)); | ||||
|                     if (mEndX < mStartX) { | ||||
|                         LogUtils.d(TAG, String.format("mEndX >= mStartX \ngetScrollX() %d", getScrollX())); | ||||
|                         //if (getScrollX() > editButton.getWidth()) { | ||||
|                         if (Math.abs(mStartX - mEndX) > editButton.getWidth()) { | ||||
|                             smoothScrollToRight(); | ||||
|                         } else { | ||||
|                             smoothScrollToLeft(); | ||||
|                         } | ||||
|                     } else { | ||||
|                         LogUtils.d(TAG, String.format("mEndX >= mStartX \ngetScrollX() %d", getScrollX())); | ||||
|                         //if (getScrollX() > deleteButton.getWidth()) { | ||||
|                         if (Math.abs(mEndX - mStartX) > deleteButton.getWidth()) { | ||||
|                             smoothScrollToLeft(); | ||||
|                         } else { | ||||
|                             smoothScrollToRight(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|         return super.onTouchEvent(event); | ||||
|     } | ||||
|  | ||||
|     void smoothScrollToRight() { | ||||
|         mEndX = 0; | ||||
|         mStartX = 0; | ||||
|         View childView = getChildAt(0); | ||||
|         if (childView != null) { | ||||
|             // 计算需要滑动到最右边的距离 | ||||
|             int scrollToX = childView.getWidth() - getWidth(); | ||||
|             // 确保滑动距离不小于0 | ||||
|             final int scrollToX2 = Math.max(0, scrollToX); | ||||
|             // 平滑滑动到最右边 | ||||
|             post(new Runnable() { | ||||
|                     @Override | ||||
|                     public void run() { | ||||
|                         smoothScrollTo(scrollToX2, 0); | ||||
|                         LogUtils.d(TAG, "smoothScrollTo(0, 0);"); | ||||
|                     } | ||||
|                 }); | ||||
|             LogUtils.d(TAG, "smoothScrollTo(scrollToX, 0);"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void smoothScrollToLeft() { | ||||
|         mEndX = 0; | ||||
|         mStartX = 0; | ||||
|         // 在手指抬起时,使用 post 方法调用 smoothScrollTo(0, 0) | ||||
|         post(new Runnable() { | ||||
|                 @Override | ||||
|                 public void run() { | ||||
|                     smoothScrollTo(0, 0); | ||||
|                     LogUtils.d(TAG, "smoothScrollTo(0, 0);"); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     // 设置文本内容 | ||||
|     public void setText(CharSequence text) { | ||||
|         textView.setText(text); | ||||
|     } | ||||
|  | ||||
|     // 定义回调接口 | ||||
|     public interface OnActionListener { | ||||
|         void onEdit(); | ||||
|         void onDelete(); | ||||
|         void onUp(); | ||||
|         void onDown(); | ||||
|     } | ||||
|  | ||||
|     private OnActionListener onActionListener; | ||||
|  | ||||
|     public void setOnActionListener(OnActionListener listener) { | ||||
|         this.onActionListener = listener; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,14 @@ | ||||
| package cc.winboll.studio.contacts.views; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen@AliYun.Com | ||||
|  * @Date 2025/03/19 14:04:20 | ||||
|  * @Describe 云盾滑视度热备控件 | ||||
|  */ | ||||
| public class ScrollDoView { | ||||
|      | ||||
|     public static final String TAG = "ScrollDoView"; | ||||
|      | ||||
|      | ||||
|      | ||||
| } | ||||
							
								
								
									
										11
									
								
								contacts/src/main/res/drawable/ic_call.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contacts/src/main/res/drawable/ic_call.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:width="24dp" | ||||
|     android:height="24dp" | ||||
|     android:viewportHeight="24" | ||||
|     android:viewportWidth="24"> | ||||
|     <path | ||||
|         android:fillColor="#ff000000" | ||||
|         android:pathData="M6.62,10.79C8.06,13.62 10.38,15.94 13.21,17.38L15.41,15.18C15.69,14.9 16.08,14.82 16.43,14.93C17.55,15.3 18.75,15.5 20,15.5A1,1 0,0 1,21 16.5V20A1,1 0,0 1,20 21A17,17 0,0 1,3 4A1,1 0,0 1,4 3H7.5A1,1 0,0 1,8.5 4C8.5,5.25 8.7,6.45 9.07,7.57C9.18,7.92 9.1,8.31 8.82,8.59L6.62,10.79Z"/> | ||||
|  | ||||
| </vector> | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 ZhanGSKen
					ZhanGSKen