From 504b78c04e37d64c7f6ab50eaa4f162e0ce7fa17 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Tue, 13 Jan 2026 15:25:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=9F=BA=E7=A1=80=E7=AA=97?= =?UTF-8?q?=E5=8F=A3=E7=AE=A1=E7=90=86=E7=B1=BB=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aes/build.properties | 4 +- aes/src/main/AndroidManifest.xml | 3 + .../cc/winboll/studio/aes/AboutActivity.java | 47 +- .../cc/winboll/studio/aes/MainActivity.java | 12 +- .../winboll/studio/aes/WinBoLLActivity.java | 2 +- aes/src/main/res/layout/activity_about.xml | 2 +- libaes/build.properties | 4 +- .../libaes/activitys/BaseWinBoLLActivity.java | 59 ++ .../activitys/DrawerFragmentActivity.java | 45 +- .../libaes/interfaces/IWinBoLLActivity.java | 24 +- .../libaes/utils/WinBoLLActivityManager.java | 504 +++++++++--------- 11 files changed, 405 insertions(+), 301 deletions(-) create mode 100644 libaes/src/main/java/cc/winboll/studio/libaes/activitys/BaseWinBoLLActivity.java diff --git a/aes/build.properties b/aes/build.properties index 95b9d36..0104250 100644 --- a/aes/build.properties +++ b/aes/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Jan 13 11:19:26 HKT 2026 +#Tue Jan 13 07:24:01 GMT 2026 stageCount=3 libraryProject=libaes baseVersion=15.15 publishVersion=15.15.2 -buildCount=0 +buildCount=25 baseBetaVersion=15.15.3 diff --git a/aes/src/main/AndroidManifest.xml b/aes/src/main/AndroidManifest.xml index c0ca77d..c732afa 100644 --- a/aes/src/main/AndroidManifest.xml +++ b/aes/src/main/AndroidManifest.xml @@ -3,6 +3,9 @@ xmlns:android="http://schemas.android.com/apk/res/android" package="cc.winboll.studio.aes"> + + + - * @Date 2026/01/11 15:16 + * @Date 2026/01/13 11:25 * @Describe 应用介绍窗口 */ -public class AboutActivity extends Activity { +public class AboutActivity extends BaseWinBoLLActivity { public static final String TAG = "AboutActivity"; + + private Toolbar mToolbar; + + @Override + public String getTag() { + return TAG; + } @Override protected void onCreate(Bundle savedInstanceState) { @@ -24,21 +33,33 @@ public class AboutActivity extends Activity { setContentView(R.layout.activity_about); // 设置工具栏 - Toolbar toolbar = findViewById(R.id.toolbar); - setActionBar(toolbar); - getActionBar().setSubtitle(TAG); - getActionBar().setDisplayHomeAsUpEnabled(true); - toolbar.setNavigationOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finish(); // 点击导航栏返回按钮,触发 finish() - } - }); + initToolbar(); AboutView aboutView = findViewById(R.id.aboutview); aboutView.setAPPInfo(genDefaultAppInfo()); } + private void initToolbar() { + LogUtils.d(TAG, "initToolbar() 开始初始化"); + mToolbar = findViewById(R.id.toolbar); + if (mToolbar == null) { + LogUtils.e(TAG, "initToolbar() | Toolbar未找到"); + return; + } + setSupportActionBar(mToolbar); + mToolbar.setSubtitle(getTag()); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + mToolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LogUtils.d(TAG, "导航栏 点击返回按钮"); + WinBoLLActivityManager.getInstance().resumeActivity(MainActivity.class); + WinBoLLActivityManager.getInstance().finish(AboutActivity.this); + } + }); + LogUtils.d(TAG, "initToolbar() 配置完成"); + } + private APPInfo genDefaultAppInfo() { LogUtils.d(TAG, "genDefaultAppInfo() 调用"); String branchName = "aes"; 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 5360023..927c852 100644 --- a/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java +++ b/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java @@ -30,7 +30,7 @@ import cc.winboll.studio.libappbase.LogUtils; import com.a4455jkjh.colorpicker.ColorPickerDialog; import java.util.ArrayList; -public class MainActivity extends DrawerFragmentActivity implements IWinBoLLActivity { +public class MainActivity extends DrawerFragmentActivity { public static final String TAG = "MainActivity"; @@ -38,11 +38,6 @@ public class MainActivity extends DrawerFragmentActivity implements IWinBoLLActi TestAButtonFragment mTestAButtonFragment; TestViewPageFragment mTestViewPageFragment; - @Override - public Activity getActivity() { - return this; - } - @Override public String getTag() { return TAG; @@ -188,8 +183,9 @@ public class MainActivity extends DrawerFragmentActivity implements IWinBoLLActi Intent intent = new Intent(this, SettingsActivity.class); startActivity(intent); } else if (nItemId == R.id.item_about) { - Intent intent = new Intent(this, AboutActivity.class); - startActivity(intent); +// Intent intent = new Intent(this, AboutActivity.class); +// startActivity(intent); + WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, AboutActivity.class); } diff --git a/aes/src/main/java/cc/winboll/studio/aes/WinBoLLActivity.java b/aes/src/main/java/cc/winboll/studio/aes/WinBoLLActivity.java index 76005db..bc45765 100644 --- a/aes/src/main/java/cc/winboll/studio/aes/WinBoLLActivity.java +++ b/aes/src/main/java/cc/winboll/studio/aes/WinBoLLActivity.java @@ -55,6 +55,6 @@ public class WinBoLLActivity extends AppCompatActivity implements IWinBoLLActivi @Override protected void onDestroy() { super.onDestroy(); - WinBoLLActivityManager.getInstance().registeRemove(this); + WinBoLLActivityManager.getInstance().finish(this); } } diff --git a/aes/src/main/res/layout/activity_about.xml b/aes/src/main/res/layout/activity_about.xml index bfd02eb..e003607 100644 --- a/aes/src/main/res/layout/activity_about.xml +++ b/aes/src/main/res/layout/activity_about.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - diff --git a/libaes/build.properties b/libaes/build.properties index b5fe9cf..0104250 100644 --- a/libaes/build.properties +++ b/libaes/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Jan 13 11:19:15 HKT 2026 +#Tue Jan 13 07:24:01 GMT 2026 stageCount=3 libraryProject=libaes baseVersion=15.15 publishVersion=15.15.2 -buildCount=0 +buildCount=25 baseBetaVersion=15.15.3 diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/activitys/BaseWinBoLLActivity.java b/libaes/src/main/java/cc/winboll/studio/libaes/activitys/BaseWinBoLLActivity.java new file mode 100644 index 0000000..00e49f0 --- /dev/null +++ b/libaes/src/main/java/cc/winboll/studio/libaes/activitys/BaseWinBoLLActivity.java @@ -0,0 +1,59 @@ +package cc.winboll.studio.libaes.activitys; + +import android.app.Activity; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; +import cc.winboll.studio.libaes.models.AESThemeBean; +import cc.winboll.studio.libaes.utils.AESThemeUtil; +import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; +import cc.winboll.studio.libappbase.ToastUtils; + +/** + * @Author 豆包&ZhanGSKen + * @Date 2026/01/13 14:22 + * @Describe BaseWinBollActivity + */ +public abstract class BaseWinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity { + public static final String TAG = "BaseWinBoLLActivity"; + + protected volatile AESThemeBean.ThemeType mThemeType; + + @Override + protected void onCreate(Bundle savedInstanceState) { + mThemeType = getThemeType(); + setThemeStyle(); + super.onCreate(savedInstanceState); + WinBoLLActivityManager.getInstance().add(this); + ToastUtils.show(getTag() + ": onCreate"); + } + + AESThemeBean.ThemeType getThemeType() { + /*SharedPreferences sharedPreferences = getSharedPreferences( + SHAREDPREFERENCES_NAME, MODE_PRIVATE); + return AESThemeBean.ThemeType.values()[((sharedPreferences.getInt(DRAWER_THEME_TYPE, AESThemeBean.ThemeType.DEFAULT.ordinal())))]; + */ + return AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext())); + } + + void setThemeStyle() { + //setTheme(AESThemeBean.getThemeStyle(getThemeType())); + setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext())); + } + + @Override + protected void onDestroy() { + WinBoLLActivityManager.getInstance().registeRemove(this); + super.onDestroy(); + } + + // 子类必须实现getTag(),确保唯一标识 + @Override + public abstract String getTag(); + + @Override + public Activity getActivity() { + return this; + } +} + diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/activitys/DrawerFragmentActivity.java b/libaes/src/main/java/cc/winboll/studio/libaes/activitys/DrawerFragmentActivity.java index 430e4fd..1bbd12a 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 @@ -34,7 +34,7 @@ import cc.winboll.studio.libappbase.LogUtils; import com.baoyz.widget.PullRefreshLayout; import java.util.ArrayList; -public abstract class DrawerFragmentActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { +public abstract class DrawerFragmentActivity extends BaseWinBoLLActivity implements AdapterView.OnItemClickListener { public static final String TAG = "DrawerFragmentActivity"; @@ -62,14 +62,19 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement @Override protected void onCreate(Bundle savedInstanceState) { //mContext = this; - mThemeType = getThemeType(); - setThemeStyle(); +// mThemeType = getThemeType(); +// setThemeStyle(); super.onCreate(savedInstanceState); mActivityType = initActivityType(); initRootView(); LogUtils.d(TAG, "onCreate end."); } + @Override + public String getTag() { + return TAG; + } + @Override protected void onDestroy() { super.onDestroy(); @@ -157,22 +162,22 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement super.onBackPressed(); } - void setThemeStyle() { - //setTheme(AESThemeBean.getThemeStyle(getThemeType())); - setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext())); - } +// void setThemeStyle() { +// //setTheme(AESThemeBean.getThemeStyle(getThemeType())); +// setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext())); +// } - boolean checkThemeStyleChange() { - return mThemeType != getThemeType(); - } +// boolean checkThemeStyleChange() { +// return mThemeType != getThemeType(); +// } - AESThemeBean.ThemeType getThemeType() { - /*SharedPreferences sharedPreferences = getSharedPreferences( - SHAREDPREFERENCES_NAME, MODE_PRIVATE); - return AESThemeBean.ThemeType.values()[((sharedPreferences.getInt(DRAWER_THEME_TYPE, AESThemeBean.ThemeType.DEFAULT.ordinal())))]; - */ - return AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext())); - } +// AESThemeBean.ThemeType getThemeType() { +// /*SharedPreferences sharedPreferences = getSharedPreferences( +// SHAREDPREFERENCES_NAME, MODE_PRIVATE); +// return AESThemeBean.ThemeType.values()[((sharedPreferences.getInt(DRAWER_THEME_TYPE, AESThemeBean.ThemeType.DEFAULT.ordinal())))]; +// */ +// return AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext())); +// } @Override public boolean onOptionsItemSelected(MenuItem item) { @@ -190,9 +195,9 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement @Override protected void onResume() { super.onResume(); - if (checkThemeStyleChange()) { - recreate(); - } +// if (checkThemeStyleChange()) { +// recreate(); +// } ADsBannerView adsBannerView = findViewById(R.id.adsbanner); if (adsBannerView != null) { diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/interfaces/IWinBoLLActivity.java b/libaes/src/main/java/cc/winboll/studio/libaes/interfaces/IWinBoLLActivity.java index aa4805b..9760d2b 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/interfaces/IWinBoLLActivity.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/interfaces/IWinBoLLActivity.java @@ -1,18 +1,24 @@ package cc.winboll.studio.libaes.interfaces; +import android.app.Activity; + /** * @Author ZhanGSKen * @Date 2025/05/10 09:34 - * @Describe WinBoLL 窗口操作接口 + * @Describe WinBoll 窗口操作接口(规范定义,职责单一) */ - import android.app.Activity; - -public abstract interface IWinBoLLActivity { +public interface IWinBoLLActivity { + String TAG = "IWinBoLLActivity"; + String ACTION_BIND = IWinBoLLActivity.class.getName() + ".ACTION_BIND"; - public static final String TAG = "IWinBoLLActivity"; + /** + * 获取当前Activity实例 + */ + Activity getActivity(); - public static final String ACTION_BIND = IWinBoLLActivity.class.getName() + ".ACTION_BIND"; - - public Activity getActivity(); - public String getTag(); + /** + * 获取Activity唯一标识(建议使用类名+UUID或固定唯一字符串) + */ + String getTag(); } + diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/utils/WinBoLLActivityManager.java b/libaes/src/main/java/cc/winboll/studio/libaes/utils/WinBoLLActivityManager.java index c95d302..cac2157 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/utils/WinBoLLActivityManager.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/utils/WinBoLLActivityManager.java @@ -1,17 +1,12 @@ package cc.winboll.studio.libaes.utils; -/** - * @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.content.Context; import android.content.Intent; +import android.os.Build; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.LogActivity; @@ -20,273 +15,292 @@ import cc.winboll.studio.libappbase.ToastUtils; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Objects; +/** + * @Author ZhanGSKen + * @Date 2025/05/10 10:02 + * @Describe 应用活动窗口管理器(改进版) + * 核心能力:多任务窗口管理、Activity栈维护、任务前台恢复、批量关闭、前后Activity切换 + * 参考 :android 类似微信小程序多任务窗口 及 设置 TaskDescription 修改 icon 和 label + */ public class WinBoLLActivityManager { public static final String TAG = "WinBoLLActivityManager"; - public static final String EXTRA_TAG = "EXTRA_TAG"; + public enum WinBoLLUI_TYPE { APPLICATION, SERVICE } // 规范命名 大写开头 - public enum WinBoLLUI_TYPE { Aplication, Service } + private GlobalApplication mGlobalApplication; + private static volatile WinBoLLActivityManager sInstance; // 单例命名规范 + private final Map mActivityListMap; // 私有不可变 + private static volatile WinBoLLUI_TYPE sWinBoLLUI_TYPE = WinBoLLUI_TYPE.SERVICE; - GlobalApplication mGlobalApplication; - 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(GlobalApplication application) { + // 私有构造 杜绝外部实例化 + private WinBoLLActivityManager(@NonNull GlobalApplication application) { mGlobalApplication = application; - mActivityListMap = new HashMap(); + mActivityListMap = new HashMap<>(); // 菱形泛型简化 } + /** + * 初始化管理器(必须在Application onCreate中调用) + */ + public static void init(@NonNull T application) { + if (sInstance == null) { + synchronized (WinBoLLActivityManager.class) { + if (sInstance == null) { + sInstance = new WinBoLLActivityManager(application); + } + } + } + } + + /** + * 获取单例(需先调用init初始化,否则抛异常) + */ + @NonNull public static WinBoLLActivityManager getInstance() { - return _mIWinBoLLActivityManager; - } - - public static synchronized void init(T application) { - if (_mIWinBoLLActivityManager == null) { - _mIWinBoLLActivityManager = new WinBoLLActivityManager(application); + if (sInstance == null) { + throw new IllegalStateException("WinBoLLActivityManager 未初始化,请先在Application中调用 init()"); } + return sInstance; + } + + // ===================== 基础配置 ===================== + public static void setWinBoLLUI_TYPE(@NonNull WinBoLLUI_TYPE winBoLLUI_TYPE) { + sWinBoLLUI_TYPE = winBoLLUI_TYPE; + } + + @NonNull + public static WinBoLLUI_TYPE getWinBoLLUI_TYPE() { + return sWinBoLLUI_TYPE; + } + + // ===================== Activity 增删查 ===================== + /** + * 把Activity添加到管理中(自动去重) + */ + public void add(@NonNull T activity) { + String tag = activity.getTag(); + if (isActivityActive(tag)) { + LogUtils.d(TAG, String.format("Activity[%s] 已处于活跃状态,无需重复添加", tag)); + return; + } + mActivityListMap.put(tag, activity); + LogUtils.d(TAG, String.format("添加Activity:%s,当前管理数量:%d", tag, mActivityListMap.size())); } /** - * 把Activity添加到管理中 + * 判断指定Tag的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 (getWinBoLLUI_TYPE() == WinBoLLUI_TYPE.Service) { - // 结束窗口和最近任务栏, 建议前台服务类应用使用,可以方便用户再次调用 UI 操作。 - iWinBoLLActivity.getActivity().finishAndRemoveTask(); - //ToastUtils.show("finishAll() activity.finishAndRemoveTask();"); - } else if (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()); - } + public boolean isActivityActive(@NonNull String tag) { + return mActivityListMap.containsKey(tag) && mActivityListMap.get(tag) != null; } /** - * 结束指定Activity + * 根据Tag获取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()); + @Nullable + public Activity getActivityByTag(@NonNull String tag) { + IWinBoLLActivity winBoLLActivity = mActivityListMap.get(tag); + if (winBoLLActivity == null) return null; + Activity activity = winBoLLActivity.getActivity(); + // 过滤已销毁/已结束的Activity + if (activity == null || activity.isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed())) { + registeRemove(winBoLLActivity); + return null; } + return activity; } - 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()); + /** + * 移除指定Activity(销毁时调用) + */ + public boolean registeRemove(@NonNull T iWinBoLLActivity) { + String tag = iWinBoLLActivity.getTag(); + if (mActivityListMap.containsKey(tag)) { + mActivityListMap.remove(tag); + LogUtils.d(TAG, String.format("移除Activity:%s,剩余管理数量:%d", tag, mActivityListMap.size())); 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."); + // ===================== Activity 启动 ===================== + /** + * 启动WinBoLLActivity(存在则前台恢复,不存在则新建多任务窗口) + */ + public void startWinBoLLActivity(@NonNull Context context, @NonNull Class clazz) { + if (!resumeActivity(clazz)) { + Intent intent = new Intent(context, clazz); + setMultiTaskFlags(intent); + context.startActivity(intent); } } + + /** + * 带Intent参数启动WinBoLLActivity + */ + public void startWinBoLLActivity(@NonNull Context context, @NonNull Intent intent, @NonNull Class clazz) { + if (!resumeActivity(clazz)) { + setMultiTaskFlags(intent); + context.startActivity(intent); + } + } + + /** + * 启动日志页面(固定多任务模式) + */ + public void startLogActivity(@NonNull Context context) { + Intent intent = new Intent(context, LogActivity.class); + setMultiTaskFlags(intent); + intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT); // 分屏相关 + context.startActivity(intent); + } + + /** + * 设置多任务窗口通用Flags + */ + private void setMultiTaskFlags(@NonNull Intent intent) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + } + + // ===================== Activity 前台恢复 ===================== + /** + * 根据Activity类 恢复前台(反射获取Tag,需保证无参构造) + */ + public boolean resumeActivity(@NonNull Class clazz) { + try { + T instance = clazz.newInstance(); + return resumeActivity(instance.getTag()); + } catch (InstantiationException | IllegalAccessException e) { + LogUtils.e(TAG, "恢复Activity失败,类需提供无参构造", e); + } + return false; + } + + /** + * 根据Tag 恢复Activity前台 + */ + public boolean resumeActivity(@NonNull String tag) { + Activity activity = getActivityByTag(tag); + return activity != null && resumeActivity(activity); + } + + /** + * 恢复指定Activity到前台(适配高版本权限) + */ + @SuppressWarnings("deprecation") + public boolean resumeActivity(@NonNull Activity activity) { + if (activity.isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed())) { + return false; + } + try { + ActivityManager am = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE); + if (am == null) { + LogUtils.w(TAG, "获取ActivityManager失败,无法恢复前台"); + return false; + } + // Android 11+ 限制,低版本正常使用 + am.moveTaskToFront(activity.getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION); + //ToastUtils.show(String.format("Activity[%s] 已恢复到前台", activity.getClass().getSimpleName())); + LogUtils.d(TAG, String.format("Activity[%s] 已恢复到前台", activity.getClass().getSimpleName())); + return true; + } catch (SecurityException e) { + //ToastUtils.show("恢复Activity前台失败,缺少权限或系统限制 :" + e.getMessage()); + LogUtils.e(TAG, "恢复Activity前台失败,缺少权限或系统限制", e); + //ToastUtils.show("窗口恢复失败,请手动打开"); + return false; + } + } + + // ===================== Activity 关闭 ===================== + /** + * 结束所有管理的Activity(按UI类型选择关闭策略) + */ + public void finishAll() { + if (mActivityListMap.isEmpty()) { + LogUtils.d(TAG, "当前无管理的Activity,无需结束"); + return; + } + LogUtils.d(TAG, String.format("开始结束所有Activity,共%d个", mActivityListMap.size())); + Iterator> iterator = mActivityListMap.entrySet().iterator(); + while (iterator.hasNext()) { + IWinBoLLActivity winBoLLActivity = iterator.next().getValue(); + Activity activity = winBoLLActivity.getActivity(); + if (activity == null) { + iterator.remove(); + continue; + } + // 安全关闭,避免重复操作 + if (!activity.isFinishing() && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed())) { + if (sWinBoLLUI_TYPE == WinBoLLUI_TYPE.SERVICE) { + activity.finishAndRemoveTask(); // 结束+移除最近任务 + } else if (sWinBoLLUI_TYPE == WinBoLLUI_TYPE.APPLICATION) { + activity.finish(); // 仅结束页面 + } + } + iterator.remove(); // 移除已处理的项 + } + LogUtils.d(TAG, "所有Activity结束完成"); + } + + /** + * 结束指定Activity,自动恢复上一个Activity前台 + */ + public void finish(@NonNull T iWinBoLLActivity) { + Activity currentActivity = iWinBoLLActivity.getActivity(); + if (currentActivity == null || currentActivity.isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && currentActivity.isDestroyed())) { + registeRemove(iWinBoLLActivity); + return; + } + + // 先获取上一个Activity,再关闭当前 + Activity preActivity = getPreActivity(iWinBoLLActivity); + currentActivity.finish(); + registeRemove(iWinBoLLActivity); // 关闭后移除管理 + + // 恢复上一个Activity前台 + if (preActivity != null) { + resumeActivity(preActivity); + } + } + + /** + * 获取当前Activity的上一个栈内Activity(修复原遍历逻辑错误) + */ + @Nullable + private Activity getPreActivity(@NonNull IWinBoLLActivity currentActivity) { + String currentTag = currentActivity.getTag(); + IWinBoLLActivity preWinBoLLActivity = null; + for (Map.Entry entry : mActivityListMap.entrySet()) { + String tag = entry.getKey(); + if (Objects.equals(tag, currentTag)) { + break; // 找到当前Activity,循环终止,pre即为上一个 + } + preWinBoLLActivity = entry.getValue(); + } + return preWinBoLLActivity != null ? preWinBoLLActivity.getActivity() : null; + } + + // ===================== 调试辅助 ===================== + /** + * 打印所有管理的Activity信息(调试用) + */ + public void printActivityListInfo() { + if (mActivityListMap.isEmpty()) { + LogUtils.d(TAG, "当前管理的Activity列表为空"); + return; + } + StringBuilder sb = new StringBuilder(String.format("Activity管理列表(总数:%d)\n", mActivityListMap.size())); + for (Map.Entry entry : mActivityListMap.entrySet()) { + sb.append("Tag: ").append(entry.getKey()) + .append(" | Activity: ").append(entry.getValue().getActivity().getClass().getSimpleName()) + .append("\n"); + } + LogUtils.d(TAG, sb.toString()); + } }