diff --git a/aes/README.md b/aes/README.md index 1606637..e99ec44 100644 --- a/aes/README.md +++ b/aes/README.md @@ -20,7 +20,7 @@ WinBoLL AndroidX 可视化元素类库。 1. Fork 本仓库 2. 新建 Feat_xxx 分支 -3. 提交代码 : ZhanGSKen(ZhanGSKen) +3. 提交代码 : ZhanGSKen(ZhanGSKen) 4. 新建 Pull Request diff --git a/aes/build.gradle b/aes/build.gradle index 8039c8c..fbc8d4a 100644 --- a/aes/build.gradle +++ b/aes/build.gradle @@ -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" diff --git a/aes/build.properties b/aes/build.properties index 8225f1b..13a8f96 100644 --- a/aes/build.properties +++ b/aes/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun May 17 20:53:39 HKT 2026 -stageCount=5 +#Wed Jun 03 07:07:27 HKT 2026 +stageCount=15 libraryProject=libaes baseVersion=15.20 -publishVersion=15.20.4 +publishVersion=15.20.14 buildCount=0 -baseBetaVersion=15.20.5 +baseBetaVersion=15.20.15 diff --git a/aes/src/main/java/cc/winboll/studio/aes/App.java b/aes/src/main/java/cc/winboll/studio/aes/App.java index f6816f7..ece7ea0 100644 --- a/aes/src/main/java/cc/winboll/studio/aes/App.java +++ b/aes/src/main/java/cc/winboll/studio/aes/App.java @@ -5,10 +5,14 @@ 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.CrashHandler; import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.ToastUtils; +import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils; +import java.io.PrintWriter; +import java.io.StringWriter; public class App extends GlobalApplication { @@ -17,13 +21,25 @@ public class App extends GlobalApplication { @Override public void onCreate() { - super.onCreate(); - setIsDebugging(BuildConfig.DEBUG); - //setIsDebugging(false); - WinBoLLActivityManager.init(this); + try { + super.onCreate(); + ToastUtils.init(this); + WinBoLLActivityManager.init(this); + AESThemeUtil.init(null); + } catch (Throwable e) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + pw.close(); + String stackTraceStr = sw.toString(); + CrashHandleNotifyUtils.handleUncaughtException( + this, + getPackageName(), + stackTraceStr, + CrashHandler.CrashActivity.class + ); + } - // 初始化 Toast 框架 - ToastUtils.init(this); } @Override diff --git a/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java b/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java index 927c852..30be2c0 100644 --- a/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java +++ b/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java @@ -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 diff --git a/appbase/README.md b/appbase/README.md index 97cdffc..1cc8792 100644 --- a/appbase/README.md +++ b/appbase/README.md @@ -20,7 +20,7 @@ WinBoLL 安卓手机端安卓应用开发基础类库。 1. Fork 本仓库 2. 新建 Feat_xxx 分支 -3. 提交代码 : ZhanGSKen(ZhanGSKen) +3. 提交代码 : ZhanGSKen(ZhanGSKen) 4. 新建 Pull Request diff --git a/appbase/build.gradle b/appbase/build.gradle index b95d796..14f2a08 100644 --- a/appbase/build.gradle +++ b/appbase/build.gradle @@ -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" diff --git a/appbase/build.properties b/appbase/build.properties index 492916a..a3eae15 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun May 17 16:16:36 HKT 2026 -stageCount=16 +#Wed Jun 03 06:52:38 HKT 2026 +stageCount=26 libraryProject=libappbase baseVersion=15.20 -publishVersion=15.20.15 +publishVersion=15.20.25 buildCount=0 -baseBetaVersion=15.20.16 +baseBetaVersion=15.20.26 diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/AboutActivity.java b/appbase/src/main/java/cc/winboll/studio/appbase/AboutActivity.java index 060bded..d6a65eb 100644 --- a/appbase/src/main/java/cc/winboll/studio/appbase/AboutActivity.java +++ b/appbase/src/main/java/cc/winboll/studio/appbase/AboutActivity.java @@ -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); diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/App.java b/appbase/src/main/java/cc/winboll/studio/appbase/App.java index 9df2ed8..4666ea9 100644 --- a/appbase/src/main/java/cc/winboll/studio/appbase/App.java +++ b/appbase/src/main/java/cc/winboll/studio/appbase/App.java @@ -1,8 +1,11 @@ package cc.winboll.studio.appbase; +import cc.winboll.studio.libappbase.CrashHandler; import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.ToastUtils; -import cc.winboll.studio.libappbase.BuildConfig; +import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils; +import java.io.PrintWriter; +import java.io.StringWriter; /** * @Author ZhanGSKen @@ -21,16 +24,24 @@ 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()); + try { + super.onCreate(); + + // 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用) + ToastUtils.init(getApplicationContext()); + } catch (Throwable e) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + pw.close(); + String stackTraceStr = sw.toString(); + CrashHandleNotifyUtils.handleUncaughtException( + this, + getPackageName(), + stackTraceStr, + CrashHandler.CrashActivity.class + ); + } } /** 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 index ee3e70a..cb91442 100644 --- a/appbase/src/main/java/cc/winboll/studio/appbase/model/TestBean.java +++ b/appbase/src/main/java/cc/winboll/studio/appbase/model/TestBean.java @@ -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; /** diff --git a/libaes/build.gradle b/libaes/build.gradle index c97935f..397e4ab 100644 --- a/libaes/build.gradle +++ b/libaes/build.gradle @@ -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.25' // 备用库 jitpack.io 地址 - //api 'com.github.ZhanGSKen:APPBase:appbase-v15.15.3' + //api 'com.github.ZhanGSKen:libappbase:appbase-v15.20.25' api fileTree(dir: 'libs', include: ['*.jar']) } diff --git a/libaes/build.properties b/libaes/build.properties index 8225f1b..13a8f96 100644 --- a/libaes/build.properties +++ b/libaes/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun May 17 20:53:39 HKT 2026 -stageCount=5 +#Wed Jun 03 07:07:27 HKT 2026 +stageCount=15 libraryProject=libaes baseVersion=15.20 -publishVersion=15.20.4 +publishVersion=15.20.14 buildCount=0 -baseBetaVersion=15.20.5 +baseBetaVersion=15.20.15 diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/activitys/DrawerFragmentActivity.java b/libaes/src/main/java/cc/winboll/studio/libaes/activitys/DrawerFragmentActivity.java index 97610a3..7d0a47a 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/activitys/DrawerFragmentActivity.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/activitys/DrawerFragmentActivity.java @@ -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 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(); @@ -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 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); } } + diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/models/AESThemeBean.java b/libaes/src/main/java/cc/winboll/studio/libaes/models/AESThemeBean.java index dd4d8d8..0a835ef 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/models/AESThemeBean.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/models/AESThemeBean.java @@ -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 themeStyleIDList) { + if (themeStyleIDList == null) { + themeStyleIDList = new ArrayList(); + + } + 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(); } } diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/models/WinBoLLClientServiceBean.java b/libaes/src/main/java/cc/winboll/studio/libaes/models/WinBoLLClientServiceBean.java index dcd112b..3f4e738 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/models/WinBoLLClientServiceBean.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/models/WinBoLLClientServiceBean.java @@ -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 { diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestASupportToolbarActivity.java b/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestASupportToolbarActivity.java index d014654..8021b3e 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestASupportToolbarActivity.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestASupportToolbarActivity.java @@ -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"); } } + diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestAToolbarActivity.java b/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestAToolbarActivity.java index bba25fc..2239d33 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestAToolbarActivity.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestAToolbarActivity.java @@ -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 { } } + diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/utils/AESThemeUtil.java b/libaes/src/main/java/cc/winboll/studio/libaes/utils/AESThemeUtil.java index b4e7548..1987dc9 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/utils/AESThemeUtil.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/utils/AESThemeUtil.java @@ -13,199 +13,159 @@ 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 themeStyleIDList = new ArrayList<>(); - protected volatile AESThemeBean.ThemeType mThemeType; + // 移除无用实例成员 mThemeType,工具类不保留实例字段 - public static 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 themeStyleIDList) { + + if (themeStyleIDList == null) { + themeStyleIDList = new ArrayList(); + AESThemeBean.fillThemeStyleIDList(themeStyleIDList); + } + AESThemeUtil.themeStyleIDList.clear(); + AESThemeUtil.themeStyleIDList.addAll(themeStyleIDList); } - public static 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 void applyAppTheme(T activity) { + // ====================== 应用主题 - 规范重载 ====================== + /** + * 应用当前持久化主题(通用 Activity) + */ + public static void applyTheme(Activity activity) { activity.setTheme(getThemeTypeID(activity)); } - public static 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 void applyWinBoLLTheme(T activity) { - activity.setTheme(getThemeTypeID(activity.getApplicationContext())); - }*/ - - public static 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 void applyAppCompatTheme(Activity activity, AESThemeBean.ThemeType themeType) { - activity.setTheme(AESThemeBean.getThemeStyleID(themeType)); - } - - /*public static void applyWinBoLLTheme(Activity activity, AESThemeBean.ThemeType themeType) { - activity.setTheme(AESThemeBean.getThemeStyleID(themeType)); - }*/ - - public static 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 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 void inflateWinBoLLMenu(T activity, Menu menu) { - activity.getMenuInflater().inflate(R.menu.toolbar_apptheme, menu); - }*/ - - public static 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 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 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 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]; } } + diff --git a/libaes/src/main/res/values-night/colors.xml b/libaes/src/main/res/values-night/colors.xml index 06857dc..77b7b0a 100644 --- a/libaes/src/main/res/values-night/colors.xml +++ b/libaes/src/main/res/values-night/colors.xml @@ -3,15 +3,15 @@ #FFFFFFFF #FF03AB4E - #FF027C39 - #FF3DDC84 + #FF3DDC84 + #FF027C39 #FFFFFB8D - #FFA9A9A9 + #FF555555 #FF000000 - #FFFFFFFF - #FF7D3F12 - #FFCC6E2B - #FFF4B98F + #FF3A3A3A + #FF5A3A1A + #FFA05A2A + #FFD4A07A @color/colorPrimaryDark @color/colorPrimary @@ -24,7 +24,7 @@ @color/colorPrimary @color/colorPrimaryDark - #FF03AB4E + #FF3DDC84 #FFFFFFFF #FF2C2C2C #FFFFFFFF diff --git a/libaes/src/main/res/values-night/styles.xml b/libaes/src/main/res/values-night/styles.xml index 020baff..c660e53 100644 --- a/libaes/src/main/res/values-night/styles.xml +++ b/libaes/src/main/res/values-night/styles.xml @@ -2,6 +2,9 @@ - \ No newline at end of file + diff --git a/libappbase/build.properties b/libappbase/build.properties index 492916a..a3eae15 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun May 17 16:16:36 HKT 2026 -stageCount=16 +#Wed Jun 03 06:52:38 HKT 2026 +stageCount=26 libraryProject=libappbase baseVersion=15.20 -publishVersion=15.20.15 +publishVersion=15.20.25 buildCount=0 -baseBetaVersion=15.20.16 +baseBetaVersion=15.20.26 diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/APPModel.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/APPModel.java index d4dc9ca..56c3c5c 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/APPModel.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/APPModel.java @@ -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; /** diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/CrashHandler.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/CrashHandler.java index bfdcf5f..dcd0aa9 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/CrashHandler.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/CrashHandler.java @@ -263,7 +263,7 @@ public final class CrashHandler { setContentView(contentView); getActionBar().setTitle(TITTLE); - getActionBar().setSubtitle(GlobalApplication.class.getSimpleName() + " Error"); + getActionBar().setSubtitle(GlobalApplication.getAppName(getApplicationContext()) + " Error"); } @Override diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java index 378ac59..05df241 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java @@ -6,6 +6,10 @@ import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.util.Log; +import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils; +import java.io.PrintWriter; +import java.io.StringWriter; /** * @Author ZhanGSKen&豆包大模型 @@ -125,18 +129,32 @@ public class GlobalApplication extends Application { */ @Override public void onCreate() { - super.onCreate(); - // 初始化单例实例(确保在所有初始化操作前完成) - sInstance = this; + try { + super.onCreate(); + + // 初始化单例实例(确保在所有初始化操作前完成) + sInstance = this; - // 初始化基础组件(日志、崩溃处理、Toast) - initCoreComponents(); - // 恢复/初始化调试模式状态(从本地文件读取,无文件则默认关闭调试) - restoreDebugStatus(); - // 新增:初始化服务器地址(从 SP 读取到内存,提高后续访问效率) - initWinbollHost(); + restoreDebugStatus(); + // 初始化基础组件(日志、崩溃处理、Toast) + initCoreComponents(); + // 初始化服务器地址(从 SP 读取到内存,提高后续访问效率) + initWinbollHost(); - LogUtils.d(TAG, "GlobalApplication 初始化完成,单例实例已创建"); + LogUtils.d(TAG, "GlobalApplication 初始化完成,单例实例已创建"); + } catch (Throwable e) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + pw.close(); + String stackTraceStr = sw.toString(); + CrashHandleNotifyUtils.handleUncaughtException( + this, + getPackageName(), + stackTraceStr, + CrashHandler.CrashActivity.class + ); + } } /** @@ -144,7 +162,11 @@ public class GlobalApplication extends Application { */ private void initCoreComponents() { // 初始化日志工具(传入 Application 上下文) - LogUtils.init(this); + + // 调试状态下初始化日志工具 + if (GlobalApplication.isDebugging()) { + LogUtils.init(this); + } // 初始化全局异常处理器(捕获应用崩溃信息,用于调试或上报) CrashHandler.init(this); // 初始化 Toast 工具(统一 Toast 样式、避免内存泄漏等) @@ -187,7 +209,7 @@ public class GlobalApplication extends Application { */ public static String getAppName(Context context) { if (context == null) { - LogUtils.w(TAG, "getAppName: 上下文为空,返回 null"); + Log.w(TAG, "getAppName: 上下文为空,返回 null"); return null; } PackageManager packageManager = context.getPackageManager(); @@ -203,8 +225,7 @@ public class GlobalApplication extends Application { return appName; } catch (NameNotFoundException e) { // 包名不存在(理论上不会发生,捕获异常避免崩溃) - LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); - //LogUtils.e(TAG, "获取应用名称失败:包名不存在", e); + Log.e(TAG, "获取应用名称失败:包名不存在", e); e.printStackTrace(); } return null; diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogActivity.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogActivity.java index 8c0e5af..1cc8f5c 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogActivity.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogActivity.java @@ -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); diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java index a0e4f2a..e2124da 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java @@ -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> iterator = mapTAGList.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry entry = iterator.next(); @@ -279,6 +266,9 @@ public class LogUtils { } public static void setALlTAGListEnable(final boolean isEnable) { + if (!_IsInited) { + return; + } for (final Map.Entry 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; } diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtilsBean.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtilsBean.java index b8556b9..b237f41 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtilsBean.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtilsBean.java @@ -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; /** diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtilsClassTAGBean.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtilsClassTAGBean.java index 83ce79e..6e91314 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtilsClassTAGBean.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtilsClassTAGBean.java @@ -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; /** diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogViewThread.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogViewThread.java index 3941e45..2c4f805 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogViewThread.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogViewThread.java @@ -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, "日志文件监听已停止,线程退出"); + } } /** diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/models/libs1520000/BaseBean.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/models/libs1520000/BaseBean.java new file mode 100644 index 0000000..af140aa --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/models/libs1520000/BaseBean.java @@ -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 + * @CreateTime 2026/05/19 22:33:00 + * @EditTime 2026/05/19 23:15:48 + * @param 泛型约束,限定子类必须继承自BaseBean + */ +public abstract class 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 实体泛型 + * @return JSON字符串 + */ + public static String toStringByBeanList(final ArrayList 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 实体泛型 + * @return 解析完成实体 + * @throws IOException 解析IO异常 + */ + public static T parseStringToBean(final String szBean, final Class 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 实体泛型 + * @return 解析结果 + */ + public static boolean parseStringToBeanList(final String szBeanList, ArrayList beanList, final Class clazz) { + LogUtils.d(TAG, "进入列表字符串解析方法"); + try { + if (beanList == null) { + beanList = new ArrayList(); + } 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 实体泛型 + * @return 加载实体 + */ + public static T loadBean(final Context context, final Class 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 实体泛型 + * @return 实体对象 + */ + public static T loadBeanFromFile(final String szFilePath, final Class 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 实体泛型 + * @return 加载结果 + */ + public static boolean loadBeanList(final Context context, final ArrayList beanListDst, final Class 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 实体泛型 + * @return 加载结果 + */ + public static boolean loadBeanListFromFile(final String szFilePath, final ArrayList beanList, final Class 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 实体泛型 + * @return 保存结果 + */ + public static boolean saveBean(final Context context, final T bean) { + return saveBeanToFile(bean.getBeanJsonFilePath(context), bean); + } + + /** + * 自定义路径保存单个实体 + * @param szFilePath 保存路径 + * @param bean 待保存实体 + * @param 实体泛型 + * @return 保存结果 + */ + public static 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 实体泛型 + * @return 保存结果 + */ + public static boolean saveBeanList(final Context context, final ArrayList beanList, final Class 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 实体泛型 + * @return 保存结果 + */ + public static boolean saveBeanListToFile(final String szFilePath, final ArrayList 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 实体泛型 + * @return 校验结果信息 + */ + public static String checkIsTheSameBeanListAndFile(final String szFilePath, final Class 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(); + } +} + diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/views/AboutView.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/views/AboutView.java index 050a8b6..633bd04 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/views/AboutView.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/views/AboutView.java @@ -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():功能按钮点击事件绑定完成"); } diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/views/DebugSwitchInfoImageView.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/views/DebugSwitchInfoImageView.java new file mode 100644 index 0000000..6c03ff2 --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/views/DebugSwitchInfoImageView.java @@ -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 + * @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(); + } + } + }); + } +} + diff --git a/libappbase/src/main/res/layout-night/layout_about_view.xml b/libappbase/src/main/res/layout-night/layout_about_view.xml index a1753ff..09db2eb 100644 --- a/libappbase/src/main/res/layout-night/layout_about_view.xml +++ b/libappbase/src/main/res/layout-night/layout_about_view.xml @@ -15,7 +15,7 @@ android:paddingRight="16dp" android:paddingBottom="16dp"> - diff --git a/libappbase/src/main/res/layout/layout_about_view.xml b/libappbase/src/main/res/layout/layout_about_view.xml index a1753ff..09db2eb 100644 --- a/libappbase/src/main/res/layout/layout_about_view.xml +++ b/libappbase/src/main/res/layout/layout_about_view.xml @@ -15,7 +15,7 @@ android:paddingRight="16dp" android:paddingBottom="16dp"> - diff --git a/libappbase/src/main/res/values/attrs.xml b/libappbase/src/main/res/values/attrs.xml index b569d4f..7b7627f 100644 --- a/libappbase/src/main/res/values/attrs.xml +++ b/libappbase/src/main/res/values/attrs.xml @@ -60,4 +60,7 @@ + + + \ No newline at end of file