Compare commits

..

43 Commits

Author SHA1 Message Date
ZhanGSKen 048433b422 <appbase>APK 15.20.20 release Publish. 2026-05-20 16:12:13 +08:00
LaizyBoy 1b02e96088 <libappbase>修复borderCornerRadius属性缺失导致Release构建verifyReleaseResources失败 2026-05-20 16:10:31 +08:00
ZhanGSKen fd45d0de01 <libappbase>Library Release 15.20.19 2026-05-20 14:46:48 +08:00
ZhanGSKen 9b2e5f152b <appbase>APK 15.20.19 release Publish. 2026-05-20 14:46:35 +08:00
ZhanGSKen 3b6e7586be 添加https://jitpack.io网站编译配置文件 2026-05-20 14:30:24 +08:00
ZhanGSKen 13f5ad2a6b 更新联系邮箱 2026-05-20 11:17:51 +08:00
ZhanGSKen 2b2fb495b6 <libappbase>Library Release 15.20.18 2026-05-20 03:13:56 +08:00
ZhanGSKen 71fd0ae1da <appbase>APK 15.20.18 release Publish. 2026-05-20 03:13:41 +08:00
LaizyBoy 003e1ccca2 修复调试模式初始化顺序及LogUtils空指针崩溃
- GlobalApplication: 调整restoreDebugStatus在initCoreComponents之前执行,确保调试标志先恢复再初始化日志

- LogUtils: init/setLogLevel/setTAG方法增加未初始化保护

- LogViewThread: getLogCacheDir空判断防止NPE
2026-05-20 03:09:27 +08:00
ZhanGSKen 3649020434 整理应用调试策略,非调试状态,日志记录功能取消。 2026-05-20 02:50:46 +08:00
ZhanGSKen 12ed35d807 建立以数据模型版本号为标准的数据模型类库。 2026-05-19 22:36:23 +08:00
ZhanGSKen 9e5ec3c51f <libappbase>Library Release 15.20.17 2026-05-19 20:49:26 +08:00
ZhanGSKen 408b68a307 <appbase>APK 15.20.17 release Publish. 2026-05-19 20:49:13 +08:00
ZhanGSKen f414d4b3c9 更新应用介绍页里的APPBase源码仓库地址。 2026-05-19 20:48:19 +08:00
ZhanGSKen fce24aac80 更新versionCode 1520000配置说明。 2026-05-19 19:06:07 +08:00
ZhanGSKen d973423578 <libappbase>Library Release 15.20.16 2026-05-19 18:54:50 +08:00
ZhanGSKen 4833ce2a59 <appbase>APK 15.20.16 release Publish. 2026-05-19 18:54:18 +08:00
ZhanGSKen df15a8c7d2 添加build.gradle设置的注释说明 2026-05-19 18:48:23 +08:00
ZhanGSKen 6574f561b2 设置版本号为1520000。 2026-05-19 18:43:12 +08:00
ZhanGSKen cca63a9a4a <libappbase>Library Release 15.20.15 2026-05-17 16:16:53 +08:00
ZhanGSKen 14e4a9af8a <appbase>APK 15.20.15 release Publish. 2026-05-17 16:16:36 +08:00
ZhanGSKen 16a9a1cecc <appbase>APK 15.20.14 release Publish. 2026-05-17 15:55:29 +08:00
ZhanGSKen ee3de5c808 <appbase>APK 15.20.13 release Publish. 2026-05-17 15:52:00 +08:00
ZhanGSKen 970ce087a5 <appbase>APK 15.20.12 release Publish. 2026-05-17 15:28:35 +08:00
ZhanGSKen c7184ad6df <appbase>APK 15.20.11 release Publish. 2026-05-17 14:46:57 +08:00
ZhanGSKen 74299bf9b2 <libappbase>Library Release 15.20.10 2026-05-17 02:08:12 +08:00
ZhanGSKen b55b13b7f3 <appbase>APK 15.20.10 release Publish. 2026-05-17 02:07:57 +08:00
LaizyBoy 76c855854b 减少日志裁剪阈值至25KB,确保剪贴板可完整复制日志内容 2026-05-17 01:30:14 +08:00
ZhanGSKen 0af0c42478 编译调试 2026-05-16 17:15:45 +08:00
ZhanGSKen 75e01ba634 忽略其他旧的未排除的不需要的项目模块文件 2026-05-16 17:13:37 +08:00
ZhanGSKen 5558c0275c 重置忽略配置 2026-05-16 17:07:15 +08:00
LaizyBoy 27056ff308 清理libappbase中冗余的权限声明
移除 AndroidManifest.xml 中 4 个无用权限:
INTERNET、READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE、MANAGE_EXTERNAL_STORAGE
对应网络及存储类库已移除,权限不再需要。
2026-05-16 03:51:48 +08:00
ZhanGSKen e50893860f 减少日志容量,优化日志调试时的显示速度。 2026-05-16 03:26:01 +08:00
ZhanGSKen f6a70519ab 移除网络类库依赖,相应减少冗余功能。 2026-05-16 02:58:13 +08:00
LaizyBoy d3bc40fb12 修复libappbase缺少外部依赖导致的编译失败
添加 JSch、Gson、OkHttp 三个依赖到 libappbase/build.gradle,
解决 FTPUtils、APPUtils 中 44 个找不到符号的编译错误。
2026-05-16 02:20:11 +08:00
ZhanGSKen a56d068092 初始化类库纯净环境,预备使用OpenCode工具按需添加类库。 2026-05-16 01:55:54 +08:00
ZhanGSKen f8917e198a APPBase Source Code Purification 2026-05-16 01:51:00 +08:00
ZhanGSKen 0fb2b9f5fe Ignore Commit After WinBoLL-Demo Project 2026-05-15 18:10:56 +08:00
ZhanGSKen 8006a05749 恢复winboll/winboll分支的基础类库资料,以便项目初始化时的基础控件使用的分析辅助。 2026-05-13 20:52:54 +08:00
ZhanGSKen 3c9a6ec884 修改示例模板的编译配置文件,设置版本号模板。 2026-05-13 20:21:55 +08:00
ZhanGSKen 8b47043053 更新 LICENSE-Private-Demo
字符更新
2026-05-13 19:51:47 +08:00
ZhanGSKen 202eeed72b 更新 LICENSE-Private-Demo_docs/LICENSE-Private-Demo
字符更新
2026-05-13 19:48:38 +08:00
ZhanGSKen 4511bf8730 添加WinBoLL-Demo使用的WinBoLL基础类库的基线型号配置文件。整理创建winboll衍生分支所要具备的基础文件。 2026-05-13 19:25:51 +08:00
491 changed files with 547 additions and 44938 deletions
+5 -2
View File
@@ -94,8 +94,11 @@ lint-results.html
## 忽略 AndroidIDE 临时文件夹
.androidide
## MyWinBoLL 基础应用(避免上传敏感配置)
## WinBoLL 基础应用(避免上传敏感配置)
/winboll.properties
/local.properties
#/settings.gradle
#/gradle.properties
#/gradle.properties
## APPBase 项目配置
@@ -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,6 +72,7 @@ libaes
libappbase
libdebugtemp
libgpsrelaysentinel
libwinboll
local.properties-demo
mymessagemanager
positions
@@ -81,14 +82,13 @@ winboll
winboll.properties-demo
)
# ====================== 5. 获取当前目录真实文件列表(兼容过滤 . .. ======================
# ====================== 5. 获取当前目录真实文件列表 ======================
REAL_ITEMS=()
# 使用固定排序ls,自动过滤 . 和 ..,不会进入比对数组
while IFS= read -r line; do
if [[ "$line" != "." && "$line" != ".." ]]; then
REAL_ITEMS+=("$line")
fi
done < <(LC_COLLATE=C ls -a1 --color=none)
done < <(ls -a)
# ====================== 6. 差异比对函数 ======================
check_diff() {
@@ -158,7 +158,7 @@ echo -e "## 对象列表结束
## 合并 APP 项目
MERGE_APP_PROJECT_LIST=(
WinBoLL
DemoAPP
)
echo -e "#@@@ 开始合并应用型模块源码 @@@#
## 目标合并对象列表:"
@@ -166,13 +166,14 @@ 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
)
@@ -182,10 +183,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
@@ -1,158 +0,0 @@
#!/system/bin/sh
## 逻辑:按时间取最新标签 → 拉取该标签目录合并
# 按创建时间取模块最新标签(最新打的标签排最后)
get_latest_module_tag(){
local mod=$1
git for-each-ref --sort=-creatordate --format='%(refname)' refs/tags/${mod}-* \
| grep -v '\^{}' \
| head -1 \
| sed 's/refs\/tags\///'
}
# 通过标签获取commit
get_commit_from_tag(){
local tag=$1
git rev-parse --short "${tag}^{commit}"
}
# 工作目录
TARGET_DIR="/sdcard/AppProjects/Projects_Keeper_Tag"
echo "进入目录:${TARGET_DIR}"
cd "${TARGET_DIR}" || exit 1
# 同步远程
echo "========================================"
echo "同步远程分支与全部版本标签"
echo "========================================"
git fetch origin --prune
git fetch origin --tags
echo "同步完成"
echo ""
# 校验目标分支
NOW_BRANCH=$(git symbolic-ref --short HEAD)
TARGET_BRANCH="projects_keeper_tag"
if [ "${NOW_BRANCH}" != "${TARGET_BRANCH}" ];then
echo "错误:请先切换到 ${TARGET_BRANCH} 分支"
exit 1
fi
# 目录结构校验白名单(不含 . ..)
MERGE_OBJECTS_LIST=(
.git
.gitignore
.gitmodules
.winboll
GenKeyStore
LICENSE
LICENSE-Private-Demo
LICENSE-Private-Demo_docs
README.md
aes
appbase
autonfc
build.gradle
contacts
debugtemp
gallery
gpsrelaysentinel
gradle
gradle.properties-android-demo
gradle.properties-androidx-demo
gradlew
libaes
libappbase
libdebugtemp
libgpsrelaysentinel
local.properties-demo
mymessagemanager
positions
powerbell
settings.gradle-demo
winboll
winboll.properties-demo
)
REAL_ITEMS=()
# 标准排序ls输出,循环强制过滤 . 和 ..
while IFS= read -r line; do
# 跳过虚拟目录 . 和 ..
if [[ "$line" != "." && "$line" != ".." ]]; then
REAL_ITEMS+=("$line")
fi
done < <(LC_COLLATE=C ls -a1 --color=none)
check_diff(){
local miss=() extra=()
for i in "${MERGE_OBJECTS_LIST[@]}";do
[[ ! " ${REAL_ITEMS[@]} " =~ " ${i} " ]] && miss+=("$i")
done
for i in "${REAL_ITEMS[@]}";do
[[ ! " ${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
}
check_diff
echo -e "#@@@ 按时间获取最新标签合并模块源码 @@@#"
# 应用型模块
MERGE_APP_PROJECT_LIST=(WinBoLL)
echo -e "---------- 应用型模块 ----------"
for name in "${MERGE_APP_PROJECT_LIST[@]}";do
low_name=$(echo "$name" | tr 'A-Z' 'a-z')
tag=$(get_latest_module_tag "${low_name}")
if [[ -z "${tag}" ]];then
echo "跳过 ${low_name}:无远程标签"
continue
fi
commit=$(get_commit_from_tag "${tag}")
if [[ -z "${commit}" ]];then
echo "跳过 ${low_name}:标签无有效提交点"
continue
fi
echo "模块:${low_name} 最新时间标签:${tag} 提交ID:${commit}"
# 强制拉取覆盖
git checkout -f "${tag}" -- "${low_name}"
git add "${low_name}"
git commit -m "合并模块${name} 同步最新时间标签${tag}"
done
# 类库模块
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')
tag=$(get_latest_module_tag "${low_name}")
if [[ -z "${tag}" ]];then
echo "跳过 ${low_name}:无远程标签"
continue
fi
commit=$(get_commit_from_tag "${tag}")
if [[ -z "${commit}" ]];then
echo "跳过 ${low_name}:标签无有效提交点"
continue
fi
echo "模块:${low_name} 最新时间标签:${tag} 提交ID:${commit}"
git checkout -f "${tag}" -- "${low_name}" "lib${low_name}"
git add "${low_name}" "lib${low_name}"
git commit -m "合并模块${name} 同步最新时间标签${tag}"
done
echo "全部模块合并执行完毕"
echo "执行推送:git push"
git push
-228
View File
@@ -1,228 +0,0 @@
#!/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
@@ -1,20 +0,0 @@
#!/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
+27 -56
View File
@@ -122,6 +122,7 @@ 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()) {
@@ -129,7 +130,6 @@ android {
println "Output Folder Created.(WinBoLLStudio) : " + outBuildBckDir.getAbsolutePath()
}
if(outBuildBckDir.exists()) {
def targetApkFile = new File(outBuildBckDir, outputFileName)
copy{
from file.outputFile
into outBuildBckDir
@@ -138,14 +138,6 @@ 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)
@@ -168,7 +160,8 @@ android {
assert(libraryProjectBuildPropsFile.exists())
java.nio.file.Path sourceFilePath = winbollBuildPropsFile.toPath();
java.nio.file.Path targetFilePath = libraryProjectBuildPropsFile.toPath();
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
// 使用copyTo()方法复制文件,如果目标文件存在会被覆盖,可选参数可以选择不覆盖
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
println "\n\n>>> Library Project build.properties saved.\n\n";
}
@@ -179,12 +172,16 @@ 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()) {
@@ -195,10 +192,12 @@ 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
@@ -207,16 +206,7 @@ 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
@@ -225,14 +215,6 @@ 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)
@@ -257,11 +239,14 @@ 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);
}
@@ -278,12 +263,17 @@ 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()) {
@@ -294,11 +284,13 @@ 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
@@ -307,16 +299,7 @@ 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
@@ -325,13 +308,8 @@ 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}"
}
//不保存编译标志配置
}
}
@@ -350,13 +328,6 @@ 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
@@ -0,0 +1 @@
15.20
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-97
View File
@@ -1,97 +0,0 @@
# 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
@@ -103,3 +103,7 @@ $bash gradlew assembleStageRelease
#### V<应用开发环境编号><应用功能变更号><应用调试阶段号> (示例: APPBase_15.7.0 
### ☆ Beta 渠道:
#### V<应用开发环境编号><应用功能变更号><应用调试阶段号>-beta<调试编译计数>_<调试编译时间(分钟+秒钟)> (示例: APPBase_15.9.6-beta8_5413 
#### 设置统一版本号,实现应用数据管理阶段性调整的坚实框架。
#### 以版本号versionCode 1520000为开发起点,15.20基线产品可实现的产品阶段编号为1520000到1520999之间。
#### 其中版本号000~999是应用数据管理架构的型号。与产品versionName “15.20.<产品编号>”其中的<产品编号>无关。
-1
View File
@@ -1 +0,0 @@
/build
-36
View File
@@ -1,36 +0,0 @@
# AES
[![](https://jitpack.io/v/ZhanGSKen/AES.svg)](https://jitpack.io/#ZhanGSKen/AES)
#### 介绍
WinBoLL AndroidX 可视化元素类库。
#### 软件架构
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
#### Gradle 编译说明
调试版编译命令 gradle assembleBetaDebug
阶段版编译命令 bash .winboll/bashPublishAPKAddTag.sh aes
阶段版类库发布命令 git pull &&bash .winboll/bashPublishLIBAddTag.sh libaes
#### 使用说明
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码 : ZhanGSKen(ZhanGSKen<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
@@ -1 +0,0 @@
-47
View File
@@ -1,47 +0,0 @@
apply plugin: 'com.android.application'
apply from: '../.winboll/winboll_app_build.gradle'
apply from: '../.winboll/winboll_lint_build.gradle'
def genVersionName(def versionName){
// 检查编译标志位配置
assert (winbollBuildProps['stageCount'] != null)
assert (winbollBuildProps['baseVersion'] != null)
// 保存基础版本号
winbollBuildProps.setProperty("baseVersion", "${versionName}");
//保存编译标志配置
FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
fos.close();
// 返回编译版本号
return "${versionName}." + winbollBuildProps['stageCount']
}
android {
// 适配MIUI12
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "cc.winboll.studio.aes"
minSdkVersion 26
targetSdkVersion 30
//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 project(':libaes')
api fileTree(dir: 'libs', include: ['*.jar'])
}
-8
View File
@@ -1,8 +0,0 @@
#Created by .winboll/winboll_app_build.gradle
#Sat Jul 11 20:07:00 HKT 2026
stageCount=19
libraryProject=libaes
baseVersion=15.20
publishVersion=15.20.18
buildCount=0
baseBetaVersion=15.20.19
-137
View File
@@ -1,137 +0,0 @@
# 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
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<!-- Put flavor specific code here -->
</application>
</manifest>
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Put flavor specific strings here -->
<string name="app_name">AES+</string>
</resources>
-45
View File
@@ -1,45 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.aes">
<!-- 对正在运行的应用重新排序 -->
<uses-permission android:name="android.permission.REORDER_TASKS"/>
<application
android:name=".App"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/MyAESTheme"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name=".MainActivity"
android:exported="true">
<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=".TestActivityManagerActivity"/>
<activity android:name=".SettingsActivity"/>
<activity android:name=".AboutActivity"/>
</application>
</manifest>
@@ -1,78 +0,0 @@
package cc.winboll.studio.aes;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.aes.R;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.models.APPInfo;
import cc.winboll.studio.libappbase.views.AboutView;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/13 11:25
* @Describe 应用介绍窗口
*/
public class AboutActivity extends BaseWinBoLLActivity {
public static final String TAG = "AboutActivity";
private Toolbar mToolbar;
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
// 设置工具栏
initToolbar();
AboutView aboutView = findViewById(R.id.aboutview);
aboutView.setAPPInfo(genDefaultAppInfo());
}
private void initToolbar() {
LogUtils.d(TAG, "initToolbar() 开始初始化");
mToolbar = findViewById(R.id.toolbar);
if (mToolbar == null) {
LogUtils.e(TAG, "initToolbar() | Toolbar未找到");
return;
}
setSupportActionBar(mToolbar);
mToolbar.setSubtitle(getTag());
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d(TAG, "导航栏 点击返回按钮");
WinBoLLActivityManager.getInstance().resumeActivity(MainActivity.class);
WinBoLLActivityManager.getInstance().finish(AboutActivity.this);
}
});
LogUtils.d(TAG, "initToolbar() 配置完成");
}
private APPInfo genDefaultAppInfo() {
LogUtils.d(TAG, "genDefaultAppInfo() 调用");
String branchName = "aes";
APPInfo appInfo = new APPInfo();
appInfo.setAppName(getString(R.string.app_name));
appInfo.setAppIcon(R.drawable.ic_winboll);
appInfo.setAppDescription(getString(R.string.app_description));
appInfo.setAppGitName("WinBoLL");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(branchName);
appInfo.setAppGitAPPSubProjectFolder(branchName);
appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=AES");
appInfo.setAppAPKName("AES");
appInfo.setAppAPKFolderName("AES");
LogUtils.d(TAG, "genDefaultAppInfo: 应用信息已生成");
return appInfo;
}
}
@@ -1,50 +0,0 @@
package cc.winboll.studio.aes;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/13 19:03:58
* @Describe AES应用类
*/
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
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 java.io.PrintWriter;
import java.io.StringWriter;
public class App extends GlobalApplication {
public static final String TAG = "App";
@Override
public void onCreate() {
try {
super.onCreate();
ToastUtils.init(this);
WinBoLLActivityManager.init(this);
AESThemeUtil.init(null);
} 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
);
}
}
@Override
public void onTerminate() {
super.onTerminate();
ToastUtils.release();
}
}
@@ -1,45 +0,0 @@
package cc.winboll.studio.aes;
import android.app.Activity;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.models.AESThemeBean;
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/13 16:35
* @Describe 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 = AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext()));
setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext()));
super.onCreate(savedInstanceState);
WinBoLLActivityManager.getInstance().add(this);
}
@Override
protected void onDestroy() {
WinBoLLActivityManager.getInstance().registeRemove(this);
super.onDestroy();
}
// 子类必须实现getTag(),确保唯一标识
@Override
public abstract String getTag();
@Override
public Activity getActivity() {
return this;
}
}
@@ -1,197 +0,0 @@
package cc.winboll.studio.aes;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/13 19:05:52
* @Describe 应用主窗口
*/
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Toast;
import cc.winboll.studio.aes.R;
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
import cc.winboll.studio.libaes.dialogs.LocalFileSelectDialog;
import cc.winboll.studio.libaes.dialogs.StoragePathDialog;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.models.DrawerMenuBean;
import cc.winboll.studio.libaes.unittests.SecondaryLibraryActivity;
import cc.winboll.studio.libaes.unittests.TestAButtonFragment;
import cc.winboll.studio.libaes.unittests.TestASupportToolbarActivity;
import cc.winboll.studio.libaes.unittests.TestAToolbarActivity;
import cc.winboll.studio.libaes.unittests.TestDrawerFragmentActivity;
import cc.winboll.studio.libaes.unittests.TestViewPageFragment;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.LogUtils;
import com.a4455jkjh.colorpicker.ColorPickerDialog;
import java.util.ArrayList;
public class MainActivity extends DrawerFragmentActivity {
public static final String TAG = "MainActivity";
TestAButtonFragment mTestAButtonFragment;
TestViewPageFragment mTestViewPageFragment;
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mTestAButtonFragment == null) {
mTestAButtonFragment = new TestAButtonFragment();
addFragment(mTestAButtonFragment);
}
showFragment(mTestAButtonFragment);
//setSubtitle(TAG);
//ToastUtils.show("onCreate");
}
@Override
public void initDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
super.initDrawerMenuItemList(listDrawerMenu);
LogUtils.d(TAG, "initDrawerMenuItemList");
//listDrawerMenu.clear();
// 添加抽屉菜单项
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestAButtonFragment.TAG));
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestViewPageFragment.TAG));
notifyDrawerMenuDataChanged();
}
@Override
public void reinitDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
super.reinitDrawerMenuItemList(listDrawerMenu);
LogUtils.d(TAG, "reinitDrawerMenuItemList");
//listDrawerMenu.clear();
// 添加抽屉菜单项
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestAButtonFragment.TAG));
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestViewPageFragment.TAG));
notifyDrawerMenuDataChanged();
}
@Override
public DrawerFragmentActivity.ActivityType initActivityType() {
return DrawerFragmentActivity.ActivityType.Main;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.toolbar_main, menu);
// if(App.isDebugging()) {
// getMenuInflater().inflate(cc.winboll.studio.libaes.R.menu.toolbar_studio_debug, menu);
// }
return true;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
super.onItemClick(parent, view, position, id);
switch (position) {
case 0 : {
if (mTestAButtonFragment == null) {
mTestAButtonFragment = new TestAButtonFragment();
addFragment(mTestAButtonFragment);
}
showFragment(mTestAButtonFragment);
break;
}
case 1 : {
if (mTestViewPageFragment == null) {
mTestViewPageFragment = new TestViewPageFragment();
addFragment(mTestViewPageFragment);
}
showFragment(mTestViewPageFragment);
break;
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int nItemId = item.getItemId();
if (item.getItemId() == R.id.item_testactivitymanager) {
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, TestActivityManagerActivity.class);
//ToastUtils.show("item_testactivitymanager");
} else
if (nItemId == R.id.item_atoast) {
Toast.makeText(getApplication(), "item_testatoast", Toast.LENGTH_SHORT).show();
} else if (nItemId == R.id.item_atoolbar) {
Intent intent = new Intent(this, TestAToolbarActivity.class);
startActivity(intent);
} else if (nItemId == R.id.item_asupporttoolbar) {
Intent intent = new Intent(this, TestASupportToolbarActivity.class);
startActivity(intent);
} else if (nItemId == R.id.item_colordialog) {
ColorPickerDialog dlg = new ColorPickerDialog(this, getResources().getColor(R.color.colorPrimary));
dlg.setOnColorChangedListener(new com.a4455jkjh.colorpicker.view.OnColorChangedListener() {
@Override
public void beforeColorChanged() {
}
@Override
public void onColorChanged(int color) {
}
@Override
public void afterColorChanged() {
}
});
dlg.show();
} else if (nItemId == R.id.item_dialogstoragepath) {
final StoragePathDialog dialog = new StoragePathDialog(this, 0);
dialog.setOnOKClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
dialog.show();
} else if (nItemId == R.id.item_localfileselectdialog) {
final LocalFileSelectDialog dialog = new LocalFileSelectDialog(this);
dialog.setOnOKClickListener(new LocalFileSelectDialog.OKClickListener() {
@Override
public void onOKClick(String sz) {
Toast.makeText(getApplication(), sz, Toast.LENGTH_SHORT).show();
//dialog.dismiss();
}
});
dialog.open();
} else if (nItemId == R.id.item_secondarylibraryactivity) {
Intent intent = new Intent(this, SecondaryLibraryActivity.class);
startActivity(intent);
} else if (nItemId == R.id.item_drawerfragmentactivity) {
Intent intent = new Intent(this, TestDrawerFragmentActivity.class);
startActivity(intent);
} else if (nItemId == R.id.item_settings) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
} else if (nItemId == R.id.item_about) {
// Intent intent = new Intent(this, AboutActivity.class);
// startActivity(intent);
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, AboutActivity.class);
}
return super.onOptionsItemSelected(item);
}
}
@@ -1,35 +0,0 @@
package cc.winboll.studio.aes;
import android.app.Activity;
import android.os.Bundle;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/26 18:01
* @Describe SettingsActivity
*/
public class SettingsActivity extends Activity {
public static final String TAG = "SettingsActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
//ADsControlView adsControlView = (ADsControlView) findViewById(R.id.ads_control_view);
// adsControlView.setOnAdsModeSelectedListener(new ADsControlView.OnAdsModeSelectedListener() {
// @Override
// public void onModeSelected(ADsMode selectedMode) {
// if (selectedMode == ADsMode.STANDALONE) {
// // 处理单机模式逻辑(如释放米盟资源)
// ToastUtils.show("STANDALONE");
// } else if (selectedMode == ADsMode.MIMO_SDK) {
// // 处理米盟SDK模式逻辑(如初始化SDK)
// ToastUtils.show("MIMO_SDK");
// }
// }
// });
}
}
@@ -1,33 +0,0 @@
package cc.winboll.studio.aes;
import android.app.Activity;
import android.os.Bundle;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/09/28 21:07
* @Describe 窗口管理类测试窗口
*/
public class TestActivityManagerActivity extends WinBoLLActivity implements IWinBoLLActivity {
public static final String TAG = "TestActivityManagerActivity";
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testactivitymanager);
}
}
@@ -1,60 +0,0 @@
package cc.winboll.studio.aes;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/09/29 00:11
* @Describe WinBoLL 窗口基础类
*/
import android.app.Activity;
import android.os.Bundle;
import android.view.MenuItem;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.LogUtils;
public class WinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity {
public static final String TAG = "WinBoLLActivity";
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onResume() {
super.onResume();
LogUtils.d(TAG, String.format("onResume %s", getTag()));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
/*if (item.getItemId() == R.id.item_log) {
WinBoLLActivityManager.getInstance().startLogActivity(this);
return true;
} else if (item.getItemId() == R.id.item_home) {
startActivity(new Intent(this, MainActivity.class));
return true;
}*/
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
return super.onOptionsItemSelected(item);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
WinBoLLActivityManager.getInstance().add(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
WinBoLLActivityManager.getInstance().finish(this);
}
}
@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:left="2dp"
android:top="2dp"
android:right="2dp"
android:bottom="2dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:endColor="#0F000000"
android:startColor="#0F000000" />
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
<item
android:left="3dp"
android:top="3dp"
android:right="3dp"
android:bottom="5dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:endColor="#00000000"
android:startColor="#1AFFFFFF" />
<stroke
android:width="1dp"
android:color="#FF666666" />
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
</layer-list>
-33
View File
@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 阴影部分 -->
<!-- 个人觉得更形象的表达:top代表下边的阴影高度,left代表右边的阴影宽度。其实也就是相对应的offset,solid中的颜色是阴影的颜色,也可以设置角度等等 -->
<item
android:left="2dp"
android:top="2dp"
android:right="2dp"
android:bottom="2dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:endColor="#0F000000"
android:startColor="#0F000000" />
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
<!-- 背景部分 -->
<!-- 形象的表达:bottom代表背景部分在上边缘超出阴影的高度,right代表背景部分在左边超出阴影的宽度(相对应的offset) -->
<item
android:left="3dp"
android:top="3dp"
android:right="3dp"
android:bottom="5dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:endColor="#0FFFFFFF"
android:startColor="#FFFFFFFF" />
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
</layer-list>
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" android:background="@drawable/bg_container_border">
<cc.winboll.studio.libaes.views.ASupportToolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/toolbar"/>
<cc.winboll.studio.libappbase.views.AboutView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:id="@+id/aboutview"/>
</LinearLayout>
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" android:background="@drawable/bg_container_border">
</LinearLayout>
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" android:background="@drawable/bg_container_border">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="窗口管理类测试窗口"/>
</LinearLayout>
-41
View File
@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/item_testactivitymanager"
android:title="TestActivityManager"/>
<item
android:id="@+id/item_log"
android:title="LogActivity"/>
<item
android:id="@+id/item_colordialog"
android:title="ColorDialog"/>
<item
android:id="@+id/item_dialogstoragepath"
android:title="StoragePathDialog"/>
<item
android:id="@+id/item_localfileselectdialog"
android:title="LocalFileSelectDialog"/>
<item
android:id="@+id/item_atoolbar"
android:title="Test AToolbar"/>
<item
android:id="@+id/item_asupporttoolbar"
android:title="Test ASupportToolbar"/>
<item
android:id="@+id/item_atoast"
android:title="Test AToast"/>
<item
android:id="@+id/item_secondarylibraryactivity"
android:title="Test SecondaryLibraryActivity"/>
<item
android:id="@+id/item_drawerfragmentactivity"
android:title="Test DrawerFragmentActivity"/>
<item
android:id="@+id/item_settings"
android:title="Settings"/>
<item
android:id="@+id/item_about"
android:title="About"/>
</menu>
-16
View File
@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyAESTheme" parent="AESTheme">
<item name="themeDebug">@style/MyDebugActivityTheme</item>
</style>
<style name="MyDebugActivityTheme" parent="Theme.AppCompat.NoActionBar">
<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>
<item name="colorTextBackgound">@color/mainWindowBackgroundColor</item>
<item name="debugTextColor">@color/debugTextColor</item>
<item name="toolbarTextColor">@color/toolbarTextColor</item>
</style>
</resources>
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#FF00B322</color>
<color name="colorPrimaryDark">#FF005C12</color>
<color name="colorAccent">#FF8DFFA2</color>
<color name="colorText">#FFFFFB8D</color>
</resources>
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">AES</string>
<string name="app_description">WinBoLL AndroidX 可视化元素类库。</string>
</resources>
-16
View File
@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyAESTheme" parent="AESTheme">
<item name="themeDebug">@style/MyDebugActivityTheme</item>
</style>
<style name="MyDebugActivityTheme" parent="Theme.AppCompat.Light.NoActionBar">
<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>
<item name="colorTextBackgound">@color/mainWindowBackgroundColor</item>
<item name="debugTextColor">@color/debugTextColor</item>
<item name="toolbarTextColor">@color/toolbarTextColor</item>
</style>
</resources>
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">winboll.cc</domain>
</domain-config>
</network-security-config>
-12
View File
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<!-- Put flavor specific code here -->
</application>
</manifest>
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Put flavor specific strings here -->
</resources>
+4 -4
View File
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Sun Jun 28 20:55:33 HKT 2026
stageCount=35
#Wed May 20 16:12:13 HKT 2026
stageCount=21
libraryProject=libappbase
baseVersion=15.20
publishVersion=15.20.34
publishVersion=15.20.20
buildCount=0
baseBetaVersion=15.20.35
baseBetaVersion=15.20.21
@@ -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("WinBoLL");
appInfo.setAppGitName("APPBase");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(branchName);
appInfo.setAppGitAPPSubProjectFolder(branchName);
@@ -1,11 +1,8 @@
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.utils.CrashHandleNotifyUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
import cc.winboll.studio.libappbase.BuildConfig;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
@@ -24,24 +21,10 @@ public class App extends GlobalApplication {
*/
@Override
public void onCreate() {
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
);
}
super.onCreate();
// 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用)
ToastUtils.init(getApplicationContext());
}
/**
+2 -2
View File
@@ -14,8 +14,8 @@ org.gradle.jvmargs=-Xmx2048m
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
android.useAndroidX=false
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
android.enableJetifier=false
# 保持与旧版Gradle插件的兼容
android.disableAutomaticComponentCreation=true
Vendored Executable → Regular
View File
-1
View File
@@ -1 +0,0 @@
/build
-70
View File
@@ -1,70 +0,0 @@
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
apply from: '../.winboll/winboll_lib_build.gradle'
apply from: '../.winboll/winboll_lint_build.gradle'
android {
// 适配MIUI12
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
minSdkVersion 26
targetSdkVersion 30
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
dependencies {
// 下拉控件
api 'com.baoyz.pullrefreshlayout:library:1.2.0'
// 拼音搜索
// https://mvnrepository.com/artifact/com.github.open-android/pinyin4j
api 'com.github.open-android:pinyin4j:2.5.0'
// SSH
api 'com.jcraft:jsch:0.1.55'
// Html 解析
api 'org.jsoup:jsoup:1.13.1'
// 二维码类库
api 'com.google.zxing:core:3.4.1'
api 'com.journeyapps:zxing-android-embedded:3.6.0'
// 应用介绍页类库
api 'io.github.medyo:android-about-page:2.0.0'
// 网络连接类库
api 'com.squareup.okhttp3:okhttp:4.4.1'
// 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.google.code.gson:gson:2.8.5'
api 'com.github.bumptech.glide:glide:4.9.0'
//annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
// WinBoLL库 nexus.winboll.cc 地址
api 'cc.winboll.studio:libappbase:15.20.34'
// 备用库 jitpack.io 地址
//api 'com.github.ZhanGSKen:libappbase:appbase-v15.20.33'
api fileTree(dir: 'libs', include: ['*.jar'])
}
-8
View File
@@ -1,8 +0,0 @@
#Created by .winboll/winboll_app_build.gradle
#Sat Jul 11 20:07:00 HKT 2026
stageCount=19
libraryProject=libaes
baseVersion=15.20
publishVersion=15.20.18
buildCount=0
baseBetaVersion=15.20.19
Binary file not shown.
-17
View File
@@ -1,17 +0,0 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:/tools/adt-bundle-windows-x86_64-20131030/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
-57
View File
@@ -1,57 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="cc.winboll.studio.libaes">
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<application
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name="cc.winboll.studio.libaes.unittests.SecondaryLibraryActivity"
android:exported="true"/>
<activity
android:name="cc.winboll.studio.libaes.unittests.TestDrawerFragmentActivity"
android:exported="true"/>
<activity
android:name="cc.winboll.studio.libaes.unittests.TestAToolbarActivity"
android:exported="true"/>
<activity
android:name="cc.winboll.studio.libaes.unittests.TestASupportToolbarActivity"
android:exported="true"/>
<service
android:name="cc.winboll.studio.libaes.winboll.WinBoLLClientService"
android:exported="true"/>
<service
android:name="cc.winboll.studio.libaes.winboll.AssistantService"
android:exported="true"/>
<service
android:name="cc.winboll.studio.libaes.winboll.WinBoLLMail"
android:exported="true"/>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/>
</provider>
</application>
</manifest>
@@ -1,207 +0,0 @@
package cc.winboll.studio.libaes;
import android.content.Context;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
public abstract class DrawerMenuDataAdapter<T> extends BaseAdapter {
private ArrayList<T> mData;
private int mLayoutResource; //布局id
public DrawerMenuDataAdapter() {
}
public DrawerMenuDataAdapter(ArrayList<T> mData, int mLayoutRes) {
this.mData = mData;
this.mLayoutResource = mLayoutRes;
}
@Override
public int getCount() {
return mData != null ? mData.size() : 0;
}
@Override
public T getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = ViewHolder.bind(parent.getContext(), convertView, parent, mLayoutResource
, position);
bindView(viewHolder, getItem(position));
return viewHolder.getItemView();
}
public abstract void bindView(ViewHolder holder, T obj);
// 添加数据项
//
public void add(T item) {
if (mData == null) {
mData = new ArrayList<>();
}
mData.add(item);
notifyDataSetChanged();
}
// 添加数据项在指定位置
//
public void add(int position, T item) {
if (mData == null) {
mData = new ArrayList<>();
}
mData.add(position, item);
notifyDataSetChanged();
}
// 删除数据项
//
public void remove(T item) {
if (mData != null) {
mData.remove(item);
}
notifyDataSetChanged();
}
// 删除指定位置数据项
//
public void remove(int position) {
if (mData != null) {
mData.remove(position);
}
notifyDataSetChanged();
}
// 清理所有数据项
//
public void clear() {
if (mData != null) {
mData.clear();
}
notifyDataSetChanged();
}
public static class ViewHolder {
// 存储在 ListView 的 item 中的 View
SparseArray<View> mSparseArrayView;
// 存放convertView
View mViewItem;
// 游标
int mnPosition;
Context mContext;
//构造方法
//
private ViewHolder(Context context, ViewGroup parent, int layoutResource) {
mSparseArrayView = new SparseArray<>();
this.mContext = context;
View convertView = LayoutInflater.from(context).inflate(layoutResource, parent, false);
convertView.setTag(this);
mViewItem = convertView;
}
//绑定 ViewHolder 与数据项
//
public static ViewHolder bind(Context context, View convertView, ViewGroup parent,
int layoutResource, int position) {
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder(context, parent, layoutResource);
} else {
viewHolder = (ViewHolder) convertView.getTag();
viewHolder.mViewItem = convertView;
}
viewHolder.mnPosition = position;
return viewHolder;
}
@SuppressWarnings("unchecked")
public <T extends View> T getView(int id) {
T t = (T) mSparseArrayView.get(id);
if (t == null) {
t = (T) mViewItem.findViewById(id);
mSparseArrayView.put(id, t);
}
return t;
}
// 获取当前条目
//
public View getItemView() {
return mViewItem;
}
// 获取条目位置
//
public int getItemPosition() {
return mnPosition;
}
// 设置文字
//
public ViewHolder setText(int id, CharSequence text) {
View view = getView(id);
if (view instanceof TextView) {
((TextView) view).setText(text);
}
return this;
}
// 设置图片
//
public ViewHolder setImageResource(int id, int drawableResource) {
View view = getView(id);
if (view instanceof ImageView) {
((ImageView) view).setImageResource(drawableResource);
} else {
view.setBackgroundResource(drawableResource);
}
return this;
}
// 设置点击监听
//
public ViewHolder setOnClickListener(int id, View.OnClickListener listener) {
getView(id).setOnClickListener(listener);
return this;
}
// 设置可见
//
public ViewHolder setVisibility(int id, int visible) {
getView(id).setVisibility(visible);
return this;
}
// 设置标签
//
public ViewHolder setTag(int id, Object obj) {
getView(id).setTag(obj);
return this;
}
}
}
@@ -1,75 +0,0 @@
package cc.winboll.studio.libaes;
import android.view.View;
import android.view.ViewGroup;
import androidx.viewpager.widget.PagerAdapter;
import java.util.List;
public class ImagePagerAdapter extends PagerAdapter {
/*
* 四个必须重写的方法,否则会报错
*
*/
private List<View> views;
//构造方法,拿到views
public ImagePagerAdapter(List<View> views) {
this.views = views;
}
//以下四个是重写的方法
// 获取要滑动的控件的数量,在这里我们以滑动的广告栏为例,那么这里就应该是展示的广告图片的ImageView数量
@Override
public int getCount() {
// TODO Auto-generated method stub
return this.views.size();
}
// 来判断显示的是否是同一张图片,这里我们将两个参数相比较返回即可
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return arg0 == arg1;
}
/**
* position是在viewPager中显示图片的下标
* 把对应的图片放到对应的位置就好了
* instantiateItem和destroyItem是对应的
* 一个是创建item,一个是销毁item
* 当要显示的图片可以进行缓存的时候,会调用instantiateItem()进行显示图片的初始化,
* 我们将要显示的ImageView加入到ViewGroup中,然后作为返回值返回即可
*
* ViewPager 是扩展于 ViewGroupcontainer参数是当前的ViewPager对象,
* 所有的item都会被加入到ViewPager中,
* position就是 每个item对应的下标
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(views.get(position));
return views.get(position);
}
//如果出现IllegalStateException: The specified child already has a parent. 这样的错误:则可替换为以下的try catch 代码
/*try{
    if(views.get(position).getParent()==null){
  container.addView(views.get(position));
    }else{
((ViewGroup)views.get(position).getParent()).removeView(views.get(position));
container.addView(views.get(position));
}
}catch(Exception e){
e.printStackTrace();
}*/
// PagerAdapter只缓存5张要显示的图片,如果滑动的图片超出了缓存的范围,就会调用destroyItem(),将图片销毁
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(views.get(position));
}
}
@@ -1,336 +0,0 @@
package cc.winboll.studio.libaes.activitys;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/13 18:58:54
* @Describe 可以加入Fragment的有抽屉的活动窗口抽象类
*/
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.Menu;
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;
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;
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.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 {
public static final String TAG = "DrawerFragmentActivity";
static final String SHAREDPREFERENCES_NAME = "SHAREDPREFERENCES_NAME";
static final String DRAWER_THEME_TYPE = "DRAWER_THEME_TYPE";
ActivityType mActivityType;
ActionBarDrawerToggle mActionBarDrawerToggle;
DrawerLayout mDrawerLayout;
PullRefreshLayout mPullRefreshLayout;
ADrawerMenuListView mADrawerMenuListView;
DrawerMenuDataAdapter mDrawerMenuDataAdapter;
boolean mIsDrawerOpened = false;
boolean mIsDrawerOpening = false;
boolean mIsDrawerClosing = false;
protected Toolbar mToolbar;
public enum ActivityType { Main, Secondary }
protected volatile AESThemeBean.ThemeType mThemeType;
protected ArrayList<DrawerMenuBean> malDrawerMenuItem;
abstract protected ActivityType initActivityType();
@Override
protected void onCreate(Bundle savedInstanceState) {
// 替换:使用工具类统一应用主题
AESThemeUtil.applyAppCompatTheme(this);
mThemeType = AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext()));
super.onCreate(savedInstanceState);
WinBoLLActivityManager.getInstance().add(this);
mActivityType = initActivityType();
initRootView();
LogUtils.d(TAG, "onCreate end.");
}
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onDestroy() {
WinBoLLActivityManager.getInstance().registeRemove(this);
super.onDestroy();
// 修复:释放广告资源,避免内存泄漏
// ADsBannerView adsBannerView = findViewById(R.id.adsbanner);
// if (adsBannerView != null) {
// adsBannerView.releaseAdResources();
// }
}
@Override
public MenuInflater getMenuInflater() {
return super.getMenuInflater();
}
@Override
public void recreate() {
super.recreate();
}
@Override
public void startActivity(Intent intent) {
super.startActivity(intent);
}
@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
super.startActivityForResult(intent, requestCode, options);
}
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return super.getSharedPreferences(name, mode);
}
@Override
public Context getApplicationContext() {
return super.getApplicationContext();
}
@Override
public void onBackPressed() {
super.onBackPressed();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 替换为 DrawerFragmentActivity 专属点击处理方法
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;
}
@Override
protected void onResume() {
super.onResume();
// ADsBannerView adsBannerView = findViewById(R.id.adsbanner);
// if (adsBannerView != null) {
// adsBannerView.resumeADs(DrawerFragmentActivity.this);
// }
}
void initRootView() {
setContentView(R.layout.activity_drawerfragment);
mToolbar = findViewById(R.id.activitydrawerfragmentASupportToolbar1);
setSupportActionBar(mToolbar);
if (mActivityType == ActivityType.Main) {
initMainRootView();
} else if (mActivityType == ActivityType.Secondary) {
initSecondaryRootView();
}
}
void initMainRootView() {
mDrawerLayout = findViewById(R.id.activitydrawerfragmentDrawerLayout1);
mADrawerMenuListView = findViewById(R.id.activitydrawerfragmentDrawerMenuListView1);
mPullRefreshLayout = findViewById(R.id.activitydrawerfragmentPullRefreshLayout1);
mPullRefreshLayout.setOnRefreshListener(new PullRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
reinitDrawerMenuItemList(malDrawerMenuItem);
mDrawerMenuDataAdapter.notifyDataSetChanged();
mPullRefreshLayout.setRefreshing(false);
}
});
malDrawerMenuItem = new ArrayList<DrawerMenuBean>();
mDrawerMenuDataAdapter = new DrawerMenuDataAdapter<DrawerMenuBean>(malDrawerMenuItem, R.layout.listview_drawermenu) {
@Override
public void bindView(ViewHolder holder, DrawerMenuBean obj) {
holder.setImageResource(R.id.listviewdrawermenuImageView1, obj.getIconId());
holder.setText(R.id.listviewdrawermenuTextView1, obj.getIconName());
}
};
mADrawerMenuListView.setAdapter(mDrawerMenuDataAdapter);
mADrawerMenuListView.setOnItemClickListener(this);
mActionBarDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.lib_name, R.string.lib_name) {
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
mIsDrawerOpened = true;
mIsDrawerOpening = false;
}
@Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
mIsDrawerOpened = false;
mIsDrawerClosing = false;
}
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, slideOffset);
}
@Override
public void onDrawerStateChanged(int newState) {
super.onDrawerStateChanged(newState);
}
};
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mActionBarDrawerToggle.syncState();
mDrawerLayout.setDrawerListener(mActionBarDrawerToggle);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mIsDrawerOpened || mIsDrawerOpening) {
mIsDrawerClosing = true;
mIsDrawerOpening = false;
mDrawerLayout.closeDrawer(mPullRefreshLayout);
return;
}
if (!mIsDrawerOpened || mIsDrawerClosing) {
mIsDrawerOpening = true;
mIsDrawerClosing = false;
mDrawerLayout.openDrawer(mPullRefreshLayout);
return;
}
}
});
initDrawerMenuItemList(malDrawerMenuItem);
}
void initSecondaryRootView() {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
public <T extends Fragment> int removeFragment(T fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(fragment);
fragmentTransaction.commit();
return fragmentManager.getFragments().size() - 1;
}
public <T extends Fragment> int addFragment(T fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.activitydrawerfragmentFrameLayout1, fragment);
fragmentTransaction.commit();
return fragmentManager.getFragments().size() - 1;
}
public <T extends Fragment> void showFragment(T fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
for (int i = 0; i < fragmentManager.getFragments().size(); i++) {
if (fragmentManager.getFragments().get(i).equals(fragment)) {
fragmentTransaction.show(fragmentManager.getFragments().get(i));
} else {
fragmentTransaction.hide(fragmentManager.getFragments().get(i));
}
}
fragmentTransaction.commit();
}
public void showFragment(int nPosition) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
for (int i = 0; i < fragmentManager.getFragments().size(); i++) {
if (i == nPosition) {
fragmentTransaction.show(fragmentManager.getFragments().get(i));
} else {
fragmentTransaction.hide(fragmentManager.getFragments().get(i));
}
}
fragmentTransaction.commit();
}
protected void initDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
}
protected void reinitDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
}
public void notifyDrawerMenuDataChanged() {
mDrawerMenuDataAdapter.notifyDataSetChanged();
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mDrawerLayout.closeDrawer(mPullRefreshLayout);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (mActivityType == ActivityType.Main) {
// 替换为兼容版菜单加载方法
AESThemeUtil.inflateCompatThemeMenu(this, menu);
// 调试工具菜单
if (GlobalApplication.isDebugging()) {
DevelopUtils.inflateMenu(this, menu);
}
// 应用信息菜单
getMenuInflater().inflate(R.menu.toolbar_drawerbase, menu);
}
return super.onCreateOptionsMenu(menu);
}
@Override
protected void onActivityResult(int who, int targetFragment, Intent requestCode) {
super.onActivityResult(who, targetFragment, requestCode);
}
}
@@ -1,204 +0,0 @@
package cc.winboll.studio.libaes.dialogs;
import android.content.Context;
import android.content.DialogInterface;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import cc.winboll.studio.libappbase.LogUtils;
import java.io.File;
import java.lang.reflect.Field;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class LocalFileSelectDialog {
public static final String TAG = LocalFileSelectDialog.class.getSimpleName();
File mfCurrentPath = new File("/storage/emulated/0");
String mszResultPath = "/storage/emulated/0";
OKClickListener mOKClickListener;
Context mContext;
public LocalFileSelectDialog(Context context) {
mContext = context;
}
public void open() {
LogUtils.d(TAG, "call open()");
String[] szlist = getChildFileList(mfCurrentPath);
if (szlist != null) {
showSingleChoiceDialog(szlist, 0);
}
}
int yourChoice;
protected void showSingleChoiceDialog(final String[] szItems, final int nChoice) {
LogUtils.d(TAG, "call showSingleChoiceDialog(...)");
yourChoice = nChoice;
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
String sz = mfCurrentPath.getPath();
builder.setTitle(sz);
//builder.setCancelable(false);
builder.setSingleChoiceItems(szItems, nChoice,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
yourChoice = which;
}
});
// 确定按钮
builder.setPositiveButton("确定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(
DialogInterface dialog,
int which) {
mszResultPath = mfCurrentPath.getPath() + File.separator + szItems[yourChoice];
//Toast.makeText(mContext, mszResultPath, Toast.LENGTH_SHORT).show();
mOKClickListener.onOKClick(mszResultPath);
}
});
// 下一层文件按钮
builder.setNegativeButton(">>>", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
File file = new File(mfCurrentPath.getPath() + File.separator + szItems[yourChoice]);
String[] szlist = getChildFileList(file);
if (szlist != null) {
mfCurrentPath = new File(mfCurrentPath.getPath() + File.separator + szItems[yourChoice]);
showSingleChoiceDialog(szlist, 0);
} else {
Toast.makeText(mContext, "这是一个最低的目录", Toast.LENGTH_SHORT).show();
String[] szlistOld = getChildFileList(mfCurrentPath);
showSingleChoiceDialog(szlistOld, yourChoice);
}
}
});
// 上一层文件按钮
builder.setNeutralButton("<<<",
new DialogInterface.OnClickListener() {// 添加返回按钮
@Override
public void onClick(
DialogInterface dialog,
int which) {// 响应事件
String[] szlist = getChildFileList(mfCurrentPath.getParentFile());
if (szlist != null) {
mfCurrentPath = mfCurrentPath.getParentFile();
showSingleChoiceDialog(szlist, 0);
} else {
Toast.makeText(mContext, "这是一个最高的目录", Toast.LENGTH_SHORT).show();
String[] szlistOld = getChildFileList(mfCurrentPath);
showSingleChoiceDialog(szlistOld, yourChoice);
}
}
});
AlertDialog dialog = builder.create();
dialog.show();
// 反射原理修改对话框元素
//
//需要在show()方法之后才能修改
//修改“确认”、“取消”按钮的字体大小
//dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextSize(16);
//dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setTextSize(16);
//dialog.getButton(DialogInterface.BUTTON_NEUTRAL).setTextSize(16);
try {
Field mAlert = AlertDialog.class.getDeclaredField("mAlert");
mAlert.setAccessible(true);
Object mAlertController = mAlert.get(dialog);
//通过反射修改title字体大小和颜色
Field mTitle = mAlertController.getClass().getDeclaredField("mTitleView");
mTitle.setAccessible(true);
TextView mTitleView = (TextView) mTitle.get(mAlertController);
//mTitleView.setTextSize(16);
//mTitleView.setTextColor(Color.RED);
mTitleView.setSingleLine(false);
//通过反射修改message字体大小和颜色
//Field mMessage = mAlertController.getClass().getDeclaredField("mMessageView");
//mMessage.setAccessible(true);
//TextView mMessageView = (TextView) mMessage.get(mAlertController);
//mMessageView.setTextSize(16);
//mMessageView.setTextColor(Color.GREEN);
} catch (IllegalAccessException e) {
LogUtils.d(TAG, "IllegalAccessException : " + e.getMessage());
} catch (NoSuchFieldException e) {
LogUtils.d(TAG, "NoSuchFieldException : " + e.getMessage());
}
}
public void setOnOKClickListener(OKClickListener listener) {
mOKClickListener = listener;
}
public interface OKClickListener {
void onOKClick(String szResultPath);
}
// 读取文件夹子目录
//
// "/storage/emulated/0"以上
// 和没有子目录的f参数返回空列表
protected String[] getChildFileList(File file) {
ArrayList<String> szlistFiles = new ArrayList<String>();
if (!file.getPath().equals("/storage/emulated")) {
File[] fileList = file.listFiles();
if (fileList != null) {
for (File fileItem : fileList) {
if (fileItem.getName().charAt(0) != '.') {
if (fileItem.isDirectory()) {
szlistFiles.add(fileItem.getName());
}
}
}
}
}
Collections.sort(szlistFiles, new SortChineseName(true));
if (szlistFiles.size() > 0) {
return szlistFiles.toArray(new String[szlistFiles.size()]);
} else {
return null;
}
}
private class SortChineseName implements Comparator<String> {
private boolean mIsA2Z = true;
public SortChineseName(boolean isA2Z) {
mIsA2Z = isA2Z;
}
Collator cmp = Collator.getInstance(java.util.Locale.CHINA);
@Override
public int compare(String o1, String o2) {
if (mIsA2Z) {
if (cmp.compare(o1, o2) > 0) {
return 1;
} else if (cmp.compare(o1, o2) < 0) {
return -1;
}
} else {
if (cmp.compare(o1, o2) > 0) {
return -1;
} else if (cmp.compare(o1, o2) < 0) {
return 1;
}
}
return 0;
}
}
}
@@ -1,57 +0,0 @@
package cc.winboll.studio.libaes.dialogs;
import android.app.Dialog;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libaes.utils.ScreenUtil;
public class StoragePathDialog extends Dialog {
public static final String TAG = "StoragePathDialog";
View.OnClickListener mOnOKClickListener;
public StoragePathDialog(android.content.Context context) {
super(context);
}
public StoragePathDialog(android.content.Context context, int themeResId) {
super(context, (themeResId == 0) ? cc.winboll.studio.libaes.R.style.NormalDialogStyle: themeResId);
// 加载默认布局
View view = View.inflate(context, R.layout.dialog_storagepath, null);
setContentView(view);
// 添加按键点击监听
view.findViewById(R.id.dialogstoragepathButton1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mOnOKClickListener != null) {
mOnOKClickListener.onClick(view);
}
}
});
// 使得点击对话框外部不消失对话框
setCanceledOnTouchOutside(false);
// 设置对话框大小
ScreenUtil.ScreenSize ss = ScreenUtil.getScreenSize(context);
view.setMinimumHeight((int) (ss.getHeightPixels() * 0.23f));
Window dialogWindow = getWindow();
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
lp.width = (int) (ss.getWidthPixels() * 0.75f);
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.CENTER;
dialogWindow.setAttributes(lp);
}
protected StoragePathDialog(android.content.Context context, boolean cancelable, android.content.DialogInterface.OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
public void setOnOKClickListener(View.OnClickListener listener) {
mOnOKClickListener = listener;
}
}
@@ -1,60 +0,0 @@
package cc.winboll.studio.libaes.dialogs;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/28 17:40:47
* @Date 2024/08/12 14:46:25
* @Describe 询问用户确定与否的选择框
*/
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
public class YesNoAlertDialog {
public static final String TAG = "YesNoAlertDialog";
public static void show(Context context, String szTitle, String szMessage, final OnDialogResultListener listener) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
context);
// set title
alertDialogBuilder.setTitle(szTitle);
// set dialog message
alertDialogBuilder
.setMessage(szMessage)
.setCancelable(true)
.setOnCancelListener(new DialogInterface.OnCancelListener(){
@Override
public void onCancel(DialogInterface dialog) {
listener.onNo();
}
})
.setPositiveButton("YES", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, close
// current activity
listener.onYes();
}
})
.setNegativeButton("NO", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, just close
// the dialog box and do nothing
dialog.cancel();
}
});
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
public interface OnDialogResultListener {
abstract void onYes();
abstract void onNo();
}
}
@@ -1,32 +0,0 @@
package cc.winboll.studio.libaes.enums;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/26 17:49
* @Describe 广告控制模式枚举
*/
public enum ADsMode {
STANDALONE("单机模式"), // 单机模式(默认)
MIMO_SDK("米盟广告SDK支持模式"), // 米盟广告SDK模式
STORE_QRCODE("云宝物语模式"); // 米盟广告SDK模式
private final String modeName;
ADsMode(String modeName) {
this.modeName = modeName;
}
public String getModeName() {
return modeName;
}
// 根据保存的字符串值解析枚举(SP读取时使用)
public static ADsMode fromValue(String value) {
if (value == null) return STANDALONE;
try {
return ADsMode.valueOf(value);
} catch (IllegalArgumentException e) {
return STANDALONE;
}
}
}
@@ -1,67 +0,0 @@
package cc.winboll.studio.libaes.enums;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/27 12:35
* @Describe 隐私协议签约状态枚举
* 对应值:0-拒绝,1-赞同,2-未签约(默认)
*/
public enum PrivacyAgreeStatus {
REJECTED(0, "拒绝"), // 0: 拒绝隐私协议
AGREED(1, "赞同"), // 1: 赞同隐私协议
UN_SIGNED(2, "未签约"); // 2: 未签约(初始默认状态)
private final int statusCode; // 对应存储的int值
private final String statusDesc; // 状态描述(可选,便于日志/UI显示)
// Java 7 枚举构造方法(必须private)
private PrivacyAgreeStatus(int statusCode, String statusDesc) {
this.statusCode = statusCode;
this.statusDesc = statusDesc;
}
/**
* 根据int值获取枚举(SP读取时使用,兼容Java 7)
* @param code 存储的int值(0/1/2
* @return 对应枚举,默认返回UN_SIGNED(未签约)
*/
public static PrivacyAgreeStatus fromCode(int code) {
// Java 7 不支持switch(String),用if-else兼容
if (code == REJECTED.statusCode) {
return REJECTED;
} else if (code == AGREED.statusCode) {
return AGREED;
} else {
return UN_SIGNED; // 默认未签约
}
}
/**
* 根据SP存储的字符串值获取枚举(兼容原逻辑中String类型存储)
* @param codeStr 存储的字符串值("0"/"1"/"2"
* @return 对应枚举,默认返回UN_SIGNED(未签约)
*/
public static PrivacyAgreeStatus fromString(String codeStr) {
if (codeStr == null) {
return UN_SIGNED;
}
try {
int code = Integer.parseInt(codeStr);
return fromCode(code);
} catch (NumberFormatException e) {
// 字符串格式异常时,默认返回未签约
return UN_SIGNED;
}
}
// 获取状态码(用于存储到SP
public int getStatusCode() {
return statusCode;
}
// 获取状态描述(用于日志/UI显示,可选)
public String getStatusDesc() {
return statusDesc;
}
}
@@ -1,24 +0,0 @@
package cc.winboll.studio.libaes.interfaces;
import android.app.Activity;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/05/10 09:34
* @Describe WinBoll 窗口操作接口(规范定义,职责单一)
*/
public interface IWinBoLLActivity {
String TAG = "IWinBoLLActivity";
String ACTION_BIND = IWinBoLLActivity.class.getName() + ".ACTION_BIND";
/**
* 获取当前Activity实例
*/
Activity getActivity();
/**
* 获取Activity唯一标识(建议使用类名+UUID或固定唯一字符串)
*/
String getTag();
}
@@ -1,158 +0,0 @@
package cc.winboll.studio.libaes.models;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/14 02:42:57
* @Describe 主题元素项目类
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libappbase.models.libs1520000.BaseBean;
import java.io.IOException;
import java.util.ArrayList;
public class AESThemeBean extends BaseBean {
public static final String TAG = "AESThemeBean";
public enum ThemeType {
AES("默认主题"),
DEPTH("深奥主题"),
SKY("天空主题"),
GOLDEN("辉煌主题"),
BEARING("智芋主题"),
MEMOR("梦箩主题"),
TAO("黑白主题");
private String name;
// 枚举构造函数
ThemeType(String name) {
this.name = name;
}
// 将字符串转换为枚举
public static ThemeType fromString(String themeTypeStr) {
return ThemeType.valueOf(themeTypeStr.toUpperCase()); // 注意这里用了toUpperCase(),确保匹配时不区分大小写
}
// 获取枚举的名称
public String getName() {
return name;
}
}
public static void fillThemeStyleIDList(ArrayList<Integer> themeStyleIDList) {
if (themeStyleIDList == null) {
themeStyleIDList = new ArrayList<Integer>();
}
themeStyleIDList.clear();
themeStyleIDList.add(cc.winboll.studio.libaes.R.style.AESTheme);
themeStyleIDList.add(cc.winboll.studio.libaes.R.style.DepthAESTheme);
themeStyleIDList.add(cc.winboll.studio.libaes.R.style.SkyAESTheme);
themeStyleIDList.add(cc.winboll.studio.libaes.R.style.GoldenAESTheme);
themeStyleIDList.add(cc.winboll.studio.libaes.R.style.BearingAESTheme);
themeStyleIDList.add(cc.winboll.studio.libaes.R.style.MemorAESTheme);
themeStyleIDList.add(cc.winboll.studio.libaes.R.style.TaoAESTheme);
}
// 保存当前主题
int currentThemeStyleID = getThemeStyleID(ThemeType.AES);
public AESThemeBean() {
}
public AESThemeBean(int currentThemeStyleID) {
this.currentThemeStyleID = currentThemeStyleID;
}
public void setCurrentThemeTypeID(int currentThemeTypeID) {
this.currentThemeStyleID = currentThemeTypeID;
}
public int getCurrentThemeTypeID() {
return this.currentThemeStyleID;
}
@Override
public String getName() {
return AESThemeBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
AESThemeBean bean = this;
jsonWriter.name("currentThemeTypeID").value(bean.getCurrentThemeTypeID());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("currentThemeTypeID")) {
setCurrentThemeTypeID(jsonReader.nextInt());
} else {
return false;
}
}
return true;
}
@Override
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (!initObjectsFromJsonReader(jsonReader, name)) {
jsonReader.skipValue();
}
}
// 结束 JSON 对象
jsonReader.endObject();
return this;
}
public static int getThemeStyleID(ThemeType themeType) {
int themeStyleID = R.style.AESTheme;
if (AESThemeBean.ThemeType.DEPTH == themeType) {
themeStyleID = R.style.DepthAESTheme;
} else if (AESThemeBean.ThemeType.SKY == themeType) {
themeStyleID = R.style.SkyAESTheme;
} else if (AESThemeBean.ThemeType.GOLDEN == themeType) {
themeStyleID = R.style.GoldenAESTheme;
} else if (AESThemeBean.ThemeType.BEARING == themeType) {
themeStyleID = R.style.BearingAESTheme;
} else if (AESThemeBean.ThemeType.MEMOR == themeType) {
themeStyleID = R.style.MemorAESTheme;
} else if (AESThemeBean.ThemeType.TAO == themeType) {
themeStyleID = R.style.TaoAESTheme;
} else if (AESThemeBean.ThemeType.AES == themeType) {
themeStyleID = R.style.AESTheme;
}
//LogUtils.d(TAG, "themeStyleID " + Integer.toString(themeStyleID));
return themeStyleID;
}
public static AESThemeBean.ThemeType getThemeStyleType(int nThemeStyleID) {
AESThemeBean.ThemeType themeStyle = AESThemeBean.ThemeType.AES;
if (R.style.DepthAESTheme == nThemeStyleID) {
themeStyle = AESThemeBean.ThemeType.DEPTH ;
} else if (R.style.SkyAESTheme == nThemeStyleID) {
themeStyle = AESThemeBean.ThemeType.SKY ;
} else if (R.style.GoldenAESTheme == nThemeStyleID) {
themeStyle = AESThemeBean.ThemeType.GOLDEN ;
} else if (R.style.BearingAESTheme == nThemeStyleID) {
themeStyle = AESThemeBean.ThemeType.BEARING ;
} else if (R.style.MemorAESTheme == nThemeStyleID) {
themeStyle = AESThemeBean.ThemeType.MEMOR ;
} else if (R.style.TaoAESTheme == nThemeStyleID) {
themeStyle = AESThemeBean.ThemeType.TAO ;
} else if (R.style.AESTheme == nThemeStyleID) {
themeStyle = AESThemeBean.ThemeType.AES;
}
//LogUtils.d(TAG, "themeStyle " + Integer.toString(themeStyle.ordinal()));
return themeStyle;
}
}
@@ -1,169 +0,0 @@
package cc.winboll.studio.libaes.models;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/01/20 14:19:02
* @Describe 应用信息类
*/
import cc.winboll.studio.libaes.R;
import java.io.Serializable;
public class APPInfo implements Serializable {
public static final String TAG = "APPInfo";
// 应用名称
String appName;
// 应用图标
int appIcon;
// 应用描述
String appDescription;
// 应用Git仓库地址
String appGitName;
// 应用Git仓库拥有者
String appGitOwner;
// 应用Git仓库分支
String appGitAPPBranch;
// 应用Git仓库子项目文件夹
String appGitAPPSubProjectFolder;
// 应用主页
String appHomePage;
// 应用包名称
String appAPKName;
// 应用包存储文件夹名称
String appAPKFolderName;
// 是否添加调试工具
boolean isAddDebugTools;
public APPInfo(String appName, int appIcon, String appDescription, String appGitName, String appGitOwner, String appGitAPPBranch, String appGitAPPSubProjectFolder, String appHomePage, String appAPKName, String appAPKFolderName) {
this.appName = appName;
this.appIcon = appIcon;
this.appDescription = appDescription;
this.appGitName = appGitName;
this.appGitOwner = appGitOwner;
this.appGitAPPBranch = appGitAPPBranch;
this.appGitAPPSubProjectFolder = appGitAPPSubProjectFolder;
this.appHomePage = appHomePage;
this.appAPKName = appAPKName;
this.appAPKFolderName = appAPKFolderName;
this.isAddDebugTools = false;
}
public APPInfo(String appName, int appIcon, String appDescription, String appGitName, String appGitOwner, String appGitAPPBranch, String appGitAPPSubProjectFolder, String appHomePage, String appAPKName, String appAPKFolderName, boolean isAddDebugTools) {
this.appName = appName;
this.appIcon = appIcon;
this.appDescription = appDescription;
this.appGitName = appGitName;
this.appGitOwner = appGitOwner;
this.appGitAPPBranch = appGitAPPBranch;
this.appGitAPPSubProjectFolder = appGitAPPSubProjectFolder;
this.appHomePage = appHomePage;
this.appAPKName = appAPKName;
this.appAPKFolderName = appAPKFolderName;
this.isAddDebugTools = isAddDebugTools;
}
public APPInfo() {
String szBranchName = "app";
this.appName = "APP";
this.appIcon = R.drawable.ic_launcher;
this.appDescription = "APP Description";
this.appGitName = "APP";
this.appGitOwner = "Studio";
this.appGitAPPBranch = szBranchName;
this.appGitAPPSubProjectFolder = szBranchName;
this.appHomePage = "https://www.winboll.cc/studio/details.php?app=APP";
this.appAPKName = "APP";
this.appAPKFolderName = "APP";
this.isAddDebugTools = false;
}
public void setIsAddDebugTools(boolean isAddDebugTools) {
this.isAddDebugTools = isAddDebugTools;
}
public boolean isAddDebugTools() {
return isAddDebugTools;
}
public void setAppGitOwner(String appGitOwner) {
this.appGitOwner = appGitOwner;
}
public String getAppGitOwner() {
return appGitOwner;
}
public void setAppGitAPPBranch(String appGitAPPBranch) {
this.appGitAPPBranch = appGitAPPBranch;
}
public String getAppGitAPPBranch() {
return appGitAPPBranch;
}
public void setAppGitAPPSubProjectFolder(String appGitAPPSubProjectFolder) {
this.appGitAPPSubProjectFolder = appGitAPPSubProjectFolder;
}
public String getAppGitAPPSubProjectFolder() {
return appGitAPPSubProjectFolder;
}
public void setAppIcon(int appIcon) {
this.appIcon = appIcon;
}
public int getAppIcon() {
return appIcon;
}
public void setAppDescription(String appDescription) {
this.appDescription = appDescription;
}
public String getAppDescription() {
return appDescription;
}
public void setAppAPKFolderName(String appAPKFolderName) {
this.appAPKFolderName = appAPKFolderName;
}
public String getAppAPKFolderName() {
return appAPKFolderName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getAppName() {
return appName;
}
public void setAppGitName(String appGitName) {
this.appGitName = appGitName;
}
public String getAppGitName() {
return appGitName;
}
public void setAppHomePage(String appHomePage) {
this.appHomePage = appHomePage;
}
public String getAppHomePage() {
return appHomePage;
}
public void setAppAPKName(String appAPKName) {
this.appAPKName = appAPKName;
}
public String getAppAPKName() {
return appAPKName;
}
}
@@ -1,35 +0,0 @@
package cc.winboll.studio.libaes.models;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/14 01:53:34
* @Describe 抽屉菜单项目类
*/
public class DrawerMenuBean {
public static final String TAG = "DrawerMenuBean";
private int iconId;
private String iconName;
public DrawerMenuBean(int iconId, String iconName) {
this.iconId = iconId;
this.iconName = iconName;
}
public int getIconId() {
return iconId;
}
public String getIconName() {
return iconName;
}
public void setIconId(int iconId) {
this.iconId = iconId;
}
public void setIconName(String iconName) {
this.iconName = iconName;
}
}
@@ -1,78 +0,0 @@
package cc.winboll.studio.libaes.models;
/**
* @Author ZhanGSKen
* @Date 2025/05/03 19:16
*/
import android.content.Context;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.models.libs1520000.BaseBean;
import java.io.IOException;
public class WinBoLLClientServiceBean extends BaseBean {
public static final String TAG = "WinBoLLClientServiceBean";
// 服务是否正在使用中
boolean isEnable;
public WinBoLLClientServiceBean() {
this.isEnable = false;
}
public WinBoLLClientServiceBean(boolean isEnable) {
this.isEnable = isEnable;
}
public void setIsEnable(boolean isEnable) {
this.isEnable = isEnable;
}
public boolean isEnable() {
return isEnable;
}
@Override
public String getName() {
return WinBoLLClientServiceBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
WinBoLLClientServiceBean bean = this;
//jsonWriter.name("logLevel").value(bean.getLogLevel().ordinal());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
// if (name.equals("logLevel")) {
// setLogLevel(LogUtils.LOG_LEVEL.values()[jsonReader.nextInt()]);
// } else {
// return false;
// }
}
return true;
}
@Override
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (!initObjectsFromJsonReader(jsonReader, name)) {
jsonReader.skipValue();
}
}
// 结束 JSON 对象
jsonReader.endObject();
return this;
}
public static WinBoLLClientServiceBean loadWinBoLLClientServiceBean(Context context) {
return new WinBoLLClientServiceBean();
}
}
@@ -1,57 +0,0 @@
package cc.winboll.studio.libaes.unittests;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/15 00:58:10
* @Describe 第二级窗口
*/
public class SecondaryLibraryActivity extends DrawerFragmentActivity implements IWinBoLLActivity {
public static final String TAG = "SecondaryLibraryActivity";
SecondaryLibraryFragment mSecondaryLibraryFragment;
@Override
public String getTag() {
return null;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mSecondaryLibraryFragment == null) {
mSecondaryLibraryFragment = new SecondaryLibraryFragment();
addFragment(mSecondaryLibraryFragment);
}
showFragment(mSecondaryLibraryFragment);
}
@Override
public DrawerFragmentActivity.ActivityType initActivityType() {
return DrawerFragmentActivity.ActivityType.Secondary;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar_secondarylibrary, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int nItemId = item.getItemId();
if (nItemId == R.id.item_test) {
Toast.makeText(getApplicationContext(), "item_test", Toast.LENGTH_SHORT).show();
}
return super.onOptionsItemSelected(item);
}
}
@@ -1,25 +0,0 @@
package cc.winboll.studio.libaes.unittests;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 02:36:34
* @Describe SecondaryLibraryFragment
*/
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import cc.winboll.studio.libaes.R;
public class SecondaryLibraryFragment extends Fragment {
public static final String TAG = "SecondaryLibraryFragment";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_secondarylibrary, container, false);
return view;
}
}
@@ -1,37 +0,0 @@
package cc.winboll.studio.libaes.unittests;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 01:27:50
* @Describe TestAButtonFragment
*/
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libaes.views.AButton;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
public class TestAButtonFragment extends Fragment {
public static final String TAG = "TestAButtonFragment";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_abutton, container, false);
AButton aButton = view.findViewById(R.id.fragmentabuttonAButton1);
aButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
LogUtils.d(TAG, "onClick");
ToastUtils.show("AButton");
}
});
return view;
}
}
@@ -1,48 +0,0 @@
package cc.winboll.studio.libaes.unittests;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 01:14:00
* @Describe TestASupportToolbarActivity
*/
import android.app.Activity;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libappbase.LogUtils;
public class TestASupportToolbarActivity extends AppCompatActivity implements IWinBoLLActivity {
public static final String TAG = "TestASupportToolbarActivity";
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
LogUtils.d(TAG, "onCreate() start");
// 替换此处:原 applyAppTheme -> 新方法 applyAppCompatTheme
AESThemeUtil.applyAppCompatTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testasupporttoolbar);
LogUtils.d(TAG, "setContentView() done");
Toolbar toolbar = findViewById(R.id.activitytestasupporttoolbarASupportToolbar1);
LogUtils.d(TAG, "findViewById() done, toolbar=" + toolbar.getClass().getSimpleName());
setSupportActionBar(toolbar);
LogUtils.d(TAG, "setSupportActionBar() done");
getSupportActionBar().setTitle(TAG);
LogUtils.d(TAG, "setTitle() done");
LogUtils.d(TAG, "onCreate() end");
}
}
@@ -1,30 +0,0 @@
package cc.winboll.studio.libaes.unittests;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 01:16:07
* @Describe TestAToolbarActivity
*/
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toolbar;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libaes.utils.AESThemeUtil;
public class TestAToolbarActivity extends Activity {
public static final String TAG = "TestAToolbarActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
// 原生Activity 使用 applyTheme
AESThemeUtil.applyTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testatoolbar);
Toolbar toolbar = findViewById(R.id.activitytestatoolbarAToolbar1);
setActionBar(toolbar);
getActionBar().setTitle(TAG);
}
}
@@ -1,124 +0,0 @@
package cc.winboll.studio.libaes.unittests;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/30 15:00:51
*/
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
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.DrawerMenuBean;
import cc.winboll.studio.libappbase.LogUtils;
import java.util.ArrayList;
public class TestDrawerFragmentActivity extends DrawerFragmentActivity implements IWinBoLLActivity {
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return null;
}
public static final String TAG = "TestDrawerFragmentActivity";
TestFragment1 mTestFragment1;
TestFragment2 mTestFragment2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTestFragment1 = new TestFragment1();
addFragment(mTestFragment1);
mTestFragment2 = new TestFragment2();
addFragment(mTestFragment2);
showFragment(0);
}
@Override
protected DrawerFragmentActivity.ActivityType initActivityType() {
return DrawerFragmentActivity.ActivityType.Main;
}
@Override
public void initDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
super.initDrawerMenuItemList(listDrawerMenu);
LogUtils.d(TAG, "initDrawerMenuItemList");
//listDrawerMenu.clear();
// 添加抽屉菜单项
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestFragment1.TAG));
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestFragment2.TAG));
notifyDrawerMenuDataChanged();
}
@Override
public void reinitDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
super.reinitDrawerMenuItemList(listDrawerMenu);
LogUtils.d(TAG, "reinitDrawerMenuItemList");
//listDrawerMenu.clear();
// 添加抽屉菜单项
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestFragment1.TAG));
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestFragment2.TAG));
notifyDrawerMenuDataChanged();
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
super.onItemClick(parent, view, position, id);
switch (position) {
case 0 : {
Toast.makeText(getApplicationContext(), "0", Toast.LENGTH_SHORT).show();
//LogUtils.d(TAG, "MenuItem 1");
showFragment(mTestFragment1);
break;
}
case 1 : {
//LogUtils.d(TAG, "MenuItem 2");
showFragment(mTestFragment2);
break;
}
}
}
public static class TestFragment1 extends Fragment {
public static final String TAG = "TestFragment1";
View mView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_test1, container, false);
return mView;
}
}
public static class TestFragment2 extends Fragment {
public static final String TAG = "TestFragment2";
View mView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_test2, container, false);
return mView;
}
}
}
@@ -1,226 +0,0 @@
package cc.winboll.studio.libaes.unittests;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 01:35:56
* @Describe TestViewPageFragment
*/
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import cc.winboll.studio.libaes.ImagePagerAdapter;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libappbase.ToastUtils;
import java.util.ArrayList;
import java.util.List;
public class TestViewPageFragment extends Fragment implements ViewPager.OnPageChangeListener, View.OnClickListener {
public static final String TAG = "TestViewPageFragment";
Context mContext;
LogView mLogView;
private ViewPager viewPager;
private List<View> views; //用来存放放进ViewPager里面的布局
//实例化存储imageView(导航原点)的集合
ImageView[] imageViews;
private ImagePagerAdapter adapter;//适配器
private LinearLayout linearLayout;//下标所在在LinearLayout布局里
private int currentPoint = 0;//当前被选中中页面的下标
View mView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_viewpage, container, false);
mContext = getActivity();
mLogView = mView.findViewById(R.id.logview);
mLogView.start();
//viewPager = findViewById(R.id.activitymainViewPager1);
initData();
initView();//调用初始化视图方法
initPoint();//调用初始化导航原点的方法
viewPager.addOnPageChangeListener(this);//滑动事件
//viewPager.setAdapter(new MyAdapter());
// 获取屏幕参数
//ScreenUtil.ScreenSize ss = ScreenUtil.getScreenSize(MainActivity.this);
//Toast.makeText(getApplication(), Integer.toString(ss.getHeightPixels())+" "+Integer.toString(ss.getWidthPixels()), Toast.LENGTH_SHORT).show();
return mView;
}
//初始化view,即显示的图片
void initView() {
adapter = new ImagePagerAdapter(views);
viewPager = mView.findViewById(R.id.fragmentviewpageViewPager1);
viewPager.setAdapter(adapter);
linearLayout = mView.findViewById(R.id.fragmentviewpageLinearLayout1);
initPoint();//初始化页面下方的点
viewPager.setOnPageChangeListener(this);
initAOHPCTCSeekBar();
initAOHPCTCSeekBar2();
}
//初始化所要显示的布局
void initData() {
ViewPager viewPager = mView.findViewById(R.id.fragmentviewpageViewPager1);
LayoutInflater inflater = LayoutInflater.from(mContext);
View view1 = inflater.inflate(R.layout.viewpage_atickprogressbar, viewPager, false);
View view2 = inflater.inflate(R.layout.viewpage_acard, viewPager, false);
View view3 = inflater.inflate(R.layout.viewpage_aohpctccard, viewPager, false);
View view4 = inflater.inflate(R.layout.viewpage_aohpctcsb, viewPager, false);
views = new ArrayList<>();
views.add(view1);
views.add(view2);
views.add(view3);
views.add(view4);
}
//setTag注释
/*
//View中的setTagOnbect)表示给View添加一个格外的数据,以后可以用getTag()将这个数据取出来。来
代表这个数据,即实例化
Tag是标签的bai意识,这里的tag是object类型。所以通常会使用setTag()设置不同的Object子类对象,
然后使用强制转换getTag()获得对象。
//可以用在多个Button添加一个监听器,每个Button都设置不同的setTag。
这个监听器就通过getTag来分辨是哪个Button 被按下。
public class Main extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button1 = (Button) findViewById(R.id.Button01);
Button button2 = (Button) findViewById(R.id.Button02);
Button button3 = (Button) findViewById(R.id.Button03);
Button button4 = (Button) findViewById(R.id.Button04);
MyListener listener = new MyListener();
button1.setTag(1);
button1.setOnClickListener(listener);
button2.setTag(2);
button2.setOnClickListener(listener);
button3.setTag(3);
button3.setOnClickListener(listener);
button4.setTag(4);
button4.setOnClickListener(listener);
}
public class MyListener implements View.OnClickListener {
@Override
public void onClick(View v) {
int tag = (Integer) v.getTag();
switch (tag) {
case 1:
System.out.println(“button1 click”);
break;
case 2:
System.out.println(“button2 click”);
break;
case 3:
System.out.println(“button3 click”);
break;
case 4:
System.out.println(“button4 click”);
break;
}
*/
private void initPoint() {
imageViews = new ImageView[5];//实例化5个图片
for (int i = 0; i < linearLayout.getChildCount(); i++) {
imageViews[i] = (ImageView) linearLayout.getChildAt(i);
imageViews[i].setImageResource(R.drawable.ic_arrow_left_right_bold);
imageViews[i].setOnClickListener(this);//点击导航点,即可跳转
imageViews[i].setTag(i);//重复利用实例化的对象
}
currentPoint = 0;//默认第一个坐标
imageViews[currentPoint].setImageResource(R.drawable.ic_arrow_up_circle_outline);
}
//OnPageChangeListener接口要实现的三个方法
/* onPageScrollStateChanged(int state)
此方法是在状态改变的时候调用,其中state这个参数有三种状态:
SCROLL_STATE_DRAGGING1)表示用户手指“按在屏幕上并且开始拖动”的状态
(手指按下但是还没有拖动的时候还不是这个状态,只有按下并且手指开始拖动后log才打出。)
SCROLL_STATE_IDLE0)滑动动画做完的状态。
SCROLL_STATE_SETTLING2)在“手指离开屏幕”的状态。*/
@Override
public void onPageScrollStateChanged(int state) {
}
/* onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为:
position :当前页面,即你点击滑动的页面(从A滑B,则是A页面的position。
positionOffset:当前页面偏移的百分比
positionOffsetPixels:当前页面偏移的像素位置*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
/* onPageSelected(int position)
此方法是页面滑动完后得到调用,position是你当前选中的页面的Position(位置编号)
(从A滑动到B,就是B的position)*/
public void onPageSelected(int position) {
ImageView preView = imageViews[currentPoint];
preView.setImageResource(R.drawable.ic_arrow_left_right_bold);
ImageView currView = imageViews[position];
currView.setImageResource(R.drawable.ic_arrow_up_circle_outline);
currentPoint = position;
}
//小圆点点击事件
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
//通过getTag(),可以判断是哪个控件
int i = (Integer) v.getTag();
viewPager.setCurrentItem(i);//直接跳转到某一个页面的情况
}
void initAOHPCTCSeekBar() {
AOHPCTCSeekBar seekbar = views.get(3).findViewById(R.id.fragmentviewpageAOHPCTCSeekBar1);
seekbar.setThumb(mContext.getDrawable(R.drawable.ic_launcher));
//seekbar.setThumbOffset(200);
//seekbar.setThumbOffset(1);
seekbar.setBlurRightDP(50);
seekbar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() {
@Override
public void onOHPCommit() {
ToastUtils.show("onOHPCommit");
}
});
}
void initAOHPCTCSeekBar2() {
AOHPCTCSeekBar seekbar = views.get(3).findViewById(R.id.fragmentviewpageAOHPCTCSeekBar2);
seekbar.setThumb(mContext.getDrawable(R.drawable.ic_call));
//seekbar.setThumbOffset(200);
//seekbar.setThumbOffset(1);
seekbar.setBlurRightDP(50);
seekbar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() {
@Override
public void onOHPCommit() {
ToastUtils.show("onOHPCommit 2");
}
});
}
}
@@ -1,171 +0,0 @@
package cc.winboll.studio.libaes.utils;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/11/29 22:52:09
* @Describe AES 主题工具集
*/
import android.app.Activity;
import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
import cc.winboll.studio.libaes.models.AESThemeBean;
import java.util.ArrayList;
public class AESThemeUtil {
public static final String TAG = "AESThemeUtil";
private static final String SHAREDPREFERENCES_NAME = "SHAREDPREFERENCES_NAME";
private static final String DRAWER_THEME_TYPE = "DRAWER_THEME_TYPE";
// 私有静态集合,外部不可直接修改
private static ArrayList<Integer> themeStyleIDList = new ArrayList<>();
// 移除无用实例成员 mThemeType,工具类不保留实例字段
/**
* 初始化主题样式ID集合
*/
public static void init(ArrayList<Integer> themeStyleIDList) {
if (themeStyleIDList == null) {
themeStyleIDList = new ArrayList<Integer>();
AESThemeBean.fillThemeStyleIDList(themeStyleIDList);
}
AESThemeUtil.themeStyleIDList.clear();
AESThemeUtil.themeStyleIDList.addAll(themeStyleIDList);
}
/**
* 获取当前主题样式ID
*/
public static int getThemeTypeID(Context context) {
AESThemeBean bean = AESThemeBean.loadBean(context, AESThemeBean.class);
return bean == null ? getThemeStyleID(AESThemeBean.ThemeType.AES) : bean.getCurrentThemeTypeID();
}
/**
* 保存主题样式ID
*/
public static void saveThemeStyleID(Context context, int themeTypeID) {
AESThemeBean bean = new AESThemeBean(themeTypeID);
AESThemeBean.saveBean(context, bean);
}
// ====================== 应用主题 - 规范重载 ======================
/**
* 应用当前持久化主题(通用 Activity)
*/
public static void applyTheme(Activity activity) {
activity.setTheme(getThemeTypeID(activity));
}
/**
* 应用指定主题(通用 Activity)
*/
public static void applyTheme(Activity activity, AESThemeBean.ThemeType themeType) {
activity.setTheme(getThemeStyleID(themeType));
}
/**
* 应用当前持久化主题(AppCompat 兼容 Activity
*/
public static void applyAppCompatTheme(AppCompatActivity activity) {
activity.setTheme(getThemeTypeID(activity));
}
/**
* 应用指定主题(AppCompat 兼容 Activity
*/
public static void applyAppCompatTheme(AppCompatActivity activity, AESThemeBean.ThemeType themeType) {
activity.setTheme(getThemeStyleID(themeType));
}
// ====================== 加载菜单 ======================
/**
* 加载主题菜单(通用 Activity)
*/
public static void inflateThemeMenu(Activity activity, Menu menu) {
activity.getMenuInflater().inflate(R.menu.toolbar_apptheme, menu);
}
/**
* 加载主题菜单(AppCompat Activity
*/
public static void inflateCompatThemeMenu(AppCompatActivity activity, Menu menu) {
activity.getMenuInflater().inflate(R.menu.toolbar_apptheme, menu);
}
// ====================== 菜单点击统一核心逻辑(消除重复代码) ======================
/**
* 主题菜单项点击统一处理
* @param context 上下文(用于持久化)
* @param item 点击的菜单项
* @return 是否消费点击事件
*/
public static boolean handleThemeMenuClick(Context context, MenuItem item) {
int themeStyleId;
int itemId = item.getItemId();
if (R.id.item_depththeme == itemId) {
themeStyleId = getThemeStyleID(AESThemeBean.ThemeType.DEPTH);
} else if (R.id.item_skytheme == itemId) {
themeStyleId = getThemeStyleID(AESThemeBean.ThemeType.SKY);
} else if (R.id.item_goldentheme == itemId) {
themeStyleId = getThemeStyleID(AESThemeBean.ThemeType.GOLDEN);
} else if (R.id.item_bearingtheme == itemId) {
themeStyleId = getThemeStyleID(AESThemeBean.ThemeType.BEARING);
} else if (R.id.item_memortheme == itemId) {
themeStyleId = getThemeStyleID(AESThemeBean.ThemeType.MEMOR);
} else if (R.id.item_taotheme == itemId) {
themeStyleId = getThemeStyleID(AESThemeBean.ThemeType.TAO);
} else if (R.id.item_defaulttheme == itemId) {
themeStyleId = getThemeStyleID(AESThemeBean.ThemeType.AES);
} else {
return false;
}
saveThemeStyleID(context, themeStyleId);
return true;
}
// 对外暴露不同 Activity 类型的入口,内部调用统一核心方法
public static boolean onThemeItemSelected(Activity activity, MenuItem item) {
return handleThemeMenuClick(activity, item);
}
public static boolean onAppCompatThemeItemSelected(AppCompatActivity activity, MenuItem item) {
return handleThemeMenuClick(activity, item);
}
public static boolean onWinBoLLThemeItemSelected(AppCompatActivity activity, MenuItem item) {
// 使用 Application 上下文保存,避免 Activity 泄漏
return handleThemeMenuClick(activity.getApplicationContext(), item);
}
public static boolean onWinBoLLThemeItemSelected(DrawerFragmentActivity activity, MenuItem item) {
return handleThemeMenuClick(activity.getApplicationContext(), item);
}
// ====================== 主题类型转换工具 ======================
/**
* 根据枚举获取对应样式ID
*/
public static int getThemeStyleID(AESThemeBean.ThemeType themeType) {
return themeStyleIDList.get(themeType.ordinal());
}
/**
* 根据样式ID反向获取主题枚举
*/
public static AESThemeBean.ThemeType getThemeStyleType(int themeStyleID) {
for (int i = 0; i < themeStyleIDList.size(); i++) {
if (themeStyleIDList.get(i) == themeStyleID) {
return AESThemeBean.ThemeType.values()[i];
}
}
return AESThemeBean.ThemeType.values()[0];
}
}
@@ -1,162 +0,0 @@
package cc.winboll.studio.libaes.utils;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/08/12 14:45:35
* @Describe 应用版本工具集
*/
import cc.winboll.studio.libappbase.LogUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AppVersionUtils {
public static final String TAG = "AppVersionUtils";
//
// 检查新版本是否成立
// szCurrentCode : 当前版本应用包名
// szNextCode : 新版本应用包名
// 返回 :情况1:当前版本是发布版
// 返回 true (新版本 > 当前版本)
// 情况1:当前版本是Beta版
// true 新版本 == 当前版本
//
public static boolean isHasNewVersion2(String szCurrentName, String szNextName) {
LogUtils.d(TAG, String.format("isHasNewVersion2\nszCurrentName : %s\nszNextName : %s", szCurrentName, szNextName));
//szCurrentName = "AES_6.2.0-beta0_3234.apk";
//szNextName = "AES_6.1.12.apk";
//szCurrentName = "AES_6.2.0-beta0_3234.apk";
//szNextName = "AES_6.2.0.apk";
//szCurrentName = "AES_6.2.0-beta0_3234.apk";
//szNextName = "AES_6.2.2.apk";
//szCurrentName = "AES_6.2.0-beta0_3234.apk";
//szNextName = "AES_6.2.0.apk";
//szCurrentName = "AES_6.1.0.apk";
//szNextName = "AES_6.2.0.apk";
//LogUtils.d(TAG, "szCurrentName : " + szCurrentName);
//LogUtils.d(TAG, "szNextName : " + szNextName);
//boolean isVersionNewer = false;
//if(szCurrentName.equals(szNextName)) {
// isVersionNewer = false;
//} else {
//ToastUtils.show("szCurrent : " + szCurrent + "\nszNext : " + szNext);
//int nApk = szNextName.lastIndexOf(".apk");
//ToastUtils.show("nApk : " + Integer.toString(nApk));
//String szNextNoApkName = szNextName.substring(0, nApk);
//ToastUtils.show("szNextNoApkName : " + szNextNoApkName);
//String szCurrentNoApkName = szCurrentName.substring(0, szNextNoApkName.length());
//ToastUtils.show("szCurrentNoApkName : " + szCurrentNoApkName);
//String str1 = "3.4.50";
//String str2 = "3.3.60";
//String str1 = getCodeInPackageName(szCurrentName);
//String str2 = getCodeInPackageName(szNextName);
//String str1 = getCodeInPackageName(szNextName);
//String str2 = getCodeInPackageName(szCurrentName);
//Boolean isVersionNewer2 = checkNewVersion(str1,str2);
//ToastUtils.show("isVersionNewer2 : " + Boolean.toString(isVersionNewer2));
//ToastUtils.show(checkNewVersion(getCodeInPackageName(szCurrentName), getCodeInPackageName(szNextName)));
//return checkNewVersion(getCodeInPackageName(szCurrentName), getCodeInPackageName(szNextName));
//}
//return isVersionNewer;
if (checkNewVersion(getCodeInPackageName(szCurrentName), getCodeInPackageName(szNextName))) {
// 比 AES_6.2.0.apk 版本大,如 AES_6.2.1.apk。
// 比 AES_6.2.0-beta0_3234.apk 大,如 AES_6.2.1.apk。
//LogUtils.d(TAG, "App newer stage version is released. Release name : " + szNextName);
return true;
}
if (szCurrentName.matches(".*_\\d+\\.\\d+\\.\\d+-beta.*\\.apk")) {
String szCurrentReleasePackageName = getReleasePackageName(szCurrentName);
//LogUtils.d(TAG, "szCurrentReleasePackageName : " + szCurrentReleasePackageName);
if (szCurrentReleasePackageName.equals(szNextName)) {
// 与 AES_6.2.0-beta0_3234.apk 版本相同,如 AES_6.2.0.apk。
//LogUtils.d(TAG, "App stage version is released. Release name : " + szNextName);
return true;
}
}
//LogUtils.d(TAG, "App version is the newest. ");
return false;
}
public static boolean isHasNewStageReleaseVersion(String szCurrentName, String szNextName) {
LogUtils.d(TAG, String.format("isHasNewStageReleaseVersion\nszCurrentName : %s\nszNextName : %s", szCurrentName, szNextName));
//szCurrentName = "AES_6.2.12.apk";
//szNextName = "AES_6.3.12.apk";
if (checkNewVersion(getCodeInPackageName(szCurrentName), getCodeInPackageName(szNextName))) {
// 比 AES_6.2.0.apk 版本大,如 AES_6.2.1.apk。
//LogUtils.d(TAG, "App newer stage version is released. Release name : " + szNextName);
return true;
}
return false;
}
//
// 检查新版本是否成立
// szCurrentCode : 当前版本
// szNextCode : 新版本
// 返回 :true 新版本 > 当前版本
//
public static Boolean checkNewVersion(String szCurrentCode, String szNextCode) {
if (szCurrentCode == null || szCurrentCode.equals("") || szNextCode == null || szNextCode.equals("")) {
LogUtils.d(TAG, String.format("checkNewVersion unexpected parameters:\nszCurrentCode : %s\nszNextCode : %s", szCurrentCode, szNextCode));
return false;
}
boolean isNew = false;
String[] appVersionCurrent = szCurrentCode.split("\\.");
String[] appVersionNext = szNextCode.split("\\.");
//根据位数最短的判断
int lim = appVersionCurrent.length > appVersionNext.length ? appVersionNext.length : appVersionCurrent.length;
//根据位数循环判断各个版本
for (int i = 0; i < lim; i++) {
if (Integer.parseInt(appVersionNext[i]) > Integer.parseInt(appVersionCurrent[i])) {
isNew = true;
return isNew;
} else if (Integer.parseInt(appVersionNext[i]) == Integer.parseInt(appVersionCurrent[i])) {
continue ;
} else {
isNew = false;
return isNew;
}
}
return isNew;
}
//
// 截取应用包名称版本号信息
// 如 AppUtils_7.0.4-beta1_0120.apk 版本号为 7.0.4
// 如 AppUtils_7.0.4.apk 版本号为 7.0.4
//
public static String getCodeInPackageName(String apkName) {
LogUtils.d(TAG, String.format("getCodeInPackageName apkName : %s", apkName));
//String apkName = "AppUtils_7.0.0.apk";
Pattern pattern = Pattern.compile("\\d+\\.\\d+\\.\\d+");
Matcher matcher = pattern.matcher(apkName);
if (matcher.find()) {
String version = matcher.group();
LogUtils.d(TAG, String.format("version is %s", version));
return version;
//System.out.println("Version number: " + version); // 输出:7.0.0
}
LogUtils.d(TAG, String.format("No result."));
return "";
}
//
// 根据Beta版名称生成发布版应用包名称
// 如 AppUtils_7.0.4-beta1_0120.apk
// 发布版名称就为AppUtils_7.0.4.apk
//
public static String getReleasePackageName(String szBetaPackageName) {
//String szBetaPackageName = "AppUtils_7.0.4-beta1_0120.apk";
Pattern pattern = Pattern.compile(".*\\d+\\.\\d+\\.\\d+");
Matcher matcher = pattern.matcher(szBetaPackageName);
if (matcher.find()) {
String szReleasePackageName = matcher.group();
return szReleasePackageName + ".apk";
//System.out.println("Version number: " + version); // 输出:7.0.0
}
return "";
}
}
@@ -1,35 +0,0 @@
package cc.winboll.studio.libaes.utils;
import android.app.Activity;
import android.view.Menu;
import android.view.MenuItem;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libappbase.LogActivity;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/07 13:31
* @Describe 应用开发工具类
*/
public class DevelopUtils {
public static final String TAG = "DevelopUtils";
public static <T extends Activity> void inflateMenu(T activity, Menu menu) {
activity.getMenuInflater().inflate(R.menu.toolbar_appdebug, menu);
}
public static <T extends Activity> boolean onDevelopItemSelected(T activity, MenuItem item) {
if (R.id.item_testappcrash == item.getItemId()) {
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
activity.getString(i);
}
} else if (R.id.item_log == item.getItemId()) {
//ToastUtils.show("Test");
LogActivity.startLogActivity(activity);
} else {
return false;
}
return true;
}
}
@@ -1,33 +0,0 @@
package cc.winboll.studio.libaes.utils;
import android.content.Context;
import android.util.DisplayMetrics;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/18 15:23
* @Describe 米盟 MimoUtils
*/
public final class MimoUtils {
public static final String TAG = "Utils";
public static int dpToPx(Context context, float dp) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return (int) (dp * displayMetrics.density + 0.5f);
}
public static int pxToDp(Context context, float px) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return (int) (px / displayMetrics.density + 0.5f);
}
public static int pxToSp(Context context, float pxValue) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return (int) (pxValue / displayMetrics.scaledDensity + 0.5f);
}
public static int spToPx(Context context, float spValue) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return (int) (spValue * displayMetrics.scaledDensity + 0.5f);
}
}
@@ -1,97 +0,0 @@
package cc.winboll.studio.libaes.utils;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/25 04:29:19
*/
import android.app.Activity;
import android.app.Application;
import android.content.Intent;
import android.os.Bundle;
import cc.winboll.studio.libappbase.LogUtils;
public class MyActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
public static final String TAG = "MyActivityLifecycleCallbacks";
public String mInfo = "";
public MyActivityLifecycleCallbacks() {
}
void createActivityeInfo(Activity activity) {
StringBuilder sb = new StringBuilder();
Intent receivedIntent = activity.getIntent();
sb.append("\nCallingActivity : \n");
if (activity.getCallingActivity() != null) {
sb.append(activity.getCallingActivity().getPackageName());
}
sb.append("\nReceived Intent Package : \n");
sb.append(receivedIntent.getPackage());
Bundle extras = receivedIntent.getExtras();
if (extras != null) {
for (String key : extras.keySet()) {
sb.append("\nIntentInfo");
sb.append("\n键: ");
sb.append(key);
sb.append(", 值: ");
sb.append(extras.get(key));
//Log.d("IntentInfo", "键: " + key + ", 值: " + extras.get(key));
}
}
mInfo = sb.toString();
//Log.d("IntentInfo", "发送Intent的应用包名: " + senderPackage);
}
public void showActivityeInfo() {
//ToastUtils.show("ActivityeInfo : " + mInfo);
LogUtils.d(TAG, "ActivityeInfo : " + mInfo);
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
// 在这里可以做一些初始化相关的操作,例如记录Activity的创建时间等
//System.out.println(activity.getLocalClassName() + " was created");
LogUtils.d(TAG, activity.getLocalClassName() + " was created");
createActivityeInfo(activity);
}
@Override
public void onActivityStarted(Activity activity) {
//System.out.println(activity.getLocalClassName() + " was started");
LogUtils.d(TAG, activity.getLocalClassName() + " was started");
//createActivityeInfo(activity);
}
@Override
public void onActivityResumed(Activity activity) {
//System.out.println(activity.getLocalClassName() + " was resumed");
LogUtils.d(TAG, activity.getLocalClassName() + " was resumed");
//createActivityeInfo(activity);
}
@Override
public void onActivityPaused(Activity activity) {
//System.out.println(activity.getLocalClassName() + " was paused");
LogUtils.d(TAG, activity.getLocalClassName() + " was paused");
}
@Override
public void onActivityStopped(Activity activity) {
//System.out.println(activity.getLocalClassName() + " was stopped");
LogUtils.d(TAG, activity.getLocalClassName() + " was stopped");
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
// 可以在这里添加保存状态的自定义逻辑
}
@Override
public void onActivityDestroyed(Activity activity) {
//System.out.println(activity.getLocalClassName() + " was destroyed");
LogUtils.d(TAG, activity.getLocalClassName() + " was destroyed");
}
}
@@ -1,33 +0,0 @@
package cc.winboll.studio.libaes.utils;
import android.content.Context;
import android.content.SharedPreferences;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/13 06:50
* @Describe 应用变量保存工具
*/
public class PrefUtils {
public static final String TAG = "PrefUtils";
//
// 保存字符串到SharedPreferences的函数
//
public static void saveString(Context context, String key, String value) {
SharedPreferences sharedPreferences = context.getSharedPreferences("myPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(key, value);
editor.apply();
}
//
// 从SharedPreferences读取字符串的函数
//
public static String getString(Context context, String key, String defaultValue) {
SharedPreferences sharedPreferences = context.getSharedPreferences("myPrefs", Context.MODE_PRIVATE);
return sharedPreferences.getString(key, defaultValue);
}
}
@@ -1,64 +0,0 @@
package cc.winboll.studio.libaes.utils;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.WindowManager;
public class ScreenUtil {
// 分辨率宽度和高度计量类
//
public static class ScreenSize {
int widthPixels;
int heightPixels;
public ScreenSize(int widthPixels, int heightPixels) {
this.widthPixels = widthPixels;
this.heightPixels = heightPixels;
}
public void setWidthPixels(int widthPixels) {
this.widthPixels = widthPixels;
}
public int getWidthPixels() {
return widthPixels;
}
public void setHeightPixels(int heightPixels) {
this.heightPixels = heightPixels;
}
public int getHeightPixels() {
return heightPixels;
}
}
// 获取屏幕分辨率宽度和高度
//
public static ScreenSize getScreenSize(Context mContext) {
WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(dm);
return new ScreenSize(dm.widthPixels, dm.heightPixels);
}
// 获取屏幕宽度
//
public static int getScreenWidth(Context mContext) {
WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(dm);
return dm.widthPixels;
}
// 获取屏幕高度
//
public static int getScreenHeight(Context mContext) {
WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(dm);
return dm.heightPixels;
}
}
@@ -1,102 +0,0 @@
package cc.winboll.studio.libaes.utils;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
import cc.winboll.studio.libappbase.LogUtils;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/05 15:45
* @LastEditTime 2026/01/05 19:30:00 HKT
* @Describe 网页工具集(优化:新增合法性校验+浏览器可用性检查+链接格式自动修复)
*/
public class WebUtils {
public static final String TAG = "WebUtils";
/**
* 唤起系统默认浏览器打开指定网站
* @param context 上下文对象(建议使用 ApplicationContext 避免内存泄漏)
* @param url 目标 URL(支持自动修复格式错误)
*/
public static void openUrlInBrowser(Context context, String url) {
// 1. 空指针与合法性校验
if (context == null) {
LogUtils.e(TAG, "openUrlInBrowser: Context is null");
return;
}
if (url == null || url.trim().isEmpty()) {
LogUtils.e(TAG, "openUrlInBrowser: Url is null or empty");
showToast(context, "链接不能为空");
return;
}
// 2. 链接格式自动修复(核心新增:处理多斜杠、补全协议头)
String fixedUrl = fixUrlFormat(url.trim());
LogUtils.d(TAG, "openUrlInBrowser: Fixed url from [" + url + "] to [" + fixedUrl + "]");
// 3. 构建隐式意图
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(fixedUrl));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 新任务栈启动
// 4. 检查浏览器可用性
if (intent.resolveActivity(context.getPackageManager()) != null) {
try {
context.startActivity(intent);
} catch (Exception e) {
LogUtils.e(TAG, "openUrlInBrowser: Start activity failed", e);
showToast(context, "打开浏览器失败,请手动复制链接");
}
} else {
LogUtils.e(TAG, "openUrlInBrowser: No browser app found");
showToast(context, "未找到可用的浏览器应用");
}
}
/**
* 工具方法:修复 URL 格式错误
* 1. 补全 http/https 协议头
* 2. 处理协议头后的多斜杠问题(如 https://mmec//path → https://mmec/path
* @param originalUrl 原始 URL
* @return 修复后的 URL
*/
private static String fixUrlFormat(String originalUrl) {
String url = originalUrl;
// 步骤1:补全协议头(优先 https)
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://" + url;
}
// 步骤2:修复协议头后的多斜杠问题
// 匹配 https:// 或 http:// 后的任意数量斜杠,替换为单斜杠
url = url.replaceAll("(?<=https?://)[/]+", "/");
return url;
}
/**
* 工具方法:显示 Toast 提示(确保在主线程执行)
*/
private static void showToast(final Context context, final String message) {
if (context == null || message == null) {
return;
}
if (Looper.myLooper() == Looper.getMainLooper()) {
Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_SHORT).show();
} else {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
});
}
}
}
@@ -1,306 +0,0 @@
package cc.winboll.studio.libaes.utils;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogActivity;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/05/10 10:02
* @Describe 应用活动窗口管理器(改进版)
* 核心能力:多任务窗口管理、Activity栈维护、任务前台恢复、批量关闭、前后Activity切换
* 参考 :android 类似微信小程序多任务窗口 及 设置 TaskDescription 修改 icon 和 label
*/
public class WinBoLLActivityManager {
public static final String TAG = "WinBoLLActivityManager";
public static final String EXTRA_TAG = "EXTRA_TAG";
public enum WinBoLLUI_TYPE { APPLICATION, SERVICE } // 规范命名 大写开头
private GlobalApplication mGlobalApplication;
private static volatile WinBoLLActivityManager sInstance; // 单例命名规范
private final Map<String, IWinBoLLActivity> mActivityListMap; // 私有不可变
private static volatile WinBoLLUI_TYPE sWinBoLLUI_TYPE = WinBoLLUI_TYPE.SERVICE;
// 私有构造 杜绝外部实例化
private WinBoLLActivityManager(@NonNull GlobalApplication application) {
mGlobalApplication = application;
mActivityListMap = new HashMap<>(); // 菱形泛型简化
}
/**
* 初始化管理器(必须在Application onCreate中调用)
*/
public static <T extends GlobalApplication> void init(@NonNull T application) {
if (sInstance == null) {
synchronized (WinBoLLActivityManager.class) {
if (sInstance == null) {
sInstance = new WinBoLLActivityManager(application);
}
}
}
}
/**
* 获取单例(需先调用init初始化,否则抛异常)
*/
@NonNull
public static WinBoLLActivityManager getInstance() {
if (sInstance == null) {
throw new IllegalStateException("WinBoLLActivityManager 未初始化,请先在Application中调用 init()");
}
return sInstance;
}
// ===================== 基础配置 =====================
public static void setWinBoLLUI_TYPE(@NonNull WinBoLLUI_TYPE winBoLLUI_TYPE) {
sWinBoLLUI_TYPE = winBoLLUI_TYPE;
}
@NonNull
public static WinBoLLUI_TYPE getWinBoLLUI_TYPE() {
return sWinBoLLUI_TYPE;
}
// ===================== Activity 增删查 =====================
/**
* 把Activity添加到管理中(自动去重)
*/
public <T extends IWinBoLLActivity> void add(@NonNull T activity) {
String tag = activity.getTag();
if (isActivityActive(tag)) {
LogUtils.d(TAG, String.format("Activity[%s] 已处于活跃状态,无需重复添加", tag));
return;
}
mActivityListMap.put(tag, activity);
LogUtils.d(TAG, String.format("添加Activity%s,当前管理数量:%d", tag, mActivityListMap.size()));
}
/**
* 判断指定Tag的Activity是否活跃
*/
public boolean isActivityActive(@NonNull String tag) {
return mActivityListMap.containsKey(tag) && mActivityListMap.get(tag) != null;
}
/**
* 根据Tag获取Activity(空安全)
*/
@Nullable
public Activity getActivityByTag(@NonNull String tag) {
IWinBoLLActivity winBoLLActivity = mActivityListMap.get(tag);
if (winBoLLActivity == null) return null;
Activity activity = winBoLLActivity.getActivity();
// 过滤已销毁/已结束的Activity
if (activity == null || activity.isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed())) {
registeRemove(winBoLLActivity);
return null;
}
return activity;
}
/**
* 移除指定Activity(销毁时调用)
*/
public <T extends IWinBoLLActivity> boolean registeRemove(@NonNull T iWinBoLLActivity) {
String tag = iWinBoLLActivity.getTag();
if (mActivityListMap.containsKey(tag)) {
mActivityListMap.remove(tag);
LogUtils.d(TAG, String.format("移除Activity%s,剩余管理数量:%d", tag, mActivityListMap.size()));
return true;
}
return false;
}
// ===================== Activity 启动 =====================
/**
* 启动WinBoLLActivity(存在则前台恢复,不存在则新建多任务窗口)
*/
public <T extends IWinBoLLActivity> void startWinBoLLActivity(@NonNull Context context, @NonNull Class<T> clazz) {
if (!resumeActivity(clazz)) {
Intent intent = new Intent(context, clazz);
setMultiTaskFlags(intent);
context.startActivity(intent);
}
}
/**
* 带Intent参数启动WinBoLLActivity
*/
public <T extends IWinBoLLActivity> void startWinBoLLActivity(@NonNull Context context, @NonNull Intent intent, @NonNull Class<T> clazz) {
if (!resumeActivity(clazz)) {
setMultiTaskFlags(intent);
context.startActivity(intent);
}
}
/**
* 启动日志页面(固定多任务模式)
*/
public void startLogActivity(@NonNull Context context) {
Intent intent = new Intent(context, LogActivity.class);
setMultiTaskFlags(intent);
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT); // 分屏相关
context.startActivity(intent);
}
/**
* 设置多任务窗口通用Flags
*/
private void setMultiTaskFlags(@NonNull Intent intent) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
}
// ===================== Activity 前台恢复 =====================
/**
* 根据Activity类 恢复前台(反射获取Tag,需保证无参构造)
*/
public <T extends IWinBoLLActivity> boolean resumeActivity(@NonNull Class<T> clazz) {
try {
T instance = clazz.newInstance();
return resumeActivity(instance.getTag());
} catch (InstantiationException | IllegalAccessException e) {
LogUtils.e(TAG, "恢复Activity失败,类需提供无参构造", e);
}
return false;
}
/**
* 根据Tag 恢复Activity前台
*/
public boolean resumeActivity(@NonNull String tag) {
Activity activity = getActivityByTag(tag);
return activity != null && resumeActivity(activity);
}
/**
* 恢复指定Activity到前台(适配高版本权限)
*/
@SuppressWarnings("deprecation")
public boolean resumeActivity(@NonNull Activity activity) {
if (activity.isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed())) {
return false;
}
try {
ActivityManager am = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
if (am == null) {
LogUtils.w(TAG, "获取ActivityManager失败,无法恢复前台");
return false;
}
// Android 11+ 限制,低版本正常使用
am.moveTaskToFront(activity.getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION);
//ToastUtils.show(String.format("Activity[%s] 已恢复到前台", activity.getClass().getSimpleName()));
LogUtils.d(TAG, String.format("Activity[%s] 已恢复到前台", activity.getClass().getSimpleName()));
return true;
} catch (SecurityException e) {
//ToastUtils.show("恢复Activity前台失败,缺少权限或系统限制 " + e.getMessage());
LogUtils.e(TAG, "恢复Activity前台失败,缺少权限或系统限制", e);
//ToastUtils.show("窗口恢复失败,请手动打开");
return false;
}
}
// ===================== Activity 关闭 =====================
/**
* 结束所有管理的Activity(按UI类型选择关闭策略)
*/
public void finishAll() {
if (mActivityListMap.isEmpty()) {
LogUtils.d(TAG, "当前无管理的Activity,无需结束");
return;
}
LogUtils.d(TAG, String.format("开始结束所有Activity,共%d个", mActivityListMap.size()));
Iterator<Map.Entry<String, IWinBoLLActivity>> iterator = mActivityListMap.entrySet().iterator();
while (iterator.hasNext()) {
IWinBoLLActivity winBoLLActivity = iterator.next().getValue();
Activity activity = winBoLLActivity.getActivity();
if (activity == null) {
iterator.remove();
continue;
}
// 安全关闭,避免重复操作
if (!activity.isFinishing() && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed())) {
if (sWinBoLLUI_TYPE == WinBoLLUI_TYPE.SERVICE) {
activity.finishAndRemoveTask(); // 结束+移除最近任务
} else if (sWinBoLLUI_TYPE == WinBoLLUI_TYPE.APPLICATION) {
activity.finish(); // 仅结束页面
}
}
iterator.remove(); // 移除已处理的项
}
LogUtils.d(TAG, "所有Activity结束完成");
}
/**
* 结束指定Activity,自动恢复上一个Activity前台
*/
public <T extends IWinBoLLActivity> void finish(@NonNull T iWinBoLLActivity) {
Activity currentActivity = iWinBoLLActivity.getActivity();
if (currentActivity == null || currentActivity.isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && currentActivity.isDestroyed())) {
registeRemove(iWinBoLLActivity);
return;
}
// 先获取上一个Activity,再关闭当前
Activity preActivity = getPreActivity(iWinBoLLActivity);
currentActivity.finish();
registeRemove(iWinBoLLActivity); // 关闭后移除管理
// 恢复上一个Activity前台
if (preActivity != null) {
resumeActivity(preActivity);
}
}
/**
* 获取当前Activity的上一个栈内Activity(修复原遍历逻辑错误)
*/
@Nullable
private Activity getPreActivity(@NonNull IWinBoLLActivity currentActivity) {
String currentTag = currentActivity.getTag();
IWinBoLLActivity preWinBoLLActivity = null;
for (Map.Entry<String, IWinBoLLActivity> entry : mActivityListMap.entrySet()) {
String tag = entry.getKey();
if (Objects.equals(tag, currentTag)) {
break; // 找到当前Activity,循环终止,pre即为上一个
}
preWinBoLLActivity = entry.getValue();
}
return preWinBoLLActivity != null ? preWinBoLLActivity.getActivity() : null;
}
// ===================== 调试辅助 =====================
/**
* 打印所有管理的Activity信息(调试用)
*/
public void printActivityListInfo() {
if (mActivityListMap.isEmpty()) {
LogUtils.d(TAG, "当前管理的Activity列表为空");
return;
}
StringBuilder sb = new StringBuilder(String.format("Activity管理列表(总数:%d\n", mActivityListMap.size()));
for (Map.Entry<String, IWinBoLLActivity> entry : mActivityListMap.entrySet()) {
sb.append("Tag: ").append(entry.getKey())
.append(" | Activity: ").append(entry.getValue().getActivity().getClass().getSimpleName())
.append("\n");
}
LogUtils.d(TAG, sb.toString());
}
}
@@ -1,28 +0,0 @@
package cc.winboll.studio.libaes.views;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 01:41:22
* @Describe AButton
*/
import android.content.Context;
import android.util.AttributeSet;
import cc.winboll.studio.libaes.R;
public class AButton extends android.widget.Button {
public static final String TAG = "AButton";
public AButton(Context context) {
super(context);
}
public AButton(Context context, AttributeSet attrs) {
super(context, attrs);
setBackground(context.getDrawable(R.drawable.btn_style));
}
public AButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
@@ -1,45 +0,0 @@
package cc.winboll.studio.libaes.views;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 01:44:27
* @Describe ACard
*/
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import cc.winboll.studio.libaes.R;
public class ACard extends LinearLayout {
public static final String TAG = "ACard";
public ACard(Context context) {
super(context);
}
public ACard(Context context, AttributeSet attrs) {
super(context, attrs);
setPadding(0 + 0 + 2 + 1, 0 + 0 + 2 + 1, 0 + 1 + 3 + 1, 0 + 2 + 3 + 1);
// 获得TypedArray
//TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AToolbar);
// 获得attrs.xml里面的属性值,格式为:名称_属性名,后面是默认值
//int colorBackgroud = a.getColor(R.styleable.ACard_backgroudColor, context.getColor(R.color.colorACardBackgroung));
//int centerColor = a.getColor(R.styleable.AToolbar_centerColor, context.getColor(R.color.colorAToolbarCenterColor));
//int endColor = a.getColor(R.styleable.AToolbar_endColor, context.getColor(R.color.colorAToolbarEndColor));
//float tSize = a.getDimension(R.styleable.CustomView_tSize, 35);
//p.setColor(tColor);
//p.setTextSize(tSize);
//Drawable drawable = context.getDrawable(R.drawable.frame_atoolbar);
setBackground(context.getDrawable(R.drawable.acard_frame_main));
// 返回一个绑定资源结束的信号给资源
//a.recycle();
}
public ACard(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
@@ -1,19 +0,0 @@
package cc.winboll.studio.libaes.views;
/**
* @Author ZhanGSKen
* @Date 2023/05/30 11:30:07
*/
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;
public class ADrawerMenuListView extends ListView {
public static final String TAG = "ADrawerMenuListView";
public ADrawerMenuListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
@@ -1,491 +0,0 @@
package cc.winboll.studio.libaes.views;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libaes.enums.ADsMode;
import cc.winboll.studio.libaes.utils.MimoUtils;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils;
import com.miui.zeus.mimo.sdk.ADParams;
import com.miui.zeus.mimo.sdk.BannerAd;
import com.miui.zeus.mimo.sdk.MimoCustomController;
import com.miui.zeus.mimo.sdk.MimoLocation;
import com.miui.zeus.mimo.sdk.MimoSdk;
import java.util.ArrayList;
import java.util.List;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/18 14:41
* @Describe WinBoLL 横幅广告类
*/
public class ADsBannerView extends LinearLayout {
public static final String TAG = "ADsBannerView";
private String BANNER_POS_ID = "802e356f1726f9ff39c69308bfd6f06a";
private String BANNER_POS_ID_WINBOLL_BETA = "d129ee5a263911f981a6dc7a9802e3e7";
private String BANNER_POS_ID_WINBOLL = "4ec30efdb32271765b9a4efac902828b";
/*
private String BANNER_POS_ID = "802e356f1726f9ff39c69308bfd6f06a";
private String BANNER_POS_ID_WINBOLL_BETA = "802e356f1726f9ff39c69308bfd6f06a";
private String BANNER_POS_ID_WINBOLL = "802e356f1726f9ff39c69308bfd6f06a";
*/
Context mContext;
View mMianView;
SharedPreferences mSharedPreferences;
ViewGroup mContainer;
BannerAd mBannerAd;
List<BannerAd> mAllBanners = new ArrayList<>();
// 新增:主线程Handler,确保广告操作在主线程执行
private Handler mMainHandler;
public ADsBannerView(Context context) {
super(context);
initView(context);
}
public ADsBannerView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public ADsBannerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
public ADsBannerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(context);
}
void initView(Context context) {
this.mContext = context;
initMimoSdk(this.mContext);
// 初始化主线程Handler(关键:确保广告操作在主线程执行)
mMainHandler = new Handler(Looper.getMainLooper());
this.mMianView = inflate(this.mContext, R.layout.view_adsbanner, null);
mContainer = this.mMianView.findViewById(R.id.ads_container);
addView(this.mMianView);
}
public void resumeADs(final Activity activity) {
// 没有设置米盟广告支持就退出
if (ADsControlView.getAdsModeFromStatic(this.mContext) != ADsMode.MIMO_SDK) {
// 2. 释放之前的广告资源
if (mBannerAd != null) {
mBannerAd.destroy();
}
return;
}
// 修复:优化广告请求逻辑(添加生命周期判断 + 主线程执行)
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
if (ADsControlView.getAdsModeFromStatic(this.mContext) == ADsMode.MIMO_SDK) {
LogUtils.i(TAG, "已设置播放米盟广告,正在播放...");
mMainHandler.postDelayed(new Runnable() {
@Override
public void run() {
// 再次校验生命周期,避免延迟执行时Activity已销毁
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
fetchAd(activity);
}
}
}, 1000); // 延迟1秒请求广告,提升页面加载体验
}
}
}
/**
* 释放广告资源(关键:避免内存泄漏和空Context调用)
*/
public void releaseAdResources() {
// 没有设置米盟广告支持就退出
if (ADsControlView.getAdsModeFromStatic(this.mContext) != ADsMode.MIMO_SDK) {
return;
}
LogUtils.d(TAG, "releaseAdResources()");
// 移除Handler回调
if (mMainHandler != null) {
mMainHandler.removeCallbacksAndMessages(null);
}
// 销毁所有广告实例
if (mAllBanners != null && !mAllBanners.isEmpty()) {
for (BannerAd ad : mAllBanners) {
if (ad != null) {
ad.destroy();
}
}
mAllBanners.clear();
}
// 置空当前广告引用
mBannerAd = null;
// 移除广告容器中的视图
if (mContainer != null) {
mContainer.removeAllViews();
}
}
/**
* 显示广告(核心修复:传递安全的Context + 生命周期校验)
*/
private void showAd(final Activity activity) {
// 没有设置米盟广告支持就退出
if (ADsControlView.getAdsModeFromStatic(this.mContext) != ADsMode.MIMO_SDK) {
return;
}
LogUtils.d(TAG, "showAd()");
// 1. 生命周期校验:避免Activity已销毁时操作UI
if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
LogUtils.e(TAG, "showAd: Activity is finishing or destroyed");
return;
}
// 2. 非空校验:广告实例和容器
if (mBannerAd == null || mContainer == null) {
LogUtils.e(TAG, "showAd: BannerAd or Container is null");
return;
}
// 3. 创建广告容器(使用ApplicationContext避免内存泄漏)
final FrameLayout container = new FrameLayout(activity.getApplicationContext());
container.setPadding(0, 0, 0, MimoUtils.dpToPx(activity, 10));
mContainer.addView(container, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
));
// if (mIsBiddingWin) {
// mBannerAd.setPrice(getPrice());
// }
// 4. 显示广告:传递ApplicationContext,避免Activity Context失效
mBannerAd.showAd(activity, container, new BannerAd.BannerInteractionListener() {
@Override
public void onAdClick() {
LogUtils.d(TAG, "onAdClick");
}
@Override
public void onAdShow() {
LogUtils.d(TAG, "onAdShow");
}
@Override
public void onAdDismiss() {
LogUtils.d(TAG, "onAdDismiss");
// 修复:移除容器时校验Activity状态
if (activity != null && !activity.isFinishing() && !activity.isDestroyed() && mContainer != null) {
mContainer.removeView(container);
}
}
@Override
public void onRenderSuccess() {
LogUtils.d(TAG, "onRenderSuccess");
}
@Override
public void onRenderFail(int code, String msg) {
LogUtils.e(TAG, "onRenderFail errorCode " + code + " errorMsg " + msg);
// 修复:渲染失败时移除容器
if (activity != null && !activity.isFinishing() && !activity.isDestroyed() && mContainer != null) {
mContainer.removeView(container);
}
}
});
}
/**
* 请求广告(核心修复:Context安全校验 + 异常捕获 + 资源管理)
*/
private void fetchAd(final Activity activity) {
// 没有设置米盟广告支持就退出
if (ADsControlView.getAdsModeFromStatic(this.mContext) != ADsMode.MIMO_SDK) {
return;
}
LogUtils.d(TAG, "fetchAd()");
// 1. 双重校验:Activity未销毁 + Context非空
if (activity == null || activity.isFinishing() || activity.isDestroyed() || activity.getApplicationContext() == null) {
LogUtils.e(TAG, "fetchAd: Invalid Context or Activity state");
return;
}
// 2. 释放之前的广告资源,避免内存泄漏
if (mBannerAd != null) {
mBannerAd.destroy();
}
// 3. 初始化广告(使用ApplicationContext,避免Activity Context失效)
try {
mBannerAd = new BannerAd();
mAllBanners.add(mBannerAd);
} catch (Exception e) {
LogUtils.e(TAG, "fetchAd: Init BannerAd failed", e);
return;
}
// 4. 设置下载监听
mBannerAd.setDownLoadListener(new BannerAd.BannerDownloadListener() {
@Override
public void onDownloadStarted() {
LogUtils.d(TAG, "onDownloadStarted");
}
@Override
public void onDownloadPaused() {
LogUtils.d(TAG, "onDownloadPaused");
}
@Override
public void onDownloadFailed(int errorCode) {
String msg = "onDownloadFailed, errorCode = " + errorCode;
LogUtils.d(TAG, msg);
//ToastUtils.show(msg);
}
@Override
public void onDownloadFinished() {
LogUtils.d(TAG, "onDownloadFinished");
}
@Override
public void onDownloadProgressUpdated(int progress) {
LogUtils.d(TAG, "onDownloadProgressUpdated " + progress + "%");
}
@Override
public void onInstallFailed(int errorCode) {
LogUtils.d(TAG, "onInstallFailed, errorCode = " + errorCode);
}
@Override
public void onInstallStart() {
LogUtils.d(TAG, "onInstallStart");
}
@Override
public void onInstallSuccess() {
LogUtils.d(TAG, "onInstallSuccess");
}
@Override
public void onDownloadCancel() {
LogUtils.d(TAG, "onDownloadCancel");
}
});
// 5. 构建广告参数并请求
String currentAD_ID = getAD_ID();
LogUtils.d(TAG, String.format("currentAD_ID = %s", currentAD_ID));
ADParams params = new ADParams.Builder().setUpId(currentAD_ID).build();
mBannerAd.loadAd(params, new BannerAd.BannerLoadListener() {
@Override
public void onBannerAdLoadSuccess() {
LogUtils.d(TAG, "onBannerAdLoadSuccess()");
// 修复:广告加载成功后校验Activity状态
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
showAd(activity);
//ToastUtils.show("showAd()");
}
}
@Override
public void onAdLoadFailed(int errorCode, String errorMsg) {
String msg = "onAdLoadFailed: errorCode = " + errorCode + ", errorMsg = " + errorMsg;
LogUtils.d(TAG, msg);
removeAllBanners();
}
});
}
void removeAllBanners() {
// 没有设置米盟广告支持就退出
if (ADsControlView.getAdsModeFromStatic(this.mContext) != ADsMode.MIMO_SDK) {
return;
}
// 修复:加载失败时移除当前广告实例
if (mAllBanners.contains(mBannerAd)) {
mAllBanners.remove(mBannerAd);
}
mBannerAd.destroy();
mBannerAd = null;
}
/**
* 根据当前秒数获取广告ID(原逻辑保留)
*/
private String getAD_ID() {
long currentSecond = System.currentTimeMillis() / 1000;
return (currentSecond % 2 == 0) ? BANNER_POS_ID :
(GlobalApplication.isDebugging() ? BANNER_POS_ID_WINBOLL_BETA : BANNER_POS_ID_WINBOLL);
}
/**
* 获取广告价格(原逻辑保留,添加空指针校验)
*/
// private long getPrice() {
// if (mBannerAd == null) {
// return 0;
// }
// Map<String, Object> map = mBannerAd.getMediaExtraInfo();
// if (map == null || map.isEmpty() || !map.containsKey("price")) {
// LogUtils.w(TAG, "getPrice: media extra info is null or no price key");
// return 0;
// }
// Object priceObj = map.get("price");
// if (priceObj instanceof Long) {
// return (Long) priceObj;
// } else if (priceObj instanceof Integer) {
// return ((Integer) priceObj).longValue();
// } else {
// LogUtils.e(TAG, "getPrice: price type is invalid");
// return 0;
// }
// }
/**
* 显示隐私协议弹窗(原逻辑保留,优化Context使用)
*/
// private void showPrivacy() {
// // 校验Activity状态,避免弹窗泄露
// if (getActivity() == null || getActivity().isFinishing() || getActivity().isDestroyed()) {
// return;
// }
// ADsMode adsMode = ADsControlView.getAdsModeFromStatic(this.mContext);
// if (adsMode == ADsMode.STANDALONE) {
// ADsControlView.updateAdsModeByStatic(this.mContext, ADsMode.STANDALONE);
// LogUtils.i(TAG, "单机模式,广告已处于不可用状态...");
// Toast.makeText(getActivity().getApplicationContext(), "单机模式,广告已处于不可用状态...", Toast.LENGTH_SHORT).show();
// return;
// } else if (adsMode == ADsMode.MIMO_SDK) {
// ADsControlView.updateAdsModeByStatic(this.mContext, ADsMode.MIMO_SDK);
// LogUtils.i(TAG, "米盟广告SDK支持模式,现在初始化SDK...");
// initMimoSdk();
// return;
// }
// else {
// LogUtils.i(TAG, "开始弹出隐私协议...");
// AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// builder.setTitle("用户须知");
// builder.setMessage("小米广告SDK隐私政策: https://dev.mi.com/distribute/doc/details?pId=1688, 请复制到浏览器查看");
// builder.setIcon(R.drawable.ic_launcher);
// builder.setCancelable(false); // 点击对话框以外的区域不消失
// builder.setPositiveButton("同意", new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
// getSharedPreferences().edit()
// .putString(PRIVACY_VALUE, String.valueOf(1))
// .apply();
// initMimoSdk();
// dialog.dismiss();
// }
// });
// builder.setNegativeButton("拒绝", new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
// getSharedPreferences().edit()
// .putString(PRIVACY_VALUE, String.valueOf(0))
// .apply();
// dialog.dismiss();
// }
// });
// AlertDialog dialog = builder.create();
//
// // 配置弹窗位置(底部全屏)
// Window window = dialog.getWindow();
// if (window != null) {
// window.setGravity(Gravity.BOTTOM);
// WindowManager m = getActivity().getWindowManager();
// Display d = m.getDefaultDisplay();
// WindowManager.LayoutParams p = window.getAttributes();
// p.width = d.getWidth();
// window.setAttributes(p);
// }
// dialog.show();
// }
// }
/**
* 初始化米盟SDK(核心修复:传递ApplicationContext + 异常捕获)
*/
private void initMimoSdk(Context context) {
// 1. 安全获取ApplicationContext,避免Activity Context失效
Context appContext = context.getApplicationContext();
if (appContext == null) {
LogUtils.e(TAG, "initMimoSdk: ApplicationContext is null");
return;
}
// 2. 初始化SDK,捕获异常避免崩溃
try {
MimoSdk.init(appContext, new MimoCustomController() {
@Override
public boolean isCanUseLocation() {
return true;
}
@Override
public MimoLocation getMimoLocation() {
return null;
}
@Override
public boolean isCanUseWifiState() {
return true;
}
@Override
public boolean alist() {
return true;
}
}, new MimoSdk.InitCallback() {
@Override
public void success() {
LogUtils.d(TAG, "MimoSdk init success");
}
@Override
public void fail(int code, String msg) {
LogUtils.e(TAG, "MimoSdk init fail, code=" + code + ",msg=" + msg);
}
});
MimoSdk.setDebugOn(true);
} catch (Exception e) {
LogUtils.e(TAG, "initMimoSdk: init failed", e);
}
}
/**
* 获取SharedPreferences实例(原逻辑保留,添加空指针校验)
*/
// SharedPreferences getSharedPreferences() {
//// if (mSharedPreferences == null) {
//// // 修复:使用ApplicationContext获取SharedPreferences,避免Activity Context泄露
//// Context appContext = getActivity().getApplicationContext();
//// if (appContext != null) {
//// mSharedPreferences = appContext.getSharedPreferences(PRIVACY_FILE, Context.MODE_PRIVATE);
//// } else {
//// LogUtils.e(TAG, "getSharedPreferences: ApplicationContext is null");
//// // 降级方案:若ApplicationContext为空,使用Activity Context(仅作兼容)
//// mSharedPreferences = getActivity().getSharedPreferences(PRIVACY_FILE, Context.MODE_PRIVATE);
//// }
//// }
// return mSharedPreferences;
// }
}
@@ -1,633 +0,0 @@
package cc.winboll.studio.libaes.views;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.Html;
import android.util.AttributeSet;
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.PopupWindow;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libaes.enums.ADsMode;
import cc.winboll.studio.libaes.enums.PrivacyAgreeStatus;
import cc.winboll.studio.libaes.utils.WebUtils;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
import com.miui.zeus.mimo.sdk.MimoCustomController;
import com.miui.zeus.mimo.sdk.MimoLocation;
import com.miui.zeus.mimo.sdk.MimoSdk;
import java.lang.reflect.Field;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/26 17:51
* @LastEditTime 2026/01/08 11:00:00 HKT
* @Describe 广告模式控制控件(Java 7 兼容,云宝物语模式)
* 核心修改:将PopupMenu锚点绑定到view_popmenu_anchor_point控件,菜单精准显示在锚点位置
*/
public class ADsControlView extends LinearLayout {
public static final String TAG = "ADsControlView";
// SP存储配置
private static final String SP_NAME = "ads_control_config";
private static final String KEY_SELECTED_MODE = "selected_ads_mode";
ADsMode mADsMode;
private static final String PRIVACY_VALUE = "privacy_value";
PrivacyAgreeStatus mPrivacyAgreeStatus;
// Handler消息标识
private static final int MSG_UPDATE_MODE = 1001;
// 控件引用
private RadioGroup rgADsMode;
private RadioButton rbStandalone;
private RadioButton rbMimoSDK;
private RadioButton rbStoreQrcode;
private RelativeLayout rlWinbollStore;
private ImageView ivWinbollStoreQrcode;
// 新增:锚点控件引用
private TextView viewPopmenuAnchorPoint;
// 外部监听、SP实例、Handler实例
private OnAdsModeSelectedListener listener;
private SharedPreferences sharedPreferences;
private InternalHandler mHandler;
private Context mContext;
// 静态列表:存储所有已创建的控件实例
private static final java.util.List<ADsControlView> sControlViews = new java.util.ArrayList<ADsControlView>();
// 常量定义
private static final String WECHAT_STORE_URL = "https://store.weixin.qq.com/shop/b/XhrPkZgoeHo4zug";
private static final int MENU_ITEM_OPEN_STORE = 1001;
// 构造方法(Java 7 兼容)
public ADsControlView(Context context) {
super(context);
initView(context);
}
public ADsControlView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
@SuppressWarnings("deprecation")
public ADsControlView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
public void setPrivacyAgreeStatus(PrivacyAgreeStatus privacyAgreeStatus) {
this.mPrivacyAgreeStatus = privacyAgreeStatus;
sharedPreferences.edit().putString(PRIVACY_VALUE, this.mPrivacyAgreeStatus.name()).apply();
}
public PrivacyAgreeStatus getPrivacyAgreeStatus() {
String privacyAgreeStatusStr = sharedPreferences.getString(PRIVACY_VALUE, PrivacyAgreeStatus.UN_SIGNED.name());
PrivacyAgreeStatus privacyAgreeStatus = PrivacyAgreeStatus.fromString(privacyAgreeStatusStr);
return privacyAgreeStatus;
}
public void setADsMode(ADsMode mADsMode) {
this.mADsMode = mADsMode;
sharedPreferences.edit().putString(KEY_SELECTED_MODE, this.mADsMode.name()).apply();
updateStoreQrcodeLayoutVisibility(mADsMode);
}
public ADsMode getADsMode() {
String savedModeStr = sharedPreferences.getString(KEY_SELECTED_MODE, ADsMode.STANDALONE.name());
mADsMode = ADsMode.fromValue(savedModeStr);
return mADsMode;
}
/**
* 初始化视图、SP、Handler
*/
private void initView(final Context context) {
this.mContext = context;
// 加载布局
LayoutInflater.from(context).inflate(R.layout.view_adscontrol, this, true);
// 初始化SP
sharedPreferences = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
// 绑定控件
rgADsMode = (RadioGroup) findViewById(R.id.rg_ads_mode);
rbStandalone = (RadioButton) findViewById(R.id.rb_standalone);
rbMimoSDK = (RadioButton) findViewById(R.id.rb_mimo_sdk);
rbStoreQrcode = (RadioButton) findViewById(R.id.rb_store_qrcode);
rlWinbollStore = (RelativeLayout) findViewById(R.id.rl_winboll_store);
ivWinbollStoreQrcode = (ImageView) findViewById(R.id.iv_winboll_store);
// 绑定锚点控件
viewPopmenuAnchorPoint = (TextView) findViewById(R.id.view_popmenu_anchor_point);
// 初始化Handler
mHandler = new InternalHandler(Looper.getMainLooper());
// 核心修改:初始化图片的点击和长按事件(锚点改为view_popmenu_anchor_point
initImageViewClickAndLongClick();
// 注册控件实例
registerControlView(this);
// 从SP读取初始模式
setSelectedMode(getADsMode());
// 单选组选择事件监听
rgADsMode.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId == R.id.rb_standalone) {
setADsMode(ADsMode.STANDALONE);
if (listener != null) listener.onModeSelected(ADsMode.STANDALONE);
} else if (checkedId == R.id.rb_mimo_sdk) {
handlePrivacyLogic((Activity) context, PrivacyAgreeStatus.UN_SIGNED, new OnPrivacyChangeListener() {
@Override
public void onAgreePrivacy() {
setADsMode(ADsMode.MIMO_SDK);
if (listener != null) listener.onModeSelected(ADsMode.MIMO_SDK);
}
@Override
public void onDisagreePrivacy() {
setADsMode(ADsMode.STANDALONE);
setSelectedMode(ADsMode.STANDALONE);
if (listener != null) listener.onModeSelected(ADsMode.STANDALONE);
}
});
} else if (checkedId == R.id.rb_store_qrcode) {
setADsMode(ADsMode.STORE_QRCODE);
if (listener != null) listener.onModeSelected(ADsMode.STORE_QRCODE);
}
}
});
}
/**
* 初始化图片的点击和长按事件
* 核心:将PopupMenu锚点绑定到view_popmenu_anchor_point控件
*/
private void initImageViewClickAndLongClick() {
if (ivWinbollStoreQrcode == null || viewPopmenuAnchorPoint == null) {
LogUtils.e(TAG, "initImageViewClickAndLongClick: 控件引用为空");
return;
}
// 1. 点击事件:简化为提示信息
ivWinbollStoreQrcode.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ToastUtils.show("长按图片可打开微信小店");
LogUtils.d(TAG, "图片点击:提示用户长按打开微信小店");
}
});
// 2. 长按事件:锚点改为view_popmenu_anchor_point
ivWinbollStoreQrcode.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
// 计算锚点控件的屏幕坐标(用于菜单位置微调)
int[] anchorLocation = new int[2];
viewPopmenuAnchorPoint.getLocationOnScreen(anchorLocation);
final int anchorX = anchorLocation[0];
final int anchorY = anchorLocation[1];
// 创建PopupMenu,锚点绑定到view_popmenu_anchor_point
PopupMenu popupMenu = new PopupMenu(mContext, viewPopmenuAnchorPoint);
// 设置菜单重力:相对锚点居中显示
popupMenu.setGravity(Gravity.CENTER);
Menu menu = popupMenu.getMenu();
menu.add(Menu.NONE, MENU_ITEM_OPEN_STORE, Menu.NONE, "打开微信小店");
// 设置菜单点击事件
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == MENU_ITEM_OPEN_STORE) {
WebUtils.openUrlInBrowser(mContext, WECHAT_STORE_URL);
return true;
}
return false;
}
});
try {
// 反射获取PopupWindow,微调菜单位置(可选)
Field popupField = PopupMenu.class.getDeclaredField("mPopup");
popupField.setAccessible(true);
Object popupObject = popupField.get(popupMenu);
if (popupObject instanceof PopupWindow) {
final PopupWindow popupWindow = (PopupWindow) popupObject;
popupWindow.setAnimationStyle(0); // 关闭默认动画
// 延迟微调菜单位置(确保布局测量完成)
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
int menuX = anchorX + viewPopmenuAnchorPoint.getWidth() / 2 - popupWindow.getWidth() / 2;
int menuY = anchorY + viewPopmenuAnchorPoint.getHeight() / 2 - popupWindow.getHeight() / 2;
if (!popupWindow.isShowing()) {
popupWindow.showAtLocation(viewPopmenuAnchorPoint, Gravity.NO_GRAVITY, menuX, menuY);
}
}
}, 30);
}
} catch (NoSuchFieldException | IllegalAccessException e) {
LogUtils.e(TAG, "反射获取PopupWindow失败", e);
}
// 显示菜单
popupMenu.show();
LogUtils.d(TAG, "长按图片,菜单锚点为view_popmenu_anchor_point");
return true;
}
});
// 设置控件可交互标识
ivWinbollStoreQrcode.setClickable(true);
ivWinbollStoreQrcode.setFocusable(true);
ivWinbollStoreQrcode.setLongClickable(true);
viewPopmenuAnchorPoint.setClickable(false); // 锚点控件不可点击
viewPopmenuAnchorPoint.setLongClickable(false);
}
/**
* 从ImageView中提取Bitmap(保留方法,无实际调用)
*/
private Bitmap getBitmapFromImageView(ImageView imageView) {
Drawable drawable = imageView.getDrawable();
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
return null;
}
/**
* 压缩Bitmap(备用方法,无实际调用)
*/
private Bitmap compressBitmapBySize(Bitmap src, int maxWidth, int maxHeight) {
if (src == null) return null;
int width = src.getWidth();
int height = src.getHeight();
float scale = Math.min((float) maxWidth / width, (float) maxHeight / height);
int newWidth = (int) (width * scale);
int newHeight = (int) (height * scale);
return Bitmap.createScaledBitmap(src, newWidth, newHeight, true);
}
/**
* 计算Bitmap采样率(备用方法,无实际调用)
*/
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
/**
* 从ImageView反射获取资源ID(备用方法,无实际调用)
*/
private int getResIdFromImageView(ImageView imageView) {
try {
Field field = ImageView.class.getDeclaredField("mSrcResource");
field.setAccessible(true);
return field.getInt(imageView);
} catch (NoSuchFieldException | IllegalAccessException e) {
LogUtils.e(TAG, "getResIdFromImageView: 反射失败", e);
return 0;
}
}
/**
* 更新二维码布局显示状态
*/
private void updateStoreQrcodeLayoutVisibility(ADsMode mode) {
if (rlWinbollStore == null) return;
rlWinbollStore.setVisibility(mode == ADsMode.STORE_QRCODE ? View.VISIBLE : View.GONE);
}
/**
* 清理SP中的隐私协议状态
*/
public static void cleanPrivacyStatus(Context context) {
if (context == null) {
LogUtils.e(TAG, "cleanPrivacyStatus: Context is null");
return;
}
SharedPreferences sp = getPrivacySharedPreferences(context);
sp.edit().remove(PRIVACY_VALUE).apply();
LogUtils.i(TAG, "隐私协议状态清理成功");
ToastUtils.show("隐私协议状态已清理");
}
/**
* 获取隐私协议SP实例
*/
private static SharedPreferences getPrivacySharedPreferences(Context context) {
Context appContext = context.getApplicationContext();
if (appContext != null) {
return appContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
}
return context.getSharedPreferences(PRIVACY_VALUE, Context.MODE_PRIVATE);
}
/**
* 处理隐私协议逻辑
*/
private static void handlePrivacyLogic(final Activity activity, PrivacyAgreeStatus privacyAgreeStatus, final OnPrivacyChangeListener onPrivacyChangeListener) {
if (privacyAgreeStatus == PrivacyAgreeStatus.REJECTED) {
Toast.makeText(activity.getApplicationContext(), "已拒绝隐私协议,广告已处于不可用状态", Toast.LENGTH_SHORT).show();
return;
} else if (privacyAgreeStatus == PrivacyAgreeStatus.AGREED) {
initMimoSdkStatic(activity.getApplicationContext());
return;
} else {
AlertDialog dialog = createPrivacyDialog(activity, onPrivacyChangeListener);
Window window = dialog.getWindow();
if (window != null) {
window.setGravity(Gravity.BOTTOM);
WindowManager m = activity.getWindowManager();
Display d = m.getDefaultDisplay();
WindowManager.LayoutParams p = window.getAttributes();
p.width = d.getWidth();
window.setAttributes(p);
}
dialog.show();
}
}
/**
* 初始化米盟SDK
*/
private static void initMimoSdkStatic(Context appContext) {
if (appContext == null) return;
try {
MimoSdk.init(appContext, new MimoCustomController() {
@Override
public boolean isCanUseLocation() {
return true;
}
@Override
public MimoLocation getMimoLocation() {
return null;
}
@Override
public boolean isCanUseWifiState() {
return true;
}
@Override
public boolean alist() {
return true;
}
}, new MimoSdk.InitCallback() {
@Override
public void success() {
LogUtils.d(TAG, "米盟SDK初始化成功");
}
@Override
public void fail(int code, String msg) {
LogUtils.e(TAG, "米盟SDK初始化失败:" + code + ", " + msg);
}
});
MimoSdk.setDebugOn(true);
} catch (Exception e) {
LogUtils.e(TAG, "米盟SDK初始化异常", e);
}
}
/**
* 静态方法:更新SP中的模式
*/
public static void updateAdsModeByStatic(Context context, ADsMode mode) {
if (context == null || mode == null) return;
SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
sp.edit().putString(KEY_SELECTED_MODE, mode.name()).apply();
InternalHandler.sendUpdateModeMessage(mode);
}
/**
* 静态方法:读取SP中的模式
*/
public static ADsMode getAdsModeFromStatic(Context context) {
if (context == null) return ADsMode.STANDALONE;
SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
String savedModeStr = sp.getString(KEY_SELECTED_MODE, ADsMode.STANDALONE.name());
return ADsMode.fromValue(savedModeStr);
}
/**
* 注册控件实例
*/
private static void registerControlView(ADsControlView view) {
synchronized (sControlViews) {
if (!sControlViews.contains(view)) {
sControlViews.add(view);
}
}
}
/**
* 移除控件实例
*/
private static void unregisterControlView(ADsControlView view) {
synchronized (sControlViews) {
sControlViews.remove(view);
}
}
/**
* 设置选中模式
*/
private void setSelectedMode(final ADsMode mode) {
final ADsMode mode2 = (mode == null) ? ADsMode.STANDALONE : mode;
if (Looper.myLooper() == Looper.getMainLooper()) {
if (mode2 == ADsMode.STANDALONE) {
rbStandalone.setChecked(true);
} else if (mode2 == ADsMode.MIMO_SDK) {
rbMimoSDK.setChecked(true);
} else if (mode2 == ADsMode.STORE_QRCODE) {
rbStoreQrcode.setChecked(true);
}
updateStoreQrcodeLayoutVisibility(mode2);
} else {
mHandler.post(new Runnable() {
@Override
public void run() {
setSelectedMode(mode2);
}
});
}
}
/**
* 获取选中模式
*/
public ADsMode getSelectedMode() {
int checkedId = rgADsMode.getCheckedRadioButtonId();
if (checkedId == R.id.rb_mimo_sdk) {
return ADsMode.MIMO_SDK;
} else if (checkedId == R.id.rb_store_qrcode) {
return ADsMode.STORE_QRCODE;
} else {
return ADsMode.STANDALONE;
}
}
/**
* 设置外部监听
*/
public void setOnAdsModeSelectedListener(OnAdsModeSelectedListener listener) {
this.listener = listener;
}
/**
* 内部Handler类
*/
private static class InternalHandler extends Handler {
static volatile InternalHandler _InternalHandler;
public InternalHandler(Looper looper) {
super(looper);
_InternalHandler = this;
}
public static void sendUpdateModeMessage(ADsMode mode) {
if (mode == null || _InternalHandler == null) return;
Message msg = _InternalHandler.obtainMessage();
msg.what = MSG_UPDATE_MODE;
msg.obj = mode;
_InternalHandler.sendMessage(msg);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == MSG_UPDATE_MODE) {
ADsMode mode = (ADsMode) msg.obj;
if (mode == null) return;
synchronized (sControlViews) {
for (ADsControlView view : sControlViews) {
if (view != null && view.isShown() && view.isAttachedToWindow()) {
view.setSelectedMode(mode);
view.updateStoreQrcodeLayoutVisibility(mode);
}
}
}
}
}
}
/**
* 生命周期:控件销毁
*/
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
}
unregisterControlView(this);
}
/**
* 外部监听接口
*/
public interface OnAdsModeSelectedListener {
void onModeSelected(ADsMode selectedMode);
}
/**
* 隐私协议监听接口
*/
public interface OnPrivacyChangeListener {
void onAgreePrivacy();
void onDisagreePrivacy();
}
/**
* 创建隐私协议对话框
*/
private static AlertDialog createPrivacyDialog(final Activity activity, final OnPrivacyChangeListener onPrivacyChangeListener) {
View dialogView = LayoutInflater.from(activity).inflate(R.layout.dialog_privacy_agreement, null);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setView(dialogView).setCancelable(false);
final AlertDialog dialog = builder.create();
final TextView tvPrivacyUrl = (TextView) dialogView.findViewById(R.id.tv_privacy_url);
Button btnAgree = (Button) dialogView.findViewById(R.id.btn_agree);
Button btnDisagree = (Button) dialogView.findViewById(R.id.btn_disagree);
tvPrivacyUrl.setText(Html.fromHtml("<u>" + tvPrivacyUrl.getText().toString() + "</u>"));
tvPrivacyUrl.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String url = tvPrivacyUrl.getText().toString().trim();
ToastUtils.show("隐私协议链接:" + url);
}
});
tvPrivacyUrl.setClickable(true);
tvPrivacyUrl.setFocusable(true);
btnAgree.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onPrivacyChangeListener != null) {
onPrivacyChangeListener.onAgreePrivacy();
}
dialog.dismiss();
}
});
btnDisagree.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onPrivacyChangeListener != null) {
onPrivacyChangeListener.onDisagreePrivacy();
}
dialog.dismiss();
}
});
return dialog;
}
}
@@ -1,108 +0,0 @@
package cc.winboll.studio.libaes.views;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 01:46:30
* @Describe AOneHundredPercantClickToCommitSeekBar
*/
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.SeekBar;
import cc.winboll.studio.libappbase.LogUtils;
public class AOHPCTCSeekBar extends SeekBar {
public static final String TAG = "AOHPCTCSeekBar";
volatile int thumbWidth = 1;
volatile int progressBarWidth = 1;
// 设置按钮模糊右边边缘像素
volatile int blurRightDP = 1;
// 是否从起点拉动的标志
volatile boolean isStartSeek = false;
// 外部接口对象,确定事件提交会调用该对象的方法
OnOHPCListener mOnOHPCListener;
public void setBlurRightDP(int blurRight) {
this.blurRightDP = blurRight;
}
public void setOnOHPCListener(OnOHPCListener listener) {
mOnOHPCListener = listener;
}
public interface OnOHPCListener {
abstract void onOHPCommit();
}
public AOHPCTCSeekBar(Context context) {
super(context);
initView(context);
}
public AOHPCTCSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public AOHPCTCSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
void initView(Context context) {
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (thumbWidth + blurRightDP > event.getX() && event.getX() > 0) {
getParent().requestDisallowInterceptTouchEvent(true);
isStartSeek = true;
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (isStartSeek) {
super.dispatchTouchEvent(event);
}
} else if (event.getAction() == MotionEvent.ACTION_UP
|| event.getAction() == MotionEvent.ACTION_CANCEL) {
getParent().requestDisallowInterceptTouchEvent(false);
if (getProgress() == progressBarWidth) {
mOnOHPCListener.onOHPCommit();
}
// 重置控件状态
setProgress(0);
isStartSeek = false;
}
return true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
//int height = MeasureSpec.getSize(heightMeasureSpec);
//LogUtils.d(TAG, String.format("width %d height %d", width, height));
// 获取SeekBar的图标宽度
Drawable thumbDrawable = getThumb();
if (thumbDrawable != null) {
// 获取图标宽度
thumbWidth = thumbDrawable.getIntrinsicWidth();
}
// 获取进度条宽度
progressBarWidth = width;
//LogUtils.d(TAG, String.format("thumbWidth %d progressBarWidth %d", thumbWidth, progressBarWidth));
// 设置图标位置
setThumbOffset(0);
// 设置进度条刻度
setMax(progressBarWidth);
}
}
@@ -1,43 +0,0 @@
package cc.winboll.studio.libaes.views;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 01:49:23
* @Describe AOneHundredPercantClickToStartCard
*/
import android.content.Context;
import android.util.AttributeSet;
public class AOHPCTSCard extends ACard {
public static final String TAG = "AOHPCTSCard";
public AOHPCTSCard(Context context) {
super(context);
}
public AOHPCTSCard(Context context, AttributeSet attrs) {
super(context, attrs);
//setPadding(0 + 0 + 2 + 1, 0 + 0 + 2 + 1, 0 + 1 + 3 + 1, 0 + 2 + 3 + 1);
// 获得TypedArray
//TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AToolbar);
// 获得attrs.xml里面的属性值,格式为:名称_属性名,后面是默认值
//int colorBackgroud = a.getColor(R.styleable.ACard_backgroudColor, context.getColor(R.color.colorACardBackgroung));
//int centerColor = a.getColor(R.styleable.AToolbar_centerColor, context.getColor(R.color.colorAToolbarCenterColor));
//int endColor = a.getColor(R.styleable.AToolbar_endColor, context.getColor(R.color.colorAToolbarEndColor));
//float tSize = a.getDimension(R.styleable.CustomView_tSize, 35);
//p.setColor(tColor);
//p.setTextSize(tSize);
//Drawable drawable = context.getDrawable(R.drawable.frame_atoolbar);
//setBackground(context.getDrawable(R.drawable.acard_frame_main));
// 返回一个绑定资源结束的信号给资源
//a.recycle();
}
public AOHPCTSCard(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
@@ -1,93 +0,0 @@
package cc.winboll.studio.libaes.views;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 01:54:40
* @Describe ASupportToolbar
*/
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libaes.R;
import android.graphics.drawable.Drawable;
import androidx.core.content.ContextCompat;
import android.graphics.PorterDuff;
public class ASupportToolbar extends Toolbar {
public static final String TAG = "ASupportToolbar";
int mTitleTextColor;
int mStartColor;
int mCenterColor;
int mEndColor;
LayerDrawable ld;
GradientDrawable[] array = new GradientDrawable[3];
//private GradientDrawable gradientDrawable;
public ASupportToolbar(Context context) {
super(context);
}
public ASupportToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ASupportToolbar, R.attr.aSupportToolbar, 0);
mTitleTextColor = a.getColor(R.styleable.ASupportToolbar_attrASupportToolbarTitleTextColor, Color.GREEN);
mStartColor = a.getColor(R.styleable.ASupportToolbar_attrASupportToolbarStartColor, Color.BLUE);
mCenterColor = a.getColor(R.styleable.ASupportToolbar_attrASupportToolbarCenterColor, Color.RED);
mEndColor = a.getColor(R.styleable.ASupportToolbar_attrASupportToolbarEndColor, Color.YELLOW);
// 返回一个绑定资源结束的信号给资源
a.recycle();
notifyColorChange();
}
void notifyColorChange() {
// 工具栏描边
int nStroke = 5;
TypedArray taBorder = getContext().obtainStyledAttributes(new int[]{R.attr.borderCornerRadius});
float cornerRadius = taBorder.getDimension(0, 6 * getResources().getDisplayMetrics().density);
taBorder.recycle();
//分别为开始颜色,中间夜色,结束颜色
int colors0[] = { mEndColor , mCenterColor, mStartColor};
GradientDrawable gradientDrawable0;
array[2] = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors0);
gradientDrawable0 = array[2];
gradientDrawable0.setShape(GradientDrawable.RECTANGLE);
gradientDrawable0.setColors(colors0); //添加颜色组
gradientDrawable0.setGradientType(GradientDrawable.LINEAR_GRADIENT);//设置线性渐变
gradientDrawable0.setCornerRadius(cornerRadius);
int colors1[] = { mCenterColor , mCenterColor, mCenterColor };
GradientDrawable gradientDrawable1;
array[1] = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors1);
gradientDrawable1 = array[1];
gradientDrawable1.setShape(GradientDrawable.RECTANGLE);
gradientDrawable1.setColors(colors1); //添加颜色组
gradientDrawable1.setGradientType(GradientDrawable.LINEAR_GRADIENT);//设置线性渐变
gradientDrawable1.setCornerRadius(cornerRadius);
int colors2[] = { mEndColor, mCenterColor, mStartColor };
GradientDrawable gradientDrawable2;
array[0] = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors2);
gradientDrawable2 = array[0];
gradientDrawable2.setShape(GradientDrawable.RECTANGLE);
gradientDrawable2.setColors(colors2); //添加颜色组
gradientDrawable2.setGradientType(GradientDrawable.LINEAR_GRADIENT);//设置线性渐变
gradientDrawable2.setCornerRadius(cornerRadius);
ld = new LayerDrawable(array); //参数为上面的Drawable数组
ld.setLayerInset(2, nStroke * 2, nStroke * 2, getWidth() + nStroke * 2, getHeight() + nStroke * 2);
ld.setLayerInset(1, nStroke, nStroke, getWidth() + nStroke, getHeight() + nStroke);
ld.setLayerInset(0, 0, 0, getWidth(), getHeight());
setBackgroundDrawable(ld);
setTitleTextColor(mTitleTextColor);
setSubtitleTextColor(mTitleTextColor);
}
}
@@ -1,55 +0,0 @@
package cc.winboll.studio.libaes.views;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 01:56:38
* @Describe ATickProgressBar
*/
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.ProgressBar;
public class ATickProgressBar extends ProgressBar {
public static final String TAG = "ATickProgressBar";
int mnStepDistantce = 100 / 10;
int mnProgress = 0;
public ATickProgressBar(Context context) {
super(context);
}
public ATickProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
setProgress(50);
}
public int stepOnTick(int nStepDistantce) {
if (mnProgress < 100) {
int nProgressOld = mnProgress;
mnProgress += nStepDistantce;
new Handler().postDelayed(new Runnable(){
@Override
public void run() {
;
}
}, 1000);
return nProgressOld;
} else {
return mnProgress;
}
}
/*@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int nWidthSize = MeasureSpec.getSize(widthMeasureSpec);
int nHeightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(nWidthSize, nHeightSize);
}*/
}
@@ -1,96 +0,0 @@
package cc.winboll.studio.libaes.views;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/16 01:58:01
* @Describe AToolbar
*/
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import android.widget.Toolbar;
import cc.winboll.studio.libaes.R;
public class AToolbar extends Toolbar {
public static final String TAG = "AToolbar";
int mTitleTextColor;
int mStartColor;
int mCenterColor;
int mEndColor;
LayerDrawable ld;
GradientDrawable[] array = new GradientDrawable[3];
public AToolbar(Context context) {
super(context);
}
public AToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AToolbar, R.attr.aToolbar, 0);
mTitleTextColor = a.getColor(R.styleable.AToolbar_attrAToolbarTitleTextColor, Color.GREEN);
mStartColor = a.getColor(R.styleable.AToolbar_attrAToolbarStartColor, Color.BLUE);
mCenterColor = a.getColor(R.styleable.AToolbar_attrAToolbarCenterColor, Color.RED);
mEndColor = a.getColor(R.styleable.AToolbar_attrAToolbarEndColor, Color.YELLOW);
// 返回一个绑定资源结束的信号给资源
a.recycle();
notifyColorChange();
}
public AToolbar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
void notifyColorChange() {
// 工具栏描边
int nStroke = 5;
TypedArray taBorder = getContext().obtainStyledAttributes(new int[]{R.attr.borderCornerRadius});
float cornerRadius = taBorder.getDimension(0, 6 * getResources().getDisplayMetrics().density);
taBorder.recycle();
//分别为开始颜色,中间夜色,结束颜色
int colors0[] = { mEndColor , mCenterColor, mStartColor};
GradientDrawable gradientDrawable0;
array[2] = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors0);
gradientDrawable0 = array[2];
gradientDrawable0.setShape(GradientDrawable.RECTANGLE);
gradientDrawable0.setColors(colors0); //添加颜色组
gradientDrawable0.setGradientType(GradientDrawable.LINEAR_GRADIENT);//设置线性渐变
gradientDrawable0.setCornerRadius(cornerRadius);
int colors1[] = { mCenterColor , mCenterColor, mCenterColor };
GradientDrawable gradientDrawable1;
array[1] = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors1);
gradientDrawable1 = array[1];
gradientDrawable1.setShape(GradientDrawable.RECTANGLE);
gradientDrawable1.setColors(colors1); //添加颜色组
gradientDrawable1.setGradientType(GradientDrawable.LINEAR_GRADIENT);//设置线性渐变
gradientDrawable1.setCornerRadius(cornerRadius);
int colors2[] = { mEndColor, mCenterColor, mStartColor };
GradientDrawable gradientDrawable2;
array[0] = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors2);
gradientDrawable2 = array[0];
gradientDrawable2.setShape(GradientDrawable.RECTANGLE);
gradientDrawable2.setColors(colors2); //添加颜色组
gradientDrawable2.setGradientType(GradientDrawable.LINEAR_GRADIENT);//设置线性渐变
gradientDrawable2.setCornerRadius(cornerRadius);
ld = new LayerDrawable(array); //参数为上面的Drawable数组
ld.setLayerInset(2, nStroke * 2, nStroke * 2, getWidth() + nStroke * 2, getHeight() + nStroke * 2);
ld.setLayerInset(1, nStroke, nStroke, getWidth() + nStroke, getHeight() + nStroke);
ld.setLayerInset(0, 0, 0, getWidth(), getHeight());
setBackgroundDrawable(ld);
setTitleTextColor(mTitleTextColor);
setSubtitleTextColor(mTitleTextColor);
}
}
@@ -1,379 +0,0 @@
package cc.winboll.studio.libaes.views;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/24 15:08:52
* @Describe WinBoLL应用介绍视图
*/
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libaes.dialogs.YesNoAlertDialog;
import cc.winboll.studio.libaes.models.APPInfo;
import cc.winboll.studio.libaes.utils.AppVersionUtils;
import cc.winboll.studio.libaes.utils.PrefUtils;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
import java.io.IOException;
import mehdi.sakout.aboutpage.AboutPage;
import mehdi.sakout.aboutpage.Element;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class AboutView extends LinearLayout {
public static final String TAG = "AboutView";
public static final int MSG_APPUPDATE_CHECKED = 0;
static Context _mContext;
APPInfo mAPPInfo;
//WinBoLLServiceStatusView mWinBoLLServiceStatusView;
OnRequestDevUserInfoAutofillListener mOnRequestDevUserInfoAutofillListener;
String mszAppName = "";
String mszAppAPKFolderName = "";
String mszAppAPKName = "";
String mszAppGitName = "";
String mszAppVersionName = "";
String mszCurrentAppPackageName = "";
boolean mIsAddDebugTools;
volatile String mszNewestAppPackageName = "";
String mszAppDescription = "";
String mszHomePage = "";
String mszGitea = "";
int mnAppIcon = 0;
String mszWinBoLLServerHost;
String mszReleaseAPKName;
EditText metDevUserName;
EditText metDevUserPassword;
public AboutView(Context context, APPInfo appInfo) {
super(context);
_mContext = context;
setAPPInfo(appInfo);
initView(context);
}
public AboutView(Context context, AttributeSet attrs) {
super(context, attrs);
_mContext = context;
initView(context, attrs);
}
public void setAPPInfo(APPInfo appInfo) {
mAPPInfo = appInfo;
}
APPInfo createAppInfo(Context context, AttributeSet attrs) {
APPInfo appInfo = new APPInfo();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AboutView);
appInfo.setAppName(typedArray.getString(R.styleable.AboutView_app_name));
appInfo.setAppAPKFolderName(typedArray.getString(R.styleable.AboutView_app_apkfoldername));
appInfo.setAppAPKName(typedArray.getString(R.styleable.AboutView_app_apkname));
appInfo.setAppGitName(typedArray.getString(R.styleable.AboutView_app_gitname));
appInfo.setAppGitOwner(typedArray.getString(R.styleable.AboutView_app_gitowner));
appInfo.setAppGitAPPBranch(typedArray.getString(R.styleable.AboutView_app_gitappbranch));
appInfo.setAppGitAPPSubProjectFolder(typedArray.getString(R.styleable.AboutView_app_gitappsubprojectfolder));
appInfo.setAppDescription(typedArray.getString(R.styleable.AboutView_appdescription));
appInfo.setAppIcon(typedArray.getResourceId(R.styleable.AboutView_appicon, R.drawable.ic_winboll));
appInfo.setIsAddDebugTools(typedArray.getBoolean(R.styleable.AboutView_is_adddebugtools, false));
// 返回一个绑定资源结束的信号给资源
typedArray.recycle();
return appInfo;
}
void initView(Context context) {
mszAppName = mAPPInfo.getAppName();
mszAppAPKFolderName = mAPPInfo.getAppAPKFolderName();
mszAppAPKName = mAPPInfo.getAppAPKName();
mszAppGitName = mAPPInfo.getAppGitName();
mszAppDescription = mAPPInfo.getAppDescription();
mnAppIcon = mAPPInfo.getAppIcon();
mszWinBoLLServerHost = GlobalApplication.isDebugging() ? "https://yun-preivew.winboll.cc": "https://yun.winboll.cc";
try {
mszAppVersionName = _mContext.getPackageManager().getPackageInfo(_mContext.getPackageName(), 0).versionName;
} catch (PackageManager.NameNotFoundException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
mszCurrentAppPackageName = mszAppAPKName + "_" + mszAppVersionName + ".apk";
mszHomePage = mAPPInfo.getAppHomePage();
//mszHomePage = mszWinBoLLServerHost + "/studio/details.php?app=" + mszAppAPKFolderName;
if (mAPPInfo.getAppGitAPPBranch().equals("")) {
mszGitea = "https://gitea.winboll.cc/" + mAPPInfo.getAppGitOwner() + "/" + mszAppGitName;
} else {
mszGitea = "https://gitea.winboll.cc/" + mAPPInfo.getAppGitOwner() + "/" + mszAppGitName + "/src/branch/" + mAPPInfo.getAppGitAPPBranch() + "/" + mAPPInfo.getAppGitAPPSubProjectFolder();
}
addView(createAboutPage());
// 初始化标题栏
//setSubtitle(getContext().getString(R.string.text_about));
// LinearLayout llMain = findViewById(R.id.viewaboutLinearLayout1);
// llMain.addView(createAboutPage());
// 就读取正式版应用包版本号,设置 Release 应用包文件名
String szReleaseAppVersionName = "";
try {
//LogUtils.d(TAG, String.format("mContext.getPackageName() %s", mContext.getPackageName()));
String szSubBetaSuffix = subBetaSuffix(_mContext.getPackageName());
//LogUtils.d(TAG, String.format("szSubBetaSuffix : %s", szSubBetaSuffix));
szReleaseAppVersionName = _mContext.getPackageManager().getPackageInfo(szSubBetaSuffix, 0).versionName;
} catch (PackageManager.NameNotFoundException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
mszReleaseAPKName = mszAppAPKName + "_" + szReleaseAppVersionName + ".apk";
}
void initView(Context context, AttributeSet attrs) {
mAPPInfo = createAppInfo(context, attrs);
initView(context);
}
public static String subBetaSuffix(String input) {
if (input.endsWith(".beta")) {
return input.substring(0, input.length() - ".beta".length());
}
return input;
}
android.os.Handler mHandler = new android.os.Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_APPUPDATE_CHECKED : {
/*//检查当前应用包文件名是否是测试版,如果是就忽略检查
if(mszCurrentAppPackageName.matches(".*_\\d+\\.\\d+\\.\\d+-beta.*\\.apk")) {
ToastUtils.show("APP is the beta Version. Version check ignore.");
return;
}*/
// if (!AppVersionUtils.isHasNewStageReleaseVersion(mszReleaseAPKName, mszNewestAppPackageName)) {
// ToastUtils.delayedShow("Current app is the newest.", 5000);
// }
if (!AppVersionUtils.isHasNewVersion2(mszCurrentAppPackageName, mszNewestAppPackageName)) {
ToastUtils.show("Current app is the newest.");
} else {
String szMsg = "Current app is :\n[ " + mszCurrentAppPackageName
+ " ]\nThe last app is :\n[ " + mszNewestAppPackageName
+ " ]\nIs download the last app?";
YesNoAlertDialog.show(_mContext, "Application Update Prompt", szMsg, mIsDownlaodUpdateListener);
}
break;
}
}
}
};
protected View createAboutPage() {
// 定义 GitWeb 按钮
//
Element elementGitWeb = new Element(_mContext.getString(R.string.gitea_home), R.drawable.ic_winboll);
elementGitWeb.setOnClickListener(mGitWebOnClickListener);
// 定义检查更新按钮
//
/*Element elementAppUpdate = new Element(_mContext.getString(R.string.app_update), R.drawable.ic_winboll);
elementAppUpdate.setOnClickListener(mAppUpdateOnClickListener);
*/
String szAppInfo = "";
try {
szAppInfo = mszAppName + " "
+ _mContext.getPackageManager().getPackageInfo(_mContext.getPackageName(), 0).versionName
+ "\n" + mszAppDescription;
} catch (PackageManager.NameNotFoundException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
AboutPage aboutPage = new AboutPage(_mContext);
aboutPage.setDescription(szAppInfo)
//.isRTL(false)
//.setCustomFont(String) // or Typeface
.setImage(mnAppIcon)
//.addItem(versionElement)
//.addItem(adsElement)
//.addGroup("Connect with us")
.addEmail("WinBoLLStudio<studio@winboll.cc>")
.addWebsite(mszHomePage)
.addItem(elementGitWeb);
//.addItem(elementAppUpdate);
//.addFacebook("the.medy")
//.addTwitter("medyo80")
//.addYoutube("UCdPQtdWIsg7_pi4mrRu46vA")
//.addPlayStore("com.ideashower.readitlater.pro")
//.addGitHub("medyo")
//.addInstagram("medyo80")
//.create();
/*if (mAPPInfo.isAddDebugTools()) {
// 定义应用调试按钮
//
Element elementAppMode;
if (GlobalApplication.isDebugging()) {
elementAppMode = new Element(_mContext.getString(R.string.app_normal), R.drawable.ic_winboll);
elementAppMode.setOnClickListener(mAppNormalOnClickListener);
} else {
elementAppMode = new Element(_mContext.getString(R.string.app_debug), R.drawable.ic_winboll);
elementAppMode.setOnClickListener(mAppDebugOnClickListener);
}
aboutPage.addItem(elementAppMode);
}*/
return aboutPage.create();
}
View.OnClickListener mAppDebugOnClickListener = new View.OnClickListener(){
@Override
public void onClick(View view) {
//ToastUtils.show("mAppDebugOnClickListener");
setApp2DebugMode(_mContext);
}
};
View.OnClickListener mAppNormalOnClickListener = new View.OnClickListener(){
@Override
public void onClick(View view) {
//ToastUtils.show("mAppNormalOnClickListener");
setApp2NormalMode(_mContext);
}
};
public static void setApp2DebugMode(Context context) {
Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
if (intent != null) {
//intent.setAction(cc.winboll.studio.libapputils.intent.action.DEBUGVIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
GlobalApplication.setIsDebugging(true);
GlobalApplication.saveDebugStatus((GlobalApplication)_mContext.getApplicationContext());
WinBoLLActivityManager.getInstance().finishAll();
context.startActivity(intent);
}
}
public static void setApp2NormalMode(Context context) {
Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
GlobalApplication.setIsDebugging(false);
GlobalApplication.saveDebugStatus((GlobalApplication)_mContext.getApplicationContext());
WinBoLLActivityManager.getInstance().finishAll();
context.startActivity(intent);
}
}
View.OnClickListener mGitWebOnClickListener = new View.OnClickListener(){
@Override
public void onClick(View view) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(mszGitea));
_mContext.startActivity(browserIntent);
}
};
View.OnClickListener mAppUpdateOnClickListener = new View.OnClickListener(){
@Override
public void onClick(View view) {
ToastUtils.show("Start app update checking.");
new Thread(new Runnable() {
@Override
public void run() {
String szUrl = mszWinBoLLServerHost + "/studio/details.php?app=" + mszAppAPKFolderName;
// 构建包含认证信息的请求
String credential = "";
if (GlobalApplication.isDebugging()) {
credential = Credentials.basic(metDevUserName.getText().toString(), metDevUserPassword.getText().toString());
PrefUtils.saveString(_mContext, "metDevUserName", metDevUserName.getText().toString());
PrefUtils.saveString(_mContext, "metDevUserPassword", metDevUserPassword.getText().toString());
} else {
String username = "WinBoLL";
String password = "WinBoLLPowerByZhanGSKen";
credential = Credentials.basic(username, password);
}
Request request = new Request.Builder()
.url(szUrl)
.header("Accept", "text/plain") // 设置正确的Content-Type头
.header("Authorization", credential)
.build();
OkHttpClient client = new OkHttpClient();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 处理网络请求失败
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
LogUtils.d(TAG, "Unexpected code " + response, Thread.currentThread().getStackTrace());
return;
}
try {
// 读取响应体作为字符串,注意这里可能需要解码
String text = response.body().string();
org.jsoup.nodes.Document doc = org.jsoup.Jsoup.parse(text);
LogUtils.v(TAG, doc.text());
// 使用id选择器找到具有特定id的元素
org.jsoup.nodes.Element elementWithId = doc.select("#LastRelease").first(); // 获取第一个匹配的元素
// 提取并打印元素的文本内容
mszNewestAppPackageName = elementWithId.text();
//ToastUtils.delayedShow(text + "\n" + mszNewestAppPackageName, 5000);
mHandler.sendMessage(mHandler.obtainMessage(MSG_APPUPDATE_CHECKED));
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
});
}
}).start();
}
};
YesNoAlertDialog.OnDialogResultListener mIsDownlaodUpdateListener = new YesNoAlertDialog.OnDialogResultListener() {
@Override
public void onYes() {
String szUrl = mszWinBoLLServerHost + "/studio/download.php?appname=" + mszAppAPKFolderName + "&apkname=" + mszNewestAppPackageName;
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(szUrl));
_mContext.startActivity(browserIntent);
}
@Override
public void onNo() {
}
};
public interface OnRequestDevUserInfoAutofillListener {
void requestAutofill(EditText etDevUserName, EditText etDevUserPassword);
}
public void setOnRequestDevUserInfoAutofillListener(OnRequestDevUserInfoAutofillListener l) {
mOnRequestDevUserInfoAutofillListener = l;
}
}
@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<scale
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromXScale="0.1"
android:toXScale="1.0"
android:fromYScale="0.1"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false"
android:duration="300" />
<set android:interpolator="@android:anim/decelerate_interpolator">
<scale
android:fromXScale="1.0"
android:toXScale="1.0"
android:fromYScale="1.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="100"
android:fillBefore="false" />
<rotate
android:fromDegrees="0"
android:toDegrees="0"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="100" />
</set>
</set>
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<scale
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromXScale="0.1"
android:toXScale="1.0"
android:fromYScale="0.1"
android:toYScale="1.0"
android:pivotX="@dimen/dialogPivotX"
android:pivotY="@dimen/dialogPivotY"
android:fillAfter="false"
android:duration="500" />
</set>
@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<scale
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromXScale="1.0"
android:toXScale="1.0"
android:fromYScale="1.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false"
android:duration="100" />
<set android:interpolator="@android:anim/decelerate_interpolator">
<scale
android:fromXScale="1.0"
android:toXScale="0.1"
android:fromYScale="1.0"
android:toYScale="0.1"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="100"
android:fillBefore="false" />
<rotate
android:fromDegrees="0"
android:toDegrees="0"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="100" />
</set>
</set>
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<scale
android:fromXScale="1.0"
android:toXScale="0.1"
android:fromYScale="1.0"
android:toYScale="0.1"
android:pivotX="@dimen/dialogPivotX"
android:pivotY="@dimen/dialogPivotY"
android:duration="500"
android:fillBefore="false" />
</set>
@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 边框阴影部分 -->
<!-- 相对边框的Offset设置(android:left, top, right, bottom) -->
<item
android:left="0dp"
android:top="0dp"
android:right="0dp"
android:bottom="0dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:startColor="@color/colorACardShadow"
android:centerColor="@color/colorACardShadow"
android:endColor="@color/colorACardShadow"/>
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
<!-- 边框部分 -->
<item
android:left="0dp"
android:top="0dp"
android:right="1dp"
android:bottom="2dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:startColor="@color/colorACardFrame"
android:centerColor="@color/colorACardFrame"
android:endColor="@color/colorACardFrame"/>
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
<!-- 背景主体部分 -->
<item
android:left="2dp"
android:top="2dp"
android:right="3dp"
android:bottom="3dp">
<shape android:shape="rectangle" >
<gradient
android:type="linear"
android:angle="90"
android:startColor="@color/colorACardBackgroung"
android:centerColor="@color/colorACardBackgroung"
android:endColor="@color/colorACardBackgroung"/>
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
</layer-list>
@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 边框阴影部分 -->
<!-- 相对边框的Offset设置(android:left, top, right, bottom) -->
<item
android:left="0dp"
android:top="0dp"
android:right="0dp"
android:bottom="2dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:startColor="?attr/attrAToolbarEndColor"
android:centerColor="?attr/attrAToolbarCenterColor"
android:endColor="?attr/attrAToolbarStartColor"/>
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
<!-- 边框部分 -->
<item
android:left="2dp"
android:top="2dp"
android:right="2dp"
android:bottom="4dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:startColor="?attr/attrAToolbarCenterColor"
android:centerColor="?attr/attrAToolbarCenterColor"
android:endColor="?attr/attrAToolbarCenterColor"/>
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
<!-- 背景主体部分 -->
<item
android:left="4dp"
android:top="4dp"
android:right="4dp"
android:bottom="6dp">
<shape android:shape="rectangle" >
<gradient
android:type="linear"
android:angle="90"
android:startColor="?attr/attrAToolbarStartColor"
android:centerColor="?attr/attrAToolbarCenterColor"
android:endColor="?attr/attrAToolbarEndColor"/>
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
</layer-list>
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<stroke android:width="1dp" android:color="#FFB0B0B0" />
<corners android:radius="?attr/borderCornerRadius" />
</shape>
@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 阴影部分 -->
<!-- 个人觉得更形象的表达:top代表下边的阴影高度,left代表右边的阴影宽度。其实也就是相对应的offset,solid中的颜色是阴影的颜色,也可以设置角度等等 -->
<item
android:left="2dp"
android:top="2dp"
android:right="2dp"
android:bottom="2dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:endColor="#0F000000"
android:startColor="#0F000000" />
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
<!-- 背景部分 -->
<!-- 形象的表达:bottom代表背景部分在上边缘超出阴影的高度,right代表背景部分在左边超出阴影的宽度(相对应的offset) -->
<item
android:left="3dp"
android:top="3dp"
android:right="3dp"
android:bottom="5dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:endColor="@color/colorAccent"
android:startColor="@color/colorAccent" />
<corners android:radius="?attr/borderCornerRadius" />
</shape>
</item>
</layer-list>

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