diff --git a/winboll/build.gradle b/winboll/build.gradle index 7cc77dfe..923a34c5 100644 --- a/winboll/build.gradle +++ b/winboll/build.gradle @@ -66,8 +66,8 @@ dependencies { //api 'androidx.vectordrawable:vectordrawable:1.1.0' //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0' //api 'androidx.fragment:fragment:1.1.0' - + api 'cc.winboll.studio:libaes:15.8.0' - api 'cc.winboll.studio:libapputils:15.8.1' - api 'cc.winboll.studio:libappbase:15.8.0' + api 'cc.winboll.studio:libapputils:15.8.2' + api 'cc.winboll.studio:libappbase:15.8.2' } diff --git a/winboll/src/main/AndroidManifest.xml b/winboll/src/main/AndroidManifest.xml index 08260eef..a098c3ce 100644 --- a/winboll/src/main/AndroidManifest.xml +++ b/winboll/src/main/AndroidManifest.xml @@ -3,6 +3,15 @@ xmlns:android="http://schemas.android.com/apk/res/android" package="cc.winboll.studio.winboll"> + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java b/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java index 6c9d1423..ce829c86 100644 --- a/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java +++ b/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java @@ -1,18 +1,16 @@ package cc.winboll.studio.winboll; -import android.graphics.drawable.Drawable; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; -import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogView; -import com.hjq.toast.ToastUtils; public class MainActivity extends AppCompatActivity { public static final String TAG = "MainActivity"; LogView mLogView; + private TerminalView mTerminalView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -39,7 +37,10 @@ public class MainActivity extends AppCompatActivity { mLogView = findViewById(R.id.logview); - ToastUtils.show("onCreate"); + mTerminalView = findViewById(R.id.terminalView); + + // 示例:模拟用户输入 "ls" 命令 + mTerminalView.sendInput("ls"); } @Override diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/MyTileService.java b/winboll/src/main/java/cc/winboll/studio/winboll/MyTileService.java new file mode 100644 index 00000000..9e174108 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/MyTileService.java @@ -0,0 +1,79 @@ +package cc.winboll.studio.appbase; + +/** + * @Author ZhanGSKen + * @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/winboll/activities/LogonActivity.java b/winboll/src/main/java/cc/winboll/studio/winboll/activities/LogonActivity.java new file mode 100644 index 00000000..029b1eee --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/activities/LogonActivity.java @@ -0,0 +1,150 @@ +package cc.winboll.studio.libappbase.activities; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.RadioButton; +import cc.winboll.studio.libappbase.BuildConfig; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.LogView; +import cc.winboll.studio.libappbase.R; +import cc.winboll.studio.libappbase.models.UserInfoModel; +import cc.winboll.studio.libappbase.utils.RSAUtils; +import cc.winboll.studio.libappbase.utils.YunUtils; +import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; + +/** + * @Author ZhanGSKen + * @Date 2025/06/04 13:29 + * @Describe 用户登录框 + */ +public class LogonActivity extends Activity implements IWinBoLLActivity { + + public static final String TAG = "LogonActivity"; + + public static final String DEBUG_HOST = "http://10.8.0.250:456"; + public static final String YUN_HOST = "https://yun.winboll.cc"; + + + String mHost = ""; + RadioButton mrbYunHost; + RadioButton mrbDebugHost; + 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_logon); + mLogView = findViewById(R.id.logview); + mLogView.start(); + + mHost = BuildConfig.DEBUG ? DEBUG_HOST: YUN_HOST; + if (BuildConfig.DEBUG) { + mrbYunHost = findViewById(R.id.rb_yunhost); + mrbDebugHost = findViewById(R.id.rb_debughost); + mrbYunHost.setChecked(!BuildConfig.DEBUG); + mrbDebugHost.setChecked(BuildConfig.DEBUG); + } else { + findViewById(R.id.ll_hostbar).setVisibility(View.GONE); + } + } + + public void onSwitchHost(View view) { + if (view.getId() == R.id.rb_yunhost) { + mrbDebugHost.setChecked(false); + mHost = YUN_HOST; + } else if (view.getId() == R.id.rb_debughost) { + mrbYunHost.setChecked(false); + mHost = DEBUG_HOST; + } + } + + @Override + protected void onResume() { + super.onResume(); + mLogView.start(); + } + + public void onTestLogin(View view) { + LogUtils.d(TAG, "onTestLogin"); + final YunUtils yunUtils = YunUtils.getInstance(this); + + UserInfoModel userInfoModel = new UserInfoModel(); + userInfoModel.setUsername("jian"); + userInfoModel.setPassword("kkiio"); + userInfoModel.setToken("aaa111"); + yunUtils.login(mHost, userInfoModel); + } + + public void onTestRSA(View view) { + LogUtils.d(TAG, "onTestRSA"); + RSAUtils utils = RSAUtils.getInstance(this); + + try { + // 测试 1:首次生成密钥对 + LogUtils.d(TAG, "==== 首次生成密钥对 ===="); + if (utils.keysExist()) { + LogUtils.d(TAG, "密钥对已生成"); + } else { + utils.generateAndSaveKeys(); + LogUtils.d(TAG, "密钥对生成成功。"); + } + + // 测试 2:获取密钥对(自动读取已生成的文件) + KeyPair keyPair = utils.getOrGenerateKeys(); + PublicKey publicKey = keyPair.getPublic(); + PrivateKey privateKey = keyPair.getPrivate(); + + // 打印密钥信息 + LogUtils.d(TAG, "\n==== 密钥信息 ===="); + LogUtils.d(TAG, "公钥算法:" + publicKey.getAlgorithm()); + LogUtils.d(TAG, "公钥编码长度:" + publicKey.getEncoded().length + "字节"); + LogUtils.d(TAG, "私钥算法:" + privateKey.getAlgorithm()); + LogUtils.d(TAG, "私钥编码长度:" + privateKey.getEncoded().length + "字节"); + + // 测试 3:重复调用时检查是否复用文件 + LogUtils.d(TAG, "\n==== 二次调用 ===="); + KeyPair reusedPair = utils.getOrGenerateKeys(); + LogUtils.d(TAG, "是否为同一公钥:" + (publicKey.equals(reusedPair.getPublic()))); // true(单例引用) + LogUtils.d(TAG, "操作完成"); + + String testMessage = "Hello, RSA Encryption!"; + + // 1. 获取或生成密钥对 + PublicKey publicKeyReused = reusedPair.getPublic(); + PrivateKey privateKeyReused = reusedPair.getPrivate(); + + // 2. 公钥加密 + byte[] encryptedData = utils.encryptWithPublicKey(testMessage, publicKeyReused); + LogUtils.d(TAG, "加密后数据(字节长度):" + encryptedData.length); + + // 3. 私钥解密 + String decryptedMessage = utils.decryptWithPrivateKey(encryptedData, privateKeyReused); + LogUtils.d(TAG, "解密结果: " + decryptedMessage); + + // 4. 验证解密是否成功 + if (testMessage.equals(decryptedMessage)) { + LogUtils.d(TAG, "加密解密测试通过!"); + } else { + LogUtils.d(TAG, "测试失败:内容不一致"); + } + } catch (Exception e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + } + + +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/activities/New2Activity.java b/winboll/src/main/java/cc/winboll/studio/winboll/activities/New2Activity.java new file mode 100644 index 00000000..faf79d20 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/activities/New2Activity.java @@ -0,0 +1,77 @@ +package cc.winboll.studio.appbase.activities; + +/** + * @Author ZhanGSKen + * @Date 2025/03/25 11:46:40 + * @Describe 测试窗口2 + */ +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Toolbar; +import cc.winboll.studio.appbase.R; +import cc.winboll.studio.libappbase.GlobalApplication; +import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; + +public class New2Activity extends WinBoLLActivity 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); + setActionBar(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); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 + return super.onOptionsItemSelected(item); + } +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/activities/NewActivity.java b/winboll/src/main/java/cc/winboll/studio/winboll/activities/NewActivity.java new file mode 100644 index 00000000..f87156e1 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/activities/NewActivity.java @@ -0,0 +1,76 @@ +package cc.winboll.studio.appbase.activities; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/03/25 05:04:22 + * @Describe + */ +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Toolbar; +import cc.winboll.studio.appbase.R; +import cc.winboll.studio.libappbase.GlobalApplication; +import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; + +public class NewActivity extends WinBoLLActivity 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); + setActionBar(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); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 + return super.onOptionsItemSelected(item); + } +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/activities/WinBoLLActivity.java b/winboll/src/main/java/cc/winboll/studio/winboll/activities/WinBoLLActivity.java new file mode 100644 index 00000000..0721c401 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/activities/WinBoLLActivity.java @@ -0,0 +1,61 @@ +package cc.winboll.studio.appbase.activities; + +/** + * @Author ZhanGSKen + * @Date 2025/05/10 09:48 + * @Describe WinBoLL 窗口基础类 + */ +import android.app.Activity; +import android.os.Bundle; +import android.view.MenuItem; +import cc.winboll.studio.appbase.MainActivity; +import cc.winboll.studio.appbase.R; +import cc.winboll.studio.libappbase.GlobalApplication; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; + +public class WinBoLLActivity extends Activity implements IWinBoLLActivity { + + public static final String TAG = "WinBoLLActivity"; + + @Override + public Activity getActivity() { + return this; + } + + @Override + public String getTag() { + return TAG; + } + + @Override + protected void onResume() { + super.onResume(); + LogUtils.d(TAG, String.format("onResume %s", getTag())); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.item_log) { + GlobalApplication.getWinBoLLActivityManager().startLogActivity(this); + return true; + } else if (item.getItemId() == R.id.item_home) { + GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class); + return true; + } + // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 + return super.onOptionsItemSelected(item); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + GlobalApplication.getWinBoLLActivityManager().add(this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + GlobalApplication.getWinBoLLActivityManager().registeRemove(this); + } +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/activities/WinBoLLUnitTestActivity.java b/winboll/src/main/java/cc/winboll/studio/winboll/activities/WinBoLLUnitTestActivity.java new file mode 100644 index 00000000..e40f48fa --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/activities/WinBoLLUnitTestActivity.java @@ -0,0 +1,164 @@ +package cc.winboll.studio.appbase; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.CheckBox; +import android.widget.Toolbar; +import cc.winboll.studio.appbase.R; +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.utils.ToastUtils; + +public class WinBoLLUnitTestActivity extends Activity { + + public static final String TAG = "MainActivity"; + + Toolbar mToolbar; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ToastUtils.show("onCreate"); + setContentView(R.layout.activity_main); + + mToolbar = findViewById(R.id.toolbar); + setActionBar(mToolbar); + + CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1); + cbIsDebugMode.setChecked(GlobalApplication.isDebuging()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.toolbar_main, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if(item.getItemId() == R.id.item_yun) { + GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.YunActivity.class); + } else if(item.getItemId() == R.id.item_logon) { + GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.LogonActivity.class); + } + // 在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); + } + + @Override + protected void onResume() { + super.onResume(); + } + + +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/activities/YunActivity.java b/winboll/src/main/java/cc/winboll/studio/winboll/activities/YunActivity.java new file mode 100644 index 00000000..fe929f4c --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/activities/YunActivity.java @@ -0,0 +1,126 @@ +package cc.winboll.studio.libappbase.activities; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import cc.winboll.studio.libappbase.BuildConfig; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.R; +import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; +import java.io.IOException; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import android.widget.RadioButton; +import cc.winboll.studio.libappbase.LogView; + +/** + * @Author ZhanGSKen + * @Date 2025/06/04 11:06 + * @Describe 云宝云 + */ +public class YunActivity extends Activity implements IWinBoLLActivity { + + public static final String TAG = "YunActivity"; + + public static final String DEBUG_HOST = "http://10.8.0.250:456"; + public static final String YUN_HOST = "https://yun.winboll.cc"; + + String mHost = ""; + RadioButton mrbYunHost; + RadioButton mrbDebugHost; + 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_yun); + mLogView = findViewById(R.id.logview); + mLogView.start(); + + mHost = BuildConfig.DEBUG ? DEBUG_HOST: YUN_HOST; + if (BuildConfig.DEBUG) { + mrbYunHost = findViewById(R.id.rb_yunhost); + mrbDebugHost = findViewById(R.id.rb_debughost); + mrbYunHost.setChecked(!BuildConfig.DEBUG); + mrbDebugHost.setChecked(BuildConfig.DEBUG); + } else { + findViewById(R.id.ll_hostbar).setVisibility(View.GONE); + } + } + + public void onSwitchHost(View view) { + if (view.getId() == R.id.rb_yunhost) { + mrbDebugHost.setChecked(false); + mHost = YUN_HOST; + } else if (view.getId() == R.id.rb_debughost) { + mrbYunHost.setChecked(false); + mHost = DEBUG_HOST; + } + } + + @Override + protected void onResume() { + super.onResume(); + mLogView.start(); + } + + public void onTestYun(View view) { + LogUtils.d(TAG, "onTestYun"); + (new Thread(new Runnable(){ + @Override + public void run() { + testYun(); + } + })).start(); + } + + void testYun() { + OkHttpClient client = new OkHttpClient(); + Request request = new Request.Builder() + .url(mHost + "/backups/") + .build(); + + Response response = null; + try { + response = client.newCall(request).execute(); + if (response.isSuccessful()) { + String responseBody = ""; + if (response.body() != null) { + responseBody = response.body().string(); + } + + // 正则匹配:任意主机名 -> Test OK(主机名部分匹配非空字符) + boolean isMatch = responseBody.matches(".+? -> Test OK"); + + if (isMatch) { + LogUtils.d(TAG, responseBody); + } else { + LogUtils.d(TAG, "响应内容不匹配,内容:" + responseBody); + } + } else { + LogUtils.d(TAG, "请求失败,状态码:" + response.code()); + } + } catch (IOException e) { + LogUtils.d(TAG, "读取响应体失败:" + e.getMessage()); + } catch (Exception e) { + LogUtils.d(TAG, "异常:" + e.getMessage()); + e.printStackTrace(); // Java 7 需显式打印堆栈 + } finally { + // 手动关闭 Response(Java 7 不支持 try-with-resources) + if (response != null && response.body() != null) { + response.body().close(); + } + } + } +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/handlers/MainServiceHandler.java b/winboll/src/main/java/cc/winboll/studio/winboll/handlers/MainServiceHandler.java new file mode 100644 index 00000000..c9d6a0e8 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/handlers/MainServiceHandler.java @@ -0,0 +1,38 @@ +package cc.winboll.studio.appbase.handlers; + +/** + * @Author ZhanGSKen + * @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/winboll/models/MainServiceBean.java b/winboll/src/main/java/cc/winboll/studio/winboll/models/MainServiceBean.java new file mode 100644 index 00000000..01dc4144 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/models/MainServiceBean.java @@ -0,0 +1,67 @@ +package cc.winboll.studio.appbase.models; + +/** + * @Author ZhanGSKen + * @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/winboll/models/TestDemoBindServiceBean.java b/winboll/src/main/java/cc/winboll/studio/winboll/models/TestDemoBindServiceBean.java new file mode 100644 index 00000000..75f7184b --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/models/TestDemoBindServiceBean.java @@ -0,0 +1,67 @@ +package cc.winboll.studio.appbase.models; + +/** + * @Author ZhanGSKen + * @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/winboll/models/TestDemoServiceBean.java b/winboll/src/main/java/cc/winboll/studio/winboll/models/TestDemoServiceBean.java new file mode 100644 index 00000000..8d6a379a --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/models/TestDemoServiceBean.java @@ -0,0 +1,68 @@ +package cc.winboll.studio.appbase.models; + +/** + * @Author ZhanGSKen + * @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/winboll/models/WinBoLLNewsBean.java b/winboll/src/main/java/cc/winboll/studio/winboll/models/WinBoLLNewsBean.java new file mode 100644 index 00000000..de2b03d9 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/models/WinBoLLNewsBean.java @@ -0,0 +1,70 @@ +package cc.winboll.studio.libappbase.models; +import android.util.JsonReader; +import android.util.JsonWriter; +import cc.winboll.studio.libappbase.BaseBean; +import java.io.IOException; + +/** + * @Author ZhanGSKen + * @Date 2025/05/10 09:36 + * @Describe WinBoLL 应用消息数据模型 + */ +public class WinBoLLNewsBean extends BaseBean { + + public static final String TAG = "WinBoLLNewsBean"; + + 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/winboll/receivers/APPNewsWidgetClickListener.java b/winboll/src/main/java/cc/winboll/studio/winboll/receivers/APPNewsWidgetClickListener.java new file mode 100644 index 00000000..fd4a35d9 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/receivers/APPNewsWidgetClickListener.java @@ -0,0 +1,36 @@ +package cc.winboll.studio.appbase.receivers; + +/** + * @Author ZhanGSKen + * @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/winboll/receivers/MainReceiver.java b/winboll/src/main/java/cc/winboll/studio/winboll/receivers/MainReceiver.java new file mode 100644 index 00000000..ee435a93 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/receivers/MainReceiver.java @@ -0,0 +1,115 @@ +package cc.winboll.studio.appbase.receivers; + +/** + * @Author ZhanGSKen + * @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.services.MainService; +import cc.winboll.studio.appbase.widgets.APPNewsWidget; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.models.WinBoLLModel; +import cc.winboll.studio.libappbase.models.WinBoLLNewsBean; +import cc.winboll.studio.libappbase.sos.SOS; +import cc.winboll.studio.libappbase.sos.SOSObject; +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(IWinBoLLActivity.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 szWinBoLLModel = intent.getStringExtra(WinBoLL.EXTRA_WINBOLLMODEL); + LogUtils.d(TAG, String.format("szAPPModel %s", szWinBoLLModel)); + if (szWinBoLLModel != null && !szWinBoLLModel.equals("")) { + try { + WinBoLLModel bean = WinBoLLModel.parseStringToBean(szWinBoLLModel, WinBoLLModel.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().bindWinBoLLModelConnection(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/winboll/receivers/MyBroadcastReceiver.java b/winboll/src/main/java/cc/winboll/studio/winboll/receivers/MyBroadcastReceiver.java new file mode 100644 index 00000000..bcd970ed --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/receivers/MyBroadcastReceiver.java @@ -0,0 +1,29 @@ +package cc.winboll.studio.libappbase.receiver; + +/** + * @Author ZhanGSKen + * @Date 2025/02/13 21:19:09 + * @Describe MyBroadcastReceiver + */ +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.R; + +public class MyBroadcastReceiver extends BroadcastReceiver { + + public static final String TAG = "MyBroadcastReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + if (context.getString(R.string.action_sos).equals(intent.getAction())) { + String message = intent.getStringExtra("message"); + String sosPackage = intent.getStringExtra("sosPackage"); + + // 处理接收到的广播消息 + LogUtils.d(TAG, String.format("MyBroadcastReceiver action %s \n%s\n%s", intent.getAction(), sosPackage, message)); + } + } +} + diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/services/AssistantService.java b/winboll/src/main/java/cc/winboll/studio/winboll/services/AssistantService.java new file mode 100644 index 00000000..07702412 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/services/AssistantService.java @@ -0,0 +1,138 @@ +package cc.winboll.studio.appbase.services; + +/** + * @Author ZhanGSKen + * @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/winboll/services/MainService.java b/winboll/src/main/java/cc/winboll/studio/winboll/services/MainService.java new file mode 100644 index 00000000..f71bdbb7 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/services/MainService.java @@ -0,0 +1,317 @@ +package cc.winboll.studio.appbase.services; + +/** + * @Author ZhanGSKen + * @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.handlers.MainServiceHandler; +import cc.winboll.studio.appbase.models.MainServiceBean; +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 cc.winboll.studio.libappbase.models.APPModel; +import cc.winboll.studio.libappbase.models.WinBoLLModel; +import java.util.ArrayList; + +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 bindWinBoLLModelConnection(WinBoLLModel 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); + WinBoLLModel appSOSBean = new WinBoLLModel(bean.getAppPackageName(), bean.getAppMainServiveName()); + intentWidget.putExtra("APPSOSBean", appSOSBean.toString()); + sendBroadcast(intentWidget); + } + + public class APPConnection implements ServiceConnection { + + ComponentName mComponentName; + + boolean isBindToAPP(WinBoLLModel 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/winboll/services/TestDemoBindService.java b/winboll/src/main/java/cc/winboll/studio/winboll/services/TestDemoBindService.java new file mode 100644 index 00000000..7136d3ce --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/services/TestDemoBindService.java @@ -0,0 +1,179 @@ +package cc.winboll.studio.appbase.services; + +/** + * @Author ZhanGSKen + * @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.App; +import cc.winboll.studio.appbase.models.TestDemoBindServiceBean; +import cc.winboll.studio.appbase.services.TestDemoBindService; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.sos.SOS; +import cc.winboll.studio.libappbase.winboll.WinBoLL; + +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/winboll/services/TestDemoService.java b/winboll/src/main/java/cc/winboll/studio/winboll/services/TestDemoService.java new file mode 100644 index 00000000..ee7de32d --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/services/TestDemoService.java @@ -0,0 +1,155 @@ +package cc.winboll.studio.appbase.services; + +/** + * @Author ZhanGSKen + * @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; + +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/winboll/sos/SOS.java b/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOS.java new file mode 100644 index 00000000..6d80d255 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOS.java @@ -0,0 +1,59 @@ +package cc.winboll.studio.libappbase.sos; + +/** + * @Author ZhanGSKen + * @Date 2025/03/02 09:36:29 + * @Describe WinBoLL 应用 SOS 机理保护类 + */ +import android.content.Context; +import android.content.Intent; +import cc.winboll.studio.libappbase.LogUtils; +import java.io.IOException; + +public class SOS { + + public static final String TAG = "SOS"; + + public static final String ACTION_SOS = SOS.class.getName() + ".ACTION_SOS"; + public static final String EXTRA_OBJECT = "EXTRA_OBJECT"; + + public static void sosToAppBase(Context context, String sosService) { + LogUtils.d(TAG, "sosToAppBase()"); + String szToPackage = "cc.winboll.studio.appbase"; + sos(context, szToPackage, sosService); + + } + + public static void sosToAppBaseBeta(Context context, String sosService) { + LogUtils.d(TAG, "sosToAppBaseBeta()"); + String szToPackage = "cc.winboll.studio.appbase.beta"; + sos(context, szToPackage, sosService); + + } + + static void sos(Context context, String szToPackage, String sosService) { + LogUtils.d(TAG, "sos(...)"); + Intent intent = new Intent(ACTION_SOS); + intent.putExtra(EXTRA_OBJECT, genSOSObject(context.getPackageName(), sosService)); + intent.setPackage(szToPackage); + LogUtils.d(TAG, String.format("ACTION_SOS :\nTo Package : %sSOS Service : %s\n", szToPackage, sosService)); + context.sendBroadcast(intent); + } + + public static SOSObject parseSOSObject(String szSOSObject) { + try { + return SOSObject.parseStringToBean(szSOSObject, SOSObject.class); + } catch (IOException e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + return null; + } + + public static String sosObjectToString(SOSObject object) { + return object.toString(); + } + + public static String genSOSObject(String objectPackageName, String objectServiveName) { + return (new SOSObject(objectPackageName, objectServiveName)).toString(); + } +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSCenterService.java b/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSCenterService.java new file mode 100644 index 00000000..7c7c1fa7 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSCenterService.java @@ -0,0 +1,182 @@ +package cc.winboll.studio.libappbase.sos; + +/** + * @Author ZhanGSKen + * @Date 2025/02/27 14:00:21 + * @Describe Simple Operate Signal Service Center. + * 简单操作信号服务中心 + */ +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.os.IInterface; +import android.os.Parcel; +import android.os.RemoteException; +import java.io.FileDescriptor; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import cc.winboll.studio.libappbase.LogUtils; + +public class SOSCenterService extends Service { + + public static final String TAG = "SOSCenterService"; + + private final IBinder binder =(IBinder)new SOSBinder(); + + SOSCenterServiceModel mSOSCenterServiceModel; + static MainThread _MainThread; + public static synchronized MainThread getMainThreadInstance() { + if (_MainThread == null) { + _MainThread = new MainThread(); + } + return _MainThread; + } + + @Override + public IBinder onBind(Intent intent) { + return binder; + } + + public class SOSBinder implements IBinder { + + @Override + public void dump(FileDescriptor fileDescriptor, String[] string) throws RemoteException { + } + + @Override + public void dumpAsync(FileDescriptor fileDescriptor, String[] string) throws RemoteException { + } + + @Override + public String getInterfaceDescriptor() throws RemoteException { + return null; + } + + @Override + public boolean isBinderAlive() { + return false; + } + + @Override + public void linkToDeath(IBinder.DeathRecipient deathRecipient, int p) throws RemoteException { + } + + @Override + public boolean pingBinder() { + return false; + } + + @Override + public IInterface queryLocalInterface(String string) { + return null; + } + + @Override + public boolean transact(int p, Parcel parcel, Parcel parcel1, int p1) throws RemoteException { + return false; + } + + @Override + public boolean unlinkToDeath(IBinder.DeathRecipient deathRecipient, int p) { + return false; + } + + public static final String TAG = "SOSBinder"; + SOSCenterService getService() { + return SOSCenterService.this; + } + } + + @Override + public void onCreate() { + super.onCreate(); + LogUtils.d(TAG, "onCreate"); + mSOSCenterServiceModel = SOSCenterServiceModel.loadBean(this, SOSCenterServiceModel.class); + if(mSOSCenterServiceModel == null) { + mSOSCenterServiceModel = new SOSCenterServiceModel(); + SOSCenterServiceModel.saveBean(this, mSOSCenterServiceModel); + } + runMainThread(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + LogUtils.d(TAG, "onStartCommand"); + + runMainThread(); + + return mSOSCenterServiceModel.isEnable() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId); + } + + void runMainThread() { + mSOSCenterServiceModel = mSOSCenterServiceModel.loadBean(this, SOSCenterServiceModel.class); + if (mSOSCenterServiceModel.isEnable() + && _MainThread == null) { + getMainThreadInstance().start(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + LogUtils.d(TAG, "onDestroy"); + mSOSCenterServiceModel = SOSCenterServiceModel.loadBean(this, SOSCenterServiceModel.class); + if (mSOSCenterServiceModel.isEnable()) { + LogUtils.d(TAG, "mSOSCenterServiceModel.isEnable()"); +// ISOSAPP iSOSAPP = (ISOSAPP)getApplication(); +// iSOSAPP.helpISOSService(getISOSServiceIntentWhichAskForHelp()); + } + if (_MainThread != null) { + _MainThread.isExist = true; + _MainThread = null; + } + } + + public static void stopISOSService(Context context) { + LogUtils.d(TAG, "stopISOSService"); + SOSCenterServiceModel bean = new SOSCenterServiceModel(); + bean.setIsEnable(false); + SOSCenterServiceModel.saveBean(context, bean); + context.stopService(new Intent(context, SOSCenterServiceModel.class)); + } + + public static void startISOSService(Context context) { + LogUtils.d(TAG, "startISOSService"); + SOSCenterServiceModel bean = new SOSCenterServiceModel(); + bean.setIsEnable(true); + SOSCenterServiceModel.saveBean(context, bean); + context.startService(new Intent(context, SOSCenterServiceModel.class)); + } + + public String getMessage() { + return "Hello from SOSCenterServiceModel"; + } + + static class MainThread extends Thread { + volatile boolean isExist = false; + + public void setIsExist(boolean isExist) { + this.isExist = isExist; + } + + public boolean isExist() { + return isExist; + } + + @Override + public void run() { + super.run(); + while (!isExist) { + LogUtils.d(TAG, "run"); + try { + sleep(1000); + } catch (InterruptedException e) { + LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); + } + } + } + + } +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSCenterServiceModel.java b/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSCenterServiceModel.java new file mode 100644 index 00000000..f2cfc401 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSCenterServiceModel.java @@ -0,0 +1,69 @@ +package cc.winboll.studio.libappbase.sos; + +/** + * @Author ZhanGSKen + * @Date 2025/03/02 09:49:45 + * @Describe SOSCenterServiceModel + * Simple Operate Signal Service Model. + */ +import android.util.JsonReader; +import android.util.JsonWriter; +import cc.winboll.studio.libappbase.BaseBean; +import java.io.IOException; + +public class SOSCenterServiceModel extends BaseBean { + + public static final String TAG = "SOSCenterServiceModel"; + + boolean isEnable; + + public SOSCenterServiceModel() { + this.isEnable = false; + } + + public void setIsEnable(boolean isEnable) { + this.isEnable = isEnable; + } + + public boolean isEnable() { + return isEnable; + } + + @Override + public String getName() { + return SOSCenterServiceModel.class.getName(); + } + + @Override + public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { + super.writeThisToJsonWriter(jsonWriter); + jsonWriter.name("isEnable").value(isEnable()); + + } + + @Override + public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException { + if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { + if (name.equals("isEnable")) { + setIsEnable(jsonReader.nextBoolean()); + } else { + return false; + } + } + return true; + } + + @Override + public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { + jsonReader.beginObject(); + while (jsonReader.hasNext()) { + String name = jsonReader.nextName(); + if (!initObjectsFromJsonReader(jsonReader, name)) { + jsonReader.skipValue(); + } + } + // 结束 JSON 对象 + jsonReader.endObject(); + return this; + } +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSCenterServiceReceiver.java b/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSCenterServiceReceiver.java new file mode 100644 index 00000000..ec3341b1 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSCenterServiceReceiver.java @@ -0,0 +1,29 @@ +package cc.winboll.studio.libappbase.sos; + +/** + * @Author ZhanGSKen + * @Date 2025/02/27 14:04:35 + * @Describe SOSCenterServiceReceiver + */ +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import cc.winboll.studio.libappbase.LogUtils; + +public class SOSCenterServiceReceiver extends BroadcastReceiver { + + public static final String TAG = "SOSCenterServiceReceiver"; + + public static final String ACTION_SOS = SOSCenterServiceReceiver.class.getName() + ".ACTION_SOS"; + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(ACTION_SOS)) { + // 处理接收到的广播消息 + LogUtils.d(TAG, String.format("Action %s \n%s\n%s", action)); + } else { + LogUtils.d(TAG, String.format("%s", action)); + } + } +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSObject.java b/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSObject.java new file mode 100644 index 00000000..5f19563a --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/sos/SOSObject.java @@ -0,0 +1,86 @@ +package cc.winboll.studio.libappbase.sos; + +/** + * @Author ZhanGSKen + * @Date 2025/02/27 14:12:05 + * @Describe SOSBean + */ +import android.util.JsonReader; +import android.util.JsonWriter; +import cc.winboll.studio.libappbase.BaseBean; +import java.io.IOException; + +public class SOSObject extends BaseBean { + + public static final String TAG = "SOSObject"; + + String objectPackageName; + String objectServiveName; + + public SOSObject() { + this.objectPackageName = ""; + this.objectServiveName = ""; + } + + public SOSObject(String objectPackageName, String objectServiveName) { + this.objectPackageName = objectPackageName; + this.objectServiveName = objectServiveName; + } + + public void setObjectPackageName(String objectPackageName) { + this.objectPackageName = objectPackageName; + } + + public String getObjectPackageName() { + return objectPackageName; + } + + public void setObjectServiveName(String objectServiveName) { + this.objectServiveName = objectServiveName; + } + + public String getObjectServiveName() { + return objectServiveName; + } + + @Override + public String getName() { + return SOSObject.class.getName(); + } + + @Override + public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { + super.writeThisToJsonWriter(jsonWriter); + jsonWriter.name("objectPackageName").value(getObjectPackageName()); + jsonWriter.name("objectServiveName").value(getObjectServiveName()); + + } + + @Override + public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException { + if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { + if (name.equals("objectPackageName")) { + setObjectPackageName(jsonReader.nextString()); + } else if (name.equals("objectServiveName")) { + setObjectServiveName(jsonReader.nextString()); + } else { + return false; + } + } + return true; + } + + @Override + public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { + jsonReader.beginObject(); + while (jsonReader.hasNext()) { + String name = jsonReader.nextName(); + if (!initObjectsFromJsonReader(jsonReader, name)) { + jsonReader.skipValue(); + } + } + // 结束 JSON 对象 + jsonReader.endObject(); + return this; + } +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/threads/MainServiceThread.java b/winboll/src/main/java/cc/winboll/studio/winboll/threads/MainServiceThread.java new file mode 100644 index 00000000..c922e84a --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/threads/MainServiceThread.java @@ -0,0 +1,54 @@ +package cc.winboll.studio.appbase.threads; + +/** + * @Author ZhanGSKen + * @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/winboll/utils/TermuxUtils.java b/winboll/src/main/java/cc/winboll/studio/winboll/utils/TermuxUtils.java new file mode 100644 index 00000000..ad7a72d9 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/utils/TermuxUtils.java @@ -0,0 +1,31 @@ +package cc.winboll.studio.winboll.utils; + +/** + * @Author ZhanGSKen + * @Date 2025/06/08 09:05 + * @Describe Termux 应用操作工具集 + */ +public abstract class TermuxUtils { + + public static final String TAG = "TermuxUtils"; + + private void runTermuxCommand(String command) { + // 1. 创建 Intent,指定 Termux 的 RunCommandService + Intent intent = new Intent("com.termux.RUN_COMMAND"); + intent.setPackage("com.termux"); // Termux 应用的包名 + + // 2. 传递命令参数(必填) + //intent.putExtra("command", command); + intent.putExtra("cd ~/WinBoLL&&echo 'WinBoLL cmd exec at (data", command); + + // 3. 可选:设置工作目录(默认为 Termux 的 home 目录) + intent.putExtra("dir", "/data/data/com.termux/files/home/WinBoLL"); + + // 4. 发送 Intent(需处理可能的安全异常或 ActivityNotFoundException) + try { + getApplicationContext().startService(intent); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/widgets/APPNewsWidget.java b/winboll/src/main/java/cc/winboll/studio/winboll/widgets/APPNewsWidget.java new file mode 100644 index 00000000..71a5caa1 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/widgets/APPNewsWidget.java @@ -0,0 +1,187 @@ +package cc.winboll.studio.appbase.widgets; +/** + * @Author ZhanGSKen + * @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.receivers.APPNewsWidgetClickListener; +import cc.winboll.studio.libappbase.AppUtils; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.models.APPModel; +import cc.winboll.studio.libappbase.models.WinBoLLNewsBean; +import cc.winboll.studio.libappbase.winboll.WinBoLL; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import cc.winboll.studio.libappbase.models.WinBoLLModel; + +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 szWinBoLLModel = intent.getStringExtra(WinBoLL.EXTRA_WINBOLLMODEL); + LogUtils.d(TAG, String.format("szWinBoLLModel %s", szWinBoLLModel)); + if (szWinBoLLModel != null && !szWinBoLLModel.equals("")) { + try { + WinBoLLModel bean = WinBoLLModel.parseStringToBean(szWinBoLLModel, WinBoLLModel.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/java/cc/winboll/studio/winboll/widgets/StatusWidget.java b/winboll/src/main/java/cc/winboll/studio/winboll/widgets/StatusWidget.java new file mode 100644 index 00000000..cf3d4ee1 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/widgets/StatusWidget.java @@ -0,0 +1,63 @@ +package cc.winboll.studio.appbase.widgets; + +/** + * @Author ZhanGSKen + * @Date 2025/02/17 20:32:12 + */ +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.libappbase.LogUtils; +import cc.winboll.studio.libappbase.R; +import cc.winboll.studio.libappbase.utils.ServiceUtils; +import android.content.ServiceConnection; +import android.os.IBinder; +import cc.winboll.studio.libappbase.utils.ToastUtils; + +public class StatusWidget extends AppWidgetProvider { + + public static final String TAG = "StatusWidget"; + + public static final String ACTION_STATUS_UPDATE = "cc.winboll.studio.libappbase.widgets.APPWidget.ACTION_STATUS_UPDATE"; + + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + for (int appWidgetId : appWidgetIds) { + updateAppWidget(context, appWidgetManager, appWidgetId); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + super.onReceive(context, intent); + if (intent.getAction().equals(ACTION_STATUS_UPDATE)) { + ToastUtils.show("Test"); + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, StatusWidget.class)); + for (int appWidgetId : appWidgetIds) { + updateAppWidget(context, appWidgetManager, appWidgetId); + } + } + } + + private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { + RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_status); + //设置按钮点击事件 + Intent intentAppButton = new Intent(context, StatusWidgetClickListener.class); + intentAppButton.setAction(StatusWidgetClickListener.ACTION_IVAPP); + PendingIntent pendingIntentAppButton = PendingIntent.getBroadcast(context, 0, intentAppButton, PendingIntent.FLAG_UPDATE_CURRENT); + views.setOnClickPendingIntent(R.id.ivapp, pendingIntentAppButton); + +// boolean isActive = ServiceUtils.isServiceRunning(context, TestService.class.getName()); +// if (isActive) { +// views.setImageViewResource(R.id.ivapp, cc.winboll.studio.libappbase.R.drawable.ic_launcher); +// } else { +// views.setImageViewResource(R.id.ivapp, cc.winboll.studio.libappbase.R.drawable.ic_launcher_disable); +// } + appWidgetManager.updateAppWidget(appWidgetId, views); + } +} diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/widgets/StatusWidgetClickListener.java b/winboll/src/main/java/cc/winboll/studio/winboll/widgets/StatusWidgetClickListener.java new file mode 100644 index 00000000..911b1d64 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/widgets/StatusWidgetClickListener.java @@ -0,0 +1,33 @@ +package cc.winboll.studio.libappbase.widgets; + +/** + * @Author ZhanGSKen + * @Date 2025/02/17 20:33:53 + * @Describe APPWidgetClickListener + */ +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.utils.ToastUtils; + +public class StatusWidgetClickListener extends BroadcastReceiver { + + public static final String TAG = "APPWidgetClickListener"; + + public static final String ACTION_IVAPP = "cc.winboll.studio.libappbase.widgets.StatusWidgetClickListener.ACTION_IVAPP"; + + @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_IVAPP)) { + ToastUtils.show("ACTION_LAUNCHER"); + } else { + LogUtils.d(TAG, String.format("action %s", action)); + } + } +} diff --git a/winboll/src/main/res/drawable/bg_shadow.xml b/winboll/src/main/res/drawable/bg_shadow.xml new file mode 100644 index 00000000..6d3d8989 --- /dev/null +++ b/winboll/src/main/res/drawable/bg_shadow.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + 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 00000000..62b99afd --- /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 00000000..fb06b79a --- /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/drawable/ic_dev_connected.xml b/winboll/src/main/res/drawable/ic_dev_connected.xml new file mode 100644 index 00000000..1fb2f264 --- /dev/null +++ b/winboll/src/main/res/drawable/ic_dev_connected.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/winboll/src/main/res/drawable/ic_dev_disconnected.xml b/winboll/src/main/res/drawable/ic_dev_disconnected.xml new file mode 100644 index 00000000..42679750 --- /dev/null +++ b/winboll/src/main/res/drawable/ic_dev_disconnected.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/winboll/src/main/res/drawable/ic_email.xml b/winboll/src/main/res/drawable/ic_email.xml new file mode 100644 index 00000000..d526b262 --- /dev/null +++ b/winboll/src/main/res/drawable/ic_email.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/winboll/src/main/res/drawable/ic_email_alert.xml b/winboll/src/main/res/drawable/ic_email_alert.xml new file mode 100644 index 00000000..f3ed6134 --- /dev/null +++ b/winboll/src/main/res/drawable/ic_email_alert.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/winboll/src/main/res/drawable/ic_launcher.xml b/winboll/src/main/res/drawable/ic_launcher.xml new file mode 100644 index 00000000..21f28b1a --- /dev/null +++ b/winboll/src/main/res/drawable/ic_launcher.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/winboll/src/main/res/drawable/ic_launcher_disable.xml b/winboll/src/main/res/drawable/ic_launcher_disable.xml new file mode 100644 index 00000000..174f475c --- /dev/null +++ b/winboll/src/main/res/drawable/ic_launcher_disable.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/winboll/src/main/res/drawable/ic_launcher_foreground.xml b/winboll/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 00000000..872b04e2 --- /dev/null +++ b/winboll/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,10 @@ + + + + diff --git a/winboll/src/main/res/drawable/ic_launcher_foreground_disable.xml b/winboll/src/main/res/drawable/ic_launcher_foreground_disable.xml new file mode 100644 index 00000000..4622116b --- /dev/null +++ b/winboll/src/main/res/drawable/ic_launcher_foreground_disable.xml @@ -0,0 +1,10 @@ + + + + diff --git a/winboll/src/main/res/drawable/ic_miapp.png b/winboll/src/main/res/drawable/ic_miapp.png new file mode 100644 index 00000000..8d0939b8 Binary files /dev/null and b/winboll/src/main/res/drawable/ic_miapp.png differ diff --git a/winboll/src/main/res/drawable/ic_winboll_help.xml b/winboll/src/main/res/drawable/ic_winboll_help.xml new file mode 100644 index 00000000..564175fa --- /dev/null +++ b/winboll/src/main/res/drawable/ic_winboll_help.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/winboll/src/main/res/drawable/ic_winboll_log.xml b/winboll/src/main/res/drawable/ic_winboll_log.xml new file mode 100644 index 00000000..011f2b2c --- /dev/null +++ b/winboll/src/main/res/drawable/ic_winboll_log.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/winboll/src/main/res/drawable/ic_winboll_logo.xml b/winboll/src/main/res/drawable/ic_winboll_logo.xml new file mode 100644 index 00000000..ea28987a --- /dev/null +++ b/winboll/src/main/res/drawable/ic_winboll_logo.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/winboll/src/main/res/drawable/ic_winboll_point.xml b/winboll/src/main/res/drawable/ic_winboll_point.xml new file mode 100644 index 00000000..48028cc4 --- /dev/null +++ b/winboll/src/main/res/drawable/ic_winboll_point.xml @@ -0,0 +1,20 @@ + + + + diff --git a/winboll/src/main/res/drawable/shape_gradient.xml b/winboll/src/main/res/drawable/shape_gradient.xml new file mode 100644 index 00000000..c164fe9d --- /dev/null +++ b/winboll/src/main/res/drawable/shape_gradient.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/winboll/src/main/res/drawable/view_border.xml b/winboll/src/main/res/drawable/view_border.xml new file mode 100644 index 00000000..58b374aa --- /dev/null +++ b/winboll/src/main/res/drawable/view_border.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/winboll/src/main/res/layout/activity_logon.xml b/winboll/src/main/res/layout/activity_logon.xml new file mode 100644 index 00000000..2a2c2d16 --- /dev/null +++ b/winboll/src/main/res/layout/activity_logon.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + +