diff --git a/.winboll/winboll_lib_build.gradle b/.winboll/winboll_lib_build.gradle
index 0f19949..d167ec1 100644
--- a/.winboll/winboll_lib_build.gradle
+++ b/.winboll/winboll_lib_build.gradle
@@ -14,7 +14,7 @@ def DefaultGroupId = 'cc.winboll.studio' // 类库所有者groupId
def DefaultVersion = getDefaultVersion() // 版本号
def DeveloperId='zhangsken' // 开发者账号
def DeveloperName='ZhanGSKen' // 开发者名称
-def DeveloperEMail='ZhanGSKen@QQ.COM' // 开发者邮箱地址
+def DeveloperEMail='zhangsken@188.com' // 开发者邮箱地址
def LicenseName='The Apache Software License, Version 2.0'
def LicenseUrl='http://www.apache.org/licenses/LICENSE-2.0.txt'
diff --git a/README.md b/README.md
index 207e65c..d41a785 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# ☁ ☁ ☁ WinBoll APP ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁
-# ☁ ☁ 这是 WinBoll 系列 APP 汇总项目。☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁
-# ☁ ☁ ☁ WinBoll 网站地址 https://www.winboll.cc/studio/app/ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁
+# ☁ ☁ ☁ WinBoLL APP ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁
+# ☁ ☁ WinBoLL Studio Android 应用开源项目。☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁
+# ☁ ☁ ☁ WinBoLL 网站地址 https://www.winboll.cc/ ☁ ☁ ☁ ☁ ☁ ☁ ☁ ☁
## WinBoll 提问
同样是 /sdcard 目录,在开发 Android 应用时,
@@ -92,7 +92,12 @@
### ☁ 云游四方 ☁ ###
### ☁ 呔! ☁ WinBoll-APP 应用需求规划
-☁ WinBoll-APP 提供手机目录 /sdcard/WinBollStudio/Sources 的 WinBoll 项目源码管理功能。
+☁ 如要使用 WinBoLL Android 项目的 Gradle 编译功能,则需要设置以下两个文件夹。
+☁ 1. 则需要建立数据存储目录 /sdcard/WinBoLLStudio/APKs。
+ WinBoLL 项目源码编译出来的安装包会拷贝一份到 /sdcard/WinBoLLStudio/APKs 目录下。
+☁ 2. 则需要建立数据存储目录 /sdcard/AppProjects。
+ WinBoLL 项目源码编译出来的安装包会拷贝一份并命名 "app.apk" 的安装文件为到 /sdcard/AppProjects 目录下。
+
### ☁ 吁! ☁ WinBoll-APP 共享计划前景
☁ WinBoll-APP 将会实现 https://winboll.cc/api 访问功能。
diff --git a/appbase/build.gradle b/appbase/build.gradle
index aa3ffa6..727c7be 100644
--- a/appbase/build.gradle
+++ b/appbase/build.gradle
@@ -30,7 +30,7 @@ android {
// versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
- versionName "15.4"
+ versionName "15.2"
if(true) {
versionName = genVersionName("${versionName}")
}
diff --git a/appbase/build.properties b/appbase/build.properties
index 11257d9..6bc1b93 100644
--- a/appbase/build.properties
+++ b/appbase/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Sun Apr 27 10:05:57 HKT 2025
-stageCount=8
+#Sat Apr 26 12:27:57 GMT 2025
+stageCount=6
libraryProject=libappbase
-baseVersion=15.4
-publishVersion=15.4.7
-buildCount=0
-baseBetaVersion=15.4.8
+baseVersion=15.2
+publishVersion=15.2.5
+buildCount=4
+baseBetaVersion=15.2.6
diff --git a/libappbase/build.properties b/libappbase/build.properties
index 557857f..6bc1b93 100644
--- a/libappbase/build.properties
+++ b/libappbase/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Sun Apr 27 10:05:40 HKT 2025
-stageCount=8
+#Sat Apr 26 12:27:57 GMT 2025
+stageCount=6
libraryProject=libappbase
-baseVersion=15.4
-publishVersion=15.4.7
-buildCount=0
-baseBetaVersion=15.4.8
+baseVersion=15.2
+publishVersion=15.2.5
+buildCount=4
+baseBetaVersion=15.2.6
diff --git a/winboll-x b/winboll-x
deleted file mode 160000
index d94d700..0000000
--- a/winboll-x
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit d94d70050f92a24b407f35cbd93a8e9ed8b007d8
diff --git a/winboll/.gitignore b/winboll/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/winboll/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/winboll/app_update_description.txt b/winboll/app_update_description.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/winboll/app_update_description.txt
@@ -0,0 +1 @@
+
diff --git a/winboll/build.gradle b/winboll/build.gradle
new file mode 100644
index 0000000..727c7be
--- /dev/null
+++ b/winboll/build.gradle
@@ -0,0 +1,50 @@
+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 "32.0.0"
+
+ defaultConfig {
+ applicationId "cc.winboll.studio.appbase"
+ minSdkVersion 24
+ targetSdkVersion 29
+ versionCode 1
+ // versionName 更新后需要手动设置
+ // .winboll/winbollBuildProps.properties 文件的 stageCount=0
+ // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
+ versionName "15.2"
+ if(true) {
+ versionName = genVersionName("${versionName}")
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ api project(':libappbase')
+ api fileTree(dir: 'libs', include: ['*.jar'])
+}
diff --git a/winboll/build.properties b/winboll/build.properties
new file mode 100644
index 0000000..04487f9
--- /dev/null
+++ b/winboll/build.properties
@@ -0,0 +1,8 @@
+#Created by .winboll/winboll_app_build.gradle
+#Sat Apr 12 15:06:52 HKT 2025
+stageCount=6
+libraryProject=libappbase
+baseVersion=15.2
+publishVersion=15.2.5
+buildCount=0
+baseBetaVersion=15.2.6
diff --git a/winboll/proguard-rules.pro b/winboll/proguard-rules.pro
new file mode 100644
index 0000000..233bad2
--- /dev/null
+++ b/winboll/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/winboll/src/beta/AndroidManifest.xml b/winboll/src/beta/AndroidManifest.xml
new file mode 100644
index 0000000..ee78d9f
--- /dev/null
+++ b/winboll/src/beta/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/winboll/src/beta/res/values/strings.xml b/winboll/src/beta/res/values/strings.xml
new file mode 100644
index 0000000..024cdd2
--- /dev/null
+++ b/winboll/src/beta/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ AppBase+
+
+
diff --git a/winboll/src/main/AndroidManifest.xml b/winboll/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3ff9087
--- /dev/null
+++ b/winboll/src/main/AndroidManifest.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/winboll/src/main/java/cc/winboll/studio/appbase/App.java b/winboll/src/main/java/cc/winboll/studio/appbase/App.java
new file mode 100644
index 0000000..74f9bbc
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/App.java
@@ -0,0 +1,27 @@
+package cc.winboll.studio.appbase;
+
+/**
+ * @Author ZhanGSKen@QQ.COM
+ * @Date 2025/01/05 09:54:42
+ * @Describe APPbase 应用类
+ */
+import cc.winboll.studio.libappbase.GlobalApplication;
+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";
+
+ SOSCenterServiceReceiver mSOSCenterServiceReceiver;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mSOSCenterServiceReceiver = new SOSCenterServiceReceiver();
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(SOS.ACTION_SOS);
+ registerReceiver(mSOSCenterServiceReceiver, intentFilter);
+ }
+}
diff --git a/winboll/src/main/java/cc/winboll/studio/appbase/MainActivity.java b/winboll/src/main/java/cc/winboll/studio/appbase/MainActivity.java
new file mode 100644
index 0000000..c99a20d
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/MainActivity.java
@@ -0,0 +1,184 @@
+package cc.winboll.studio.appbase;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.CheckBox;
+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.CrashHandler;
+import cc.winboll.studio.libappbase.GlobalApplication;
+import cc.winboll.studio.libappbase.GlobalCrashActivity;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libappbase.sos.SOS;
+import cc.winboll.studio.libappbase.utils.ToastUtils;
+import cc.winboll.studio.libappbase.widgets.StatusWidget;
+import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
+import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
+
+public class MainActivity extends WinBollActivityBase 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
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ToastUtils.show("onCreate");
+ setContentView(R.layout.activity_main);
+
+ mToolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(mToolbar);
+
+ CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
+ cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
+ //mLogView = findViewById(R.id.activitymainLogView1);
+
+// if (GlobalApplication.isDebuging()) {
+// mLogView.start();
+// ToastUtils.show("LogView start.");
+// }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.toolbar_main, menu);
+ getMenuInflater().inflate(R.menu.toolbar_appbase, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
+ return super.onOptionsItemSelected(item);
+ }
+
+
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Intent intentAPPWidget = new Intent(this, StatusWidget.class);
+ intentAPPWidget.setAction(StatusWidget.ACTION_STATUS_UPDATE);
+ sendBroadcast(intentAPPWidget);
+ }
+
+ public void onSwitchDebugMode(View view) {
+ boolean isDebuging = ((CheckBox)view).isChecked();
+ GlobalApplication.setIsDebuging(isDebuging);
+ GlobalApplication.saveDebugStatus();
+ }
+
+ public void onPreviewGlobalCrashActivity(View view) {
+ Intent intent = new Intent(this, GlobalCrashActivity.class);
+ intent.putExtra(CrashHandler.EXTRA_CRASH_INFO, "Demo log...");
+ startActivity(intent);
+ }
+
+ public void onStartCenter(View view) {
+ MainService.startMainService(this);
+ }
+
+ public void onStopCenter(View view) {
+ MainService.stopMainService(this);
+ }
+
+ public void onTestStopMainServiceWithoutSettingEnable(View view) {
+ LogUtils.d(TAG, "onTestStopMainServiceWithoutSettingEnable");
+ stopService(new Intent(this, MainService.class));
+ }
+
+ public void onTestUseComponentStartService(View view) {
+ LogUtils.d(TAG, "onTestUseComponentStartService");
+
+ // 目标服务的包名和类名
+ String packageName = this.getPackageName();
+ String serviceClassName = TestDemoService.class.getName();
+
+ // 构建Intent
+ Intent intentService = new Intent();
+ intentService.setComponent(new ComponentName(packageName, serviceClassName));
+
+ startService(intentService);
+ }
+
+ public void onTestDemoServiceSOS(View view) {
+ Intent intent = new Intent(this, TestDemoService.class);
+ stopService(intent);
+ if (App.isDebuging()) {
+ SOS.sosToAppBaseBeta(this, TestDemoService.class.getName());
+ } else {
+ SOS.sosToAppBase(this, TestDemoService.class.getName());
+ }
+ }
+
+ public void onSartTestDemoService(View view) {
+ Intent intent = new Intent(this, TestDemoService.class);
+ intent.setAction(TestDemoService.ACTION_ENABLE);
+ startService(intent);
+
+ }
+
+
+
+ public void onStopTestDemoService(View view) {
+ Intent intent = new Intent(this, TestDemoService.class);
+ intent.setAction(TestDemoService.ACTION_DISABLE);
+ startService(intent);
+
+ Intent intentStop = new Intent(this, TestDemoService.class);
+ stopService(intentStop);
+ }
+
+ 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) {
+ GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, NewActivity.class);
+ }
+
+
+}
diff --git a/winboll/src/main/java/cc/winboll/studio/appbase/MyTileService.java b/winboll/src/main/java/cc/winboll/studio/appbase/MyTileService.java
new file mode 100644
index 0000000..3f3fcca
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/MyTileService.java
@@ -0,0 +1,79 @@
+package cc.winboll.studio.appbase;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/13 19:30:10
+ */
+import android.content.Context;
+import android.service.quicksettings.Tile;
+import android.service.quicksettings.TileService;
+import cc.winboll.studio.appbase.models.MainServiceBean;
+import cc.winboll.studio.appbase.services.MainService;
+
+public class MyTileService extends TileService {
+ public static final String TAG = "MyTileService";
+
+ volatile static MyTileService _MyTileService;
+
+ @Override
+ public void onStartListening() {
+ super.onStartListening();
+ _MyTileService = this;
+ Tile tile = getQsTile();
+ MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
+ if (bean != null && bean.isEnable()) {
+ //MainService.startMainService(context);
+ tile.setState(Tile.STATE_ACTIVE);
+ tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud));
+ } else {
+ //MainService.stopMainService(context);
+ tile.setState(Tile.STATE_INACTIVE);
+ tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
+ }
+ tile.updateTile();
+// Tile tile = getQsTile();
+// tile.setState(Tile.STATE_INACTIVE);
+// tile.setLabel(getString(R.string.tileservice_name));
+// tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
+// tile.updateTile();
+
+ }
+
+ @Override
+ public void onClick() {
+ super.onClick();
+ Tile tile = getQsTile();
+ MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
+ if (bean == null) {
+ bean = new MainServiceBean();
+ }
+
+ if (tile.getState() == Tile.STATE_ACTIVE) {
+ bean.setIsEnable(false);
+ MainServiceBean.saveBean(this, bean);
+ MainService.stopMainService(this);
+ } else if (tile.getState() == Tile.STATE_INACTIVE) {
+ bean.setIsEnable(true);
+ MainServiceBean.saveBean(this, bean);
+ MainService.startMainService(this);
+ }
+ updateServiceIconStatus(this);
+ }
+
+ public static void updateServiceIconStatus(Context context) {
+ if (_MyTileService == null) {
+ return;
+ }
+
+ Tile tile = _MyTileService.getQsTile();
+ MainServiceBean bean = MainServiceBean.loadBean(context, MainServiceBean.class);
+ if (bean != null && bean.isEnable()) {
+ tile.setState(Tile.STATE_ACTIVE);
+ tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud));
+ } else {
+ tile.setState(Tile.STATE_INACTIVE);
+ tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud_outline));
+ }
+ tile.updateTile();
+ }
+}
diff --git a/winboll/src/main/java/cc/winboll/studio/appbase/WinBollActivityBase.java b/winboll/src/main/java/cc/winboll/studio/appbase/WinBollActivityBase.java
new file mode 100644
index 0000000..54765d8
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/WinBollActivityBase.java
@@ -0,0 +1,82 @@
+package cc.winboll.studio.appbase;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/28 15:34:16
+ * @Describe 应用活动窗口基类
+ */
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.MenuItem;
+import cc.winboll.studio.appbase.App;
+import cc.winboll.studio.appbase.R;
+import cc.winboll.studio.libappbase.GlobalApplication;
+import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
+import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
+import cc.winboll.studio.libappbase.winboll.WinBollActivityManager;
+
+public class WinBollActivityBase extends AppCompatActivity implements IWinBollActivity {
+
+ public static final String TAG = "WinBollActivityBase";
+
+ @Override
+ public Activity getActivity() {
+ return this;
+ }
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ WinBollActivityManager getWinBollActivityManager() {
+ return WinBollActivityManager.getInstance(GlobalApplication.getInstance());
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWinBollActivityManager().add(this);
+ }
+
+ @Override
+ public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
+ super.onPostCreate(savedInstanceState, persistentState);
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
+ GlobalApplication.getWinBollActivityManager().startLogActivity(this);
+ return true;
+ } else if(item.getItemId() == cc.winboll.studio.appbase.R.id.item_minimal) {
+ //moveTaskToBack(true);
+ exit();
+ }
+ // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
+ return super.onOptionsItemSelected(item);
+ }
+
+ void exit() {
+ YesNoAlertDialog.show(this, "Exit " + getString(R.string.app_name), "Close all activity and exit?", new YesNoAlertDialog.OnDialogResultListener(){
+
+ @Override
+ public void onYes() {
+ App.getWinBollActivityManager().finishAll();
+ }
+
+ @Override
+ public void onNo() {
+ }
+ });
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ getWinBollActivityManager().registeRemove(this);
+ }
+}
diff --git a/winboll/src/main/java/cc/winboll/studio/appbase/activities/New2Activity.java b/winboll/src/main/java/cc/winboll/studio/appbase/activities/New2Activity.java
new file mode 100644
index 0000000..37f4422
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/activities/New2Activity.java
@@ -0,0 +1,83 @@
+package cc.winboll.studio.appbase.activities;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/25 11:46:40
+ * @Describe 测试窗口2
+ */
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import cc.winboll.studio.appbase.R;
+import cc.winboll.studio.appbase.WinBollActivityBase;
+import cc.winboll.studio.libappbase.GlobalApplication;
+import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
+
+public class New2Activity extends WinBollActivityBase implements IWinBollActivity {
+
+ public static final String TAG = "New2Activity";
+
+ Toolbar mToolbar;
+ //LogView mLogView;
+
+ @Override
+ public Activity getActivity() {
+ return this;
+ }
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_new2);
+
+// mLogView = findViewById(R.id.logview);
+// mLogView.start();
+ mToolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(mToolbar);
+
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ //mLogView.start();
+ }
+
+ public void onCloseThisActivity(View view) {
+ GlobalApplication.getWinBollActivityManager().finish(this);
+ }
+
+ public void onCloseAllActivity(View view) {
+ GlobalApplication.getWinBollActivityManager().finishAll();
+ }
+
+ public void onNewActivity(View view) {
+ GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, NewActivity.class);
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.toolbar_main, menu);
+ getMenuInflater().inflate(R.menu.toolbar_appbase, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
+ GlobalApplication.getWinBollActivityManager().startLogActivity(this);
+ return true;
+ }
+ // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/winboll/src/main/java/cc/winboll/studio/appbase/activities/NewActivity.java b/winboll/src/main/java/cc/winboll/studio/appbase/activities/NewActivity.java
new file mode 100644
index 0000000..907c3ad
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/activities/NewActivity.java
@@ -0,0 +1,81 @@
+package cc.winboll.studio.appbase.activities;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/25 05:04:22
+ */
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import cc.winboll.studio.appbase.R;
+import cc.winboll.studio.appbase.WinBollActivityBase;
+import cc.winboll.studio.libappbase.GlobalApplication;
+import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
+
+public class NewActivity extends WinBollActivityBase implements IWinBollActivity {
+
+ public static final String TAG = "NewActivity";
+
+ Toolbar mToolbar;
+ //LogView mLogView;
+
+ @Override
+ public Activity getActivity() {
+ return this;
+ }
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_new);
+// mLogView = findViewById(R.id.logview);
+// mLogView.start();
+ mToolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(mToolbar);
+
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ //mLogView.start();
+ }
+
+ public void onCloseThisActivity(View view) {
+ GlobalApplication.getWinBollActivityManager().finish(this);
+ }
+
+ public void onCloseAllActivity(View view) {
+ GlobalApplication.getWinBollActivityManager().finishAll();
+ }
+
+ public void onNew2Activity(View view) {
+ GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, New2Activity.class);
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.toolbar_main, menu);
+ getMenuInflater().inflate(R.menu.toolbar_appbase, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
+ GlobalApplication.getWinBollActivityManager().startLogActivity(this);
+ return true;
+ }
+ // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/winboll/src/main/java/cc/winboll/studio/appbase/handlers/MainServiceHandler.java b/winboll/src/main/java/cc/winboll/studio/appbase/handlers/MainServiceHandler.java
new file mode 100644
index 0000000..9bda14b
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/handlers/MainServiceHandler.java
@@ -0,0 +1,38 @@
+package cc.winboll.studio.appbase.handlers;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/14 03:51:40
+ */
+import android.os.Handler;
+import android.os.Message;
+import cc.winboll.studio.appbase.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/winboll/src/main/java/cc/winboll/studio/appbase/models/MainServiceBean.java b/winboll/src/main/java/cc/winboll/studio/appbase/models/MainServiceBean.java
new file mode 100644
index 0000000..5df134e
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/models/MainServiceBean.java
@@ -0,0 +1,67 @@
+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;
+
+public class MainServiceBean extends BaseBean {
+
+ public static final String TAG = "MainServiceBean";
+
+ boolean isEnable;
+
+ public MainServiceBean() {
+ this.isEnable = false;
+ }
+
+ public void setIsEnable(boolean isEnable) {
+ this.isEnable = isEnable;
+ }
+
+ public boolean isEnable() {
+ return isEnable;
+ }
+
+ @Override
+ public String getName() {
+ return MainServiceBean.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/winboll/src/main/java/cc/winboll/studio/appbase/models/TestDemoBindServiceBean.java b/winboll/src/main/java/cc/winboll/studio/appbase/models/TestDemoBindServiceBean.java
new file mode 100644
index 0000000..f702494
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/models/TestDemoBindServiceBean.java
@@ -0,0 +1,67 @@
+package cc.winboll.studio.appbase.models;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/03/07 12:47:22
+ * @Describe TestServiceBean
+ */
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import cc.winboll.studio.libappbase.BaseBean;
+import java.io.IOException;
+
+public class TestDemoBindServiceBean extends BaseBean {
+
+ public static final String TAG = "TestServiceBean";
+
+ boolean isEnable;
+
+ public TestDemoBindServiceBean() {
+ this.isEnable = false;
+ }
+
+ public void setIsEnable(boolean isEnable) {
+ this.isEnable = isEnable;
+ }
+
+ public boolean isEnable() {
+ return isEnable;
+ }
+
+ @Override
+ public String getName() {
+ return TestDemoBindServiceBean.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/winboll/src/main/java/cc/winboll/studio/appbase/models/TestDemoServiceBean.java b/winboll/src/main/java/cc/winboll/studio/appbase/models/TestDemoServiceBean.java
new file mode 100644
index 0000000..90f7518
--- /dev/null
+++ b/winboll/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/winboll/src/main/java/cc/winboll/studio/appbase/models/WinBollNewsBean.java b/winboll/src/main/java/cc/winboll/studio/appbase/models/WinBollNewsBean.java
new file mode 100644
index 0000000..66ee6b6
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/models/WinBollNewsBean.java
@@ -0,0 +1,72 @@
+package cc.winboll.studio.appbase.models;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/17 10:05:09
+ * @Describe APPSOSReportBean
+ */
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import cc.winboll.studio.libappbase.BaseBean;
+import java.io.IOException;
+
+public class WinBollNewsBean extends BaseBean {
+
+ public static final String TAG = "WinBollNewsBean";
+
+ protected String message;
+
+ public WinBollNewsBean() {
+ this.message = "";
+ }
+
+ public WinBollNewsBean(String message) {
+ this.message = message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ public String getName() {
+ return WinBollNewsBean.class.getName();
+ }
+
+ @Override
+ public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
+ super.writeThisToJsonWriter(jsonWriter);
+ 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("message")) {
+ setMessage(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/winboll/src/main/java/cc/winboll/studio/appbase/receivers/APPNewsWidgetClickListener.java b/winboll/src/main/java/cc/winboll/studio/appbase/receivers/APPNewsWidgetClickListener.java
new file mode 100644
index 0000000..70561c9
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/receivers/APPNewsWidgetClickListener.java
@@ -0,0 +1,36 @@
+package cc.winboll.studio.appbase.receivers;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @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 APPNewsWidgetClickListener extends BroadcastReceiver {
+
+ 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) {
+ String action = intent.getAction();
+ if (action == null) {
+ LogUtils.d(TAG, String.format("action %s", action));
+ return;
+ }
+ if (action.equals(ACTION_PRE)) {
+ LogUtils.d(TAG, "ACTION_PRE");
+ APPNewsWidget.prePage(context);
+ } else if (action.equals(ACTION_NEXT)) {
+ LogUtils.d(TAG, "ACTION_NEXT");
+ APPNewsWidget.nextPage(context);
+ } else {
+ LogUtils.d(TAG, String.format("action %s", action));
+ }
+ }
+}
diff --git a/winboll/src/main/java/cc/winboll/studio/appbase/receivers/MainReceiver.java b/winboll/src/main/java/cc/winboll/studio/appbase/receivers/MainReceiver.java
new file mode 100644
index 0000000..498f877
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/receivers/MainReceiver.java
@@ -0,0 +1,117 @@
+package cc.winboll.studio.appbase.receivers;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/13 06:58:04
+ * @Describe 主要广播接收器
+ */
+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.models.WinBollNewsBean;
+import cc.winboll.studio.appbase.services.MainService;
+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.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;
+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;
+
+ 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");
+ } 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 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");
+ 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(szObjectPackageName, szObjectServiveName));
+ context.startService(intentService);
+
+ 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());
+
+ APPNewsWidget.addWinBollNewsBean(context, appWinBollNewsBean);
+
+ Intent intentWidget = new Intent(context, APPNewsWidget.class);
+ intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
+ context.sendBroadcast(intentWidget);
+ }
+
+
+ }
+ } else {
+ ToastUtils.show(szAction);
+ }
+ }
+
+ // 注册 Receiver
+ //
+ public void registerAction(MainService service) {
+ IntentFilter filter=new IntentFilter();
+ filter.addAction(ACTION_BOOT_COMPLETED);
+ filter.addAction(SOS.ACTION_SOS);
+ filter.addAction(WinBoll.ACTION_BIND);
+ //filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ service.registerReceiver(this, filter);
+ }
+}
diff --git a/winboll/src/main/java/cc/winboll/studio/appbase/services/AssistantService.java b/winboll/src/main/java/cc/winboll/studio/appbase/services/AssistantService.java
new file mode 100644
index 0000000..8db3f50
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/services/AssistantService.java
@@ -0,0 +1,138 @@
+package cc.winboll.studio.appbase.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.IBinder;
+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;
+import android.os.Binder;
+
+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/winboll/src/main/java/cc/winboll/studio/appbase/services/MainService.java b/winboll/src/main/java/cc/winboll/studio/appbase/services/MainService.java
new file mode 100644
index 0000000..fdbbd62
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/services/MainService.java
@@ -0,0 +1,316 @@
+package cc.winboll.studio.appbase.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 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.appbase.MyTileService;
+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.APPNewsWidget;
+import cc.winboll.studio.libappbase.LogUtils;
+import java.util.ArrayList;
+import cc.winboll.studio.libappbase.sos.APPModel;
+
+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;
+ ArrayList mAPPModelConnectionList;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new MyBinder();
+ }
+
+ public MainServiceThread getRemindThread() {
+ return mMainServiceThread;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ LogUtils.d(TAG, "onCreate()");
+ mAPPModelConnectionList = new ArrayList();
+
+ _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();
+
+ if (mMainReceiver == null) {
+ // 注册广播接收器
+ mMainReceiver = new MainReceiver(this);
+ mMainReceiver.registerAction(this);
+ }
+
+ // 启动小部件
+ Intent intentTimeWidget = new Intent(this, APPNewsWidget.class);
+ intentTimeWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
+ this.sendBroadcast(intentTimeWidget);
+
+ startMainServiceThread();
+
+ MyTileService.updateServiceIconStatus(this);
+
+ LogUtils.i(TAG, "Main Service Is Start.");
+ }
+ }
+
+ // 唤醒和绑定守护进程
+ //
+ void wakeupAndBindAssistant() {
+ LogUtils.d(TAG, "wakeupAndBindAssistant()");
+
+ Intent intent = new Intent(this, AssistantService.class);
+ startService(intent);
+ // 绑定服务的Intent
+ bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
+ }
+
+ // 开启提醒铃声线程
+ //
+ public void startMainServiceThread() {
+ LogUtils.d(TAG, "startMainServiceThread");
+ if (mMainServiceThread == null) {
+ mMainServiceThread = new MainServiceThread(this, mMainServiceHandler);
+ LogUtils.d(TAG, "new MainServiceThread");
+ } else {
+ if (mMainServiceThread.isExist() == true) {
+ mMainServiceThread = new MainServiceThread(this, mMainServiceHandler);
+ LogUtils.d(TAG, "renew MainServiceThread");
+ } else {
+ // 提醒进程正在进行中就更新状态后退出
+ LogUtils.d(TAG, "A mMainServiceThread running.");
+ return;
+ }
+ }
+ mMainServiceThread.start();
+ }
+
+ public void stopRemindThread() {
+ if (mMainServiceThread != null) {
+ mMainServiceThread.setIsExist(true);
+ mMainServiceThread = null;
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ //LogUtils.d(TAG, "onDestroy");
+ mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
+ 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);
+ // 停止消息提醒进程
+ stopRemindThread();
+
+ MyTileService.updateServiceIconStatus(this);
+
+ super.onDestroy();
+ //LogUtils.d(TAG, "onDestroy done");
+ }
+ }
+
+ public void bindAPPModelConnection(APPModel bean) {
+ LogUtils.d(TAG, "bindAPPModelConnection(...)");
+ // 清理旧的绑定链接
+ 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);
+ mAPPModelConnectionList.remove(i);
+ }
+ }
+
+ // 绑定服务
+ APPConnection appConnection = new APPConnection();
+ Intent intentService = new Intent();
+ intentService.setComponent(new ComponentName(bean.getAppPackageName(), bean.getAppMainServiveName()));
+ bindService(intentService, appConnection, Context.BIND_IMPORTANT);
+ mAPPModelConnectionList.add(appConnection);
+
+ 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 APPConnection implements ServiceConnection {
+
+ ComponentName mComponentName;
+
+ boolean isBindToAPP(APPModel bean) {
+ return mComponentName != null
+ && mComponentName.getClassName().equals(bean.getAppMainServiveName())
+ && mComponentName.getPackageName().equals(bean.getAppPackageName());
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ LogUtils.d(TAG, "onServiceConnected(...)");
+ mComponentName = name;
+ LogUtils.d(TAG, String.format("onServiceConnected : \ngetClassName %s\ngetPackageName %s", name.getClassName(), name.getPackageName()));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ LogUtils.d(TAG, "onServiceDisconnected(...)");
+ LogUtils.d(TAG, String.format("onServiceDisconnected : \ngetClassName %s\ngetPackageName %s", name.getClassName(), name.getPackageName()));
+
+ // 尝试无参数启动一下服务
+ 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(appPackage, appMainServiceClassName));
+ startService(intentService);
+ }
+
+ }
+
+ // 主进程与守护进程连接时需要用到此类
+ //
+ 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();
+ }
+ 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/winboll/src/main/java/cc/winboll/studio/appbase/services/TestDemoBindService.java b/winboll/src/main/java/cc/winboll/studio/appbase/services/TestDemoBindService.java
new file mode 100644
index 0000000..14209de
--- /dev/null
+++ b/winboll/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/winboll/src/main/java/cc/winboll/studio/appbase/services/TestDemoService.java b/winboll/src/main/java/cc/winboll/studio/appbase/services/TestDemoService.java
new file mode 100644
index 0000000..6098dce
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/services/TestDemoService.java
@@ -0,0 +1,156 @@
+package cc.winboll.studio.appbase.services;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @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.WinBoll;
+
+public class TestDemoService extends Service {
+
+ 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;
+
+ 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 TestDemoService getService() {
+ return TestDemoService.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(...)");
+ TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
+ if (bean == null) {
+ bean = new TestDemoServiceBean();
+ }
+
+ 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);
+ }
+ }
+
+ run();
+
+ return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
+ //return super.onStartCommand(intent, flags, startId);
+ }
+
+ void run() {
+ LogUtils.d(TAG, "run()");
+ TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
+ if (bean == null) {
+ bean = new TestDemoServiceBean();
+ TestDemoServiceBean.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()");
+ TestThread.getInstance(this).setIsExit(true);
+
+ _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");
+
+ 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/winboll/src/main/java/cc/winboll/studio/appbase/threads/MainServiceThread.java b/winboll/src/main/java/cc/winboll/studio/appbase/threads/MainServiceThread.java
new file mode 100644
index 0000000..25d7231
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/threads/MainServiceThread.java
@@ -0,0 +1,54 @@
+package cc.winboll.studio.appbase.threads;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/14 03:46:44
+ */
+import android.content.Context;
+import cc.winboll.studio.appbase.handlers.MainServiceHandler;
+import cc.winboll.studio.libappbase.LogUtils;
+import java.lang.ref.WeakReference;
+
+public class MainServiceThread extends Thread {
+
+ public static final String TAG = "MainServiceThread";
+
+ Context mContext;
+
+ // 控制线程是否退出的标志
+ volatile boolean isExist = false;
+
+ // 服务Handler, 用于线程发送消息使用
+ WeakReference mwrMainServiceHandler;
+
+ public void setIsExist(boolean isExist) {
+ this.isExist = isExist;
+ }
+
+ public boolean isExist() {
+ return isExist;
+ }
+
+ public MainServiceThread(Context context, MainServiceHandler handler) {
+ mContext = context;
+ mwrMainServiceHandler = new WeakReference(handler);
+ }
+
+ @Override
+ public void run() {
+ LogUtils.d(TAG, "run()");
+
+ while (!isExist()) {
+ //ToastUtils.show("run()");
+ //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/winboll/src/main/java/cc/winboll/studio/appbase/widgets/APPNewsWidget.java b/winboll/src/main/java/cc/winboll/studio/appbase/widgets/APPNewsWidget.java
new file mode 100644
index 0000000..c844983
--- /dev/null
+++ b/winboll/src/main/java/cc/winboll/studio/appbase/widgets/APPNewsWidget.java
@@ -0,0 +1,186 @@
+package cc.winboll.studio.appbase.widgets;
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/15 14:41:25
+ * @Describe TimeWidget
+ */
+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.appbase.R;
+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.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 APPNewsWidget extends AppWidgetProvider {
+
+ public static final String TAG = "APPNewsWidget";
+
+ 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 _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) {
+ initWinBollNewsBeanList(context);
+ for (int appWidgetId : appWidgetIds) {
+ updateAppWidget(context, appWidgetManager, appWidgetId);
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ super.onReceive(context, intent);
+ 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, 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 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));
+
+
+ String appName = AppUtils.getAppNameByPackageName(context, szAppPackageName);
+ LogUtils.d(TAG, String.format("appName %s", appName));
+ WinBollNewsBean winBollNewsBean = 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("] Wake up ");
+ sbLine.append(appName);
+ winBollNewsBean.setMessage(sbLine.toString());
+
+ addWinBollNewsBean(context, winBollNewsBean);
+
+ AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
+ int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class));
+ for (int appWidgetId : appWidgetIds) {
+ updateAppWidget(context, appWidgetManager, appWidgetId);
+ }
+ }
+ } catch (IOException e) {
+ LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
+ }
+ }
+ }
+ }
+
+ //
+ // 加入新报告信息
+ //
+ public synchronized static void addWinBollNewsBean(Context context, WinBollNewsBean bean) {
+ initWinBollNewsBeanList(context);
+ _WinBollNewsBeanList.add(0, bean);
+ // 控制记录总数
+ while (_WinBollNewsBeanList.size() > _MAX_PAGES * _OnePageLinesCount) {
+ _WinBollNewsBeanList.remove(_WinBollNewsBeanList.size() - 1);
+ }
+ WinBollNewsBean.saveBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class);
+ }
+
+ synchronized static void initWinBollNewsBeanList(Context context) {
+ if (_WinBollNewsBeanList == null) {
+ _WinBollNewsBeanList = new ArrayList();
+ WinBollNewsBean.loadBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.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_news);
+ //设置按钮点击事件
+ 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, 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.tv_msg, getPageInfo());
+ views.setTextViewText(R.id.tv_news, getMessage());
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+ }
+
+ public static String getMessage() {
+ ArrayList msgTemp = new ArrayList();
+ if (_WinBollNewsBeanList != null) {
+ int start = _OnePageLinesCount * _CurrentPageIndex;
+ 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;
+ }
+ return "";
+ }
+
+ public static void prePage(Context context) {
+ if (_WinBollNewsBeanList != null) {
+ if (_CurrentPageIndex > 0) {
+ _CurrentPageIndex = _CurrentPageIndex - 1;
+ }
+ Intent intentWidget = new Intent(context, APPNewsWidget.class);
+ intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
+ context.sendBroadcast(intentWidget);
+ }
+ }
+
+ public static void nextPage(Context context) {
+ if (_WinBollNewsBeanList != null) {
+ if ((_CurrentPageIndex + 1) * _OnePageLinesCount < _WinBollNewsBeanList.size()) {
+ _CurrentPageIndex = _CurrentPageIndex + 1;
+ }
+ Intent intentWidget = new Intent(context, APPNewsWidget.class);
+ intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
+ context.sendBroadcast(intentWidget);
+ }
+ }
+
+ String getPageInfo() {
+ if (_WinBollNewsBeanList == null) {
+ return "0/0";
+ }
+ 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/winboll/src/main/res/drawable/ic_cloud.xml b/winboll/src/main/res/drawable/ic_cloud.xml
new file mode 100644
index 0000000..62b99af
--- /dev/null
+++ b/winboll/src/main/res/drawable/ic_cloud.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/winboll/src/main/res/drawable/ic_cloud_outline.xml b/winboll/src/main/res/drawable/ic_cloud_outline.xml
new file mode 100644
index 0000000..fb06b79
--- /dev/null
+++ b/winboll/src/main/res/drawable/ic_cloud_outline.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/winboll/src/main/res/layout/activity_main.xml b/winboll/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..193d0cc
--- /dev/null
+++ b/winboll/src/main/res/layout/activity_main.xml
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/winboll/src/main/res/layout/activity_new.xml b/winboll/src/main/res/layout/activity_new.xml
new file mode 100644
index 0000000..0917de8
--- /dev/null
+++ b/winboll/src/main/res/layout/activity_new.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/winboll/src/main/res/layout/activity_new2.xml b/winboll/src/main/res/layout/activity_new2.xml
new file mode 100644
index 0000000..38b5906
--- /dev/null
+++ b/winboll/src/main/res/layout/activity_new2.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/winboll/src/main/res/layout/widget_news.xml b/winboll/src/main/res/layout/widget_news.xml
new file mode 100644
index 0000000..f5741ae
--- /dev/null
+++ b/winboll/src/main/res/layout/widget_news.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/winboll/src/main/res/menu/toolbar_main.xml b/winboll/src/main/res/menu/toolbar_main.xml
new file mode 100644
index 0000000..a5662ee
--- /dev/null
+++ b/winboll/src/main/res/menu/toolbar_main.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/winboll/src/main/res/values/colors.xml b/winboll/src/main/res/values/colors.xml
new file mode 100644
index 0000000..87d3836
--- /dev/null
+++ b/winboll/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #FF00B322
+ #FF005C12
+ #FF8DFFA2
+ #FFFFFB8D
+
diff --git a/winboll/src/main/res/values/strings.xml b/winboll/src/main/res/values/strings.xml
new file mode 100644
index 0000000..eb33ef7
--- /dev/null
+++ b/winboll/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ AppBase
+ WinBoll
+
diff --git a/winboll/src/main/res/values/styles.xml b/winboll/src/main/res/values/styles.xml
new file mode 100644
index 0000000..d8e7e1b
--- /dev/null
+++ b/winboll/src/main/res/values/styles.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/winboll/src/main/res/xml/widget_provider_info_sos.xml b/winboll/src/main/res/xml/widget_provider_info_sos.xml
new file mode 100644
index 0000000..e03bd34
--- /dev/null
+++ b/winboll/src/main/res/xml/widget_provider_info_sos.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/winboll/src/stage/AndroidManifest.xml b/winboll/src/stage/AndroidManifest.xml
new file mode 100644
index 0000000..ee78d9f
--- /dev/null
+++ b/winboll/src/stage/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/winboll/src/stage/res/values/strings.xml b/winboll/src/stage/res/values/strings.xml
new file mode 100644
index 0000000..ace0c41
--- /dev/null
+++ b/winboll/src/stage/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+