Compare commits
189 Commits
appbase-v1
...
appbase-v1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d9b99ee371 | ||
![]() |
6f7414b576 | ||
![]() |
2f6e297bab | ||
![]() |
e35a6b9a7f | ||
![]() |
e71e5a852d | ||
![]() |
4ea48a051d | ||
![]() |
ae680b5b48 | ||
![]() |
80767ca5b5 | ||
![]() |
be5ed936df | ||
![]() |
b3075f778d | ||
![]() |
ce05d69132 | ||
![]() |
6a9434b194 | ||
![]() |
6ca7b210a6 | ||
![]() |
d83d460342 | ||
![]() |
9a53d04050 | ||
![]() |
ada664f4ab | ||
![]() |
92a9bbb455 | ||
![]() |
211eef7232 | ||
![]() |
31bad8a279 | ||
![]() |
bc0eb69edf | ||
![]() |
08f990de65 | ||
![]() |
e2b6fadd43 | ||
![]() |
2074f68ef3 | ||
![]() |
56a13b4a40 | ||
![]() |
8bdd42ce68 | ||
![]() |
d4986c6ba4 | ||
![]() |
529f420668 | ||
![]() |
b3439aa36a | ||
![]() |
7488fa7af2 | ||
![]() |
d4e4541bfd | ||
![]() |
ac627e53b1 | ||
![]() |
42a2e6d559 | ||
![]() |
0d44734c0b | ||
![]() |
e89873046b | ||
![]() |
f3ef7226d8 | ||
![]() |
9706a39f41 | ||
![]() |
e85cbd803e | ||
![]() |
00a9ec1d66 | ||
![]() |
64051bb9fe | ||
![]() |
5e42f21c9a | ||
![]() |
c394a37e0a | ||
![]() |
dd7600d617 | ||
![]() |
c203557a6a | ||
![]() |
00b619ee99 | ||
![]() |
3a6fb3e17c | ||
![]() |
75c2f6304c | ||
![]() |
ec6926f629 | ||
![]() |
ede6c07deb | ||
![]() |
3fe1ea7ff2 | ||
![]() |
5411d5e590 | ||
![]() |
facf0b001d | ||
![]() |
df58c0355c | ||
![]() |
e657e45218 | ||
![]() |
c22946f0a5 | ||
![]() |
1363a7dcce | ||
![]() |
e0ca5725f1 | ||
![]() |
3c4988532f | ||
![]() |
b0a34579fa | ||
![]() |
dd08747edb | ||
![]() |
603640b3cb | ||
![]() |
5b06ce9699 | ||
![]() |
363fc6c7c1 | ||
![]() |
7f43f8b5c5 | ||
![]() |
a117e6e110 | ||
![]() |
c1f576e343 | ||
![]() |
1dad84b65e | ||
![]() |
1ae377e665 | ||
![]() |
02fa0a4134 | ||
![]() |
a730aa3f92 | ||
![]() |
befea40f61 | ||
![]() |
cc1ca5b092 | ||
![]() |
af77c09415 | ||
![]() |
2968d1dbfa | ||
![]() |
db96ece15f | ||
![]() |
c0347ed706 | ||
![]() |
ad408dfb53 | ||
![]() |
953da400be | ||
![]() |
165765e0eb | ||
![]() |
f5f88a61b0 | ||
![]() |
2f33574d5c | ||
![]() |
afd93d6871 | ||
![]() |
b7affa477f | ||
![]() |
4f317322fc | ||
![]() |
5623d2d341 | ||
![]() |
3d9b203760 | ||
![]() |
498a8ae458 | ||
![]() |
9b8d6ad46b | ||
![]() |
482f289df3 | ||
![]() |
28ab4e2859 | ||
![]() |
fd0cb05812 | ||
![]() |
39b6c0ec89 | ||
![]() |
3b3ddbc8c9 | ||
![]() |
38aee09f87 | ||
![]() |
89c4d00091 | ||
![]() |
4f57678012 | ||
![]() |
b31e55d8ca | ||
![]() |
80129470d7 | ||
![]() |
7b03e8302a | ||
![]() |
4d321dde84 | ||
![]() |
b3a495ff6a | ||
![]() |
a49b6cbfe0 | ||
![]() |
274a7135e7 | ||
![]() |
00d0871809 | ||
![]() |
bc252041d3 | ||
![]() |
bca31f9079 | ||
![]() |
fb7017a857 | ||
![]() |
820574a4ba | ||
![]() |
36368bd28e | ||
![]() |
90e66889a9 | ||
![]() |
3031c9bfe0 | ||
![]() |
51acbb1f54 | ||
![]() |
26fc0b4684 | ||
![]() |
ecb67edec1 | ||
![]() |
3b30acfdec | ||
![]() |
c633e1663b | ||
![]() |
40589ad879 | ||
![]() |
0357fcf6b3 | ||
![]() |
c56fdaf14d | ||
![]() |
9b1264234e | ||
![]() |
d207a9ea9b | ||
![]() |
404239de23 | ||
![]() |
c16b80ffae | ||
![]() |
0bf0a1497a | ||
![]() |
2cc3850c95 | ||
![]() |
ca16a8677a | ||
![]() |
bb089a4eb1 | ||
![]() |
0c00ace869 | ||
![]() |
e06b642b70 | ||
![]() |
5e107028de | ||
![]() |
3bcfc5a5da | ||
![]() |
63c10e6dc9 | ||
![]() |
539afd0861 | ||
![]() |
234d2d57de | ||
![]() |
e849e54960 | ||
![]() |
8b2866419c | ||
![]() |
22a4a71203 | ||
![]() |
c16c6bba74 | ||
![]() |
c590c96fa6 | ||
![]() |
64a9712294 | ||
![]() |
e5dfd5b975 | ||
![]() |
ffac543f6f | ||
![]() |
292f7c553f | ||
![]() |
df9970c6df | ||
![]() |
0312ca50d2 | ||
![]() |
56187b588f | ||
![]() |
d91372af52 | ||
![]() |
288725eca0 | ||
![]() |
bd728b83a3 | ||
![]() |
d4f9fc3561 | ||
![]() |
e7c614ebec | ||
![]() |
c226a92ffe | ||
![]() |
358e8b3522 | ||
![]() |
83ab4f9417 | ||
![]() |
26cdacf1c6 | ||
![]() |
6378433424 | ||
![]() |
7b05d613e4 | ||
![]() |
691f9bbd1c | ||
![]() |
7a8d3329d4 | ||
![]() |
bbac0c7306 | ||
![]() |
8d2b325172 | ||
![]() |
825dfb944e | ||
![]() |
419244b12a | ||
![]() |
39fda1b5da | ||
![]() |
464f2da89f | ||
![]() |
4f37b6f0e3 | ||
![]() |
f91cd0c9c3 | ||
![]() |
d09f0783a4 | ||
![]() |
accca716d4 | ||
![]() |
1baa0c5a61 | ||
![]() |
dfd09eb647 | ||
![]() |
79547adcb3 | ||
![]() |
be39eef6e7 | ||
![]() |
50490096b4 | ||
![]() |
3dd4c84602 | ||
![]() |
b0e81fc960 | ||
![]() |
311bc8e339 | ||
![]() |
6467bcd675 | ||
![]() |
206c804b4d | ||
![]() |
7a4df0444e | ||
![]() |
75b2245061 | ||
![]() |
3c827a56cf | ||
![]() |
36af7947e8 | ||
![]() |
805edcf9c4 | ||
![]() |
11005e3356 | ||
![]() |
75361b2313 | ||
![]() |
1d29ce81e8 | ||
![]() |
0297e81090 | ||
![]() |
df7203e985 | ||
![]() |
8c4e8b8e23 |
1
.gitignore
vendored
@@ -96,6 +96,7 @@ local.properties
|
|||||||
|
|
||||||
## 忽略模块应用编译配置
|
## 忽略模块应用编译配置
|
||||||
/settings.gradle
|
/settings.gradle
|
||||||
|
/gradle.properties
|
||||||
|
|
||||||
## 忽略 srv 纠结问题
|
## 忽略 srv 纠结问题
|
||||||
/srv/
|
/srv/
|
||||||
|
3
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
1
.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
|||||||
|
appbase
|
6
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="17" />
|
||||||
|
</component>
|
||||||
|
</project>
|
10
.idea/deploymentTargetDropDown.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetDropDown">
|
||||||
|
<value>
|
||||||
|
<entry key="appbase">
|
||||||
|
<State />
|
||||||
|
</entry>
|
||||||
|
</value>
|
||||||
|
</component>
|
||||||
|
</project>
|
10
.idea/migrations.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectMigrations">
|
||||||
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
10
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
1
androiddemo/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
0
androiddemo/app_update_description.txt
Normal file
71
androiddemo/build.gradle
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
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.androiddemo"
|
||||||
|
minSdkVersion 26
|
||||||
|
targetSdkVersion 29
|
||||||
|
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.github.getActivity:ToastUtils:10.5'
|
||||||
|
|
||||||
|
// Android 类库
|
||||||
|
api 'com.android.support:appcompat-v7:28.0.0' // 包含 AppCompatActivity
|
||||||
|
// https://mvnrepository.com/artifact/com.android.support/support-compat
|
||||||
|
api 'com.android.support:support-compat:28.0.0' // 保留原有依赖(可选)
|
||||||
|
// https://mvnrepository.com/artifact/com.android.support/support-v4
|
||||||
|
api 'com.android.support:support-v4:28.0.0'
|
||||||
|
// https://mvnrepository.com/artifact/com.android.support/support-media-compat
|
||||||
|
api 'com.android.support:support-media-compat:28.0.0'
|
||||||
|
// https://mvnrepository.com/artifact/com.android.support/support-core-utils
|
||||||
|
api 'com.android.support:support-core-utils:28.0.0'
|
||||||
|
// https://mvnrepository.com/artifact/com.android.support/support-core-ui
|
||||||
|
api 'com.android.support:support-core-ui:28.0.0'
|
||||||
|
// https://mvnrepository.com/artifact/com.android.support/support-fragment
|
||||||
|
api 'com.android.support:support-fragment:28.0.0'
|
||||||
|
// https://mvnrepository.com/artifact/com.android.support/recyclerview-v7
|
||||||
|
api 'com.android.support:recyclerview-v7:28.0.0'
|
||||||
|
|
||||||
|
api 'cc.winboll.studio:libappbase:15.0.9'
|
||||||
|
api 'cc.winboll.studio:libapputils:15.0.11'
|
||||||
|
}
|
8
androiddemo/build.properties
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
|
#Wed Mar 26 07:23:51 GMT 2025
|
||||||
|
stageCount=0
|
||||||
|
libraryProject=
|
||||||
|
baseVersion=15.0
|
||||||
|
publishVersion=15.0.0
|
||||||
|
buildCount=11
|
||||||
|
baseBetaVersion=15.0.1
|
21
androiddemo/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
|
12
androiddemo/src/beta/AndroidManifest.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
<!-- Put flavor specific code here -->
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
7
androiddemo/src/beta/res/values/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="app_name">Android Demo +</string>
|
||||||
|
|
||||||
|
</resources>
|
39
androiddemo/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<manifest
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="cc.winboll.studio.androiddemo">
|
||||||
|
|
||||||
|
<application
|
||||||
|
tools:replace="android:appComponentFactory"
|
||||||
|
android:appComponentFactory="android.support.v4.app.CoreComponentFactory"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/AppTheme"
|
||||||
|
android:resizeableActivity="true"
|
||||||
|
android:name=".App">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:exported="true">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.max_aspect"
|
||||||
|
android:value="4.0"/>
|
||||||
|
|
||||||
|
<activity android:name=".GlobalApplication$CrashActivity"/>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
334
androiddemo/src/main/java/cc/winboll/studio/androiddemo/App.java
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
package cc.winboll.studio.androiddemo;
|
||||||
|
|
||||||
|
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.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.libappbase.GlobalApplication;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
//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,25 @@
|
|||||||
|
package cc.winboll.studio.androiddemo;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import cc.winboll.studio.libappbase.LogView;
|
||||||
|
|
||||||
|
public class MainActivity extends Activity {
|
||||||
|
|
||||||
|
LogView mLogView;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
mLogView = findViewById(R.id.logview);
|
||||||
|
mLogView.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
mLogView.start();
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
37
androiddemo/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:gravity="center_vertical|center_horizontal"
|
||||||
|
android:layout_weight="1.0">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Android Demo"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
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="wrap_content"
|
||||||
|
android:text="Text"
|
||||||
|
android:id="@+id/logview"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
9
androiddemo/src/main/res/values-v21/styles.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="AppTheme" parent="@android:style/Theme.Material.Light.DarkActionBar">
|
||||||
|
<item name="android:colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="android:colorAccent">@color/colorAccent</item>
|
||||||
|
<item name="android:navigationBarColor">?android:colorPrimary</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
6
androiddemo/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#009688</color>
|
||||||
|
<color name="colorPrimaryDark">#00796B</color>
|
||||||
|
<color name="colorAccent">#FF9800</color>
|
||||||
|
</resources>
|
4
androiddemo/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Android Demo</string>
|
||||||
|
</resources>
|
5
androiddemo/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="AppTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
|
||||||
|
</style>
|
||||||
|
</resources>
|
12
androiddemo/src/stage/AndroidManifest.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
<!-- Put flavor specific code here -->
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
6
androiddemo/src/stage/res/values/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Put flavor specific strings here -->
|
||||||
|
|
||||||
|
</resources>
|
1
androidxdemo/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
0
androidxdemo/app_update_description.txt
Normal file
72
androidxdemo/build.gradle
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
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.androidxdemo"
|
||||||
|
minSdkVersion 26
|
||||||
|
targetSdkVersion 29
|
||||||
|
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'])
|
||||||
|
|
||||||
|
// 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.0.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:libappbase:15.0.9'
|
||||||
|
api 'cc.winboll.studio:libapputils:15.0.11'
|
||||||
|
}
|
8
androidxdemo/build.properties
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
|
#Mon Mar 24 06:19:57 GMT 2025
|
||||||
|
stageCount=0
|
||||||
|
libraryProject=
|
||||||
|
baseVersion=15.0
|
||||||
|
publishVersion=15.0.0
|
||||||
|
buildCount=8
|
||||||
|
baseBetaVersion=15.0.1
|
21
androidxdemo/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
|
12
androidxdemo/src/beta/AndroidManifest.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
<!-- Put flavor specific code here -->
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
6
androidxdemo/src/beta/res/values/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">AndroidX Demo +</string>
|
||||||
|
|
||||||
|
</resources>
|
37
androidxdemo/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<manifest
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="cc.winboll.studio.androidxdemo">
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/MyAppTheme"
|
||||||
|
android:resizeableActivity="true"
|
||||||
|
android:name=".App">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:label="@string/app_name">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.max_aspect"
|
||||||
|
android:value="4.0"/>
|
||||||
|
|
||||||
|
<activity android:name=".GlobalApplication$CrashActivity"/>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
@@ -0,0 +1,334 @@
|
|||||||
|
package cc.winboll.studio.androidxdemo;
|
||||||
|
|
||||||
|
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.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.libappbase.GlobalApplication;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
//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,28 @@
|
|||||||
|
package cc.winboll.studio.androidxdemo;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import cc.winboll.studio.libappbase.LogView;
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
LogView mLogView;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
|
mLogView = findViewById(R.id.logview);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
mLogView.start();
|
||||||
|
}
|
||||||
|
}
|
@@ -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>
|
170
androidxdemo/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>
|
51
androidxdemo/src/main/res/layout/activity_main.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">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<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="AndroidX Demo"
|
||||||
|
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>
|
||||||
|
|
@@ -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
androidxdemo/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
androidxdemo/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
androidxdemo/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
androidxdemo/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
androidxdemo/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
androidxdemo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
androidxdemo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 15 KiB |
6
androidxdemo/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#009688</color>
|
||||||
|
<color name="colorPrimaryDark">#00796B</color>
|
||||||
|
<color name="colorAccent">#FF9800</color>
|
||||||
|
</resources>
|
4
androidxdemo/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">AndroidX Demo</string>
|
||||||
|
|
||||||
|
</resources>
|
11
androidxdemo/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="MyAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
12
androidxdemo/src/stage/AndroidManifest.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
<!-- Put flavor specific code here -->
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
6
androidxdemo/src/stage/res/values/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Put flavor specific strings here -->
|
||||||
|
|
||||||
|
</resources>
|
@@ -18,18 +18,19 @@ def genVersionName(def versionName){
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
compileSdkVersion 32
|
compileSdkVersion 32
|
||||||
buildToolsVersion "33.0.3"
|
buildToolsVersion "32.0.0"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "cc.winboll.studio.appbase"
|
applicationId "cc.winboll.studio.appbase"
|
||||||
minSdkVersion 21
|
minSdkVersion 24
|
||||||
targetSdkVersion 30
|
targetSdkVersion 29
|
||||||
versionCode 1
|
versionCode 1
|
||||||
// versionName 更新后需要手动设置
|
// versionName 更新后需要手动设置
|
||||||
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
||||||
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||||
versionName "1.4"
|
versionName "15.1"
|
||||||
if(true) {
|
if(true) {
|
||||||
versionName = genVersionName("${versionName}")
|
versionName = genVersionName("${versionName}")
|
||||||
}
|
}
|
||||||
@@ -41,14 +42,9 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api project(':libappbase')
|
api project(':libappbase')
|
||||||
api fileTree(dir: 'libs', include: ['*.jar'])
|
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Wed Feb 12 06:12:57 GMT 2025
|
#Fri Mar 28 18:20:22 HKT 2025
|
||||||
stageCount=0
|
stageCount=4
|
||||||
libraryProject=libappbase
|
libraryProject=libappbase
|
||||||
baseVersion=1.4
|
baseVersion=15.1
|
||||||
publishVersion=1.4.0
|
publishVersion=15.1.3
|
||||||
buildCount=1
|
buildCount=0
|
||||||
baseBetaVersion=1.4.1
|
baseBetaVersion=15.1.4
|
||||||
|
@@ -7,13 +7,16 @@
|
|||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/MyAPPBaseTheme"
|
||||||
android:resizeableActivity="true">
|
android:resizeableActivity="true"
|
||||||
|
android:process=":App">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:exported="true">
|
android:exported="true"
|
||||||
|
android:resizeableActivity="true"
|
||||||
|
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|
||||||
@@ -21,15 +24,104 @@
|
|||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
|
||||||
|
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</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>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.max_aspect"
|
android:name="android.max_aspect"
|
||||||
android:value="4.0"/>
|
android:value="4.0"/>
|
||||||
|
|
||||||
<activity android:name=".GlobalApplication$CrashActivity"/>
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
@@ -6,18 +6,22 @@ package cc.winboll.studio.appbase;
|
|||||||
* @Describe APPbase 应用类
|
* @Describe APPbase 应用类
|
||||||
*/
|
*/
|
||||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import android.content.IntentFilter;
|
||||||
|
import cc.winboll.studio.libappbase.sos.SOSCenterServiceReceiver;
|
||||||
|
import cc.winboll.studio.libappbase.sos.SOS;
|
||||||
|
|
||||||
public class App extends GlobalApplication {
|
public class App extends GlobalApplication {
|
||||||
|
|
||||||
public static final String TAG = "App";
|
public static final String TAG = "App";
|
||||||
|
|
||||||
|
SOSCenterServiceReceiver mSOSCenterServiceReceiver;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
GlobalApplication.setIsDebuging(this, BuildConfig.DEBUG);
|
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
LogUtils.setLogLevel(LogUtils.LOG_LEVEL.Debug);
|
mSOSCenterServiceReceiver = new SOSCenterServiceReceiver();
|
||||||
LogUtils.setALlTAGListEnable(true);
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
LogUtils.d(TAG, "LogUtils init");
|
intentFilter.addAction(SOS.ACTION_SOS);
|
||||||
|
registerReceiver(mSOSCenterServiceReceiver, intentFilter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,27 +1,183 @@
|
|||||||
package cc.winboll.studio.appbase;
|
package cc.winboll.studio.appbase;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import cc.winboll.studio.appbase.R;
|
||||||
|
import cc.winboll.studio.appbase.activities.NewActivity;
|
||||||
|
import cc.winboll.studio.appbase.services.MainService;
|
||||||
|
import cc.winboll.studio.appbase.services.TestDemoBindService;
|
||||||
|
import cc.winboll.studio.appbase.services.TestDemoService;
|
||||||
|
import cc.winboll.studio.libappbase.CrashHandler;
|
||||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import cc.winboll.studio.libappbase.GlobalCrashActivity;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libappbase.sos.SOS;
|
||||||
|
import cc.winboll.studio.libappbase.utils.ToastUtils;
|
||||||
|
import cc.winboll.studio.libappbase.widgets.StatusWidget;
|
||||||
|
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
|
||||||
|
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends WinBollActivityBase implements IWinBollActivity {
|
||||||
|
|
||||||
|
public static final String TAG = "MainActivity";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Activity getActivity() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toolbar mToolbar;
|
||||||
|
//LogView mLogView;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
ToastUtils.show("onCreate");
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
Toolbar toolbar = findViewById(R.id.activitymainToolbar1);
|
mToolbar = findViewById(R.id.toolbar);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(mToolbar);
|
||||||
|
|
||||||
CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
|
CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
|
||||||
cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
|
cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
|
||||||
|
//mLogView = findViewById(R.id.activitymainLogView1);
|
||||||
|
|
||||||
|
// if (GlobalApplication.isDebuging()) {
|
||||||
|
// mLogView.start();
|
||||||
|
// ToastUtils.show("LogView start.");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
||||||
|
getMenuInflater().inflate(R.menu.toolbar_appbase, menu);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
Intent intentAPPWidget = new Intent(this, StatusWidget.class);
|
||||||
|
intentAPPWidget.setAction(StatusWidget.ACTION_STATUS_UPDATE);
|
||||||
|
sendBroadcast(intentAPPWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSwitchDebugMode(View view) {
|
public void onSwitchDebugMode(View view) {
|
||||||
GlobalApplication.setIsDebuging(this, ((CheckBox)view).isChecked());
|
boolean isDebuging = ((CheckBox)view).isChecked();
|
||||||
|
GlobalApplication.setIsDebuging(isDebuging);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onPreviewGlobalCrashActivity(View view) {
|
||||||
|
Intent intent = new Intent(this, GlobalCrashActivity.class);
|
||||||
|
intent.putExtra(CrashHandler.EXTRA_CRASH_INFO, "Demo log...");
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStartCenter(View view) {
|
||||||
|
MainService.startMainService(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStopCenter(View view) {
|
||||||
|
MainService.stopMainService(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTestStopMainServiceWithoutSettingEnable(View view) {
|
||||||
|
LogUtils.d(TAG, "onTestStopMainServiceWithoutSettingEnable");
|
||||||
|
stopService(new Intent(this, MainService.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTestUseComponentStartService(View view) {
|
||||||
|
LogUtils.d(TAG, "onTestUseComponentStartService");
|
||||||
|
|
||||||
|
// 目标服务的包名和类名
|
||||||
|
String packageName = this.getPackageName();
|
||||||
|
String serviceClassName = TestDemoService.class.getName();
|
||||||
|
|
||||||
|
// 构建Intent
|
||||||
|
Intent intentService = new Intent();
|
||||||
|
intentService.setComponent(new ComponentName(packageName, serviceClassName));
|
||||||
|
|
||||||
|
startService(intentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTestDemoServiceSOS(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoService.class);
|
||||||
|
stopService(intent);
|
||||||
|
if (App.isDebuging()) {
|
||||||
|
SOS.sosToAppBaseBeta(this, TestDemoService.class.getName());
|
||||||
|
} else {
|
||||||
|
SOS.sosToAppBase(this, TestDemoService.class.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSartTestDemoService(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoService.class);
|
||||||
|
intent.setAction(TestDemoService.ACTION_ENABLE);
|
||||||
|
startService(intent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void onStopTestDemoService(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoService.class);
|
||||||
|
intent.setAction(TestDemoService.ACTION_DISABLE);
|
||||||
|
startService(intent);
|
||||||
|
|
||||||
|
Intent intentStop = new Intent(this, TestDemoService.class);
|
||||||
|
stopService(intentStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStopTestDemoServiceNoSettings(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoService.class);
|
||||||
|
stopService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSartTestDemoBindService(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoBindService.class);
|
||||||
|
intent.setAction(TestDemoBindService.ACTION_ENABLE);
|
||||||
|
startService(intent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStopTestDemoBindService(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoBindService.class);
|
||||||
|
intent.setAction(TestDemoBindService.ACTION_DISABLE);
|
||||||
|
startService(intent);
|
||||||
|
|
||||||
|
Intent intentStop = new Intent(this, TestDemoBindService.class);
|
||||||
|
stopService(intentStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStopTestDemoBindServiceNoSettings(View view) {
|
||||||
|
Intent intent = new Intent(this, TestDemoBindService.class);
|
||||||
|
stopService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTestOpenNewActivity(View view) {
|
||||||
|
GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, NewActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,79 @@
|
|||||||
|
package cc.winboll.studio.appbase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/02/13 19:30:10
|
||||||
|
*/
|
||||||
|
import android.content.Context;
|
||||||
|
import android.service.quicksettings.Tile;
|
||||||
|
import android.service.quicksettings.TileService;
|
||||||
|
import cc.winboll.studio.appbase.models.MainServiceBean;
|
||||||
|
import cc.winboll.studio.appbase.services.MainService;
|
||||||
|
|
||||||
|
public class MyTileService extends TileService {
|
||||||
|
public static final String TAG = "MyTileService";
|
||||||
|
|
||||||
|
volatile static MyTileService _MyTileService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartListening() {
|
||||||
|
super.onStartListening();
|
||||||
|
_MyTileService = this;
|
||||||
|
Tile tile = getQsTile();
|
||||||
|
MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||||
|
if (bean != null && bean.isEnable()) {
|
||||||
|
//MainService.startMainService(context);
|
||||||
|
tile.setState(Tile.STATE_ACTIVE);
|
||||||
|
tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud));
|
||||||
|
} else {
|
||||||
|
//MainService.stopMainService(context);
|
||||||
|
tile.setState(Tile.STATE_INACTIVE);
|
||||||
|
tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
|
||||||
|
}
|
||||||
|
tile.updateTile();
|
||||||
|
// Tile tile = getQsTile();
|
||||||
|
// tile.setState(Tile.STATE_INACTIVE);
|
||||||
|
// tile.setLabel(getString(R.string.tileservice_name));
|
||||||
|
// tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
|
||||||
|
// tile.updateTile();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick() {
|
||||||
|
super.onClick();
|
||||||
|
Tile tile = getQsTile();
|
||||||
|
MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||||
|
if (bean == null) {
|
||||||
|
bean = new MainServiceBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tile.getState() == Tile.STATE_ACTIVE) {
|
||||||
|
bean.setIsEnable(false);
|
||||||
|
MainServiceBean.saveBean(this, bean);
|
||||||
|
MainService.stopMainService(this);
|
||||||
|
} else if (tile.getState() == Tile.STATE_INACTIVE) {
|
||||||
|
bean.setIsEnable(true);
|
||||||
|
MainServiceBean.saveBean(this, bean);
|
||||||
|
MainService.startMainService(this);
|
||||||
|
}
|
||||||
|
updateServiceIconStatus(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateServiceIconStatus(Context context) {
|
||||||
|
if (_MyTileService == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile tile = _MyTileService.getQsTile();
|
||||||
|
MainServiceBean bean = MainServiceBean.loadBean(context, MainServiceBean.class);
|
||||||
|
if (bean != null && bean.isEnable()) {
|
||||||
|
tile.setState(Tile.STATE_ACTIVE);
|
||||||
|
tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud));
|
||||||
|
} else {
|
||||||
|
tile.setState(Tile.STATE_INACTIVE);
|
||||||
|
tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud_outline));
|
||||||
|
}
|
||||||
|
tile.updateTile();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,82 @@
|
|||||||
|
package cc.winboll.studio.appbase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/03/28 15:34:16
|
||||||
|
* @Describe 应用活动窗口基类
|
||||||
|
*/
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.PersistableBundle;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import cc.winboll.studio.appbase.App;
|
||||||
|
import cc.winboll.studio.appbase.R;
|
||||||
|
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||||
|
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
|
||||||
|
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
|
||||||
|
import cc.winboll.studio.libappbase.winboll.WinBollActivityManager;
|
||||||
|
|
||||||
|
public class WinBollActivityBase extends AppCompatActivity implements IWinBollActivity {
|
||||||
|
|
||||||
|
public static final String TAG = "WinBollActivityBase";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Activity getActivity() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
WinBollActivityManager getWinBollActivityManager() {
|
||||||
|
return WinBollActivityManager.getInstance(GlobalApplication.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
getWinBollActivityManager().add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
|
||||||
|
super.onPostCreate(savedInstanceState, persistentState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
|
||||||
|
GlobalApplication.getWinBollActivityManager().startLogActivity(this);
|
||||||
|
return true;
|
||||||
|
} else if(item.getItemId() == cc.winboll.studio.appbase.R.id.item_minimal) {
|
||||||
|
//moveTaskToBack(true);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit() {
|
||||||
|
YesNoAlertDialog.show(this, "Exit " + getString(R.string.app_name), "Close all activity and exit?", new YesNoAlertDialog.OnDialogResultListener(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onYes() {
|
||||||
|
App.getWinBollActivityManager().finishAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNo() {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
getWinBollActivityManager().registeRemove(this);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,83 @@
|
|||||||
|
package cc.winboll.studio.appbase.activities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/03/25 11:46:40
|
||||||
|
* @Describe 测试窗口2
|
||||||
|
*/
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import cc.winboll.studio.appbase.R;
|
||||||
|
import cc.winboll.studio.appbase.WinBollActivityBase;
|
||||||
|
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||||
|
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
|
||||||
|
|
||||||
|
public class New2Activity extends WinBollActivityBase implements IWinBollActivity {
|
||||||
|
|
||||||
|
public static final String TAG = "New2Activity";
|
||||||
|
|
||||||
|
Toolbar mToolbar;
|
||||||
|
//LogView mLogView;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Activity getActivity() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_new2);
|
||||||
|
|
||||||
|
// mLogView = findViewById(R.id.logview);
|
||||||
|
// mLogView.start();
|
||||||
|
mToolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(mToolbar);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
//mLogView.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCloseThisActivity(View view) {
|
||||||
|
GlobalApplication.getWinBollActivityManager().finish(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCloseAllActivity(View view) {
|
||||||
|
GlobalApplication.getWinBollActivityManager().finishAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onNewActivity(View view) {
|
||||||
|
GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, NewActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
||||||
|
getMenuInflater().inflate(R.menu.toolbar_appbase, menu);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
|
||||||
|
GlobalApplication.getWinBollActivityManager().startLogActivity(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,81 @@
|
|||||||
|
package cc.winboll.studio.appbase.activities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/03/25 05:04:22
|
||||||
|
*/
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import cc.winboll.studio.appbase.R;
|
||||||
|
import cc.winboll.studio.appbase.WinBollActivityBase;
|
||||||
|
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||||
|
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
|
||||||
|
|
||||||
|
public class NewActivity extends WinBollActivityBase implements IWinBollActivity {
|
||||||
|
|
||||||
|
public static final String TAG = "NewActivity";
|
||||||
|
|
||||||
|
Toolbar mToolbar;
|
||||||
|
//LogView mLogView;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Activity getActivity() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_new);
|
||||||
|
// mLogView = findViewById(R.id.logview);
|
||||||
|
// mLogView.start();
|
||||||
|
mToolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(mToolbar);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
//mLogView.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCloseThisActivity(View view) {
|
||||||
|
GlobalApplication.getWinBollActivityManager().finish(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCloseAllActivity(View view) {
|
||||||
|
GlobalApplication.getWinBollActivityManager().finishAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onNew2Activity(View view) {
|
||||||
|
GlobalApplication.getWinBollActivityManager().startWinBollActivity(this, New2Activity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
||||||
|
getMenuInflater().inflate(R.menu.toolbar_appbase, menu);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
|
||||||
|
GlobalApplication.getWinBollActivityManager().startLogActivity(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
package cc.winboll.studio.appbase.handlers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/02/14 03:51:40
|
||||||
|
*/
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import cc.winboll.studio.appbase.services.MainService;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
public class MainServiceHandler extends Handler {
|
||||||
|
public static final String TAG = "MainServiceHandler";
|
||||||
|
|
||||||
|
public static final int MSG_REMINDTHREAD = 0;
|
||||||
|
|
||||||
|
WeakReference<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.appbase.models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/02/13 07:06:13
|
||||||
|
*/
|
||||||
|
import android.util.JsonReader;
|
||||||
|
import android.util.JsonWriter;
|
||||||
|
import cc.winboll.studio.libappbase.BaseBean;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class MainServiceBean extends BaseBean {
|
||||||
|
|
||||||
|
public static final String TAG = "MainServiceBean";
|
||||||
|
|
||||||
|
boolean isEnable;
|
||||||
|
|
||||||
|
public MainServiceBean() {
|
||||||
|
this.isEnable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsEnable(boolean isEnable) {
|
||||||
|
this.isEnable = isEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnable() {
|
||||||
|
return isEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return MainServiceBean.class.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||||
|
super.writeThisToJsonWriter(jsonWriter);
|
||||||
|
jsonWriter.name("isEnable").value(isEnable());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||||
|
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||||
|
if (name.equals("isEnable")) {
|
||||||
|
setIsEnable(jsonReader.nextBoolean());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||||
|
jsonReader.beginObject();
|
||||||
|
while (jsonReader.hasNext()) {
|
||||||
|
String name = jsonReader.nextName();
|
||||||
|
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||||
|
jsonReader.skipValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 结束 JSON 对象
|
||||||
|
jsonReader.endObject();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,67 @@
|
|||||||
|
package cc.winboll.studio.appbase.models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/03/07 12:47:22
|
||||||
|
* @Describe TestServiceBean
|
||||||
|
*/
|
||||||
|
import android.util.JsonReader;
|
||||||
|
import android.util.JsonWriter;
|
||||||
|
import cc.winboll.studio.libappbase.BaseBean;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class TestDemoBindServiceBean extends BaseBean {
|
||||||
|
|
||||||
|
public static final String TAG = "TestServiceBean";
|
||||||
|
|
||||||
|
boolean isEnable;
|
||||||
|
|
||||||
|
public TestDemoBindServiceBean() {
|
||||||
|
this.isEnable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsEnable(boolean isEnable) {
|
||||||
|
this.isEnable = isEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnable() {
|
||||||
|
return isEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return TestDemoBindServiceBean.class.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||||
|
super.writeThisToJsonWriter(jsonWriter);
|
||||||
|
jsonWriter.name("isEnable").value(isEnable());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||||
|
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||||
|
if (name.equals("isEnable")) {
|
||||||
|
setIsEnable(jsonReader.nextBoolean());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||||
|
jsonReader.beginObject();
|
||||||
|
while (jsonReader.hasNext()) {
|
||||||
|
String name = jsonReader.nextName();
|
||||||
|
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||||
|
jsonReader.skipValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 结束 JSON 对象
|
||||||
|
jsonReader.endObject();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,68 @@
|
|||||||
|
package cc.winboll.studio.appbase.models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/03/07 12:49:21
|
||||||
|
* @Describe TestDemoServiceBean
|
||||||
|
*/
|
||||||
|
import android.util.JsonReader;
|
||||||
|
import android.util.JsonWriter;
|
||||||
|
import cc.winboll.studio.libappbase.BaseBean;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class TestDemoServiceBean extends BaseBean {
|
||||||
|
|
||||||
|
public static final String TAG = "TestDemoServiceBean";
|
||||||
|
|
||||||
|
boolean isEnable;
|
||||||
|
|
||||||
|
public TestDemoServiceBean() {
|
||||||
|
this.isEnable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsEnable(boolean isEnable) {
|
||||||
|
this.isEnable = isEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnable() {
|
||||||
|
return isEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return TestDemoServiceBean.class.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||||
|
super.writeThisToJsonWriter(jsonWriter);
|
||||||
|
jsonWriter.name("isEnable").value(isEnable());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||||
|
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||||
|
if (name.equals("isEnable")) {
|
||||||
|
setIsEnable(jsonReader.nextBoolean());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||||
|
jsonReader.beginObject();
|
||||||
|
while (jsonReader.hasNext()) {
|
||||||
|
String name = jsonReader.nextName();
|
||||||
|
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||||
|
jsonReader.skipValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 结束 JSON 对象
|
||||||
|
jsonReader.endObject();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,72 @@
|
|||||||
|
package cc.winboll.studio.appbase.models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/02/17 10:05:09
|
||||||
|
* @Describe APPSOSReportBean
|
||||||
|
*/
|
||||||
|
import android.util.JsonReader;
|
||||||
|
import android.util.JsonWriter;
|
||||||
|
import cc.winboll.studio.libappbase.BaseBean;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class WinBollNewsBean extends BaseBean {
|
||||||
|
|
||||||
|
public static final String TAG = "WinBollNewsBean";
|
||||||
|
|
||||||
|
protected String message;
|
||||||
|
|
||||||
|
public WinBollNewsBean() {
|
||||||
|
this.message = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public WinBollNewsBean(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return WinBollNewsBean.class.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||||
|
super.writeThisToJsonWriter(jsonWriter);
|
||||||
|
jsonWriter.name("message").value(getMessage());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||||
|
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||||
|
if (name.equals("message")) {
|
||||||
|
setMessage(jsonReader.nextString());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||||
|
jsonReader.beginObject();
|
||||||
|
while (jsonReader.hasNext()) {
|
||||||
|
String name = jsonReader.nextName();
|
||||||
|
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||||
|
jsonReader.skipValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 结束 JSON 对象
|
||||||
|
jsonReader.endObject();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
package cc.winboll.studio.appbase.receivers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/03/24 07:11:44
|
||||||
|
*/
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
|
||||||
|
public class APPNewsWidgetClickListener extends BroadcastReceiver {
|
||||||
|
|
||||||
|
public static final String TAG = "APPNewsWidgetClickListener";
|
||||||
|
public static final String ACTION_PRE = APPNewsWidgetClickListener.class.getName() + ".ACTION_PRE";
|
||||||
|
public static final String ACTION_NEXT = APPNewsWidgetClickListener.class.getName() + ".ACTION_NEXT";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (action == null) {
|
||||||
|
LogUtils.d(TAG, String.format("action %s", action));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (action.equals(ACTION_PRE)) {
|
||||||
|
LogUtils.d(TAG, "ACTION_PRE");
|
||||||
|
APPNewsWidget.prePage(context);
|
||||||
|
} else if (action.equals(ACTION_NEXT)) {
|
||||||
|
LogUtils.d(TAG, "ACTION_NEXT");
|
||||||
|
APPNewsWidget.nextPage(context);
|
||||||
|
} else {
|
||||||
|
LogUtils.d(TAG, String.format("action %s", action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,117 @@
|
|||||||
|
package cc.winboll.studio.appbase.receivers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/02/13 06:58:04
|
||||||
|
* @Describe 主要广播接收器
|
||||||
|
*/
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import cc.winboll.studio.appbase.models.WinBollNewsBean;
|
||||||
|
import cc.winboll.studio.appbase.services.MainService;
|
||||||
|
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
|
||||||
|
import cc.winboll.studio.libappbase.AppUtils;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libappbase.sos.APPModel;
|
||||||
|
import cc.winboll.studio.libappbase.sos.SOS;
|
||||||
|
import cc.winboll.studio.libappbase.sos.SOSObject;
|
||||||
|
import cc.winboll.studio.libappbase.sos.WinBoll;
|
||||||
|
import cc.winboll.studio.libappbase.utils.ToastUtils;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class MainReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
public static final String TAG = "MainReceiver";
|
||||||
|
|
||||||
|
public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
|
||||||
|
|
||||||
|
WeakReference<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(WinBoll.ACTION_BIND)) {
|
||||||
|
LogUtils.d(TAG, "ACTION_BIND");
|
||||||
|
LogUtils.d(TAG, String.format("context.getPackageName() %s", context.getPackageName()));
|
||||||
|
LogUtils.d(TAG, String.format("intent.getAction() %s", intent.getAction()));
|
||||||
|
String szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL);
|
||||||
|
LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel));
|
||||||
|
if (szAPPModel != null && !szAPPModel.equals("")) {
|
||||||
|
try {
|
||||||
|
APPModel bean = APPModel.parseStringToBean(szAPPModel, APPModel.class);
|
||||||
|
if (bean != null) {
|
||||||
|
String szAppPackageName = bean.getAppPackageName();
|
||||||
|
LogUtils.d(TAG, String.format("szAppPackageName %s", szAppPackageName));
|
||||||
|
String szAppMainServiveName = bean.getAppMainServiveName();
|
||||||
|
LogUtils.d(TAG, String.format("szAppMainServiveName %s", szAppMainServiveName));
|
||||||
|
mwrService.get().bindAPPModelConnection(bean);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (intent.getAction().equals(SOS.ACTION_SOS)) {
|
||||||
|
LogUtils.d(TAG, "ACTION_SOS");
|
||||||
|
String sos = intent.getStringExtra(SOS.EXTRA_OBJECT);
|
||||||
|
LogUtils.d(TAG, String.format("SOS %s", sos));
|
||||||
|
if (sos != null && !sos.equals("")) {
|
||||||
|
SOSObject bean = SOS.parseSOSObject(sos);
|
||||||
|
if (bean != null) {
|
||||||
|
String szObjectPackageName = bean.getObjectPackageName();
|
||||||
|
LogUtils.d(TAG, String.format("szObjectPackageName %s", szObjectPackageName));
|
||||||
|
String szObjectServiveName = bean.getObjectServiveName();
|
||||||
|
LogUtils.d(TAG, String.format("szObjectServiveName %s", szObjectServiveName));
|
||||||
|
|
||||||
|
Intent intentService = new Intent();
|
||||||
|
intentService.setComponent(new ComponentName(szObjectPackageName, szObjectServiveName));
|
||||||
|
context.startService(intentService);
|
||||||
|
|
||||||
|
String appName = AppUtils.getAppNameByPackageName(context, szObjectPackageName);
|
||||||
|
LogUtils.d(TAG, String.format("appName %s", appName));
|
||||||
|
WinBollNewsBean appWinBollNewsBean = new WinBollNewsBean(appName);
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
|
||||||
|
String currentTime = sdf.format(new Date());
|
||||||
|
StringBuilder sbLine = new StringBuilder();
|
||||||
|
sbLine.append("[");
|
||||||
|
sbLine.append(currentTime);
|
||||||
|
sbLine.append("] Power to ");
|
||||||
|
sbLine.append(appName);
|
||||||
|
appWinBollNewsBean.setMessage(sbLine.toString());
|
||||||
|
|
||||||
|
APPNewsWidget.addWinBollNewsBean(context, appWinBollNewsBean);
|
||||||
|
|
||||||
|
Intent intentWidget = new Intent(context, APPNewsWidget.class);
|
||||||
|
intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
|
||||||
|
context.sendBroadcast(intentWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ToastUtils.show(szAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册 Receiver
|
||||||
|
//
|
||||||
|
public void registerAction(MainService service) {
|
||||||
|
IntentFilter filter=new IntentFilter();
|
||||||
|
filter.addAction(ACTION_BOOT_COMPLETED);
|
||||||
|
filter.addAction(SOS.ACTION_SOS);
|
||||||
|
filter.addAction(WinBoll.ACTION_BIND);
|
||||||
|
//filter.addAction(Intent.ACTION_BATTERY_CHANGED);
|
||||||
|
service.registerReceiver(this, filter);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,138 @@
|
|||||||
|
package cc.winboll.studio.appbase.services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/02/14 03:38:31
|
||||||
|
* @Describe 守护进程服务
|
||||||
|
*/
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import cc.winboll.studio.appbase.models.MainServiceBean;
|
||||||
|
import cc.winboll.studio.appbase.services.AssistantService;
|
||||||
|
import cc.winboll.studio.appbase.services.MainService;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import android.os.Binder;
|
||||||
|
|
||||||
|
public class AssistantService extends Service {
|
||||||
|
|
||||||
|
public static final String TAG = "AssistantService";
|
||||||
|
|
||||||
|
MainServiceBean mMainServiceBean;
|
||||||
|
MyServiceConnection mMyServiceConnection;
|
||||||
|
MainService mMainService;
|
||||||
|
boolean isBound = false;
|
||||||
|
volatile boolean isThreadAlive = false;
|
||||||
|
|
||||||
|
public synchronized void setIsThreadAlive(boolean isThreadAlive) {
|
||||||
|
LogUtils.d(TAG, "setIsThreadAlive(...)");
|
||||||
|
LogUtils.d(TAG, String.format("isThreadAlive %s", isThreadAlive));
|
||||||
|
this.isThreadAlive = isThreadAlive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isThreadAlive() {
|
||||||
|
return isThreadAlive;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return new MyBinder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
LogUtils.d(TAG, "onCreate");
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
//mMyBinder = new MyBinder();
|
||||||
|
if (mMyServiceConnection == null) {
|
||||||
|
mMyServiceConnection = new MyServiceConnection();
|
||||||
|
}
|
||||||
|
// 设置运行参数
|
||||||
|
setIsThreadAlive(false);
|
||||||
|
assistantService();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
LogUtils.d(TAG, "call onStartCommand(...)");
|
||||||
|
assistantService();
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
//LogUtils.d(TAG, "onDestroy");
|
||||||
|
setIsThreadAlive(false);
|
||||||
|
// 解除绑定
|
||||||
|
if (isBound) {
|
||||||
|
unbindService(mMyServiceConnection);
|
||||||
|
isBound = false;
|
||||||
|
}
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运行服务内容
|
||||||
|
//
|
||||||
|
void assistantService() {
|
||||||
|
LogUtils.d(TAG, "assistantService()");
|
||||||
|
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||||
|
LogUtils.d(TAG, String.format("mMainServiceBean.isEnable() %s", mMainServiceBean.isEnable()));
|
||||||
|
if (mMainServiceBean.isEnable()) {
|
||||||
|
LogUtils.d(TAG, String.format("mIsThreadAlive %s", isThreadAlive()));
|
||||||
|
if (isThreadAlive() == false) {
|
||||||
|
// 设置运行状态
|
||||||
|
setIsThreadAlive(true);
|
||||||
|
// 唤醒和绑定主进程
|
||||||
|
wakeupAndBindMain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 唤醒和绑定主进程
|
||||||
|
//
|
||||||
|
void wakeupAndBindMain() {
|
||||||
|
LogUtils.d(TAG, "wakeupAndBindMain()");
|
||||||
|
// 绑定服务的Intent
|
||||||
|
Intent intent = new Intent(this, MainService.class);
|
||||||
|
startService(new Intent(this, MainService.class));
|
||||||
|
bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
|
||||||
|
|
||||||
|
// startService(new Intent(this, MainService.class));
|
||||||
|
// bindService(new Intent(AssistantService.this, MainService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主进程与守护进程连接时需要用到此类
|
||||||
|
//
|
||||||
|
class MyServiceConnection implements ServiceConnection {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
LogUtils.d(TAG, "onServiceConnected(...)");
|
||||||
|
MainService.MyBinder binder = (MainService.MyBinder) service;
|
||||||
|
mMainService = binder.getService();
|
||||||
|
isBound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
LogUtils.d(TAG, "onServiceDisconnected(...)");
|
||||||
|
mMainServiceBean = MainServiceBean.loadBean(AssistantService.this, MainServiceBean.class);
|
||||||
|
if (mMainServiceBean.isEnable()) {
|
||||||
|
wakeupAndBindMain();
|
||||||
|
}
|
||||||
|
isBound = false;
|
||||||
|
mMainService = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用于返回服务实例的Binder
|
||||||
|
public class MyBinder extends Binder {
|
||||||
|
AssistantService getService() {
|
||||||
|
LogUtils.d(TAG, "AssistantService MyBinder getService()");
|
||||||
|
return AssistantService.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,316 @@
|
|||||||
|
package cc.winboll.studio.appbase.services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/02/13 06:56:41
|
||||||
|
* @Describe 拨号主服务
|
||||||
|
* 参考:
|
||||||
|
* 进程保活-双进程守护的正确姿势
|
||||||
|
* https://blog.csdn.net/sinat_35159441/article/details/75267380
|
||||||
|
* Android Service之onStartCommand方法研究
|
||||||
|
* https://blog.csdn.net/cyp331203/article/details/38920491
|
||||||
|
*/
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import cc.winboll.studio.appbase.MyTileService;
|
||||||
|
import cc.winboll.studio.appbase.models.MainServiceBean;
|
||||||
|
import cc.winboll.studio.appbase.handlers.MainServiceHandler;
|
||||||
|
import cc.winboll.studio.appbase.receivers.MainReceiver;
|
||||||
|
import cc.winboll.studio.appbase.services.AssistantService;
|
||||||
|
import cc.winboll.studio.appbase.threads.MainServiceThread;
|
||||||
|
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import cc.winboll.studio.libappbase.sos.APPModel;
|
||||||
|
|
||||||
|
public class MainService extends Service {
|
||||||
|
|
||||||
|
public static final String TAG = "MainService";
|
||||||
|
|
||||||
|
public static final int MSG_UPDATE_STATUS = 0;
|
||||||
|
|
||||||
|
static MainService _mControlCenterService;
|
||||||
|
|
||||||
|
volatile boolean isServiceRunning;
|
||||||
|
|
||||||
|
MainServiceBean mMainServiceBean;
|
||||||
|
MainServiceThread mMainServiceThread;
|
||||||
|
MainServiceHandler mMainServiceHandler;
|
||||||
|
MyServiceConnection mMyServiceConnection;
|
||||||
|
AssistantService mAssistantService;
|
||||||
|
boolean isBound = false;
|
||||||
|
MainReceiver mMainReceiver;
|
||||||
|
ArrayList<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 bindAPPModelConnection(APPModel bean) {
|
||||||
|
LogUtils.d(TAG, "bindAPPModelConnection(...)");
|
||||||
|
// 清理旧的绑定链接
|
||||||
|
for (int i = mAPPModelConnectionList.size() - 1; i > -1; i--) {
|
||||||
|
APPConnection item = mAPPModelConnectionList.get(i);
|
||||||
|
if (item.isBindToAPP(bean)) {
|
||||||
|
LogUtils.d(TAG, "Bind Servive exist.");
|
||||||
|
unbindService(item);
|
||||||
|
mAPPModelConnectionList.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定服务
|
||||||
|
APPConnection appConnection = new APPConnection();
|
||||||
|
Intent intentService = new Intent();
|
||||||
|
intentService.setComponent(new ComponentName(bean.getAppPackageName(), bean.getAppMainServiveName()));
|
||||||
|
bindService(intentService, appConnection, Context.BIND_IMPORTANT);
|
||||||
|
mAPPModelConnectionList.add(appConnection);
|
||||||
|
|
||||||
|
Intent intentWidget = new Intent(this, APPNewsWidget.class);
|
||||||
|
intentWidget.setAction(APPNewsWidget.ACTION_WAKEUP_SERVICE);
|
||||||
|
APPModel appSOSBean = new APPModel(bean.getAppPackageName(), bean.getAppMainServiveName());
|
||||||
|
intentWidget.putExtra("APPSOSBean", appSOSBean.toString());
|
||||||
|
sendBroadcast(intentWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class APPConnection implements ServiceConnection {
|
||||||
|
|
||||||
|
ComponentName mComponentName;
|
||||||
|
|
||||||
|
boolean isBindToAPP(APPModel bean) {
|
||||||
|
return mComponentName != null
|
||||||
|
&& mComponentName.getClassName().equals(bean.getAppMainServiveName())
|
||||||
|
&& mComponentName.getPackageName().equals(bean.getAppPackageName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
LogUtils.d(TAG, "onServiceConnected(...)");
|
||||||
|
mComponentName = name;
|
||||||
|
LogUtils.d(TAG, String.format("onServiceConnected : \ngetClassName %s\ngetPackageName %s", name.getClassName(), name.getPackageName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
LogUtils.d(TAG, "onServiceDisconnected(...)");
|
||||||
|
LogUtils.d(TAG, String.format("onServiceDisconnected : \ngetClassName %s\ngetPackageName %s", name.getClassName(), name.getPackageName()));
|
||||||
|
|
||||||
|
// 尝试无参数启动一下服务
|
||||||
|
String appPackage = mComponentName.getPackageName();
|
||||||
|
LogUtils.d(TAG, String.format("appPackage %s", appPackage));
|
||||||
|
String appMainServiceClassName = mComponentName.getClassName();
|
||||||
|
LogUtils.d(TAG, String.format("appMainServiceClassName %s", appMainServiceClassName));
|
||||||
|
|
||||||
|
Intent intentService = new Intent();
|
||||||
|
intentService.setComponent(new ComponentName(appPackage, appMainServiceClassName));
|
||||||
|
startService(intentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主进程与守护进程连接时需要用到此类
|
||||||
|
//
|
||||||
|
private class MyServiceConnection implements ServiceConnection {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
LogUtils.d(TAG, "onServiceConnected(...)");
|
||||||
|
AssistantService.MyBinder binder = (AssistantService.MyBinder) service;
|
||||||
|
mAssistantService = binder.getService();
|
||||||
|
isBound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
LogUtils.d(TAG, "onServiceDisconnected(...)");
|
||||||
|
|
||||||
|
if (mMainServiceBean.isEnable()) {
|
||||||
|
// 唤醒守护进程
|
||||||
|
wakeupAndBindAssistant();
|
||||||
|
}
|
||||||
|
isBound = false;
|
||||||
|
mAssistantService = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 用于返回服务实例的Binder
|
||||||
|
public class MyBinder extends Binder {
|
||||||
|
MainService getService() {
|
||||||
|
LogUtils.d(TAG, "MainService MyBinder getService()");
|
||||||
|
return MainService.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// //
|
||||||
|
// // 启动服务
|
||||||
|
// //
|
||||||
|
// public static void startControlCenterService(Context context) {
|
||||||
|
// Intent intent = new Intent(context, MainService.class);
|
||||||
|
// context.startForegroundService(intent);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //
|
||||||
|
// // 停止服务
|
||||||
|
// //
|
||||||
|
// public static void stopControlCenterService(Context context) {
|
||||||
|
// Intent intent = new Intent(context, MainService.class);
|
||||||
|
// context.stopService(intent);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void appenMessage(String message) {
|
||||||
|
LogUtils.d(TAG, String.format("Message : %s", message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void stopMainService(Context context) {
|
||||||
|
LogUtils.d(TAG, "stopMainService");
|
||||||
|
MainServiceBean bean = new MainServiceBean();
|
||||||
|
bean.setIsEnable(false);
|
||||||
|
MainServiceBean.saveBean(context, bean);
|
||||||
|
context.stopService(new Intent(context, MainService.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startMainService(Context context) {
|
||||||
|
LogUtils.d(TAG, "startMainService");
|
||||||
|
MainServiceBean bean = new MainServiceBean();
|
||||||
|
bean.setIsEnable(true);
|
||||||
|
MainServiceBean.saveBean(context, bean);
|
||||||
|
context.startService(new Intent(context, MainService.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -0,0 +1,178 @@
|
|||||||
|
package cc.winboll.studio.appbase.services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/03/07 12:45:49
|
||||||
|
* @Describe 启动时申请绑定到APPBase主服务的服务示例
|
||||||
|
*/
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import cc.winboll.studio.appbase.models.TestDemoBindServiceBean;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libappbase.sos.WinBoll;
|
||||||
|
import cc.winboll.studio.appbase.App;
|
||||||
|
import cc.winboll.studio.libappbase.sos.SOS;
|
||||||
|
|
||||||
|
public class TestDemoBindService extends Service {
|
||||||
|
|
||||||
|
public static final String TAG = "TestDemoBindService";
|
||||||
|
|
||||||
|
public static final String ACTION_ENABLE = TestDemoBindService.class.getName() + ".ACTION_ENABLE";
|
||||||
|
public static final String ACTION_DISABLE = TestDemoBindService.class.getName() + ".ACTION_DISABLE";
|
||||||
|
|
||||||
|
volatile static TestThread _TestThread;
|
||||||
|
|
||||||
|
volatile static boolean _IsRunning;
|
||||||
|
|
||||||
|
public synchronized static void setIsRunning(boolean isRunning) {
|
||||||
|
_IsRunning = isRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isRunning() {
|
||||||
|
return _IsRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return new MyBinder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MyBinder extends Binder {
|
||||||
|
public TestDemoBindService getService() {
|
||||||
|
return TestDemoBindService.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
LogUtils.d(TAG, "onCreate()");
|
||||||
|
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
LogUtils.d(TAG, "onStartCommand(...)");
|
||||||
|
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
|
||||||
|
if (bean == null) {
|
||||||
|
bean = new TestDemoBindServiceBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intent.getAction() != null) {
|
||||||
|
if (intent.getAction().equals(ACTION_ENABLE)) {
|
||||||
|
bean.setIsEnable(true);
|
||||||
|
LogUtils.d(TAG, "setIsEnable(true);");
|
||||||
|
TestDemoBindServiceBean.saveBean(this, bean);
|
||||||
|
} else if (intent.getAction().equals(ACTION_DISABLE)) {
|
||||||
|
bean.setIsEnable(false);
|
||||||
|
LogUtils.d(TAG, "setIsEnable(false);");
|
||||||
|
TestDemoBindServiceBean.saveBean(this, bean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
|
||||||
|
//return super.onStartCommand(intent, flags, startId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
LogUtils.d(TAG, "run()");
|
||||||
|
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
|
||||||
|
if (bean == null) {
|
||||||
|
bean = new TestDemoBindServiceBean();
|
||||||
|
TestDemoBindServiceBean.saveBean(this, bean);
|
||||||
|
}
|
||||||
|
if (bean.isEnable()) {
|
||||||
|
LogUtils.d(TAG, "run() bean.isEnable()");
|
||||||
|
TestThread.getInstance(this).start();
|
||||||
|
LogUtils.d(TAG, "_TestThread.start()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
LogUtils.d(TAG, "onDestroy()");
|
||||||
|
TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
|
||||||
|
if (bean == null) {
|
||||||
|
bean = new TestDemoBindServiceBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
TestThread.getInstance(this).setIsExit(true);
|
||||||
|
|
||||||
|
// 预防 APPBase 应用重启绑定失效。
|
||||||
|
// 所以退出时检查本服务是否配置启用,如果启用就发送一个 SOS 信号。
|
||||||
|
// 这样 APPBase 就会用组件方式启动本服务。
|
||||||
|
if (bean.isEnable()) {
|
||||||
|
if (App.isDebuging()) {
|
||||||
|
SOS.sosToAppBaseBeta(this, TestDemoBindService.class.getName());
|
||||||
|
} else {
|
||||||
|
SOS.sosToAppBase(this, TestDemoBindService.class.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_IsRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestThread extends Thread {
|
||||||
|
|
||||||
|
volatile static TestThread _TestThread;
|
||||||
|
Context mContext;
|
||||||
|
volatile boolean isStarted = false;
|
||||||
|
volatile boolean isExit = false;
|
||||||
|
|
||||||
|
TestThread(Context context) {
|
||||||
|
super();
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized TestThread getInstance(Context context) {
|
||||||
|
if (_TestThread != null) {
|
||||||
|
_TestThread.setIsExit(true);
|
||||||
|
}
|
||||||
|
_TestThread = new TestThread(context);
|
||||||
|
|
||||||
|
return _TestThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setIsExit(boolean isExit) {
|
||||||
|
this.isExit = isExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExit() {
|
||||||
|
return isExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isStarted == false) {
|
||||||
|
isStarted = true;
|
||||||
|
super.run();
|
||||||
|
LogUtils.d(TAG, "run() start");
|
||||||
|
if (App.isDebuging()) {
|
||||||
|
WinBoll.bindToAPPBaseBeta(mContext, TestDemoBindService.class.getName());
|
||||||
|
} else {
|
||||||
|
WinBoll.bindToAPPBase(mContext, TestDemoBindService.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!isExit()) {
|
||||||
|
LogUtils.d(TAG, "run()");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogUtils.d(TAG, "run() exit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,156 @@
|
|||||||
|
package cc.winboll.studio.appbase.services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/03/07 12:39:24
|
||||||
|
* @Describe 普通服务示例
|
||||||
|
*/
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import cc.winboll.studio.appbase.models.TestDemoServiceBean;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libappbase.sos.WinBoll;
|
||||||
|
|
||||||
|
public class TestDemoService extends Service {
|
||||||
|
|
||||||
|
public static final String TAG = "TestDemoService";
|
||||||
|
|
||||||
|
public static final String ACTION_ENABLE = TestDemoService.class.getName() + ".ACTION_ENABLE";
|
||||||
|
public static final String ACTION_DISABLE = TestDemoService.class.getName() + ".ACTION_DISABLE";
|
||||||
|
|
||||||
|
volatile static TestThread _TestThread;
|
||||||
|
|
||||||
|
volatile static boolean _IsRunning;
|
||||||
|
|
||||||
|
public synchronized static void setIsRunning(boolean isRunning) {
|
||||||
|
_IsRunning = isRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isRunning() {
|
||||||
|
return _IsRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return new MyBinder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MyBinder extends Binder {
|
||||||
|
public TestDemoService getService() {
|
||||||
|
return TestDemoService.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
LogUtils.d(TAG, "onCreate()");
|
||||||
|
|
||||||
|
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
LogUtils.d(TAG, "onStartCommand(...)");
|
||||||
|
TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
|
||||||
|
if (bean == null) {
|
||||||
|
bean = new TestDemoServiceBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intent.getAction() != null) {
|
||||||
|
if (intent.getAction().equals(ACTION_ENABLE)) {
|
||||||
|
bean.setIsEnable(true);
|
||||||
|
LogUtils.d(TAG, "setIsEnable(true);");
|
||||||
|
TestDemoServiceBean.saveBean(this, bean);
|
||||||
|
} else if (intent.getAction().equals(ACTION_DISABLE)) {
|
||||||
|
bean.setIsEnable(false);
|
||||||
|
LogUtils.d(TAG, "setIsEnable(false);");
|
||||||
|
TestDemoServiceBean.saveBean(this, bean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
|
||||||
|
//return super.onStartCommand(intent, flags, startId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
LogUtils.d(TAG, "run()");
|
||||||
|
TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
|
||||||
|
if (bean == null) {
|
||||||
|
bean = new TestDemoServiceBean();
|
||||||
|
TestDemoServiceBean.saveBean(this, bean);
|
||||||
|
}
|
||||||
|
if (bean.isEnable()) {
|
||||||
|
LogUtils.d(TAG, "run() bean.isEnable()");
|
||||||
|
TestThread.getInstance(this).start();
|
||||||
|
LogUtils.d(TAG, "_TestThread.start()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
LogUtils.d(TAG, "onDestroy()");
|
||||||
|
TestThread.getInstance(this).setIsExit(true);
|
||||||
|
|
||||||
|
_IsRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestThread extends Thread {
|
||||||
|
|
||||||
|
volatile static TestThread _TestThread;
|
||||||
|
Context mContext;
|
||||||
|
volatile boolean isStarted = false;
|
||||||
|
volatile boolean isExit = false;
|
||||||
|
|
||||||
|
TestThread(Context context) {
|
||||||
|
super();
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized TestThread getInstance(Context context) {
|
||||||
|
if (_TestThread != null) {
|
||||||
|
_TestThread.setIsExit(true);
|
||||||
|
}
|
||||||
|
_TestThread = new TestThread(context);
|
||||||
|
|
||||||
|
return _TestThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setIsExit(boolean isExit) {
|
||||||
|
this.isExit = isExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExit() {
|
||||||
|
return isExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isStarted == false) {
|
||||||
|
isStarted = true;
|
||||||
|
super.run();
|
||||||
|
LogUtils.d(TAG, "run() start");
|
||||||
|
|
||||||
|
while (!isExit()) {
|
||||||
|
LogUtils.d(TAG, "run()");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogUtils.d(TAG, "run() exit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,54 @@
|
|||||||
|
package cc.winboll.studio.appbase.threads;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/02/14 03:46:44
|
||||||
|
*/
|
||||||
|
import android.content.Context;
|
||||||
|
import cc.winboll.studio.appbase.handlers.MainServiceHandler;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
public class MainServiceThread extends Thread {
|
||||||
|
|
||||||
|
public static final String TAG = "MainServiceThread";
|
||||||
|
|
||||||
|
Context mContext;
|
||||||
|
|
||||||
|
// 控制线程是否退出的标志
|
||||||
|
volatile boolean isExist = false;
|
||||||
|
|
||||||
|
// 服务Handler, 用于线程发送消息使用
|
||||||
|
WeakReference<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,186 @@
|
|||||||
|
package cc.winboll.studio.appbase.widgets;
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@AliYun.Com
|
||||||
|
* @Date 2025/02/15 14:41:25
|
||||||
|
* @Describe TimeWidget
|
||||||
|
*/
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.appwidget.AppWidgetProvider;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
import cc.winboll.studio.appbase.R;
|
||||||
|
import cc.winboll.studio.appbase.models.WinBollNewsBean;
|
||||||
|
import cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener;
|
||||||
|
import cc.winboll.studio.libappbase.AppUtils;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libappbase.sos.APPModel;
|
||||||
|
import cc.winboll.studio.libappbase.sos.WinBoll;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class APPNewsWidget extends AppWidgetProvider {
|
||||||
|
|
||||||
|
public static final String TAG = "APPNewsWidget";
|
||||||
|
|
||||||
|
public static final String ACTION_WAKEUP_SERVICE = APPNewsWidget.class.getName() + ".ACTION_WAKEUP_SERVICE";
|
||||||
|
public static final String ACTION_RELOAD_REPORT = APPNewsWidget.class.getName() + ".ACTION_RELOAD_REPORT";
|
||||||
|
|
||||||
|
|
||||||
|
volatile static ArrayList<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 szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL);
|
||||||
|
LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel));
|
||||||
|
if (szAPPModel != null && !szAPPModel.equals("")) {
|
||||||
|
try {
|
||||||
|
APPModel bean = APPModel.parseStringToBean(szAPPModel, APPModel.class);
|
||||||
|
if (bean != null) {
|
||||||
|
String szAppPackageName = bean.getAppPackageName();
|
||||||
|
LogUtils.d(TAG, String.format("szAppPackageName %s", szAppPackageName));
|
||||||
|
String szAppMainServiveName = bean.getAppMainServiveName();
|
||||||
|
LogUtils.d(TAG, String.format("szAppMainServiveName %s", szAppMainServiveName));
|
||||||
|
|
||||||
|
|
||||||
|
String appName = AppUtils.getAppNameByPackageName(context, szAppPackageName);
|
||||||
|
LogUtils.d(TAG, String.format("appName %s", appName));
|
||||||
|
WinBollNewsBean winBollNewsBean = new WinBollNewsBean(appName);
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
|
||||||
|
String currentTime = sdf.format(new Date());
|
||||||
|
StringBuilder sbLine = new StringBuilder();
|
||||||
|
sbLine.append("[");
|
||||||
|
sbLine.append(currentTime);
|
||||||
|
sbLine.append("] Wake up ");
|
||||||
|
sbLine.append(appName);
|
||||||
|
winBollNewsBean.setMessage(sbLine.toString());
|
||||||
|
|
||||||
|
addWinBollNewsBean(context, winBollNewsBean);
|
||||||
|
|
||||||
|
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||||
|
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class));
|
||||||
|
for (int appWidgetId : appWidgetIds) {
|
||||||
|
updateAppWidget(context, appWidgetManager, appWidgetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 加入新报告信息
|
||||||
|
//
|
||||||
|
public synchronized static void addWinBollNewsBean(Context context, WinBollNewsBean bean) {
|
||||||
|
initWinBollNewsBeanList(context);
|
||||||
|
_WinBollNewsBeanList.add(0, bean);
|
||||||
|
// 控制记录总数
|
||||||
|
while (_WinBollNewsBeanList.size() > _MAX_PAGES * _OnePageLinesCount) {
|
||||||
|
_WinBollNewsBeanList.remove(_WinBollNewsBeanList.size() - 1);
|
||||||
|
}
|
||||||
|
WinBollNewsBean.saveBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized static void initWinBollNewsBeanList(Context context) {
|
||||||
|
if (_WinBollNewsBeanList == null) {
|
||||||
|
_WinBollNewsBeanList = new ArrayList<WinBollNewsBean>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
11
appbase/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
appbase/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>
|
@@ -1,53 +1,216 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
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:orientation="vertical"
|
||||||
android:gravity="center">
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<android.support.v7.widget.Toolbar
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/activitymainToolbar1"/>
|
android:id="@+id/toolbar"/>
|
||||||
|
|
||||||
<LinearLayout
|
<ScrollView
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1.0"
|
android:layout_weight="1.0">
|
||||||
android:gravity="center_horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Hello, WinBoll!"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="horizontal"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="right|center_vertical">
|
android:orientation="vertical"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
<CheckBox
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:orientation="vertical"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:text="Debug Mode"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1.0"
|
android:layout_weight="1.0"
|
||||||
android:onClick="onSwitchDebugMode"
|
android:gravity="center_horizontal">
|
||||||
android:id="@+id/activitymainCheckBox1"/>
|
|
||||||
|
|
||||||
<Button
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Test Application CrashReport"
|
android:text="Hello, WinBoll!"/>
|
||||||
android:textAllCaps="false"
|
|
||||||
android:onClick="onTestApplicationCrashReport"/>
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Android版本10的代号是“Q”,API级别是29。 Android 10开始谷歌不再公开使用甜品作为版本代号,但内部仍保留了大量与“Q”相关的元素。Android 10本身并没有严格对应某个特定的Java版本,但在开发Android 10应用时,通常可以使用Java 8或更高版本。 Java 8为Android开发带来了诸如Lambda表达式、方法引用等新特性,能提高开发效率和代码可读性,与Android 10开发适配良好。Java 9及更高版本也可用于Android 10开发,能使用一些新的语言特性和API,但可能需要注意兼容性和配置问题。"/>
|
||||||
|
|
||||||
|
<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>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</ScrollView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
43
appbase/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.support.v7.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
appbase/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.support.v7.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>
|
||||||
|
|
51
appbase/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>
|
||||||
|
|
6
appbase/src/main/res/menu/toolbar_main.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
|
||||||
|
</menu>
|
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="colorPrimary">#005800FF</color>
|
<color name="colorPrimary">#FF00B322</color>
|
||||||
<color name="colorPrimaryDark">#005800FF</color>
|
<color name="colorPrimaryDark">#FF005C12</color>
|
||||||
<color name="colorAccent">#005800FF</color>
|
<color name="colorAccent">#FF8DFFA2</color>
|
||||||
|
<color name="colorText">#FFFFFB8D</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">AppBase</string>
|
<string name="app_name">AppBase</string>
|
||||||
</resources>
|
<string name="tileservice_name">WinBoll</string>
|
||||||
|
</resources>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<style name="AppTheme" parent="APPBaseTheme">
|
<style name="MyAPPBaseTheme" parent="APPBaseTheme">
|
||||||
|
<item name="attrColorPrimary">@color/colorPrimary</item>
|
||||||
<item name="themeGlobalCrashActivity">@style/MyGlobalCrashActivityTheme</item>
|
<item name="themeGlobalCrashActivity">@style/MyGlobalCrashActivityTheme</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
7
appbase/src/main/res/xml/widget_provider_info_sos.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:minWidth="200dp"
|
||||||
|
android:minHeight="100dp"
|
||||||
|
android:updatePeriodMillis="1000"
|
||||||
|
android:initialLayout="@layout/widget_news">
|
||||||
|
</appwidget-provider>
|
25
build.gradle
@@ -1,11 +1,24 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
// Nexus Maven 库地址
|
||||||
|
// "WinBoll Release"
|
||||||
|
maven { url "https://nexus.winboll.cc/repository/maven-public/" }
|
||||||
|
// "WinBoll Snapshot"
|
||||||
|
maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" }
|
||||||
|
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/public/' }
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/google/' }
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
|
||||||
|
maven { url 'https://dl.bintray.com/ppartisan/maven/' }
|
||||||
|
maven { url "https://clojars.org/repo/" }
|
||||||
|
maven { url "https://jitpack.io" }
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
google()
|
||||||
|
mavenLocal()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.2.1'
|
classpath 'com.android.tools.build:gradle:7.2.1' // 对应 compileSdkVersion 32
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
}
|
}
|
||||||
@@ -69,6 +82,14 @@ allprojects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.compilerArgs << "-parameters"
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
|
40
contacts/README.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Contacts
|
||||||
|
源码参考自:
|
||||||
|
https://github.com/aJIEw/PhoneCallApp.git
|
||||||
|
|
||||||
|
#### 介绍
|
||||||
|
通讯录与拨号
|
||||||
|
|
||||||
|
#### 软件架构
|
||||||
|
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
|
||||||
|
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
|
||||||
|
|
||||||
|
|
||||||
|
#### Gradle 编译说明
|
||||||
|
调试版编译命令 :gradle assembleBetaDebug
|
||||||
|
阶段版编译命令 :gradle assembleStageRelease
|
||||||
|
|
||||||
|
#### 使用说明
|
||||||
|
|
||||||
|
在安卓系统中需要设置两个权限允许。
|
||||||
|
1.自启动权限允许。
|
||||||
|
2.省电策略-无限制权限允许。
|
||||||
|
|
||||||
|
#### 参与贡献
|
||||||
|
|
||||||
|
1. Fork 本仓库
|
||||||
|
2. 新建 Feat_xxx 分支
|
||||||
|
3. 提交代码 : ZhanGSKen(ZhanGSKen@AliYun.Com)
|
||||||
|
4. 新建 Pull Request
|
||||||
|
|
||||||
|
|
||||||
|
#### 特技
|
||||||
|
|
||||||
|
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
|
||||||
|
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
|
||||||
|
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
|
||||||
|
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
|
||||||
|
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
|
||||||
|
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||||
|
|
||||||
|
#### 参考文档
|
1
contacts/app_update_description.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
71
contacts/build.gradle
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
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 "33.0.3"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "cc.winboll.studio.contacts"
|
||||||
|
minSdkVersion 21
|
||||||
|
targetSdkVersion 30
|
||||||
|
versionCode 1
|
||||||
|
// versionName 更新后需要手动设置
|
||||||
|
// 项目模块目录的 build.gradle 文件的 stageCount=0
|
||||||
|
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||||
|
versionName "1.0"
|
||||||
|
if(true) {
|
||||||
|
versionName = genVersionName("${versionName}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// 二维码使用的类库
|
||||||
|
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.jcraft:jsch:0.1.55'
|
||||||
|
api 'org.jsoup:jsoup:1.13.1'
|
||||||
|
api 'com.squareup.okhttp3:okhttp:4.4.1'
|
||||||
|
|
||||||
|
api 'androidx.appcompat:appcompat:1.1.0'
|
||||||
|
api 'androidx.viewpager:viewpager:1.0.0'
|
||||||
|
api 'androidx.fragment:fragment:1.1.0'
|
||||||
|
api 'com.google.android.material:material:1.4.0'
|
||||||
|
|
||||||
|
api 'cc.winboll.studio:libapputils:9.3.2'
|
||||||
|
api 'cc.winboll.studio:libappbase:1.5.6'
|
||||||
|
|
||||||
|
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
}
|
8
contacts/build.properties
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
|
#Tue Feb 25 00:17:29 HKT 2025
|
||||||
|
stageCount=2
|
||||||
|
libraryProject=
|
||||||
|
baseVersion=1.0
|
||||||
|
publishVersion=1.0.1
|
||||||
|
buildCount=0
|
||||||
|
baseBetaVersion=1.0.2
|
17
contacts/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
13
contacts/src/beta/AndroidManifest.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?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_winbollbeta">
|
||||||
|
|
||||||
|
<!-- Put flavor specific code here -->
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
6
contacts/src/beta/res/values/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">Contacts+</string>
|
||||||
|
|
||||||
|
</resources>
|
187
contacts/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<manifest
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="cc.winboll.studio.contacts">
|
||||||
|
|
||||||
|
<!-- BIND_AUTOFILL_SERVICE -->
|
||||||
|
<uses-permission android:name="android.permission.BIND_AUTOFILL_SERVICE"/>
|
||||||
|
|
||||||
|
<!-- 拨打电话 -->
|
||||||
|
<uses-permission android:name="android.permission.CALL_PHONE"/>
|
||||||
|
|
||||||
|
<!-- 读取手机状态和身份 -->
|
||||||
|
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||||
|
|
||||||
|
<!-- 修改系统设置 -->
|
||||||
|
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
|
||||||
|
|
||||||
|
<!-- 重新设置外拨电话的路径 -->
|
||||||
|
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
|
||||||
|
|
||||||
|
<!-- 读取联系人 -->
|
||||||
|
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||||
|
|
||||||
|
<!-- 修改您的通讯录 -->
|
||||||
|
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||||
|
|
||||||
|
<!-- 此应用可显示在其他应用上方 -->
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
|
|
||||||
|
<!-- 更改您的音频设置 -->
|
||||||
|
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".App"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@drawable/ic_winboll"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/MyAppTheme"
|
||||||
|
android:supportsRtl="true">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:exported="true">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activities.CallActivity"
|
||||||
|
android:label="CallActivity"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:exported="true">
|
||||||
|
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".phonecallui.PhoneCallActivity"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:exported="true">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="android.intent.action.DIAL"/>
|
||||||
|
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data android:scheme="tel"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="android.intent.action.DIAL"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name="cc.winboll.studio.contacts.activities.SettingsActivity"/>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name="cc.winboll.studio.contacts.services.MainService"
|
||||||
|
android:exported="true"/>
|
||||||
|
|
||||||
|
<service android:name="cc.winboll.studio.contacts.services.AssistantService"/>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".phonecallui.PhoneCallService"
|
||||||
|
android:permission="android.permission.BIND_INCALL_SERVICE"
|
||||||
|
android:exported="false">
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.telecom.IN_CALL_SERVICE_UI"
|
||||||
|
android:value="true"/>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="android.telecom.InCallService"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".listenphonecall.CallListenerService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="false">
|
||||||
|
|
||||||
|
<intent-filter android:priority="1000">
|
||||||
|
|
||||||
|
<action android:name=".service.CallShowService"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<receiver android:name=".receivers.MainReceiver">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="cc.winboll.studio.contacts.receivers.MainReceiver"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".widgets.APPStatusWidget"
|
||||||
|
android:exported="true">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||||
|
|
||||||
|
<action android:name="cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_ACTIVE"/>
|
||||||
|
|
||||||
|
<action android:name="cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_NOACTIVE"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/appwidget_provider_info"/>
|
||||||
|
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<receiver android:name=".widgets.APPStatusWidgetClickListener">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
|
||||||
|
<action android:name="cc.winboll.studio.contacts.widgets.APPStatusWidgetClickListener.ACTION_APPICON_CLICK"/>
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/studio_provider"/>
|
||||||
|
|
||||||
|
</provider>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
@@ -0,0 +1,59 @@
|
|||||||
|
package cc.winboll.studio.contacts;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class ActivityStack {
|
||||||
|
|
||||||
|
private static final ActivityStack INSTANCE = new ActivityStack();
|
||||||
|
|
||||||
|
private List<Activity> activities = new ArrayList<>();
|
||||||
|
|
||||||
|
public static ActivityStack getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addActivity(Activity activity) {
|
||||||
|
activities.add(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Activity getTopActivity() {
|
||||||
|
if (activities.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return activities.get(activities.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finishTopActivity() {
|
||||||
|
if (!activities.isEmpty()) {
|
||||||
|
activities.remove(activities.size() - 1).finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finishActivity(Activity activity) {
|
||||||
|
if (activity != null) {
|
||||||
|
activities.remove(activity);
|
||||||
|
activity.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finishActivity(Class activityClass) {
|
||||||
|
for (Activity activity : activities) {
|
||||||
|
if (activity.getClass().equals(activityClass)) {
|
||||||
|
finishActivity(activity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finishAllActivity() {
|
||||||
|
if (!activities.isEmpty()) {
|
||||||
|
for (Activity activity : activities) {
|
||||||
|
activity.finish();
|
||||||
|
activities.remove(activity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
contacts/src/main/java/cc/winboll/studio/contacts/App.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package cc.winboll.studio.contacts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen@QQ.COM
|
||||||
|
* @Date 2024/12/08 15:10:51
|
||||||
|
* @Describe 全局应用类
|
||||||
|
*/
|
||||||
|
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libapputils.app.WinBollActivityManager;
|
||||||
|
|
||||||
|
public class App extends GlobalApplication {
|
||||||
|
|
||||||
|
public static final String TAG = "App";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
// 必须在调用基类前设置应用调试标志,
|
||||||
|
// 这样可以预先设置日志与数据的存储根目录。
|
||||||
|
setIsDebuging(this, BuildConfig.DEBUG);
|
||||||
|
super.onCreate();
|
||||||
|
// 设置 WinBoll 应用 UI 类型
|
||||||
|
WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Aplication);
|
||||||
|
|
||||||
|
LogUtils.d(TAG, "onCreate");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,406 @@
|
|||||||
|
package cc.winboll.studio.contacts;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.app.role.RoleManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.telecom.TelecomManager;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Switch;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
import cc.winboll.studio.contacts.R;
|
||||||
|
import cc.winboll.studio.contacts.activities.CallActivity;
|
||||||
|
import cc.winboll.studio.contacts.adapters.MyPagerAdapter;
|
||||||
|
import cc.winboll.studio.contacts.beans.MainServiceBean;
|
||||||
|
import cc.winboll.studio.contacts.services.MainService;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libappbase.LogView;
|
||||||
|
import cc.winboll.studio.libapputils.app.IWinBollActivity;
|
||||||
|
import cc.winboll.studio.libapputils.app.WinBollActivityManager;
|
||||||
|
import cc.winboll.studio.libapputils.bean.APPInfo;
|
||||||
|
import cc.winboll.studio.libapputils.view.YesNoAlertDialog;
|
||||||
|
import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
|
||||||
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import cc.winboll.studio.contacts.activities.SettingsActivity;
|
||||||
|
|
||||||
|
final public class MainActivity extends AppCompatActivity implements IWinBollActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
|
||||||
|
|
||||||
|
public static final String TAG = "MainActivity";
|
||||||
|
|
||||||
|
public static final int REQUEST_HOME_ACTIVITY = 0;
|
||||||
|
public static final int REQUEST_ABOUT_ACTIVITY = 1;
|
||||||
|
|
||||||
|
public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoll.ACTION_SOS";
|
||||||
|
|
||||||
|
LogView mLogView;
|
||||||
|
Toolbar mToolbar;
|
||||||
|
CheckBox cbMainService;
|
||||||
|
MainServiceBean mMainServiceBean;
|
||||||
|
ViewPager viewPager;
|
||||||
|
private List<View> views; //用来存放放进ViewPager里面的布局
|
||||||
|
//实例化存储imageView(导航原点)的集合
|
||||||
|
ImageView[] imageViews;
|
||||||
|
//MyPagerAdapter adapter;//适配器
|
||||||
|
MyPagerAdapter pagerAdapter;
|
||||||
|
LinearLayout linearLayout;//下标所在在LinearLayout布局里
|
||||||
|
int currentPoint = 0;//当前被选中中页面的下标
|
||||||
|
|
||||||
|
private static final int DIALER_REQUEST_CODE = 1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AppCompatActivity getActivity() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public APPInfo getAppInfo() {
|
||||||
|
// String szBranchName = "contacts";
|
||||||
|
//
|
||||||
|
// APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo();
|
||||||
|
// appInfo.setAppName("Contacts");
|
||||||
|
// appInfo.setAppIcon(cc.winboll.studio.libapputils.R.drawable.ic_winboll);
|
||||||
|
// appInfo.setAppDescription("Contacts Description");
|
||||||
|
// appInfo.setAppGitName("APP");
|
||||||
|
// appInfo.setAppGitOwner("Studio");
|
||||||
|
// appInfo.setAppGitAPPBranch(szBranchName);
|
||||||
|
// appInfo.setAppGitAPPSubProjectFolder(szBranchName);
|
||||||
|
// appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts");
|
||||||
|
// appInfo.setAppAPKName("Contacts");
|
||||||
|
// appInfo.setAppAPKFolderName("Contacts");
|
||||||
|
// return appInfo;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
// 接收并处理 Intent 数据,函数 Intent 处理接收就直接返回
|
||||||
|
//if (prosessIntents(getIntent())) return;
|
||||||
|
// 以下正常创建主窗口
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
// 初始化工具栏
|
||||||
|
mToolbar = findViewById(R.id.activitymainToolbar1);
|
||||||
|
setSupportActionBar(mToolbar);
|
||||||
|
if (isEnableDisplayHomeAsUp()) {
|
||||||
|
// 显示后退按钮
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
}
|
||||||
|
getSupportActionBar().setSubtitle(getTag());
|
||||||
|
|
||||||
|
initData();
|
||||||
|
initView();
|
||||||
|
//initPoint();//调用初始化导航原点的方法
|
||||||
|
viewPager.addOnPageChangeListener(this);//滑动事件
|
||||||
|
|
||||||
|
ViewPager viewPager = findViewById(R.id.activitymainViewPager1);
|
||||||
|
MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
|
||||||
|
viewPager.setAdapter(pagerAdapter);
|
||||||
|
TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1);
|
||||||
|
tabLayout.setupWithViewPager(viewPager);
|
||||||
|
|
||||||
|
// mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||||
|
// if (mMainServiceBean == null) {
|
||||||
|
// mMainServiceBean = new MainServiceBean();
|
||||||
|
// }
|
||||||
|
// cbMainService = findViewById(R.id.activitymainCheckBox1);
|
||||||
|
// cbMainService.setChecked(mMainServiceBean.isEnable());
|
||||||
|
// cbMainService.setOnClickListener(new View.OnClickListener(){
|
||||||
|
// @Override
|
||||||
|
// public void onClick(View view) {
|
||||||
|
// if (cbMainService.isChecked()) {
|
||||||
|
// MainService.startMainService(MainActivity.this);
|
||||||
|
// } else {
|
||||||
|
// MainService.stopMainService(MainActivity.this);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
MainService.startMainService(MainActivity.this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化view,即显示的图片
|
||||||
|
void initView() {
|
||||||
|
viewPager = findViewById(R.id.activitymainViewPager1);
|
||||||
|
pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
|
||||||
|
viewPager.setAdapter(pagerAdapter);
|
||||||
|
//adapter = new MyPagerAdapter(views);
|
||||||
|
//viewPager = findViewById(R.id.activitymainViewPager1);
|
||||||
|
//viewPager.setAdapter(adapter);
|
||||||
|
//linearLayout = findViewById(R.id.activitymainLinearLayout1);
|
||||||
|
//initPoint();//初始化页面下方的点
|
||||||
|
viewPager.setOnPageChangeListener(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化所要显示的布局
|
||||||
|
void initData() {
|
||||||
|
ViewPager viewPager = findViewById(R.id.activitymainViewPager1);
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(getActivity());
|
||||||
|
View view1 = inflater.inflate(R.layout.fragment_call, viewPager, false);
|
||||||
|
View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false);
|
||||||
|
View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false);
|
||||||
|
|
||||||
|
views = new ArrayList<>();
|
||||||
|
views.add(view1);
|
||||||
|
views.add(view2);
|
||||||
|
views.add(view3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// void initPoint() {
|
||||||
|
// imageViews = new ImageView[5];//实例化5个图片
|
||||||
|
// for (int i = 0; i < linearLayout.getChildCount(); i++) {
|
||||||
|
// imageViews[i] = (ImageView) linearLayout.getChildAt(i);
|
||||||
|
// imageViews[i].setImageResource(R.drawable.ic_launcher);
|
||||||
|
// imageViews[i].setOnClickListener(this);//点击导航点,即可跳转
|
||||||
|
// imageViews[i].setTag(i);//重复利用实例化的对象
|
||||||
|
// }
|
||||||
|
// currentPoint = 0;//默认第一个坐标
|
||||||
|
// imageViews[currentPoint].setImageResource(R.drawable.ic_launcher);
|
||||||
|
// }
|
||||||
|
|
||||||
|
//OnPageChangeListener接口要实现的三个方法
|
||||||
|
/* onPageScrollStateChanged(int state)
|
||||||
|
此方法是在状态改变的时候调用,其中state这个参数有三种状态:
|
||||||
|
SCROLL_STATE_DRAGGING(1)表示用户手指“按在屏幕上并且开始拖动”的状态
|
||||||
|
(手指按下但是还没有拖动的时候还不是这个状态,只有按下并且手指开始拖动后log才打出。)
|
||||||
|
SCROLL_STATE_IDLE(0)滑动动画做完的状态。
|
||||||
|
SCROLL_STATE_SETTLING(2)在“手指离开屏幕”的状态。*/
|
||||||
|
@Override
|
||||||
|
public void onPageScrollStateChanged(int state) {
|
||||||
|
|
||||||
|
}
|
||||||
|
/* onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
|
||||||
|
当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为:
|
||||||
|
|
||||||
|
position :当前页面,即你点击滑动的页面(从A滑B,则是A页面的position。
|
||||||
|
positionOffset:当前页面偏移的百分比
|
||||||
|
positionOffsetPixels:当前页面偏移的像素位置*/
|
||||||
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
|
||||||
|
}
|
||||||
|
/* onPageSelected(int position)
|
||||||
|
此方法是页面滑动完后得到调用,position是你当前选中的页面的Position(位置编号)
|
||||||
|
(从A滑动到B,就是B的position)*/
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
|
||||||
|
// ImageView preView = imageViews[currentPoint];
|
||||||
|
// preView.setImageResource(R.drawable.ic_launcher);
|
||||||
|
// ImageView currView = imageViews[position];
|
||||||
|
// currView.setImageResource(R.drawable.ic_launcher);
|
||||||
|
// currentPoint = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
//小圆点点击事件
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
//通过getTag(),可以判断是哪个控件
|
||||||
|
// int i = (Integer) v.getTag();
|
||||||
|
// viewPager.setCurrentItem(i);//直接跳转到某一个页面的情况
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostCreate(Bundle savedInstanceState) {
|
||||||
|
super.onPostCreate(savedInstanceState);
|
||||||
|
//setSubTitle("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
LogUtils.d(TAG, "onDestroy() SOS");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 处理传入的 Intent 数据
|
||||||
|
//
|
||||||
|
// boolean prosessIntents(Intent intent) {
|
||||||
|
// if (intent == null
|
||||||
|
// || intent.getAction() == null
|
||||||
|
// || intent.getAction().equals(""))
|
||||||
|
// return false;
|
||||||
|
//
|
||||||
|
// if (intent.getAction().equals(StringToQrCodeView.ACTION_UNITTEST_QRCODE)) {
|
||||||
|
// try {
|
||||||
|
// WinBollActivity clazzActivity = UnitTestActivity.class.newInstance();
|
||||||
|
// String tag = clazzActivity.getTag();
|
||||||
|
// LogUtils.d(TAG, "String tag = clazzActivity.getTag(); tag " + tag);
|
||||||
|
// Intent subIntent = new Intent(this, UnitTestActivity.class);
|
||||||
|
// subIntent.setAction(intent.getAction());
|
||||||
|
// File file = new File(getCacheDir(), UUID.randomUUID().toString());
|
||||||
|
// //取出文件uri
|
||||||
|
// Uri uri = intent.getData();
|
||||||
|
// if (uri == null) {
|
||||||
|
// uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||||
|
// }
|
||||||
|
// //获取文件真实地址
|
||||||
|
// String szSrcPath = UriUtils.getFileFromUri(getApplication(), uri);
|
||||||
|
// if (TextUtils.isEmpty(szSrcPath)) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Files.copy(Paths.get(szSrcPath), Paths.get(file.getPath()));
|
||||||
|
// //startWinBollActivity(subIntent, tag);
|
||||||
|
// WinBollActivityManager.getInstance(this).startWinBollActivity(this, subIntent, UnitTestActivity.class);
|
||||||
|
// } catch (IllegalAccessException | InstantiationException | IOException e) {
|
||||||
|
// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
|
// // 函数处理异常返回失败
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// LogUtils.d(TAG, "prosessIntents|" + intent.getAction() + "|yet");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Toolbar initToolBar() {
|
||||||
|
return findViewById(R.id.activitymainToolbar1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAddWinBollToolBar() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnableDisplayHomeAsUp() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit() {
|
||||||
|
YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onYes() {
|
||||||
|
WinBollActivityManager.getInstance(getApplicationContext()).finishAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNo() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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_call) {
|
||||||
|
Intent intent = new Intent(this, CallActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
|
||||||
|
} else if (item.getItemId() == R.id.item_settings) {
|
||||||
|
Intent intent = new Intent(this, SettingsActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
|
||||||
|
}
|
||||||
|
// } else
|
||||||
|
// if (item.getItemId() == R.id.item_exit) {
|
||||||
|
// exit();
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Android M 及以上检查是否是系统默认电话应用
|
||||||
|
*/
|
||||||
|
public boolean isDefaultPhoneCallApp() {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);
|
||||||
|
if (manger != null && manger.getDefaultDialerPackage() != null) {
|
||||||
|
return manger.getDefaultDialerPackage().equals(getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isServiceRunning(Class<?> serviceClass) {
|
||||||
|
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||||
|
if (manager == null) return false;
|
||||||
|
|
||||||
|
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
|
||||||
|
Integer.MAX_VALUE)) {
|
||||||
|
if (serviceClass.getName().equals(service.service.getClassName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
// switch (resultCode) {
|
||||||
|
// case REQUEST_HOME_ACTIVITY : {
|
||||||
|
// LogUtils.d(TAG, "REQUEST_HOME_ACTIVITY");
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case REQUEST_ABOUT_ACTIVITY : {
|
||||||
|
// LogUtils.d(TAG, "REQUEST_ABOUT_ACTIVITY");
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// default : {
|
||||||
|
// super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if (requestCode == DIALER_REQUEST_CODE) {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|