添加WinBoLL项目
@@ -69,3 +69,7 @@
|
|||||||
// Positions 项目编译设置
|
// Positions 项目编译设置
|
||||||
//include ':positions'
|
//include ':positions'
|
||||||
//rootProject.name = "positions"
|
//rootProject.name = "positions"
|
||||||
|
|
||||||
|
// WinBoLL 项目编译设置
|
||||||
|
//include ':winboll'
|
||||||
|
//rootProject.name = "winboll"
|
||||||
|
|||||||
78
winboll/build.gradle
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply from: '../.winboll/winboll_app_build.gradle'
|
||||||
|
apply from: '../.winboll/winboll_lint_build.gradle'
|
||||||
|
|
||||||
|
def genVersionName(def versionName){
|
||||||
|
// 检查编译标志位配置
|
||||||
|
assert (winbollBuildProps['stageCount'] != null)
|
||||||
|
assert (winbollBuildProps['baseVersion'] != null)
|
||||||
|
// 保存基础版本号
|
||||||
|
winbollBuildProps.setProperty("baseVersion", "${versionName}");
|
||||||
|
//保存编译标志配置
|
||||||
|
FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
|
||||||
|
winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
|
||||||
|
fos.close();
|
||||||
|
|
||||||
|
// 返回编译版本号
|
||||||
|
return "${versionName}." + winbollBuildProps['stageCount']
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 32
|
||||||
|
buildToolsVersion "32.0.0"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "cc.winboll.studio.winboll"
|
||||||
|
minSdkVersion 24
|
||||||
|
targetSdkVersion 30
|
||||||
|
versionCode 1
|
||||||
|
// versionName 更新后需要手动设置
|
||||||
|
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
||||||
|
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||||
|
versionName "15.0"
|
||||||
|
if(true) {
|
||||||
|
versionName = genVersionName("${versionName}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
|
||||||
|
api 'com.google.code.gson:gson:2.10.1'
|
||||||
|
|
||||||
|
// 下拉控件
|
||||||
|
api 'com.baoyz.pullrefreshlayout:library:1.2.0'
|
||||||
|
|
||||||
|
// SSH
|
||||||
|
api 'com.jcraft:jsch:0.1.55'
|
||||||
|
// Html 解析
|
||||||
|
api 'org.jsoup:jsoup:1.13.1'
|
||||||
|
// 二维码类库
|
||||||
|
api 'com.google.zxing:core:3.4.1'
|
||||||
|
api 'com.journeyapps:zxing-android-embedded:3.6.0'
|
||||||
|
// 应用介绍页类库
|
||||||
|
api 'io.github.medyo:android-about-page:2.0.0'
|
||||||
|
// 吐司类库
|
||||||
|
api 'com.github.getActivity:ToastUtils:10.5'
|
||||||
|
// 网络连接类库
|
||||||
|
api 'com.squareup.okhttp3:okhttp:4.4.1'
|
||||||
|
// AndroidX 类库
|
||||||
|
api 'androidx.appcompat:appcompat:1.1.0'
|
||||||
|
api 'com.google.android.material:material:1.4.0'
|
||||||
|
//api 'androidx.viewpager:viewpager:1.0.0'
|
||||||
|
//api 'androidx.vectordrawable:vectordrawable:1.1.0'
|
||||||
|
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
|
||||||
|
//api 'androidx.fragment:fragment:1.1.0'
|
||||||
|
|
||||||
|
api 'cc.winboll.studio:libaes:15.10.2'
|
||||||
|
api 'cc.winboll.studio:libapputils:15.10.2'
|
||||||
|
api 'cc.winboll.studio:libappbase:15.10.9'
|
||||||
|
}
|
||||||
8
winboll/build.properties
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
|
#Thu Nov 27 01:06:01 GMT 2025
|
||||||
|
stageCount=3
|
||||||
|
libraryProject=
|
||||||
|
baseVersion=15.0
|
||||||
|
publishVersion=15.0.2
|
||||||
|
buildCount=15
|
||||||
|
baseBetaVersion=15.0.3
|
||||||
21
winboll/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
14
winboll/src/beta/AndroidManifest.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools" >
|
||||||
|
|
||||||
|
<application
|
||||||
|
tools:replace="android:icon"
|
||||||
|
android:icon="@drawable/ic_launcher_beta">
|
||||||
|
|
||||||
|
<!-- Put flavor specific code here -->
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
||||||
3
winboll/src/beta/res/values-zh/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
</resources>
|
||||||
8
winboll/src/beta/res/values/strings.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">WinBoLL+</string>
|
||||||
|
<string name="app_name_cn1">筋斗云★</string>
|
||||||
|
<string name="app_name_cn2">金抖云☆</string>
|
||||||
|
|
||||||
|
</resources>
|
||||||
46
winboll/src/beta/res/xml/shortcutsmaincn1.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- 切换启动入口的快捷菜单 -->
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="switchto_en1"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:shortcutShortLabel="@string/switchto_en1"
|
||||||
|
android:shortcutLongLabel="@string/switchto_en1"
|
||||||
|
android:shortcutDisabledMessage="@string/en1_switch_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_EN1"
|
||||||
|
android:targetPackage="cc.winboll.studio.winboll.beta"
|
||||||
|
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
|
||||||
|
android:data="switchto_en1" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>
|
||||||
|
<!--<shortcut
|
||||||
|
android:shortcutId="switchto_cn1"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:shortcutShortLabel="@string/switchto_cn1"
|
||||||
|
android:shortcutLongLabel="@string/switchto_cn1"
|
||||||
|
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN1"
|
||||||
|
android:targetPackage="cc.winboll.studio.winboll.beta"
|
||||||
|
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
|
||||||
|
android:data="switchto_cn1" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>-->
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="switchto_cn2"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:shortcutShortLabel="@string/switchto_cn2"
|
||||||
|
android:shortcutLongLabel="@string/switchto_cn2"
|
||||||
|
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN2"
|
||||||
|
android:targetPackage="cc.winboll.studio.winboll.beta"
|
||||||
|
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
|
||||||
|
android:data="switchto_cn2" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>
|
||||||
|
</shortcuts>
|
||||||
46
winboll/src/beta/res/xml/shortcutsmaincn2.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- 切换启动入口的快捷菜单 -->
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="switchto_en1"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:shortcutShortLabel="@string/switchto_en1"
|
||||||
|
android:shortcutLongLabel="@string/switchto_en1"
|
||||||
|
android:shortcutDisabledMessage="@string/en1_switch_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_EN1"
|
||||||
|
android:targetPackage="cc.winboll.studio.winboll.beta"
|
||||||
|
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
|
||||||
|
android:data="switchto_en1" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="switchto_cn1"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:shortcutShortLabel="@string/switchto_cn1"
|
||||||
|
android:shortcutLongLabel="@string/switchto_cn1"
|
||||||
|
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN1"
|
||||||
|
android:targetPackage="cc.winboll.studio.winboll.beta"
|
||||||
|
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
|
||||||
|
android:data="switchto_cn1" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>
|
||||||
|
<!--<shortcut
|
||||||
|
android:shortcutId="switchto_cn2"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:shortcutShortLabel="@string/switchto_cn2"
|
||||||
|
android:shortcutLongLabel="@string/switchto_cn2"
|
||||||
|
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN2"
|
||||||
|
android:targetPackage="cc.winboll.studio.winboll.beta"
|
||||||
|
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
|
||||||
|
android:data="switchto_cn2" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>-->
|
||||||
|
</shortcuts>
|
||||||
46
winboll/src/beta/res/xml/shortcutsmainen1.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- 切换启动入口的快捷菜单 -->
|
||||||
|
<!--<shortcut
|
||||||
|
android:shortcutId="switchto_en1"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:shortcutShortLabel="@string/switchto_en1"
|
||||||
|
android:shortcutLongLabel="@string/switchto_en1"
|
||||||
|
android:shortcutDisabledMessage="@string/en1_switch_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_EN1"
|
||||||
|
android:targetPackage="cc.winboll.studio.winboll.beta"
|
||||||
|
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
|
||||||
|
android:data="switchto_en1" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>-->
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="switchto_cn1"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:shortcutShortLabel="@string/switchto_cn1"
|
||||||
|
android:shortcutLongLabel="@string/switchto_cn1"
|
||||||
|
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN1"
|
||||||
|
android:targetPackage="cc.winboll.studio.winboll.beta"
|
||||||
|
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
|
||||||
|
android:data="switchto_cn1" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="switchto_cn2"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:shortcutShortLabel="@string/switchto_cn2"
|
||||||
|
android:shortcutLongLabel="@string/switchto_cn2"
|
||||||
|
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN2"
|
||||||
|
android:targetPackage="cc.winboll.studio.winboll.beta"
|
||||||
|
android:targetClass="cc.winboll.studio.winboll.activities.ShortcutActionActivity"
|
||||||
|
android:data="switchto_cn2" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>
|
||||||
|
</shortcuts>
|
||||||
279
winboll/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<manifest
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="cc.winboll.studio.winboll">
|
||||||
|
|
||||||
|
<!-- 拥有完全的网络访问权限 -->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
|
<!-- 发送持久广播 -->
|
||||||
|
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
|
||||||
|
|
||||||
|
<!-- 对正在运行的应用重新排序 -->
|
||||||
|
<uses-permission android:name="android.permission.REORDER_TASKS"/>
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@drawable/ic_launcher_stage"
|
||||||
|
android:roundIcon="@drawable/ic_launcher_stage"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/MyAppTheme"
|
||||||
|
android:resizeableActivity="true"
|
||||||
|
android:name=".App">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:exported="true">
|
||||||
|
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name=".activities.ShortcutActionActivity"/>
|
||||||
|
|
||||||
|
<activity-alias
|
||||||
|
android:name=".MainActivityEN1"
|
||||||
|
android:targetActivity=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:icon="@drawable/ic_launcher_stage"
|
||||||
|
android:enabled="true">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.shortcuts"
|
||||||
|
android:resource="@xml/shortcutsmainen1"/>
|
||||||
|
|
||||||
|
</activity-alias>
|
||||||
|
|
||||||
|
<activity-alias
|
||||||
|
android:name=".MainActivityCN1"
|
||||||
|
android:targetActivity=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:label="@string/app_name_cn1"
|
||||||
|
android:icon="@drawable/ic_launcher_stage"
|
||||||
|
android:enabled="false">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.shortcuts"
|
||||||
|
android:resource="@xml/shortcutsmaincn1"/>
|
||||||
|
|
||||||
|
</activity-alias>
|
||||||
|
|
||||||
|
<activity-alias
|
||||||
|
android:name=".MainActivityCN2"
|
||||||
|
android:targetActivity=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:label="@string/app_name_cn2"
|
||||||
|
android:icon="@drawable/ic_launcher_stage"
|
||||||
|
android:enabled="false">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.shortcuts"
|
||||||
|
android:resource="@xml/shortcutsmaincn2"/>
|
||||||
|
|
||||||
|
</activity-alias>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activities.WinBoLLUnitTestActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:exported="true"
|
||||||
|
android:resizeableActivity="true"
|
||||||
|
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<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"/>
|
||||||
|
|
||||||
|
<activity android:name="cc.winboll.studio.winboll.activities.AboutActivity"/>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
357
winboll/src/main/java/cc/winboll/studio/winboll/App.java
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
package cc.winboll.studio.winboll;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.HorizontalScrollView;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||||
|
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||||
|
import com.hjq.toast.ToastUtils;
|
||||||
|
import com.hjq.toast.style.WhiteToastStyle;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.lang.Thread.UncaughtExceptionHandler;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public class App extends GlobalApplication {
|
||||||
|
|
||||||
|
public static final String TAG = "App";
|
||||||
|
|
||||||
|
public static final String COMPONENT_EN1 = "cc.winboll.studio.winboll.MainActivityEN1";
|
||||||
|
public static final String COMPONENT_CN1 = "cc.winboll.studio.winboll.MainActivityCN1";
|
||||||
|
public static final String COMPONENT_CN2 = "cc.winboll.studio.winboll.MainActivityCN2";
|
||||||
|
public static final String ACTION_SWITCHTO_EN1 = "cc.winboll.studio.winboll.App.ACTION_SWITCHTO_EN1";
|
||||||
|
public static final String ACTION_SWITCHTO_CN1 = "cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN1";
|
||||||
|
public static final String ACTION_SWITCHTO_CN2 = "cc.winboll.studio.winboll.App.ACTION_SWITCHTO_CN2";
|
||||||
|
|
||||||
|
private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
WinBoLLActivityManager.init(this);
|
||||||
|
|
||||||
|
// 初始化 Toast 框架
|
||||||
|
ToastUtils.init(this);
|
||||||
|
// 设置 Toast 布局样式
|
||||||
|
//ToastUtils.setView(R.layout.view_toast);
|
||||||
|
ToastUtils.setStyle(new WhiteToastStyle());
|
||||||
|
ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
|
||||||
|
|
||||||
|
//CrashHandler.getInstance().registerGlobal(this);
|
||||||
|
//CrashHandler.getInstance().registerPart(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(InputStream input, OutputStream output) throws IOException {
|
||||||
|
byte[] buf = new byte[1024 * 8];
|
||||||
|
int len;
|
||||||
|
while ((len = input.read(buf)) != -1) {
|
||||||
|
output.write(buf, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(File file, byte[] data) throws IOException {
|
||||||
|
File parent = file.getParentFile();
|
||||||
|
if (parent != null && !parent.exists()) parent.mkdirs();
|
||||||
|
|
||||||
|
ByteArrayInputStream input = new ByteArrayInputStream(data);
|
||||||
|
FileOutputStream output = new FileOutputStream(file);
|
||||||
|
try {
|
||||||
|
write(input, output);
|
||||||
|
} finally {
|
||||||
|
closeIO(input, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(InputStream input) throws IOException {
|
||||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||||
|
write(input, output);
|
||||||
|
try {
|
||||||
|
return output.toString("UTF-8");
|
||||||
|
} finally {
|
||||||
|
closeIO(input, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void closeIO(Closeable... closeables) {
|
||||||
|
for (Closeable closeable : closeables) {
|
||||||
|
try {
|
||||||
|
if (closeable != null) closeable.close();
|
||||||
|
} catch (IOException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CrashHandler {
|
||||||
|
|
||||||
|
public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
|
||||||
|
|
||||||
|
private static CrashHandler sInstance;
|
||||||
|
|
||||||
|
private PartCrashHandler mPartCrashHandler;
|
||||||
|
|
||||||
|
public static CrashHandler getInstance() {
|
||||||
|
if (sInstance == null) {
|
||||||
|
sInstance = new CrashHandler();
|
||||||
|
}
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerGlobal(Context context) {
|
||||||
|
registerGlobal(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerGlobal(Context context, String crashDir) {
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl(context.getApplicationContext(), crashDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregister() {
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(DEFAULT_UNCAUGHT_EXCEPTION_HANDLER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerPart(Context context) {
|
||||||
|
unregisterPart(context);
|
||||||
|
mPartCrashHandler = new PartCrashHandler(context.getApplicationContext());
|
||||||
|
MAIN_HANDLER.postAtFrontOfQueue(mPartCrashHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterPart(Context context) {
|
||||||
|
if (mPartCrashHandler != null) {
|
||||||
|
mPartCrashHandler.isRunning.set(false);
|
||||||
|
mPartCrashHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PartCrashHandler implements Runnable {
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
public AtomicBoolean isRunning = new AtomicBoolean(true);
|
||||||
|
|
||||||
|
public PartCrashHandler(Context context) {
|
||||||
|
this.mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (isRunning.get()) {
|
||||||
|
try {
|
||||||
|
Looper.loop();
|
||||||
|
} catch (final Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
if (isRunning.get()) {
|
||||||
|
MAIN_HANDLER.post(new Runnable(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(mContext, e.toString(), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (e instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException)e;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler {
|
||||||
|
|
||||||
|
private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss");
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
private final File mCrashDir;
|
||||||
|
|
||||||
|
public UncaughtExceptionHandlerImpl(Context context, String crashDir) {
|
||||||
|
this.mContext = context;
|
||||||
|
this.mCrashDir = TextUtils.isEmpty(crashDir) ? new File(mContext.getExternalCacheDir(), "crash") : new File(crashDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uncaughtException(Thread thread, Throwable throwable) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
String log = buildLog(throwable);
|
||||||
|
writeLog(log);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(mContext, CrashActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, log);
|
||||||
|
mContext.startActivity(intent);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
writeLog(e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
throwable.printStackTrace();
|
||||||
|
android.os.Process.killProcess(android.os.Process.myPid());
|
||||||
|
System.exit(0);
|
||||||
|
|
||||||
|
} catch (Throwable e) {
|
||||||
|
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildLog(Throwable throwable) {
|
||||||
|
String time = DATE_FORMAT.format(new Date());
|
||||||
|
|
||||||
|
String versionName = "unknown";
|
||||||
|
long versionCode = 0;
|
||||||
|
try {
|
||||||
|
PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
|
||||||
|
versionName = packageInfo.versionName;
|
||||||
|
versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode() : packageInfo.versionCode;
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
|
|
||||||
|
LinkedHashMap<String, String> head = new LinkedHashMap<String, String>();
|
||||||
|
head.put("Time Of Crash", time);
|
||||||
|
head.put("Device", String.format("%s, %s", Build.MANUFACTURER, Build.MODEL));
|
||||||
|
head.put("Android Version", String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT));
|
||||||
|
head.put("App Version", String.format("%s (%d)", versionName, versionCode));
|
||||||
|
head.put("Kernel", getKernel());
|
||||||
|
head.put("Support Abis", Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS != null ? Arrays.toString(Build.SUPPORTED_ABIS): "unknown");
|
||||||
|
head.put("Fingerprint", Build.FINGERPRINT);
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
for (String key : head.keySet()) {
|
||||||
|
if (builder.length() != 0) builder.append("\n");
|
||||||
|
builder.append(key);
|
||||||
|
builder.append(" : ");
|
||||||
|
builder.append(head.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append("\n\n");
|
||||||
|
builder.append(Log.getStackTraceString(throwable));
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeLog(String log) {
|
||||||
|
String time = DATE_FORMAT.format(new Date());
|
||||||
|
File file = new File(mCrashDir, "crash_" + time + ".txt");
|
||||||
|
try {
|
||||||
|
write(file, log.getBytes("UTF-8"));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getKernel() {
|
||||||
|
try {
|
||||||
|
return App.toString(new FileInputStream("/proc/version")).trim();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class CrashActivity extends Activity {
|
||||||
|
|
||||||
|
private String mLog;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setTheme(android.R.style.Theme_DeviceDefault);
|
||||||
|
setTitle("App Crash");
|
||||||
|
|
||||||
|
mLog = getIntent().getStringExtra(Intent.EXTRA_TEXT);
|
||||||
|
|
||||||
|
ScrollView contentView = new ScrollView(this);
|
||||||
|
contentView.setFillViewport(true);
|
||||||
|
|
||||||
|
HorizontalScrollView horizontalScrollView = new HorizontalScrollView(this);
|
||||||
|
|
||||||
|
TextView textView = new TextView(this);
|
||||||
|
int padding = dp2px(16);
|
||||||
|
textView.setPadding(padding, padding, padding, padding);
|
||||||
|
textView.setText(mLog);
|
||||||
|
textView.setTextIsSelectable(true);
|
||||||
|
textView.setTypeface(Typeface.DEFAULT);
|
||||||
|
textView.setLinksClickable(true);
|
||||||
|
|
||||||
|
horizontalScrollView.addView(textView);
|
||||||
|
contentView.addView(horizontalScrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||||
|
|
||||||
|
setContentView(contentView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restart() {
|
||||||
|
Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
|
||||||
|
if (intent != null) {
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
android.os.Process.killProcess(android.os.Process.myPid());
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int dp2px(float dpValue) {
|
||||||
|
final float scale = Resources.getSystem().getDisplayMetrics().density;
|
||||||
|
return (int) (dpValue * scale + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
menu.add(0, android.R.id.copy, 0, android.R.string.copy)
|
||||||
|
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case android.R.id.copy:
|
||||||
|
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package cc.winboll.studio.winboll;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
|
* @Date 2025/03/28 19:12:12
|
||||||
|
* @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.libaes.models.WinBoLLClientServiceBean;
|
||||||
|
import cc.winboll.studio.libapputils.utils.ServiceUtils;
|
||||||
|
|
||||||
|
public class AssistantService extends Service {
|
||||||
|
|
||||||
|
public final static String TAG = "AssistantService";
|
||||||
|
|
||||||
|
WinBoLLClientServiceBean mWinBoLLServiceBean;
|
||||||
|
MyServiceConnection mMyServiceConnection;
|
||||||
|
volatile boolean mIsServiceRunning;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
mWinBoLLServiceBean = WinBoLLClientServiceBean.loadWinBoLLClientServiceBean(this);
|
||||||
|
if (mMyServiceConnection == null) {
|
||||||
|
mMyServiceConnection = new MyServiceConnection();
|
||||||
|
}
|
||||||
|
// 设置运行参数
|
||||||
|
mIsServiceRunning = false;
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
run();
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
mIsServiceRunning = false;
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 运行服务内容
|
||||||
|
//
|
||||||
|
void run() {
|
||||||
|
mWinBoLLServiceBean = WinBoLLClientServiceBean.loadWinBoLLClientServiceBean(this);
|
||||||
|
if (mWinBoLLServiceBean.isEnable()) {
|
||||||
|
if (mIsServiceRunning == false) {
|
||||||
|
// 设置运行状态
|
||||||
|
mIsServiceRunning = true;
|
||||||
|
// 唤醒和绑定主进程
|
||||||
|
wakeupAndBindMain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 唤醒和绑定主进程
|
||||||
|
//
|
||||||
|
void wakeupAndBindMain() {
|
||||||
|
if (ServiceUtils.isServiceAlive(getApplicationContext(), WinBoLLClientService.class.getName()) == false) {
|
||||||
|
startForegroundService(new Intent(AssistantService.this, WinBoLLClientService.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
bindService(new Intent(AssistantService.this, WinBoLLClientService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 主进程与守护进程连接时需要用到此类
|
||||||
|
//
|
||||||
|
class MyServiceConnection implements ServiceConnection {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
mWinBoLLServiceBean = WinBoLLClientServiceBean.loadWinBoLLClientServiceBean(AssistantService.this);
|
||||||
|
if (mWinBoLLServiceBean.isEnable()) {
|
||||||
|
wakeupAndBindMain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package cc.winboll.studio.winboll;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen<zhangsken@188.com>
|
||||||
|
* @Date 2025/05/22 13:08
|
||||||
|
*/
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
|
public class CustomToolbar extends Toolbar {
|
||||||
|
|
||||||
|
private View viewMain;
|
||||||
|
|
||||||
|
public CustomToolbar(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomToolbar(Context context, @Nullable AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomToolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
initView(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initView(Context context, AttributeSet attrs) {
|
||||||
|
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomToolbar);
|
||||||
|
|
||||||
|
// 获取属性值
|
||||||
|
String toolbarTitle = typedArray.getString(R.styleable.CustomToolbar_toolbarTitle);
|
||||||
|
int toolbarTitleColor = typedArray.getColor(R.styleable.CustomToolbar_toolbarTitleColor, android.graphics.Color.WHITE);
|
||||||
|
int toolbarBackgroundColor = typedArray.getColor(R.styleable.CustomToolbar_toolbarBackgroundColor, android.graphics.Color.BLUE);
|
||||||
|
|
||||||
|
// 加载布局
|
||||||
|
viewMain = LayoutInflater.from(context).inflate(R.layout.view_toolbar, this, true);
|
||||||
|
|
||||||
|
// 应用属性值
|
||||||
|
TextView toolbarTitleTextView = viewMain.findViewById(R.id.toolbar_title);
|
||||||
|
toolbarTitleTextView.setText(toolbarTitle);
|
||||||
|
toolbarTitleTextView.setTextColor(toolbarTitleColor);
|
||||||
|
viewMain.setBackgroundColor(toolbarBackgroundColor);
|
||||||
|
|
||||||
|
// 释放 TypedArray
|
||||||
|
typedArray.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package cc.winboll.studio.winboll;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
|
* @Date 2025/03/28 19:11:27
|
||||||
|
* @Describe WinBoLL UI 状态图标枚举
|
||||||
|
*/
|
||||||
|
import cc.winboll.studio.libaes.R;
|
||||||
|
|
||||||
|
public enum EWUIStatusIconDrawable {
|
||||||
|
NORMAL(0),
|
||||||
|
NEWS(1)
|
||||||
|
;
|
||||||
|
|
||||||
|
static final String TAG = "WUIStatusIconDrawable";
|
||||||
|
|
||||||
|
static String[] _mlistCNName = { "正常", "新的消息" };
|
||||||
|
|
||||||
|
private int value = 0;
|
||||||
|
private EWUIStatusIconDrawable(int value) { //必须是private的,否则编译错误
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getIconDrawableId(EWUIStatusIconDrawable drawableId) {
|
||||||
|
int res;
|
||||||
|
switch(drawableId){
|
||||||
|
case NEWS :
|
||||||
|
res = R.drawable.ic_winbollbeta;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
res = R.drawable.ic_winboll;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package cc.winboll.studio.winboll;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
|
||||||
|
import cc.winboll.studio.libaes.dialogs.LocalFileSelectDialog;
|
||||||
|
import cc.winboll.studio.libaes.dialogs.StoragePathDialog;
|
||||||
|
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
|
||||||
|
import cc.winboll.studio.libaes.models.DrawerMenuBean;
|
||||||
|
import cc.winboll.studio.libaes.unittests.SecondaryLibraryActivity;
|
||||||
|
import cc.winboll.studio.libaes.unittests.TestAButtonFragment;
|
||||||
|
import cc.winboll.studio.libaes.unittests.TestASupportToolbarActivity;
|
||||||
|
import cc.winboll.studio.libaes.unittests.TestAToolbarActivity;
|
||||||
|
import cc.winboll.studio.libaes.unittests.TestDrawerFragmentActivity;
|
||||||
|
import cc.winboll.studio.libaes.unittests.TestViewPageFragment;
|
||||||
|
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
import cc.winboll.studio.winboll.fragments.MainFragment;
|
||||||
|
import com.a4455jkjh.colorpicker.ColorPickerDialog;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import cc.winboll.studio.winboll.activities.AboutActivity;
|
||||||
|
|
||||||
|
public class MainActivity extends DrawerFragmentActivity implements IWinBoLLActivity {
|
||||||
|
|
||||||
|
|
||||||
|
public static final String TAG = "MainActivity";
|
||||||
|
|
||||||
|
MainFragment mMainFragment;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Activity getActivity() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (mMainFragment == null) {
|
||||||
|
mMainFragment = new MainFragment();
|
||||||
|
addFragment(mMainFragment);
|
||||||
|
}
|
||||||
|
showFragment(mMainFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
|
||||||
|
super.initDrawerMenuItemList(listDrawerMenu);
|
||||||
|
LogUtils.d(TAG, "initDrawerMenuItemList");
|
||||||
|
//listDrawerMenu.clear();
|
||||||
|
// 添加抽屉菜单项
|
||||||
|
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestAButtonFragment.TAG));
|
||||||
|
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestViewPageFragment.TAG));
|
||||||
|
notifyDrawerMenuDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reinitDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
|
||||||
|
super.reinitDrawerMenuItemList(listDrawerMenu);
|
||||||
|
LogUtils.d(TAG, "reinitDrawerMenuItemList");
|
||||||
|
//listDrawerMenu.clear();
|
||||||
|
// 添加抽屉菜单项
|
||||||
|
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestAButtonFragment.TAG));
|
||||||
|
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestViewPageFragment.TAG));
|
||||||
|
notifyDrawerMenuDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DrawerFragmentActivity.ActivityType initActivityType() {
|
||||||
|
return DrawerFragmentActivity.ActivityType.Main;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
||||||
|
if(App.isDebuging()) {
|
||||||
|
getMenuInflater().inflate(cc.winboll.studio.libapputils.R.menu.toolbar_studio_debug, menu);
|
||||||
|
}
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
super.onItemClick(parent, view, position, id);
|
||||||
|
switch (position) {
|
||||||
|
case 0 : {
|
||||||
|
if (mMainFragment == null) {
|
||||||
|
mMainFragment = new MainFragment();
|
||||||
|
addFragment(mMainFragment);
|
||||||
|
}
|
||||||
|
showFragment(mMainFragment);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int nItemId = item.getItemId();
|
||||||
|
if (item.getItemId() == R.id.item_log) {
|
||||||
|
WinBoLLActivityManager.getInstance().startLogActivity(getApplicationContext());
|
||||||
|
} else if (nItemId == R.id.item_about) {
|
||||||
|
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), AboutActivity.class);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package cc.winboll.studio.winboll;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
|
* @Date 2025/02/13 19:30:10
|
||||||
|
*/
|
||||||
|
import android.content.Context;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
import android.service.quicksettings.Tile;
|
||||||
|
import android.service.quicksettings.TileService;
|
||||||
|
import cc.winboll.studio.winboll.models.MainServiceBean;
|
||||||
|
import cc.winboll.studio.winboll.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
40
winboll/src/main/java/cc/winboll/studio/winboll/WinBoLL.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package cc.winboll.studio.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.winboll.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package cc.winboll.studio.winboll;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen
|
||||||
|
* @Date 2025/05/03 19:28
|
||||||
|
*/
|
||||||
|
public class WinBoLLClientService extends Service {
|
||||||
|
|
||||||
|
public static final String TAG = "WinBoLLClientService";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package cc.winboll.studio.winboll;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen
|
||||||
|
* @Date 2025/05/03 19:14
|
||||||
|
*/
|
||||||
|
public class WinBoLLServiceStatusView extends LinearLayout {
|
||||||
|
|
||||||
|
public static final String TAG = "WinBoLLServiceStatusView";
|
||||||
|
|
||||||
|
public WinBoLLServiceStatusView(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WinBoLLServiceStatusView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WinBoLLServiceStatusView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WinBoLLServiceStatusView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setServerHost(String szWinBoLLServerHost) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAuthInfo(String szDevUserName, String szDevUserPassword) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package cc.winboll.studio.winboll.activities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
|
* @Date 2025/09/29 13:30
|
||||||
|
* @Describe 应用介绍窗口
|
||||||
|
*/
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
|
||||||
|
import cc.winboll.studio.libaes.models.APPInfo;
|
||||||
|
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||||
|
import cc.winboll.studio.libaes.views.AboutView;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
import cc.winboll.studio.libaes.BuildConfig;
|
||||||
|
|
||||||
|
public class AboutActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
||||||
|
|
||||||
|
public static final String TAG = "AboutActivity";
|
||||||
|
|
||||||
|
Context mContext;
|
||||||
|
Toolbar mToolbar;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Activity getActivity() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
mContext = this;
|
||||||
|
setContentView(R.layout.activity_about);
|
||||||
|
|
||||||
|
mToolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(mToolbar);
|
||||||
|
mToolbar.setSubtitle(TAG);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
AboutView aboutView = CreateAboutView();
|
||||||
|
// 在 Activity 的 onCreate 或其他生命周期方法中调用
|
||||||
|
// LinearLayout layout = new LinearLayout(this);
|
||||||
|
// layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
// // 创建布局参数(宽度和高度)
|
||||||
|
// ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
|
||||||
|
// ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
// ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
// );
|
||||||
|
// addContentView(aboutView, params);
|
||||||
|
|
||||||
|
LinearLayout layout = findViewById(R.id.aboutviewroot_ll);
|
||||||
|
// 创建布局参数(宽度和高度)
|
||||||
|
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
);
|
||||||
|
layout.addView(aboutView, params);
|
||||||
|
|
||||||
|
WinBoLLActivityManager.getInstance().add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
WinBoLLActivityManager.getInstance().registeRemove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AboutView CreateAboutView() {
|
||||||
|
String szBranchName = "winboll";
|
||||||
|
APPInfo appInfo = new APPInfo();
|
||||||
|
appInfo.setAppName("WinBoLL");
|
||||||
|
appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll);
|
||||||
|
appInfo.setAppDescription("WinBoLL Description");
|
||||||
|
appInfo.setAppGitName("APPBase");
|
||||||
|
appInfo.setAppGitOwner("Studio");
|
||||||
|
appInfo.setAppGitAPPBranch(szBranchName);
|
||||||
|
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
|
||||||
|
appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=3&extra=page%3D1");
|
||||||
|
appInfo.setAppAPKName("WinBoLL");
|
||||||
|
appInfo.setAppAPKFolderName("WinBoLL");
|
||||||
|
//appInfo.setIsAddDebugTools(false);
|
||||||
|
appInfo.setIsAddDebugTools(BuildConfig.DEBUG);
|
||||||
|
return new AboutView(mContext, appInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
package cc.winboll.studio.winboll.activities;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
|
||||||
|
import cc.winboll.studio.libappbase.BuildConfig;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libappbase.LogView;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
import cc.winboll.studio.winboll.models.UserInfoModel;
|
||||||
|
import cc.winboll.studio.winboll.utils.RSAUtils;
|
||||||
|
import cc.winboll.studio.winboll.utils.YunUtils;
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package cc.winboll.studio.winboll.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.libaes.interfaces.IWinBoLLActivity;
|
||||||
|
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
WinBoLLActivityManager.getInstance().finish(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCloseAllActivity(View view) {
|
||||||
|
WinBoLLActivityManager.getInstance().finishAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onNewActivity(View view) {
|
||||||
|
WinBoLLActivityManager.getInstance().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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package cc.winboll.studio.winboll.activities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
|
* @Date 2025/03/25 05:04:22
|
||||||
|
* @Describe
|
||||||
|
*/
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Toolbar;
|
||||||
|
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
|
||||||
|
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
WinBoLLActivityManager.getInstance().finish(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCloseAllActivity(View view) {
|
||||||
|
WinBoLLActivityManager.getInstance().finishAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onNew2Activity(View view) {
|
||||||
|
WinBoLLActivityManager.getInstance().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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package cc.winboll.studio.winboll.activities;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import cc.winboll.studio.libappbase.ToastUtils;
|
||||||
|
import cc.winboll.studio.winboll.App;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
import cc.winboll.studio.winboll.utils.APPPlusUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
|
* @Date 2025/11/27 09:00
|
||||||
|
* @Describe 应用快捷方式活动类
|
||||||
|
*/
|
||||||
|
public class ShortcutActionActivity extends Activity {
|
||||||
|
|
||||||
|
public static final String TAG = "ShortcutActionActivity";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
// 处理应用级别的切换请求
|
||||||
|
handleSwitchRequest();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理应用图标快捷菜单的请求
|
||||||
|
*/
|
||||||
|
private void handleSwitchRequest() {
|
||||||
|
Intent intent = getIntent();
|
||||||
|
if (intent != null && "switchto_en1".equals(intent.getDataString())) {
|
||||||
|
APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_EN1);
|
||||||
|
ToastUtils.show("切换至" + getString(R.string.app_name) + "图标");
|
||||||
|
//moveTaskToBack(true);
|
||||||
|
}
|
||||||
|
if (intent != null && "switchto_cn1".equals(intent.getDataString())) {
|
||||||
|
APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_CN1);
|
||||||
|
ToastUtils.show("切换至" + getString(R.string.app_name_cn1) + "图标");
|
||||||
|
//moveTaskToBack(true);
|
||||||
|
}
|
||||||
|
if (intent != null && "switchto_cn2".equals(intent.getDataString())) {
|
||||||
|
APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_CN2);
|
||||||
|
ToastUtils.show("切换至" + getString(R.string.app_name_cn2) + "图标");
|
||||||
|
//moveTaskToBack(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package cc.winboll.studio.winboll.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 androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
|
||||||
|
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
|
||||||
|
public class WinBoLLActivity extends AppCompatActivity 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) {
|
||||||
|
WinBoLLActivityManager.getInstance().startLogActivity(this);
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.item_home) {
|
||||||
|
startActivity(new Intent(this, MainActivity.class));
|
||||||
|
return true;
|
||||||
|
}*/
|
||||||
|
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostCreate(Bundle savedInstanceState) {
|
||||||
|
super.onPostCreate(savedInstanceState);
|
||||||
|
WinBoLLActivityManager.getInstance().add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
WinBoLLActivityManager.getInstance().registeRemove(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
package cc.winboll.studio.winboll.activities;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||||
|
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.ToastUtils;
|
||||||
|
import cc.winboll.studio.winboll.App;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
import cc.winboll.studio.winboll.services.MainService;
|
||||||
|
import cc.winboll.studio.winboll.services.TestDemoBindService;
|
||||||
|
import cc.winboll.studio.winboll.services.TestDemoService;
|
||||||
|
import cc.winboll.studio.winboll.sos.SOS;
|
||||||
|
import cc.winboll.studio.winboll.widgets.StatusWidget;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
|
public class WinBoLLUnitTestActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
public static final String TAG = "MainActivity";
|
||||||
|
|
||||||
|
Toolbar mToolbar;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ToastUtils.show("onCreate");
|
||||||
|
setContentView(R.layout.activity_winbollunittest);
|
||||||
|
|
||||||
|
mToolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(mToolbar);
|
||||||
|
|
||||||
|
CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
|
||||||
|
cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
//getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if(item.getItemId() == R.id.item_yun) {
|
||||||
|
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, cc.winboll.studio.winboll.activities.YunActivity.class);
|
||||||
|
} else if(item.getItemId() == R.id.item_logon) {
|
||||||
|
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, cc.winboll.studio.winboll.activities.LogonActivity.class);
|
||||||
|
}
|
||||||
|
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
Intent intentAPPWidget = new Intent(this, StatusWidget.class);
|
||||||
|
intentAPPWidget.setAction(StatusWidget.ACTION_STATUS_UPDATE);
|
||||||
|
sendBroadcast(intentAPPWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSwitchDebugMode(View view) {
|
||||||
|
boolean isDebuging = ((CheckBox)view).isChecked();
|
||||||
|
GlobalApplication.setIsDebuging(isDebuging);
|
||||||
|
GlobalApplication.saveDebugStatus(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPreviewGlobalCrashActivity(View view) {
|
||||||
|
Intent intent = new Intent(this, GlobalCrashActivity.class);
|
||||||
|
intent.putExtra(CrashHandler.EXTRA_CRASH_INFO, "Demo log...");
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStartCenter(View view) {
|
||||||
|
MainService.startMainService(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStopCenter(View view) {
|
||||||
|
MainService.stopMainService(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTestStopMainServiceWithoutSettingEnable(View view) {
|
||||||
|
LogUtils.d(TAG, "onTestStopMainServiceWithoutSettingEnable");
|
||||||
|
stopService(new Intent(this, MainService.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTestUseComponentStartService(View view) {
|
||||||
|
LogUtils.d(TAG, "onTestUseComponentStartService");
|
||||||
|
|
||||||
|
// 目标服务的包名和类名
|
||||||
|
String packageName = this.getPackageName();
|
||||||
|
String serviceClassName = TestDemoService.class.getName();
|
||||||
|
|
||||||
|
// 构建Intent
|
||||||
|
Intent intentService = new Intent();
|
||||||
|
intentService.setComponent(new ComponentName(packageName, serviceClassName));
|
||||||
|
|
||||||
|
startService(intentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTestDemoServiceSOS(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoService.class);
|
||||||
|
stopService(intent);
|
||||||
|
if (App.isDebuging()) {
|
||||||
|
SOS.sosToAppBaseBeta(this, TestDemoService.class.getName());
|
||||||
|
} else {
|
||||||
|
SOS.sosToAppBase(this, TestDemoService.class.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSartTestDemoService(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoService.class);
|
||||||
|
intent.setAction(TestDemoService.ACTION_ENABLE);
|
||||||
|
startService(intent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStopTestDemoService(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoService.class);
|
||||||
|
intent.setAction(TestDemoService.ACTION_DISABLE);
|
||||||
|
startService(intent);
|
||||||
|
|
||||||
|
Intent intentStop = new Intent(this, TestDemoService.class);
|
||||||
|
stopService(intentStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStopTestDemoServiceNoSettings(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoService.class);
|
||||||
|
stopService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSartTestDemoBindService(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoBindService.class);
|
||||||
|
intent.setAction(TestDemoBindService.ACTION_ENABLE);
|
||||||
|
startService(intent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStopTestDemoBindService(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoBindService.class);
|
||||||
|
intent.setAction(TestDemoBindService.ACTION_DISABLE);
|
||||||
|
startService(intent);
|
||||||
|
|
||||||
|
Intent intentStop = new Intent(this, TestDemoBindService.class);
|
||||||
|
stopService(intentStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStopTestDemoBindServiceNoSettings(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoBindService.class);
|
||||||
|
stopService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTestOpenNewActivity(View view) {
|
||||||
|
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, NewActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package cc.winboll.studio.winboll.activities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
|
* @Date 2025/06/04 11:06
|
||||||
|
* @Describe 云宝云
|
||||||
|
*/
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
|
||||||
|
import cc.winboll.studio.libappbase.BuildConfig;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libappbase.LogView;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
import java.io.IOException;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package cc.winboll.studio.winboll.fragments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
|
* @Date 2025/09/29 13:15
|
||||||
|
* @Describe MainFragment
|
||||||
|
*/
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
import cc.winboll.studio.libaes.views.AButton;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libappbase.ToastUtils;
|
||||||
|
import android.widget.Switch;
|
||||||
|
|
||||||
|
|
||||||
|
public class MainFragment extends Fragment {
|
||||||
|
|
||||||
|
public static final String TAG = "MainFragment";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_main, container, false);
|
||||||
|
Switch swEnablePosition = view.findViewById(R.id.fragmentmainSwitch1);
|
||||||
|
swEnablePosition.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
ToastUtils.show("Position");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package cc.winboll.studio.winboll.handlers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
|
* @Date 2025/02/14 03:51:40
|
||||||
|
*/
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import cc.winboll.studio.winboll.services.MainService;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package cc.winboll.studio.winboll.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package cc.winboll.studio.winboll.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package cc.winboll.studio.winboll.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package cc.winboll.studio.winboll.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package cc.winboll.studio.winboll.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package cc.winboll.studio.winboll.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;
|
||||||
|
import cc.winboll.studio.libappbase.APPModel;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package cc.winboll.studio.winboll.models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
|
* @Date 2025/05/10 09:36
|
||||||
|
* @Describe WinBoLL 应用消息数据模型
|
||||||
|
*/
|
||||||
|
import android.util.JsonReader;
|
||||||
|
import android.util.JsonWriter;
|
||||||
|
import cc.winboll.studio.libappbase.BaseBean;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class WinBoLLNewsBean extends BaseBean {
|
||||||
|
|
||||||
|
public static final String TAG = "WinBoLLNewsBean";
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package cc.winboll.studio.winboll.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.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.winboll.widgets.APPNewsWidget;
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package cc.winboll.studio.winboll.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.libaes.interfaces.IWinBoLLActivity;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libappbase.ToastUtils;
|
||||||
|
import cc.winboll.studio.libapputils.utils.AppUtils;
|
||||||
|
import cc.winboll.studio.winboll.WinBoLL;
|
||||||
|
import cc.winboll.studio.winboll.models.WinBoLLModel;
|
||||||
|
import cc.winboll.studio.winboll.models.WinBoLLNewsBean;
|
||||||
|
import cc.winboll.studio.winboll.services.MainService;
|
||||||
|
import cc.winboll.studio.winboll.sos.SOS;
|
||||||
|
import cc.winboll.studio.winboll.sos.SOSObject;
|
||||||
|
import cc.winboll.studio.winboll.widgets.APPNewsWidget;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package cc.winboll.studio.winboll.receivers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package cc.winboll.studio.winboll.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.libappbase.LogUtils;
|
||||||
|
import android.os.Binder;
|
||||||
|
import cc.winboll.studio.winboll.models.MainServiceBean;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,316 @@
|
|||||||
|
package cc.winboll.studio.winboll.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.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.winboll.MyTileService;
|
||||||
|
import cc.winboll.studio.winboll.handlers.MainServiceHandler;
|
||||||
|
import cc.winboll.studio.winboll.models.MainServiceBean;
|
||||||
|
import cc.winboll.studio.winboll.models.WinBoLLModel;
|
||||||
|
import cc.winboll.studio.winboll.receivers.MainReceiver;
|
||||||
|
import cc.winboll.studio.winboll.services.AssistantService;
|
||||||
|
import cc.winboll.studio.winboll.threads.MainServiceThread;
|
||||||
|
import cc.winboll.studio.winboll.widgets.APPNewsWidget;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
package cc.winboll.studio.winboll.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.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.winboll.App;
|
||||||
|
import cc.winboll.studio.winboll.WinBoLL;
|
||||||
|
import cc.winboll.studio.winboll.models.TestDemoBindServiceBean;
|
||||||
|
import cc.winboll.studio.winboll.services.TestDemoBindService;
|
||||||
|
import cc.winboll.studio.winboll.sos.SOS;
|
||||||
|
|
||||||
|
public class TestDemoBindService extends Service {
|
||||||
|
|
||||||
|
public static final String TAG = "TestDemoBindService";
|
||||||
|
|
||||||
|
public static final String ACTION_ENABLE = TestDemoBindService.class.getName() + ".ACTION_ENABLE";
|
||||||
|
public static final String ACTION_DISABLE = TestDemoBindService.class.getName() + ".ACTION_DISABLE";
|
||||||
|
|
||||||
|
volatile static TestThread _TestThread;
|
||||||
|
|
||||||
|
volatile static boolean _IsRunning;
|
||||||
|
|
||||||
|
public synchronized static void setIsRunning(boolean isRunning) {
|
||||||
|
_IsRunning = isRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isRunning() {
|
||||||
|
return _IsRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return new MyBinder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MyBinder extends Binder {
|
||||||
|
public TestDemoBindService getService() {
|
||||||
|
return TestDemoBindService.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
LogUtils.d(TAG, "onCreate()");
|
||||||
|
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
LogUtils.d(TAG, "onStartCommand(...)");
|
||||||
|
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
|
||||||
|
if (bean == null) {
|
||||||
|
bean = new TestDemoBindServiceBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intent.getAction() != null) {
|
||||||
|
if (intent.getAction().equals(ACTION_ENABLE)) {
|
||||||
|
bean.setIsEnable(true);
|
||||||
|
LogUtils.d(TAG, "setIsEnable(true);");
|
||||||
|
TestDemoBindServiceBean.saveBean(this, bean);
|
||||||
|
} else if (intent.getAction().equals(ACTION_DISABLE)) {
|
||||||
|
bean.setIsEnable(false);
|
||||||
|
LogUtils.d(TAG, "setIsEnable(false);");
|
||||||
|
TestDemoBindServiceBean.saveBean(this, bean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
|
||||||
|
//return super.onStartCommand(intent, flags, startId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
LogUtils.d(TAG, "run()");
|
||||||
|
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
|
||||||
|
if (bean == null) {
|
||||||
|
bean = new TestDemoBindServiceBean();
|
||||||
|
TestDemoBindServiceBean.saveBean(this, bean);
|
||||||
|
}
|
||||||
|
if (bean.isEnable()) {
|
||||||
|
LogUtils.d(TAG, "run() bean.isEnable()");
|
||||||
|
TestThread.getInstance(this).start();
|
||||||
|
LogUtils.d(TAG, "_TestThread.start()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
LogUtils.d(TAG, "onDestroy()");
|
||||||
|
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
|
||||||
|
if (bean == null) {
|
||||||
|
bean = new TestDemoBindServiceBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
TestThread.getInstance(this).setIsExit(true);
|
||||||
|
|
||||||
|
// 预防 APPBase 应用重启绑定失效。
|
||||||
|
// 所以退出时检查本服务是否配置启用,如果启用就发送一个 SOS 信号。
|
||||||
|
// 这样 APPBase 就会用组件方式启动本服务。
|
||||||
|
if (bean.isEnable()) {
|
||||||
|
if (App.isDebuging()) {
|
||||||
|
SOS.sosToAppBaseBeta(this, TestDemoBindService.class.getName());
|
||||||
|
} else {
|
||||||
|
SOS.sosToAppBase(this, TestDemoBindService.class.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_IsRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestThread extends Thread {
|
||||||
|
|
||||||
|
volatile static TestThread _TestThread;
|
||||||
|
Context mContext;
|
||||||
|
volatile boolean isStarted = false;
|
||||||
|
volatile boolean isExit = false;
|
||||||
|
|
||||||
|
TestThread(Context context) {
|
||||||
|
super();
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized TestThread getInstance(Context context) {
|
||||||
|
if (_TestThread != null) {
|
||||||
|
_TestThread.setIsExit(true);
|
||||||
|
}
|
||||||
|
_TestThread = new TestThread(context);
|
||||||
|
|
||||||
|
return _TestThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setIsExit(boolean isExit) {
|
||||||
|
this.isExit = isExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExit() {
|
||||||
|
return isExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isStarted == false) {
|
||||||
|
isStarted = true;
|
||||||
|
super.run();
|
||||||
|
LogUtils.d(TAG, "run() start");
|
||||||
|
if (App.isDebuging()) {
|
||||||
|
WinBoLL.bindToAPPBaseBeta(mContext, TestDemoBindService.class.getName());
|
||||||
|
} else {
|
||||||
|
WinBoLL.bindToAPPBase(mContext, TestDemoBindService.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!isExit()) {
|
||||||
|
LogUtils.d(TAG, "run()");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogUtils.d(TAG, "run() exit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package cc.winboll.studio.winboll.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.winboll.models.TestDemoServiceBean;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.winboll.models.TestDemoServiceBean;
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
winboll/src/main/java/cc/winboll/studio/winboll/sos/SOS.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package cc.winboll.studio.winboll.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
package cc.winboll.studio.winboll.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package cc.winboll.studio.winboll.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package cc.winboll.studio.winboll.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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package cc.winboll.studio.winboll.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package cc.winboll.studio.winboll.threads;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||||
|
* @Date 2025/02/14 03:46:44
|
||||||
|
*/
|
||||||
|
import android.content.Context;
|
||||||
|
import cc.winboll.studio.winboll.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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
package cc.winboll.studio.winboll.utils;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.winboll.App;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
|
* @Date 2025/11/27 08:56
|
||||||
|
* @Describe APPPlusUtils
|
||||||
|
*/
|
||||||
|
public class APPPlusUtils {
|
||||||
|
public static final String TAG = "APPPlusUtils";
|
||||||
|
|
||||||
|
// 快捷方式配置(名称+图标,需与实际资源匹配)
|
||||||
|
// private static final String PLUS_SHORTCUT_NAME = "位置服务-Laojun";
|
||||||
|
// private static final int PLUS_SHORTCUT_ICON = R.mipmap.ic_launcher; // Laojun 图标资源
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加Plus组件与图标
|
||||||
|
*/
|
||||||
|
public static boolean switchAppLauncherToComponent(Context context, String componentName) {
|
||||||
|
if (context == null) {
|
||||||
|
LogUtils.d(TAG, "切换失败:上下文为空");
|
||||||
|
Toast.makeText(context, context.getString(R.string.app_name) + "图标切换失败", Toast.LENGTH_SHORT).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageManager pm = context.getPackageManager();
|
||||||
|
|
||||||
|
ComponentName plusComponentSwitchTo = new ComponentName(context, componentName);
|
||||||
|
ComponentName plusComponentEN1 = new ComponentName(context, App.COMPONENT_EN1);
|
||||||
|
ComponentName plusComponentCN1 = new ComponentName(context, App.COMPONENT_CN1);
|
||||||
|
ComponentName plusComponentCN2 = new ComponentName(context, App.COMPONENT_CN2);
|
||||||
|
|
||||||
|
try {
|
||||||
|
disableComponent(pm, plusComponentEN1);
|
||||||
|
disableComponent(pm, plusComponentCN1);
|
||||||
|
disableComponent(pm, plusComponentCN2);
|
||||||
|
enableComponent(pm, plusComponentSwitchTo);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.e(TAG, "图标切换失败:" + e.getMessage());
|
||||||
|
Toast.makeText(context, context.getString(R.string.app_name) + "图标切换失败" + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建指定组件的桌面快捷方式(自动去重,兼容 Android 8.0+)
|
||||||
|
* @param component 目标组件(如 LAOJUN_ACTIVITY)
|
||||||
|
* @param name 快捷方式名称
|
||||||
|
* @param iconRes 快捷方式图标资源ID
|
||||||
|
* @return 是否创建成功
|
||||||
|
*/
|
||||||
|
private static boolean createComponentShortcut(Context context, ComponentName component, String name, int iconRes) {
|
||||||
|
if (context == null || component == null || name == null || iconRes == 0) {
|
||||||
|
LogUtils.d(TAG, "快捷方式创建失败:参数为空");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Android 8.0+(API 26+):使用 ShortcutManager(系统推荐)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
try {
|
||||||
|
PackageManager pm = context.getPackageManager();
|
||||||
|
android.content.pm.ShortcutManager shortcutManager = context.getSystemService(android.content.pm.ShortcutManager.class);
|
||||||
|
if (shortcutManager == null || !shortcutManager.isRequestPinShortcutSupported()) {
|
||||||
|
LogUtils.d(TAG, "系统不支持创建快捷方式");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已存在该组件的快捷方式(去重)
|
||||||
|
for (android.content.pm.ShortcutInfo info : shortcutManager.getPinnedShortcuts()) {
|
||||||
|
if (component.getClassName().equals(info.getIntent().getComponent().getClassName())) {
|
||||||
|
LogUtils.d(TAG, "快捷方式已存在:" + component.getClassName());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建启动目标组件的意图
|
||||||
|
Intent launchIntent = new Intent(Intent.ACTION_MAIN)
|
||||||
|
.setComponent(component)
|
||||||
|
.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
|
||||||
|
// 构建快捷方式信息
|
||||||
|
android.content.pm.ShortcutInfo shortcutInfo = new android.content.pm.ShortcutInfo.Builder(context, component.getClassName())
|
||||||
|
.setShortLabel(name)
|
||||||
|
.setLongLabel(name)
|
||||||
|
.setIcon(android.graphics.drawable.Icon.createWithResource(context, iconRes))
|
||||||
|
.setIntent(launchIntent)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 请求创建快捷方式(需用户确认)
|
||||||
|
shortcutManager.requestPinShortcut(shortcutInfo, null);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.d(TAG, "Android O+ 快捷方式创建失败:" + e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Android 8.0 以下:使用广播(兼容旧机型)
|
||||||
|
try {
|
||||||
|
// 构建启动目标组件的意图
|
||||||
|
Intent launchIntent = new Intent(Intent.ACTION_MAIN)
|
||||||
|
.setComponent(component)
|
||||||
|
.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
|
||||||
|
// 构建创建快捷方式的广播意图
|
||||||
|
Intent installIntent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
|
||||||
|
installIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent);
|
||||||
|
installIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
|
||||||
|
installIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
|
||||||
|
Intent.ShortcutIconResource.fromContext(context, iconRes));
|
||||||
|
installIntent.putExtra("duplicate", false); // 禁止重复创建
|
||||||
|
|
||||||
|
context.sendBroadcast(installIntent);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.d(TAG, "Android O- 快捷方式创建失败:" + e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用组件(带状态检查,避免重复操作)
|
||||||
|
*/
|
||||||
|
private static void enableComponent(PackageManager pm, ComponentName component) {
|
||||||
|
if (pm.getComponentEnabledSetting(component) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
|
||||||
|
pm.setComponentEnabledSetting(
|
||||||
|
component,
|
||||||
|
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||||
|
PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁用组件(带状态检查,避免重复操作)
|
||||||
|
*/
|
||||||
|
private static void disableComponent(PackageManager pm, ComponentName component) {
|
||||||
|
if (pm.getComponentEnabledSetting(component) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
|
||||||
|
pm.setComponentEnabledSetting(
|
||||||
|
component,
|
||||||
|
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||||
|
PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
package cc.winboll.studio.winboll.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package cc.winboll.studio.winboll.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen<zhangsken@188.com>
|
||||||
|
* @Date 2025/06/08 09:05
|
||||||
|
* @Describe Termux 应用操作工具集
|
||||||
|
*/
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
public abstract class TermuxUtils {
|
||||||
|
|
||||||
|
public static final String TAG = "TermuxUtils";
|
||||||
|
|
||||||
|
private void runTermuxCommand(String command) {
|
||||||
|
// 1. 创建 Intent,指定 Termux 的 RunCommandService
|
||||||
|
/*Intent intent = new Intent("com.termux.RUN_COMMAND");
|
||||||
|
intent.setPackage("com.termux"); // Termux 应用的包名
|
||||||
|
|
||||||
|
// 2. 传递命令参数(必填)
|
||||||
|
//intent.putExtra("command", command);
|
||||||
|
intent.putExtra("cd ~/WinBoLL&&echo 'WinBoLL cmd exec at (data", command);
|
||||||
|
|
||||||
|
// 3. 可选:设置工作目录(默认为 Termux 的 home 目录)
|
||||||
|
intent.putExtra("dir", "/data/data/com.termux/files/home/WinBoLL");
|
||||||
|
|
||||||
|
// 4. 发送 Intent(需处理可能的安全异常或 ActivityNotFoundException)
|
||||||
|
try {
|
||||||
|
getApplicationContext().startService(intent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
package cc.winboll.studio.winboll.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 com.google.gson.Gson;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
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 cc.winboll.studio.winboll.models.UserInfoModel;
|
||||||
|
import cc.winboll.studio.winboll.models.ResponseData;
|
||||||
|
import cc.winboll.studio.libapputils.utils.FileUtils;
|
||||||
|
|
||||||
|
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.readByteArrayFromFile(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
package cc.winboll.studio.winboll.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.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libapputils.utils.AppUtils;
|
||||||
|
import cc.winboll.studio.winboll.R;
|
||||||
|
import cc.winboll.studio.winboll.WinBoLL;
|
||||||
|
import cc.winboll.studio.winboll.models.WinBoLLModel;
|
||||||
|
import cc.winboll.studio.winboll.models.WinBoLLNewsBean;
|
||||||
|
import cc.winboll.studio.winboll.receivers.APPNewsWidgetClickListener;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class APPNewsWidget extends AppWidgetProvider {
|
||||||
|
|
||||||
|
public static final String TAG = "APPNewsWidget";
|
||||||
|
|
||||||
|
public static final String ACTION_WAKEUP_SERVICE = APPNewsWidget.class.getName() + ".ACTION_WAKEUP_SERVICE";
|
||||||
|
public static final String ACTION_RELOAD_REPORT = APPNewsWidget.class.getName() + ".ACTION_RELOAD_REPORT";
|
||||||
|
|
||||||
|
|
||||||
|
volatile static ArrayList<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package cc.winboll.studio.winboll.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.winboll.R;
|
||||||
|
import cc.winboll.studio.libappbase.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package cc.winboll.studio.winboll.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.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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
winboll/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportHeight="108"
|
||||||
|
android:viewportWidth="108">
|
||||||
|
<path
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||||
|
android:strokeColor="#00000000"
|
||||||
|
android:strokeWidth="1">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="78.5885"
|
||||||
|
android:endY="90.9159"
|
||||||
|
android:startX="48.7653"
|
||||||
|
android:startY="61.0927"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||||
|
android:strokeColor="#00000000"
|
||||||
|
android:strokeWidth="1" />
|
||||||
|
</vector>
|
||||||
41
winboll/src/main/res/drawable/bg_shadow.xml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?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>
|
||||||
11
winboll/src/main/res/drawable/ic_cloud.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?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>
|
||||||
11
winboll/src/main/res/drawable/ic_cloud_outline.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?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>
|
||||||
11
winboll/src/main/res/drawable/ic_dev_connected.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?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>
|
||||||
11
winboll/src/main/res/drawable/ic_dev_disconnected.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?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>
|
||||||
11
winboll/src/main/res/drawable/ic_email.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?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>
|
||||||
11
winboll/src/main/res/drawable/ic_email_alert.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?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>
|
||||||
35
winboll/src/main/res/drawable/ic_iw.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="1565dp"
|
||||||
|
android:height="1565dp"
|
||||||
|
android:viewportWidth="1565"
|
||||||
|
android:viewportHeight="1565">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:strokeColor="#FFFFFFFF"
|
||||||
|
android:strokeWidth="4.0"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeMiterLimit="10"
|
||||||
|
android:pathData="M793.8 222.7C782.5 239.2 736.3 298.6 685.6 361.9 647 410.2 633.3 428.9 633.6 432.7 633.7 433.4 633.8 434.6 633.9 435.3 634.1 437.8 647.9 440.7 663.8 441.5 706.7 443.8 717 445.3 724.9 450.6 733.7 456.4 735.9 467.4 736 504.2 736 504.2 736 521.9 736 521.9 736 521.9 730.8 522.4 730.8 522.4 727.9 522.8 691.8 525.3 650.5 528 609.3 530.8 571.9 533.3 567.5 533.6 531.8 536 503.4 540.5 487.4 546 475 550.3 442.2 566.5 428.2 575.2 392.1 597.6 360.1 629.2 338 664.3 317 697.7 304.6 729.6 292.5 781 287.8 801 283.9 805.6 267.2 811 255.1 814.9 248.7 818.3 243.4 823.5 239.7 827.2 239.2 828.4 237.7 836.5 236.7 841.5 235.5 850 235 855.5 234.3 863.9 232.6 911.1 232.6 924.5 232.6 934.6 234.2 959.8 235 963 237.4 972 242.3 975.8 258.7 981.7 271.7 986.3 277.4 989.5 280.4 993.9 283.1 997.9 286.5 1008.7 287.4 1016.5 289.3 1031.7 293.3 1050.2 297.7 1064 306.4 1091.9 317.6 1107.7 330 1109.5 332.4 1109.9 334.7 1110.3 335 1110.5 335.3 1110.6 339.1 1111.1 343.5 1111.4 347.9 1111.8 353.8 1112.3 356.5 1112.5 370 1113.7 407.8 1115.8 430 1116.5 439.6 1116.8 453.4 1117.3 460.5 1117.5 467.7 1117.8 482.3 1118.2 493 1118.5 503.7 1118.8 520.8 1119.2 531 1119.5 587.8 1121.1 683 1122 794.5 1122 918.8 1122 996.1 1121 1075 1118.5 1083.5 1118.2 1098.6 1117.8 1108.5 1117.4 1118.4 1117.1 1129.4 1116.7 1133 1116.5 1136.6 1116.3 1144.7 1115.8 1151 1115.5 1183.3 1114 1207.5 1110.9 1221.5 1106.3 1235.3 1101.9 1244.8 1094.1 1249.9 1083.2 1253.5 1075.4 1253.7 1074.4 1259 1044 1266.4 1001.7 1269.1 993.5 1276.9 989.2 1278.9 988.1 1284.7 986.1 1289.9 984.6 1305.8 980.1 1313.3 975.5 1314.4 969.7 1316.2 959.5 1317.1 881.4 1315.7 859.5 1314 834.8 1313.1 831.4 1307 824.7 1301.2 818.3 1295 814.8 1283 811 1278 809.5 1272.4 807 1270.5 805.6 1264 800.6 1259.5 790.1 1252.5 763 1243.5 728.4 1235.9 706.8 1224.6 683.5 1202.3 637.7 1167.5 602.5 1111.8 569.1 1087.6 554.7 1054.4 542.7 1028.1 539 1024.1 538.4 1020.5 537.8 1020.1 537.6 1019.8 537.4 1016.4 536.9 1012.7 536.5 1009 536.2 1005.1 535.7 1004.2 535.5 999.7 534.7 988.4 533.6 973.5 532.5 969.7 532.2 964.5 531.8 962 531.5 959.5 531.2 940.4 530.1 919.5 529 851.7 525.4 836.1 523.7 829.5 519.4 821.2 514 825.2 495.6 837.6 481.8 843.8 474.9 844.2 474.3 856.8 448 862.6 436 868.9 425.2 879.9 408.7 888.2 396.2 895 385.5 895 385.1 895 384.6 889.7 383.3 883.3 382 851.1 375.9 812.7 367.8 806.9 365.9 792.4 361.2 790.7 358.7 790.2 342.5 789.7 326.8 792.4 303.4 800 258.5 802.1 246.1 803.3 220.9 801.9 219.6 801.7 219.4 800.5 218.9 799.3 218.6 797.4 218 796.5 218.7 793.8 222.7Z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF62686C"
|
||||||
|
android:strokeColor="#FF62686C"
|
||||||
|
android:strokeWidth="4.0"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeMiterLimit="10"
|
||||||
|
android:pathData="M570.4 704.1C553.5 706.1 539.7 713 532.9 722.7 523.5 736.3 519.9 764.2 519.6 826 519.5 859.4 519.7 860.6 530.1 872.9 543.4 888.6 560.4 900 574.1 902.1 586.7 904 601.5 898.9 615.7 887.5 632.5 874.1 633.5 868.9 632.7 793.6 632.2 742.5 631.6 736 627 725.2 620.5 710 596.2 700.9 570.4 704.1Z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF62686C"
|
||||||
|
android:strokeColor="#FF62686C"
|
||||||
|
android:strokeWidth="4.0"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeMiterLimit="10"
|
||||||
|
android:pathData="M949.2 705.5C935.1 708.7 927 716.8 922.6 732.4 918.4 746.7 917.8 757.3 917.3 816.5 917.1 847.8 917.3 874 917.7 874.6 919.2 876.7 945.3 892.3 955.5 897.1 973.4 905.5 983.5 905.4 978.9 896.8 977.1 893.4 979.1 892.6 985.9 893.9 993.6 895.4 997.8 894.3 1005.2 889.1 1010.9 885 1020.2 873.6 1023.8 866.5 1029.6 854.9 1031.1 841.6 1031.1 800.5 1031.1 750.1 1028.6 737.6 1014.9 720.4 1009.4 713.5 1004.6 709.8 996.9 706.8 992.2 704.9 989.1 704.6 973.5 704.4 960.6 704.1 953.7 704.5 949.2 705.5Z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF62686C"
|
||||||
|
android:strokeColor="#FF62686C"
|
||||||
|
android:strokeWidth="4.0"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeMiterLimit="10"
|
||||||
|
android:pathData="M717 988.7C671.1 990.4 640.9 994.2 635.6 998.8 631.5 1002.5 636.3 1017.5 643.6 1024 650.9 1030.3 661.7 1032.5 694 1034 721.9 1035.3 829.3 1035.3 857 1034 891.4 1032.4 901.4 1029.9 908.9 1021.3 914.5 1015 917.9 1003.4 915.2 999.4 910.4 992.1 856 987.9 772 988.2 745.9 988.3 721.1 988.5 717 988.7Z"/>
|
||||||
|
</vector>
|
||||||
13
winboll/src/main/res/drawable/ic_launcher.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?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>
|
||||||
170
winboll/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportHeight="108"
|
||||||
|
android:viewportWidth="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#26A69A"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeColor="#33FFFFFF"
|
||||||
|
android:strokeWidth="0.8" />
|
||||||
|
</vector>
|
||||||
13
winboll/src/main/res/drawable/ic_launcher_beta.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:clickable="true"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp">
|
||||||
|
<item android:drawable="@drawable/ic_launcher_background"/>
|
||||||
|
<item
|
||||||
|
android:left="0dp"
|
||||||
|
android:top="0dp"
|
||||||
|
android:right="0dp"
|
||||||
|
android:bottom="0dp"
|
||||||
|
android:drawable="@drawable/ic_iw"/>
|
||||||
|
</layer-list>
|
||||||
13
winboll/src/main/res/drawable/ic_launcher_disable.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?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>
|
||||||
10
winboll/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?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>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?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>
|
||||||
BIN
winboll/src/main/res/drawable/ic_launcher_stage.jpg
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
winboll/src/main/res/drawable/ic_miapp.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
27
winboll/src/main/res/drawable/ic_winboll_help.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<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>
|
||||||
41
winboll/src/main/res/drawable/ic_winboll_log.xml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<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>
|
||||||
48
winboll/src/main/res/drawable/ic_winboll_logo.xml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<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"/>
|
||||||
|
<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"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFFFF"
|
||||||
|
android:strokeWidth="30.0"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeMiterLimit="10"
|
||||||
|
android:pathData="M103.77 307.45C103.02 293.07 104.03 271.24 129.24 248.85 135.25 243.52 141.74 239.48 147.89 236.44 160.24 230.34 171.23 228.28 174.15 227.92 174.31 227.9 174.44 227.89 174.55 227.88 174.66 227.87 174.75 227.86 174.81 227.86 176.62 227.85 216.5 230.02 234.65 253.79 253.9 278.99 266.43 314.66 221.67 356.93 184.69 391.85 179.97 411.36 180.15 411.08"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFFFF"
|
||||||
|
android:strokeWidth="30.0"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeMiterLimit="10"
|
||||||
|
android:pathData="M248.17 309.83C247.43 295.45 248.43 273.62 273.64 251.23 279.65 245.9 286.14 241.86 292.29 238.82 304.65 232.72 315.63 230.65 318.55 230.3 318.71 230.28 318.84 230.27 318.95 230.26 319.06 230.25 319.15 230.24 319.21 230.24 321.02 230.22 360.9 232.4 379.06 256.17 398.3 281.37 410.83 317.04 366.08 359.31 329.09 394.23 324.37 413.74 324.55 413.46"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFFFF"
|
||||||
|
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>
|
||||||
20
winboll/src/main/res/drawable/ic_winboll_point.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<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>
|
||||||
10
winboll/src/main/res/drawable/shape_gradient.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?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>
|
||||||
8
winboll/src/main/res/drawable/view_border.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?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>
|
||||||
11
winboll/src/main/res/layout/activity_about.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?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:id="@+id/aboutviewroot_ll">
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
68
winboll/src/main/res/layout/activity_logon.xml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
46
winboll/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<cc.winboll.studio.winboll.CustomToolbar
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:toolbarTitle="@string/app_name"
|
||||||
|
app:toolbarTitleColor="@color/colorAccent"
|
||||||
|
app:toolbarBackgroundColor="@color/colorPrimary"
|
||||||
|
android:id="@+id/toolbar"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1.0"
|
||||||
|
android:gravity="center_vertical|center_horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Hello, WinBoLL!"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"/>
|
||||||
|
|
||||||
|
</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>
|
||||||
|
|
||||||
43
winboll/src/main/res/layout/activity_new.xml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
43
winboll/src/main/res/layout/activity_new2.xml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
215
winboll/src/main/res/layout/activity_winbollunittest.xml
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
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"/>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
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: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="Android 11(内部代号 Red Velvet Cake)是谷歌于2020年9月8日正式发布的移动端操作系统,核心升级聚焦隐私安全强化、通话与通知体验优化、5G与折叠屏适配三大方向,为用户和开发者提供更安全、灵活的使用与开发环境。\n\n一、核心升级:隐私安全(最关键变化)\n\nAndroid 11进一步收紧应用权限管控,从“被动授权”转向“主动保护用户数据”,核心变化包括:\n\n- 一次性权限:首次申请定位、麦克风、相机等敏感权限时,新增“仅本次允许”选项,应用退出后权限自动失效,避免长期授权泄露数据。\n- 后台位置权限限制:应用需先获得“前台定位权限”,才能申请“后台定位”,且用户可单独关闭后台定位(此前需完全关闭定位权限),减少后台偷跑定位的情况。\n- 存储权限细分:将原“存储”权限拆分为“媒体文件”和“所有文件”,普通应用仅能访问媒体文件,需访问其他文件(如文档)时需单独申请“所有文件访问权限”,降低存储数据泄露风险。\n\n二、体验优化:通知与通话\n\n- 通知分类管理:通知栏新增“对话”“提醒”“无声”三大分类,微信、短信等实时对话会单独归为“对话”分类,支持置顶、标记未读,重要信息更易查找。\n- 通话与通知优先级优化:来电、闹钟等重要提醒会以“浮动窗口”形式显示(非全屏遮挡),不打断当前操作(如看视频、玩游戏),用户可快速选择接听/挂断。\n- 通知历史记录:新增“通知历史”功能,可查看过去24小时内被清除的通知,避免误删重要信息。\n\n三、开发者与硬件适配\n\n- 5G网络优化:新增5G网络状态API,支持应用识别“5G高速”“5G低延迟”等场景,例如视频App可自动切换高清画质,游戏App可优化网络传输策略。\n- 折叠屏适配:提供“折叠状态监听”“多窗口模式增强”等API,帮助开发者适配折叠屏设备,避免屏幕折叠/展开时界面错乱。\n- Scoped Storage(分区存储)强制启用:要求应用适配“分区存储”机制(Android 10为可选),应用默认只能访问自身专属目录和公共媒体目录,无法随意读写手机存储,提升系统安全性。\n\n四、其他实用功能\n\n- 电源菜单集成控制:长按电源键时,可直接显示智能家居控制(如灯光、空调),无需单独打开智能家居App。\n- 无线调试:支持通过Wi-Fi连接电脑进行ADB调试,无需USB线,方便开发者测试设备。\n- 屏幕录制升级:系统自带屏幕录制功能支持录制声音(包括麦克风声音),并可选择是否显示触控操作。\n\n综上,Android 11的核心定位是“安全与体验的精细化升级”,通过隐私权限的层层收紧保护用户数据,同时针对5G、折叠屏等新硬件做了前瞻性适配,是Android系统向“更安全、更智能”迭代的重要版本。"/>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Edit TimeStamp: 20250915_180359_071"/>
|
||||||
|
|
||||||
|
|
||||||
|
<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"/>
|
||||||
|
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<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"/>
|
||||||
|
|
||||||
|
</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>
|
||||||
|
|
||||||
63
winboll/src/main/res/layout/activity_yun.xml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
24
winboll/src/main/res/layout/fragment_main.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Position Service"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:id="@+id/fragmentmainSwitch1"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
29
winboll/src/main/res/layout/view_toolbar.xml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout
|
||||||
|
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">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/toolbar_icon"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:src="@drawable/ic_iw"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:contentDescription="@string/toolbar_icon_description" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/toolbar_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="#ffffff"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toEndOf="@id/toolbar_icon"
|
||||||
|
android:layout_marginStart="16dp" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
51
winboll/src/main/res/layout/widget_news.xml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
15
winboll/src/main/res/layout/widget_status.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
20
winboll/src/main/res/menu/toolbar_main.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/item_home"
|
||||||
|
android:title="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>
|
||||||
5
winboll/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
BIN
winboll/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
winboll/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
winboll/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
winboll/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
winboll/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
winboll/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |