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 @@ + + + + + + + + + + + + + + + + + + + + + +