diff --git a/androiddemo/build.gradle b/androiddemo/build.gradle index 8357611..e498523 100644 --- a/androiddemo/build.gradle +++ b/androiddemo/build.gradle @@ -18,15 +18,8 @@ def genVersionName(def versionName){ } android { - productFlavors { - beta { - } - stage { - } - } - - compileSdkVersion 30 - buildToolsVersion "30.0.3" + compileSdkVersion 32 + buildToolsVersion "32.0.0" defaultConfig { applicationId "cc.winboll.studio.androiddemo" @@ -36,7 +29,7 @@ android { // versionName 更新后需要手动设置 // .winboll/winbollBuildProps.properties 文件的 stageCount=0 // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" - versionName "1.0" + versionName "15.0" if(true) { versionName = genVersionName("${versionName}") } @@ -54,21 +47,25 @@ dependencies { api fileTree(dir: 'libs', include: ['*.jar']) // 吐司类库 - implementation 'com.github.getActivity:ToastUtils:10.5' + api 'com.github.getActivity:ToastUtils:10.5' // Android 类库 - // https://mvnrepository.com/artifact/com.android.support/support-v4 - implementation 'com.android.support:support-v4:28.0.0' + api 'com.android.support:appcompat-v7:28.0.0' // 包含 AppCompatActivity // https://mvnrepository.com/artifact/com.android.support/support-compat - implementation 'com.android.support:support-compat:28.0.0' + api 'com.android.support:support-compat:28.0.0' // 保留原有依赖(可选) + // https://mvnrepository.com/artifact/com.android.support/support-v4 + api 'com.android.support:support-v4:28.0.0' // https://mvnrepository.com/artifact/com.android.support/support-media-compat - implementation 'com.android.support:support-media-compat:28.0.0' + api 'com.android.support:support-media-compat:28.0.0' // https://mvnrepository.com/artifact/com.android.support/support-core-utils - implementation 'com.android.support:support-core-utils:28.0.0' + api 'com.android.support:support-core-utils:28.0.0' // https://mvnrepository.com/artifact/com.android.support/support-core-ui - implementation 'com.android.support:support-core-ui:28.0.0' + api 'com.android.support:support-core-ui:28.0.0' // https://mvnrepository.com/artifact/com.android.support/support-fragment - implementation 'com.android.support:support-fragment:28.0.0' + api 'com.android.support:support-fragment:28.0.0' // https://mvnrepository.com/artifact/com.android.support/recyclerview-v7 - implementation 'com.android.support:recyclerview-v7:28.0.0' + api 'com.android.support:recyclerview-v7:28.0.0' + + api 'cc.winboll.studio:libappbase:15.0.9' + api 'cc.winboll.studio:libapputils:15.0.11' } diff --git a/androiddemo/build.properties b/androiddemo/build.properties index 4d98e2f..b67201a 100644 --- a/androiddemo/build.properties +++ b/androiddemo/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Mar 11 18:02:14 GMT 2025 +#Wed Mar 26 07:23:51 GMT 2025 stageCount=0 libraryProject= -baseVersion=1.0 -publishVersion=1.0.0 -buildCount=1 -baseBetaVersion=1.0.1 +baseVersion=15.0 +publishVersion=15.0.0 +buildCount=11 +baseBetaVersion=15.0.1 diff --git a/androiddemo/src/main/AndroidManifest.xml b/androiddemo/src/main/AndroidManifest.xml index 43eac9a..57a1145 100644 --- a/androiddemo/src/main/AndroidManifest.xml +++ b/androiddemo/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ android:label="@string/app_name" android:theme="@style/AppTheme" android:resizeableActivity="true" - android:name=".GlobalApplication"> + android:name=".App"> + android:orientation="vertical"> - + + + + + + + + + + + diff --git a/androidxdemo/build.gradle b/androidxdemo/build.gradle index fcf214f..4b7522e 100644 --- a/androidxdemo/build.gradle +++ b/androidxdemo/build.gradle @@ -18,15 +18,8 @@ def genVersionName(def versionName){ } android { - productFlavors { - beta { - } - stage { - } - } - - compileSdkVersion 30 - buildToolsVersion "30.0.3" + compileSdkVersion 32 + buildToolsVersion "32.0.0" defaultConfig { applicationId "cc.winboll.studio.androidxdemo" @@ -36,7 +29,7 @@ android { // versionName 更新后需要手动设置 // .winboll/winbollBuildProps.properties 文件的 stageCount=0 // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" - versionName "1.0" + versionName "15.0" if(true) { versionName = genVersionName("${versionName}") } @@ -54,23 +47,26 @@ dependencies { api fileTree(dir: 'libs', include: ['*.jar']) // SSH - implementation 'com.jcraft:jsch:0.1.55' + api 'com.jcraft:jsch:0.1.55' // Html 解析 - implementation 'org.jsoup:jsoup:1.13.1' + api 'org.jsoup:jsoup:1.13.1' // 二维码类库 - implementation 'com.google.zxing:core:3.4.1' - implementation 'com.journeyapps:zxing-android-embedded:3.6.0' + api 'com.google.zxing:core:3.4.1' + api 'com.journeyapps:zxing-android-embedded:3.6.0' // 应用介绍页类库 - implementation 'io.github.medyo:android-about-page:2.0.0' + api 'io.github.medyo:android-about-page:2.0.0' // 吐司类库 - implementation 'com.github.getActivity:ToastUtils:10.5' + api 'com.github.getActivity:ToastUtils:10.5' // 网络连接类库 - implementation 'com.squareup.okhttp3:okhttp:4.4.1' - // Android 类库 - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.viewpager:viewpager:1.0.0' - implementation 'androidx.vectordrawable:vectordrawable:1.1.0' - implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0' - implementation 'androidx.fragment:fragment:1.1.0' - implementation 'com.google.android.material:material:1.4.0' + api 'com.squareup.okhttp3:okhttp:4.4.1' + // AndroidX 类库 + api 'androidx.appcompat:appcompat:1.0.0' + api 'com.google.android.material:material:1.4.0' + //api 'androidx.viewpager:viewpager:1.0.0' + //api 'androidx.vectordrawable:vectordrawable:1.1.0' + //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0' + //api 'androidx.fragment:fragment:1.1.0' + + api 'cc.winboll.studio:libappbase:15.0.9' + api 'cc.winboll.studio:libapputils:15.0.11' } diff --git a/androidxdemo/build.properties b/androidxdemo/build.properties index 8d115d9..de51361 100644 --- a/androidxdemo/build.properties +++ b/androidxdemo/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Mar 11 18:25:43 GMT 2025 +#Mon Mar 24 06:19:57 GMT 2025 stageCount=0 libraryProject= -baseVersion=1.0 -publishVersion=1.0.0 -buildCount=4 -baseBetaVersion=1.0.1 +baseVersion=15.0 +publishVersion=15.0.0 +buildCount=8 +baseBetaVersion=15.0.1 diff --git a/androidxdemo/src/main/AndroidManifest.xml b/androidxdemo/src/main/AndroidManifest.xml index 6d0c707..1b70e90 100644 --- a/androidxdemo/src/main/AndroidManifest.xml +++ b/androidxdemo/src/main/AndroidManifest.xml @@ -8,9 +8,9 @@ android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:label="@string/app_name" - android:theme="@style/AppTheme" + android:theme="@style/MyAppTheme" android:resizeableActivity="true" - android:name=".GlobalApplication"> + android:name=".App"> - \ No newline at end of file + diff --git a/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/GlobalApplication.java b/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/App.java similarity index 97% rename from androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/GlobalApplication.java rename to androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/App.java index 7abaf88..baa9df1 100644 --- a/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/GlobalApplication.java +++ b/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/App.java @@ -1,7 +1,6 @@ package cc.winboll.studio.androidxdemo; import android.app.Activity; -import android.app.Application; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; @@ -22,6 +21,7 @@ import android.widget.HorizontalScrollView; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; +import cc.winboll.studio.libappbase.GlobalApplication; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; @@ -39,15 +39,15 @@ import java.util.Date; import java.util.LinkedHashMap; import java.util.concurrent.atomic.AtomicBoolean; -public class GlobalApplication extends Application { +public class App extends GlobalApplication { private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper()); @Override public void onCreate() { super.onCreate(); - CrashHandler.getInstance().registerGlobal(this); - CrashHandler.getInstance().registerPart(this); + //CrashHandler.getInstance().registerGlobal(this); + //CrashHandler.getInstance().registerPart(this); } public static void write(InputStream input, OutputStream output) throws IOException { @@ -252,7 +252,7 @@ public class GlobalApplication extends Application { private static String getKernel() { try { - return GlobalApplication.toString(new FileInputStream("/proc/version")).trim(); + return App.toString(new FileInputStream("/proc/version")).trim(); } catch (Throwable e) { return e.getMessage(); } @@ -331,4 +331,4 @@ public class GlobalApplication extends Application { restart(); } } -} \ No newline at end of file +} diff --git a/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/MainActivity.java b/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/MainActivity.java index de5c892..a912358 100644 --- a/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/MainActivity.java +++ b/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/MainActivity.java @@ -3,17 +3,26 @@ package cc.winboll.studio.androidxdemo; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import cc.winboll.studio.libappbase.LogView; public class MainActivity extends AppCompatActivity { - + + LogView mLogView; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - + Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar); setSupportActionBar(toolbar); - + + mLogView = findViewById(R.id.logview); } - -} \ No newline at end of file + + @Override + protected void onResume() { + super.onResume(); + mLogView.start(); + } +} diff --git a/androidxdemo/src/main/res/layout/activity_main.xml b/androidxdemo/src/main/res/layout/activity_main.xml index 034ba40..12fdc32 100644 --- a/androidxdemo/src/main/res/layout/activity_main.xml +++ b/androidxdemo/src/main/res/layout/activity_main.xml @@ -34,5 +34,18 @@ + + + + + + diff --git a/androidxdemo/src/main/res/values/styles.xml b/androidxdemo/src/main/res/values/styles.xml index 0eb88fe..a70e242 100644 --- a/androidxdemo/src/main/res/values/styles.xml +++ b/androidxdemo/src/main/res/values/styles.xml @@ -1,7 +1,7 @@ - diff --git a/build.gradle b/build.gradle index 5c58ec9..2af2782 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,12 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { + // Nexus Maven 库地址 + // "WinBoll Release" + maven { url "https://nexus.winboll.cc/repository/maven-public/" } + // "WinBoll Snapshot" + maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" } + maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/google/' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' } @@ -9,17 +15,10 @@ buildscript { maven { url "https://jitpack.io" } mavenCentral() google() - - // Nexus Maven 库地址 - // "WinBoll Release" - maven { url "https://nexus.winboll.cc/repository/maven-public/" } - // "WinBoll Snapshot" - maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" } - mavenLocal() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.1' + classpath 'com.android.tools.build:gradle:7.2.1' // 对应 compileSdkVersion 32 // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -27,6 +26,12 @@ buildscript { allprojects { repositories { + // Nexus Maven 库地址 + // "WinBoll Release" + maven { url "https://nexus.winboll.cc/repository/maven-public/" } + // "WinBoll Snapshot" + maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" } + maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/google/' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' } @@ -35,13 +40,6 @@ allprojects { maven { url "https://jitpack.io" } mavenCentral() google() - - // Nexus Maven 库地址 - // "WinBoll Release" - maven { url "https://nexus.winboll.cc/repository/maven-public/" } - // "WinBoll Snapshot" - maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" } - mavenLocal() } ext { diff --git a/gradle.properties-android-demo b/gradle.properties-android-demo index 7d87c1c..cb39be8 100644 --- a/gradle.properties-android-demo +++ b/gradle.properties-android-demo @@ -17,3 +17,5 @@ org.gradle.jvmargs=-Xmx2048m android.useAndroidX=false # Automatically convert third-party libraries to use AndroidX android.enableJetifier=false +# 保持与旧版Gradle插件的兼容 +android.disableAutomaticComponentCreation=true diff --git a/gradle.properties-androidx-demo b/gradle.properties-androidx-demo index 2f26404..74b1f76 100644 --- a/gradle.properties-androidx-demo +++ b/gradle.properties-androidx-demo @@ -17,3 +17,5 @@ org.gradle.jvmargs=-Xmx2048m android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true +# 保持与旧版Gradle插件的兼容 +android.disableAutomaticComponentCreation=true diff --git a/libappbase/build.gradle b/libappbase/build.gradle index aa9ecad..2071132 100644 --- a/libappbase/build.gradle +++ b/libappbase/build.gradle @@ -4,11 +4,12 @@ apply from: '../.winboll/winboll_lib_build.gradle' apply from: '../.winboll/winboll_lint_build.gradle' android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" + + compileSdkVersion 32 + buildToolsVersion "32.0.0" defaultConfig { - minSdkVersion 26 + minSdkVersion 24 targetSdkVersion 29 } buildTypes { @@ -21,4 +22,25 @@ android { dependencies { api fileTree(dir: 'libs', include: ['*.jar']) + + // Android 类库 + //api 'com.android.support:appcompat-v7:28.0.0' + api('com.android.support:appcompat-v7:28.0.0'){ + //exclude group: "com.android.support", module: "support-vector-drawable" + exclude group: "com.android.support:animated-vector-drawable:28.0.0" + } + // https://mvnrepository.com/artifact/com.android.support/support-compat + //api 'com.android.support:support-compat:28.0.0' // 保留原有依赖(可选) + // https://mvnrepository.com/artifact/com.android.support/support-v4 + api 'com.android.support:support-v4:28.0.0' + // https://mvnrepository.com/artifact/com.android.support/support-media-compat + api 'com.android.support:support-media-compat:28.0.0' + // https://mvnrepository.com/artifact/com.android.support/support-core-utils + api 'com.android.support:support-core-utils:28.0.0' + // https://mvnrepository.com/artifact/com.android.support/support-core-ui + api 'com.android.support:support-core-ui:28.0.0' + // https://mvnrepository.com/artifact/com.android.support/support-fragment + api 'com.android.support:support-fragment:28.0.0' + // https://mvnrepository.com/artifact/com.android.support/recyclerview-v7 + api 'com.android.support:recyclerview-v7:28.0.0' } diff --git a/libappbase/build.properties b/libappbase/build.properties index 436c82c..cfd7c17 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Mon Mar 17 09:51:04 HKT 2025 -stageCount=4 +#Sat Mar 29 08:43:25 HKT 2025 +stageCount=1 libraryProject=libappbase -baseVersion=15.0 -publishVersion=15.0.3 +baseVersion=15.2 +publishVersion=15.2.0 buildCount=0 -baseBetaVersion=15.0.4 +baseBetaVersion=15.2.1 diff --git a/libappbase/src/main/AndroidManifest.xml b/libappbase/src/main/AndroidManifest.xml index 69597ba..dee879c 100644 --- a/libappbase/src/main/AndroidManifest.xml +++ b/libappbase/src/main/AndroidManifest.xml @@ -9,19 +9,33 @@ + + + + android:launchMode="singleInstance" + android:process=":CrashActivity"/> + android:launchMode="singleInstance" + android:process=":GlobalCrashActivity"/> - + + + - + @@ -75,7 +91,9 @@ - + @@ -87,4 +105,4 @@ - \ No newline at end of file + diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/APPBaseModel.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/APPBaseModel.java new file mode 100644 index 0000000..a71f1d1 --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/APPBaseModel.java @@ -0,0 +1,73 @@ +package cc.winboll.studio.libappbase; + +/** + * @Author ZhanGSKen@AliYun.Com + * @Date 2025/03/25 02:52:46 + * @Describe 基础应用数据模型 + */ +import android.util.JsonReader; +import android.util.JsonWriter; +import cc.winboll.studio.libappbase.BaseBean; +import java.io.IOException; + +public class APPBaseModel extends BaseBean { + + public static final String TAG = "APPBaseModel"; + + // 应用是否处于正在调试状态 + // + boolean isDebuging = false; + + public APPBaseModel() { + this.isDebuging = false; + } + + public APPBaseModel(boolean isDebuging) { + this.isDebuging = isDebuging; + } + + public void setIsDebuging(boolean isDebuging) { + this.isDebuging = isDebuging; + } + + public boolean isDebuging() { + return isDebuging; + } + + @Override + public String getName() { + return APPBaseModel.class.getName(); + } + + @Override + public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { + super.writeThisToJsonWriter(jsonWriter); + jsonWriter.name("isDebuging").value(isDebuging()); + } + + @Override + public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException { + if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { + if (name.equals("isDebuging")) { + setIsDebuging(jsonReader.nextBoolean()); + } else { + return false; + } + } + return true; + } + + @Override + public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { + jsonReader.beginObject(); + while (jsonReader.hasNext()) { + String name = jsonReader.nextName(); + if (!initObjectsFromJsonReader(jsonReader, name)) { + jsonReader.skipValue(); + } + } + // 结束 JSON 对象 + jsonReader.endObject(); + return this; + } +} 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 50564f0..5bfb740 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/CrashHandler.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/CrashHandler.java @@ -339,6 +339,7 @@ public final class CrashHandler { int padding = dp2px(16); message.setPadding(padding, padding, padding, padding); message.setText(mLog); + message.setTextColor(Color.BLACK); message.setTextIsSelectable(true); } hw.addView(message); 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 a5d77d1..240c13f 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java @@ -7,75 +7,84 @@ 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.os.Handler; import android.os.Looper; import cc.winboll.studio.libappbase.utils.ToastUtils; +import cc.winboll.studio.libappbase.winboll.WinBollActivityManager; +import cc.winboll.studio.libappbase.winboll.MyActivityLifecycleCallbacks; public class GlobalApplication extends Application { public static final String TAG = "GlobalApplication"; - final static String PREFS = GlobalApplication.class.getName() + "PREFS"; - final static String PREFS_ISDEBUGING = "PREFS_ISDEBUGING"; - - - private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper()); - + volatile static GlobalApplication _GlobalApplication; // 是否处于调试状态 volatile static boolean isDebuging = false; + MyActivityLifecycleCallbacks mMyActivityLifecycleCallbacks; - public static void setIsDebuging(Context context, boolean isDebuging) { - GlobalApplication.isDebuging = isDebuging; - // 获取SharedPreferences实例 - SharedPreferences sharedPreferences = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE); - // 获取编辑器 - SharedPreferences.Editor editor = sharedPreferences.edit(); - // 保存数据 - editor.putBoolean(PREFS_ISDEBUGING, GlobalApplication.isDebuging); - // 提交更改 - editor.apply(); + public static void setIsDebuging(boolean isDebuging) { + if (_GlobalApplication != null) { + GlobalApplication.isDebuging = isDebuging; + APPBaseModel.saveBeanToFile(getAPPBaseModelFilePath(), new APPBaseModel(isDebuging)); + } + } + + public static GlobalApplication getInstance() { + return _GlobalApplication; + } + + static String getAPPBaseModelFilePath() { + return _GlobalApplication.getDataDir().getPath() + "/APPBaseModel.json"; } public static boolean isDebuging() { return isDebuging; } - @Override - public Context getApplicationContext() { - return super.getApplicationContext(); - } - - public Application getApplication() { - return this; + public static WinBollActivityManager getWinBollActivityManager() { + return WinBollActivityManager.getInstance(_GlobalApplication); } @Override public void onCreate() { super.onCreate(); - //GlobalApplication.isDebuging = true; - //GlobalApplication.setIsDebuging(this, true); + // 保存初始实例 + _GlobalApplication = this; + + setIsDebuging(true); + // 添加日志模块 LogUtils.init(this); //LogUtils.setLogLevel(LogUtils.LOG_LEVEL.Debug); //LogUtils.setTAGListEnable(GlobalApplication.TAG, true); //LogUtils.setALlTAGListEnable(true); //LogUtils.d(TAG, "LogUtils init"); - // 设置应用异常处理窗口 CrashHandler.init(this); - - // 设置应用调试状态 - //SharedPreferences sharedPreferences = getSharedPreferences(PREFS, Context.MODE_PRIVATE); - //GlobalApplication.isDebuging = sharedPreferences.getBoolean(PREFS_ISDEBUGING, GlobalApplication.isDebuging); - // 初始化 Toast 框架 ToastUtils.init(this); - // 设置 Toast 布局样式 - //ToastUtils.setView(R.layout.toast_custom_view); - //ToastUtils.setStyle(new WhiteToastStyle()); - //ToastUtils.setGravity(Gravity.BOTTOM, 0, 200); + + // 应用保存的调试标志 + APPBaseModel appBaseModel = APPBaseModel.loadBeanFromFile(getAPPBaseModelFilePath(), APPBaseModel.class); + if (appBaseModel == null) { + setIsDebuging(false); + } else { + setIsDebuging(appBaseModel.isDebuging()); + } + + getWinBollActivityManager().setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Service); + // 注册窗口回调监听 + mMyActivityLifecycleCallbacks = new MyActivityLifecycleCallbacks(); + registerActivityLifecycleCallbacks(mMyActivityLifecycleCallbacks); + } + + + @Override + public void onTerminate() { + super.onTerminate(); + // 注销回调(非必须,但建议释放资源) + unregisterActivityLifecycleCallbacks(mMyActivityLifecycleCallbacks); } public static String getAppName(Context context) { diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashActivity.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashActivity.java index 8163acf..dbf8c8a 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashActivity.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashActivity.java @@ -4,26 +4,20 @@ package cc.winboll.studio.libappbase; * @Author ZhanGSKen@AliYun.Com * @Date 2025/02/11 00:14:05 */ -import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.content.res.TypedArray; -import android.graphics.Color; import android.net.Uri; import android.os.Bundle; -import android.text.SpannableString; -import android.text.style.ForegroundColorSpan; +import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; -import android.widget.LinearLayout; -import android.widget.TextView; import android.widget.Toast; import cc.winboll.studio.libappbase.R; -public final class GlobalCrashActivity extends Activity implements MenuItem.OnMenuItemClickListener { +public final class GlobalCrashActivity extends AppCompatActivity implements MenuItem.OnMenuItemClickListener { private static final int MENUITEM_COPY = 0; private static final int MENUITEM_RESTART = 1; @@ -45,10 +39,10 @@ public final class GlobalCrashActivity extends Activity implements MenuItem.OnMe setContentView(R.layout.activity_globalcrash); mGlobalCrashReportView = findViewById(R.id.activityglobalcrashGlobalCrashReportView1); mGlobalCrashReportView.setReport(mLog); - setActionBar(mGlobalCrashReportView.getToolbar()); + setSupportActionBar(mGlobalCrashReportView.getToolbar()); - getActionBar().setTitle(CrashHandler.TITTLE); - getActionBar().setSubtitle(GlobalApplication.getAppName(getApplicationContext())); + getSupportActionBar().setTitle(CrashHandler.TITTLE); + getSupportActionBar().setSubtitle(GlobalApplication.getAppName(getApplicationContext())); } @Override @@ -98,20 +92,4 @@ public final class GlobalCrashActivity extends Activity implements MenuItem.OnMe mGlobalCrashReportView.updateMenuStyle(); return true; } - - void joinQQGroup(String key) { - // 创建Intent - Intent intent = new Intent(); - // 设置动作 - intent.setAction("android.intent.action.VIEW"); - // 设置数据为网址的URI - Uri content_url = Uri.parse("https://www.winboll.cc"); - intent.setData(content_url); - // 添加标志 - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - // 设置类名和活动名 - intent.setClassName("com.android.browser", "com.android.browser.BrowserActivity"); - // 启动Activity - startActivity(intent); - } } diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashReportView.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashReportView.java index efdfb3a..533e5da 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashReportView.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashReportView.java @@ -8,6 +8,7 @@ package cc.winboll.studio.libappbase; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; +import android.support.v7.widget.Toolbar; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.util.AttributeSet; @@ -16,7 +17,6 @@ import android.view.MenuItem; import android.widget.LinearLayout; import android.widget.TextView; import cc.winboll.studio.libappbase.R; -import android.widget.Toolbar; public class GlobalCrashReportView extends LinearLayout { diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogView.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogView.java index 9b8721d..345489f 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogView.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogView.java @@ -10,6 +10,8 @@ import android.content.ClipboardManager; import android.content.Context; import android.os.Handler; import android.os.Message; +import android.text.Editable; +import android.text.TextWatcher; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -18,6 +20,8 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.BaseAdapter; import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.HorizontalScrollView; import android.widget.RelativeLayout; import android.widget.ScrollView; import android.widget.Spinner; @@ -42,6 +46,7 @@ public class LogView extends RelativeLayout { Context mContext; ScrollView mScrollView; TextView mTextView; + EditText metTagSearch; CheckBox mSelectableCheckBox; CheckBox mSelectAllTAGCheckBox; TAGListAdapter mTAGListAdapter; @@ -107,9 +112,41 @@ public class LogView extends RelativeLayout { // mScrollView = findViewById(cc.winboll.studio.libappbase.R.id.viewlogScrollViewLog); mTextView = findViewById(cc.winboll.studio.libappbase.R.id.viewlogTextViewLog); + metTagSearch = findViewById(cc.winboll.studio.libappbase.R.id.tagsearch_et); // 获取Log Level spinner实例 mLogLevelSpinner = findViewById(cc.winboll.studio.libappbase.R.id.viewlogSpinner1); + metTagSearch.addTextChangedListener(new TextWatcher() { + + @Override + public void afterTextChanged(Editable editable) { + } + + @Override + public void beforeTextChanged(CharSequence charSequence, int p, int p1, int p2) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + LogUtils.d(TAG, s.toString()); + if (s.length() > 0) { + scrollToTag(s.toString()); + } else { + HorizontalScrollView hsRoot = findViewById(R.id.viewlogHorizontalScrollView1); + hsRoot.smoothScrollTo(0, 0); + mListViewTags.resetScrollToStart(); + } +// mListViewTags.postDelayed(new Runnable() { +// @Override +// public void run() { +// mListViewTags.scrollToItem(5); +// } +// }, 100); + } + // 其他方法留空或按需实现 + }); + + (findViewById(cc.winboll.studio.libappbase.R.id.viewlogButtonClean)).setOnClickListener(new View.OnClickListener(){ @Override @@ -233,6 +270,60 @@ public class LogView extends RelativeLayout { scrollLogUp(); } + public void scrollToTag(final String prefix) { + if (mTAGListAdapter == null || prefix == null || prefix.length() == 0) { + LogUtils.d(TAG, "参数为空,无法滚动"); + return; + } + + final List itemList = mTAGListAdapter.getItemList(); + + mListViewTags.post(new Runnable() { + @Override + public void run() { + // 查找匹配的标签位置 + int targetPosition = -1; + + for (int i = 0; i < itemList.size(); i++) { + String tag = itemList.get(i).getTag(); + if (tag != null && tag.toLowerCase().startsWith(prefix.toLowerCase())) { + targetPosition = i; + + break; + } + } + + if (targetPosition != -1) { + // 优化滚动逻辑 + //mListViewTags.setSelection(targetPosition); + //mListViewTags.invalidateViews(); // 强制刷新所有可见项 + + // 单独刷新目标视图 +// View targetView = mListViewTags.getChildAt(targetPosition); +// if (targetView != null) { +// targetView.requestLayout(); +// targetView.requestFocus(); +// } + + final int scrollPosition = targetPosition; + + // 延迟滚动确保布局完成 + mListViewTags.postDelayed(new Runnable() { + @Override + public void run() { + LogUtils.d(TAG, String.format("scrollPosition %d", scrollPosition)); + mListViewTags.scrollToItem(scrollPosition); + } + }, 100); + } else { + LogUtils.d(TAG, "未找到匹配的标签前缀:" + prefix); + } + } + }); + } + + + class LogViewHandler extends Handler { final static int MSG_LOGVIEW_UPDATE = 0; @@ -300,6 +391,30 @@ public class LogView extends RelativeLayout { public void setChecked(boolean checked) { isChecked = checked; } + + // getter/setter... + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TAGItemModel that = (TAGItemModel) o; + // 手动处理空值比较(Java 6 不支持 Objects.equals) + if (tag == null) { + return that.tag == null; + } else { + return tag.equals(that.tag); + } + } + + @Override + public int hashCode() { + return tag == null ? 0 : tag.hashCode(); // 手动处理空值 + } } @@ -314,7 +429,11 @@ public class LogView extends RelativeLayout { mapOrigin = map; loadMap(mapOrigin); } - + + public List getItemList() { + return itemList; + } + @Override public int getCount() { return itemList.size(); @@ -344,7 +463,7 @@ public class LogView extends RelativeLayout { loadMap(mapOrigin); super.notifyDataSetChanged(); } - + @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/dialogs/YesNoAlertDialog.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/dialogs/YesNoAlertDialog.java new file mode 100644 index 0000000..84446d7 --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/dialogs/YesNoAlertDialog.java @@ -0,0 +1,60 @@ +package cc.winboll.studio.libappbase.dialogs; + +/** + * @Author ZhanGSKen@AliYun.Com + * @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/libappbase/src/main/java/cc/winboll/studio/libappbase/views/HorizontalListView.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/views/HorizontalListView.java index 782d8fa..083b3a4 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/views/HorizontalListView.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/views/HorizontalListView.java @@ -9,23 +9,34 @@ import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.ListView; +import android.widget.Scroller; import cc.winboll.studio.libappbase.LogUtils; public class HorizontalListView extends ListView { - public static final String TAG = "HorizontalListView"; - int verticalOffset = 0; + private int verticalOffset = 0; + private Scroller scroller; + private int totalWidth; public HorizontalListView(Context context) { super(context); + init(); } public HorizontalListView(Context context, AttributeSet attrs) { super(context, attrs); + init(); } public HorizontalListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + init(); + } + + private void init() { + scroller = new Scroller(getContext()); + setHorizontalScrollBarEnabled(true); + setVerticalScrollBarEnabled(false); } public void setVerticalOffset(int verticalOffset) { @@ -38,28 +49,81 @@ public class HorizontalListView extends ListView { int childCount = getChildCount(); int left = getPaddingLeft(); int viewHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); - LogUtils.d(TAG, String.format("HorizontalListView的高度 %d", viewHeight)); - + totalWidth = left; + for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - // 计算每个子视图的宽度和高度 int width = child.getMeasuredWidth(); int height = child.getMeasuredHeight(); - //LogUtils.d(TAG, String.format("child : width %d , height %d", width, height)); - // 设置子视图的位置,实现水平布局 - child.layout(left, verticalOffset, left + width, verticalOffset + height); left += width; } + totalWidth = left + getPaddingRight(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); - //super.onMeasure(widthMeasureSpec, newHeightMeasureSpec); int newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); - //LogUtils.d(TAG, String.format("newWidthMeasureSpec %d, newHeightMeasureSpec %d", newWidthMeasureSpec, newHeightMeasureSpec)); super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec); } + + @Override + public void computeScroll() { + if (scroller.computeScrollOffset()) { + scrollTo(scroller.getCurrX(), scroller.getCurrY()); + postInvalidate(); + } + } + + public void smoothScrollTo(int x, int y) { + int dx = x - getScrollX(); + int dy = y - getScrollY(); + scroller.startScroll(getScrollX(), getScrollY(), dx, dy, 300); // 300ms平滑动画 + invalidate(); + } + + @Override + public int computeHorizontalScrollRange() { + return totalWidth; + } + + @Override + public int computeHorizontalScrollOffset() { + return getScrollX(); + } + + @Override + public int computeHorizontalScrollExtent() { + return getWidth(); + } + + public void scrollToItem(int position) { + if (position < 0 || position >= getChildCount()) { + LogUtils.d(TAG, "无效的position: " + position); + return; + } + + View targetView = getChildAt(position); + int targetLeft = targetView.getLeft(); + int scrollX = targetLeft - getPaddingLeft(); + + // 修正最大滚动范围计算 + int maxScrollX = totalWidth; + scrollX = Math.max(0, Math.min(scrollX, maxScrollX)); + + // 强制重新布局和绘制 + requestLayout(); + invalidateViews(); + smoothScrollTo(scrollX, 0); + LogUtils.d(TAG, String.format("滚动到position: %d, scrollX: %d computeHorizontalScrollRange() %d", position, scrollX, computeHorizontalScrollRange())); + } + + public void resetScrollToStart() { + // 强制重新布局和绘制 + requestLayout(); + invalidateViews(); + smoothScrollTo(0, 0); + } } diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/IWinBollActivity.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/IWinBollActivity.java new file mode 100644 index 0000000..47c8b59 --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/IWinBollActivity.java @@ -0,0 +1,18 @@ +package cc.winboll.studio.libappbase.winboll; + +/** + * @Author ZhanGSKen@AliYun.Com + * @Date 2025/03/24 08:23:40 + * @Describe WinBoll 活动窗口通用接口 + */ +import android.app.Activity; +import android.widget.Toolbar; + +public interface IWinBollActivity { + + public static final String TAG = "IWinBollActivity"; + + // 获取活动窗口 + abstract public Activity getActivity(); + abstract public String getTag(); +} diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/LogActivity.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/LogActivity.java new file mode 100644 index 0000000..c739743 --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/LogActivity.java @@ -0,0 +1,48 @@ +package cc.winboll.studio.libappbase.winboll; + +/** + * @Author ZhanGSKen@AliYun.Com + * @Date 2025/03/25 20:34:47 + * @Describe 应用日志窗口 + */ +import android.app.Activity; +import android.os.Build; +import android.os.Bundle; +import android.view.WindowManager; +import cc.winboll.studio.libappbase.LogView; +import cc.winboll.studio.libappbase.R; +import cc.winboll.studio.libappbase.utils.ToastUtils; + +public class LogActivity extends Activity implements IWinBollActivity { + + public static final String TAG = "LogActivity"; + + LogView mLogView; + + @Override + public Activity getActivity() { + return this; + } + + @Override + public String getTag() { + return TAG; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_log); + //ToastUtils.show("LogActivity onCreate"); + + mLogView = findViewById(R.id.logview); + mLogView.start(); + } + + @Override + protected void onResume() { + super.onResume(); + mLogView.start(); + } + +} diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/MyActivityLifecycleCallbacks.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/MyActivityLifecycleCallbacks.java new file mode 100644 index 0000000..774ab8b --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/MyActivityLifecycleCallbacks.java @@ -0,0 +1,98 @@ +package cc.winboll.studio.libappbase.winboll; + +/** + * @Author ZhanGSKen@AliYun.Com + * @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/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/WinBollActivityManager.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/WinBollActivityManager.java new file mode 100644 index 0000000..e6c3255 --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/WinBollActivityManager.java @@ -0,0 +1,356 @@ +package cc.winboll.studio.libappbase.winboll; + +/** + * @Author ZhanGSKen@AliYun.Com + * @Date 2025/03/24 08:25:43 + * @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.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; + +public class WinBollActivityManager { + + public static final String TAG = "WinBollActivityManager"; + public static final String EXTRA_TAG = "EXTRA_TAG"; + + public static enum WinBollUI_TYPE { + Aplication, // 退出应用后,保持最近任务栏任务记录主窗口 + Service // 退出应用后,清理所有最近任务栏任务记录窗口 + }; + + // 应用类型标志 + static volatile WinBollUI_TYPE _mWinBollUI_TYPE = WinBollUI_TYPE.Service; + + GlobalApplication mGlobalApplication; + static volatile WinBollActivityManager _Instance; + static volatile Map _mapIWinBollList; + volatile IWinBollActivity mFirstIWinBollActivity; + + WinBollActivityManager(GlobalApplication application) { + mGlobalApplication = application; + _mapIWinBollList = new HashMap(); + } + + public static synchronized WinBollActivityManager getInstance(GlobalApplication application) { + LogUtils.d(TAG, "getInstance"); + if (_Instance == null) { + LogUtils.d(TAG, "_Instance == null"); + _Instance = new WinBollActivityManager(application); + } + return _Instance; + } + + // + // 设置 WinBoll 应用 UI 类型 + // + public synchronized static void setWinBollUI_TYPE(WinBollUI_TYPE mWinBollUI_TYPE) { + _mWinBollUI_TYPE = mWinBollUI_TYPE; + } + + // + // 获取 WinBoll 应用 UI 类型 + // + public synchronized static WinBollUI_TYPE getWinBollUI_TYPE() { + return _mWinBollUI_TYPE; + } + + // + // 把Activity添加到管理中 + // + public void add(T iWinBoll) { + String tag = ((IWinBollActivity)iWinBoll).getTag(); + LogUtils.d(TAG, String.format("add(T iWinBoll) tag is %s", tag)); + if (isActive(tag)) { + LogUtils.d(TAG, String.format("isActive(tag) is true, tag : %s.", tag)); + } else { + // 设置起始活动窗口,以便最后退出时提问 + if (mFirstIWinBollActivity == null && _mapIWinBollList.size() == 0) { + LogUtils.d(TAG, "Set firstIWinBollActivity, iWinBoll.getTag() is %s" + iWinBoll.getTag()); + mFirstIWinBollActivity = iWinBoll; + } + + // 添加到活动窗口列表 + _mapIWinBollList.put(iWinBoll.getTag(), iWinBoll); + LogUtils.d(TAG, String.format("Add activity : %s\n_mapActivityList.size() : %d", iWinBoll.getTag(), _mapIWinBollList.size())); + } + } + + + // + // activity: 为 null 时, + // intent.putExtra 函数 EXTRA_TAG 参数为 tag + // activity: 不为 null 时, + // intent.putExtra 函数 "tag" 参数为 activity.getTag() + // + public void startWinBollActivity(Context context, Class clazz) { + try { + // 如果窗口已存在就重启窗口 + String tag = ((IWinBollActivity)clazz.newInstance()).getTag(); + LogUtils.d(TAG, String.format("startWinBollActivity(Context context, Class clazz) tag is %s", tag)); + if (isActive(tag)) { + resumeActivity(context, tag); + return; + } + //ToastUtils.show("startWinBollActivity(Context context, Class clazz)"); + + // 新建一个任务窗口 + Intent intent = new Intent(context, clazz); + //打开多任务窗口 flags + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRA_TAG, tag); + context.startActivity(intent); + } catch (InstantiationException | IllegalAccessException e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + } + + public void startWinBollActivity(Context context, Intent intent, Class clazz) { + try { + // 如果窗口已存在就重启窗口 + String tag = ((IWinBollActivity)clazz.newInstance()).getTag(); + LogUtils.d(TAG, String.format("startWinBollActivity(Context context, Intent intent, Class clazz) tag is %s", tag)); + if (isActive(tag)) { + resumeActivity(context, tag); + return; + } + + // 新建一个任务窗口 + //Intent intent = new Intent(context, clazz); + //打开多任务窗口 flags + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRA_TAG, tag); + context.startActivity(intent); + } catch (InstantiationException | IllegalAccessException e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + } + + public void startLogActivity(Context context) { + // 如果窗口已存在就重启窗口 + String tag = LogActivity.TAG; + if (isActive(tag)) { + resumeActivity(context, tag); + return; + } + + // 新建一个任务窗口 + 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); + } + + public boolean isFirstIWinBollActivity(IWinBollActivity iWinBollActivity) { + return mFirstIWinBollActivity != null && mFirstIWinBollActivity == iWinBollActivity; + } + + // + // 判断 tag绑定的 MyActivity是否存在 + // + public boolean isActive(String tag) { + LogUtils.d(TAG, String.format("isActive(String tag) tag is %s", tag)); + //printIWinBollListInfo(); + IWinBollActivity iWinBoll = getIWinBoll(tag); + if (iWinBoll != null) { + //LogUtils.d(TAG, "isActive(...) activity != null tag " + tag); + //ToastUtils.show("activity != null tag " + tag); + //判断是否为 BaseActivity,如果已经销毁,则移除 + if (iWinBoll.getActivity().isFinishing() || iWinBoll.getActivity().isDestroyed()) { + _mapIWinBollList.remove(iWinBoll.getTag()); + //_mWinBollActivityList.remove(activity); + LogUtils.d(TAG, String.format("isActive(...) remove activity.\ntag : %s", tag)); + return false; + } else { + LogUtils.d(TAG, String.format("isActive(...) activity is exist.\ntag : %s", tag)); + return true; + } + } else { + LogUtils.d(TAG, String.format("isActive(...) iWinBoll is null tag by %s", tag)); + return false; + } + } + + static IWinBollActivity getIWinBoll(String tag) { + LogUtils.d(TAG, String.format("getIWinBoll(String tag) %s", tag)); + return _mapIWinBollList.get(tag); + } + + // + // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台 + // + public void resumeActivity(Context context, String tag) { + LogUtils.d(TAG, "resumeActivity(Context context, String tag)"); + T iWinBoll = (T)getIWinBoll(tag); + LogUtils.d(TAG, String.format("iWinBoll.getTag() %s", iWinBoll.getTag())); + //LogUtils.d(TAG, "activity " + activity.getTag()); + if (iWinBoll != null && !iWinBoll.getActivity().isFinishing() && !iWinBoll.getActivity().isDestroyed()) { + resumeActivity(context, iWinBoll); + } + } + + // + // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台 + // + public void resumeActivity(Context context, T iWinBoll) { + LogUtils.d(TAG, "resumeActivity(Context context, T iWinBoll)"); + ActivityManager am = (ActivityManager) mGlobalApplication.getSystemService(Context.ACTIVITY_SERVICE); + //返回启动它的根任务(home 或者 MainActivity) + Intent intent = new Intent(mGlobalApplication, iWinBoll.getClass()); + TaskStackBuilder stackBuilder = TaskStackBuilder.create(mGlobalApplication); + stackBuilder.addNextIntentWithParentStack(intent); + stackBuilder.startActivities(); + //moveTaskToFront(YourTaskId, 0); + //ToastUtils.show("resumeActivity am.moveTaskToFront"); + LogUtils.d(TAG, String.format("iWinBoll.getActivity().getTaskId() %d", iWinBoll.getActivity().getTaskId())); + am.moveTaskToFront(iWinBoll.getActivity().getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION); + LogUtils.d(TAG, "am.moveTaskToFront"); + } + + + // + // 结束所有 Activity + // + public void finishAll() { + try { + for (String key : _mapIWinBollList.keySet()) { + //System.out.println("Key: " + key + ", Value: " + _mapActivityList.get(key)); + IWinBollActivity iWinBoll = _mapIWinBollList.get(key); + //ToastUtils.show("finishAll() activity"); + if (iWinBoll != null && !iWinBoll.getActivity().isFinishing() && !iWinBoll.getActivity().isDestroyed()) { + //ToastUtils.show("activity != null ..."); + if (getWinBollUI_TYPE() == WinBollUI_TYPE.Service) { + // 结束窗口和最近任务栏, 建议前台服务类应用使用,可以方便用户再次调用 UI 操作。 + iWinBoll.getActivity().finishAndRemoveTask(); + //ToastUtils.show("finishAll() activity.finishAndRemoveTask();"); + } else if (getWinBollUI_TYPE() == WinBollUI_TYPE.Aplication) { + // 结束窗口保留最近任务栏,建议前台服务类应用使用,可以保持应用的系统自觉性。 + iWinBoll.getActivity().finish(); + //ToastUtils.show("finishAll() activity.finish();"); + } else { + LogUtils.d(TAG, "WinBollApplication.WinBollUI_TYPE error."); + //ToastUtils.show("WinBollApplication.WinBollUI_TYPE error."); + } + } + } + } catch (Exception e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + } + + // + // 结束指定Activity + // + public void finish(T iWinBoll) { + try { + if (iWinBoll != null && !iWinBoll.getActivity().isFinishing() && !iWinBoll.getActivity().isDestroyed()) { + //根据tag 移除 MyActivity + //String tag= activity.getTag(); + //_mWinBollActivityList.remove(tag); + //ToastUtils.show("remove"); + //ToastUtils.show("_mWinBollActivityArrayMap.size() " + Integer.toString(_mWinBollActivityArrayMap.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] >> 直接关闭当前窗口 + //LogUtils.d(TAG, "finish no yet."); + IWinBollActivity preIWinBoll = getPreIWinBoll(iWinBoll); + iWinBoll.getActivity().finish(); + if (preIWinBoll != null) { + resumeActivity(mGlobalApplication, preIWinBoll); + } + } + + } catch (Exception e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + } + + // + // 获取窗口队列中的前一个窗口 + // + IWinBollActivity getPreIWinBoll(IWinBollActivity iWinBoll) { + try { + boolean bingo = false; + IWinBollActivity preIWinBoll = null; + for (Map.Entry entity : _mapIWinBollList.entrySet()) { + if (entity.getKey().equals(iWinBoll.getTag())) { + bingo = true; + //LogUtils.d(TAG, "bingo"); + break; + } + preIWinBoll = entity.getValue(); + } + + if (bingo) { + return preIWinBoll; + } + } catch (Exception e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + + return null; + } + + // + // 从管理列表中移除管理项 + // + public boolean registeRemove(T activity) { + IWinBollActivity iWinBollTest = _mapIWinBollList.get(activity.getTag()); + if (iWinBollTest != null) { + _mapIWinBollList.remove(activity.getTag()); + return true; + } + return false; + } + + // + // 打印管理列表项列表里的信息 + // + public static void printIWinBollListInfo() { + //LogUtils.d(TAG, "printAvtivityListInfo"); + if (!_mapIWinBollList.isEmpty()) { + StringBuilder sb = new StringBuilder("Map entries : " + Integer.toString(_mapIWinBollList.size())); + Iterator> iterator = _mapIWinBollList.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."); + } + } +} diff --git a/libappbase/src/main/res/drawable/bg_shadow.xml b/libappbase/src/main/res/drawable/bg_shadow.xml index f4d1bd7..6d3d898 100644 --- a/libappbase/src/main/res/drawable/bg_shadow.xml +++ b/libappbase/src/main/res/drawable/bg_shadow.xml @@ -10,8 +10,8 @@ + android:endColor="#0F000000" + android:startColor="#0F000000" /> + + + + + + + + + + + + + + + + + + diff --git a/libappbase/src/main/res/drawable/ic_dev_connected.xml b/libappbase/src/main/res/drawable/ic_dev_connected.xml new file mode 100644 index 0000000..1fb2f26 --- /dev/null +++ b/libappbase/src/main/res/drawable/ic_dev_connected.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/libappbase/src/main/res/drawable/ic_dev_disconnected.xml b/libappbase/src/main/res/drawable/ic_dev_disconnected.xml new file mode 100644 index 0000000..4267975 --- /dev/null +++ b/libappbase/src/main/res/drawable/ic_dev_disconnected.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/libappbase/src/main/res/drawable/ic_email.xml b/libappbase/src/main/res/drawable/ic_email.xml new file mode 100644 index 0000000..d526b26 --- /dev/null +++ b/libappbase/src/main/res/drawable/ic_email.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/libappbase/src/main/res/drawable/ic_email_alert.xml b/libappbase/src/main/res/drawable/ic_email_alert.xml new file mode 100644 index 0000000..f3ed613 --- /dev/null +++ b/libappbase/src/main/res/drawable/ic_email_alert.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/libappbase/src/main/res/drawable/ic_launcher.xml b/libappbase/src/main/res/drawable/ic_launcher.xml index 568f996..21f28b1 100644 --- a/libappbase/src/main/res/drawable/ic_launcher.xml +++ b/libappbase/src/main/res/drawable/ic_launcher.xml @@ -1,13 +1,13 @@ - - + + android:drawable="@drawable/ic_winboll_logo"> + diff --git a/libappbase/src/main/res/drawable/ic_launcher_background.xml b/libappbase/src/main/res/drawable/ic_launcher_background.xml index f63ec09..9486190 100644 --- a/libappbase/src/main/res/drawable/ic_launcher_background.xml +++ b/libappbase/src/main/res/drawable/ic_launcher_background.xml @@ -5,7 +5,7 @@ android:viewportWidth="108" android:viewportHeight="108"> + + + + diff --git a/libappbase/src/main/res/drawable/ic_winboll_help.xml b/libappbase/src/main/res/drawable/ic_winboll_help.xml new file mode 100644 index 0000000..564175f --- /dev/null +++ b/libappbase/src/main/res/drawable/ic_winboll_help.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/libappbase/src/main/res/drawable/ic_winboll_log.xml b/libappbase/src/main/res/drawable/ic_winboll_log.xml new file mode 100644 index 0000000..011f2b2 --- /dev/null +++ b/libappbase/src/main/res/drawable/ic_winboll_log.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/libappbase/src/main/res/drawable/ic_winboll_logo.xml b/libappbase/src/main/res/drawable/ic_winboll_logo.xml new file mode 100644 index 0000000..ea28987 --- /dev/null +++ b/libappbase/src/main/res/drawable/ic_winboll_logo.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/libappbase/src/main/res/drawable/ic_winboll_point.xml b/libappbase/src/main/res/drawable/ic_winboll_point.xml new file mode 100644 index 0000000..48028cc --- /dev/null +++ b/libappbase/src/main/res/drawable/ic_winboll_point.xml @@ -0,0 +1,20 @@ + + + + diff --git a/libappbase/src/main/res/drawable/ic_winbollbeta.xml b/libappbase/src/main/res/drawable/ic_winbollbeta.xml new file mode 100644 index 0000000..06fa725 --- /dev/null +++ b/libappbase/src/main/res/drawable/ic_winbollbeta.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/libappbase/src/main/res/drawable/shape_gradient.xml b/libappbase/src/main/res/drawable/shape_gradient.xml new file mode 100644 index 0000000..c164fe9 --- /dev/null +++ b/libappbase/src/main/res/drawable/shape_gradient.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/libappbase/src/main/res/drawable/view_border.xml b/libappbase/src/main/res/drawable/view_border.xml new file mode 100644 index 0000000..58b374a --- /dev/null +++ b/libappbase/src/main/res/drawable/view_border.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/libappbase/src/main/res/layout/activity_log.xml b/libappbase/src/main/res/layout/activity_log.xml index dfcde4f..a5f2ea2 100644 --- a/libappbase/src/main/res/layout/activity_log.xml +++ b/libappbase/src/main/res/layout/activity_log.xml @@ -1,16 +1,14 @@ + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> - + + diff --git a/libappbase/src/main/res/layout/view_globalcrashreport.xml b/libappbase/src/main/res/layout/view_globalcrashreport.xml index 1aff5e5..cda2c96 100644 --- a/libappbase/src/main/res/layout/view_globalcrashreport.xml +++ b/libappbase/src/main/res/layout/view_globalcrashreport.xml @@ -7,9 +7,9 @@ android:layout_height="match_parent" android:id="@+id/viewglobalcrashreportLinearLayout1"> -