diff --git a/.gitignore b/.gitignore index 110c6f0..eaa5efa 100644 --- a/.gitignore +++ b/.gitignore @@ -94,8 +94,8 @@ lint-results.html ## 忽略 AndroidIDE 临时文件夹 .androidide -## 忽略模块应用编译配置 -/settings.gradle -/gradle.properties +## WinBoLL 基础应用(避免上传敏感配置) /winboll.properties /local.properties +/settings.gradle +/gradle.properties diff --git a/.winboll/winboll_app_build.gradle b/.winboll/winboll_app_build.gradle index 3522fd3..57b6ba7 100644 --- a/.winboll/winboll_app_build.gradle +++ b/.winboll/winboll_app_build.gradle @@ -66,8 +66,8 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 } // 应用包输出配置 @@ -101,12 +101,15 @@ android { // 创建 WinBoLL Studio 发布接口文件夹 File fWinBoLLStudioDir = file("/sdcard/WinBoLLStudio/APKs"); + // 如果配置了APK接口文件夹路径,就设置应用APK输出文件夹为接口文件夹。 + if(winbollProps != null && winbollProps['APKOutputPath'] != null ) { + fWinBoLLStudioDir = file(winbollProps['APKOutputPath']); + } + if(!fWinBoLLStudioDir.exists()) { - //fWinBoLLStudioDir.mkdirs(); - // 如果没有发布接口文件就不用进行APK发布和源码管理操作 - // 当前编译环境不是 WinBoLL 主机, 以下将忽略APK发布和源码管理操作。 - println 'The current compilation environment is not in WinBoLL host, and the following APK publishing and source management operations will be ignore.' - } else { + println "[ WinBoLLStudio ] : " + fWinBoLLStudioDir.getAbsolutePath() + " Folder does not exist." + println '[ WinBoLLStudio ] : The APKOutputPath property is not defined in winboll.properties, please configure APK output folder first.' + } else { /// WINBOLL 主机的 APK 发布和源码管理操作 /// variant.getAssembleProvider().get().doFirst { /* 后期管理预留代码 */ diff --git a/README.md b/README.md index 5a7755e..2c681b4 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,9 @@ WinBoLL 手机源码计划,旨在通过核心项目 WinBoLL 构建手机端与 ### 1. WinBoLL APP 开发环境配置介绍 #### WinBoLL APK 编译输出内容包括: ☆ "/sdcard/WinBoLLStudio/APKs"` 目录内的所有应用分支的 APK 文件。 +winboll.properties 文件的 APKOutputPath 属性可配置这个 APK 输出目录的路径。 ☆ "/sdcard/AppProjects/app.apk"文件。 +winboll.properties 文件的 ExtraAPKOutputPath 属性可配置这个 APK 额外输出文件的路径 #### WinBoLL APK 源码命名空间规范 ☆ WinBoLL 项目使用 "cc.winboll.studio" 作为源码命名空间。在此命名空间下进行源码定义。 diff --git a/aes/.gitignore b/aes/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/aes/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/aes/README.md b/aes/README.md new file mode 100644 index 0000000..1606637 --- /dev/null +++ b/aes/README.md @@ -0,0 +1,36 @@ +# AES +[![](https://jitpack.io/v/ZhanGSKen/AES.svg)](https://jitpack.io/#ZhanGSKen/AES) + +#### 介绍 +WinBoLL AndroidX 可视化元素类库。 + +#### 软件架构 +适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。 +也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。 + + +#### Gradle 编译说明 +调试版编译命令 :gradle assembleBetaDebug +阶段版编译命令 :bash .winboll/bashPublishAPKAddTag.sh aes +阶段版类库发布命令 :git pull &&bash .winboll/bashPublishLIBAddTag.sh libaes + +#### 使用说明 + +#### 参与贡献 + +1. Fork 本仓库 +2. 新建 Feat_xxx 分支 +3. 提交代码 : ZhanGSKen(ZhanGSKen) +4. 新建 Pull Request + + +#### 特技 + +1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md +2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) +3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 +4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 +5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) +6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) + +#### 参考文档 diff --git a/aes/app_update_description.txt b/aes/app_update_description.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/aes/app_update_description.txt @@ -0,0 +1 @@ + diff --git a/aes/build.gradle b/aes/build.gradle new file mode 100644 index 0000000..899f105 --- /dev/null +++ b/aes/build.gradle @@ -0,0 +1,48 @@ +apply plugin: 'com.android.application' +apply from: '../.winboll/winboll_app_build.gradle' +apply from: '../.winboll/winboll_lint_build.gradle' + +def genVersionName(def versionName){ + // 检查编译标志位配置 + assert (winbollBuildProps['stageCount'] != null) + assert (winbollBuildProps['baseVersion'] != null) + // 保存基础版本号 + winbollBuildProps.setProperty("baseVersion", "${versionName}"); + //保存编译标志配置 + FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile) + winbollBuildProps.store(fos, "${winbollBuildPropsDesc}"); + fos.close(); + + // 返回编译版本号 + return "${versionName}." + winbollBuildProps['stageCount'] +} + +android { + // 适配MIUI12 + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "cc.winboll.studio.aes" + minSdkVersion 21 + targetSdkVersion 30 + versionCode 1 + // versionName 更新后需要手动设置 + // 项目模块目录的 build.gradle 文件的 stageCount=0 + // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" + versionName "15.15" + if(true) { + versionName = genVersionName("${versionName}") + } + } + + // 米盟 SDK + packagingOptions { + doNotStrip "*/*/libmimo_1011.so" + } +} + +dependencies { + api project(':libaes') + api fileTree(dir: 'libs', include: ['*.jar']) +} diff --git a/aes/build.properties b/aes/build.properties new file mode 100644 index 0000000..d611252 --- /dev/null +++ b/aes/build.properties @@ -0,0 +1,8 @@ +#Created by .winboll/winboll_app_build.gradle +#Sat Apr 25 04:16:42 HKT 2026 +stageCount=10 +libraryProject=libaes +baseVersion=15.15 +publishVersion=15.15.9 +buildCount=0 +baseBetaVersion=15.15.10 diff --git a/aes/proguard-rules.pro b/aes/proguard-rules.pro new file mode 100644 index 0000000..a18de74 --- /dev/null +++ b/aes/proguard-rules.pro @@ -0,0 +1,137 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# ============================== 基础通用规则 ============================== +# 保留系统组件 +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference + +# 保留 WinBoLL 核心包及子类(合并简化规则) +-keep class cc.winboll.studio.** { *; } +-keepclassmembers class cc.winboll.studio.** { *; } + +# 保留所有类中的 public static final String TAG 字段(便于日志定位) +-keepclassmembers class * { + public static final java.lang.String TAG; +} + +# 保留序列化类(避免Parcelable/Gson解析异常) +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} +-keepclassmembers class * implements java.io.Serializable { + static final long serialVersionUID; + private static final java.io.ObjectStreamField[] serialPersistentFields; + private void writeObject(java.io.ObjectOutputStream); + private void readObject(java.io.ObjectInputStream); + java.lang.Object writeReplace(); + java.lang.Object readResolve(); +} + +# 保留 R 文件(避免资源ID混淆) +-keepclassmembers class **.R$* { + public static ; +} + +# 保留 native 方法(避免JNI调用失败) +-keepclasseswithmembernames class * { + native ; +} + +# 保留注解和泛型(避免反射/序列化异常) +-keepattributes *Annotation* +-keepattributes Signature + +# 屏蔽 Java 8+ 警告(适配 Java 7 语法) +-dontwarn java.lang.invoke.* +-dontwarn android.support.v8.renderscript.* +-dontwarn java.util.function.** + +# ============================== 第三方框架专项规则 ============================== +# OkHttp 4.4.1(米盟广告请求依赖,完善Lambda兼容) +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-keep class okhttp3.internal.** { *; } +-keep class okio.** { *; } +-dontwarn okhttp3.internal.platform.** +-dontwarn okio.** + +# Glide 4.9.0(米盟广告图片加载依赖) +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.module.AppGlideModule +-keep public enum com.bumptech.glide.load.ImageHeaderParser$ImageType { + **[] $VALUES; + public *; +} +-keepclassmembers class * implements com.bumptech.glide.module.AppGlideModule { + (); +} +-dontwarn com.bumptech.glide.** + +# Gson 2.8.5(米盟广告数据序列化依赖) +-keep class com.google.gson.** { *; } +-keep interface com.google.gson.** { *; } +-keepclassmembers class * { + @com.google.gson.annotations.SerializedName ; +} + +# 米盟 SDK(核心广告组件,完整保留避免加载失败) +-keep class com.miui.zeus.** { *; } +-keep interface com.miui.zeus.** { *; } +# 保留米盟日志字段(便于广告加载失败排查) +-keepclassmembers class com.miui.zeus.mimo.sdk.** { + public static final java.lang.String TAG; +} + +# RecyclerView 1.0.0(米盟广告布局渲染依赖) +-keep class androidx.recyclerview.** { *; } +-keep interface androidx.recyclerview.** { *; } +-keepclassmembers class androidx.recyclerview.widget.RecyclerView$Adapter { + public *; +} + +# 其他第三方框架(按引入依赖保留,无则可删除) +# XXPermissions 18.63 +-keep class com.hjq.permissions.** { *; } +-keep interface com.hjq.permissions.** { *; } + +# ZXing 二维码(核心解析组件) +-keep class com.google.zxing.** { *; } +-keep class com.journeyapps.zxing.** { *; } + +# Jsoup HTML解析 +-keep class org.jsoup.** { *; } + +# Pinyin4j 拼音搜索 +-keep class net.sourceforge.pinyin4j.** { *; } + +# JSch SSH组件 +-keep class com.jcraft.jsch.** { *; } + +# AndroidX 基础组件 +-keep class androidx.appcompat.** { *; } +-keep interface androidx.appcompat.** { *; } + +# ============================== 优化与调试配置 ============================== +# 优化级别(平衡混淆效果与性能) +-optimizationpasses 5 +-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* + +# 调试辅助(保留行号便于崩溃定位) +-verbose +-dontpreverify +-dontusemixedcaseclassnames +-keepattributes SourceFile,LineNumberTable + diff --git a/aes/src/beta/AndroidManifest.xml b/aes/src/beta/AndroidManifest.xml new file mode 100644 index 0000000..ee78d9f --- /dev/null +++ b/aes/src/beta/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/aes/src/beta/res/values/strings.xml b/aes/src/beta/res/values/strings.xml new file mode 100644 index 0000000..6f287f2 --- /dev/null +++ b/aes/src/beta/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + + AES+ + diff --git a/aes/src/main/AndroidManifest.xml b/aes/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c732afa --- /dev/null +++ b/aes/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/aes/src/main/java/cc/winboll/studio/aes/AboutActivity.java b/aes/src/main/java/cc/winboll/studio/aes/AboutActivity.java new file mode 100644 index 0000000..26c89bf --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/AboutActivity.java @@ -0,0 +1,78 @@ +package cc.winboll.studio.aes; + +import android.os.Bundle; +import android.view.View; +import androidx.appcompat.widget.Toolbar; +import cc.winboll.studio.aes.R; +import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.models.APPInfo; +import cc.winboll.studio.libappbase.views.AboutView; + +/** + * @Author 豆包&ZhanGSKen + * @Date 2026/01/13 11:25 + * @Describe 应用介绍窗口 + */ +public class AboutActivity extends BaseWinBoLLActivity { + + public static final String TAG = "AboutActivity"; + + private Toolbar mToolbar; + + @Override + public String getTag() { + return TAG; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_about); + + // 设置工具栏 + initToolbar(); + + AboutView aboutView = findViewById(R.id.aboutview); + aboutView.setAPPInfo(genDefaultAppInfo()); + } + + private void initToolbar() { + LogUtils.d(TAG, "initToolbar() 开始初始化"); + mToolbar = findViewById(R.id.toolbar); + if (mToolbar == null) { + LogUtils.e(TAG, "initToolbar() | Toolbar未找到"); + return; + } + setSupportActionBar(mToolbar); + mToolbar.setSubtitle(getTag()); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + mToolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LogUtils.d(TAG, "导航栏 点击返回按钮"); + WinBoLLActivityManager.getInstance().resumeActivity(MainActivity.class); + WinBoLLActivityManager.getInstance().finish(AboutActivity.this); + } + }); + LogUtils.d(TAG, "initToolbar() 配置完成"); + } + + private APPInfo genDefaultAppInfo() { + LogUtils.d(TAG, "genDefaultAppInfo() 调用"); + String branchName = "aes"; + APPInfo appInfo = new APPInfo(); + appInfo.setAppName(getString(R.string.app_name)); + appInfo.setAppIcon(R.drawable.ic_winboll); + appInfo.setAppDescription(getString(R.string.app_description)); + appInfo.setAppGitName("AES"); + appInfo.setAppGitOwner("Studio"); + appInfo.setAppGitAPPBranch(branchName); + appInfo.setAppGitAPPSubProjectFolder(branchName); + appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=AES"); + appInfo.setAppAPKName("AES"); + appInfo.setAppAPKFolderName("AES"); + LogUtils.d(TAG, "genDefaultAppInfo: 应用信息已生成"); + return appInfo; + } +} diff --git a/aes/src/main/java/cc/winboll/studio/aes/App.java b/aes/src/main/java/cc/winboll/studio/aes/App.java new file mode 100644 index 0000000..f6816f7 --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/App.java @@ -0,0 +1,34 @@ +package cc.winboll.studio.aes; + +/** + * @Author ZhanGSKen + * @Date 2024/06/13 19:03:58 + * @Describe AES应用类 + */ +import android.view.Gravity; +import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; +import cc.winboll.studio.libappbase.GlobalApplication; +import cc.winboll.studio.libappbase.ToastUtils; + + +public class App extends GlobalApplication { + + public static final String TAG = "App"; + + @Override + public void onCreate() { + super.onCreate(); + setIsDebugging(BuildConfig.DEBUG); + //setIsDebugging(false); + WinBoLLActivityManager.init(this); + + // 初始化 Toast 框架 + ToastUtils.init(this); + } + + @Override + public void onTerminate() { + super.onTerminate(); + ToastUtils.release(); + } +} diff --git a/aes/src/main/java/cc/winboll/studio/aes/BaseWinBoLLActivity.java b/aes/src/main/java/cc/winboll/studio/aes/BaseWinBoLLActivity.java new file mode 100644 index 0000000..0526f82 --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/BaseWinBoLLActivity.java @@ -0,0 +1,45 @@ +package cc.winboll.studio.aes; + +import android.app.Activity; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; +import cc.winboll.studio.libaes.models.AESThemeBean; +import cc.winboll.studio.libaes.utils.AESThemeUtil; +import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; + +/** + * @Author 豆包&ZhanGSKen + * @Date 2026/01/13 16:35 + * @Describe BaseWinBollActivity 【继承AppCompatActivity,保留核心能力,不额外暴露方法】 + * 继承链路:BaseWinBoLLActivity → AppCompatActivity → FragmentActivity,AppCompat能力天然继承可用 + */ +public abstract class BaseWinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity { + public static final String TAG = "BaseWinBoLLActivity"; + + protected volatile AESThemeBean.ThemeType mThemeType; + + @Override + protected void onCreate(Bundle savedInstanceState) { + mThemeType = AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext())); + setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext())); + super.onCreate(savedInstanceState); + WinBoLLActivityManager.getInstance().add(this); + } + + @Override + protected void onDestroy() { + WinBoLLActivityManager.getInstance().registeRemove(this); + super.onDestroy(); + } + + // 子类必须实现getTag(),确保唯一标识 + @Override + public abstract String getTag(); + + @Override + public Activity getActivity() { + return this; + } +} + diff --git a/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java b/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java new file mode 100644 index 0000000..927c852 --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java @@ -0,0 +1,196 @@ +package cc.winboll.studio.aes; + +/** + * @Author ZhanGSKen + * @Date 2024/06/13 19:05:52 + * @Describe 应用主窗口 + */ +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.Toast; +import cc.winboll.studio.aes.R; +import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity; +import cc.winboll.studio.libaes.dialogs.LocalFileSelectDialog; +import cc.winboll.studio.libaes.dialogs.StoragePathDialog; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; +import cc.winboll.studio.libaes.models.DrawerMenuBean; +import cc.winboll.studio.libaes.unittests.SecondaryLibraryActivity; +import cc.winboll.studio.libaes.unittests.TestAButtonFragment; +import cc.winboll.studio.libaes.unittests.TestASupportToolbarActivity; +import cc.winboll.studio.libaes.unittests.TestAToolbarActivity; +import cc.winboll.studio.libaes.unittests.TestDrawerFragmentActivity; +import cc.winboll.studio.libaes.unittests.TestViewPageFragment; +import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; +import cc.winboll.studio.libappbase.LogUtils; +import com.a4455jkjh.colorpicker.ColorPickerDialog; +import java.util.ArrayList; + +public class MainActivity extends DrawerFragmentActivity { + + + public static final String TAG = "MainActivity"; + + TestAButtonFragment mTestAButtonFragment; + TestViewPageFragment mTestViewPageFragment; + + @Override + public String getTag() { + return TAG; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (mTestAButtonFragment == null) { + mTestAButtonFragment = new TestAButtonFragment(); + addFragment(mTestAButtonFragment); + } + showFragment(mTestAButtonFragment); + //setSubtitle(TAG); + //ToastUtils.show("onCreate"); + } + + @Override + public void initDrawerMenuItemList(ArrayList listDrawerMenu) { + super.initDrawerMenuItemList(listDrawerMenu); + LogUtils.d(TAG, "initDrawerMenuItemList"); + //listDrawerMenu.clear(); + // 添加抽屉菜单项 + listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestAButtonFragment.TAG)); + listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestViewPageFragment.TAG)); + notifyDrawerMenuDataChanged(); + } + + @Override + public void reinitDrawerMenuItemList(ArrayList listDrawerMenu) { + super.reinitDrawerMenuItemList(listDrawerMenu); + LogUtils.d(TAG, "reinitDrawerMenuItemList"); + //listDrawerMenu.clear(); + // 添加抽屉菜单项 + listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestAButtonFragment.TAG)); + listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestViewPageFragment.TAG)); + notifyDrawerMenuDataChanged(); + } + + @Override + public DrawerFragmentActivity.ActivityType initActivityType() { + return DrawerFragmentActivity.ActivityType.Main; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.toolbar_main, menu); +// if(App.isDebugging()) { +// getMenuInflater().inflate(cc.winboll.studio.libaes.R.menu.toolbar_studio_debug, menu); +// } + return super.onCreateOptionsMenu(menu); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + super.onItemClick(parent, view, position, id); + switch (position) { + case 0 : { + if (mTestAButtonFragment == null) { + mTestAButtonFragment = new TestAButtonFragment(); + addFragment(mTestAButtonFragment); + } + showFragment(mTestAButtonFragment); + break; + } + case 1 : { + if (mTestViewPageFragment == null) { + mTestViewPageFragment = new TestViewPageFragment(); + addFragment(mTestViewPageFragment); + } + showFragment(mTestViewPageFragment); + break; + } + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int nItemId = item.getItemId(); + if (item.getItemId() == R.id.item_testactivitymanager) { + WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, TestActivityManagerActivity.class); + //ToastUtils.show("item_testactivitymanager"); + } else + if (nItemId == R.id.item_atoast) { + Toast.makeText(getApplication(), "item_testatoast", Toast.LENGTH_SHORT).show(); + } else if (nItemId == R.id.item_atoolbar) { + Intent intent = new Intent(this, TestAToolbarActivity.class); + startActivity(intent); + + } else if (nItemId == R.id.item_asupporttoolbar) { + Intent intent = new Intent(this, TestASupportToolbarActivity.class); + startActivity(intent); + + } else if (nItemId == R.id.item_colordialog) { + ColorPickerDialog dlg = new ColorPickerDialog(this, getResources().getColor(R.color.colorPrimary)); + dlg.setOnColorChangedListener(new com.a4455jkjh.colorpicker.view.OnColorChangedListener() { + + @Override + public void beforeColorChanged() { + } + + @Override + public void onColorChanged(int color) { + + } + + @Override + public void afterColorChanged() { + } + + + }); + dlg.show(); + + } else if (nItemId == R.id.item_dialogstoragepath) { + final StoragePathDialog dialog = new StoragePathDialog(this, 0); + dialog.setOnOKClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dialog.dismiss(); + } + }); + dialog.show(); + + } else if (nItemId == R.id.item_localfileselectdialog) { + final LocalFileSelectDialog dialog = new LocalFileSelectDialog(this); + dialog.setOnOKClickListener(new LocalFileSelectDialog.OKClickListener() { + @Override + public void onOKClick(String sz) { + Toast.makeText(getApplication(), sz, Toast.LENGTH_SHORT).show(); + //dialog.dismiss(); + } + }); + dialog.open(); + + } else if (nItemId == R.id.item_secondarylibraryactivity) { + Intent intent = new Intent(this, SecondaryLibraryActivity.class); + startActivity(intent); + } else if (nItemId == R.id.item_drawerfragmentactivity) { + Intent intent = new Intent(this, TestDrawerFragmentActivity.class); + startActivity(intent); + } else if (nItemId == R.id.item_settings) { + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + } else if (nItemId == R.id.item_about) { +// Intent intent = new Intent(this, AboutActivity.class); +// startActivity(intent); + WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, AboutActivity.class); + } + + + return super.onOptionsItemSelected(item); + } + + +} diff --git a/aes/src/main/java/cc/winboll/studio/aes/SettingsActivity.java b/aes/src/main/java/cc/winboll/studio/aes/SettingsActivity.java new file mode 100644 index 0000000..1767c02 --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/SettingsActivity.java @@ -0,0 +1,39 @@ +package cc.winboll.studio.aes; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import cc.winboll.studio.libaes.views.ADsControlView; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/11/26 18:01 + * @Describe SettingsActivity + */ +public class SettingsActivity extends Activity { + + public static final String TAG = "SettingsActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_settings); + ADsControlView adsControlView = (ADsControlView) findViewById(R.id.ads_control_view); + +// adsControlView.setOnAdsModeSelectedListener(new ADsControlView.OnAdsModeSelectedListener() { +// @Override +// public void onModeSelected(ADsMode selectedMode) { +// if (selectedMode == ADsMode.STANDALONE) { +// // 处理单机模式逻辑(如释放米盟资源) +// ToastUtils.show("STANDALONE"); +// } else if (selectedMode == ADsMode.MIMO_SDK) { +// // 处理米盟SDK模式逻辑(如初始化SDK) +// ToastUtils.show("MIMO_SDK"); +// } +// } +// }); + } + +} diff --git a/aes/src/main/java/cc/winboll/studio/aes/TestActivityManagerActivity.java b/aes/src/main/java/cc/winboll/studio/aes/TestActivityManagerActivity.java new file mode 100644 index 0000000..71d114d --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/TestActivityManagerActivity.java @@ -0,0 +1,33 @@ +package cc.winboll.studio.aes; + +import android.app.Activity; +import android.os.Bundle; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/09/28 21:07 + * @Describe 窗口管理类测试窗口 + */ +public class TestActivityManagerActivity extends WinBoLLActivity implements IWinBoLLActivity { + + public static final String TAG = "TestActivityManagerActivity"; + + @Override + public Activity getActivity() { + return this; + } + + @Override + public String getTag() { + return TAG; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_testactivitymanager); + + } + +} diff --git a/aes/src/main/java/cc/winboll/studio/aes/WinBoLLActivity.java b/aes/src/main/java/cc/winboll/studio/aes/WinBoLLActivity.java new file mode 100644 index 0000000..bc45765 --- /dev/null +++ b/aes/src/main/java/cc/winboll/studio/aes/WinBoLLActivity.java @@ -0,0 +1,60 @@ +package cc.winboll.studio.aes; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/09/29 00:11 + * @Describe WinBoLL 窗口基础类 + */ +import android.app.Activity; +import android.os.Bundle; +import android.view.MenuItem; +import androidx.appcompat.app.AppCompatActivity; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; +import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; +import cc.winboll.studio.libappbase.LogUtils; + +public class WinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity { + + public static final String TAG = "WinBoLLActivity"; + + @Override + public Activity getActivity() { + return this; + } + + @Override + public String getTag() { + return TAG; + } + + @Override + protected void onResume() { + super.onResume(); + LogUtils.d(TAG, String.format("onResume %s", getTag())); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + /*if (item.getItemId() == R.id.item_log) { + WinBoLLActivityManager.getInstance().startLogActivity(this); + return true; + } else if (item.getItemId() == R.id.item_home) { + startActivity(new Intent(this, MainActivity.class)); + return true; + }*/ + // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 + return super.onOptionsItemSelected(item); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + WinBoLLActivityManager.getInstance().add(this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + WinBoLLActivityManager.getInstance().finish(this); + } +} diff --git a/aes/src/main/res/drawable/bg_frame.xml b/aes/src/main/res/drawable/bg_frame.xml new file mode 100644 index 0000000..75b2b94 --- /dev/null +++ b/aes/src/main/res/drawable/bg_frame.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + diff --git a/aes/src/main/res/layout/activity_about.xml b/aes/src/main/res/layout/activity_about.xml new file mode 100644 index 0000000..e003607 --- /dev/null +++ b/aes/src/main/res/layout/activity_about.xml @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/aes/src/main/res/layout/activity_settings.xml b/aes/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..c8d3432 --- /dev/null +++ b/aes/src/main/res/layout/activity_settings.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/aes/src/main/res/layout/activity_testactivitymanager.xml b/aes/src/main/res/layout/activity_testactivitymanager.xml new file mode 100644 index 0000000..e030ebb --- /dev/null +++ b/aes/src/main/res/layout/activity_testactivitymanager.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/aes/src/main/res/menu/toolbar_main.xml b/aes/src/main/res/menu/toolbar_main.xml new file mode 100644 index 0000000..b87657a --- /dev/null +++ b/aes/src/main/res/menu/toolbar_main.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/aes/src/main/res/values/colors.xml b/aes/src/main/res/values/colors.xml new file mode 100644 index 0000000..87d3836 --- /dev/null +++ b/aes/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #FF00B322 + #FF005C12 + #FF8DFFA2 + #FFFFFB8D + diff --git a/aes/src/main/res/values/strings.xml b/aes/src/main/res/values/strings.xml new file mode 100644 index 0000000..664f43a --- /dev/null +++ b/aes/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + + + AES + WinBoLL AndroidX 可视化元素类库。 + + diff --git a/aes/src/main/res/values/styles.xml b/aes/src/main/res/values/styles.xml new file mode 100644 index 0000000..1da88ba --- /dev/null +++ b/aes/src/main/res/values/styles.xml @@ -0,0 +1,5 @@ + + + + diff --git a/aes/src/main/res/xml/network_security_config.xml b/aes/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..ee39aa4 --- /dev/null +++ b/aes/src/main/res/xml/network_security_config.xml @@ -0,0 +1,6 @@ + + + + winboll.cc + + diff --git a/aes/src/stage/AndroidManifest.xml b/aes/src/stage/AndroidManifest.xml new file mode 100644 index 0000000..ee78d9f --- /dev/null +++ b/aes/src/stage/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/aes/src/stage/res/values/strings.xml b/aes/src/stage/res/values/strings.xml new file mode 100644 index 0000000..ace0c41 --- /dev/null +++ b/aes/src/stage/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/appbase/.gitignore b/appbase/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/appbase/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/appbase/README.md b/appbase/README.md new file mode 100644 index 0000000..97cdffc --- /dev/null +++ b/appbase/README.md @@ -0,0 +1,36 @@ +# APPBase +[![](https://jitpack.io/v/ZhanGSKen/APPBase.svg)](https://jitpack.io/#ZhanGSKen/APPBase) + +#### 介绍 +WinBoLL 安卓手机端安卓应用开发基础类库。 + +#### 软件架构 +适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。 +也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。 + + +#### Gradle 编译说明 +调试版编译命令 :gradle assembleBetaDebug +阶段版编译命令 :bash .winboll/bashPublishAPKAddTag.sh appbase +阶段版类库发布命令 :git pull &&bash .winboll/bashPublishLIBAddTag.sh libappbase + +#### 使用说明 + +#### 参与贡献 + +1. Fork 本仓库 +2. 新建 Feat_xxx 分支 +3. 提交代码 : ZhanGSKen(ZhanGSKen) +4. 新建 Pull Request + + +#### 特技 + +1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md +2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) +3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 +4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 +5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) +6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) + +#### 参考文档 diff --git a/appbase/app_update_description.txt b/appbase/app_update_description.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/appbase/app_update_description.txt @@ -0,0 +1 @@ + diff --git a/appbase/build.gradle b/appbase/build.gradle new file mode 100644 index 0000000..7d90452 --- /dev/null +++ b/appbase/build.gradle @@ -0,0 +1,50 @@ +apply plugin: 'com.android.application' +apply from: '../.winboll/winboll_app_build.gradle' +apply from: '../.winboll/winboll_lint_build.gradle' + +def genVersionName(def versionName){ + // 检查编译标志位配置 + assert (winbollBuildProps['stageCount'] != null) + assert (winbollBuildProps['baseVersion'] != null) + // 保存基础版本号 + winbollBuildProps.setProperty("baseVersion", "${versionName}"); + //保存编译标志配置 + FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile) + winbollBuildProps.store(fos, "${winbollBuildPropsDesc}"); + fos.close(); + + // 返回编译版本号 + return "${versionName}." + winbollBuildProps['stageCount'] +} + +android { + // 适配MIUI12 + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "cc.winboll.studio.appbase" + minSdkVersion 21 + targetSdkVersion 30 + versionCode 1 + // versionName 更新后需要手动设置 + // .winboll/winbollBuildProps.properties 文件的 stageCount=0 + // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" + versionName "15.15" + if(true) { + versionName = genVersionName("${versionName}") + } + } + + // 确保 Java 7 兼容性(已适配项目技术栈) + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } +} + +dependencies { + api project(':libappbase') + + api fileTree(dir: 'libs', include: ['*.jar']) +} diff --git a/appbase/build.properties b/appbase/build.properties new file mode 100644 index 0000000..4e84c0e --- /dev/null +++ b/appbase/build.properties @@ -0,0 +1,8 @@ +#Created by .winboll/winboll_app_build.gradle +#Tue Apr 28 17:08:30 HKT 2026 +stageCount=22 +libraryProject=libappbase +baseVersion=15.15 +publishVersion=15.15.21 +buildCount=0 +baseBetaVersion=15.15.22 diff --git a/appbase/proguard-rules.pro b/appbase/proguard-rules.pro new file mode 100644 index 0000000..574eeec --- /dev/null +++ b/appbase/proguard-rules.pro @@ -0,0 +1,126 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# ============================== 基础通用规则 ============================== +# 保留系统组件 +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference + +# 保留 WinBoLL 核心包及子类(适配你的两个包名) +#-keep public class * extends com.winboll.WinBoLLActivity +#-keep public class * extends com.winboll.WinBoLLFragment +# 主包名 +-keep class cc.winboll.studio.*.** { *; } +# beta包名 +-keep class cc.winboll.studio.*.beta.** { *; } +-keepclassmembers class cc.winboll.studio.*.** { *; } +-keepclassmembers class cc.winboll.studio.*.beta.** { *; } + +# 保留所有类中的 public static final String TAG 字段 +-keepclassmembers class * { + public static final java.lang.String TAG; +} + +# 保留序列化类 +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} +-keepclassmembers class * implements java.io.Serializable { + static final long serialVersionUID; + private static final java.io.ObjectStreamField[] serialPersistentFields; + private void writeObject(java.io.ObjectOutputStream); + private void readObject(java.io.ObjectInputStream); + java.lang.Object writeReplace(); + java.lang.Object readResolve(); +} + +# 保留 R 文件 +-keepclassmembers class **.R$* { + public static ; +} + +# 保留 native 方法 +-keepclasseswithmembernames class * { + native ; +} + +# 保留注解和泛型 +-keepattributes *Annotation* +-keepattributes Signature + +# 屏蔽 Java 8+ 警告(适配 Java 7) +-dontwarn java.lang.invoke.* +-dontwarn android.support.v8.renderscript.* +-dontwarn java.util.function.** + +# ============================== 第三方框架规则 ============================== +# Retrofit + OkHttp +-keep class retrofit2.** { *; } +-keep interface retrofit2.** { *; } +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-keep class okio.** { *; } +-keepclasseswithmembers class * { + @retrofit2.http.* ; +} + +# Glide 4.x +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.module.AppGlideModule +-keep public enum com.bumptech.glide.load.ImageHeaderParser$ImageType { + **[] $VALUES; + public *; +} +-dontwarn com.bumptech.glide.load.resource.bitmap.VideoDecoder + +# GreenDAO 3.x +-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao { + public static java.lang.String TABLENAME; +} +-keep class **$Properties +# 实体类包名(按实际调整) +#-keep class cc.winboll.studio.appbase.model.** { *; } + +# ButterKnife 8.x +-keep class butterknife.** { *; } +-dontwarn butterknife.internal.** +-keep class **$$ViewBinder { *; } +-keepclasseswithmembernames class * { + @butterknife.BindView ; + @butterknife.OnClick ; +} + +# EventBus 3.x +-keepclassmembers class ** { + @org.greenrobot.eventbus.Subscribe ; +} +-keep enum org.greenrobot.eventbus.ThreadMode { *; } + +# ============================== 优化与调试 ============================== +-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* +-optimizationpasses 5 +-verbose +-dontpreverify +-dontusemixedcaseclassnames +# 保留行号(便于崩溃定位) +-keepattributes SourceFile,LineNumberTable + diff --git a/appbase/src/beta/AndroidManifest.xml b/appbase/src/beta/AndroidManifest.xml new file mode 100644 index 0000000..32817e8 --- /dev/null +++ b/appbase/src/beta/AndroidManifest.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/appbase/src/beta/res/values/strings.xml b/appbase/src/beta/res/values/strings.xml new file mode 100644 index 0000000..49fc4c9 --- /dev/null +++ b/appbase/src/beta/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + APPBase+ + + diff --git a/appbase/src/main/AndroidManifest.xml b/appbase/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5549b71 --- /dev/null +++ b/appbase/src/main/AndroidManifest.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/AboutActivity.java b/appbase/src/main/java/cc/winboll/studio/appbase/AboutActivity.java new file mode 100644 index 0000000..060bded --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/AboutActivity.java @@ -0,0 +1,50 @@ +package cc.winboll.studio.appbase; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.Toolbar; +import cc.winboll.studio.appbase.R; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.models.APPInfo; +import cc.winboll.studio.libappbase.views.AboutView; + +/** + * @Author 豆包&ZhanGSKen + * @Date 2026/01/11 12:55 + * @Describe AboutActivity + */ +public class AboutActivity extends Activity { + + public static final String TAG = "AboutActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_about); + + Toolbar toolbar = findViewById(R.id.toolbar); + setActionBar(toolbar); + + AboutView aboutView = findViewById(R.id.aboutview); + aboutView.setAPPInfo(genDefaultAppInfo()); + } + + private APPInfo genDefaultAppInfo() { + LogUtils.d(TAG, "genDefaultAppInfo() 调用"); + String branchName = "appbase"; + APPInfo appInfo = new APPInfo(); + appInfo.setAppName("APPBase"); + appInfo.setAppIcon(R.drawable.ic_winboll); + appInfo.setAppDescription(getString(R.string.app_description)); + appInfo.setAppGitName("WinBoLL"); + appInfo.setAppGitOwner("Studio"); + appInfo.setAppGitAPPBranch(branchName); + appInfo.setAppGitAPPSubProjectFolder(branchName); + appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=APPBase"); + appInfo.setAppAPKName("APPBase"); + appInfo.setAppAPKFolderName("APPBase"); + LogUtils.d(TAG, "genDefaultAppInfo: 应用信息已生成"); + return appInfo; + } +} diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/App.java b/appbase/src/main/java/cc/winboll/studio/appbase/App.java new file mode 100644 index 0000000..cd3711b --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/App.java @@ -0,0 +1,46 @@ +package cc.winboll.studio.appbase; + +import cc.winboll.studio.libappbase.GlobalApplication; +import cc.winboll.studio.libappbase.ToastUtils; +import cc.winboll.studio.libappbase.BuildConfig; + +/** + * @Author ZhanGSKen + * @Date 2025/01/05 09:54:42 + * @Describe 应用全局入口类(继承基础库 GlobalApplication) + * 负责应用初始化、全局资源管理与生命周期回调处理,是整个应用的核心入口 + */ +public class App extends GlobalApplication { + + /** 当前应用类的日志 TAG(用于调试输出,标识日志来源) */ + public static final String TAG = "App"; + + /** + * 应用创建时回调(全局初始化入口) + * 在应用进程启动时执行,仅调用一次,用于初始化全局工具类、第三方库等 + */ + @Override + public void onCreate() { + super.onCreate(); + // 如果应用不在调试状态,就根据编译类型设置调试状态 + if (isDebugging() != true) { + setIsDebugging(BuildConfig.DEBUG); + } + + // 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用) + ToastUtils.init(getApplicationContext()); + } + + /** + * 应用终止时回调(资源释放入口) + * 仅在模拟环境(如 Android Studio 模拟器)中可靠触发,真机上可能因系统回收进程不执行 + * 用于释放全局资源,避免内存泄漏 + */ + @Override + public void onTerminate() { + super.onTerminate(); // 调用父类终止逻辑(如基础库资源释放) + // 释放 Toast 工具类资源(销毁全局 Toast 实例,避免内存泄漏) + ToastUtils.release(); + } +} + diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/Main2Activity.java b/appbase/src/main/java/cc/winboll/studio/appbase/Main2Activity.java new file mode 100644 index 0000000..1b989f7 --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/Main2Activity.java @@ -0,0 +1,20 @@ +package cc.winboll.studio.appbase; + +import android.os.Bundle; +import android.widget.Toolbar; +import cc.winboll.studio.appbase.R; + +public class Main2Activity extends MainActivity { + + public static final String TAG = "Main2Activity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main2); + Toolbar toolbar = findViewById(R.id.toolbar); + if (toolbar != null) { + setActionBar(toolbar); + } + } +} \ No newline at end of file diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java b/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java new file mode 100644 index 0000000..e524154 --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java @@ -0,0 +1,195 @@ +package cc.winboll.studio.appbase; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Toolbar; +import cc.winboll.studio.appbase.R; +import cc.winboll.studio.appbase.model.TestBean; +import cc.winboll.studio.libappbase.LogActivity; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.ToastUtils; + +/** + * @Author ZhanGSKen + * @Date 未标注(建议补充创建日期) + * @Describe 应用主界面 Activity(入口界面) + * 包含功能测试按钮(崩溃测试、日志查看、Toast测试)、顶部工具栏(菜单功能),是应用交互的核心入口 + */ +public class MainActivity extends Activity { + + /** 当前 Activity 的日志 TAG(用于调试输出,标识日志来源) */ + public static final String TAG = "MainActivity"; + + /** 顶部工具栏(用于展示标题、菜单,绑定布局中的 Toolbar 控件) */ + private Toolbar mToolbar; + + /** + * Activity 创建时回调(初始化界面) + * 在 Activity 首次创建时执行,用于加载布局、初始化控件、设置事件监听 + * @param savedInstanceState 保存 Activity 状态的 Bundle(如屏幕旋转时的数据恢复) + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //ToastUtils.show("onCreate"); // 显示 Activity 创建提示(调试用) + setContentView(R.layout.activity_main); // 加载主界面布局 + + // 初始化 Toolbar 并设置为 ActionBar + mToolbar = findViewById(R.id.toolbar); + setActionBar(mToolbar); // 将 Toolbar 替代系统默认 ActionBar + + initTestData(); + } + + void initTestData() { + TestBean bean1 = new TestBean(); + bean1.setTestNum1(456); + TestBean.saveBeanToFile(getFilesDir().getAbsolutePath() + getTestBeanRelativePath(), bean1); + TestBean bean2 = new TestBean(); + bean2.setTestNum1(789); + TestBean.saveBeanToFile(getExternalFilesDir(null).getAbsolutePath() + getTestBeanRelativePath(), bean2); + } + + String getTestBeanRelativePath() { + return "/BaseBaen/"+TestBean.class.getName()+".json"; + } + + /** + * 创建菜单时回调(加载工具栏菜单) + * 初始化 ActionBar 菜单,加载自定义菜单布局 + * @param menu 菜单对象(用于承载菜单项) + * @return true:显示菜单;false:不显示菜单 + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // 加载菜单布局(R.menu.toolbar_main 为自定义菜单文件) + getMenuInflater().inflate(R.menu.toolbar_main, menu); + return super.onCreateOptionsMenu(menu); + } + + /** + * 菜单 item 点击时回调(处理菜单事件) + * 响应 Toolbar 菜单项的点击事件,执行对应业务逻辑 + * @param item 被点击的菜单项 + * @return true:消费点击事件;false:不消费(传递给父类) + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.item_home: + // 点击 "首页/官网" 菜单项,唤起浏览器打开指定网站 + openWebsiteInBrowser(this); + break; + // 可扩展其他菜单项(如设置、关于等)的处理逻辑 + } + return super.onOptionsItemSelected(item); + } + + /** + * 崩溃测试按钮点击事件(触发应用崩溃,用于调试异常捕获) + * 故意执行非法操作(循环获取不存在的字符串资源),强制应用崩溃 + * @param view 触发事件的 View(对应布局中的崩溃测试按钮) + */ + public void onCrashTest(View view) { + // 循环从 Integer.MIN_VALUE 到 Integer.MAX_VALUE,获取不存在的字符串资源 ID,触发崩溃 + for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { + getString(i); // i 超出资源 ID 范围,抛出 Resources.NotFoundException 导致崩溃 + } + } + + public void onLogTestNewTask(View view) { + LogActivity.startLogActivity(this, true); + } + + /** + * 日志测试按钮点击事件(打开日志查看界面) + * 启动 LogActivity,用于查看应用运行日志 + * @param view 触发事件的 View(对应布局中的日志测试按钮) + */ + public void onLogTest(View view) { + LogActivity.startLogActivity(this, false); + } + + /** + * Toast 工具测试按钮点击事件(测试全局 Toast 功能) + * 测试主线程、子线程中 Toast 的显示效果,验证 ToastUtils 的可用性 + * @param view 触发事件的 View(对应布局中的 Toast 测试按钮) + */ + public void onToastUtilsTest(View view) { + LogUtils.d(TAG, "onToastUtilsTest"); // 打印调试日志,标识进入 Toast 测试 + ToastUtils.show("Hello, WinBoLL!"); // 主线程显示 Toast + + // 开启子线程,延迟 2 秒后显示 Toast(测试子线程 Toast 兼容性) + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(2000); // 线程休眠 2 秒 + // 若 ToastUtils 已处理主线程切换,此处可直接调用;否则需通过 Handler 切换到主线程 + ToastUtils.show("Thread.sleep(2000);ToastUtils.show..."); + } catch (InterruptedException e) { + // 捕获线程中断异常(如线程被销毁时),不做处理(测试场景) + e.printStackTrace(); + } + } + }).start(); + } + + /** + * 唤起系统默认浏览器打开指定网站(跳转至应用官网) + * 通过 Intent.ACTION_VIEW 隐式意图,触发浏览器打开目标 URL + * @param context 上下文对象(如 Activity、Application,此处为 MainActivity) + */ + public void openWebsiteInBrowser(Context context) { + String url = "https://www.winboll.cc"; // 目标网站 URL(应用官网) + // 构建隐式意图:ACTION_VIEW 表示查看指定数据(Uri 为网站地址) + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + // 设置标志:在新的任务栈中启动 Activity(避免与当前应用任务栈混淆) + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // 启动意图(唤起浏览器) + context.startActivity(intent); + } + + public void onAboutActivity(View view) { + LogUtils.d(TAG, "onAboutActivity() 调用"); + Intent aboutIntent = new Intent(getApplicationContext(), AboutActivity.class); + startActivity(aboutIntent); + } + + public void onSplitScreenMode(View view) { + LogUtils.d(TAG, "onSplitScreenMode() 分屏测试按钮已点击"); + ToastUtils.show("分屏测试:已启动新窗口"); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + android.graphics.Rect bounds = new android.graphics.Rect(); + getWindow().getDecorView().getDisplay().getRectSize(bounds); + int height = bounds.height(); + int width = bounds.width(); + bounds.set(0, 0, width, height / 2); + LogUtils.d(TAG, "onSplitScreenMode() 分屏窗口范围: " + bounds); + android.content.Intent intent = new android.content.Intent(this, MainActivityAlias.class); + intent.setFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK); + LogUtils.d(TAG, "onSplitScreenMode() 准备启动MainActivityAlias"); + android.app.ActivityOptions options = android.app.ActivityOptions.makeBasic(); + options.setLaunchBounds(bounds); + startActivity(intent, options.toBundle()); + LogUtils.d(TAG, "onSplitScreenMode() MainActivityAlias已启动"); + } + } + + public void onMultiInstance(View view) { + LogUtils.d(TAG, "onMultiInstance() 多开窗口按钮已点击"); + ToastUtils.show("多开窗口:已启动新窗口"); + android.content.Intent intent = new android.content.Intent(this, Main2Activity.class); + intent.setFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK); + LogUtils.d(TAG, "onMultiInstance() 准备启动Main2Activity"); + startActivity(intent); + LogUtils.d(TAG, "onMultiInstance() Main2Activity已启动"); + } +} + diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/MainActivityAlias.java b/appbase/src/main/java/cc/winboll/studio/appbase/MainActivityAlias.java new file mode 100644 index 0000000..776a0f0 --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/MainActivityAlias.java @@ -0,0 +1,17 @@ +package cc.winboll.studio.appbase; + +import android.os.Bundle; +import android.view.View; +import android.widget.Toolbar; +import cc.winboll.studio.appbase.R; + +public class MainActivityAlias extends MainActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + Toolbar toolbar = findViewById(R.id.toolbar); + setActionBar(toolbar); + } +} \ No newline at end of file diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/model/TestBean.java b/appbase/src/main/java/cc/winboll/studio/appbase/model/TestBean.java new file mode 100644 index 0000000..ee3e70a --- /dev/null +++ b/appbase/src/main/java/cc/winboll/studio/appbase/model/TestBean.java @@ -0,0 +1,154 @@ +package cc.winboll.studio.appbase.model; + +import android.util.JsonReader; +import android.util.JsonWriter; +import cc.winboll.studio.libappbase.BaseBean; +import cc.winboll.studio.libappbase.LogUtils; +import java.io.IOException; + +/** + * 测试实体类 + * 继承BaseBean实现JSON序列化/反序列化能力,提供基础int类型属性的封装与数据持久化支持 + * 适配Java7语法,遵循BaseBean统一的反射识别、JSON读写规范 + * @Author 豆包&ZhanGSKen + * @Date 2026/01/31 19:16:00 + * @LastEditTime 2026/02/01 10:46:00 + */ +public class TestBean extends BaseBean { + + // ====================================== 常量定义 ====================================== + /** 当前类的日志 TAG(用于调试输出) */ + public static final String TAG = "TestBean"; + + // ====================================== 成员属性 ====================================== + /** + * 测试数字属性(默认值:123) + * 基础int类型属性,用于测试BaseBean的JSON序列化/反序列化能力 + */ + private int testNum1; + + // ====================================== 构造方法 ====================================== + /** + * 无参构造器(默认初始化) + * 给testNum1赋值默认值123,满足反射实例化、JSON解析的无参构造要求 + */ + public TestBean() { + this.testNum1 = 123; + LogUtils.d(TAG, "TestBean无参构造器调用,testNum1默认初始化值:" + this.testNum1); + } + + /** + * 有参构造器(自定义初始化) + * @param testNum1 测试数字初始值 + */ + public TestBean(int testNum1) { + this.testNum1 = testNum1; + LogUtils.d(TAG, "TestBean有参构造器调用,传入testNum1:" + testNum1); + } + + // ====================================== Get/Set 方法 ====================================== + /** + * 设置测试数字属性值 + * @param testNum1 待设置的int类型值 + */ + public void setTestNum1(int testNum1) { + LogUtils.d(TAG, "setTestNum1调用,传入参数:" + testNum1); + this.testNum1 = testNum1; + } + + /** + * 获取测试数字属性值 + * @return 当前testNum1的int类型值 + */ + public int getTestNum1() { + LogUtils.d(TAG, "getTestNum1调用,返回值:" + this.testNum1); + return testNum1; + } + + // ====================================== 重写父类BaseBean方法 ====================================== + /** + * 重写父类方法:获取当前类的全限定名 + * 用于BaseBean反射识别、类名匹配等统一逻辑 + * @return 类全限定名(cc.winboll.studio.appbase.model.TestBean) + */ + @Override + public String getName() { + LogUtils.d(TAG, "getName方法调用,返回类全限定名:" + TestBean.class.getName()); + return TestBean.class.getName(); + } + + /** + * 重写父类方法:将当前对象序列化为JSON(持久化存储专用) + * 遵循BaseBean规范,先执行父类序列化逻辑,再处理子类专属字段 + * @param jsonWriter JSON写入器(外部传入的JSON流操作实例) + * @throws IOException JSON写入异常(流关闭、格式错误等) + */ + @Override + public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { + LogUtils.d(TAG, "writeThisToJsonWriter调用,传入参数JsonWriter:" + jsonWriter); + // 执行父类公共字段的序列化逻辑 + super.writeThisToJsonWriter(jsonWriter); + // 序列化子类专属字段testNum1 + jsonWriter.name("testNum1").value(this.getTestNum1()); + LogUtils.d(TAG, "writeThisToJsonWriter执行完成,已序列化testNum1:" + this.getTestNum1()); + } + + /** + * 重写父类方法:从JSON字段初始化当前对象属性(解析JSON专用) + * 先让父类处理公共字段,再匹配子类专属字段,不匹配则返回false跳过 + * @param jsonReader JSON读取器(外部传入的JSON流操作实例) + * @param name 当前解析的JSON字段名 + * @return true-字段解析成功;false-字段不匹配,需跳过/父类处理 + * @throws IOException JSON读取异常(字段类型不匹配、流中断等) + */ + @Override + public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException { + LogUtils.d(TAG, "initObjectsFromJsonReader调用,传入参数:name=" + name + ",JsonReader=" + jsonReader); + // 父类优先处理公共字段,处理成功则直接返回 + if (super.initObjectsFromJsonReader(jsonReader, name)) { + LogUtils.d(TAG, "initObjectsFromJsonReader:字段" + name + "由父类BaseBean处理成功"); + return true; + } + // 解析子类专属字段 + if ("testNum1".equals(name)) { + this.setTestNum1(jsonReader.nextInt()); + LogUtils.d(TAG, "initObjectsFromJsonReader:解析testNum1成功,值为:" + this.getTestNum1()); + } else { + LogUtils.w(TAG, "initObjectsFromJsonReader:字段" + name + "不匹配,返回false跳过解析"); + // 字段不匹配,返回false表示跳过 + return false; + } + return true; + } + + /** + * 重写父类方法:从JSON读取器完整解析并初始化当前对象(JSON解析入口) + * 负责JSON对象的开始/结束标识处理,遍历所有字段并调用字段解析方法 + * 严格遵循writeThisToJsonWriter的序列化结构,保证解析一致性 + * @param jsonReader JSON读取器(外部传入的JSON流操作实例) + * @return 解析后的当前TestBean实例(支持链式调用) + * @throws IOException JSON解析异常(格式错误、字段缺失、流异常等) + */ + @Override + public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { + LogUtils.d(TAG, "readBeanFromJsonReader调用,传入参数JsonReader:" + jsonReader); + // 开始解析JSON对象,与序列化结构保持一致 + jsonReader.beginObject(); + // 遍历所有JSON字段 + while (jsonReader.hasNext()) { + String fieldName = jsonReader.nextName(); + LogUtils.d(TAG, "readBeanFromJsonReader:开始解析字段,fieldName=" + fieldName); + // 解析字段,不匹配则跳过该值 + if (!this.initObjectsFromJsonReader(jsonReader, fieldName)) { + jsonReader.skipValue(); + LogUtils.w(TAG, "readBeanFromJsonReader:字段" + fieldName + "解析失败,已跳过该值"); + } + } + // 结束JSON对象解析,必须调用避免流异常 + jsonReader.endObject(); + LogUtils.d(TAG, "readBeanFromJsonReader执行完成,JSON解析结束,当前TestBean实例testNum1:" + this.getTestNum1()); + // 返回当前实例,支持链式调用 + return this; + } +} + diff --git a/appbase/src/main/res/drawable/btn_light_blue.xml b/appbase/src/main/res/drawable/btn_light_blue.xml new file mode 100644 index 0000000..58415fd --- /dev/null +++ b/appbase/src/main/res/drawable/btn_light_blue.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/appbase/src/main/res/layout/activity_about.xml b/appbase/src/main/res/layout/activity_about.xml new file mode 100644 index 0000000..bfd02eb --- /dev/null +++ b/appbase/src/main/res/layout/activity_about.xml @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/appbase/src/main/res/layout/activity_main.xml b/appbase/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..d7a5e0b --- /dev/null +++ b/appbase/src/main/res/layout/activity_main.xml @@ -0,0 +1,114 @@ + + + + + + + + + +