From cb3796bfea829c893ef2b8c2b5ceb66e977d9721 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Fri, 26 Sep 2025 20:32:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5APPBase=E8=BD=AC=E7=A7=BB?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aes/build.properties | 4 +- libaes/build.properties | 4 +- .../libaes/dialogs/YesNoAlertDialog.java | 60 ++++ .../libaes/winboll/IWinBoLLActivity.java | 17 ++ .../winboll/MyActivityLifecycleCallbacks.java | 98 ++++++ .../studio/libaes/winboll/WinBoLL.java | 40 +++ .../winboll/WinBoLLActivityManager.java | 288 ++++++++++++++++++ 7 files changed, 507 insertions(+), 4 deletions(-) create mode 100644 libaes/src/main/java/cc/winboll/studio/libaes/dialogs/YesNoAlertDialog.java create mode 100644 libaes/src/main/java/cc/winboll/studio/libaes/winboll/IWinBoLLActivity.java create mode 100644 libaes/src/main/java/cc/winboll/studio/libaes/winboll/MyActivityLifecycleCallbacks.java create mode 100644 libaes/src/main/java/cc/winboll/studio/libaes/winboll/WinBoLL.java create mode 100644 libaes/src/main/java/cc/winboll/studio/libaes/winboll/WinBoLLActivityManager.java diff --git a/aes/build.properties b/aes/build.properties index 26d3a6e..0061f26 100644 --- a/aes/build.properties +++ b/aes/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Aug 31 23:40:17 HKT 2025 +#Fri Sep 26 12:28:48 GMT 2025 stageCount=4 libraryProject=libaes baseVersion=15.9 publishVersion=15.9.3 -buildCount=0 +buildCount=2 baseBetaVersion=15.9.4 diff --git a/libaes/build.properties b/libaes/build.properties index b928936..0061f26 100644 --- a/libaes/build.properties +++ b/libaes/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Aug 31 05:00:43 CST 2025 +#Fri Sep 26 12:28:48 GMT 2025 stageCount=4 libraryProject=libaes baseVersion=15.9 publishVersion=15.9.3 -buildCount=0 +buildCount=2 baseBetaVersion=15.9.4 diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/dialogs/YesNoAlertDialog.java b/libaes/src/main/java/cc/winboll/studio/libaes/dialogs/YesNoAlertDialog.java new file mode 100644 index 0000000..f176daa --- /dev/null +++ b/libaes/src/main/java/cc/winboll/studio/libaes/dialogs/YesNoAlertDialog.java @@ -0,0 +1,60 @@ +package cc.winboll.studio.libaes.dialogs; + +/** + * @Author ZhanGSKen + * @Date 2025/03/28 17:40:47 + * @Date 2024/08/12 14:46:25 + * @Describe 询问用户确定与否的选择框 + */ +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; + +public class YesNoAlertDialog { + + public static final String TAG = "YesNoAlertDialog"; + + public static void show(Context context, String szTitle, String szMessage, final OnDialogResultListener listener) { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder( + context); + + // set title + alertDialogBuilder.setTitle(szTitle); + + // set dialog message + alertDialogBuilder + .setMessage(szMessage) + .setCancelable(true) + .setOnCancelListener(new DialogInterface.OnCancelListener(){ + @Override + public void onCancel(DialogInterface dialog) { + listener.onNo(); + } + }) + .setPositiveButton("YES", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // if this button is clicked, close + // current activity + listener.onYes(); + } + }) + .setNegativeButton("NO", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // if this button is clicked, just close + // the dialog box and do nothing + dialog.cancel(); + } + }); + + // create alert dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + + // show it + alertDialog.show(); + } + + public interface OnDialogResultListener { + abstract void onYes(); + abstract void onNo(); + } +} diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/winboll/IWinBoLLActivity.java b/libaes/src/main/java/cc/winboll/studio/libaes/winboll/IWinBoLLActivity.java new file mode 100644 index 0000000..ace14d2 --- /dev/null +++ b/libaes/src/main/java/cc/winboll/studio/libaes/winboll/IWinBoLLActivity.java @@ -0,0 +1,17 @@ +package cc.winboll.studio.libaes.winboll; +import android.app.Activity; + +/** + * @Author ZhanGSKen + * @Date 2025/05/10 09:34 + * @Describe WinBoLL 窗口操作接口 + */ +public abstract interface IWinBoLLActivity { + + public static final String TAG = "IWinBoLLActivity"; + + public static final String ACTION_BIND = IWinBoLLActivity.class.getName() + ".ACTION_BIND"; + + public Activity getActivity(); + public String getTag(); +} diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/winboll/MyActivityLifecycleCallbacks.java b/libaes/src/main/java/cc/winboll/studio/libaes/winboll/MyActivityLifecycleCallbacks.java new file mode 100644 index 0000000..09797bc --- /dev/null +++ b/libaes/src/main/java/cc/winboll/studio/libaes/winboll/MyActivityLifecycleCallbacks.java @@ -0,0 +1,98 @@ +package cc.winboll.studio.libaes.winboll; + +/** + * @Author ZhanGSKen + * @Date 2025/03/25 04:29:19 + */ +import android.app.Activity; +import android.app.Application; +import android.content.Intent; +import android.os.Bundle; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.utils.ToastUtils; + +public class MyActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { + + public static final String TAG = "MyActivityLifecycleCallbacks"; + + public String mInfo = ""; + + public MyActivityLifecycleCallbacks() { + + } + + void createActivityeInfo(Activity activity) { + StringBuilder sb = new StringBuilder(); + Intent receivedIntent = activity.getIntent(); + sb.append("\nCallingActivity : \n"); + if (activity.getCallingActivity() != null) { + sb.append(activity.getCallingActivity().getPackageName()); + } + sb.append("\nReceived Intent Package : \n"); + sb.append(receivedIntent.getPackage()); + + Bundle extras = receivedIntent.getExtras(); + if (extras != null) { + for (String key : extras.keySet()) { + sb.append("\nIntentInfo"); + sb.append("\n键: "); + sb.append(key); + sb.append(", 值: "); + sb.append(extras.get(key)); + //Log.d("IntentInfo", "键: " + key + ", 值: " + extras.get(key)); + } + } + mInfo = sb.toString(); + //Log.d("IntentInfo", "发送Intent的应用包名: " + senderPackage); + } + + public void showActivityeInfo() { + //ToastUtils.show("ActivityeInfo : " + mInfo); + LogUtils.d(TAG, "ActivityeInfo : " + mInfo); + } + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + // 在这里可以做一些初始化相关的操作,例如记录Activity的创建时间等 + //System.out.println(activity.getLocalClassName() + " was created"); + LogUtils.d(TAG, activity.getLocalClassName() + " was created"); + createActivityeInfo(activity); + } + + @Override + public void onActivityStarted(Activity activity) { + //System.out.println(activity.getLocalClassName() + " was started"); + LogUtils.d(TAG, activity.getLocalClassName() + " was started"); + //createActivityeInfo(activity); + } + + @Override + public void onActivityResumed(Activity activity) { + //System.out.println(activity.getLocalClassName() + " was resumed"); + LogUtils.d(TAG, activity.getLocalClassName() + " was resumed"); + //createActivityeInfo(activity); + } + + @Override + public void onActivityPaused(Activity activity) { + //System.out.println(activity.getLocalClassName() + " was paused"); + LogUtils.d(TAG, activity.getLocalClassName() + " was paused"); + } + + @Override + public void onActivityStopped(Activity activity) { + //System.out.println(activity.getLocalClassName() + " was stopped"); + LogUtils.d(TAG, activity.getLocalClassName() + " was stopped"); + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + // 可以在这里添加保存状态的自定义逻辑 + } + + @Override + public void onActivityDestroyed(Activity activity) { + //System.out.println(activity.getLocalClassName() + " was destroyed"); + LogUtils.d(TAG, activity.getLocalClassName() + " was destroyed"); + } +} diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/winboll/WinBoLL.java b/libaes/src/main/java/cc/winboll/studio/libaes/winboll/WinBoLL.java new file mode 100644 index 0000000..d7a90c8 --- /dev/null +++ b/libaes/src/main/java/cc/winboll/studio/libaes/winboll/WinBoLL.java @@ -0,0 +1,40 @@ +package cc.winboll.studio.libaes.winboll; + +/** + * @Author ZhanGSKen + * @Date 2025/05/10 10:13 + * @Describe WinBoLL 系列应用通用管理类 + */ +import android.content.Context; +import android.content.Intent; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.models.WinBoLLModel; + +public class WinBoLL { + + public static final String TAG = "WinBoLL"; + + public static final String ACTION_BIND = WinBoLL.class.getName() + ".ACTION_BIND"; + public static final String EXTRA_WINBOLLMODEL = "EXTRA_WINBOLLMODEL"; + + public static void bindToAPPBase(Context context, String appMainService) { + LogUtils.d(TAG, "bindToAPPBase(...)"); + String toPackage = "cc.winboll.studio.appbase"; + startBind(context, toPackage, appMainService); + } + + public static void bindToAPPBaseBeta(Context context, String appMainService) { + LogUtils.d(TAG, "bindToAPPBaseBeta(...)"); + String toPackage = "cc.winboll.studio.appbase.beta"; + startBind(context, toPackage, appMainService); + } + + static void startBind(Context context, String toPackage, String appMainService) { + Intent intent = new Intent(ACTION_BIND); + intent.putExtra(EXTRA_WINBOLLMODEL, (new WinBoLLModel(toPackage, appMainService)).toString()); + intent.setPackage(toPackage); + LogUtils.d(TAG, String.format("ACTION_BIND :\nTo Package : %s\nAPP Main Service : %s", toPackage, appMainService)); + context.sendBroadcast(intent); + } + +} diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/winboll/WinBoLLActivityManager.java b/libaes/src/main/java/cc/winboll/studio/libaes/winboll/WinBoLLActivityManager.java new file mode 100644 index 0000000..720b880 --- /dev/null +++ b/libaes/src/main/java/cc/winboll/studio/libaes/winboll/WinBoLLActivityManager.java @@ -0,0 +1,288 @@ +package cc.winboll.studio.libaes.winboll; + +/** + * @Author ZhanGSKen + * @Date 2025/05/10 10:02 + * @Describe 应用活动窗口管理器 + * 参考 : + * android 类似微信小程序多任务窗口 及 设置 TaskDescription 修改 icon 和 label + * https://blog.csdn.net/qq_29364417/article/details/109379915?app_version=6.4.2&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22109379915%22%2C%22source%22%3A%22weixin_38986226%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app + */ +import android.app.Activity; +import android.app.ActivityManager; +import android.app.TaskStackBuilder; +import android.content.Context; +import android.content.Intent; +import cc.winboll.studio.libappbase.GlobalApplication; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.utils.ToastUtils; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import cc.winboll.studio.libappbase.winboll.LogActivity; + +public class WinBoLLActivityManager { + + public static final String TAG = "WinBoLLActivityManager"; + + public static final String EXTRA_TAG = "EXTRA_TAG"; + + + public enum WinBoLLUI_TYPE { Aplication, Service } + + Context mContext; + volatile static WinBoLLActivityManager _mIWinBoLLActivityManager; + Map mActivityListMap; + + volatile static WinBoLLUI_TYPE _WinBoLLUI_TYPE = WinBoLLUI_TYPE.Service; + public static void setWinBoLLUI_TYPE(WinBoLLUI_TYPE winBoLLUI_TYPE) { + _WinBoLLUI_TYPE = winBoLLUI_TYPE; + } + + public static WinBoLLUI_TYPE getWinBoLLUI_TYPE() { + return _WinBoLLUI_TYPE; + } + WinBoLLActivityManager() { + mContext = GlobalApplication.getInstance(); + mActivityListMap = new HashMap(); + } + + public static synchronized WinBoLLActivityManager getInstance() { + if (_mIWinBoLLActivityManager == null) { + _mIWinBoLLActivityManager = new WinBoLLActivityManager(); + } + return _mIWinBoLLActivityManager; + } + + /** + * 把Activity添加到管理中 + */ + public void add(T activity) { + if (isActivityActive(activity.getTag())) { + LogUtils.d(TAG, String.format("add(...) %s is active.", activity.getTag())); + } else { + mActivityListMap.put(activity.getTag(), activity); + LogUtils.d(TAG, String.format("Add activity : %s\n_mapActivityList.size() : %d", activity.getTag(), mActivityListMap.size())); + } + } + + // + // activity: 为 null 时, + // intent.putExtra 函数 "tag" 参数为 tag + // activity: 不为 null 时, + // intent.putExtra 函数 "tag" 参数为 activity.getTag() + // + public void startWinBoLLActivity(Context context, Class clazz) { + // 如果窗口已存在就重启窗口 + if (!resumeActivity(clazz)) { + // 新建一个任务窗口 + Intent intent = new Intent(context, clazz); + //打开多任务窗口 flags + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + //intent.putExtra("tag", tag); + context.startActivity(intent); + } + } + + public void startWinBoLLActivity(Context context, Intent intent, Class clazz) { + // 如果窗口已存在就重启窗口 + if (!resumeActivity(clazz)) { + // 新建一个任务窗口 + //Intent intent = new Intent(context, clazz); + //打开多任务窗口 flags + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + //intent.putExtra("tag", tag); + context.startActivity(intent); + } + } + + public void startLogActivity(Context context) { + // 如果窗口已存在就重启窗口 + //if (!resumeActivity(LogActivity.class)) { + // 新建一个任务窗口 + Intent intent = new Intent(context, LogActivity.class); + //打开多任务窗口 flags + // Define the bounds. +// Rect bounds = new Rect(0, 0, 800, 200); +// // Set the bounds as an activity option. +// ActivityOptions options = ActivityOptions.makeBasic(); +// options.setLaunchBounds(bounds); + intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + + //intent.putExtra(EXTRA_TAG, tag); + + //context.startActivity(intent, options.toBundle()); + context.startActivity(intent); + //} + } + + // + // 判断 tag 绑定的 Activity 是否已经创建 + // + public boolean isActivityActive(String tag) { + return mActivityListMap.get(tag) != null; + } + + Activity getActivityByTag(String tag) { + return (mActivityListMap.get(tag) == null) ?null: mActivityListMap.get(tag).getActivity(); + } + + + // + // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台 + // + public boolean resumeActivity(Class clazz) { + try { + Activity activity = getActivityByTag(clazz.newInstance().getTag()); + if (activity != null) { + return resumeActivity(activity); + } + } catch (InstantiationException | IllegalAccessException e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + return false; + } + + // + // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台 + // + public boolean resumeActivity(String tag) { + Activity activity = getActivityByTag(tag); + if (activity != null) { + return resumeActivity(activity); + } + return false; + } + + // + // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台 + // + public boolean resumeActivity(Activity activity) { + ActivityManager am = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE); + //返回启动它的根任务(home 或者 MainActivity) + //Intent intent = new Intent(mContext, activity.getClass()); + //TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext); + //stackBuilder.addNextIntentWithParentStack(intent); + //stackBuilder.startActivities(); + am.moveTaskToFront(activity.getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION); + //ToastUtils.show("resumeActivity"); + return true; + } + + + /** + * 结束所有 Activity + */ + public void finishAll() { + try { + //ToastUtils.show(String.format("finishAll() size : %d", _mIWinBoLLActivityList.size())); + for (int i = mActivityListMap.size() - 1; i > -1; i--) { + IWinBoLLActivity iWinBoLLActivity = mActivityListMap.get(i); + ToastUtils.show("finishAll() activity"); + if (iWinBoLLActivity != null && iWinBoLLActivity.getActivity() != null && !iWinBoLLActivity.getActivity().isFinishing() && !iWinBoLLActivity.getActivity().isDestroyed()) { + //ToastUtils.show("activity != null ..."); + if (WinBoLLActivityManager.getInstance().getWinBoLLUI_TYPE() == WinBoLLUI_TYPE.Service) { + // 结束窗口和最近任务栏, 建议前台服务类应用使用,可以方便用户再次调用 UI 操作。 + iWinBoLLActivity.getActivity().finishAndRemoveTask(); + //ToastUtils.show("finishAll() activity.finishAndRemoveTask();"); + } else if (WinBoLLActivityManager.getInstance().getWinBoLLUI_TYPE() == WinBoLLUI_TYPE.Aplication) { + // 结束窗口保留最近任务栏,建议前台服务类应用使用,可以保持应用的系统自觉性。 + iWinBoLLActivity.getActivity().finish(); + //ToastUtils.show("finishAll() activity.finish();"); + } else { + ToastUtils.show("WinBollApplication.WinBollUI_TYPE error."); + } + } + } + } catch (Exception e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + } + + /** + * 结束指定Activity + */ + public void finish(T iWinBoLLActivity) { + try { + if (iWinBoLLActivity != null && iWinBoLLActivity.getActivity() != null && !iWinBoLLActivity.getActivity().isFinishing() && !iWinBoLLActivity.getActivity().isDestroyed()) { + //根据tag 移除 MyActivity + //String tag= activity.getTag(); + //_mIWinBoLLActivityList.remove(tag); + //ToastUtils.show("remove"); + //ToastUtils.show("_mIWinBoLLActivityArrayMap.size() " + Integer.toString(_mIWinBoLLActivityArrayMap.size())); + + // 窗口回调规则: + // [] 当前窗口位置 >> 调度出的窗口位置 + // ★:[0] 1 2 3 4 >> 1 + // ★:0 1 [2] 3 4 >> 1 + // ★:0 1 2 [3] 4 >> 2 + // ★:0 1 2 3 [4] >> 3 + // ★:[0] >> 直接关闭当前窗口 + Activity preActivity = getPreActivity(iWinBoLLActivity); + iWinBoLLActivity.getActivity().finish(); + if (preActivity != null) { + resumeActivity(preActivity); + } + } + + } catch (Exception e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + } + + Activity getPreActivity(IWinBoLLActivity iWinBoLLActivity) { + try { + boolean bingo = false; + IWinBoLLActivity preIWinBoLLActivity = null; + for (Map.Entry entity : mActivityListMap.entrySet()) { + if (entity.getKey().equals(iWinBoLLActivity.getTag())) { + bingo = true; + LogUtils.d(TAG, "bingo"); + break; + } + preIWinBoLLActivity = entity.getValue(); + } + + if (bingo) { + return preIWinBoLLActivity.getActivity(); + } + } catch (Exception e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + + return null; + } + + public boolean registeRemove(T iWinBoLLActivity) { + IWinBoLLActivity iWinBoLLActivityTest = mActivityListMap.get(iWinBoLLActivity.getTag()); + if (iWinBoLLActivityTest != null) { + mActivityListMap.remove(iWinBoLLActivity.getTag()); + return true; + } + return false; + } + + public void printAvtivityListInfo() { + if (!mActivityListMap.isEmpty()) { + StringBuilder sb = new StringBuilder("Map entries : " + Integer.toString(mActivityListMap.size())); + Iterator> iterator = mActivityListMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + sb.append("\nKey: " + entry.getKey() + ", \nValue: " + entry.getValue().getTag()); + //ToastUtils.show("\nKey: " + entry.getKey() + ", Value: " + entry.getValue().getTag()); + } + sb.append("\nMap entries end."); + LogUtils.d(TAG, sb.toString()); + } else { + LogUtils.d(TAG, "The map is empty."); + } + } +} +