Compare commits
	
		
			38 Commits
		
	
	
		
			3c36ed589a
			...
			powerbell-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f943db17e0 | |||
| 
						 | 
					d7a9cb2a20 | ||
| de34c33706 | |||
| 
						 | 
					10b8da2e21 | ||
| 
						 | 
					ca4e4c7feb | ||
| 4108371c20 | |||
| 
						 | 
					e5c8624d9b | ||
| 561330697b | |||
| 
						 | 
					f7b2c0d4c0 | ||
| 
						 | 
					c3978a1e3c | ||
| 5e198d9c68 | |||
| 
						 | 
					963a3bb7cd | ||
| e9bb789daa | |||
| dbff19e7f4 | |||
| 
						 | 
					44679d0c8a | ||
| 6656161903 | |||
| 
						 | 
					edc63c750b | ||
| b170085482 | |||
| 6e8ba3394d | |||
| b91fe3c16b | |||
| e30b7acfdb | |||
| 6221555dfe | |||
| 
						 | 
					c60eb29f4f | ||
| 
						 | 
					3efcf40025 | ||
| 
						 | 
					27e948b86d | ||
| 
						 | 
					ae4742f3e2 | ||
| 
						 | 
					d3312cbb29 | ||
| 
						 | 
					58f3778dff | ||
| 
						 | 
					f5e2961445 | ||
| 
						 | 
					e4e9c31f02 | ||
| 
						 | 
					a02a7efe43 | ||
| 
						 | 
					2220efd009 | ||
| 
						 | 
					0bc0fdf8c2 | ||
| 
						 | 
					6b7b07a6fd | ||
| 
						 | 
					8f3417818d | ||
| 
						 | 
					ca280c7334 | ||
| 
						 | 
					8172cf7b6f | ||
| 
						 | 
					8014f4149c | 
							
								
								
									
										16
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -87,19 +87,15 @@ lint/tmp/
 | 
			
		||||
# Android Profiling
 | 
			
		||||
*.hprof
 | 
			
		||||
 | 
			
		||||
# Custom
 | 
			
		||||
.androidide
 | 
			
		||||
# 忽略 Lint 输出文件
 | 
			
		||||
lint-results.xml
 | 
			
		||||
lint-results.html
 | 
			
		||||
winboll.properties
 | 
			
		||||
local.properties
 | 
			
		||||
 | 
			
		||||
## 忽略 AndroidIDE 临时文件夹
 | 
			
		||||
.androidide
 | 
			
		||||
 | 
			
		||||
## 忽略模块应用编译配置
 | 
			
		||||
/settings.gradle
 | 
			
		||||
/gradle.properties
 | 
			
		||||
 | 
			
		||||
## 忽略 srv 纠结问题
 | 
			
		||||
/srv/
 | 
			
		||||
 | 
			
		||||
## 忽略 winboll-x 文件夹
 | 
			
		||||
/winboll-x/
 | 
			
		||||
/winboll.properties
 | 
			
		||||
/local.properties
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,3 @@
 | 
			
		||||
[submodule "libjc/jcc/libs"]
 | 
			
		||||
	path = libjc/jcc/libs
 | 
			
		||||
	url = https://gitea.winboll.cc/Studio/APP_libjc_jcc_libs.git
 | 
			
		||||
[submodule "keystore"]
 | 
			
		||||
	path = keystore
 | 
			
		||||
	url = https://gitea.winboll.cc/Studio/keystore.git
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,11 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
	def winbollProps = new Properties()
 | 
			
		||||
    def winbollPropsFile = rootProject.file("${winbollFilePath}")
 | 
			
		||||
    assert(winbollPropsFile.exists())
 | 
			
		||||
    winbollProps.load(new FileInputStream(winbollPropsFile))
 | 
			
		||||
    
 | 
			
		||||
    // 读取秘钥配置文件
 | 
			
		||||
    //
 | 
			
		||||
    def keyProps = new Properties()
 | 
			
		||||
@@ -122,6 +127,7 @@ android {
 | 
			
		||||
                                    FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
 | 
			
		||||
                                    winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
 | 
			
		||||
                                    fos.close();
 | 
			
		||||
									println "\n\n>>> Project build.properties saved.\n\n";
 | 
			
		||||
                                    
 | 
			
		||||
                                    if(winbollBuildProps['libraryProject'] != "") {
 | 
			
		||||
                                        // 如果应用 build.properties 文件设置了类库模块项目文件名
 | 
			
		||||
@@ -133,14 +139,15 @@ android {
 | 
			
		||||
                                        java.nio.file.Path targetFilePath = libraryProjectBuildPropsFile.toPath();
 | 
			
		||||
                                        // 使用copyTo()方法复制文件,如果目标文件存在会被覆盖,可选参数可以选择不覆盖
 | 
			
		||||
	                                    java.nio.file.Files.copy(sourceFilePath, targetFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
 | 
			
		||||
										
 | 
			
		||||
										println "\n\n>>> Library Project build.properties saved.\n\n";
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            
 | 
			
		||||
                            // 如果正在发布,就拷贝到 WinBoLL 标签管理文件夹
 | 
			
		||||
                            // 如果正在发布,就拷贝到 WinBoLL 标签管理文件夹,和处理 Git 仓库管理任务。
 | 
			
		||||
                            //
 | 
			
		||||
                            if((variant.flavorName == "stage"&&variant.buildType.name == "debug")
 | 
			
		||||
                                || (variant.flavorName == "stage"&&variant.buildType.name == "release")){
 | 
			
		||||
                            if(variant.flavorName == "stage"&&variant.buildType.name == "release"){
 | 
			
		||||
                                // 发布 APK 文件
 | 
			
		||||
                                //
 | 
			
		||||
                                // 截取版本号的版本字段为短版本名
 | 
			
		||||
@@ -221,6 +228,7 @@ android {
 | 
			
		||||
                                    }
 | 
			
		||||
                                    
 | 
			
		||||
                                    // 提交新的编译标志配置
 | 
			
		||||
									println 'exec bashCommitAppPublishBuildFlagInfoFilePath ...'
 | 
			
		||||
                                    def resultCommitBuildFlag = exec {
 | 
			
		||||
                                        commandLine 'bash', '--', "${RootProjectDir}/${bashCommitAppPublishBuildFlagInfoFilePath}", "${RootProjectDir}", "${versionName}", variant.buildType.name , rootProject.name
 | 
			
		||||
                                    }
 | 
			
		||||
@@ -229,10 +237,65 @@ android {
 | 
			
		||||
                                }
 | 
			
		||||
                            } //  if(variant.buildType.name == "release"){
 | 
			
		||||
							
 | 
			
		||||
                            // 如果公共目录存在就拷贝到公共目录并重命名为app.apk
 | 
			
		||||
							// 如果正在调试发布版,就只生成和输出APK文件,不处理 Git 仓库提交与更新问题。
 | 
			
		||||
							//
 | 
			
		||||
                            File outCommonDir = new File("/sdcard/AppProjects")
 | 
			
		||||
                            String commandAPKName = "app.apk"
 | 
			
		||||
					        if(variant.flavorName == "stage"&&variant.buildType.name == "debug"){
 | 
			
		||||
                                // 发布 APK 文件
 | 
			
		||||
                                //
 | 
			
		||||
                                // 截取版本号的版本字段为短版本名
 | 
			
		||||
                                String szVersionName = "${versionName}"
 | 
			
		||||
                                String[] szlistTemp = szVersionName.split("-")
 | 
			
		||||
                                String szShortVersionName = szlistTemp[0]
 | 
			
		||||
                                //String szCommonTagAPKName = "${rootProject.name}_" + szShortVersionName + ".apk"
 | 
			
		||||
                                String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk"
 | 
			
		||||
                                println "CommonTagAPKName is : " + szCommonTagAPKName
 | 
			
		||||
                                
 | 
			
		||||
                                //File outTagDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/tag/")
 | 
			
		||||
                                File outTagDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}/")
 | 
			
		||||
                                // 创建目标路径目录
 | 
			
		||||
                                if(!outTagDir.exists()) {
 | 
			
		||||
                                    outTagDir.mkdirs();
 | 
			
		||||
                                    println "Output Folder Created.(Tags) : " + outTagDir.getAbsolutePath()
 | 
			
		||||
                                }
 | 
			
		||||
                                
 | 
			
		||||
                                if(outTagDir.exists()) {
 | 
			
		||||
                                    File targetAPK = new File(outTagDir, "${szCommonTagAPKName}")
 | 
			
		||||
                                    if(targetAPK.exists()) {
 | 
			
		||||
                                        // 标签版本APK文件已经存在,构建拷贝任务停止
 | 
			
		||||
										println '如果是在调试 Stage 版应用包构建,请删除(注:在debug目录)现有的 Stage 应用包('+targetAPK.getAbsolutePath()+')。再编译一次。'
 | 
			
		||||
                                        assert (!targetAPK.exists())
 | 
			
		||||
                                        // 可选择删除并继续输出APK文件
 | 
			
		||||
                                        //delete targetAPK
 | 
			
		||||
                                    }
 | 
			
		||||
                                    // 复制一个备份
 | 
			
		||||
                                    copy{
 | 
			
		||||
                                        from file.outputFile
 | 
			
		||||
                                        into outTagDir
 | 
			
		||||
                                        rename {
 | 
			
		||||
                                            String fileName -> "${outputFileName}"
 | 
			
		||||
                                        }
 | 
			
		||||
                                        println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${outputFileName}"
 | 
			
		||||
                                    }
 | 
			
		||||
                                    // 复制一个并重命名为短版本名
 | 
			
		||||
                                    copy{
 | 
			
		||||
                                        from file.outputFile
 | 
			
		||||
                                        into outTagDir
 | 
			
		||||
                                        rename {
 | 
			
		||||
                                            String fileName -> "${szCommonTagAPKName}"
 | 
			
		||||
                                        }
 | 
			
		||||
                                        println "Output APK (Tags): "+ outTagDir.getAbsolutePath() + "/${szCommonTagAPKName}"
 | 
			
		||||
                                    }
 | 
			
		||||
                                    
 | 
			
		||||
                                    //不保存编译标志配置
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            
 | 
			
		||||
                            // 如果配置了APK额外输出路径,就复制一份拷贝到额外路径。
 | 
			
		||||
                            //
 | 
			
		||||
							if(winbollProps['ExtraAPKOutputPath'] != null ) {
 | 
			
		||||
                                File apkFile = new File(winbollProps['ExtraAPKOutputPath'])
 | 
			
		||||
								File outCommonDir = apkFile.getParentFile();
 | 
			
		||||
                                String commandAPKName = apkFile.getName();
 | 
			
		||||
                                if(outCommonDir.exists()) {
 | 
			
		||||
                                    copy{
 | 
			
		||||
                                        from file.outputFile
 | 
			
		||||
@@ -243,6 +306,7 @@ android {
 | 
			
		||||
                                        println "Output APK (Common): " + outCommonDir.getAbsolutePath() + "/${commandAPKName}"
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
						    }
 | 
			
		||||
                            
 | 
			
		||||
                        
 | 
			
		||||
                    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								README.md
									
									
									
									
									
								
							@@ -119,7 +119,7 @@
 | 
			
		||||
## ★. 项目 Android SDK 编译环境设置(可选),local.properties-demo 要复制为 local.properties,并按需要设置 Android SDK 目录。
 | 
			
		||||
## ★. 应用签名密钥 keystore 设置问题。一般调试编译只需用【Termux】cd 进 GenKeyStore 目录执行 $ bash gen_debug_keystore.sh 命令即可完成设置。
 | 
			
		||||
## ☆. 应用 WiBoLL 签名密钥配置问题<非必须考虑>。设置时需要 clone 【keystore】模块源码并拷贝模块目录的 appkey.jks 与 appkey.keystore 到项目根目录即可。
 | 
			
		||||
## ☆. 类库型模块编译环境设置(可选),winboll.properties-demo 要复制为 winboll.properties,并按需要设置 WinBoLL Maven 库登录用户信息。
 | 
			
		||||
## ☆. 类库型模块编译环境设置(可选),winboll.properties-demo 要复制为 winboll.properties,并按需要设置 WinBoLL Maven 库登录用户信息, 和 APK 文件额外输出路径。
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ☆类库型项目编译方法
 | 
			
		||||
@@ -128,7 +128,8 @@
 | 
			
		||||
设置属性 libraryProject=<类库项目模块文件夹名称>
 | 
			
		||||
### 再编译测试项目
 | 
			
		||||
$ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称>
 | 
			
		||||
#### 测试项目编译后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
 | 
			
		||||
#### 测试项目编译后,编译器会复制一份 APK 到 路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
 | 
			
		||||
#### 若是 winboll.properties 文件的 [ExtraAPKOutputPath] 属性设置了路径。编译器也会复制一份 APK 到这个路径。
 | 
			
		||||
### 最后编译类库项目
 | 
			
		||||
$ bash .winboll/bashPublishLIBAddTag.sh <类库项目模块文件夹名称>
 | 
			
		||||
#### 类库模块编译命令执行后,编译器会发布到 WinBoLL Nexus Maven 库:Maven 库地址可以参阅根项目目录配置 build.gradle 文件。
 | 
			
		||||
@@ -136,4 +137,17 @@ $ bash .winboll/bashPublishLIBAddTag.sh <类库项目模块文件夹名称>
 | 
			
		||||
# ☆应用型项目编译方法
 | 
			
		||||
## 直接调用以下命令编译应用型项目
 | 
			
		||||
$ bash .winboll/bashPublishAPKAddTag.sh <应用项目模块文件夹名称>
 | 
			
		||||
#### 应用模块编译命令执行后,编译器会复制一份 APK 到以下路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
 | 
			
		||||
#### 应用模块编译命令执行后,编译器会复制一份 APK 到
 | 
			
		||||
#### 测试项目编译后,编译器会复制一份 APK 到 路径:"/sdcard/WinBoLLStudio/APKs/<项目根目录名称>/tag/" 文件夹。
 | 
			
		||||
#### 若是 winboll.properties 文件的 [ExtraAPKOutputPath] 属性设置了路径。编译器也会复制一份 APK 到这个路径。
 | 
			
		||||
 | 
			
		||||
## ☆应用调试编译方法
 | 
			
		||||
使用以下命令编译调试:
 | 
			
		||||
 | 
			
		||||
### Beta调试使用:
 | 
			
		||||
$ bash gradlew assembleBetaDebug
 | 
			
		||||
 | 
			
		||||
### Stage调试使用:
 | 
			
		||||
$ bash gradlew assembleStageDebug
 | 
			
		||||
 | 
			
		||||
### 若是 winboll.properties 文件的 [ExtraAPKOutputPath] 属性设置了路径。编译器也会复制一份 APK 到这个路径。
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ android {
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // .winboll/winbollBuildProps.properties 文件的 stageCount=0
 | 
			
		||||
        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
			
		||||
        versionName "15.9"
 | 
			
		||||
        versionName "15.10"
 | 
			
		||||
        if(true) {
 | 
			
		||||
            versionName = genVersionName("${versionName}")
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sun Aug 31 23:37:38 HKT 2025
 | 
			
		||||
stageCount=6
 | 
			
		||||
#Sat Sep 27 21:03:20 HKT 2025
 | 
			
		||||
stageCount=10
 | 
			
		||||
libraryProject=libappbase
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.5
 | 
			
		||||
baseVersion=15.10
 | 
			
		||||
publishVersion=15.10.9
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.9.6
 | 
			
		||||
baseBetaVersion=15.10.10
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,10 @@
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:tools="http://schemas.android.com/tools" >
 | 
			
		||||
 | 
			
		||||
    <application>
 | 
			
		||||
    <application
 | 
			
		||||
		tools:replace="android:icon,android:roundIcon"
 | 
			
		||||
        android:icon="@drawable/ic_winboll_beta"
 | 
			
		||||
        android:roundIcon="@drawable/ic_winboll_beta">
 | 
			
		||||
 | 
			
		||||
        <!-- Put flavor specific code here -->
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,14 +3,16 @@
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="cc.winboll.studio.appbase">
 | 
			
		||||
	
 | 
			
		||||
	<!-- 网络权限 -->
 | 
			
		||||
	<uses-permission android:name="android.permission.INTERNET" />
 | 
			
		||||
		
 | 
			
		||||
    <application
 | 
			
		||||
        android:name=".App"
 | 
			
		||||
        android:icon="@drawable/ic_miapp"
 | 
			
		||||
        android:icon="@drawable/ic_winboll"
 | 
			
		||||
        android:label="@string/app_name"
 | 
			
		||||
        android:theme="@style/MyAPPBaseTheme"
 | 
			
		||||
        android:resizeableActivity="true"
 | 
			
		||||
        android:process=":App"
 | 
			
		||||
        android:networkSecurityConfig="@xml/network_security_config">
 | 
			
		||||
        android:process=":App">
 | 
			
		||||
 | 
			
		||||
		<activity
 | 
			
		||||
            android:name=".MainActivity"
 | 
			
		||||
@@ -25,102 +27,13 @@
 | 
			
		||||
 | 
			
		||||
                <category android:name="android.intent.category.LAUNCHER"/>
 | 
			
		||||
 | 
			
		||||
                <action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES"/>
 | 
			
		||||
 | 
			
		||||
                <category android:name="android.intent.category.DEFAULT"/>
 | 
			
		||||
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
        </activity>
 | 
			
		||||
		
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".activities.NewActivity"
 | 
			
		||||
            android:label="NewActivity"
 | 
			
		||||
            android:exported="true"
 | 
			
		||||
            android:resizeableActivity="true"
 | 
			
		||||
            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"/>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".activities.New2Activity"
 | 
			
		||||
            android:label="New2Activity"
 | 
			
		||||
            android:exported="true"
 | 
			
		||||
            android:resizeableActivity="true"
 | 
			
		||||
            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"/>
 | 
			
		||||
 | 
			
		||||
        <service
 | 
			
		||||
            android:name=".MyTileService"
 | 
			
		||||
            android:exported="true"
 | 
			
		||||
            android:label="@string/tileservice_name"
 | 
			
		||||
            android:icon="@drawable/ic_launcher"
 | 
			
		||||
            android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
                <action android:name="android.service.quicksettings.action.QS_TILE"/>
 | 
			
		||||
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
        </service>
 | 
			
		||||
 | 
			
		||||
        <service
 | 
			
		||||
            android:name=".services.MainService"
 | 
			
		||||
            android:exported="true"/>
 | 
			
		||||
 | 
			
		||||
        <service
 | 
			
		||||
            android:name="cc.winboll.studio.appbase.services.TestDemoBindService"
 | 
			
		||||
            android:exported="true"/>
 | 
			
		||||
 | 
			
		||||
        <service
 | 
			
		||||
            android:name="cc.winboll.studio.appbase.services.TestDemoService"
 | 
			
		||||
            android:exported="true"/>
 | 
			
		||||
 | 
			
		||||
        <service android:name=".services.AssistantService"/>
 | 
			
		||||
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name="cc.winboll.studio.appbase.receivers.MainReceiver"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
                <action android:name="cc.winboll.studio.appbase.receivers.MainReceiver"/>
 | 
			
		||||
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
        </receiver>
 | 
			
		||||
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name=".widgets.APPNewsWidget"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
                <action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidget.ACTION_WAKEUP_SERVICE"/>
 | 
			
		||||
 | 
			
		||||
                <action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidget.ACTION_RELOAD_REPORT"/>
 | 
			
		||||
 | 
			
		||||
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
 | 
			
		||||
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
            <meta-data
 | 
			
		||||
                android:name="android.appwidget.provider"
 | 
			
		||||
                android:resource="@xml/widget_provider_info_sos"/>
 | 
			
		||||
 | 
			
		||||
        </receiver>
 | 
			
		||||
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name=".receivers.APPNewsWidgetClickListener"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
                <action android:name="cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener.ACTION_PRE"/>
 | 
			
		||||
 | 
			
		||||
                <action android:name="cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener.ACTION_NEXT"/>
 | 
			
		||||
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
        </receiver>
 | 
			
		||||
        <activity android:name=".GlobalApplication$CrashActivity"/>
 | 
			
		||||
		
 | 
			
		||||
        <meta-data
 | 
			
		||||
            android:name="android.max_aspect"
 | 
			
		||||
 
 | 
			
		||||
@@ -5,23 +5,15 @@ package cc.winboll.studio.appbase;
 | 
			
		||||
 * @Date 2025/01/05 09:54:42
 | 
			
		||||
 * @Describe APPbase 应用类
 | 
			
		||||
 */
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import android.content.IntentFilter;
 | 
			
		||||
import cc.winboll.studio.libappbase.sos.SOSCenterServiceReceiver;
 | 
			
		||||
import cc.winboll.studio.libappbase.sos.SOS;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
 | 
			
		||||
public class App extends GlobalApplication {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "App";
 | 
			
		||||
    
 | 
			
		||||
    SOSCenterServiceReceiver mSOSCenterServiceReceiver;
 | 
			
		||||
    
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate() {
 | 
			
		||||
        super.onCreate();
 | 
			
		||||
        mSOSCenterServiceReceiver = new SOSCenterServiceReceiver();
 | 
			
		||||
        IntentFilter intentFilter = new IntentFilter();
 | 
			
		||||
        intentFilter.addAction(SOS.ACTION_SOS);
 | 
			
		||||
        registerReceiver(mSOSCenterServiceReceiver, intentFilter);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,44 +1,23 @@
 | 
			
		||||
package cc.winboll.studio.appbase;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.content.ComponentName;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.Menu;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.widget.CheckBox;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
import android.widget.Toolbar;
 | 
			
		||||
import cc.winboll.studio.appbase.R;
 | 
			
		||||
import cc.winboll.studio.appbase.activities.NewActivity;
 | 
			
		||||
import cc.winboll.studio.appbase.activities.WinBoLLActivity;
 | 
			
		||||
import cc.winboll.studio.appbase.services.MainService;
 | 
			
		||||
import cc.winboll.studio.appbase.services.TestDemoBindService;
 | 
			
		||||
import cc.winboll.studio.appbase.services.TestDemoService;
 | 
			
		||||
import cc.winboll.studio.libappbase.CrashHandler;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalCrashActivity;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogView;
 | 
			
		||||
import cc.winboll.studio.libappbase.sos.SOS;
 | 
			
		||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.widgets.StatusWidget;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogActivity;
 | 
			
		||||
import cc.winboll.studio.libappbase.ToastUtils;
 | 
			
		||||
 | 
			
		||||
public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
public class MainActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "MainActivity";
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Toolbar mToolbar;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -49,9 +28,6 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
        mToolbar = findViewById(R.id.toolbar);
 | 
			
		||||
        setActionBar(mToolbar);
 | 
			
		||||
 | 
			
		||||
        CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
 | 
			
		||||
        cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -62,122 +38,37 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    @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 (item.getItemId()) {
 | 
			
		||||
			case R.id.item_home : {
 | 
			
		||||
					openWebsiteInBrowser(this);
 | 
			
		||||
				}
 | 
			
		||||
		}
 | 
			
		||||
        // 在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 onCrashTest(View view) {
 | 
			
		||||
		for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
 | 
			
		||||
			getString(i);
 | 
			
		||||
		}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void onSartTestDemoService(View view) {
 | 
			
		||||
        Intent intent = new Intent(this, TestDemoService.class);
 | 
			
		||||
        intent.setAction(TestDemoService.ACTION_ENABLE);
 | 
			
		||||
        startService(intent);
 | 
			
		||||
 | 
			
		||||
    public void onLogTest(View view) {
 | 
			
		||||
        LogActivity.startLogActivity(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
	/**
 | 
			
		||||
	 * 唤起默认浏览器打开指定网站
 | 
			
		||||
	 * @param context 上下文(如 Activity.this)
 | 
			
		||||
	 */
 | 
			
		||||
	public void openWebsiteInBrowser(Context context) {
 | 
			
		||||
		// 目标网站地址
 | 
			
		||||
		String url = "https://www.winboll.cc";
 | 
			
		||||
		// 构建打开浏览器的意图
 | 
			
		||||
		Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
 | 
			
		||||
		// 设置标志:避免创建新的任务栈(可选,按需求调整)
 | 
			
		||||
		intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
		context.startActivity(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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,79 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/13 19:30:10
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.service.quicksettings.Tile;
 | 
			
		||||
import android.service.quicksettings.TileService;
 | 
			
		||||
import cc.winboll.studio.appbase.models.MainServiceBean;
 | 
			
		||||
import cc.winboll.studio.appbase.services.MainService;
 | 
			
		||||
 | 
			
		||||
public class MyTileService extends TileService {
 | 
			
		||||
    public static final String TAG = "MyTileService";
 | 
			
		||||
 | 
			
		||||
    volatile static MyTileService _MyTileService;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onStartListening() {
 | 
			
		||||
        super.onStartListening();
 | 
			
		||||
        _MyTileService = this;
 | 
			
		||||
        Tile tile = getQsTile();
 | 
			
		||||
        MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
 | 
			
		||||
        if (bean != null && bean.isEnable()) {
 | 
			
		||||
            //MainService.startMainService(context);
 | 
			
		||||
            tile.setState(Tile.STATE_ACTIVE);
 | 
			
		||||
            tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud));
 | 
			
		||||
        } else {
 | 
			
		||||
            //MainService.stopMainService(context);
 | 
			
		||||
            tile.setState(Tile.STATE_INACTIVE);
 | 
			
		||||
            tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
 | 
			
		||||
        }
 | 
			
		||||
        tile.updateTile();
 | 
			
		||||
//        Tile tile = getQsTile();
 | 
			
		||||
//        tile.setState(Tile.STATE_INACTIVE);
 | 
			
		||||
//        tile.setLabel(getString(R.string.tileservice_name));
 | 
			
		||||
//        tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
 | 
			
		||||
//        tile.updateTile();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onClick() {
 | 
			
		||||
        super.onClick();
 | 
			
		||||
        Tile tile = getQsTile();
 | 
			
		||||
        MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
 | 
			
		||||
        if (bean == null) {
 | 
			
		||||
            bean = new MainServiceBean();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (tile.getState() == Tile.STATE_ACTIVE) {
 | 
			
		||||
            bean.setIsEnable(false);
 | 
			
		||||
            MainServiceBean.saveBean(this, bean);
 | 
			
		||||
            MainService.stopMainService(this);
 | 
			
		||||
        } else if (tile.getState() == Tile.STATE_INACTIVE) {
 | 
			
		||||
            bean.setIsEnable(true);
 | 
			
		||||
            MainServiceBean.saveBean(this, bean);
 | 
			
		||||
            MainService.startMainService(this);
 | 
			
		||||
        }
 | 
			
		||||
        updateServiceIconStatus(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void updateServiceIconStatus(Context context) {
 | 
			
		||||
        if (_MyTileService == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Tile tile = _MyTileService.getQsTile();
 | 
			
		||||
        MainServiceBean bean = MainServiceBean.loadBean(context, MainServiceBean.class);
 | 
			
		||||
        if (bean != null && bean.isEnable()) {
 | 
			
		||||
            tile.setState(Tile.STATE_ACTIVE);
 | 
			
		||||
            tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud));
 | 
			
		||||
        } else {
 | 
			
		||||
            tile.setState(Tile.STATE_INACTIVE);
 | 
			
		||||
            tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud_outline));
 | 
			
		||||
        }
 | 
			
		||||
        tile.updateTile();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,77 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.activities;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,75 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.activities;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/25 05:04:22
 | 
			
		||||
 */
 | 
			
		||||
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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,61 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.activities;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.handlers;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/14 03:51:40
 | 
			
		||||
 */
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.Message;
 | 
			
		||||
import cc.winboll.studio.appbase.services.MainService;
 | 
			
		||||
import java.lang.ref.WeakReference;
 | 
			
		||||
 | 
			
		||||
public class MainServiceHandler extends Handler {
 | 
			
		||||
    public static final String TAG = "MainServiceHandler";
 | 
			
		||||
 | 
			
		||||
    public static final int MSG_REMINDTHREAD = 0;
 | 
			
		||||
 | 
			
		||||
    WeakReference<MainService> serviceWeakReference;
 | 
			
		||||
    public MainServiceHandler(MainService service) {
 | 
			
		||||
        serviceWeakReference = new WeakReference<MainService>(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;
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,67 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.models;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/13 07:06:13
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class MainServiceBean extends BaseBean {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "MainServiceBean";
 | 
			
		||||
 | 
			
		||||
    boolean isEnable;
 | 
			
		||||
 | 
			
		||||
    public MainServiceBean() {
 | 
			
		||||
        this.isEnable = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setIsEnable(boolean isEnable) {
 | 
			
		||||
        this.isEnable = isEnable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isEnable() {
 | 
			
		||||
        return isEnable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return MainServiceBean.class.getName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
			
		||||
        super.writeThisToJsonWriter(jsonWriter);
 | 
			
		||||
        jsonWriter.name("isEnable").value(isEnable());
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
 | 
			
		||||
        if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
 | 
			
		||||
            if (name.equals("isEnable")) {
 | 
			
		||||
                setIsEnable(jsonReader.nextBoolean());
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
 | 
			
		||||
        jsonReader.beginObject();
 | 
			
		||||
        while (jsonReader.hasNext()) {
 | 
			
		||||
            String name = jsonReader.nextName();
 | 
			
		||||
            if (!initObjectsFromJsonReader(jsonReader, name)) {
 | 
			
		||||
                jsonReader.skipValue();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 结束 JSON 对象
 | 
			
		||||
        jsonReader.endObject();
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,67 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.models;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/07 12:47:22
 | 
			
		||||
 * @Describe TestServiceBean
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class TestDemoBindServiceBean extends BaseBean {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "TestServiceBean";
 | 
			
		||||
 | 
			
		||||
    boolean isEnable;
 | 
			
		||||
 | 
			
		||||
    public TestDemoBindServiceBean() {
 | 
			
		||||
        this.isEnable = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setIsEnable(boolean isEnable) {
 | 
			
		||||
        this.isEnable = isEnable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isEnable() {
 | 
			
		||||
        return isEnable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return TestDemoBindServiceBean.class.getName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
			
		||||
        super.writeThisToJsonWriter(jsonWriter);
 | 
			
		||||
        jsonWriter.name("isEnable").value(isEnable());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
 | 
			
		||||
        if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
 | 
			
		||||
            if (name.equals("isEnable")) {
 | 
			
		||||
                setIsEnable(jsonReader.nextBoolean());
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
 | 
			
		||||
        jsonReader.beginObject();
 | 
			
		||||
        while (jsonReader.hasNext()) {
 | 
			
		||||
            String name = jsonReader.nextName();
 | 
			
		||||
            if (!initObjectsFromJsonReader(jsonReader, name)) {
 | 
			
		||||
                jsonReader.skipValue();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 结束 JSON 对象
 | 
			
		||||
        jsonReader.endObject();
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.models;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/07 12:49:21
 | 
			
		||||
 * @Describe TestDemoServiceBean
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class TestDemoServiceBean extends BaseBean {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "TestDemoServiceBean";
 | 
			
		||||
 | 
			
		||||
    boolean isEnable;
 | 
			
		||||
 | 
			
		||||
    public TestDemoServiceBean() {
 | 
			
		||||
        this.isEnable = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setIsEnable(boolean isEnable) {
 | 
			
		||||
        this.isEnable = isEnable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isEnable() {
 | 
			
		||||
        return isEnable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return TestDemoServiceBean.class.getName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
			
		||||
        super.writeThisToJsonWriter(jsonWriter);
 | 
			
		||||
        jsonWriter.name("isEnable").value(isEnable());
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
 | 
			
		||||
        if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
 | 
			
		||||
            if (name.equals("isEnable")) {
 | 
			
		||||
                setIsEnable(jsonReader.nextBoolean());
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
 | 
			
		||||
        jsonReader.beginObject();
 | 
			
		||||
        while (jsonReader.hasNext()) {
 | 
			
		||||
            String name = jsonReader.nextName();
 | 
			
		||||
            if (!initObjectsFromJsonReader(jsonReader, name)) {
 | 
			
		||||
                jsonReader.skipValue();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 结束 JSON 对象
 | 
			
		||||
        jsonReader.endObject();
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.receivers;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/24 07:11:44
 | 
			
		||||
 */
 | 
			
		||||
import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
 | 
			
		||||
public class APPNewsWidgetClickListener extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "APPNewsWidgetClickListener";
 | 
			
		||||
    public static final String ACTION_PRE = APPNewsWidgetClickListener.class.getName() + ".ACTION_PRE";
 | 
			
		||||
    public static final String ACTION_NEXT = APPNewsWidgetClickListener.class.getName() + ".ACTION_NEXT";
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onReceive(Context context, Intent intent) {
 | 
			
		||||
        String action = intent.getAction();
 | 
			
		||||
        if (action == null) {
 | 
			
		||||
            LogUtils.d(TAG, String.format("action %s", action));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (action.equals(ACTION_PRE)) {
 | 
			
		||||
            LogUtils.d(TAG, "ACTION_PRE");
 | 
			
		||||
            APPNewsWidget.prePage(context);
 | 
			
		||||
        } else if (action.equals(ACTION_NEXT)) {
 | 
			
		||||
            LogUtils.d(TAG, "ACTION_NEXT");
 | 
			
		||||
            APPNewsWidget.nextPage(context);
 | 
			
		||||
        } else {
 | 
			
		||||
            LogUtils.d(TAG, String.format("action %s", action));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,119 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.receivers;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/13 06:58:04
 | 
			
		||||
 * @Describe 主要广播接收器
 | 
			
		||||
 */
 | 
			
		||||
import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.ComponentName;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.IntentFilter;
 | 
			
		||||
import cc.winboll.studio.appbase.services.MainService;
 | 
			
		||||
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
 | 
			
		||||
import cc.winboll.studio.libappbase.AppUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.models.APPModel;
 | 
			
		||||
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 cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.WinBoLL;
 | 
			
		||||
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<MainService> mwrService;
 | 
			
		||||
 | 
			
		||||
    public MainReceiver(MainService service) {
 | 
			
		||||
        mwrService = new WeakReference<MainService>(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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,138 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.services;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/14 03:38:31
 | 
			
		||||
 * @Describe 守护进程服务
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Service;
 | 
			
		||||
import android.content.ComponentName;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.ServiceConnection;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import cc.winboll.studio.appbase.models.MainServiceBean;
 | 
			
		||||
import cc.winboll.studio.appbase.services.AssistantService;
 | 
			
		||||
import cc.winboll.studio.appbase.services.MainService;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import android.os.Binder;
 | 
			
		||||
 | 
			
		||||
public class AssistantService extends Service {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "AssistantService";
 | 
			
		||||
 | 
			
		||||
    MainServiceBean mMainServiceBean;
 | 
			
		||||
    MyServiceConnection mMyServiceConnection;
 | 
			
		||||
    MainService mMainService;
 | 
			
		||||
    boolean isBound = false;
 | 
			
		||||
    volatile boolean isThreadAlive = false;
 | 
			
		||||
 | 
			
		||||
    public synchronized void setIsThreadAlive(boolean isThreadAlive) {
 | 
			
		||||
        LogUtils.d(TAG, "setIsThreadAlive(...)");
 | 
			
		||||
        LogUtils.d(TAG, String.format("isThreadAlive %s", isThreadAlive));
 | 
			
		||||
        this.isThreadAlive = isThreadAlive;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isThreadAlive() {
 | 
			
		||||
        return isThreadAlive;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public IBinder onBind(Intent intent) {
 | 
			
		||||
        return new MyBinder();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate() {
 | 
			
		||||
        LogUtils.d(TAG, "onCreate");
 | 
			
		||||
        super.onCreate();
 | 
			
		||||
 | 
			
		||||
        //mMyBinder = new MyBinder();
 | 
			
		||||
        if (mMyServiceConnection == null) {
 | 
			
		||||
            mMyServiceConnection = new MyServiceConnection();
 | 
			
		||||
        }
 | 
			
		||||
        // 设置运行参数
 | 
			
		||||
        setIsThreadAlive(false);
 | 
			
		||||
        assistantService();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int onStartCommand(Intent intent, int flags, int startId) {
 | 
			
		||||
        LogUtils.d(TAG, "call onStartCommand(...)");
 | 
			
		||||
        assistantService();
 | 
			
		||||
        return START_STICKY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onDestroy() {
 | 
			
		||||
        //LogUtils.d(TAG, "onDestroy");
 | 
			
		||||
        setIsThreadAlive(false);
 | 
			
		||||
        // 解除绑定
 | 
			
		||||
        if (isBound) {
 | 
			
		||||
            unbindService(mMyServiceConnection);
 | 
			
		||||
            isBound = false;
 | 
			
		||||
        }
 | 
			
		||||
        super.onDestroy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 运行服务内容
 | 
			
		||||
    //
 | 
			
		||||
    void assistantService() {
 | 
			
		||||
        LogUtils.d(TAG, "assistantService()");
 | 
			
		||||
        mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
 | 
			
		||||
        LogUtils.d(TAG, String.format("mMainServiceBean.isEnable() %s", mMainServiceBean.isEnable()));
 | 
			
		||||
        if (mMainServiceBean.isEnable()) {
 | 
			
		||||
            LogUtils.d(TAG, String.format("mIsThreadAlive %s", isThreadAlive()));
 | 
			
		||||
            if (isThreadAlive() == false) {
 | 
			
		||||
                // 设置运行状态
 | 
			
		||||
                setIsThreadAlive(true);
 | 
			
		||||
                // 唤醒和绑定主进程
 | 
			
		||||
                wakeupAndBindMain();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 唤醒和绑定主进程
 | 
			
		||||
    //
 | 
			
		||||
    void wakeupAndBindMain() {
 | 
			
		||||
        LogUtils.d(TAG, "wakeupAndBindMain()");
 | 
			
		||||
        // 绑定服务的Intent
 | 
			
		||||
        Intent intent = new Intent(this, MainService.class);
 | 
			
		||||
        startService(new Intent(this, MainService.class));
 | 
			
		||||
        bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
 | 
			
		||||
    
 | 
			
		||||
//        startService(new Intent(this, MainService.class));
 | 
			
		||||
//        bindService(new Intent(AssistantService.this, MainService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 主进程与守护进程连接时需要用到此类
 | 
			
		||||
    //
 | 
			
		||||
    class MyServiceConnection implements ServiceConnection {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onServiceConnected(ComponentName name, IBinder service) {
 | 
			
		||||
            LogUtils.d(TAG, "onServiceConnected(...)");
 | 
			
		||||
            MainService.MyBinder binder = (MainService.MyBinder) service;
 | 
			
		||||
            mMainService = binder.getService();
 | 
			
		||||
            isBound = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onServiceDisconnected(ComponentName name) {
 | 
			
		||||
            LogUtils.d(TAG, "onServiceDisconnected(...)");
 | 
			
		||||
            mMainServiceBean = MainServiceBean.loadBean(AssistantService.this, MainServiceBean.class);
 | 
			
		||||
            if (mMainServiceBean.isEnable()) {
 | 
			
		||||
                wakeupAndBindMain();
 | 
			
		||||
            }
 | 
			
		||||
            isBound = false;
 | 
			
		||||
            mMainService = null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // 用于返回服务实例的Binder
 | 
			
		||||
    public class MyBinder extends Binder {
 | 
			
		||||
        AssistantService getService() {
 | 
			
		||||
            LogUtils.d(TAG, "AssistantService MyBinder getService()");
 | 
			
		||||
            return AssistantService.this;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,317 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.services;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/13 06:56:41
 | 
			
		||||
 * @Describe 拨号主服务
 | 
			
		||||
 * 参考:
 | 
			
		||||
 * 进程保活-双进程守护的正确姿势
 | 
			
		||||
 * https://blog.csdn.net/sinat_35159441/article/details/75267380
 | 
			
		||||
 * Android Service之onStartCommand方法研究
 | 
			
		||||
 * https://blog.csdn.net/cyp331203/article/details/38920491
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Service;
 | 
			
		||||
import android.content.ComponentName;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.ServiceConnection;
 | 
			
		||||
import android.os.Binder;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import cc.winboll.studio.appbase.MyTileService;
 | 
			
		||||
import cc.winboll.studio.appbase.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<APPConnection> 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<APPConnection>();
 | 
			
		||||
 | 
			
		||||
        _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));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,179 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.services;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/07 12:45:49
 | 
			
		||||
 * @Describe 启动时申请绑定到APPBase主服务的服务示例
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Service;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.os.Binder;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import cc.winboll.studio.appbase.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");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,155 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.services;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/07 12:39:24
 | 
			
		||||
 * @Describe 普通服务示例
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Service;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.os.Binder;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import cc.winboll.studio.appbase.models.TestDemoServiceBean;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
 | 
			
		||||
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");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,54 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.threads;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/14 03:46:44
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import cc.winboll.studio.appbase.handlers.MainServiceHandler;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import java.lang.ref.WeakReference;
 | 
			
		||||
 | 
			
		||||
public class MainServiceThread extends Thread {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "MainServiceThread";
 | 
			
		||||
 | 
			
		||||
    Context mContext;
 | 
			
		||||
 | 
			
		||||
    // 控制线程是否退出的标志
 | 
			
		||||
    volatile boolean isExist = false;
 | 
			
		||||
 | 
			
		||||
    // 服务Handler, 用于线程发送消息使用
 | 
			
		||||
    WeakReference<MainServiceHandler> 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<MainServiceHandler>(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.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,187 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.appbase.widgets;
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/15 14:41:25
 | 
			
		||||
 * @Describe TimeWidget
 | 
			
		||||
 */
 | 
			
		||||
import android.app.PendingIntent;
 | 
			
		||||
import android.appwidget.AppWidgetManager;
 | 
			
		||||
import android.appwidget.AppWidgetProvider;
 | 
			
		||||
import android.content.ComponentName;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.widget.RemoteViews;
 | 
			
		||||
import cc.winboll.studio.appbase.R;
 | 
			
		||||
import cc.winboll.studio.appbase.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<WinBoLLNewsBean> _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>();
 | 
			
		||||
            WinBoLLNewsBean.loadBeanList(context, _WinBoLLNewsBeanList, WinBoLLNewsBean.class);
 | 
			
		||||
        }
 | 
			
		||||
        if (_WinBoLLNewsBeanList == null) {
 | 
			
		||||
            _WinBoLLNewsBeanList = new ArrayList<WinBoLLNewsBean>();
 | 
			
		||||
            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<String> msgTemp = new ArrayList<String>();
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								appbase/src/main/res/drawable/btn_light_blue.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								appbase/src/main/res/drawable/btn_light_blue.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:shape="rectangle">
 | 
			
		||||
    <solid android:color="#81C7F5"/> <!-- 浅蓝色填充 -->
 | 
			
		||||
    <corners android:radius="8dp"/> <!-- 8dp 圆角 -->
 | 
			
		||||
</shape>
 | 
			
		||||
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportHeight="24"
 | 
			
		||||
    android:viewportWidth="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#ff000000"
 | 
			
		||||
        android:pathData="M6.5,20Q4.22,20 2.61,18.43 1,16.85 1,14.58 1,12.63 2.17,11.1 3.35,9.57 5.25,9.15 5.88,6.85 7.75,5.43 9.63,4 12,4 14.93,4 16.96,6.04 19,8.07 19,11 20.73,11.2 21.86,12.5 23,13.78 23,15.5 23,17.38 21.69,18.69 20.38,20 18.5,20Z"/>
 | 
			
		||||
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportHeight="24"
 | 
			
		||||
    android:viewportWidth="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#ff000000"
 | 
			
		||||
        android:pathData="M6.5,20Q4.22,20 2.61,18.43 1,16.85 1,14.58 1,12.63 2.17,11.1 3.35,9.57 5.25,9.15 5.88,6.85 7.75,5.43 9.63,4 12,4 14.93,4 16.96,6.04 19,8.07 19,11 20.73,11.2 21.86,12.5 23,13.78 23,15.5 23,17.38 21.69,18.69 20.38,20 18.5,20M6.5,18H18.5Q19.55,18 20.27,17.27 21,16.55 21,15.5 21,14.45 20.27,13.73 19.55,13 18.5,13H17V11Q17,8.93 15.54,7.46 14.08,6 12,6 9.93,6 8.46,7.46 7,8.93 7,11H6.5Q5.05,11 4.03,12.03 3,13.05 3,14.5 3,15.95 4.03,17 5.05,18 6.5,18M12,12Z"/>
 | 
			
		||||
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -3,219 +3,45 @@
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent">
 | 
			
		||||
	android:layout_height="match_parent"
 | 
			
		||||
	android:padding="16dp">
 | 
			
		||||
 | 
			
		||||
	<android.widget.Toolbar
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:id="@+id/toolbar"/>
 | 
			
		||||
 | 
			
		||||
	<ScrollView
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0">
 | 
			
		||||
 | 
			
		||||
		<LinearLayout
 | 
			
		||||
			xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
		android:layout_weight="1"
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
			android:gravity="center">
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="vertical"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="0dp"
 | 
			
		||||
				android:layout_weight="1.0"
 | 
			
		||||
				android:gravity="center_horizontal">
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="安卓R对应的是Android 11,其API级别是30。以下是Android 11中一些重要的API相关特性:
 | 
			
		||||
 | 
			
		||||
                    \n- 隐私保护方面:引入单次授权,让用户可以选择授予应用对位置信息、麦克风和摄像头的临时访问权限。还增加了数据访问审核功能,能让开发者深入了解应用在何处访问私密数据。
 | 
			
		||||
                    
 | 
			
		||||
                    \n- 系统功能方面:提供了ControlsProviderService API,用于向连接的外部设备提供控件,这些控件显示于Android电源菜单中的设备控件下。媒体控件也得到更新,显示于快捷设置旁,来自多个应用的会话排列在一个可滑动的轮播界面中。
 | 
			
		||||
                    
 | 
			
		||||
                    \n- 硬件支持方面:提供了一些API以支持瀑布屏,通过将窗口布局属性  layoutInDisplayCutoutMode  设为  LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS ,可允许窗口延伸到屏幕各个边缘上的刘海和瀑布区域。对于合页式屏幕配置的设备,提供了具有  TYPE_HINGE_ANGLE  的新传感器以及新的  SensorEvent ,用于监控合页角度。
 | 
			
		||||
                    
 | 
			
		||||
                    \n- 安全方面:对生物识别身份验证机制进行了更新,引入了  BiometricManager.Authenticators  接口,定义了  BIOMETRIC_STRONG 、 BIOMETRIC_WEAK 、 DEVICE_CREDENTIAL  等身份验证类型。还在  BiometricPrompt  类中提供了对“每次使用时进行身份验证”密钥的更多支持。
 | 
			
		||||
                    
 | 
			
		||||
                    \n- 性能和质量方面:支持无线调试,通过Android调试桥(adb)从工作站以无线方式部署和调试应用,避免了常见的USB连接问题。"/>
 | 
			
		||||
                
 | 
			
		||||
                    
 | 
			
		||||
				<HorizontalScrollView
 | 
			
		||||
					android:layout_width="match_parent"
 | 
			
		||||
					android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
					<LinearLayout
 | 
			
		||||
						android:orientation="horizontal"
 | 
			
		||||
						android:layout_height="wrap_content"
 | 
			
		||||
						android:gravity="right|center_vertical"
 | 
			
		||||
						android:layout_width="wrap_content">
 | 
			
		||||
 | 
			
		||||
						<CheckBox
 | 
			
		||||
							android:layout_width="wrap_content"
 | 
			
		||||
							android:layout_height="wrap_content"
 | 
			
		||||
							android:text="Debug Mode"
 | 
			
		||||
							android:layout_weight="1.0"
 | 
			
		||||
							android:onClick="onSwitchDebugMode"
 | 
			
		||||
							android:id="@+id/activitymainCheckBox1"/>
 | 
			
		||||
		android:gravity="center_vertical"
 | 
			
		||||
		android:spacing="12dp">
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
							android:layout_width="wrap_content"
 | 
			
		||||
							android:layout_height="wrap_content"
 | 
			
		||||
							android:text="Test Application CrashReport"
 | 
			
		||||
							android:textAllCaps="false"
 | 
			
		||||
							android:onClick="onTestApplicationCrashReport"/>
 | 
			
		||||
 | 
			
		||||
						<Button
 | 
			
		||||
							android:layout_width="wrap_content"
 | 
			
		||||
							android:layout_height="wrap_content"
 | 
			
		||||
							android:text="PreviewGlobalCrashActivity"
 | 
			
		||||
							android:textAllCaps="false"
 | 
			
		||||
							android:onClick="onPreviewGlobalCrashActivity"/>
 | 
			
		||||
 | 
			
		||||
					</LinearLayout>
 | 
			
		||||
 | 
			
		||||
				</HorizontalScrollView>
 | 
			
		||||
 | 
			
		||||
				<ScrollView
 | 
			
		||||
					android:layout_width="match_parent"
 | 
			
		||||
					android:layout_height="400dp">
 | 
			
		||||
 | 
			
		||||
					<LinearLayout
 | 
			
		||||
						android:orientation="vertical"
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
						android:gravity="right">
 | 
			
		||||
 | 
			
		||||
						<LinearLayout
 | 
			
		||||
							android:orientation="horizontal"
 | 
			
		||||
							android:layout_width="wrap_content"
 | 
			
		||||
							android:layout_height="wrap_content">
 | 
			
		||||
			android:text="应用崩溃测试"
 | 
			
		||||
			android:textSize="16sp"
 | 
			
		||||
			android:textColor="@android:color/white"
 | 
			
		||||
			android:background="#81C7F5"
 | 
			
		||||
			android:paddingVertical="12dp"
 | 
			
		||||
			android:layout_marginHorizontal="24dp"
 | 
			
		||||
			android:onClick="onCrashTest"/>
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
								android:layout_width="wrap_content"
 | 
			
		||||
								android:layout_height="wrap_content"
 | 
			
		||||
								android:text="StartCenter"
 | 
			
		||||
								android:textAllCaps="false"
 | 
			
		||||
								android:onClick="onStartCenter"/>
 | 
			
		||||
 | 
			
		||||
							<Button
 | 
			
		||||
								android:layout_width="wrap_content"
 | 
			
		||||
								android:layout_height="wrap_content"
 | 
			
		||||
								android:text="StopCenter"
 | 
			
		||||
								android:textAllCaps="false"
 | 
			
		||||
								android:onClick="onStopCenter"/>
 | 
			
		||||
 | 
			
		||||
						</LinearLayout>
 | 
			
		||||
 | 
			
		||||
						<HorizontalScrollView
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
							android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
							<LinearLayout
 | 
			
		||||
								android:orientation="horizontal"
 | 
			
		||||
								android:layout_width="wrap_content"
 | 
			
		||||
								android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
								<Button
 | 
			
		||||
									android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
									android:text="SartTestDemoService"
 | 
			
		||||
									android:textAllCaps="false"
 | 
			
		||||
									android:onClick="onSartTestDemoService"/>
 | 
			
		||||
 | 
			
		||||
								<Button
 | 
			
		||||
									android:layout_width="wrap_content"
 | 
			
		||||
									android:layout_height="wrap_content"
 | 
			
		||||
									android:text="StopTestDemoService"
 | 
			
		||||
									android:textAllCaps="false"
 | 
			
		||||
									android:onClick="onStopTestDemoService"/>
 | 
			
		||||
 | 
			
		||||
								<Button
 | 
			
		||||
									android:layout_width="wrap_content"
 | 
			
		||||
									android:layout_height="wrap_content"
 | 
			
		||||
									android:text="StopTestDemoServiceNoSettings"
 | 
			
		||||
									android:textAllCaps="false"
 | 
			
		||||
									android:onClick="onStopTestDemoServiceNoSettings"/>
 | 
			
		||||
			android:text="应用日志测试"
 | 
			
		||||
			android:textSize="16sp"
 | 
			
		||||
			android:textColor="@android:color/white"
 | 
			
		||||
			android:background="#81C7F5"
 | 
			
		||||
			android:paddingVertical="12dp"
 | 
			
		||||
			android:layout_marginHorizontal="24dp"
 | 
			
		||||
			android:onClick="onLogTest"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
						</HorizontalScrollView>
 | 
			
		||||
 | 
			
		||||
						<HorizontalScrollView
 | 
			
		||||
							android:layout_width="match_parent"
 | 
			
		||||
							android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
							<LinearLayout
 | 
			
		||||
								android:orientation="horizontal"
 | 
			
		||||
								android:layout_width="wrap_content"
 | 
			
		||||
								android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
								<Button
 | 
			
		||||
									android:layout_width="wrap_content"
 | 
			
		||||
									android:layout_height="wrap_content"
 | 
			
		||||
									android:text="SartTestDemoBindService"
 | 
			
		||||
									android:textAllCaps="false"
 | 
			
		||||
									android:onClick="onSartTestDemoBindService"/>
 | 
			
		||||
 | 
			
		||||
								<Button
 | 
			
		||||
									android:layout_width="wrap_content"
 | 
			
		||||
									android:layout_height="wrap_content"
 | 
			
		||||
									android:text="StopTestDemoBindService"
 | 
			
		||||
									android:textAllCaps="false"
 | 
			
		||||
									android:onClick="onStopTestDemoBindService"/>
 | 
			
		||||
 | 
			
		||||
								<Button
 | 
			
		||||
									android:layout_width="wrap_content"
 | 
			
		||||
									android:layout_height="wrap_content"
 | 
			
		||||
									android:text="StopTestDemoBindServiceNoSettings"
 | 
			
		||||
									android:textAllCaps="false"
 | 
			
		||||
									android:onClick="onStopTestDemoBindServiceNoSettings"/>
 | 
			
		||||
 | 
			
		||||
							</LinearLayout>
 | 
			
		||||
 | 
			
		||||
						</HorizontalScrollView>
 | 
			
		||||
 | 
			
		||||
						<Button
 | 
			
		||||
							android:layout_width="wrap_content"
 | 
			
		||||
							android:layout_height="wrap_content"
 | 
			
		||||
							android:text="TestStopMainServiceWithoutSettingEnable"
 | 
			
		||||
							android:textAllCaps="false"
 | 
			
		||||
							android:onClick="onTestStopMainServiceWithoutSettingEnable"/>
 | 
			
		||||
 | 
			
		||||
						<Button
 | 
			
		||||
							android:layout_width="wrap_content"
 | 
			
		||||
							android:layout_height="wrap_content"
 | 
			
		||||
							android:text="TestUseComponentStartService"
 | 
			
		||||
							android:textAllCaps="false"
 | 
			
		||||
							android:onClick="onTestUseComponentStartService"/>
 | 
			
		||||
 | 
			
		||||
						<Button
 | 
			
		||||
							android:layout_width="wrap_content"
 | 
			
		||||
							android:layout_height="wrap_content"
 | 
			
		||||
							android:text="TestDemoServiceSOS"
 | 
			
		||||
							android:textAllCaps="false"
 | 
			
		||||
							android:onClick="onTestDemoServiceSOS"/>
 | 
			
		||||
 | 
			
		||||
						<Button
 | 
			
		||||
							android:layout_width="wrap_content"
 | 
			
		||||
							android:layout_height="wrap_content"
 | 
			
		||||
							android:text="TestOpenNewActivity"
 | 
			
		||||
							android:textAllCaps="false"
 | 
			
		||||
							android:onClick="onTestOpenNewActivity"/>
 | 
			
		||||
 | 
			
		||||
					</LinearLayout>
 | 
			
		||||
 | 
			
		||||
				</ScrollView>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
		</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	</ScrollView>
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent">
 | 
			
		||||
 | 
			
		||||
    <android.widget.Toolbar
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
		android:id="@+id/toolbar"/>
 | 
			
		||||
    
 | 
			
		||||
	<TextView
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:text="NewActivity"/>
 | 
			
		||||
 | 
			
		||||
    <Button
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:text="CloseThisActivity"
 | 
			
		||||
        android:textAllCaps="false"
 | 
			
		||||
        android:onClick="onCloseThisActivity"/>
 | 
			
		||||
 | 
			
		||||
    <Button
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:text="CloseAllActivity"
 | 
			
		||||
        android:textAllCaps="false"
 | 
			
		||||
        android:onClick="onCloseAllActivity"/>
 | 
			
		||||
 | 
			
		||||
    <Button
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:text="New2Activity"
 | 
			
		||||
        android:textAllCaps="false"
 | 
			
		||||
        android:onClick="onNew2Activity"/>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent">
 | 
			
		||||
 | 
			
		||||
    <android.widget.Toolbar
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
		android:id="@+id/toolbar"/>
 | 
			
		||||
 | 
			
		||||
	<TextView
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:text="New2Activity"/>
 | 
			
		||||
 | 
			
		||||
	<Button
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:text="CloseThisActivity"
 | 
			
		||||
		android:textAllCaps="false"
 | 
			
		||||
		android:onClick="onCloseThisActivity"/>
 | 
			
		||||
 | 
			
		||||
	<Button
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:text="CloseAllActivity"
 | 
			
		||||
		android:textAllCaps="false"
 | 
			
		||||
		android:onClick="onCloseAllActivity"/>
 | 
			
		||||
 | 
			
		||||
	<Button
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:text="NewActivity"
 | 
			
		||||
		android:textAllCaps="false"
 | 
			
		||||
		android:onClick="onNewActivity"/>
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -1,51 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent"
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
	android:background="#FFFFFFFF">
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:gravity="right|center_vertical">
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:layout_width="0dp"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:id="@+id/tv_title"
 | 
			
		||||
			android:layout_weight="1.0"
 | 
			
		||||
			android:text="WinBoLLNews"
 | 
			
		||||
			android:textStyle="bold"
 | 
			
		||||
			android:textSize="16sp"/>
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="48dp"
 | 
			
		||||
			android:layout_height="48dp"
 | 
			
		||||
			android:text="⇦"
 | 
			
		||||
			android:id="@+id/widget_button_pre"/>
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="48dp"
 | 
			
		||||
			android:layout_height="48dp"
 | 
			
		||||
			android:text="⇨"
 | 
			
		||||
			android:id="@+id/widget_button_next"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
    <TextView
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:id="@+id/tv_msg"/>
 | 
			
		||||
    
 | 
			
		||||
	<TextView
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:id="@+id/tv_news"
 | 
			
		||||
		android:layout_weight="1.0"/>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -3,18 +3,6 @@
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_home"
 | 
			
		||||
        android:title="HOME"
 | 
			
		||||
        android:title="WinBoLL Home"
 | 
			
		||||
        android:icon="@drawable/ic_winboll"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_yun"
 | 
			
		||||
        android:title="YUN"
 | 
			
		||||
        android:icon="@drawable/ic_winboll"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_logon"
 | 
			
		||||
        android:title="Logon"
 | 
			
		||||
        android:icon="@drawable/ic_winboll"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_log"
 | 
			
		||||
        android:title="LOG"
 | 
			
		||||
        android:icon="@drawable/ic_winboll_log"/>
 | 
			
		||||
</menu>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<network-security-config>
 | 
			
		||||
    <!-- 允许访问 winboll.cc 及其子域名(原配置) -->
 | 
			
		||||
    <domain-config cleartextTrafficPermitted="true">
 | 
			
		||||
        <domain includeSubdomains="true">winboll.cc</domain>
 | 
			
		||||
    </domain-config>
 | 
			
		||||
 | 
			
		||||
    <!-- **新增:允许访问 IP 地址 10.8.0.250** -->
 | 
			
		||||
    <domain-config cleartextTrafficPermitted="true">
 | 
			
		||||
        <domain includeSubdomains="false">10.8.0.250</domain> <!-- 不包含子域名 -->
 | 
			
		||||
    </domain-config>
 | 
			
		||||
</network-security-config>
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:minWidth="200dp"
 | 
			
		||||
    android:minHeight="100dp"
 | 
			
		||||
    android:updatePeriodMillis="1000"
 | 
			
		||||
    android:initialLayout="@layout/widget_news">
 | 
			
		||||
</appwidget-provider>
 | 
			
		||||
							
								
								
									
										1
									
								
								keystore
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								keystore
									
									
									
									
									
								
							 Submodule keystore deleted from e7f70226c1
									
								
							@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Mon Aug 18 03:56:26 HKT 2025
 | 
			
		||||
stageCount=6
 | 
			
		||||
#Sat Sep 27 21:03:08 HKT 2025
 | 
			
		||||
stageCount=10
 | 
			
		||||
libraryProject=libappbase
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.5
 | 
			
		||||
baseVersion=15.10
 | 
			
		||||
publishVersion=15.10.9
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.9.6
 | 
			
		||||
baseBetaVersion=15.10.10
 | 
			
		||||
 
 | 
			
		||||
@@ -3,15 +3,6 @@
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="cc.winboll.studio.libappbase">
 | 
			
		||||
 | 
			
		||||
    <!-- 拥有完全的网络访问权限 -->
 | 
			
		||||
    <uses-permission android:name="android.permission.INTERNET"/>
 | 
			
		||||
 | 
			
		||||
    <!-- 发送持久广播 -->
 | 
			
		||||
    <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
 | 
			
		||||
 | 
			
		||||
    <!-- 对正在运行的应用重新排序 -->
 | 
			
		||||
    <uses-permission android:name="android.permission.REORDER_TASKS"/>
 | 
			
		||||
 | 
			
		||||
    <application>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
@@ -27,7 +18,7 @@
 | 
			
		||||
            android:process=":GlobalCrashActivity"/>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".winboll.LogActivity"
 | 
			
		||||
            android:name=".LogActivity"
 | 
			
		||||
            android:label="LogActivity"
 | 
			
		||||
            android:resizeableActivity="true"
 | 
			
		||||
            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
 | 
			
		||||
@@ -37,76 +28,6 @@
 | 
			
		||||
 | 
			
		||||
        </activity>
 | 
			
		||||
 | 
			
		||||
        <service
 | 
			
		||||
            android:name=".SimpleOperateSignalCenterService"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
        </service>
 | 
			
		||||
 | 
			
		||||
        <service
 | 
			
		||||
            android:name=".services.TestService"
 | 
			
		||||
            android:exported="true"/>
 | 
			
		||||
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name=".receiver.MyBroadcastReceiver"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
                <action android:name="cc.winboll.studio.libappbase.action.SOS"/>
 | 
			
		||||
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
        </receiver>
 | 
			
		||||
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name=".widgets.StatusWidget"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
 | 
			
		||||
 | 
			
		||||
                <action android:name="cc.winboll.studio.libappbase.widgets.StatusWidget.ACTION_STATUS_UPDATE"/>
 | 
			
		||||
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
            <meta-data
 | 
			
		||||
                android:name="android.appwidget.provider"
 | 
			
		||||
                android:resource="@xml/widget_provider_info_status"/>
 | 
			
		||||
 | 
			
		||||
        </receiver>
 | 
			
		||||
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name=".widgets.StatusWidgetClickListener"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
                <action android:name="cc.winboll.studio.libappbase.widgets.StatusWidgetClickListener.ACTION_IVAPP"/>
 | 
			
		||||
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
        </receiver>
 | 
			
		||||
 | 
			
		||||
        <service android:name="cc.winboll.studio.libappbase.sos.SOSCenter"/>
 | 
			
		||||
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name="cc.winboll.studio.libappbase.sos.SOSCenterServiceReceiver"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
                <action android:name="cc.winboll.studio.libappbase.sos.SOSCenterServiceReceiver"/>
 | 
			
		||||
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
        </receiver>
 | 
			
		||||
 | 
			
		||||
        <activity android:name="cc.winboll.studio.libappbase.activities.YunActivity"/>
 | 
			
		||||
 | 
			
		||||
        <activity android:name="cc.winboll.studio.libappbase.activities.LogonActivity"/>
 | 
			
		||||
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.models;
 | 
			
		||||
package cc.winboll.studio.libappbase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
@@ -1,28 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/15 20:05:03
 | 
			
		||||
 * @Describe AppUtils
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.pm.ApplicationInfo;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.content.pm.PackageManager.NameNotFoundException;
 | 
			
		||||
 | 
			
		||||
public class AppUtils {
 | 
			
		||||
    
 | 
			
		||||
    public static final String TAG = "AppUtils";
 | 
			
		||||
    
 | 
			
		||||
    public static String getAppNameByPackageName(Context context, String packageName) {
 | 
			
		||||
        PackageManager packageManager = context.getPackageManager();
 | 
			
		||||
        try {
 | 
			
		||||
            ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
 | 
			
		||||
            return (String) packageManager.getApplicationLabel(applicationInfo);
 | 
			
		||||
        } catch (NameNotFoundException e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
            return "";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -10,85 +10,50 @@ import android.content.Context;
 | 
			
		||||
import android.content.pm.ApplicationInfo;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.libappbase.models.APPModel;
 | 
			
		||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.MyActivityLifecycleCallbacks;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.WinBoLLActivityManager;
 | 
			
		||||
 | 
			
		||||
public class GlobalApplication extends Application {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "GlobalApplication";
 | 
			
		||||
	
 | 
			
		||||
    volatile static GlobalApplication _GlobalApplication;
 | 
			
		||||
    // 是否处于调试状态
 | 
			
		||||
    // 应用是否处于调试状态
 | 
			
		||||
    volatile static boolean isDebuging = false;
 | 
			
		||||
    MyActivityLifecycleCallbacks mMyActivityLifecycleCallbacks;
 | 
			
		||||
    
 | 
			
		||||
    public static void setIsDebuging(boolean isDebuging) {
 | 
			
		||||
        GlobalApplication.isDebuging = isDebuging;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public static void saveDebugStatus() {
 | 
			
		||||
        if (_GlobalApplication != null) {
 | 
			
		||||
            APPModel.saveBeanToFile(getAPPModelFilePath(), new APPModel(GlobalApplication.isDebuging));
 | 
			
		||||
        }
 | 
			
		||||
    public static void saveDebugStatus(Context context) {
 | 
			
		||||
        APPModel.saveBeanToFile(getAPPModelFilePath(context), new APPModel(GlobalApplication.isDebuging));
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
    public static GlobalApplication getInstance() {
 | 
			
		||||
        return _GlobalApplication;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static String getAPPModelFilePath() {
 | 
			
		||||
        return _GlobalApplication.getDataDir().getPath() + "/APPModel.json";
 | 
			
		||||
    static String getAPPModelFilePath(Context context) {
 | 
			
		||||
        return context.getDataDir().getPath() + "/APPModel.json";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isDebuging() {
 | 
			
		||||
        return isDebuging;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static WinBoLLActivityManager getWinBoLLActivityManager() {
 | 
			
		||||
        return WinBoLLActivityManager.getInstance();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate() {
 | 
			
		||||
        super.onCreate();
 | 
			
		||||
        // 保存初始实例
 | 
			
		||||
        _GlobalApplication = this;
 | 
			
		||||
		
 | 
			
		||||
        setIsDebuging(true);
 | 
			
		||||
        // 添加日志模块
 | 
			
		||||
        LogUtils.init(this);
 | 
			
		||||
        //LogUtils.setLogLevel(LogUtils.LOG_LEVEL.Debug);
 | 
			
		||||
        //LogUtils.setTAGListEnable(GlobalApplication.TAG, true);
 | 
			
		||||
        //LogUtils.setALlTAGListEnable(true);
 | 
			
		||||
        //LogUtils.d(TAG, "LogUtils init");
 | 
			
		||||
        // 设置应用异常处理窗口
 | 
			
		||||
        CrashHandler.init(this);
 | 
			
		||||
        // 初始化 Toast 框架
 | 
			
		||||
        ToastUtils.init(this);
 | 
			
		||||
 | 
			
		||||
        // 应用保存的调试标志
 | 
			
		||||
        APPModel appModel = APPModel.loadBeanFromFile(getAPPModelFilePath(), APPModel.class);
 | 
			
		||||
        APPModel appModel = APPModel.loadBeanFromFile(getAPPModelFilePath(this), APPModel.class);
 | 
			
		||||
        if (appModel == null) {
 | 
			
		||||
            setIsDebuging(false);
 | 
			
		||||
            saveDebugStatus();
 | 
			
		||||
            saveDebugStatus(this);
 | 
			
		||||
        } else {
 | 
			
		||||
            setIsDebuging(appModel.isDebuging());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        getWinBoLLActivityManager().setWinBoLLUI_TYPE(WinBoLLActivityManager.WinBoLLUI_TYPE.Service);
 | 
			
		||||
        // 注册窗口回调监听
 | 
			
		||||
        mMyActivityLifecycleCallbacks = new MyActivityLifecycleCallbacks();
 | 
			
		||||
        registerActivityLifecycleCallbacks(mMyActivityLifecycleCallbacks);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTerminate() {
 | 
			
		||||
        super.onTerminate();
 | 
			
		||||
        // 注销回调(非必须,但建议释放资源)
 | 
			
		||||
        unregisterActivityLifecycleCallbacks(mMyActivityLifecycleCallbacks);
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
	public static String getAppName(Context context) {
 | 
			
		||||
@@ -102,27 +67,4 @@ public class GlobalApplication extends Application {
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
//
 | 
			
		||||
//    @Override
 | 
			
		||||
//    public void helpISOSService(Intent intent) {
 | 
			
		||||
//        String szServiceName = intent.getStringExtra(EXTRA_SERVICE);
 | 
			
		||||
//        String szPackageName = intent.getStringExtra(EXTRA_PACKAGE);
 | 
			
		||||
//        if (szServiceName != null && !szServiceName.equals("")
 | 
			
		||||
//            && szPackageName != null && !szPackageName.equals("")) {
 | 
			
		||||
//            LogUtils.d(TAG, "szPackageName " + szPackageName);
 | 
			
		||||
//            LogUtils.d(TAG, "szServiceName " + szServiceName);
 | 
			
		||||
//
 | 
			
		||||
//            // 目标服务的包名和类名
 | 
			
		||||
//            //String packageName = this.getPackageName();
 | 
			
		||||
//            //String serviceClassName = SimpleOperateSignalCenterService.class.getName();
 | 
			
		||||
//
 | 
			
		||||
//            // 构建Intent
 | 
			
		||||
//            Intent intentService = new Intent();
 | 
			
		||||
//            intentService.setComponent(new ComponentName(szPackageName, szServiceName));
 | 
			
		||||
//            intentService.putExtra(ISOSService.EXTRA_ENABLE, true);
 | 
			
		||||
//            startService(intentService);
 | 
			
		||||
//            LogUtils.d(TAG, "startService(intentService)");
 | 
			
		||||
//        }
 | 
			
		||||
//        LogUtils.d(TAG, "helpISOSService");
 | 
			
		||||
//    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.views;
 | 
			
		||||
package cc.winboll.studio.libappbase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.winboll;
 | 
			
		||||
package cc.winboll.studio.libappbase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
@@ -6,29 +6,18 @@ package cc.winboll.studio.libappbase.winboll;
 | 
			
		||||
 * @Describe 应用日志窗口
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.WindowManager;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogView;
 | 
			
		||||
import cc.winboll.studio.libappbase.R;
 | 
			
		||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
 | 
			
		||||
 | 
			
		||||
public class LogActivity extends Activity implements IWinBoLLActivity {
 | 
			
		||||
public class LogActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "LogActivity";
 | 
			
		||||
 | 
			
		||||
    LogView mLogView;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
@@ -45,4 +34,13 @@ public class LogActivity extends Activity implements IWinBoLLActivity {
 | 
			
		||||
        mLogView.start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	public static void startLogActivity(Context context) {
 | 
			
		||||
		Intent intent = new Intent(context, LogActivity.class);
 | 
			
		||||
        // 打开多任务窗口
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
 | 
			
		||||
        context.startActivity(intent);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -28,7 +28,6 @@ import android.widget.Spinner;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.R;
 | 
			
		||||
import cc.winboll.studio.libappbase.views.HorizontalListView;
 | 
			
		||||
import java.text.Collator;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,12 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.utils;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
package cc.winboll.studio.libappbase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/12 12:02:31
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
public class ToastUtils {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "ToastUtils";
 | 
			
		||||
@@ -1,150 +0,0 @@
 | 
			
		||||
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<zhangsken@qq.com>
 | 
			
		||||
 * @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());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,126 +0,0 @@
 | 
			
		||||
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<zhangsken@qq.com>
 | 
			
		||||
 * @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();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,60 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.dialogs;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/28 17:40:47
 | 
			
		||||
 * @Date 2024/08/12 14:46:25
 | 
			
		||||
 * @Describe 询问用户确定与否的选择框
 | 
			
		||||
 */
 | 
			
		||||
import android.app.AlertDialog;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.DialogInterface;
 | 
			
		||||
 | 
			
		||||
public class YesNoAlertDialog {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "YesNoAlertDialog";
 | 
			
		||||
 | 
			
		||||
    public static void show(Context context, String szTitle, String szMessage, final OnDialogResultListener listener) {
 | 
			
		||||
        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
 | 
			
		||||
            context);
 | 
			
		||||
 | 
			
		||||
        // set title
 | 
			
		||||
        alertDialogBuilder.setTitle(szTitle);
 | 
			
		||||
 | 
			
		||||
        // set dialog message
 | 
			
		||||
        alertDialogBuilder
 | 
			
		||||
            .setMessage(szMessage)
 | 
			
		||||
            .setCancelable(true)
 | 
			
		||||
            .setOnCancelListener(new DialogInterface.OnCancelListener(){
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onCancel(DialogInterface dialog) {
 | 
			
		||||
                    listener.onNo();
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .setPositiveButton("YES", new DialogInterface.OnClickListener() {
 | 
			
		||||
                public void onClick(DialogInterface dialog, int id) {
 | 
			
		||||
                    // if this button is clicked, close
 | 
			
		||||
                    // current activity
 | 
			
		||||
                    listener.onYes();
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .setNegativeButton("NO", new DialogInterface.OnClickListener() {
 | 
			
		||||
                public void onClick(DialogInterface dialog, int id) {
 | 
			
		||||
                    // if this button is clicked, just close
 | 
			
		||||
                    // the dialog box and do nothing
 | 
			
		||||
                    dialog.cancel();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // create alert dialog
 | 
			
		||||
        AlertDialog alertDialog = alertDialogBuilder.create();
 | 
			
		||||
 | 
			
		||||
        // show it
 | 
			
		||||
        alertDialog.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public interface OnDialogResultListener {
 | 
			
		||||
        abstract void onYes();
 | 
			
		||||
        abstract void onNo();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,53 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.models;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/06/05 11:26
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
public class ResponseData {
 | 
			
		||||
    
 | 
			
		||||
    public static final String STATUS_SUCCESS = "success";
 | 
			
		||||
    public static final String STATUS_ERROR = "error";
 | 
			
		||||
    
 | 
			
		||||
    private String status;
 | 
			
		||||
    private String message;
 | 
			
		||||
    private UserInfoModel data;
 | 
			
		||||
    
 | 
			
		||||
    public ResponseData() {
 | 
			
		||||
        this.status = "";
 | 
			
		||||
        this.message = "";
 | 
			
		||||
        this.data = new UserInfoModel();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public ResponseData(String status, String message, UserInfoModel data) {
 | 
			
		||||
        this.status = status;
 | 
			
		||||
        this.message = message;
 | 
			
		||||
        this.data = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setStatus(String status) {
 | 
			
		||||
        this.status = status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getStatus() {
 | 
			
		||||
        return status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setMessage(String message) {
 | 
			
		||||
        this.message = message;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getMessage() {
 | 
			
		||||
        return message;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setData(UserInfoModel data) {
 | 
			
		||||
        this.data = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public UserInfoModel getData() {
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,92 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.models;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/06/04 19:14
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class UserInfoModel extends BaseBean {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "UserInfoModel";
 | 
			
		||||
 | 
			
		||||
    String username;
 | 
			
		||||
    String password;
 | 
			
		||||
    String token;
 | 
			
		||||
 | 
			
		||||
    public UserInfoModel() {
 | 
			
		||||
        this.username = "";
 | 
			
		||||
        this.password = "";
 | 
			
		||||
        this.token = "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setUsername(String username) {
 | 
			
		||||
        this.username = username;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getUsername() {
 | 
			
		||||
        return username;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setPassword(String password) {
 | 
			
		||||
        this.password = password;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getPassword() {
 | 
			
		||||
        return password;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setToken(String token) {
 | 
			
		||||
        this.token = token;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getToken() {
 | 
			
		||||
        return token;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return UserInfoModel.class.getName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
			
		||||
        super.writeThisToJsonWriter(jsonWriter);
 | 
			
		||||
        jsonWriter.name("username").value(getUsername());
 | 
			
		||||
        jsonWriter.name("password").value(getPassword());
 | 
			
		||||
        jsonWriter.name("token").value(getToken());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
 | 
			
		||||
        if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
 | 
			
		||||
            if (name.equals("username")) {
 | 
			
		||||
                setUsername(jsonReader.nextString());
 | 
			
		||||
            } else if (name.equals("password")) {
 | 
			
		||||
                setPassword(jsonReader.nextString());
 | 
			
		||||
            } else if (name.equals("token")) {
 | 
			
		||||
                setToken(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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,91 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.models;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/05/10 10:16
 | 
			
		||||
 * @Describe WinBoLLModel
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class WinBoLLModel extends BaseBean {
 | 
			
		||||
    
 | 
			
		||||
    public static final String TAG = "WinBoLLModel";
 | 
			
		||||
    
 | 
			
		||||
    String appPackageName;
 | 
			
		||||
    String appMainServiveName;
 | 
			
		||||
 | 
			
		||||
    public WinBoLLModel() {
 | 
			
		||||
        this.appPackageName = "";
 | 
			
		||||
        this.appMainServiveName = "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public WinBoLLModel(boolean isDebuging, String appPackageName, String appMainServiveName) {
 | 
			
		||||
        this.appPackageName = appPackageName;
 | 
			
		||||
        this.appMainServiveName = appMainServiveName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public WinBoLLModel(String appPackageName, String appMainServiveName) {
 | 
			
		||||
        this.appPackageName = appPackageName;
 | 
			
		||||
        this.appMainServiveName = appMainServiveName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setAppPackageName(String appPackageName) {
 | 
			
		||||
        this.appPackageName = appPackageName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getAppPackageName() {
 | 
			
		||||
        return appPackageName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setAppMainServiveName(String appMainServiveName) {
 | 
			
		||||
        this.appMainServiveName = appMainServiveName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getAppMainServiveName() {
 | 
			
		||||
        return appMainServiveName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return APPModel.class.getName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
			
		||||
        super.writeThisToJsonWriter(jsonWriter);
 | 
			
		||||
        jsonWriter.name("appPackageName").value(getAppPackageName());
 | 
			
		||||
        jsonWriter.name("appMainServiveName").value(getAppMainServiveName());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
 | 
			
		||||
        if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
 | 
			
		||||
            if (name.equals("appPackageName")) {
 | 
			
		||||
                setAppPackageName(jsonReader.nextString());
 | 
			
		||||
            } else if (name.equals("appMainServiveName")) {
 | 
			
		||||
                setAppMainServiveName(jsonReader.nextString());
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
 | 
			
		||||
        jsonReader.beginObject();
 | 
			
		||||
        while (jsonReader.hasNext()) {
 | 
			
		||||
            String name = jsonReader.nextName();
 | 
			
		||||
            if (!initObjectsFromJsonReader(jsonReader, name)) {
 | 
			
		||||
                jsonReader.skipValue();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 结束 JSON 对象
 | 
			
		||||
        jsonReader.endObject();
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
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<zhangsken@qq.com>
 | 
			
		||||
 * @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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.receiver;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @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));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,59 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.sos;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/02 09:36:29
 | 
			
		||||
 * @Describe WinBoLL 应用 SOS 机理保护类
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class SOS {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "SOS";
 | 
			
		||||
 | 
			
		||||
    public static final String ACTION_SOS = SOS.class.getName() + ".ACTION_SOS";
 | 
			
		||||
    public static final String EXTRA_OBJECT = "EXTRA_OBJECT";
 | 
			
		||||
 | 
			
		||||
    public static void sosToAppBase(Context context, String sosService) {
 | 
			
		||||
        LogUtils.d(TAG, "sosToAppBase()");
 | 
			
		||||
        String szToPackage = "cc.winboll.studio.appbase";
 | 
			
		||||
        sos(context, szToPackage, sosService);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void sosToAppBaseBeta(Context context, String sosService) {
 | 
			
		||||
        LogUtils.d(TAG, "sosToAppBaseBeta()");
 | 
			
		||||
        String szToPackage = "cc.winboll.studio.appbase.beta";
 | 
			
		||||
        sos(context, szToPackage, sosService);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void sos(Context context, String szToPackage, String sosService) {
 | 
			
		||||
        LogUtils.d(TAG, "sos(...)");
 | 
			
		||||
        Intent intent = new Intent(ACTION_SOS);
 | 
			
		||||
        intent.putExtra(EXTRA_OBJECT, genSOSObject(context.getPackageName(), sosService));
 | 
			
		||||
        intent.setPackage(szToPackage);
 | 
			
		||||
        LogUtils.d(TAG, String.format("ACTION_SOS :\nTo Package : %sSOS Service : %s\n", szToPackage, sosService));
 | 
			
		||||
        context.sendBroadcast(intent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static SOSObject parseSOSObject(String szSOSObject) {
 | 
			
		||||
        try {
 | 
			
		||||
            return SOSObject.parseStringToBean(szSOSObject, SOSObject.class);
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String sosObjectToString(SOSObject object) {
 | 
			
		||||
        return object.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String genSOSObject(String objectPackageName, String objectServiveName) {
 | 
			
		||||
        return (new SOSObject(objectPackageName, objectServiveName)).toString();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,182 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.sos;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @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());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,69 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.sos;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/02 09:49:45
 | 
			
		||||
 * @Describe SOSCenterServiceModel
 | 
			
		||||
 *           Simple Operate Signal Service Model.
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class SOSCenterServiceModel extends BaseBean {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "SOSCenterServiceModel";
 | 
			
		||||
 | 
			
		||||
    boolean isEnable;
 | 
			
		||||
 | 
			
		||||
    public SOSCenterServiceModel() {
 | 
			
		||||
        this.isEnable = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setIsEnable(boolean isEnable) {
 | 
			
		||||
        this.isEnable = isEnable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isEnable() {
 | 
			
		||||
        return isEnable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return SOSCenterServiceModel.class.getName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
			
		||||
        super.writeThisToJsonWriter(jsonWriter);
 | 
			
		||||
        jsonWriter.name("isEnable").value(isEnable());
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
 | 
			
		||||
        if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
 | 
			
		||||
            if (name.equals("isEnable")) {
 | 
			
		||||
                setIsEnable(jsonReader.nextBoolean());
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
 | 
			
		||||
        jsonReader.beginObject();
 | 
			
		||||
        while (jsonReader.hasNext()) {
 | 
			
		||||
            String name = jsonReader.nextName();
 | 
			
		||||
            if (!initObjectsFromJsonReader(jsonReader, name)) {
 | 
			
		||||
                jsonReader.skipValue();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 结束 JSON 对象
 | 
			
		||||
        jsonReader.endObject();
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.sos;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/27 14:04:35
 | 
			
		||||
 * @Describe SOSCenterServiceReceiver
 | 
			
		||||
 */
 | 
			
		||||
import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
 | 
			
		||||
public class SOSCenterServiceReceiver extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "SOSCenterServiceReceiver";
 | 
			
		||||
 | 
			
		||||
    public static final String ACTION_SOS = SOSCenterServiceReceiver.class.getName() + ".ACTION_SOS";
 | 
			
		||||
    
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onReceive(Context context, Intent intent) {
 | 
			
		||||
        String action = intent.getAction();
 | 
			
		||||
        if (action.equals(ACTION_SOS)) {
 | 
			
		||||
            // 处理接收到的广播消息
 | 
			
		||||
            LogUtils.d(TAG, String.format("Action %s \n%s\n%s", action));
 | 
			
		||||
        } else {
 | 
			
		||||
            LogUtils.d(TAG, String.format("%s", action));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.sos;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/27 14:12:05
 | 
			
		||||
 * @Describe SOSBean
 | 
			
		||||
 */
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import android.util.JsonWriter;
 | 
			
		||||
import cc.winboll.studio.libappbase.BaseBean;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class SOSObject extends BaseBean {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "SOSObject";
 | 
			
		||||
 | 
			
		||||
    String objectPackageName;
 | 
			
		||||
    String objectServiveName;
 | 
			
		||||
 | 
			
		||||
    public SOSObject() {
 | 
			
		||||
        this.objectPackageName = "";
 | 
			
		||||
        this.objectServiveName = "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SOSObject(String objectPackageName, String objectServiveName) {
 | 
			
		||||
        this.objectPackageName = objectPackageName;
 | 
			
		||||
        this.objectServiveName = objectServiveName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setObjectPackageName(String objectPackageName) {
 | 
			
		||||
        this.objectPackageName = objectPackageName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getObjectPackageName() {
 | 
			
		||||
        return objectPackageName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setObjectServiveName(String objectServiveName) {
 | 
			
		||||
        this.objectServiveName = objectServiveName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getObjectServiveName() {
 | 
			
		||||
        return objectServiveName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return SOSObject.class.getName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
			
		||||
        super.writeThisToJsonWriter(jsonWriter);
 | 
			
		||||
        jsonWriter.name("objectPackageName").value(getObjectPackageName());
 | 
			
		||||
        jsonWriter.name("objectServiveName").value(getObjectServiveName());
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
 | 
			
		||||
        if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
 | 
			
		||||
            if (name.equals("objectPackageName")) {
 | 
			
		||||
                setObjectPackageName(jsonReader.nextString());
 | 
			
		||||
            } else if (name.equals("objectServiveName")) {
 | 
			
		||||
                setObjectServiveName(jsonReader.nextString());
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
 | 
			
		||||
        jsonReader.beginObject();
 | 
			
		||||
        while (jsonReader.hasNext()) {
 | 
			
		||||
            String name = jsonReader.nextName();
 | 
			
		||||
            if (!initObjectsFromJsonReader(jsonReader, name)) {
 | 
			
		||||
                jsonReader.skipValue();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 结束 JSON 对象
 | 
			
		||||
        jsonReader.endObject();
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,128 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/06/04 20:15
 | 
			
		||||
 * @Describe 文件操作类
 | 
			
		||||
 */
 | 
			
		||||
import java.io.BufferedReader;
 | 
			
		||||
import java.io.BufferedWriter;
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.FileReader;
 | 
			
		||||
import java.io.FileWriter;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
public class FileUtils {
 | 
			
		||||
    public static final String TAG = "FileUtils";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 读取文件为字节数组(Java 7 语法)
 | 
			
		||||
     */
 | 
			
		||||
    public static byte[] readFileToByteArray(String filePath) {
 | 
			
		||||
        FileInputStream fis = null;
 | 
			
		||||
        ByteArrayOutputStream bos = null;
 | 
			
		||||
        try {
 | 
			
		||||
            fis = new FileInputStream(filePath);
 | 
			
		||||
            bos = new ByteArrayOutputStream();
 | 
			
		||||
            byte[] buffer = new byte[4096];
 | 
			
		||||
            int bytesRead;
 | 
			
		||||
            while ((bytesRead = fis.read(buffer)) != -1) {
 | 
			
		||||
                bos.write(buffer, 0, bytesRead);
 | 
			
		||||
            }
 | 
			
		||||
            return bos.toByteArray();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return null;
 | 
			
		||||
        } finally {
 | 
			
		||||
            // 手动关闭流(Java 7 不支持 try-with-resources)
 | 
			
		||||
            if (fis != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    fis.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (bos != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    bos.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 写入字节数组到文件(Java 7 语法)
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean writeByteArrayToFile(byte[] data, String filePath) {
 | 
			
		||||
        FileOutputStream fos = null;
 | 
			
		||||
        try {
 | 
			
		||||
            fos = new FileOutputStream(filePath);
 | 
			
		||||
            fos.write(data);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return false;
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (fos != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    fos.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 原字符串读写方法(适配 Java 7)
 | 
			
		||||
    public static String readFileToString(String filePath) {
 | 
			
		||||
        BufferedReader reader = null;
 | 
			
		||||
        try {
 | 
			
		||||
            reader = new BufferedReader(new FileReader(filePath));
 | 
			
		||||
            StringBuilder content = new StringBuilder();
 | 
			
		||||
            String line;
 | 
			
		||||
            while ((line = reader.readLine()) != null) {
 | 
			
		||||
                content.append(line).append(System.getProperty("line.separator"));
 | 
			
		||||
            }
 | 
			
		||||
            // 去除最后一个换行符(可选)
 | 
			
		||||
            if (content.length() > 0) {
 | 
			
		||||
                content.deleteCharAt(content.length() - 1);
 | 
			
		||||
            }
 | 
			
		||||
            return content.toString();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return null;
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (reader != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    reader.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean writeStringToFile(String content, String filePath, boolean append) {
 | 
			
		||||
        BufferedWriter writer = null;
 | 
			
		||||
        try {
 | 
			
		||||
            writer = new BufferedWriter(new FileWriter(filePath, append));
 | 
			
		||||
            writer.write(content);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return false;
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (writer != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    writer.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,222 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/06/04 13:36
 | 
			
		||||
 * @Describe RSA加密工具
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.util.Base64;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.security.KeyFactory;
 | 
			
		||||
import java.security.KeyPair;
 | 
			
		||||
import java.security.KeyPairGenerator;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
import java.security.PrivateKey;
 | 
			
		||||
import java.security.PublicKey;
 | 
			
		||||
import java.security.spec.InvalidKeySpecException;
 | 
			
		||||
import java.security.spec.PKCS8EncodedKeySpec;
 | 
			
		||||
import java.security.spec.X509EncodedKeySpec;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import javax.crypto.Cipher;
 | 
			
		||||
 | 
			
		||||
public class RSAUtils {
 | 
			
		||||
    private static final String TAG = "RSAUtils";
 | 
			
		||||
    private static final int KEY_SIZE = 2048;
 | 
			
		||||
    private static final String KEY_ALGORITHM = "RSA";
 | 
			
		||||
    private static final String PUBLIC_KEY_FILE = "public.key";
 | 
			
		||||
    private static final String PRIVATE_KEY_FILE = "private.key";
 | 
			
		||||
    private static final String CIPHER_ALGORITHM = KEY_ALGORITHM + "/ECB/PKCS1Padding"; // 保留原加密方式
 | 
			
		||||
 | 
			
		||||
    private final String keyPath;
 | 
			
		||||
    private static volatile RSAUtils INSTANCE;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 构造方法:初始化密钥存储路径(内部存储)
 | 
			
		||||
     */
 | 
			
		||||
    private RSAUtils(Context context) {
 | 
			
		||||
        keyPath = context.getFilesDir() + File.separator + "keys" + File.separator; // 修正路径格式
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取单例实例
 | 
			
		||||
     */
 | 
			
		||||
    public static synchronized RSAUtils getInstance(Context context) {
 | 
			
		||||
        if (INSTANCE == null) {
 | 
			
		||||
            INSTANCE = new RSAUtils(context);
 | 
			
		||||
        }
 | 
			
		||||
        return INSTANCE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查密钥文件是否存在
 | 
			
		||||
     */
 | 
			
		||||
    public boolean keysExist() {
 | 
			
		||||
        File publicKeyFile = new File(keyPath + PUBLIC_KEY_FILE);
 | 
			
		||||
        File privateKeyFile = new File(keyPath + PRIVATE_KEY_FILE);
 | 
			
		||||
        return publicKeyFile.exists() && privateKeyFile.exists();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成密钥对并保存到文件
 | 
			
		||||
     */
 | 
			
		||||
    public void generateAndSaveKeys() throws Exception {
 | 
			
		||||
        LogUtils.d(TAG, "开始生成 RSA 密钥对(2048位)");
 | 
			
		||||
        KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
 | 
			
		||||
        generator.initialize(KEY_SIZE);
 | 
			
		||||
        KeyPair keyPair = generator.generateKeyPair();
 | 
			
		||||
 | 
			
		||||
        saveKey(PUBLIC_KEY_FILE, keyPair.getPublic().getEncoded());
 | 
			
		||||
        saveKey(PRIVATE_KEY_FILE, keyPair.getPrivate().getEncoded());
 | 
			
		||||
        LogUtils.d(TAG, "密钥对生成并保存成功");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取或生成密钥对(线程安全)
 | 
			
		||||
     */
 | 
			
		||||
    public KeyPair getOrGenerateKeys() throws Exception {
 | 
			
		||||
        if (!keysExist()) {
 | 
			
		||||
            synchronized (RSAUtils.class) { // 双重检查锁,避免多线程重复生成
 | 
			
		||||
                if (!keysExist()) {
 | 
			
		||||
                    generateAndSaveKeys();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return readKeysFromFile();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 从文件读取密钥对
 | 
			
		||||
     */
 | 
			
		||||
    private KeyPair readKeysFromFile() throws Exception {
 | 
			
		||||
        LogUtils.d(TAG, "读取密钥对文件");
 | 
			
		||||
        try {
 | 
			
		||||
            byte[] publicKeyBytes = readFileToBytes(keyPath + PUBLIC_KEY_FILE);
 | 
			
		||||
            byte[] privateKeyBytes = readFileToBytes(keyPath + PRIVATE_KEY_FILE);
 | 
			
		||||
 | 
			
		||||
            X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(publicKeyBytes);
 | 
			
		||||
            PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(privateKeyBytes);
 | 
			
		||||
 | 
			
		||||
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
 | 
			
		||||
            PublicKey publicKey = factory.generatePublic(publicSpec);
 | 
			
		||||
            PrivateKey privateKey = factory.generatePrivate(privateSpec);
 | 
			
		||||
 | 
			
		||||
            return new KeyPair(publicKey, privateKey);
 | 
			
		||||
        } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
 | 
			
		||||
            LogUtils.e(TAG, "密钥文件读取失败:" + e.getMessage());
 | 
			
		||||
            throw new Exception("密钥文件损坏或格式错误", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 保存密钥到文件(通用方法)
 | 
			
		||||
     */
 | 
			
		||||
    private void saveKey(String fileName, byte[] keyBytes) throws IOException {
 | 
			
		||||
        Objects.requireNonNull(keyBytes, "密钥字节数据不可为空");
 | 
			
		||||
        File dir = new File(keyPath);
 | 
			
		||||
        if (!dir.exists() && !dir.mkdirs()) {
 | 
			
		||||
            throw new IOException("创建密钥目录失败:" + keyPath);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        FileOutputStream fos = null;
 | 
			
		||||
        try {
 | 
			
		||||
            fos = new FileOutputStream(keyPath + fileName);
 | 
			
		||||
            fos.write(keyBytes);
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (fos != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    fos.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    LogUtils.e(TAG, "关闭文件流失败:" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 读取文件为字节数组(Java 7 兼容)
 | 
			
		||||
     */
 | 
			
		||||
    private byte[] readFileToBytes(String filePath) throws IOException {
 | 
			
		||||
        File file = new File(filePath);
 | 
			
		||||
        if (!file.exists() || file.isDirectory()) {
 | 
			
		||||
            throw new IOException("文件不存在或为目录:" + filePath);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        FileInputStream fis = null;
 | 
			
		||||
        try {
 | 
			
		||||
            fis = new FileInputStream(file);
 | 
			
		||||
            byte[] data = new byte[(int) file.length()];
 | 
			
		||||
            int bytesRead = fis.read(data);
 | 
			
		||||
            if (bytesRead != data.length) {
 | 
			
		||||
                throw new IOException("文件读取不完整");
 | 
			
		||||
            }
 | 
			
		||||
            return data;
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (fis != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    fis.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    LogUtils.e(TAG, "关闭文件流失败:" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 公钥加密(带参数校验)
 | 
			
		||||
     */
 | 
			
		||||
    public byte[] encryptWithPublicKey(String plainText, PublicKey publicKey) throws Exception {
 | 
			
		||||
        Objects.requireNonNull(plainText, "明文不可为空");
 | 
			
		||||
        Objects.requireNonNull(publicKey, "公钥不可为空");
 | 
			
		||||
 | 
			
		||||
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
 | 
			
		||||
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
 | 
			
		||||
 | 
			
		||||
        // 检查数据长度是否超过 RSA 限制(2048位密钥最大明文为 214字节,PKCS1Padding)
 | 
			
		||||
        int maxPlainTextSize = cipher.getBlockSize() - 11; // PKCS1Padding 固定填充长度
 | 
			
		||||
        if (plainText.getBytes("UTF-8").length > maxPlainTextSize) {
 | 
			
		||||
            throw new IllegalArgumentException("明文过长,最大支持 " + maxPlainTextSize + " 字节");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return cipher.doFinal(plainText.getBytes("UTF-8"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 私钥解密(带参数校验)
 | 
			
		||||
     */
 | 
			
		||||
    public String decryptWithPrivateKey(byte[] encryptedData, PrivateKey privateKey) throws Exception {
 | 
			
		||||
        Objects.requireNonNull(encryptedData, "密文不可为空");
 | 
			
		||||
        Objects.requireNonNull(privateKey, "私钥不可为空");
 | 
			
		||||
 | 
			
		||||
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
 | 
			
		||||
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
 | 
			
		||||
        byte[] decryptedBytes = cipher.doFinal(encryptedData);
 | 
			
		||||
        return new String(decryptedBytes, "UTF-8");
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 将 HTTP 传输的 Base64 字符串还原为加密字节数组(Java 7 兼容)
 | 
			
		||||
     * @param httpString Base64 字符串(非 null)
 | 
			
		||||
     * @return 加密字节数组
 | 
			
		||||
     * @throws IllegalArgumentException 解码失败时抛出
 | 
			
		||||
     */
 | 
			
		||||
    public byte[] httpStringToEncryptBytes(String httpString) {
 | 
			
		||||
        Objects.requireNonNull(httpString, "HTTP 字符串不可为空");
 | 
			
		||||
 | 
			
		||||
        // 计算缺失的填充符数量(Java 7 不支持 repeat(),手动拼接)
 | 
			
		||||
        int pad = httpString.length() % 4;
 | 
			
		||||
        StringBuilder paddedString = new StringBuilder(httpString);
 | 
			
		||||
        if (pad != 0) {
 | 
			
		||||
            for (int i = 0; i < pad; i++) {
 | 
			
		||||
                paddedString.append('='); // 补全 '='
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 使用 Base64 解码(Android 原生 Base64 类兼容 Java 7)
 | 
			
		||||
        return Base64.decode(paddedString.toString(), Base64.URL_SAFE);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,48 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/02/17 19:38:20
 | 
			
		||||
 * @Describe 服务工具集
 | 
			
		||||
 */
 | 
			
		||||
import android.app.ActivityManager;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class ServiceUtils {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "ServiceUtils";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查指定服务是否正在运行
 | 
			
		||||
     * @param context 上下文
 | 
			
		||||
     * @param serviceClass 服务类
 | 
			
		||||
     * @return true 如果服务正在运行,否则返回 false
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isServiceRunning(Context context, String serviceClassName) {
 | 
			
		||||
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
 | 
			
		||||
        if (activityManager == null) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        List<ActivityManager.RunningServiceInfo> runningServices;
 | 
			
		||||
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
 | 
			
		||||
//            Intent intent = new Intent(context, serviceClass);
 | 
			
		||||
//            runningServices = activityManager.getRunningServices(100, intent);
 | 
			
		||||
//        } else {
 | 
			
		||||
        runningServices = activityManager.getRunningServices(100);
 | 
			
		||||
        //}
 | 
			
		||||
        for (ActivityManager.RunningServiceInfo serviceInfo : runningServices) {
 | 
			
		||||
            if (serviceClassName.equals(serviceInfo.service.getClassName())) {
 | 
			
		||||
                LogUtils.d(TAG, "Service is running: " + serviceInfo.service.getClassName());
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        LogUtils.d(TAG, "Service is not running: " + serviceClassName);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,281 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/06/04 17:21
 | 
			
		||||
 * @Describe 应用登录与接口工具
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.Looper;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.models.ResponseData;
 | 
			
		||||
import cc.winboll.studio.libappbase.models.UserInfoModel;
 | 
			
		||||
import com.google.gson.Gson;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.net.URLDecoder;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.security.KeyPair;
 | 
			
		||||
import java.security.PrivateKey;
 | 
			
		||||
import java.security.PublicKey;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import okhttp3.Call;
 | 
			
		||||
import okhttp3.Callback;
 | 
			
		||||
import okhttp3.MediaType;
 | 
			
		||||
import okhttp3.OkHttpClient;
 | 
			
		||||
import okhttp3.Request;
 | 
			
		||||
import okhttp3.RequestBody;
 | 
			
		||||
import okhttp3.Response;
 | 
			
		||||
import java.io.UnsupportedEncodingException;
 | 
			
		||||
 | 
			
		||||
public class YunUtils {
 | 
			
		||||
    public static final String TAG = "YunUtils";
 | 
			
		||||
    // 私有静态实例,类加载时创建
 | 
			
		||||
    private static volatile YunUtils INSTANCE;
 | 
			
		||||
    Context mContext;
 | 
			
		||||
    UserInfoModel mUserInfoModel;
 | 
			
		||||
    String token = "";
 | 
			
		||||
    String mDataFolderPath = "";
 | 
			
		||||
    String mUserInfoModelPath = "";
 | 
			
		||||
 | 
			
		||||
    private static final int CONNECT_TIMEOUT = 15; // 连接超时时间(秒)
 | 
			
		||||
    private static final int READ_TIMEOUT = 20;    // 读取超时时间(秒)
 | 
			
		||||
    private static volatile YunUtils instance;
 | 
			
		||||
    private OkHttpClient okHttpClient;
 | 
			
		||||
    private Handler mainHandler; // 主线程 Handler
 | 
			
		||||
 | 
			
		||||
    // 私有构造方法,防止外部实例化
 | 
			
		||||
    private YunUtils(Context context) {
 | 
			
		||||
        LogUtils.d(TAG, "YunUtils");
 | 
			
		||||
        mContext = context;
 | 
			
		||||
        mDataFolderPath = mContext.getExternalFilesDir(TAG).toString();
 | 
			
		||||
        File fTest = new File(mDataFolderPath);
 | 
			
		||||
        if (!fTest.exists()) {
 | 
			
		||||
            fTest.mkdirs();
 | 
			
		||||
        }
 | 
			
		||||
        mUserInfoModelPath = mDataFolderPath + File.separator + "UserInfoModel.rsajson";
 | 
			
		||||
 | 
			
		||||
        okHttpClient = new OkHttpClient.Builder()
 | 
			
		||||
            .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
 | 
			
		||||
            .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
 | 
			
		||||
            .build();
 | 
			
		||||
        mainHandler = new Handler(Looper.getMainLooper()); // 获取主线程 Looper
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 公共静态方法,返回唯一实例
 | 
			
		||||
    public static synchronized YunUtils getInstance(Context context) {
 | 
			
		||||
        LogUtils.d(TAG, "getInstance");
 | 
			
		||||
        if (INSTANCE == null) {
 | 
			
		||||
            INSTANCE = new YunUtils(context);
 | 
			
		||||
        }
 | 
			
		||||
        return INSTANCE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void checkLoginStatus() {
 | 
			
		||||
        String token = getLocalToken();
 | 
			
		||||
        LogUtils.d(TAG, String.format("checkLoginStatus token is %s", token));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    String getLocalToken() {
 | 
			
		||||
        UserInfoModel userInfoModel = loadUserInfoModel();
 | 
			
		||||
        return (userInfoModel == null) ?"": userInfoModel.getToken();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void login(String host, UserInfoModel userInfoModel) {
 | 
			
		||||
        LogUtils.d(TAG, "login");
 | 
			
		||||
 | 
			
		||||
        // 发送 POST 请求
 | 
			
		||||
        String apiUrl = host + "/login/index.php";
 | 
			
		||||
        // 序列化对象为JSON
 | 
			
		||||
        Gson gson = new Gson();
 | 
			
		||||
        String jsonData = gson.toJson(userInfoModel); // 自动生成标准JSON
 | 
			
		||||
        //String jsonData = userInfoModel.toString();
 | 
			
		||||
        LogUtils.d(TAG, "要发送的数据 : " + jsonData);
 | 
			
		||||
 | 
			
		||||
        sendPostRequest(apiUrl, jsonData, new OnResponseListener() {
 | 
			
		||||
                // 成功回调(主线程)
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onSuccess(String responseBody) {
 | 
			
		||||
                    LogUtils.d(TAG, "onSuccess");
 | 
			
		||||
                    LogUtils.d(TAG, String.format("responseBody %s", responseBody));
 | 
			
		||||
                    Gson gson = new Gson();
 | 
			
		||||
                    ResponseData result = gson.fromJson(responseBody, ResponseData.class); // 转为 Result 实例
 | 
			
		||||
                    if(result.getStatus().equals(ResponseData.STATUS_SUCCESS)) {
 | 
			
		||||
                        
 | 
			
		||||
                            UserInfoModel userInfoModel = result.getData();
 | 
			
		||||
                            if (userInfoModel != null) {
 | 
			
		||||
                                LogUtils.d(TAG, "收到网站 UserInfoModel");
 | 
			
		||||
                                String token = userInfoModel.getToken();
 | 
			
		||||
                                saveLocalToken(token);
 | 
			
		||||
                                checkLoginStatus();
 | 
			
		||||
                            }
 | 
			
		||||
                       
 | 
			
		||||
                    } else if(result.getStatus().equals(ResponseData.STATUS_ERROR)) {
 | 
			
		||||
                        try {
 | 
			
		||||
                            String decodedMessage = URLDecoder.decode(result.getMessage(), "UTF-8");
 | 
			
		||||
                            LogUtils.d(TAG, "服务器返回信息: " + decodedMessage);
 | 
			
		||||
                        } catch (UnsupportedEncodingException e) {
 | 
			
		||||
                            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 失败回调(主线程)
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onFailure(String errorMsg) {
 | 
			
		||||
                    LogUtils.d(TAG, errorMsg);
 | 
			
		||||
                    // 处理错误
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void saveLocalToken(String token) {
 | 
			
		||||
        UserInfoModel userInfoModel = new UserInfoModel();
 | 
			
		||||
        userInfoModel.setToken(token);
 | 
			
		||||
        saveUserInfoModel(userInfoModel);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    UserInfoModel loadUserInfoModel() {
 | 
			
		||||
        LogUtils.d(TAG, "loadUserInfoModel");
 | 
			
		||||
        if (new File(mUserInfoModelPath).exists()) {
 | 
			
		||||
            try {
 | 
			
		||||
                // 加载加密后的模型数据
 | 
			
		||||
                byte[] encryptedData = FileUtils.readFileToByteArray(mUserInfoModelPath);
 | 
			
		||||
                // 加载 RSA 工具
 | 
			
		||||
                RSAUtils utils = RSAUtils.getInstance(mContext);
 | 
			
		||||
                KeyPair keyPair = utils.getOrGenerateKeys();
 | 
			
		||||
                //PublicKey publicKey = keyPair.getPublic();
 | 
			
		||||
                PrivateKey privateKey = keyPair.getPrivate();
 | 
			
		||||
                // 私钥解密模型数据
 | 
			
		||||
                String szInfo = utils.decryptWithPrivateKey(encryptedData, keyPair.getPrivate());
 | 
			
		||||
                LogUtils.d(TAG, String.format("szInfo %s", szInfo));
 | 
			
		||||
                mUserInfoModel = UserInfoModel.parseStringToBean(szInfo, UserInfoModel.class);
 | 
			
		||||
                if (mUserInfoModel == null) {
 | 
			
		||||
                    LogUtils.d(TAG, "模型数据解析为空数据。");
 | 
			
		||||
                }
 | 
			
		||||
                LogUtils.d(TAG, "UserInfoModel 解密加载结束。");
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            LogUtils.d(TAG, "云服务登录信息不存在。");
 | 
			
		||||
            mUserInfoModel = null;
 | 
			
		||||
        }
 | 
			
		||||
        return mUserInfoModel;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void saveUserInfoModel(UserInfoModel userInfoModel) {
 | 
			
		||||
        LogUtils.d(TAG, "saveUserInfoModel");
 | 
			
		||||
        try {
 | 
			
		||||
            String szInfo = userInfoModel.toString();
 | 
			
		||||
            LogUtils.d(TAG, "原始数据: " + szInfo);
 | 
			
		||||
 | 
			
		||||
            RSAUtils utils = RSAUtils.getInstance(mContext);
 | 
			
		||||
            KeyPair keyPair = utils.getOrGenerateKeys();
 | 
			
		||||
            PublicKey publicKey = keyPair.getPublic();
 | 
			
		||||
 | 
			
		||||
            // 公钥加密(传入字节数组,避免中间字符串转换)
 | 
			
		||||
            byte[] encryptedData = utils.encryptWithPublicKey(szInfo, publicKey);
 | 
			
		||||
 | 
			
		||||
            // 保存加密字节数组到文件(直接操作字节,无需转字符串)
 | 
			
		||||
            FileUtils.writeByteArrayToFile(encryptedData, mUserInfoModelPath);
 | 
			
		||||
            LogUtils.d(TAG, "加密数据已保存");
 | 
			
		||||
 | 
			
		||||
            // 测试解密(仅调试用)
 | 
			
		||||
            String szInfo2 = utils.decryptWithPrivateKey(encryptedData, keyPair.getPrivate());
 | 
			
		||||
            LogUtils.d(TAG, "解密结果: " + szInfo2);
 | 
			
		||||
 | 
			
		||||
            mUserInfoModel = UserInfoModel.parseStringToBean(szInfo2, UserInfoModel.class);
 | 
			
		||||
            if (mUserInfoModel == null) {
 | 
			
		||||
                LogUtils.d(TAG, "模型解析失败");
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            LogUtils.d(TAG, "加密/解密失败: " + e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 发送 POST 请求(JSON 数据)
 | 
			
		||||
    public void sendPostRequest(String url, String data, OnResponseListener listener) {
 | 
			
		||||
        RequestBody requestBody = RequestBody.create(
 | 
			
		||||
            MediaType.parse("application/json; charset=utf-8"), // 关键头信息
 | 
			
		||||
            data.getBytes(StandardCharsets.UTF_8)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        Request request = new Request.Builder()
 | 
			
		||||
            .url(url)
 | 
			
		||||
            .post(requestBody)
 | 
			
		||||
            .addHeader("Content-Type", "application/json") // 显式添加头
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
        executeRequest(request, listener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 发送 GET 请求
 | 
			
		||||
    public void sendGetRequest(String url, OnResponseListener listener) {
 | 
			
		||||
        Request request = new Request.Builder()
 | 
			
		||||
            .url(url)
 | 
			
		||||
            .get()
 | 
			
		||||
            .build();
 | 
			
		||||
        executeRequest(request, listener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 执行请求(子线程处理)
 | 
			
		||||
    private void executeRequest(final Request request, final OnResponseListener listener) {
 | 
			
		||||
        okHttpClient.newCall(request).enqueue(new Callback() {
 | 
			
		||||
                // 响应成功(子线程)
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onResponse(Call call, Response response) throws IOException {
 | 
			
		||||
                    try {
 | 
			
		||||
                        if (!response.isSuccessful()) {
 | 
			
		||||
                            postFailure(listener, "响应码错误:" + response.code());
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                        String responseBody = response.body().string();
 | 
			
		||||
                        postSuccess(listener, responseBody);
 | 
			
		||||
                    } catch (Exception e) {
 | 
			
		||||
                        postFailure(listener, "解析失败:" + e.getMessage());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 响应失败(子线程)
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onFailure(Call call, IOException e) {
 | 
			
		||||
                    postFailure(listener, "网络失败:" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 主线程回调(使用 Handler)
 | 
			
		||||
                private void postSuccess(final OnResponseListener listener, final String msg) {
 | 
			
		||||
                    mainHandler.post(new Runnable() {
 | 
			
		||||
                            @Override
 | 
			
		||||
                            public void run() {
 | 
			
		||||
                                listener.onSuccess(msg);
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                private void postFailure(final OnResponseListener listener, final String msg) {
 | 
			
		||||
                    mainHandler.post(new Runnable() {
 | 
			
		||||
                            @Override
 | 
			
		||||
                            public void run() {
 | 
			
		||||
                                listener.onFailure(msg);
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public interface OnResponseListener {
 | 
			
		||||
        /**
 | 
			
		||||
         * 成功响应(主线程回调)
 | 
			
		||||
         * @param responseBody 响应体字符串
 | 
			
		||||
         */
 | 
			
		||||
        void onSuccess(String responseBody);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 失败回调(包含错误信息)
 | 
			
		||||
         * @param errorMsg 错误描述
 | 
			
		||||
         */
 | 
			
		||||
        void onFailure(String errorMsg);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,63 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.widgets;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.widgets;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @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));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.winboll;
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/05/10 09:34
 | 
			
		||||
 * @Describe WinBoLL 窗口操作接口
 | 
			
		||||
 */
 | 
			
		||||
public abstract interface IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "IWinBoLLActivity";
 | 
			
		||||
 | 
			
		||||
    public static final String ACTION_BIND = IWinBoLLActivity.class.getName() + ".ACTION_BIND";
 | 
			
		||||
 | 
			
		||||
    public Activity getActivity();
 | 
			
		||||
    public String getTag();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,98 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.winboll;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/25 04:29:19
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.app.Application;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.utils.ToastUtils; 
 | 
			
		||||
 | 
			
		||||
public class MyActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { 
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "MyActivityLifecycleCallbacks";
 | 
			
		||||
 | 
			
		||||
    public String mInfo = "";
 | 
			
		||||
 | 
			
		||||
    public MyActivityLifecycleCallbacks() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void createActivityeInfo(Activity activity) {
 | 
			
		||||
        StringBuilder sb = new StringBuilder();
 | 
			
		||||
        Intent receivedIntent = activity.getIntent();
 | 
			
		||||
        sb.append("\nCallingActivity : \n");
 | 
			
		||||
        if (activity.getCallingActivity() != null) {
 | 
			
		||||
            sb.append(activity.getCallingActivity().getPackageName());
 | 
			
		||||
        }
 | 
			
		||||
        sb.append("\nReceived Intent Package : \n");
 | 
			
		||||
        sb.append(receivedIntent.getPackage());
 | 
			
		||||
 | 
			
		||||
        Bundle extras = receivedIntent.getExtras();
 | 
			
		||||
        if (extras != null) {
 | 
			
		||||
            for (String key : extras.keySet()) {
 | 
			
		||||
                sb.append("\nIntentInfo");
 | 
			
		||||
                sb.append("\n键: ");
 | 
			
		||||
                sb.append(key);
 | 
			
		||||
                sb.append(", 值: ");
 | 
			
		||||
                sb.append(extras.get(key));
 | 
			
		||||
                //Log.d("IntentInfo", "键: " + key + ", 值: " + extras.get(key));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        mInfo = sb.toString();
 | 
			
		||||
        //Log.d("IntentInfo", "发送Intent的应用包名: " + senderPackage);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void showActivityeInfo() {
 | 
			
		||||
        //ToastUtils.show("ActivityeInfo : " + mInfo);
 | 
			
		||||
        LogUtils.d(TAG, "ActivityeInfo : " + mInfo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override 
 | 
			
		||||
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 
 | 
			
		||||
        // 在这里可以做一些初始化相关的操作,例如记录Activity的创建时间等 
 | 
			
		||||
        //System.out.println(activity.getLocalClassName() + " was created"); 
 | 
			
		||||
        LogUtils.d(TAG, activity.getLocalClassName() + " was created");
 | 
			
		||||
        createActivityeInfo(activity);
 | 
			
		||||
    } 
 | 
			
		||||
 | 
			
		||||
    @Override 
 | 
			
		||||
    public void onActivityStarted(Activity activity) { 
 | 
			
		||||
        //System.out.println(activity.getLocalClassName() + " was started");
 | 
			
		||||
        LogUtils.d(TAG, activity.getLocalClassName() + " was started");
 | 
			
		||||
        //createActivityeInfo(activity);
 | 
			
		||||
    } 
 | 
			
		||||
 | 
			
		||||
    @Override 
 | 
			
		||||
    public void onActivityResumed(Activity activity) { 
 | 
			
		||||
        //System.out.println(activity.getLocalClassName() + " was resumed");
 | 
			
		||||
        LogUtils.d(TAG, activity.getLocalClassName() + " was resumed");
 | 
			
		||||
        //createActivityeInfo(activity);
 | 
			
		||||
    } 
 | 
			
		||||
 | 
			
		||||
    @Override 
 | 
			
		||||
    public void onActivityPaused(Activity activity) { 
 | 
			
		||||
        //System.out.println(activity.getLocalClassName() + " was paused");
 | 
			
		||||
        LogUtils.d(TAG, activity.getLocalClassName() + " was paused");
 | 
			
		||||
    } 
 | 
			
		||||
 | 
			
		||||
    @Override 
 | 
			
		||||
    public void onActivityStopped(Activity activity) { 
 | 
			
		||||
        //System.out.println(activity.getLocalClassName() + " was stopped");
 | 
			
		||||
        LogUtils.d(TAG, activity.getLocalClassName() + " was stopped");
 | 
			
		||||
    } 
 | 
			
		||||
 | 
			
		||||
    @Override 
 | 
			
		||||
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 
 | 
			
		||||
        // 可以在这里添加保存状态的自定义逻辑 
 | 
			
		||||
    } 
 | 
			
		||||
 | 
			
		||||
    @Override 
 | 
			
		||||
    public void onActivityDestroyed(Activity activity) { 
 | 
			
		||||
        //System.out.println(activity.getLocalClassName() + " was destroyed");
 | 
			
		||||
        LogUtils.d(TAG, activity.getLocalClassName() + " was destroyed");
 | 
			
		||||
    } 
 | 
			
		||||
}
 | 
			
		||||
@@ -1,40 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.winboll;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/05/10 10:13
 | 
			
		||||
 * @Describe WinBoLL 系列应用通用管理类
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.models.WinBoLLModel;
 | 
			
		||||
 | 
			
		||||
public class WinBoLL {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "WinBoLL";
 | 
			
		||||
 | 
			
		||||
    public static final String ACTION_BIND = WinBoLL.class.getName() + ".ACTION_BIND";
 | 
			
		||||
    public static final String EXTRA_WINBOLLMODEL = "EXTRA_WINBOLLMODEL";
 | 
			
		||||
 | 
			
		||||
    public static void bindToAPPBase(Context context, String appMainService) {
 | 
			
		||||
        LogUtils.d(TAG, "bindToAPPBase(...)");
 | 
			
		||||
        String toPackage = "cc.winboll.studio.appbase";
 | 
			
		||||
        startBind(context, toPackage, appMainService);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void bindToAPPBaseBeta(Context context, String appMainService) {
 | 
			
		||||
        LogUtils.d(TAG, "bindToAPPBaseBeta(...)");
 | 
			
		||||
        String toPackage = "cc.winboll.studio.appbase.beta";
 | 
			
		||||
        startBind(context, toPackage, appMainService);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void startBind(Context context, String toPackage, String appMainService) {
 | 
			
		||||
        Intent intent = new Intent(ACTION_BIND);
 | 
			
		||||
        intent.putExtra(EXTRA_WINBOLLMODEL, (new WinBoLLModel(toPackage, appMainService)).toString());
 | 
			
		||||
        intent.setPackage(toPackage);
 | 
			
		||||
        LogUtils.d(TAG, String.format("ACTION_BIND :\nTo Package : %s\nAPP Main Service : %s", toPackage, appMainService));
 | 
			
		||||
        context.sendBroadcast(intent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,287 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.libappbase.winboll;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/05/10 10:02
 | 
			
		||||
 * @Describe 应用活动窗口管理器
 | 
			
		||||
 * 参考 :
 | 
			
		||||
 * android 类似微信小程序多任务窗口 及 设置 TaskDescription 修改 icon 和 label
 | 
			
		||||
 * https://blog.csdn.net/qq_29364417/article/details/109379915?app_version=6.4.2&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22109379915%22%2C%22source%22%3A%22weixin_38986226%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.app.ActivityManager;
 | 
			
		||||
import android.app.TaskStackBuilder;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
public class WinBoLLActivityManager {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "WinBoLLActivityManager";
 | 
			
		||||
 | 
			
		||||
    public static final String EXTRA_TAG = "EXTRA_TAG";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public enum WinBoLLUI_TYPE { Aplication, Service }
 | 
			
		||||
 | 
			
		||||
    Context mContext;
 | 
			
		||||
    volatile static WinBoLLActivityManager _mIWinBoLLActivityManager;
 | 
			
		||||
    Map<String, IWinBoLLActivity> mActivityListMap;
 | 
			
		||||
 | 
			
		||||
    volatile static WinBoLLUI_TYPE _WinBoLLUI_TYPE = WinBoLLUI_TYPE.Service;
 | 
			
		||||
    public static void setWinBoLLUI_TYPE(WinBoLLUI_TYPE winBoLLUI_TYPE) {
 | 
			
		||||
        _WinBoLLUI_TYPE = winBoLLUI_TYPE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static WinBoLLUI_TYPE getWinBoLLUI_TYPE() {
 | 
			
		||||
        return _WinBoLLUI_TYPE;
 | 
			
		||||
    }
 | 
			
		||||
    WinBoLLActivityManager() {
 | 
			
		||||
        mContext = GlobalApplication.getInstance();
 | 
			
		||||
        mActivityListMap = new HashMap<String, IWinBoLLActivity>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static synchronized WinBoLLActivityManager getInstance() {
 | 
			
		||||
        if (_mIWinBoLLActivityManager == null) {
 | 
			
		||||
            _mIWinBoLLActivityManager = new WinBoLLActivityManager();
 | 
			
		||||
        }
 | 
			
		||||
        return _mIWinBoLLActivityManager;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 把Activity添加到管理中
 | 
			
		||||
     */
 | 
			
		||||
    public <T extends IWinBoLLActivity> void add(T activity) {
 | 
			
		||||
        if (isActivityActive(activity.getTag())) {
 | 
			
		||||
            LogUtils.d(TAG, String.format("add(...) %s is active.", activity.getTag()));
 | 
			
		||||
        } else {
 | 
			
		||||
            mActivityListMap.put(activity.getTag(), activity);
 | 
			
		||||
            LogUtils.d(TAG, String.format("Add activity : %s\n_mapActivityList.size() : %d", activity.getTag(), mActivityListMap.size()));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // activity: 为 null 时,
 | 
			
		||||
    // intent.putExtra 函数 "tag" 参数为 tag
 | 
			
		||||
    // activity: 不为 null 时,
 | 
			
		||||
    // intent.putExtra 函数 "tag" 参数为 activity.getTag()
 | 
			
		||||
    //
 | 
			
		||||
    public <T extends IWinBoLLActivity> void startWinBoLLActivity(Context context, Class<T> clazz) {
 | 
			
		||||
        // 如果窗口已存在就重启窗口
 | 
			
		||||
        if (!resumeActivity(clazz)) {
 | 
			
		||||
            // 新建一个任务窗口
 | 
			
		||||
            Intent intent = new Intent(context, clazz);
 | 
			
		||||
            //打开多任务窗口 flags
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
 | 
			
		||||
            //intent.putExtra("tag", tag);
 | 
			
		||||
            context.startActivity(intent);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public <T extends IWinBoLLActivity> void startWinBoLLActivity(Context context, Intent intent, Class<T> clazz) {
 | 
			
		||||
        // 如果窗口已存在就重启窗口
 | 
			
		||||
        if (!resumeActivity(clazz)) {
 | 
			
		||||
            // 新建一个任务窗口
 | 
			
		||||
            //Intent intent = new Intent(context, clazz);
 | 
			
		||||
            //打开多任务窗口 flags
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
 | 
			
		||||
            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
 | 
			
		||||
            //intent.putExtra("tag", tag);
 | 
			
		||||
            context.startActivity(intent);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public <T extends IWinBoLLActivity> void startLogActivity(Context context) {
 | 
			
		||||
        // 如果窗口已存在就重启窗口
 | 
			
		||||
        //if (!resumeActivity(LogActivity.class)) {
 | 
			
		||||
        // 新建一个任务窗口
 | 
			
		||||
        Intent intent = new Intent(context, LogActivity.class);
 | 
			
		||||
        //打开多任务窗口 flags
 | 
			
		||||
        // Define the bounds.
 | 
			
		||||
//        Rect bounds = new Rect(0, 0, 800, 200);
 | 
			
		||||
//        // Set the bounds as an activity option.
 | 
			
		||||
//        ActivityOptions options = ActivityOptions.makeBasic();
 | 
			
		||||
//        options.setLaunchBounds(bounds);
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
 | 
			
		||||
 | 
			
		||||
        //intent.putExtra(EXTRA_TAG, tag);
 | 
			
		||||
 | 
			
		||||
        //context.startActivity(intent, options.toBundle());
 | 
			
		||||
        context.startActivity(intent);
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 判断 tag 绑定的 Activity 是否已经创建
 | 
			
		||||
    //
 | 
			
		||||
    public boolean isActivityActive(String tag) {
 | 
			
		||||
        return mActivityListMap.get(tag) != null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Activity getActivityByTag(String tag) {
 | 
			
		||||
        return (mActivityListMap.get(tag) == null) ?null: mActivityListMap.get(tag).getActivity();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
 | 
			
		||||
    //
 | 
			
		||||
    public <T extends IWinBoLLActivity> boolean resumeActivity(Class<T> clazz) {
 | 
			
		||||
        try {
 | 
			
		||||
            Activity activity = getActivityByTag(clazz.newInstance().getTag());
 | 
			
		||||
            if (activity != null) {
 | 
			
		||||
                return resumeActivity(activity);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (InstantiationException | IllegalAccessException e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
 | 
			
		||||
    //
 | 
			
		||||
    public <T extends IWinBoLLActivity> boolean resumeActivity(String tag) {
 | 
			
		||||
        Activity activity = getActivityByTag(tag);
 | 
			
		||||
        if (activity != null) {
 | 
			
		||||
            return resumeActivity(activity);
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
 | 
			
		||||
    //
 | 
			
		||||
    public <T extends IWinBoLLActivity> boolean resumeActivity(Activity activity) {
 | 
			
		||||
        ActivityManager am = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
 | 
			
		||||
        //返回启动它的根任务(home 或者 MainActivity)
 | 
			
		||||
        //Intent intent = new Intent(mContext, activity.getClass());
 | 
			
		||||
        //TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
 | 
			
		||||
        //stackBuilder.addNextIntentWithParentStack(intent);
 | 
			
		||||
        //stackBuilder.startActivities();
 | 
			
		||||
        am.moveTaskToFront(activity.getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION);
 | 
			
		||||
        //ToastUtils.show("resumeActivity");
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 结束所有 Activity
 | 
			
		||||
     */
 | 
			
		||||
    public void finishAll() {
 | 
			
		||||
        try {
 | 
			
		||||
            //ToastUtils.show(String.format("finishAll() size : %d", _mIWinBoLLActivityList.size()));
 | 
			
		||||
            for (int i = mActivityListMap.size() - 1; i > -1; i--) {
 | 
			
		||||
                IWinBoLLActivity iWinBoLLActivity = mActivityListMap.get(i);
 | 
			
		||||
                ToastUtils.show("finishAll() activity");
 | 
			
		||||
                if (iWinBoLLActivity != null && iWinBoLLActivity.getActivity() != null && !iWinBoLLActivity.getActivity().isFinishing() && !iWinBoLLActivity.getActivity().isDestroyed()) {
 | 
			
		||||
                    //ToastUtils.show("activity != null ...");
 | 
			
		||||
                    if (GlobalApplication.getWinBoLLActivityManager().getWinBoLLUI_TYPE() == WinBoLLUI_TYPE.Service) {
 | 
			
		||||
                        // 结束窗口和最近任务栏, 建议前台服务类应用使用,可以方便用户再次调用 UI 操作。
 | 
			
		||||
                        iWinBoLLActivity.getActivity().finishAndRemoveTask();
 | 
			
		||||
                        //ToastUtils.show("finishAll() activity.finishAndRemoveTask();");
 | 
			
		||||
                    } else if (GlobalApplication.getWinBoLLActivityManager().getWinBoLLUI_TYPE() == WinBoLLUI_TYPE.Aplication) {
 | 
			
		||||
                        // 结束窗口保留最近任务栏,建议前台服务类应用使用,可以保持应用的系统自觉性。
 | 
			
		||||
                        iWinBoLLActivity.getActivity().finish();
 | 
			
		||||
                        //ToastUtils.show("finishAll() activity.finish();");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        ToastUtils.show("WinBollApplication.WinBollUI_TYPE error.");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 结束指定Activity
 | 
			
		||||
     */
 | 
			
		||||
    public <T extends IWinBoLLActivity> void finish(T iWinBoLLActivity) {
 | 
			
		||||
        try {
 | 
			
		||||
            if (iWinBoLLActivity != null && iWinBoLLActivity.getActivity() != null && !iWinBoLLActivity.getActivity().isFinishing() && !iWinBoLLActivity.getActivity().isDestroyed()) {
 | 
			
		||||
                //根据tag 移除 MyActivity
 | 
			
		||||
                //String tag= activity.getTag();
 | 
			
		||||
                //_mIWinBoLLActivityList.remove(tag);
 | 
			
		||||
                //ToastUtils.show("remove");
 | 
			
		||||
                //ToastUtils.show("_mIWinBoLLActivityArrayMap.size() " + Integer.toString(_mIWinBoLLActivityArrayMap.size()));
 | 
			
		||||
 | 
			
		||||
                // 窗口回调规则:
 | 
			
		||||
                // [] 当前窗口位置 >> 调度出的窗口位置
 | 
			
		||||
                // ★:[0] 1 2 3 4 >> 1
 | 
			
		||||
                // ★:0 1 [2] 3 4 >> 1
 | 
			
		||||
                // ★:0 1 2 [3] 4 >> 2
 | 
			
		||||
                // ★:0 1 2 3 [4] >> 3
 | 
			
		||||
                // ★:[0] >> 直接关闭当前窗口
 | 
			
		||||
                Activity preActivity = getPreActivity(iWinBoLLActivity);
 | 
			
		||||
                iWinBoLLActivity.getActivity().finish();
 | 
			
		||||
                if (preActivity != null) {
 | 
			
		||||
                    resumeActivity(preActivity);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Activity getPreActivity(IWinBoLLActivity iWinBoLLActivity) {
 | 
			
		||||
        try {
 | 
			
		||||
            boolean bingo = false;
 | 
			
		||||
            IWinBoLLActivity preIWinBoLLActivity = null;
 | 
			
		||||
            for (Map.Entry<String, IWinBoLLActivity> entity : mActivityListMap.entrySet()) {
 | 
			
		||||
                if (entity.getKey().equals(iWinBoLLActivity.getTag())) {
 | 
			
		||||
                    bingo = true;
 | 
			
		||||
                    LogUtils.d(TAG, "bingo");
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                preIWinBoLLActivity = entity.getValue();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (bingo) {
 | 
			
		||||
                return preIWinBoLLActivity.getActivity();
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public <T extends IWinBoLLActivity> boolean registeRemove(T iWinBoLLActivity) {
 | 
			
		||||
        IWinBoLLActivity iWinBoLLActivityTest = mActivityListMap.get(iWinBoLLActivity.getTag());
 | 
			
		||||
        if (iWinBoLLActivityTest != null) {
 | 
			
		||||
            mActivityListMap.remove(iWinBoLLActivity.getTag());
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void printAvtivityListInfo() {
 | 
			
		||||
        if (!mActivityListMap.isEmpty()) {
 | 
			
		||||
            StringBuilder sb = new StringBuilder("Map entries : " + Integer.toString(mActivityListMap.size()));
 | 
			
		||||
            Iterator<Map.Entry<String, IWinBoLLActivity>> iterator = mActivityListMap.entrySet().iterator();
 | 
			
		||||
            while (iterator.hasNext()) {
 | 
			
		||||
                Map.Entry<String, IWinBoLLActivity> entry = iterator.next();
 | 
			
		||||
                sb.append("\nKey: " + entry.getKey() + ", \nValue: " + entry.getValue().getTag());
 | 
			
		||||
                //ToastUtils.show("\nKey: " + entry.getKey() + ", Value: " + entry.getValue().getTag());
 | 
			
		||||
            }
 | 
			
		||||
            sb.append("\nMap entries end.");
 | 
			
		||||
            LogUtils.d(TAG, sb.toString());
 | 
			
		||||
        } else {
 | 
			
		||||
            LogUtils.d(TAG, "The map is empty.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?> 
 | 
			
		||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > 
 | 
			
		||||
    <!-- 阴影部分 --> 
 | 
			
		||||
    <!-- 个人觉得更形象的表达:top代表下边的阴影高度,left代表右边的阴影宽度。其实也就是相对应的offset,solid中的颜色是阴影的颜色,也可以设置角度等等 --> 
 | 
			
		||||
    <item 
 | 
			
		||||
        android:left="2dp" 
 | 
			
		||||
        android:top="2dp" 
 | 
			
		||||
        android:right="2dp" 
 | 
			
		||||
        android:bottom="2dp"> 
 | 
			
		||||
        <shape android:shape="rectangle" > 
 | 
			
		||||
            <gradient 
 | 
			
		||||
                android:angle="270" 
 | 
			
		||||
                android:endColor="#0F000000" 
 | 
			
		||||
                android:startColor="#0F000000" /> 
 | 
			
		||||
            <corners 
 | 
			
		||||
                android:bottomLeftRadius="6dip" 
 | 
			
		||||
                android:bottomRightRadius="6dip" 
 | 
			
		||||
                android:topLeftRadius="6dip" 
 | 
			
		||||
                android:topRightRadius="6dip" /> 
 | 
			
		||||
        </shape> 
 | 
			
		||||
    </item> 
 | 
			
		||||
    <!-- 背景部分 --> 
 | 
			
		||||
    <!-- 形象的表达:bottom代表背景部分在上边缘超出阴影的高度,right代表背景部分在左边超出阴影的宽度(相对应的offset) --> 
 | 
			
		||||
    <item 
 | 
			
		||||
        android:left="3dp" 
 | 
			
		||||
        android:top="3dp" 
 | 
			
		||||
        android:right="3dp" 
 | 
			
		||||
        android:bottom="5dp"> 
 | 
			
		||||
        <shape android:shape="rectangle" > 
 | 
			
		||||
            <gradient 
 | 
			
		||||
                android:angle="270" 
 | 
			
		||||
                android:endColor="@color/colorAccent" 
 | 
			
		||||
                android:startColor="@color/colorAccent" /> 
 | 
			
		||||
            <corners 
 | 
			
		||||
                android:bottomLeftRadius="6dip" 
 | 
			
		||||
                android:bottomRightRadius="6dip" 
 | 
			
		||||
                android:topLeftRadius="6dip" 
 | 
			
		||||
                android:topRightRadius="6dip" /> 
 | 
			
		||||
        </shape> 
 | 
			
		||||
    </item> 
 | 
			
		||||
</layer-list>
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportHeight="24"
 | 
			
		||||
    android:viewportWidth="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#ff000000"
 | 
			
		||||
        android:pathData="M4,1C2.89,1 2,1.89 2,3V7C2,8.11 2.89,9 4,9H1V11H13V9H10C11.11,9 12,8.11 12,7V3C12,1.89 11.11,1 10,1H4M4,3H10V7H4V3M3,12V14H5V12H3M14,13C12.89,13 12,13.89 12,15V19C12,20.11 12.89,21 14,21H11V23H23V21H20C21.11,21 22,20.11 22,19V15C22,13.89 21.11,13 20,13H14M3,15V17H5V15H3M14,15H20V19H14V15M3,18V20H5V18H3M6,18V20H8V18H6M9,18V20H11V18H9Z"/>
 | 
			
		||||
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportHeight="24"
 | 
			
		||||
    android:viewportWidth="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#ff000000"
 | 
			
		||||
        android:pathData="M4,1C2.89,1 2,1.89 2,3V7C2,8.11 2.89,9 4,9H1V11H13V9H10C11.11,9 12,8.11 12,7V3C12,1.89 11.11,1 10,1H4M4,3H10V7H4V3M14,13C12.89,13 12,13.89 12,15V19C12,20.11 12.89,21 14,21H11V23H23V21H20C21.11,21 22,20.11 22,19V15C22,13.89 21.11,13 20,13H14M3.88,13.46L2.46,14.88L4.59,17L2.46,19.12L3.88,20.54L6,18.41L8.12,20.54L9.54,19.12L7.41,17L9.54,14.88L8.12,13.46L6,15.59L3.88,13.46M14,15H20V19H14V15Z"/>
 | 
			
		||||
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportHeight="24"
 | 
			
		||||
    android:viewportWidth="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#ff000000"
 | 
			
		||||
        android:pathData="M22,6C22,4.9 21.1,4 20,4H4C2.9,4 2,4.9 2,6V18C2,19.1 2.9,20 4,20H20C21.1,20 22,19.1 22,18V6M20,6L12,11L4,6H20M20,18H4V8L12,13L20,8V18Z"/>
 | 
			
		||||
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportHeight="24"
 | 
			
		||||
    android:viewportWidth="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#ff000000"
 | 
			
		||||
        android:pathData="M24,7H22V13H24V7M24,15H22V17H24V15M20,6C20,4.9 19.1,4 18,4H2C0.9,4 0,4.9 0,6V18C0,19.1 0.9,20 2,20H18C19.1,20 20,19.1 20,18V6M18,6L10,11L2,6H18M18,18H2V8L10,13L18,8V18Z"/>
 | 
			
		||||
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:clickable="true">
 | 
			
		||||
    <item
 | 
			
		||||
        android:width="256dp"
 | 
			
		||||
        android:height="256dp"
 | 
			
		||||
        android:left="0dp" 
 | 
			
		||||
        android:top="0dp" 
 | 
			
		||||
        android:right="0dp" 
 | 
			
		||||
        android:bottom="0dp"
 | 
			
		||||
        android:drawable="@drawable/ic_winboll_logo">
 | 
			
		||||
    </item>
 | 
			
		||||
</layer-list>
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:clickable="true"
 | 
			
		||||
    android:layout_width="24dp"
 | 
			
		||||
	android:layout_height="24dp">
 | 
			
		||||
    <item android:drawable="@drawable/ic_launcher_background"/> 
 | 
			
		||||
    <item 
 | 
			
		||||
        android:left="0dp" 
 | 
			
		||||
        android:top="0dp" 
 | 
			
		||||
        android:right="0dp" 
 | 
			
		||||
        android:bottom="0dp"
 | 
			
		||||
        android:drawable="@drawable/ic_launcher_foreground_disable"/>
 | 
			
		||||
</layer-list>
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportHeight="24"
 | 
			
		||||
    android:viewportWidth="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FFFFFFFF"
 | 
			
		||||
        android:pathData="M16.61,15.15C16.15,15.15 15.77,14.78 15.77,14.32S16.15,13.5 16.61,13.5H16.61C17.07,13.5 17.45,13.86 17.45,14.32C17.45,14.78 17.07,15.15 16.61,15.15M7.41,15.15C6.95,15.15 6.57,14.78 6.57,14.32C6.57,13.86 6.95,13.5 7.41,13.5H7.41C7.87,13.5 8.24,13.86 8.24,14.32C8.24,14.78 7.87,15.15 7.41,15.15M16.91,10.14L18.58,7.26C18.67,7.09 18.61,6.88 18.45,6.79C18.28,6.69 18.07,6.75 18,6.92L16.29,9.83C14.95,9.22 13.5,8.9 12,8.91C10.47,8.91 9,9.24 7.73,9.82L6.04,6.91C5.95,6.74 5.74,6.68 5.57,6.78C5.4,6.87 5.35,7.08 5.44,7.25L7.1,10.13C4.25,11.69 2.29,14.58 2,18H22C21.72,14.59 19.77,11.7 16.91,10.14H16.91Z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportHeight="24"
 | 
			
		||||
    android:viewportWidth="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF808080"
 | 
			
		||||
        android:pathData="M16.61,15.15C16.15,15.15 15.77,14.78 15.77,14.32S16.15,13.5 16.61,13.5H16.61C17.07,13.5 17.45,13.86 17.45,14.32C17.45,14.78 17.07,15.15 16.61,15.15M7.41,15.15C6.95,15.15 6.57,14.78 6.57,14.32C6.57,13.86 6.95,13.5 7.41,13.5H7.41C7.87,13.5 8.24,13.86 8.24,14.32C8.24,14.78 7.87,15.15 7.41,15.15M16.91,10.14L18.58,7.26C18.67,7.09 18.61,6.88 18.45,6.79C18.28,6.69 18.07,6.75 18,6.92L16.29,9.83C14.95,9.22 13.5,8.9 12,8.91C10.47,8.91 9,9.24 7.73,9.82L6.04,6.91C5.95,6.74 5.74,6.68 5.57,6.78C5.4,6.87 5.35,7.08 5.44,7.25L7.1,10.13C4.25,11.69 2.29,14.58 2,18H22C21.72,14.59 19.77,11.7 16.91,10.14H16.91Z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 21 KiB  | 
@@ -1,27 +0,0 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="512dp"
 | 
			
		||||
    android:height="512dp"
 | 
			
		||||
    android:viewportWidth="512"
 | 
			
		||||
    android:viewportHeight="512">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF1E9B54"
 | 
			
		||||
        android:strokeColor="#FFF8E733"
 | 
			
		||||
        android:strokeWidth="20.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M254.63 35.45C374.95 35.45 473.38 133.89 473.38 254.2 473.38 374.51 374.95 472.95 254.63 472.95 134.32 472.95 35.88 374.51 35.88 254.2 35.88 133.89 134.32 35.45 254.63 35.45"/>
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF000000"
 | 
			
		||||
        android:strokeColor="#FF000000"
 | 
			
		||||
        android:strokeWidth="1.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M257.28 361.25C266.56 361.25 274.14 368.84 274.14 378.11 274.14 387.39 266.56 394.98 257.28 394.98 248.01 394.98 240.42 387.39 240.42 378.11 240.42 368.84 248.01 361.25 257.28 361.25"/>
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:strokeColor="#FF000000"
 | 
			
		||||
        android:strokeWidth="30.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M182.16 214.09C181.42 199.71 182.42 177.87 207.64 155.49 213.64 150.16 220.13 146.12 226.28 143.08 238.64 136.97 249.62 134.91 252.55 134.56 252.7 134.54 252.83 134.53 252.94 134.52 253.05 134.51 253.14 134.5 253.2 134.5 255.01 134.48 294.9 136.66 313.05 160.43 332.29 185.63 344.82 221.3 300.07 263.56 263.08 298.49 258.36 318 258.54 317.72"/>
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="512dp"
 | 
			
		||||
    android:height="512dp"
 | 
			
		||||
    android:viewportWidth="512"
 | 
			
		||||
    android:viewportHeight="512">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF1E9B54"
 | 
			
		||||
        android:strokeColor="#FFF8E733"
 | 
			
		||||
        android:strokeWidth="20.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M254.63 35.45C374.95 35.45 473.38 133.89 473.38 254.2 473.38 374.51 374.95 472.95 254.63 472.95 134.32 472.95 35.88 374.51 35.88 254.2 35.88 133.89 134.32 35.45 254.63 35.45"/>
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FFFFFFFF"
 | 
			
		||||
        android:strokeColor="#FF000000"
 | 
			
		||||
        android:strokeWidth="20.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M151.49 130.96C151.49 130.96 348.53 130.96 348.53 130.96 348.53 130.96 348.53 393.46 348.53 393.46 348.53 393.46 151.49 393.46 151.49 393.46 151.49 393.46 151.49 130.96 151.49 130.96"/>
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FFFFFFFF"
 | 
			
		||||
        android:strokeColor="#FF000000"
 | 
			
		||||
        android:strokeWidth="20.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M186.28 207.75C186.28 207.75 304.95 207.75 304.95 207.75 304.95 207.75 304.95 205.97 304.95 205.97 304.95 205.97 186.28 205.97 186.28 205.97 186.28 205.97 186.28 207.75 186.28 207.75"/>
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FFFFFFFF"
 | 
			
		||||
        android:strokeColor="#FF000000"
 | 
			
		||||
        android:strokeWidth="20.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M186.28 263.52C186.28 263.52 304.95 263.52 304.95 263.52 304.95 263.52 304.95 264.41 304.95 264.41 304.95 264.41 186.28 264.41 186.28 264.41 186.28 264.41 186.28 263.52 186.28 263.52"/>
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FFFFFFFF"
 | 
			
		||||
        android:strokeColor="#FF000000"
 | 
			
		||||
        android:strokeWidth="20.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M186.28 323.62C186.28 323.62 304.95 323.62 304.95 323.62 304.95 323.62 304.95 320.62 304.95 320.62 304.95 320.62 186.28 320.62 186.28 320.62 186.28 320.62 186.28 323.62 186.28 323.62"/>
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="512dp"
 | 
			
		||||
    android:height="512dp"
 | 
			
		||||
    android:viewportWidth="512"
 | 
			
		||||
    android:viewportHeight="512">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF1E9B54"
 | 
			
		||||
        android:strokeColor="#FFF8E733"
 | 
			
		||||
        android:strokeWidth="20.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M254.63 35.45C374.95 35.45 473.38 133.89 473.38 254.2 473.38 374.51 374.95 472.95 254.63 472.95 134.32 472.95 35.88 374.51 35.88 254.2 35.88 133.89 134.32 35.45 254.63 35.45"/>
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FFFFFFFF"
 | 
			
		||||
        android:strokeColor="#FFFFFFFF"
 | 
			
		||||
        android:strokeWidth="1.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M257.28 361.25C266.56 361.25 274.14 368.84 274.14 378.11 274.14 387.39 266.56 394.98 257.28 394.98 248.01 394.98 240.42 387.39 240.42 378.11 240.42 368.84 248.01 361.25 257.28 361.25"/>
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <gradient
 | 
			
		||||
        android:angle="180"
 | 
			
		||||
        android:endColor="#FFFFFFFF"
 | 
			
		||||
        android:startColor="#FFFFFFFF"
 | 
			
		||||
        android:type="linear" />
 | 
			
		||||
 | 
			
		||||
    <corners android:radius="10dp" />
 | 
			
		||||
</shape>
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:shape="rectangle">
 | 
			
		||||
    <stroke
 | 
			
		||||
        android:width="1dp"
 | 
			
		||||
        android:color="#000000" /> <!-- 这里可调整边框宽度和颜色 -->
 | 
			
		||||
    <solid android:color="@android:color/transparent" />
 | 
			
		||||
</shape>
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent">
 | 
			
		||||
 | 
			
		||||
    <LinearLayout
 | 
			
		||||
        android:orientation="horizontal"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_gravity="right"
 | 
			
		||||
        android:gravity="right"
 | 
			
		||||
        android:padding="10dp"
 | 
			
		||||
        android:id="@+id/ll_hostbar">
 | 
			
		||||
 | 
			
		||||
        <RadioButton
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:text="10.8.0.250:456"
 | 
			
		||||
            android:id="@+id/rb_debughost"
 | 
			
		||||
            android:onClick="onSwitchHost"/>
 | 
			
		||||
 | 
			
		||||
        <RadioButton
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:text="yun.winboll.cc"
 | 
			
		||||
            android:id="@+id/rb_yunhost"
 | 
			
		||||
            android:onClick="onSwitchHost"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
    
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:gravity="right">
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Test RSA"
 | 
			
		||||
            android:onClick="onTestRSA"/>
 | 
			
		||||
 | 
			
		||||
        <Button
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:text="Test Login"
 | 
			
		||||
            android:onClick="onTestLogin"/>
 | 
			
		||||
        
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0">
 | 
			
		||||
 | 
			
		||||
		<cc.winboll.studio.libappbase.LogView
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:id="@+id/logview"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -1,63 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent">
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:layout_gravity="right"
 | 
			
		||||
		android:gravity="right"
 | 
			
		||||
		android:padding="10dp"
 | 
			
		||||
		android:id="@+id/ll_hostbar">
 | 
			
		||||
 | 
			
		||||
		<RadioButton
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="10.8.0.250:456"
 | 
			
		||||
			android:id="@+id/rb_debughost"
 | 
			
		||||
			android:onClick="onSwitchHost"/>
 | 
			
		||||
 | 
			
		||||
		<RadioButton
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="yun.winboll.cc"
 | 
			
		||||
			android:id="@+id/rb_yunhost"
 | 
			
		||||
			android:onClick="onSwitchHost"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:layout_gravity="right"
 | 
			
		||||
		android:gravity="right">
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="TestYun"
 | 
			
		||||
			android:onClick="onTestYun"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0">
 | 
			
		||||
 | 
			
		||||
		<cc.winboll.studio.libappbase.LogView
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:id="@+id/logview"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -99,7 +99,7 @@
 | 
			
		||||
			android:layout_weight="1.0"
 | 
			
		||||
			android:id="@+id/viewlogHorizontalScrollView1">
 | 
			
		||||
 | 
			
		||||
			<cc.winboll.studio.libappbase.views.HorizontalListView
 | 
			
		||||
			<cc.winboll.studio.libappbase.HorizontalListView
 | 
			
		||||
				android:layout_width="wrap_content"
 | 
			
		||||
				android:layout_height="match_parent"
 | 
			
		||||
				android:id="@+id/tags_listview"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
	android:layout_width="40dp"
 | 
			
		||||
	android:layout_height="40dp">
 | 
			
		||||
 | 
			
		||||
	<ImageView
 | 
			
		||||
		android:layout_width="40dp"
 | 
			
		||||
		android:layout_height="40dp"
 | 
			
		||||
		android:id="@+id/ivapp"/>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:minWidth="40dp"
 | 
			
		||||
    android:minHeight="40dp"
 | 
			
		||||
    android:updatePeriodMillis="1000"
 | 
			
		||||
    android:initialLayout="@layout/widget_status"
 | 
			
		||||
    android:resizeMode="none">
 | 
			
		||||
</appwidget-provider>
 | 
			
		||||
@@ -41,6 +41,17 @@ android {
 | 
			
		||||
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
	// 允许使用系统隐藏API
 | 
			
		||||
    lintOptions {
 | 
			
		||||
        checkReleaseBuilds false
 | 
			
		||||
        abortOnError false
 | 
			
		||||
    }
 | 
			
		||||
    // 针对PowerProfile的依赖配置
 | 
			
		||||
    dependenciesInfo {
 | 
			
		||||
        includeInApk = false
 | 
			
		||||
        includeInBundle = false
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Wed Sep 03 20:59:53 HKT 2025
 | 
			
		||||
stageCount=13
 | 
			
		||||
#Wed Oct 22 20:17:00 HKT 2025
 | 
			
		||||
stageCount=18
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.4
 | 
			
		||||
publishVersion=15.4.12
 | 
			
		||||
publishVersion=15.4.17
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.4.13
 | 
			
		||||
baseBetaVersion=15.4.18
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
<?xml version='1.0' encoding='utf-8'?>
 | 
			
		||||
<manifest
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:tools="http://schemas.android.com/tools"
 | 
			
		||||
    package="cc.winboll.studio.powerbell">
 | 
			
		||||
 | 
			
		||||
    <!-- 拍摄照片和视频 -->
 | 
			
		||||
@@ -24,10 +25,29 @@
 | 
			
		||||
    <!-- 显示通知 -->
 | 
			
		||||
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
 | 
			
		||||
 | 
			
		||||
    <!-- PACKAGE_USAGE_STATS -->
 | 
			
		||||
    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
 | 
			
		||||
 | 
			
		||||
    <!-- BATTERY_STATS -->
 | 
			
		||||
    <uses-permission android:name="android.permission.BATTERY_STATS"/>
 | 
			
		||||
 | 
			
		||||
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
 | 
			
		||||
 | 
			
		||||
    <uses-feature android:name="android.hardware.camera"/>
 | 
			
		||||
 | 
			
		||||
    <uses-feature android:name="android.hardware.camera.autofocus"/>
 | 
			
		||||
		
 | 
			
		||||
	<!-- 1. 基础应用信息读取权限(Android 11 及以下) -->
 | 
			
		||||
	<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
 | 
			
		||||
 | 
			
		||||
	<!-- 2. Android 11+ 应用列表读取权限(必须声明,否则无法获取全部应用) -->
 | 
			
		||||
	<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
 | 
			
		||||
		tools:ignore="QueryAllPackagesPermission" />
 | 
			
		||||
 | 
			
		||||
	<!-- 3. 可选:若需读取系统应用,添加此权限(部分机型需要) -->
 | 
			
		||||
	<uses-permission android:name="android.permission.ACCESS_PACKAGE_USAGE_STATS"
 | 
			
		||||
		tools:ignore="ProtectedPermissions" />
 | 
			
		||||
	
 | 
			
		||||
    <application
 | 
			
		||||
        android:name=".App"
 | 
			
		||||
        android:allowBackup="true"
 | 
			
		||||
@@ -123,6 +143,8 @@
 | 
			
		||||
 | 
			
		||||
        <activity android:name="cc.winboll.studio.powerbell.activities.PixelPickerActivity"/>
 | 
			
		||||
 | 
			
		||||
        <activity android:name="cc.winboll.studio.powerbell.activities.BatteryReportActivity"/>
 | 
			
		||||
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
@@ -17,7 +17,7 @@ import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.powerbell.MainActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.AboutActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.BatteryReporterActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.BatteryReportActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.ClearRecordActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.WinBoLLActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
 | 
			
		||||
@@ -159,9 +159,9 @@ public class MainActivity extends WinBoLLActivity {
 | 
			
		||||
        if (menuItemId == R.id.action_about) {
 | 
			
		||||
            Intent intent = new Intent(this, AboutActivity.class);
 | 
			
		||||
            startActivity(intent);
 | 
			
		||||
        } else if (menuItemId == R.id.action_battery_reporter) {
 | 
			
		||||
        } else if (menuItemId == R.id.action_battery_report) {
 | 
			
		||||
            Intent intent = new Intent();
 | 
			
		||||
            intent.setClass(this, BatteryReporterActivity.class);
 | 
			
		||||
            intent.setClass(this, BatteryReportActivity.class);
 | 
			
		||||
            startActivity(intent);
 | 
			
		||||
        } else if (menuItemId == R.id.action_clearrecord) {
 | 
			
		||||
            Intent intent = new Intent();
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,516 @@
 | 
			
		||||
package cc.winboll.studio.powerbell.activities;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/10/22 13:21
 | 
			
		||||
 * @Describe BatteryReportActivity
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.IntentFilter;
 | 
			
		||||
import android.content.pm.ApplicationInfo;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.provider.Settings;
 | 
			
		||||
import android.text.Editable;
 | 
			
		||||
import android.text.TextWatcher;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.EditText;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
import androidx.recyclerview.widget.LinearLayoutManager;
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView;
 | 
			
		||||
import cc.winboll.studio.powerbell.R;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Comparator;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
 | 
			
		||||
public class BatteryReportActivity extends Activity {
 | 
			
		||||
    public static final String TAG = "BatteryReportActivity";
 | 
			
		||||
 | 
			
		||||
    private RecyclerView rvBatteryReport;
 | 
			
		||||
    private BatteryReportAdapter adapter;
 | 
			
		||||
    private List<AppBatteryModel> dataList = new ArrayList<AppBatteryModel>(); 
 | 
			
		||||
    private List<AppBatteryModel> filteredList = new ArrayList<AppBatteryModel>(); 
 | 
			
		||||
    private BroadcastReceiver batteryReceiver;
 | 
			
		||||
    private int batteryCapacity = 5400; // 电池容量(mAh)
 | 
			
		||||
    private float lastBatteryPercent = 100.0f;
 | 
			
		||||
    private long lastCheckTime = System.currentTimeMillis();
 | 
			
		||||
    private EditText etSearch;
 | 
			
		||||
    private Map<String, Long> appRunTimeCache = new HashMap<String, Long>();
 | 
			
		||||
    private Map<String, String> packageToAppNameCache = new HashMap<String, String>();
 | 
			
		||||
    private PackageManager mPackageManager;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        setContentView(R.layout.activity_battery_report);
 | 
			
		||||
        mPackageManager = getPackageManager();
 | 
			
		||||
 | 
			
		||||
        // 权限检查(Java7 传统条件判断)
 | 
			
		||||
        if (!hasUsageStatsPermission(this)) {
 | 
			
		||||
            Toast.makeText(this, "请进入设置-应用-权限-特殊访问权限-使用情况访问权限,开启本应用的权限", Toast.LENGTH_LONG).show();
 | 
			
		||||
            startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        etSearch = (EditText) findViewById(R.id.et_search); 
 | 
			
		||||
        rvBatteryReport = (RecyclerView) findViewById(R.id.rv_battery_report); 
 | 
			
		||||
        rvBatteryReport.setLayoutManager(new LinearLayoutManager(this));
 | 
			
		||||
 | 
			
		||||
        // 初始化流程:新增“加载24小时累计耗电”步骤
 | 
			
		||||
        loadAllAppPackage();
 | 
			
		||||
        preCacheAllAppNames();
 | 
			
		||||
        appRunTimeCache = getAppRunTime();
 | 
			
		||||
        updateAppRunTimeToModel();
 | 
			
		||||
        calculateInitial24hTotalConsumption(); // 初始化时计算24小时累计耗电
 | 
			
		||||
        filteredList.addAll(dataList);
 | 
			
		||||
        adapter = new BatteryReportAdapter(this, filteredList, mPackageManager, packageToAppNameCache);
 | 
			
		||||
        rvBatteryReport.setAdapter(adapter);
 | 
			
		||||
 | 
			
		||||
        // 搜索监听(不变)
 | 
			
		||||
        etSearch.addTextChangedListener(new TextWatcher() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
 | 
			
		||||
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onTextChanged(CharSequence s, int start, int before, int count) {
 | 
			
		||||
					filterAppsByPackageAndName(s.toString());
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				@Override
 | 
			
		||||
				public void afterTextChanged(Editable s) {}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 电池广播:调用修改后的“单次耗电计算+累计累加”方法
 | 
			
		||||
        batteryReceiver = new BroadcastReceiver() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onReceive(Context context, Intent intent) {
 | 
			
		||||
                int level = intent.getIntExtra("level", 100);
 | 
			
		||||
                int scale = intent.getIntExtra("scale", 100);
 | 
			
		||||
                float currentPercent = (float) level / scale * 100;
 | 
			
		||||
                LogUtils.d(TAG, "电池百分比变化:" + lastBatteryPercent + " -> " + currentPercent);
 | 
			
		||||
 | 
			
		||||
                if (currentPercent < lastBatteryPercent) {
 | 
			
		||||
                    float dropPercent = lastBatteryPercent - currentPercent;
 | 
			
		||||
                    long duration = System.currentTimeMillis() - lastCheckTime;
 | 
			
		||||
                    LogUtils.d(TAG, "电池消耗:" + dropPercent + "%,时长:" + duration + "ms");
 | 
			
		||||
                    appRunTimeCache = getAppRunTime();
 | 
			
		||||
                    updateAppRunTimeToModel();
 | 
			
		||||
                    calculateSingleConsumptionAndAccumulate(dropPercent, appRunTimeCache); // 单次+累计逻辑
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                lastBatteryPercent = currentPercent;
 | 
			
		||||
                lastCheckTime = System.currentTimeMillis();
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onDestroy() {
 | 
			
		||||
        super.onDestroy();
 | 
			
		||||
        // Java7 显式非空判断
 | 
			
		||||
        if (batteryReceiver != null) {
 | 
			
		||||
            unregisterReceiver(batteryReceiver);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 加载所有应用(仅获取包名,初始化模型时单次耗电、累计耗电均设为0)
 | 
			
		||||
     */
 | 
			
		||||
    private void loadAllAppPackage() {
 | 
			
		||||
        List<ApplicationInfo> appList = mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA);
 | 
			
		||||
        dataList.clear();
 | 
			
		||||
 | 
			
		||||
        LogUtils.d(TAG, "开始加载应用包名列表,共找到" + appList.size() + "个应用");
 | 
			
		||||
 | 
			
		||||
        for (ApplicationInfo appInfo : appList) {
 | 
			
		||||
            String packageName = appInfo.packageName;
 | 
			
		||||
            // 初始化:单次耗电(consumption)=0,累计耗电(totalConsumption)=0,运行时长=0
 | 
			
		||||
            dataList.add(new AppBatteryModel(packageName, 0.0f, 0.0f, 0));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LogUtils.d(TAG, "应用包名列表加载完成,共添加" + dataList.size() + "个包名。");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 预缓存应用名称(逻辑不变)
 | 
			
		||||
     */
 | 
			
		||||
    private void preCacheAllAppNames() {
 | 
			
		||||
        packageToAppNameCache.clear();
 | 
			
		||||
        LogUtils.d(TAG, "开始预缓存包名-应用名称映射");
 | 
			
		||||
 | 
			
		||||
        for (AppBatteryModel model : dataList) {
 | 
			
		||||
            String packageName = model.getPackageName();
 | 
			
		||||
            String appName = getAppNameByPackage(packageName);
 | 
			
		||||
            packageToAppNameCache.put(packageName, appName);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LogUtils.d(TAG, "预缓存完成,共缓存" + packageToAppNameCache.size() + "个应用名称");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 通过包名获取应用名称(逻辑不变)
 | 
			
		||||
     */
 | 
			
		||||
    private String getAppNameByPackage(String packageName) {
 | 
			
		||||
        try {
 | 
			
		||||
            ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
 | 
			
		||||
            return mPackageManager.getApplicationLabel(appInfo).toString();
 | 
			
		||||
        } catch (PackageManager.NameNotFoundException e) {
 | 
			
		||||
            LogUtils.e(TAG, "包名" + packageName + "对应的应用未找到:" + e.getMessage());
 | 
			
		||||
            return packageName;
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            LogUtils.e(TAG, "查询应用名称失败(包名:" + packageName + "):" + e.getMessage());
 | 
			
		||||
            return packageName;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 更新运行时长到模型(逻辑不变)
 | 
			
		||||
     */
 | 
			
		||||
    private void updateAppRunTimeToModel() {
 | 
			
		||||
        int nCount = 0;
 | 
			
		||||
        for (AppBatteryModel model : dataList) {
 | 
			
		||||
            String packageName = model.getPackageName();
 | 
			
		||||
            Long runTime;
 | 
			
		||||
            if (appRunTimeCache.containsKey(packageName)) {
 | 
			
		||||
                runTime = appRunTimeCache.get(packageName);
 | 
			
		||||
                LogUtils.d(TAG, String.format("应用包 %s 运行时长已更新。", packageName));
 | 
			
		||||
                nCount++;
 | 
			
		||||
            } else {
 | 
			
		||||
                runTime = 0L;
 | 
			
		||||
            }
 | 
			
		||||
            model.setRunTime(runTime);
 | 
			
		||||
        }
 | 
			
		||||
        LogUtils.d(TAG, String.format("dataList.size() %d, appRunTimeCache.size() %d。", dataList.size(), appRunTimeCache.size()));
 | 
			
		||||
        LogUtils.d(TAG, String.format("updateAppRunTimeToModel() 更新的数据量为:%d", nCount));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 【新增】初始化时计算24小时累计耗电(赋值给totalConsumption)
 | 
			
		||||
     * 逻辑:基于24小时运行时长占比,分配当前电池容量的理论24小时消耗
 | 
			
		||||
     */
 | 
			
		||||
    private void calculateInitial24hTotalConsumption() {
 | 
			
		||||
        long total24hRunTime = 0;
 | 
			
		||||
        // 1. 计算24小时内所有应用总运行时长
 | 
			
		||||
        for (Map.Entry<String, Long> entry : appRunTimeCache.entrySet()) {
 | 
			
		||||
            total24hRunTime += entry.getValue();
 | 
			
		||||
        }
 | 
			
		||||
        LogUtils.d(TAG, "24小时内所有应用总运行时长:" + formatRunTime(total24hRunTime));
 | 
			
		||||
 | 
			
		||||
        // 2. 按运行时长占比分配24小时累计耗电(假设电池满电循环,用总容量近似24小时总消耗)
 | 
			
		||||
        for (AppBatteryModel model : dataList) {
 | 
			
		||||
            String packageName = model.getPackageName();
 | 
			
		||||
            Long app24hRunTime = appRunTimeCache.getOrDefault(packageName, 0L);
 | 
			
		||||
 | 
			
		||||
            // 计算占比与累计耗电
 | 
			
		||||
            float ratio = (total24hRunTime > 0) ? (float) app24hRunTime / total24hRunTime : 0;
 | 
			
		||||
            float initialTotalConsumption = batteryCapacity * ratio; // 用电池容量近似24小时总消耗
 | 
			
		||||
            model.setTotalConsumption(initialTotalConsumption); // 初始化累计耗电
 | 
			
		||||
            LogUtils.d(TAG, String.format("应用包 %s 24小时累计耗电初始化:%.1f mAh", packageName, initialTotalConsumption));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 【核心修改】计算单次耗电(赋值给consumption)+ 累加至累计耗电(totalConsumption = totalConsumption + consumption)
 | 
			
		||||
     */
 | 
			
		||||
    private void calculateSingleConsumptionAndAccumulate(float dropPercent, Map<String, Long> runTimeMap) {
 | 
			
		||||
        long totalSingleRunTime = 0;
 | 
			
		||||
        // 1. 计算本次电池下降期间的总运行时长
 | 
			
		||||
        for (Map.Entry<String, Long> entry : runTimeMap.entrySet()) {
 | 
			
		||||
            totalSingleRunTime += entry.getValue();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 2. 遍历计算每个应用的“单次耗电”并“累加至累计”
 | 
			
		||||
        for (AppBatteryModel model : dataList) {
 | 
			
		||||
            String packageName = model.getPackageName();
 | 
			
		||||
            Long appSingleRunTime = runTimeMap.getOrDefault(packageName, 0L);
 | 
			
		||||
 | 
			
		||||
            // 步骤1:计算本次单次耗电(赋值给consumption)
 | 
			
		||||
            float ratio = (totalSingleRunTime > 0) ? (float) appSingleRunTime / totalSingleRunTime : 0;
 | 
			
		||||
            float singleConsumption = batteryCapacity * dropPercent / 100 * ratio; // 单次消耗
 | 
			
		||||
            model.setConsumption(singleConsumption); // 存储单次耗电
 | 
			
		||||
 | 
			
		||||
            // 步骤2:累加单次耗电到累计耗电(totalConsumption = 原有累计 + 本次单次)
 | 
			
		||||
            float newTotalConsumption = model.getTotalConsumption() + singleConsumption;
 | 
			
		||||
            model.setTotalConsumption(newTotalConsumption); // 更新累计耗电
 | 
			
		||||
 | 
			
		||||
            // 同步运行时长
 | 
			
		||||
            model.setRunTime(appSingleRunTime);
 | 
			
		||||
 | 
			
		||||
            LogUtils.d(TAG, String.format("应用包 %s:单次耗电%.1f mAh,累计耗电%.1f mAh", 
 | 
			
		||||
										  packageName, singleConsumption, newTotalConsumption));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 3. 按累计耗电排序(从高到低)
 | 
			
		||||
        Collections.sort(dataList, new Comparator<AppBatteryModel>() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public int compare(AppBatteryModel m1, AppBatteryModel m2) {
 | 
			
		||||
					return Float.compare(m2.getTotalConsumption(), m1.getTotalConsumption());
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 4. 重新应用过滤并刷新列表
 | 
			
		||||
        filterAppsByPackageAndName(etSearch.getText().toString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 双维度过滤(逻辑不变)
 | 
			
		||||
     */
 | 
			
		||||
    private void filterAppsByPackageAndName(String keyword) {
 | 
			
		||||
        filteredList.clear();
 | 
			
		||||
        if (keyword == null || keyword.isEmpty()) {
 | 
			
		||||
            filteredList.addAll(dataList);
 | 
			
		||||
        } else {
 | 
			
		||||
            String lowerKeyword = keyword.toLowerCase();
 | 
			
		||||
 | 
			
		||||
            for (AppBatteryModel model : dataList) {
 | 
			
		||||
                String packageName = model.getPackageName();
 | 
			
		||||
                String packageNameLower = packageName.toLowerCase();
 | 
			
		||||
                String appName = packageToAppNameCache.get(packageName);
 | 
			
		||||
                String appNameLower = appName.toLowerCase();
 | 
			
		||||
 | 
			
		||||
                boolean isMatched = packageNameLower.contains(lowerKeyword) 
 | 
			
		||||
                    || appNameLower.contains(lowerKeyword);
 | 
			
		||||
 | 
			
		||||
                if (isMatched) {
 | 
			
		||||
                    filteredList.add(model);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        adapter.notifyDataSetChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取应用运行时长(逻辑不变,返回24小时运行时长)
 | 
			
		||||
     */
 | 
			
		||||
    private Map<String, Long> getAppRunTime() {
 | 
			
		||||
        Map<String, Long> runTimeMap = new HashMap<String, Long>();
 | 
			
		||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 | 
			
		||||
            try {
 | 
			
		||||
                android.app.usage.UsageStatsManager manager =
 | 
			
		||||
                    (android.app.usage.UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
 | 
			
		||||
                long endTime = System.currentTimeMillis();
 | 
			
		||||
                long startTime = endTime - 24 * 3600 * 1000; // 近24小时
 | 
			
		||||
                List<android.app.usage.UsageStats> statsList = manager.queryUsageStats(
 | 
			
		||||
                    android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime);
 | 
			
		||||
 | 
			
		||||
                for (android.app.usage.UsageStats stats : statsList) {
 | 
			
		||||
                    long runTimeMs = stats.getTotalTimeInForeground();
 | 
			
		||||
                    String packageName = stats.getPackageName();
 | 
			
		||||
                    LogUtils.d(TAG, "包名" + packageName + "24小时运行时长:" + formatRunTime(runTimeMs));
 | 
			
		||||
                    runTimeMap.put(packageName, runTimeMs);
 | 
			
		||||
                    if (packageName.equals("aidepro.top")) {
 | 
			
		||||
                        LogUtils.d(TAG, String.format("runTimeMap.put(packageName, runTimeMs) 特殊查询 %s 查询有结果。", packageName));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                LogUtils.e(TAG, "获取应用运行时长失败:" + e.getMessage());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LogUtils.d(TAG, String.format("应用运行时长列表数量%d。", runTimeMap.size()));
 | 
			
		||||
        return runTimeMap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 格式化运行时长(逻辑不变)
 | 
			
		||||
     */
 | 
			
		||||
    private String formatRunTime(long runTimeMs) {
 | 
			
		||||
        if (runTimeMs <= 0) {
 | 
			
		||||
            return "0秒";
 | 
			
		||||
        }
 | 
			
		||||
        long seconds = runTimeMs / 1000;
 | 
			
		||||
        long hours = seconds / 3600;
 | 
			
		||||
        long minutes = (seconds % 3600) / 60;
 | 
			
		||||
        seconds = seconds % 60;
 | 
			
		||||
 | 
			
		||||
        if (hours > 0) {
 | 
			
		||||
            return String.format("%d时%d分%d秒", hours, minutes, seconds);
 | 
			
		||||
        } else if (minutes > 0) {
 | 
			
		||||
            return String.format("%d分%d秒", minutes, seconds);
 | 
			
		||||
        } else {
 | 
			
		||||
            return String.format("%d秒", seconds);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 权限检查(逻辑不变)
 | 
			
		||||
     */
 | 
			
		||||
    private boolean hasUsageStatsPermission(Context context) {
 | 
			
		||||
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        android.app.usage.UsageStatsManager manager =
 | 
			
		||||
            (android.app.usage.UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
 | 
			
		||||
        if (manager == null) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        long endTime = System.currentTimeMillis();
 | 
			
		||||
        long startTime = endTime - 1000 * 60;
 | 
			
		||||
        List<android.app.usage.UsageStats> statsList = manager.queryUsageStats(
 | 
			
		||||
            android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime);
 | 
			
		||||
 | 
			
		||||
        return statsList != null && !statsList.isEmpty();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 【核心修改】数据模型:明确字段含义
 | 
			
		||||
     * - consumption:单次耗电(两次电池广播间的消耗,float类型便于计算)
 | 
			
		||||
     * - totalConsumption:累计耗电(24小时初始化值+后续单次累加,显示用)
 | 
			
		||||
     */
 | 
			
		||||
    public static class AppBatteryModel {
 | 
			
		||||
        private String packageName;    // 应用包名(核心标识)
 | 
			
		||||
        private float consumption;     // 单次耗电(mAh,float类型)
 | 
			
		||||
        private float totalConsumption;// 累计耗电(mAh,显示+排序用)
 | 
			
		||||
        private long runTime;          // 运行时长(ms)
 | 
			
		||||
 | 
			
		||||
		// Java7 显式构造:初始化单次耗电、累计耗电为0
 | 
			
		||||
        public AppBatteryModel(String packageName, float consumption, float totalConsumption, long runTime) {
 | 
			
		||||
            this.packageName = packageName;
 | 
			
		||||
            this.consumption = consumption; // 单次耗电初始为0
 | 
			
		||||
            this.totalConsumption = totalConsumption; // 累计耗电初始为0(后续初始化时赋值)
 | 
			
		||||
            this.runTime = runTime;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Getter/Setter:覆盖所有字段,确保数据操作正常
 | 
			
		||||
        public String getPackageName() {
 | 
			
		||||
            return packageName;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public float getConsumption() {
 | 
			
		||||
            return consumption; // 获取单次耗电
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void setConsumption(float consumption) {
 | 
			
		||||
            this.consumption = consumption; // 设置单次耗电
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public float getTotalConsumption() {
 | 
			
		||||
            return totalConsumption; // 获取累计耗电(显示用)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void setTotalConsumption(float totalConsumption) {
 | 
			
		||||
            this.totalConsumption = totalConsumption; // 设置累计耗电(初始化/累加用)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public long getRunTime() {
 | 
			
		||||
            return runTime;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void setRunTime(long runTime) {
 | 
			
		||||
            this.runTime = runTime;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * RecyclerView 适配器:仅显示累计耗电(totalConsumption),逻辑适配模型修改
 | 
			
		||||
     */
 | 
			
		||||
    public static class BatteryReportAdapter extends RecyclerView.Adapter<BatteryReportAdapter.ViewHolder> {
 | 
			
		||||
        private Context mContext;
 | 
			
		||||
        private List<AppBatteryModel> mDataList;
 | 
			
		||||
        private PackageManager mPm;
 | 
			
		||||
        private Map<String, String> mPackageToNameCache;
 | 
			
		||||
 | 
			
		||||
        // Java7 显式构造:接收名称缓存,确保显示时高效获取应用名
 | 
			
		||||
        public BatteryReportAdapter(Context context, List<AppBatteryModel> dataList, 
 | 
			
		||||
                                    PackageManager pm, Map<String, String> packageToNameCache) {
 | 
			
		||||
            this.mContext = context;
 | 
			
		||||
            this.mDataList = dataList;
 | 
			
		||||
            this.mPm = pm;
 | 
			
		||||
            this.mPackageToNameCache = packageToNameCache;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 | 
			
		||||
            // 加载系统列表项布局(text1显示应用名,text2显示累计耗电+时长)
 | 
			
		||||
            View itemView = LayoutInflater.from(mContext)
 | 
			
		||||
                .inflate(android.R.layout.simple_list_item_2, parent, false);
 | 
			
		||||
            return new ViewHolder(itemView);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onBindViewHolder(ViewHolder holder, int position) {
 | 
			
		||||
            // Java7 显式非空判断:避免空指针异常
 | 
			
		||||
            if (mDataList == null || mDataList.isEmpty() || position >= mDataList.size()) {
 | 
			
		||||
                holder.tvAppName.setText("未知应用");
 | 
			
		||||
                holder.tvConsumption.setText("累计耗电:0.0 mAh | 运行时长:0秒");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            AppBatteryModel model = mDataList.get(position);
 | 
			
		||||
            String packageName = model.getPackageName();
 | 
			
		||||
            String appName = "";
 | 
			
		||||
 | 
			
		||||
            // 优先从缓存获取应用名:减少PackageManager调用,提升性能
 | 
			
		||||
            if (mPackageToNameCache != null && mPackageToNameCache.containsKey(packageName)) {
 | 
			
		||||
                appName = mPackageToNameCache.get(packageName);
 | 
			
		||||
            } else {
 | 
			
		||||
                // 缓存无数据时兜底查询,并同步更新缓存
 | 
			
		||||
                try {
 | 
			
		||||
                    ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0);
 | 
			
		||||
                    appName = mPm.getApplicationLabel(appInfo).toString();
 | 
			
		||||
                    if (mPackageToNameCache != null) {
 | 
			
		||||
                        mPackageToNameCache.put(packageName, appName);
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (PackageManager.NameNotFoundException e) {
 | 
			
		||||
                    appName = packageName; // 包名不存在时用包名兜底
 | 
			
		||||
                    LogUtils.e("Adapter", "包名" + packageName + "对应的应用未找到:" + e.getMessage());
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    appName = packageName; // 其他异常时用包名兜底
 | 
			
		||||
                    LogUtils.e("Adapter", "查询应用名称失败(包名:" + packageName + "):" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 显示逻辑:仅展示累计耗电(totalConsumption),隐藏单次耗电
 | 
			
		||||
            holder.tvAppName.setText(appName);
 | 
			
		||||
            // 格式化运行时长 + 累计耗电(保留1位小数,提升可读性)
 | 
			
		||||
            String runTimeStr = ((BatteryReportActivity) mContext).formatRunTime(model.getRunTime());
 | 
			
		||||
            String totalConsumptionText = String.format("累计耗电:%.1f mAh | 运行时长:%s",
 | 
			
		||||
														model.getTotalConsumption(), runTimeStr);
 | 
			
		||||
            holder.tvConsumption.setText(totalConsumptionText);
 | 
			
		||||
 | 
			
		||||
            // 显示优化:文字颜色区分(避免所有应用均标蓝,仅示例可按需修改)
 | 
			
		||||
            holder.tvAppName.setTextColor(mContext.getResources().getColor(android.R.color.black));
 | 
			
		||||
            holder.tvConsumption.setTextColor(mContext.getResources().getColor(android.R.color.darker_gray));
 | 
			
		||||
 | 
			
		||||
            // 调整文字大小:适配手机屏幕,提升可读性
 | 
			
		||||
            holder.tvAppName.setTextSize(16);
 | 
			
		||||
            holder.tvConsumption.setTextSize(14);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 获取列表长度:Java7 三元运算符判断空值,避免空指针
 | 
			
		||||
        @Override
 | 
			
		||||
        public int getItemCount() {
 | 
			
		||||
            return mDataList == null ? 0 : mDataList.size();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * ViewHolder:绑定系统布局控件,与显示逻辑对应
 | 
			
		||||
         */
 | 
			
		||||
        public static class ViewHolder extends RecyclerView.ViewHolder {
 | 
			
		||||
            TextView tvAppName;     // 显示应用名称
 | 
			
		||||
            TextView tvConsumption; // 显示累计耗电 + 运行时长
 | 
			
		||||
 | 
			
		||||
            // Java7 显式构造:绑定控件ID(系统布局固定ID:text1、text2)
 | 
			
		||||
            public ViewHolder(View itemView) {
 | 
			
		||||
                super(itemView);
 | 
			
		||||
                tvAppName = (TextView) itemView.findViewById(android.R.id.text1);
 | 
			
		||||
                tvConsumption = (TextView) itemView.findViewById(android.R.id.text2);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,51 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.powerbell.activities;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
			
		||||
 * @Date 2025/03/22 14:20:15
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import androidx.recyclerview.widget.DividerItemDecoration;
 | 
			
		||||
import androidx.recyclerview.widget.LinearLayoutManager;
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView;
 | 
			
		||||
import cc.winboll.studio.powerbell.R;
 | 
			
		||||
import cc.winboll.studio.powerbell.adapters.BatteryAdapter;
 | 
			
		||||
import cc.winboll.studio.powerbell.beans.BatteryData;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class BatteryReporterActivity extends Activity {
 | 
			
		||||
    public static final String TAG = "BatteryReporterActivity";
 | 
			
		||||
 | 
			
		||||
    private RecyclerView rvBatteryReport;
 | 
			
		||||
    private BatteryAdapter adapter;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        setContentView(R.layout.activity_battery_reporter);
 | 
			
		||||
 | 
			
		||||
        rvBatteryReport = findViewById(R.id.rvBatteryReport);
 | 
			
		||||
        setupRecyclerView();
 | 
			
		||||
        loadSampleData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setupRecyclerView() {
 | 
			
		||||
        adapter = new BatteryAdapter();
 | 
			
		||||
        rvBatteryReport.setLayoutManager(new LinearLayoutManager(this));
 | 
			
		||||
        rvBatteryReport.setAdapter(adapter);
 | 
			
		||||
        rvBatteryReport.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void loadSampleData() {
 | 
			
		||||
        List<BatteryData> dataList = Arrays.asList(
 | 
			
		||||
            new BatteryData(95, "01:23:45", "00:05:12"),
 | 
			
		||||
            new BatteryData(80, "02:15:30", "00:10:00"),
 | 
			
		||||
            new BatteryData(65, "03:45:15", "00:15:30"),
 | 
			
		||||
            new BatteryData(50, "05:00:00", "00:20:45")
 | 
			
		||||
        );
 | 
			
		||||
        adapter.updateData(dataList);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								powerbell/src/main/res/layout/activity_battery_report.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								powerbell/src/main/res/layout/activity_battery_report.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout 
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="match_parent"
 | 
			
		||||
    android:orientation="vertical"
 | 
			
		||||
    android:background="@android:color/white">
 | 
			
		||||
 | 
			
		||||
    <!-- 搜索框:提示文本改为“搜索应用名称或包名” -->
 | 
			
		||||
    <EditText
 | 
			
		||||
        android:id="@+id/et_search"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_margin="8dp"
 | 
			
		||||
        android:padding="12dp"
 | 
			
		||||
        android:hint="搜索应用名称或包名"
 | 
			
		||||
        android:background="@android:drawable/btn_default_small"
 | 
			
		||||
        android:inputType="text"
 | 
			
		||||
        android:textSize="16sp"/>
 | 
			
		||||
 | 
			
		||||
    <!-- 应用列表 -->
 | 
			
		||||
    <androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
        android:id="@+id/rv_battery_report"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="0dp"
 | 
			
		||||
        android:layout_weight="1"
 | 
			
		||||
        android:layout_marginLeft="8dp"
 | 
			
		||||
        android:layout_marginRight="8dp"/>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout 
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="match_parent"
 | 
			
		||||
    android:orientation="vertical"
 | 
			
		||||
    android:padding="16dp">
 | 
			
		||||
 | 
			
		||||
    <TextView
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:text="电池使用报告"
 | 
			
		||||
        android:textSize="24sp"
 | 
			
		||||
        android:fontFamily="sans-serif-medium"
 | 
			
		||||
        android:layout_marginBottom="16dp"/>
 | 
			
		||||
 | 
			
		||||
    <androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
        android:id="@+id/rvBatteryReport"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="match_parent"
 | 
			
		||||
        android:divider="@drawable/divider_line"
 | 
			
		||||
        android:dividerHeight="1dp"/>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/action_battery_reporter"
 | 
			
		||||
        android:title="@string/item_battery_reporter"/>
 | 
			
		||||
        android:id="@+id/action_battery_report"
 | 
			
		||||
        android:title="@string/item_battery_report"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/action_clearrecord"
 | 
			
		||||
        android:title="@string/item_clearrecord"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
    <string name="about_crashed">This application has crashed, the author level is limited, please understand!</string>
 | 
			
		||||
    <string name="item_mainview">Main View</string>
 | 
			
		||||
    <string name="item_aboutview">About</string>
 | 
			
		||||
    <string name="item_battery_reporter">Battery Reporter</string>
 | 
			
		||||
    <string name="item_battery_report">Battery Report</string>
 | 
			
		||||
    <string name="item_clearrecord">Clear Record</string>
 | 
			
		||||
    <string name="item_changepicture">Change Picture</string>
 | 
			
		||||
    <string name="item_devoloperoptionsview">Developer View</string>
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user