diff --git a/.gitignore b/.gitignore
index 88afb93..5dea002 100644
--- a/.gitignore
+++ b/.gitignore
@@ -96,6 +96,7 @@ local.properties
## 忽略模块应用编译配置
/settings.gradle
+/gradle.properties
## 忽略 srv 纠结问题
/srv/
diff --git a/androiddemo/.gitignore b/androiddemo/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/androiddemo/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/androiddemo/app_update_description.txt b/androiddemo/app_update_description.txt
new file mode 100644
index 0000000..e69de29
diff --git a/androiddemo/build.gradle b/androiddemo/build.gradle
new file mode 100644
index 0000000..8357611
--- /dev/null
+++ b/androiddemo/build.gradle
@@ -0,0 +1,74 @@
+apply plugin: 'com.android.application'
+apply from: '../.winboll/winboll_app_build.gradle'
+apply from: '../.winboll/winboll_lint_build.gradle'
+
+def genVersionName(def versionName){
+ // 检查编译标志位配置
+ assert (winbollBuildProps['stageCount'] != null)
+ assert (winbollBuildProps['baseVersion'] != null)
+ // 保存基础版本号
+ winbollBuildProps.setProperty("baseVersion", "${versionName}");
+ //保存编译标志配置
+ FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
+ winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
+ fos.close();
+
+ // 返回编译版本号
+ return "${versionName}." + winbollBuildProps['stageCount']
+}
+
+android {
+ productFlavors {
+ beta {
+ }
+ stage {
+ }
+ }
+
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "cc.winboll.studio.androiddemo"
+ minSdkVersion 26
+ targetSdkVersion 29
+ versionCode 1
+ // versionName 更新后需要手动设置
+ // .winboll/winbollBuildProps.properties 文件的 stageCount=0
+ // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
+ versionName "1.0"
+ if(true) {
+ versionName = genVersionName("${versionName}")
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ api fileTree(dir: 'libs', include: ['*.jar'])
+
+ // 吐司类库
+ implementation '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'
+ // https://mvnrepository.com/artifact/com.android.support/support-compat
+ implementation 'com.android.support:support-compat:28.0.0'
+ // https://mvnrepository.com/artifact/com.android.support/support-media-compat
+ implementation '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'
+ // https://mvnrepository.com/artifact/com.android.support/support-core-ui
+ implementation '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'
+ // https://mvnrepository.com/artifact/com.android.support/recyclerview-v7
+ implementation 'com.android.support:recyclerview-v7:28.0.0'
+}
diff --git a/androiddemo/build.properties b/androiddemo/build.properties
new file mode 100644
index 0000000..4d98e2f
--- /dev/null
+++ b/androiddemo/build.properties
@@ -0,0 +1,8 @@
+#Created by .winboll/winboll_app_build.gradle
+#Tue Mar 11 18:02:14 GMT 2025
+stageCount=0
+libraryProject=
+baseVersion=1.0
+publishVersion=1.0.0
+buildCount=1
+baseBetaVersion=1.0.1
diff --git a/androiddemo/proguard-rules.pro b/androiddemo/proguard-rules.pro
new file mode 100644
index 0000000..64b4a05
--- /dev/null
+++ b/androiddemo/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/androiddemo/src/beta/AndroidManifest.xml b/androiddemo/src/beta/AndroidManifest.xml
new file mode 100644
index 0000000..ee78d9f
--- /dev/null
+++ b/androiddemo/src/beta/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/androiddemo/src/beta/res/values/strings.xml b/androiddemo/src/beta/res/values/strings.xml
new file mode 100644
index 0000000..e0c92e9
--- /dev/null
+++ b/androiddemo/src/beta/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+ Android Demo +
+
+
diff --git a/androiddemo/src/main/AndroidManifest.xml b/androiddemo/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..43eac9a
--- /dev/null
+++ b/androiddemo/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/androiddemo/src/main/java/cc/winboll/studio/androiddemo/GlobalApplication.java b/androiddemo/src/main/java/cc/winboll/studio/androiddemo/GlobalApplication.java
new file mode 100644
index 0000000..e13af9a
--- /dev/null
+++ b/androiddemo/src/main/java/cc/winboll/studio/androiddemo/GlobalApplication.java
@@ -0,0 +1,334 @@
+package cc.winboll.studio.androiddemo;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+import android.widget.HorizontalScrollView;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.Toast;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class GlobalApplication extends Application {
+
+ private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ CrashHandler.getInstance().registerGlobal(this);
+ CrashHandler.getInstance().registerPart(this);
+ }
+
+ public static void write(InputStream input, OutputStream output) throws IOException {
+ byte[] buf = new byte[1024 * 8];
+ int len;
+ while ((len = input.read(buf)) != -1) {
+ output.write(buf, 0, len);
+ }
+ }
+
+ public static void write(File file, byte[] data) throws IOException {
+ File parent = file.getParentFile();
+ if (parent != null && !parent.exists()) parent.mkdirs();
+
+ ByteArrayInputStream input = new ByteArrayInputStream(data);
+ FileOutputStream output = new FileOutputStream(file);
+ try {
+ write(input, output);
+ } finally {
+ closeIO(input, output);
+ }
+ }
+
+ public static String toString(InputStream input) throws IOException {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ write(input, output);
+ try {
+ return output.toString("UTF-8");
+ } finally {
+ closeIO(input, output);
+ }
+ }
+
+ public static void closeIO(Closeable... closeables) {
+ for (Closeable closeable : closeables) {
+ try {
+ if (closeable != null) closeable.close();
+ } catch (IOException ignored) {}
+ }
+ }
+
+ public static class CrashHandler {
+
+ public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
+
+ private static CrashHandler sInstance;
+
+ private PartCrashHandler mPartCrashHandler;
+
+ public static CrashHandler getInstance() {
+ if (sInstance == null) {
+ sInstance = new CrashHandler();
+ }
+ return sInstance;
+ }
+
+ public void registerGlobal(Context context) {
+ registerGlobal(context, null);
+ }
+
+ public void registerGlobal(Context context, String crashDir) {
+ Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl(context.getApplicationContext(), crashDir));
+ }
+
+ public void unregister() {
+ Thread.setDefaultUncaughtExceptionHandler(DEFAULT_UNCAUGHT_EXCEPTION_HANDLER);
+ }
+
+ public void registerPart(Context context) {
+ unregisterPart(context);
+ mPartCrashHandler = new PartCrashHandler(context.getApplicationContext());
+ MAIN_HANDLER.postAtFrontOfQueue(mPartCrashHandler);
+ }
+
+ public void unregisterPart(Context context) {
+ if (mPartCrashHandler != null) {
+ mPartCrashHandler.isRunning.set(false);
+ mPartCrashHandler = null;
+ }
+ }
+
+ private static class PartCrashHandler implements Runnable {
+
+ private final Context mContext;
+
+ public AtomicBoolean isRunning = new AtomicBoolean(true);
+
+ public PartCrashHandler(Context context) {
+ this.mContext = context;
+ }
+
+ @Override
+ public void run() {
+ while (isRunning.get()) {
+ try {
+ Looper.loop();
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ if (isRunning.get()) {
+ MAIN_HANDLER.post(new Runnable(){
+
+ @Override
+ public void run() {
+ Toast.makeText(mContext, e.toString(), Toast.LENGTH_LONG).show();
+ }
+ });
+ } else {
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException)e;
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler {
+
+ private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss");
+
+ private final Context mContext;
+
+ private final File mCrashDir;
+
+ public UncaughtExceptionHandlerImpl(Context context, String crashDir) {
+ this.mContext = context;
+ this.mCrashDir = TextUtils.isEmpty(crashDir) ? new File(mContext.getExternalCacheDir(), "crash") : new File(crashDir);
+ }
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable throwable) {
+ try {
+
+ String log = buildLog(throwable);
+ writeLog(log);
+
+ try {
+ Intent intent = new Intent(mContext, CrashActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Intent.EXTRA_TEXT, log);
+ mContext.startActivity(intent);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ writeLog(e.toString());
+ }
+
+ throwable.printStackTrace();
+ android.os.Process.killProcess(android.os.Process.myPid());
+ System.exit(0);
+
+ } catch (Throwable e) {
+ if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
+ }
+ }
+
+ private String buildLog(Throwable throwable) {
+ String time = DATE_FORMAT.format(new Date());
+
+ String versionName = "unknown";
+ long versionCode = 0;
+ try {
+ PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
+ versionName = packageInfo.versionName;
+ versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode() : packageInfo.versionCode;
+ } catch (Throwable ignored) {}
+
+ LinkedHashMap head = new LinkedHashMap();
+ head.put("Time Of Crash", time);
+ head.put("Device", String.format("%s, %s", Build.MANUFACTURER, Build.MODEL));
+ head.put("Android Version", String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT));
+ head.put("App Version", String.format("%s (%d)", versionName, versionCode));
+ head.put("Kernel", getKernel());
+ head.put("Support Abis", Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS != null ? Arrays.toString(Build.SUPPORTED_ABIS): "unknown");
+ head.put("Fingerprint", Build.FINGERPRINT);
+
+ StringBuilder builder = new StringBuilder();
+
+ for (String key : head.keySet()) {
+ if (builder.length() != 0) builder.append("\n");
+ builder.append(key);
+ builder.append(" : ");
+ builder.append(head.get(key));
+ }
+
+ builder.append("\n\n");
+ builder.append(Log.getStackTraceString(throwable));
+
+ return builder.toString();
+ }
+
+ private void writeLog(String log) {
+ String time = DATE_FORMAT.format(new Date());
+ File file = new File(mCrashDir, "crash_" + time + ".txt");
+ try {
+ write(file, log.getBytes("UTF-8"));
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static String getKernel() {
+ try {
+ return GlobalApplication.toString(new FileInputStream("/proc/version")).trim();
+ } catch (Throwable e) {
+ return e.getMessage();
+ }
+ }
+ }
+ }
+
+ public static final class CrashActivity extends Activity {
+
+ private String mLog;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setTheme(android.R.style.Theme_DeviceDefault);
+ setTitle("App Crash");
+
+ mLog = getIntent().getStringExtra(Intent.EXTRA_TEXT);
+
+ ScrollView contentView = new ScrollView(this);
+ contentView.setFillViewport(true);
+
+ HorizontalScrollView horizontalScrollView = new HorizontalScrollView(this);
+
+ TextView textView = new TextView(this);
+ int padding = dp2px(16);
+ textView.setPadding(padding, padding, padding, padding);
+ textView.setText(mLog);
+ textView.setTextIsSelectable(true);
+ textView.setTypeface(Typeface.DEFAULT);
+ textView.setLinksClickable(true);
+
+ horizontalScrollView.addView(textView);
+ contentView.addView(horizontalScrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+
+ setContentView(contentView);
+ }
+
+ private void restart() {
+ Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
+ if (intent != null) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+ finish();
+ android.os.Process.killProcess(android.os.Process.myPid());
+ System.exit(0);
+ }
+
+ private static int dp2px(float dpValue) {
+ final float scale = Resources.getSystem().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, android.R.id.copy, 0, android.R.string.copy)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.copy:
+ ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onBackPressed() {
+ restart();
+ }
+ }
+}
\ No newline at end of file
diff --git a/androiddemo/src/main/java/cc/winboll/studio/androiddemo/MainActivity.java b/androiddemo/src/main/java/cc/winboll/studio/androiddemo/MainActivity.java
new file mode 100644
index 0000000..dcef375
--- /dev/null
+++ b/androiddemo/src/main/java/cc/winboll/studio/androiddemo/MainActivity.java
@@ -0,0 +1,15 @@
+package cc.winboll.studio.androiddemo;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ }
+
+}
\ No newline at end of file
diff --git a/androiddemo/src/main/res/drawable/ic_launcher.png b/androiddemo/src/main/res/drawable/ic_launcher.png
new file mode 100644
index 0000000..b824ebd
Binary files /dev/null and b/androiddemo/src/main/res/drawable/ic_launcher.png differ
diff --git a/androiddemo/src/main/res/layout/activity_main.xml b/androiddemo/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..f8b9a89
--- /dev/null
+++ b/androiddemo/src/main/res/layout/activity_main.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/androiddemo/src/main/res/values-v21/styles.xml b/androiddemo/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..0aed032
--- /dev/null
+++ b/androiddemo/src/main/res/values-v21/styles.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/androiddemo/src/main/res/values/colors.xml b/androiddemo/src/main/res/values/colors.xml
new file mode 100644
index 0000000..294809a
--- /dev/null
+++ b/androiddemo/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #009688
+ #00796B
+ #FF9800
+
\ No newline at end of file
diff --git a/androiddemo/src/main/res/values/strings.xml b/androiddemo/src/main/res/values/strings.xml
new file mode 100644
index 0000000..21acbbd
--- /dev/null
+++ b/androiddemo/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ Android Demo
+
diff --git a/androiddemo/src/main/res/values/styles.xml b/androiddemo/src/main/res/values/styles.xml
new file mode 100644
index 0000000..6799c28
--- /dev/null
+++ b/androiddemo/src/main/res/values/styles.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/androiddemo/src/stage/AndroidManifest.xml b/androiddemo/src/stage/AndroidManifest.xml
new file mode 100644
index 0000000..ee78d9f
--- /dev/null
+++ b/androiddemo/src/stage/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/androiddemo/src/stage/res/values/strings.xml b/androiddemo/src/stage/res/values/strings.xml
new file mode 100644
index 0000000..ace0c41
--- /dev/null
+++ b/androiddemo/src/stage/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/androidxdemo/.gitignore b/androidxdemo/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/androidxdemo/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/androidxdemo/app_update_description.txt b/androidxdemo/app_update_description.txt
new file mode 100644
index 0000000..e69de29
diff --git a/androidxdemo/build.gradle b/androidxdemo/build.gradle
new file mode 100644
index 0000000..fcf214f
--- /dev/null
+++ b/androidxdemo/build.gradle
@@ -0,0 +1,76 @@
+apply plugin: 'com.android.application'
+apply from: '../.winboll/winboll_app_build.gradle'
+apply from: '../.winboll/winboll_lint_build.gradle'
+
+def genVersionName(def versionName){
+ // 检查编译标志位配置
+ assert (winbollBuildProps['stageCount'] != null)
+ assert (winbollBuildProps['baseVersion'] != null)
+ // 保存基础版本号
+ winbollBuildProps.setProperty("baseVersion", "${versionName}");
+ //保存编译标志配置
+ FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
+ winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
+ fos.close();
+
+ // 返回编译版本号
+ return "${versionName}." + winbollBuildProps['stageCount']
+}
+
+android {
+ productFlavors {
+ beta {
+ }
+ stage {
+ }
+ }
+
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "cc.winboll.studio.androidxdemo"
+ minSdkVersion 26
+ targetSdkVersion 29
+ versionCode 1
+ // versionName 更新后需要手动设置
+ // .winboll/winbollBuildProps.properties 文件的 stageCount=0
+ // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
+ versionName "1.0"
+ if(true) {
+ versionName = genVersionName("${versionName}")
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ api fileTree(dir: 'libs', include: ['*.jar'])
+
+ // SSH
+ implementation 'com.jcraft:jsch:0.1.55'
+ // Html 解析
+ implementation 'org.jsoup:jsoup:1.13.1'
+ // 二维码类库
+ implementation 'com.google.zxing:core:3.4.1'
+ implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
+ // 应用介绍页类库
+ implementation 'io.github.medyo:android-about-page:2.0.0'
+ // 吐司类库
+ implementation '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'
+}
diff --git a/androidxdemo/build.properties b/androidxdemo/build.properties
new file mode 100644
index 0000000..8d115d9
--- /dev/null
+++ b/androidxdemo/build.properties
@@ -0,0 +1,8 @@
+#Created by .winboll/winboll_app_build.gradle
+#Tue Mar 11 18:25:43 GMT 2025
+stageCount=0
+libraryProject=
+baseVersion=1.0
+publishVersion=1.0.0
+buildCount=4
+baseBetaVersion=1.0.1
diff --git a/androidxdemo/proguard-rules.pro b/androidxdemo/proguard-rules.pro
new file mode 100644
index 0000000..64b4a05
--- /dev/null
+++ b/androidxdemo/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/androidxdemo/src/beta/AndroidManifest.xml b/androidxdemo/src/beta/AndroidManifest.xml
new file mode 100644
index 0000000..ee78d9f
--- /dev/null
+++ b/androidxdemo/src/beta/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/androidxdemo/src/beta/res/values/strings.xml b/androidxdemo/src/beta/res/values/strings.xml
new file mode 100644
index 0000000..bf50bcf
--- /dev/null
+++ b/androidxdemo/src/beta/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ AndroidX Demo +
+
+
diff --git a/androidxdemo/src/main/AndroidManifest.xml b/androidxdemo/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6d0c707
--- /dev/null
+++ b/androidxdemo/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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/GlobalApplication.java
new file mode 100644
index 0000000..7abaf88
--- /dev/null
+++ b/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/GlobalApplication.java
@@ -0,0 +1,334 @@
+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;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+import android.widget.HorizontalScrollView;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.Toast;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class GlobalApplication extends Application {
+
+ private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ CrashHandler.getInstance().registerGlobal(this);
+ CrashHandler.getInstance().registerPart(this);
+ }
+
+ public static void write(InputStream input, OutputStream output) throws IOException {
+ byte[] buf = new byte[1024 * 8];
+ int len;
+ while ((len = input.read(buf)) != -1) {
+ output.write(buf, 0, len);
+ }
+ }
+
+ public static void write(File file, byte[] data) throws IOException {
+ File parent = file.getParentFile();
+ if (parent != null && !parent.exists()) parent.mkdirs();
+
+ ByteArrayInputStream input = new ByteArrayInputStream(data);
+ FileOutputStream output = new FileOutputStream(file);
+ try {
+ write(input, output);
+ } finally {
+ closeIO(input, output);
+ }
+ }
+
+ public static String toString(InputStream input) throws IOException {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ write(input, output);
+ try {
+ return output.toString("UTF-8");
+ } finally {
+ closeIO(input, output);
+ }
+ }
+
+ public static void closeIO(Closeable... closeables) {
+ for (Closeable closeable : closeables) {
+ try {
+ if (closeable != null) closeable.close();
+ } catch (IOException ignored) {}
+ }
+ }
+
+ public static class CrashHandler {
+
+ public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
+
+ private static CrashHandler sInstance;
+
+ private PartCrashHandler mPartCrashHandler;
+
+ public static CrashHandler getInstance() {
+ if (sInstance == null) {
+ sInstance = new CrashHandler();
+ }
+ return sInstance;
+ }
+
+ public void registerGlobal(Context context) {
+ registerGlobal(context, null);
+ }
+
+ public void registerGlobal(Context context, String crashDir) {
+ Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl(context.getApplicationContext(), crashDir));
+ }
+
+ public void unregister() {
+ Thread.setDefaultUncaughtExceptionHandler(DEFAULT_UNCAUGHT_EXCEPTION_HANDLER);
+ }
+
+ public void registerPart(Context context) {
+ unregisterPart(context);
+ mPartCrashHandler = new PartCrashHandler(context.getApplicationContext());
+ MAIN_HANDLER.postAtFrontOfQueue(mPartCrashHandler);
+ }
+
+ public void unregisterPart(Context context) {
+ if (mPartCrashHandler != null) {
+ mPartCrashHandler.isRunning.set(false);
+ mPartCrashHandler = null;
+ }
+ }
+
+ private static class PartCrashHandler implements Runnable {
+
+ private final Context mContext;
+
+ public AtomicBoolean isRunning = new AtomicBoolean(true);
+
+ public PartCrashHandler(Context context) {
+ this.mContext = context;
+ }
+
+ @Override
+ public void run() {
+ while (isRunning.get()) {
+ try {
+ Looper.loop();
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ if (isRunning.get()) {
+ MAIN_HANDLER.post(new Runnable(){
+
+ @Override
+ public void run() {
+ Toast.makeText(mContext, e.toString(), Toast.LENGTH_LONG).show();
+ }
+ });
+ } else {
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException)e;
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler {
+
+ private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss");
+
+ private final Context mContext;
+
+ private final File mCrashDir;
+
+ public UncaughtExceptionHandlerImpl(Context context, String crashDir) {
+ this.mContext = context;
+ this.mCrashDir = TextUtils.isEmpty(crashDir) ? new File(mContext.getExternalCacheDir(), "crash") : new File(crashDir);
+ }
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable throwable) {
+ try {
+
+ String log = buildLog(throwable);
+ writeLog(log);
+
+ try {
+ Intent intent = new Intent(mContext, CrashActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Intent.EXTRA_TEXT, log);
+ mContext.startActivity(intent);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ writeLog(e.toString());
+ }
+
+ throwable.printStackTrace();
+ android.os.Process.killProcess(android.os.Process.myPid());
+ System.exit(0);
+
+ } catch (Throwable e) {
+ if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
+ }
+ }
+
+ private String buildLog(Throwable throwable) {
+ String time = DATE_FORMAT.format(new Date());
+
+ String versionName = "unknown";
+ long versionCode = 0;
+ try {
+ PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
+ versionName = packageInfo.versionName;
+ versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode() : packageInfo.versionCode;
+ } catch (Throwable ignored) {}
+
+ LinkedHashMap head = new LinkedHashMap();
+ head.put("Time Of Crash", time);
+ head.put("Device", String.format("%s, %s", Build.MANUFACTURER, Build.MODEL));
+ head.put("Android Version", String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT));
+ head.put("App Version", String.format("%s (%d)", versionName, versionCode));
+ head.put("Kernel", getKernel());
+ head.put("Support Abis", Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS != null ? Arrays.toString(Build.SUPPORTED_ABIS): "unknown");
+ head.put("Fingerprint", Build.FINGERPRINT);
+
+ StringBuilder builder = new StringBuilder();
+
+ for (String key : head.keySet()) {
+ if (builder.length() != 0) builder.append("\n");
+ builder.append(key);
+ builder.append(" : ");
+ builder.append(head.get(key));
+ }
+
+ builder.append("\n\n");
+ builder.append(Log.getStackTraceString(throwable));
+
+ return builder.toString();
+ }
+
+ private void writeLog(String log) {
+ String time = DATE_FORMAT.format(new Date());
+ File file = new File(mCrashDir, "crash_" + time + ".txt");
+ try {
+ write(file, log.getBytes("UTF-8"));
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static String getKernel() {
+ try {
+ return GlobalApplication.toString(new FileInputStream("/proc/version")).trim();
+ } catch (Throwable e) {
+ return e.getMessage();
+ }
+ }
+ }
+ }
+
+ public static final class CrashActivity extends Activity {
+
+ private String mLog;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setTheme(android.R.style.Theme_DeviceDefault);
+ setTitle("App Crash");
+
+ mLog = getIntent().getStringExtra(Intent.EXTRA_TEXT);
+
+ ScrollView contentView = new ScrollView(this);
+ contentView.setFillViewport(true);
+
+ HorizontalScrollView horizontalScrollView = new HorizontalScrollView(this);
+
+ TextView textView = new TextView(this);
+ int padding = dp2px(16);
+ textView.setPadding(padding, padding, padding, padding);
+ textView.setText(mLog);
+ textView.setTextIsSelectable(true);
+ textView.setTypeface(Typeface.DEFAULT);
+ textView.setLinksClickable(true);
+
+ horizontalScrollView.addView(textView);
+ contentView.addView(horizontalScrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+
+ setContentView(contentView);
+ }
+
+ private void restart() {
+ Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
+ if (intent != null) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+ finish();
+ android.os.Process.killProcess(android.os.Process.myPid());
+ System.exit(0);
+ }
+
+ private static int dp2px(float dpValue) {
+ final float scale = Resources.getSystem().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, android.R.id.copy, 0, android.R.string.copy)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.copy:
+ ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onBackPressed() {
+ 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
new file mode 100644
index 0000000..de5c892
--- /dev/null
+++ b/androidxdemo/src/main/java/cc/winboll/studio/androidxdemo/MainActivity.java
@@ -0,0 +1,19 @@
+package cc.winboll.studio.androidxdemo;
+
+import android.os.Bundle;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+public class MainActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ }
+
+}
\ No newline at end of file
diff --git a/androidxdemo/src/main/res/drawable-v24/ic_launcher_foreground.xml b/androidxdemo/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..c7bd21d
--- /dev/null
+++ b/androidxdemo/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/androidxdemo/src/main/res/drawable/ic_launcher_background.xml b/androidxdemo/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..d5fccc5
--- /dev/null
+++ b/androidxdemo/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/androidxdemo/src/main/res/layout/activity_main.xml b/androidxdemo/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..034ba40
--- /dev/null
+++ b/androidxdemo/src/main/res/layout/activity_main.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/androidxdemo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher.png b/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a2f5908
Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher_round.png b/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..1b52399
Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher.png b/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..ff10afd
Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher_round.png b/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..115a4c7
Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher.png b/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..dcd3cd8
Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..459ca60
Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher.png b/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..8ca12fe
Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..8e19b41
Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b824ebd
Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4c19a13
Binary files /dev/null and b/androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/androidxdemo/src/main/res/values/colors.xml b/androidxdemo/src/main/res/values/colors.xml
new file mode 100644
index 0000000..479769a
--- /dev/null
+++ b/androidxdemo/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #009688
+ #00796B
+ #FF9800
+
\ No newline at end of file
diff --git a/androidxdemo/src/main/res/values/strings.xml b/androidxdemo/src/main/res/values/strings.xml
new file mode 100644
index 0000000..6aea12c
--- /dev/null
+++ b/androidxdemo/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ AndroidX Demo
+
+
diff --git a/androidxdemo/src/main/res/values/styles.xml b/androidxdemo/src/main/res/values/styles.xml
new file mode 100644
index 0000000..0eb88fe
--- /dev/null
+++ b/androidxdemo/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/androidxdemo/src/stage/AndroidManifest.xml b/androidxdemo/src/stage/AndroidManifest.xml
new file mode 100644
index 0000000..ee78d9f
--- /dev/null
+++ b/androidxdemo/src/stage/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/androidxdemo/src/stage/res/values/strings.xml b/androidxdemo/src/stage/res/values/strings.xml
new file mode 100644
index 0000000..ace0c41
--- /dev/null
+++ b/androidxdemo/src/stage/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/appbase/build.gradle b/appbase/build.gradle
index 6193aa5..4632155 100644
--- a/appbase/build.gradle
+++ b/appbase/build.gradle
@@ -18,8 +18,8 @@ def genVersionName(def versionName){
}
android {
- compileSdkVersion 30
- buildToolsVersion "30.0.3"
+ compileSdkVersion 32
+ buildToolsVersion "32.0.0"
defaultConfig {
applicationId "cc.winboll.studio.appbase"
@@ -29,7 +29,7 @@ android {
// versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
- versionName "2.0"
+ versionName "15.0"
if(true) {
versionName = genVersionName("${versionName}")
}
@@ -45,5 +45,5 @@ android {
dependencies {
api project(':libappbase')
- api fileTree(dir: 'libs', include: ['*.jar'])
+ api fileTree(dir: 'libs', include: ['*.jar'])
}
diff --git a/appbase/build.properties b/appbase/build.properties
index 9e59591..b0c800f 100644
--- a/appbase/build.properties
+++ b/appbase/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Tue Feb 25 16:51:17 HKT 2025
-stageCount=3
+#Mon Mar 24 21:28:41 GMT 2025
+stageCount=11
libraryProject=libappbase
-baseVersion=2.0
-publishVersion=2.0.2
-buildCount=0
-baseBetaVersion=2.0.3
+baseVersion=15.0
+publishVersion=15.0.10
+buildCount=12
+baseBetaVersion=15.0.11
diff --git a/appbase/src/main/AndroidManifest.xml b/appbase/src/main/AndroidManifest.xml
index ed6e233..19f89ca 100644
--- a/appbase/src/main/AndroidManifest.xml
+++ b/appbase/src/main/AndroidManifest.xml
@@ -7,12 +7,13 @@
android:name=".App"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
- android:theme="@style/AppTheme"
+ android:theme="@style/MyAPPBaseTheme"
android:resizeableActivity="true">
@@ -49,24 +50,38 @@
android:name=".services.MainService"
android:exported="true"/>
+
+
+
+
+
+
-
-
+
+
+
+
+
+
-
+
-
+
-
+
@@ -91,6 +106,10 @@
android:name="android.max_aspect"
android:value="4.0"/>
+
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/App.java b/appbase/src/main/java/cc/winboll/studio/appbase/App.java
index 84d0424..74f9bbc 100644
--- a/appbase/src/main/java/cc/winboll/studio/appbase/App.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/App.java
@@ -6,22 +6,22 @@ package cc.winboll.studio.appbase;
* @Describe APPbase 应用类
*/
import cc.winboll.studio.libappbase.GlobalApplication;
-import cc.winboll.studio.libappbase.SOSCSBroadcastReceiver;
import android.content.IntentFilter;
+import cc.winboll.studio.libappbase.sos.SOSCenterServiceReceiver;
+import cc.winboll.studio.libappbase.sos.SOS;
public class App extends GlobalApplication {
public static final String TAG = "App";
- SOSCSBroadcastReceiver mSOSCSBroadcastReceiver;
+ SOSCenterServiceReceiver mSOSCenterServiceReceiver;
@Override
public void onCreate() {
super.onCreate();
- GlobalApplication.setIsDebuging(this, BuildConfig.DEBUG);
- mSOSCSBroadcastReceiver = new SOSCSBroadcastReceiver();
+ mSOSCenterServiceReceiver = new SOSCenterServiceReceiver();
IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(SOSCSBroadcastReceiver.ACTION_SOS);
- registerReceiver(mSOSCSBroadcastReceiver, intentFilter);
+ intentFilter.addAction(SOS.ACTION_SOS);
+ registerReceiver(mSOSCenterServiceReceiver, intentFilter);
}
}
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java b/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java
index 3e4cac0..b2b5842 100644
--- a/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java
@@ -1,28 +1,41 @@
package cc.winboll.studio.appbase;
+import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
+import android.widget.Toolbar;
import cc.winboll.studio.appbase.R;
+import cc.winboll.studio.appbase.activities.NewActivity;
import cc.winboll.studio.appbase.services.MainService;
+import cc.winboll.studio.appbase.services.TestDemoBindService;
+import cc.winboll.studio.appbase.services.TestDemoService;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
-import cc.winboll.studio.libappbase.SOS;
-import cc.winboll.studio.libappbase.SimpleOperateSignalCenterService;
-import cc.winboll.studio.libappbase.bean.APPSOSBean;
-import cc.winboll.studio.libappbase.services.TestService;
+import cc.winboll.studio.libappbase.sos.SOS;
+import cc.winboll.studio.libappbase.utils.ToastUtils;
import cc.winboll.studio.libappbase.widgets.StatusWidget;
-import com.hjq.toast.ToastUtils;
+import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
+import cc.winboll.studio.libappbase.winboll.WinBollActivityManager;
-public class MainActivity extends AppCompatActivity {
+public class MainActivity extends Activity implements IWinBollActivity {
public static final String TAG = "MainActivity";
+
+ @Override
+ public Activity getActivity() {
+ return this;
+ }
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ Toolbar mToolbar;
LogView mLogView;
@Override
@@ -31,14 +44,17 @@ public class MainActivity extends AppCompatActivity {
ToastUtils.show("onCreate");
setContentView(R.layout.activity_main);
- Toolbar toolbar = findViewById(R.id.activitymainToolbar1);
- setSupportActionBar(toolbar);
+ mToolbar = findViewById(R.id.toolbar);
+ setActionBar(mToolbar);
CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
mLogView = findViewById(R.id.activitymainLogView1);
- if (GlobalApplication.isDebuging()) { mLogView.start(); }
+ if (GlobalApplication.isDebuging()) {
+ mLogView.start();
+ ToastUtils.show("LogView start.");
+ }
}
@Override
@@ -57,7 +73,8 @@ public class MainActivity extends AppCompatActivity {
}
public void onSwitchDebugMode(View view) {
- GlobalApplication.setIsDebuging(this, ((CheckBox)view).isChecked());
+ boolean isDebuging = ((CheckBox)view).isChecked();
+ GlobalApplication.setIsDebuging(isDebuging);
}
public void onStartCenter(View view) {
@@ -68,17 +85,17 @@ public class MainActivity extends AppCompatActivity {
MainService.stopMainService(this);
}
- public void onTestStopWithoutSettingEnable(View view) {
- LogUtils.d(TAG, "onTestStopWithoutSettingEnable");
- stopService(new Intent(this, SimpleOperateSignalCenterService.class));
+ public void onTestStopMainServiceWithoutSettingEnable(View view) {
+ LogUtils.d(TAG, "onTestStopMainServiceWithoutSettingEnable");
+ stopService(new Intent(this, MainService.class));
}
- public void onTestStartWithString(View view) {
- LogUtils.d(TAG, "onTestStartWithString");
+ public void onTestUseComponentStartService(View view) {
+ LogUtils.d(TAG, "onTestUseComponentStartService");
// 目标服务的包名和类名
String packageName = this.getPackageName();
- String serviceClassName = SimpleOperateSignalCenterService.class.getName();
+ String serviceClassName = TestDemoService.class.getName();
// 构建Intent
Intent intentService = new Intent();
@@ -87,30 +104,59 @@ public class MainActivity extends AppCompatActivity {
startService(intentService);
}
- public void onSOS(View view) {
- Intent intent = new Intent(this, TestService.class);
+ public void onTestDemoServiceSOS(View view) {
+ Intent intent = new Intent(this, TestDemoService.class);
stopService(intent);
- SOS.sosWinBollService(this, new APPSOSBean(getPackageName(), TestService.class.getName()));
+ if (App.isDebuging()) {
+ SOS.sosToAppBaseBeta(this, TestDemoService.class.getName());
+ } else {
+ SOS.sosToAppBase(this, TestDemoService.class.getName());
+ }
}
- public void onStartTestService(View view) {
- Intent intent = new Intent(this, TestService.class);
- intent.setAction(SOS.ACTION_SERVICE_ENABLE);
+ public void onSartTestDemoService(View view) {
+ Intent intent = new Intent(this, TestDemoService.class);
+ intent.setAction(TestDemoService.ACTION_ENABLE);
startService(intent);
}
- public void onStopTestService(View view) {
- Intent intent = new Intent(this, TestService.class);
- intent.setAction(SOS.ACTION_SERVICE_DISABLE);
+ public void onStopTestDemoService(View view) {
+ Intent intent = new Intent(this, TestDemoService.class);
+ intent.setAction(TestDemoService.ACTION_DISABLE);
startService(intent);
-
- Intent intentStop = new Intent(this, TestService.class);
+
+ Intent intentStop = new Intent(this, TestDemoService.class);
stopService(intentStop);
}
- public void onStopTestServiceNoSettings(View view) {
- Intent intent = new Intent(this, TestService.class);
+ public void onStopTestDemoServiceNoSettings(View view) {
+ Intent intent = new Intent(this, TestDemoService.class);
stopService(intent);
}
+
+ public void onSartTestDemoBindService(View view) {
+ Intent intent = new Intent(this, TestDemoBindService.class);
+ intent.setAction(TestDemoBindService.ACTION_ENABLE);
+ startService(intent);
+
+ }
+
+ public void onStopTestDemoBindService(View view) {
+ Intent intent = new Intent(this, TestDemoBindService.class);
+ intent.setAction(TestDemoBindService.ACTION_DISABLE);
+ startService(intent);
+
+ Intent intentStop = new Intent(this, TestDemoBindService.class);
+ stopService(intentStop);
+ }
+
+ public void onStopTestDemoBindServiceNoSettings(View view) {
+ Intent intent = new Intent(this, TestDemoBindService.class);
+ stopService(intent);
+ }
+
+ public void onTestOpenNewActivity(View view) {
+ WinBollActivityManager.getInstance(this).startWinBollActivity(this, NewActivity.class);
+ }
}
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/MyTileService.java b/appbase/src/main/java/cc/winboll/studio/appbase/MyTileService.java
index ab3b255..3f3fcca 100644
--- a/appbase/src/main/java/cc/winboll/studio/appbase/MyTileService.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/MyTileService.java
@@ -7,7 +7,7 @@ package cc.winboll.studio.appbase;
import android.content.Context;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
-import cc.winboll.studio.appbase.beans.MainServiceBean;
+import cc.winboll.studio.appbase.models.MainServiceBean;
import cc.winboll.studio.appbase.services.MainService;
public class MyTileService extends TileService {
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/activities/NewActivity.java b/appbase/src/main/java/cc/winboll/studio/appbase/activities/NewActivity.java
new file mode 100644
index 0000000..289be86
--- /dev/null
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/activities/NewActivity.java
@@ -0,0 +1,41 @@
+package cc.winboll.studio.appbase.activities;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import cc.winboll.studio.appbase.R;
+import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
+import cc.winboll.studio.libappbase.winboll.WinBollActivityManager;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/25 05:04:22
+ */
+public class NewActivity extends Activity implements IWinBollActivity {
+
+ public static final String TAG = "NewActivity";
+
+ @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_new);
+ }
+
+ public void onCloseThisActivity(View view) {
+ WinBollActivityManager.getInstance(this).finish(this);
+ }
+
+ public void onCloseAllActivity(View view) {
+ WinBollActivityManager.getInstance(this).finishAll();
+ }
+}
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/bean/SimpleOperateSignalCenterServiceBean.java b/appbase/src/main/java/cc/winboll/studio/appbase/models/MainServiceBean.java
similarity index 81%
rename from libappbase/src/main/java/cc/winboll/studio/libappbase/bean/SimpleOperateSignalCenterServiceBean.java
rename to appbase/src/main/java/cc/winboll/studio/appbase/models/MainServiceBean.java
index d6d3f00..5df134e 100644
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/bean/SimpleOperateSignalCenterServiceBean.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/models/MainServiceBean.java
@@ -1,20 +1,21 @@
-package cc.winboll.studio.libappbase.bean;
+package cc.winboll.studio.appbase.models;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/13 07:06:13
+ */
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
-/**
- * @Author ZhanGSKen@AliYun.Com
- * @Date 2025/02/13 04:27:42
- */
-public class SimpleOperateSignalCenterServiceBean extends BaseBean {
+public class MainServiceBean extends BaseBean {
- public static final String TAG = "SimpleOperateSignalCenterServiceBean";
+ public static final String TAG = "MainServiceBean";
boolean isEnable;
-
- public SimpleOperateSignalCenterServiceBean() {
+
+ public MainServiceBean() {
this.isEnable = false;
}
@@ -28,7 +29,7 @@ public class SimpleOperateSignalCenterServiceBean extends BaseBean {
@Override
public String getName() {
- return SimpleOperateSignalCenterServiceBean.class.getName();
+ return MainServiceBean.class.getName();
}
@Override
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/bean/TestServiceBean.java b/appbase/src/main/java/cc/winboll/studio/appbase/models/TestDemoBindServiceBean.java
similarity index 87%
rename from libappbase/src/main/java/cc/winboll/studio/libappbase/bean/TestServiceBean.java
rename to appbase/src/main/java/cc/winboll/studio/appbase/models/TestDemoBindServiceBean.java
index 30d8cfe..f702494 100644
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/bean/TestServiceBean.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/models/TestDemoBindServiceBean.java
@@ -1,8 +1,8 @@
-package cc.winboll.studio.libappbase.bean;
+package cc.winboll.studio.appbase.models;
/**
* @Author ZhanGSKen@AliYun.Com
- * @Date 2025/02/19 13:34:52
+ * @Date 2025/03/07 12:47:22
* @Describe TestServiceBean
*/
import android.util.JsonReader;
@@ -10,13 +10,13 @@ import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
-public class TestServiceBean extends BaseBean {
+public class TestDemoBindServiceBean extends BaseBean {
public static final String TAG = "TestServiceBean";
boolean isEnable;
- public TestServiceBean() {
+ public TestDemoBindServiceBean() {
this.isEnable = false;
}
@@ -30,7 +30,7 @@ public class TestServiceBean extends BaseBean {
@Override
public String getName() {
- return TestServiceBean.class.getName();
+ return TestDemoBindServiceBean.class.getName();
}
@Override
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/models/TestDemoServiceBean.java b/appbase/src/main/java/cc/winboll/studio/appbase/models/TestDemoServiceBean.java
new file mode 100644
index 0000000..90f7518
--- /dev/null
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/models/TestDemoServiceBean.java
@@ -0,0 +1,68 @@
+package cc.winboll.studio.appbase.models;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/07 12:49:21
+ * @Describe TestDemoServiceBean
+ */
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import cc.winboll.studio.libappbase.BaseBean;
+import java.io.IOException;
+
+public class TestDemoServiceBean extends BaseBean {
+
+ public static final String TAG = "TestDemoServiceBean";
+
+ boolean isEnable;
+
+ public TestDemoServiceBean() {
+ this.isEnable = false;
+ }
+
+ public void setIsEnable(boolean isEnable) {
+ this.isEnable = isEnable;
+ }
+
+ public boolean isEnable() {
+ return isEnable;
+ }
+
+ @Override
+ public String getName() {
+ return TestDemoServiceBean.class.getName();
+ }
+
+ @Override
+ public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
+ super.writeThisToJsonWriter(jsonWriter);
+ jsonWriter.name("isEnable").value(isEnable());
+
+ }
+
+ @Override
+ public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
+ if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
+ if (name.equals("isEnable")) {
+ setIsEnable(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/appbase/src/main/java/cc/winboll/studio/appbase/beans/SOSReportBean.java b/appbase/src/main/java/cc/winboll/studio/appbase/models/WinBollNewsBean.java
similarity index 64%
rename from appbase/src/main/java/cc/winboll/studio/appbase/beans/SOSReportBean.java
rename to appbase/src/main/java/cc/winboll/studio/appbase/models/WinBollNewsBean.java
index d579482..66ee6b6 100644
--- a/appbase/src/main/java/cc/winboll/studio/appbase/beans/SOSReportBean.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/models/WinBollNewsBean.java
@@ -1,4 +1,4 @@
-package cc.winboll.studio.appbase.beans;
+package cc.winboll.studio.appbase.models;
/**
* @Author ZhanGSKen@AliYun.Com
@@ -10,45 +10,45 @@ import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
-public class SOSReportBean extends BaseBean {
+public class WinBollNewsBean extends BaseBean {
- public static final String TAG = "APPSOSReportBean";
+ public static final String TAG = "WinBollNewsBean";
- protected String sosReport;
+ protected String message;
- public SOSReportBean() {
- this.sosReport = "";
+ public WinBollNewsBean() {
+ this.message = "";
}
- public SOSReportBean(String sosReport) {
- this.sosReport = sosReport;
+ public WinBollNewsBean(String message) {
+ this.message = message;
}
- public void setSosReport(String sosReport) {
- this.sosReport = sosReport;
+ public void setMessage(String message) {
+ this.message = message;
}
- public String getSosReport() {
- return sosReport;
+ public String getMessage() {
+ return message;
}
@Override
public String getName() {
- return SOSReportBean.class.getName();
+ return WinBollNewsBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
- jsonWriter.name("sosReport").value(getSosReport());
+ jsonWriter.name("message").value(getMessage());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
- if (name.equals("sosReport")) {
- setSosReport(jsonReader.nextString());
+ if (name.equals("message")) {
+ setMessage(jsonReader.nextString());
} else {
return false;
}
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/widgets/SOSWidgetClickListener.java b/appbase/src/main/java/cc/winboll/studio/appbase/receivers/APPNewsWidgetClickListener.java
similarity index 55%
rename from appbase/src/main/java/cc/winboll/studio/appbase/widgets/SOSWidgetClickListener.java
rename to appbase/src/main/java/cc/winboll/studio/appbase/receivers/APPNewsWidgetClickListener.java
index 42dc31f..70561c9 100644
--- a/appbase/src/main/java/cc/winboll/studio/appbase/widgets/SOSWidgetClickListener.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/receivers/APPNewsWidgetClickListener.java
@@ -1,20 +1,20 @@
-package cc.winboll.studio.appbase.widgets;
+package cc.winboll.studio.appbase.receivers;
/**
* @Author ZhanGSKen@AliYun.Com
- * @Date 2025/02/15 17:20:46
- * @Describe WidgetButtonClickListener
+ * @Date 2025/03/24 07:11:44
*/
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import cc.winboll.studio.appbase.widgets.APPNewsWidget;
import cc.winboll.studio.libappbase.LogUtils;
-public class SOSWidgetClickListener extends BroadcastReceiver {
+public class APPNewsWidgetClickListener extends BroadcastReceiver {
- public static final String TAG = "SOSWidgetClickListener";
- public static final String ACTION_PRE = "cc.winboll.studio.appbase.widgets.SOSWidgetClickListener.ACTION_PRE";
- public static final String ACTION_NEXT = "cc.winboll.studio.appbase.widgets.SOSWidgetClickListener.ACTION_NEXT";
+ public static final String TAG = "APPNewsWidgetClickListener";
+ public static final String ACTION_PRE = APPNewsWidgetClickListener.class.getName() + ".ACTION_PRE";
+ public static final String ACTION_NEXT = APPNewsWidgetClickListener.class.getName() + ".ACTION_NEXT";
@Override
public void onReceive(Context context, Intent intent) {
@@ -25,10 +25,10 @@ public class SOSWidgetClickListener extends BroadcastReceiver {
}
if (action.equals(ACTION_PRE)) {
LogUtils.d(TAG, "ACTION_PRE");
- SOSWidget.prePage(context);
+ APPNewsWidget.prePage(context);
} else if (action.equals(ACTION_NEXT)) {
LogUtils.d(TAG, "ACTION_NEXT");
- SOSWidget.nextPage(context);
+ APPNewsWidget.nextPage(context);
} else {
LogUtils.d(TAG, String.format("action %s", action));
}
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/receivers/MainReceiver.java b/appbase/src/main/java/cc/winboll/studio/appbase/receivers/MainReceiver.java
index 36b12ed..498f877 100644
--- a/appbase/src/main/java/cc/winboll/studio/appbase/receivers/MainReceiver.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/receivers/MainReceiver.java
@@ -5,20 +5,21 @@ package cc.winboll.studio.appbase.receivers;
* @Date 2025/02/13 06:58:04
* @Describe 主要广播接收器
*/
-import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import cc.winboll.studio.appbase.beans.SOSReportBean;
+import cc.winboll.studio.appbase.models.WinBollNewsBean;
import cc.winboll.studio.appbase.services.MainService;
-import cc.winboll.studio.appbase.widgets.SOSWidget;
+import cc.winboll.studio.appbase.widgets.APPNewsWidget;
import cc.winboll.studio.libappbase.AppUtils;
import cc.winboll.studio.libappbase.LogUtils;
-import cc.winboll.studio.libappbase.SOS;
-import cc.winboll.studio.libappbase.bean.APPSOSBean;
-import com.hjq.toast.ToastUtils;
+import cc.winboll.studio.libappbase.sos.APPModel;
+import cc.winboll.studio.libappbase.sos.SOS;
+import cc.winboll.studio.libappbase.sos.SOSObject;
+import cc.winboll.studio.libappbase.sos.WinBoll;
+import cc.winboll.studio.libappbase.utils.ToastUtils;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
@@ -27,12 +28,10 @@ import java.util.Date;
public class MainReceiver extends BroadcastReceiver {
public static final String TAG = "MainReceiver";
+
public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
+
WeakReference mwrService;
- // 存储电量指示值,
- // 用于校验电量消息时的电量变化
- static volatile int _mnTheQuantityOfElectricityOld = -1;
- static volatile boolean _mIsCharging = false;
public MainReceiver(MainService service) {
mwrService = new WeakReference(service);
@@ -43,74 +42,62 @@ public class MainReceiver extends BroadcastReceiver {
String szAction = intent.getAction();
if (szAction.equals(ACTION_BOOT_COMPLETED)) {
ToastUtils.show("ACTION_BOOT_COMPLETED");
- } else if (szAction.equals(SOS.ACTION_BIND)) {
+ } else if (szAction.equals(WinBoll.ACTION_BIND)) {
LogUtils.d(TAG, "ACTION_BIND");
LogUtils.d(TAG, String.format("context.getPackageName() %s", context.getPackageName()));
LogUtils.d(TAG, String.format("intent.getAction() %s", intent.getAction()));
- String SOS = intent.getStringExtra("SOS");
- LogUtils.d(TAG, String.format("SOS %s", SOS));
- if (SOS != null && SOS.equals("Service")) {
- String szAPPSOSBean = intent.getStringExtra("APPSOSBean");
- LogUtils.d(TAG, String.format("szAPPSOSBean %s", szAPPSOSBean));
- if (szAPPSOSBean != null && !szAPPSOSBean.equals("")) {
- try {
- APPSOSBean bean = APPSOSBean.parseStringToBean(szAPPSOSBean, APPSOSBean.class);
- if (bean != null) {
- String sosPackage = bean.getSosPackage();
- LogUtils.d(TAG, String.format("sosPackage %s", sosPackage));
- String sosClassName = bean.getSosClassName();
- LogUtils.d(TAG, String.format("sosClassName %s", sosClassName));
- mwrService.get().bindSOSConnection(bean);
- }
- } catch (IOException e) {
- LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
+ String szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL);
+ LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel));
+ if (szAPPModel != null && !szAPPModel.equals("")) {
+ try {
+ APPModel bean = APPModel.parseStringToBean(szAPPModel, APPModel.class);
+ if (bean != null) {
+ String szAppPackageName = bean.getAppPackageName();
+ LogUtils.d(TAG, String.format("szAppPackageName %s", szAppPackageName));
+ String szAppMainServiveName = bean.getAppMainServiveName();
+ LogUtils.d(TAG, String.format("szAppMainServiveName %s", szAppMainServiveName));
+ mwrService.get().bindAPPModelConnection(bean);
}
+ } catch (IOException e) {
+ LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
} else if (intent.getAction().equals(SOS.ACTION_SOS)) {
LogUtils.d(TAG, "ACTION_SOS");
- LogUtils.d(TAG, String.format("context.getPackageName() %s", context.getPackageName()));
- LogUtils.d(TAG, String.format("intent.getAction() %s", intent.getAction()));
- String SOS = intent.getStringExtra("SOS");
- LogUtils.d(TAG, String.format("SOS %s", SOS));
- if (SOS != null && SOS.equals("Service")) {
- String szAPPSOSBean = intent.getStringExtra("APPSOSBean");
- LogUtils.d(TAG, String.format("szAPPSOSBean %s", szAPPSOSBean));
- if (szAPPSOSBean != null && !szAPPSOSBean.equals("")) {
- try {
- APPSOSBean bean = APPSOSBean.parseStringToBean(szAPPSOSBean, APPSOSBean.class);
- if (bean != null) {
- String sosPackage = bean.getSosPackage();
- LogUtils.d(TAG, String.format("sosPackage %s", sosPackage));
- String sosClassName = bean.getSosClassName();
- LogUtils.d(TAG, String.format("sosClassName %s", sosClassName));
+ String sos = intent.getStringExtra(SOS.EXTRA_OBJECT);
+ LogUtils.d(TAG, String.format("SOS %s", sos));
+ if (sos != null && !sos.equals("")) {
+ SOSObject bean = SOS.parseSOSObject(sos);
+ if (bean != null) {
+ String szObjectPackageName = bean.getObjectPackageName();
+ LogUtils.d(TAG, String.format("szObjectPackageName %s", szObjectPackageName));
+ String szObjectServiveName = bean.getObjectServiveName();
+ LogUtils.d(TAG, String.format("szObjectServiveName %s", szObjectServiveName));
- Intent intentService = new Intent();
- intentService.setComponent(new ComponentName(sosPackage, sosClassName));
- context.startService(intentService);
+ Intent intentService = new Intent();
+ intentService.setComponent(new ComponentName(szObjectPackageName, szObjectServiveName));
+ context.startService(intentService);
- String appName = AppUtils.getAppNameByPackageName(context, sosPackage);
- LogUtils.d(TAG, String.format("appName %s", appName));
- SOSReportBean appSOSReportBean = new SOSReportBean(appName);
- SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
- String currentTime = sdf.format(new Date());
- StringBuilder sbLine = new StringBuilder();
- sbLine.append("[");
- sbLine.append(currentTime);
- sbLine.append("] Power to ");
- sbLine.append(appName);
- appSOSReportBean.setSosReport(sbLine.toString());
+ String appName = AppUtils.getAppNameByPackageName(context, szObjectPackageName);
+ LogUtils.d(TAG, String.format("appName %s", appName));
+ WinBollNewsBean appWinBollNewsBean = new WinBollNewsBean(appName);
+ SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
+ String currentTime = sdf.format(new Date());
+ StringBuilder sbLine = new StringBuilder();
+ sbLine.append("[");
+ sbLine.append(currentTime);
+ sbLine.append("] Power to ");
+ sbLine.append(appName);
+ appWinBollNewsBean.setMessage(sbLine.toString());
- SOSWidget.addAPPSOSReportBean(context, appSOSReportBean);
-
- Intent intentWidget = new Intent(context, SOSWidget.class);
- intentWidget.setAction(SOSWidget.ACTION_RELOAD_REPORT);
- context.sendBroadcast(intentWidget);
- }
- } catch (IOException e) {
- LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
- }
+ APPNewsWidget.addWinBollNewsBean(context, appWinBollNewsBean);
+
+ Intent intentWidget = new Intent(context, APPNewsWidget.class);
+ intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
+ context.sendBroadcast(intentWidget);
}
+
+
}
} else {
ToastUtils.show(szAction);
@@ -123,9 +110,7 @@ public class MainReceiver extends BroadcastReceiver {
IntentFilter filter=new IntentFilter();
filter.addAction(ACTION_BOOT_COMPLETED);
filter.addAction(SOS.ACTION_SOS);
- filter.addAction(SOS.ACTION_BIND);
- filter.addAction(SOS.ACTION_SERVICE_ENABLE);
- filter.addAction(SOS.ACTION_SERVICE_DISABLE);
+ filter.addAction(WinBoll.ACTION_BIND);
//filter.addAction(Intent.ACTION_BATTERY_CHANGED);
service.registerReceiver(this, filter);
}
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/services/AssistantService.java b/appbase/src/main/java/cc/winboll/studio/appbase/services/AssistantService.java
index b2e2f9b..8db3f50 100644
--- a/appbase/src/main/java/cc/winboll/studio/appbase/services/AssistantService.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/services/AssistantService.java
@@ -11,7 +11,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
-import cc.winboll.studio.appbase.beans.MainServiceBean;
+import cc.winboll.studio.appbase.models.MainServiceBean;
import cc.winboll.studio.appbase.services.AssistantService;
import cc.winboll.studio.appbase.services.MainService;
import cc.winboll.studio.libappbase.LogUtils;
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/services/MainService.java b/appbase/src/main/java/cc/winboll/studio/appbase/services/MainService.java
index 93e41b7..fdbbd62 100644
--- a/appbase/src/main/java/cc/winboll/studio/appbase/services/MainService.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/services/MainService.java
@@ -18,15 +18,15 @@ import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import cc.winboll.studio.appbase.MyTileService;
-import cc.winboll.studio.appbase.beans.MainServiceBean;
+import cc.winboll.studio.appbase.models.MainServiceBean;
import cc.winboll.studio.appbase.handlers.MainServiceHandler;
import cc.winboll.studio.appbase.receivers.MainReceiver;
import cc.winboll.studio.appbase.services.AssistantService;
import cc.winboll.studio.appbase.threads.MainServiceThread;
-import cc.winboll.studio.appbase.widgets.SOSWidget;
+import cc.winboll.studio.appbase.widgets.APPNewsWidget;
import cc.winboll.studio.libappbase.LogUtils;
-import cc.winboll.studio.libappbase.bean.APPSOSBean;
import java.util.ArrayList;
+import cc.winboll.studio.libappbase.sos.APPModel;
public class MainService extends Service {
@@ -45,7 +45,7 @@ public class MainService extends Service {
AssistantService mAssistantService;
boolean isBound = false;
MainReceiver mMainReceiver;
- ArrayList mSOSConnectionList;
+ ArrayList mAPPModelConnectionList;
@Override
public IBinder onBind(Intent intent) {
@@ -60,7 +60,7 @@ public class MainService extends Service {
public void onCreate() {
super.onCreate();
LogUtils.d(TAG, "onCreate()");
- mSOSConnectionList = new ArrayList();
+ mAPPModelConnectionList = new ArrayList();
_mControlCenterService = MainService.this;
isServiceRunning = false;
@@ -101,8 +101,8 @@ public class MainService extends Service {
}
// 启动小部件
- Intent intentTimeWidget = new Intent(this, SOSWidget.class);
- intentTimeWidget.setAction(SOSWidget.ACTION_RELOAD_REPORT);
+ Intent intentTimeWidget = new Intent(this, APPNewsWidget.class);
+ intentTimeWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
this.sendBroadcast(intentTimeWidget);
startMainServiceThread();
@@ -117,21 +117,11 @@ public class MainService extends Service {
//
void wakeupAndBindAssistant() {
LogUtils.d(TAG, "wakeupAndBindAssistant()");
-// if (ServiceUtils.isServiceAlive(getApplicationContext(), AssistantService.class.getName()) == false) {
-// startService(new Intent(MainService.this, AssistantService.class));
-// //LogUtils.d(TAG, "call wakeupAndBindAssistant() : Binding... AssistantService");
-// bindService(new Intent(MainService.this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
-// }
+
Intent intent = new Intent(this, AssistantService.class);
startService(intent);
// 绑定服务的Intent
- //Intent intent = new Intent(this, AssistantService.class);
bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
-
-// Intent intent = new Intent(this, AssistantService.class);
-// startService(intent);
-// LogUtils.d(TAG, "startService(intent)");
-// bindService(new Intent(this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
}
// 开启提醒铃声线程
@@ -192,40 +182,40 @@ public class MainService extends Service {
}
}
- public void bindSOSConnection(APPSOSBean bean) {
- LogUtils.d(TAG, "bindSOSConnection(...)");
+ public void bindAPPModelConnection(APPModel bean) {
+ LogUtils.d(TAG, "bindAPPModelConnection(...)");
// 清理旧的绑定链接
- for (int i = mSOSConnectionList.size() - 1; i > -1; i--) {
- SOSConnection item = mSOSConnectionList.get(i);
- if (item.isBindToAPPSOSBean(bean)) {
+ for (int i = mAPPModelConnectionList.size() - 1; i > -1; i--) {
+ APPConnection item = mAPPModelConnectionList.get(i);
+ if (item.isBindToAPP(bean)) {
LogUtils.d(TAG, "Bind Servive exist.");
unbindService(item);
- mSOSConnectionList.remove(i);
+ mAPPModelConnectionList.remove(i);
}
}
// 绑定服务
- SOSConnection sosConnection = new SOSConnection();
+ APPConnection appConnection = new APPConnection();
Intent intentService = new Intent();
- intentService.setComponent(new ComponentName(bean.getSosPackage(), bean.getSosClassName()));
- bindService(intentService, sosConnection, Context.BIND_IMPORTANT);
- mSOSConnectionList.add(sosConnection);
+ intentService.setComponent(new ComponentName(bean.getAppPackageName(), bean.getAppMainServiveName()));
+ bindService(intentService, appConnection, Context.BIND_IMPORTANT);
+ mAPPModelConnectionList.add(appConnection);
- Intent intentWidget = new Intent(this, SOSWidget.class);
- intentWidget.setAction(SOSWidget.ACTION_WAKEUP_SERVICE);
- APPSOSBean appSOSBean = new APPSOSBean(bean.getSosPackage(), bean.getSosClassName());
+ Intent intentWidget = new Intent(this, APPNewsWidget.class);
+ intentWidget.setAction(APPNewsWidget.ACTION_WAKEUP_SERVICE);
+ APPModel appSOSBean = new APPModel(bean.getAppPackageName(), bean.getAppMainServiveName());
intentWidget.putExtra("APPSOSBean", appSOSBean.toString());
sendBroadcast(intentWidget);
}
- public class SOSConnection implements ServiceConnection {
+ public class APPConnection implements ServiceConnection {
ComponentName mComponentName;
- boolean isBindToAPPSOSBean(APPSOSBean bean) {
+ boolean isBindToAPP(APPModel bean) {
return mComponentName != null
- && mComponentName.getClassName().equals(bean.getSosClassName())
- && mComponentName.getPackageName().equals(bean.getSosPackage());
+ && mComponentName.getClassName().equals(bean.getAppMainServiveName())
+ && mComponentName.getPackageName().equals(bean.getAppPackageName());
}
@Override
@@ -241,13 +231,13 @@ public class MainService extends Service {
LogUtils.d(TAG, String.format("onServiceDisconnected : \ngetClassName %s\ngetPackageName %s", name.getClassName(), name.getPackageName()));
// 尝试无参数启动一下服务
- String sosPackage = mComponentName.getPackageName();
- LogUtils.d(TAG, String.format("sosPackage %s", sosPackage));
- String sosClassName = mComponentName.getClassName();
- LogUtils.d(TAG, String.format("sosClassName %s", sosClassName));
+ String appPackage = mComponentName.getPackageName();
+ LogUtils.d(TAG, String.format("appPackage %s", appPackage));
+ String appMainServiceClassName = mComponentName.getClassName();
+ LogUtils.d(TAG, String.format("appMainServiceClassName %s", appMainServiceClassName));
Intent intentService = new Intent();
- intentService.setComponent(new ComponentName(sosPackage, sosClassName));
+ intentService.setComponent(new ComponentName(appPackage, appMainServiceClassName));
startService(intentService);
}
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/services/TestDemoBindService.java b/appbase/src/main/java/cc/winboll/studio/appbase/services/TestDemoBindService.java
new file mode 100644
index 0000000..14209de
--- /dev/null
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/services/TestDemoBindService.java
@@ -0,0 +1,178 @@
+package cc.winboll.studio.appbase.services;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/07 12:45:49
+ * @Describe 启动时申请绑定到APPBase主服务的服务示例
+ */
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import cc.winboll.studio.appbase.models.TestDemoBindServiceBean;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libappbase.sos.WinBoll;
+import cc.winboll.studio.appbase.App;
+import cc.winboll.studio.libappbase.sos.SOS;
+
+public class TestDemoBindService extends Service {
+
+ public static final String TAG = "TestDemoBindService";
+
+ public static final String ACTION_ENABLE = TestDemoBindService.class.getName() + ".ACTION_ENABLE";
+ public static final String ACTION_DISABLE = TestDemoBindService.class.getName() + ".ACTION_DISABLE";
+
+ volatile static TestThread _TestThread;
+
+ volatile static boolean _IsRunning;
+
+ public synchronized static void setIsRunning(boolean isRunning) {
+ _IsRunning = isRunning;
+ }
+
+ public static boolean isRunning() {
+ return _IsRunning;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new MyBinder();
+ }
+
+ public class MyBinder extends Binder {
+ public TestDemoBindService getService() {
+ return TestDemoBindService.this;
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ LogUtils.d(TAG, "onCreate()");
+
+ run();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ LogUtils.d(TAG, "onStartCommand(...)");
+ TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
+ if (bean == null) {
+ bean = new TestDemoBindServiceBean();
+ }
+
+ if (intent.getAction() != null) {
+ if (intent.getAction().equals(ACTION_ENABLE)) {
+ bean.setIsEnable(true);
+ LogUtils.d(TAG, "setIsEnable(true);");
+ TestDemoBindServiceBean.saveBean(this, bean);
+ } else if (intent.getAction().equals(ACTION_DISABLE)) {
+ bean.setIsEnable(false);
+ LogUtils.d(TAG, "setIsEnable(false);");
+ TestDemoBindServiceBean.saveBean(this, bean);
+ }
+ }
+
+ run();
+
+ return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
+ //return super.onStartCommand(intent, flags, startId);
+ }
+
+ void run() {
+ LogUtils.d(TAG, "run()");
+ TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
+ if (bean == null) {
+ bean = new TestDemoBindServiceBean();
+ TestDemoBindServiceBean.saveBean(this, bean);
+ }
+ if (bean.isEnable()) {
+ LogUtils.d(TAG, "run() bean.isEnable()");
+ TestThread.getInstance(this).start();
+ LogUtils.d(TAG, "_TestThread.start()");
+ }
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ LogUtils.d(TAG, "onDestroy()");
+ TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
+ if (bean == null) {
+ bean = new TestDemoBindServiceBean();
+ }
+
+ TestThread.getInstance(this).setIsExit(true);
+
+ // 预防 APPBase 应用重启绑定失效。
+ // 所以退出时检查本服务是否配置启用,如果启用就发送一个 SOS 信号。
+ // 这样 APPBase 就会用组件方式启动本服务。
+ if (bean.isEnable()) {
+ if (App.isDebuging()) {
+ SOS.sosToAppBaseBeta(this, TestDemoBindService.class.getName());
+ } else {
+ SOS.sosToAppBase(this, TestDemoBindService.class.getName());
+ }
+ }
+
+ _IsRunning = false;
+ }
+
+ static class TestThread extends Thread {
+
+ volatile static TestThread _TestThread;
+ Context mContext;
+ volatile boolean isStarted = false;
+ volatile boolean isExit = false;
+
+ TestThread(Context context) {
+ super();
+ mContext = context;
+ }
+
+ public static synchronized TestThread getInstance(Context context) {
+ if (_TestThread != null) {
+ _TestThread.setIsExit(true);
+ }
+ _TestThread = new TestThread(context);
+
+ return _TestThread;
+ }
+
+ public synchronized void setIsExit(boolean isExit) {
+ this.isExit = isExit;
+ }
+
+ public boolean isExit() {
+ return isExit;
+ }
+
+ @Override
+ public void run() {
+ if (isStarted == false) {
+ isStarted = true;
+ super.run();
+ LogUtils.d(TAG, "run() start");
+ if (App.isDebuging()) {
+ WinBoll.bindToAPPBaseBeta(mContext, TestDemoBindService.class.getName());
+ } else {
+ WinBoll.bindToAPPBase(mContext, TestDemoBindService.class.getName());
+ }
+
+ while (!isExit()) {
+ LogUtils.d(TAG, "run()");
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
+ }
+ }
+
+ LogUtils.d(TAG, "run() exit");
+ }
+ }
+ }
+}
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/services/TestService.java b/appbase/src/main/java/cc/winboll/studio/appbase/services/TestDemoService.java
similarity index 67%
rename from libappbase/src/main/java/cc/winboll/studio/libappbase/services/TestService.java
rename to appbase/src/main/java/cc/winboll/studio/appbase/services/TestDemoService.java
index fc86253..6098dce 100644
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/services/TestService.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/services/TestDemoService.java
@@ -1,23 +1,25 @@
-package cc.winboll.studio.libappbase.services;
+package cc.winboll.studio.appbase.services;
/**
* @Author ZhanGSKen@AliYun.Com
- * @Date 2025/02/15 20:48:36
- * @Describe TestService
+ * @Date 2025/03/07 12:39:24
+ * @Describe 普通服务示例
*/
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
+import cc.winboll.studio.appbase.models.TestDemoServiceBean;
import cc.winboll.studio.libappbase.LogUtils;
-import cc.winboll.studio.libappbase.SOS;
-import cc.winboll.studio.libappbase.bean.APPSOSBean;
-import cc.winboll.studio.libappbase.bean.TestServiceBean;
+import cc.winboll.studio.libappbase.sos.WinBoll;
-public class TestService extends Service {
+public class TestDemoService extends Service {
- public static final String TAG = "TestService";
+ public static final String TAG = "TestDemoService";
+
+ public static final String ACTION_ENABLE = TestDemoService.class.getName() + ".ACTION_ENABLE";
+ public static final String ACTION_DISABLE = TestDemoService.class.getName() + ".ACTION_DISABLE";
volatile static TestThread _TestThread;
@@ -37,8 +39,8 @@ public class TestService extends Service {
}
public class MyBinder extends Binder {
- public TestService getService() {
- return TestService.this;
+ public TestDemoService getService() {
+ return TestDemoService.this;
}
}
@@ -54,29 +56,35 @@ public class TestService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "onStartCommand(...)");
- TestServiceBean bean = TestServiceBean.loadBean(this, TestServiceBean.class);
+ TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
if (bean == null) {
- bean = new TestServiceBean();
+ bean = new TestDemoServiceBean();
}
- if (intent.getAction() != null && intent.getAction().equals(SOS.ACTION_SERVICE_ENABLE)) {
- bean.setIsEnable(true);
- TestServiceBean.saveBean(this, bean);
- run();
- } else if (intent.getAction() != null && intent.getAction().equals(SOS.ACTION_SERVICE_DISABLE)) {
- bean.setIsEnable(false);
- TestServiceBean.saveBean(this, bean);
+
+ if (intent.getAction() != null) {
+ if (intent.getAction().equals(ACTION_ENABLE)) {
+ bean.setIsEnable(true);
+ LogUtils.d(TAG, "setIsEnable(true);");
+ TestDemoServiceBean.saveBean(this, bean);
+ } else if (intent.getAction().equals(ACTION_DISABLE)) {
+ bean.setIsEnable(false);
+ LogUtils.d(TAG, "setIsEnable(false);");
+ TestDemoServiceBean.saveBean(this, bean);
+ }
}
- LogUtils.d(TAG, String.format("TestServiceBean.saveBean setIsEnable %s", bean.isEnable()));
+
+ run();
+
return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
//return super.onStartCommand(intent, flags, startId);
}
void run() {
LogUtils.d(TAG, "run()");
- TestServiceBean bean = TestServiceBean.loadBean(this, TestServiceBean.class);
+ TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
if (bean == null) {
- bean = new TestServiceBean();
- TestServiceBean.saveBean(this, bean);
+ bean = new TestDemoServiceBean();
+ TestDemoServiceBean.saveBean(this, bean);
}
if (bean.isEnable()) {
LogUtils.d(TAG, "run() bean.isEnable()");
@@ -91,7 +99,7 @@ public class TestService extends Service {
super.onDestroy();
LogUtils.d(TAG, "onDestroy()");
TestThread.getInstance(this).setIsExit(true);
-
+
_IsRunning = false;
}
@@ -130,7 +138,6 @@ public class TestService extends Service {
isStarted = true;
super.run();
LogUtils.d(TAG, "run() start");
- SOS.bindToAPPService(mContext, new APPSOSBean(mContext.getPackageName(), TestService.class.getName()));
while (!isExit()) {
LogUtils.d(TAG, "run()");
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/widgets/SOSWidget.java b/appbase/src/main/java/cc/winboll/studio/appbase/widgets/APPNewsWidget.java
similarity index 53%
rename from appbase/src/main/java/cc/winboll/studio/appbase/widgets/SOSWidget.java
rename to appbase/src/main/java/cc/winboll/studio/appbase/widgets/APPNewsWidget.java
index b614da9..c844983 100644
--- a/appbase/src/main/java/cc/winboll/studio/appbase/widgets/SOSWidget.java
+++ b/appbase/src/main/java/cc/winboll/studio/appbase/widgets/APPNewsWidget.java
@@ -12,31 +12,33 @@ import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
import cc.winboll.studio.appbase.R;
-import cc.winboll.studio.appbase.beans.SOSReportBean;
+import cc.winboll.studio.appbase.models.WinBollNewsBean;
+import cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener;
import cc.winboll.studio.libappbase.AppUtils;
import cc.winboll.studio.libappbase.LogUtils;
-import cc.winboll.studio.libappbase.bean.APPSOSBean;
+import cc.winboll.studio.libappbase.sos.APPModel;
+import cc.winboll.studio.libappbase.sos.WinBoll;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
-public class SOSWidget extends AppWidgetProvider {
+public class APPNewsWidget extends AppWidgetProvider {
- public static final String TAG = "SOSWidget";
+ public static final String TAG = "APPNewsWidget";
- public static final String ACTION_WAKEUP_SERVICE = "cc.winboll.studio.appbase.widgets.SOSWidget.ACTION_WAKEUP_SERVICE";
- public static final String ACTION_RELOAD_REPORT = "cc.winboll.studio.appbase.widgets.SOSWidget.ACTION_RELOAD_REPORT";
+ public static final String ACTION_WAKEUP_SERVICE = APPNewsWidget.class.getName() + ".ACTION_WAKEUP_SERVICE";
+ public static final String ACTION_RELOAD_REPORT = APPNewsWidget.class.getName() + ".ACTION_RELOAD_REPORT";
- volatile static ArrayList _SOSReportBeanList;
+ volatile static ArrayList _WinBollNewsBeanList;
final static int _MAX_PAGES = 10;
final static int _OnePageLinesCount = 5;
volatile static int _CurrentPageIndex = 0;
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
- initAPPSOSReportBeanList(context);
+ initWinBollNewsBeanList(context);
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
@@ -45,31 +47,31 @@ public class SOSWidget extends AppWidgetProvider {
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
- initAPPSOSReportBeanList(context);
+ initWinBollNewsBeanList(context);
if (intent.getAction().equals(ACTION_RELOAD_REPORT)) {
LogUtils.d(TAG, "ACTION_RELOAD_REPORT");
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
- int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, SOSWidget.class));
+ int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class));
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}else if (intent.getAction().equals(ACTION_WAKEUP_SERVICE)) {
LogUtils.d(TAG, "ACTION_WAKEUP_SERVICE");
- String szAPPSOSBean = intent.getStringExtra("APPSOSBean");
- LogUtils.d(TAG, String.format("szAPPSOSBean %s", szAPPSOSBean));
- if (szAPPSOSBean != null && !szAPPSOSBean.equals("")) {
+ String szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL);
+ LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel));
+ if (szAPPModel != null && !szAPPModel.equals("")) {
try {
- APPSOSBean bean = APPSOSBean.parseStringToBean(szAPPSOSBean, APPSOSBean.class);
+ APPModel bean = APPModel.parseStringToBean(szAPPModel, APPModel.class);
if (bean != null) {
- String sosPackage = bean.getSosPackage();
- LogUtils.d(TAG, String.format("sosPackage %s", sosPackage));
- String sosClassName = bean.getSosClassName();
- LogUtils.d(TAG, String.format("sosClassName %s", sosClassName));
+ String szAppPackageName = bean.getAppPackageName();
+ LogUtils.d(TAG, String.format("szAppPackageName %s", szAppPackageName));
+ String szAppMainServiveName = bean.getAppMainServiveName();
+ LogUtils.d(TAG, String.format("szAppMainServiveName %s", szAppMainServiveName));
- String appName = AppUtils.getAppNameByPackageName(context, sosPackage);
+ String appName = AppUtils.getAppNameByPackageName(context, szAppPackageName);
LogUtils.d(TAG, String.format("appName %s", appName));
- SOSReportBean appSOSReportBean = new SOSReportBean(appName);
+ WinBollNewsBean winBollNewsBean = new WinBollNewsBean(appName);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String currentTime = sdf.format(new Date());
StringBuilder sbLine = new StringBuilder();
@@ -77,12 +79,12 @@ public class SOSWidget extends AppWidgetProvider {
sbLine.append(currentTime);
sbLine.append("] Wake up ");
sbLine.append(appName);
- appSOSReportBean.setSosReport(sbLine.toString());
+ winBollNewsBean.setMessage(sbLine.toString());
- addAPPSOSReportBean(context, appSOSReportBean);
+ addWinBollNewsBean(context, winBollNewsBean);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
- int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, SOSWidget.class));
+ int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class));
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
@@ -97,53 +99,53 @@ public class SOSWidget extends AppWidgetProvider {
//
// 加入新报告信息
//
- public synchronized static void addAPPSOSReportBean(Context context, SOSReportBean bean) {
- initAPPSOSReportBeanList(context);
- _SOSReportBeanList.add(0, bean);
+ public synchronized static void addWinBollNewsBean(Context context, WinBollNewsBean bean) {
+ initWinBollNewsBeanList(context);
+ _WinBollNewsBeanList.add(0, bean);
// 控制记录总数
- while (_SOSReportBeanList.size() > _MAX_PAGES * _OnePageLinesCount) {
- _SOSReportBeanList.remove(_SOSReportBeanList.size() - 1);
+ while (_WinBollNewsBeanList.size() > _MAX_PAGES * _OnePageLinesCount) {
+ _WinBollNewsBeanList.remove(_WinBollNewsBeanList.size() - 1);
}
- SOSReportBean.saveBeanList(context, _SOSReportBeanList, SOSReportBean.class);
+ WinBollNewsBean.saveBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class);
}
- synchronized static void initAPPSOSReportBeanList(Context context) {
- if (_SOSReportBeanList == null) {
- _SOSReportBeanList = new ArrayList();
- SOSReportBean.loadBeanList(context, _SOSReportBeanList, SOSReportBean.class);
+ synchronized static void initWinBollNewsBeanList(Context context) {
+ if (_WinBollNewsBeanList == null) {
+ _WinBollNewsBeanList = new ArrayList();
+ WinBollNewsBean.loadBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class);
}
- if (_SOSReportBeanList == null) {
- _SOSReportBeanList = new ArrayList();
- SOSReportBean.saveBeanList(context, _SOSReportBeanList, SOSReportBean.class);
+ if (_WinBollNewsBeanList == null) {
+ _WinBollNewsBeanList = new ArrayList();
+ WinBollNewsBean.saveBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class);
}
}
private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
LogUtils.d(TAG, "updateAppWidget(...)");
- RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_sos);
+ RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_news);
//设置按钮点击事件
- Intent intentPre = new Intent(context, SOSWidgetClickListener.class);
- intentPre.setAction(SOSWidgetClickListener.ACTION_PRE);
+ Intent intentPre = new Intent(context, APPNewsWidgetClickListener.class);
+ intentPre.setAction(APPNewsWidgetClickListener.ACTION_PRE);
PendingIntent pendingIntentPre = PendingIntent.getBroadcast(context, 0, intentPre, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.widget_button_pre, pendingIntentPre);
- Intent intentNext = new Intent(context, SOSWidgetClickListener.class);
- intentNext.setAction(SOSWidgetClickListener.ACTION_NEXT);
+ Intent intentNext = new Intent(context, APPNewsWidgetClickListener.class);
+ intentNext.setAction(APPNewsWidgetClickListener.ACTION_NEXT);
PendingIntent pendingIntentNext = PendingIntent.getBroadcast(context, 0, intentNext, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.widget_button_next, pendingIntentNext);
- views.setTextViewText(R.id.infoTextView, getPageInfo());
- views.setTextViewText(R.id.sosReportTextView, getMessage());
+ views.setTextViewText(R.id.tv_msg, getPageInfo());
+ views.setTextViewText(R.id.tv_news, getMessage());
appWidgetManager.updateAppWidget(appWidgetId, views);
}
public static String getMessage() {
ArrayList msgTemp = new ArrayList();
- if (_SOSReportBeanList != null) {
+ if (_WinBollNewsBeanList != null) {
int start = _OnePageLinesCount * _CurrentPageIndex;
- start = _SOSReportBeanList.size() > start ? start : _SOSReportBeanList.size() - 1;
- for (int i = start, j = 0; i < _SOSReportBeanList.size() && j < _OnePageLinesCount && start > -1; i++, j++) {
- msgTemp.add(_SOSReportBeanList.get(i).getSosReport());
+ start = _WinBollNewsBeanList.size() > start ? start : _WinBollNewsBeanList.size() - 1;
+ for (int i = start, j = 0; i < _WinBollNewsBeanList.size() && j < _OnePageLinesCount && start > -1; i++, j++) {
+ msgTemp.add(_WinBollNewsBeanList.get(i).getMessage());
}
String message = String.join("\n", msgTemp);
return message;
@@ -152,33 +154,33 @@ public class SOSWidget extends AppWidgetProvider {
}
public static void prePage(Context context) {
- if (_SOSReportBeanList != null) {
+ if (_WinBollNewsBeanList != null) {
if (_CurrentPageIndex > 0) {
_CurrentPageIndex = _CurrentPageIndex - 1;
}
- Intent intentWidget = new Intent(context, SOSWidget.class);
- intentWidget.setAction(SOSWidget.ACTION_RELOAD_REPORT);
+ Intent intentWidget = new Intent(context, APPNewsWidget.class);
+ intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
context.sendBroadcast(intentWidget);
}
}
public static void nextPage(Context context) {
- if (_SOSReportBeanList != null) {
- if ((_CurrentPageIndex + 1) * _OnePageLinesCount < _SOSReportBeanList.size()) {
+ if (_WinBollNewsBeanList != null) {
+ if ((_CurrentPageIndex + 1) * _OnePageLinesCount < _WinBollNewsBeanList.size()) {
_CurrentPageIndex = _CurrentPageIndex + 1;
}
- Intent intentWidget = new Intent(context, SOSWidget.class);
- intentWidget.setAction(SOSWidget.ACTION_RELOAD_REPORT);
+ Intent intentWidget = new Intent(context, APPNewsWidget.class);
+ intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
context.sendBroadcast(intentWidget);
}
}
String getPageInfo() {
- if (_SOSReportBeanList == null) {
+ if (_WinBollNewsBeanList == null) {
return "0/0";
}
- int leftCount = _SOSReportBeanList.size() % _OnePageLinesCount;
- int currentPageCount = _SOSReportBeanList.size() / _OnePageLinesCount + (leftCount == 0 ?0: 1);
+ int leftCount = _WinBollNewsBeanList.size() % _OnePageLinesCount;
+ int currentPageCount = _WinBollNewsBeanList.size() / _OnePageLinesCount + (leftCount == 0 ?0: 1);
return String.format("%d/%d", _CurrentPageIndex + 1, currentPageCount);
}
}
diff --git a/appbase/src/main/res/layout/activity_main.xml b/appbase/src/main/res/layout/activity_main.xml
index a17706a..0c085fa 100644
--- a/appbase/src/main/res/layout/activity_main.xml
+++ b/appbase/src/main/res/layout/activity_main.xml
@@ -1,149 +1,208 @@
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
-
+ android:id="@+id/toolbar"/>
-
-
-
-
-
+ android:layout_weight="1.0">
-
-
-
-
-
-
-
-
+ android:orientation="vertical"
+ android:gravity="center">
+ android:layout_height="0dp"
+ android:layout_weight="1.0"
+ android:gravity="center_horizontal">
-
+
+
+
+
+ android:layout_height="wrap_content"
+ android:gravity="right|center_vertical">
+
+
+
+
+
+
+
+
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:onClick="onTestStopMainServiceWithoutSettingEnable"/>
+ android:onClick="onTestUseComponentStartService"/>
-
+ android:onClick="onTestDemoServiceSOS"/>
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
+
-
+
diff --git a/appbase/src/main/res/layout/activity_new.xml b/appbase/src/main/res/layout/activity_new.xml
new file mode 100644
index 0000000..a405f12
--- /dev/null
+++ b/appbase/src/main/res/layout/activity_new.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/appbase/src/main/res/layout/widget_sos.xml b/appbase/src/main/res/layout/widget_news.xml
similarity index 53%
rename from appbase/src/main/res/layout/widget_sos.xml
rename to appbase/src/main/res/layout/widget_news.xml
index 994313b..f5741ae 100644
--- a/appbase/src/main/res/layout/widget_sos.xml
+++ b/appbase/src/main/res/layout/widget_news.xml
@@ -11,31 +11,40 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="right">
+ android:gravity="right|center_vertical">
-
-
-
-
-
+
+
+
+
+
+
diff --git a/appbase/src/main/res/values/colors.xml b/appbase/src/main/res/values/colors.xml
index 18d6383..87d3836 100644
--- a/appbase/src/main/res/values/colors.xml
+++ b/appbase/src/main/res/values/colors.xml
@@ -1,6 +1,7 @@
- #005800FF
- #005800FF
- #005800FF
+ #FF00B322
+ #FF005C12
+ #FF8DFFA2
+ #FFFFFB8D
diff --git a/appbase/src/main/res/values/styles.xml b/appbase/src/main/res/values/styles.xml
index ec1f403..d8e7e1b 100644
--- a/appbase/src/main/res/values/styles.xml
+++ b/appbase/src/main/res/values/styles.xml
@@ -1,6 +1,6 @@
-
diff --git a/appbase/src/main/res/xml/widget_provider_info_sos.xml b/appbase/src/main/res/xml/widget_provider_info_sos.xml
index 469927d..e03bd34 100644
--- a/appbase/src/main/res/xml/widget_provider_info_sos.xml
+++ b/appbase/src/main/res/xml/widget_provider_info_sos.xml
@@ -3,5 +3,5 @@
android:minWidth="200dp"
android:minHeight="100dp"
android:updatePeriodMillis="1000"
- android:initialLayout="@layout/widget_sos">
+ android:initialLayout="@layout/widget_news">
diff --git a/build.gradle b/build.gradle
index 17875d0..491faab 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/' }
@@ -10,12 +16,6 @@ buildscript {
mavenCentral()
google()
mavenLocal()
- // Nexus Maven 库地址
- // "WinBoll Release"
- maven { url "https://nexus.winboll.cc/repository/maven-public/" }
- // "WinBoll Snapshot"
- maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" }
-
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.1'
@@ -26,12 +26,11 @@ buildscript {
allprojects {
repositories {
- maven {
- url "https://mirrors.tencent.com/repository/maven/tencent_public/"
- }
- maven {
- url "https://mirrors.tencent.com/repository/maven/tencent_public_snapshots"
- }
+ // 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/' }
@@ -42,12 +41,6 @@ allprojects {
mavenCentral()
google()
mavenLocal()
- // Nexus Maven 库地址
- // "WinBoll Release"
- maven { url "https://nexus.winboll.cc/repository/maven-public/" }
- // "WinBoll Snapshot"
- maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" }
-
}
ext {
// 定义全局变量,常用于版本管理
diff --git a/contacts/README.md b/contacts/README.md
new file mode 100644
index 0000000..a1f131a
--- /dev/null
+++ b/contacts/README.md
@@ -0,0 +1,40 @@
+# Contacts
+源码参考自:
+https://github.com/aJIEw/PhoneCallApp.git
+
+#### 介绍
+通讯录与拨号
+
+#### 软件架构
+适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
+也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
+
+
+#### Gradle 编译说明
+调试版编译命令 :gradle assembleBetaDebug
+阶段版编译命令 :gradle assembleStageRelease
+
+#### 使用说明
+
+在安卓系统中需要设置两个权限允许。
+1.自启动权限允许。
+2.省电策略-无限制权限允许。
+
+#### 参与贡献
+
+1. Fork 本仓库
+2. 新建 Feat_xxx 分支
+3. 提交代码 : ZhanGSKen(ZhanGSKen@AliYun.Com)
+4. 新建 Pull Request
+
+
+#### 特技
+
+1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
+2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
+3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
+4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
+5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
+6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
+
+#### 参考文档
diff --git a/contacts/app_update_description.txt b/contacts/app_update_description.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/contacts/app_update_description.txt
@@ -0,0 +1 @@
+
diff --git a/contacts/build.gradle b/contacts/build.gradle
new file mode 100644
index 0000000..552a42a
--- /dev/null
+++ b/contacts/build.gradle
@@ -0,0 +1,71 @@
+apply plugin: 'com.android.application'
+apply from: '../.winboll/winboll_app_build.gradle'
+apply from: '../.winboll/winboll_lint_build.gradle'
+
+def genVersionName(def versionName){
+ // 检查编译标志位配置
+ assert (winbollBuildProps['stageCount'] != null)
+ assert (winbollBuildProps['baseVersion'] != null)
+ // 保存基础版本号
+ winbollBuildProps.setProperty("baseVersion", "${versionName}");
+ //保存编译标志配置
+ FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
+ winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
+ fos.close();
+
+ // 返回编译版本号
+ return "${versionName}." + winbollBuildProps['stageCount']
+}
+
+android {
+ compileSdkVersion 32
+ buildToolsVersion "33.0.3"
+
+ defaultConfig {
+ applicationId "cc.winboll.studio.contacts"
+ minSdkVersion 21
+ targetSdkVersion 30
+ versionCode 1
+ // versionName 更新后需要手动设置
+ // 项目模块目录的 build.gradle 文件的 stageCount=0
+ // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
+ versionName "1.0"
+ if(true) {
+ versionName = genVersionName("${versionName}")
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+}
+
+dependencies {
+ // 二维码使用的类库
+ api 'com.google.zxing:core:3.4.1'
+ api 'com.journeyapps:zxing-android-embedded:3.6.0'
+
+ api 'io.github.medyo:android-about-page:2.0.0'
+ api 'com.github.getActivity:ToastUtils:10.5'
+ api 'com.jcraft:jsch:0.1.55'
+ api 'org.jsoup:jsoup:1.13.1'
+ api 'com.squareup.okhttp3:okhttp:4.4.1'
+
+ api 'androidx.appcompat:appcompat:1.1.0'
+ api 'androidx.viewpager:viewpager:1.0.0'
+ api 'androidx.fragment:fragment:1.1.0'
+ api 'com.google.android.material:material:1.4.0'
+
+ api 'cc.winboll.studio:libapputils:9.3.2'
+ api 'cc.winboll.studio:libappbase:1.5.6'
+
+ api fileTree(dir: 'libs', include: ['*.jar'])
+}
diff --git a/contacts/build.properties b/contacts/build.properties
new file mode 100644
index 0000000..86e2b57
--- /dev/null
+++ b/contacts/build.properties
@@ -0,0 +1,8 @@
+#Created by .winboll/winboll_app_build.gradle
+#Tue Feb 25 00:17:29 HKT 2025
+stageCount=2
+libraryProject=
+baseVersion=1.0
+publishVersion=1.0.1
+buildCount=0
+baseBetaVersion=1.0.2
diff --git a/contacts/proguard-rules.pro b/contacts/proguard-rules.pro
new file mode 100644
index 0000000..233bad2
--- /dev/null
+++ b/contacts/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/contacts/src/beta/AndroidManifest.xml b/contacts/src/beta/AndroidManifest.xml
new file mode 100644
index 0000000..c598f4f
--- /dev/null
+++ b/contacts/src/beta/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/beta/res/values/strings.xml b/contacts/src/beta/res/values/strings.xml
new file mode 100644
index 0000000..dc3720a
--- /dev/null
+++ b/contacts/src/beta/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ Contacts+
+
+
diff --git a/contacts/src/main/AndroidManifest.xml b/contacts/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..36ac1ee
--- /dev/null
+++ b/contacts/src/main/AndroidManifest.xml
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/ActivityStack.java b/contacts/src/main/java/cc/winboll/studio/contacts/ActivityStack.java
new file mode 100644
index 0000000..2f37ec0
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/ActivityStack.java
@@ -0,0 +1,59 @@
+package cc.winboll.studio.contacts;
+
+import android.app.Activity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class ActivityStack {
+
+ private static final ActivityStack INSTANCE = new ActivityStack();
+
+ private List activities = new ArrayList<>();
+
+ public static ActivityStack getInstance() {
+ return INSTANCE;
+ }
+
+ public void addActivity(Activity activity) {
+ activities.add(activity);
+ }
+
+ public Activity getTopActivity() {
+ if (activities.isEmpty()) {
+ return null;
+ }
+ return activities.get(activities.size() - 1);
+ }
+
+ public void finishTopActivity() {
+ if (!activities.isEmpty()) {
+ activities.remove(activities.size() - 1).finish();
+ }
+ }
+
+ public void finishActivity(Activity activity) {
+ if (activity != null) {
+ activities.remove(activity);
+ activity.finish();
+ }
+ }
+
+ public void finishActivity(Class activityClass) {
+ for (Activity activity : activities) {
+ if (activity.getClass().equals(activityClass)) {
+ finishActivity(activity);
+ }
+ }
+ }
+
+ public void finishAllActivity() {
+ if (!activities.isEmpty()) {
+ for (Activity activity : activities) {
+ activity.finish();
+ activities.remove(activity);
+ }
+ }
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/App.java b/contacts/src/main/java/cc/winboll/studio/contacts/App.java
new file mode 100644
index 0000000..6f482d5
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/App.java
@@ -0,0 +1,28 @@
+package cc.winboll.studio.contacts;
+
+/**
+ * @Author ZhanGSKen@QQ.COM
+ * @Date 2024/12/08 15:10:51
+ * @Describe 全局应用类
+ */
+import cc.winboll.studio.libappbase.GlobalApplication;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libapputils.app.WinBollActivityManager;
+
+public class App extends GlobalApplication {
+
+ public static final String TAG = "App";
+
+ @Override
+ public void onCreate() {
+ // 必须在调用基类前设置应用调试标志,
+ // 这样可以预先设置日志与数据的存储根目录。
+ setIsDebuging(this, BuildConfig.DEBUG);
+ super.onCreate();
+ // 设置 WinBoll 应用 UI 类型
+ WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Aplication);
+
+ LogUtils.d(TAG, "onCreate");
+ }
+
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/MainActivity.java b/contacts/src/main/java/cc/winboll/studio/contacts/MainActivity.java
new file mode 100644
index 0000000..24f9f4f
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/MainActivity.java
@@ -0,0 +1,406 @@
+package cc.winboll.studio.contacts;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+import android.widget.Toast;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.content.ContextCompat;
+import androidx.viewpager.widget.ViewPager;
+import cc.winboll.studio.contacts.R;
+import cc.winboll.studio.contacts.activities.CallActivity;
+import cc.winboll.studio.contacts.adapters.MyPagerAdapter;
+import cc.winboll.studio.contacts.beans.MainServiceBean;
+import cc.winboll.studio.contacts.services.MainService;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libappbase.LogView;
+import cc.winboll.studio.libapputils.app.IWinBollActivity;
+import cc.winboll.studio.libapputils.app.WinBollActivityManager;
+import cc.winboll.studio.libapputils.bean.APPInfo;
+import cc.winboll.studio.libapputils.view.YesNoAlertDialog;
+import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
+import com.google.android.material.tabs.TabLayout;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import android.content.DialogInterface;
+import cc.winboll.studio.contacts.activities.SettingsActivity;
+
+final public class MainActivity extends AppCompatActivity implements IWinBollActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
+
+ public static final String TAG = "MainActivity";
+
+ public static final int REQUEST_HOME_ACTIVITY = 0;
+ public static final int REQUEST_ABOUT_ACTIVITY = 1;
+
+ public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoll.ACTION_SOS";
+
+ LogView mLogView;
+ Toolbar mToolbar;
+ CheckBox cbMainService;
+ MainServiceBean mMainServiceBean;
+ ViewPager viewPager;
+ private List views; //用来存放放进ViewPager里面的布局
+ //实例化存储imageView(导航原点)的集合
+ ImageView[] imageViews;
+ //MyPagerAdapter adapter;//适配器
+ MyPagerAdapter pagerAdapter;
+ LinearLayout linearLayout;//下标所在在LinearLayout布局里
+ int currentPoint = 0;//当前被选中中页面的下标
+
+ private static final int DIALER_REQUEST_CODE = 1;
+
+ @Override
+ public AppCompatActivity getActivity() {
+ return this;
+ }
+
+ @Override
+ public APPInfo getAppInfo() {
+// String szBranchName = "contacts";
+//
+// APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo();
+// appInfo.setAppName("Contacts");
+// appInfo.setAppIcon(cc.winboll.studio.libapputils.R.drawable.ic_winboll);
+// appInfo.setAppDescription("Contacts Description");
+// appInfo.setAppGitName("APP");
+// appInfo.setAppGitOwner("Studio");
+// appInfo.setAppGitAPPBranch(szBranchName);
+// appInfo.setAppGitAPPSubProjectFolder(szBranchName);
+// appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts");
+// appInfo.setAppAPKName("Contacts");
+// appInfo.setAppAPKFolderName("Contacts");
+// return appInfo;
+ return null;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // 接收并处理 Intent 数据,函数 Intent 处理接收就直接返回
+ //if (prosessIntents(getIntent())) return;
+ // 以下正常创建主窗口
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ // 初始化工具栏
+ mToolbar = findViewById(R.id.activitymainToolbar1);
+ setSupportActionBar(mToolbar);
+ if (isEnableDisplayHomeAsUp()) {
+ // 显示后退按钮
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ getSupportActionBar().setSubtitle(getTag());
+
+ initData();
+ initView();
+ //initPoint();//调用初始化导航原点的方法
+ viewPager.addOnPageChangeListener(this);//滑动事件
+
+ ViewPager viewPager = findViewById(R.id.activitymainViewPager1);
+ MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
+ viewPager.setAdapter(pagerAdapter);
+ TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1);
+ tabLayout.setupWithViewPager(viewPager);
+
+// mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
+// if (mMainServiceBean == null) {
+// mMainServiceBean = new MainServiceBean();
+// }
+// cbMainService = findViewById(R.id.activitymainCheckBox1);
+// cbMainService.setChecked(mMainServiceBean.isEnable());
+// cbMainService.setOnClickListener(new View.OnClickListener(){
+// @Override
+// public void onClick(View view) {
+// if (cbMainService.isChecked()) {
+// MainService.startMainService(MainActivity.this);
+// } else {
+// MainService.stopMainService(MainActivity.this);
+// }
+// }
+// });
+ MainService.startMainService(MainActivity.this);
+ }
+
+ //初始化view,即显示的图片
+ void initView() {
+ viewPager = findViewById(R.id.activitymainViewPager1);
+ pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
+ viewPager.setAdapter(pagerAdapter);
+ //adapter = new MyPagerAdapter(views);
+ //viewPager = findViewById(R.id.activitymainViewPager1);
+ //viewPager.setAdapter(adapter);
+ //linearLayout = findViewById(R.id.activitymainLinearLayout1);
+ //initPoint();//初始化页面下方的点
+ viewPager.setOnPageChangeListener(this);
+
+ }
+
+ //初始化所要显示的布局
+ void initData() {
+ ViewPager viewPager = findViewById(R.id.activitymainViewPager1);
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ View view1 = inflater.inflate(R.layout.fragment_call, viewPager, false);
+ View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false);
+ View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false);
+
+ views = new ArrayList<>();
+ views.add(view1);
+ views.add(view2);
+ views.add(view3);
+ }
+
+// void initPoint() {
+// imageViews = new ImageView[5];//实例化5个图片
+// for (int i = 0; i < linearLayout.getChildCount(); i++) {
+// imageViews[i] = (ImageView) linearLayout.getChildAt(i);
+// imageViews[i].setImageResource(R.drawable.ic_launcher);
+// imageViews[i].setOnClickListener(this);//点击导航点,即可跳转
+// imageViews[i].setTag(i);//重复利用实例化的对象
+// }
+// currentPoint = 0;//默认第一个坐标
+// imageViews[currentPoint].setImageResource(R.drawable.ic_launcher);
+// }
+
+ //OnPageChangeListener接口要实现的三个方法
+ /* onPageScrollStateChanged(int state)
+ 此方法是在状态改变的时候调用,其中state这个参数有三种状态:
+ SCROLL_STATE_DRAGGING(1)表示用户手指“按在屏幕上并且开始拖动”的状态
+ (手指按下但是还没有拖动的时候还不是这个状态,只有按下并且手指开始拖动后log才打出。)
+ SCROLL_STATE_IDLE(0)滑动动画做完的状态。
+ SCROLL_STATE_SETTLING(2)在“手指离开屏幕”的状态。*/
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ /* onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
+ 当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为:
+
+ position :当前页面,即你点击滑动的页面(从A滑B,则是A页面的position。
+ positionOffset:当前页面偏移的百分比
+ positionOffsetPixels:当前页面偏移的像素位置*/
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+ /* onPageSelected(int position)
+ 此方法是页面滑动完后得到调用,position是你当前选中的页面的Position(位置编号)
+ (从A滑动到B,就是B的position)*/
+ public void onPageSelected(int position) {
+
+// ImageView preView = imageViews[currentPoint];
+// preView.setImageResource(R.drawable.ic_launcher);
+// ImageView currView = imageViews[position];
+// currView.setImageResource(R.drawable.ic_launcher);
+// currentPoint = position;
+ }
+
+ //小圆点点击事件
+ @Override
+ public void onClick(View v) {
+ // TODO Auto-generated method stub
+ //通过getTag(),可以判断是哪个控件
+// int i = (Integer) v.getTag();
+// viewPager.setCurrentItem(i);//直接跳转到某一个页面的情况
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ //setSubTitle("");
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ LogUtils.d(TAG, "onDestroy() SOS");
+ }
+
+
+
+ //
+ // 处理传入的 Intent 数据
+ //
+// boolean prosessIntents(Intent intent) {
+// if (intent == null
+// || intent.getAction() == null
+// || intent.getAction().equals(""))
+// return false;
+//
+// if (intent.getAction().equals(StringToQrCodeView.ACTION_UNITTEST_QRCODE)) {
+// try {
+// WinBollActivity clazzActivity = UnitTestActivity.class.newInstance();
+// String tag = clazzActivity.getTag();
+// LogUtils.d(TAG, "String tag = clazzActivity.getTag(); tag " + tag);
+// Intent subIntent = new Intent(this, UnitTestActivity.class);
+// subIntent.setAction(intent.getAction());
+// File file = new File(getCacheDir(), UUID.randomUUID().toString());
+// //取出文件uri
+// Uri uri = intent.getData();
+// if (uri == null) {
+// uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
+// }
+// //获取文件真实地址
+// String szSrcPath = UriUtils.getFileFromUri(getApplication(), uri);
+// if (TextUtils.isEmpty(szSrcPath)) {
+// return false;
+// }
+//
+// Files.copy(Paths.get(szSrcPath), Paths.get(file.getPath()));
+// //startWinBollActivity(subIntent, tag);
+// WinBollActivityManager.getInstance(this).startWinBollActivity(this, subIntent, UnitTestActivity.class);
+// } catch (IllegalAccessException | InstantiationException | IOException e) {
+// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
+// // 函数处理异常返回失败
+// return false;
+// }
+// } else {
+// LogUtils.d(TAG, "prosessIntents|" + intent.getAction() + "|yet");
+// return false;
+// }
+// return true;
+// }
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ @Override
+ public Toolbar initToolBar() {
+ return findViewById(R.id.activitymainToolbar1);
+ }
+
+ @Override
+ public boolean isAddWinBollToolBar() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnableDisplayHomeAsUp() {
+ return false;
+ }
+
+ @Override
+ public void onBackPressed() {
+ exit();
+ }
+
+ void exit() {
+ YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){
+
+ @Override
+ public void onYes() {
+ WinBollActivityManager.getInstance(getApplicationContext()).finishAll();
+ }
+
+ @Override
+ public void onNo() {
+ }
+ };
+ YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.toolbar_main, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.item_call) {
+ Intent intent = new Intent(this, CallActivity.class);
+ startActivity(intent);
+ //WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
+ } else if (item.getItemId() == R.id.item_settings) {
+ Intent intent = new Intent(this, SettingsActivity.class);
+ startActivity(intent);
+ //WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
+ }
+// } else
+// if (item.getItemId() == R.id.item_exit) {
+// exit();
+// return true;
+// }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ /**
+ * Android M 及以上检查是否是系统默认电话应用
+ */
+ public boolean isDefaultPhoneCallApp() {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);
+ if (manger != null && manger.getDefaultDialerPackage() != null) {
+ return manger.getDefaultDialerPackage().equals(getPackageName());
+ }
+ }
+ return false;
+ }
+
+ public boolean isServiceRunning(Class> serviceClass) {
+ ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ if (manager == null) return false;
+
+ for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
+ Integer.MAX_VALUE)) {
+ if (serviceClass.getName().equals(service.service.getClassName())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+// switch (resultCode) {
+// case REQUEST_HOME_ACTIVITY : {
+// LogUtils.d(TAG, "REQUEST_HOME_ACTIVITY");
+// break;
+// }
+// case REQUEST_ABOUT_ACTIVITY : {
+// LogUtils.d(TAG, "REQUEST_ABOUT_ACTIVITY");
+// break;
+// }
+// default : {
+// super.onActivityResult(requestCode, resultCode, data);
+// }
+// }
+ if (requestCode == DIALER_REQUEST_CODE) {
+ if (resultCode == Activity.RESULT_OK) {
+ Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/PhoneCallManager.java b/contacts/src/main/java/cc/winboll/studio/contacts/PhoneCallManager.java
new file mode 100644
index 0000000..7f7292f
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/PhoneCallManager.java
@@ -0,0 +1,35 @@
+package cc.winboll.studio.contacts;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 21:14:52
+ * @Describe PhoneCallManager
+ */
+
+import android.telecom.Call;
+import android.telecom.VideoProfile;
+
+public class PhoneCallManager {
+
+ public static final String TAG = "PhoneCallManager";
+
+ public static Call call;
+
+ /**
+ * 接听电话
+ */
+ public void answer() {
+ if (call != null) {
+ call.answer(VideoProfile.STATE_AUDIO_ONLY);
+ }
+ }
+
+ /**
+ * 断开电话,包括来电时的拒接以及接听后的挂断
+ */
+ public void disconnect() {
+ if (call != null) {
+ call.disconnect();
+ }
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/activities/CallActivity.java b/contacts/src/main/java/cc/winboll/studio/contacts/activities/CallActivity.java
new file mode 100644
index 0000000..e8f8e3e
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/activities/CallActivity.java
@@ -0,0 +1,118 @@
+package cc.winboll.studio.contacts.activities;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 17:15:46
+ * @Describe 拨号窗口
+ */
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import cc.winboll.studio.contacts.MainActivity;
+import cc.winboll.studio.contacts.R;
+
+public class CallActivity extends AppCompatActivity {
+ public static final String TAG = "CallActivity";
+
+ private static final int REQUEST_CALL_PHONE = 1;
+ private EditText phoneNumberEditText;
+ private TextView callStatusTextView;
+ private TelephonyManager telephonyManager;
+ private MyPhoneStateListener phoneStateListener;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ //setContentView(R.layout.activity_main);
+ setContentView(R.layout.activity_call);
+
+ phoneNumberEditText = findViewById(R.id.phone_number);
+ Button dialButton = findViewById(R.id.dial_button);
+ callStatusTextView = findViewById(R.id.call_status);
+
+ dialButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String phoneNumber = phoneNumberEditText.getText().toString().trim();
+ if (!phoneNumber.isEmpty()) {
+ if (ContextCompat.checkSelfPermission(CallActivity.this, Manifest.permission.CALL_PHONE)
+ != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(CallActivity.this,
+ new String[]{Manifest.permission.CALL_PHONE},
+ REQUEST_CALL_PHONE);
+ } else {
+ dialPhoneNumber(phoneNumber);
+ }
+ } else {
+ Toast.makeText(CallActivity.this, "请输入电话号码", Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+
+ // 初始化TelephonyManager和PhoneStateListener
+ telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
+ phoneStateListener = new MyPhoneStateListener();
+ telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == REQUEST_CALL_PHONE) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ String phoneNumber = phoneNumberEditText.getText().toString().trim();
+ dialPhoneNumber(phoneNumber);
+ } else {
+ Toast.makeText(this, "未授予拨打电话权限", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ private void dialPhoneNumber(String phoneNumber) {
+ Intent intent = new Intent(Intent.ACTION_CALL);
+ intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ startActivity(intent);
+ }
+
+ private class MyPhoneStateListener extends PhoneStateListener {
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ switch (state) {
+ case TelephonyManager.CALL_STATE_IDLE:
+ callStatusTextView.setText("电话已挂断");
+ break;
+ case TelephonyManager.CALL_STATE_OFFHOOK:
+ callStatusTextView.setText("正在通话中");
+ break;
+ case TelephonyManager.CALL_STATE_RINGING:
+ callStatusTextView.setText("来电: " + incomingNumber);
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ // 取消监听
+ if (telephonyManager != null) {
+ telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
+ }
+ }
+}
+
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/activities/DialerActivity.java b/contacts/src/main/java/cc/winboll/studio/contacts/activities/DialerActivity.java
new file mode 100644
index 0000000..ed1afa8
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/activities/DialerActivity.java
@@ -0,0 +1,40 @@
+package cc.winboll.studio.contacts.activities;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 20:18:26
+ */
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import androidx.appcompat.app.AppCompatActivity;
+import cc.winboll.studio.contacts.R;
+
+public class DialerActivity extends AppCompatActivity {
+
+ public static final String TAG = "DialerActivity";
+
+ private EditText phoneNumberEditText;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_dialer);
+
+ phoneNumberEditText = findViewById(R.id.phone_number_edit_text);
+ Button dialButton = findViewById(R.id.dial_button);
+
+ dialButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String phoneNumber = phoneNumberEditText.getText().toString();
+ Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber));
+ startActivity(intent);
+ }
+ });
+ }
+}
+
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/activities/SettingsActivity.java b/contacts/src/main/java/cc/winboll/studio/contacts/activities/SettingsActivity.java
new file mode 100644
index 0000000..bc54d95
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/activities/SettingsActivity.java
@@ -0,0 +1,143 @@
+package cc.winboll.studio.contacts.activities;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/21 05:37:42
+ */
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Switch;
+import android.widget.Toast;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import cc.winboll.studio.contacts.R;
+import cc.winboll.studio.contacts.beans.RingTongBean;
+import cc.winboll.studio.libappbase.IWinBollActivity;
+import cc.winboll.studio.libappbase.bean.APPInfo;
+import com.hjq.toast.ToastUtils;
+import java.lang.reflect.Field;
+
+public class SettingsActivity extends AppCompatActivity implements IWinBollActivity {
+
+ public static final String TAG = "SettingsActivity";
+
+ Toolbar mToolbar;
+ Switch swSilent;
+
+ @Override
+ public APPInfo getAppInfo() {
+ return null;
+ }
+
+ @Override
+ public AppCompatActivity getActivity() {
+ return this;
+ }
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ @Override
+ public Toolbar initToolBar() {
+ return findViewById(R.id.activitymainToolbar1);
+ }
+
+ @Override
+ public boolean isAddWinBollToolBar() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnableDisplayHomeAsUp() {
+ return false;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_settings);
+
+ // 初始化工具栏
+ mToolbar = findViewById(R.id.activitymainToolbar1);
+ setSupportActionBar(mToolbar);
+ if (isEnableDisplayHomeAsUp()) {
+ // 显示后退按钮
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ getSupportActionBar().setSubtitle(getTag());
+ }
+
+ public void onDefaultPhone(View view) {
+ Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
+ startActivity(intent);
+ }
+
+ public void onCanDrawOverlays(View view) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ && !Settings.canDrawOverlays(this)) {
+ // 请求 悬浮框 权限
+ askForDrawOverlay();
+ } else {
+ ToastUtils.show("悬浮窗已开启");
+ }
+ }
+
+ private void askForDrawOverlay() {
+ AlertDialog alertDialog = new AlertDialog.Builder(this)
+ .setTitle("允许显示悬浮框")
+ .setMessage("为了使电话监听服务正常工作,请允许这项权限")
+ .setPositiveButton("去设置", new DialogInterface.OnClickListener(){
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ openDrawOverlaySettings();
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton("稍后再说", new DialogInterface.OnClickListener(){
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .create();
+
+ //noinspection ConstantConditions
+ alertDialog.getWindow().setFlags(
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+ alertDialog.show();
+ }
+
+ /**
+ * 跳转悬浮窗管理设置界面
+ */
+ private void openDrawOverlaySettings() {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ // Android M 以上引导用户去系统设置中打开允许悬浮窗
+ // 使用反射是为了用尽可能少的代码保证在大部分机型上都可用
+ try {
+ Context context = this;
+ Class clazz = Settings.class;
+ Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION");
+ Intent intent = new Intent(field.get(null).toString());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setData(Uri.parse("package:" + context.getPackageName()));
+ context.startActivity(intent);
+ } catch (Exception e) {
+ Toast.makeText(this, "请在悬浮窗管理中打开权限", Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/adapters/ImagePagerAdapter.java b/contacts/src/main/java/cc/winboll/studio/contacts/adapters/ImagePagerAdapter.java
new file mode 100644
index 0000000..974c4ce
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/adapters/ImagePagerAdapter.java
@@ -0,0 +1,14 @@
+package cc.winboll.studio.contacts.adapters;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 14:20:38
+ * @Describe ImagePagerAdapter
+ */
+public class ImagePagerAdapter {
+
+ public static final String TAG = "ImagePagerAdapter";
+
+
+
+}
\ No newline at end of file
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/adapters/MyPagerAdapter.java b/contacts/src/main/java/cc/winboll/studio/contacts/adapters/MyPagerAdapter.java
new file mode 100644
index 0000000..3212231
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/adapters/MyPagerAdapter.java
@@ -0,0 +1,42 @@
+package cc.winboll.studio.contacts.adapters;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 13:33:04
+ * @Describe MyPagerAdapter
+ */
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+import cc.winboll.studio.contacts.fragments.CallFragment;
+import cc.winboll.studio.contacts.fragments.ContactsFragment;
+import cc.winboll.studio.contacts.fragments.LogFragment;
+
+public class MyPagerAdapter extends FragmentPagerAdapter {
+ public static final String TAG = "MyPagerAdapter";
+
+ private static final int PAGE_COUNT = 3;
+
+ public MyPagerAdapter(@NonNull FragmentManager fm) {
+ super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NonNull
+ @Override
+ public Fragment getItem(int position) {
+ if(position == 1) {
+ return ContactsFragment.newInstance(position);
+ } else if(position == 2) {
+ return LogFragment.newInstance(position);
+ } else {
+ return CallFragment.newInstance(position);
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return PAGE_COUNT;
+ }
+}
+
diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/beans/MainServiceBean.java b/contacts/src/main/java/cc/winboll/studio/contacts/beans/MainServiceBean.java
similarity index 97%
rename from appbase/src/main/java/cc/winboll/studio/appbase/beans/MainServiceBean.java
rename to contacts/src/main/java/cc/winboll/studio/contacts/beans/MainServiceBean.java
index 81a3a7a..de752f3 100644
--- a/appbase/src/main/java/cc/winboll/studio/appbase/beans/MainServiceBean.java
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/beans/MainServiceBean.java
@@ -1,4 +1,4 @@
-package cc.winboll.studio.appbase.beans;
+package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen@AliYun.Com
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/beans/PhoneBlackRuleBean.java b/contacts/src/main/java/cc/winboll/studio/contacts/beans/PhoneBlackRuleBean.java
new file mode 100644
index 0000000..69550cc
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/beans/PhoneBlackRuleBean.java
@@ -0,0 +1,88 @@
+package cc.winboll.studio.contacts.beans;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/21 09:52:10
+ * @Describe 电话黑名单规则
+ */
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import cc.winboll.studio.libappbase.BaseBean;
+import java.io.IOException;
+
+public class PhoneBlackRuleBean extends BaseBean {
+
+ public static final String TAG = "PhoneBlackRuleBean";
+
+ String ruleText;
+ boolean isEnable;
+
+ public PhoneBlackRuleBean() {
+ this.ruleText = "";
+ this.isEnable = false;
+ }
+
+ public PhoneBlackRuleBean(String ruleText, boolean isEnable) {
+ this.ruleText = ruleText;
+ this.isEnable = isEnable;
+ }
+
+ public void setRuleText(String ruleText) {
+ this.ruleText = ruleText;
+ }
+
+ public String getRuleText() {
+ return ruleText;
+ }
+
+ public void setIsEnable(boolean isEnable) {
+ this.isEnable = isEnable;
+ }
+
+ public boolean isEnable() {
+ return isEnable;
+ }
+
+ @Override
+ public String getName() {
+ return PhoneBlackRuleBean.class.getName();
+ }
+
+ @Override
+ public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
+ super.writeThisToJsonWriter(jsonWriter);
+ jsonWriter.name("ruleText").value(getRuleText());
+ jsonWriter.name("isEnable").value(isEnable());
+
+ }
+
+ @Override
+ public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
+ if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
+ if (name.equals("ruleText")) {
+ setRuleText(jsonReader.nextString());
+ } else if (name.equals("isEnable")) {
+ setIsEnable(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/contacts/src/main/java/cc/winboll/studio/contacts/beans/RingTongBean.java b/contacts/src/main/java/cc/winboll/studio/contacts/beans/RingTongBean.java
new file mode 100644
index 0000000..792bce4
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/beans/RingTongBean.java
@@ -0,0 +1,74 @@
+package cc.winboll.studio.contacts.beans;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/24 18:47:11
+ * @Describe 手机铃声设置参数类
+ */
+import cc.winboll.studio.libappbase.BaseBean;
+import android.util.JsonWriter;
+import java.io.IOException;
+import android.media.AudioManager;
+import android.util.JsonReader;
+
+public class RingTongBean extends BaseBean {
+
+ public static final String TAG = "AudioRingTongBean";
+
+ // 模式
+ int ringerMode;
+
+ public RingTongBean() {
+ this.ringerMode = AudioManager.RINGER_MODE_NORMAL;
+ }
+
+ public RingTongBean(int ringerMode) {
+ this.ringerMode = ringerMode;
+ }
+
+ public void setRingerMode(int ringerMode) {
+ this.ringerMode = ringerMode;
+ }
+
+ public int getRingerMode() {
+ return ringerMode;
+ }
+
+ @Override
+ public String getName() {
+ return RingTongBean.class.getName();
+ }
+
+ @Override
+ public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
+ super.writeThisToJsonWriter(jsonWriter);
+ jsonWriter.name("ringerMode").value(getRingerMode());
+
+ }
+
+ @Override
+ public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
+ if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
+ if (name.equals("ringerMode")) {
+ setRingerMode(jsonReader.nextInt());
+ } 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/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java b/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java
new file mode 100644
index 0000000..a4f9a1e
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java
@@ -0,0 +1,76 @@
+package cc.winboll.studio.contacts.dun;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/21 06:15:10
+ * @Describe 云盾防御规则
+ */
+import cc.winboll.studio.contacts.beans.PhoneBlackRuleBean;
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+import android.content.Context;
+
+public class Rules {
+
+ public static final String TAG = "Rules";
+
+ ArrayList _PhoneBlacRuleBeanList;
+ static volatile Rules _Rules;
+ Context mContext;
+
+ Rules(Context context) {
+ mContext = context;
+ _PhoneBlacRuleBeanList = new ArrayList();
+ PhoneBlackRuleBean.loadBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
+
+ }
+ public static synchronized Rules getInstance(Context context) {
+ if (_Rules == null) {
+ _Rules = new Rules(context);
+ }
+ return _Rules;
+ }
+
+ public boolean isAllowed(String phoneNumber) {
+ // 黑名单拒接
+ for (int i = 0; i < _PhoneBlacRuleBeanList.size(); i++) {
+ if (_PhoneBlacRuleBeanList.get(i).isEnable()) {
+ String regex = _PhoneBlacRuleBeanList.get(i).getRuleText();
+ if (Pattern.matches(regex, phoneNumber)) {
+ return false;
+ }
+ }
+ }
+
+ // 手机号码允许
+ // 中国手机号码正则表达式,以1开头,第二位可以是3、4、5、6、7、8、9,后面跟9位数字
+ String regex = "^1[3-9]\\d{9}$";
+ if (Pattern.matches(regex, phoneNumber)) {
+ return true;
+ }
+
+ // 指定区号号码允许
+ regex = "^0660\\d+$";
+ if (Pattern.matches(regex, phoneNumber)) {
+ return true;
+ }
+
+ // 指定区号号码允许
+ regex = "^020\\d+$";
+ if (Pattern.matches(regex, phoneNumber)) {
+ return true;
+ }
+
+ // 其他拒接
+ return false;
+ }
+
+ public void add(String phoneRuleBlack, boolean isEnable) {
+ _PhoneBlacRuleBeanList.add(new PhoneBlackRuleBean(phoneRuleBlack, isEnable));
+ PhoneBlackRuleBean.saveBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
+ }
+
+ public ArrayList getPhoneBlacRuleBeanList() {
+ return _PhoneBlacRuleBeanList;
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/fragments/CallFragment.java b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/CallFragment.java
new file mode 100644
index 0000000..6acb172
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/CallFragment.java
@@ -0,0 +1,51 @@
+package cc.winboll.studio.contacts.fragments;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 12:57:00
+ * @Describe 拨号
+ */
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.fragment.app.Fragment;
+import cc.winboll.studio.contacts.R;
+import cc.winboll.studio.libappbase.LogView;
+import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
+import android.widget.TextView;
+
+public class CallFragment extends Fragment {
+
+ public static final String TAG = "CallFragment";
+
+ private static final String ARG_PAGE = "ARG_PAGE";
+ private int mPage;
+
+ public static CallFragment newInstance(int page) {
+ Bundle args = new Bundle();
+ args.putInt(ARG_PAGE, page);
+ CallFragment fragment = new CallFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments()!= null) {
+ mPage = getArguments().getInt(ARG_PAGE);
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_call, container, false);
+ TextView textView = view.findViewById(R.id.page_text);
+ textView.setText("这是第 " + mPage + " 页");
+ return view;
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/fragments/ContactsFragment.java b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/ContactsFragment.java
new file mode 100644
index 0000000..2f7dad6
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/ContactsFragment.java
@@ -0,0 +1,50 @@
+package cc.winboll.studio.contacts.fragments;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 12:57:50
+ * @Describe 联系人
+ */
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import cc.winboll.studio.contacts.R;
+
+public class ContactsFragment extends Fragment {
+
+ public static final String TAG = "ContactsFragment";
+
+ private static final String ARG_PAGE = "ARG_PAGE";
+ private int mPage;
+
+ public static ContactsFragment newInstance(int page) {
+ Bundle args = new Bundle();
+ args.putInt(ARG_PAGE, page);
+ ContactsFragment fragment = new ContactsFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments()!= null) {
+ mPage = getArguments().getInt(ARG_PAGE);
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_contacts, container, false);
+ TextView textView = view.findViewById(R.id.page_text);
+ textView.setText("这是第 " + mPage + " 页");
+ return view;
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/fragments/LogFragment.java b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/LogFragment.java
new file mode 100644
index 0000000..e2df22a
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/LogFragment.java
@@ -0,0 +1,50 @@
+package cc.winboll.studio.contacts.fragments;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 12:58:15
+ * @Describe 应用日志
+ */
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import cc.winboll.studio.contacts.R;
+import cc.winboll.studio.libappbase.LogView;
+
+public class LogFragment extends Fragment {
+
+ public static final String TAG = "LogFragment";
+
+ private static final String ARG_PAGE = "ARG_PAGE";
+ private int mPage;
+
+ public static LogFragment newInstance(int page) {
+ Bundle args = new Bundle();
+ args.putInt(ARG_PAGE, page);
+ LogFragment fragment = new LogFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments() != null) {
+ mPage = getArguments().getInt(ARG_PAGE);
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_log, container, false);
+ LogView logView = view.findViewById(R.id.logview);
+ logView.start();
+ return view;
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/handlers/MainServiceHandler.java b/contacts/src/main/java/cc/winboll/studio/contacts/handlers/MainServiceHandler.java
new file mode 100644
index 0000000..d19cd99
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/handlers/MainServiceHandler.java
@@ -0,0 +1,38 @@
+package cc.winboll.studio.contacts.handlers;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/14 03:51:40
+ */
+import android.os.Handler;
+import android.os.Message;
+import cc.winboll.studio.contacts.services.MainService;
+import java.lang.ref.WeakReference;
+
+public class MainServiceHandler extends Handler {
+ public static final String TAG = "MainServiceHandler";
+
+ public static final int MSG_REMINDTHREAD = 0;
+
+ WeakReference serviceWeakReference;
+ public MainServiceHandler(MainService service) {
+ serviceWeakReference = new WeakReference(service);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_REMINDTHREAD: // 处理下载完成消息,更新UI
+ {
+ // 显示提醒消息
+ //
+ //LogUtils.d(TAG, "显示提醒消息");
+ MainService mainService = serviceWeakReference.get();
+ if (mainService != null) {
+ mainService.appenMessage((String)msg.obj);
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/listenphonecall/CallListenerService.java b/contacts/src/main/java/cc/winboll/studio/contacts/listenphonecall/CallListenerService.java
new file mode 100644
index 0000000..f555114
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/listenphonecall/CallListenerService.java
@@ -0,0 +1,206 @@
+package cc.winboll.studio.contacts.listenphonecall;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.graphics.PixelFormat;
+import android.os.Build;
+import android.os.IBinder;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+import androidx.annotation.Nullable;
+import cc.winboll.studio.contacts.MainActivity;
+import cc.winboll.studio.contacts.R;
+
+
+public class CallListenerService extends Service {
+
+ private View phoneCallView;
+ private TextView tvCallNumber;
+ private Button btnOpenApp;
+
+ private WindowManager windowManager;
+ private WindowManager.LayoutParams params;
+
+ private PhoneStateListener phoneStateListener;
+ private TelephonyManager telephonyManager;
+
+ private String callNumber;
+ private boolean hasShown;
+ private boolean isCallingIn;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ initPhoneStateListener();
+
+ initPhoneCallView();
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ /**
+ * 初始化来电状态监听器
+ */
+ private void initPhoneStateListener() {
+ phoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ super.onCallStateChanged(state, incomingNumber);
+
+ callNumber = incomingNumber;
+
+ switch (state) {
+ case TelephonyManager.CALL_STATE_IDLE: // 待机,即无电话时,挂断时触发
+ dismiss();
+ break;
+
+ case TelephonyManager.CALL_STATE_RINGING: // 响铃,来电时触发
+ isCallingIn = true;
+ updateUI();
+ show();
+ break;
+
+ case TelephonyManager.CALL_STATE_OFFHOOK: // 摘机,接听或拨出电话时触发
+ updateUI();
+ show();
+ break;
+
+ default:
+ break;
+
+ }
+ }
+ };
+
+ // 设置来电监听器
+ telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
+ if (telephonyManager != null) {
+ telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+ }
+
+ }
+
+ private void initPhoneCallView() {
+ windowManager = (WindowManager) getApplicationContext()
+ .getSystemService(Context.WINDOW_SERVICE);
+ int width = windowManager.getDefaultDisplay().getWidth();
+ int height = windowManager.getDefaultDisplay().getHeight();
+
+ params = new WindowManager.LayoutParams();
+ params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ params.width = width;
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+
+ // 设置图片格式,效果为背景透明
+ params.format = PixelFormat.TRANSLUCENT;
+ // 设置 Window flag 为系统级弹框 | 覆盖表层
+ params.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
+ WindowManager.LayoutParams.TYPE_PHONE;
+
+ // 不可聚集(不响应返回键)| 全屏
+ params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+ // API 19 以上则还可以开启透明状态栏与导航栏
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ params.flags = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
+ | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+ }
+
+ FrameLayout interceptorLayout = new FrameLayout(this) {
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+
+ return true;
+ }
+ }
+
+ return super.dispatchKeyEvent(event);
+ }
+ };
+
+ phoneCallView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE))
+ .inflate(R.layout.view_phone_call, interceptorLayout);
+ tvCallNumber = phoneCallView.findViewById(R.id.tv_call_number);
+ btnOpenApp = phoneCallView.findViewById(R.id.btn_open_app);
+ btnOpenApp.setOnClickListener(new View.OnClickListener(){
+
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(getApplicationContext(), MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ CallListenerService.this.startActivity(intent);
+ }
+ });
+ }
+
+ /**
+ * 显示顶级弹框展示通话信息
+ */
+ private void show() {
+ if (!hasShown) {
+ windowManager.addView(phoneCallView, params);
+ hasShown = true;
+ }
+ }
+
+ /**
+ * 取消显示
+ */
+ private void dismiss() {
+ if (hasShown) {
+ windowManager.removeView(phoneCallView);
+ isCallingIn = false;
+ hasShown = false;
+ }
+ }
+
+ private void updateUI() {
+ tvCallNumber.setText(formatPhoneNumber(callNumber));
+
+ int callTypeDrawable = isCallingIn ? R.drawable.ic_phone_call_in : R.drawable.ic_phone_call_out;
+ tvCallNumber.setCompoundDrawablesWithIntrinsicBounds(null, null,
+ getResources().getDrawable(callTypeDrawable), null);
+ }
+
+ public static String formatPhoneNumber(String phoneNum) {
+ if (!TextUtils.isEmpty(phoneNum) && phoneNum.length() == 11) {
+ return phoneNum.substring(0, 3) + "-"
+ + phoneNum.substring(3, 7) + "-"
+ + phoneNum.substring(7);
+ }
+ return phoneNum;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/phonecallui/PhoneCallActivity.java b/contacts/src/main/java/cc/winboll/studio/contacts/phonecallui/PhoneCallActivity.java
new file mode 100644
index 0000000..eec1ab7
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/phonecallui/PhoneCallActivity.java
@@ -0,0 +1,166 @@
+package cc.winboll.studio.contacts.phonecallui;
+
+import static cc.winboll.studio.contacts.listenphonecall.CallListenerService.formatPhoneNumber;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.AppCompatActivity;
+
+import cc.winboll.studio.contacts.ActivityStack;
+import cc.winboll.studio.contacts.R;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+
+/**
+ * 提供接打电话的界面,仅支持 Android M (6.0, API 23) 及以上的系统
+ *
+ * @author aJIEw
+ */
+@RequiresApi(api = Build.VERSION_CODES.M)
+public class PhoneCallActivity extends AppCompatActivity implements View.OnClickListener {
+
+ private TextView tvCallNumberLabel;
+ private TextView tvCallNumber;
+ private TextView tvPickUp;
+ private TextView tvCallingTime;
+ private TextView tvHangUp;
+
+ private PhoneCallManager phoneCallManager;
+ private PhoneCallService.CallType callType;
+ private String phoneNumber;
+
+ private Timer onGoingCallTimer;
+ private int callingTime;
+
+ public static void actionStart(Context context, String phoneNumber,
+ PhoneCallService.CallType callType) {
+ Intent intent = new Intent(context, PhoneCallActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, callType);
+ intent.putExtra(Intent.EXTRA_PHONE_NUMBER, phoneNumber);
+ context.startActivity(intent);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_phone_call);
+
+ ActivityStack.getInstance().addActivity(this);
+
+ initData();
+
+ initView();
+ }
+
+ private void initData() {
+ phoneCallManager = new PhoneCallManager(this);
+ onGoingCallTimer = new Timer();
+ if (getIntent() != null) {
+ phoneNumber = getIntent().getStringExtra(Intent.EXTRA_PHONE_NUMBER);
+ callType = (PhoneCallService.CallType) getIntent().getSerializableExtra(Intent.EXTRA_MIME_TYPES);
+ }
+ }
+
+ private void initView() {
+ int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
+ getWindow().getDecorView().setSystemUiVisibility(uiOptions);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+
+ tvCallNumberLabel = findViewById(R.id.tv_call_number_label);
+ tvCallNumber = findViewById(R.id.tv_call_number);
+ tvPickUp = findViewById(R.id.tv_phone_pick_up);
+ tvCallingTime = findViewById(R.id.tv_phone_calling_time);
+ tvHangUp = findViewById(R.id.tv_phone_hang_up);
+
+ tvCallNumber.setText(formatPhoneNumber(phoneNumber));
+ tvPickUp.setOnClickListener(this);
+ tvHangUp.setOnClickListener(this);
+
+ // 打进的电话
+ if (callType == PhoneCallService.CallType.CALL_IN) {
+ tvCallNumberLabel.setText("来电号码");
+ tvPickUp.setVisibility(View.VISIBLE);
+ }
+ // 打出的电话
+ else if (callType == PhoneCallService.CallType.CALL_OUT) {
+ tvCallNumberLabel.setText("呼叫号码");
+ tvPickUp.setVisibility(View.GONE);
+ phoneCallManager.openSpeaker();
+ }
+
+ showOnLockScreen();
+ }
+
+ public void showOnLockScreen() {
+ this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
+ WindowManager.LayoutParams.FLAG_FULLSCREEN |
+ WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
+ WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
+ WindowManager.LayoutParams.FLAG_FULLSCREEN |
+ WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
+ WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.tv_phone_pick_up) {
+ phoneCallManager.answer();
+ tvPickUp.setVisibility(View.GONE);
+ tvCallingTime.setVisibility(View.VISIBLE);
+ onGoingCallTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ runOnUiThread(new Runnable() {
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void run() {
+ callingTime++;
+ tvCallingTime.setText("通话中:" + getCallingTime());
+ }
+ });
+ }
+ }, 0, 1000);
+ } else if (v.getId() == R.id.tv_phone_hang_up) {
+ phoneCallManager.disconnect();
+ stopTimer();
+ }
+ }
+
+ private String getCallingTime() {
+ int minute = callingTime / 60;
+ int second = callingTime % 60;
+ return (minute < 10 ? "0" + minute : minute) +
+ ":" +
+ (second < 10 ? "0" + second : second);
+ }
+
+ private void stopTimer() {
+ if (onGoingCallTimer != null) {
+ onGoingCallTimer.cancel();
+ }
+
+ callingTime = 0;
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ phoneCallManager.destroy();
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/phonecallui/PhoneCallManager.java b/contacts/src/main/java/cc/winboll/studio/contacts/phonecallui/PhoneCallManager.java
new file mode 100644
index 0000000..6fd2f52
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/phonecallui/PhoneCallManager.java
@@ -0,0 +1,62 @@
+package cc.winboll.studio.contacts.phonecallui;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.Build;
+import android.telecom.Call;
+import android.telecom.VideoProfile;
+import androidx.annotation.RequiresApi;
+import cc.winboll.studio.contacts.dun.Rules;
+
+
+@RequiresApi(api = Build.VERSION_CODES.M)
+public class PhoneCallManager {
+
+ public static Call call;
+
+ private Context context;
+ private AudioManager audioManager;
+
+ public PhoneCallManager(Context context) {
+ this.context = context;
+ audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ /**
+ * 接听电话
+ */
+ public void answer() {
+ if (call != null) {
+ call.answer(VideoProfile.STATE_AUDIO_ONLY);
+ openSpeaker();
+ }
+ }
+
+ /**
+ * 断开电话,包括来电时的拒接以及接听后的挂断
+ */
+ public void disconnect() {
+ if (call != null) {
+ call.disconnect();
+ }
+ }
+
+ /**
+ * 打开免提
+ */
+ public void openSpeaker() {
+ if (audioManager != null) {
+ audioManager.setMode(AudioManager.MODE_IN_CALL);
+ audioManager.setSpeakerphoneOn(true);
+ }
+ }
+
+ /**
+ * 销毁资源
+ */
+ public void destroy() {
+ call = null;
+ context = null;
+ audioManager = null;
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/phonecallui/PhoneCallService.java b/contacts/src/main/java/cc/winboll/studio/contacts/phonecallui/PhoneCallService.java
new file mode 100644
index 0000000..88eb69e
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/phonecallui/PhoneCallService.java
@@ -0,0 +1,103 @@
+package cc.winboll.studio.contacts.phonecallui;
+
+/**
+ * 监听电话通信状态的服务,实现该类的同时必须提供电话管理的 UI
+ *
+ * @author aJIEw
+ * @see PhoneCallActivity
+ * @see android.telecom.InCallService
+ */
+import android.media.AudioManager;
+import android.os.Build;
+import android.telecom.Call;
+import android.telecom.InCallService;
+import androidx.annotation.RequiresApi;
+import cc.winboll.studio.contacts.ActivityStack;
+import cc.winboll.studio.contacts.beans.RingTongBean;
+import cc.winboll.studio.contacts.dun.Rules;
+import cc.winboll.studio.libappbase.LogUtils;
+
+@RequiresApi(api = Build.VERSION_CODES.M)
+public class PhoneCallService extends InCallService {
+
+ public static final String TAG = "PhoneCallService";
+
+ private volatile int originalRingVolume;
+
+ private final Call.Callback callback = new Call.Callback() {
+ @Override
+ public void onStateChanged(Call call, int state) {
+ super.onStateChanged(call, state);
+ switch (state) {
+ case Call.STATE_ACTIVE: {
+ break;
+ }
+
+ case Call.STATE_DISCONNECTED: {
+ ActivityStack.getInstance().finishActivity(PhoneCallActivity.class);
+ break;
+ }
+
+ }
+ }
+ };
+
+ @Override
+ public void onCallAdded(Call call) {
+ super.onCallAdded(call);
+
+ call.registerCallback(callback);
+ PhoneCallManager.call = call;
+ CallType callType = null;
+
+ if (call.getState() == Call.STATE_RINGING) {
+ callType = CallType.CALL_IN;
+ } else if (call.getState() == Call.STATE_CONNECTING) {
+ callType = CallType.CALL_OUT;
+ }
+
+ if (callType != null) {
+ Call.Details details = call.getDetails();
+ String phoneNumber = details.getHandle().getSchemeSpecificPart();
+
+ // 记录原始铃声音量
+ AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
+ originalRingVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
+ // 检查电话接收规则
+ if (!Rules.getInstance(this).isAllowed(phoneNumber)) {
+ // 预先静音
+ audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
+ // 断开电话
+ call.disconnect();
+ // 停顿1秒,预防第一声铃声响动
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ LogUtils.d(TAG, "");
+ }
+ // 恢复铃声音量
+ audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0);
+ // 屏蔽电话结束
+ return;
+ }
+ // 正常接听电话
+ PhoneCallActivity.actionStart(this, phoneNumber, callType);
+ }
+ }
+
+ @Override
+ public void onCallRemoved(Call call) {
+ super.onCallRemoved(call);
+
+ call.unregisterCallback(callback);
+ PhoneCallManager.call = null;
+ AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
+ // 恢复铃声音量
+ audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0);
+ }
+
+ public enum CallType {
+ CALL_IN,
+ CALL_OUT,
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/receivers/MainReceiver.java b/contacts/src/main/java/cc/winboll/studio/contacts/receivers/MainReceiver.java
new file mode 100644
index 0000000..d1dfb52
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/receivers/MainReceiver.java
@@ -0,0 +1,49 @@
+package cc.winboll.studio.contacts.receivers;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/13 06:58:04
+ * @Describe 主要广播接收器
+ */
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.util.Log;
+import cc.winboll.studio.contacts.services.MainService;
+import com.hjq.toast.ToastUtils;
+import java.lang.ref.WeakReference;
+import cc.winboll.studio.libappbase.LogUtils;
+
+public class MainReceiver extends BroadcastReceiver {
+
+ public static final String TAG = "MainReceiver";
+ public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
+ WeakReference mwrService;
+
+ public MainReceiver(MainService service) {
+ mwrService = new WeakReference(service);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String szAction = intent.getAction();
+ if (szAction.equals(ACTION_BOOT_COMPLETED)) {
+ ToastUtils.show("ACTION_BOOT_COMPLETED");
+ MainService.startMainService(context);
+ } else {
+ ToastUtils.show(szAction);
+ }
+ }
+
+ // 注册 Receiver
+ //
+ public void registerAction(Context context) {
+ IntentFilter filter=new IntentFilter();
+ filter.addAction(ACTION_BOOT_COMPLETED);
+ //filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ context.registerReceiver(this, filter);
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/services/AssistantService.java b/contacts/src/main/java/cc/winboll/studio/contacts/services/AssistantService.java
new file mode 100644
index 0000000..a031f4b
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/services/AssistantService.java
@@ -0,0 +1,139 @@
+package cc.winboll.studio.contacts.services;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/14 03:38:31
+ * @Describe 守护进程服务
+ */
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import cc.winboll.studio.contacts.beans.MainServiceBean;
+import cc.winboll.studio.contacts.services.MainService;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libappbase.SOS;
+import cc.winboll.studio.libappbase.bean.APPSOSBean;
+
+public class AssistantService extends Service {
+
+ public static final String TAG = "AssistantService";
+
+ MainServiceBean mMainServiceBean;
+ MyServiceConnection mMyServiceConnection;
+ MainService mMainService;
+ boolean isBound = false;
+ volatile boolean isThreadAlive = false;
+
+ public synchronized void setIsThreadAlive(boolean isThreadAlive) {
+ LogUtils.d(TAG, "setIsThreadAlive(...)");
+ LogUtils.d(TAG, String.format("isThreadAlive %s", isThreadAlive));
+ this.isThreadAlive = isThreadAlive;
+ }
+
+ public boolean isThreadAlive() {
+ return isThreadAlive;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new MyBinder();
+ }
+
+ @Override
+ public void onCreate() {
+ LogUtils.d(TAG, "onCreate");
+ super.onCreate();
+
+ //mMyBinder = new MyBinder();
+ if (mMyServiceConnection == null) {
+ mMyServiceConnection = new MyServiceConnection();
+ }
+ // 设置运行参数
+ setIsThreadAlive(false);
+ assistantService();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ LogUtils.d(TAG, "call onStartCommand(...)");
+ assistantService();
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ //LogUtils.d(TAG, "onDestroy");
+ setIsThreadAlive(false);
+ // 解除绑定
+ if (isBound) {
+ unbindService(mMyServiceConnection);
+ isBound = false;
+ }
+ super.onDestroy();
+ }
+
+ // 运行服务内容
+ //
+ void assistantService() {
+ LogUtils.d(TAG, "assistantService()");
+ mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
+ LogUtils.d(TAG, String.format("mMainServiceBean.isEnable() %s", mMainServiceBean.isEnable()));
+ if (mMainServiceBean.isEnable()) {
+ LogUtils.d(TAG, String.format("mIsThreadAlive %s", isThreadAlive()));
+ if (isThreadAlive() == false) {
+ // 设置运行状态
+ setIsThreadAlive(true);
+ // 唤醒和绑定主进程
+ wakeupAndBindMain();
+ }
+ }
+ }
+
+ // 唤醒和绑定主进程
+ //
+ void wakeupAndBindMain() {
+ LogUtils.d(TAG, "wakeupAndBindMain()");
+ // 绑定服务的Intent
+ Intent intent = new Intent(this, MainService.class);
+ startService(new Intent(this, MainService.class));
+ bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
+
+// startService(new Intent(this, MainService.class));
+// bindService(new Intent(AssistantService.this, MainService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
+ }
+
+ // 主进程与守护进程连接时需要用到此类
+ //
+ class MyServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ LogUtils.d(TAG, "onServiceConnected(...)");
+ MainService.MyBinder binder = (MainService.MyBinder) service;
+ mMainService = binder.getService();
+ isBound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ LogUtils.d(TAG, "onServiceDisconnected(...)");
+ mMainServiceBean = MainServiceBean.loadBean(AssistantService.this, MainServiceBean.class);
+ if (mMainServiceBean.isEnable()) {
+ wakeupAndBindMain();
+ }
+ isBound = false;
+ mMainService = null;
+ }
+ }
+
+ // 用于返回服务实例的Binder
+ public class MyBinder extends Binder {
+ AssistantService getService() {
+ LogUtils.d(TAG, "AssistantService MyBinder getService()");
+ return AssistantService.this;
+ }
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/services/MainService.java b/contacts/src/main/java/cc/winboll/studio/contacts/services/MainService.java
new file mode 100644
index 0000000..3eef1a4
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/services/MainService.java
@@ -0,0 +1,247 @@
+package cc.winboll.studio.contacts.services;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/13 06:56:41
+ * @Describe 拨号主服务
+ * 参考:
+ * 进程保活-双进程守护的正确姿势
+ * https://blog.csdn.net/sinat_35159441/article/details/75267380
+ * Android Service之onStartCommand方法研究
+ * https://blog.csdn.net/cyp331203/article/details/38920491
+ */
+import android.app.Service;
+import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import cc.winboll.studio.contacts.beans.MainServiceBean;
+import cc.winboll.studio.contacts.handlers.MainServiceHandler;
+import cc.winboll.studio.contacts.receivers.MainReceiver;
+import cc.winboll.studio.contacts.services.MainService;
+import cc.winboll.studio.contacts.threads.MainServiceThread;
+import cc.winboll.studio.contacts.widgets.APPStatusWidget;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libappbase.SOS;
+import cc.winboll.studio.libappbase.bean.APPSOSBean;
+import cc.winboll.studio.contacts.dun.Rules;
+import android.media.AudioManager;
+import com.hjq.toast.ToastUtils;
+
+public class MainService extends Service {
+
+ public static final String TAG = "MainService";
+
+ public static final int MSG_UPDATE_STATUS = 0;
+
+ static MainService _mControlCenterService;
+
+ volatile boolean isServiceRunning;
+
+ MainServiceBean mMainServiceBean;
+ MainServiceThread mMainServiceThread;
+ MainServiceHandler mMainServiceHandler;
+ MyServiceConnection mMyServiceConnection;
+ AssistantService mAssistantService;
+ boolean isBound = false;
+ MainReceiver mMainReceiver;
+
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new MyBinder();
+ }
+
+ public MainServiceThread getRemindThread() {
+ return mMainServiceThread;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ LogUtils.d(TAG, "onCreate()");
+ _mControlCenterService = MainService.this;
+ isServiceRunning = false;
+ mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
+
+ if (mMyServiceConnection == null) {
+ mMyServiceConnection = new MyServiceConnection();
+ }
+ mMainServiceHandler = new MainServiceHandler(this);
+
+
+
+ // 运行服务内容
+ mainService();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ LogUtils.d(TAG, "onStartCommand(...)");
+ // 运行服务内容
+ mainService();
+ return (mMainServiceBean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
+ }
+
+ // 运行服务内容
+ //
+ void mainService() {
+ LogUtils.d(TAG, "mainService()");
+ mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
+ if (mMainServiceBean.isEnable() && isServiceRunning == false) {
+ LogUtils.d(TAG, "mainService() start running");
+ isServiceRunning = true;
+ // 唤醒守护进程
+ wakeupAndBindAssistant();
+ // 召唤 WinBoll APP 绑定本服务
+ SOS.bindToAPPService(this, new APPSOSBean(getPackageName(), MainService.class.getName()));
+
+ if (mMainReceiver == null) {
+ // 注册广播接收器
+ mMainReceiver = new MainReceiver(this);
+ mMainReceiver.registerAction(this);
+ }
+
+ Rules.getInstance(this);
+ //Rules.getInstance(this).add("18888888888", true);
+ //Rules.getInstance(this).add("16769764848", true);
+
+ startPhoneCallListener();
+
+ MainServiceThread.getInstance(this, mMainServiceHandler).start();
+
+ LogUtils.i(TAG, "Main Service Is Start.");
+ }
+ }
+
+ // 唤醒和绑定守护进程
+ //
+ void wakeupAndBindAssistant() {
+ LogUtils.d(TAG, "wakeupAndBindAssistant()");
+// if (ServiceUtils.isServiceAlive(getApplicationContext(), AssistantService.class.getName()) == false) {
+// startService(new Intent(MainService.this, AssistantService.class));
+// //LogUtils.d(TAG, "call wakeupAndBindAssistant() : Binding... AssistantService");
+// bindService(new Intent(MainService.this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
+// }
+ Intent intent = new Intent(this, AssistantService.class);
+ startService(intent);
+ // 绑定服务的Intent
+ //Intent intent = new Intent(this, AssistantService.class);
+ bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
+
+// Intent intent = new Intent(this, AssistantService.class);
+// startService(intent);
+// LogUtils.d(TAG, "startService(intent)");
+// bindService(new Intent(this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
+ }
+
+ void startPhoneCallListener() {
+ Intent callListener = new Intent(this, CallListenerService.class);
+ startService(callListener);
+ }
+
+ @Override
+ public void onDestroy() {
+ //LogUtils.d(TAG, "onDestroy");
+ mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
+ //LogUtils.d(TAG, "onDestroy done");
+ if (mMainServiceBean.isEnable() == false) {
+ // 设置运行状态
+ isServiceRunning = false;// 解除绑定
+ if (isBound) {
+ unbindService(mMyServiceConnection);
+ isBound = false;
+ }
+ // 停止守护进程
+ Intent intent = new Intent(this, AssistantService.class);
+ stopService(intent);
+ // 停止Receiver
+ if (mMainReceiver != null) {
+ unregisterReceiver(mMainReceiver);
+ mMainReceiver = null;
+ }
+ // 停止前台通知栏
+ stopForeground(true);
+
+ // 停止主要进程
+ MainServiceThread.getInstance(this, mMainServiceHandler).setIsExit(true);
+
+ }
+
+ super.onDestroy();
+ }
+
+ // 主进程与守护进程连接时需要用到此类
+ //
+ private class MyServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ LogUtils.d(TAG, "onServiceConnected(...)");
+ AssistantService.MyBinder binder = (AssistantService.MyBinder) service;
+ mAssistantService = binder.getService();
+ isBound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ LogUtils.d(TAG, "onServiceDisconnected(...)");
+ if (mMainServiceBean.isEnable()) {
+ // 唤醒守护进程
+ wakeupAndBindAssistant();
+ SOS.sosWinBollService(getApplicationContext(), new APPSOSBean(getPackageName(), MainService.class.getName()));
+ }
+ isBound = false;
+ mAssistantService = null;
+ }
+
+ }
+
+
+ // 用于返回服务实例的Binder
+ public class MyBinder extends Binder {
+ MainService getService() {
+ LogUtils.d(TAG, "MainService MyBinder getService()");
+ return MainService.this;
+ }
+ }
+
+// //
+// // 启动服务
+// //
+// public static void startControlCenterService(Context context) {
+// Intent intent = new Intent(context, MainService.class);
+// context.startForegroundService(intent);
+// }
+//
+// //
+// // 停止服务
+// //
+// public static void stopControlCenterService(Context context) {
+// Intent intent = new Intent(context, MainService.class);
+// context.stopService(intent);
+// }
+
+ public void appenMessage(String message) {
+ LogUtils.d(TAG, String.format("Message : %s", message));
+ }
+
+ public static void stopMainService(Context context) {
+ LogUtils.d(TAG, "stopMainService");
+ MainServiceBean bean = new MainServiceBean();
+ bean.setIsEnable(false);
+ MainServiceBean.saveBean(context, bean);
+ context.stopService(new Intent(context, MainService.class));
+ }
+
+ public static void startMainService(Context context) {
+ LogUtils.d(TAG, "startMainService");
+ MainServiceBean bean = new MainServiceBean();
+ bean.setIsEnable(true);
+ MainServiceBean.saveBean(context, bean);
+ context.startService(new Intent(context, MainService.class));
+ }
+}
+
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/threads/MainServiceThread.java b/contacts/src/main/java/cc/winboll/studio/contacts/threads/MainServiceThread.java
new file mode 100644
index 0000000..0dfd918
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/threads/MainServiceThread.java
@@ -0,0 +1,77 @@
+package cc.winboll.studio.contacts.threads;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/14 03:46:44
+ */
+import android.content.Context;
+import cc.winboll.studio.contacts.handlers.MainServiceHandler;
+import cc.winboll.studio.contacts.services.MainService;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libappbase.SOS;
+import cc.winboll.studio.libappbase.bean.APPSOSBean;
+import com.hjq.toast.ToastUtils;
+import java.lang.ref.WeakReference;
+
+public class MainServiceThread extends Thread {
+
+ public static final String TAG = "MainServiceThread";
+
+ volatile static MainServiceThread _MainServiceThread;
+ // 控制线程是否退出的标志
+ volatile boolean isExit = false;
+ volatile boolean isStarted = false;
+ Context mContext;
+ // 服务Handler, 用于线程发送消息使用
+ WeakReference mwrMainServiceHandler;
+
+ MainServiceThread(Context context, MainServiceHandler handler) {
+ mContext = context;
+ mwrMainServiceHandler = new WeakReference(handler);
+ }
+
+ public void setIsExit(boolean isExit) {
+ this.isExit = isExit;
+ }
+
+ public boolean isExit() {
+ return isExit;
+ }
+
+ public void setIsStarted(boolean isStarted) {
+ this.isStarted = isStarted;
+ }
+
+ public boolean isStarted() {
+ return isStarted;
+ }
+
+ public static MainServiceThread getInstance(Context context, MainServiceHandler handler) {
+ if (_MainServiceThread != null) {
+ _MainServiceThread.setIsExit(true);
+ }
+ _MainServiceThread = new MainServiceThread(context, handler);
+ return _MainServiceThread;
+ }
+
+ @Override
+ public void run() {
+ if (isStarted == false) {
+ isStarted = true;
+ LogUtils.d(TAG, "run()");
+
+ while (!isExit()) {
+ //ToastUtils.show("run");
+ //LogUtils.d(TAG, "run()");
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
+ }
+ }
+ _MainServiceThread = null;
+ LogUtils.d(TAG, "run() exit");
+ }
+ }
+
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/widgets/APPStatusWidget.java b/contacts/src/main/java/cc/winboll/studio/contacts/widgets/APPStatusWidget.java
new file mode 100644
index 0000000..8a9b7c0
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/widgets/APPStatusWidget.java
@@ -0,0 +1,76 @@
+package cc.winboll.studio.contacts.widgets;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/17 14:49:31
+ * @Describe APPStatusWidget
+ */
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.RemoteViews;
+import cc.winboll.studio.contacts.R;
+import cc.winboll.studio.contacts.threads.MainServiceThread;
+import cc.winboll.studio.libappbase.LogUtils;
+import com.hjq.toast.ToastUtils;
+
+public class APPStatusWidget extends AppWidgetProvider {
+
+ public static final String TAG = "APPSOSReportWidget";
+
+ public static final String ACTION_STATUS_ACTIVE = "cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_ACTIVE";
+ public static final String ACTION_STATUS_NOACTIVE = "cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_NOACTIVE";
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ for (int appWidgetId : appWidgetIds) {
+ updateAppWidget(context, appWidgetManager, appWidgetId);
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ super.onReceive(context, intent);
+ if (intent.getAction().equals(ACTION_STATUS_ACTIVE)) {
+ AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
+ int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPStatusWidget.class));
+ for (int appWidgetId : appWidgetIds) {
+ updateAppWidget(context, appWidgetManager, appWidgetId);
+ }
+ } else if (intent.getAction().equals(ACTION_STATUS_NOACTIVE)) {
+ AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
+ int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPStatusWidget.class));
+ for (int appWidgetId : appWidgetIds) {
+ updateAppWidget(context, appWidgetManager, appWidgetId);
+ }
+ }
+
+ }
+
+ private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
+ LogUtils.d(TAG, "updateAppWidget(...)");
+
+ RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
+ //设置按钮点击事件
+ Intent intentAppButton = new Intent(context, APPStatusWidgetClickListener.class);
+ intentAppButton.setAction(APPStatusWidgetClickListener.ACTION_APPICON_CLICK);
+ PendingIntent pendingIntentAppButton = PendingIntent.getBroadcast(context, 0, intentAppButton, PendingIntent.FLAG_UPDATE_CURRENT);
+ views.setOnClickPendingIntent(R.id.widgetlayoutImageView1, pendingIntentAppButton);
+
+// boolean isActive = !MainServiceThread.isExist();
+// if (isActive) {
+// views.setImageViewResource(R.id.widgetlayoutImageView1, R.drawable.ic_launcher);
+// } else {
+// views.setImageViewResource(R.id.widgetlayoutImageView1, R.drawable.ic_launcher_disable);
+//
+// }
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+ }
+
+ public static void onAPPStatusWidgetClick(Context context) {
+ ToastUtils.show("onAPPStatusWidgetClick");
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/widgets/APPStatusWidgetClickListener.java b/contacts/src/main/java/cc/winboll/studio/contacts/widgets/APPStatusWidgetClickListener.java
new file mode 100644
index 0000000..016ccb2
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/widgets/APPStatusWidgetClickListener.java
@@ -0,0 +1,32 @@
+package cc.winboll.studio.contacts.widgets;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/17 14:59:55
+ * @Describe WidgetButtonClickListener
+ */
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.Toast;
+import cc.winboll.studio.libappbase.LogUtils;
+
+public class APPStatusWidgetClickListener extends BroadcastReceiver {
+
+ public static final String TAG = "APPStatusWidgetClickListener";
+
+ public static final String ACTION_APPICON_CLICK = "cc.winboll.studio.contacts.widgets.APPStatusWidgetClickListener.ACTION_APPICON_CLICK";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ LogUtils.d(TAG, String.format("action %s", action));
+ return;
+ }
+ if (action.equals(ACTION_APPICON_CLICK)) {
+ LogUtils.d(TAG, "ACTION_APPICON_CLICK");
+ Toast.makeText(context, "ACTION_APPICON_CLICK", Toast.LENGTH_SHORT).show();
+ }
+ }
+}
diff --git a/contacts/src/main/res/drawable/ic_launcher.xml b/contacts/src/main/res/drawable/ic_launcher.xml
new file mode 100644
index 0000000..d4d1eaf
--- /dev/null
+++ b/contacts/src/main/res/drawable/ic_launcher.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/contacts/src/main/res/drawable/ic_launcher_background.xml b/contacts/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..9486190
--- /dev/null
+++ b/contacts/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/drawable/ic_launcher_disable.xml b/contacts/src/main/res/drawable/ic_launcher_disable.xml
new file mode 100644
index 0000000..9a31905
--- /dev/null
+++ b/contacts/src/main/res/drawable/ic_launcher_disable.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/contacts/src/main/res/drawable/ic_launcher_foreground.xml b/contacts/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..872b04e
--- /dev/null
+++ b/contacts/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/contacts/src/main/res/drawable/ic_launcher_foreground_disable.xml b/contacts/src/main/res/drawable/ic_launcher_foreground_disable.xml
new file mode 100644
index 0000000..763b72c
--- /dev/null
+++ b/contacts/src/main/res/drawable/ic_launcher_foreground_disable.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/contacts/src/main/res/drawable/ic_phone_call_in.xml b/contacts/src/main/res/drawable/ic_phone_call_in.xml
new file mode 100644
index 0000000..792377d
--- /dev/null
+++ b/contacts/src/main/res/drawable/ic_phone_call_in.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/contacts/src/main/res/drawable/ic_phone_call_out.xml b/contacts/src/main/res/drawable/ic_phone_call_out.xml
new file mode 100644
index 0000000..833d25a
--- /dev/null
+++ b/contacts/src/main/res/drawable/ic_phone_call_out.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/contacts/src/main/res/drawable/ic_phone_hang_up.xml b/contacts/src/main/res/drawable/ic_phone_hang_up.xml
new file mode 100644
index 0000000..98b57b2
--- /dev/null
+++ b/contacts/src/main/res/drawable/ic_phone_hang_up.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/contacts/src/main/res/drawable/ic_phone_pick_up.xml b/contacts/src/main/res/drawable/ic_phone_pick_up.xml
new file mode 100644
index 0000000..23080f1
--- /dev/null
+++ b/contacts/src/main/res/drawable/ic_phone_pick_up.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/drawable/shape_gradient.xml b/contacts/src/main/res/drawable/shape_gradient.xml
new file mode 100644
index 0000000..c164fe9
--- /dev/null
+++ b/contacts/src/main/res/drawable/shape_gradient.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/activity_call.xml b/contacts/src/main/res/layout/activity_call.xml
new file mode 100644
index 0000000..460b416
--- /dev/null
+++ b/contacts/src/main/res/layout/activity_call.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/activity_dialer.xml b/contacts/src/main/res/layout/activity_dialer.xml
new file mode 100644
index 0000000..dc18c88
--- /dev/null
+++ b/contacts/src/main/res/layout/activity_dialer.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/activity_main.xml b/contacts/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..5376c86
--- /dev/null
+++ b/contacts/src/main/res/layout/activity_main.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/activity_phone_call.xml b/contacts/src/main/res/layout/activity_phone_call.xml
new file mode 100644
index 0000000..9768e9f
--- /dev/null
+++ b/contacts/src/main/res/layout/activity_phone_call.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/contacts/src/main/res/layout/activity_settings.xml b/contacts/src/main/res/layout/activity_settings.xml
new file mode 100644
index 0000000..2122230
--- /dev/null
+++ b/contacts/src/main/res/layout/activity_settings.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/fragment_call.xml b/contacts/src/main/res/layout/fragment_call.xml
new file mode 100644
index 0000000..1b8673c
--- /dev/null
+++ b/contacts/src/main/res/layout/fragment_call.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/fragment_contacts.xml b/contacts/src/main/res/layout/fragment_contacts.xml
new file mode 100644
index 0000000..aeec420
--- /dev/null
+++ b/contacts/src/main/res/layout/fragment_contacts.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/fragment_log.xml b/contacts/src/main/res/layout/fragment_log.xml
new file mode 100644
index 0000000..7636182
--- /dev/null
+++ b/contacts/src/main/res/layout/fragment_log.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/contacts/src/main/res/layout/view_phone_call.xml b/contacts/src/main/res/layout/view_phone_call.xml
new file mode 100644
index 0000000..5036c76
--- /dev/null
+++ b/contacts/src/main/res/layout/view_phone_call.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/contacts/src/main/res/layout/view_toast.xml b/contacts/src/main/res/layout/view_toast.xml
new file mode 100644
index 0000000..d6a9915
--- /dev/null
+++ b/contacts/src/main/res/layout/view_toast.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/widget_layout.xml b/contacts/src/main/res/layout/widget_layout.xml
new file mode 100644
index 0000000..0dc6b46
--- /dev/null
+++ b/contacts/src/main/res/layout/widget_layout.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/menu/toolbar_main.xml b/contacts/src/main/res/menu/toolbar_main.xml
new file mode 100644
index 0000000..d248d27
--- /dev/null
+++ b/contacts/src/main/res/menu/toolbar_main.xml
@@ -0,0 +1,12 @@
+
+
diff --git a/contacts/src/main/res/values/colors.xml b/contacts/src/main/res/values/colors.xml
new file mode 100644
index 0000000..bb20e29
--- /dev/null
+++ b/contacts/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+
+ #FF196ABC
+ #FF002B57
+ #FF80BFFF
+
diff --git a/contacts/src/main/res/values/strings.xml b/contacts/src/main/res/values/strings.xml
new file mode 100644
index 0000000..cc96fb0
--- /dev/null
+++ b/contacts/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ Contacts
+
+
diff --git a/contacts/src/main/res/values/styles.xml b/contacts/src/main/res/values/styles.xml
new file mode 100644
index 0000000..8380acf
--- /dev/null
+++ b/contacts/src/main/res/values/styles.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/contacts/src/main/res/xml/appwidget_provider_info.xml b/contacts/src/main/res/xml/appwidget_provider_info.xml
new file mode 100644
index 0000000..7dda9ab
--- /dev/null
+++ b/contacts/src/main/res/xml/appwidget_provider_info.xml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/contacts/src/main/res/xml/studio_provider.xml b/contacts/src/main/res/xml/studio_provider.xml
new file mode 100644
index 0000000..f045677
--- /dev/null
+++ b/contacts/src/main/res/xml/studio_provider.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/stage/AndroidManifest.xml b/contacts/src/stage/AndroidManifest.xml
new file mode 100644
index 0000000..ee78d9f
--- /dev/null
+++ b/contacts/src/stage/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/stage/res/values/strings.xml b/contacts/src/stage/res/values/strings.xml
new file mode 100644
index 0000000..ace0c41
--- /dev/null
+++ b/contacts/src/stage/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/gradle.properties-android-demo b/gradle.properties-android-demo
new file mode 100644
index 0000000..cb39be8
--- /dev/null
+++ b/gradle.properties-android-demo
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=false
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=false
+# 保持与旧版Gradle插件的兼容
+android.disableAutomaticComponentCreation=true
diff --git a/gradle.properties b/gradle.properties-androidx-demo
similarity index 92%
rename from gradle.properties
rename to gradle.properties-androidx-demo
index 71ba985..74b1f76 100644
--- a/gradle.properties
+++ b/gradle.properties-androidx-demo
@@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx4096m
+org.gradle.jvmargs=-Xmx2048m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
@@ -17,10 +17,5 @@ org.gradle.jvmargs=-Xmx4096m
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
-
-org.gradle.caching=true
-
+# 保持与旧版Gradle插件的兼容
android.disableAutomaticComponentCreation=true
-
-android.injected.testOnly=false
-
diff --git a/libappbase/build.gradle b/libappbase/build.gradle
index 594d80d..92cddde 100644
--- a/libappbase/build.gradle
+++ b/libappbase/build.gradle
@@ -4,8 +4,8 @@ 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
@@ -20,13 +20,5 @@ android {
}
dependencies {
- api 'com.github.getActivity:ToastUtils:10.5'
-
- api 'androidx.appcompat:appcompat:1.3.1'
- api 'androidx.vectordrawable:vectordrawable:1.1.0'
- api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
- api 'androidx.fragment:fragment:1.1.0'
- api 'com.google.android.material:material:1.1.0'
-
api fileTree(dir: 'libs', include: ['*.jar'])
}
diff --git a/libappbase/build.properties b/libappbase/build.properties
index af188da..b0c800f 100644
--- a/libappbase/build.properties
+++ b/libappbase/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Tue Feb 25 16:51:09 HKT 2025
-stageCount=3
+#Mon Mar 24 21:28:41 GMT 2025
+stageCount=11
libraryProject=libappbase
-baseVersion=2.0
-publishVersion=2.0.2
-buildCount=0
-baseBetaVersion=2.0.3
+baseVersion=15.0
+publishVersion=15.0.10
+buildCount=12
+baseBetaVersion=15.0.11
diff --git a/libappbase/src/main/AndroidManifest.xml b/libappbase/src/main/AndroidManifest.xml
index 4765d9f..69597ba 100644
--- a/libappbase/src/main/AndroidManifest.xml
+++ b/libappbase/src/main/AndroidManifest.xml
@@ -73,6 +73,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ 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/GlobalApplication.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java
index 76a2f84..86519ea 100644
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java
@@ -6,17 +6,14 @@ package cc.winboll.studio.libappbase;
* @Describe 全局应用类
*/
import android.app.Application;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
-import android.view.Gravity;
-import com.hjq.toast.ToastUtils;
-import com.hjq.toast.style.WhiteToastStyle;
+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 {
@@ -25,22 +22,31 @@ public class GlobalApplication extends Application {
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;
+ WinBollActivityManager mWinBollActivityManager;
+ 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));
+ // 获取SharedPreferences实例
+// SharedPreferences sharedPreferences = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+// // 获取编辑器
+// SharedPreferences.Editor editor = sharedPreferences.edit();
+// // 保存数据
+// editor.putBoolean(PREFS_ISDEBUGING, GlobalApplication.isDebuging);
+// // 提交更改
+// editor.apply();
+ }
+ }
+
+ static String getAPPBaseModelFilePath() {
+ return _GlobalApplication.getDataDir().getPath() + "/APPBaseModel.json";
}
public static boolean isDebuging() {
@@ -59,8 +65,16 @@ public class GlobalApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
- //GlobalApplication.isDebuging = true;
- //GlobalApplication.setIsDebuging(this, true);
+ _GlobalApplication = this;
+
+ // 设置应用调试标志
+ APPBaseModel appBaseModel = APPBaseModel.loadBeanFromFile(getAPPBaseModelFilePath(), APPBaseModel.class);
+ if (appBaseModel == null) {
+ setIsDebuging(false);
+ } else {
+ setIsDebuging(appBaseModel.isDebuging());
+ }
+
LogUtils.init(this);
//LogUtils.setLogLevel(LogUtils.LOG_LEVEL.Debug);
//LogUtils.setTAGListEnable(GlobalApplication.TAG, true);
@@ -70,16 +84,22 @@ public class GlobalApplication extends Application {
// 设置应用异常处理窗口
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);
+
+ mWinBollActivityManager = WinBollActivityManager.getInstance(this);
+ mWinBollActivityManager.setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Service);
+ // 注册回调
+ mMyActivityLifecycleCallbacks = new MyActivityLifecycleCallbacks(mWinBollActivityManager);
+ 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 626149e..8163acf 100644
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashActivity.java
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashActivity.java
@@ -21,11 +21,9 @@ import android.view.MenuItem;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
-import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.R;
-import androidx.appcompat.app.AppCompatActivity;
-public final class GlobalCrashActivity extends AppCompatActivity implements MenuItem.OnMenuItemClickListener {
+public final class GlobalCrashActivity extends Activity implements MenuItem.OnMenuItemClickListener {
private static final int MENUITEM_COPY = 0;
private static final int MENUITEM_RESTART = 1;
@@ -47,10 +45,10 @@ public final class GlobalCrashActivity extends AppCompatActivity implements Menu
setContentView(R.layout.activity_globalcrash);
mGlobalCrashReportView = findViewById(R.id.activityglobalcrashGlobalCrashReportView1);
mGlobalCrashReportView.setReport(mLog);
- setSupportActionBar(mGlobalCrashReportView.getToolbar());
+ setActionBar(mGlobalCrashReportView.getToolbar());
- getSupportActionBar().setTitle(CrashHandler.TITTLE);
- getSupportActionBar().setSubtitle(GlobalApplication.getAppName(getApplicationContext()));
+ getActionBar().setTitle(CrashHandler.TITTLE);
+ getActionBar().setSubtitle(GlobalApplication.getAppName(getApplicationContext()));
}
@Override
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 e72dd74..efdfb3a 100644
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashReportView.java
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalCrashReportView.java
@@ -15,8 +15,8 @@ import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;
import android.widget.TextView;
-import androidx.appcompat.widget.Toolbar;
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/IWinBollActivity.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/IWinBollActivity.java
deleted file mode 100644
index fe51ef8..0000000
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/IWinBollActivity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package cc.winboll.studio.libappbase;
-
-/**
- * @Author ZhanGSKen@QQ.COM
- * @Date 2025/02/04 10:20:16
- * @Describe WinBoll Activity 接口
- */
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-import cc.winboll.studio.libappbase.bean.APPInfo;
-
-public interface IWinBollActivity {
-
- public static final String TAG = "IWinBollActivity";
-
- // 获取当前具有 IWinBoll 特征的 AppCompatActivity 活动窗口
- AppCompatActivity getActivity();
-
- abstract public APPInfo getAppInfo();
- abstract public String getTag();
- abstract Toolbar initToolBar();
- abstract boolean isEnableDisplayHomeAsUp();
- abstract boolean isAddWinBollToolBar();
-}
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogActivity.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogActivity.java
deleted file mode 100644
index f20061a..0000000
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogActivity.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package cc.winboll.studio.libappbase;
-
-/**
- * @Author ZhanGSKen@QQ.COM
- * @Date 2024/08/12 15:07:58
- * @Describe WinBoll 应用日志窗口
- */
-import android.os.Bundle;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-import cc.winboll.studio.libappbase.GlobalApplication;
-import cc.winboll.studio.libappbase.LogUtils;
-import cc.winboll.studio.libappbase.bean.APPInfo;
-import cc.winboll.studio.libappbase.R;
-
-public class LogActivity extends AppCompatActivity implements IWinBollActivity {
-
- public static final String TAG = "LogActivity";
-
- LogView mLogView;
-
- @Override
- public AppCompatActivity getActivity() {
- return this;
- }
-
- @Override
- public APPInfo getAppInfo() {
- return null;
- }
-
- @Override
- public String getTag() {
- return TAG;
- }
-
- @Override
- public Toolbar initToolBar() {
- return null;
- }
-
- @Override
- public boolean isEnableDisplayHomeAsUp() {
- return false;
- }
-
- @Override
- public boolean isAddWinBollToolBar() {
- return false;
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- LogUtils.d(TAG, "onCreate");
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_log);
- mLogView = findViewById(R.id.logview);
-
- if (GlobalApplication.isDebuging()) { mLogView.start(); }
- }
-
- @Override
- protected void onResume() {
- LogUtils.d(TAG, "onResume");
- super.onResume();
- mLogView.start();
- }
-}
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java
index 7a529a4..a49ea55 100644
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogUtils.java
@@ -38,7 +38,7 @@ public class LogUtils {
static volatile boolean _IsInited = false;
static Context _mContext;
// 日志显示时间格式
- static SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("[yyyyMMdd_HHmmSS]", Locale.getDefault());
+ static SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("[yyyyMMdd_HHmmss_SSS]", Locale.getDefault());
// 应用日志文件夹
static File _mfLogCacheDir;
static File _mfLogDataDir;
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 ec71977..9b8721d 100644
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/LogView.java
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/LogView.java
@@ -16,16 +16,15 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.R;
+import cc.winboll.studio.libappbase.views.HorizontalListView;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
@@ -51,7 +50,7 @@ public class LogView extends RelativeLayout {
Spinner mLogLevelSpinner;
ArrayAdapter mLogLevelSpinnerAdapter;
// 标签列表
- RecyclerView recyclerView;
+ HorizontalListView mListViewTags;
public LogView(Context context) {
super(context);
@@ -190,11 +189,10 @@ public class LogView extends RelativeLayout {
cbALLTAG.setChecked(isAllSelect);
// 加载标签表
- recyclerView = findViewById(cc.winboll.studio.libappbase.R.id.viewlogRecyclerView1);
- LinearLayoutManager layoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false);
- recyclerView.setLayoutManager(layoutManager);
- mTAGListAdapter = new TAGListAdapter(mapTAGList);
- recyclerView.setAdapter(mTAGListAdapter);
+ mListViewTags = findViewById(cc.winboll.studio.libappbase.R.id.tags_listview);
+ mListViewTags.setVerticalOffset(10);
+ mTAGListAdapter = new TAGListAdapter(mContext, mapTAGList);
+ mListViewTags.setAdapter(mTAGListAdapter);
// 可以添加点击监听器来处理勾选框状态变化后的逻辑,比如获取当前勾选情况等
mTAGListAdapter.notifyDataSetChanged();
@@ -305,15 +303,32 @@ public class LogView extends RelativeLayout {
}
- public class TAGListAdapter extends RecyclerView.Adapter {
+ public class TAGListAdapter extends BaseAdapter {
+ private Context context;
private Map mapOrigin;
private List itemList;
- public TAGListAdapter(Map map) {
+ public TAGListAdapter(Context context, Map map) {
+ this.context = context;
mapOrigin = map;
loadMap(mapOrigin);
}
+
+ @Override
+ public int getCount() {
+ return itemList.size();
+ }
+
+ @Override
+ public Object getItem(int p) {
+ return itemList.get(p);
+ }
+
+ @Override
+ public long getItemId(int p) {
+ return p;
+ }
void loadMap(Map map) {
itemList = new ArrayList();
@@ -329,16 +344,20 @@ public class LogView extends RelativeLayout {
loadMap(mapOrigin);
super.notifyDataSetChanged();
}
-
- @NonNull
+
@Override
- public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_logtag, parent, false);
- return new ViewHolder(view);
- }
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder holder;
+ if (convertView == null) {
+ convertView = LayoutInflater.from(context).inflate(R.layout.item_logtag, parent, false);
+ holder = new ViewHolder();
+ holder.tvText = convertView.findViewById(R.id.viewlogtagTextView1);
+ holder.cbChecked = convertView.findViewById(R.id.viewlogtagCheckBox1);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
- @Override
- public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
final TAGItemModel item = itemList.get(position);
holder.tvText.setText(item.getTag());
holder.cbChecked.setChecked(item.isChecked());
@@ -349,22 +368,13 @@ public class LogView extends RelativeLayout {
LogUtils.setTAGListEnable(item.getTag(), ((CheckBox)v).isChecked());
}
});
+
+ return convertView;
}
- @Override
- public int getItemCount() {
- return itemList.size();
- }
-
- public class ViewHolder extends RecyclerView.ViewHolder {
+ public class ViewHolder {
TextView tvText;
CheckBox cbChecked;
-
- public ViewHolder(@NonNull View itemView) {
- super(itemView);
- tvText = itemView.findViewById(R.id.viewlogtagTextView1);
- cbChecked = itemView.findViewById(R.id.viewlogtagCheckBox1);
- }
}
}
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/SOS.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/SOS.java
deleted file mode 100644
index 0cb730e..0000000
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/SOS.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package cc.winboll.studio.libappbase;
-
-/**
- * @Author ZhanGSKen@AliYun.Com
- * @Date 2025/02/13 21:09:36
- * @Describe SOS 组件
- */
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import cc.winboll.studio.libappbase.bean.APPSOSBean;
-import java.util.ArrayList;
-
-public class SOS {
-
- public static final String TAG = "SOS";
-
- public static final String ACTION_SOS = SOS.class.getName() + ".ACTION_SOS";
- public static final String ACTION_BIND = SOS.class.getName() + ".ACTION_BIND";
- public static final String ACTION_SERVICE_ENABLE = SOS.class.getName() + ".ACTION_SERVICE_ENABLE";
- public static final String ACTION_SERVICE_DISABLE = SOS.class.getName() + ".ACTION_SERVICE_DISENABLE";
-
- public static void sosWinBollService(Context context, APPSOSBean bean) {
- Intent intent = new Intent(ACTION_SOS);
- intent.putExtra("SOS", "Service");
- intent.putExtra("APPSOSBean", bean.toString());
- String szToPackage = "";
- if (GlobalApplication.isDebuging()) {
- szToPackage = "cc.winboll.studio.appbase.beta";
- } else {
- szToPackage = "cc.winboll.studio.appbase";
- }
- intent.setPackage(szToPackage);
- context.sendBroadcast(intent);
-
- LogUtils.d(TAG, String.format("Send ACTION_SOS To WinBoll. (szToPackage : %s)", szToPackage));
- //ToastUtils.show("SOS Send To WinBoll");
- }
-
- public static void bindToAPPService(Context context, APPSOSBean bean) {
- Intent intent = new Intent(ACTION_BIND);
- intent.putExtra("SOS", "Service");
- intent.putExtra("APPSOSBean", bean.toString());
- String szToPackage = "";
- if (GlobalApplication.isDebuging()) {
- szToPackage = "cc.winboll.studio.appbase.beta";
- } else {
- szToPackage = "cc.winboll.studio.appbase";
- }
- intent.setPackage(szToPackage);
- context.sendBroadcast(intent);
- LogUtils.d(TAG, String.format("Send ACTION_BIND To WinBoll. (szToPackage : %s)", szToPackage));
- }
-
-}
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/SOSCSBroadcastReceiver.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/SOSCSBroadcastReceiver.java
deleted file mode 100644
index d471aaa..0000000
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/SOSCSBroadcastReceiver.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package cc.winboll.studio.libappbase;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * @Author ZhanGSKen@AliYun.Com
- * @Date 2025/02/12 23:44:57
- * @Describe 简单信号通信中心接收器
- */
-public class SOSCSBroadcastReceiver extends BroadcastReceiver {
-
- public static final String TAG = "SOSCSBroadcastReceiver";
- public static final String ACTION_SOS = SOSCSBroadcastReceiver.class.getName() + ".ACTION_SOS";
-
- //ISOSAPP mISOSAPP;
-
- public SOSCSBroadcastReceiver() {
- //mISOSAPP = iSOSAPP;
- }
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(ACTION_SOS)) {
- LogUtils.d(TAG, "ACTION_SOS");
- //mISOSAPP.helpISOSService(intent);
- } else {
- LogUtils.d(TAG, String.format("%s", action));
- }
- }
-}
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/WinBoll.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/WinBoll.java
new file mode 100644
index 0000000..bfd8d0a
--- /dev/null
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/WinBoll.java
@@ -0,0 +1,3 @@
+package cc.winboll.studio.libappbase;
+
+
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/bean/APPInfo.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/bean/APPInfo.java
deleted file mode 100644
index ef4c3a8..0000000
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/bean/APPInfo.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package cc.winboll.studio.libappbase.bean;
-
-/**
- * @Author ZhanGSKen@QQ.COM
- * @Date 2025/01/20 14:19:02
- * @Describe 应用信息类
- */
-import cc.winboll.studio.libappbase.R;
-import java.io.Serializable;
-
-public class APPInfo implements Serializable {
-
- public static final String TAG = "APPInfo";
-
- // 应用名称
- String appName;
- // 应用图标
- int appIcon;
- // 应用描述
- String appDescription;
- // 应用Git仓库地址
- String appGitName;
- // 应用Git仓库拥有者
- String appGitOwner;
- // 应用Git仓库分支
- String appGitAPPBranch;
- // 应用Git仓库子项目文件夹
- String appGitAPPSubProjectFolder;
- // 应用主页
- String appHomePage;
- // 应用包名称
- String appAPKName;
- // 应用包存储文件夹名称
- String appAPKFolderName;
-
- public APPInfo(String appName, int appIcon, String appDescription, String appGitName, String appGitOwner, String appGitAPPBranch, String appGitAPPSubProjectFolder, String appHomePage, String appAPKName, String appAPKFolderName) {
- this.appName = appName;
- this.appIcon = appIcon;
- this.appDescription = appDescription;
- this.appGitName = appGitName;
- this.appGitOwner = appGitOwner;
- this.appGitAPPBranch = appGitAPPBranch;
- this.appGitAPPSubProjectFolder = appGitAPPSubProjectFolder;
- this.appHomePage = appHomePage;
- this.appAPKName = appAPKName;
- this.appAPKFolderName = appAPKFolderName;
- }
-
- public APPInfo() {
- this.appName = "WinBoll-APP";
- this.appIcon = R.drawable.ic_launcher;
- this.appDescription = "WinBoll APP";
- this.appGitName = "APP";
- this.appGitOwner = "Studio";
- this.appGitAPPBranch = "app";
- this.appGitAPPSubProjectFolder = "app";
- this.appHomePage = "https://www.winboll.cc/studio/details.php?app=APP";
- this.appAPKName = "APP";
- this.appAPKFolderName = "APP";
- }
-
- public void setAppGitOwner(String appGitOwner) {
- this.appGitOwner = appGitOwner;
- }
-
- public String getAppGitOwner() {
- return appGitOwner;
- }
-
- public void setAppGitAPPBranch(String appGitAPPBranch) {
- this.appGitAPPBranch = appGitAPPBranch;
- }
-
- public String getAppGitAPPBranch() {
- return appGitAPPBranch;
- }
-
- public void setAppGitAPPSubProjectFolder(String appGitAPPSubProjectFolder) {
- this.appGitAPPSubProjectFolder = appGitAPPSubProjectFolder;
- }
-
- public String getAppGitAPPSubProjectFolder() {
- return appGitAPPSubProjectFolder;
- }
-
- public void setAppIcon(int appIcon) {
- this.appIcon = appIcon;
- }
-
- public int getAppIcon() {
- return appIcon;
- }
-
- public void setAppDescription(String appDescription) {
- this.appDescription = appDescription;
- }
-
- public String getAppDescription() {
- return appDescription;
- }
-
- public void setAppAPKFolderName(String appAPKFolderName) {
- this.appAPKFolderName = appAPKFolderName;
- }
-
- public String getAppAPKFolderName() {
- return appAPKFolderName;
- }
-
- public void setAppName(String appName) {
- this.appName = appName;
- }
-
- public String getAppName() {
- return appName;
- }
-
- public void setAppGitName(String appGitName) {
- this.appGitName = appGitName;
- }
-
- public String getAppGitName() {
- return appGitName;
- }
-
- public void setAppHomePage(String appHomePage) {
- this.appHomePage = appHomePage;
- }
-
- public String getAppHomePage() {
- return appHomePage;
- }
-
- public void setAppAPKName(String appAPKName) {
- this.appAPKName = appAPKName;
- }
-
- public String getAppAPKName() {
- return appAPKName;
- }
-}
-
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/bean/APPSOSBean.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/bean/APPSOSBean.java
deleted file mode 100644
index d8e4e1b..0000000
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/bean/APPSOSBean.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package cc.winboll.studio.libappbase.bean;
-
-/**
- * @Author ZhanGSKen@AliYun.Com
- * @Date 2025/02/17 00:29:29
- * @Describe APPSOSReportBean
- */
-import android.util.JsonReader;
-import android.util.JsonWriter;
-import cc.winboll.studio.libappbase.BaseBean;
-import java.io.IOException;
-import java.io.Serializable;
-
-public class APPSOSBean extends BaseBean {
-
- public static final String TAG = "APPSOSBean";
-
- protected String sosPackage;
- protected String sosClassName;
-
- public APPSOSBean() {
- this.sosPackage = "";
- this.sosClassName = "";
- }
-
- public APPSOSBean(String sosPackage, String sosClassName) {
- this.sosPackage = sosPackage;
- this.sosClassName = sosClassName;
- }
-
- public void setSosPackage(String sosPackage) {
- this.sosPackage = sosPackage;
- }
-
- public String getSosPackage() {
- return sosPackage;
- }
-
- public void setSosClassName(String sosClassName) {
- this.sosClassName = sosClassName;
- }
-
- public String getSosClassName() {
- return sosClassName;
- }
-
- @Override
- public String getName() {
- return APPSOSBean.class.getName();
- }
-
- @Override
- public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
- super.writeThisToJsonWriter(jsonWriter);
- jsonWriter.name("sosPackage").value(getSosPackage());
- jsonWriter.name("sosClassName").value(getSosClassName());
-
- }
-
- @Override
- public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
- if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
- if (name.equals("sosPackage")) {
- setSosPackage(jsonReader.nextString());
- } else if (name.equals("sosClassName")) {
- setSosClassName(jsonReader.nextString());
- } 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/sos/APPModel.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/APPModel.java
new file mode 100644
index 0000000..5a3543f
--- /dev/null
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/APPModel.java
@@ -0,0 +1,86 @@
+package cc.winboll.studio.libappbase.sos;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/02 10:28:08
+ * @Describe APPModel
+ */
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import cc.winboll.studio.libappbase.BaseBean;
+import java.io.IOException;
+
+public class APPModel extends BaseBean {
+
+ public static final String TAG = "APPModel";
+
+ String appPackageName;
+ String appMainServiveName;
+
+ public APPModel() {
+ this.appPackageName = "";
+ this.appMainServiveName = "";
+ }
+
+ public APPModel(String appPackageName, String appMainServiveName) {
+ this.appPackageName = appPackageName;
+ this.appMainServiveName = appMainServiveName;
+ }
+
+ public void setAppPackageName(String appPackageName) {
+ this.appPackageName = appPackageName;
+ }
+
+ public String getAppPackageName() {
+ return appPackageName;
+ }
+
+ public void setAppMainServiveName(String appMainServiveName) {
+ this.appMainServiveName = appMainServiveName;
+ }
+
+ public String getAppMainServiveName() {
+ return appMainServiveName;
+ }
+
+ @Override
+ public String getName() {
+ return APPModel.class.getName();
+ }
+
+ @Override
+ public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
+ super.writeThisToJsonWriter(jsonWriter);
+ jsonWriter.name("appPackageName").value(getAppPackageName());
+ jsonWriter.name("appMainServiveName").value(getAppMainServiveName());
+
+ }
+
+ @Override
+ public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
+ if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
+ if (name.equals("appPackageName")) {
+ setAppPackageName(jsonReader.nextString());
+ } else if (name.equals("appMainServiveName")) {
+ setAppMainServiveName(jsonReader.nextString());
+ } 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/sos/SOS.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOS.java
new file mode 100644
index 0000000..fc48a28
--- /dev/null
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOS.java
@@ -0,0 +1,59 @@
+package cc.winboll.studio.libappbase.sos;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/02 09:36:29
+ * @Describe WinBoll 应用 SOS 机理保护类
+ */
+import android.content.Context;
+import android.content.Intent;
+import cc.winboll.studio.libappbase.LogUtils;
+import java.io.IOException;
+
+public class SOS {
+
+ public static final String TAG = "SOS";
+
+ public static final String ACTION_SOS = SOS.class.getName() + ".ACTION_SOS";
+ public static final String EXTRA_OBJECT = "EXTRA_OBJECT";
+
+ public static void sosToAppBase(Context context, String sosService) {
+ LogUtils.d(TAG, "sosToAppBase()");
+ String szToPackage = "cc.winboll.studio.appbase";
+ sos(context, szToPackage, sosService);
+
+ }
+
+ public static void sosToAppBaseBeta(Context context, String sosService) {
+ LogUtils.d(TAG, "sosToAppBaseBeta()");
+ String szToPackage = "cc.winboll.studio.appbase.beta";
+ sos(context, szToPackage, sosService);
+
+ }
+
+ static void sos(Context context, String szToPackage, String sosService) {
+ LogUtils.d(TAG, "sos(...)");
+ Intent intent = new Intent(ACTION_SOS);
+ intent.putExtra(EXTRA_OBJECT, genSOSObject(context.getPackageName(), sosService));
+ intent.setPackage(szToPackage);
+ LogUtils.d(TAG, String.format("ACTION_SOS :\nTo Package : %sSOS Service : %s\n", szToPackage, sosService));
+ context.sendBroadcast(intent);
+ }
+
+ public static SOSObject parseSOSObject(String szSOSObject) {
+ try {
+ return SOSObject.parseStringToBean(szSOSObject, SOSObject.class);
+ } catch (IOException e) {
+ LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
+ }
+ return null;
+ }
+
+ public static String sosObjectToString(SOSObject object) {
+ return object.toString();
+ }
+
+ public static String genSOSObject(String objectPackageName, String objectServiveName) {
+ return (new SOSObject(objectPackageName, objectServiveName)).toString();
+ }
+}
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/SimpleOperateSignalCenterService.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOSCenterService.java
similarity index 56%
rename from libappbase/src/main/java/cc/winboll/studio/libappbase/SimpleOperateSignalCenterService.java
rename to libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOSCenterService.java
index 332d1eb..d76b036 100644
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/SimpleOperateSignalCenterService.java
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOSCenterService.java
@@ -1,10 +1,10 @@
-package cc.winboll.studio.libappbase;
-
+package cc.winboll.studio.libappbase.sos;
/**
* @Author ZhanGSKen@AliYun.Com
- * @Date 2025/02/12 11:12:25
- * @Describe 简单信号服务中心
+ * @Date 2025/02/27 14:00:21
+ * @Describe Simple Operate Signal Service Center.
+ * 简单操作信号服务中心
*/
import android.app.Service;
import android.content.Context;
@@ -13,18 +13,19 @@ import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
-import cc.winboll.studio.libappbase.bean.SimpleOperateSignalCenterServiceBean;
import java.io.FileDescriptor;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import cc.winboll.studio.libappbase.LogUtils;
-public class SimpleOperateSignalCenterService extends Service {
+public class SOSCenterService extends Service {
- public static final String TAG = "SimpleOperateSignalCenterService";
- public static final String ACTION_ENABLE = SimpleOperateSignalCenterService.class.getName() + ".ACTION_ENABLE";
- public static final String ACTION_DISABLE = SimpleOperateSignalCenterService.class.getName() + ".ACTION_DISABLE";
+ public static final String TAG = "SOSCenterService";
private final IBinder binder =(IBinder)new SOSBinder();
-
- SimpleOperateSignalCenterServiceBean mSimpleOperateSignalCenterServiceBean;
+
+ SOSCenterServiceModel mSOSCenterServiceModel;
static MainThread _MainThread;
public static synchronized MainThread getMainThreadInstance() {
if (_MainThread == null) {
@@ -37,7 +38,7 @@ public class SimpleOperateSignalCenterService extends Service {
public IBinder onBind(Intent intent) {
return binder;
}
-
+
public class SOSBinder implements IBinder {
@Override
@@ -81,21 +82,21 @@ public class SimpleOperateSignalCenterService extends Service {
public boolean unlinkToDeath(IBinder.DeathRecipient deathRecipient, int p) {
return false;
}
-
+
public static final String TAG = "SOSBinder";
- SimpleOperateSignalCenterService getService() {
- return SimpleOperateSignalCenterService.this;
+ SOSCenterService getService() {
+ return SOSCenterService.this;
}
}
-
+
@Override
public void onCreate() {
super.onCreate();
LogUtils.d(TAG, "onCreate");
- mSimpleOperateSignalCenterServiceBean = SimpleOperateSignalCenterServiceBean.loadBean(this, SimpleOperateSignalCenterServiceBean.class);
- if(mSimpleOperateSignalCenterServiceBean == null) {
- mSimpleOperateSignalCenterServiceBean = new SimpleOperateSignalCenterServiceBean();
- SimpleOperateSignalCenterServiceBean.saveBean(this, mSimpleOperateSignalCenterServiceBean);
+ mSOSCenterServiceModel = SOSCenterServiceModel.loadBean(this, SOSCenterServiceModel.class);
+ if(mSOSCenterServiceModel == null) {
+ mSOSCenterServiceModel = new SOSCenterServiceModel();
+ SOSCenterServiceModel.saveBean(this, mSOSCenterServiceModel);
}
runMainThread();
}
@@ -103,21 +104,15 @@ public class SimpleOperateSignalCenterService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "onStartCommand");
-// if (intent.getBooleanExtra(ISOSService.EXTRA_ENABLE, false)) {
-// LogUtils.d(TAG, "onStartCommand enable service");
-// mSimpleOperateSignalCenterServiceBean.setIsEnable(true);
-// SimpleOperateSignalCenterServiceBean.saveBean(this, mSimpleOperateSignalCenterServiceBean);
-// }
runMainThread();
- //return super.onStartCommand(intent, flags, startId);
- return mSimpleOperateSignalCenterServiceBean.isEnable() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId);
+ return mSOSCenterServiceModel.isEnable() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId);
}
void runMainThread() {
- mSimpleOperateSignalCenterServiceBean = SimpleOperateSignalCenterServiceBean.loadBean(this, SimpleOperateSignalCenterServiceBean.class);
- if (mSimpleOperateSignalCenterServiceBean.isEnable()
+ mSOSCenterServiceModel = mSOSCenterServiceModel.loadBean(this, SOSCenterServiceModel.class);
+ if (mSOSCenterServiceModel.isEnable()
&& _MainThread == null) {
getMainThreadInstance().start();
}
@@ -127,9 +122,9 @@ public class SimpleOperateSignalCenterService extends Service {
public void onDestroy() {
super.onDestroy();
LogUtils.d(TAG, "onDestroy");
- mSimpleOperateSignalCenterServiceBean = SimpleOperateSignalCenterServiceBean.loadBean(this, SimpleOperateSignalCenterServiceBean.class);
- if (mSimpleOperateSignalCenterServiceBean.isEnable()) {
- LogUtils.d(TAG, "mSimpleOperateSignalCenterServiceBean.isEnable()");
+ mSOSCenterServiceModel = SOSCenterServiceModel.loadBean(this, SOSCenterServiceModel.class);
+ if (mSOSCenterServiceModel.isEnable()) {
+ LogUtils.d(TAG, "mSOSCenterServiceModel.isEnable()");
// ISOSAPP iSOSAPP = (ISOSAPP)getApplication();
// iSOSAPP.helpISOSService(getISOSServiceIntentWhichAskForHelp());
}
@@ -141,22 +136,22 @@ public class SimpleOperateSignalCenterService extends Service {
public static void stopISOSService(Context context) {
LogUtils.d(TAG, "stopISOSService");
- SimpleOperateSignalCenterServiceBean bean = new SimpleOperateSignalCenterServiceBean();
+ SOSCenterServiceModel bean = new SOSCenterServiceModel();
bean.setIsEnable(false);
- SimpleOperateSignalCenterServiceBean.saveBean(context, bean);
- context.stopService(new Intent(context, SimpleOperateSignalCenterService.class));
+ SOSCenterServiceModel.saveBean(context, bean);
+ context.stopService(new Intent(context, SOSCenterServiceModel.class));
}
public static void startISOSService(Context context) {
LogUtils.d(TAG, "startISOSService");
- SimpleOperateSignalCenterServiceBean bean = new SimpleOperateSignalCenterServiceBean();
+ SOSCenterServiceModel bean = new SOSCenterServiceModel();
bean.setIsEnable(true);
- SimpleOperateSignalCenterServiceBean.saveBean(context, bean);
- context.startService(new Intent(context, SimpleOperateSignalCenterService.class));
+ SOSCenterServiceModel.saveBean(context, bean);
+ context.startService(new Intent(context, SOSCenterServiceModel.class));
}
-
+
public String getMessage() {
- return "Hello from SimpleOperateSignalCenterService";
+ return "Hello from SOSCenterServiceModel";
}
static class MainThread extends Thread {
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOSCenterServiceModel.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOSCenterServiceModel.java
new file mode 100644
index 0000000..095248d
--- /dev/null
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOSCenterServiceModel.java
@@ -0,0 +1,69 @@
+package cc.winboll.studio.libappbase.sos;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/02 09:49:45
+ * @Describe SOSCenterServiceModel
+ * Simple Operate Signal Service Model.
+ */
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import cc.winboll.studio.libappbase.BaseBean;
+import java.io.IOException;
+
+public class SOSCenterServiceModel extends BaseBean {
+
+ public static final String TAG = "SOSCenterServiceModel";
+
+ boolean isEnable;
+
+ public SOSCenterServiceModel() {
+ this.isEnable = false;
+ }
+
+ public void setIsEnable(boolean isEnable) {
+ this.isEnable = isEnable;
+ }
+
+ public boolean isEnable() {
+ return isEnable;
+ }
+
+ @Override
+ public String getName() {
+ return SOSCenterServiceModel.class.getName();
+ }
+
+ @Override
+ public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
+ super.writeThisToJsonWriter(jsonWriter);
+ jsonWriter.name("isEnable").value(isEnable());
+
+ }
+
+ @Override
+ public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
+ if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
+ if (name.equals("isEnable")) {
+ setIsEnable(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/sos/SOSCenterServiceReceiver.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOSCenterServiceReceiver.java
new file mode 100644
index 0000000..4464a0c
--- /dev/null
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOSCenterServiceReceiver.java
@@ -0,0 +1,29 @@
+package cc.winboll.studio.libappbase.sos;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/27 14:04:35
+ * @Describe SOSCenterServiceReceiver
+ */
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import cc.winboll.studio.libappbase.LogUtils;
+
+public class SOSCenterServiceReceiver extends BroadcastReceiver {
+
+ public static final String TAG = "SOSCenterServiceReceiver";
+
+ public static final String ACTION_SOS = SOSCenterServiceReceiver.class.getName() + ".ACTION_SOS";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(ACTION_SOS)) {
+ // 处理接收到的广播消息
+ LogUtils.d(TAG, String.format("Action %s \n%s\n%s", action));
+ } else {
+ LogUtils.d(TAG, String.format("%s", action));
+ }
+ }
+}
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOSObject.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOSObject.java
new file mode 100644
index 0000000..8d309db
--- /dev/null
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/SOSObject.java
@@ -0,0 +1,86 @@
+package cc.winboll.studio.libappbase.sos;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/27 14:12:05
+ * @Describe SOSBean
+ */
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import cc.winboll.studio.libappbase.BaseBean;
+import java.io.IOException;
+
+public class SOSObject extends BaseBean {
+
+ public static final String TAG = "SOSObject";
+
+ String objectPackageName;
+ String objectServiveName;
+
+ public SOSObject() {
+ this.objectPackageName = "";
+ this.objectServiveName = "";
+ }
+
+ public SOSObject(String objectPackageName, String objectServiveName) {
+ this.objectPackageName = objectPackageName;
+ this.objectServiveName = objectServiveName;
+ }
+
+ public void setObjectPackageName(String objectPackageName) {
+ this.objectPackageName = objectPackageName;
+ }
+
+ public String getObjectPackageName() {
+ return objectPackageName;
+ }
+
+ public void setObjectServiveName(String objectServiveName) {
+ this.objectServiveName = objectServiveName;
+ }
+
+ public String getObjectServiveName() {
+ return objectServiveName;
+ }
+
+ @Override
+ public String getName() {
+ return SOSObject.class.getName();
+ }
+
+ @Override
+ public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
+ super.writeThisToJsonWriter(jsonWriter);
+ jsonWriter.name("objectPackageName").value(getObjectPackageName());
+ jsonWriter.name("objectServiveName").value(getObjectServiveName());
+
+ }
+
+ @Override
+ public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
+ if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
+ if (name.equals("objectPackageName")) {
+ setObjectPackageName(jsonReader.nextString());
+ } else if (name.equals("objectServiveName")) {
+ setObjectServiveName(jsonReader.nextString());
+ } 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/sos/WinBoll.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/WinBoll.java
new file mode 100644
index 0000000..f826b7b
--- /dev/null
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/sos/WinBoll.java
@@ -0,0 +1,39 @@
+package cc.winboll.studio.libappbase.sos;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/02 09:54:28
+ * @Describe WinBoll 系列应用通用管理类
+ */
+import android.content.Context;
+import android.content.Intent;
+import cc.winboll.studio.libappbase.LogUtils;
+
+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_APPMODEL = "EXTRA_APPMODEL";
+
+ 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_APPMODEL, (new APPModel(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/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/ToastUtils.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/ToastUtils.java
new file mode 100644
index 0000000..476a20a
--- /dev/null
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/ToastUtils.java
@@ -0,0 +1,33 @@
+package cc.winboll.studio.libappbase.utils;
+import android.content.Context;
+import android.widget.Toast;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/12 12:02:31
+ */
+public class ToastUtils {
+
+ public static final String TAG = "ToastUtils";
+
+ volatile static ToastUtils _ToastUtils;
+ Context mContext;
+
+ ToastUtils() {
+ }
+
+ synchronized static ToastUtils getInstance() {
+ if (_ToastUtils == null) {
+ _ToastUtils = new ToastUtils();
+ }
+ return _ToastUtils;
+ }
+
+ public static void init(Context context) {
+ getInstance().mContext = context;
+ }
+
+ public static void show(String message) {
+ Toast.makeText(getInstance().mContext, message, Toast.LENGTH_SHORT).show();
+ }
+}
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
new file mode 100644
index 0000000..782d8fa
--- /dev/null
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/views/HorizontalListView.java
@@ -0,0 +1,65 @@
+package cc.winboll.studio.libappbase.views;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/12 12:29:01
+ * @Describe 水平布局的 ListView
+ */
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ListView;
+import cc.winboll.studio.libappbase.LogUtils;
+
+public class HorizontalListView extends ListView {
+
+ public static final String TAG = "HorizontalListView";
+ int verticalOffset = 0;
+
+ public HorizontalListView(Context context) {
+ super(context);
+ }
+
+ public HorizontalListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public HorizontalListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setVerticalOffset(int verticalOffset) {
+ this.verticalOffset = verticalOffset;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ int childCount = getChildCount();
+ int left = getPaddingLeft();
+ int viewHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
+ LogUtils.d(TAG, String.format("HorizontalListView的高度 %d", viewHeight));
+
+ 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;
+ }
+ }
+
+ @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);
+ }
+}
+
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidget.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidget.java
index a4a2eee..0219949 100644
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidget.java
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidget.java
@@ -14,10 +14,9 @@ import android.widget.RemoteViews;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.R;
import cc.winboll.studio.libappbase.utils.ServiceUtils;
-import com.hjq.toast.ToastUtils;
import android.content.ServiceConnection;
import android.os.IBinder;
-import cc.winboll.studio.libappbase.services.TestService;
+import cc.winboll.studio.libappbase.utils.ToastUtils;
public class StatusWidget extends AppWidgetProvider {
@@ -36,6 +35,7 @@ public class StatusWidget extends AppWidgetProvider {
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (intent.getAction().equals(ACTION_STATUS_UPDATE)) {
+ ToastUtils.show("Test");
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, StatusWidget.class));
for (int appWidgetId : appWidgetIds) {
@@ -52,12 +52,12 @@ public class StatusWidget extends AppWidgetProvider {
PendingIntent pendingIntentAppButton = PendingIntent.getBroadcast(context, 0, intentAppButton, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.ivapp, pendingIntentAppButton);
- boolean isActive = ServiceUtils.isServiceRunning(context, TestService.class.getName());
- if (isActive) {
- views.setImageViewResource(R.id.ivapp, cc.winboll.studio.libappbase.R.drawable.ic_launcher);
- } else {
- views.setImageViewResource(R.id.ivapp, cc.winboll.studio.libappbase.R.drawable.ic_launcher_disable);
- }
+// boolean isActive = ServiceUtils.isServiceRunning(context, TestService.class.getName());
+// if (isActive) {
+// views.setImageViewResource(R.id.ivapp, cc.winboll.studio.libappbase.R.drawable.ic_launcher);
+// } else {
+// views.setImageViewResource(R.id.ivapp, cc.winboll.studio.libappbase.R.drawable.ic_launcher_disable);
+// }
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidgetClickListener.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidgetClickListener.java
index afbf9cf..19ceed3 100644
--- a/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidgetClickListener.java
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/widgets/StatusWidgetClickListener.java
@@ -9,7 +9,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import cc.winboll.studio.libappbase.LogUtils;
-import com.hjq.toast.ToastUtils;
+import cc.winboll.studio.libappbase.utils.ToastUtils;
public class StatusWidgetClickListener extends BroadcastReceiver {
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/MyActivityLifecycleCallbacks.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/MyActivityLifecycleCallbacks.java
new file mode 100644
index 0000000..2c3ca1f
--- /dev/null
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/MyActivityLifecycleCallbacks.java
@@ -0,0 +1,103 @@
+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.Context;
+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";
+
+ WinBollActivityManager mWinBollActivityManager;
+
+ public String mInfo = "";
+
+ public MyActivityLifecycleCallbacks(WinBollActivityManager winBollActivityManager) {
+ mWinBollActivityManager = winBollActivityManager;
+ }
+
+ 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);
+ mWinBollActivityManager.add((IWinBollActivity)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");
+ mWinBollActivityManager.registeRemove((IWinBollActivity)activity);
+ }
+}
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..3bfacd3
--- /dev/null
+++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/winboll/WinBollActivityManager.java
@@ -0,0 +1,318 @@
+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.LogUtils;
+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 // 退出应用后,清理所有最近任务栏任务记录窗口
+ };
+
+ // 应用类型标志
+ volatile static WinBollUI_TYPE _mWinBollUI_TYPE = WinBollUI_TYPE.Service;
+
+ Context mContext;
+ MyActivityLifecycleCallbacks mMyActivityLifecycleCallbacks;
+ static WinBollActivityManager _mWinBollActivityManager;
+ static Map _mapIWinBollList;
+ IWinBollActivity firstIWinBollActivity;
+
+ public WinBollActivityManager(Context context) {
+ mContext = context;
+ LogUtils.d(TAG, "WinBollActivityManager()");
+ _mapIWinBollList = new HashMap();
+ }
+
+ public static synchronized WinBollActivityManager getInstance(Context context) {
+ LogUtils.d(TAG, "getInstance");
+ if (_mWinBollActivityManager == null) {
+ LogUtils.d(TAG, "_mWinBollActivityManager == null");
+ _mWinBollActivityManager = new WinBollActivityManager(context);
+ }
+ return _mWinBollActivityManager;
+ }
+
+ //
+ // 设置 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) {
+ if (isActive(iWinBoll.getTag())) {
+ LogUtils.d(TAG, String.format("add(...) %s is active.", iWinBoll.getTag()));
+ } else {
+ // 设置起始活动窗口,以便最后退出时提问
+ if (firstIWinBollActivity == null && _mapIWinBollList.size() == 0) {
+ firstIWinBollActivity = 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 = clazz.newInstance().getTag();
+ 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);
+ mContext.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 = clazz.newInstance().getTag();
+ 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);
+ mContext.startActivity(intent);
+ } catch (InstantiationException | IllegalAccessException e) {
+ LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
+ }
+ }
+
+ public boolean isFirstIWinBollActivity(IWinBollActivity iWinBollActivity) {
+ return firstIWinBollActivity != null && firstIWinBollActivity == iWinBollActivity;
+ }
+
+ //
+ // 判断 tag绑定的 MyActivity是否存在
+ //
+ public boolean isActive(String tag) {
+ //printAvtivityListInfo();
+ 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(...) activity == null\ntag : %s", tag));
+ return false;
+ }
+ }
+
+ static IWinBollActivity getIWinBoll(String tag) {
+ return _mapIWinBollList.get(tag);
+ }
+
+ //
+ // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
+ //
+ public void resumeActivity(Context context, String tag) {
+ LogUtils.d(TAG, "resumeActivty");
+ T iWinBoll = (T)getIWinBoll(tag);
+ //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) {
+ ActivityManager am = (ActivityManager) iWinBoll.getActivity().getSystemService(Context.ACTIVITY_SERVICE);
+ //返回启动它的根任务(home 或者 MainActivity)
+ Intent intent = new Intent(context, iWinBoll.getClass());
+ TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
+ stackBuilder.addNextIntentWithParentStack(intent);
+ stackBuilder.startActivities();
+ //moveTaskToFront(YourTaskId, 0);
+ LogUtils.d(TAG, "am.moveTaskToFront");
+ //ToastUtils.show("resumeActivity am.moveTaskToFront");
+ am.moveTaskToFront(iWinBoll.getActivity().getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION);
+ }
+
+
+ //
+ // 结束所有 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(mContext, 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/view_border.xml b/libappbase/src/main/res/drawable/bg_border.xml
similarity index 100%
rename from libappbase/src/main/res/drawable/view_border.xml
rename to libappbase/src/main/res/drawable/bg_border.xml
diff --git a/libappbase/src/main/res/drawable/bg_border_round.xml b/libappbase/src/main/res/drawable/bg_border_round.xml
new file mode 100644
index 0000000..2036d51
--- /dev/null
+++ b/libappbase/src/main/res/drawable/bg_border_round.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/libappbase/src/main/res/drawable/bg_shadow.xml b/libappbase/src/main/res/drawable/bg_shadow.xml
index 6d3d898..f4d1bd7 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="@color/colorAccent"
+ android:startColor="@color/colorAccent" />
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/libappbase/src/main/res/layout/item_logtag.xml b/libappbase/src/main/res/layout/item_logtag.xml
new file mode 100644
index 0000000..c7eb6d6
--- /dev/null
+++ b/libappbase/src/main/res/layout/item_logtag.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/libappbase/src/main/res/layout/view_globalcrashreport.xml b/libappbase/src/main/res/layout/view_globalcrashreport.xml
index 405d0b4..1aff5e5 100644
--- a/libappbase/src/main/res/layout/view_globalcrashreport.xml
+++ b/libappbase/src/main/res/layout/view_globalcrashreport.xml
@@ -7,7 +7,7 @@
android:layout_height="match_parent"
android:id="@+id/viewglobalcrashreportLinearLayout1">
-
diff --git a/libappbase/src/main/res/layout/view_log.xml b/libappbase/src/main/res/layout/view_log.xml
index 1b98af1..f0857d4 100644
--- a/libappbase/src/main/res/layout/view_log.xml
+++ b/libappbase/src/main/res/layout/view_log.xml
@@ -1,121 +1,130 @@
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#FF000000">
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
diff --git a/libappbase/src/main/res/layout/view_logtag.xml b/libappbase/src/main/res/layout/view_logtag.xml
deleted file mode 100644
index 17e36f8..0000000
--- a/libappbase/src/main/res/layout/view_logtag.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/libappbase/src/main/res/values/dimens.xml b/libappbase/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..25f95ea
--- /dev/null
+++ b/libappbase/src/main/res/values/dimens.xml
@@ -0,0 +1,9 @@
+
+
+
+ 16sp
+
+ 8dp
+
+ 24dp
+
diff --git a/libappbase/src/main/res/values/styles.xml b/libappbase/src/main/res/values/styles.xml
index 13f26cd..da69f41 100644
--- a/libappbase/src/main/res/values/styles.xml
+++ b/libappbase/src/main/res/values/styles.xml
@@ -1,7 +1,7 @@
-
diff --git a/settings.gradle-demo b/settings.gradle-demo
index 70140d5..351aef6 100644
--- a/settings.gradle-demo
+++ b/settings.gradle-demo
@@ -38,6 +38,10 @@
//include ':libaes'
//rootProject.name = "aes"
+// Contacts 项目编译设置
+//include ':contacts'
+//rootProject.name = "contacts"
+
// Positions 项目编译设置
//include ':positions'
//rootProject.name = "positions"
@@ -46,3 +50,10 @@
//include ':mymessagemanager'
//rootProject.name = "mymessagemanager"
+// AndroidDemo 项目编译设置
+//include ':androiddemo'
+//rootProject.name = "androiddemo"
+
+// AndroidXDemo 项目编译设置
+//include ':androidxdemo'
+//rootProject.name = "androidxdemo"