commit 47ace290e5cea965516ccf2b6cc9bd79f0c09fa3 Author: ZhanGSKen Date: Wed May 13 15:54:02 2026 +0800 拷贝https://gitea.winboll.cc/Studio/WinBoLL_Bck20260513_154646_588.git项目提交点为9d90b35fc20c7190472db1fdaa9e1417067856c0的源码 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3be31ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,101 @@ +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +*.jks +*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +# Android Profiling +*.hprof + +# 忽略 Lint 输出文件 +lint-results.xml +lint-results.html + +## 忽略 AndroidIDE 临时文件夹 +.androidide + +## WinBoLL 基础应用(避免上传敏感配置) +/winboll.properties +/local.properties +/settings.gradle +/gradle.properties \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c97416e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libjc/jcc/libs"] + path = libjc/jcc/libs + url = https://gitea.winboll.cc/Studio/APP_libjc_jcc_libs.git diff --git a/.winboll/Readme.txt b/.winboll/Readme.txt new file mode 100644 index 0000000..f618520 --- /dev/null +++ b/.winboll/Readme.txt @@ -0,0 +1,18 @@ +## WinBoLL 主机编译事项提醒 + +## 类库类型源码发布 +# 类库发布使用以下面命令 +git pull && bash .winboll/bashPublishLIBAddTag.sh <类库模块文件夹名称> + +## 纯应用类型源码发布 +# 应用发布使用以下命令 +git pull && bash .winboll/bashPublishAPKAddTag.sh <应用模块文件夹名称> + +## 编译时提问。Add Github Workflows Tag? (yes/No) +回答yes: 将会添加一个 GitHub 工作流标签 + GitHub 仓库会执行以该标签为标志的编译工作流。 +回答No(默认): 就忽略 GitHub 标签,忽略 GitHub 工作流调用。 + +## Github Workflows 工作流设置注意事项 +应用名称改变时需要修改.github/workflows/android.yml文件设置, +在第79行:asset_name: 处有应用包名称设置。 diff --git a/.winboll/bashCheckGitCommitStatus.sh b/.winboll/bashCheckGitCommitStatus.sh new file mode 100644 index 0000000..1adb83c --- /dev/null +++ b/.winboll/bashCheckGitCommitStatus.sh @@ -0,0 +1,32 @@ +#!/usr/bin/bash + +# 使用 `-z` 命令检查变量是否为空 +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Script parameter error: $0" + exit 2 +fi + +# 进入项目根目录 +cd ${1} +echo -e "Work dir : \n"`pwd` + +git config --global --add safe.directory "${1}" +echo "Current dir : "`pwd` +versionName=${2} + +## 设置要检查的标签 +tag="v"${versionName} + +## 如果Git已经提交了所有代码就执行标签检查操作 +if [[ -n $(git diff --stat) ]] +then + echo 'Source is no commit git completely, tag action cancel.' + exit 1 +else + echo "Git status is clean." + if [ "$(git tag -l ${tag})" == "${tag}" ]; then + echo "Tag ${tag} exist." + exit 2 + fi + echo "${0}: Git tag is checked OK: (${tag})" +fi diff --git a/.winboll/bashCommitAppPublishBuildFlagInfo.sh b/.winboll/bashCommitAppPublishBuildFlagInfo.sh new file mode 100644 index 0000000..791e58c --- /dev/null +++ b/.winboll/bashCommitAppPublishBuildFlagInfo.sh @@ -0,0 +1,17 @@ +#!/usr/bin/bash +## 提交新的 APK 编译配置标志信息,并推送到Git仓库。 + +# 使用 `-z` 命令检查变量是否为空 +if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ]; then + echo "$0 Script parameter error." + echo "(Script Demo : [ bashCommitAppPublishBuildFlagInfo.sh ])" + exit 2 +fi + +# 进入项目根目录 +cd ${1} +echo -e "Work dir : \n"`pwd` + +git add . +git commit -m "<$4>APK ${2} ${3} Publish." +git push origin && git push origin --tags diff --git a/.winboll/bashCommitLibReleaseBuildFlagInfo.sh b/.winboll/bashCommitLibReleaseBuildFlagInfo.sh new file mode 100644 index 0000000..d92f320 --- /dev/null +++ b/.winboll/bashCommitLibReleaseBuildFlagInfo.sh @@ -0,0 +1,48 @@ +#!/usr/bin/bash +## 提交新的 Library 编译配置标志信息,并推送到Git仓库。 + +# 检查是否指定了将要发布的类库名称 +# 使用 `-z` 命令检查变量是否为空 +if [ -z "$1" ]; then + echo "Library name error: $0" + exit 2 +fi + +## 开始执行脚本 +echo -e "Current dir : \n"`pwd` +# 检查当前目录是否是项目根目录 +if [[ -e $1/build.properties ]]; then + echo "The $1/build.properties file exists." + echo -e "Work dir correctly." +else + echo "The $1/build.properties file does not exist." + echo "尝试进入根目录" + # 进入项目根目录 + cd .. +fi +## 本脚本需要在项目根目录下执行 +echo -e "Current dir : \n"`pwd` +# 检查当前目录是否是项目根目录 +if [[ -e $1/build.properties ]]; then + echo "The $1/build.properties file exists." + echo -e "Work dir correctly." +else + echo "The $1/build.properties file does not exist." + echo -e "Work dir error." + exit 1 +fi + +# 就读取脚本 .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 标签 +# 脚本调试时使用 +#tag="v7.6.4-test1" +# 正式设置标签时使用 +#tag="v"${PUBLISH_VERSION} + +git add . +git commit -m "<$1>Library Release ${PUBLISH_VERSION}" +git push origin && git push origin --tags diff --git a/.winboll/bashPublishAPKAddTag.sh b/.winboll/bashPublishAPKAddTag.sh new file mode 100644 index 0000000..082dc7e --- /dev/null +++ b/.winboll/bashPublishAPKAddTag.sh @@ -0,0 +1,223 @@ +#!/usr/bin/bash +# ============================================================================== +# WinBoLL 应用发布脚本 +# 功能:检查Git源码状态 → 编译Stage Release包 → 添加WinBoLL标签 → 提交并推送源码 +# 依赖:build.properties、app_update_description.txt(项目根目录下) +# 使用:./script_name.sh +# 作者:豆包&ZhanGSKen +# ============================================================================== + +# ==================== 常量定义 ==================== +# 脚本退出码 +EXIT_CODE_SUCCESS=0 +EXIT_CODE_ERR_NO_APP_NAME=2 +EXIT_CODE_ERR_WORK_DIR=1 +EXIT_CODE_ERR_GIT_CHECK=1 +EXIT_CODE_ERR_ADD_WINBOLL_TAG=1 + +# Gradle 任务(正式发布) +GRADLE_TASK_PUBLISH="assembleStageRelease" +# Gradle 任务(调试用,注释备用) +# GRADLE_TASK_DEBUG="assembleBetaDebug" + +# ==================== 函数定义 ==================== +# 检查Git源码是否已完全提交(无未提交变更) +# 返回值:0=已完全提交,1=存在未提交变更 +function checkGitSources() { + # 配置Git安全目录(解决权限问题) + git config --global --add safe.directory "$(pwd)" + + # 检查是否有未提交的变更 + if [[ -n $(git diff --stat) ]]; then + echo "[ERROR] Git源码存在未提交变更,请先提交所有修改!" + return 1 + fi + + echo "[INFO] Git源码检查通过:所有变更已提交。" + return 0 +} + +# 询问是否添加GitHub Workflows标签(当前逻辑注释,保留扩展能力) +# 返回值:1=用户选择是,0=用户选择否 +function askAddWorkflowsTag() { + read -p "是否添加GitHub Workflows标签?(Y/n) " answer + if [[ $answer =~ ^[Yy]$ ]]; then + return 1 + else + return 0 + fi +} + +# 添加WinBoLL正式标签 +# 参数:$1=应用名称(项目根目录名) +# 返回值:0=标签添加成功,1=标签已存在/添加失败 +function addWinBoLLTag() { + local app_name=$1 + local build_prop_path="${app_name}/build.properties" + + # 从build.properties中提取publishVersion + local publish_version=$(grep -o "publishVersion=.*" "${build_prop_path}" | awk -F '=' '{print $2}') + if [[ -z ${publish_version} ]]; then + echo "[ERROR] 未从${build_prop_path}中提取到publishVersion配置!" + return 1 + fi + echo "[INFO] 从${build_prop_path}读取到publishVersion:${publish_version}" + + # 构造WinBoLL标签(格式:-v) + local tag="${app_name}-v${publish_version}" + echo "[INFO] 准备添加WinBoLL标签:${tag}" + + # 检查标签是否已存在 + if [[ "$(git tag -l ${tag})" == "${tag}" ]]; then + echo "[ERROR] WinBoLL标签${tag}已存在!" + return 1 + fi + + # 添加带注释的标签(注释来自app_update_description.txt) + git tag -a "${tag}" -F "${app_name}/app_update_description.txt" + echo "[INFO] WinBoLL标签${tag}添加成功!" + return 0 +} + +# 添加GitHub Workflows Beta标签(当前逻辑注释,保留扩展能力) +# 参数:$1=应用名称(项目根目录名) +# 返回值:0=标签添加成功,1=标签已存在/添加失败 +function addWorkflowsTag() { + local app_name=$1 + local build_prop_path="${app_name}/build.properties" + + # 从build.properties中提取baseBetaVersion + local base_beta_version=$(grep -o "baseBetaVersion=.*" "${build_prop_path}" | awk -F '=' '{print $2}') + if [[ -z ${base_beta_version} ]]; then + echo "[ERROR] 未从${build_prop_path}中提取到baseBetaVersion配置!" + return 1 + fi + echo "[INFO] 从${build_prop_path}读取到baseBetaVersion:${base_beta_version}" + + # 构造Workflows标签(格式:-v-beta) + local tag="${app_name}-v${base_beta_version}-beta" + echo "[INFO] 准备添加Workflows标签:${tag}" + + # 检查标签是否已存在 + if [[ "$(git tag -l ${tag})" == "${tag}" ]]; then + echo "[ERROR] Workflows标签${tag}已存在!" + return 1 + fi + + # 添加带注释的标签(注释来自app_update_description.txt) + git tag -a "${tag}" -F "${app_name}/app_update_description.txt" + echo "[INFO] Workflows标签${tag}添加成功!" + return 0 +} + +# ==================== 主流程开始 ==================== +echo "=============================================" +echo " WinBoLL 应用发布脚本" +echo "=============================================" + +# 1. 检查应用名称参数是否指定 +if [ -z "$1" ]; then + echo "[ERROR] 未指定应用名称!使用方式:${0} " + exit ${EXIT_CODE_ERR_NO_APP_NAME} +fi +APP_NAME=$1 +echo "[INFO] 待发布应用名称:${APP_NAME}" + +# 2. 检查并切换到项目根目录(确保build.properties存在) +echo "[INFO] 当前工作目录:$(pwd)" +if [[ ! -e "${APP_NAME}/build.properties" ]]; then + echo "[WARNING] 当前目录不存在${APP_NAME}/build.properties,尝试切换到上级目录..." + cd .. + echo "[INFO] 切换后工作目录:$(pwd)" +fi + +# 验证最终工作目录是否正确 +if [[ ! -e "${APP_NAME}/build.properties" ]]; then + echo "[ERROR] 工作目录错误!${APP_NAME}/build.properties 文件不存在。" + exit ${EXIT_CODE_ERR_WORK_DIR} +fi +echo "[INFO] 工作目录验证通过:${APP_NAME}/build.properties 存在。" + +# 3. 检查Git源码状态 +echo "---------------------------------------------" +echo " 步骤1:检查Git源码状态" +echo "---------------------------------------------" +checkGitSources +if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then + echo "[ERROR] Git源码检查失败,脚本终止!" + exit ${EXIT_CODE_ERR_GIT_CHECK} +fi + +# 4. 编译Stage Release版本APK +echo "---------------------------------------------" +echo " 步骤2:编译Stage Release APK" +echo "---------------------------------------------" +echo "[INFO] 开始执行Gradle任务:${GRADLE_TASK_PUBLISH}" +# 调试用(注释正式任务,启用调试任务) +# bash gradlew :${APP_NAME}:${GRADLE_TASK_DEBUG} +bash gradlew :${APP_NAME}:${GRADLE_TASK_PUBLISH} + +if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then + echo "[ERROR] Gradle编译任务失败!" + exit 1 +fi +echo "[INFO] Stage Release APK编译成功!" + +# 5. 添加WinBoLL正式标签 +echo "---------------------------------------------" +echo " 步骤3:添加WinBoLL标签" +echo "---------------------------------------------" +addWinBoLLTag ${APP_NAME} +if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then + echo "[ERROR] WinBoLL标签添加失败,脚本终止!" + exit ${EXIT_CODE_ERR_ADD_WINBOLL_TAG} +fi + +# 6. (可选)添加GitHub Workflows标签(当前逻辑注释,保留扩展能力) +# echo "---------------------------------------------" +# echo " 步骤4:添加Workflows标签(可选)" +# echo "---------------------------------------------" +# echo "是否添加GitHub Workflows Beta标签?(Y/n) " +# askAddWorkflowsTag +# nAskAddWorkflowsTag=$? +# if [[ ${nAskAddWorkflowsTag} -eq 1 ]]; then +# addWorkflowsTag ${APP_NAME} +# if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then +# echo "[ERROR] Workflows标签添加失败,脚本终止!" +# exit 1 +# fi +# fi + +# 7. 清理更新描述文件 +echo "---------------------------------------------" +echo " 步骤5:清理更新描述文件" +echo "---------------------------------------------" +echo "" > "${APP_NAME}/app_update_description.txt" +echo "[INFO] 已清空${APP_NAME}/app_update_description.txt" + +# 8. 提交并推送源码与标签 +echo "---------------------------------------------" +echo " 步骤6:提交并推送源码" +echo "---------------------------------------------" +git add . +git commit -m "<${APP_NAME}> 开始新的Stage版本开发。" +echo "[INFO] 源码提交成功,开始推送..." + +# 推送源码到远程仓库 +git push origin +# 推送标签到远程仓库 +git push origin --tags + +if [[ $? -eq ${EXIT_CODE_SUCCESS} ]]; then + echo "[INFO] 源码与标签推送成功!" +else + echo "[ERROR] 源码与标签推送失败!" + exit 1 +fi + +# ==================== 主流程结束 ==================== +echo "=============================================" +echo " WinBoLL 应用发布完成!" +echo "=============================================" +exit ${EXIT_CODE_SUCCESS} + diff --git a/.winboll/bashPublishDebugAPKAddTag.sh b/.winboll/bashPublishDebugAPKAddTag.sh new file mode 100644 index 0000000..0deb526 --- /dev/null +++ b/.winboll/bashPublishDebugAPKAddTag.sh @@ -0,0 +1,166 @@ +#!/usr/bin/bash + +# 检查是否指定了将要发布的调试版应用名称 +# 使用 `-z` 命令检查变量是否为空 +if [ -z "$1" ]; then + echo "No APP name specified : $0" + exit 2 +fi + +## 定义相关函数 +## 检查 Git 源码是否完全提交了,完全提交就返回0 +function checkGitSources { + #local input="$1" + #echo "The string is: $input" + git config --global --add safe.directory `pwd` + if [[ -n $(git diff --stat) ]] + then + local result="Source is no commit completely." + echo $result + # 脚本调试时使用 + #return 0 + # 正式检查源码时使用 + return 1 + fi + local result="Git Source Check OK." + echo $result + return 0 +} + +function askAddWorkflowsTag { + read answer + if [[ $answer =~ ^[Yy]$ ]]; then + #echo "You chose yes." + return 1 + else + #echo "You chose no." + return 0 + fi +} + +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 标签 + # 脚本调试时使用 + #tag="v7.6.4-test1" + # 正式调试版设置标签时使用 + tag=$1"-v"${PUBLISH_VERSION}"-debug" + echo "< WinBoLL Tag To: $tag >"; + # 检查是否已经添加了 WinBoLL Tag + if [ "$(git tag -l ${tag})" == "${tag}" ]; then + echo -e "< WinBoLL Tag ${tag} exist! >" + return 1 # WinBoLL标签重复 + fi + # 添加WinBoLL标签 + git tag -a ${tag} -F $1/app_update_description.txt + return 0 +} + +function addWorkflowsTag { + # 就读取脚本 .winboll/winboll_app_build.gradle 生成的 baseBetaVersion。 + # 如果文件中有 baseBetaVersion 这一项, + # 使用grep找到包含"baseBetaVersion="的那一行,然后用awk提取其后的值 + BASE_BETA_VERSION=$(grep -o "baseBetaVersion=.*" $1/build.properties | awk -F '=' '{print $2}') + echo "< $1/build.properties baseBetaVersion : ${BASE_BETA_VERSION} >" + ## 设新的 workflows 标签 + # 脚本调试时使用 + #tag="v7.6.4-beta" + # 正式设置标签时使用 + tag=$1"-"v"${BASE_BETA_VERSION}-beta-debug + echo "< Workflows Tag To: $tag >"; + # 检查是否已经添加了工作流 Tag + if [ "$(git tag -l ${tag})" == "${tag}" ]; then + echo -e "< Github Workflows Tag ${tag} exist! >" + return 1 # 工作流标签重复 + fi + # 添加工作流标签 + git tag -a ${tag} -F $1/app_update_description.txt + return 0 +} + +## 开始执行脚本 +echo -e "Current dir : \n"`pwd` +# 检查当前目录是否是项目根目录 +if [[ -e $1/build.properties ]]; then + echo "The $1/build.properties file exists." + echo -e "Work dir correctly." +else + echo "The $1/build.properties file does not exist." + echo "尝试进入根目录" + # 进入项目根目录 + cd .. +fi +## 本脚本需要在项目根目录下执行 +echo -e "Current dir : \n"`pwd` +# 检查当前目录是否是项目根目录 +if [[ -e $1/build.properties ]]; then + echo "The $1/build.properties file exists." + echo -e "Work dir correctly." +else + echo "The $1/build.properties file does not exist." + echo -e "Work dir error." + exit 1 +fi + +# 检查源码状态 +result=$(checkGitSources) +if [[ $? -eq 0 ]]; then + echo $result + # 如果Git已经提交了所有代码就执行标签和应用发布操作 + + # 预先询问是否添加工作流标签 + echo "Add Github Workflows Tag? (yes/no)" + result=$(askAddWorkflowsTag) + nAskAddWorkflowsTag=$? + echo $result + + # 发布应用 + echo "Publishing WinBoLL Debug APK ..." + # 脚本调试时使用 + #bash gradlew :$1:assembleBetaDebug + # 正式发布调试版 + bash gradlew :$1:assembleStageDebug + echo "Publishing WinBoLL Debug APK OK." + + # 添加 WinBoLL 标签 + result=$(addWinBoLLTag $1) + echo $result + if [[ $? -eq 0 ]]; then + echo $result + # WinBoLL 标签添加成功 + else + echo -e "${0}: addWinBoLLTag $1\n${result}\nAdd WinBoLL tag cancel." + exit 1 # addWinBoLLTag 异常 + fi + + # 添加 GitHub 工作流标签 + if [[ $nAskAddWorkflowsTag -eq 1 ]]; then + # 如果用户选择添加工作流标签 + result=$(addWorkflowsTag $1) + if [[ $? -eq 0 ]]; then + echo $result + # 工作流标签添加成功 + else + echo -e "${0}: addWorkflowsTag $1\n${result}\nAdd workflows tag cancel." + exit 1 # addWorkflowsTag 异常 + fi + fi + + ## 清理更新描述文件内容 + echo "" > $1/app_update_description.txt + + # 设置新版本开发参数配置 + # 提交配置 + git add . + git commit -m "<$1>Start New Stage Debug Version." + echo "Push sources to git repositories ..." + # 推送源码到所有仓库 + git push origin && git push origin --tags +else + echo -e "${0}: checkGitSources\n${result}\nShell cancel." + exit 1 # checkGitSources 异常 +fi diff --git a/.winboll/bashPublishLIBAddTag.sh b/.winboll/bashPublishLIBAddTag.sh new file mode 100644 index 0000000..7ab6a3a --- /dev/null +++ b/.winboll/bashPublishLIBAddTag.sh @@ -0,0 +1,14 @@ +#!/usr/bin/bash + +# 检查是否指定了将要发布的类库名称 +# 使用 `-z` 命令检查变量是否为空 +if [ -z "$1" ]; then + echo "No Library name specified : $0" + exit 2 +fi + +## 正式发布使用 +git pull && bash gradlew :$1:publishReleasePublicationToWinBoLLReleaseRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1 + +## 调试使用 +#bash gradlew :$1:publishSnapshotWinBoLLPublicationToWinBoLLSnapshotRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1 diff --git a/.winboll/winboll_app_build.gradle b/.winboll/winboll_app_build.gradle new file mode 100644 index 0000000..57b6ba7 --- /dev/null +++ b/.winboll/winboll_app_build.gradle @@ -0,0 +1,342 @@ +// WinBoLL 应用签名配置 +// + +android { + def winbollProps = new Properties() + def winbollPropsFile = rootProject.file("${winbollFilePath}") + if(winbollPropsFile.exists()) { + winbollProps.load(new FileInputStream(winbollPropsFile)) + } + + // 读取秘钥配置文件 + // + def keyProps = new Properties() + def keyPropsFile = rootProject.file("${keyPropsFilePath}") + //println 'Test keystore path' + //println 'KeyProsFile :' + Boolean.toString(keyPropsFile.exists()) + //assert(false) + if(keyPropsFile.exists()) { + keyProps.load(new FileInputStream(keyPropsFile)) + } + + // 配置签名 + signingConfigs { + if(keyPropsFile.exists()) { + winboll { + assert(keyProps['keyAlias'] != null && keyProps['keyPassword'] != null && keyProps['storeFile'] != null && keyProps['storePassword'] != null) + keyAlias keyProps['keyAlias'] + keyPassword keyProps['keyPassword'] + storeFile keyProps['storeFile'] ? file(keyProps['storeFile']) : null + storePassword keyProps['storePassword'] + } + } + } + buildTypes { + debug { + if(keyPropsFile.exists()) { + signingConfig signingConfigs.winboll + } + } + release { + if(keyPropsFile.exists()) { + signingConfig signingConfigs.winboll + } + + minifyEnabled true // 开启混淆(核心开关) + shrinkResources true // 可选:移除无用资源(进一步减小体积) + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), // 官方默认规则(优化版) + 'proguard-rules.pro' // 自定义规则文件 + } + + } + + flavorDimensions "WinBoLLApp" + productFlavors { + beta { + // 检查编译标志位配置 + assert (winbollBuildProps['buildCount'] != null) + dimension "WinBoLLApp" + applicationIdSuffix ".beta" + LocalDateTime localDateTimeNow = LocalDateTime.now(ZoneId.of("Asia/Shanghai")); + versionNameSuffix "-beta" + winbollBuildProps['buildCount'] + "_" + localDateTimeNow.format('mmss') + } + stage { + dimension "WinBoLLApp" + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + // 应用包输出配置 + // + android.applicationVariants.all { variant -> + // + // GitHub 应用包输出配置 + // 1. 配置 Beta Release 版应用包输出 + // 注意 :GitHub 打包使用 android.yml 的 "bash ./gradlew assembleBetaRelease" 命令 + // + if(variant.flavorName == "beta" && variant.buildType.name == "release") { + /* 后期管理预留代码 */ + /* 暂时没有需要的 GitHub 应用包输出配置 */ + /* GitHub 部分代码忽略 */ + } + + // + // WinBoLL 应用包输出配置 + // 1. 配置 Stage Release 版应用包输出 + // 2. 配置 Beta Debug 版应用包输出 + // + if((variant.flavorName == "beta" && variant.buildType.name == "debug") + || (variant.flavorName == "beta" && variant.buildType.name == "release") + || (variant.flavorName == "stage" && variant.buildType.name == "debug") + || (variant.flavorName == "stage" && variant.buildType.name == "release")) { + println "Project root directory: " + project.rootDir.toString() + println "Project root directory name : " + project.rootDir.name + def outputPath="${project.projectDir.absolutePath}/build/outputs/apk/${variant.buildType.name}" + //def outputFileName="${rootProject.name}_${versionName}.apk" + def outputFileName=project.rootDir.name + "_${versionName}.apk" + + // 创建 WinBoLL Studio 发布接口文件夹 + File fWinBoLLStudioDir = file("/sdcard/WinBoLLStudio/APKs"); + // 如果配置了APK接口文件夹路径,就设置应用APK输出文件夹为接口文件夹。 + if(winbollProps != null && winbollProps['APKOutputPath'] != null ) { + fWinBoLLStudioDir = file(winbollProps['APKOutputPath']); + } + + if(!fWinBoLLStudioDir.exists()) { + println "[ WinBoLLStudio ] : " + fWinBoLLStudioDir.getAbsolutePath() + " Folder does not exist." + println '[ WinBoLLStudio ] : The APKOutputPath property is not defined in winboll.properties, please configure APK output folder first.' + } else { + /// WINBOLL 主机的 APK 发布和源码管理操作 /// + variant.getAssembleProvider().get().doFirst { + /* 后期管理预留代码 */ + } //doFirst { + + // 编译输出后处理文件部分 + // + variant.getAssembleProvider().get().doLast { + variant.outputs.forEach{ file-> + // 如果正在调试,就拷贝到 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}") + // 创建目标路径目录 + if(!outBuildBckDir.exists()) { + outBuildBckDir.mkdirs(); + println "Output Folder Created.(WinBoLLStudio) : " + outBuildBckDir.getAbsolutePath() + } + if(outBuildBckDir.exists()) { + copy{ + from file.outputFile + into outBuildBckDir + rename { + String fileName -> "${outputFileName}" + } + println "Output APK (WinBoLLStudio): " + outBuildBckDir.getAbsolutePath() + "/${outputFileName}" + } + // 检查编译标志位配置 + assert (winbollBuildProps['buildCount'] != null) + assert (winbollBuildProps['libraryProject'] != null) + //构建计数增加 + int buildCount = Integer.parseInt(winbollBuildProps['buildCount']) + 1; + // 设置编译计数 + winbollBuildProps.setProperty("buildCount", Integer.toString(buildCount)); + + //保存编译标志配置 + FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile) + winbollBuildProps.store(fos, "${winbollBuildPropsDesc}"); + fos.close(); + println "\n\n>>> Project build.properties saved.\n\n"; + + if(winbollBuildProps['libraryProject'] != "") { + // 如果应用 build.properties 文件设置了类库模块项目文件名 + // 就拷贝一份新的编译标志配置到类库项目文件夹 + File libraryProjectBuildPropsFile = new File("$RootProjectDir/" + winbollBuildProps['libraryProject'] + "/build.properties") + assert(winbollBuildPropsFile.exists()) + assert(libraryProjectBuildPropsFile.exists()) + java.nio.file.Path sourceFilePath = winbollBuildPropsFile.toPath(); + java.nio.file.Path targetFilePath = libraryProjectBuildPropsFile.toPath(); + // 使用copyTo()方法复制文件,如果目标文件存在会被覆盖,可选参数可以选择不覆盖 + java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING); + + println "\n\n>>> Library Project build.properties saved.\n\n"; + } + } + } + + // 如果正在发布,就拷贝到 WinBoLL 标签管理文件夹,和处理 Git 仓库管理任务。 + // + if(variant.flavorName == "stage"&&variant.buildType.name == "release"){ + // 发布 APK 文件 + // + // 截取版本号的版本字段为短版本名 + String szVersionName = "${versionName}" + String[] szlistTemp = szVersionName.split("-") + String szShortVersionName = szlistTemp[0] + //String szCommonTagAPKName = "${rootProject.name}_" + szShortVersionName + ".apk" + 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/") + // 创建目标路径目录 + if(!outTagDir.exists()) { + outTagDir.mkdirs(); + println "Output Folder Created.(Tags) : " + outTagDir.getAbsolutePath() + } + + if(outTagDir.exists()) { + File targetAPK = new File(outTagDir, "${szCommonTagAPKName}") + if(targetAPK.exists()) { + // 标签版本APK文件已经存在,构建拷贝任务停止 + assert (!targetAPK.exists()) + // 可选择删除并继续输出APK文件 + //delete targetAPK + } + // 复制一个备份 + copy{ + from file.outputFile + into outTagDir + rename { + String fileName -> "${outputFileName}" + } + println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${outputFileName}" + } + // 复制一个并重命名为短版本名 + copy{ + from file.outputFile + into outTagDir + rename { + String fileName -> "${szCommonTagAPKName}" + } + println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${szCommonTagAPKName}" + } + // 检查编译标志位配置 + assert (winbollBuildProps['stageCount'] != null) + assert (winbollBuildProps['publishVersion'] != null) + assert (winbollBuildProps['buildCount'] != null) + assert (winbollBuildProps['baseVersion'] != null) + assert (winbollBuildProps['baseBetaVersion'] != null) + assert (winbollBuildProps['libraryProject'] != null) + + // 设置类库的默认版本名 + winbollBuildProps.setProperty("publishVersion", "${versionName}"); + // Stage 发布计数增加 + int stageCount = Integer.parseInt(winbollBuildProps['stageCount']) + 1; + winbollBuildProps.setProperty("stageCount", Integer.toString(stageCount)); + // 设置类库的默认Beta版本名 + winbollBuildProps.setProperty("baseBetaVersion", winbollBuildProps['baseVersion'] + "." + Integer.toString(stageCount)); + // 构建计数重置 + winbollBuildProps.setProperty("buildCount", "0"); + + //保存编译标志配置 + FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile) + winbollBuildProps.store(fos, "${winbollBuildPropsDesc}"); + fos.close(); + + if(winbollBuildProps['libraryProject'] != "") { + // 如果应用 build.properties 文件设置了类库模块项目文件名 + // 就拷贝一份新的编译标志配置到类库项目文件夹 + File libraryProjectBuildPropsFile = new File("$RootProjectDir/" + winbollBuildProps['libraryProject'] + "/build.properties") + assert(winbollBuildPropsFile.exists()) + assert(libraryProjectBuildPropsFile.exists()) + java.nio.file.Path sourceFilePath = winbollBuildPropsFile.toPath(); + java.nio.file.Path targetFilePath = libraryProjectBuildPropsFile.toPath(); + // 使用copyTo()方法复制文件,如果目标文件存在会被覆盖,可选参数可以选择不覆盖 + java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING); + } + + // 提交新的编译标志配置 + println 'exec bashCommitAppPublishBuildFlagInfoFilePath ...' + def resultCommitBuildFlag = exec { + commandLine 'bash', '--', "${RootProjectDir}/${bashCommitAppPublishBuildFlagInfoFilePath}", "${RootProjectDir}", "${versionName}", variant.buildType.name , rootProject.name + } + // 检查bash命令的返回值(假设非零表示失败) + assert(resultCommitBuildFlag.getExitValue() == 0) + } + } // if(variant.buildType.name == "release"){ + + // 如果正在调试发布版,就只生成和输出APK文件,不处理 Git 仓库提交与更新问题。 + // + if(variant.flavorName == "stage"&&variant.buildType.name == "debug"){ + // 发布 APK 文件 + // + // 截取版本号的版本字段为短版本名 + String szVersionName = "${versionName}" + String[] szlistTemp = szVersionName.split("-") + String szShortVersionName = szlistTemp[0] + //String szCommonTagAPKName = "${rootProject.name}_" + szShortVersionName + ".apk" + 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 + "/${variant.buildType.name}/") + // 创建目标路径目录 + if(!outTagDir.exists()) { + outTagDir.mkdirs(); + println "Output Folder Created.(Tags) : " + outTagDir.getAbsolutePath() + } + + if(outTagDir.exists()) { + File targetAPK = new File(outTagDir, "${szCommonTagAPKName}") + if(targetAPK.exists()) { + // 标签版本APK文件已经存在,构建拷贝任务停止 + println '如果是在调试 Stage 版应用包构建,请删除(注:在debug目录)现有的 Stage 应用包('+targetAPK.getAbsolutePath()+')。再编译一次。' + assert (!targetAPK.exists()) + // 可选择删除并继续输出APK文件 + //delete targetAPK + } + // 复制一个备份 + copy{ + from file.outputFile + into outTagDir + rename { + String fileName -> "${outputFileName}" + } + println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${outputFileName}" + } + // 复制一个并重命名为短版本名 + copy{ + from file.outputFile + into outTagDir + rename { + String fileName -> "${szCommonTagAPKName}" + } + println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${szCommonTagAPKName}" + } + + //不保存编译标志配置 + } + } + + // 如果配置了APK额外输出路径,就复制一份拷贝到额外路径。 + // + if(winbollProps != null && winbollProps['ExtraAPKOutputPath'] != null ) { + File apkFile = new File(winbollProps['ExtraAPKOutputPath']) + File outCommonDir = apkFile.getParentFile(); + String commandAPKName = apkFile.getName(); + if(outCommonDir.exists()) { + copy{ + from file.outputFile + into outCommonDir + rename { + String fileName -> "${commandAPKName}" + } + println "Output APK (Common): " + outCommonDir.getAbsolutePath() + "/${commandAPKName}" + } + } + } + + + } + }// End of (variant.getAssembleProvider().get().doLast {) + }/// WINBOLL 主机的 APK 发布和源码管理操作结束 /// + } + + } // End of (android.applicationVariants.all { variant ->) +} + diff --git a/.winboll/winboll_lib_build.gradle b/.winboll/winboll_lib_build.gradle new file mode 100644 index 0000000..f63aa52 --- /dev/null +++ b/.winboll/winboll_lib_build.gradle @@ -0,0 +1,211 @@ +// 本机和 WinBoLL Maven 仓库传输配置。 +// + +def getDefaultVersion(){ + // 检查编译标志位配置 + assert (winbollBuildProps['publishVersion'] != null) + // 返回编译版本号 + return winbollBuildProps['publishVersion'] +} + +def siteUrl = 'https://winboll.cc/?page=studio/details.php&app=${rootProject.name}' // 项目主页 +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@188.com' // 开发者邮箱地址 +def LicenseName='The Apache Software License, Version 2.0' +def LicenseUrl='http://www.apache.org/licenses/LICENSE-2.0.txt' + +Properties properties = new Properties() + +afterEvaluate { + publishing { + repositories { + if(file("${RootProjectDir}/${winbollFilePath}").exists()) { + properties.load(file("${RootProjectDir}/${winbollFilePath}").newDataInputStream()) + def NexusUserName = properties.getProperty("Nexus.name") + def NexusPassword = properties.getProperty("Nexus.password") + // WinBoLL Release 仓库 + maven{ + //仓库的名字和地址 + name = "WinBoLLRelease" + url="https://nexus.winboll.cc/repository/maven-releases/" + // 仓库用户名密码 + credentials { + username = NexusUserName + password = NexusPassword + } + } + // WinBoLL Snapshot 仓库 + maven{ + //仓库的名字和地址 + name = "WinBoLLSnapshot" + url="https://nexus.winboll.cc/repository/maven-snapshots/" + // 仓库用户名密码 + credentials { + username = NexusUserName + password = NexusPassword + } + } + } + } + publications { + // Local Maven 仓库传输任务 + // + release(MavenPublication) { + groupId = DefaultGroupId + artifactId = project.name + version = DefaultVersion + + //from components.java + // 必须有这个 否则不会上传AAR包 + afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) } + // 上传source,这样使用方可以看到方法注释 + //artifact generateSourcesJar + //要上传的aar路径 + //artifact "$buildDir/outputs/aar/${project.getName()}-release.aar" + //artifact "$buildDir/outputs/aar/${project.getName()}-debug.aar" + + //对pom进行的操作 + pom.withXml{ + Node pomNode = asNode() + pomNode.dependencies.'*'.findAll() { + //将所有的默认依赖移除 + //it.parent().remove(it) + } + } + pom { + name = artifactId + url = siteUrl + licenses { + license { //证书说明 + name=LicenseName // 开源协议名称 + url=LicenseUrl // 协议地址 + } + } + developers { + developer { + id=DeveloperId // 开发者账号 + name=DeveloperName // 开发者名称 + email=DeveloperEMail // 开发者邮箱地址 + } + } + //软件配置管理 + scm { + connection=gitUrl + developerConnection=gitUrl + url=siteUrl + } + } + } + + // WinBoLL Maven Release 仓库传输任务 + // + releaseWinBoLL(MavenPublication) { + // 需要使用的变体,假设有free和pay两个变体,可以选择一个 + //from components.free + + groupId = DefaultGroupId // 文件的groupId + artifactId = project.name //文件的名字 + version = DefaultVersion //版本号 + + //from components.java + // 必须有这个 否则不会上传AAR包 + afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) } + // 上传source,这样使用方可以看到方法注释 + //artifact generateSourcesJar + //要上传的aar路径 + //artifact "$buildDir/outputs/aar/${project.getName()}-release.aar" + //artifact "$buildDir/outputs/aar/${project.getName()}-debug.aar" + + //对pom进行的操作 + pom.withXml{ + Node pomNode = asNode() + pomNode.dependencies.'*'.findAll() { + //将所有的默认依赖移除 + //it.parent().remove(it) + } + } + pom { + name = artifactId + url = siteUrl + licenses { + license { //证书说明 + name=LicenseName // 开源协议名称 + url=LicenseUrl // 协议地址 + } + } + developers { + developer { + id=DeveloperId // 开发者账号 + name=DeveloperName // 开发者名称 + email=DeveloperEMail // 开发者邮箱地址 + } + } + //软件配置管理 + scm { + connection=gitUrl + developerConnection=gitUrl + url=siteUrl + } + } + + } // 创建名为 release 的任务结束 + + // WinBoLL Maven Snapshot 仓库传输任务 + // + snapshotWinBoLL(MavenPublication) { + // 需要使用的变体,假设有free和pay两个变体,可以选择一个 + //from components.free + + groupId = DefaultGroupId // 文件的groupId + artifactId = project.name //文件的名字 + version = DefaultVersion + "-SNAPSHOT" //版本号 + + //from components.java + // 必须有这个 否则不会上传AAR包 + afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) } + // 上传source,这样使用方可以看到方法注释 + //artifact generateSourcesJar + //要上传的aar路径 + //artifact "$buildDir/outputs/aar/${project.getName()}-release.aar" + //artifact "$buildDir/outputs/aar/${project.getName()}-debug.aar" + + //对pom进行的操作 + pom.withXml{ + Node pomNode = asNode() + pomNode.dependencies.'*'.findAll() { + //将所有的默认依赖移除 + //it.parent().remove(it) + } + } + pom { + name = artifactId + url = siteUrl + licenses { + license { //证书说明 + name=LicenseName // 开源协议名称 + url=LicenseUrl // 协议地址 + } + } + developers { + developer { + id=DeveloperId // 开发者账号 + name=DeveloperName // 开发者名称 + email=DeveloperEMail // 开发者邮箱地址 + } + } + //软件配置管理 + scm { + connection=gitUrl + developerConnection=gitUrl + url=siteUrl + } + } + } // 创建名为 snapshot 的任务结束 + } + + } +} diff --git a/.winboll/winboll_lint_build.gradle b/.winboll/winboll_lint_build.gradle new file mode 100644 index 0000000..b2ec440 --- /dev/null +++ b/.winboll/winboll_lint_build.gradle @@ -0,0 +1,50 @@ +android { + lintOptions { + // true--关闭lint报告的分析进度 + //quiet true + // true--错误发生后停止gradle构建 + abortOnError false + // true--只报告error + //ignoreWarnings true + // true--忽略有错误的文件的全/绝对路径(默认是true) + //absolutePaths true + // true--检查所有问题点,包含其他默认关闭项 + checkAllWarnings true + // true--所有warning当做error + //warningsAsErrors true + // 关闭指定问题检查 + //disable 'ExpiredTargetSdkVersion','HardcodedText','UnknownNullness','ButtonStyle','GradleDependency','UnusedResources' + // 打开指定问题检查 + //enable 'RtlHardcoded','RtlCompat', 'RtlEnabled' + // 仅检查指定问题 + //check 'NewApi', 'InlinedApi' + // true--error输出文件不包含源码行号 + //noLines true + // true--显示错误的所有发生位置,不截取 + showAll true + // 回退lint设置(默认规则) + //lintConfig file("default-lint.xml") + // true--生成txt格式报告(默认false) + //textReport true + // 重定向输出;可以是文件或'stdout' + //textOutput 'stdout' + // true--生成XML格式报告 + xmlReport true + // 指定xml报告文档(默认lint-results.xml) + xmlOutput file("${lintXmlReportFilePath}") + // true--生成HTML报告(带问题解释,源码位置,等) + htmlReport true + // html报告可选路径(构建器默认是lint-results.html ) + htmlOutput file("${lintHTMLReportFilePath}") + // true--所有正式版构建执行规则生成崩溃的lint检查,如果有崩溃问题将停止构建 + checkReleaseBuilds true + // 在发布版本编译时检查(即使不包含lint目标),指定问题的规则生成崩溃 + //fatal 'NewApi', 'InlineApi' + // 指定问题的规则生成错误 + //error 'Wakelock', 'TextViewEdits' + // 指定问题的规则生成警告 + //warning 'ResourceAsColor' + // 忽略指定问题的规则(同关闭检查) + //ignore 'TypographyQuotes' + } +} diff --git a/GenKeyStore/gen_debug_keystore.sh b/GenKeyStore/gen_debug_keystore.sh new file mode 100644 index 0000000..6ecd6ac --- /dev/null +++ b/GenKeyStore/gen_debug_keystore.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# 应用秘钥创建脚本 +# Linux 命令行创建JKS秘钥,alias和keyAlias可配置,文件名含时间戳 + +# 可配置参数(按需修改) +ALIAS="WinBoLL.CC_Debug" # 别名(与keyAlias一致) +STORE_PASS="androiddebugkey" +KEY_PASS="androiddebugkey" +COUNTRY="CN" # 国家代码 + +# 获取当前时间戳 +TIMESTAMP=$(date +%Y%m%d%H%M%S) +FILENAME="${ALIAS}_${TIMESTAMP}.jks" +STORENAME="${ALIAS}_${TIMESTAMP}.keystore" + +# 生成JKS文件(alias与keyAlias同步) +keytool -genkeypair \ + -alias "${ALIAS}" \ + -keyalg RSA \ + -keysize 2048 \ + -validity 1 \ + -keystore "${FILENAME}" \ + -dname "CN=WBFans, OU=Studio, O=WinBoLL, L=Shanwei, ST=Guangdong, C=${COUNTRY}" \ + -storepass "${STORE_PASS}" \ + -keypass "${KEY_PASS}" + +# 写入配置文件 +cat < ${STORENAME} +keyAlias=${ALIAS} +keyPassword=${KEY_PASS} +storeFile=../appkey.jks +storePassword=${STORE_PASS} +EOF + +echo "已生成秘钥:${FILENAME}" +echo "配置已写入 ${STORENAME}(keyAlias=${ALIAS})" + +# 询问是否复制文件 +read -p "是否需要将文件复制为 appkey.jks 和 appkey.keystore?(y/n): " CONFIRM + +if [[ $CONFIRM =~ ^[Yy]$ ]]; then + # 复制 jks 文件为 appkey.jks + cp -v ${FILENAME} ../appkey.jks + # 复制 keystore 文件为 appkey.keystore + cp -v ${STORENAME} ../appkey.keystore + echo "文件复制完成" +else + echo "已取消文件复制" +fi diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE-Private-Demo b/LICENSE-Private-Demo new file mode 100644 index 0000000..dc5f366 --- /dev/null +++ b/LICENSE-Private-Demo @@ -0,0 +1,97 @@ +# WinBoLL 源码 LICENSE-Private-Demo 规范说明书 + +# LICENSE-Private-Demo + +# WinBoLL 源码公共转私有继承开发规范守则 + +## 核心声明 + +本文档**唯一核心设计目的**:通过文件标识、分支隔离、操作规范、责任界定四重约束,**从根源规避私有开发分支代码被人为合并、推送、提交至公共开源主流分支的风险**,明确人为操作失误、违规合并的全部责任归属,同时保证私有分支可正常同步、拉取公共主流分支的上游更新。 + +## 一、文件宗旨与风险防控说明 + +本文件为 WinBoLL 项目公共开源分支转为私有独立分支开发的**强制标准化操作手册与责任界定文件**,核心风控目标: + +1. 严格隔离公共开源分支与私有开发分支,通过授权文件标记实现分支属性一眼可辨,杜绝人为操作混淆 + +2. **重点防控人为操作导致的私有分支代码违规合并、回合、推送至公共 ****`winboll`**** 主流分支**,从流程上封堵合并风险 + +3. 明确所有开发提交者的操作责任,违规合并公共分支的行为由操作人承担全部代码泄露、合规风险 + +4. 规范私有分支初始化全流程,保证私有分支仅可单向同步公共分支更新,禁止任何反向代码流入公共分支 + +## 二、公私分支授权标识文件定义(风控核心依据) + +### 1. 公共开源分支唯一标识 + +**文件名:LICENSE** + +- 仅允许存在于公共主流分支 `winboll` 及官方公共衍生分支 + +- 标识当前分支为**开源公开可贡献分支**,遵循原开源授权协议 + +- **严禁私有分支内保留、恢复此文件**,出现即判定分支属性异常 + +### 2. 私有开发分支唯一标识 + +**文件名:LICENSE-Private** + +- 仅允许存在于私有开发分支,**绝对禁止出现在公共 ****`winboll`**** 分支** + +- 标识当前分支为**私有闭源分支**,代码仅限内部使用,禁止公开、禁止对外贡献 + +- 为本分支私有属性的法定判定依据,也是禁止合并至公共分支的核心标记 + +## 三、分支管理与合并风控规则(强制遵守) + +1. **公共主流分支**:固定为 `winboll`,为项目唯一开源主线,仅保留 `LICENSE` 文件,**禁止接收任何私有分支的合并、提交、推送请求**。 + +2. **私有开发分支**:统一从 `winboll` 分支检出,命名固定格式为 `private-demo-*`,与公共分支物理隔离。 + +3. **核心合并风控铁则** + + - 私有分支 → 公共分支:**永久禁止任何形式的合并、推送、PR 提交、代码回合,人为操作也绝不允许** + + - 公共分支 → 私有分支:允许正常拉取、同步上游更新,不影响私有开发迭代 + +4. 所有仓库提交者、合并操作者,均视为已阅读并完全认可本规则,**人为执行私有分支向公共分支的合并操作,由操作人承担全部代码泄露、合规违约、项目安全风险**。 + +## 四、公共转私有标准化操作步骤(锁死合并风险) + +请严格按顺序执行,每一步均为风控必要环节,不可跳过、不可修改顺序。 + +1. 基于公共主流分支 `winboll`,新建私有开发分支,严格使用 `private-demo-*` 命名,从名称上明确分支私有属性,避免人为混淆。 + +2. 本地仓库切换至新建私有分支,确认当前分支名称、检出来源无误。 + +3. **永久删除项目根目录公共授权文件 ****`LICENSE`**,彻底移除公共分支标识,断绝误合并的标识漏洞。 + +4. 将本规范文件 `LICENSE-Private-Demo` 复制并重命名为 `LICENSE-Private`,作为私有分支生效授权文件。 + +5. 将以上所有变更执行一次性 Git 提交,**提交信息必须固定使用以下内容,不可修改**: + + > 初始化私有开发分支,已切换私有授权文件,本分支禁止任何人为合并、推送至 winboll 公共分支 + > + > + +6. 提交完成后,本分支正式转为私有开发状态,后续所有代码提交、分支合并、版本迭代,均严禁指向公共 `winboll` 分支。 + +## 五、人为操作责任界定(核心补充条款) + +1. 本分支所有开发者、代码提交者、分支合并操作者,均视为**完全知晓本分支的私有属性与合并禁令**,自愿遵守本规范全部约束。 + +2. **无论故意或过失,凡是人为执行私有分支向公共 ****`winboll`**** 分支的合并、推送、PR 提交、代码回合操作,全部责任由执行操作的本人独立承担**,项目方不承担任何因人为违规操作导致的代码泄露、开源合规、版本污染风险。 + +3. 仓库管理员需严格校验合并请求的分支标识与授权文件,发现带有 `LICENSE-Private` 标记的分支申请合并至公共分支,一律直接拒绝,并记录操作人信息。 + +4. 分支属性校验以根目录授权文件为唯一标准:只要分支内存在 `LICENSE-Private` 文件,就绝对禁止向公共分支发起任何合并操作。 + +## 六、分支状态校验与异常处理 + +- 合规公共分支:仅存在 `LICENSE`,无 `LICENSE-Private` + +- 合规私有分支:仅存在 `LICENSE-Private`,无 `LICENSE` + +- 异常状态:两个文件同时存在 / 均不存在 → 立即停止开发与提交,按本规范重置分支状态,严禁执行任何合并操作 + +> (注:文档部分内容可能由 AI 生成) diff --git a/LICENSE-Private-Demo_docs/LICENSE-Private-Demo b/LICENSE-Private-Demo_docs/LICENSE-Private-Demo new file mode 100644 index 0000000..dc5f366 --- /dev/null +++ b/LICENSE-Private-Demo_docs/LICENSE-Private-Demo @@ -0,0 +1,97 @@ +# WinBoLL 源码 LICENSE-Private-Demo 规范说明书 + +# LICENSE-Private-Demo + +# WinBoLL 源码公共转私有继承开发规范守则 + +## 核心声明 + +本文档**唯一核心设计目的**:通过文件标识、分支隔离、操作规范、责任界定四重约束,**从根源规避私有开发分支代码被人为合并、推送、提交至公共开源主流分支的风险**,明确人为操作失误、违规合并的全部责任归属,同时保证私有分支可正常同步、拉取公共主流分支的上游更新。 + +## 一、文件宗旨与风险防控说明 + +本文件为 WinBoLL 项目公共开源分支转为私有独立分支开发的**强制标准化操作手册与责任界定文件**,核心风控目标: + +1. 严格隔离公共开源分支与私有开发分支,通过授权文件标记实现分支属性一眼可辨,杜绝人为操作混淆 + +2. **重点防控人为操作导致的私有分支代码违规合并、回合、推送至公共 ****`winboll`**** 主流分支**,从流程上封堵合并风险 + +3. 明确所有开发提交者的操作责任,违规合并公共分支的行为由操作人承担全部代码泄露、合规风险 + +4. 规范私有分支初始化全流程,保证私有分支仅可单向同步公共分支更新,禁止任何反向代码流入公共分支 + +## 二、公私分支授权标识文件定义(风控核心依据) + +### 1. 公共开源分支唯一标识 + +**文件名:LICENSE** + +- 仅允许存在于公共主流分支 `winboll` 及官方公共衍生分支 + +- 标识当前分支为**开源公开可贡献分支**,遵循原开源授权协议 + +- **严禁私有分支内保留、恢复此文件**,出现即判定分支属性异常 + +### 2. 私有开发分支唯一标识 + +**文件名:LICENSE-Private** + +- 仅允许存在于私有开发分支,**绝对禁止出现在公共 ****`winboll`**** 分支** + +- 标识当前分支为**私有闭源分支**,代码仅限内部使用,禁止公开、禁止对外贡献 + +- 为本分支私有属性的法定判定依据,也是禁止合并至公共分支的核心标记 + +## 三、分支管理与合并风控规则(强制遵守) + +1. **公共主流分支**:固定为 `winboll`,为项目唯一开源主线,仅保留 `LICENSE` 文件,**禁止接收任何私有分支的合并、提交、推送请求**。 + +2. **私有开发分支**:统一从 `winboll` 分支检出,命名固定格式为 `private-demo-*`,与公共分支物理隔离。 + +3. **核心合并风控铁则** + + - 私有分支 → 公共分支:**永久禁止任何形式的合并、推送、PR 提交、代码回合,人为操作也绝不允许** + + - 公共分支 → 私有分支:允许正常拉取、同步上游更新,不影响私有开发迭代 + +4. 所有仓库提交者、合并操作者,均视为已阅读并完全认可本规则,**人为执行私有分支向公共分支的合并操作,由操作人承担全部代码泄露、合规违约、项目安全风险**。 + +## 四、公共转私有标准化操作步骤(锁死合并风险) + +请严格按顺序执行,每一步均为风控必要环节,不可跳过、不可修改顺序。 + +1. 基于公共主流分支 `winboll`,新建私有开发分支,严格使用 `private-demo-*` 命名,从名称上明确分支私有属性,避免人为混淆。 + +2. 本地仓库切换至新建私有分支,确认当前分支名称、检出来源无误。 + +3. **永久删除项目根目录公共授权文件 ****`LICENSE`**,彻底移除公共分支标识,断绝误合并的标识漏洞。 + +4. 将本规范文件 `LICENSE-Private-Demo` 复制并重命名为 `LICENSE-Private`,作为私有分支生效授权文件。 + +5. 将以上所有变更执行一次性 Git 提交,**提交信息必须固定使用以下内容,不可修改**: + + > 初始化私有开发分支,已切换私有授权文件,本分支禁止任何人为合并、推送至 winboll 公共分支 + > + > + +6. 提交完成后,本分支正式转为私有开发状态,后续所有代码提交、分支合并、版本迭代,均严禁指向公共 `winboll` 分支。 + +## 五、人为操作责任界定(核心补充条款) + +1. 本分支所有开发者、代码提交者、分支合并操作者,均视为**完全知晓本分支的私有属性与合并禁令**,自愿遵守本规范全部约束。 + +2. **无论故意或过失,凡是人为执行私有分支向公共 ****`winboll`**** 分支的合并、推送、PR 提交、代码回合操作,全部责任由执行操作的本人独立承担**,项目方不承担任何因人为违规操作导致的代码泄露、开源合规、版本污染风险。 + +3. 仓库管理员需严格校验合并请求的分支标识与授权文件,发现带有 `LICENSE-Private` 标记的分支申请合并至公共分支,一律直接拒绝,并记录操作人信息。 + +4. 分支属性校验以根目录授权文件为唯一标准:只要分支内存在 `LICENSE-Private` 文件,就绝对禁止向公共分支发起任何合并操作。 + +## 六、分支状态校验与异常处理 + +- 合规公共分支:仅存在 `LICENSE`,无 `LICENSE-Private` + +- 合规私有分支:仅存在 `LICENSE-Private`,无 `LICENSE` + +- 异常状态:两个文件同时存在 / 均不存在 → 立即停止开发与提交,按本规范重置分支状态,严禁执行任何合并操作 + +> (注:文档部分内容可能由 AI 生成) diff --git a/LICENSE-Private-Demo_docs/WinBoLL_源码_LICENSE-Private-Demo_规范说明书.docx b/LICENSE-Private-Demo_docs/WinBoLL_源码_LICENSE-Private-Demo_规范说明书.docx new file mode 100644 index 0000000..0859049 Binary files /dev/null and b/LICENSE-Private-Demo_docs/WinBoLL_源码_LICENSE-Private-Demo_规范说明书.docx differ diff --git a/LICENSE-Private-Demo_docs/WinBoLL_源码_LICENSE-Private-Demo_规范说明书.pdf b/LICENSE-Private-Demo_docs/WinBoLL_源码_LICENSE-Private-Demo_规范说明书.pdf new file mode 100644 index 0000000..10c3c11 Binary files /dev/null and b/LICENSE-Private-Demo_docs/WinBoLL_源码_LICENSE-Private-Demo_规范说明书.pdf differ diff --git a/LICENSE-Private-Demo_docs/WinBoLL_源码_LICENSE-Private-Demo_规范说明书.txt b/LICENSE-Private-Demo_docs/WinBoLL_源码_LICENSE-Private-Demo_规范说明书.txt new file mode 100644 index 0000000..e191e48 --- /dev/null +++ b/LICENSE-Private-Demo_docs/WinBoLL_源码_LICENSE-Private-Demo_规范说明书.txt @@ -0,0 +1,97 @@ +# WinBoLL 源码 LICENSE\-Private\-Demo 规范说明书 + +# LICENSE\-Private\-Demo + +# WinBoLL 源码公共转私有继承开发规范守则 + +## 核心声明 + +本文档**唯一核心设计目的**:通过文件标识、分支隔离、操作规范、责任界定四重约束,**从根源规避私有开发分支代码被人为合并、推送、提交至公共开源主流分支的风险**,明确人为操作失误、违规合并的全部责任归属,同时保证私有分支可正常同步、拉取公共主流分支的上游更新。 + +## 一、文件宗旨与风险防控说明 + +本文件为 WinBoLL 项目公共开源分支转为私有独立分支开发的**强制标准化操作手册与责任界定文件**,核心风控目标: + +1. 严格隔离公共开源分支与私有开发分支,通过授权文件标记实现分支属性一眼可辨,杜绝人为操作混淆 + +2. **重点防控人为操作导致的私有分支代码违规合并、回合、推送至公共 ****`winboll`**** 主流分支**,从流程上封堵合并风险 + +3. 明确所有开发提交者的操作责任,违规合并公共分支的行为由操作人承担全部代码泄露、合规风险 + +4. 规范私有分支初始化全流程,保证私有分支仅可单向同步公共分支更新,禁止任何反向代码流入公共分支 + +## 二、公私分支授权标识文件定义(风控核心依据) + +### 1\. 公共开源分支唯一标识 + +**文件名:LICENSE** + +- 仅允许存在于公共主流分支 `winboll` 及官方公共衍生分支 + +- 标识当前分支为**开源公开可贡献分支**,遵循原开源授权协议 + +- **严禁私有分支内保留、恢复此文件**,出现即判定分支属性异常 + +### 2\. 私有开发分支唯一标识 + +**文件名:LICENSE\-Private** + +- 仅允许存在于私有开发分支,**绝对禁止出现在公共 ****`winboll`**** 分支** + +- 标识当前分支为**私有闭源分支**,代码仅限内部使用,禁止公开、禁止对外贡献 + +- 为本分支私有属性的法定判定依据,也是禁止合并至公共分支的核心标记 + +## 三、分支管理与合并风控规则(强制遵守) + +1. **公共主流分支**:固定为 `winboll`,为项目唯一开源主线,仅保留 `LICENSE` 文件,**禁止接收任何私有分支的合并、提交、推送请求**。 + +2. **私有开发分支**:统一从 `winboll` 分支检出,命名固定格式为 `private\-demo\-\*`,与公共分支物理隔离。 + +3. **核心合并风控铁则** + + - 私有分支 → 公共分支:**永久禁止任何形式的合并、推送、PR 提交、代码回合,人为操作也绝不允许** + + - 公共分支 → 私有分支:允许正常拉取、同步上游更新,不影响私有开发迭代 + +4. 所有仓库提交者、合并操作者,均视为已阅读并完全认可本规则,**人为执行私有分支向公共分支的合并操作,由操作人承担全部代码泄露、合规违约、项目安全风险**。 + +## 四、公共转私有标准化操作步骤(锁死合并风险) + +请严格按顺序执行,每一步均为风控必要环节,不可跳过、不可修改顺序。 + +1. 基于公共主流分支 `winboll`,新建私有开发分支,严格使用 `private\-demo\-\*` 命名,从名称上明确分支私有属性,避免人为混淆。 + +2. 本地仓库切换至新建私有分支,确认当前分支名称、检出来源无误。 + +3. **永久删除项目根目录公共授权文件 ****`LICENSE`**,彻底移除公共分支标识,断绝误合并的标识漏洞。 + +4. 将本规范文件 `LICENSE\-Private\-Demo` 复制并重命名为 `LICENSE\-Private`,作为私有分支生效授权文件。 + +5. 将以上所有变更执行一次性 Git 提交,**提交信息必须固定使用以下内容,不可修改**: + + > 初始化私有开发分支,已切换私有授权文件,本分支禁止任何人为合并、推送至 winboll 公共分支 + > + > + +6. 提交完成后,本分支正式转为私有开发状态,后续所有代码提交、分支合并、版本迭代,均严禁指向公共 `winboll` 分支。 + +## 五、人为操作责任界定(核心补充条款) + +1. 本分支所有开发者、代码提交者、分支合并操作者,均视为**完全知晓本分支的私有属性与合并禁令**,自愿遵守本规范全部约束。 + +2. **无论故意或过失,凡是人为执行私有分支向公共 ****`winboll`**** 分支的合并、推送、PR 提交、代码回合操作,全部责任由执行操作的本人独立承担**,项目方不承担任何因人为违规操作导致的代码泄露、开源合规、版本污染风险。 + +3. 仓库管理员需严格校验合并请求的分支标识与授权文件,发现带有 `LICENSE\-Private` 标记的分支申请合并至公共分支,一律直接拒绝,并记录操作人信息。 + +4. 分支属性校验以根目录授权文件为唯一标准:只要分支内存在 `LICENSE\-Private` 文件,就绝对禁止向公共分支发起任何合并操作。 + +## 六、分支状态校验与异常处理 + +- 合规公共分支:仅存在 `LICENSE`,无 `LICENSE\-Private` + +- 合规私有分支:仅存在 `LICENSE\-Private`,无 `LICENSE` + +- 异常状态:两个文件同时存在 / 均不存在 → 立即停止开发与提交,按本规范重置分支状态,严禁执行任何合并操作 + +> (注:文档部分内容可能由 AI 生成) diff --git a/LICENSE-Private-Demo_docs/long_pic_1778650008421587_503089827979333.jpg b/LICENSE-Private-Demo_docs/long_pic_1778650008421587_503089827979333.jpg new file mode 100644 index 0000000..77b5918 Binary files /dev/null and b/LICENSE-Private-Demo_docs/long_pic_1778650008421587_503089827979333.jpg differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c681b4 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# WinBoLL 源生态计划项目说明书 + +## 一、项目概述 + +### 1. 核心定位 +WinBoLL 手机源码计划,旨在通过核心项目 WinBoLL 构建手机端与服务器端的 Android 项目的开发源码生态。实现手机与服务器的源码的联合开发。 + +### 2. 仓库架构 +#### **仓库类型:功能说明** +☆ 基础项目分支 WinBoLL:手机端安卓应用开发基础模板。 +☆ 应用项目分支 APPBase、AES、PowerBell、Positions**:安卓应用单一管理系列项目。 +☆ 源码汇总管理 OriginMaster**:各类分支源码合并存档,不适宜作为开发库使用。 + +### 3. 源码合并管理推送路线图 +⚠️ **注意**:仅仅展示不同应用模块源码的综合管理路线。分支合并操作时,必须具备 Git 管理经验。 + +★ WinBoLL → APPBase → OriginMaster +★ WinBoLL → AES → OriginMaster +★ WinBoLL → PowerBell → OriginMaster +★ WinBoLL → Positions → OriginMaster + +## 二、WinBoLL 项目核心信息 + +### 1. 项目简介 +☆ WinBoLL 项目是为手机端开发Android 项目的需求而设计的项目。 + +### 2. 官方资源 +#### ☆ 官方网站**:https://www.winboll.cc/ +#### ☆ 源码地址: +★ Gitea:https://gitea.winboll.cc/Studio/WinBoLL.git +★ GitHub:https://github.com/ZhanGSKen/WinBoLL.git +★ 码云:https://gitee.com/zhangsken/winboll.git + +## 三、应用编译环境检查问题 +### 核心判断条件: +☆ WinBoLL 项目以文件夹 `"/sdcard/WinBoLLStudio/APKs"` 是否存在为判断环境编译输出条件,因为编译输出的APK文件需要一个可供保存的环境。 + +☆ 文件夹"/sdcard/WinBoLLStudio/APKs" 目录条件设置方法: +***Linux 服务器端方面***:建立 `/sdcard/WinBoLLStudio/APKs` 目录即可。 +***手机开发端方面***:建立 `"/sdcard/WinBoLLStudio/APKs"` 目录(即 `"/storage/emulated/0/WinBoLLStudio/APKs"` 目录) 即可。 + +## 四、前置条件 + +### 1. WinBoLL APP 开发环境配置介绍 +#### WinBoLL APK 编译输出内容包括: +☆ "/sdcard/WinBoLLStudio/APKs"` 目录内的所有应用分支的 APK 文件。 +winboll.properties 文件的 APKOutputPath 属性可配置这个 APK 输出目录的路径。 +☆ "/sdcard/AppProjects/app.apk"文件。 +winboll.properties 文件的 ExtraAPKOutputPath 属性可配置这个 APK 额外输出文件的路径 +#### WinBoLL APK 源码命名空间规范 +☆ WinBoLL 项目使用 "cc.winboll.studio" 作为源码命名空间。在此命名空间下进行源码定义。 + +## 五、核心需求规划 + +### 1. WinBoLL 应用安全验证需求 +#### ☆ 支持访问 https://console.winboll.cc/ 服务器以校验应用包签名与版本。 + +### 2. 手机端源码开发管理需求 +#### ☆ 支持切换不同 WinBoLL 分支,以开发不同安卓应用。 + +## 六、编译与使用指南 + +### 1. 项目初始化(必须) +#### 1. 复制 `settings.gradle-demo` 为 `settings.gradle`。编辑 `settings.gradle` 文件内容,取消对应项目模块注释。 +#### 2. 复制 `gradle.properties-androidx-demo` (Android X 项目) 或 `gradle.properties-android-demo` (基本 Android 项目) 为 `gradle.properties`。 +#### 3. 复制(可选)`local.properties-demo` 为 `local.properties`,编辑 `local.properties` 文件内容,配置 Android SDK 目录。 +#### 4. **签名设置**: + ☆ **调试编译秘钥制作**:使用 Termux 应用终端,cd 进入 GenKeyStore 目录,运行 `bash gen_debug_keystore.sh` 脚本即可生成应用调试秘钥。 + ☆ **应用秘钥配置方法**:拷贝调试编译秘钥制作生成的 `appkey.jks` 与 `appkey.keystore` 文件到项目根目录即可。 + +## 七、应用编译命令介绍 + +### (1)类库型模块配置要点 +#### 1. **优先修改配置文件**:优先修改应用测试项目(目录为 `"/<类库测试应用>/"`)内 `build.properties` 文件,设置对应的类库项目名称:`libraryProject=<类库项目模块名>`。 +#### 2. **编译优先启动步骤**:使用 Termux 应用,进入 `""`,运行 `$ bash .winboll/bashPublishAPKAddTag.sh <类库测试项目模块名>` 命令。运行后可生成测试项目与类库项目的编译参数文件 `build.properties`。生成的 `build.properties` 文件有两份,一份在测试项目模块的文件夹内,一份在类库项目本身的模块文件夹内。 +#### 3. **最后类库编译发布步骤**:使用 Termux 应用,进入 `""`,运行 `$ bash .winboll/bashPublishLIBAddTag.sh <类库项目模块名>` 命令。运行后可发布至 WinBoLL Nexus Maven 库、本地 maven 目录或者是通用默认的 Gradle Maven 库。 + +### (2)单一应用型模块与类库测试型模块配置要点 +#### ☆ APK 编译方法: +使用 Termux 应用,进入 `""`,运行 `$ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块名>`。 +#### ☆ 运行后的 APK 输出路径: +★ 默认路径 (`$ bash gradlew assembleBetaDebug` 任务):APK 在 `/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/debug/` 目录。 +★ 默认路径 (`$ bash assembleStageRelease` 任务):APK 在 `/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/` 目录。 +★ 额外输出路径:(假设 `winboll.properties` 文件已配置 `ExtraAPKOutputPath` 属性) 输出至 `ExtraAPKOutputPath` 属性配置的目录下。 + +### (3)手机端应用调试命令介绍 +#### ☆ Beta 渠道调试命令 +$bash gradlew assembleBetaDebug + +#### ☆ Stage 渠道调试命令 +$bash gradlew assembleStageDebug + +### (4)服务器端开发命令介绍 +##### ☆ Stage 渠道应用发布命令为: +("/settings.gradle"文件需要配置编译模块开启参数,拷贝 settings.gradle-demo 为 settings.gradle 文件取消对应的分支配置部分即可。) +$bash .winboll/bashPublishAPKAddTag.sh <应用项目模块名>  +或者是 +$bash gradlew assembleStageRelease + + +## 八、WinBoLL 应用 APK 版本号命名规则 +### ☆ Stage 渠道: +#### V<应用开发环境编号><应用功能变更号><应用调试阶段号> (示例: APPBase_15.7.0 ) +### ☆ Beta 渠道: +#### V<应用开发环境编号><应用功能变更号><应用调试阶段号>-beta<调试编译计数>_<调试编译时间(分钟+秒钟)> (示例: APPBase_15.9.6-beta8_5413 ) diff --git a/aes/.gitignore b/aes/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/aes/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/aes/README.md b/aes/README.md new file mode 100644 index 0000000..1606637 --- /dev/null +++ b/aes/README.md @@ -0,0 +1,36 @@ +# AES +[![](https://jitpack.io/v/ZhanGSKen/AES.svg)](https://jitpack.io/#ZhanGSKen/AES) + +#### 介绍 +WinBoLL AndroidX 可视化元素类库。 + +#### 软件架构 +适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。 +也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。 + + +#### Gradle 编译说明 +调试版编译命令 :gradle assembleBetaDebug +阶段版编译命令 :bash .winboll/bashPublishAPKAddTag.sh aes +阶段版类库发布命令 :git pull &&bash .winboll/bashPublishLIBAddTag.sh libaes + +#### 使用说明 + +#### 参与贡献 + +1. Fork 本仓库 +2. 新建 Feat_xxx 分支 +3. 提交代码 : ZhanGSKen(ZhanGSKen) +4. 新建 Pull Request + + +#### 特技 + +1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md +2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) +3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 +4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 +5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) +6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) + +#### 参考文档 diff --git a/aes/app_update_description.txt b/aes/app_update_description.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/aes/app_update_description.txt @@ -0,0 +1 @@ + diff --git a/aes/build.gradle b/aes/build.gradle new file mode 100644 index 0000000..8039c8c --- /dev/null +++ b/aes/build.gradle @@ -0,0 +1,48 @@ +apply plugin: 'com.android.application' +apply from: '../.winboll/winboll_app_build.gradle' +apply from: '../.winboll/winboll_lint_build.gradle' + +def genVersionName(def versionName){ + // 检查编译标志位配置 + assert (winbollBuildProps['stageCount'] != null) + assert (winbollBuildProps['baseVersion'] != null) + // 保存基础版本号 + winbollBuildProps.setProperty("baseVersion", "${versionName}"); + //保存编译标志配置 + FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile) + winbollBuildProps.store(fos, "${winbollBuildPropsDesc}"); + fos.close(); + + // 返回编译版本号 + return "${versionName}." + winbollBuildProps['stageCount'] +} + +android { + // 适配MIUI12 + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "cc.winboll.studio.aes" + minSdkVersion 26 + targetSdkVersion 30 + versionCode 1 + // versionName 更新后需要手动设置 + // 项目模块目录的 build.gradle 文件的 stageCount=0 + // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" + versionName "15.20" + if(true) { + versionName = genVersionName("${versionName}") + } + } + + // 米盟 SDK + packagingOptions { + doNotStrip "*/*/libmimo_1011.so" + } +} + +dependencies { + api project(':libaes') + api fileTree(dir: 'libs', include: ['*.jar']) +} diff --git a/aes/build.properties b/aes/build.properties new file mode 100644 index 0000000..4e746cb --- /dev/null +++ b/aes/build.properties @@ -0,0 +1,8 @@ +#Created by .winboll/winboll_app_build.gradle +#Tue May 12 13:11:28 HKT 2026 +stageCount=4 +libraryProject=libaes +baseVersion=15.20 +publishVersion=15.20.3 +buildCount=0 +baseBetaVersion=15.20.4 diff --git a/aes/proguard-rules.pro b/aes/proguard-rules.pro new file mode 100644 index 0000000..a18de74 --- /dev/null +++ b/aes/proguard-rules.pro @@ -0,0 +1,137 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# ============================== 基础通用规则 ============================== +# 保留系统组件 +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference + +# 保留 WinBoLL 核心包及子类(合并简化规则) +-keep class cc.winboll.studio.** { *; } +-keepclassmembers class cc.winboll.studio.** { *; } + +# 保留所有类中的 public static final String TAG 字段(便于日志定位) +-keepclassmembers class * { + public static final java.lang.String TAG; +} + +# 保留序列化类(避免Parcelable/Gson解析异常) +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} +-keepclassmembers class * implements java.io.Serializable { + static final long serialVersionUID; + private static final java.io.ObjectStreamField[] serialPersistentFields; + private void writeObject(java.io.ObjectOutputStream); + private void readObject(java.io.ObjectInputStream); + java.lang.Object writeReplace(); + java.lang.Object readResolve(); +} + +# 保留 R 文件(避免资源ID混淆) +-keepclassmembers class **.R$* { + public static ; +} + +# 保留 native 方法(避免JNI调用失败) +-keepclasseswithmembernames class * { + native ; +} + +# 保留注解和泛型(避免反射/序列化异常) +-keepattributes *Annotation* +-keepattributes Signature + +# 屏蔽 Java 8+ 警告(适配 Java 7 语法) +-dontwarn java.lang.invoke.* +-dontwarn android.support.v8.renderscript.* +-dontwarn java.util.function.** + +# ============================== 第三方框架专项规则 ============================== +# OkHttp 4.4.1(米盟广告请求依赖,完善Lambda兼容) +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-keep class okhttp3.internal.** { *; } +-keep class okio.** { *; } +-dontwarn okhttp3.internal.platform.** +-dontwarn okio.** + +# Glide 4.9.0(米盟广告图片加载依赖) +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.module.AppGlideModule +-keep public enum com.bumptech.glide.load.ImageHeaderParser$ImageType { + **[] $VALUES; + public *; +} +-keepclassmembers class * implements com.bumptech.glide.module.AppGlideModule { + (); +} +-dontwarn com.bumptech.glide.** + +# Gson 2.8.5(米盟广告数据序列化依赖) +-keep class com.google.gson.** { *; } +-keep interface com.google.gson.** { *; } +-keepclassmembers class * { + @com.google.gson.annotations.SerializedName ; +} + +# 米盟 SDK(核心广告组件,完整保留避免加载失败) +-keep class com.miui.zeus.** { *; } +-keep interface com.miui.zeus.** { *; } +# 保留米盟日志字段(便于广告加载失败排查) +-keepclassmembers class com.miui.zeus.mimo.sdk.** { + public static final java.lang.String TAG; +} + +# RecyclerView 1.0.0(米盟广告布局渲染依赖) +-keep class androidx.recyclerview.** { *; } +-keep interface androidx.recyclerview.** { *; } +-keepclassmembers class androidx.recyclerview.widget.RecyclerView$Adapter { + public *; +} + +# 其他第三方框架(按引入依赖保留,无则可删除) +# XXPermissions 18.63 +-keep class com.hjq.permissions.** { *; } +-keep interface com.hjq.permissions.** { *; } + +# ZXing 二维码(核心解析组件) +-keep class com.google.zxing.** { *; } +-keep class com.journeyapps.zxing.** { *; } + +# Jsoup HTML解析 +-keep class org.jsoup.** { *; } + +# Pinyin4j 拼音搜索 +-keep class net.sourceforge.pinyin4j.** { *; } + +# JSch SSH组件 +-keep class com.jcraft.jsch.** { *; } + +# AndroidX 基础组件 +-keep class androidx.appcompat.** { *; } +-keep interface androidx.appcompat.** { *; } + +# ============================== 优化与调试配置 ============================== +# 优化级别(平衡混淆效果与性能) +-optimizationpasses 5 +-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* + +# 调试辅助(保留行号便于崩溃定位) +-verbose +-dontpreverify +-dontusemixedcaseclassnames +-keepattributes SourceFile,LineNumberTable + diff --git a/aes/src/beta/AndroidManifest.xml b/aes/src/beta/AndroidManifest.xml new file mode 100644 index 0000000..ee78d9f --- /dev/null +++ b/aes/src/beta/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/aes/src/beta/res/values/strings.xml b/aes/src/beta/res/values/strings.xml new file mode 100644 index 0000000..6f287f2 --- /dev/null +++ b/aes/src/beta/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + + AES+ + diff --git a/aes/src/main/AndroidManifest.xml b/aes/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c732afa --- /dev/null +++ b/aes/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/aes/src/main/java/cc/winboll/studio/aes/AboutActivity.java b/aes/src/main/java/cc/winboll/studio/aes/AboutActivity.java new file mode 100644 index 0000000..26c89bf --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/AboutActivity.java @@ -0,0 +1,78 @@ +package cc.winboll.studio.aes; + +import android.os.Bundle; +import android.view.View; +import androidx.appcompat.widget.Toolbar; +import cc.winboll.studio.aes.R; +import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.models.APPInfo; +import cc.winboll.studio.libappbase.views.AboutView; + +/** + * @Author 豆包&ZhanGSKen + * @Date 2026/01/13 11:25 + * @Describe 应用介绍窗口 + */ +public class AboutActivity extends BaseWinBoLLActivity { + + public static final String TAG = "AboutActivity"; + + private Toolbar mToolbar; + + @Override + public String getTag() { + return TAG; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_about); + + // 设置工具栏 + initToolbar(); + + AboutView aboutView = findViewById(R.id.aboutview); + aboutView.setAPPInfo(genDefaultAppInfo()); + } + + private void initToolbar() { + LogUtils.d(TAG, "initToolbar() 开始初始化"); + mToolbar = findViewById(R.id.toolbar); + if (mToolbar == null) { + LogUtils.e(TAG, "initToolbar() | Toolbar未找到"); + return; + } + setSupportActionBar(mToolbar); + mToolbar.setSubtitle(getTag()); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + mToolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LogUtils.d(TAG, "导航栏 点击返回按钮"); + WinBoLLActivityManager.getInstance().resumeActivity(MainActivity.class); + WinBoLLActivityManager.getInstance().finish(AboutActivity.this); + } + }); + LogUtils.d(TAG, "initToolbar() 配置完成"); + } + + private APPInfo genDefaultAppInfo() { + LogUtils.d(TAG, "genDefaultAppInfo() 调用"); + String branchName = "aes"; + APPInfo appInfo = new APPInfo(); + appInfo.setAppName(getString(R.string.app_name)); + appInfo.setAppIcon(R.drawable.ic_winboll); + appInfo.setAppDescription(getString(R.string.app_description)); + appInfo.setAppGitName("AES"); + appInfo.setAppGitOwner("Studio"); + appInfo.setAppGitAPPBranch(branchName); + appInfo.setAppGitAPPSubProjectFolder(branchName); + appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=AES"); + appInfo.setAppAPKName("AES"); + appInfo.setAppAPKFolderName("AES"); + LogUtils.d(TAG, "genDefaultAppInfo: 应用信息已生成"); + return appInfo; + } +} diff --git a/aes/src/main/java/cc/winboll/studio/aes/App.java b/aes/src/main/java/cc/winboll/studio/aes/App.java new file mode 100644 index 0000000..f6816f7 --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/App.java @@ -0,0 +1,34 @@ +package cc.winboll.studio.aes; + +/** + * @Author ZhanGSKen + * @Date 2024/06/13 19:03:58 + * @Describe AES应用类 + */ +import android.view.Gravity; +import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; +import cc.winboll.studio.libappbase.GlobalApplication; +import cc.winboll.studio.libappbase.ToastUtils; + + +public class App extends GlobalApplication { + + public static final String TAG = "App"; + + @Override + public void onCreate() { + super.onCreate(); + setIsDebugging(BuildConfig.DEBUG); + //setIsDebugging(false); + WinBoLLActivityManager.init(this); + + // 初始化 Toast 框架 + ToastUtils.init(this); + } + + @Override + public void onTerminate() { + super.onTerminate(); + ToastUtils.release(); + } +} diff --git a/aes/src/main/java/cc/winboll/studio/aes/BaseWinBoLLActivity.java b/aes/src/main/java/cc/winboll/studio/aes/BaseWinBoLLActivity.java new file mode 100644 index 0000000..0526f82 --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/BaseWinBoLLActivity.java @@ -0,0 +1,45 @@ +package cc.winboll.studio.aes; + +import android.app.Activity; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; +import cc.winboll.studio.libaes.models.AESThemeBean; +import cc.winboll.studio.libaes.utils.AESThemeUtil; +import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; + +/** + * @Author 豆包&ZhanGSKen + * @Date 2026/01/13 16:35 + * @Describe BaseWinBollActivity 【继承AppCompatActivity,保留核心能力,不额外暴露方法】 + * 继承链路:BaseWinBoLLActivity → AppCompatActivity → FragmentActivity,AppCompat能力天然继承可用 + */ +public abstract class BaseWinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity { + public static final String TAG = "BaseWinBoLLActivity"; + + protected volatile AESThemeBean.ThemeType mThemeType; + + @Override + protected void onCreate(Bundle savedInstanceState) { + mThemeType = AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext())); + setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext())); + super.onCreate(savedInstanceState); + WinBoLLActivityManager.getInstance().add(this); + } + + @Override + protected void onDestroy() { + WinBoLLActivityManager.getInstance().registeRemove(this); + super.onDestroy(); + } + + // 子类必须实现getTag(),确保唯一标识 + @Override + public abstract String getTag(); + + @Override + public Activity getActivity() { + return this; + } +} + diff --git a/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java b/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java new file mode 100644 index 0000000..927c852 --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java @@ -0,0 +1,196 @@ +package cc.winboll.studio.aes; + +/** + * @Author ZhanGSKen + * @Date 2024/06/13 19:05:52 + * @Describe 应用主窗口 + */ +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.Toast; +import cc.winboll.studio.aes.R; +import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity; +import cc.winboll.studio.libaes.dialogs.LocalFileSelectDialog; +import cc.winboll.studio.libaes.dialogs.StoragePathDialog; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; +import cc.winboll.studio.libaes.models.DrawerMenuBean; +import cc.winboll.studio.libaes.unittests.SecondaryLibraryActivity; +import cc.winboll.studio.libaes.unittests.TestAButtonFragment; +import cc.winboll.studio.libaes.unittests.TestASupportToolbarActivity; +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.libaes.utils.WinBoLLActivityManager; +import cc.winboll.studio.libappbase.LogUtils; +import com.a4455jkjh.colorpicker.ColorPickerDialog; +import java.util.ArrayList; + +public class MainActivity extends DrawerFragmentActivity { + + + public static final String TAG = "MainActivity"; + + TestAButtonFragment mTestAButtonFragment; + TestViewPageFragment mTestViewPageFragment; + + @Override + public String getTag() { + return TAG; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (mTestAButtonFragment == null) { + mTestAButtonFragment = new TestAButtonFragment(); + addFragment(mTestAButtonFragment); + } + showFragment(mTestAButtonFragment); + //setSubtitle(TAG); + //ToastUtils.show("onCreate"); + } + + @Override + public void initDrawerMenuItemList(ArrayList listDrawerMenu) { + super.initDrawerMenuItemList(listDrawerMenu); + LogUtils.d(TAG, "initDrawerMenuItemList"); + //listDrawerMenu.clear(); + // 添加抽屉菜单项 + listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestAButtonFragment.TAG)); + listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestViewPageFragment.TAG)); + notifyDrawerMenuDataChanged(); + } + + @Override + public void reinitDrawerMenuItemList(ArrayList listDrawerMenu) { + super.reinitDrawerMenuItemList(listDrawerMenu); + LogUtils.d(TAG, "reinitDrawerMenuItemList"); + //listDrawerMenu.clear(); + // 添加抽屉菜单项 + listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestAButtonFragment.TAG)); + listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestViewPageFragment.TAG)); + notifyDrawerMenuDataChanged(); + } + + @Override + public DrawerFragmentActivity.ActivityType initActivityType() { + return DrawerFragmentActivity.ActivityType.Main; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.toolbar_main, menu); +// if(App.isDebugging()) { +// getMenuInflater().inflate(cc.winboll.studio.libaes.R.menu.toolbar_studio_debug, menu); +// } + return super.onCreateOptionsMenu(menu); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + super.onItemClick(parent, view, position, id); + switch (position) { + case 0 : { + if (mTestAButtonFragment == null) { + mTestAButtonFragment = new TestAButtonFragment(); + addFragment(mTestAButtonFragment); + } + showFragment(mTestAButtonFragment); + break; + } + case 1 : { + if (mTestViewPageFragment == null) { + mTestViewPageFragment = new TestViewPageFragment(); + addFragment(mTestViewPageFragment); + } + showFragment(mTestViewPageFragment); + break; + } + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int nItemId = item.getItemId(); + if (item.getItemId() == R.id.item_testactivitymanager) { + WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, TestActivityManagerActivity.class); + //ToastUtils.show("item_testactivitymanager"); + } else + if (nItemId == R.id.item_atoast) { + Toast.makeText(getApplication(), "item_testatoast", Toast.LENGTH_SHORT).show(); + } else if (nItemId == R.id.item_atoolbar) { + Intent intent = new Intent(this, TestAToolbarActivity.class); + startActivity(intent); + + } else if (nItemId == R.id.item_asupporttoolbar) { + Intent intent = new Intent(this, TestASupportToolbarActivity.class); + startActivity(intent); + + } else if (nItemId == R.id.item_colordialog) { + ColorPickerDialog dlg = new ColorPickerDialog(this, getResources().getColor(R.color.colorPrimary)); + dlg.setOnColorChangedListener(new com.a4455jkjh.colorpicker.view.OnColorChangedListener() { + + @Override + public void beforeColorChanged() { + } + + @Override + public void onColorChanged(int color) { + + } + + @Override + public void afterColorChanged() { + } + + + }); + dlg.show(); + + } else if (nItemId == R.id.item_dialogstoragepath) { + final StoragePathDialog dialog = new StoragePathDialog(this, 0); + dialog.setOnOKClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dialog.dismiss(); + } + }); + dialog.show(); + + } else if (nItemId == R.id.item_localfileselectdialog) { + final LocalFileSelectDialog dialog = new LocalFileSelectDialog(this); + dialog.setOnOKClickListener(new LocalFileSelectDialog.OKClickListener() { + @Override + public void onOKClick(String sz) { + Toast.makeText(getApplication(), sz, Toast.LENGTH_SHORT).show(); + //dialog.dismiss(); + } + }); + dialog.open(); + + } else if (nItemId == R.id.item_secondarylibraryactivity) { + Intent intent = new Intent(this, SecondaryLibraryActivity.class); + startActivity(intent); + } else if (nItemId == R.id.item_drawerfragmentactivity) { + Intent intent = new Intent(this, TestDrawerFragmentActivity.class); + startActivity(intent); + } else if (nItemId == R.id.item_settings) { + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + } else if (nItemId == R.id.item_about) { +// Intent intent = new Intent(this, AboutActivity.class); +// startActivity(intent); + WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, AboutActivity.class); + } + + + return super.onOptionsItemSelected(item); + } + + +} diff --git a/aes/src/main/java/cc/winboll/studio/aes/SettingsActivity.java b/aes/src/main/java/cc/winboll/studio/aes/SettingsActivity.java new file mode 100644 index 0000000..1767c02 --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/SettingsActivity.java @@ -0,0 +1,39 @@ +package cc.winboll.studio.aes; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import cc.winboll.studio.libaes.views.ADsControlView; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/11/26 18:01 + * @Describe SettingsActivity + */ +public class SettingsActivity extends Activity { + + public static final String TAG = "SettingsActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_settings); + ADsControlView adsControlView = (ADsControlView) findViewById(R.id.ads_control_view); + +// adsControlView.setOnAdsModeSelectedListener(new ADsControlView.OnAdsModeSelectedListener() { +// @Override +// public void onModeSelected(ADsMode selectedMode) { +// if (selectedMode == ADsMode.STANDALONE) { +// // 处理单机模式逻辑(如释放米盟资源) +// ToastUtils.show("STANDALONE"); +// } else if (selectedMode == ADsMode.MIMO_SDK) { +// // 处理米盟SDK模式逻辑(如初始化SDK) +// ToastUtils.show("MIMO_SDK"); +// } +// } +// }); + } + +} diff --git a/aes/src/main/java/cc/winboll/studio/aes/TestActivityManagerActivity.java b/aes/src/main/java/cc/winboll/studio/aes/TestActivityManagerActivity.java new file mode 100644 index 0000000..71d114d --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/TestActivityManagerActivity.java @@ -0,0 +1,33 @@ +package cc.winboll.studio.aes; + +import android.app.Activity; +import android.os.Bundle; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/09/28 21:07 + * @Describe 窗口管理类测试窗口 + */ +public class TestActivityManagerActivity extends WinBoLLActivity implements IWinBoLLActivity { + + public static final String TAG = "TestActivityManagerActivity"; + + @Override + public Activity getActivity() { + return this; + } + + @Override + public String getTag() { + return TAG; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_testactivitymanager); + + } + +} diff --git a/aes/src/main/java/cc/winboll/studio/aes/WinBoLLActivity.java b/aes/src/main/java/cc/winboll/studio/aes/WinBoLLActivity.java new file mode 100644 index 0000000..bc45765 --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/WinBoLLActivity.java @@ -0,0 +1,60 @@ +package cc.winboll.studio.aes; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/09/29 00:11 + * @Describe WinBoLL 窗口基础类 + */ +import android.app.Activity; +import android.os.Bundle; +import android.view.MenuItem; +import androidx.appcompat.app.AppCompatActivity; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; +import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; +import cc.winboll.studio.libappbase.LogUtils; + +public class WinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity { + + public static final String TAG = "WinBoLLActivity"; + + @Override + public Activity getActivity() { + return this; + } + + @Override + public String getTag() { + return TAG; + } + + @Override + protected void onResume() { + super.onResume(); + LogUtils.d(TAG, String.format("onResume %s", getTag())); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + /*if (item.getItemId() == R.id.item_log) { + WinBoLLActivityManager.getInstance().startLogActivity(this); + return true; + } else if (item.getItemId() == R.id.item_home) { + startActivity(new Intent(this, MainActivity.class)); + return true; + }*/ + // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 + return super.onOptionsItemSelected(item); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + WinBoLLActivityManager.getInstance().add(this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + WinBoLLActivityManager.getInstance().finish(this); + } +} diff --git a/aes/src/main/res/drawable-night/bg_frame.xml b/aes/src/main/res/drawable-night/bg_frame.xml new file mode 100644 index 0000000..bf2fe54 --- /dev/null +++ b/aes/src/main/res/drawable-night/bg_frame.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + diff --git a/aes/src/main/res/drawable/bg_frame.xml b/aes/src/main/res/drawable/bg_frame.xml new file mode 100644 index 0000000..2f208a6 --- /dev/null +++ b/aes/src/main/res/drawable/bg_frame.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + diff --git a/aes/src/main/res/layout/activity_about.xml b/aes/src/main/res/layout/activity_about.xml new file mode 100644 index 0000000..5cbe3fd --- /dev/null +++ b/aes/src/main/res/layout/activity_about.xml @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/aes/src/main/res/layout/activity_settings.xml b/aes/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..99dee06 --- /dev/null +++ b/aes/src/main/res/layout/activity_settings.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/aes/src/main/res/layout/activity_testactivitymanager.xml b/aes/src/main/res/layout/activity_testactivitymanager.xml new file mode 100644 index 0000000..f46d824 --- /dev/null +++ b/aes/src/main/res/layout/activity_testactivitymanager.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/aes/src/main/res/menu/toolbar_main.xml b/aes/src/main/res/menu/toolbar_main.xml new file mode 100644 index 0000000..b87657a --- /dev/null +++ b/aes/src/main/res/menu/toolbar_main.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/aes/src/main/res/values-night/styles.xml b/aes/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..5ac0e76 --- /dev/null +++ b/aes/src/main/res/values-night/styles.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/aes/src/main/res/values/colors.xml b/aes/src/main/res/values/colors.xml new file mode 100644 index 0000000..87d3836 --- /dev/null +++ b/aes/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #FF00B322 + #FF005C12 + #FF8DFFA2 + #FFFFFB8D + diff --git a/aes/src/main/res/values/strings.xml b/aes/src/main/res/values/strings.xml new file mode 100644 index 0000000..664f43a --- /dev/null +++ b/aes/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + + + AES + WinBoLL AndroidX 可视化元素类库。 + + diff --git a/aes/src/main/res/values/styles.xml b/aes/src/main/res/values/styles.xml new file mode 100644 index 0000000..267068a --- /dev/null +++ b/aes/src/main/res/values/styles.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/aes/src/main/res/xml/network_security_config.xml b/aes/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..ee39aa4 --- /dev/null +++ b/aes/src/main/res/xml/network_security_config.xml @@ -0,0 +1,6 @@ + + + + winboll.cc + + diff --git a/aes/src/stage/AndroidManifest.xml b/aes/src/stage/AndroidManifest.xml new file mode 100644 index 0000000..ee78d9f --- /dev/null +++ b/aes/src/stage/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/aes/src/stage/res/values/strings.xml b/aes/src/stage/res/values/strings.xml new file mode 100644 index 0000000..ace0c41 --- /dev/null +++ b/aes/src/stage/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/appbase/.gitignore b/appbase/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/appbase/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/appbase/README.md b/appbase/README.md new file mode 100644 index 0000000..97cdffc --- /dev/null +++ b/appbase/README.md @@ -0,0 +1,36 @@ +# APPBase +[![](https://jitpack.io/v/ZhanGSKen/APPBase.svg)](https://jitpack.io/#ZhanGSKen/APPBase) + +#### 介绍 +WinBoLL 安卓手机端安卓应用开发基础类库。 + +#### 软件架构 +适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。 +也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。 + + +#### Gradle 编译说明 +调试版编译命令 :gradle assembleBetaDebug +阶段版编译命令 :bash .winboll/bashPublishAPKAddTag.sh appbase +阶段版类库发布命令 :git pull &&bash .winboll/bashPublishLIBAddTag.sh libappbase + +#### 使用说明 + +#### 参与贡献 + +1. Fork 本仓库 +2. 新建 Feat_xxx 分支 +3. 提交代码 : ZhanGSKen(ZhanGSKen) +4. 新建 Pull Request + + +#### 特技 + +1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md +2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) +3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 +4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 +5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) +6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) + +#### 参考文档 diff --git a/appbase/app_update_description.txt b/appbase/app_update_description.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/appbase/app_update_description.txt @@ -0,0 +1 @@ + diff --git a/appbase/build.gradle b/appbase/build.gradle new file mode 100644 index 0000000..b95d796 --- /dev/null +++ b/appbase/build.gradle @@ -0,0 +1,50 @@ +apply plugin: 'com.android.application' +apply from: '../.winboll/winboll_app_build.gradle' +apply from: '../.winboll/winboll_lint_build.gradle' + +def genVersionName(def versionName){ + // 检查编译标志位配置 + assert (winbollBuildProps['stageCount'] != null) + assert (winbollBuildProps['baseVersion'] != null) + // 保存基础版本号 + winbollBuildProps.setProperty("baseVersion", "${versionName}"); + //保存编译标志配置 + FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile) + winbollBuildProps.store(fos, "${winbollBuildPropsDesc}"); + fos.close(); + + // 返回编译版本号 + return "${versionName}." + winbollBuildProps['stageCount'] +} + +android { + // 适配MIUI12 + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "cc.winboll.studio.appbase" + minSdkVersion 26 + targetSdkVersion 30 + versionCode 1 + // versionName 更新后需要手动设置 + // .winboll/winbollBuildProps.properties 文件的 stageCount=0 + // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" + versionName "15.20" + if(true) { + versionName = genVersionName("${versionName}") + } + } + + // 确保 Java 7 兼容性(已适配项目技术栈) + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } +} + +dependencies { + api project(':libappbase') + + api fileTree(dir: 'libs', include: ['*.jar']) +} diff --git a/appbase/build.properties b/appbase/build.properties new file mode 100644 index 0000000..68642b1 --- /dev/null +++ b/appbase/build.properties @@ -0,0 +1,8 @@ +#Created by .winboll/winboll_app_build.gradle +#Tue May 12 09:17:15 HKT 2026 +stageCount=10 +libraryProject=libappbase +baseVersion=15.20 +publishVersion=15.20.9 +buildCount=0 +baseBetaVersion=15.20.10 diff --git a/appbase/proguard-rules.pro b/appbase/proguard-rules.pro new file mode 100644 index 0000000..574eeec --- /dev/null +++ b/appbase/proguard-rules.pro @@ -0,0 +1,126 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# ============================== 基础通用规则 ============================== +# 保留系统组件 +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference + +# 保留 WinBoLL 核心包及子类(适配你的两个包名) +#-keep public class * extends com.winboll.WinBoLLActivity +#-keep public class * extends com.winboll.WinBoLLFragment +# 主包名 +-keep class cc.winboll.studio.*.** { *; } +# beta包名 +-keep class cc.winboll.studio.*.beta.** { *; } +-keepclassmembers class cc.winboll.studio.*.** { *; } +-keepclassmembers class cc.winboll.studio.*.beta.** { *; } + +# 保留所有类中的 public static final String TAG 字段 +-keepclassmembers class * { + public static final java.lang.String TAG; +} + +# 保留序列化类 +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} +-keepclassmembers class * implements java.io.Serializable { + static final long serialVersionUID; + private static final java.io.ObjectStreamField[] serialPersistentFields; + private void writeObject(java.io.ObjectOutputStream); + private void readObject(java.io.ObjectInputStream); + java.lang.Object writeReplace(); + java.lang.Object readResolve(); +} + +# 保留 R 文件 +-keepclassmembers class **.R$* { + public static ; +} + +# 保留 native 方法 +-keepclasseswithmembernames class * { + native ; +} + +# 保留注解和泛型 +-keepattributes *Annotation* +-keepattributes Signature + +# 屏蔽 Java 8+ 警告(适配 Java 7) +-dontwarn java.lang.invoke.* +-dontwarn android.support.v8.renderscript.* +-dontwarn java.util.function.** + +# ============================== 第三方框架规则 ============================== +# Retrofit + OkHttp +-keep class retrofit2.** { *; } +-keep interface retrofit2.** { *; } +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-keep class okio.** { *; } +-keepclasseswithmembers class * { + @retrofit2.http.* ; +} + +# Glide 4.x +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.module.AppGlideModule +-keep public enum com.bumptech.glide.load.ImageHeaderParser$ImageType { + **[] $VALUES; + public *; +} +-dontwarn com.bumptech.glide.load.resource.bitmap.VideoDecoder + +# GreenDAO 3.x +-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao { + public static java.lang.String TABLENAME; +} +-keep class **$Properties +# 实体类包名(按实际调整) +#-keep class cc.winboll.studio.appbase.model.** { *; } + +# ButterKnife 8.x +-keep class butterknife.** { *; } +-dontwarn butterknife.internal.** +-keep class **$$ViewBinder { *; } +-keepclasseswithmembernames class * { + @butterknife.BindView ; + @butterknife.OnClick ; +} + +# EventBus 3.x +-keepclassmembers class ** { + @org.greenrobot.eventbus.Subscribe ; +} +-keep enum org.greenrobot.eventbus.ThreadMode { *; } + +# ============================== 优化与调试 ============================== +-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* +-optimizationpasses 5 +-verbose +-dontpreverify +-dontusemixedcaseclassnames +# 保留行号(便于崩溃定位) +-keepattributes SourceFile,LineNumberTable + diff --git a/appbase/src/beta/AndroidManifest.xml b/appbase/src/beta/AndroidManifest.xml new file mode 100644 index 0000000..32817e8 --- /dev/null +++ b/appbase/src/beta/AndroidManifest.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/appbase/src/beta/res/values/strings.xml b/appbase/src/beta/res/values/strings.xml new file mode 100644 index 0000000..49fc4c9 --- /dev/null +++ b/appbase/src/beta/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + APPBase+ + + diff --git a/appbase/src/main/AndroidManifest.xml b/appbase/src/main/AndroidManifest.xml new file mode 100644 index 0000000..4c0df09 --- /dev/null +++ b/appbase/src/main/AndroidManifest.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/AboutActivity.java b/appbase/src/main/java/cc/winboll/studio/appbase/AboutActivity.java new file mode 100644 index 0000000..060bded --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/AboutActivity.java @@ -0,0 +1,50 @@ +package cc.winboll.studio.appbase; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.Toolbar; +import cc.winboll.studio.appbase.R; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.models.APPInfo; +import cc.winboll.studio.libappbase.views.AboutView; + +/** + * @Author 豆包&ZhanGSKen + * @Date 2026/01/11 12:55 + * @Describe AboutActivity + */ +public class AboutActivity extends Activity { + + public static final String TAG = "AboutActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_about); + + Toolbar toolbar = findViewById(R.id.toolbar); + setActionBar(toolbar); + + AboutView aboutView = findViewById(R.id.aboutview); + aboutView.setAPPInfo(genDefaultAppInfo()); + } + + private APPInfo genDefaultAppInfo() { + LogUtils.d(TAG, "genDefaultAppInfo() 调用"); + String branchName = "appbase"; + APPInfo appInfo = new APPInfo(); + appInfo.setAppName("APPBase"); + appInfo.setAppIcon(R.drawable.ic_winboll); + appInfo.setAppDescription(getString(R.string.app_description)); + appInfo.setAppGitName("WinBoLL"); + appInfo.setAppGitOwner("Studio"); + appInfo.setAppGitAPPBranch(branchName); + appInfo.setAppGitAPPSubProjectFolder(branchName); + appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=APPBase"); + appInfo.setAppAPKName("APPBase"); + appInfo.setAppAPKFolderName("APPBase"); + LogUtils.d(TAG, "genDefaultAppInfo: 应用信息已生成"); + return appInfo; + } +} diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/App.java b/appbase/src/main/java/cc/winboll/studio/appbase/App.java new file mode 100644 index 0000000..9df2ed8 --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/App.java @@ -0,0 +1,48 @@ +package cc.winboll.studio.appbase; + +import cc.winboll.studio.libappbase.GlobalApplication; +import cc.winboll.studio.libappbase.ToastUtils; +import cc.winboll.studio.libappbase.BuildConfig; + +/** + * @Author ZhanGSKen + * @Date 2025/01/05 09:54:42 + * @Describe 应用全局入口类(继承基础库 GlobalApplication) + * 负责应用初始化、全局资源管理与生命周期回调处理,是整个应用的核心入口 + */ +public class App extends GlobalApplication { + + /** 当前应用类的日志 TAG(用于调试输出,标识日志来源) */ + public static final String TAG = "App"; + + /** + * 应用创建时回调(全局初始化入口) + * 在应用进程启动时执行,仅调用一次,用于初始化全局工具类、第三方库等 + */ + @Override + public void onCreate() { + super.onCreate(); + // 如果应用不在调试状态,就根据编译类型设置调试状态 + if (isDebugging() != true) { + setIsDebugging(BuildConfig.DEBUG); + } + // release 版调试码 + //setIsDebugging(!BuildConfig.DEBUG); + + // 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用) + ToastUtils.init(getApplicationContext()); + } + + /** + * 应用终止时回调(资源释放入口) + * 仅在模拟环境(如 Android Studio 模拟器)中可靠触发,真机上可能因系统回收进程不执行 + * 用于释放全局资源,避免内存泄漏 + */ + @Override + public void onTerminate() { + super.onTerminate(); // 调用父类终止逻辑(如基础库资源释放) + // 释放 Toast 工具类资源(销毁全局 Toast 实例,避免内存泄漏) + ToastUtils.release(); + } +} + diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/CrashTestActivity.java b/appbase/src/main/java/cc/winboll/studio/appbase/CrashTestActivity.java new file mode 100644 index 0000000..6f2a687 --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/CrashTestActivity.java @@ -0,0 +1,28 @@ +package cc.winboll.studio.appbase; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.ToastUtils; + +public class CrashTestActivity extends Activity { + + public static final String TAG = "CrashTestActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_crash_test); + LogUtils.d(TAG, "CrashTestActivity onCreate()"); + } + + public void onBack(View view) { + finish(); + } + + public void onTestCrash(View view) { + LogUtils.d(TAG, "onTestCrash()"); + ToastUtils.show("测试布局崩溃..."); + } +} diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/Main2Activity.java b/appbase/src/main/java/cc/winboll/studio/appbase/Main2Activity.java new file mode 100644 index 0000000..1b989f7 --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/Main2Activity.java @@ -0,0 +1,20 @@ +package cc.winboll.studio.appbase; + +import android.os.Bundle; +import android.widget.Toolbar; +import cc.winboll.studio.appbase.R; + +public class Main2Activity extends MainActivity { + + public static final String TAG = "Main2Activity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main2); + Toolbar toolbar = findViewById(R.id.toolbar); + if (toolbar != null) { + setActionBar(toolbar); + } + } +} \ No newline at end of file diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java b/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java new file mode 100644 index 0000000..96b99c4 --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java @@ -0,0 +1,177 @@ +package cc.winboll.studio.appbase; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +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.model.TestBean; +import cc.winboll.studio.libappbase.LogActivity; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.ToastUtils; + +/** + * @Author ZhanGSKen + * @Date 未标注(建议补充创建日期) + * @Describe 应用主界面 Activity(入口界面) + * 包含功能测试按钮(崩溃测试、日志查看、Toast测试)、顶部工具栏(菜单功能),是应用交互的核心入口 + */ +public class MainActivity extends Activity { + + /** 当前 Activity 的日志 TAG(用于调试输出,标识日志来源) */ + public static final String TAG = "MainActivity"; + + /** 顶部工具栏(用于展示标题、菜单,绑定布局中的 Toolbar 控件) */ + private Toolbar mToolbar; + + /** + * Activity 创建时回调(初始化界面) + * 在 Activity 首次创建时执行,用于加载布局、初始化控件、设置事件监听 + * @param savedInstanceState 保存 Activity 状态的 Bundle(如屏幕旋转时的数据恢复) + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //ToastUtils.show("onCreate"); // 显示 Activity 创建提示(调试用) + setContentView(R.layout.activity_main); // 加载主界面布局 + + // 初始化 Toolbar 并设置为 ActionBar + mToolbar = findViewById(R.id.toolbar); + setActionBar(mToolbar); // 将 Toolbar 替代系统默认 ActionBar + + initTestData(); + } + + void initTestData() { + TestBean bean1 = new TestBean(); + bean1.setTestNum1(456); + TestBean.saveBeanToFile(getFilesDir().getAbsolutePath() + getTestBeanRelativePath(), bean1); + TestBean bean2 = new TestBean(); + bean2.setTestNum1(789); + TestBean.saveBeanToFile(getExternalFilesDir(null).getAbsolutePath() + getTestBeanRelativePath(), bean2); + } + + String getTestBeanRelativePath() { + return "/BaseBaen/"+TestBean.class.getName()+".json"; + } + + /** + * 创建菜单时回调(加载工具栏菜单) + * 初始化 ActionBar 菜单,加载自定义菜单布局 + * @param menu 菜单对象(用于承载菜单项) + * @return true:显示菜单;false:不显示菜单 + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // 加载菜单布局(R.menu.toolbar_main 为自定义菜单文件) + getMenuInflater().inflate(R.menu.toolbar_main, menu); + return super.onCreateOptionsMenu(menu); + } + + /** + * 菜单 item 点击时回调(处理菜单事件) + * 响应 Toolbar 菜单项的点击事件,执行对应业务逻辑 + * @param item 被点击的菜单项 + * @return true:消费点击事件;false:不消费(传递给父类) + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.item_home: + // 点击 "首页/官网" 菜单项,唤起浏览器打开指定网站 + openWebsiteInBrowser(this); + break; + // 可扩展其他菜单项(如设置、关于等)的处理逻辑 + } + return super.onOptionsItemSelected(item); + } + + /** + * 崩溃测试按钮点击事件(触发应用崩溃,用于调试异常捕获) + * 故意执行非法操作(循环获取不存在的字符串资源),强制应用崩溃 + * @param view 触发事件的 View(对应布局中的崩溃测试按钮) + */ + public void onCrashTest(View view) { + // 循环从 Integer.MIN_VALUE 到 Integer.MAX_VALUE,获取不存在的字符串资源 ID,触发崩溃 + for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { + getString(i); // i 超出资源 ID 范围,抛出 Resources.NotFoundException 导致崩溃 + } + } + + public void onLogTestNewTask(View view) { + LogActivity.startLogActivity(this, true); + } + + /** + * 日志测试按钮点击事件(打开日志查看界面) + * 启动 LogActivity,用于查看应用运行日志 + * @param view 触发事件的 View(对应布局中的日志测试按钮) + */ + public void onLogTest(View view) { + LogActivity.startLogActivity(this, false); + } + + /** + * Toast 工具测试按钮点击事件(测试全局 Toast 功能) + * 测试主线程、子线程中 Toast 的显示效果,验证 ToastUtils 的可用性 + * @param view 触发事件的 View(对应布局中的 Toast 测试按钮) + */ + public void onToastUtilsTest(View view) { + LogUtils.d(TAG, "onToastUtilsTest"); // 打印调试日志,标识进入 Toast 测试 + ToastUtils.show("Hello, WinBoLL!"); // 主线程显示 Toast + + // 开启子线程,延迟 2 秒后显示 Toast(测试子线程 Toast 兼容性) + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(2000); // 线程休眠 2 秒 + // 若 ToastUtils 已处理主线程切换,此处可直接调用;否则需通过 Handler 切换到主线程 + ToastUtils.show("Thread.sleep(2000);ToastUtils.show..."); + } catch (InterruptedException e) { + // 捕获线程中断异常(如线程被销毁时),不做处理(测试场景) + e.printStackTrace(); + } + } + }).start(); + } + + /** + * 唤起系统默认浏览器打开指定网站(跳转至应用官网) + * 通过 Intent.ACTION_VIEW 隐式意图,触发浏览器打开目标 URL + * @param context 上下文对象(如 Activity、Application,此处为 MainActivity) + */ + public void openWebsiteInBrowser(Context context) { + String url = "https://www.winboll.cc"; // 目标网站 URL(应用官网) + // 构建隐式意图:ACTION_VIEW 表示查看指定数据(Uri 为网站地址) + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + // 设置标志:在新的任务栈中启动 Activity(避免与当前应用任务栈混淆) + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // 启动意图(唤起浏览器) + context.startActivity(intent); + } + + public void onAboutActivity(View view) { + LogUtils.d(TAG, "onAboutActivity() 调用"); + Intent aboutIntent = new Intent(getApplicationContext(), AboutActivity.class); + startActivity(aboutIntent); + } + + + + public void onMultiInstance(View view) { + LogUtils.d(TAG, "onMultiInstance() 多开窗口按钮已点击"); + ToastUtils.show("多开窗口:已启动新窗口"); + android.content.Intent intent = new android.content.Intent(this, Main2Activity.class); + intent.setFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK); + LogUtils.d(TAG, "onMultiInstance() 准备启动Main2Activity"); + startActivity(intent); + LogUtils.d(TAG, "onMultiInstance() Main2Activity已启动"); + } +} + diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/model/TestBean.java b/appbase/src/main/java/cc/winboll/studio/appbase/model/TestBean.java new file mode 100644 index 0000000..ee3e70a --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/model/TestBean.java @@ -0,0 +1,154 @@ +package cc.winboll.studio.appbase.model; + +import android.util.JsonReader; +import android.util.JsonWriter; +import cc.winboll.studio.libappbase.BaseBean; +import cc.winboll.studio.libappbase.LogUtils; +import java.io.IOException; + +/** + * 测试实体类 + * 继承BaseBean实现JSON序列化/反序列化能力,提供基础int类型属性的封装与数据持久化支持 + * 适配Java7语法,遵循BaseBean统一的反射识别、JSON读写规范 + * @Author 豆包&ZhanGSKen + * @Date 2026/01/31 19:16:00 + * @LastEditTime 2026/02/01 10:46:00 + */ +public class TestBean extends BaseBean { + + // ====================================== 常量定义 ====================================== + /** 当前类的日志 TAG(用于调试输出) */ + public static final String TAG = "TestBean"; + + // ====================================== 成员属性 ====================================== + /** + * 测试数字属性(默认值:123) + * 基础int类型属性,用于测试BaseBean的JSON序列化/反序列化能力 + */ + private int testNum1; + + // ====================================== 构造方法 ====================================== + /** + * 无参构造器(默认初始化) + * 给testNum1赋值默认值123,满足反射实例化、JSON解析的无参构造要求 + */ + public TestBean() { + this.testNum1 = 123; + LogUtils.d(TAG, "TestBean无参构造器调用,testNum1默认初始化值:" + this.testNum1); + } + + /** + * 有参构造器(自定义初始化) + * @param testNum1 测试数字初始值 + */ + public TestBean(int testNum1) { + this.testNum1 = testNum1; + LogUtils.d(TAG, "TestBean有参构造器调用,传入testNum1:" + testNum1); + } + + // ====================================== Get/Set 方法 ====================================== + /** + * 设置测试数字属性值 + * @param testNum1 待设置的int类型值 + */ + public void setTestNum1(int testNum1) { + LogUtils.d(TAG, "setTestNum1调用,传入参数:" + testNum1); + this.testNum1 = testNum1; + } + + /** + * 获取测试数字属性值 + * @return 当前testNum1的int类型值 + */ + public int getTestNum1() { + LogUtils.d(TAG, "getTestNum1调用,返回值:" + this.testNum1); + return testNum1; + } + + // ====================================== 重写父类BaseBean方法 ====================================== + /** + * 重写父类方法:获取当前类的全限定名 + * 用于BaseBean反射识别、类名匹配等统一逻辑 + * @return 类全限定名(cc.winboll.studio.appbase.model.TestBean) + */ + @Override + public String getName() { + LogUtils.d(TAG, "getName方法调用,返回类全限定名:" + TestBean.class.getName()); + return TestBean.class.getName(); + } + + /** + * 重写父类方法:将当前对象序列化为JSON(持久化存储专用) + * 遵循BaseBean规范,先执行父类序列化逻辑,再处理子类专属字段 + * @param jsonWriter JSON写入器(外部传入的JSON流操作实例) + * @throws IOException JSON写入异常(流关闭、格式错误等) + */ + @Override + public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { + LogUtils.d(TAG, "writeThisToJsonWriter调用,传入参数JsonWriter:" + jsonWriter); + // 执行父类公共字段的序列化逻辑 + super.writeThisToJsonWriter(jsonWriter); + // 序列化子类专属字段testNum1 + jsonWriter.name("testNum1").value(this.getTestNum1()); + LogUtils.d(TAG, "writeThisToJsonWriter执行完成,已序列化testNum1:" + this.getTestNum1()); + } + + /** + * 重写父类方法:从JSON字段初始化当前对象属性(解析JSON专用) + * 先让父类处理公共字段,再匹配子类专属字段,不匹配则返回false跳过 + * @param jsonReader JSON读取器(外部传入的JSON流操作实例) + * @param name 当前解析的JSON字段名 + * @return true-字段解析成功;false-字段不匹配,需跳过/父类处理 + * @throws IOException JSON读取异常(字段类型不匹配、流中断等) + */ + @Override + public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException { + LogUtils.d(TAG, "initObjectsFromJsonReader调用,传入参数:name=" + name + ",JsonReader=" + jsonReader); + // 父类优先处理公共字段,处理成功则直接返回 + if (super.initObjectsFromJsonReader(jsonReader, name)) { + LogUtils.d(TAG, "initObjectsFromJsonReader:字段" + name + "由父类BaseBean处理成功"); + return true; + } + // 解析子类专属字段 + if ("testNum1".equals(name)) { + this.setTestNum1(jsonReader.nextInt()); + LogUtils.d(TAG, "initObjectsFromJsonReader:解析testNum1成功,值为:" + this.getTestNum1()); + } else { + LogUtils.w(TAG, "initObjectsFromJsonReader:字段" + name + "不匹配,返回false跳过解析"); + // 字段不匹配,返回false表示跳过 + return false; + } + return true; + } + + /** + * 重写父类方法:从JSON读取器完整解析并初始化当前对象(JSON解析入口) + * 负责JSON对象的开始/结束标识处理,遍历所有字段并调用字段解析方法 + * 严格遵循writeThisToJsonWriter的序列化结构,保证解析一致性 + * @param jsonReader JSON读取器(外部传入的JSON流操作实例) + * @return 解析后的当前TestBean实例(支持链式调用) + * @throws IOException JSON解析异常(格式错误、字段缺失、流异常等) + */ + @Override + public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { + LogUtils.d(TAG, "readBeanFromJsonReader调用,传入参数JsonReader:" + jsonReader); + // 开始解析JSON对象,与序列化结构保持一致 + jsonReader.beginObject(); + // 遍历所有JSON字段 + while (jsonReader.hasNext()) { + String fieldName = jsonReader.nextName(); + LogUtils.d(TAG, "readBeanFromJsonReader:开始解析字段,fieldName=" + fieldName); + // 解析字段,不匹配则跳过该值 + if (!this.initObjectsFromJsonReader(jsonReader, fieldName)) { + jsonReader.skipValue(); + LogUtils.w(TAG, "readBeanFromJsonReader:字段" + fieldName + "解析失败,已跳过该值"); + } + } + // 结束JSON对象解析,必须调用避免流异常 + jsonReader.endObject(); + LogUtils.d(TAG, "readBeanFromJsonReader执行完成,JSON解析结束,当前TestBean实例testNum1:" + this.getTestNum1()); + // 返回当前实例,支持链式调用 + return this; + } +} + diff --git a/appbase/src/main/res/drawable/bg_container_border.xml b/appbase/src/main/res/drawable/bg_container_border.xml new file mode 100644 index 0000000..09c5bdf --- /dev/null +++ b/appbase/src/main/res/drawable/bg_container_border.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/appbase/src/main/res/drawable/btn_light_blue.xml b/appbase/src/main/res/drawable/btn_light_blue.xml new file mode 100644 index 0000000..58415fd --- /dev/null +++ b/appbase/src/main/res/drawable/btn_light_blue.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/appbase/src/main/res/layout-night/activity_about.xml b/appbase/src/main/res/layout-night/activity_about.xml new file mode 100644 index 0000000..f8af6b0 --- /dev/null +++ b/appbase/src/main/res/layout-night/activity_about.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/appbase/src/main/res/layout-night/activity_main.xml b/appbase/src/main/res/layout-night/activity_main.xml new file mode 100644 index 0000000..383caad --- /dev/null +++ b/appbase/src/main/res/layout-night/activity_main.xml @@ -0,0 +1,106 @@ + + + + + + + + + +