diff --git a/appbase/build.properties b/appbase/build.properties index 2e5a1a5..4be9f3e 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Thu Jan 22 12:38:42 GMT 2026 +#Thu Jan 22 13:18:42 GMT 2026 stageCount=7 libraryProject=libappbase baseVersion=15.15 publishVersion=15.15.6 -buildCount=11 +buildCount=15 baseBetaVersion=15.15.7 diff --git a/libappbase/build.properties b/libappbase/build.properties index 2e5a1a5..b03a2a5 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Thu Jan 22 12:38:42 GMT 2026 +#Thu Jan 22 13:18:03 GMT 2026 stageCount=7 libraryProject=libappbase baseVersion=15.15 publishVersion=15.15.6 -buildCount=11 +buildCount=15 baseBetaVersion=15.15.7 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 9fb8035..4e51b99 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java @@ -2,6 +2,7 @@ package cc.winboll.studio.libappbase; import android.app.Application; import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -26,6 +27,12 @@ public class GlobalApplication extends Application { */ private static volatile boolean isDebugging = false; + // 新增:WinBoLL 服务器主机地址(volatile 保证多线程可见性) + private static volatile String winbollHost = null; + // 新增:SP 存储相关常量(私有存储,仅当前应用可访问) + private static final String SP_NAME = "WinBoLL_SP_CONFIG"; + private static final String SP_KEY_WINBOLL_HOST = "winboll_host"; + /** * 获取全局 Application 单例实例(外部可通过此方法获取上下文) * @return GlobalApplication 单例(未初始化时返回 null,需确保配置 AndroidManifest) @@ -53,7 +60,7 @@ public class GlobalApplication extends Application { } // 将调试状态封装为 APPModel 并保存到文件 APPModel.saveBeanToFile( - getAppModelFilePath(application), + getAppModelFilePath(application), new APPModel(isDebugging) ); } @@ -76,6 +83,37 @@ public class GlobalApplication extends Application { return isDebugging; } + // 新增:设置 WinBoLL 服务器主机地址(同时保存到 SP 持久化) + public static void setWinbollHost(String host) { + if (sInstance == null) { + LogUtils.e(TAG, "setWinbollHost: 应用未初始化,设置失败"); + return; + } + // 更新内存中的字段 + winbollHost = host; + // 保存到 SP 持久化(私有模式,安全) + SharedPreferences sp = sInstance.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + sp.edit().putString(SP_KEY_WINBOLL_HOST, host).apply(); + LogUtils.d(TAG, "setWinbollHost: 服务器地址已设置并持久化,host=" + host); + } + + // 新增:获取 WinBoLL 服务器主机地址(优先内存,内存为空则从 SP 读取) + public static String getWinbollHost() { + if (winbollHost != null) { + // 内存中存在,直接返回(提高效率) + return winbollHost; + } + if (sInstance == null) { + LogUtils.e(TAG, "getWinbollHost: 应用未初始化,获取失败"); + return null; + } + // 内存中不存在,从 SP 读取并更新到内存 + SharedPreferences sp = sInstance.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + winbollHost = sp.getString(SP_KEY_WINBOLL_HOST, null); + LogUtils.d(TAG, "getWinbollHost: 从 SP 读取服务器地址,host=" + winbollHost); + return winbollHost; + } + /** * 应用启动时初始化(仅执行一次) * 初始化核心框架、恢复调试状态、配置全局异常处理等 @@ -85,12 +123,13 @@ public class GlobalApplication extends Application { super.onCreate(); // 初始化单例实例(确保在所有初始化操作前完成) sInstance = this; - // 初始化基础组件(日志、崩溃处理、Toast) initCoreComponents(); // 恢复/初始化调试模式状态(从本地文件读取,无文件则默认关闭调试) restoreDebugStatus(); + // 新增:初始化服务器地址(从 SP 读取到内存,提高后续访问效率) + initWinbollHost(); LogUtils.d(TAG, "GlobalApplication 初始化完成,单例实例已创建"); } @@ -115,7 +154,7 @@ public class GlobalApplication extends Application { private void restoreDebugStatus() { // 从文件加载 APPModel 实例(存储调试状态的模型类) APPModel appModel = APPModel.loadBeanFromFile( - getAppModelFilePath(this), + getAppModelFilePath(this), APPModel.class ); @@ -131,6 +170,11 @@ public class GlobalApplication extends Application { } } + // 新增:初始化服务器地址(应用启动时从 SP 读取到内存) + private void initWinbollHost() { + getWinbollHost(); // 触发从 SP 读取并更新内存 + } + /** * 获取应用名称(从 AndroidManifest.xml 的 android:label 读取) * @param context 上下文(建议传入 Application 上下文,避免内存泄漏) @@ -154,7 +198,7 @@ public class GlobalApplication extends Application { return appName; } catch (NameNotFoundException e) { // 包名不存在(理论上不会发生,捕获异常避免崩溃) - LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); //LogUtils.e(TAG, "获取应用名称失败:包名不存在", e); e.printStackTrace(); } @@ -170,7 +214,6 @@ public class GlobalApplication extends Application { // 释放单例引用(可选,避免内存泄漏风险) sInstance = null; LogUtils.d(TAG, "GlobalApplication 终止,单例实例已释放"); - } } diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/dialogs/DebugHostDialog.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/dialogs/DebugHostDialog.java new file mode 100644 index 0000000..79a9bb6 --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/dialogs/DebugHostDialog.java @@ -0,0 +1,99 @@ +package cc.winboll.studio.libappbase.dialogs; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; +import cc.winboll.studio.libappbase.GlobalApplication; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.R; +import cc.winboll.studio.libappbase.ToastUtils; + +/** + * @Author 豆包&ZhanGSKen + * @Date 2026/01/22 20:59 + * @Describe WinBoLL服务器地址设置对话框(调试模式专用) + */ +public class DebugHostDialog extends Dialog implements View.OnClickListener { + public static final String TAG = "DebugHostDialog"; + + private Context mContext; + private EditText etHostInput; + private Button btnConfirm; + private Button btnCancel; + + // 构造方法(适配默认样式) + public DebugHostDialog(Context context) { + super(context, R.style.DialogStyle); + this.mContext = context; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.dialog_debug_host); // 绑定XML布局 + setCancelable(true); // 点击外部可关闭 + initView(); + initData(); + LogUtils.d(TAG, "DebugHostDialog 初始化完成"); + } + + // 初始化视图 + private void initView() { + etHostInput = findViewById(R.id.et_host_input); + btnConfirm = findViewById(R.id.btn_confirm); + btnCancel = findViewById(R.id.btn_cancel); + + // 绑定点击事件 + btnConfirm.setOnClickListener(this); + btnCancel.setOnClickListener(this); + } + + // 初始化数据(显示当前已保存的地址) + private void initData() { + String currentHost = GlobalApplication.getWinbollHost(); + if (!TextUtils.isEmpty(currentHost)) { + etHostInput.setText(currentHost); + etHostInput.setSelection(currentHost.length()); // 光标定位到末尾 + LogUtils.d(TAG, "当前已保存的服务器地址:" + currentHost); + } + } + + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.btn_confirm) { + handleConfirm(); // 确认设置 + } else if (id == R.id.btn_cancel) { + dismiss(); // 取消对话框 + } + } + + // 处理确认设置逻辑 + private void handleConfirm() { + String inputHost = etHostInput.getText().toString().trim(); + if (TextUtils.isEmpty(inputHost)) { + ToastUtils.show("服务器地址不能为空"); + LogUtils.w(TAG, "设置失败:地址为空"); + return; + } + + // 简单校验URL格式(避免明显错误) + if (!inputHost.startsWith("http://") && !inputHost.startsWith("https://")) { + ToastUtils.show("地址需以http://或https://开头"); + LogUtils.w(TAG, "设置失败:地址格式错误,input=" + inputHost); + return; + } + + // 保存地址到SP+内存 + GlobalApplication.setWinbollHost(inputHost); + ToastUtils.show("服务器地址设置成功"); + LogUtils.d(TAG, "服务器地址设置成功:" + inputHost); + dismiss(); // 关闭对话框 + } +} + 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 3c3892f..ddc294b 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 @@ -116,7 +116,8 @@ public class AboutView extends LinearLayout { // LogUtils.d(TAG, "initViewFromXml 布局加载+视图绑定完成"); // } // 1. 新增视图绑定属性(加在原有视图属性后面) - private ImageButton ibSigngetdialog; + private ImageButton ibSigngetDialog; + private ImageButton ibWinBoLLHostDialog; // 2. 完善initViewFromXml方法,新增按钮绑定 private void initViewFromXml() { @@ -125,14 +126,15 @@ public class AboutView extends LinearLayout { tvAppNameVersion = findViewById(R.id.tv_app_name_version); tvAppDesc = findViewById(R.id.tv_app_desc); llFunctionContainer = findViewById(R.id.ll_function_container); - ibSigngetdialog = findViewById(R.id.ib_signgetdialog); // 新增按钮绑定 + ibSigngetDialog = findViewById(R.id.ib_signgetdialog); // 新增按钮绑定 + ibWinBoLLHostDialog = findViewById(R.id.ib_winbollhostdialog); // 新增按钮绑定 setBtnClickListener(); // 新增绑定点击事件 LogUtils.d(TAG, "initViewFromXml 布局加载+视图绑定完成"); } // 3. 新增按钮点击事件方法(放在initViewFromXml下面即可) private void setBtnClickListener() { - ibSigngetdialog.setOnClickListener(new OnClickListener() { + ibSigngetDialog.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { LogUtils.d(TAG, "签名获取按钮点击,弹出SignGetDialog"); diff --git a/libappbase/src/main/res/drawable/ic_bug.xml b/libappbase/src/main/res/drawable/ic_bug.xml new file mode 100644 index 0000000..41a7159 --- /dev/null +++ b/libappbase/src/main/res/drawable/ic_bug.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/libappbase/src/main/res/layout/dialog_debug_host.xml b/libappbase/src/main/res/layout/dialog_debug_host.xml new file mode 100644 index 0000000..df7fe80 --- /dev/null +++ b/libappbase/src/main/res/layout/dialog_debug_host.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + +