Compare commits

..

34 Commits

Author SHA1 Message Date
ZhanGSKen e136045d1f Merge remote-tracking branch 'winboll_https/projects_keeper_tag' into mywinboll 2026-07-11 20:21:13 +08:00
qinglong bd635425a1 合并模块AES 同步最新时间标签aes-v15.20.18 2026-07-11 20:17:35 +08:00
BigPickle 49414a3938 App/MainActivity: 修复主题切换 crash 及工具栏主题不刷新问题
- App.onCreate 添加 AESThemeUtil.init(null) 初始化 themeStyleIDList,
  修复点击主题菜单时 IndexOutOfBoundsException 崩溃
- MainActivity.onCreate 添加 AESThemeUtil.applyAppCompatTheme(this)
  置于 super.onCreate 之前,使 recreate() 后应用已保存的主题样式
2026-07-02 01:34:58 +08:00
BigPickle 08a47768a1 MainActivity: 接入 AESThemeUtil/DevelopUtils 工具类,替换 Toolbar 为 ASupportToolbar
- MainActivity 新增 onCreateOptionsMenu/onOptionsItemSelected,
  使用 AESThemeUtil.inflateCompatThemeMenu 加载主题菜单、
  DevelopUtils.inflateMenu 加载调试菜单
- 布局文件 Toolbar 替换为 cc.winboll.studio.libaes.views.ASupportToolbar,
  移除 AppBarLayout 包裹层
- MyAppTheme 日间/夜间主题增加 aSupportToolbar 属性引用 AESASupportToolbar 样式
2026-07-02 01:26:50 +08:00
ZhanGSKen 79f161b559 fix: 修复 MyAppTheme 主题继承链,适配 Android 16(SDK 36)
- 修复 view_log.xml 自定义属性解析失败崩溃(需要 APPBaseTheme 的属性集)
- 修复 AppCompatActivity 需要 Theme.AppCompat 子主题的 IllegalStateException
- 父主题改为 Theme.AppCompat.Light.NoActionBar / Theme.AppCompat.NoActionBar
- 新增 values-night/ 日夜模式自动切换支持
- 主色调从 Teal/Orange 更换为 Blue/Amber 专业配色方案
2026-07-01 17:06:20 +08:00
LaizyBoy b14538db47 20260701_163734_738 2026-07-01 16:37:41 +08:00
ZhanGSKen c9e16a9a1e 编译配置完成 2026-07-01 15:53:23 +08:00
ZhanGSKen 42dbfb4238 加入WinBoLL框架文件 2026-07-01 15:47:18 +08:00
ZhanGSKen b274503187 20260701_154438_390 2026-07-01 15:45:11 +08:00
ZhanGSKen b50e4e02ea 修复git管理脚本 2026-07-01 04:49:24 +08:00
ZhanGSKen 84f1c820cb 修复git管理脚本 2026-07-01 04:48:30 +08:00
ZhanGSKen 0e60d75ac6 修复git管理脚本 2026-07-01 04:44:47 +08:00
ZhanGSKen 773cc9f6c1 修复git管理脚本 2026-07-01 04:42:45 +08:00
ZhanGSKen ca33dab4da 修复git管理脚本 2026-07-01 04:33:36 +08:00
ZhanGSKen eee8809485 修复git管理脚本 2026-07-01 04:11:36 +08:00
ZhanGSKen 69f79549e9 合并模块WinBoLL 同步最新时间标签winboll-v15.20.8 2026-07-01 04:10:40 +08:00
ZhanGSKen f204cd2608 修复git管理脚本 2026-07-01 04:08:42 +08:00
ZhanGSKen 2d3592690a 更新新版winboll架构 2026-07-01 03:49:09 +08:00
ZhanGSKen 576f8bb5e5 更新新版winboll架构 2026-07-01 03:48:29 +08:00
qinglong e787697d6b 合并模块AES 同步最新时间标签aes-v15.20.17 2026-06-30 19:00:05 +08:00
qinglong 3db3edcf2c 合并模块APPBase 同步最新时间标签appbase-v15.20.34 2026-06-30 19:00:05 +08:00
qinglong 643d08bf22 合并模块AES 同步最新时间标签aes-v15.20.16 2026-06-26 02:00:28 +08:00
qinglong beefb16c32 合并模块APPBase 同步最新时间标签appbase-v15.20.33 2026-06-26 02:00:28 +08:00
qinglong 878009ec9f 合并模块WinBoLL 同步最新时间标签winboll-v15.20.8 2026-06-26 02:00:28 +08:00
qinglong 2d8b5d515b 合并模块AES 同步最新时间标签aes-v15.20.15 2026-06-03 20:30:22 +08:00
qinglong 1453287667 合并模块APPBase 同步最新时间标签appbase-v15.20.26 2026-06-03 20:30:22 +08:00
qinglong 5fc59f3305 合并模块AES 同步最新时间标签aes-v15.20.14 2026-06-03 08:00:01 +08:00
qinglong b6ba73126f 合并模块WinBoLL 同步最新时间标签winboll-v15.20.7 2026-06-03 08:00:01 +08:00
qinglong bdd8d1e2b6 合并模块APPBase 同步最新时间标签appbase-v15.20.25 2026-06-03 07:00:01 +08:00
qinglong 138ca80e35 合并模块APPBase 同步最新时间标签appbase-v15.20.23 2026-06-03 06:06:41 +08:00
qinglong 7a3b54b78b 合并模块WinBoLL 同步最新时间标签winboll-v15.20.6 2026-06-02 21:00:01 +08:00
qinglong db804d1897 合并模块AES 同步最新时间标签aes-v15.20.12 2026-06-02 08:55:05 +08:00
qinglong 039c8fcd98 合并模块WinBoLL 同步最新时间标签winboll-v15.20.5 2026-06-02 04:00:01 +08:00
qinglong ae63d1ec0a 合并模块AES 同步最新时间标签aes-v15.20.11 2026-06-02 03:00:01 +08:00
211 changed files with 13726 additions and 595 deletions
+2 -2
View File
@@ -94,8 +94,8 @@ lint-results.html
## 忽略 AndroidIDE 临时文件夹
.androidide
## WinBoLL 基础应用(避免上传敏感配置)
## MyWinBoLL 基础应用(避免上传敏感配置)
/winboll.properties
/local.properties
#/settings.gradle
#/gradle.properties
#/gradle.properties
@@ -1,5 +1,5 @@
#!/system/bin/sh
## 合并其他项目分支的模块源码到projects-keeper分支。
## 合并其他项目分支的模块源码到projects_keeper分支。
# ====================== 0. 进入目标目录 ======================
TARGET_DIR="/sdcard/AppProjects/Projects_Keeper"
@@ -36,7 +36,7 @@ fi
# ====================== 3. Git 分支检查 ======================
CUR_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
TARGET_BRANCH="projects-keeper"
TARGET_BRANCH="projects_keeper"
if [ "$CUR_BRANCH" != "$TARGET_BRANCH" ]; then
echo "错误:当前不在 $TARGET_BRANCH 分支!"
@@ -72,7 +72,6 @@ libaes
libappbase
libdebugtemp
libgpsrelaysentinel
libwinboll
local.properties-demo
mymessagemanager
positions
@@ -82,13 +81,14 @@ winboll
winboll.properties-demo
)
# ====================== 5. 获取当前目录真实文件列表 ======================
# ====================== 5. 获取当前目录真实文件列表(兼容过滤 . .. ======================
REAL_ITEMS=()
# 使用固定排序ls,自动过滤 . 和 ..,不会进入比对数组
while IFS= read -r line; do
if [[ "$line" != "." && "$line" != ".." ]]; then
REAL_ITEMS+=("$line")
fi
done < <(ls -a)
done < <(LC_COLLATE=C ls -a1 --color=none)
# ====================== 6. 差异比对函数 ======================
check_diff() {
@@ -158,7 +158,7 @@ echo -e "## 对象列表结束
## 合并 APP 项目
MERGE_APP_PROJECT_LIST=(
DemoAPP
WinBoLL
)
echo -e "#@@@ 开始合并应用型模块源码 @@@#
## 目标合并对象列表:"
@@ -166,14 +166,13 @@ echo -e "#@@@ 开始合并应用型模块源码 @@@#
for item in "${MERGE_APP_PROJECT_LIST[@]}"; do
echo "正在合并 $item 项目 ..."
item_lower=$(echo "$item" | tr 'A-Z' 'a-z')
git checkout origin/$item_lower $item_lower
git checkout origin/$item_lower $item_lower
git add .
git commit -m "合并 $item 项目"
done
## 合并 LIB 项目
MERGE_LIB_PROJECT_LIST=(
WinBoLL
APPBase
AES
)
@@ -183,10 +182,10 @@ echo -e "#@@@ 开始合并类库型模块源码 @@@#
for item in "${MERGE_LIB_PROJECT_LIST[@]}"; do
echo "正在合并 $item 项目 ..."
item_lower=$(echo "$item" | tr 'A-Z' 'a-z')
git checkout origin/$item_lower $item_lower lib$item_lower
git checkout origin/$item_lower $item_lower lib$item_lower
git add .
git commit -m "合并 $item 项目"
done
echo '正在推送 Projects_Keeper 项目'
git push
git push
@@ -38,7 +38,7 @@ if [ "${NOW_BRANCH}" != "${TARGET_BRANCH}" ];then
exit 1
fi
# 目录结构校验
# 目录结构校验白名单(不含 . ..
MERGE_OBJECTS_LIST=(
.git
.gitignore
@@ -65,7 +65,6 @@ libaes
libappbase
libdebugtemp
libgpsrelaysentinel
libwinboll
local.properties-demo
mymessagemanager
positions
@@ -76,9 +75,13 @@ winboll.properties-demo
)
REAL_ITEMS=()
# 标准排序ls输出,循环强制过滤 . 和 ..
while IFS= read -r line; do
[[ $line != "." && $line != ".." ]] && REAL_ITEMS+=("$line")
done < <(ls -a)
# 跳过虚拟目录 . 和 ..
if [[ "$line" != "." && "$line" != ".." ]]; then
REAL_ITEMS+=("$line")
fi
done < <(LC_COLLATE=C ls -a1 --color=none)
check_diff(){
local miss=() extra=()
@@ -89,7 +92,17 @@ check_diff(){
[[ ! " ${MERGE_OBJECTS_LIST[@]} " =~ " ${i} " ]] && extra+=("$i")
done
if [[ ${#miss[@]} -gt 0 || ${#extra[@]} -gt 0 ]];then
echo "========================================"
echo "本地目录结构不一致,终止运行"
if [[ ${#miss[@]} -gt 0 ]]; then
echo -e "\n缺失条目:"
for m in "${miss[@]}"; do echo " $m"; done
fi
if [[ ${#extra[@]} -gt 0 ]]; then
echo -e "\n多余条目:"
for e in "${extra[@]}"; do echo " $e"; done
fi
echo "========================================"
exit 1
fi
}
@@ -98,7 +111,7 @@ check_diff
echo -e "#@@@ 按时间获取最新标签合并模块源码 @@@#"
# 应用型模块
MERGE_APP_PROJECT_LIST=(DemoAPP)
MERGE_APP_PROJECT_LIST=(WinBoLL)
echo -e "---------- 应用型模块 ----------"
for name in "${MERGE_APP_PROJECT_LIST[@]}";do
low_name=$(echo "$name" | tr 'A-Z' 'a-z')
@@ -120,7 +133,7 @@ for name in "${MERGE_APP_PROJECT_LIST[@]}";do
done
# 类库模块
MERGE_LIB_PROJECT_LIST=(WinBoLL APPBase AES)
MERGE_LIB_PROJECT_LIST=(APPBase AES)
echo -e "---------- 类库模块 ----------"
for name in "${MERGE_LIB_PROJECT_LIST[@]}";do
low_name=$(echo "$name" | tr 'A-Z' 'a-z')
+228
View File
@@ -0,0 +1,228 @@
#!/usr/bin/bash
# ==============================================================================
# WinBoLL 应用发布脚本
# 功能:检查Git源码状态 → 编译Stage Release包 → 添加WinBoLL标签 → 提交并推送源码
# 依赖:build.properties、app_update_description.txt(项目根目录下)
# 使用:./script_name.sh <APP_NAME>
# 作者:豆包&ZhanGSKen<zhangsken@qq.com>
# ==============================================================================
# ==================== 常量定义 ====================
# 脚本退出码
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"
# aapt2本地覆盖参数
AAPT2_OVERRIDE_ARG="-Pandroid.aapt2FromMavenOverride=/data/data/com.termux/files/usr/bin/aapt2"
# 禁用Gradle守护进程
GRADLE_NO_DAEMON="--no-daemon"
# ==================== 函数定义 ====================
# 检查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标签(格式:<APP_NAME>-v<publishVersion>
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标签(格式:<APP_NAME>-v<baseBetaVersion>-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} <APP_NAME>"
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(携带aapt2覆盖参数 + --no-daemon
echo "---------------------------------------------"
echo " 步骤2:编译Stage Release APK"
echo "---------------------------------------------"
echo "[INFO] 开始执行Gradle任务:${GRADLE_TASK_PUBLISH}"
# 调试用(注释正式任务,启用调试任务)
# bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :${APP_NAME}:${GRADLE_TASK_DEBUG}
bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :${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}
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/bash
# aapt2本地覆盖参数
AAPT2_OVERRIDE_ARG="-Pandroid.aapt2FromMavenOverride=/data/data/com.termux/files/usr/bin/aapt2"
# Gradle禁用守护进程参数
GRADLE_NO_DAEMON="--no-daemon"
# 检查是否指定了将要发布的类库名称
# 使用 `-z` 命令检查变量是否为空
if [ -z "$1" ]; then
echo "No Library name specified : $0"
exit 2
fi
## 正式发布使用
git pull && bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :$1:publishReleasePublicationToWinBoLLReleaseRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1
## 调试使用
#bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :$1:publishSnapshotWinBoLLPublicationToWinBoLLSnapshotRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1
+56 -27
View File
@@ -122,7 +122,6 @@ android {
// 如果正在调试,就拷贝到 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()) {
@@ -130,6 +129,7 @@ android {
println "Output Folder Created.(WinBoLLStudio) : " + outBuildBckDir.getAbsolutePath()
}
if(outBuildBckDir.exists()) {
def targetApkFile = new File(outBuildBckDir, outputFileName)
copy{
from file.outputFile
into outBuildBckDir
@@ -138,6 +138,14 @@ android {
}
println "Output APK (WinBoLLStudio): " + outBuildBckDir.getAbsolutePath() + "/${outputFileName}"
}
// ========== 设置文件权限为775 ==========
if(targetApkFile.exists()){
exec {
commandLine 'chmod', '775', targetApkFile.absolutePath
}
println "Set file permission to 775 : ${targetApkFile.absolutePath}"
}
// 检查编译标志位配置
assert (winbollBuildProps['buildCount'] != null)
assert (winbollBuildProps['libraryProject'] != null)
@@ -160,8 +168,7 @@ android {
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);
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
println "\n\n>>> Library Project build.properties saved.\n\n";
}
@@ -172,16 +179,12 @@ android {
//
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()) {
@@ -192,12 +195,10 @@ android {
if(outTagDir.exists()) {
File targetAPK = new File(outTagDir, "${szCommonTagAPKName}")
if(targetAPK.exists()) {
// 标签版本APK文件已经存在,构建拷贝任务停止
assert (!targetAPK.exists())
// 可选择删除并继续输出APK文件
//delete targetAPK
}
// 复制一个备份
// 复制完整版APK
def fullApkFile = new File(outTagDir, outputFileName)
copy{
from file.outputFile
into outTagDir
@@ -206,7 +207,16 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${outputFileName}"
}
// 复制一个并重命名为短版本名
// 设置权限775。
if(fullApkFile.exists()){
exec {
commandLine 'chmod', '775', fullApkFile.absolutePath
}
println "Set file permission to 775 : ${fullApkFile.absolutePath}"
}
// 复制短版本名APK
def shortApkFile = new File(outTagDir, szCommonTagAPKName)
copy{
from file.outputFile
into outTagDir
@@ -215,6 +225,14 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${szCommonTagAPKName}"
}
// 设置权限775。
if(shortApkFile.exists()){
exec {
commandLine 'chmod', '775', shortApkFile.absolutePath
}
println "Set file permission to 775 : ${shortApkFile.absolutePath}"
}
// 检查编译标志位配置
assert (winbollBuildProps['stageCount'] != null)
assert (winbollBuildProps['publishVersion'] != null)
@@ -239,14 +257,11 @@ android {
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);
}
@@ -263,17 +278,12 @@ android {
// 如果正在调试发布版,就只生成和输出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()) {
@@ -284,13 +294,11 @@ android {
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
}
// 复制一个备份
// 复制完整版APK
def debugFullApk = new File(outTagDir, outputFileName)
copy{
from file.outputFile
into outTagDir
@@ -299,7 +307,16 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${outputFileName}"
}
// 复制一个并重命名为短版本名
// 权限设为775。
if(debugFullApk.exists()){
exec {
commandLine 'chmod', '775', debugFullApk.absolutePath
}
println "Set file permission to 775 : ${debugFullApk.absolutePath}"
}
// 复制短版本名APK
def debugShortApk = new File(outTagDir, szCommonTagAPKName)
copy{
from file.outputFile
into outTagDir
@@ -308,8 +325,13 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${szCommonTagAPKName}"
}
//不保存编译标志配置
// 权限设为775
if(debugShortApk.exists()){
exec {
commandLine 'chmod', '775', debugShortApk.absolutePath
}
println "Set file permission to 775 : ${debugShortApk.absolutePath}"
}
}
}
@@ -328,6 +350,13 @@ android {
}
println "Output APK (Common): " + outCommonDir.getAbsolutePath() + "/${commandAPKName}"
}
// 额外输出文件设置775权限
if(apkFile.exists()){
exec {
commandLine 'chmod', '775', apkFile.absolutePath
}
println "Set file permission to 775 : ${apkFile.absolutePath}"
}
}
}
-1
View File
@@ -1 +0,0 @@
15.20
-201
View File
@@ -1,201 +0,0 @@
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.
+97
View File
@@ -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 生成)
+14 -14
View File
@@ -6,11 +6,11 @@
## 核心声明
本文档**唯一核心设计目的**: 通过文件标识、分支隔离、操作规范、责任界定四重约束,**从根源规避私有开发分支代码被人为合并、推送、提交至公共开源主流分支的风险**,明确人为操作失误、违规合并的全部责任归属,同时保证私有分支可正常同步、拉取公共主流分支的上游更新。
本文档**唯一核心设计目的**通过文件标识、分支隔离、操作规范、责任界定四重约束,**从根源规避私有开发分支代码被人为合并、推送、提交至公共开源主流分支的风险**,明确人为操作失误、违规合并的全部责任归属,同时保证私有分支可正常同步、拉取公共主流分支的上游更新。
## 一、文件宗旨与风险防控说明
本文件为 WinBoLL 项目公共开源分支转为私有独立分支开发的**强制标准化操作手册与责任界定文件**,核心风控目标:
本文件为 WinBoLL 项目公共开源分支转为私有独立分支开发的**强制标准化操作手册与责任界定文件**,核心风控目标
1. 严格隔离公共开源分支与私有开发分支,通过授权文件标记实现分支属性一眼可辨,杜绝人为操作混淆
@@ -24,7 +24,7 @@
### 1. 公共开源分支唯一标识
**文件名: LICENSE**
**文件名LICENSE**
- 仅允许存在于公共主流分支 `winboll` 及官方公共衍生分支
@@ -34,7 +34,7 @@
### 2. 私有开发分支唯一标识
**文件名: LICENSE-Private**
**文件名LICENSE-Private**
- 仅允许存在于私有开发分支,**绝对禁止出现在公共 ****`winboll`**** 分支**
@@ -44,15 +44,15 @@
## 三、分支管理与合并风控规则(强制遵守)
1. **公共主流分支**: 固定为 `winboll`,为项目唯一开源主线,仅保留 `LICENSE` 文件,**禁止接收任何私有分支的合并、提交、推送请求**。
1. **公共主流分支**固定为 `winboll`,为项目唯一开源主线,仅保留 `LICENSE` 文件,**禁止接收任何私有分支的合并、提交、推送请求**。
2. **私有开发分支**: 统一从 `winboll` 分支检出,命名固定格式为 `private-demo-*`,与公共分支物理隔离。
2. **私有开发分支**统一从 `winboll` 分支检出,命名固定格式为 `private-demo-*`,与公共分支物理隔离。
3. **核心合并风控铁则**
- 私有分支 → 公共分支: **永久禁止任何形式的合并、推送、PR 提交、代码回合,人为操作也绝不允许**
- 私有分支 → 公共分支**永久禁止任何形式的合并、推送、PR 提交、代码回合,人为操作也绝不允许**
- 公共分支 → 私有分支: 允许正常拉取、同步上游更新,不影响私有开发迭代
- 公共分支 → 私有分支允许正常拉取、同步上游更新,不影响私有开发迭代
4. 所有仓库提交者、合并操作者,均视为已阅读并完全认可本规则,**人为执行私有分支向公共分支的合并操作,由操作人承担全部代码泄露、合规违约、项目安全风险**。
@@ -68,7 +68,7 @@
4. 将本规范文件 `LICENSE-Private-Demo` 复制并重命名为 `LICENSE-Private`,作为私有分支生效授权文件。
5. 将以上所有变更执行一次性 Git 提交,**提交信息必须固定使用以下内容,不可修改**:
5. 将以上所有变更执行一次性 Git 提交,**提交信息必须固定使用以下内容,不可修改**
> 初始化私有开发分支,已切换私有授权文件,本分支禁止任何人为合并、推送至 winboll 公共分支
>
@@ -84,14 +84,14 @@
3. 仓库管理员需严格校验合并请求的分支标识与授权文件,发现带有 `LICENSE-Private` 标记的分支申请合并至公共分支,一律直接拒绝,并记录操作人信息。
4. 分支属性校验以根目录授权文件为唯一标准: 只要分支内存在 `LICENSE-Private` 文件,就绝对禁止向公共分支发起任何合并操作。
4. 分支属性校验以根目录授权文件为唯一标准只要分支内存在 `LICENSE-Private` 文件,就绝对禁止向公共分支发起任何合并操作。
## 六、分支状态校验与异常处理
- 合规公共分支: 仅存在 `LICENSE`,无 `LICENSE-Private`
- 合规公共分支仅存在 `LICENSE`,无 `LICENSE-Private`
- 合规私有分支: 仅存在 `LICENSE-Private`,无 `LICENSE`
- 合规私有分支仅存在 `LICENSE-Private`,无 `LICENSE`
- 异常状态: 两个文件同时存在 / 均不存在 → 立即停止开发与提交,按本规范重置分支状态,严禁执行任何合并操作
- 异常状态两个文件同时存在 / 均不存在 → 立即停止开发与提交,按本规范重置分支状态,严禁执行任何合并操作
> (注: 文档部分内容可能由 AI 生成)
> (注文档部分内容可能由 AI 生成)
+14 -14
View File
@@ -6,11 +6,11 @@
## 核心声明
本文档**唯一核心设计目的**: 通过文件标识、分支隔离、操作规范、责任界定四重约束,**从根源规避私有开发分支代码被人为合并、推送、提交至公共开源主流分支的风险**,明确人为操作失误、违规合并的全部责任归属,同时保证私有分支可正常同步、拉取公共主流分支的上游更新。
本文档**唯一核心设计目的**通过文件标识、分支隔离、操作规范、责任界定四重约束,**从根源规避私有开发分支代码被人为合并、推送、提交至公共开源主流分支的风险**,明确人为操作失误、违规合并的全部责任归属,同时保证私有分支可正常同步、拉取公共主流分支的上游更新。
## 一、文件宗旨与风险防控说明
本文件为 WinBoLL 项目公共开源分支转为私有独立分支开发的**强制标准化操作手册与责任界定文件**,核心风控目标:
本文件为 WinBoLL 项目公共开源分支转为私有独立分支开发的**强制标准化操作手册与责任界定文件**,核心风控目标
1. 严格隔离公共开源分支与私有开发分支,通过授权文件标记实现分支属性一眼可辨,杜绝人为操作混淆
@@ -24,7 +24,7 @@
### 1. 公共开源分支唯一标识
**文件名: LICENSE**
**文件名LICENSE**
- 仅允许存在于公共主流分支 `winboll` 及官方公共衍生分支
@@ -34,7 +34,7 @@
### 2. 私有开发分支唯一标识
**文件名: LICENSE-Private**
**文件名LICENSE-Private**
- 仅允许存在于私有开发分支,**绝对禁止出现在公共 ****`winboll`**** 分支**
@@ -44,15 +44,15 @@
## 三、分支管理与合并风控规则(强制遵守)
1. **公共主流分支**: 固定为 `winboll`,为项目唯一开源主线,仅保留 `LICENSE` 文件,**禁止接收任何私有分支的合并、提交、推送请求**。
1. **公共主流分支**固定为 `winboll`,为项目唯一开源主线,仅保留 `LICENSE` 文件,**禁止接收任何私有分支的合并、提交、推送请求**。
2. **私有开发分支**: 统一从 `winboll` 分支检出,命名固定格式为 `private-demo-*`,与公共分支物理隔离。
2. **私有开发分支**统一从 `winboll` 分支检出,命名固定格式为 `private-demo-*`,与公共分支物理隔离。
3. **核心合并风控铁则**
- 私有分支 → 公共分支: **永久禁止任何形式的合并、推送、PR 提交、代码回合,人为操作也绝不允许**
- 私有分支 → 公共分支**永久禁止任何形式的合并、推送、PR 提交、代码回合,人为操作也绝不允许**
- 公共分支 → 私有分支: 允许正常拉取、同步上游更新,不影响私有开发迭代
- 公共分支 → 私有分支允许正常拉取、同步上游更新,不影响私有开发迭代
4. 所有仓库提交者、合并操作者,均视为已阅读并完全认可本规则,**人为执行私有分支向公共分支的合并操作,由操作人承担全部代码泄露、合规违约、项目安全风险**。
@@ -68,7 +68,7 @@
4. 将本规范文件 `LICENSE-Private-Demo` 复制并重命名为 `LICENSE-Private`,作为私有分支生效授权文件。
5. 将以上所有变更执行一次性 Git 提交,**提交信息必须固定使用以下内容,不可修改**:
5. 将以上所有变更执行一次性 Git 提交,**提交信息必须固定使用以下内容,不可修改**
> 初始化私有开发分支,已切换私有授权文件,本分支禁止任何人为合并、推送至 winboll 公共分支
>
@@ -84,14 +84,14 @@
3. 仓库管理员需严格校验合并请求的分支标识与授权文件,发现带有 `LICENSE-Private` 标记的分支申请合并至公共分支,一律直接拒绝,并记录操作人信息。
4. 分支属性校验以根目录授权文件为唯一标准: 只要分支内存在 `LICENSE-Private` 文件,就绝对禁止向公共分支发起任何合并操作。
4. 分支属性校验以根目录授权文件为唯一标准只要分支内存在 `LICENSE-Private` 文件,就绝对禁止向公共分支发起任何合并操作。
## 六、分支状态校验与异常处理
- 合规公共分支: 仅存在 `LICENSE`,无 `LICENSE-Private`
- 合规公共分支仅存在 `LICENSE`,无 `LICENSE-Private`
- 合规私有分支: 仅存在 `LICENSE-Private`,无 `LICENSE`
- 合规私有分支仅存在 `LICENSE-Private`,无 `LICENSE`
- 异常状态: 两个文件同时存在 / 均不存在 → 立即停止开发与提交,按本规范重置分支状态,严禁执行任何合并操作
- 异常状态两个文件同时存在 / 均不存在 → 立即停止开发与提交,按本规范重置分支状态,严禁执行任何合并操作
> (注: 文档部分内容可能由 AI 生成)
> (注文档部分内容可能由 AI 生成)
-4
View File
@@ -39,10 +39,6 @@ android {
}
}
// 米盟 SDK
packagingOptions {
doNotStrip "*/*/libmimo_1011.so"
}
}
dependencies {
+4 -4
View File
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed Jun 03 07:07:27 HKT 2026
stageCount=15
#Sat Jul 11 20:07:00 HKT 2026
stageCount=19
libraryProject=libaes
baseVersion=15.20
publishVersion=15.20.14
publishVersion=15.20.18
buildCount=0
baseBetaVersion=15.20.15
baseBetaVersion=15.20.19
@@ -65,7 +65,7 @@ public class AboutActivity extends BaseWinBoLLActivity {
appInfo.setAppName(getString(R.string.app_name));
appInfo.setAppIcon(R.drawable.ic_winboll);
appInfo.setAppDescription(getString(R.string.app_description));
appInfo.setAppGitName("AES");
appInfo.setAppGitName("WinBoLL");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(branchName);
appInfo.setAppGitAPPSubProjectFolder(branchName);
@@ -7,7 +7,7 @@ package cc.winboll.studio.aes;
*/
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.CrashHandler;
import cc.winboll.studio.libappbase.CrashActivity;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils;
@@ -36,7 +36,7 @@ public class App extends GlobalApplication {
this,
getPackageName(),
stackTraceStr,
CrashHandler.CrashActivity.class
CrashActivity.class
);
}
@@ -1,11 +1,7 @@
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&豆包大模型<zhangsken@qq.com>
@@ -20,7 +16,7 @@ public class SettingsActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
ADsControlView adsControlView = (ADsControlView) findViewById(R.id.ads_control_view);
//ADsControlView adsControlView = (ADsControlView) findViewById(R.id.ads_control_view);
// adsControlView.setOnAdsModeSelectedListener(new ADsControlView.OnAdsModeSelectedListener() {
// @Override
@@ -6,12 +6,5 @@
android:layout_width="match_parent"
android:layout_height="match_parent" android:background="@drawable/bg_container_border">
<cc.winboll.studio.libaes.views.ADsControlView
android:id="@+id/ads_control_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_frame"
android:padding="10dp"/>
</LinearLayout>
+1 -1
View File
@@ -5,7 +5,7 @@
</style>
<style name="MyDebugActivityTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:statusBarColor">@color/toolbarBackgroundColor</item>
<item name="android:statusBarColor">?attr/colorPrimaryDark</item>
<item name="colorTittle">@color/mainWindowTextColor</item>
<item name="colorTittleBackgound">@color/toolbarBackgroundColor</item>
<item name="colorText">@color/debugTextColor</item>
+1 -1
View File
@@ -5,7 +5,7 @@
</style>
<style name="MyDebugActivityTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:statusBarColor">@color/toolbarBackgroundColor</item>
<item name="android:statusBarColor">?attr/colorPrimaryDark</item>
<item name="colorTittle">@color/mainWindowTextColor</item>
<item name="colorTittleBackgound">@color/toolbarBackgroundColor</item>
<item name="colorText">@color/debugTextColor</item>
+4 -4
View File
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed May 27 14:51:29 HKT 2026
stageCount=23
#Sun Jun 28 20:55:33 HKT 2026
stageCount=35
libraryProject=libappbase
baseVersion=15.20
publishVersion=15.20.22
publishVersion=15.20.34
buildCount=0
baseBetaVersion=15.20.23
baseBetaVersion=15.20.35
@@ -37,7 +37,7 @@ public class AboutActivity extends Activity {
appInfo.setAppName("APPBase");
appInfo.setAppIcon(R.drawable.ic_winboll);
appInfo.setAppDescription(getString(R.string.app_description));
appInfo.setAppGitName("APPBase");
appInfo.setAppGitName("WinBoLL");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(branchName);
appInfo.setAppGitAPPSubProjectFolder(branchName);
@@ -1,8 +1,11 @@
package cc.winboll.studio.appbase;
import cc.winboll.studio.libappbase.CrashActivity;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.libappbase.BuildConfig;
import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
@@ -21,10 +24,24 @@ public class App extends GlobalApplication {
*/
@Override
public void onCreate() {
super.onCreate();
// 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用)
ToastUtils.init(getApplicationContext());
try {
super.onCreate();
// 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用)
ToastUtils.init(getApplicationContext());
} catch (Throwable e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.close();
String stackTraceStr = sw.toString();
CrashHandleNotifyUtils.handleUncaughtException(
this,
getPackageName(),
stackTraceStr,
CrashActivity.class
);
}
}
/**
Vendored Regular → Executable
View File
+4 -8
View File
@@ -20,15 +20,11 @@ android {
}
}
// 米盟 SDK
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
packagingOptions {
doNotStrip "*/*/libmimo_1011.so"
}
}
dependencies {
@@ -57,7 +53,7 @@ dependencies {
//api 'androidx.fragment:fragment:1.1.0'
// 米盟
api 'com.miui.zeus:mimo-ad-sdk:5.3.+'//请使用最新版sdk
//api 'com.miui.zeus:mimo-ad-sdk:5.3.+'//请使用最新版sdk
//注意:以下5个库必须要引入
//implementation 'androidx.appcompat:appcompat:1.4.1'
api 'androidx.recyclerview:recyclerview:1.0.0'
@@ -66,9 +62,9 @@ dependencies {
//annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
// WinBoLL库 nexus.winboll.cc 地址
api 'cc.winboll.studio:libappbase:15.20.25'
api 'cc.winboll.studio:libappbase:15.20.34'
// 备用库 jitpack.io 地址
//api 'com.github.ZhanGSKen:libappbase:appbase-v15.20.25'
//api 'com.github.ZhanGSKen:libappbase:appbase-v15.20.33'
api fileTree(dir: 'libs', include: ['*.jar'])
}
+4 -4
View File
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed Jun 03 07:07:27 HKT 2026
stageCount=15
#Sat Jul 11 20:07:00 HKT 2026
stageCount=19
libraryProject=libaes
baseVersion=15.20
publishVersion=15.20.14
publishVersion=15.20.18
buildCount=0
baseBetaVersion=15.20.15
baseBetaVersion=15.20.19
@@ -15,7 +15,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
@@ -23,9 +22,9 @@ import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import cc.winboll.studio.libaes.DrawerMenuDataAdapter;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.models.AESThemeBean;
import cc.winboll.studio.libaes.models.DrawerMenuBean;
@@ -33,11 +32,9 @@ import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libaes.utils.DevelopUtils;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libaes.views.ADrawerMenuListView;
import cc.winboll.studio.libaes.views.ADsBannerView;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils;
import com.baoyz.widget.PullRefreshLayout;
import java.util.ArrayList;
public abstract class DrawerFragmentActivity extends AppCompatActivity implements IWinBoLLActivity, AdapterView.OnItemClickListener {
@@ -91,10 +88,10 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
WinBoLLActivityManager.getInstance().registeRemove(this);
super.onDestroy();
// 修复:释放广告资源,避免内存泄漏
ADsBannerView adsBannerView = findViewById(R.id.adsbanner);
if (adsBannerView != null) {
adsBannerView.releaseAdResources();
}
// ADsBannerView adsBannerView = findViewById(R.id.adsbanner);
// if (adsBannerView != null) {
// adsBannerView.releaseAdResources();
// }
}
@Override
@@ -151,10 +148,10 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
protected void onResume() {
super.onResume();
ADsBannerView adsBannerView = findViewById(R.id.adsbanner);
if (adsBannerView != null) {
adsBannerView.resumeADs(DrawerFragmentActivity.this);
}
// ADsBannerView adsBannerView = findViewById(R.id.adsbanner);
// if (adsBannerView != null) {
// adsBannerView.resumeADs(DrawerFragmentActivity.this);
// }
}
void initRootView() {
@@ -56,10 +56,5 @@
</LinearLayout>
<cc.winboll.studio.libaes.views.ADsBannerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/adsbanner"/>
</LinearLayout>
+1 -1
View File
@@ -25,7 +25,7 @@
<item name="mainWindowTextColor">@color/mainWindowTextColor</item>
<item name="mainWindowDarkBackgroundColor">@color/mainWindowBackgroundColor</item>
<item name="mainWindowDarkTextColor">@color/mainWindowTextColor</item>
<item name="android:statusBarColor">@color/toolbarBackgroundColor</item>
<item name="android:statusBarColor">?attr/colorPrimaryDark</item>
<item name="aToolbar">@style/AESAToolbar</item>
<item name="aSupportToolbar">@style/AESASupportToolbar</item>
+1 -1
View File
@@ -25,7 +25,7 @@
<item name="mainWindowTextColor">@color/mainWindowTextColor</item>
<item name="mainWindowDarkBackgroundColor">@color/mainWindowBackgroundColor</item>
<item name="mainWindowDarkTextColor">@color/mainWindowTextColor</item>
<item name="android:statusBarColor">@color/toolbarBackgroundColor</item>
<item name="android:statusBarColor">?attr/colorPrimaryDark</item>
<item name="aToolbar">@style/AESAToolbar</item>
<item name="aSupportToolbar">@style/AESASupportToolbar</item>
+4 -4
View File
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed May 27 14:51:29 HKT 2026
stageCount=23
#Sun Jun 28 20:55:33 HKT 2026
stageCount=35
libraryProject=libappbase
baseVersion=15.20
publishVersion=15.20.22
publishVersion=15.20.34
buildCount=0
baseBetaVersion=15.20.23
baseBetaVersion=15.20.35
+1 -1
View File
@@ -8,7 +8,7 @@
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name=".CrashHandler$CrashActivity"
android:name=".CrashActivity"
android:label="CrashActivity"
android:launchMode="singleInstance"
android:process=":CrashActivity"/>
@@ -0,0 +1,109 @@
package cc.winboll.studio.libappbase;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
public final class CrashActivity extends Activity implements MenuItem.OnMenuItemClickListener {
private static final int MENUITEM_COPY = 0;
private static final int MENUITEM_RESTART = 1;
private String mLog;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppCrashSafetyWire.getInstance().postResumeCrashSafetyWireHandler(getApplicationContext());
mLog = getIntent().getStringExtra(CrashHandler.EXTRA_CRASH_LOG);
setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
initLayout();
}
private void initLayout() {
ScrollView contentView = new ScrollView(this);
contentView.setFillViewport(true);
HorizontalScrollView hw = new HorizontalScrollView(this);
hw.setBackgroundColor(0xFFF5F5F5);
TextView message = new TextView(this);
final int padding = dp2px(16);
message.setPadding(padding, padding, padding, padding);
message.setText(mLog);
message.setTextColor(0xFF000000);
message.setTextIsSelectable(true);
hw.addView(message);
contentView.addView(hw, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
setContentView(contentView);
getActionBar().setTitle(CrashHandler.TITTLE);
getActionBar().setSubtitle(GlobalApplication.getAppName(getApplicationContext()) + " Error");
}
@Override
public void onBackPressed() {
restartApp();
}
private void restartApp() {
final Intent intent = getPackageManager()
.getLaunchIntentForPackage(getPackageName());
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
finish();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
private int dp2px(final float dpValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
menu.add(0, MENUITEM_COPY, 0, "Copy")
.setOnMenuItemClickListener(this)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(0, MENUITEM_RESTART, 0, "Restart")
.setOnMenuItemClickListener(this)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return true;
}
@Override
public boolean onMenuItemClick(final MenuItem item) {
switch (item.getItemId()) {
case MENUITEM_COPY:
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
Toast.makeText(getApplication(), "The text is copied.", Toast.LENGTH_SHORT).show();
break;
case MENUITEM_RESTART:
AppCrashSafetyWire.getInstance().resumeToMaximumImmediately();
restartApp();
break;
default:
break;
}
return false;
}
}
@@ -1,27 +1,13 @@
package cc.winboll.studio.libappbase;
import android.app.Activity;
import android.app.Application;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils;
@@ -220,111 +206,5 @@ public final class CrashHandler {
}
}
// ====================== 内部Activity页面 ======================
/**
* 基础极简崩溃页面
* 保险丝熔断时启动,避免复杂布局二次崩溃
*/
public static final class CrashActivity extends Activity implements MenuItem.OnMenuItemClickListener {
private static final int MENUITEM_COPY = 0;
private static final int MENUITEM_RESTART = 1;
private String mLog;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppCrashSafetyWire.getInstance().postResumeCrashSafetyWireHandler(getApplicationContext());
mLog = getIntent().getStringExtra(EXTRA_CRASH_LOG);
setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
initLayout();
}
/**
* 动态初始化布局
*/
private void initLayout() {
ScrollView contentView = new ScrollView(this);
contentView.setFillViewport(true);
HorizontalScrollView hw = new HorizontalScrollView(this);
hw.setBackgroundColor(0xFFF5F5F5);
TextView message = new TextView(this);
final int padding = dp2px(16);
message.setPadding(padding, padding, padding, padding);
message.setText(mLog);
message.setTextColor(0xFF000000);
message.setTextIsSelectable(true);
hw.addView(message);
contentView.addView(hw, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
setContentView(contentView);
getActionBar().setTitle(TITTLE);
getActionBar().setSubtitle(GlobalApplication.class.getSimpleName() + " Error");
}
@Override
public void onBackPressed() {
restartApp();
}
/**
* 重启应用
*/
private void restartApp() {
final Intent intent = getPackageManager()
.getLaunchIntentForPackage(getPackageName());
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
finish();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
/**
* dp转px
*/
private int dp2px(final float dpValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
menu.add(0, MENUITEM_COPY, 0, "Copy")
.setOnMenuItemClickListener(this)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(0, MENUITEM_RESTART, 0, "Restart")
.setOnMenuItemClickListener(this)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return true;
}
@Override
public boolean onMenuItemClick(final MenuItem item) {
switch (item.getItemId()) {
case MENUITEM_COPY:
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
Toast.makeText(getApplication(), "The text is copied.", Toast.LENGTH_SHORT).show();
break;
case MENUITEM_RESTART:
AppCrashSafetyWire.getInstance().resumeToMaximumImmediately();
restartApp();
break;
default:
break;
}
return false;
}
}
}
@@ -6,6 +6,10 @@ import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
@@ -125,17 +129,32 @@ public class GlobalApplication extends Application {
*/
@Override
public void onCreate() {
super.onCreate();
// 初始化单例实例(确保在所有初始化操作前完成)
sInstance = this;
try {
super.onCreate();
// 初始化单例实例(确保在所有初始化操作前完成)
sInstance = this;
restoreDebugStatus();
// 初始化基础组件(日志、崩溃处理、Toast)
initCoreComponents();
// 初始化服务器地址(从 SP 读取到内存,提高后续访问效率)
initWinbollHost();
restoreDebugStatus();
// 初始化基础组件(日志、崩溃处理、Toast)
initCoreComponents();
// 初始化服务器地址(从 SP 读取到内存,提高后续访问效率)
initWinbollHost();
LogUtils.d(TAG, "GlobalApplication 初始化完成,单例实例已创建");
LogUtils.d(TAG, "GlobalApplication 初始化完成,单例实例已创建");
} catch (Throwable e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.close();
String stackTraceStr = sw.toString();
CrashHandleNotifyUtils.handleUncaughtException(
this,
getPackageName(),
stackTraceStr,
CrashActivity.class
);
}
}
/**
@@ -190,7 +209,7 @@ public class GlobalApplication extends Application {
*/
public static String getAppName(Context context) {
if (context == null) {
LogUtils.w(TAG, "getAppName: 上下文为空,返回 null");
Log.w(TAG, "getAppName: 上下文为空,返回 null");
return null;
}
PackageManager packageManager = context.getPackageManager();
@@ -206,8 +225,7 @@ public class GlobalApplication extends Application {
return appName;
} catch (NameNotFoundException e) {
// 包名不存在(理论上不会发生,捕获异常避免崩溃)
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
//LogUtils.e(TAG, "获取应用名称失败:包名不存在", e);
Log.e(TAG, "获取应用名称失败:包名不存在", e);
e.printStackTrace();
}
return null;
@@ -66,7 +66,7 @@ public final class GlobalCrashActivity extends Activity implements MenuItem.OnMe
mCrashLog = getIntent().getStringExtra(CrashHandler.EXTRA_CRASH_LOG);
final Intent intent = new Intent();
intent.putExtra(CrashHandler.EXTRA_CRASH_LOG, mCrashLog);
CrashHandleNotifyUtils.handleUncaughtException(GlobalApplication.getInstance(), intent, CrashHandler.CrashActivity.class);
CrashHandleNotifyUtils.handleUncaughtException(GlobalApplication.getInstance(), intent, CrashActivity.class);
StackTraceElement[] stackElements = Thread.currentThread().getStackTrace();
StringBuilder sb = new StringBuilder("GlobalCrashActivity onCreate StackTrace");
+13 -13
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="?attr/themeDebug"
<!-- Theme removed for compatibility -->
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -12,7 +12,7 @@
android:layout_width="match_parent"
android:layout_height="34dp"
android:layout_alignParentTop="true"
android:background="?attr/colorTittleBackgound"
android:background=" android:background="@color/toolbarBackgroundColor""
android:id="@+id/viewlogRelativeLayoutToolbar">
<Button
@@ -20,8 +20,8 @@
android:layout_height="@dimen/log_button_height"
android:textSize="@dimen/log_text_size"
android:text="Clean"
android:textColor="?attr/activityTextColor"
android:background="?attr/toolbarBackgroundColor"
android:textColor=" android:textColor="@color/mainWindowTextColor""
android:background=" android:background="@color/toolbarBackgroundColor""
android:layout_centerVertical="true"
android:id="@+id/viewlogButtonClean"
android:layout_marginLeft="5dp"/>
@@ -35,7 +35,7 @@
android:layout_toRightOf="@+id/viewlogButtonClean"
android:layout_centerVertical="true"
android:id="@+id/viewlogTextView1"
android:background="?attr/colorTittleBackgound"
android:background=" android:background="@color/toolbarBackgroundColor""
android:textColor="?attr/toolbarTextColor"/>
<cc.winboll.studio.libappbase.widget.LogTagSpinner
@@ -53,7 +53,7 @@
android:layout_toLeftOf="@+id/viewlogButtonCopy"
android:layout_centerVertical="true"
android:text="Selectable"
android:background="?attr/colorTittleBackgound"
android:background=" android:background="@color/toolbarBackgroundColor""
android:id="@+id/viewlogCheckBoxSelectable"
android:padding="@dimen/log_text_padding"
android:textColor="?attr/toolbarTextColor"/>
@@ -62,8 +62,8 @@
android:layout_width="@dimen/log_button_width"
android:layout_height="@dimen/log_button_height"
android:textSize="@dimen/log_text_size"
android:textColor="?attr/activityTextColor"
android:background="?attr/toolbarBackgroundColor"
android:textColor=" android:textColor="@color/mainWindowTextColor""
android:background=" android:background="@color/toolbarBackgroundColor""
android:text="Copy"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
@@ -79,7 +79,7 @@
android:layout_below="@+id/viewlogRelativeLayoutToolbar"
android:id="@+id/viewlogLinearLayout1"
android:gravity="center_vertical"
android:background="?attr/colorTittleBackgound">
android:background=" android:background="@color/toolbarBackgroundColor"">
<CheckBox
android:layout_width="wrap_content"
@@ -88,7 +88,7 @@
android:text="ALL"
android:padding="2dp"
android:id="@+id/viewlogCheckBox1"
android:background="?attr/colorTittleBackgound"
android:background=" android:background="@color/toolbarBackgroundColor""
android:textColor="?attr/toolbarTextColor"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"/>
@@ -99,14 +99,14 @@
android:layout_height="@dimen/log_button_height"
android:textSize="@dimen/log_text_size"
android:textColor="?attr/colorText"
android:background="?attr/colorTittleBackgound"
android:background=" android:background="@color/toolbarBackgroundColor""
android:singleLine="true"
android:id="@+id/tagsearch_et"/>
<HorizontalScrollView
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="?attr/colorTittleBackgound"
android:background=" android:background="@color/toolbarBackgroundColor""
android:scrollbars="none"
android:padding="2dp"
android:layout_weight="1.0"
@@ -132,7 +132,7 @@
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorTextBackgound"
android:background="@color/colorTextBackgound"
android:id="@+id/viewlogScrollViewLog">
<TextView
+1
View File
@@ -0,0 +1 @@
/build
+37
View File
@@ -0,0 +1,37 @@
# WinBoLL
#### 介绍
WinBoLL 网站浏览器。
#### 软件架构
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
#### Gradle 编译说明
调试版编译命令 gradle assembleBetaDebug
阶段版编译命令 bash .winboll/bashPublishAPKAddTag.sh winboll
#### 使用说明
3. Termux应用配置:
- 已安装Termux(包名 com.termux );
- 执行  echo "allow-external-apps = true" > ~/.termux/termux.properties
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码 : ZhanGSKen(ZhanGSKen<ZhanGSKen@QQ.COM>)
4. 新建 Pull Request
#### 特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
#### 参考文档
+1
View File
@@ -0,0 +1 @@
+110
View File
@@ -0,0 +1,110 @@
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.mywinboll"
minSdkVersion 26
targetSdkVersion 30
//1. Android 官方规则
//-  versionCode  类型:int 整型
//- Java int 最大值:2147483647
versionCode 1520000
// versionName 更新后需要手动设置
// 项目模块目录的 build.gradle 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "15.20"
if(true) {
versionName = genVersionName("${versionName}")
}
}
}
dependencies {
api 'com.google.code.gson:gson:2.10.1'
// 下拉控件
api 'com.baoyz.pullrefreshlayout:library:1.2.0'
// SSH
api 'com.jcraft:jsch:0.1.55'
// Html 解析
api 'org.jsoup:jsoup:1.13.1'
// 二维码类库
api 'com.google.zxing:core:3.4.1'
api 'com.journeyapps:zxing-android-embedded:3.6.0'
// 应用介绍页类库
api 'io.github.medyo:android-about-page:2.0.0'
// 网络连接类库
api 'com.squareup.okhttp3:okhttp:4.4.1'
// FastJSON解析
implementation 'com.alibaba:fastjson:1.2.76'
// AndroidX 类库
/*api 'androidx.appcompat:appcompat:1.1.0'
//api 'com.google.android.material:material:1.4.0'
//api 'androidx.viewpager:viewpager:1.0.0'
//api 'androidx.vectordrawable:vectordrawable:1.1.0'
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
//api 'androidx.fragment:fragment:1.1.0'*/
// 米盟
//api 'com.miui.zeus:mimo-ad-sdk:5.3.+'//请使用最新版sdk
//注意:以下5个库必须要引入
//implementation 'androidx.appcompat:appcompat:1.4.1'
api 'androidx.recyclerview:recyclerview:1.0.0'
api 'com.github.bumptech.glide:glide:4.9.0'
//annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
implementation "androidx.annotation:annotation:1.3.0"
implementation "androidx.core:core:1.6.0"
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
implementation "androidx.preference:preference:1.1.1"
implementation "androidx.viewpager:viewpager:1.0.0"
implementation "com.google.android.material:material:1.4.0"
implementation "com.google.guava:guava:24.1-jre"
/*
implementation "io.noties.markwon:core:$markwonVersion"
implementation "io.noties.markwon:ext-strikethrough:$markwonVersion"
implementation "io.noties.markwon:linkify:$markwonVersion"
implementation "io.noties.markwon:recycler:$markwonVersion"
*/
implementation 'com.termux:terminal-emulator:0.118.0'
implementation 'com.termux:terminal-view:0.118.0'
implementation 'com.termux:termux-shared:0.118.0'
// Biometric (指纹识别)
implementation 'androidx.biometric:biometric:1.1.0'
// WinBoLL库 nexus.winboll.cc 地址
api 'cc.winboll.studio:libappbase:15.20.34'
api 'cc.winboll.studio:libaes:15.20.17'
// 备用库 jitpack.io 地址
//api 'com.github.ZhanGSKen:libappbase:appbase-v15.20.33'
//api 'com.github.ZhanGSKen:libaes:aes-v15.20.16'
api fileTree(dir: 'libs', include: ['*.jar'])
}
+8
View File
@@ -0,0 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed Jul 01 16:36:17 CST 2026
stageCount=0
libraryProject=
baseVersion=15.20
publishVersion=15.20.0
buildCount=11
baseBetaVersion=15.20.1
+137
View File
@@ -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 <fields>;
}
# 保留 native 方法(避免JNI调用失败)
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留注解和泛型(避免反射/序列化异常)
-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 {
<init>();
}
-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 <fields>;
}
# 米盟 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
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<!-- Put flavor specific code here -->
</application>
</manifest>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MyWinBoLL +</string>
</resources>
+37
View File
@@ -0,0 +1,37 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.mywinboll">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:theme="@style/MyAppTheme"
android:resizeableActivity="true"
android:name=".App">
<activity
android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="android.max_aspect"
android:value="4.0"/>
<activity android:name=".GlobalApplication$CrashActivity"/>
</application>
</manifest>
@@ -0,0 +1,358 @@
package cc.winboll.studio.mywinboll;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import cc.winboll.studio.libappbase.CrashActivity;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils;
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class App extends GlobalApplication {
private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
@Override
public void onCreate() {
try {
super.onCreate();
// 初始化主题样式ID列表
AESThemeUtil.init(null);
// 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用)
ToastUtils.init(getApplicationContext());
} catch (Throwable e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.close();
String stackTraceStr = sw.toString();
CrashHandleNotifyUtils.handleUncaughtException(
this,
getPackageName(),
stackTraceStr,
CrashActivity.class
);
}
}
public static void write(InputStream input, OutputStream output) throws IOException {
byte[] buf = new byte[1024 * 8];
int len;
while ((len = input.read(buf)) != -1) {
output.write(buf, 0, len);
}
}
public static void write(File file, byte[] data) throws IOException {
File parent = file.getParentFile();
if (parent != null && !parent.exists()) parent.mkdirs();
ByteArrayInputStream input = new ByteArrayInputStream(data);
FileOutputStream output = new FileOutputStream(file);
try {
write(input, output);
} finally {
closeIO(input, output);
}
}
public static String toString(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
write(input, output);
try {
return output.toString("UTF-8");
} finally {
closeIO(input, output);
}
}
public static void closeIO(Closeable... closeables) {
for (Closeable closeable : closeables) {
try {
if (closeable != null) closeable.close();
} catch (IOException ignored) {}
}
}
public static class CrashHandler {
public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
private static CrashHandler sInstance;
private PartCrashHandler mPartCrashHandler;
public static CrashHandler getInstance() {
if (sInstance == null) {
sInstance = new CrashHandler();
}
return sInstance;
}
public void registerGlobal(Context context) {
registerGlobal(context, null);
}
public void registerGlobal(Context context, String crashDir) {
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl(context.getApplicationContext(), crashDir));
}
public void unregister() {
Thread.setDefaultUncaughtExceptionHandler(DEFAULT_UNCAUGHT_EXCEPTION_HANDLER);
}
public void registerPart(Context context) {
unregisterPart(context);
mPartCrashHandler = new PartCrashHandler(context.getApplicationContext());
MAIN_HANDLER.postAtFrontOfQueue(mPartCrashHandler);
}
public void unregisterPart(Context context) {
if (mPartCrashHandler != null) {
mPartCrashHandler.isRunning.set(false);
mPartCrashHandler = null;
}
}
private static class PartCrashHandler implements Runnable {
private final Context mContext;
public AtomicBoolean isRunning = new AtomicBoolean(true);
public PartCrashHandler(Context context) {
this.mContext = context;
}
@Override
public void run() {
while (isRunning.get()) {
try {
Looper.loop();
} catch (final Throwable e) {
e.printStackTrace();
if (isRunning.get()) {
MAIN_HANDLER.post(new Runnable(){
@Override
public void run() {
Toast.makeText(mContext, e.toString(), Toast.LENGTH_LONG).show();
}
});
} else {
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
} else {
throw new RuntimeException(e);
}
}
}
}
}
}
private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler {
private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss");
private final Context mContext;
private final File mCrashDir;
public UncaughtExceptionHandlerImpl(Context context, String crashDir) {
this.mContext = context;
this.mCrashDir = TextUtils.isEmpty(crashDir) ? new File(mContext.getExternalCacheDir(), "crash") : new File(crashDir);
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
try {
String log = buildLog(throwable);
writeLog(log);
try {
Intent intent = new Intent(mContext, CrashActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_TEXT, log);
mContext.startActivity(intent);
} catch (Throwable e) {
e.printStackTrace();
writeLog(e.toString());
}
throwable.printStackTrace();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
} catch (Throwable e) {
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
}
}
private String buildLog(Throwable throwable) {
String time = DATE_FORMAT.format(new Date());
String versionName = "unknown";
long versionCode = 0;
try {
PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
versionName = packageInfo.versionName;
versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode() : packageInfo.versionCode;
} catch (Throwable ignored) {}
LinkedHashMap<String, String> head = new LinkedHashMap<String, String>();
head.put("Time Of Crash", time);
head.put("Device", String.format("%s, %s", Build.MANUFACTURER, Build.MODEL));
head.put("Android Version", String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT));
head.put("App Version", String.format("%s (%d)", versionName, versionCode));
head.put("Kernel", getKernel());
head.put("Support Abis", Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS != null ? Arrays.toString(Build.SUPPORTED_ABIS): "unknown");
head.put("Fingerprint", Build.FINGERPRINT);
StringBuilder builder = new StringBuilder();
for (String key : head.keySet()) {
if (builder.length() != 0) builder.append("\n");
builder.append(key);
builder.append(" : ");
builder.append(head.get(key));
}
builder.append("\n\n");
builder.append(Log.getStackTraceString(throwable));
return builder.toString();
}
private void writeLog(String log) {
String time = DATE_FORMAT.format(new Date());
File file = new File(mCrashDir, "crash_" + time + ".txt");
try {
write(file, log.getBytes("UTF-8"));
} catch (Throwable e) {
e.printStackTrace();
}
}
private static String getKernel() {
try {
return App.toString(new FileInputStream("/proc/version")).trim();
} catch (Throwable e) {
return e.getMessage();
}
}
}
}
public static final class CrashActivity extends Activity {
private String mLog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(android.R.style.Theme_DeviceDefault);
setTitle("App Crash");
mLog = getIntent().getStringExtra(Intent.EXTRA_TEXT);
ScrollView contentView = new ScrollView(this);
contentView.setFillViewport(true);
HorizontalScrollView horizontalScrollView = new HorizontalScrollView(this);
TextView textView = new TextView(this);
int padding = dp2px(16);
textView.setPadding(padding, padding, padding, padding);
textView.setText(mLog);
textView.setTextIsSelectable(true);
textView.setTypeface(Typeface.DEFAULT);
textView.setLinksClickable(true);
horizontalScrollView.addView(textView);
contentView.addView(horizontalScrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
setContentView(contentView);
}
private void restart() {
Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
finish();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
private static int dp2px(float dpValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, android.R.id.copy, 0, android.R.string.copy)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.copy:
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
restart();
}
}
}
@@ -0,0 +1,62 @@
package cc.winboll.studio.mywinboll;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libaes.utils.DevelopUtils;
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
LogView mLogView;
@Override
protected void onCreate(Bundle savedInstanceState) {
AESThemeUtil.applyAppCompatTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mLogView = findViewById(R.id.logview);
ToastUtils.show("onCreate");
}
@Override
protected void onResume() {
super.onResume();
mLogView.start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
AESThemeUtil.inflateCompatThemeMenu(this, menu);
if (GlobalApplication.isDebugging()) {
DevelopUtils.inflateMenu(this, menu);
}
getMenuInflater().inflate(cc.winboll.studio.libaes.R.menu.toolbar_drawerbase, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (AESThemeUtil.onWinBoLLThemeItemSelected(this, item)) {
recreate();
}
if (DevelopUtils.onDevelopItemSelected(this, item)) {
LogUtils.d(TAG, String.format("onOptionsItemSelected item.getItemId() %d ", item.getItemId()));
} else {
return super.onOptionsItemSelected(item);
}
return true;
}
}
@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<cc.winboll.studio.libaes.views.ASupportToolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/toolbar"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:gravity="center_vertical|center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MyWinBoLL"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0">
<cc.winboll.studio.libappbase.LogView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/logview"/>
</LinearLayout>
</LinearLayout>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#42A5F5</color>
<color name="colorPrimaryDark">#1565C0</color>
<color name="colorAccent">#FFD740</color>
<color name="mainWindowBackgroundColor">#121212</color>
<color name="mainWindowTextColor">#E0E0E0</color>
<color name="toolbarTextColor">#FFFFFF</color>
<color name="toolbarBackgroundColor">#1E3A5F</color>
<color name="debugTextColor">#00E676</color>
<color name="colorTextBackgound">#121212</color>
</resources>
@@ -0,0 +1,29 @@
<resources>
<style name="MyAppTheme" parent="Theme.AppCompat.NoActionBar">
<item name="aSupportToolbar">@style/MyAESASupportToolbar</item>
<item name="themeDebug">@style/MyDebugActivityTheme</item>
<item name="aboutViewBackgroundColor">?attr/mainWindowDarkBackgroundColor</item>
<item name="aboutViewTextColor">?attr/mainWindowDarkTextColor</item>
<item name="aboutViewTitleColor">?attr/mainWindowDarkTextColor</item>
<item name="aboutViewDividerColor">?attr/mainWindowTextColor</item>
<item name="dialogBackgroundColor">?attr/mainWindowDarkBackgroundColor</item>
<item name="dialogTextColor">?attr/mainWindowDarkTextColor</item>
<item name="toolbarBackgroundColor">@color/toolbarBackgroundColor</item>
<item name="toolbarTextColor">@color/toolbarTextColor</item>
<item name="textViewBackgroundColor">?attr/mainWindowDarkBackgroundColor</item>
<item name="textViewTextColor">?attr/mainWindowDarkTextColor</item>
<item name="editTextBackgroundColor">?attr/mainWindowDarkBackgroundColor</item>
<item name="editTextTextColor">?attr/mainWindowDarkTextColor</item>
<item name="scrollViewBackgroundColor">?attr/mainWindowDarkBackgroundColor</item>
<item name="activityBackgroundColor">?attr/mainWindowDarkBackgroundColor</item>
<item name="activityTextColor">?attr/mainWindowDarkTextColor</item>
<item name="mainWindowBackgroundColor">@color/mainWindowBackgroundColor</item>
<item name="mainWindowTextColor">@color/mainWindowTextColor</item>
<item name="mainWindowDarkBackgroundColor">@color/mainWindowBackgroundColor</item>
<item name="mainWindowDarkTextColor">@color/mainWindowTextColor</item>
</style>
<style name="MyAESASupportToolbar" parent="AESASupportToolbar" />
</resources>
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#1565C0</color>
<color name="colorPrimaryDark">#0D47A1</color>
<color name="colorAccent">#FF6F00</color>
<color name="mainWindowBackgroundColor">#FAFAFA</color>
<color name="mainWindowTextColor">#212121</color>
<color name="toolbarTextColor">#FFFFFF</color>
<color name="toolbarBackgroundColor">#1565C0</color>
<color name="debugTextColor">#616161</color>
<color name="colorTextBackgound">#FAFAFA</color>
</resources>
@@ -0,0 +1,4 @@
<resources>
<string name="app_name">MyWinBoLL</string>
</resources>
+37
View File
@@ -0,0 +1,37 @@
<resources>
<style name="MyAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="aSupportToolbar">@style/MyAESASupportToolbar</item>
<item name="themeDebug">@style/MyDebugActivityTheme</item>
<item name="aboutViewBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="aboutViewTextColor">?attr/mainWindowTextColor</item>
<item name="aboutViewTitleColor">?attr/mainWindowTextColor</item>
<item name="aboutViewDividerColor">?attr/mainWindowDarkTextColor</item>
<item name="dialogBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="dialogTextColor">?attr/mainWindowTextColor</item>
<item name="toolbarBackgroundColor">@color/toolbarBackgroundColor</item>
<item name="toolbarTextColor">@color/toolbarTextColor</item>
<item name="textViewBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="textViewTextColor">?attr/mainWindowTextColor</item>
<item name="editTextBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="editTextTextColor">?attr/mainWindowTextColor</item>
<item name="scrollViewBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="activityBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="activityTextColor">?attr/mainWindowTextColor</item>
<item name="mainWindowBackgroundColor">@color/mainWindowBackgroundColor</item>
<item name="mainWindowTextColor">@color/mainWindowTextColor</item>
<item name="mainWindowDarkBackgroundColor">@color/mainWindowBackgroundColor</item>
<item name="mainWindowDarkTextColor">@color/mainWindowTextColor</item>
</style>
<style name="MyDebugActivityTheme" parent="DebugActivityTheme">
<item name="colorTittle">?attr/mainWindowTextColor</item>
<item name="colorTittleBackgound">?attr/toolbarBackgroundColor</item>
<item name="colorText">?attr/debugTextColor</item>
<item name="colorTextBackgound">?attr/mainWindowBackgroundColor</item>
<item name="toolbarTextColor">@color/toolbarTextColor</item>
</style>
<style name="MyAESASupportToolbar" parent="AESASupportToolbar" />
</resources>
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<!-- Put flavor specific code here -->
</application>
</manifest>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Put flavor specific strings here -->
</resources>
+3 -5
View File
@@ -1,5 +1,3 @@
// AES 项目编译设置
include ':aes'
include ':libaes'
rootProject.name = "aes"
// MyWinBoLL 项目编译设置
include ':mywinboll'
rootProject.name = "mywinboll"
+3 -98
View File
@@ -1,98 +1,3 @@
// AutoInstaller 项目编译设置
//include ':autoinstaller'
//rootProject.name = "autoinstaller"
// MJ 项目编译设置
//include ':mj'
//rootProject.name = "mj"
// PowerBell 项目编译设置
//include ':powerbell'
//rootProject.name = "powerbell"
// APPBase 项目编译设置
//include ':appbase'
//include ':libappbase'
//rootProject.name = "appbase"
// APPUtils 项目编译设置
//include ':apputils'
//include ':libapputils'
//rootProject.name = "apputils"
// JC 项目编译设置
//include ':jc'
//include ':libjc'
//rootProject.name = "jc"
// AES 项目编译设置
//include ':aes'
//include ':libaes'
//rootProject.name = "aes"
// Contacts 项目编译设置
//include ':contacts'
//rootProject.name = "contacts"
// MyMessageManager 项目编译设置
//include ':mymessagemanager'
//rootProject.name = "mymessagemanager"
// TimeStamp 项目编译设置
//include ':timestamp'
//rootProject.name = "timestamp"
// AndroidDemo 项目编译设置
//include ':androiddemo'
//rootProject.name = "androiddemo"
// AndroidXDemo 项目编译设置
//include ':androidxdemo'
//rootProject.name = "androidxdemo"
// Ollama 项目编译设置
//include ':ollama'
//rootProject.name = "ollama"
// NumTable 项目编译设置
//include ':numtable'
//rootProject.name = "numtable"
// MidiPlayer 项目编译设置
//include ':midiplayer'
//rootProject.name = "midiplayer"
// WebPageSources 项目编译设置
//include ':webpagesources'
//rootProject.name = "webpagesources"
// Positions 项目编译设置
//include ':positions'
//rootProject.name = "positions"
// WinBoLL 项目编译设置
//include ':winboll'
//include ':libwinboll'
//rootProject.name = "winboll"
// RegExpUtils 项目编译设置
//include ':regexputils'
//rootProject.name = "regexputils"
// GPSRelaySentinel 项目编译设置
//include ':gpsrelaysentinel'
//include ':libgpsrelaysentinel'
//rootProject.name = "gpsrelaysentinel"
// Gallery 项目编译设置
//include ':gallery'
//rootProject.name = "gallery"
// DebugTemp 项目编译设置
//include ':debugtemp'
//include ':libdebugtemp'
//rootProject.name = "debugtemp"
// AutoNFC 项目编译设置
//include ':autonfc'
//rootProject.name = "autonfc"
// MyWinBoLL 项目编译设置
//include ':mywinboll'
//rootProject.name = "mywinboll"
+37
View File
@@ -0,0 +1,37 @@
# WinBoLL
#### 介绍
WinBoLL 网站浏览器。
#### 软件架构
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
#### Gradle 编译说明
调试版编译命令 gradle assembleBetaDebug
阶段版编译命令 bash .winboll/bashPublishAPKAddTag.sh winboll
#### 使用说明
3. Termux应用配置:
- 已安装Termux(包名 com.termux );
- 执行  echo "allow-external-apps = true" > ~/.termux/termux.properties
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码 : ZhanGSKen(ZhanGSKen<ZhanGSKen@QQ.COM>)
4. 新建 Pull Request
#### 特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
#### 参考文档
+1
View File
@@ -0,0 +1 @@
+111
View File
@@ -0,0 +1,111 @@
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
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
defaultConfig {
applicationId "cc.winboll.studio.winboll"
minSdkVersion 26
// 适配MIUI12
targetSdkVersion 30
versionCode 1
// versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "15.20"
if(true) {
versionName = genVersionName("${versionName}")
}
}
}
dependencies {
api 'com.google.code.gson:gson:2.10.1'
// 下拉控件
api 'com.baoyz.pullrefreshlayout:library:1.2.0'
// SSH
api 'com.jcraft:jsch:0.1.55'
// Html 解析
api 'org.jsoup:jsoup:1.13.1'
// 二维码类库
api 'com.google.zxing:core:3.4.1'
api 'com.journeyapps:zxing-android-embedded:3.6.0'
// 应用介绍页类库
api 'io.github.medyo:android-about-page:2.0.0'
// 网络连接类库
api 'com.squareup.okhttp3:okhttp:4.4.1'
// FastJSON解析
implementation 'com.alibaba:fastjson:1.2.76'
// AndroidX 类库
/*api 'androidx.appcompat:appcompat:1.1.0'
//api 'com.google.android.material:material:1.4.0'
//api 'androidx.viewpager:viewpager:1.0.0'
//api 'androidx.vectordrawable:vectordrawable:1.1.0'
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
//api 'androidx.fragment:fragment:1.1.0'*/
// 米盟
//api 'com.miui.zeus:mimo-ad-sdk:5.3.+'//请使用最新版sdk
//注意:以下5个库必须要引入
//implementation 'androidx.appcompat:appcompat:1.4.1'
api 'androidx.recyclerview:recyclerview:1.0.0'
api 'com.github.bumptech.glide:glide:4.9.0'
//annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
implementation "androidx.annotation:annotation:1.3.0"
implementation "androidx.core:core:1.6.0"
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
implementation "androidx.preference:preference:1.1.1"
implementation "androidx.viewpager:viewpager:1.0.0"
implementation "com.google.android.material:material:1.4.0"
implementation "com.google.guava:guava:24.1-jre"
/*
implementation "io.noties.markwon:core:$markwonVersion"
implementation "io.noties.markwon:ext-strikethrough:$markwonVersion"
implementation "io.noties.markwon:linkify:$markwonVersion"
implementation "io.noties.markwon:recycler:$markwonVersion"
*/
implementation 'com.termux:terminal-emulator:0.118.0'
implementation 'com.termux:terminal-view:0.118.0'
implementation 'com.termux:termux-shared:0.118.0'
// Biometric (指纹识别)
implementation 'androidx.biometric:biometric:1.1.0'
// WinBoLL库 nexus.winboll.cc 地址
api 'cc.winboll.studio:libappbase:15.20.34'
api 'cc.winboll.studio:libaes:15.20.17'
// 备用库 jitpack.io 地址
//api 'com.github.ZhanGSKen:libappbase:appbase-v15.20.33'
//api 'com.github.ZhanGSKen:libaes:aes-v15.20.16'
api fileTree(dir: 'libs', include: ['*.jar'])
}
+8
View File
@@ -0,0 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed Jul 01 14:49:57 CST 2026
stageCount=9
libraryProject=
baseVersion=15.20
publishVersion=15.20.8
buildCount=16
baseBetaVersion=15.20.9
Binary file not shown.
+137
View File
@@ -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 <fields>;
}
# 保留 native 方法(避免JNI调用失败)
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留注解和泛型(避免反射/序列化异常)
-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 {
<init>();
}
-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 <fields>;
}
# 米盟 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
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application
tools:replace="android:icon"
android:icon="@drawable/ic_launcher_beta">
<!-- Put flavor specific code here -->
</application>
</manifest>
@@ -0,0 +1,32 @@
package cc.winboll.studio.winboll.activities;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.winboll.R;
/**
* Ollama 模型对话窗口
*/
public class OllamaWindowActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ollama_window);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("Ollama 窗口");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_ollama_window, menu);
return true;
}
}
@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1E1E1E"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#333333"
android:elevation="4dp"
android:theme="@style/ThemeOverlay.MaterialComponents.ActionBar" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="56dp"
android:layout_marginBottom="48dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ollama 模型对话"
android:textColor="#FFFFFF"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="系统提示:"
android:textColor="#B0B0B0" />
<EditText
android:id="@+id/etSystemPrompt"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#2D2D2D"
android:hint="请输入系统提示"
android:textColor="#FFFFFF"
android:textColorHint="#666666"
android:gravity="top|start"
android:inputType="textMultiLine" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="模型名称:"
android:textColor="#B0B0B0" />
<EditText
android:id="@+id/etModelName"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#2D2D2D"
android:hint="llama3"
android:text="llama3"
android:textColor="#FFFFFF"
android:textColorHint="#666666"
android:singleLine="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="系统消息:"
android:textColor="#B0B0B0" />
<EditText
android:id="@+id/etUserMessage"
android:layout_width="match_parent"
android:layout_height="120dp"
android:background="#2D2D2D"
android:ems="10"
android:gravity="top|start"
android:hint="输入你要发送的消息..."
android:inputType="textMultiLine" />
<EditText
android:id="@+id/etAssistantMessage"
android:layout_width="match_parent"
android:layout_height="120dp"
android:background="#2D2D2D"
android:gravity="top|start"
android:hint="模型响应将显示在这里..."
android:inputType="textMultiLine" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<Button
android:id="@+id/btnSend"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="发送"
android:textColor="#FFFFFF" />
<Button
android:id="@+id/btnStop"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_weight="1"
android:text="停止"
android:textColor="#FF5252" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_config"
android:title="配置"
android:orderInCategory="100"
app:showAsAction="always" />
</menu>
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">WinBoLL+</string>
<string name="app_name_cn1">筋斗云★</string>
<string name="app_name_cn2">金抖云☆</string>
</resources>
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.winboll.beta"
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<!--<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.winboll.beta"
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.winboll.beta"
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.winboll.beta"
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.winboll.beta"
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<!--<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.winboll.beta"
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
</shortcuts>
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<!--<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.winboll.beta"
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.winboll.beta"
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.winboll.beta"
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>
+353
View File
@@ -0,0 +1,353 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="cc.winboll.studio.winboll"
android:sharedUserId="com.termux">
<!-- 拥有完全的网络访问权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- 发送持久广播 -->
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
<!-- 创建桌面快捷方式(Android 8.0 以下兼容) -->
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
<!-- 对正在运行的应用重新排序 -->
<uses-permission android:name="android.permission.REORDER_TASKS"/>
<!-- 计算应用存储空间 -->
<uses-permission
android:name="android.permission.GET_PACKAGE_SIZE"/>
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:roundIcon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/MyAppTheme"
android:resizeableActivity="true"
android:name=".App"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
</intent-filter>
</activity>
<activity android:name=".activities.ShortcutActionActivity"/>
<activity-alias
android:name=".MainActivityEN1"
android:targetActivity=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmainen1"/>
</activity-alias>
<activity-alias
android:name=".MainActivityCN1"
android:targetActivity=".MainActivity"
android:exported="true"
android:label="@string/app_name_cn1"
android:icon="@drawable/ic_winboll_jindouyun1"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmaincn1"/>
</activity-alias>
<activity-alias
android:name=".MainActivityCN2"
android:targetActivity=".MainActivity"
android:exported="true"
android:label="@string/app_name_cn2"
android:icon="@drawable/ic_winboll_jindouyun2"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmaincn2"/>
</activity-alias>
<activity
android:name=".activities.WinBoLLUnitTestActivity"
android:label="@string/app_name"
android:exported="true"
android:resizeableActivity="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name=".activities.NewActivity"
android:label="NewActivity"
android:exported="true"
android:resizeableActivity="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"/>
<activity
android:name=".activities.New2Activity"
android:label="New2Activity"
android:exported="true"
android:resizeableActivity="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"/>
<service
android:name=".MyTileService"
android:exported="true"
android:label="@string/tileservice_name"
android:icon="@drawable/ic_launcher"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE"/>
</intent-filter>
</service>
<service
android:name=".services.MainService"
android:exported="true"/>
<service
android:name="cc.winboll.studio.appbase.services.TestDemoBindService"
android:exported="true"/>
<service
android:name="cc.winboll.studio.appbase.services.TestDemoService"
android:exported="true"/>
<service android:name=".services.AssistantService"/>
<receiver
android:name="cc.winboll.studio.appbase.receivers.MainReceiver"
android:exported="true">
<intent-filter>
<action android:name="cc.winboll.studio.appbase.receivers.MainReceiver"/>
</intent-filter>
</receiver>
<receiver
android:name=".widgets.APPNewsWidget"
android:exported="true">
<intent-filter>
<action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidget.ACTION_WAKEUP_SERVICE"/>
<action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidget.ACTION_RELOAD_REPORT"/>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_provider_info_sos"/>
</receiver>
<receiver
android:name=".receivers.APPNewsWidgetClickListener"
android:exported="true">
<intent-filter>
<action android:name="cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener.ACTION_PRE"/>
<action android:name="cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener.ACTION_NEXT"/>
</intent-filter>
</receiver>
<service
android:name=".SimpleOperateSignalCenterService"
android:exported="true">
</service>
<service
android:name=".services.TestService"
android:exported="true"/>
<receiver
android:name=".receiver.MyBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="cc.winboll.studio.libappbase.action.SOS"/>
</intent-filter>
</receiver>
<receiver
android:name=".widgets.StatusWidget"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="cc.winboll.studio.libappbase.widgets.StatusWidget.ACTION_STATUS_UPDATE"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_provider_info_status"/>
</receiver>
<receiver
android:name=".widgets.StatusWidgetClickListener"
android:exported="true">
<intent-filter>
<action android:name="cc.winboll.studio.libappbase.widgets.StatusWidgetClickListener.ACTION_IVAPP"/>
</intent-filter>
</receiver>
<service android:name="cc.winboll.studio.libappbase.sos.SOSCenter"/>
<receiver
android:name="cc.winboll.studio.libappbase.sos.SOSCenterServiceReceiver"
android:exported="true">
<intent-filter>
<action android:name="cc.winboll.studio.libappbase.sos.SOSCenterServiceReceiver"/>
</intent-filter>
</receiver>
<activity android:name="cc.winboll.studio.libappbase.activities.YunActivity"/>
<activity android:name="cc.winboll.studio.libappbase.activities.LogonActivity"/>
<activity android:name="cc.winboll.studio.winboll.activities.AboutActivity"/>
<activity android:name="cc.winboll.studio.winboll.activities.SettingsActivity"/>
<activity android:name="cc.winboll.studio.winboll.unittest.TestWeWorkSpecSDK"/>
<activity android:name="cc.winboll.studio.winboll.activities.WXPayActivity"/>
<activity android:name="cc.winboll.studio.winboll.activities.PatternLockActivity"
android:exported="true">
<intent-filter>
<action android:name="cc.winboll.studio.winboll.activities.PatternLockActivity.ACTION_OPEN_PATTERN"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name="cc.winboll.studio.winboll.unittest.TermuxEnvTestActivity"/>
<activity
android:name=".termux.NfcTermuxBridgeActivity"
android:exported="true">
<intent-filter>
<action android:name="cc.winboll.studio.winboll.termux.NfcTermuxBridgeActivity.ACTION_BUILD"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name="cc.winboll.studio.winboll.termux.MyTermuxActivity"
android:label="@string/my_termux_activity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="cc.winboll.studio.winboll.action.EXECUTE_TERMUX_BUTTON"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,387 @@
package cc.winboll.studio.winboll;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.GlobalCrashActivity;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class App extends GlobalApplication {
public static final String TAG = "App";
public static final String COMPONENT_EN1 = "cc.winboll.studio.winboll.MainActivityEN1";
public static final String COMPONENT_CN1 = "cc.winboll.studio.winboll.MainActivityCN1";
public static final String COMPONENT_CN2 = "cc.winboll.studio.winboll.MainActivityCN2";
public static final String ACTION_SWITCHTO_EN1 = "cc.winboll.studio.winboll.App.ACTION_SWITCHTO_EN1";
public static final String ACTION_SWITCHTO_CN1 = "cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN1";
public static final String ACTION_SWITCHTO_CN2 = "cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN2";
private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
@Override
public void onCreate() {
try {
super.onCreate();
ToastUtils.init(this);
WinBoLLActivityManager.init(this);
// 初始化 AES 主题工具(注入当前应用命名空间的主题ID列表,按 ThemeType.ordinal() 顺序)
ArrayList<Integer> themeStyleList = new ArrayList<Integer>();
themeStyleList.add(R.style.MyAppTheme); // AES(0)
themeStyleList.add(R.style.MyDepthAppTheme); // DEPTH(1)
themeStyleList.add(R.style.MySkyAppTheme); // SKY(2)
themeStyleList.add(R.style.MyGoldenAppTheme); // GOLDEN(3)
themeStyleList.add(R.style.MyBearingAppTheme); // BEARING(4)
themeStyleList.add(R.style.MyMemorAppTheme); // MEMOR(5)
themeStyleList.add(R.style.MyTaoAppTheme); // TAO(6)
AESThemeUtil.init(themeStyleList);
} catch (Throwable e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.close();
String stackTraceStr = sw.toString();
CrashHandleNotifyUtils.handleUncaughtException(
this,
getPackageName(),
stackTraceStr,
GlobalCrashActivity.class
);
}
}
@Override
public void onTerminate() {
super.onTerminate();
ToastUtils.release();
}
public static void write(InputStream input, OutputStream output) throws IOException {
byte[] buf = new byte[1024 * 8];
int len;
while ((len = input.read(buf)) != -1) {
output.write(buf, 0, len);
}
}
public static void write(File file, byte[] data) throws IOException {
File parent = file.getParentFile();
if (parent != null && !parent.exists()) parent.mkdirs();
ByteArrayInputStream input = new ByteArrayInputStream(data);
FileOutputStream output = new FileOutputStream(file);
try {
write(input, output);
} finally {
closeIO(input, output);
}
}
public static String toString(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
write(input, output);
try {
return output.toString("UTF-8");
} finally {
closeIO(input, output);
}
}
public static void closeIO(Closeable... closeables) {
for (Closeable closeable : closeables) {
try {
if (closeable != null) closeable.close();
} catch (IOException ignored) {}
}
}
public static class CrashHandler {
public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
private static CrashHandler sInstance;
private PartCrashHandler mPartCrashHandler;
public static CrashHandler getInstance() {
if (sInstance == null) {
sInstance = new CrashHandler();
}
return sInstance;
}
public void registerGlobal(Context context) {
registerGlobal(context, null);
}
public void registerGlobal(Context context, String crashDir) {
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl(context.getApplicationContext(), crashDir));
}
public void unregister() {
Thread.setDefaultUncaughtExceptionHandler(DEFAULT_UNCAUGHT_EXCEPTION_HANDLER);
}
public void registerPart(Context context) {
unregisterPart(context);
mPartCrashHandler = new PartCrashHandler(context.getApplicationContext());
MAIN_HANDLER.postAtFrontOfQueue(mPartCrashHandler);
}
public void unregisterPart(Context context) {
if (mPartCrashHandler != null) {
mPartCrashHandler.isRunning.set(false);
mPartCrashHandler = null;
}
}
private static class PartCrashHandler implements Runnable {
private final Context mContext;
public AtomicBoolean isRunning = new AtomicBoolean(true);
public PartCrashHandler(Context context) {
this.mContext = context;
}
@Override
public void run() {
while (isRunning.get()) {
try {
Looper.loop();
} catch (final Throwable e) {
e.printStackTrace();
if (isRunning.get()) {
MAIN_HANDLER.post(new Runnable(){
@Override
public void run() {
Toast.makeText(mContext, e.toString(), Toast.LENGTH_LONG).show();
}
});
} else {
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
} else {
throw new RuntimeException(e);
}
}
}
}
}
}
private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler {
private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss");
private final Context mContext;
private final File mCrashDir;
public UncaughtExceptionHandlerImpl(Context context, String crashDir) {
this.mContext = context;
this.mCrashDir = TextUtils.isEmpty(crashDir) ? new File(mContext.getExternalCacheDir(), "crash") : new File(crashDir);
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
try {
String log = buildLog(throwable);
writeLog(log);
try {
Intent intent = new Intent(mContext, CrashActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_TEXT, log);
mContext.startActivity(intent);
} catch (Throwable e) {
e.printStackTrace();
writeLog(e.toString());
}
throwable.printStackTrace();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
} catch (Throwable e) {
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
}
}
private String buildLog(Throwable throwable) {
String time = DATE_FORMAT.format(new Date());
String versionName = "unknown";
long versionCode = 0;
try {
PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
versionName = packageInfo.versionName;
versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode() : packageInfo.versionCode;
} catch (Throwable ignored) {}
LinkedHashMap<String, String> head = new LinkedHashMap<String, String>();
head.put("Time Of Crash", time);
head.put("Device", String.format("%s, %s", Build.MANUFACTURER, Build.MODEL));
head.put("Android Version", String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT));
head.put("App Version", String.format("%s (%d)", versionName, versionCode));
head.put("Kernel", getKernel());
head.put("Support Abis", Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS != null ? Arrays.toString(Build.SUPPORTED_ABIS): "unknown");
head.put("Fingerprint", Build.FINGERPRINT);
StringBuilder builder = new StringBuilder();
for (String key : head.keySet()) {
if (builder.length() != 0) builder.append("\n");
builder.append(key);
builder.append(" : ");
builder.append(head.get(key));
}
builder.append("\n\n");
builder.append(Log.getStackTraceString(throwable));
return builder.toString();
}
private void writeLog(String log) {
String time = DATE_FORMAT.format(new Date());
File file = new File(mCrashDir, "crash_" + time + ".txt");
try {
write(file, log.getBytes("UTF-8"));
} catch (Throwable e) {
e.printStackTrace();
}
}
private static String getKernel() {
try {
return App.toString(new FileInputStream("/proc/version")).trim();
} catch (Throwable e) {
return e.getMessage();
}
}
}
}
public static final class CrashActivity extends Activity {
private String mLog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(android.R.style.Theme_DeviceDefault);
setTitle("App Crash");
mLog = getIntent().getStringExtra(Intent.EXTRA_TEXT);
ScrollView contentView = new ScrollView(this);
contentView.setFillViewport(true);
HorizontalScrollView horizontalScrollView = new HorizontalScrollView(this);
TextView textView = new TextView(this);
int padding = dp2px(16);
textView.setPadding(padding, padding, padding, padding);
textView.setText(mLog);
textView.setTextIsSelectable(true);
textView.setTypeface(Typeface.DEFAULT);
textView.setLinksClickable(true);
horizontalScrollView.addView(textView);
contentView.addView(horizontalScrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
setContentView(contentView);
}
private void restart() {
Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
finish();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
private static int dp2px(float dpValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, android.R.id.copy, 0, android.R.string.copy)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.copy:
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
restart();
}
}
}
@@ -0,0 +1,97 @@
package cc.winboll.studio.winboll;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/28 19:12:12
* @Describe 应用主要服务组件类守护进程服务组件类
*/
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import cc.winboll.studio.libaes.models.WinBoLLClientServiceBean;
import cc.winboll.studio.winboll.WinBoLLClientService;
import cc.winboll.studio.winboll.utils.ServiceUtils;
public class AssistantService extends Service {
public final static String TAG = "AssistantService";
WinBoLLClientServiceBean mWinBoLLServiceBean;
MyServiceConnection mMyServiceConnection;
volatile boolean mIsServiceRunning;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
mWinBoLLServiceBean = WinBoLLClientServiceBean.loadWinBoLLClientServiceBean(this);
if (mMyServiceConnection == null) {
mMyServiceConnection = new MyServiceConnection();
}
// 设置运行参数
mIsServiceRunning = false;
run();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
run();
return START_STICKY;
}
@Override
public void onDestroy() {
mIsServiceRunning = false;
super.onDestroy();
}
//
// 运行服务内容
//
void run() {
mWinBoLLServiceBean = WinBoLLClientServiceBean.loadWinBoLLClientServiceBean(this);
if (mWinBoLLServiceBean.isEnable()) {
if (mIsServiceRunning == false) {
// 设置运行状态
mIsServiceRunning = true;
// 唤醒和绑定主进程
wakeupAndBindMain();
}
}
}
//
// 唤醒和绑定主进程
//
void wakeupAndBindMain() {
if (ServiceUtils.isServiceAlive(getApplicationContext(), WinBoLLClientService.class.getName()) == false) {
startForegroundService(new Intent(AssistantService.this, WinBoLLClientService.class));
}
bindService(new Intent(AssistantService.this, WinBoLLClientService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
}
//
// 主进程与守护进程连接时需要用到此类
//
class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
mWinBoLLServiceBean = WinBoLLClientServiceBean.loadWinBoLLClientServiceBean(AssistantService.this);
if (mWinBoLLServiceBean.isEnable()) {
wakeupAndBindMain();
}
}
}
}
@@ -0,0 +1,35 @@
package cc.winboll.studio.winboll;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/28 19:11:27
* @Describe WinBoLL UI 状态图标枚举
*/
import cc.winboll.studio.libaes.R;
public enum EWUIStatusIconDrawable {
NORMAL(0),
NEWS(1)
;
static final String TAG = "WUIStatusIconDrawable";
static String[] _mlistCNName = { "正常", "新的消息" };
private int value = 0;
private EWUIStatusIconDrawable(int value) { //必须是private的,否则编译错误
this.value = value;
}
public static int getIconDrawableId(EWUIStatusIconDrawable drawableId) {
int res;
switch(drawableId){
case NEWS :
res = R.drawable.ic_winbollbeta;
break;
default :
res = R.drawable.ic_winboll;
}
return res;
}
}
@@ -0,0 +1,220 @@
package cc.winboll.studio.winboll;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
import cc.winboll.studio.libaes.models.DrawerMenuBean;
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.winboll.R;
import cc.winboll.studio.winboll.activities.AboutActivity;
import cc.winboll.studio.winboll.activities.SettingsActivity;
import cc.winboll.studio.winboll.termux.MyTermuxActivity;
import cc.winboll.studio.winboll.fragments.BrowserFragment;
import cc.winboll.studio.winboll.unittest.TermuxEnvTestActivity;
import java.util.ArrayList;
public class MainActivity extends DrawerFragmentActivity {
public static final String TAG = "MainActivity";
BrowserFragment mBrowserFragment;
// ------------------- 新增:Handler 消息定义(接收URL历史更新消息) -------------------
// 消息标识:URL加载历史更新(刷新抽屉菜单的历史列表)
public static final int MSG_URLLOADHISTORY_UPDATE = 1002;
// 自定义Handler(接收应用内消息,如BrowserFragment发送的历史更新消息)
private static Handler _mMainHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(cc.winboll.studio.winboll.theme.WinBoLLThemeUtil.getThemeTypeID(getApplicationContext()));
super.onCreate(savedInstanceState);
initMainHandler();
if (mBrowserFragment == null) {
String externalUrl = extractExternalUrl(getIntent());
if (externalUrl != null) {
mBrowserFragment = BrowserFragment.newInstance(externalUrl);
} else {
mBrowserFragment = BrowserFragment.newInstance();
}
addFragment(mBrowserFragment);
}
showFragment(mBrowserFragment);
}
// @Override
// protected void onNewIntent(Intent intent) {
// super.onNewIntent(intent);
// String externalUrl = extractExternalUrl(intent);
// if (externalUrl != null && mBrowserFragment != null && mBrowserFragment.getBrowserHandler() != null) {
// Message msg = Message.obtain();
// msg.what = BrowserFragment.MSG_OPEN_URL;
// msg.obj = externalUrl;
// mBrowserFragment.getBrowserHandler().sendMessage(msg);
// }
// }
private String extractExternalUrl(Intent intent) {
if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
if (uri != null) {
return uri.toString();
}
}
return null;
}
public static void sendMessage(Message msg) {
_mMainHandler.sendMessage(msg);
}
/**
* 初始化Handler(接收MSG_URLLOADHISTORY_UPDATE消息,刷新抽屉历史菜单)
*/
private void initMainHandler() {
// 清理旧数据
if (_mMainHandler != null) {
_mMainHandler.removeCallbacksAndMessages(null);
_mMainHandler = null;
}
// Java 7 匿名内部类实现Handler(主线程创建,安全更新UI/抽屉菜单)
_mMainHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_URLLOADHISTORY_UPDATE:
// 处理URL历史更新消息:刷新抽屉菜单的历史列表
LogUtils.d(TAG, "收到URL历史更新消息,刷新抽屉菜单");
refreshUrlHistoryDrawerMenu();
break;
default:
break;
}
}
};
}
@Override
public void initDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
super.initDrawerMenuItemList(listDrawerMenu);
//LogUtils.d(TAG, "initDrawerMenuItemList");
// 加载URL历史菜单(初始化时加载)
refreshUrlHistoryDrawerMenu();
notifyDrawerMenuDataChanged();
}
@Override
public void reinitDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
super.reinitDrawerMenuItemList(listDrawerMenu);
//LogUtils.d(TAG, "reinitDrawerMenuItemList");
// 重新加载URL历史菜单(菜单重置时加载)
refreshUrlHistoryDrawerMenu();
notifyDrawerMenuDataChanged();
}
void loadUrlLoadHistotyMenu(ArrayList<DrawerMenuBean> listDrawerMenu) {
listDrawerMenu.clear();
if (BrowserFragment._mUrlLoadHistory != null) {
for (String url : BrowserFragment._mUrlLoadHistory) {
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, url));
}
}
}
// ------------------- 新增:刷新URL历史抽屉菜单(提取独立方法,复用) -------------------
private void refreshUrlHistoryDrawerMenu() {
// 获取抽屉菜单列表,重新加载历史数据并刷新
ArrayList<DrawerMenuBean> drawerMenuList = super.malDrawerMenuItem; // 假设父类提供获取菜单列表的方法
if (drawerMenuList != null) {
loadUrlLoadHistotyMenu(drawerMenuList); // 重新加载更新后的历史数据
notifyDrawerMenuDataChanged(); // 通知抽屉菜单刷新UI
}
}
@Override
public DrawerFragmentActivity.ActivityType initActivityType() {
return DrawerFragmentActivity.ActivityType.Main;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
if (App.isDebugging()) {
getMenuInflater().inflate(R.menu.toolbar_test, menu);
}
getMenuInflater().inflate(R.menu.toolbar_main, menu);
return true;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
super.onItemClick(parent, view, position, id);
if (mBrowserFragment != null && mBrowserFragment.getBrowserHandler() != null) {
Message msg = Message.obtain();
msg.what = BrowserFragment.MSG_HISTORY_POSITION;
msg.obj = position;
mBrowserFragment.getBrowserHandler().sendMessage(msg);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (AESThemeUtil.onWinBoLLThemeItemSelected(this, item)) {
recreate();
} else {
int nItemId = item.getItemId();
if (nItemId == R.id.item_home) {
if (mBrowserFragment != null && mBrowserFragment.getBrowserHandler() != null) {
Message msg = Message.obtain();
msg.what = BrowserFragment.MSG_HOMEPAGE;
mBrowserFragment.getBrowserHandler().sendMessage(msg);
}
} else if (nItemId == R.id.item_settings) {
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), SettingsActivity.class);
} else if (nItemId == R.id.item_about) {
Intent intent = new Intent(getApplicationContext(), AboutActivity.class);
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), intent, AboutActivity.class);
} else if (nItemId == R.id.item_mytermux) {
Intent intent = new Intent(getApplicationContext(), MyTermuxActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else if (nItemId == R.id.item_termux_env_test) {
Intent intent = new Intent(getApplicationContext(), TermuxEnvTestActivity.class);
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), intent, AboutActivity.class);
} else {
return super.onOptionsItemSelected(item);
}
}
return true;
}
// ------------------- 新增:对外提供Handler(供其他组件发送消息,如BrowserFragment -------------------
public Handler getMainHandler() {
return _mMainHandler;
}
// ------------------- 新增:生命周期管理(防止Handler内存泄漏) -------------------
@Override
protected void onDestroy() {
super.onDestroy();
// 清除Handler所有消息和回调,避免内存泄漏
if (_mMainHandler != null) {
_mMainHandler.removeCallbacksAndMessages(null);
_mMainHandler = null;
}
}
}
@@ -0,0 +1,80 @@
package cc.winboll.studio.winboll;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/02/13 19:30:10
*/
import android.content.Context;
import cc.winboll.studio.winboll.R;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import cc.winboll.studio.winboll.models.MainServiceBean;
import cc.winboll.studio.winboll.services.MainService;
public class MyTileService extends TileService {
public static final String TAG = "MyTileService";
volatile static MyTileService _MyTileService;
@Override
public void onStartListening() {
super.onStartListening();
_MyTileService = this;
Tile tile = getQsTile();
MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
if (bean != null && bean.isEnable()) {
//MainService.startMainService(context);
tile.setState(Tile.STATE_ACTIVE);
tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud));
} else {
//MainService.stopMainService(context);
tile.setState(Tile.STATE_INACTIVE);
tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
}
tile.updateTile();
// Tile tile = getQsTile();
// tile.setState(Tile.STATE_INACTIVE);
// tile.setLabel(getString(R.string.tileservice_name));
// tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
// tile.updateTile();
}
@Override
public void onClick() {
super.onClick();
Tile tile = getQsTile();
MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
if (bean == null) {
bean = new MainServiceBean();
}
if (tile.getState() == Tile.STATE_ACTIVE) {
bean.setIsEnable(false);
MainServiceBean.saveBean(this, bean);
MainService.stopMainService(this);
} else if (tile.getState() == Tile.STATE_INACTIVE) {
bean.setIsEnable(true);
MainServiceBean.saveBean(this, bean);
MainService.startMainService(this);
}
updateServiceIconStatus(this);
}
public static void updateServiceIconStatus(Context context) {
if (_MyTileService == null) {
return;
}
Tile tile = _MyTileService.getQsTile();
MainServiceBean bean = MainServiceBean.loadBean(context, MainServiceBean.class);
if (bean != null && bean.isEnable()) {
tile.setState(Tile.STATE_ACTIVE);
tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud));
} else {
tile.setState(Tile.STATE_INACTIVE);
tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud_outline));
}
tile.updateTile();
}
}
@@ -0,0 +1,40 @@
package cc.winboll.studio.winboll;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/05/10 10:13
* @Describe WinBoLL 系列应用通用管理类
*/
import android.content.Context;
import android.content.Intent;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.winboll.models.WinBoLLModel;
public class WinBoLL {
public static final String TAG = "WinBoLL";
public static final String ACTION_BIND = WinBoLL.class.getName() + ".ACTION_BIND";
public static final String EXTRA_WINBOLLMODEL = "EXTRA_WINBOLLMODEL";
public static void bindToAPPBase(Context context, String appMainService) {
LogUtils.d(TAG, "bindToAPPBase(...)");
String toPackage = "cc.winboll.studio.appbase";
startBind(context, toPackage, appMainService);
}
public static void bindToAPPBaseBeta(Context context, String appMainService) {
LogUtils.d(TAG, "bindToAPPBaseBeta(...)");
String toPackage = "cc.winboll.studio.appbase.beta";
startBind(context, toPackage, appMainService);
}
static void startBind(Context context, String toPackage, String appMainService) {
Intent intent = new Intent(ACTION_BIND);
intent.putExtra(EXTRA_WINBOLLMODEL, (new WinBoLLModel(toPackage, appMainService)).toString());
intent.setPackage(toPackage);
LogUtils.d(TAG, String.format("ACTION_BIND :\nTo Package : %s\nAPP Main Service : %s", toPackage, appMainService));
context.sendBroadcast(intent);
}
}
@@ -0,0 +1,21 @@
package cc.winboll.studio.winboll;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
/**
* @Author ZhanGSKen
* @Date 2025/05/03 19:28
*/
public class WinBoLLClientService extends Service {
public static final String TAG = "WinBoLLClientService";
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
@@ -0,0 +1,38 @@
package cc.winboll.studio.winboll;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
/**
* @Author ZhanGSKen
* @Date 2025/05/03 19:14
*/
public class WinBoLLServiceStatusView extends LinearLayout {
public static final String TAG = "WinBoLLServiceStatusView";
public WinBoLLServiceStatusView(Context context) {
super(context);
}
public WinBoLLServiceStatusView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WinBoLLServiceStatusView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public WinBoLLServiceStatusView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
void setServerHost(String szWinBoLLServerHost) {
}
void setAuthInfo(String szDevUserName, String szDevUserPassword) {
}
}
@@ -0,0 +1,28 @@
package cc.winboll.studio.winboll;
/**
* 微信支付配置类
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/07
*/
public class WxPayConfig {
// ========== 核心修改点:替换为你的服务端地址 ==========
// 服务端IP/域名 + 端口(Docker部署的服务端,需确保安卓端可访问)
public static final String BASE_URL = "https://wxpay.winboll.cc";
// 统一下单接口路径(对应服务端的测试接口)
public static final String CREATE_ORDER_URL = BASE_URL + "/pay/createOrder";
// 订单查询接口路径
public static final String QUERY_ORDER_URL = BASE_URL + "/pay/queryOrder";
// ========== 固定支付配置 ==========
public static final String ORDER_BODY = "定额测试支付"; // 商品描述
public static final int TOTAL_FEE = 1; // 固定金额:1分(沙箱环境推荐)
public static final String TRADE_TYPE = "NATIVE"; // 支付类型:二维码
// ========== 轮询配置 ==========
public static final long POLL_INTERVAL = 10000; // 轮询间隔:10秒
public static final long POLL_TIMEOUT = 45000; // 轮询超时:45秒
}
@@ -0,0 +1,86 @@
package cc.winboll.studio.winboll.activities;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
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;
import cc.winboll.studio.winboll.MainActivity;
import cc.winboll.studio.winboll.R;
import android.app.Activity;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/13 11:54
* @Describe 应用介绍窗口
*/
public class AboutActivity extends BaseWinBoLLActivity {
@Override
public Activity getActivity() {
return this;
}
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 = getActivity().findViewById(R.id.aboutview);
aboutView.setAPPInfo(genDefaultAppInfo());
}
private void initToolbar() {
LogUtils.d(TAG, "initToolbar() 开始初始化");
mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar == null) {
LogUtils.e(TAG, "initToolbar() | Toolbar未找到");
return;
}
setSupportActionBar(mToolbar);
mToolbar.setSubtitle(getTag());
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d(TAG, "导航栏 点击返回按钮");
getActivity().finish();
}
});
LogUtils.d(TAG, "initToolbar() 配置完成");
}
private APPInfo genDefaultAppInfo() {
LogUtils.d(TAG, "genDefaultAppInfo() 调用");
String branchName = "winboll";
APPInfo appInfo = new APPInfo();
appInfo.setAppName(getActivity().getString(R.string.app_name));
appInfo.setAppIcon(R.drawable.ic_winboll);
appInfo.setAppDescription(getActivity().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=WinBoLL");
appInfo.setAppAPKName("WinBoLL");
appInfo.setAppAPKFolderName("WinBoLL");
LogUtils.d(TAG, "genDefaultAppInfo: 应用信息已生成");
return appInfo;
}
}
@@ -0,0 +1,51 @@
package cc.winboll.studio.winboll.activities;
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.WinBoLLActivityManager;
import cc.winboll.studio.winboll.theme.WinBoLLThemeUtil;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/13 16:33
* BaseWinBollActivity 【继承AppCompatActivity,保留核心能力,不额外暴露方法】
* 继承链路:BaseWinBoLLActivity → AppCompatActivity → FragmentActivityAppCompat能力天然继承可用
*/
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 = getThemeType();
setThemeStyle();
super.onCreate(savedInstanceState);
WinBoLLActivityManager.getInstance().add(this);
//ToastUtils.show(getTag() + ": onCreate");
}
AESThemeBean.ThemeType getThemeType() {
return WinBoLLThemeUtil.getThemeStyleType(WinBoLLThemeUtil.getThemeTypeID(getApplicationContext()));
}
void setThemeStyle() {
setTheme(WinBoLLThemeUtil.getThemeTypeID(getApplicationContext()));
}
@Override
protected void onDestroy() {
WinBoLLActivityManager.getInstance().registeRemove(this);
super.onDestroy();
}
// 子类必须实现getTag(),确保唯一标识
@Override
public abstract String getTag();
public abstract Activity getActivity();
}
@@ -0,0 +1,150 @@
package cc.winboll.studio.winboll.activities;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.RadioButton;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libappbase.BuildConfig;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.winboll.R;
import cc.winboll.studio.winboll.models.UserInfoModel;
import cc.winboll.studio.winboll.utils.RSAUtils;
import cc.winboll.studio.winboll.utils.YunUtils;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/06/04 13:29
* @Describe 用户登录框
*/
public class LogonActivity extends Activity implements IWinBoLLActivity {
public static final String TAG = "LogonActivity";
public static final String DEBUG_HOST = "http://10.8.0.250:456";
public static final String YUN_HOST = "https://yun.winboll.cc";
String mHost = "";
RadioButton mrbYunHost;
RadioButton mrbDebugHost;
LogView mLogView;
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logon);
mLogView = findViewById(R.id.logview);
mLogView.start();
mHost = BuildConfig.DEBUG ? DEBUG_HOST: YUN_HOST;
if (BuildConfig.DEBUG) {
mrbYunHost = findViewById(R.id.rb_yunhost);
mrbDebugHost = findViewById(R.id.rb_debughost);
mrbYunHost.setChecked(!BuildConfig.DEBUG);
mrbDebugHost.setChecked(BuildConfig.DEBUG);
} else {
findViewById(R.id.ll_hostbar).setVisibility(View.GONE);
}
}
public void onSwitchHost(View view) {
if (view.getId() == R.id.rb_yunhost) {
mrbDebugHost.setChecked(false);
mHost = YUN_HOST;
} else if (view.getId() == R.id.rb_debughost) {
mrbYunHost.setChecked(false);
mHost = DEBUG_HOST;
}
}
@Override
protected void onResume() {
super.onResume();
mLogView.start();
}
public void onTestLogin(View view) {
LogUtils.d(TAG, "onTestLogin");
final YunUtils yunUtils = YunUtils.getInstance(this);
UserInfoModel userInfoModel = new UserInfoModel();
userInfoModel.setUsername("jian");
userInfoModel.setPassword("kkiio");
userInfoModel.setToken("aaa111");
yunUtils.login(mHost, userInfoModel);
}
public void onTestRSA(View view) {
LogUtils.d(TAG, "onTestRSA");
RSAUtils utils = RSAUtils.getInstance(this);
try {
// 测试 1:首次生成密钥对
LogUtils.d(TAG, "==== 首次生成密钥对 ====");
if (utils.keysExist()) {
LogUtils.d(TAG, "密钥对已生成");
} else {
utils.generateAndSaveKeys();
LogUtils.d(TAG, "密钥对生成成功。");
}
// 测试 2:获取密钥对(自动读取已生成的文件)
KeyPair keyPair = utils.getOrGenerateKeys();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 打印密钥信息
LogUtils.d(TAG, "\n==== 密钥信息 ====");
LogUtils.d(TAG, "公钥算法:" + publicKey.getAlgorithm());
LogUtils.d(TAG, "公钥编码长度:" + publicKey.getEncoded().length + "字节");
LogUtils.d(TAG, "私钥算法:" + privateKey.getAlgorithm());
LogUtils.d(TAG, "私钥编码长度:" + privateKey.getEncoded().length + "字节");
// 测试 3:重复调用时检查是否复用文件
LogUtils.d(TAG, "\n==== 二次调用 ====");
KeyPair reusedPair = utils.getOrGenerateKeys();
LogUtils.d(TAG, "是否为同一公钥:" + (publicKey.equals(reusedPair.getPublic()))); // true(单例引用)
LogUtils.d(TAG, "操作完成");
String testMessage = "Hello, RSA Encryption!";
// 1. 获取或生成密钥对
PublicKey publicKeyReused = reusedPair.getPublic();
PrivateKey privateKeyReused = reusedPair.getPrivate();
// 2. 公钥加密
byte[] encryptedData = utils.encryptWithPublicKey(testMessage, publicKeyReused);
LogUtils.d(TAG, "加密后数据(字节长度):" + encryptedData.length);
// 3. 私钥解密
String decryptedMessage = utils.decryptWithPrivateKey(encryptedData, privateKeyReused);
LogUtils.d(TAG, "解密结果: " + decryptedMessage);
// 4. 验证解密是否成功
if (testMessage.equals(decryptedMessage)) {
LogUtils.d(TAG, "加密解密测试通过!");
} else {
LogUtils.d(TAG, "测试失败:内容不一致");
}
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
}
@@ -0,0 +1,59 @@
package cc.winboll.studio.winboll.activities;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/25 11:46:40
* @Describe 测试窗口2
*/
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.winboll.R;
import android.app.Activity;
public class New2Activity extends BaseWinBoLLActivity {
@Override
public Activity getActivity() {
return this;
}
public static final String TAG = "New2Activity";
Toolbar mToolbar;
@Override
public String getTag() {
return TAG;
}
//LogView mLogView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new2);
// mLogView = findViewById(R.id.logview);
// mLogView.start();
mToolbar = findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
}
public void onCloseThisActivity(View view) {
//WinBoLLActivityManager.getInstance().finish(this);
}
public void onCloseAllActivity(View view) {
//WinBoLLActivityManager.getInstance().finishAll();
}
public void onNewActivity(View view) {
//WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, NewActivity.class);
}
}

Some files were not shown because too many files have changed in this diff Show More