Compare commits

..

58 Commits

Author SHA1 Message Date
LaizyBoy
042ba4b0e9 Remove ADsControlView from SettingsActivity layout
The class  was referenced in  but not present in the current module, causing an InflateException at runtime. The tag has been commented out to prevent the crash while preserving the UI layout structure. No other source files were changed.
2026-07-01 14:53:43 +08:00
50a06f028c 修复git管理脚本 2026-07-01 04:54:11 +08:00
2aed435668 修复git管理脚本 2026-07-01 04:19:41 +08:00
506a8da12c Update build metadata and clean up menu handling
- Bump libappbase to 15.20.34 and libaes to 15.20.17
- Increment buildCount in winboll/build.properties and libwinboll/build.properties (0 → 4)
- Update timestamps to current date
- Apply Java 7 source/target level
- Remove unused library activity menu item from MainActivity
2026-07-01 03:25:50 +08:00
1e40883810 修复编译参数配置 2026-07-01 03:17:14 +08:00
ea90877e6b 移除 libwinboll模块 2026-07-01 03:12:18 +08:00
1bec3dc08d Update build artifacts and dependency versions
- Bump  to 15.20.34 and  to 15.20.17 in libwinboll build.gradle (new release of the core libraries).
- Revise build timestamps in libwinboll/build.properties and winbolt/build.properties to current date.
- Increment  from 0 to 4, reflecting four incremental builds made since last snapshot.
2026-07-01 02:10:49 +08:00
ebd9b64eea <winboll>APK 15.20.8 release Publish. 2026-06-24 08:11:15 +08:00
40f8170751 升级 libappbase/libaes 依赖至最新版,移除米盟 SDK 配置
- cc.winboll.studio:libappbase 15.20.25 → 15.20.33
- cc.winboll.studio:libaes    15.20.14 → 15.20.16
- 注释米盟广告 SDK (mimo-ad-sdk) 依赖及相关 packagingOptions 配置
2026-06-24 06:57:34 +08:00
da92eb7dee 添加--no-daemon参数,解决在Termux环境下编译调用java资源环境不同的问题。 2026-06-11 21:16:33 +08:00
07c3c2e967 添加Termux终端编译发布脚本 2026-06-11 20:48:12 +08:00
Studio
4ac78cd63b <winboll>APK 15.20.7 release Publish. 2026-06-03 07:32:48 +08:00
16c3153d95 改进应用创建函数,提高应用调试能力。 2026-06-03 07:30:38 +08:00
3e65cbc326 更新类库 2026-06-03 07:16:44 +08:00
Studio
97f036bf5e <winboll>APK 15.20.6 release Publish. 2026-06-02 20:00:52 +08:00
76d93acdd5 feat(MyTermuxActivity): 创建桌面快捷方式时使用按钮自定义图标
新增 loadButtonIcon() 加载模型 iconPath 对应图片,
API 26+ 用 Icon.createWithBitmap,旧 API 用 EXTRA_SHORTCUT_ICON,
无图标时回退 ic_menu_manage。
2026-06-02 19:57:29 +08:00
7219fd0c87 feat(TermuxButton): 添加按钮自定义图标功能
- TermuxButtonModel 新增 iconPath 字段及 JSON 序列化
- MyTermuxActivity 编辑对话框增加图标导入/预览/清除
- 列表项改为 LinearLayout,图标 MATCH_PARENT 撑满高度,5dp 边距
- 图标文件保存在 getFilesDir()/termux_icons/
- 新增 strings 国际化资源(中/英)
2026-06-02 19:53:16 +08:00
756cf88b55 feat(MyTermuxActivity): 添加桌面快捷方式及指纹验证功能
- 列表项长按菜单新增"创建桌面快捷方式"
- 支持 ShortcutManager(API 26+) 和 INSTALL_SHORTCUT 广播两种方式
- TermuxCommandExecutor 所有命令执行前增加指纹验证
- 自定义AlertDialog显示命令信息(名称蓝色粗体),通过后启动BiometricPrompt
- 列表项名称蓝色粗体显示
- 新增 EXTRA_SESSION_ACTION 确保每次创建新终端会话
- MyTermuxActivity 添加 shortcut 意图处理(onCreate/onNewIntent)
2026-06-02 19:32:01 +08:00
ac8b789bcb fix(Termux): 修复按钮命令未执行及返回值反转问题
- 移除 stdbuf bash 占位,改为执行命令后进入交互终端
- 替换 \n 为 ; 以支持多行命令分段执行
- 修复 executeTerminalCommand 返回值反转逻辑
- MyTermuxActivity列表文字颜色 white→black 适配浅色主题
2026-06-02 18:09:37 +08:00
bac0a957aa refactor: 将 MyTermuxActivity 移至 termux 包并清理空目录 2026-06-02 17:01:16 +08:00
acfd4744f8 更新菜单排列方式 2026-06-02 09:33:59 +08:00
e15c8076de 更新类库 2026-06-02 09:14:33 +08:00
375635436b Merge remote-tracking branch 'origin/projects_keeper_tag' into winboll 2026-06-02 08:57:30 +08:00
qinglong
db804d1897 合并模块AES 同步最新时间标签aes-v15.20.12 2026-06-02 08:55:05 +08:00
qinglong
039c8fcd98 合并模块WinBoLL 同步最新时间标签winboll-v15.20.5 2026-06-02 04:00:01 +08:00
STUDIO
e8c5cefeac <winboll>APK 15.20.5 release Publish. 2026-06-02 03:18:21 +08:00
85fb42ca97 fix: 修复主题切换时 IndexOutOfBoundsException 崩溃
- App.onCreate() 中调用 AESThemeUtil.init() 注入当前应用的
  R.style.* 主题ID列表(按 ThemeType.ordinal() 顺序排列),
  避免 Jitpack AESThemeUtil 内部 ArrayList 为空导致越界崩溃
- PatternLockActivity / SettingsActivity 删除冗余的
  AESThemeUtil.applyAppTheme(this) 调用(父类 BaseWinBoLLActivity
  已在 onCreate 中通过 setThemeStyle() 处理主题设置)
2026-06-02 03:15:35 +08:00
qinglong
ae63d1ec0a 合并模块AES 同步最新时间标签aes-v15.20.11 2026-06-02 03:00:01 +08:00
f99632cbea Merge branch 'winboll' into merge 2026-06-02 02:58:06 +08:00
c8ef451232 Merge remote-tracking branch 'origin/projects_keeper_tag' into merge 2026-06-02 02:57:59 +08:00
92e59bdb9e 更新类库 2026-06-02 02:55:10 +08:00
9ce03ea542 更新类库 2026-06-02 02:31:39 +08:00
qinglong
9e9486b488 合并模块WinBoLL 同步最新时间标签winboll-v15.20.4 2026-06-01 21:00:01 +08:00
STUDIO
b5d4036d6d <winboll>APK 15.20.4 release Publish. 2026-06-01 20:31:36 +08:00
qinglong
4b8967b253 合并模块WinBoLL 同步最新时间标签winboll-v15.20.3 2026-05-31 21:00:02 +08:00
25daecd8b5 <winboll>APK 15.20.3 release Publish. 2026-05-31 20:40:44 +08:00
7b48ca8fee 去掉应用自定义调试逻辑判断。 2026-05-31 20:39:20 +08:00
26f247b409 <winboll>APK 15.20.2 release Publish. 2026-05-31 20:28:41 +08:00
59080de7f3 更新类库,修复工具栏风格设置问题。 2026-05-27 20:33:43 +08:00
c3f84afb62 Merge remote-tracking branch 'origin/projects_keeper_tag' into winboll 2026-05-27 20:27:10 +08:00
qinglong
b1059c3f46 合并模块AES 同步最新时间标签aes-v15.20.10 2026-05-27 20:26:41 +08:00
00220a382d Merge remote-tracking branch 'origin/projects_keeper_tag' into winboll 2026-05-27 20:26:14 +08:00
qinglong
f3d723fbee 合并模块APPBase 同步最新时间标签appbase-v15.20.22 2026-05-27 15:00:01 +08:00
d0e70407f9 修改应用包权限设置更新说明 2026-05-24 11:17:27 +08:00
79eb4e3247 更新类库 2026-05-24 11:08:06 +08:00
71e6f1f03f Merge remote-tracking branch 'origin/projects_keeper_tag' into winboll 2026-05-24 11:00:11 +08:00
qinglong
e3c30ea9a3 合并模块AES 同步最新时间标签aes-v15.20.9 2026-05-24 10:49:26 +08:00
fdfae270d2 Merge remote-tracking branch 'origin/projects_keeper_tag' into winboll 2026-05-24 10:26:27 +08:00
d147f9dc08 更新类库版本,使用GitHub推向https://jitpack.io网站编译的类库仓库 2026-05-20 20:21:35 +08:00
c9272b6341 更新联系邮箱 2026-05-20 11:20:54 +08:00
0b5eea447b 更新基础类 2026-05-17 16:23:52 +08:00
6dbcca8fad Merge branch 'projects_keeper_tag' into winboll 2026-05-17 16:22:53 +08:00
68ebd0181b Merge branch 'projects-keeper' into winboll 2026-05-16 17:47:48 +08:00
2bf62a510a Merge branch 'projects-keeper' into winboll 2026-05-16 15:36:36 +08:00
fcc81c6e1f Restore the deleted library file 2026-05-15 20:15:27 +08:00
e813afe8b1 中文:进入 WinBoLL 分支开发阶段
英文:Entering the development stage of the WinBoLL branch
2026-05-15 20:07:12 +08:00
LaizyBoy
a8b4d31fa6 fix: 补充borderCornerRadius属性定义修复编译资源链接失败
在 winboll/src/main/res/values/attrs.xml 中添加缺失的
borderCornerRadius 属性声明,该属性被 bg_container_border.xml
引用但未在当前激活的模块中定义,导致 assembleBetaDebug 编译
时 AAPT 报错 resource attr/borderCornerRadius not found。
2026-05-15 14:15:33 +08:00
Transformers
99479ac830 修改文档:将 OriginMaster 重命名为 Projects_Keeper 2026-05-15 13:57:35 +08:00
309 changed files with 22046 additions and 379 deletions

48
.gitignore vendored
View File

@@ -97,9 +97,47 @@ lint-results.html
## WinBoLL 基础应用(避免上传敏感配置)
/winboll.properties
/local.properties
#/settings.gradle
#/gradle.properties
/settings.gradle
/gradle.properties
## APPBase 项目配置
aes/
libaes/
## WinBoLL 项目配置
#.git
#.gitignore
#.gitmodules
#.gradle
#.winboll
#GenKeyStore
#LICENSE
#LICENSE-Private-Demo
#LICENSE-Private-Demo_docs
#README.md
#aes
#appbase
appkey.jks
appkey.keystore
autonfc
#build.gradle
contacts
debugtemp
gallery
gpsrelaysentinel
#gradle
gradle.properties
#gradle.properties-android-demo
#gradle.properties-androidx-demo
#gradlew
#libaes
#libappbase
libdebugtemp
libgpsrelaysentinel
#libwinboll
local.properties
#local.properties-demo
mymessagemanager
positions
powerbell
settings.gradle
#settings.gradle-demo
#winboll
winboll.properties
#winboll.properties-demo

View File

@@ -1,5 +1,5 @@
#!/system/bin/sh
## 合并其他项目分支的模块源码到projects-keeper分支。
## 合并其他项目分支的模块源码到projects_keeper分支。
# ====================== 0. 进入目标目录 ======================
TARGET_DIR="/sdcard/AppProjects/Projects_Keeper"
@@ -36,7 +36,7 @@ fi
# ====================== 3. Git 分支检查 ======================
CUR_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
TARGET_BRANCH="projects-keeper"
TARGET_BRANCH="projects_keeper"
if [ "$CUR_BRANCH" != "$TARGET_BRANCH" ]; then
echo "错误:当前不在 $TARGET_BRANCH 分支!"
@@ -72,7 +72,6 @@ libaes
libappbase
libdebugtemp
libgpsrelaysentinel
libwinboll
local.properties-demo
mymessagemanager
positions
@@ -82,13 +81,14 @@ winboll
winboll.properties-demo
)
# ====================== 5. 获取当前目录真实文件列表 ======================
# ====================== 5. 获取当前目录真实文件列表(兼容过滤 . .. ======================
REAL_ITEMS=()
# 使用固定排序ls自动过滤 . 和 ..,不会进入比对数组
while IFS= read -r line; do
if [[ "$line" != "." && "$line" != ".." ]]; then
REAL_ITEMS+=("$line")
fi
done < <(ls -a)
done < <(LC_COLLATE=C ls -a1 --color=none)
# ====================== 6. 差异比对函数 ======================
check_diff() {
@@ -158,7 +158,7 @@ echo -e "## 对象列表结束
## 合并 APP 项目
MERGE_APP_PROJECT_LIST=(
DemoAPP
WinBoLL
)
echo -e "#@@@ 开始合并应用型模块源码 @@@#
## 目标合并对象列表:"
@@ -166,14 +166,13 @@ echo -e "#@@@ 开始合并应用型模块源码 @@@#
for item in "${MERGE_APP_PROJECT_LIST[@]}"; do
echo "正在合并 $item 项目 ..."
item_lower=$(echo "$item" | tr 'A-Z' 'a-z')
git checkout origin/$item_lower $item_lower
git checkout origin/$item_lower $item_lower
git add .
git commit -m "合并 $item 项目"
done
## 合并 LIB 项目
MERGE_LIB_PROJECT_LIST=(
WinBoLL
APPBase
AES
)
@@ -183,10 +182,10 @@ echo -e "#@@@ 开始合并类库型模块源码 @@@#
for item in "${MERGE_LIB_PROJECT_LIST[@]}"; do
echo "正在合并 $item 项目 ..."
item_lower=$(echo "$item" | tr 'A-Z' 'a-z')
git checkout origin/$item_lower $item_lower lib$item_lower
git checkout origin/$item_lower $item_lower lib$item_lower
git add .
git commit -m "合并 $item 项目"
done
echo '正在推送 Projects_Keeper 项目'
git push
git push

View File

@@ -38,7 +38,7 @@ if [ "${NOW_BRANCH}" != "${TARGET_BRANCH}" ];then
exit 1
fi
# 目录结构校验
# 目录结构校验白名单(不含 . ..
MERGE_OBJECTS_LIST=(
.git
.gitignore
@@ -65,7 +65,6 @@ libaes
libappbase
libdebugtemp
libgpsrelaysentinel
libwinboll
local.properties-demo
mymessagemanager
positions
@@ -76,9 +75,13 @@ winboll.properties-demo
)
REAL_ITEMS=()
# 标准排序ls输出循环强制过滤 . 和 ..
while IFS= read -r line; do
[[ $line != "." && $line != ".." ]] && REAL_ITEMS+=("$line")
done < <(ls -a)
# 跳过虚拟目录 . 和 ..
if [[ "$line" != "." && "$line" != ".." ]]; then
REAL_ITEMS+=("$line")
fi
done < <(LC_COLLATE=C ls -a1 --color=none)
check_diff(){
local miss=() extra=()
@@ -89,7 +92,17 @@ check_diff(){
[[ ! " ${MERGE_OBJECTS_LIST[@]} " =~ " ${i} " ]] && extra+=("$i")
done
if [[ ${#miss[@]} -gt 0 || ${#extra[@]} -gt 0 ]];then
echo "========================================"
echo "本地目录结构不一致,终止运行"
if [[ ${#miss[@]} -gt 0 ]]; then
echo -e "\n缺失条目"
for m in "${miss[@]}"; do echo " $m"; done
fi
if [[ ${#extra[@]} -gt 0 ]]; then
echo -e "\n多余条目"
for e in "${extra[@]}"; do echo " $e"; done
fi
echo "========================================"
exit 1
fi
}
@@ -98,7 +111,7 @@ check_diff
echo -e "#@@@ 按时间获取最新标签合并模块源码 @@@#"
# 应用型模块
MERGE_APP_PROJECT_LIST=(DemoAPP)
MERGE_APP_PROJECT_LIST=(WinBoLL)
echo -e "---------- 应用型模块 ----------"
for name in "${MERGE_APP_PROJECT_LIST[@]}";do
low_name=$(echo "$name" | tr 'A-Z' 'a-z')
@@ -120,7 +133,7 @@ for name in "${MERGE_APP_PROJECT_LIST[@]}";do
done
# 类库模块
MERGE_LIB_PROJECT_LIST=(WinBoLL APPBase AES)
MERGE_LIB_PROJECT_LIST=(APPBase AES)
echo -e "---------- 类库模块 ----------"
for name in "${MERGE_LIB_PROJECT_LIST[@]}";do
low_name=$(echo "$name" | tr 'A-Z' 'a-z')

View File

@@ -0,0 +1,228 @@
#!/usr/bin/bash
# ==============================================================================
# WinBoLL 应用发布脚本
# 功能检查Git源码状态 → 编译Stage Release包 → 添加WinBoLL标签 → 提交并推送源码
# 依赖build.properties、app_update_description.txt项目根目录下
# 使用:./script_name.sh <APP_NAME>
# 作者:豆包&ZhanGSKen<zhangsken@qq.com>
# ==============================================================================
# ==================== 常量定义 ====================
# 脚本退出码
EXIT_CODE_SUCCESS=0
EXIT_CODE_ERR_NO_APP_NAME=2
EXIT_CODE_ERR_WORK_DIR=1
EXIT_CODE_ERR_GIT_CHECK=1
EXIT_CODE_ERR_ADD_WINBOLL_TAG=1
# Gradle 任务(正式发布)
GRADLE_TASK_PUBLISH="assembleStageRelease"
# Gradle 任务(调试用,注释备用)
# GRADLE_TASK_DEBUG="assembleBetaDebug"
# aapt2本地覆盖参数
AAPT2_OVERRIDE_ARG="-Pandroid.aapt2FromMavenOverride=/data/data/com.termux/files/usr/bin/aapt2"
# 禁用Gradle守护进程
GRADLE_NO_DAEMON="--no-daemon"
# ==================== 函数定义 ====================
# 检查Git源码是否已完全提交无未提交变更
# 返回值0=已完全提交1=存在未提交变更
function checkGitSources() {
# 配置Git安全目录解决权限问题
git config --global --add safe.directory "$(pwd)"
# 检查是否有未提交的变更
if [[ -n $(git diff --stat) ]]; then
echo "[ERROR] Git源码存在未提交变更请先提交所有修改"
return 1
fi
echo "[INFO] Git源码检查通过所有变更已提交。"
return 0
}
# 询问是否添加GitHub Workflows标签当前逻辑注释保留扩展能力
# 返回值1=用户选择是0=用户选择否
function askAddWorkflowsTag() {
read -p "是否添加GitHub Workflows标签(Y/n) " answer
if [[ $answer =~ ^[Yy]$ ]]; then
return 1
else
return 0
fi
}
# 添加WinBoLL正式标签
# 参数:$1=应用名称(项目根目录名)
# 返回值0=标签添加成功1=标签已存在/添加失败
function addWinBoLLTag() {
local app_name=$1
local build_prop_path="${app_name}/build.properties"
# 从build.properties中提取publishVersion
local publish_version=$(grep -o "publishVersion=.*" "${build_prop_path}" | awk -F '=' '{print $2}')
if [[ -z ${publish_version} ]]; then
echo "[ERROR] 未从${build_prop_path}中提取到publishVersion配置"
return 1
fi
echo "[INFO] 从${build_prop_path}读取到publishVersion${publish_version}"
# 构造WinBoLL标签格式<APP_NAME>-v<publishVersion>
local tag="${app_name}-v${publish_version}"
echo "[INFO] 准备添加WinBoLL标签${tag}"
# 检查标签是否已存在
if [[ "$(git tag -l ${tag})" == "${tag}" ]]; then
echo "[ERROR] WinBoLL标签${tag}已存在!"
return 1
fi
# 添加带注释的标签注释来自app_update_description.txt
git tag -a "${tag}" -F "${app_name}/app_update_description.txt"
echo "[INFO] WinBoLL标签${tag}添加成功!"
return 0
}
# 添加GitHub Workflows Beta标签当前逻辑注释保留扩展能力
# 参数:$1=应用名称(项目根目录名)
# 返回值0=标签添加成功1=标签已存在/添加失败
function addWorkflowsTag() {
local app_name=$1
local build_prop_path="${app_name}/build.properties"
# 从build.properties中提取baseBetaVersion
local base_beta_version=$(grep -o "baseBetaVersion=.*" "${build_prop_path}" | awk -F '=' '{print $2}')
if [[ -z ${base_beta_version} ]]; then
echo "[ERROR] 未从${build_prop_path}中提取到baseBetaVersion配置"
return 1
fi
echo "[INFO] 从${build_prop_path}读取到baseBetaVersion${base_beta_version}"
# 构造Workflows标签格式<APP_NAME>-v<baseBetaVersion>-beta
local tag="${app_name}-v${base_beta_version}-beta"
echo "[INFO] 准备添加Workflows标签${tag}"
# 检查标签是否已存在
if [[ "$(git tag -l ${tag})" == "${tag}" ]]; then
echo "[ERROR] Workflows标签${tag}已存在!"
return 1
fi
# 添加带注释的标签注释来自app_update_description.txt
git tag -a "${tag}" -F "${app_name}/app_update_description.txt"
echo "[INFO] Workflows标签${tag}添加成功!"
return 0
}
# ==================== 主流程开始 ====================
echo "============================================="
echo " WinBoLL 应用发布脚本"
echo "============================================="
# 1. 检查应用名称参数是否指定
if [ -z "$1" ]; then
echo "[ERROR] 未指定应用名称!使用方式:${0} <APP_NAME>"
exit ${EXIT_CODE_ERR_NO_APP_NAME}
fi
APP_NAME=$1
echo "[INFO] 待发布应用名称:${APP_NAME}"
# 2. 检查并切换到项目根目录确保build.properties存在
echo "[INFO] 当前工作目录:$(pwd)"
if [[ ! -e "${APP_NAME}/build.properties" ]]; then
echo "[WARNING] 当前目录不存在${APP_NAME}/build.properties尝试切换到上级目录..."
cd ..
echo "[INFO] 切换后工作目录:$(pwd)"
fi
# 验证最终工作目录是否正确
if [[ ! -e "${APP_NAME}/build.properties" ]]; then
echo "[ERROR] 工作目录错误!${APP_NAME}/build.properties 文件不存在。"
exit ${EXIT_CODE_ERR_WORK_DIR}
fi
echo "[INFO] 工作目录验证通过:${APP_NAME}/build.properties 存在。"
# 3. 检查Git源码状态
echo "---------------------------------------------"
echo " 步骤1检查Git源码状态"
echo "---------------------------------------------"
checkGitSources
if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then
echo "[ERROR] Git源码检查失败脚本终止"
exit ${EXIT_CODE_ERR_GIT_CHECK}
fi
# 4. 编译Stage Release版本APK携带aapt2覆盖参数 + --no-daemon
echo "---------------------------------------------"
echo " 步骤2编译Stage Release APK"
echo "---------------------------------------------"
echo "[INFO] 开始执行Gradle任务${GRADLE_TASK_PUBLISH}"
# 调试用(注释正式任务,启用调试任务)
# bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :${APP_NAME}:${GRADLE_TASK_DEBUG}
bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :${APP_NAME}:${GRADLE_TASK_PUBLISH}
if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then
echo "[ERROR] Gradle编译任务失败"
exit 1
fi
echo "[INFO] Stage Release APK编译成功"
# 5. 添加WinBoLL正式标签
echo "---------------------------------------------"
echo " 步骤3添加WinBoLL标签"
echo "---------------------------------------------"
addWinBoLLTag ${APP_NAME}
if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then
echo "[ERROR] WinBoLL标签添加失败脚本终止"
exit ${EXIT_CODE_ERR_ADD_WINBOLL_TAG}
fi
# 6. 可选添加GitHub Workflows标签当前逻辑注释保留扩展能力
# echo "---------------------------------------------"
# echo " 步骤4添加Workflows标签可选"
# echo "---------------------------------------------"
# echo "是否添加GitHub Workflows Beta标签(Y/n) "
# askAddWorkflowsTag
# nAskAddWorkflowsTag=$?
# if [[ ${nAskAddWorkflowsTag} -eq 1 ]]; then
# addWorkflowsTag ${APP_NAME}
# if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then
# echo "[ERROR] Workflows标签添加失败脚本终止"
# exit 1
# fi
# fi
# 7. 清理更新描述文件
echo "---------------------------------------------"
echo " 步骤5清理更新描述文件"
echo "---------------------------------------------"
echo "" > "${APP_NAME}/app_update_description.txt"
echo "[INFO] 已清空${APP_NAME}/app_update_description.txt"
# 8. 提交并推送源码与标签
echo "---------------------------------------------"
echo " 步骤6提交并推送源码"
echo "---------------------------------------------"
git add .
git commit -m "<${APP_NAME}> 开始新的Stage版本开发。"
echo "[INFO] 源码提交成功,开始推送..."
# 推送源码到远程仓库
git push origin
# 推送标签到远程仓库
git push origin --tags
if [[ $? -eq ${EXIT_CODE_SUCCESS} ]]; then
echo "[INFO] 源码与标签推送成功!"
else
echo "[ERROR] 源码与标签推送失败!"
exit 1
fi
# ==================== 主流程结束 ====================
echo "============================================="
echo " WinBoLL 应用发布完成!"
echo "============================================="
exit ${EXIT_CODE_SUCCESS}

View File

@@ -0,0 +1,20 @@
#!/usr/bin/bash
# aapt2本地覆盖参数
AAPT2_OVERRIDE_ARG="-Pandroid.aapt2FromMavenOverride=/data/data/com.termux/files/usr/bin/aapt2"
# Gradle禁用守护进程参数
GRADLE_NO_DAEMON="--no-daemon"
# 检查是否指定了将要发布的类库名称
# 使用 `-z` 命令检查变量是否为空
if [ -z "$1" ]; then
echo "No Library name specified : $0"
exit 2
fi
## 正式发布使用
git pull && bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :$1:publishReleasePublicationToWinBoLLReleaseRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1
## 调试使用
#bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :$1:publishSnapshotWinBoLLPublicationToWinBoLLSnapshotRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1

View File

@@ -122,7 +122,6 @@ android {
// 如果正在调试,就拷贝到 WinBoLL 备份管理文件夹
//
if(variant.flavorName == "beta"&&variant.buildType.name == "debug"){
//File outBuildBckDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/${variant.buildType.name}")
File outBuildBckDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}")
// 创建目标路径目录
if(!outBuildBckDir.exists()) {
@@ -130,6 +129,7 @@ android {
println "Output Folder Created.(WinBoLLStudio) : " + outBuildBckDir.getAbsolutePath()
}
if(outBuildBckDir.exists()) {
def targetApkFile = new File(outBuildBckDir, outputFileName)
copy{
from file.outputFile
into outBuildBckDir
@@ -138,6 +138,14 @@ android {
}
println "Output APK (WinBoLLStudio): " + outBuildBckDir.getAbsolutePath() + "/${outputFileName}"
}
// ========== 设置文件权限为775 ==========
if(targetApkFile.exists()){
exec {
commandLine 'chmod', '775', targetApkFile.absolutePath
}
println "Set file permission to 775 : ${targetApkFile.absolutePath}"
}
// 检查编译标志位配置
assert (winbollBuildProps['buildCount'] != null)
assert (winbollBuildProps['libraryProject'] != null)
@@ -160,8 +168,7 @@ android {
assert(libraryProjectBuildPropsFile.exists())
java.nio.file.Path sourceFilePath = winbollBuildPropsFile.toPath();
java.nio.file.Path targetFilePath = libraryProjectBuildPropsFile.toPath();
// 使用copyTo()方法复制文件,如果目标文件存在会被覆盖,可选参数可以选择不覆盖
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
println "\n\n>>> Library Project build.properties saved.\n\n";
}
@@ -172,16 +179,12 @@ android {
//
if(variant.flavorName == "stage"&&variant.buildType.name == "release"){
// 发布 APK 文件
//
// 截取版本号的版本字段为短版本名
String szVersionName = "${versionName}"
String[] szlistTemp = szVersionName.split("-")
String szShortVersionName = szlistTemp[0]
//String szCommonTagAPKName = "${rootProject.name}_" + szShortVersionName + ".apk"
String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk"
println "CommonTagAPKName is : " + szCommonTagAPKName
//File outTagDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/tag/")
File outTagDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/tag/")
// 创建目标路径目录
if(!outTagDir.exists()) {
@@ -192,12 +195,10 @@ android {
if(outTagDir.exists()) {
File targetAPK = new File(outTagDir, "${szCommonTagAPKName}")
if(targetAPK.exists()) {
// 标签版本APK文件已经存在构建拷贝任务停止
assert (!targetAPK.exists())
// 可选择删除并继续输出APK文件
//delete targetAPK
}
// 复制一个备份
// 复制完整版APK
def fullApkFile = new File(outTagDir, outputFileName)
copy{
from file.outputFile
into outTagDir
@@ -206,7 +207,16 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${outputFileName}"
}
// 复制一个并重命名为短版本名
// 设置权限775。
if(fullApkFile.exists()){
exec {
commandLine 'chmod', '775', fullApkFile.absolutePath
}
println "Set file permission to 775 : ${fullApkFile.absolutePath}"
}
// 复制短版本名APK
def shortApkFile = new File(outTagDir, szCommonTagAPKName)
copy{
from file.outputFile
into outTagDir
@@ -215,6 +225,14 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${szCommonTagAPKName}"
}
// 设置权限775。
if(shortApkFile.exists()){
exec {
commandLine 'chmod', '775', shortApkFile.absolutePath
}
println "Set file permission to 775 : ${shortApkFile.absolutePath}"
}
// 检查编译标志位配置
assert (winbollBuildProps['stageCount'] != null)
assert (winbollBuildProps['publishVersion'] != null)
@@ -239,14 +257,11 @@ android {
fos.close();
if(winbollBuildProps['libraryProject'] != "") {
// 如果应用 build.properties 文件设置了类库模块项目文件名
// 就拷贝一份新的编译标志配置到类库项目文件夹
File libraryProjectBuildPropsFile = new File("$RootProjectDir/" + winbollBuildProps['libraryProject'] + "/build.properties")
assert(winbollBuildPropsFile.exists())
assert(libraryProjectBuildPropsFile.exists())
java.nio.file.Path sourceFilePath = winbollBuildPropsFile.toPath();
java.nio.file.Path targetFilePath = libraryProjectBuildPropsFile.toPath();
// 使用copyTo()方法复制文件,如果目标文件存在会被覆盖,可选参数可以选择不覆盖
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
}
@@ -263,17 +278,12 @@ android {
// 如果正在调试发布版就只生成和输出APK文件不处理 Git 仓库提交与更新问题。
//
if(variant.flavorName == "stage"&&variant.buildType.name == "debug"){
// 发布 APK 文件
//
// 截取版本号的版本字段为短版本名
String szVersionName = "${versionName}"
String[] szlistTemp = szVersionName.split("-")
String szShortVersionName = szlistTemp[0]
//String szCommonTagAPKName = "${rootProject.name}_" + szShortVersionName + ".apk"
String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk"
println "CommonTagAPKName is : " + szCommonTagAPKName
//File outTagDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/tag/")
File outTagDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}/")
// 创建目标路径目录
if(!outTagDir.exists()) {
@@ -284,13 +294,11 @@ android {
if(outTagDir.exists()) {
File targetAPK = new File(outTagDir, "${szCommonTagAPKName}")
if(targetAPK.exists()) {
// 标签版本APK文件已经存在构建拷贝任务停止
println '如果是在调试 Stage 版应用包构建,请删除(注在debug目录)现有的 Stage 应用包('+targetAPK.getAbsolutePath()+')。再编译一次。'
assert (!targetAPK.exists())
// 可选择删除并继续输出APK文件
//delete targetAPK
}
// 复制一个备份
// 复制完整版APK
def debugFullApk = new File(outTagDir, outputFileName)
copy{
from file.outputFile
into outTagDir
@@ -299,7 +307,16 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${outputFileName}"
}
// 复制一个并重命名为短版本名
// 权限设为775。
if(debugFullApk.exists()){
exec {
commandLine 'chmod', '775', debugFullApk.absolutePath
}
println "Set file permission to 775 : ${debugFullApk.absolutePath}"
}
// 复制短版本名APK
def debugShortApk = new File(outTagDir, szCommonTagAPKName)
copy{
from file.outputFile
into outTagDir
@@ -308,8 +325,13 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${szCommonTagAPKName}"
}
//不保存编译标志配置
// 权限设为775
if(debugShortApk.exists()){
exec {
commandLine 'chmod', '775', debugShortApk.absolutePath
}
println "Set file permission to 775 : ${debugShortApk.absolutePath}"
}
}
}
@@ -328,6 +350,13 @@ android {
}
println "Output APK (Common): " + outCommonDir.getAbsolutePath() + "/${commandAPKName}"
}
// 额外输出文件设置775权限
if(apkFile.exists()){
exec {
commandLine 'chmod', '775', apkFile.absolutePath
}
println "Set file permission to 775 : ${apkFile.absolutePath}"
}
}
}

View File

@@ -1 +0,0 @@
15.20

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 生成)

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 生成)

View File

@@ -9,15 +9,15 @@ WinBoLL 手机源码计划,旨在通过核心项目 WinBoLL 构建手机端与
#### **仓库类型:功能说明**
☆ 基础项目分支 WinBoLL手机端安卓应用开发基础模板。
☆ 应用项目分支 APPBase、AES、PowerBell、Positions**:安卓应用单一管理系列项目。
☆ 源码汇总管理 OriginMaster**:各类分支源码合并存档,不适宜作为开发库使用。
☆ 源码汇总管理 Projects_Keeper**:各类分支源码合并存档,不适宜作为开发库使用。
### 3. 源码合并管理推送路线图
⚠️ **注意**:仅仅展示不同应用模块源码的综合管理路线。分支合并操作时,必须具备 Git 管理经验。
★ WinBoLL → APPBase → OriginMaster
★ WinBoLL → AES → OriginMaster
★ WinBoLL → PowerBell → OriginMaster
★ WinBoLL → Positions → OriginMaster
★ WinBoLL → APPBase → Projects_Keeper
★ WinBoLL → AES → Projects_Keeper
★ WinBoLL → PowerBell → Projects_Keeper
★ WinBoLL → Positions → Projects_Keeper
## 二、WinBoLL 项目核心信息
@@ -103,7 +103,3 @@ $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
aes/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

36
aes/README.md Normal file
View File

@@ -0,0 +1,36 @@
# AES
[![](https://jitpack.io/v/ZhanGSKen/AES.svg)](https://jitpack.io/#ZhanGSKen/AES)
#### 介绍
WinBoLL AndroidX 可视化元素类库。
#### 软件架构
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
#### Gradle 编译说明
调试版编译命令 gradle assembleBetaDebug
阶段版编译命令 bash .winboll/bashPublishAPKAddTag.sh aes
阶段版类库发布命令 git pull &&bash .winboll/bashPublishLIBAddTag.sh libaes
#### 使用说明
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码 : ZhanGSKen(ZhanGSKen<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/)
#### 参考文档

View File

@@ -0,0 +1 @@

51
aes/build.gradle Normal file
View File

@@ -0,0 +1,51 @@
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}")
}
}
// 米盟 SDK
packagingOptions {
doNotStrip "*/*/libmimo_1011.so"
}
}
dependencies {
api project(':libaes')
api fileTree(dir: 'libs', include: ['*.jar'])
}

8
aes/build.properties Normal file
View File

@@ -0,0 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Tue Jun 02 08:54:20 HKT 2026
stageCount=13
libraryProject=libaes
baseVersion=15.20
publishVersion=15.20.12
buildCount=0
baseBetaVersion=15.20.13

137
aes/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,137 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# ============================== 基础通用规则 ==============================
# 保留系统组件
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
# 保留 WinBoLL 核心包及子类(合并简化规则)
-keep class cc.winboll.studio.** { *; }
-keepclassmembers class cc.winboll.studio.** { *; }
# 保留所有类中的 public static final String TAG 字段(便于日志定位)
-keepclassmembers class * {
public static final java.lang.String TAG;
}
# 保留序列化类避免Parcelable/Gson解析异常
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# 保留 R 文件避免资源ID混淆
-keepclassmembers class **.R$* {
public static <fields>;
}
# 保留 native 方法避免JNI调用失败
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留注解和泛型(避免反射/序列化异常)
-keepattributes *Annotation*
-keepattributes Signature
# 屏蔽 Java 8+ 警告(适配 Java 7 语法)
-dontwarn java.lang.invoke.*
-dontwarn android.support.v8.renderscript.*
-dontwarn java.util.function.**
# ============================== 第三方框架专项规则 ==============================
# OkHttp 4.4.1米盟广告请求依赖完善Lambda兼容
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-keep class okhttp3.internal.** { *; }
-keep class okio.** { *; }
-dontwarn okhttp3.internal.platform.**
-dontwarn okio.**
# Glide 4.9.0(米盟广告图片加载依赖)
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$ImageType {
**[] $VALUES;
public *;
}
-keepclassmembers class * implements com.bumptech.glide.module.AppGlideModule {
<init>();
}
-dontwarn com.bumptech.glide.**
# Gson 2.8.5(米盟广告数据序列化依赖)
-keep class com.google.gson.** { *; }
-keep interface com.google.gson.** { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
# 米盟 SDK(核心广告组件,完整保留避免加载失败)
-keep class com.miui.zeus.** { *; }
-keep interface com.miui.zeus.** { *; }
# 保留米盟日志字段(便于广告加载失败排查)
-keepclassmembers class com.miui.zeus.mimo.sdk.** {
public static final java.lang.String TAG;
}
# RecyclerView 1.0.0(米盟广告布局渲染依赖)
-keep class androidx.recyclerview.** { *; }
-keep interface androidx.recyclerview.** { *; }
-keepclassmembers class androidx.recyclerview.widget.RecyclerView$Adapter {
public *;
}
# 其他第三方框架(按引入依赖保留,无则可删除)
# XXPermissions 18.63
-keep class com.hjq.permissions.** { *; }
-keep interface com.hjq.permissions.** { *; }
# ZXing 二维码(核心解析组件)
-keep class com.google.zxing.** { *; }
-keep class com.journeyapps.zxing.** { *; }
# Jsoup HTML解析
-keep class org.jsoup.** { *; }
# Pinyin4j 拼音搜索
-keep class net.sourceforge.pinyin4j.** { *; }
# JSch SSH组件
-keep class com.jcraft.jsch.** { *; }
# AndroidX 基础组件
-keep class androidx.appcompat.** { *; }
-keep interface androidx.appcompat.** { *; }
# ============================== 优化与调试配置 ==============================
# 优化级别(平衡混淆效果与性能)
-optimizationpasses 5
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
# 调试辅助(保留行号便于崩溃定位)
-verbose
-dontpreverify
-dontusemixedcaseclassnames
-keepattributes SourceFile,LineNumberTable

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<!-- Put flavor specific code here -->
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Put flavor specific strings here -->
<string name="app_name">AES+</string>
</resources>

View File

@@ -0,0 +1,45 @@
<?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>

View File

@@ -0,0 +1,78 @@
package cc.winboll.studio.aes;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.aes.R;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.models.APPInfo;
import cc.winboll.studio.libappbase.views.AboutView;
/**
* @Author 豆包&ZhanGSKen<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("AES");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(branchName);
appInfo.setAppGitAPPSubProjectFolder(branchName);
appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=AES");
appInfo.setAppAPKName("AES");
appInfo.setAppAPKFolderName("AES");
LogUtils.d(TAG, "genDefaultAppInfo: 应用信息已生成");
return appInfo;
}
}

View File

@@ -0,0 +1,34 @@
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.GlobalApplication;
import cc.winboll.studio.libappbase.ToastUtils;
import java.util.ArrayList;
public class App extends GlobalApplication {
public static final String TAG = "App";
@Override
public void onCreate() {
super.onCreate();
AESThemeUtil.init(null);
WinBoLLActivityManager.init(this);
// 初始化 Toast 框架
ToastUtils.init(this);
}
@Override
public void onTerminate() {
super.onTerminate();
ToastUtils.release();
}
}

View File

@@ -0,0 +1,45 @@
package cc.winboll.studio.aes;
import android.app.Activity;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.models.AESThemeBean;
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
/**
* @Author 豆包&ZhanGSKen<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;
}
}

View File

@@ -0,0 +1,197 @@
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);
}
}

View File

@@ -0,0 +1,39 @@
package cc.winboll.studio.aes;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import cc.winboll.studio.libaes.views.ADsControlView;
/**
* @Author ZhanGSKen&豆包大模型<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");
// }
// }
// });
}
}

View File

@@ -0,0 +1,33 @@
package cc.winboll.studio.aes;
import android.app.Activity;
import android.os.Bundle;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
/**
* @Author ZhanGSKen&豆包大模型<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);
}
}

View File

@@ -0,0 +1,60 @@
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);
}
}

View File

@@ -0,0 +1,32 @@
<?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>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 阴影部分 -->
<!-- 个人觉得更形象的表达top代表下边的阴影高度left代表右边的阴影宽度。其实也就是相对应的offsetsolid中的颜色是阴影的颜色也可以设置角度等等 -->
<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>

View File

@@ -0,0 +1,21 @@
<?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>

View File

@@ -0,0 +1,17 @@
<?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.ADsControlView
android:id="@+id/ads_control_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_frame"
android:padding="10dp"/>
</LinearLayout>

View File

@@ -0,0 +1,15 @@
<?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>

View File

@@ -0,0 +1,41 @@
<?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>

View File

@@ -0,0 +1,16 @@
<?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">@color/toolbarBackgroundColor</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>

View File

@@ -0,0 +1,7 @@
<?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>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">AES</string>
<string name="app_description">WinBoLL AndroidX 可视化元素类库。</string>
</resources>

View File

@@ -0,0 +1,16 @@
<?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">@color/toolbarBackgroundColor</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>

View File

@@ -0,0 +1,6 @@
<?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>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<!-- Put flavor specific code here -->
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Put flavor specific strings here -->
</resources>

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed Jun 03 19:56:34 HKT 2026
stageCount=26
#Wed May 27 14:51:29 HKT 2026
stageCount=23
libraryProject=libappbase
baseVersion=15.20
publishVersion=15.20.25
buildCount=2
baseBetaVersion=15.20.26
publishVersion=15.20.22
buildCount=0
baseBetaVersion=15.20.23

View File

@@ -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());
}
/**

View File

@@ -1,21 +0,0 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# 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=false
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=false
# 保持与旧版Gradle插件的兼容
android.disableAutomaticComponentCreation=true

0
gradlew vendored Normal file → Executable file
View File

1
libaes/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

74
libaes/build.gradle Normal file
View File

@@ -0,0 +1,74 @@
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'
}
}
// 米盟 SDK
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
packagingOptions {
doNotStrip "*/*/libmimo_1011.so"
}
}
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.22'
// 备用库 jitpack.io 地址
api 'com.github.ZhanGSKen:libappbase:appbase-v15.20.22'
api fileTree(dir: 'libs', include: ['*.jar'])
}

8
libaes/build.properties Normal file
View File

@@ -0,0 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Tue Jun 02 08:54:20 HKT 2026
stageCount=13
libraryProject=libaes
baseVersion=15.20
publishVersion=15.20.12
buildCount=0
baseBetaVersion=15.20.13

Binary file not shown.

17
libaes/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# 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 *;
#}

View File

@@ -0,0 +1,57 @@
<?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>

View File

@@ -0,0 +1,207 @@
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;
}
}
}

View File

@@ -0,0 +1,75 @@
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));
}
}

View File

@@ -0,0 +1,339 @@
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.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.libaes.views.ADsBannerView;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils;
import com.baoyz.widget.PullRefreshLayout;
import java.util.ArrayList;
public abstract class DrawerFragmentActivity extends AppCompatActivity implements IWinBoLLActivity, AdapterView.OnItemClickListener {
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);
}
}

View File

@@ -0,0 +1,204 @@
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;
}
}
}

View File

@@ -0,0 +1,57 @@
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;
}
}

View File

@@ -0,0 +1,60 @@
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();
}
}

View File

@@ -0,0 +1,32 @@
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;
}
}
}

View File

@@ -0,0 +1,67 @@
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;
}
}

View File

@@ -0,0 +1,24 @@
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();
}

View File

@@ -0,0 +1,158 @@
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;
}
}

View File

@@ -0,0 +1,169 @@
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;
}
}

View File

@@ -0,0 +1,35 @@
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;
}
}

View File

@@ -0,0 +1,78 @@
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();
}
}

View File

@@ -0,0 +1,57 @@
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);
}
}

View File

@@ -0,0 +1,25 @@
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;
}
}

View File

@@ -0,0 +1,37 @@
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;
}
}

View File

@@ -0,0 +1,48 @@
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");
}
}

View File

@@ -0,0 +1,30 @@
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);
}
}

View File

@@ -0,0 +1,124 @@
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;
}
}
}

View File

@@ -0,0 +1,226 @@
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");
}
});
}
}

View File

@@ -0,0 +1,173 @@
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];
}
}

View File

@@ -0,0 +1,162 @@
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 "";
}
}

View File

@@ -0,0 +1,35 @@
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;
}
}

View File

@@ -0,0 +1,33 @@
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);
}
}

View File

@@ -0,0 +1,97 @@
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");
}
}

View File

@@ -0,0 +1,33 @@
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);
}
}

View File

@@ -0,0 +1,64 @@
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;
}
}

View File

@@ -0,0 +1,102 @@
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();
}
});
}
}
}

View File

@@ -0,0 +1,306 @@
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());
}
}

View File

@@ -0,0 +1,28 @@
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);
}
}

View File

@@ -0,0 +1,45 @@
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);
}
}

View File

@@ -0,0 +1,19 @@
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);
}
}

View File

@@ -0,0 +1,491 @@
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;
// }
}

View File

@@ -0,0 +1,633 @@
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;
}
}

View File

@@ -0,0 +1,108 @@
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);
}
}

View File

@@ -0,0 +1,43 @@
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);
}
}

View File

@@ -0,0 +1,93 @@
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);
}
}

View File

@@ -0,0 +1,55 @@
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);
}*/
}

View File

@@ -0,0 +1,96 @@
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);
}
}

View File

@@ -0,0 +1,379 @@
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;
}
}

View File

@@ -0,0 +1,35 @@
<?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>

View File

@@ -0,0 +1,15 @@
<?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>

View File

@@ -0,0 +1,35 @@
<?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>

View File

@@ -0,0 +1,14 @@
<?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>

View File

@@ -0,0 +1,50 @@
<?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>

View File

@@ -0,0 +1,50 @@
<?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>

View File

@@ -0,0 +1,6 @@
<?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>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 阴影部分 -->
<!-- 个人觉得更形象的表达top代表下边的阴影高度left代表右边的阴影宽度。其实也就是相对应的offsetsolid中的颜色是阴影的颜色也可以设置角度等等 -->
<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>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/pressed_shape" android:state_pressed="true"></item>
<item android:drawable="@drawable/default_shape"></item>
</selector>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<!-- 颜色填充方式绘画按钮 -->
<!--<solid android:color="?attr/colorPrimaryDark" />-->
<stroke
android:width="2dp"
android:color="?attr/colorPrimary" />
<corners android:radius="15dp" />
<!-- 颜色线性渐变方式绘画按钮 -->
<gradient
android:angle="270"
android:startColor="?attr/colorAccent"
android:centerColor="?attr/colorPrimary"
android:endColor="?attr/colorPrimaryDark"/>
<padding
android:left="5dp"
android:top="5dp"
android:right="5dp"
android:bottom="5dp" />
</shape>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#ff000000"
android:pathData="M8,14V18L2,12L8,6V10H16V6L22,12L16,18V14H8Z"/>
</vector>

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