Compare commits

..

53 Commits

Author SHA1 Message Date
ZhanGSKen ebd9b64eea <winboll>APK 15.20.8 release Publish. 2026-06-24 08:11:15 +08:00
BigPickle 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
ZhanGSKen da92eb7dee 添加--no-daemon参数,解决在Termux环境下编译调用java资源环境不同的问题。 2026-06-11 21:16:33 +08:00
ZhanGSKen 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
ZhanGSKen 16c3153d95 改进应用创建函数,提高应用调试能力。 2026-06-03 07:30:38 +08:00
ZhanGSKen 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
BigPickle 76d93acdd5 feat(MyTermuxActivity): 创建桌面快捷方式时使用按钮自定义图标
新增 loadButtonIcon() 加载模型 iconPath 对应图片,
API 26+ 用 Icon.createWithBitmap,旧 API 用 EXTRA_SHORTCUT_ICON,
无图标时回退 ic_menu_manage。
2026-06-02 19:57:29 +08:00
BigPickle 7219fd0c87 feat(TermuxButton): 添加按钮自定义图标功能
- TermuxButtonModel 新增 iconPath 字段及 JSON 序列化
- MyTermuxActivity 编辑对话框增加图标导入/预览/清除
- 列表项改为 LinearLayout,图标 MATCH_PARENT 撑满高度,5dp 边距
- 图标文件保存在 getFilesDir()/termux_icons/
- 新增 strings 国际化资源(中/英)
2026-06-02 19:53:16 +08:00
BigPickle 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
BigPickle ac8b789bcb fix(Termux): 修复按钮命令未执行及返回值反转问题
- 移除 stdbuf bash 占位,改为执行命令后进入交互终端
- 替换 \n 为 ; 以支持多行命令分段执行
- 修复 executeTerminalCommand 返回值反转逻辑
- MyTermuxActivity列表文字颜色 white→black 适配浅色主题
2026-06-02 18:09:37 +08:00
BigPickle bac0a957aa refactor: 将 MyTermuxActivity 移至 termux 包并清理空目录 2026-06-02 17:01:16 +08:00
ZhanGSKen acfd4744f8 更新菜单排列方式 2026-06-02 09:33:59 +08:00
ZhanGSKen e15c8076de 更新类库 2026-06-02 09:14:33 +08:00
ZhanGSKen 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
ZhanGSKen 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
ZhanGSKen f99632cbea Merge branch 'winboll' into merge 2026-06-02 02:58:06 +08:00
ZhanGSKen c8ef451232 Merge remote-tracking branch 'origin/projects_keeper_tag' into merge 2026-06-02 02:57:59 +08:00
ZhanGSKen 92e59bdb9e 更新类库 2026-06-02 02:55:10 +08:00
ZhanGSKen 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
ZhanGSKen 25daecd8b5 <winboll>APK 15.20.3 release Publish. 2026-05-31 20:40:44 +08:00
ZhanGSKen 7b48ca8fee 去掉应用自定义调试逻辑判断。 2026-05-31 20:39:20 +08:00
ZhanGSKen 26f247b409 <winboll>APK 15.20.2 release Publish. 2026-05-31 20:28:41 +08:00
ZhanGSKen 59080de7f3 更新类库,修复工具栏风格设置问题。 2026-05-27 20:33:43 +08:00
ZhanGSKen 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
ZhanGSKen 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
ZhanGSKen d0e70407f9 修改应用包权限设置更新说明 2026-05-24 11:17:27 +08:00
ZhanGSKen 79eb4e3247 更新类库 2026-05-24 11:08:06 +08:00
ZhanGSKen 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
ZhanGSKen fdfae270d2 Merge remote-tracking branch 'origin/projects_keeper_tag' into winboll 2026-05-24 10:26:27 +08:00
qinglong ab4cef21f0 合并模块APPBase 同步最新时间标签appbase-v15.20.21 2026-05-24 10:00:01 +08:00
QingLong 2754a2ad7c 合并模块AES 同步最新时间标签aes-v15.20.8 2026-05-20 21:00:01 +08:00
ZhanGSKen d147f9dc08 更新类库版本,使用GitHub推向https://jitpack.io网站编译的类库仓库 2026-05-20 20:21:35 +08:00
QingLong 8876896cbc 合并模块APPBase 同步最新时间标签appbase-v15.20.20 2026-05-20 17:00:01 +08:00
QingLong 28e6a8ee78 合并模块APPBase 同步最新时间标签appbase-v15.20.19 2026-05-20 15:00:01 +08:00
ZhanGSKen c9272b6341 更新联系邮箱 2026-05-20 11:20:54 +08:00
QingLong 7d872fd14c 合并模块APPBase 同步最新时间标签appbase-v15.20.18 2026-05-20 04:00:01 +08:00
QingLong d6fab2133f 合并模块AES 同步最新时间标签aes-v15.20.7 2026-05-19 21:00:01 +08:00
QingLong 45821b8daa 合并模块APPBase 同步最新时间标签appbase-v15.20.17 2026-05-19 21:00:01 +08:00
QingLong edd312a525 合并模块APPBase 同步最新时间标签appbase-v15.20.16 2026-05-19 19:00:01 +08:00
QingLong aec680e36e 合并模块AES 同步最新时间标签aes-v15.20.6 2026-05-19 01:53:02 +08:00
QingLong 1c6f7783e5 合并模块AES 同步最新时间标签aes-v15.20.5 2026-05-19 01:49:06 +08:00
233 changed files with 12893 additions and 3757 deletions
+228
View File
@@ -0,0 +1,228 @@
#!/usr/bin/bash
# ==============================================================================
# WinBoLL 应用发布脚本
# 功能:检查Git源码状态 → 编译Stage Release包 → 添加WinBoLL标签 → 提交并推送源码
# 依赖:build.properties、app_update_description.txt(项目根目录下)
# 使用:./script_name.sh <APP_NAME>
# 作者:豆包&ZhanGSKen<zhangsken@qq.com>
# ==============================================================================
# ==================== 常量定义 ====================
# 脚本退出码
EXIT_CODE_SUCCESS=0
EXIT_CODE_ERR_NO_APP_NAME=2
EXIT_CODE_ERR_WORK_DIR=1
EXIT_CODE_ERR_GIT_CHECK=1
EXIT_CODE_ERR_ADD_WINBOLL_TAG=1
# Gradle 任务(正式发布)
GRADLE_TASK_PUBLISH="assembleStageRelease"
# Gradle 任务(调试用,注释备用)
# GRADLE_TASK_DEBUG="assembleBetaDebug"
# aapt2本地覆盖参数
AAPT2_OVERRIDE_ARG="-Pandroid.aapt2FromMavenOverride=/data/data/com.termux/files/usr/bin/aapt2"
# 禁用Gradle守护进程
GRADLE_NO_DAEMON="--no-daemon"
# ==================== 函数定义 ====================
# 检查Git源码是否已完全提交(无未提交变更)
# 返回值:0=已完全提交,1=存在未提交变更
function checkGitSources() {
# 配置Git安全目录(解决权限问题)
git config --global --add safe.directory "$(pwd)"
# 检查是否有未提交的变更
if [[ -n $(git diff --stat) ]]; then
echo "[ERROR] Git源码存在未提交变更,请先提交所有修改!"
return 1
fi
echo "[INFO] Git源码检查通过:所有变更已提交。"
return 0
}
# 询问是否添加GitHub Workflows标签(当前逻辑注释,保留扩展能力)
# 返回值:1=用户选择是,0=用户选择否
function askAddWorkflowsTag() {
read -p "是否添加GitHub Workflows标签?(Y/n) " answer
if [[ $answer =~ ^[Yy]$ ]]; then
return 1
else
return 0
fi
}
# 添加WinBoLL正式标签
# 参数:$1=应用名称(项目根目录名)
# 返回值:0=标签添加成功,1=标签已存在/添加失败
function addWinBoLLTag() {
local app_name=$1
local build_prop_path="${app_name}/build.properties"
# 从build.properties中提取publishVersion
local publish_version=$(grep -o "publishVersion=.*" "${build_prop_path}" | awk -F '=' '{print $2}')
if [[ -z ${publish_version} ]]; then
echo "[ERROR] 未从${build_prop_path}中提取到publishVersion配置!"
return 1
fi
echo "[INFO] 从${build_prop_path}读取到publishVersion${publish_version}"
# 构造WinBoLL标签(格式:<APP_NAME>-v<publishVersion>
local tag="${app_name}-v${publish_version}"
echo "[INFO] 准备添加WinBoLL标签:${tag}"
# 检查标签是否已存在
if [[ "$(git tag -l ${tag})" == "${tag}" ]]; then
echo "[ERROR] WinBoLL标签${tag}已存在!"
return 1
fi
# 添加带注释的标签(注释来自app_update_description.txt
git tag -a "${tag}" -F "${app_name}/app_update_description.txt"
echo "[INFO] WinBoLL标签${tag}添加成功!"
return 0
}
# 添加GitHub Workflows Beta标签(当前逻辑注释,保留扩展能力)
# 参数:$1=应用名称(项目根目录名)
# 返回值:0=标签添加成功,1=标签已存在/添加失败
function addWorkflowsTag() {
local app_name=$1
local build_prop_path="${app_name}/build.properties"
# 从build.properties中提取baseBetaVersion
local base_beta_version=$(grep -o "baseBetaVersion=.*" "${build_prop_path}" | awk -F '=' '{print $2}')
if [[ -z ${base_beta_version} ]]; then
echo "[ERROR] 未从${build_prop_path}中提取到baseBetaVersion配置!"
return 1
fi
echo "[INFO] 从${build_prop_path}读取到baseBetaVersion${base_beta_version}"
# 构造Workflows标签(格式:<APP_NAME>-v<baseBetaVersion>-beta
local tag="${app_name}-v${base_beta_version}-beta"
echo "[INFO] 准备添加Workflows标签:${tag}"
# 检查标签是否已存在
if [[ "$(git tag -l ${tag})" == "${tag}" ]]; then
echo "[ERROR] Workflows标签${tag}已存在!"
return 1
fi
# 添加带注释的标签(注释来自app_update_description.txt
git tag -a "${tag}" -F "${app_name}/app_update_description.txt"
echo "[INFO] Workflows标签${tag}添加成功!"
return 0
}
# ==================== 主流程开始 ====================
echo "============================================="
echo " WinBoLL 应用发布脚本"
echo "============================================="
# 1. 检查应用名称参数是否指定
if [ -z "$1" ]; then
echo "[ERROR] 未指定应用名称!使用方式:${0} <APP_NAME>"
exit ${EXIT_CODE_ERR_NO_APP_NAME}
fi
APP_NAME=$1
echo "[INFO] 待发布应用名称:${APP_NAME}"
# 2. 检查并切换到项目根目录(确保build.properties存在)
echo "[INFO] 当前工作目录:$(pwd)"
if [[ ! -e "${APP_NAME}/build.properties" ]]; then
echo "[WARNING] 当前目录不存在${APP_NAME}/build.properties,尝试切换到上级目录..."
cd ..
echo "[INFO] 切换后工作目录:$(pwd)"
fi
# 验证最终工作目录是否正确
if [[ ! -e "${APP_NAME}/build.properties" ]]; then
echo "[ERROR] 工作目录错误!${APP_NAME}/build.properties 文件不存在。"
exit ${EXIT_CODE_ERR_WORK_DIR}
fi
echo "[INFO] 工作目录验证通过:${APP_NAME}/build.properties 存在。"
# 3. 检查Git源码状态
echo "---------------------------------------------"
echo " 步骤1:检查Git源码状态"
echo "---------------------------------------------"
checkGitSources
if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then
echo "[ERROR] Git源码检查失败,脚本终止!"
exit ${EXIT_CODE_ERR_GIT_CHECK}
fi
# 4. 编译Stage Release版本APK(携带aapt2覆盖参数 + --no-daemon
echo "---------------------------------------------"
echo " 步骤2:编译Stage Release APK"
echo "---------------------------------------------"
echo "[INFO] 开始执行Gradle任务:${GRADLE_TASK_PUBLISH}"
# 调试用(注释正式任务,启用调试任务)
# bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :${APP_NAME}:${GRADLE_TASK_DEBUG}
bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :${APP_NAME}:${GRADLE_TASK_PUBLISH}
if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then
echo "[ERROR] Gradle编译任务失败!"
exit 1
fi
echo "[INFO] Stage Release APK编译成功!"
# 5. 添加WinBoLL正式标签
echo "---------------------------------------------"
echo " 步骤3:添加WinBoLL标签"
echo "---------------------------------------------"
addWinBoLLTag ${APP_NAME}
if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then
echo "[ERROR] WinBoLL标签添加失败,脚本终止!"
exit ${EXIT_CODE_ERR_ADD_WINBOLL_TAG}
fi
# 6. (可选)添加GitHub Workflows标签(当前逻辑注释,保留扩展能力)
# echo "---------------------------------------------"
# echo " 步骤4:添加Workflows标签(可选)"
# echo "---------------------------------------------"
# echo "是否添加GitHub Workflows Beta标签?(Y/n) "
# askAddWorkflowsTag
# nAskAddWorkflowsTag=$?
# if [[ ${nAskAddWorkflowsTag} -eq 1 ]]; then
# addWorkflowsTag ${APP_NAME}
# if [[ $? -ne ${EXIT_CODE_SUCCESS} ]]; then
# echo "[ERROR] Workflows标签添加失败,脚本终止!"
# exit 1
# fi
# fi
# 7. 清理更新描述文件
echo "---------------------------------------------"
echo " 步骤5:清理更新描述文件"
echo "---------------------------------------------"
echo "" > "${APP_NAME}/app_update_description.txt"
echo "[INFO] 已清空${APP_NAME}/app_update_description.txt"
# 8. 提交并推送源码与标签
echo "---------------------------------------------"
echo " 步骤6:提交并推送源码"
echo "---------------------------------------------"
git add .
git commit -m "<${APP_NAME}> 开始新的Stage版本开发。"
echo "[INFO] 源码提交成功,开始推送..."
# 推送源码到远程仓库
git push origin
# 推送标签到远程仓库
git push origin --tags
if [[ $? -eq ${EXIT_CODE_SUCCESS} ]]; then
echo "[INFO] 源码与标签推送成功!"
else
echo "[ERROR] 源码与标签推送失败!"
exit 1
fi
# ==================== 主流程结束 ====================
echo "============================================="
echo " WinBoLL 应用发布完成!"
echo "============================================="
exit ${EXIT_CODE_SUCCESS}
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/bash
# aapt2本地覆盖参数
AAPT2_OVERRIDE_ARG="-Pandroid.aapt2FromMavenOverride=/data/data/com.termux/files/usr/bin/aapt2"
# Gradle禁用守护进程参数
GRADLE_NO_DAEMON="--no-daemon"
# 检查是否指定了将要发布的类库名称
# 使用 `-z` 命令检查变量是否为空
if [ -z "$1" ]; then
echo "No Library name specified : $0"
exit 2
fi
## 正式发布使用
git pull && bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :$1:publishReleasePublicationToWinBoLLReleaseRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1
## 调试使用
#bash gradlew ${AAPT2_OVERRIDE_ARG} ${GRADLE_NO_DAEMON} :$1:publishSnapshotWinBoLLPublicationToWinBoLLSnapshotRepository && bash .winboll/bashCommitLibReleaseBuildFlagInfo.sh $1
+56 -27
View File
@@ -122,7 +122,6 @@ android {
// 如果正在调试,就拷贝到 WinBoLL 备份管理文件夹
//
if(variant.flavorName == "beta"&&variant.buildType.name == "debug"){
//File outBuildBckDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/${variant.buildType.name}")
File outBuildBckDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}")
// 创建目标路径目录
if(!outBuildBckDir.exists()) {
@@ -130,6 +129,7 @@ android {
println "Output Folder Created.(WinBoLLStudio) : " + outBuildBckDir.getAbsolutePath()
}
if(outBuildBckDir.exists()) {
def targetApkFile = new File(outBuildBckDir, outputFileName)
copy{
from file.outputFile
into outBuildBckDir
@@ -138,6 +138,14 @@ android {
}
println "Output APK (WinBoLLStudio): " + outBuildBckDir.getAbsolutePath() + "/${outputFileName}"
}
// ========== 设置文件权限为775 ==========
if(targetApkFile.exists()){
exec {
commandLine 'chmod', '775', targetApkFile.absolutePath
}
println "Set file permission to 775 : ${targetApkFile.absolutePath}"
}
// 检查编译标志位配置
assert (winbollBuildProps['buildCount'] != null)
assert (winbollBuildProps['libraryProject'] != null)
@@ -160,8 +168,7 @@ android {
assert(libraryProjectBuildPropsFile.exists())
java.nio.file.Path sourceFilePath = winbollBuildPropsFile.toPath();
java.nio.file.Path targetFilePath = libraryProjectBuildPropsFile.toPath();
// 使用copyTo()方法复制文件,如果目标文件存在会被覆盖,可选参数可以选择不覆盖
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
println "\n\n>>> Library Project build.properties saved.\n\n";
}
@@ -172,16 +179,12 @@ android {
//
if(variant.flavorName == "stage"&&variant.buildType.name == "release"){
// 发布 APK 文件
//
// 截取版本号的版本字段为短版本名
String szVersionName = "${versionName}"
String[] szlistTemp = szVersionName.split("-")
String szShortVersionName = szlistTemp[0]
//String szCommonTagAPKName = "${rootProject.name}_" + szShortVersionName + ".apk"
String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk"
println "CommonTagAPKName is : " + szCommonTagAPKName
//File outTagDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/tag/")
File outTagDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/tag/")
// 创建目标路径目录
if(!outTagDir.exists()) {
@@ -192,12 +195,10 @@ android {
if(outTagDir.exists()) {
File targetAPK = new File(outTagDir, "${szCommonTagAPKName}")
if(targetAPK.exists()) {
// 标签版本APK文件已经存在,构建拷贝任务停止
assert (!targetAPK.exists())
// 可选择删除并继续输出APK文件
//delete targetAPK
}
// 复制一个备份
// 复制完整版APK
def fullApkFile = new File(outTagDir, outputFileName)
copy{
from file.outputFile
into outTagDir
@@ -206,7 +207,16 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${outputFileName}"
}
// 复制一个并重命名为短版本名
// 设置权限775。
if(fullApkFile.exists()){
exec {
commandLine 'chmod', '775', fullApkFile.absolutePath
}
println "Set file permission to 775 : ${fullApkFile.absolutePath}"
}
// 复制短版本名APK
def shortApkFile = new File(outTagDir, szCommonTagAPKName)
copy{
from file.outputFile
into outTagDir
@@ -215,6 +225,14 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${szCommonTagAPKName}"
}
// 设置权限775。
if(shortApkFile.exists()){
exec {
commandLine 'chmod', '775', shortApkFile.absolutePath
}
println "Set file permission to 775 : ${shortApkFile.absolutePath}"
}
// 检查编译标志位配置
assert (winbollBuildProps['stageCount'] != null)
assert (winbollBuildProps['publishVersion'] != null)
@@ -239,14 +257,11 @@ android {
fos.close();
if(winbollBuildProps['libraryProject'] != "") {
// 如果应用 build.properties 文件设置了类库模块项目文件名
// 就拷贝一份新的编译标志配置到类库项目文件夹
File libraryProjectBuildPropsFile = new File("$RootProjectDir/" + winbollBuildProps['libraryProject'] + "/build.properties")
assert(winbollBuildPropsFile.exists())
assert(libraryProjectBuildPropsFile.exists())
java.nio.file.Path sourceFilePath = winbollBuildPropsFile.toPath();
java.nio.file.Path targetFilePath = libraryProjectBuildPropsFile.toPath();
// 使用copyTo()方法复制文件,如果目标文件存在会被覆盖,可选参数可以选择不覆盖
java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
}
@@ -263,17 +278,12 @@ android {
// 如果正在调试发布版,就只生成和输出APK文件,不处理 Git 仓库提交与更新问题。
//
if(variant.flavorName == "stage"&&variant.buildType.name == "debug"){
// 发布 APK 文件
//
// 截取版本号的版本字段为短版本名
String szVersionName = "${versionName}"
String[] szlistTemp = szVersionName.split("-")
String szShortVersionName = szlistTemp[0]
//String szCommonTagAPKName = "${rootProject.name}_" + szShortVersionName + ".apk"
String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk"
println "CommonTagAPKName is : " + szCommonTagAPKName
//File outTagDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/tag/")
File outTagDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}/")
// 创建目标路径目录
if(!outTagDir.exists()) {
@@ -284,13 +294,11 @@ android {
if(outTagDir.exists()) {
File targetAPK = new File(outTagDir, "${szCommonTagAPKName}")
if(targetAPK.exists()) {
// 标签版本APK文件已经存在,构建拷贝任务停止
println '如果是在调试 Stage 版应用包构建,请删除(注:在debug目录)现有的 Stage 应用包('+targetAPK.getAbsolutePath()+')。再编译一次。'
assert (!targetAPK.exists())
// 可选择删除并继续输出APK文件
//delete targetAPK
}
// 复制一个备份
// 复制完整版APK
def debugFullApk = new File(outTagDir, outputFileName)
copy{
from file.outputFile
into outTagDir
@@ -299,7 +307,16 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${outputFileName}"
}
// 复制一个并重命名为短版本名
// 权限设为775。
if(debugFullApk.exists()){
exec {
commandLine 'chmod', '775', debugFullApk.absolutePath
}
println "Set file permission to 775 : ${debugFullApk.absolutePath}"
}
// 复制短版本名APK
def debugShortApk = new File(outTagDir, szCommonTagAPKName)
copy{
from file.outputFile
into outTagDir
@@ -308,8 +325,13 @@ android {
}
println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${szCommonTagAPKName}"
}
//不保存编译标志配置
// 权限设为775
if(debugShortApk.exists()){
exec {
commandLine 'chmod', '775', debugShortApk.absolutePath
}
println "Set file permission to 775 : ${debugShortApk.absolutePath}"
}
}
}
@@ -328,6 +350,13 @@ android {
}
println "Output APK (Common): " + outCommonDir.getAbsolutePath() + "/${commandAPKName}"
}
// 额外输出文件设置775权限
if(apkFile.exists()){
exec {
commandLine 'chmod', '775', apkFile.absolutePath
}
println "Set file permission to 775 : ${apkFile.absolutePath}"
}
}
}
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+1 -1
View File
@@ -20,7 +20,7 @@ WinBoLL AndroidX 可视化元素类库。
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码 : ZhanGSKen(ZhanGSKen<zhangsken@188.com>)
3. 提交代码 : ZhanGSKen(ZhanGSKen<ZhanGSKen@QQ.COM>)
4. 新建 Pull Request
+4 -1
View File
@@ -26,7 +26,10 @@ android {
applicationId "cc.winboll.studio.aes"
minSdkVersion 26
targetSdkVersion 30
versionCode 1
//1. Android 官方规则
//-  versionCode  类型:int 整型
//- Java int 最大值:2147483647
versionCode 1520000
// versionName 更新后需要手动设置
// 项目模块目录的 build.gradle 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
+4 -4
View File
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Sun May 17 20:53:39 HKT 2026
stageCount=5
#Tue Jun 02 08:54:20 HKT 2026
stageCount=13
libraryProject=libaes
baseVersion=15.20
publishVersion=15.20.4
publishVersion=15.20.12
buildCount=0
baseBetaVersion=15.20.5
baseBetaVersion=15.20.13
@@ -5,10 +5,11 @@ package cc.winboll.studio.aes;
* @Date 2024/06/13 19:03:58
* @Describe AES应用类
*/
import android.view.Gravity;
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 {
@@ -18,8 +19,7 @@ public class App extends GlobalApplication {
@Override
public void onCreate() {
super.onCreate();
setIsDebugging(BuildConfig.DEBUG);
//setIsDebugging(false);
AESThemeUtil.init(null);
WinBoLLActivityManager.init(this);
// 初始化 Toast 框架
@@ -84,11 +84,12 @@ public class MainActivity extends DrawerFragmentActivity {
@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 super.onCreateOptionsMenu(menu);
return true;
}
@Override
+1 -1
View File
@@ -20,7 +20,7 @@ WinBoLL 安卓手机端安卓应用开发基础类库。
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码 : ZhanGSKen(ZhanGSKen<zhangsken@188.com>)
3. 提交代码 : ZhanGSKen(ZhanGSKen<ZhanGSKen@QQ.COM>)
4. 新建 Pull Request
+4 -1
View File
@@ -26,7 +26,10 @@ android {
applicationId "cc.winboll.studio.appbase"
minSdkVersion 26
targetSdkVersion 30
versionCode 1
//1. Android 官方规则
//-  versionCode  类型:int 整型
//- Java int 最大值:2147483647
versionCode 1520000
// versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
+4 -4
View File
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Sun May 17 16:16:36 HKT 2026
stageCount=16
#Wed May 27 14:51:29 HKT 2026
stageCount=23
libraryProject=libappbase
baseVersion=15.20
publishVersion=15.20.15
publishVersion=15.20.22
buildCount=0
baseBetaVersion=15.20.16
baseBetaVersion=15.20.23
@@ -37,7 +37,7 @@ public class AboutActivity extends Activity {
appInfo.setAppName("APPBase");
appInfo.setAppIcon(R.drawable.ic_winboll);
appInfo.setAppDescription(getString(R.string.app_description));
appInfo.setAppGitName("WinBoLL");
appInfo.setAppGitName("APPBase");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(branchName);
appInfo.setAppGitAPPSubProjectFolder(branchName);
@@ -22,12 +22,6 @@ public class App extends GlobalApplication {
@Override
public void onCreate() {
super.onCreate();
// 如果应用不在调试状态,就根据编译类型设置调试状态
if (isDebugging() != true) {
setIsDebugging(BuildConfig.DEBUG);
}
// release 版调试码
//setIsDebugging(!BuildConfig.DEBUG);
// 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用)
ToastUtils.init(getApplicationContext());
@@ -2,8 +2,8 @@ package cc.winboll.studio.appbase.model;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.models.libs1520000.BaseBean;
import java.io.IOException;
/**
+2 -2
View File
@@ -66,9 +66,9 @@ dependencies {
//annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
// WinBoLL库 nexus.winboll.cc 地址
api 'cc.winboll.studio:libappbase:15.20.9'
//api 'cc.winboll.studio:libappbase:15.20.22'
// 备用库 jitpack.io 地址
//api 'com.github.ZhanGSKen:APPBase:appbase-v15.15.3'
api 'com.github.ZhanGSKen:libappbase:appbase-v15.20.22'
api fileTree(dir: 'libs', include: ['*.jar'])
}
+4 -4
View File
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Sun May 17 20:53:39 HKT 2026
stageCount=5
#Tue Jun 02 08:54:20 HKT 2026
stageCount=13
libraryProject=libaes
baseVersion=15.20
publishVersion=15.20.4
publishVersion=15.20.12
buildCount=0
baseBetaVersion=15.20.5
baseBetaVersion=15.20.13
@@ -15,6 +15,7 @@ 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;
@@ -22,8 +23,10 @@ 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;
@@ -34,8 +37,8 @@ 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;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
public abstract class DrawerFragmentActivity extends AppCompatActivity implements IWinBoLLActivity, AdapterView.OnItemClickListener {
@@ -44,7 +47,6 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
static final String SHAREDPREFERENCES_NAME = "SHAREDPREFERENCES_NAME";
static final String DRAWER_THEME_TYPE = "DRAWER_THEME_TYPE";
//protected Context mContext;
ActivityType mActivityType;
ActionBarDrawerToggle mActionBarDrawerToggle;
DrawerLayout mDrawerLayout;
@@ -59,13 +61,14 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
public enum ActivityType { Main, Secondary }
protected volatile AESThemeBean.ThemeType mThemeType;
protected ArrayList<DrawerMenuBean> malDrawerMenuItem;
abstract protected ActivityType initActivityType();
//abstract protected View initContentView(LayoutInflater inflater, ViewGroup rootView);
@Override
protected void onCreate(Bundle savedInstanceState) {
// 替换:使用工具类统一应用主题
AESThemeUtil.applyAppCompatTheme(this);
mThemeType = AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext()));
setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext()));
super.onCreate(savedInstanceState);
WinBoLLActivityManager.getInstance().add(this);
mActivityType = initActivityType();
@@ -78,53 +81,32 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onDestroy() {
WinBoLLActivityManager.getInstance().registeRemove(this);
WinBoLLActivityManager.getInstance().registeRemove(this);
super.onDestroy();
// 修复:释放广告资源,避免内存泄漏
ADsBannerView adsBannerView = findViewById(R.id.adsbanner);
if (adsBannerView != null) {
adsBannerView.releaseAdResources();
}
// 修复:释放广告资源,避免内存泄漏
ADsBannerView adsBannerView = findViewById(R.id.adsbanner);
if (adsBannerView != null) {
adsBannerView.releaseAdResources();
}
}
/*@Override
public Intent getIntent() {
// TODO: Implement this method
return super.getIntent();
}
public Context getContext() {
return this.mContext;
}*/
@Override
public MenuInflater getMenuInflater() {
// TODO: Implement this method
return super.getMenuInflater();
}
/*public void setSubtitle(CharSequence context) {
// TODO: Implement this method
getSupportActionBar().setSubtitle(context);
}*/
@Override
public void recreate() {
super.recreate();
}
/*@Override
public boolean moveTaskToBack(boolean nonRoot) {
return super.moveTaskToBack(nonRoot);
}*/
@Override
public void startActivity(Intent intent) {
super.startActivity(intent);
@@ -135,26 +117,6 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
super.startActivityForResult(intent, requestCode, options);
}
/*@Override
public FragmentManager getSupportFragmentManager() {
return super.getSupportFragmentManager();
}
public void setSubtitle(int resId) {
// TODO: Implement this method
getSupportActionBar().setSubtitle(resId);
}
public void setTitle(CharSequence context) {
// TODO: Implement this method
getSupportActionBar().setTitle(context);
}
public void setTitle(int resId) {
// TODO: Implement this method
getSupportActionBar().setTitle(resId);
}*/
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return super.getSharedPreferences(name, mode);
@@ -162,7 +124,6 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
@Override
public Context getApplicationContext() {
// TODO: Implement this method
return super.getApplicationContext();
}
@@ -173,25 +134,27 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (AESThemeUtil.onAppThemeItemSelected(this, 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);
}
}
if (DevelopUtils.onDevelopItemSelected(this, item)) {
LogUtils.d(TAG, String.format("onOptionsItemSelected item.getItemId() %d ", item.getItemId()));
} else {
return super.onOptionsItemSelected(item);
}
return true;
return true;
}
@Override
protected void onResume() {
super.onResume();
ADsBannerView adsBannerView = findViewById(R.id.adsbanner);
if (adsBannerView != null) {
adsBannerView.resumeADs(DrawerFragmentActivity.this);
}
ADsBannerView adsBannerView = findViewById(R.id.adsbanner);
if (adsBannerView != null) {
adsBannerView.resumeADs(DrawerFragmentActivity.this);
}
}
void initRootView() {
@@ -213,14 +176,13 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
mPullRefreshLayout = findViewById(R.id.activitydrawerfragmentPullRefreshLayout1);
mPullRefreshLayout.setOnRefreshListener(new PullRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
//LogUtils.d(TAG, "onRefresh");
reinitDrawerMenuItemList(malDrawerMenuItem);
mDrawerMenuDataAdapter.notifyDataSetChanged();
mPullRefreshLayout.setRefreshing(false);
}
});
@Override
public void onRefresh() {
reinitDrawerMenuItemList(malDrawerMenuItem);
mDrawerMenuDataAdapter.notifyDataSetChanged();
mPullRefreshLayout.setRefreshing(false);
}
});
malDrawerMenuItem = new ArrayList<DrawerMenuBean>();
@@ -236,68 +198,51 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
mActionBarDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.lib_name, R.string.lib_name) {
@Override
public void onDrawerOpened(View drawerView) {//完全打开时触发
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
mIsDrawerOpened = true;
mIsDrawerOpening = false;
//Toast.makeText(MainActivity.this,"onDrawerOpened",Toast.LENGTH_SHORT).show();
}
@Override
public void onDrawerClosed(View drawerView) {//完全关闭时触发
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
mIsDrawerOpened = false;
mIsDrawerClosing = false;
//Toast.makeText(MainActivity.this,"onDrawerClosed",Toast.LENGTH_SHORT).show();
}
/**
* 当抽屉被滑动的时候调用此方法
* slideOffset表示 滑动的幅度(0-1
*/
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, slideOffset);
}
/**
* 当抽屉滑动状态改变的时候被调用
* 状态值是STATE_IDLE(闲置--0, STATE_DRAGGING(拖拽的--1, STATE_SETTLING(固定--2)中之一。
*具体状态可以慢慢调试
*/
@Override
public void onDrawerStateChanged(int newState) {
super.onDrawerStateChanged(newState);
}
};
//设置显示旋转菜单
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//通过下面这句实现toolbar和Drawer的联动:如果没有这行代码,箭头是不会随着侧滑菜单的开关而变换的(或者没有箭头),
// 可以尝试一下,不影响正常侧滑
mActionBarDrawerToggle.syncState();
mDrawerLayout.setDrawerListener(mActionBarDrawerToggle);
//去掉侧滑的默认图标(动画箭头图标),也可以选择不去,
//不去的话把这一行注释掉或者改成true,然后把toolbar.setNavigationIcon注释掉就行了
//mActionBarDrawerToggle.setDrawerIndicatorEnabled(false);
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;
}
}
});
@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);
}
@@ -305,12 +250,11 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
void initSecondaryRootView() {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//LogUtils.d(TAG, "onClick " + Integer.toString(v.getId()));
finish();
}
});
@Override
public void onClick(View v) {
finish();
}
});
}
public <T extends Fragment> int removeFragment(T fragment) {
@@ -375,13 +319,13 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (mActivityType == ActivityType.Main) {
// 主题菜单
AESThemeUtil.inflateMenu(this, menu);
// 调试工具菜单
if (GlobalApplication.isDebugging()) {
DevelopUtils.inflateMenu(this, menu);
}
// 应用信息菜单
// 替换为兼容版菜单加载方法
AESThemeUtil.inflateCompatThemeMenu(this, menu);
// 调试工具菜单
if (GlobalApplication.isDebugging()) {
DevelopUtils.inflateMenu(this, menu);
}
// 应用信息菜单
getMenuInflater().inflate(R.menu.toolbar_drawerbase, menu);
}
return super.onCreateOptionsMenu(menu);
@@ -392,3 +336,4 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement
super.onActivityResult(who, targetFragment, requestCode);
}
}
@@ -8,8 +8,9 @@ package cc.winboll.studio.libaes.models;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libaes.R;
import cc.winboll.studio.libappbase.BaseBean;
import cc.winboll.studio.libappbase.models.libs1520000.BaseBean;
import java.io.IOException;
import java.util.ArrayList;
public class AESThemeBean extends BaseBean {
@@ -41,13 +42,28 @@ public class AESThemeBean extends BaseBean {
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;
}
@@ -59,7 +75,7 @@ public class AESThemeBean extends BaseBean {
public int getCurrentThemeTypeID() {
return this.currentThemeStyleID;
}
@Override
public String getName() {
return AESThemeBean.class.getName();
@@ -74,8 +90,7 @@ public class AESThemeBean extends BaseBean {
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if(super.initObjectsFromJsonReader(jsonReader, name)) { return true; }
else{
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("currentThemeTypeID")) {
setCurrentThemeTypeID(jsonReader.nextInt());
} else {
@@ -90,7 +105,7 @@ public class AESThemeBean extends BaseBean {
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if(!initObjectsFromJsonReader(jsonReader, name)) {
if (!initObjectsFromJsonReader(jsonReader, name)) {
jsonReader.skipValue();
}
}
@@ -7,7 +7,7 @@ package cc.winboll.studio.libaes.models;
import android.content.Context;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import cc.winboll.studio.libappbase.models.libs1520000.BaseBean;
import java.io.IOException;
public class WinBoLLClientServiceBean extends BaseBean {
@@ -12,7 +12,6 @@ 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.libaes.views.ASupportToolbar;
import cc.winboll.studio.libappbase.LogUtils;
public class TestASupportToolbarActivity extends AppCompatActivity implements IWinBoLLActivity {
@@ -28,11 +27,12 @@ public class TestASupportToolbarActivity extends AppCompatActivity implements IW
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
LogUtils.d(TAG, "onCreate() start");
AESThemeUtil.applyAppTheme(this);
// 替换此处:原 applyAppTheme -> 新方法 applyAppCompatTheme
AESThemeUtil.applyAppCompatTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testasupporttoolbar);
LogUtils.d(TAG, "setContentView() done");
@@ -45,3 +45,4 @@ public class TestASupportToolbarActivity extends AppCompatActivity implements IW
LogUtils.d(TAG, "onCreate() end");
}
}
@@ -14,10 +14,11 @@ import cc.winboll.studio.libaes.utils.AESThemeUtil;
public class TestAToolbarActivity extends Activity {
public static final String TAG = "TestAToolbarActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
AESThemeUtil.applyAppTheme(this);
// 原生Activity 使用 applyTheme
AESThemeUtil.applyTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testatoolbar);
Toolbar toolbar = findViewById(R.id.activitytestatoolbarAToolbar1);
@@ -26,3 +27,4 @@ public class TestAToolbarActivity extends Activity {
}
}
@@ -9,203 +9,165 @@ 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";
static final String SHAREDPREFERENCES_NAME = "SHAREDPREFERENCES_NAME";
static final String DRAWER_THEME_TYPE = "DRAWER_THEME_TYPE";
// 私有静态集合,外部不可直接修改
private static ArrayList<Integer> themeStyleIDList = new ArrayList<>();
protected volatile AESThemeBean.ThemeType mThemeType;
// 移除无用实例成员 mThemeType,工具类不保留实例字段
public static <T extends Context> int getThemeTypeID(T context) {
AESThemeBean bean = AESThemeBean.loadBean(context, AESThemeBean.class);
return bean == null ? AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.AES): bean.getCurrentThemeTypeID();
/**
* 初始化主题样式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);
}
public static <T extends Context> void saveThemeStyleID(T context, int nThemeTypeID) {
AESThemeBean bean = new AESThemeBean(nThemeTypeID);
/**
* 获取当前主题样式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);
}
public static <T extends Activity> void applyAppTheme(T activity) {
// ====================== 应用主题 - 规范重载 ======================
/**
* 应用当前持久化主题(通用 Activity)
*/
public static void applyTheme(Activity activity) {
activity.setTheme(getThemeTypeID(activity));
}
public static <T extends AppCompatActivity> void applyAppCompatTheme(T 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));
}
/*public static <T extends WinBoLLActivity> void applyWinBoLLTheme(T activity) {
activity.setTheme(getThemeTypeID(activity.getApplicationContext()));
}*/
public static <T extends Activity> void applyAppTheme(Activity activity, AESThemeBean.ThemeType themeType) {
activity.setTheme(AESThemeBean.getThemeStyleID(themeType));
/**
* 应用指定主题(AppCompat 兼容 Activity
*/
public static void applyAppCompatTheme(AppCompatActivity activity, AESThemeBean.ThemeType themeType) {
activity.setTheme(getThemeStyleID(themeType));
}
public static <T extends AppCompatActivity> void applyAppCompatTheme(Activity activity, AESThemeBean.ThemeType themeType) {
activity.setTheme(AESThemeBean.getThemeStyleID(themeType));
}
/*public static <T extends WinBoLLActivity> void applyWinBoLLTheme(Activity activity, AESThemeBean.ThemeType themeType) {
activity.setTheme(AESThemeBean.getThemeStyleID(themeType));
}*/
public static <T extends Activity> void inflateMenu(T activity, Menu menu) {
// ====================== 加载菜单 ======================
/**
* 加载主题菜单(通用 Activity)
*/
public static void inflateThemeMenu(Activity activity, Menu menu) {
activity.getMenuInflater().inflate(R.menu.toolbar_apptheme, menu);
}
public static <T extends AppCompatActivity> void inflateCompatMenu(T activity, Menu menu) {
/**
* 加载主题菜单(AppCompat Activity
*/
public static void inflateCompatThemeMenu(AppCompatActivity activity, Menu menu) {
activity.getMenuInflater().inflate(R.menu.toolbar_apptheme, menu);
}
/*public static <T extends WinBoLLActivity> void inflateWinBoLLMenu(T activity, Menu menu) {
activity.getMenuInflater().inflate(R.menu.toolbar_apptheme, menu);
}*/
public static <T extends Activity> boolean onAppThemeItemSelected(T activity, MenuItem item) {
int nThemeStyleID;
if (R.id.item_depththeme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.DEPTH);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_skytheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.SKY);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_goldentheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.GOLDEN);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_bearingtheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.BEARING);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_memortheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.MEMOR);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_taotheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.TAO);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_defaulttheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.AES);
saveThemeStyleID(activity, nThemeStyleID);
return true;
// ====================== 菜单点击统一核心逻辑(消除重复代码) ======================
/**
* 主题菜单项点击统一处理
* @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;
}
return false;
saveThemeStyleID(context, themeStyleId);
return true;
}
public static <T extends AppCompatActivity> boolean onAppCompatThemeItemSelected(T activity, MenuItem item) {
int nThemeStyleID;
if (R.id.item_depththeme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.DEPTH);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_skytheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.SKY);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_goldentheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.GOLDEN);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_bearingtheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.BEARING);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_memortheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.MEMOR);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_taotheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.TAO);
saveThemeStyleID(activity, nThemeStyleID);
return true;
} else if (R.id.item_defaulttheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.AES);
saveThemeStyleID(activity, nThemeStyleID);
return true;
}
return false;
// 对外暴露不同 Activity 类型的入口,内部调用统一核心方法
public static boolean onThemeItemSelected(Activity activity, MenuItem item) {
return handleThemeMenuClick(activity, item);
}
public static <T extends AppCompatActivity> boolean onWinBoLLThemeItemSelected(T activity, MenuItem item) {
int nThemeStyleID;
if (R.id.item_depththeme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.DEPTH);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_skytheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.SKY);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_goldentheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.GOLDEN);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_bearingtheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.BEARING);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_memortheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.MEMOR);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_taotheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.TAO);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_defaulttheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.AES);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
}
return false;
public static boolean onAppCompatThemeItemSelected(AppCompatActivity activity, MenuItem item) {
return handleThemeMenuClick(activity, item);
}
public static <T extends DrawerFragmentActivity> boolean onWinBoLLThemeItemSelected(T activity, MenuItem item) {
int nThemeStyleID;
if (R.id.item_depththeme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.DEPTH);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_skytheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.SKY);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_goldentheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.GOLDEN);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_bearingtheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.BEARING);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_memortheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.MEMOR);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_taotheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.TAO);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
} else if (R.id.item_defaulttheme == item.getItemId()) {
nThemeStyleID = AESThemeBean.getThemeStyleID(AESThemeBean.ThemeType.AES);
saveThemeStyleID(activity.getApplicationContext(), nThemeStyleID);
return true;
}
public static boolean onWinBoLLThemeItemSelected(AppCompatActivity activity, MenuItem item) {
// 使用 Application 上下文保存,避免 Activity 泄漏
return handleThemeMenuClick(activity.getApplicationContext(), item);
}
return false;
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];
}
}
+8 -8
View File
@@ -3,15 +3,15 @@
<color name="colorTextColor">#FFFFFFFF</color>
<color name="colorPrimary">#FF03AB4E</color>
<color name="colorPrimaryDark">#FF027C39</color>
<color name="colorAccent">#FF3DDC84</color>
<color name="colorPrimaryDark">#FF3DDC84</color>
<color name="colorAccent">#FF027C39</color>
<color name="colorText">#FFFFFB8D</color>
<color name="colorToastFrame">#FFA9A9A9</color>
<color name="colorToastFrame">#FF555555</color>
<color name="colorToastShadow">#FF000000</color>
<color name="colorToastBackgroung">#FFFFFFFF</color>
<color name="colorAToolbarStartColor">#FF7D3F12</color>
<color name="colorAToolbarCenterColor">#FFCC6E2B</color>
<color name="colorAToolbarEndColor">#FFF4B98F</color>
<color name="colorToastBackgroung">#FF3A3A3A</color>
<color name="colorAToolbarStartColor">#FF5A3A1A</color>
<color name="colorAToolbarCenterColor">#FFA05A2A</color>
<color name="colorAToolbarEndColor">#FFD4A07A</color>
<color name="colorACardShadow">@color/colorPrimaryDark</color>
<color name="colorACardFrame">@color/colorPrimary</color>
@@ -24,7 +24,7 @@
<color name="colorOHPCTSSecondaryProgress">@color/colorPrimary</color>
<color name="colorOHPCTSProgress">@color/colorPrimaryDark</color>
<color name="toolbarBackgroundColor">#FF03AB4E</color>
<color name="toolbarBackgroundColor">#FF3DDC84</color>
<color name="toolbarTextColor">#FFFFFFFF</color>
<color name="mainWindowBackgroundColor">#FF2C2C2C</color>
<color name="mainWindowTextColor">#FFFFFFFF</color>
+22 -19
View File
@@ -2,6 +2,9 @@
<resources>
<style name="AESTheme" parent="Theme.AppCompat.NoActionBar">
<item name="colorPrimary">#FF03AB4E</item>
<item name="colorPrimaryDark">#FF3DDC84</item>
<item name="colorAccent">#FF027C39</item>
<item name="themeDebug">@style/DebugActivityTheme</item>
<item name="aboutViewBackgroundColor">@color/mainWindowBackgroundColor</item>
<item name="aboutViewTextColor">@color/mainWindowTextColor</item>
@@ -40,52 +43,52 @@
<style name="AESAToolbar">
<item name="attrAToolbarTitleTextColor">@color/colorTextColor</item>
<item name="attrAToolbarStartColor">@color/colorPrimaryDark</item>
<item name="attrAToolbarCenterColor">@color/colorPrimary</item>
<item name="attrAToolbarEndColor">@color/colorAccent</item>
<item name="attrAToolbarStartColor">?attr/colorPrimaryDark</item>
<item name="attrAToolbarCenterColor">?attr/colorPrimary</item>
<item name="attrAToolbarEndColor">?attr/colorAccent</item>
</style>
<style name="AESASupportToolbar">
<item name="attrASupportToolbarTitleTextColor">@color/colorTextColor</item>
<item name="attrASupportToolbarStartColor">@color/colorPrimaryDark</item>
<item name="attrASupportToolbarCenterColor">@color/colorPrimary</item>
<item name="attrASupportToolbarEndColor">@color/colorAccent</item>
<item name="attrASupportToolbarStartColor">?attr/colorPrimaryDark</item>
<item name="attrASupportToolbarCenterColor">?attr/colorPrimary</item>
<item name="attrASupportToolbarEndColor">?attr/colorAccent</item>
</style>
<style name="DepthAESTheme" parent="AESTheme">
<item name="colorPrimary">#FF0065EC</item>
<item name="colorPrimaryDark">#FF004DB4</item>
<item name="colorAccent">#FF4A97FF</item>
<item name="colorPrimaryDark">#FF4A97FF</item>
<item name="colorAccent">#FF004DB4</item>
</style>
<style name="SkyAESTheme" parent="AESTheme">
<item name="colorPrimary">#FF00A6FF</item>
<item name="colorPrimaryDark">#FF007ABB</item>
<item name="colorAccent">#FF84D4FF</item>
<item name="colorPrimaryDark">#FF84D4FF</item>
<item name="colorAccent">#FF007ABB</item>
</style>
<style name="GoldenAESTheme" parent="AESTheme">
<item name="colorPrimary">#FFF0CA11</item>
<item name="colorPrimaryDark">#FFD3AF00</item>
<item name="colorAccent">#FFFFE35C</item>
<item name="colorPrimaryDark">#FFFFE35C</item>
<item name="colorAccent">#FFD3AF00</item>
</style>
<style name="BearingAESTheme" parent="AESTheme">
<item name="colorPrimary">#FF840FFF</item>
<item name="colorPrimaryDark">#FF6900D7</item>
<item name="colorAccent">#FFBA78FF</item>
<item name="colorPrimaryDark">#FFBA78FF</item>
<item name="colorAccent">#FF6900D7</item>
</style>
<style name="MemorAESTheme" parent="AESTheme">
<item name="colorPrimary">#FFFF00F5</item>
<item name="colorPrimaryDark">#FFE500DC</item>
<item name="colorAccent">#FFFF76FA</item>
<item name="colorPrimaryDark">#FFFF76FA</item>
<item name="colorAccent">#FFE500DC</item>
</style>
<style name="TaoAESTheme" parent="AESTheme">
<item name="colorPrimary">#FFACACAC</item>
<item name="colorPrimaryDark">#FF898989</item>
<item name="colorAccent">#FFD8D8D8</item>
<item name="colorPrimary">#FF7E7E7E</item>
<item name="colorPrimaryDark">#FFE2E2E2</item>
<item name="colorAccent">#FF000000</item>
</style>
<style name="NormalDialogStyle" parent="Theme.AppCompat.Dialog">
+13 -10
View File
@@ -2,6 +2,9 @@
<resources>
<style name="AESTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#FF03AB4E</item>
<item name="colorPrimaryDark">#FF027C39</item>
<item name="colorAccent">#FF3DDC84</item>
<item name="themeDebug">@style/DebugActivityTheme</item>
<item name="aboutViewBackgroundColor">@color/mainWindowBackgroundColor</item>
<item name="aboutViewTextColor">@color/mainWindowTextColor</item>
@@ -40,16 +43,16 @@
<style name="AESAToolbar">
<item name="attrAToolbarTitleTextColor">@color/colorTextColor</item>
<item name="attrAToolbarStartColor">@color/colorPrimaryDark</item>
<item name="attrAToolbarCenterColor">@color/colorPrimary</item>
<item name="attrAToolbarEndColor">@color/colorAccent</item>
<item name="attrAToolbarStartColor">?attr/colorPrimaryDark</item>
<item name="attrAToolbarCenterColor">?attr/colorPrimary</item>
<item name="attrAToolbarEndColor">?attr/colorAccent</item>
</style>
<style name="AESASupportToolbar">
<item name="attrASupportToolbarTitleTextColor">@color/colorTextColor</item>
<item name="attrASupportToolbarStartColor">@color/colorPrimaryDark</item>
<item name="attrASupportToolbarCenterColor">@color/colorPrimary</item>
<item name="attrASupportToolbarEndColor">@color/colorAccent</item>
<item name="attrASupportToolbarStartColor">?attr/colorPrimaryDark</item>
<item name="attrASupportToolbarCenterColor">?attr/colorPrimary</item>
<item name="attrASupportToolbarEndColor">?attr/colorAccent</item>
</style>
<style name="DepthAESTheme" parent="AESTheme">
@@ -83,9 +86,9 @@
</style>
<style name="TaoAESTheme" parent="AESTheme">
<item name="colorPrimary">#FFACACAC</item>
<item name="colorPrimaryDark">#FF898989</item>
<item name="colorAccent">#FFD8D8D8</item>
<item name="colorPrimary">#FF7E7E7E</item>
<item name="colorPrimaryDark">#FF000000</item>
<item name="colorAccent">#FFE2E2E2</item>
</style>
<style name="NormalDialogStyle" parent="Theme.AppCompat.Light.Dialog">
@@ -109,4 +112,4 @@
<item name="@android:windowExitAnimation">@anim/normal_dialog_exit_corner</item>
</style>
</resources>
</resources>
+4 -4
View File
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Sun May 17 16:16:36 HKT 2026
stageCount=16
#Wed May 27 14:51:29 HKT 2026
stageCount=23
libraryProject=libappbase
baseVersion=15.20
publishVersion=15.20.15
publishVersion=15.20.22
buildCount=0
baseBetaVersion=15.20.16
baseBetaVersion=15.20.23
@@ -2,6 +2,7 @@ package cc.winboll.studio.libappbase;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.models.libs1520000.BaseBean;
import java.io.IOException;
/**
@@ -129,11 +129,10 @@ public class GlobalApplication extends Application {
// 初始化单例实例(确保在所有初始化操作前完成)
sInstance = this;
restoreDebugStatus();
// 初始化基础组件(日志、崩溃处理、Toast)
initCoreComponents();
// 恢复/初始化调试模式状态(从本地文件读取,无文件则默认关闭调试
restoreDebugStatus();
// 新增:初始化服务器地址(从 SP 读取到内存,提高后续访问效率)
// 初始化服务器地址(从 SP 读取到内存,提高后续访问效率
initWinbollHost();
LogUtils.d(TAG, "GlobalApplication 初始化完成,单例实例已创建");
@@ -144,7 +143,11 @@ public class GlobalApplication extends Application {
*/
private void initCoreComponents() {
// 初始化日志工具(传入 Application 上下文)
LogUtils.init(this);
// 调试状态下初始化日志工具
if (GlobalApplication.isDebugging()) {
LogUtils.init(this);
}
// 初始化全局异常处理器(捕获应用崩溃信息,用于调试或上报)
CrashHandler.init(this);
// 初始化 Toast 工具(统一 Toast 样式、避免内存泄漏等)
@@ -27,6 +27,11 @@ public class LogActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!GlobalApplication.isDebugging()) {
ToastUtils.show("非调试状态日志功能不可用");
finish();
}
// 设置布局文件(包含 LogView 控件)
setContentView(R.layout.activity_log);
@@ -67,45 +67,29 @@ public class LogUtils {
// ====================== 初始化入口 ======================
public static void init(final Context context) {
init(context, LOG_LEVEL.Off);
init(context, LOG_LEVEL.Off);
}
public static void init(final Context context, final LOG_LEVEL logLevel) {
Log.d(TAG, "init 执行日志工具初始化");
_mContext = context;
if (!GlobalApplication.isDebugging()) {
return;
}
Log.d(TAG, "init 执行日志工具初始化");
_mContext = context;
if (GlobalApplication.isDebugging()) {
initDebugDir();
} else {
initReleaseDir();
}
initLogUtilsDir();
initLogConfigBean();
addClassTAGList();
loadTAGBeanSettings();
checkAndTrimLogFileSize();
initLogConfigBean();
addClassTAGList();
loadTAGBeanSettings();
checkAndTrimLogFileSize();
_IsInited = true;
Log.d(TAG, "init 日志工具初始化完成");
_IsInited = true;
Log.d(TAG, "init 执行日志工具初始化完成");
}
// ====================== 目录初始化 ======================
private static void initDebugDir() {
final Context appContext = _mContext.getApplicationContext();
_mfLogCacheDir = new File(appContext.getExternalCacheDir(), TAG);
if (!_mfLogCacheDir.exists()) {
_mfLogCacheDir.mkdirs();
}
_mfLogCatchFile = new File(_mfLogCacheDir, "log.txt");
_mfLogDataDir = appContext.getExternalFilesDir(TAG);
if (!_mfLogDataDir.exists()) {
_mfLogDataDir.mkdirs();
}
_mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json");
}
private static void initReleaseDir() {
private static void initLogUtilsDir() {
final Context appContext = _mContext.getApplicationContext();
_mfLogCacheDir = new File(appContext.getCacheDir(), TAG);
if (!_mfLogCacheDir.exists()) {
@@ -113,7 +97,7 @@ public class LogUtils {
}
_mfLogCatchFile = new File(_mfLogCacheDir, "log.txt");
_mfLogDataDir = new File(appContext.getFilesDir(), TAG);
_mfLogDataDir = appContext.getExternalFilesDir(TAG);
if (!_mfLogDataDir.exists()) {
_mfLogDataDir.mkdirs();
}
@@ -136,7 +120,7 @@ public class LogUtils {
}
final long KEEP_FILE_SIZE = 25000L; // ~25KB 确保剪贴板可完整复制
final long MAX_FILE_SIZE = 2*KEEP_FILE_SIZE;
final long MAX_FILE_SIZE = 2 * KEEP_FILE_SIZE;
final long fileSize = _mfLogCatchFile.length();
if (fileSize <= MAX_FILE_SIZE) {
@@ -266,6 +250,9 @@ public class LogUtils {
}
public static void setTAGListEnable(final String tag, final boolean isEnable) {
if (!_IsInited) {
return;
}
final Iterator<Map.Entry<String, Boolean>> iterator = mapTAGList.entrySet().iterator();
while (iterator.hasNext()) {
final Map.Entry<String, Boolean> entry = iterator.next();
@@ -279,6 +266,9 @@ public class LogUtils {
}
public static void setALlTAGListEnable(final boolean isEnable) {
if (!_IsInited) {
return;
}
for (final Map.Entry<String, Boolean> entry : mapTAGList.entrySet()) {
entry.setValue(isEnable);
}
@@ -288,15 +278,22 @@ public class LogUtils {
// ====================== 日志级别控制 ======================
public static void setLogLevel(final LOG_LEVEL logLevel) {
if (_mLogUtilsBean == null) {
Log.d(TAG, "setLogLevel LogUtils未初始化,忽略设置日志级别");
return;
}
_mLogUtilsBean.setLogLevel(logLevel);
_mLogUtilsBean.saveBeanToFile(_mfLogUtilsBeanFile.getPath(), _mLogUtilsBean);
}
public static LOG_LEVEL getLogLevel() {
return _mLogUtilsBean.getLogLevel();
return _mLogUtilsBean == null ?LOG_LEVEL.Off: _mLogUtilsBean.getLogLevel();
}
private static boolean isLoggable(final String tag, final LOG_LEVEL logLevel) {
if (!GlobalApplication.isDebugging()) {
return false;
}
if (!_IsInited) {
return false;
}
@@ -2,6 +2,7 @@ package cc.winboll.studio.libappbase;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.models.libs1520000.BaseBean;
import java.io.IOException;
/**
@@ -2,6 +2,7 @@ package cc.winboll.studio.libappbase;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.models.libs1520000.BaseBean;
import java.io.IOException;
/**
@@ -1,6 +1,7 @@
package cc.winboll.studio.libappbase;
import android.os.FileObserver;
import java.io.File;
import java.lang.ref.WeakReference;
/**
@@ -51,30 +52,38 @@ public class LogViewThread extends Thread {
*/
@Override
public void run() {
// 获取日志缓存目录路径(从 LogUtils 统一获取,确保路径一致性)
String logDirPath = LogUtils.getLogCacheDir().getPath();
LogUtils.d(TAG, "启动日志文件监听,监听目录:" + logDirPath);
// 调试状态进行日志输出任务
if (GlobalApplication.isDebugging()) {
// 获取日志缓存目录路径(从 LogUtils 统一获取,确保路径一致性)
File logDir = LogUtils.getLogCacheDir();
if (logDir == null) {
LogUtils.d(TAG, "日志缓存目录未初始化,线程退出");
return;
}
String logDirPath = logDir.getPath();
LogUtils.d(TAG, "启动日志文件监听,监听目录:" + logDirPath);
// 初始化日志文件监听器(监听目标目录的文件事件)
mLogListener = new LogListener(logDirPath);
// 开始监听文件事件(非阻塞,内部通过 Native 层实现)
mLogListener.startWatching();
// 初始化日志文件监听器(监听目标目录的文件事件)
mLogListener = new LogListener(logDirPath);
// 开始监听文件事件(非阻塞,内部通过 Native 层实现)
mLogListener.startWatching();
// 循环等待退出标志(每 1 秒检查一次,降低 CPU 占用)
while (!isExit()) {
try {
Thread.sleep(1000); // 休眠 1 秒,避免忙等
} catch (InterruptedException e) {
// 线程被中断时,恢复中断标志并退出循环(避免无限阻塞)
Thread.currentThread().interrupt();
LogUtils.d(TAG, "日志监听线程被中断,准备退出。" + e);
break;
}
}
// 循环等待退出标志(每 1 秒检查一次,降低 CPU 占用)
while (!isExit()) {
try {
Thread.sleep(1000); // 休眠 1 秒,避免忙等
} catch (InterruptedException e) {
// 线程被中断时,恢复中断标志并退出循环(避免无限阻塞)
Thread.currentThread().interrupt();
LogUtils.d(TAG, "日志监听线程被中断,准备退出。" + e);
break;
}
}
// 收到退出标志,停止监听并释放资源
mLogListener.stopWatching();
LogUtils.d(TAG, "日志文件监听已停止,线程退出");
// 收到退出标志,停止监听并释放资源
mLogListener.stopWatching();
LogUtils.d(TAG, "日志文件监听已停止,线程退出");
}
}
/**
@@ -0,0 +1,415 @@
package cc.winboll.studio.libappbase.models.libs1520000;
import android.content.Context;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.UTF8FileUtils;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
/**
* WinBoLL JSON 数据模型基类(抽象类)
* 定义 Json Bean 的核心规范:序列化/反序列化、文件持久化、列表处理等通用逻辑,
* 子类需实现抽象方法,完成自身字段JSON读写业务逻辑
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @CreateTime 2026/05/19 22:33:00
* @EditTime 2026/05/19 23:15:48
* @param <T> 泛型约束,限定子类必须继承自BaseBean
*/
public abstract class BaseBean<T extends BaseBean> {
// ====================== 静态常量 ======================
/** 日志输出标识TAG */
public static final String TAG = "BaseBean";
/** JSON存储Bean类名字段Key,用于类型校验 */
static final String BEAN_NAME = "BeanName";
// ====================== 构造方法 ======================
/**
* 无参空构造,满足反射实例化要求
*/
public BaseBean() {
}
// ====================== 抽象方法 ======================
/**
* 获取当前实体类全类名
* @return 全限定类名字符串
*/
public abstract String getName();
/**
* 从JSON读取器解析构建实体对象
* @param jsonReader JSON读取流
* @return 解析完成实体对象
* @throws IOException IO读写异常
*/
public abstract T readBeanFromJsonReader(final JsonReader jsonReader) throws IOException;
// ====================== 路径获取相关 ======================
/**
* 获取单个实体默认存储JSON文件路径
* @param context 应用上下文
* @return 文件绝对路径
*/
public String getBeanJsonFilePath(final Context context) {
return context.getExternalFilesDir(TAG) + "/" + getName() + ".json";
}
/**
* 获取实体列表默认存储JSON文件路径
* @param context 应用上下文
* @return 文件绝对路径
*/
public String getBeanListJsonFilePath(final Context context) {
return context.getExternalFilesDir(TAG) + "/" + getName() + "_List.json";
}
// ====================== JSON序列化基础方法 ======================
/**
* 写入基础Bean标识字段至JSON
* @param jsonWriter JSON写入流
* @throws IOException 写入异常
*/
public void writeThisToJsonWriter(final JsonWriter jsonWriter) throws IOException {
jsonWriter.name(BEAN_NAME).value(getName());
}
/**
* 基类通用字段解析回调
* @param jsonReader JSON读取流
* @param name 字段名
* @return 是否完成当前字段解析
* @throws IOException 读取异常
*/
public boolean initObjectsFromJsonReader(final JsonReader jsonReader, final String name) throws IOException {
return false;
}
// ====================== 实体字符串序列化 ======================
@Override
public String toString() {
LogUtils.d(TAG, "执行BaseBean实体转JSON字符串");
final StringWriter stringWriter = new StringWriter();
final JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.setIndent(" ");
try {
jsonWriter.beginObject();
writeThisToJsonWriter(jsonWriter);
jsonWriter.endObject();
return stringWriter.toString();
} catch (final IOException e) {
LogUtils.d(TAG, "实体转JSON字符串异常", Thread.currentThread().getStackTrace());
}
return "";
}
// ====================== 列表序列化工具 ======================
/**
* Bean列表转为格式化JSON数组字符串
* @param beanList 实体集合
* @param <T> 实体泛型
* @return JSON字符串
*/
public static <T extends BaseBean> String toStringByBeanList(final ArrayList<T> beanList) {
LogUtils.d(TAG, "执行Bean列表序列化JSON操作");
try {
final StringWriter stringWriter = new StringWriter();
final JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.setIndent(" ");
jsonWriter.beginArray();
for (int i = 0; i < beanList.size(); i++) {
jsonWriter.beginObject();
beanList.get(i).writeThisToJsonWriter(jsonWriter);
jsonWriter.endObject();
}
jsonWriter.endArray();
jsonWriter.close();
return stringWriter.toString();
} catch (final IOException e) {
LogUtils.d(TAG, "列表序列化JSON异常", Thread.currentThread().getStackTrace());
}
return "";
}
// ====================== JSON字符串解析实体 ======================
/**
* JSON文本解析为单个实体对象
* @param szBean JSON文本
* @param clazz 实体Class字节码
* @param <T> 实体泛型
* @return 解析完成实体
* @throws IOException 解析IO异常
*/
public static <T extends BaseBean> T parseStringToBean(final String szBean, final Class<T> clazz) throws IOException {
LogUtils.d(TAG, "进入字符串解析实体方法,目标Class:" + clazz.getSimpleName());
final StringReader stringReader = new StringReader(szBean);
final JsonReader jsonReader = new JsonReader(stringReader);
try {
final T beanTemp = clazz.newInstance();
return (T) beanTemp.readBeanFromJsonReader(jsonReader);
} catch (final InstantiationException e) {
LogUtils.d(TAG, "实体反射实例化失败(InstantiationException)", Thread.currentThread().getStackTrace());
} catch (final IllegalAccessException e) {
LogUtils.d(TAG, "实体反射权限访问失败(IllegalAccessException)", Thread.currentThread().getStackTrace());
}
return null;
}
/**
* JSON数组文本解析填充实体列表
* @param szBeanList JSON数组文本
* @param beanList 目标存储集合
* @param clazz 实体字节码
* @param <T> 实体泛型
* @return 解析结果
*/
public static <T extends BaseBean> boolean parseStringToBeanList(final String szBeanList, ArrayList<T> beanList, final Class<T> clazz) {
LogUtils.d(TAG, "进入列表字符串解析方法");
try {
if (beanList == null) {
beanList = new ArrayList<T>();
} else {
beanList.clear();
}
final StringReader stringReader = new StringReader(szBeanList);
final JsonReader jsonReader = new JsonReader(stringReader);
jsonReader.beginArray();
while (jsonReader.hasNext()) {
final T beanTemp = clazz.newInstance();
final T bean = (T) beanTemp.readBeanFromJsonReader(jsonReader);
if (bean != null) {
beanList.add(bean);
}
}
jsonReader.endArray();
return true;
} catch (final InstantiationException e) {
LogUtils.d(TAG, "列表解析反射实例化异常", Thread.currentThread().getStackTrace());
} catch (final IllegalAccessException e) {
LogUtils.d(TAG, "列表解析反射权限异常", Thread.currentThread().getStackTrace());
} catch (final IOException e) {
LogUtils.d(TAG, "列表解析JSON读写异常", Thread.currentThread().getStackTrace());
}
return false;
}
// ====================== 文件加载实体 ======================
/**
* 从默认路径加载单个实体
* @param context 上下文
* @param clazz 实体字节码
* @param <T> 实体泛型
* @return 加载实体
*/
public static <T extends BaseBean> T loadBean(final Context context, final Class<T> clazz) {
LogUtils.d(TAG, "执行默认路径加载实体数据");
try {
final T beanTemp = clazz.newInstance();
return loadBeanFromFile(beanTemp.getBeanJsonFilePath(context), clazz);
} catch (final InstantiationException e) {
LogUtils.d(TAG, "加载实体反射实例化失败", Thread.currentThread().getStackTrace());
} catch (final IllegalAccessException e) {
LogUtils.d(TAG, "加载实体反射权限失败", Thread.currentThread().getStackTrace());
}
return null;
}
/**
* 自定义文件路径加载单个实体
* @param szFilePath 文件路径
* @param clazz 实体字节码
* @param <T> 实体泛型
* @return 实体对象
*/
public static <T extends BaseBean> T loadBeanFromFile(final String szFilePath, final Class<T> clazz) {
LogUtils.d(TAG, "指定路径加载实体,路径:" + szFilePath);
try {
final File file = new File(szFilePath);
if (file.exists()) {
final T beanTemp = clazz.newInstance();
final String json = UTF8FileUtils.readStringFromFile(szFilePath);
return beanTemp.parseStringToBean(json, clazz);
}
} catch (final InstantiationException e) {
LogUtils.d(TAG, "文件加载实体反射实例化异常", Thread.currentThread().getStackTrace());
} catch (final IllegalAccessException e) {
LogUtils.d(TAG, "文件加载实体反射权限异常", Thread.currentThread().getStackTrace());
} catch (final IOException e) {
LogUtils.d(TAG, "文件读取JSON数据异常", Thread.currentThread().getStackTrace());
}
return null;
}
/**
* 默认路径加载实体列表
* @param context 上下文
* @param beanListDst 目标集合
* @param clazz 实体字节码
* @param <T> 实体泛型
* @return 加载结果
*/
public static <T extends BaseBean> boolean loadBeanList(final Context context, final ArrayList<T> beanListDst, final Class<T> clazz) {
LogUtils.d(TAG, "默认路径加载实体列表数据");
try {
final T beanTemp = clazz.newInstance();
return loadBeanListFromFile(beanTemp.getBeanListJsonFilePath(context), beanListDst, clazz);
} catch (final InstantiationException e) {
LogUtils.d(TAG, "列表加载反射实例化异常", Thread.currentThread().getStackTrace());
} catch (final IllegalAccessException e) {
LogUtils.d(TAG, "列表加载反射权限异常", Thread.currentThread().getStackTrace());
}
return false;
}
/**
* 自定义路径加载实体列表
* @param szFilePath 文件路径
* @param beanList 目标集合
* @param clazz 实体字节码
* @param <T> 实体泛型
* @return 加载结果
*/
public static <T extends BaseBean> boolean loadBeanListFromFile(final String szFilePath, final ArrayList<T> beanList, final Class<T> clazz) {
LogUtils.d(TAG, "指定路径加载实体列表,路径:" + szFilePath);
try {
final File file = new File(szFilePath);
if (file.exists()) {
final String listJson = UTF8FileUtils.readStringFromFile(szFilePath);
return parseStringToBeanList(listJson, beanList, clazz);
}
} catch (final IOException e) {
LogUtils.d(TAG, "列表文件读取异常", Thread.currentThread().getStackTrace());
}
return false;
}
// ====================== 实体数据保存文件 ======================
/**
* 默认路径保存单个实体
* @param context 上下文
* @param bean 待保存实体
* @param <T> 实体泛型
* @return 保存结果
*/
public static <T extends BaseBean> boolean saveBean(final Context context, final T bean) {
return saveBeanToFile(bean.getBeanJsonFilePath(context), bean);
}
/**
* 自定义路径保存单个实体
* @param szFilePath 保存路径
* @param bean 待保存实体
* @param <T> 实体泛型
* @return 保存结果
*/
public static <T extends BaseBean> boolean saveBeanToFile(final String szFilePath, final T bean) {
LogUtils.d(TAG, "执行实体数据写入文件操作");
try {
final String json = bean.toString();
UTF8FileUtils.writeStringToFile(szFilePath, json);
return true;
} catch (final IOException e) {
LogUtils.d(TAG, "实体写入文件异常", Thread.currentThread().getStackTrace());
}
return false;
}
/**
* 默认路径保存实体列表
* @param context 上下文
* @param beanList 实体集合
* @param clazz 实体字节码
* @param <T> 实体泛型
* @return 保存结果
*/
public static <T extends BaseBean> boolean saveBeanList(final Context context, final ArrayList<T> beanList, final Class<T> clazz) {
LogUtils.d(TAG, "默认路径保存实体列表数据");
try {
final T beanTemp = clazz.newInstance();
return saveBeanListToFile(beanTemp.getBeanListJsonFilePath(context), beanList);
} catch (final InstantiationException e) {
LogUtils.d(TAG, "列表保存反射实例化异常", Thread.currentThread().getStackTrace());
} catch (final IllegalAccessException e) {
LogUtils.d(TAG, "列表保存反射权限异常", Thread.currentThread().getStackTrace());
}
return false;
}
/**
* 自定义路径保存实体列表
* @param szFilePath 保存路径
* @param beanList 实体集合
* @param <T> 实体泛型
* @return 保存结果
*/
public static <T extends BaseBean> boolean saveBeanListToFile(final String szFilePath, final ArrayList<T> beanList) {
LogUtils.d(TAG, "指定路径保存实体列表数据");
try {
final String json = toStringByBeanList(beanList);
UTF8FileUtils.writeStringToFile(szFilePath, json);
return true;
} catch (final IOException e) {
LogUtils.d(TAG, "列表数据写入文件异常", Thread.currentThread().getStackTrace());
}
return false;
}
// ====================== Bean类型一致性校验 ======================
/**
* 校验本地JSON列表内实体类型是否统一
* @param szFilePath 列表文件路径
* @param clazz 目标校验实体Class
* @param <T> 实体泛型
* @return 校验结果信息
*/
public static <T extends BaseBean> String checkIsTheSameBeanListAndFile(final String szFilePath, final Class<T> clazz) {
final StringBuilder sbResult = new StringBuilder();
final String szErrorInfo = "Check Is The Same Bean List And File Error : ";
try {
int sameCount = 0;
int totalCount = 0;
final T beanTemp = clazz.newInstance();
final String targetBeanName = beanTemp.getName();
final String listJson = UTF8FileUtils.readStringFromFile(szFilePath);
final StringReader stringReader = new StringReader(listJson);
final JsonReader jsonReader = new JsonReader(stringReader);
jsonReader.beginArray();
while (jsonReader.hasNext()) {
totalCount++;
jsonReader.beginObject();
while (jsonReader.hasNext()) {
final String name = jsonReader.nextName();
if (BEAN_NAME.equals(name)) {
if (targetBeanName.equals(jsonReader.nextString())) {
sameCount++;
}
} else {
jsonReader.skipValue();
}
}
jsonReader.endObject();
}
jsonReader.endArray();
if (sameCount != totalCount) {
sbResult.append("Total : ").append(totalCount).append(" Diff : ").append(totalCount - sameCount);
}
} catch (final InstantiationException e) {
sbResult.append(szErrorInfo).append(e);
LogUtils.d(TAG, "类型校验反射实例化异常", Thread.currentThread().getStackTrace());
} catch (final IllegalAccessException e) {
sbResult.append(szErrorInfo).append(e);
LogUtils.d(TAG, "类型校验反射权限异常", Thread.currentThread().getStackTrace());
} catch (final IOException e) {
sbResult.append(szErrorInfo).append(e);
LogUtils.d(TAG, "类型校验文件读取解析异常", Thread.currentThread().getStackTrace());
}
return sbResult.toString();
}
}
@@ -1,6 +1,8 @@
package cc.winboll.studio.libappbase.views;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
@@ -12,6 +14,7 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.R;
@@ -74,12 +77,12 @@ public class AboutView extends LinearLayout {
private EditText metDevUserPassword;
// ===================================== 页面视图控件 =====================================
private DebugSwitchImageView ivAppIcon;
private DebugSwitchInfoImageView ivAppIcon;
private TextView tvAppNameVersion;
private TextView tvAppDesc;
private LinearLayout llFunctionContainer;
private ImageButton ibSebugStepOver;
private ImageButton ibSigngetDialog;
private ImageButton ibDebugUnlock;
private ImageButton ibWinBoLLHostDialog;
// ===================================== 构造方法(按参数从少到多排序) =====================================
@@ -193,12 +196,12 @@ public class AboutView extends LinearLayout {
llFunctionContainer = findViewById(R.id.ll_function_container);
// 功能按钮绑定
ibSebugStepOver = findViewById(R.id.ib_debug_step_over);
ibSigngetDialog = findViewById(R.id.ib_signgetdialog);
ibDebugUnlock = findViewById(R.id.ib_debug_unlock);
ibWinBoLLHostDialog = findViewById(R.id.ib_winbollhostdialog);
// 调试按钮统一只在调试模式显示
ibWinBoLLHostDialog.setVisibility(GlobalApplication.isDebugging() ? View.VISIBLE : View.GONE);
//ibSigngetDialog.setVisibility(GlobalApplication.isDebugging() ? View.VISIBLE : View.GONE);
//ibDebugUnlock.setVisibility(GlobalApplication.isDebugging() ? View.VISIBLE : View.GONE);
ibSebugStepOver.setVisibility(GlobalApplication.isDebugging() ? View.VISIBLE : View.GONE);
// 绑定按钮点击事件
@@ -310,6 +313,47 @@ public class AboutView extends LinearLayout {
LogUtils.d(TAG, "initAboutPageView():视图组装完成,功能项加载完毕");
}
// ===================================== 调试解锁弹窗 =====================================
private void showDebugUnlockDialog() {
final AlertDialog dialog = new AlertDialog.Builder(mContext).create();
dialog.setTitle("应用调试解锁");
dialog.setCanceledOnTouchOutside(true);
final EditText etToken = new EditText(mContext);
etToken.setHint("请输入调试Token");
dialog.setView(etToken);
dialog.setButton(DialogInterface.BUTTON_POSITIVE, "调试解锁", (DialogInterface.OnClickListener) null);
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, "关闭", (DialogInterface.OnClickListener) null);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface d) {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String inputToken = etToken.getText().toString().trim();
String savedToken = DebugSwitchInfoImageView.getDebugToken();
if (savedToken != null && savedToken.equals(inputToken)) {
GlobalApplication.setIsDebugging(true);
GlobalApplication.saveDebugStatus(GlobalApplication.getInstance());
Toast.makeText(mContext, "调试解锁成功,重启应用后生效", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, "调试Token不匹配", Toast.LENGTH_SHORT).show();
dialog.dismiss();
}
}
});
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
}
});
dialog.show();
}
// ===================================== 内部工具/事件方法 =====================================
/**
* 绑定功能按钮点击事件,处理正版校验、调试地址配置弹窗唤起
@@ -335,6 +379,15 @@ public class AboutView extends LinearLayout {
new DebugHostDialog(mContext).show();
}
});
// 应用调试解锁按钮
ibDebugUnlock.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d(TAG, "ibDebugUnlock onClick:弹出调试解锁对话框");
showDebugUnlockDialog();
}
});
LogUtils.d(TAG, "setBtnClickListener():功能按钮点击事件绑定完成");
}
@@ -0,0 +1,127 @@
package cc.winboll.studio.libappbase.views;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import java.util.UUID;
import cc.winboll.studio.libappbase.GlobalApplication;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/04/06 19:32
* @Describe 应用Logo控件,连续点击6次弹出调试Token对话框,支持复制与重置
*/
public class DebugSwitchInfoImageView extends ImageView {
public static final String TAG = "DebugSwitchInfoImageView";
// 连续点击计数
private int mClickCount = 0;
// 目标点击次数
private static final int TARGET_CLICK_COUNT = 7;
private static String mDebugToken = null;
private static final String SP_DEBUG_TOKEN = "debug_token_prefs";
private static final String KEY_DEBUG_TOKEN = "debug_token";
public static String getDebugToken() {
if (mDebugToken != null) {
return mDebugToken;
}
Context context = GlobalApplication.getInstance();
if (context != null) {
SharedPreferences sp = context.getSharedPreferences(SP_DEBUG_TOKEN, Context.MODE_PRIVATE);
mDebugToken = sp.getString(KEY_DEBUG_TOKEN, null);
if (mDebugToken == null) {
mDebugToken = UUID.randomUUID().toString();
sp.edit().putString(KEY_DEBUG_TOKEN, mDebugToken).apply();
}
}
return mDebugToken;
}
public static void resetDebugToken() {
Context context = GlobalApplication.getInstance();
if (context != null) {
mDebugToken = UUID.randomUUID().toString();
SharedPreferences sp = context.getSharedPreferences(SP_DEBUG_TOKEN, Context.MODE_PRIVATE);
sp.edit().putString(KEY_DEBUG_TOKEN, mDebugToken).apply();
}
}
private void showDebugTokenDialog() {
final AlertDialog dialog = new AlertDialog.Builder(getContext()).create();
dialog.setTitle("调试Token");
dialog.setMessage(getDebugToken());
dialog.setCanceledOnTouchOutside(false);
dialog.setButton(DialogInterface.BUTTON_POSITIVE, "复制到剪贴板", (DialogInterface.OnClickListener) null);
dialog.setButton(DialogInterface.BUTTON_NEUTRAL, "重置", (DialogInterface.OnClickListener) null);
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, "关闭", (DialogInterface.OnClickListener) null);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface d) {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ClipboardManager cm = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(ClipData.newPlainText("DebugToken", getDebugToken()));
}
});
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
resetDebugToken();
dialog.setMessage(getDebugToken());
}
});
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
}
});
dialog.show();
}
public DebugSwitchInfoImageView(Context context) {
super(context);
init();
}
public DebugSwitchInfoImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DebugSwitchInfoImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public DebugSwitchInfoImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mClickCount++;
if (mClickCount >= TARGET_CLICK_COUNT) {
mClickCount = 0;
showDebugTokenDialog();
}
}
});
}
}
@@ -15,7 +15,7 @@
android:paddingRight="16dp"
android:paddingBottom="16dp">
<cc.winboll.studio.libappbase.views.DebugSwitchImageView
<cc.winboll.studio.libappbase.views.DebugSwitchInfoImageView
android:id="@+id/iv_app_icon"
android:layout_width="48dp"
android:layout_height="48dp"
@@ -81,7 +81,8 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_key"
android:id="@+id/ib_signgetdialog"
android:id="@+id/ib_debug_unlock"
android:contentDescription="应用调试解锁"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:background="@null"/>
@@ -15,7 +15,7 @@
android:paddingRight="16dp"
android:paddingBottom="16dp">
<cc.winboll.studio.libappbase.views.DebugSwitchImageView
<cc.winboll.studio.libappbase.views.DebugSwitchInfoImageView
android:id="@+id/iv_app_icon"
android:layout_width="48dp"
android:layout_height="48dp"
@@ -81,7 +81,8 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_key"
android:id="@+id/ib_signgetdialog"
android:id="@+id/ib_debug_unlock"
android:contentDescription="应用调试解锁"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:background="@null"/>
+3
View File
@@ -60,4 +60,7 @@
<!-- DebugLogStyle 应用调试日志样式属性 -->
<attr name="debugTextColor" format="color"/>
<!-- 边框圆角属性 -->
<attr name="borderCornerRadius" format="dimension"/>
</resources>
+1
View File
@@ -0,0 +1 @@
/build
+55
View File
@@ -0,0 +1,55 @@
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
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
// 网络连接类库
api 'com.squareup.okhttp3:okhttp:4.4.1'
// Gson
api 'com.google.code.gson:gson:2.8.9'
// Html 解析
api 'org.jsoup:jsoup:1.13.1'
// 添加JSch依赖(SFTP核心,com.jcraft:jsch:0.1.54
api 'com.jcraft:jsch:0.1.54'
// 米盟
//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.33'
api 'cc.winboll.studio:libaes:15.20.16'
// 备用库 jitpack.io 地址
//api 'com.github.ZhanGSKen:libappbase:appbase-v15.20.33'
//api 'com.github.ZhanGSKen:libaes:aes-v15.20.16'
api fileTree(dir: 'libs', include: ['*.jar'])
}
+8
View File
@@ -0,0 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed Jun 24 08:11:15 CST 2026
stageCount=9
libraryProject=libwinboll
baseVersion=15.20
publishVersion=15.20.8
buildCount=0
baseBetaVersion=15.20.9
+17
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 *;
#}
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.libwinboll" >
<application>
<activity
android:name=".WinBoLLLibraryActivity">
</activity>
</application>
</manifest>
@@ -0,0 +1,17 @@
package cc.winboll.studio.libwinboll;
import android.app.Activity;
import android.os.Bundle;
import cc.winboll.studio.libappbase.ToastUtils;
public class WinBoLLLibraryActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_winbolllibrary);
ToastUtils.show("WinBoLLLibraryActivity onCreate");
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

@@ -0,0 +1,11 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cc.winboll.studio.libwinboll.WinBoLLLibraryActivity"/>
</LinearLayout>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@android:style/Theme.Material.Light">
</style>
</resources>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="lib_name">libwinboll</string>
<string name="hello_world">Hello world!</string>
</resources>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@android:style/Theme.Holo.Light">
</style>
</resources>
-4
View File
@@ -96,7 +96,3 @@
// AutoNFC 项目编译设置
//include ':autonfc'
//rootProject.name = "autonfc"
// StallHelper 项目编译设置
//include ':stallhelper'
//rootProject.name = "stallhelper"
-4
View File
@@ -1,4 +0,0 @@
# StallHelper
# 排档辅助管理工具
## LargeStall Stand Auxiliary Management Tool.
-8
View File
@@ -1,8 +0,0 @@
#Created by .winboll/winboll_app_build.gradle
#Mon May 18 19:22:43 HKT 2026
stageCount=3
libraryProject=
baseVersion=15.20
publishVersion=15.20.2
buildCount=0
baseBetaVersion=15.20.3
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">排档辅助管理工具+</string>
</resources>
-51
View File
@@ -1,51 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.stallhelper">
<!-- 拥有完全的网络访问权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:name=".App"
android:allowBackup="true"
android:icon="@drawable/ic_stall"
android:label="@string/app_name"
android:theme="@style/MyStallHelperTheme"
android:supportsRtl="true">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="standard"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".activities.AboutActivity"
android:label="AboutActivity"/>
<activity
android:name="cc.winboll.studio.stallhelper.activities.PreNullDiningTableActivity"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name="cc.winboll.studio.stallhelper.activities.CompleteDiningTableActivity"
android:windowSoftInputMode="adjustResize"/>
<activity android:name="cc.winboll.studio.stallhelper.activities.NoteRecordHelperActivity"/>
<activity android:name="cc.winboll.studio.stallhelper.activities.TaskSchedulerActivity"/>
</application>
</manifest>
@@ -1,36 +0,0 @@
package cc.winboll.studio.stallhelper;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2025/01/22 00:11:41
* @Describe 全局应用类
*/
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.libappbase.BuildConfig;
public class App extends GlobalApplication {
public static final String TAG = "App";
@Override
public void onCreate() {
super.onCreate();
if (isDebugging() != true) {
setIsDebugging(BuildConfig.DEBUG);
}
//setIsDebugging(false);
WinBoLLActivityManager.init(this);
// 初始化 Toast 框架
ToastUtils.init(this);
}
@Override
public void onTerminate() {
super.onTerminate();
ToastUtils.release();
}
}
@@ -1,135 +0,0 @@
package cc.winboll.studio.stallhelper;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.stallhelper.R;
import cc.winboll.studio.stallhelper.activities.AboutActivity;
import cc.winboll.studio.stallhelper.activities.CompleteDiningTableActivity;
import cc.winboll.studio.stallhelper.activities.NoteRecordHelperActivity;
import cc.winboll.studio.stallhelper.activities.PreNullDiningTableActivity;
import cc.winboll.studio.stallhelper.activities.TaskSchedulerActivity;
import cc.winboll.studio.stallhelper.activities.WinBoLLActivity;
final public class MainActivity extends WinBoLLActivity {
public static final String TAG = "MainActivity";
public static final int REQUEST_HOME_ACTIVITY = 0;
public static final int REQUEST_ABOUT_ACTIVITY = 1;
Context mContext;
private Toolbar mToolbar;
// @Override
// protected boolean isEnableDisplayHomeAsUp() {
// return false;
// }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = getApplicationContext();
mToolbar = findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
}
@Override
public String getTag() {
return TAG;
}
// @Override
// protected boolean isAddWinBoLLToolBar() {
// return true;
// }
//
// @Override
// protected Toolbar initToolBar() {
// return findViewById(R.id.activitymainToolbar1);
// }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_about) {
startActivity(new Intent(this, AboutActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
void openAboutActivity() {
Intent intent = new Intent(getApplicationContext(), AboutActivity.class);
startActivity(intent);
//App.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), AboutActivity.class);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (resultCode) {
case REQUEST_HOME_ACTIVITY : {
LogUtils.d(TAG, "REQUEST_HOME_ACTIVITY");
break;
}
case REQUEST_ABOUT_ACTIVITY : {
LogUtils.d(TAG, "REQUEST_ABOUT_ACTIVITY");
break;
}
default : {
super.onActivityResult(requestCode, resultCode, data);
}
}
}
public void onPreNullDiningTable(View view) {
//ToastUtils.show("onDiningTable");
Intent intent = new Intent(getApplicationContext(), PreNullDiningTableActivity.class);
startActivity(intent);
//App.getWinBoLLActivityManager().startWinBoLLActivity(mContext, PreNullDiningTableActivity.class);
// try {
// WinBoLLActivity clazzActivity = PreNullDiningTableActivity.class.newInstance();
// String tag = clazzActivity.getTag();
// LogUtils.d(TAG, "String tag = clazzActivity.getTag(); tag " + tag);
// Intent intent = new Intent(getApplicationContext(), PreNullDiningTableActivity.class);
// startWinBoLLActivity(intent, tag);
// } catch (IllegalAccessException e) {} catch (InstantiationException e) {}
}
public void onCompleteDiningTable(View view) {
//ToastUtils.show("onDiningTable");
Intent intent = new Intent(getApplicationContext(), CompleteDiningTableActivity.class);
startActivity(intent);
//App.getWinBoLLActivityManager().startWinBoLLActivity(mContext, CompleteDiningTableActivity.class);
// try {
// WinBoLLActivity clazzActivity = CompleteDiningTableActivity.class.newInstance();
// String tag = clazzActivity.getTag();
// LogUtils.d(TAG, "String tag = clazzActivity.getTag(); tag " + tag);
// Intent intent = new Intent(getApplicationContext(), CompleteDiningTableActivity.class);
// startWinBoLLActivity(intent, tag);
// } catch (IllegalAccessException e) {} catch (InstantiationException e) {}
}
public void onNoteRecordHelper(View view) {
Intent intent = new Intent(getApplicationContext(), NoteRecordHelperActivity.class);
startActivity(intent);
}
public void onTaskScheduler(View view) {
Intent intent = new Intent(getApplicationContext(), TaskSchedulerActivity.class);
startActivity(intent);
}
}
@@ -1,50 +0,0 @@
package cc.winboll.studio.stallhelper.activities;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.widget.Toolbar;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.stallhelper.R;
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/11 12:55
* @Describe AboutActivity
*/
public class AboutActivity extends AppCompatActivity {
public static final String TAG = "AboutActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
AboutView aboutView = findViewById(R.id.aboutview);
aboutView.setAPPInfo(genDefaultAppInfo());
}
private APPInfo genDefaultAppInfo() {
LogUtils.d(TAG, "genDefaultAppInfo() 调用");
String branchName = "stallhelper";
APPInfo appInfo = new APPInfo();
appInfo.setAppName("StallHelper");
appInfo.setAppIcon(R.drawable.ic_winboll);
appInfo.setAppDescription(getString(R.string.app_description));
appInfo.setAppGitName("StallHelper");
appInfo.setAppGitOwner("ZhanGSKen");
appInfo.setAppGitAPPBranch(branchName);
appInfo.setAppGitAPPSubProjectFolder(branchName);
appInfo.setAppHomePage("http://10.8.0.4:9876/apks/index-manager.php?project=StallHelper");
appInfo.setAppAPKName("StallHelper");
appInfo.setAppAPKFolderName("StallHelper");
LogUtils.d(TAG, "genDefaultAppInfo: 应用信息已生成");
return appInfo;
}
}
@@ -1,371 +0,0 @@
package cc.winboll.studio.stallhelper.activities;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/10/29 09:22:24
* @Describe 餐桌表补全窗口
*/
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.stallhelper.App;
import cc.winboll.studio.stallhelper.R;
import cc.winboll.studio.stallhelper.beans.DiningTableBean;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
public class CompleteDiningTableActivity extends WinBoLLActivity {
public static final String TAG = "CompleteDiningTableActivity";
// 某一号晚上的餐桌预定补全列表
ArrayList<DiningTableBean> mCompleteTableInfoList;
// 原始预定信息内容输入框
EditText metOriginBookText;
EditText metPreBookList;
EditText metCompleteDate;
ScrollView mScrollView;
ArrayList<String> mszOriginBookList;
@Override
public String getTag() {
return TAG;
}
// @Override
// protected boolean isEnableDisplayHomeAsUp() {
// return true;
// }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_completediningtable);
setTitle("餐桌表补全");
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
StringBuilder sbDateNow = new StringBuilder("当前日期:");
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timeString = sdf.format(now);
sbDateNow.append(timeString);
TextView tvDateNow = findViewById(R.id.activitycompletediningtableTextView2);
tvDateNow.setText(sbDateNow.toString());
metOriginBookText = findViewById(R.id.activitycompletediningtableEditText1);
metPreBookList = findViewById(R.id.activitycompletediningtableEditText2);
metCompleteDate = findViewById(R.id.activitycompletediningtableEditText3);
mScrollView = findViewById(R.id.activitycompletediningtableScrollView1);
}
// @Override
// protected Toolbar initToolBar() {
// return findViewById(R.id.activitycompletediningtableToolbar1);
// }
//
// @Override
// protected boolean isAddWinBoLLToolBar() {
// return false;
// }
boolean isInCompleteRule(DiningTableBean item) {
String szCompleteDate = metCompleteDate.getText().toString() + "";
if (item.getDate().equals(szCompleteDate) && item.getDinnerType() == DiningTableBean.DinnerType.Evening) {
return true;
}
return false;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
WinBoLLActivityManager.getInstance().finish(this);
}
return super.onOptionsItemSelected(item);
}
void createNullCompleteTableInfoList() {
mCompleteTableInfoList = new ArrayList<DiningTableBean>();
String szCompleteDate = metCompleteDate.getText().toString() + "";
mCompleteTableInfoList.add(new DiningTableBean("201", 18, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("202", 8, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("203", 8, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("205", 10, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("206", 0, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("207", 0, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("208", 8, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("209", 10, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("210", 8, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("211", 12, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("212", 8, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("213", 9, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("666", 14, true, szCompleteDate));
mCompleteTableInfoList.add(new DiningTableBean("888", 20, true, szCompleteDate));
for (int i = 1; i < 23; i++) {
DiningTableBean tempBean = new DiningTableBean(Integer.toString(i), 0, true, szCompleteDate);
tempBean.setDinnerType(DiningTableBean.DinnerType.Evening);
tempBean.setTableType(DiningTableBean.TableType.Table);
tempBean.setIsSelected(false);
mCompleteTableInfoList.add(tempBean);
}
//DiningTableBean.saveBeanListToFile(getApplicationContext().getExternalFilesDir("temp") + "/1.json", mCompleteTableInfoList);
//ToastUtils.show("Test");
}
ArrayList<DiningTableBean> getOriginBookList() {
//ToastUtils.show(metOriginBookText.getText().toString());
ArrayList<DiningTableBean> listReturn = new ArrayList<DiningTableBean>();
String[] szOriginBookText = metOriginBookText.getText().toString().split("\\r?\\n");
for (String szTemp : szOriginBookText) {
String szTemp2 = szTemp.replace(" ", "");
DiningTableBean bean = format2DiningTableBean(szTemp2);
if (bean != null) {
listReturn.add(bean);
// 更新补全列表
updateCompleteTableInfoList(bean);
}
}
//ToastUtils.show("szOriginBookText : " + Integer.toString(szOriginBookText.length));
//ToastUtils.show("listReturn : " + Integer.toString(listReturn.size()));
return listReturn;
}
//
// 如果补全列表某一项时间和房号与指定项一致,
// 就把指定项信息更新至补全列表中。
//
void updateCompleteTableInfoList(DiningTableBean beanNew) {
for (DiningTableBean completeTableInfo : mCompleteTableInfoList) {
if (completeTableInfo.getDate().equals(beanNew.getDate())
&& completeTableInfo.getDinnerType() == beanNew.getDinnerType()
&& completeTableInfo.getTableNumber().equals(beanNew.getTableNumber())) {
completeTableInfo.setBookDesc(beanNew.getBookDesc());
completeTableInfo.setIsSelected(true);
}
}
}
//
// 检验每一个数据行的日期格式,
// 如果出现(x号/x月x号)以外的日期标注,
// 则提示有数据行日期标识不清晰的情况。
//
boolean checkLinesDateValidAndShow() {
boolean checkResult = true;
String[] szLines = metOriginBookText.getText().toString().split("\\r?\\n");
if (szLines.length < 1) {
ToastUtils.show("没有数据行。");
checkResult = false;
} else {
StringBuilder sb = new StringBuilder("日期标识不清晰,在");
for (int i = 0; i < szLines.length; i++) {
Pattern pattern = Pattern.compile("(\\d*[月]{0,1}\\d+号)\\s*([晚|午]{0,1}).*");
Matcher matcher = pattern.matcher(szLines[i]);
if (!matcher.find()) {
sb.append("(");
sb.append(Integer.toString(i + 1));
sb.append(")");
checkResult = false;
}
}
sb.append("行。");
if (checkResult == false) {
ToastUtils.show(sb.toString());
}
}
return checkResult;
}
//
// 获取指定日期的第一条数据的位置
// 返回 -1 表示所有数据都没有该日期
//
int getFirstIndexOfDate(ArrayList<DiningTableBean> originBookList, String szDate) {
int nResult = -1;
//ToastUtils.show("szDate " + szDate);
for (int i = originBookList.size() - 1 ; i > 0; i--) {
//LogUtils.d(TAG, "originBookList.get(i).getDate() " + originBookList.get(i).getDate());
if (originBookList.get(i).isDiningTableBean()) {
if (originBookList.get(i).getDate().equals(szDate)) {
nResult = i;
continue;
} else {
return nResult;
}
} else {
nResult = i;
continue;
}
}
return nResult;
}
DiningTableBean format2DiningTableBean(String szBookInfo) {
if (!szBookInfo.trim().equals("")) {
//String sz = "29号晚 201房 123456";
// 定义正则表达式模式
Pattern pattern = Pattern.compile("(\\d*[月]{0,1}\\d+号)\\s*([晚|午]{0,1})\\s*(\\d+)([房|桌])[-\\s]*(.*)");
Matcher matcher = pattern.matcher(szBookInfo);
if (matcher.find()) {
String date = matcher.group(1);
String szDinnerType = matcher.group(2);
String tableNumber = matcher.group(3);
String szTableType = matcher.group(4);
String bookDesc = matcher.group(5);
DiningTableBean.DinnerType dinnerType = DiningTableBean.DinnerType.Evening;
if (szDinnerType.equals("")) {
dinnerType = DiningTableBean.DinnerType.Noon;
}
DiningTableBean.TableType tableType = DiningTableBean.TableType.Room;
if (szTableType.equals("")) {
tableType = DiningTableBean.TableType.Table;
}
return new DiningTableBean(tableNumber, 0, true, dinnerType, bookDesc, date, tableType);
} else {
return new DiningTableBean(true, false, szBookInfo);
}
}
return null;
}
void checkAndAddCompleteTableInfoList(ArrayList<DiningTableBean> originBookList) {
boolean isAddCompleteTableInfoList = false;
for (int i = 0; i < originBookList.size(); i++) {
DiningTableBean item = originBookList.get(i);
// 循环读取原始 Bean 列表,
// 如果遇到第一个匹配指定日期号,
// 就不显示 item 项,并且添加预制好的补全表(一次性添加,后续再遇到同类 item 项则不添加补全表。)。
if (isInCompleteRule(item)) {
// 设置不显示 item 项
item.setIsSelected(false);
// 添加补全列表(一次性添加,后续再遇到同类 item 项则不添加补全表。)
if (isAddCompleteTableInfoList == false) {
originBookList.addAll(i, mCompleteTableInfoList);
// 设置后续遇到同类项时,不再添加补全列表
isAddCompleteTableInfoList = true;
i = i + mCompleteTableInfoList.size();
}
}
}
// 如果遍历完原始 Bean 表后还未添加补全表,
// 就添加一次补全表
if (isAddCompleteTableInfoList == false) {
int nInsertPost = getFirstIndexOfDate(originBookList, metCompleteDate.getText().toString() + "");
//ToastUtils.show("nInsertPost " + Integer.toString(nInsertPost));
if (nInsertPost == -1) {
originBookList.addAll(mCompleteTableInfoList);
} else {
originBookList.addAll(nInsertPost, mCompleteTableInfoList);
}
// 设置后续遇到同类项时,不再添加补全列表
isAddCompleteTableInfoList = true;
}
}
public void onCopyResultText(View view) {
// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager) getApplicationContext().getSystemService(Context.CLIPBOARD_SERVICE);
// Creates a new text clip to put on the clipboard
ClipData clip = ClipData.newPlainText("simple text", metPreBookList.getText());
// Set the clipboard's primary clip.
clipboard.setPrimaryClip(clip);
Toast.makeText(getApplicationContext(), "Copy to clipboard.", Toast.LENGTH_SHORT).show();
}
public void onCleanResultText(View view) {
metPreBookList.setText("");
}
public void onCreatePreBookList(View view) {
if(metCompleteDate.getText().toString().trim().equals("")) {
ToastUtils.show("没有填写补全日期");
return;
}
//checkLinesDateValidAndShow();
createNullCompleteTableInfoList();
ArrayList<DiningTableBean> originBookList = getOriginBookList();
checkAndAddCompleteTableInfoList(originBookList);
StringBuilder sbPreBookList = new StringBuilder();
for (int i = 0; i < originBookList.size(); i++) {
DiningTableBean item = originBookList.get(i);
if (item.isDiningTableBean()) {
if (item.getIsSelected()) {
String szDinnerTyp = "";
if (item.getDinnerType() == DiningTableBean.DinnerType.Evening) {
szDinnerTyp = "";
}
if (item.getDinnerType() == DiningTableBean.DinnerType.Noon) {
szDinnerTyp = "";
}
String szTableType = "";
if (item.getTableType() == DiningTableBean.TableType.Room) {
szTableType = "";
}
if (item.getTableType() == DiningTableBean.TableType.Table) {
szTableType = "";
}
sbPreBookList.append(item.getDate());
//sbPreBookList.append("号");
sbPreBookList.append(szDinnerTyp);
//sbPreBookList.append(" ");
sbPreBookList.append(item.getTableNumber());
sbPreBookList.append(szTableType);
sbPreBookList.append("-");
sbPreBookList.append(item.getBookDesc());
sbPreBookList.append("\n");
}
} else {
sbPreBookList.append(item.getOriginDesc());
sbPreBookList.append("\n");
}
// 不同日期间隔行排版
if (i + 1 != originBookList.size()) {
if (!originBookList.get(i + 1).getDate().equals(originBookList.get(i).getDate())) {
sbPreBookList.append("\n");
}
}
}
//metPreBookList.append("\n");
metPreBookList.append(sbPreBookList.toString());
mScrollView.post(new Runnable() {
@Override
public void run() {
mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
}
}
@@ -1,226 +0,0 @@
package cc.winboll.studio.stallhelper.activities;
import android.graphics.Color;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.BackgroundColorSpan;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.stallhelper.R;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @CreateTime 2026-03-31 00:20:00
* @ModifyTime 2026-03-31 14:47:00
* @Describe 订单笔录辅助窗口,支持房号、桌号、多房间号(含"号"字)、手机号尾4位及'-'后文本高亮,原文完整输出
*/
public class NoteRecordHelperActivity extends AppCompatActivity {
public static final String TAG = "NoteRecordHelperActivity";
// 高亮颜色数组
private final int[] highlightColors = {
Color.parseColor("#4CAF50"),
Color.parseColor("#FF9800"),
Color.parseColor("#E91E63"),
Color.parseColor("#F44336"),
Color.parseColor("#FFC107"),
Color.parseColor("#2196F3")
};
// 正则匹配
// 匹配多房间号(包含后面的"号"字):11-16-17号
private final Pattern PATTERN_MULTI_ROOM = Pattern.compile("(\\d+(?:-\\d+)+\\s*号)");
// 匹配手机号(连续11位或带分隔符)
private final Pattern PATTERN_PHONE = Pattern.compile("(?:\\d{11}|(?:\\d{3,4}[\\s-]?){3,4})");
// 控件
private EditText etContent;
private Button btnHighlight;
private TextView tvHighlightResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_noterecordhelper);
LogUtils.i(TAG, "onCreate: 页面初始化");
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setTitle("订单笔录辅助");
initViews();
}
/**
* 初始化控件
*/
private void initViews() {
etContent = findViewById(R.id.et_content);
btnHighlight = findViewById(R.id.btn_highlight);
tvHighlightResult = findViewById(R.id.tv_highlight_result);
btnHighlight.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.i(TAG, "onClick: 点击高亮按钮");
highlightContent();
}
});
}
/**
* 解析并高亮内容
*/
private void highlightContent() {
LogUtils.i(TAG, "highlightContent: 开始处理高亮");
String raw = etContent.getText().toString();
if (raw.isEmpty()) {
LogUtils.i(TAG, "highlightContent: 输入内容为空,直接返回");
return;
}
String[] lines = raw.split("\\n");
SpannableStringBuilder ssb = new SpannableStringBuilder();
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
// 空行也完整保留
if (line.isEmpty()) {
ssb.append("\n");
continue;
}
int colorIndex = i % highlightColors.length;
int color = highlightColors[colorIndex];
LogUtils.d(TAG, "highlightContent: 处理第" + (i + 1) + "行,颜色索引=" + colorIndex);
processLine(line, ssb, color);
if (i != lines.length - 1) {
ssb.append("\n");
}
}
tvHighlightResult.setText(ssb);
LogUtils.i(TAG, "highlightContent: 高亮处理完成");
}
/**
* 处理单行高亮(原文完整保留)
* @param line 单行文本
* @param ssb 富文本构建器
* @param color 高亮颜色
*/
private void processLine(String line, SpannableStringBuilder ssb, int color) {
LogUtils.d(TAG, "processLine: line=" + line + ", color=" + Integer.toHexString(color));
// 1. 先把原文完整追加到 ssb,不会丢失任何字符
int start = ssb.length();
ssb.append(line);
// 2. 高亮多房间号(包含"号"字):11-16-17号
Matcher mMulti = PATTERN_MULTI_ROOM.matcher(line);
if (mMulti.find()) {
int s = start + mMulti.start(1);
int e = start + mMulti.end(1);
ssb.setSpan(new BackgroundColorSpan(color), s, e, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
// 3. 高亮房号 / 桌号
int roomIdx = line.indexOf("");
int tableIdx = line.indexOf("号桌");
if (roomIdx != -1) {
int s = findNumberStart(line, roomIdx);
int e = roomIdx + 1;
ssb.setSpan(new BackgroundColorSpan(color), start + s, start + e, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} else if (tableIdx != -1) {
int s = findNumberStart(line, tableIdx);
int e = tableIdx + 2;
ssb.setSpan(new BackgroundColorSpan(color), start + s, start + e, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
// 4. 处理'-'后的内容:优先高亮手机号尾4位,否则高亮全部文本
int dashIndex = line.indexOf("-");
if (dashIndex != -1) {
String afterDash = line.substring(dashIndex + 1);
Matcher phoneMatcher = PATTERN_PHONE.matcher(afterDash);
boolean foundPhone = false;
// 尝试匹配手机号并高亮尾4位
while (phoneMatcher.find()) {
String phone = phoneMatcher.group();
if (phone != null) {
foundPhone = true;
// 提取纯数字
String pureNumber = phone.replaceAll("[^0-9]", "");
if (pureNumber.length() >= 4) {
String last4 = pureNumber.substring(pureNumber.length() - 4);
// 在原行中定位尾4位(从 dashIndex+1 开始查找,避免匹配前面的数字)
int tailStart = line.indexOf(last4, dashIndex + 1);
if (tailStart != -1) {
int s = start + tailStart;
int e = s + 4;
ssb.setSpan(new BackgroundColorSpan(color), s, e, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
}
// 没有手机号,则高亮'-'后的全部文本
if (!foundPhone) {
int s = start + dashIndex + 1;
int e = start + line.length();
ssb.setSpan(new BackgroundColorSpan(color), s, e, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {
// 无'-'的行,尝试匹配并高亮手机号尾4位
Matcher phoneMatcher = PATTERN_PHONE.matcher(line);
while (phoneMatcher.find()) {
String phone = phoneMatcher.group();
if (phone != null) {
String pureNumber = phone.replaceAll("[^0-9]", "");
if (pureNumber.length() >= 4) {
String last4 = pureNumber.substring(pureNumber.length() - 4);
int tailStart = line.indexOf(last4);
if (tailStart != -1) {
int s = start + tailStart;
int e = s + 4;
ssb.setSpan(new BackgroundColorSpan(color), s, e, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
}
}
}
/**
* 查找数字起始位置
* @param line 文本行
* @param endIdx 结束位置
* @return 数字起始下标
*/
private int findNumberStart(String line, int endIdx) {
LogUtils.d(TAG, "findNumberStart: endIdx=" + endIdx);
for (int i = endIdx - 1; i >= 0; i--) {
char c = line.charAt(i);
if (!Character.isDigit(c) && c != ' ') {
return i + 1;
}
}
return 0;
}
}
@@ -1,264 +0,0 @@
package cc.winboll.studio.stallhelper.activities;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/10/16 05:13:42
* @Describe 餐桌空白预定表生成窗口
*/
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.stallhelper.R;
import cc.winboll.studio.stallhelper.beans.DiningTableBean;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
public class PreNullDiningTableActivity extends WinBoLLActivity {
public static final String TAG = "DiningTableActivity";
ArrayList<DiningTableBean> mListTableInfo;
DiningTableBeanListAdapter mDiningTableBeanListAdapter;
EditText metPreBookList;
ScrollView mScrollView;
RadioButton mrbDinnerTypeNoon;
RadioButton mrbDinnerTypeEvening;
@Override
public String getTag() {
return TAG;
}
// @Override
// protected boolean isEnableDisplayHomeAsUp() {
// return true;
// }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_prenulldiningtable);
setTitle("餐桌空白预定表制作");
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
StringBuilder sbDateNow = new StringBuilder("当前日期:");
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timeString = sdf.format(now);
sbDateNow.append(timeString);
TextView tvDateNow = findViewById(R.id.activityprenulldiningtableTextView2);
tvDateNow.setText(sbDateNow.toString());
LocalDate currentDate = LocalDate.now();
int dayOfMonth = currentDate.getDayOfMonth();
EditText etDateNow = findViewById(R.id.activityprenulldiningtableEditText1);
etDateNow.setText(Integer.toString(dayOfMonth));
mListTableInfo = new ArrayList<DiningTableBean>();
addTable();
RecyclerView recyclerView = findViewById(R.id.activityprenulldiningtableRecyclerView1);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.HORIZONTAL, false));
mDiningTableBeanListAdapter = new DiningTableBeanListAdapter(mListTableInfo);
// 设置 RecyclerView 的适配器
recyclerView.setAdapter(mDiningTableBeanListAdapter);
metPreBookList = findViewById(R.id.activityprenulldiningtableEditText2);
mScrollView = findViewById(R.id.activityprenulldiningtableScrollView1);
mrbDinnerTypeNoon = findViewById(R.id.activityprenulldiningtableRadioButton1);
mrbDinnerTypeEvening = findViewById(R.id.activityprenulldiningtableRadioButton2);
mrbDinnerTypeEvening.setChecked(true);
}
//
// @Override
// protected Toolbar initToolBar() {
// return findViewById(R.id.activityprenulldiningtableToolbar1);
// }
//
// @Override
// protected boolean isAddWinBoLLToolBar() {
// return false;
// }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
WinBoLLActivityManager.getInstance().finish(this);
}
return super.onOptionsItemSelected(item);
}
void addTable() {
mListTableInfo.add(new DiningTableBean("201", 18, false));
mListTableInfo.add(new DiningTableBean("202", 8, false));
mListTableInfo.add(new DiningTableBean("203", 8, false));
mListTableInfo.add(new DiningTableBean("205", 10, false));
mListTableInfo.add(new DiningTableBean("206", 0, false));
mListTableInfo.add(new DiningTableBean("207", 0, false));
mListTableInfo.add(new DiningTableBean("208", 8, false));
mListTableInfo.add(new DiningTableBean("209", 10, false));
mListTableInfo.add(new DiningTableBean("210", 8, false));
mListTableInfo.add(new DiningTableBean("211", 12, false));
mListTableInfo.add(new DiningTableBean("212", 8, false));
mListTableInfo.add(new DiningTableBean("213", 9, false));
mListTableInfo.add(new DiningTableBean("666", 14, false));
mListTableInfo.add(new DiningTableBean("888", 20, false));
}
public void onDinnerType(View view) {
RadioButton rb = (RadioButton)view;
if (rb.getId() == mrbDinnerTypeNoon.getId()) {
ToastUtils.show("午餐");
mrbDinnerTypeEvening.setChecked(false);
} else {
ToastUtils.show("晚餐");
mrbDinnerTypeNoon.setChecked(false);
}
}
public void onCopyResultText(View view) {
// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager) getApplicationContext().getSystemService(Context.CLIPBOARD_SERVICE);
// Creates a new text clip to put on the clipboard
ClipData clip = ClipData.newPlainText("simple text", metPreBookList.getText());
// Set the clipboard's primary clip.
clipboard.setPrimaryClip(clip);
Toast.makeText(getApplicationContext(), "Copy to clipboard.", Toast.LENGTH_SHORT).show();
}
public void onCleanResultText(View view) {
metPreBookList.setText("");
}
public void onCreatePreBookList(View view) {
String szDinnerTyp = mrbDinnerTypeEvening.isChecked() ?"": "";
EditText etDate = findViewById(R.id.activityprenulldiningtableEditText1);
StringBuilder sbPreBookList = new StringBuilder();
for (DiningTableBean item : mListTableInfo) {
if (item.getIsSelected()) {
/*sbPreBookList.append(etDate.getText());
sbPreBookList.append("号 ");
sbPreBookList.append(szDinnerTyp);
sbPreBookList.append(" ");
sbPreBookList.append(item.getTableNumber());
sbPreBookList.append("房-\n");*/
String szTableType = "";
if (item.getTableType() == DiningTableBean.TableType.Room) {
szTableType = "";
}
if (item.getTableType() == DiningTableBean.TableType.Table) {
szTableType = "";
}
sbPreBookList.append(etDate.getText());
sbPreBookList.append("");
sbPreBookList.append(szDinnerTyp);
sbPreBookList.append(" ");
sbPreBookList.append(item.getTableNumber());
sbPreBookList.append(szTableType);
sbPreBookList.append("-");
sbPreBookList.append(item.getBookDesc());
sbPreBookList.append("\n");
}
}
//metPreBookList.append("\n");
metPreBookList.append(sbPreBookList.toString());
mScrollView.post(new Runnable() {
@Override
public void run() {
mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
}
public void onSelectALLTable(View view) {
boolean isSelectAll =((CheckBox)findViewById(R.id.activityprenulldiningtableCheckBox1)).isChecked();
for (DiningTableBean item : mListTableInfo) {
item.setIsSelected(isSelectAll);
}
//ToastUtils.show(Boolean.toString(isSelectAll));
mDiningTableBeanListAdapter.notifyDataSetChanged();
}
class DiningTableBeanListAdapter extends RecyclerView.Adapter {
ArrayList<DiningTableBean> mDataList;
public DiningTableBeanListAdapter(ArrayList<DiningTableBean> listInfo) {
mDataList = listInfo;
}
@Override
public int getItemCount() {
return mDataList.size();
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
final DiningTableBean item = mDataList.get(position);
if (holder.getItemViewType() == 0) {
final SimpleViewHolder viewHolder = (SimpleViewHolder) holder;
viewHolder.mcbTable.setText(item.getTableNumber() + "房(" + Integer.toString(item.getMaxPersonCount()) + "人)");
viewHolder.mcbTable.setChecked(item.getIsSelected());
viewHolder.mcbTable.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
item.setIsSelected(viewHolder.mcbTable.isChecked());
}
});
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == 0) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.listview_tableinfo, parent, false);
return new SimpleViewHolder(view);
}
return null;
}
class SimpleViewHolder extends RecyclerView.ViewHolder {
CheckBox mcbTable;
SimpleViewHolder(View itemView) {
super(itemView);
mcbTable = itemView.findViewById(R.id.listviewtableinfoCheckBox1);
}
}
}
}
@@ -1,432 +0,0 @@
package cc.winboll.studio.stallhelper.activities;
import android.app.Dialog;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.stallhelper.R;
import cc.winboll.studio.stallhelper.beans.TaskSchedulerBean;
import java.util.ArrayList;
/**
* @Author ZhanGSKen
* @Date 2026/05/18
* @Describe 任务排档窗口
*/
public class TaskSchedulerActivity extends WinBoLLActivity {
public static final String TAG = "TaskSchedulerActivity";
// 时间转换常量
private static final int MINUTES_PER_HOUR = 60;
private static final int HOURS_PER_DAY = 24;
private static final int DAYS_PER_MONTH = 30; // 默认按30天计算
ArrayList<TaskSchedulerBean> mTaskList;
TaskSchedulerAdapter mAdapter;
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, 1001, 0, "编辑任务内容");
}
@Override
public boolean onContextItemSelected(MenuItem item) {
if (item.getItemId() == 1001) {
View triggerView = item.getActionView();
if (triggerView != null && triggerView.getTag() instanceof Integer) {
int position = (Integer) triggerView.getTag();
showEditTaskDialog(position);
}
return true;
}
return super.onContextItemSelected(item);
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_task_scheduler);
LogUtils.i(TAG, "onCreate: 任务排档页面初始化");
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setTitle("任务排档");
// 初始化任务列表
mTaskList = new ArrayList<TaskSchedulerBean>();
initSampleData();
// 设置 RecyclerView
RecyclerView recyclerView = findViewById(R.id.task_scheduler_recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.VERTICAL, false));
mAdapter = new TaskSchedulerAdapter(mTaskList);
recyclerView.setAdapter(mAdapter);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
WinBoLLActivityManager.getInstance().finish(this);
}
return super.onOptionsItemSelected(item);
}
/**
* 格式化时间戳为年月日和小时
* @param timestamp 时间戳
* @return 格式化的时间字符串,如 "2026-05-18 09"
*/
private String formatTimestamp(long timestamp) {
if (timestamp == 0) {
return "";
}
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH");
java.util.Date date = new java.util.Date(timestamp);
return sdf.format(date);
}
/**
* 解析时间字符串为时间戳
* @param timeStr 时间字符串,如 "2026-05-18 09"
* @return 时间戳
*/
private long parseTimeString(String timeStr) {
if (timeStr == null || timeStr.trim().isEmpty()) {
return 0;
}
try {
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH");
java.util.Date date = sdf.parse(timeStr);
return date.getTime();
} catch (Exception e) {
LogUtils.e(TAG, "parseTimeString: 时间解析错误", e);
return 0;
}
}
/**
* 初始化示例数据
*/
private void initSampleData() {
// 创建今天的日期作为基础
java.util.Calendar calendar = java.util.Calendar.getInstance();
calendar.set(java.util.Calendar.MINUTE, 0);
calendar.set(java.util.Calendar.SECOND, 0);
calendar.set(java.util.Calendar.MILLISECOND, 0);
// 设置不同的小时
calendar.set(java.util.Calendar.HOUR_OF_DAY, 9);
long time9 = calendar.getTimeInMillis();
calendar.set(java.util.Calendar.HOUR_OF_DAY, 10);
long time10 = calendar.getTimeInMillis();
calendar.set(java.util.Calendar.HOUR_OF_DAY, 11);
calendar.set(java.util.Calendar.MINUTE, 30);
long time1130 = calendar.getTimeInMillis();
calendar.set(java.util.Calendar.HOUR_OF_DAY, 12);
calendar.set(java.util.Calendar.MINUTE, 30);
long time1230 = calendar.getTimeInMillis();
calendar.set(java.util.Calendar.HOUR_OF_DAY, 16);
calendar.set(java.util.Calendar.MINUTE, 0);
long time16 = calendar.getTimeInMillis();
calendar.set(java.util.Calendar.HOUR_OF_DAY, 17);
long time17 = calendar.getTimeInMillis();
mTaskList.add(new TaskSchedulerBean("准备食材", "购买新鲜蔬菜和肉类", time9, 60));
mTaskList.add(new TaskSchedulerBean("烹饪午餐", "按照食谱准备午餐", time10, 90));
mTaskList.add(new TaskSchedulerBean("用餐休息", "享受美食并休息", time1130, 60));
mTaskList.add(new TaskSchedulerBean("清洁厨房", "清洗餐具和整理厨房", time1230, 45));
mTaskList.add(new TaskSchedulerBean("准备晚餐", "提前准备晚餐食材", time16, 60));
mTaskList.add(new TaskSchedulerBean("烹饪晚餐", "制作丰盛的晚餐", time17, 90));
}
/**
* 将月、天、小时转换为总分钟数
* @param month 月份
* @param day 天数
* @param hour 小时
* @return 总分钟数
*/
private int convertToTotalMinutes(int month, int day, int hour) {
return month * DAYS_PER_MONTH * HOURS_PER_DAY * MINUTES_PER_HOUR +
day * HOURS_PER_DAY * MINUTES_PER_HOUR +
hour * MINUTES_PER_HOUR;
}
/**
* 将总分钟数转换为月、天、小时的格式字符串
* @param totalMinutes 总分钟数
* @return 格式化的字符串,如 "0月0天1小时"
*/
private String formatDurationToString(int totalMinutes) {
int totalHours = totalMinutes / MINUTES_PER_HOUR;
int totalDays = totalHours / HOURS_PER_DAY;
int month = totalDays / DAYS_PER_MONTH;
int day = totalDays % DAYS_PER_MONTH;
int hour = totalHours % HOURS_PER_DAY;
StringBuilder sb = new StringBuilder();
if (month > 0) {
sb.append(month).append("");
}
if (day > 0) {
sb.append(day).append("");
}
if (hour > 0) {
sb.append(hour).append("小时");
}
if (sb.length() == 0) {
sb.append("0小时");
}
return sb.toString();
}
/**
* 显示编辑任务对话框
* @param position 要编辑的任务位置
*/
private void showEditTaskDialog(final int position) {
if (position < 0 || position >= mTaskList.size()) {
return;
}
final TaskSchedulerBean task = mTaskList.get(position);
final Dialog dialog = new Dialog(this);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(R.layout.dialog_task_edit);
// 初始化对话框控件
final EditText etTaskName = dialog.findViewById(R.id.dialog_et_task_name);
final EditText etCurrentScheduleTime = dialog.findViewById(R.id.dialog_et_start_time);
final EditText etMonth = dialog.findViewById(R.id.dialog_et_month);
final EditText etDay = dialog.findViewById(R.id.dialog_et_day);
final EditText etHour = dialog.findViewById(R.id.dialog_et_hour);
final EditText etRemark = dialog.findViewById(R.id.dialog_et_remark);
Button btnCancel = dialog.findViewById(R.id.dialog_btn_cancel);
Button btnSave = dialog.findViewById(R.id.dialog_btn_save);
// 填充现有数据 - 将分钟转换为月、天、小时
etTaskName.setText(task.getTaskName());
etCurrentScheduleTime.setText(formatTimestamp(task.getCurrentScheduleTime()));
etRemark.setText(task.getRemark());
// 反向转换:将分钟转换为月、天、小时
int totalMinutes = task.getDurationMinutes();
int totalHours = totalMinutes / MINUTES_PER_HOUR;
int totalDays = totalHours / HOURS_PER_DAY;
int month = totalDays / DAYS_PER_MONTH;
int day = totalDays % DAYS_PER_MONTH;
int hour = totalHours % HOURS_PER_DAY;
if (month > 0) {
etMonth.setText(Integer.toString(month));
}
if (day > 0) {
etDay.setText(Integer.toString(day));
}
if (hour > 0) {
etHour.setText(Integer.toString(hour));
}
// 取消按钮
btnCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
// 保存按钮
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 获取输入的数据
String taskName = etTaskName.getText().toString().trim();
String currentScheduleTime = etCurrentScheduleTime.getText().toString().trim();
String monthStr = etMonth.getText().toString().trim();
String dayStr = etDay.getText().toString().trim();
String hourStr = etHour.getText().toString().trim();
String remark = etRemark.getText().toString().trim();
// 验证输入
if (taskName.isEmpty()) {
etTaskName.setError("请输入任务名称");
return;
}
if (currentScheduleTime.isEmpty()) {
etCurrentScheduleTime.setError("请输入当前排档时间");
return;
}
// 解析时间字符串为时间戳
long timestamp = parseTimeString(currentScheduleTime);
if (timestamp == 0) {
etCurrentScheduleTime.setError("时间格式不正确,请使用 yyyy-MM-dd HH 格式");
return;
}
// 解析排档幅度 - 月、天、小时
int month = 0;
int day = 0;
int hour = 0;
try {
if (!monthStr.isEmpty()) {
month = Integer.parseInt(monthStr);
}
if (!dayStr.isEmpty()) {
day = Integer.parseInt(dayStr);
}
if (!hourStr.isEmpty()) {
hour = Integer.parseInt(hourStr);
}
} catch (NumberFormatException e) {
LogUtils.e(TAG, "showEditTaskDialog: 数字解析错误", e);
return;
}
// 转换为总分钟数
int durationMinutes = convertToTotalMinutes(month, day, hour);
// 更新任务数据
task.setTaskName(taskName);
task.setCurrentScheduleTime(timestamp);
task.setDurationMinutes(durationMinutes);
task.setRemark(remark);
// 刷新列表
mAdapter.notifyItemChanged(position);
LogUtils.i(TAG, "showEditTaskDialog: 任务 [" + taskName + "] 数据已更新,排档幅度=" + durationMinutes + "分钟");
dialog.dismiss();
}
});
dialog.show();
}
/**
* 执行排档操作
* @param position 点击排档按钮的任务位置
*/
private void performSchedule(int position) {
if (position < 0 || position >= mTaskList.size()) {
return;
}
TaskSchedulerBean currentTask = mTaskList.get(position);
long currentTime = currentTask.getCurrentScheduleTime();
int duration = currentTask.getDurationMinutes();
// 计算新的时间(时间戳加上分钟数)
long newTime = currentTime + duration * 60 * 1000L;
LogUtils.i(TAG, "performSchedule: 当前时间 " + formatTimestamp(currentTime) + " + " + duration + "分钟 = " + formatTimestamp(newTime));
// 更新下一个任务的时间(如果有)
if (position + 1 < mTaskList.size()) {
TaskSchedulerBean nextTask = mTaskList.get(position + 1);
nextTask.setCurrentScheduleTime(newTime);
mAdapter.notifyItemChanged(position + 1);
LogUtils.i(TAG, "performSchedule: 更新任务 [" + nextTask.getTaskName() + "] 时间更新为 " + formatTimestamp(newTime));
}
}
/**
* RecyclerView 适配器
*/
class TaskSchedulerAdapter extends RecyclerView.Adapter {
ArrayList<TaskSchedulerBean> mDataList;
public TaskSchedulerAdapter(ArrayList<TaskSchedulerBean> dataList) {
mDataList = dataList;
}
@Override
public int getItemCount() {
return mDataList.size();
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
final TaskSchedulerBean item = mDataList.get(position);
final TaskViewHolder viewHolder = (TaskViewHolder) holder;
viewHolder.mtvTaskName.setText(item.getTaskName());
viewHolder.mtvCurrentScheduleTime.setText(formatTimestamp(item.getCurrentScheduleTime()));
// 格式化显示排档幅度
viewHolder.mtvDuration.setText(formatDurationToString(item.getDurationMinutes()));
viewHolder.mtvRemark.setText(item.getRemark());
// mllMain长按存入当前条目独立位置Tag,互不干扰
viewHolder.mllMain.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
v.setTag(position);
registerForContextMenu(v);
openContextMenu(v);
return true;
}
});
// 设置排档按钮点击事件
viewHolder.mbtnSchedule.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
performSchedule(position);
}
});
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_task_scheduler, parent, false);
return new TaskViewHolder(view);
}
class TaskViewHolder extends RecyclerView.ViewHolder {
TextView mtvTaskName;
TextView mtvCurrentScheduleTime;
TextView mtvDuration;
TextView mtvRemark;
LinearLayout mllMain;
Button mbtnSchedule;
TaskViewHolder(View itemView) {
super(itemView);
mtvTaskName = itemView.findViewById(R.id.item_task_name);
mtvCurrentScheduleTime = itemView.findViewById(R.id.item_start_time);
mtvDuration = itemView.findViewById(R.id.item_duration);
mtvRemark = itemView.findViewById(R.id.item_remark);
mllMain = itemView.findViewById(R.id.itemtaskscheduler_llmain);
mbtnSchedule = itemView.findViewById(R.id.item_btn_schedule);
}
}
}
}
@@ -1,82 +0,0 @@
package cc.winboll.studio.stallhelper.activities;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/28 15:34:16
* @Describe 应用活动窗口基类
*/
import android.app.Activity;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.view.MenuItem;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libaes.dialogs.YesNoAlertDialog;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.stallhelper.App;
import cc.winboll.studio.stallhelper.R;
public class WinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity {
public static final String TAG = "WinBoLLActivityBase";
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
WinBoLLActivityManager getWinBoLLActivityManager() {
return WinBoLLActivityManager.getInstance();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWinBoLLActivityManager().add(this);
}
@Override
public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onPostCreate(savedInstanceState, persistentState);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
// GlobalApplication.getWinBoLLActivityManager().startLogActivity(this);
// return true;
// } else if(item.getItemId() == cc.winboll.studio.appbase.R.id.item_minimal) {
// //moveTaskToBack(true);
// exit();
// }
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
return super.onOptionsItemSelected(item);
}
void exit() {
YesNoAlertDialog.show(this, "Exit " + getString(R.string.app_name), "Close all activity and exit?", new YesNoAlertDialog.OnDialogResultListener(){
@Override
public void onYes() {
WinBoLLActivityManager.getInstance().finishAll();
}
@Override
public void onNo() {
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
getWinBoLLActivityManager().registeRemove(this);
}
}
@@ -1,216 +0,0 @@
package cc.winboll.studio.stallhelper.beans;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/10/16 06:09:54
* @Describe 餐桌
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
public class DiningTableBean extends BaseBean {
public static final String TAG = "AuthenticationBean";
public enum DinnerType { Noon, Evening };
public enum TableType { Room, Table };
// 餐桌号
String tableNumber;
// 最大就餐人数
int maxPersonCount;
// 是否选取了这张桌号
boolean isSelected;
// 预定时间类型
DinnerType dinnerType;
// 预定信息
String bookDesc;
// 预定时间
String date;
// 餐桌类型
TableType tableType;
// 是否被识别为 DiningTableBean
boolean isDiningTableBean;
// 或者是无法识别为 DiningTableBean 的原始信息。
String originDesc;
public DiningTableBean(String tableNumber, int maxPersonCount, boolean isSelected) {
this.tableNumber = tableNumber;
this.maxPersonCount = maxPersonCount;
this.isSelected = isSelected;
this.dinnerType = DinnerType.Evening;
this.bookDesc = "";
this.date = "";
this.tableType = TableType.Room;
this.isDiningTableBean = true;
this.originDesc = "";
}
public DiningTableBean(String tableNumber, int maxPersonCount, boolean isSelected, String date) {
this.tableNumber = tableNumber;
this.maxPersonCount = maxPersonCount;
this.isSelected = isSelected;
this.dinnerType = DinnerType.Evening;
this.bookDesc = "";
this.date = date;
this.tableType = TableType.Room;
this.isDiningTableBean = true;
this.originDesc = "";
}
public DiningTableBean(String tableNumber, int maxPersonCount, boolean isSelected, DinnerType dinnerType, String bookDesc, String date, TableType tableType) {
this.tableNumber = tableNumber;
this.maxPersonCount = maxPersonCount;
this.isSelected = isSelected;
this.dinnerType = dinnerType;
this.bookDesc = bookDesc;
this.date = date;
this.tableType = tableType;
this.isDiningTableBean = true;
this.originDesc = "";
}
public DiningTableBean(boolean isSelected, boolean isDiningTableBean, String originDesc) {
this.tableNumber = "";
this.maxPersonCount = 0;
this.isSelected = isSelected;
this.dinnerType = DinnerType.Evening;
this.bookDesc = "";
this.date = "";
this.tableType = TableType.Room;
this.isDiningTableBean = isDiningTableBean;
this.originDesc = originDesc;
}
public void setOriginDesc(String originDesc) {
this.originDesc = originDesc;
}
public String getOriginDesc() {
return originDesc;
}
public void setIsDiningTableBean(boolean isDiningTableBean) {
this.isDiningTableBean = isDiningTableBean;
}
public boolean isDiningTableBean() {
return isDiningTableBean;
}
public void setTableType(TableType tableType) {
this.tableType = tableType;
}
public TableType getTableType() {
return tableType;
}
public void setDate(String date) {
this.date = date;
}
public String getDate() {
return date;
}
public void setDinnerType(DinnerType dinnerType) {
this.dinnerType = dinnerType;
}
public DinnerType getDinnerType() {
return dinnerType;
}
public void setBookDesc(String bookDesc) {
this.bookDesc = bookDesc;
}
public String getBookDesc() {
return bookDesc;
}
public void setIsSelected(boolean isSelected) {
this.isSelected = isSelected;
}
public boolean getIsSelected() {
return isSelected;
}
public void setTableNumber(String tableNumber) {
this.tableNumber = tableNumber;
}
public String getTableNumber() {
return tableNumber;
}
public void setMaxPersonCount(int maxPersonCount) {
this.maxPersonCount = maxPersonCount;
}
public int getMaxPersonCount() {
return maxPersonCount;
}
@Override
public String getName() {
return DiningTableBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
DiningTableBean bean = this;
jsonWriter.name("tableNumber").value(bean.getTableNumber());
jsonWriter.name("maxPersonCount").value(bean.getMaxPersonCount());
jsonWriter.name("isSelected").value(bean.getTableNumber());
jsonWriter.name("dinnerType").value(bean.getDinnerType().ordinal());
jsonWriter.name("bookDesc").value(bean.getBookDesc());
jsonWriter.name("date").value(bean.getDate());
jsonWriter.name("tableType").value(bean.getTableType().ordinal());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("tableNumber")) {
setTableNumber(jsonReader.nextString());
} else if (name.equals("maxPersonCount")) {
setMaxPersonCount(jsonReader.nextInt());
} else if (name.equals("isSelected")) {
setIsSelected(jsonReader.nextBoolean());
} else if (name.equals("dinnerType")) {
setDinnerType(DinnerType.values()[jsonReader.nextInt()]);
} else if (name.equals("bookDesc")) {
setBookDesc(jsonReader.nextString());
} else if (name.equals("date")) {
setDate(jsonReader.nextString());
} else if (name.equals("tableType")) {
setTableType(TableType.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;
}
}
@@ -1,117 +0,0 @@
package cc.winboll.studio.stallhelper.beans;
/**
* @Author ZhanGSKen
* @Date 2026/05/18
* @Describe 任务排档数据模型
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
public class TaskSchedulerBean extends BaseBean {
public static final String TAG = "TaskSchedulerBean";
// 任务排档名称
String taskName;
// 备注
String remark;
// 当前排档时间(时间戳)
long currentScheduleTime;
// 排档时间幅度(分钟)- 用于计算下一个任务的起始时间
int durationMinutes;
public TaskSchedulerBean() {
this.taskName = "";
this.remark = "";
this.currentScheduleTime = 0;
this.durationMinutes = 0;
}
public TaskSchedulerBean(String taskName, String remark, long currentScheduleTime, int durationMinutes) {
this.taskName = taskName;
this.remark = remark;
this.currentScheduleTime = currentScheduleTime;
this.durationMinutes = durationMinutes;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public long getCurrentScheduleTime() {
return currentScheduleTime;
}
public void setCurrentScheduleTime(long currentScheduleTime) {
this.currentScheduleTime = currentScheduleTime;
}
public int getDurationMinutes() {
return durationMinutes;
}
public void setDurationMinutes(int durationMinutes) {
this.durationMinutes = durationMinutes;
}
@Override
public String getName() {
return TaskSchedulerBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
TaskSchedulerBean bean = this;
jsonWriter.name("taskName").value(bean.getTaskName());
jsonWriter.name("remark").value(bean.getRemark());
jsonWriter.name("startTime").value(bean.getCurrentScheduleTime());
jsonWriter.name("durationMinutes").value(bean.getDurationMinutes());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("taskName")) {
setTaskName(jsonReader.nextString());
} else if (name.equals("remark")) {
setRemark(jsonReader.nextString());
} else if (name.equals("startTime")) {
setCurrentScheduleTime(jsonReader.nextLong());
} else if (name.equals("durationMinutes")) {
setDurationMinutes(jsonReader.nextInt());
} else {
return false;
}
}
return true;
}
@Override
public TaskSchedulerBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (!initObjectsFromJsonReader(jsonReader, name)) {
jsonReader.skipValue();
}
}
jsonReader.endObject();
return this;
}
}
@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 阴影部分 -->
<!-- 个人觉得更形象的表达:top代表下边的阴影高度,left代表右边的阴影宽度。其实也就是相对应的offset,solid中的颜色是阴影的颜色,也可以设置角度等等 -->
<item
android:left="2dp"
android:top="2dp"
android:right="2dp"
android:bottom="2dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:endColor="#0F000000"
android:startColor="#0F000000" />
<corners
android:bottomLeftRadius="6dip"
android:bottomRightRadius="6dip"
android:topLeftRadius="6dip"
android:topRightRadius="6dip" />
</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:bottomLeftRadius="6dip"
android:bottomRightRadius="6dip"
android:topLeftRadius="6dip"
android:topRightRadius="6dip" />
</shape>
</item>
</layer-list>
@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 阴影部分 -->
<!-- 个人觉得更形象的表达:top代表下边的阴影高度,left代表右边的阴影宽度。其实也就是相对应的offset,solid中的颜色是阴影的颜色,也可以设置角度等等 -->
<item
android:left="2dp"
android:top="2dp"
android:right="2dp"
android:bottom="2dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:endColor="#0F000000"
android:startColor="#0F000000" />
<corners
android:bottomLeftRadius="6dip"
android:bottomRightRadius="6dip"
android:topLeftRadius="6dip"
android:topRightRadius="6dip" />
</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="#AF000000"
android:startColor="#AF000000" />
<corners
android:bottomLeftRadius="6dip"
android:bottomRightRadius="6dip"
android:topLeftRadius="6dip"
android:topRightRadius="6dip" />
</shape>
</item>
</layer-list>
@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 阴影部分 -->
<!-- 个人觉得更形象的表达:top代表下边的阴影高度,left代表右边的阴影宽度。其实也就是相对应的offset,solid中的颜色是阴影的颜色,也可以设置角度等等 -->
<item
android:left="2dp"
android:top="2dp"
android:right="2dp"
android:bottom="2dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:endColor="#0FFFFFFF"
android:startColor="#0FFFFFFF" />
<corners
android:bottomLeftRadius="6dip"
android:bottomRightRadius="6dip"
android:topLeftRadius="6dip"
android:topRightRadius="6dip" />
</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="#CFFFFFFF"
android:startColor="#CFFFFFFF" />
<corners
android:bottomLeftRadius="6dip"
android:bottomRightRadius="6dip"
android:topLeftRadius="6dip"
android:topRightRadius="6dip" />
</shape>
</item>
</layer-list>
@@ -1,69 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:fillColor="#FF977A1F"
android:strokeColor="#FF977A1F"
android:strokeWidth="2.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M67.56 132.48C67.56 132.48 196.61 132.48 196.61 132.48 196.61 132.48 196.61 140.79 196.61 140.79 196.61 140.79 67.56 140.79 67.56 140.79 67.56 140.79 67.56 132.48 67.56 132.48"/>
<path
android:fillColor="#FF977A1F"
android:strokeColor="#FF977A1F"
android:strokeWidth="2.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M100.51 140.79C100.51 140.79 108.38 140.79 108.38 140.79 108.38 140.79 108.38 211.22 108.38 211.22 108.38 211.22 100.51 211.22 100.51 211.22 100.51 211.22 100.51 140.79 100.51 140.79"/>
<path
android:fillColor="#FF977A1F"
android:strokeColor="#FF977A1F"
android:strokeWidth="2.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M157.4 140.79C157.4 140.79 165.72 140.79 165.72 140.79 165.72 140.79 165.72 211.22 165.72 211.22 165.72 211.22 157.4 211.22 157.4 211.22 157.4 211.22 157.4 140.79 157.4 140.79"/>
<path
android:fillColor="#FF977A1F"
android:strokeColor="#FF977A1F"
android:strokeWidth="2.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M32.05 85.46C32.05 85.46 38.61 85.46 38.61 85.46 38.61 85.46 38.61 211.22 38.61 211.22 38.61 211.22 32.05 211.22 32.05 211.22 32.05 211.22 32.05 85.46 32.05 85.46"/>
<path
android:fillColor="#FF977A1F"
android:strokeColor="#FF977A1F"
android:strokeWidth="2.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M38.61 160.26C38.61 160.26 77.24 160.26 77.24 160.26 77.24 160.26 77.24 165.51 77.24 165.51 77.24 165.51 38.61 165.51 38.61 165.51 38.61 165.51 38.61 160.26 38.61 160.26"/>
<path
android:fillColor="#FF977A1F"
android:strokeColor="#FF977A1F"
android:strokeWidth="2.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M73.52 160.26C73.52 160.26 80.96 160.26 80.96 160.26 80.96 160.26 80.96 211.22 80.96 211.22 80.96 211.22 73.52 211.22 73.52 211.22 73.52 211.22 73.52 160.26 73.52 160.26"/>
<path
android:fillColor="#FF977A1F"
android:strokeColor="#FF977A1F"
android:strokeWidth="2.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M226.71 85.46C226.71 85.46 233.27 85.46 233.27 85.46 233.27 85.46 233.27 211.22 233.27 211.22 233.27 211.22 226.71 211.22 226.71 211.22 226.71 211.22 226.71 85.46 226.71 85.46"/>
<path
android:fillColor="#FF977A1F"
android:strokeColor="#FF977A1F"
android:strokeWidth="2.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M188.08 157.64C188.08 157.64 226.71 157.64 226.71 157.64 226.71 157.64 226.71 162.89 226.71 162.89 226.71 162.89 188.08 162.89 188.08 162.89 188.08 162.89 188.08 157.64 188.08 157.64"/>
<path
android:fillColor="#FF977A1F"
android:strokeColor="#FF977A1F"
android:strokeWidth="2.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M188.08 157.64C188.08 157.64 195.52 157.64 195.52 157.64 195.52 157.64 195.52 208.59 195.52 208.59 195.52 208.59 188.08 208.59 188.08 208.59 188.08 208.59 188.08 157.64 188.08 157.64"/>
</vector>
@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:clickable="true">
<item android:drawable="@drawable/ic_launcher_background"/>
<item
android:left="15dp"
android:top="15dp"
android:right="15dp"
android:bottom="15dp"
android:drawable="@drawable/ic_launcher_foreground"/>
</layer-list>
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M16.61,15.15C16.15,15.15 15.77,14.78 15.77,14.32S16.15,13.5 16.61,13.5H16.61C17.07,13.5 17.45,13.86 17.45,14.32C17.45,14.78 17.07,15.15 16.61,15.15M7.41,15.15C6.95,15.15 6.57,14.78 6.57,14.32C6.57,13.86 6.95,13.5 7.41,13.5H7.41C7.87,13.5 8.24,13.86 8.24,14.32C8.24,14.78 7.87,15.15 7.41,15.15M16.91,10.14L18.58,7.26C18.67,7.09 18.61,6.88 18.45,6.79C18.28,6.69 18.07,6.75 18,6.92L16.29,9.83C14.95,9.22 13.5,8.9 12,8.91C10.47,8.91 9,9.24 7.73,9.82L6.04,6.91C5.95,6.74 5.74,6.68 5.57,6.78C5.4,6.87 5.35,7.08 5.44,7.25L7.1,10.13C4.25,11.69 2.29,14.58 2,18H22C21.72,14.59 19.77,11.7 16.91,10.14H16.91Z"/>
</vector>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

@@ -1,62 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="256"
android:viewportHeight="256">
<!-- 背景圆形 -->
<path
android:fillColor="#FF4CAF50"
android:strokeColor="#FF4CAF50"
android:strokeWidth="2.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M128,44 C81.4,44 44,81.4 44,128 C44,174.6 81.4,212 128,212 C174.6,212 212,174.6 212,128 C212,81.4 174.6,44 128,44 Z"/>
<!-- 文档/列表背景 -->
<path
android:fillColor="#FFFFFFFF"
android:strokeColor="#FFFFFFFF"
android:strokeWidth="2.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M85,80 L171,80 L171,180 L85,180 L85,80 Z"/>
<!-- 列表线1 -->
<path
android:fillColor="#FF333333"
android:strokeColor="#FF333333"
android:strokeWidth="4.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M100,100 L156,100"/>
<!-- 列表线2 -->
<path
android:fillColor="#FF333333"
android:strokeColor="#FF333333"
android:strokeWidth="4.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M100,120 L156,120"/>
<!-- 列表线3 -->
<path
android:fillColor="#FF333333"
android:strokeColor="#FF333333"
android:strokeWidth="4.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M100,140 L156,140"/>
<!-- 勾选标记 -->
<path
android:fillColor="#FF4CAF50"
android:strokeColor="#FF4CAF50"
android:strokeWidth="4.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M95,98 L101,104 L115,90"/>
<!-- 时钟指针暗示 -->
<path
android:fillColor="#FF4CAF50"
android:strokeColor="#FF4CAF50"
android:strokeWidth="4.0"
android:strokeLineCap="round"
android:strokeMiterLimit="10"
android:pathData="M128,150 L128,168 L144,168"/>
</vector>
@@ -1,138 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/activityBackgroundColor">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/toolbarBackgroundColor"
android:id="@+id/toolbar"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="当前日期:"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/activitycompletediningtableTextView2"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="补全设置:"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<EditText
android:layout_width="wrap_content"
android:ems="10"
android:hint="输入(N或N月N)号"
android:layout_height="wrap_content"
android:id="@+id/activitycompletediningtableEditText3"
android:layout_weight="1.0"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="号晚"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
<RelativeLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="清空"
android:layout_toLeftOf="@id/activitycompletediningtableButton2"
android:id="@+id/activitycompletediningtableButton3"
android:onClick="onCleanResultText"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="复制"
android:layout_toLeftOf="@id/activitycompletediningtableButton1"
android:id="@+id/activitycompletediningtableButton2"
android:onClick="onCopyResultText"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="补全订餐列表"
android:id="@+id/activitycompletediningtableButton1"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:onClick="onCreatePreBookList"/>
</RelativeLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/activitycompletediningtableScrollView2"
android:layout_weight="1.0">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="top"
android:id="@+id/activitycompletediningtableEditText1"/>
</LinearLayout>
</ScrollView>
<View
android:layout_width="match_parent"
android:layout_height="5dp"
android:background="#ccc"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/activitycompletediningtableScrollView1"
android:layout_weight="1.0">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="top"
android:id="@+id/activitycompletediningtableEditText2"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
@@ -1,119 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/activityBackgroundColor">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/toolbarBackgroundColor"
android:id="@+id/toolbar"/>
<LinearLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:background="@drawable/bg_frame"
android:onClick="onPreNullDiningTable">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorPreNullBackgroung"
android:src="@drawable/diningtable"
android:layout_margin="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="餐桌空白预定表制作"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:background="@drawable/bg_frame"
android:onClick="onCompleteDiningTable">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorCompleteBackgroung"
android:src="@drawable/diningtable"
android:layout_margin="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="订餐列表补全"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:background="@drawable/bg_frame"
android:onClick="onNoteRecordHelper">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorCompleteBackgroung"
android:src="@drawable/ic_noterecordhelper"
android:layout_margin="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="订单笔录辅助"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:background="@drawable/bg_frame"
android:onClick="onTaskScheduler">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorPreNullBackgroung"
android:src="@drawable/ic_task_scheduler"
android:layout_margin="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="任务排档"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
@@ -1,83 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="?attr/activityBackgroundColor">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/toolbarBackgroundColor"
android:id="@+id/toolbar"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="订单笔录辅助"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:paddingBottom="16dp"/>
<!-- 输入区域 -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:fillViewport="true">
<EditText
android:id="@+id/et_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="请输入订单内容,每行一条记录"
android:gravity="top|start"
android:background="@android:color/white"
android:textColor="@android:color/black"
android:textSize="18sp"
android:padding="12dp"
android:inputType="textMultiLine|textNoSuggestions"
android:minLines="10"
android:scrollbars="vertical"/>
</ScrollView>
<Button
android:id="@+id/btn_highlight"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="一键高亮标记"
android:layout_marginTop="12dp"/>
<!-- 结果区域:关键是 fillViewport -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:layout_marginTop="12dp"
android:fillViewport="true">
<TextView
android:id="@+id/tv_highlight_result"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:padding="12dp"
android:textSize="18sp"
android:gravity="top|start"
android:scrollbars="vertical"/>
</ScrollView>
</LinearLayout>
</LinearLayout>
@@ -1,160 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/activityBackgroundColor">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/toolbarBackgroundColor"
android:id="@+id/toolbar"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="当前日期:"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/activityprenulldiningtableTextView2"/>
</LinearLayout>
<RelativeLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="订餐日期:"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_alignParentLeft="true"
android:id="@+id/activityprenulldiningtableTextView1"
android:layout_centerVertical="true"/>
<EditText
android:layout_width="50dp"
android:layout_height="wrap_content"
android:inputType="number"
android:ems="10"
android:layout_toRightOf="@+id/activityprenulldiningtableTextView1"
android:id="@+id/activityprenulldiningtableEditText1"
android:layout_centerVertical="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="号"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_toRightOf="@id/activityprenulldiningtableEditText1"
android:layout_centerVertical="true"/>
</RelativeLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="午餐"
android:id="@+id/activityprenulldiningtableRadioButton1"
android:onClick="onDinnerType"/>
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="晚餐"
android:id="@+id/activityprenulldiningtableRadioButton2"
android:onClick="onDinnerType"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/activityprenulldiningtableCheckBox1"
android:text="全选"
android:layout_marginRight="10dp"
android:onClick="onSelectALLTable"/>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_shadow"
android:padding="10dp"
android:id="@+id/activityprenulldiningtableRecyclerView1"/>
</LinearLayout>
<RelativeLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="清空"
android:layout_toLeftOf="@id/activityprenulldiningtableButton2"
android:id="@+id/activityprenulldiningtableButton3"
android:onClick="onCleanResultText"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="复制"
android:layout_toLeftOf="@id/activityprenulldiningtableButton1"
android:id="@+id/activityprenulldiningtableButton2"
android:onClick="onCopyResultText"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="制作订餐列表"
android:id="@+id/activityprenulldiningtableButton1"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:onClick="onCreatePreBookList"/>
</RelativeLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/activityprenulldiningtableScrollView1"
android:layout_weight="1.0">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:id="@+id/activityprenulldiningtableEditText2"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="?attr/activityBackgroundColor">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/toolbarBackgroundColor"
android:id="@+id/toolbar"/>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="10dp"
android:id="@+id/task_scheduler_recycler_view"/>
</LinearLayout>
@@ -1,158 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp"
android:background="?android:attr/windowBackground">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="编辑任务排档"
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center"
android:paddingBottom="15dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="任务名称:"
android:textSize="14sp"
android:paddingBottom="5dp"/>
<EditText
android:id="@+id/dialog_et_task_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入任务名称"
android:inputType="text"
android:padding="10dp"
android:background="@android:drawable/edit_text"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="当前排档时间(yyyy-MM-dd HH):"
android:textSize="14sp"
android:paddingTop="15dp"
android:paddingBottom="5dp"/>
<EditText
android:id="@+id/dialog_et_start_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="例如:09:00"
android:inputType="time"
android:padding="10dp"
android:background="@android:drawable/edit_text"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="排档幅度:"
android:textSize="14sp"
android:paddingTop="15dp"
android:paddingBottom="5dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<EditText
android:id="@+id/dialog_et_month"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="月份"
android:inputType="number"
android:padding="10dp"
android:background="@android:drawable/edit_text"
android:layout_marginEnd="5dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="月"/>
<EditText
android:id="@+id/dialog_et_day"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="天数"
android:inputType="number"
android:padding="10dp"
android:background="@android:drawable/edit_text"
android:layout_marginStart="10dp"
android:layout_marginEnd="5dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="天"/>
<EditText
android:id="@+id/dialog_et_hour"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="小时"
android:inputType="number"
android:padding="10dp"
android:background="@android:drawable/edit_text"
android:layout_marginStart="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="小时"/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="备注:"
android:textSize="14sp"
android:paddingTop="15dp"
android:paddingBottom="5dp"/>
<EditText
android:id="@+id/dialog_et_remark"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入备注信息"
android:inputType="textMultiLine"
android:minLines="3"
android:gravity="top|start"
android:padding="10dp"
android:background="@android:drawable/edit_text"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="20dp"
android:gravity="end">
<Button
android:id="@+id/dialog_btn_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="取消"
android:layout_marginEnd="10dp"/>
<Button
android:id="@+id/dialog_btn_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="保存"/>
</LinearLayout>
</LinearLayout>
@@ -1,103 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/bg_frame"
android:padding="10dp"
android:layout_marginBottom="5dp"
android:id="@+id/itemtaskscheduler_llmain">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="任务:"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"/>
<TextView
android:id="@+id/item_task_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"/>
<Button
android:id="@+id/item_btn_schedule"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="▷"
android:textSize="12sp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_vertical"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="排档时间:"
android:textAppearance="?android:attr/textAppearanceSmall"/>
<TextView
android:id="@+id/item_start_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" 排档幅度:"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_marginLeft="0dp"/>
<TextView
android:id="@+id/item_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="备注:"
android:textAppearance="?android:attr/textAppearanceSmall"/>
<TextView
android:id="@+id/item_remark"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceSmall"/>
</LinearLayout>
</LinearLayout>
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardBackgroundColor="#F5F5F5"
app:cardElevation="4dp"
app:cardCornerRadius="4dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/listviewtableinfoCheckBox1"/>
</androidx.cardview.widget.CardView>
@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shape_gradient"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_launcher"/>
<TextView
android:id="@android:id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textColor="#FF000000"
android:textSize="16sp"/>
</LinearLayout>
</LinearLayout>
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_about"
android:title="About"
android:icon="@android:drawable/ic_menu_info_details"
app:showAsAction="ifRoom"/>
</menu>
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">排档辅助管理工具</string>
</resources>
@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- WinBoLL 默认方案 -->
<color name="colorPrimary">#FF196ABC</color>
<color name="colorPrimaryDark">#FF002B57</color>
<color name="colorAccent">#FF80BFFF</color>
<color name="colorToastFrame">#FFA9A9A9</color>
<color name="colorToastShadow">#FF000000</color>
<color name="colorToastBackgroung">#FFFFFFFF</color>
<color name="colorPreNullBackgroung">#FF31D423</color>
<color name="colorCompleteBackgroung">#FF34E4E7</color>
<!-- APPBase 主题必需颜色 -->
<color name="mainWindowBackgroundColor">#FFF5F5F5</color>
<color name="mainWindowTextColor">#FF000000</color>
<color name="toolbarTextColor">#FFFFFFFF</color>
<color name="toolbarBackgroundColor">#FF196ABC</color>
<color name="debugTextColor">#FF808080</color>
<!-- 基础颜色 -->
<color name="white">#FFFFFF</color>
<color name="black">#000000</color>
</resources>
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">StallHelper</string>
<string name="app_description">LargeStall Stand Auxiliary Management Tool.</string>
</resources>
@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyStallHelperTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="themeDebug">@style/MyDebugActivityTheme</item>
<item name="aboutViewBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="aboutViewTextColor">?attr/mainWindowTextColor</item>
<item name="aboutViewTitleColor">?attr/mainWindowTextColor</item>
<item name="aboutViewDividerColor">?attr/mainWindowDarkTextColor</item>
<item name="dialogBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="dialogTextColor">?attr/mainWindowTextColor</item>
<item name="toolbarBackgroundColor">@color/toolbarBackgroundColor</item>
<item name="toolbarTextColor">@color/toolbarTextColor</item>
<item name="textViewBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="textViewTextColor">?attr/mainWindowTextColor</item>
<item name="editTextBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="editTextTextColor">?attr/mainWindowTextColor</item>
<item name="scrollViewBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="activityBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="activityTextColor">?attr/mainWindowTextColor</item>
<item name="mainWindowBackgroundColor">@color/mainWindowBackgroundColor</item>
<item name="mainWindowTextColor">@color/mainWindowTextColor</item>
<item name="mainWindowDarkBackgroundColor">@color/mainWindowBackgroundColor</item>
<item name="mainWindowDarkTextColor">@color/mainWindowTextColor</item>
</style>
<style name="MyDebugActivityTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:statusBarColor">@color/toolbarBackgroundColor</item>
<item name="colorTittle">?attr/mainWindowTextColor</item>
<item name="colorTittleBackgound">@color/toolbarBackgroundColor</item>
<item name="colorText">?attr/debugTextColor</item>
<item name="colorTextBackgound">?attr/mainWindowBackgroundColor</item>
<item name="debugTextColor">@color/debugTextColor</item>
<item name="toolbarTextColor">@color/toolbarTextColor</item>
</style>
</resources>
+37
View File
@@ -0,0 +1,37 @@
# WinBoLL
#### 介绍
WinBoLL 网站浏览器。
#### 软件架构
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
#### Gradle 编译说明
调试版编译命令 gradle assembleBetaDebug
阶段版编译命令 bash .winboll/bashPublishAPKAddTag.sh winboll
#### 使用说明
3. Termux应用配置:
- 已安装Termux(包名 com.termux );
- 执行  echo "allow-external-apps = true" > ~/.termux/termux.properties
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码 : ZhanGSKen(ZhanGSKen<ZhanGSKen@QQ.COM>)
4. 新建 Pull Request
#### 特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
#### 参考文档
@@ -18,21 +18,20 @@ def genVersionName(def versionName){
}
android {
compileSdkVersion 30
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
// MIUI12
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "cc.winboll.studio.stallhelper"
applicationId "cc.winboll.studio.winboll"
minSdkVersion 26
// MIUI12
targetSdkVersion 30
versionCode 1
// versionName
// .winboll/winbollBuildProps.properties stageCount=0
// Gradle编译环境下合起来的 versionName "${versionName}.0"
versionName "15.20"
versionName "15.20"
if(true) {
versionName = genVersionName("${versionName}")
}
@@ -40,11 +39,12 @@ android {
}
dependencies {
//
api project(':libwinboll')
api 'com.google.code.gson:gson:2.10.1'
//
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
@@ -54,27 +54,55 @@ dependencies {
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 'androidx.cardview:cardview:1.0.0'
// FastJSON解析
implementation 'com.alibaba:fastjson:1.2.76'
// AndroidX
/*api 'androidx.appcompat:appcompat:1.1.0'
//api 'com.google.android.material:material:1.4.0'
//api 'androidx.viewpager:viewpager:1.0.0'
//api 'androidx.vectordrawable:vectordrawable:1.1.0'
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
//api 'androidx.fragment:fragment:1.1.0'
//api 'androidx.fragment:fragment:1.1.0'*/
//5
//
//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'
implementation "androidx.annotation:annotation:1.3.0"
implementation "androidx.core:core:1.6.0"
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
implementation "androidx.preference:preference:1.1.1"
implementation "androidx.viewpager:viewpager:1.0.0"
implementation "com.google.android.material:material:1.4.0"
implementation "com.google.guava:guava:24.1-jre"
/*
implementation "io.noties.markwon:core:$markwonVersion"
implementation "io.noties.markwon:ext-strikethrough:$markwonVersion"
implementation "io.noties.markwon:linkify:$markwonVersion"
implementation "io.noties.markwon:recycler:$markwonVersion"
*/
implementation 'com.termux:terminal-emulator:0.118.0'
implementation 'com.termux:terminal-view:0.118.0'
implementation 'com.termux:termux-shared:0.118.0'
// Biometric ()
implementation 'androidx.biometric:biometric:1.1.0'
// WinBoLL库 nexus.winboll.cc
api 'cc.winboll.studio:libaes:15.20.4'
api 'cc.winboll.studio:libappbase:15.20.15'
api 'cc.winboll.studio:libappbase:15.20.33'
api 'cc.winboll.studio:libaes:15.20.16'
// jitpack.io
//api 'com.github.ZhanGSKen:libappbase:appbase-v15.20.33'
//api 'com.github.ZhanGSKen:libaes:aes-v15.20.16'
api fileTree(dir: 'libs', include: ['*.jar'])
}
+8
View File
@@ -0,0 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed Jun 24 08:11:15 CST 2026
stageCount=9
libraryProject=libwinboll
baseVersion=15.20
publishVersion=15.20.8
buildCount=0
baseBetaVersion=15.20.9
Binary file not shown.
@@ -67,12 +67,6 @@
-keep class okio.** { *; }
-dontwarn okhttp3.internal.platform.**
-dontwarn okio.**
# ============================== 必要补充规则 ==============================
# OkHttp 4.4.1 补充规则(Java 7 兼容)
-keep class okhttp3.internal.concurrent.** { *; }
-keep class okhttp3.internal.connection.** { *; }
-dontwarn okhttp3.internal.concurrent.TaskRunner
-dontwarn okhttp3.internal.connection.RealCall
# Glide 4.9.0(米盟广告图片加载依赖)
-keep public class * implements com.bumptech.glide.module.GlideModule

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