Compare commits
40 Commits
contacts-v
...
appbase-v1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9052a3924b | ||
![]() |
9642ad1966 | ||
![]() |
6686da0e8f | ||
![]() |
9e7aff09d1 | ||
![]() |
494b2b7fbc | ||
![]() |
b1f9b74e28 | ||
![]() |
32287e17c0 | ||
![]() |
8a0605c12e | ||
![]() |
5768881a47 | ||
![]() |
6e02c130b2 | ||
![]() |
fb9443bbc7 | ||
![]() |
bfc1180822 | ||
![]() |
ee52a6c28c | ||
![]() |
2f72f1795e | ||
![]() |
db28447c0f | ||
![]() |
ad2c29d8ff | ||
![]() |
b0e3bfe243 | ||
![]() |
7b1aeeae8b | ||
![]() |
54cceeed3b | ||
![]() |
a72c5e1e6e | ||
![]() |
26fcd9b584 | ||
![]() |
ea7a0bda14 | ||
![]() |
b26b9dbbc4 | ||
![]() |
8a405d33a7 | ||
![]() |
2b92da3e01 | ||
![]() |
ac1c9fb9f5 | ||
![]() |
16dd559c5b | ||
![]() |
3793ae3252 | ||
![]() |
dc8f127a8d | ||
![]() |
3cf4155eab | ||
![]() |
7ca144bfc4 | ||
![]() |
73222d3272 | ||
![]() |
b9c9eb926d | ||
![]() |
35efcdddcd | ||
![]() |
fc5ac32514 | ||
![]() |
afa8a97dba | ||
![]() |
fe004c7b82 | ||
![]() |
b9582cb99d | ||
![]() |
b3a69283ab | ||
![]() |
36e10db8e3 |
@@ -29,7 +29,7 @@ android {
|
||||
// versionName 更新后需要手动设置
|
||||
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
||||
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||
versionName "1.0"
|
||||
versionName "1.3"
|
||||
if(true) {
|
||||
versionName = genVersionName("${versionName}")
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Sun Jan 05 02:30:11 GMT 2025
|
||||
stageCount=0
|
||||
libraryProject=
|
||||
baseVersion=1.0
|
||||
publishVersion=1.0.0
|
||||
buildCount=2
|
||||
baseBetaVersion=1.0.1
|
||||
#Tue Feb 11 13:11:17 HKT 2025
|
||||
stageCount=1
|
||||
libraryProject=libappbase
|
||||
baseVersion=1.3
|
||||
publishVersion=1.3.0
|
||||
buildCount=0
|
||||
baseBetaVersion=1.3.1
|
||||
|
@@ -5,12 +5,19 @@ package cc.winboll.studio.appbase;
|
||||
* @Date 2025/01/05 09:54:42
|
||||
* @Describe APPbase 应用类
|
||||
*/
|
||||
import cc.winboll.studio.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
public class App extends GlobalApplication {
|
||||
|
||||
|
||||
public static final String TAG = "App";
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
GlobalApplication.setIsDebuging(this, BuildConfig.DEBUG);
|
||||
super.onCreate();
|
||||
LogUtils.setLogLevel(LogUtils.LOG_LEVEL.Debug);
|
||||
LogUtils.setALlTAGListEnable(true);
|
||||
LogUtils.d(TAG, "LogUtils init");
|
||||
}
|
||||
}
|
||||
|
@@ -1,15 +1,23 @@
|
||||
package cc.winboll.studio.appbase;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
package cc.winboll.studio.appbase;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
|
||||
cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
|
||||
}
|
||||
|
||||
public void onSwitchDebugMode(View view) {
|
||||
GlobalApplication.setIsDebuging(this, ((CheckBox)view).isChecked());
|
||||
}
|
||||
}
|
||||
|
@@ -4,13 +4,36 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello, WinBoll!"
|
||||
android:onClick="onHello"/>
|
||||
android:text="Hello, WinBoll!"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right|center_vertical">
|
||||
|
||||
<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"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@@ -1,9 +0,0 @@
|
||||
<?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>
|
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#009688</color>
|
||||
<color name="colorPrimaryDark">#00796B</color>
|
||||
<color name="colorAccent">#FF9800</color>
|
||||
</resources>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorTextColor">#006D26C0</color>
|
||||
<color name="colorPrimary">#001BDCC0</color>
|
||||
<color name="colorPrimaryDark">#0028C025</color>
|
||||
<color name="colorAccent">#002985CC</color>
|
||||
</resources>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="AppTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
|
||||
</style>
|
||||
</resources>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="AppTheme" parent="@android:style/Theme.Material.Light.DarkActionBar">
|
||||
</style>
|
||||
</resources>
|
||||
|
@@ -1,40 +0,0 @@
|
||||
# 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 +0,0 @@
|
||||
|
@@ -1,71 +0,0 @@
|
||||
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'])
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Tue Feb 25 00:17:29 HKT 2025
|
||||
stageCount=2
|
||||
libraryProject=winboll-shared
|
||||
baseVersion=1.0
|
||||
publishVersion=1.0.1
|
||||
buildCount=0
|
||||
baseBetaVersion=1.0.2
|
17
contacts/proguard-rules.pro
vendored
17
contacts/proguard-rules.pro
vendored
@@ -1,17 +0,0 @@
|
||||
# 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 *;
|
||||
#}
|
@@ -1,13 +0,0 @@
|
||||
<?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>
|
||||
|
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Contacts+</string>
|
||||
|
||||
</resources>
|
@@ -1,187 +0,0 @@
|
||||
<?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>
|
@@ -1,59 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
@@ -1,406 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
package cc.winboll.studio.contacts;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/20 21:14:52
|
||||
* @Describe PhoneCallManager
|
||||
*/
|
||||
|
||||
import android.telecom.Call;
|
||||
import android.telecom.VideoProfile;
|
||||
|
||||
public class PhoneCallManager {
|
||||
|
||||
public static final String TAG = "PhoneCallManager";
|
||||
|
||||
public static Call call;
|
||||
|
||||
/**
|
||||
* 接听电话
|
||||
*/
|
||||
public void answer() {
|
||||
if (call != null) {
|
||||
call.answer(VideoProfile.STATE_AUDIO_ONLY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开电话,包括来电时的拒接以及接听后的挂断
|
||||
*/
|
||||
public void disconnect() {
|
||||
if (call != null) {
|
||||
call.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,118 +0,0 @@
|
||||
package cc.winboll.studio.contacts.activities;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/20 17:15:46
|
||||
* @Describe 拨号窗口
|
||||
*/
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import cc.winboll.studio.contacts.MainActivity;
|
||||
import cc.winboll.studio.contacts.R;
|
||||
|
||||
public class CallActivity extends AppCompatActivity {
|
||||
public static final String TAG = "CallActivity";
|
||||
|
||||
private static final int REQUEST_CALL_PHONE = 1;
|
||||
private EditText phoneNumberEditText;
|
||||
private TextView callStatusTextView;
|
||||
private TelephonyManager telephonyManager;
|
||||
private MyPhoneStateListener phoneStateListener;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
//setContentView(R.layout.activity_main);
|
||||
setContentView(R.layout.activity_call);
|
||||
|
||||
phoneNumberEditText = findViewById(R.id.phone_number);
|
||||
Button dialButton = findViewById(R.id.dial_button);
|
||||
callStatusTextView = findViewById(R.id.call_status);
|
||||
|
||||
dialButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
String phoneNumber = phoneNumberEditText.getText().toString().trim();
|
||||
if (!phoneNumber.isEmpty()) {
|
||||
if (ContextCompat.checkSelfPermission(CallActivity.this, Manifest.permission.CALL_PHONE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(CallActivity.this,
|
||||
new String[]{Manifest.permission.CALL_PHONE},
|
||||
REQUEST_CALL_PHONE);
|
||||
} else {
|
||||
dialPhoneNumber(phoneNumber);
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(CallActivity.this, "请输入电话号码", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化TelephonyManager和PhoneStateListener
|
||||
telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
|
||||
phoneStateListener = new MyPhoneStateListener();
|
||||
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == REQUEST_CALL_PHONE) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
String phoneNumber = phoneNumberEditText.getText().toString().trim();
|
||||
dialPhoneNumber(phoneNumber);
|
||||
} else {
|
||||
Toast.makeText(this, "未授予拨打电话权限", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void dialPhoneNumber(String phoneNumber) {
|
||||
Intent intent = new Intent(Intent.ACTION_CALL);
|
||||
intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
|
||||
return;
|
||||
}
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private class MyPhoneStateListener extends PhoneStateListener {
|
||||
@Override
|
||||
public void onCallStateChanged(int state, String incomingNumber) {
|
||||
switch (state) {
|
||||
case TelephonyManager.CALL_STATE_IDLE:
|
||||
callStatusTextView.setText("电话已挂断");
|
||||
break;
|
||||
case TelephonyManager.CALL_STATE_OFFHOOK:
|
||||
callStatusTextView.setText("正在通话中");
|
||||
break;
|
||||
case TelephonyManager.CALL_STATE_RINGING:
|
||||
callStatusTextView.setText("来电: " + incomingNumber);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
// 取消监听
|
||||
if (telephonyManager != null) {
|
||||
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,40 +0,0 @@
|
||||
package cc.winboll.studio.contacts.activities;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/20 20:18:26
|
||||
*/
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import cc.winboll.studio.contacts.R;
|
||||
|
||||
public class DialerActivity extends AppCompatActivity {
|
||||
|
||||
public static final String TAG = "DialerActivity";
|
||||
|
||||
private EditText phoneNumberEditText;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_dialer);
|
||||
|
||||
phoneNumberEditText = findViewById(R.id.phone_number_edit_text);
|
||||
Button dialButton = findViewById(R.id.dial_button);
|
||||
|
||||
dialButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
String phoneNumber = phoneNumberEditText.getText().toString();
|
||||
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber));
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -1,143 +0,0 @@
|
||||
package cc.winboll.studio.contacts.activities;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/21 05:37:42
|
||||
*/
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Switch;
|
||||
import android.widget.Toast;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import cc.winboll.studio.contacts.R;
|
||||
import cc.winboll.studio.contacts.beans.RingTongBean;
|
||||
import cc.winboll.studio.libappbase.IWinBollActivity;
|
||||
import cc.winboll.studio.libappbase.bean.APPInfo;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class SettingsActivity extends AppCompatActivity implements IWinBollActivity {
|
||||
|
||||
public static final String TAG = "SettingsActivity";
|
||||
|
||||
Toolbar mToolbar;
|
||||
Switch swSilent;
|
||||
|
||||
@Override
|
||||
public APPInfo getAppInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppCompatActivity getActivity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@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
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_settings);
|
||||
|
||||
// 初始化工具栏
|
||||
mToolbar = findViewById(R.id.activitymainToolbar1);
|
||||
setSupportActionBar(mToolbar);
|
||||
if (isEnableDisplayHomeAsUp()) {
|
||||
// 显示后退按钮
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
getSupportActionBar().setSubtitle(getTag());
|
||||
}
|
||||
|
||||
public void onDefaultPhone(View view) {
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
public void onCanDrawOverlays(View view) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& !Settings.canDrawOverlays(this)) {
|
||||
// 请求 悬浮框 权限
|
||||
askForDrawOverlay();
|
||||
} else {
|
||||
ToastUtils.show("悬浮窗已开启");
|
||||
}
|
||||
}
|
||||
|
||||
private void askForDrawOverlay() {
|
||||
AlertDialog alertDialog = new AlertDialog.Builder(this)
|
||||
.setTitle("允许显示悬浮框")
|
||||
.setMessage("为了使电话监听服务正常工作,请允许这项权限")
|
||||
.setPositiveButton("去设置", new DialogInterface.OnClickListener(){
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
openDrawOverlaySettings();
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("稍后再说", new DialogInterface.OnClickListener(){
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
|
||||
//noinspection ConstantConditions
|
||||
alertDialog.getWindow().setFlags(
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转悬浮窗管理设置界面
|
||||
*/
|
||||
private void openDrawOverlaySettings() {
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// Android M 以上引导用户去系统设置中打开允许悬浮窗
|
||||
// 使用反射是为了用尽可能少的代码保证在大部分机型上都可用
|
||||
try {
|
||||
Context context = this;
|
||||
Class clazz = Settings.class;
|
||||
Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION");
|
||||
Intent intent = new Intent(field.get(null).toString());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setData(Uri.parse("package:" + context.getPackageName()));
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, "请在悬浮窗管理中打开权限", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
package cc.winboll.studio.contacts.adapters;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/20 14:20:38
|
||||
* @Describe ImagePagerAdapter
|
||||
*/
|
||||
public class ImagePagerAdapter {
|
||||
|
||||
public static final String TAG = "ImagePagerAdapter";
|
||||
|
||||
|
||||
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
package cc.winboll.studio.contacts.adapters;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/20 13:33:04
|
||||
* @Describe MyPagerAdapter
|
||||
*/
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
import cc.winboll.studio.contacts.fragments.CallFragment;
|
||||
import cc.winboll.studio.contacts.fragments.ContactsFragment;
|
||||
import cc.winboll.studio.contacts.fragments.LogFragment;
|
||||
|
||||
public class MyPagerAdapter extends FragmentPagerAdapter {
|
||||
public static final String TAG = "MyPagerAdapter";
|
||||
|
||||
private static final int PAGE_COUNT = 3;
|
||||
|
||||
public MyPagerAdapter(@NonNull FragmentManager fm) {
|
||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
if(position == 1) {
|
||||
return ContactsFragment.newInstance(position);
|
||||
} else if(position == 2) {
|
||||
return LogFragment.newInstance(position);
|
||||
} else {
|
||||
return CallFragment.newInstance(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return PAGE_COUNT;
|
||||
}
|
||||
}
|
||||
|
@@ -1,88 +0,0 @@
|
||||
package cc.winboll.studio.contacts.beans;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/21 09:52:10
|
||||
* @Describe 电话黑名单规则
|
||||
*/
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PhoneBlackRuleBean extends BaseBean {
|
||||
|
||||
public static final String TAG = "PhoneBlackRuleBean";
|
||||
|
||||
String ruleText;
|
||||
boolean isEnable;
|
||||
|
||||
public PhoneBlackRuleBean() {
|
||||
this.ruleText = "";
|
||||
this.isEnable = false;
|
||||
}
|
||||
|
||||
public PhoneBlackRuleBean(String ruleText, boolean isEnable) {
|
||||
this.ruleText = ruleText;
|
||||
this.isEnable = isEnable;
|
||||
}
|
||||
|
||||
public void setRuleText(String ruleText) {
|
||||
this.ruleText = ruleText;
|
||||
}
|
||||
|
||||
public String getRuleText() {
|
||||
return ruleText;
|
||||
}
|
||||
|
||||
public void setIsEnable(boolean isEnable) {
|
||||
this.isEnable = isEnable;
|
||||
}
|
||||
|
||||
public boolean isEnable() {
|
||||
return isEnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return PhoneBlackRuleBean.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("ruleText").value(getRuleText());
|
||||
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("ruleText")) {
|
||||
setRuleText(jsonReader.nextString());
|
||||
} else if (name.equals("isEnable")) {
|
||||
setIsEnable(jsonReader.nextBoolean());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -1,74 +0,0 @@
|
||||
package cc.winboll.studio.contacts.beans;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/24 18:47:11
|
||||
* @Describe 手机铃声设置参数类
|
||||
*/
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import android.util.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import android.media.AudioManager;
|
||||
import android.util.JsonReader;
|
||||
|
||||
public class RingTongBean extends BaseBean {
|
||||
|
||||
public static final String TAG = "AudioRingTongBean";
|
||||
|
||||
// 模式
|
||||
int ringerMode;
|
||||
|
||||
public RingTongBean() {
|
||||
this.ringerMode = AudioManager.RINGER_MODE_NORMAL;
|
||||
}
|
||||
|
||||
public RingTongBean(int ringerMode) {
|
||||
this.ringerMode = ringerMode;
|
||||
}
|
||||
|
||||
public void setRingerMode(int ringerMode) {
|
||||
this.ringerMode = ringerMode;
|
||||
}
|
||||
|
||||
public int getRingerMode() {
|
||||
return ringerMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return RingTongBean.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("ringerMode").value(getRingerMode());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||
if (name.equals("ringerMode")) {
|
||||
setRingerMode(jsonReader.nextInt());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return this;
|
||||
}
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
package cc.winboll.studio.contacts.dun;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/21 06:15:10
|
||||
* @Describe 云盾防御规则
|
||||
*/
|
||||
import cc.winboll.studio.contacts.beans.PhoneBlackRuleBean;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
import android.content.Context;
|
||||
|
||||
public class Rules {
|
||||
|
||||
public static final String TAG = "Rules";
|
||||
|
||||
ArrayList<PhoneBlackRuleBean> _PhoneBlacRuleBeanList;
|
||||
static volatile Rules _Rules;
|
||||
Context mContext;
|
||||
|
||||
Rules(Context context) {
|
||||
mContext = context;
|
||||
_PhoneBlacRuleBeanList = new ArrayList<PhoneBlackRuleBean>();
|
||||
PhoneBlackRuleBean.loadBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
|
||||
|
||||
}
|
||||
public static synchronized Rules getInstance(Context context) {
|
||||
if (_Rules == null) {
|
||||
_Rules = new Rules(context);
|
||||
}
|
||||
return _Rules;
|
||||
}
|
||||
|
||||
public boolean isAllowed(String phoneNumber) {
|
||||
// 黑名单拒接
|
||||
for (int i = 0; i < _PhoneBlacRuleBeanList.size(); i++) {
|
||||
if (_PhoneBlacRuleBeanList.get(i).isEnable()) {
|
||||
String regex = _PhoneBlacRuleBeanList.get(i).getRuleText();
|
||||
if (Pattern.matches(regex, phoneNumber)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 手机号码允许
|
||||
// 中国手机号码正则表达式,以1开头,第二位可以是3、4、5、6、7、8、9,后面跟9位数字
|
||||
String regex = "^1[3-9]\\d{9}$";
|
||||
if (Pattern.matches(regex, phoneNumber)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 指定区号号码允许
|
||||
regex = "^0660\\d+$";
|
||||
if (Pattern.matches(regex, phoneNumber)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 指定区号号码允许
|
||||
regex = "^020\\d+$";
|
||||
if (Pattern.matches(regex, phoneNumber)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 其他拒接
|
||||
return false;
|
||||
}
|
||||
|
||||
public void add(String phoneRuleBlack, boolean isEnable) {
|
||||
_PhoneBlacRuleBeanList.add(new PhoneBlackRuleBean(phoneRuleBlack, isEnable));
|
||||
PhoneBlackRuleBean.saveBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
|
||||
}
|
||||
|
||||
public ArrayList<PhoneBlackRuleBean> getPhoneBlacRuleBeanList() {
|
||||
return _PhoneBlacRuleBeanList;
|
||||
}
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
package cc.winboll.studio.contacts.fragments;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/20 12:57:00
|
||||
* @Describe 拨号
|
||||
*/
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import cc.winboll.studio.contacts.R;
|
||||
import cc.winboll.studio.libappbase.LogView;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class CallFragment extends Fragment {
|
||||
|
||||
public static final String TAG = "CallFragment";
|
||||
|
||||
private static final String ARG_PAGE = "ARG_PAGE";
|
||||
private int mPage;
|
||||
|
||||
public static CallFragment newInstance(int page) {
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_PAGE, page);
|
||||
CallFragment fragment = new CallFragment();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getArguments()!= null) {
|
||||
mPage = getArguments().getInt(ARG_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_call, container, false);
|
||||
TextView textView = view.findViewById(R.id.page_text);
|
||||
textView.setText("这是第 " + mPage + " 页");
|
||||
return view;
|
||||
}
|
||||
}
|
@@ -1,50 +0,0 @@
|
||||
package cc.winboll.studio.contacts.fragments;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/20 12:57:50
|
||||
* @Describe 联系人
|
||||
*/
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import cc.winboll.studio.contacts.R;
|
||||
|
||||
public class ContactsFragment extends Fragment {
|
||||
|
||||
public static final String TAG = "ContactsFragment";
|
||||
|
||||
private static final String ARG_PAGE = "ARG_PAGE";
|
||||
private int mPage;
|
||||
|
||||
public static ContactsFragment newInstance(int page) {
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_PAGE, page);
|
||||
ContactsFragment fragment = new ContactsFragment();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getArguments()!= null) {
|
||||
mPage = getArguments().getInt(ARG_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_contacts, container, false);
|
||||
TextView textView = view.findViewById(R.id.page_text);
|
||||
textView.setText("这是第 " + mPage + " 页");
|
||||
return view;
|
||||
}
|
||||
}
|
@@ -1,50 +0,0 @@
|
||||
package cc.winboll.studio.contacts.fragments;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/20 12:58:15
|
||||
* @Describe 应用日志
|
||||
*/
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import cc.winboll.studio.contacts.R;
|
||||
import cc.winboll.studio.libappbase.LogView;
|
||||
|
||||
public class LogFragment extends Fragment {
|
||||
|
||||
public static final String TAG = "LogFragment";
|
||||
|
||||
private static final String ARG_PAGE = "ARG_PAGE";
|
||||
private int mPage;
|
||||
|
||||
public static LogFragment newInstance(int page) {
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_PAGE, page);
|
||||
LogFragment fragment = new LogFragment();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getArguments() != null) {
|
||||
mPage = getArguments().getInt(ARG_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_log, container, false);
|
||||
LogView logView = view.findViewById(R.id.logview);
|
||||
logView.start();
|
||||
return view;
|
||||
}
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
package cc.winboll.studio.contacts.handlers;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/14 03:51:40
|
||||
*/
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import cc.winboll.studio.contacts.services.MainService;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class MainServiceHandler extends Handler {
|
||||
public static final String TAG = "MainServiceHandler";
|
||||
|
||||
public static final int MSG_REMINDTHREAD = 0;
|
||||
|
||||
WeakReference<MainService> serviceWeakReference;
|
||||
public MainServiceHandler(MainService service) {
|
||||
serviceWeakReference = new WeakReference<MainService>(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_REMINDTHREAD: // 处理下载完成消息,更新UI
|
||||
{
|
||||
// 显示提醒消息
|
||||
//
|
||||
//LogUtils.d(TAG, "显示提醒消息");
|
||||
MainService mainService = serviceWeakReference.get();
|
||||
if (mainService != null) {
|
||||
mainService.appenMessage((String)msg.obj);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,206 +0,0 @@
|
||||
package cc.winboll.studio.contacts.listenphonecall;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.winboll.studio.contacts.MainActivity;
|
||||
import cc.winboll.studio.contacts.R;
|
||||
|
||||
|
||||
public class CallListenerService extends Service {
|
||||
|
||||
private View phoneCallView;
|
||||
private TextView tvCallNumber;
|
||||
private Button btnOpenApp;
|
||||
|
||||
private WindowManager windowManager;
|
||||
private WindowManager.LayoutParams params;
|
||||
|
||||
private PhoneStateListener phoneStateListener;
|
||||
private TelephonyManager telephonyManager;
|
||||
|
||||
private String callNumber;
|
||||
private boolean hasShown;
|
||||
private boolean isCallingIn;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
initPhoneStateListener();
|
||||
|
||||
initPhoneCallView();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化来电状态监听器
|
||||
*/
|
||||
private void initPhoneStateListener() {
|
||||
phoneStateListener = new PhoneStateListener() {
|
||||
@Override
|
||||
public void onCallStateChanged(int state, String incomingNumber) {
|
||||
super.onCallStateChanged(state, incomingNumber);
|
||||
|
||||
callNumber = incomingNumber;
|
||||
|
||||
switch (state) {
|
||||
case TelephonyManager.CALL_STATE_IDLE: // 待机,即无电话时,挂断时触发
|
||||
dismiss();
|
||||
break;
|
||||
|
||||
case TelephonyManager.CALL_STATE_RINGING: // 响铃,来电时触发
|
||||
isCallingIn = true;
|
||||
updateUI();
|
||||
show();
|
||||
break;
|
||||
|
||||
case TelephonyManager.CALL_STATE_OFFHOOK: // 摘机,接听或拨出电话时触发
|
||||
updateUI();
|
||||
show();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 设置来电监听器
|
||||
telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
|
||||
if (telephonyManager != null) {
|
||||
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void initPhoneCallView() {
|
||||
windowManager = (WindowManager) getApplicationContext()
|
||||
.getSystemService(Context.WINDOW_SERVICE);
|
||||
int width = windowManager.getDefaultDisplay().getWidth();
|
||||
int height = windowManager.getDefaultDisplay().getHeight();
|
||||
|
||||
params = new WindowManager.LayoutParams();
|
||||
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
|
||||
params.width = width;
|
||||
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
params.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||
|
||||
// 设置图片格式,效果为背景透明
|
||||
params.format = PixelFormat.TRANSLUCENT;
|
||||
// 设置 Window flag 为系统级弹框 | 覆盖表层
|
||||
params.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
|
||||
WindowManager.LayoutParams.TYPE_PHONE;
|
||||
|
||||
// 不可聚集(不响应返回键)| 全屏
|
||||
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
|
||||
// API 19 以上则还可以开启透明状态栏与导航栏
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
params.flags = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
|
||||
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
|
||||
}
|
||||
|
||||
FrameLayout interceptorLayout = new FrameLayout(this) {
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
phoneCallView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE))
|
||||
.inflate(R.layout.view_phone_call, interceptorLayout);
|
||||
tvCallNumber = phoneCallView.findViewById(R.id.tv_call_number);
|
||||
btnOpenApp = phoneCallView.findViewById(R.id.btn_open_app);
|
||||
btnOpenApp.setOnClickListener(new View.OnClickListener(){
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
CallListenerService.this.startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示顶级弹框展示通话信息
|
||||
*/
|
||||
private void show() {
|
||||
if (!hasShown) {
|
||||
windowManager.addView(phoneCallView, params);
|
||||
hasShown = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消显示
|
||||
*/
|
||||
private void dismiss() {
|
||||
if (hasShown) {
|
||||
windowManager.removeView(phoneCallView);
|
||||
isCallingIn = false;
|
||||
hasShown = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
tvCallNumber.setText(formatPhoneNumber(callNumber));
|
||||
|
||||
int callTypeDrawable = isCallingIn ? R.drawable.ic_phone_call_in : R.drawable.ic_phone_call_out;
|
||||
tvCallNumber.setCompoundDrawablesWithIntrinsicBounds(null, null,
|
||||
getResources().getDrawable(callTypeDrawable), null);
|
||||
}
|
||||
|
||||
public static String formatPhoneNumber(String phoneNum) {
|
||||
if (!TextUtils.isEmpty(phoneNum) && phoneNum.length() == 11) {
|
||||
return phoneNum.substring(0, 3) + "-"
|
||||
+ phoneNum.substring(3, 7) + "-"
|
||||
+ phoneNum.substring(7);
|
||||
}
|
||||
return phoneNum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
|
||||
}
|
||||
}
|
@@ -1,166 +0,0 @@
|
||||
package cc.winboll.studio.contacts.phonecallui;
|
||||
|
||||
import static cc.winboll.studio.contacts.listenphonecall.CallListenerService.formatPhoneNumber;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import cc.winboll.studio.contacts.ActivityStack;
|
||||
import cc.winboll.studio.contacts.R;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
|
||||
/**
|
||||
* 提供接打电话的界面,仅支持 Android M (6.0, API 23) 及以上的系统
|
||||
*
|
||||
* @author aJIEw
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public class PhoneCallActivity extends AppCompatActivity implements View.OnClickListener {
|
||||
|
||||
private TextView tvCallNumberLabel;
|
||||
private TextView tvCallNumber;
|
||||
private TextView tvPickUp;
|
||||
private TextView tvCallingTime;
|
||||
private TextView tvHangUp;
|
||||
|
||||
private PhoneCallManager phoneCallManager;
|
||||
private PhoneCallService.CallType callType;
|
||||
private String phoneNumber;
|
||||
|
||||
private Timer onGoingCallTimer;
|
||||
private int callingTime;
|
||||
|
||||
public static void actionStart(Context context, String phoneNumber,
|
||||
PhoneCallService.CallType callType) {
|
||||
Intent intent = new Intent(context, PhoneCallActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, callType);
|
||||
intent.putExtra(Intent.EXTRA_PHONE_NUMBER, phoneNumber);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_phone_call);
|
||||
|
||||
ActivityStack.getInstance().addActivity(this);
|
||||
|
||||
initData();
|
||||
|
||||
initView();
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
phoneCallManager = new PhoneCallManager(this);
|
||||
onGoingCallTimer = new Timer();
|
||||
if (getIntent() != null) {
|
||||
phoneNumber = getIntent().getStringExtra(Intent.EXTRA_PHONE_NUMBER);
|
||||
callType = (PhoneCallService.CallType) getIntent().getSerializableExtra(Intent.EXTRA_MIME_TYPES);
|
||||
}
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
||||
getWindow().getDecorView().setSystemUiVisibility(uiOptions);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||
|
||||
tvCallNumberLabel = findViewById(R.id.tv_call_number_label);
|
||||
tvCallNumber = findViewById(R.id.tv_call_number);
|
||||
tvPickUp = findViewById(R.id.tv_phone_pick_up);
|
||||
tvCallingTime = findViewById(R.id.tv_phone_calling_time);
|
||||
tvHangUp = findViewById(R.id.tv_phone_hang_up);
|
||||
|
||||
tvCallNumber.setText(formatPhoneNumber(phoneNumber));
|
||||
tvPickUp.setOnClickListener(this);
|
||||
tvHangUp.setOnClickListener(this);
|
||||
|
||||
// 打进的电话
|
||||
if (callType == PhoneCallService.CallType.CALL_IN) {
|
||||
tvCallNumberLabel.setText("来电号码");
|
||||
tvPickUp.setVisibility(View.VISIBLE);
|
||||
}
|
||||
// 打出的电话
|
||||
else if (callType == PhoneCallService.CallType.CALL_OUT) {
|
||||
tvCallNumberLabel.setText("呼叫号码");
|
||||
tvPickUp.setVisibility(View.GONE);
|
||||
phoneCallManager.openSpeaker();
|
||||
}
|
||||
|
||||
showOnLockScreen();
|
||||
}
|
||||
|
||||
public void showOnLockScreen() {
|
||||
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN |
|
||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
|
||||
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
|
||||
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN |
|
||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
|
||||
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.tv_phone_pick_up) {
|
||||
phoneCallManager.answer();
|
||||
tvPickUp.setVisibility(View.GONE);
|
||||
tvCallingTime.setVisibility(View.VISIBLE);
|
||||
onGoingCallTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
public void run() {
|
||||
callingTime++;
|
||||
tvCallingTime.setText("通话中:" + getCallingTime());
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 0, 1000);
|
||||
} else if (v.getId() == R.id.tv_phone_hang_up) {
|
||||
phoneCallManager.disconnect();
|
||||
stopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
private String getCallingTime() {
|
||||
int minute = callingTime / 60;
|
||||
int second = callingTime % 60;
|
||||
return (minute < 10 ? "0" + minute : minute) +
|
||||
":" +
|
||||
(second < 10 ? "0" + second : second);
|
||||
}
|
||||
|
||||
private void stopTimer() {
|
||||
if (onGoingCallTimer != null) {
|
||||
onGoingCallTimer.cancel();
|
||||
}
|
||||
|
||||
callingTime = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
phoneCallManager.destroy();
|
||||
}
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
package cc.winboll.studio.contacts.phonecallui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.telecom.Call;
|
||||
import android.telecom.VideoProfile;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import cc.winboll.studio.contacts.dun.Rules;
|
||||
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public class PhoneCallManager {
|
||||
|
||||
public static Call call;
|
||||
|
||||
private Context context;
|
||||
private AudioManager audioManager;
|
||||
|
||||
public PhoneCallManager(Context context) {
|
||||
this.context = context;
|
||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接听电话
|
||||
*/
|
||||
public void answer() {
|
||||
if (call != null) {
|
||||
call.answer(VideoProfile.STATE_AUDIO_ONLY);
|
||||
openSpeaker();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开电话,包括来电时的拒接以及接听后的挂断
|
||||
*/
|
||||
public void disconnect() {
|
||||
if (call != null) {
|
||||
call.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开免提
|
||||
*/
|
||||
public void openSpeaker() {
|
||||
if (audioManager != null) {
|
||||
audioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
audioManager.setSpeakerphoneOn(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁资源
|
||||
*/
|
||||
public void destroy() {
|
||||
call = null;
|
||||
context = null;
|
||||
audioManager = null;
|
||||
}
|
||||
}
|
@@ -1,103 +0,0 @@
|
||||
package cc.winboll.studio.contacts.phonecallui;
|
||||
|
||||
/**
|
||||
* 监听电话通信状态的服务,实现该类的同时必须提供电话管理的 UI
|
||||
*
|
||||
* @author aJIEw
|
||||
* @see PhoneCallActivity
|
||||
* @see android.telecom.InCallService
|
||||
*/
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.telecom.Call;
|
||||
import android.telecom.InCallService;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import cc.winboll.studio.contacts.ActivityStack;
|
||||
import cc.winboll.studio.contacts.beans.RingTongBean;
|
||||
import cc.winboll.studio.contacts.dun.Rules;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public class PhoneCallService extends InCallService {
|
||||
|
||||
public static final String TAG = "PhoneCallService";
|
||||
|
||||
private volatile int originalRingVolume;
|
||||
|
||||
private final Call.Callback callback = new Call.Callback() {
|
||||
@Override
|
||||
public void onStateChanged(Call call, int state) {
|
||||
super.onStateChanged(call, state);
|
||||
switch (state) {
|
||||
case Call.STATE_ACTIVE: {
|
||||
break;
|
||||
}
|
||||
|
||||
case Call.STATE_DISCONNECTED: {
|
||||
ActivityStack.getInstance().finishActivity(PhoneCallActivity.class);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCallAdded(Call call) {
|
||||
super.onCallAdded(call);
|
||||
|
||||
call.registerCallback(callback);
|
||||
PhoneCallManager.call = call;
|
||||
CallType callType = null;
|
||||
|
||||
if (call.getState() == Call.STATE_RINGING) {
|
||||
callType = CallType.CALL_IN;
|
||||
} else if (call.getState() == Call.STATE_CONNECTING) {
|
||||
callType = CallType.CALL_OUT;
|
||||
}
|
||||
|
||||
if (callType != null) {
|
||||
Call.Details details = call.getDetails();
|
||||
String phoneNumber = details.getHandle().getSchemeSpecificPart();
|
||||
|
||||
// 记录原始铃声音量
|
||||
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
|
||||
originalRingVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
|
||||
// 检查电话接收规则
|
||||
if (!Rules.getInstance(this).isAllowed(phoneNumber)) {
|
||||
// 预先静音
|
||||
audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
|
||||
// 断开电话
|
||||
call.disconnect();
|
||||
// 停顿1秒,预防第一声铃声响动
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
LogUtils.d(TAG, "");
|
||||
}
|
||||
// 恢复铃声音量
|
||||
audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0);
|
||||
// 屏蔽电话结束
|
||||
return;
|
||||
}
|
||||
// 正常接听电话
|
||||
PhoneCallActivity.actionStart(this, phoneNumber, callType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCallRemoved(Call call) {
|
||||
super.onCallRemoved(call);
|
||||
|
||||
call.unregisterCallback(callback);
|
||||
PhoneCallManager.call = null;
|
||||
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
|
||||
// 恢复铃声音量
|
||||
audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0);
|
||||
}
|
||||
|
||||
public enum CallType {
|
||||
CALL_IN,
|
||||
CALL_OUT,
|
||||
}
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
package cc.winboll.studio.contacts.receivers;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/13 06:58:04
|
||||
* @Describe 主要广播接收器
|
||||
*/
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import cc.winboll.studio.contacts.services.MainService;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import java.lang.ref.WeakReference;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
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");
|
||||
MainService.startMainService(context);
|
||||
} else {
|
||||
ToastUtils.show(szAction);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册 Receiver
|
||||
//
|
||||
public void registerAction(Context context) {
|
||||
IntentFilter filter=new IntentFilter();
|
||||
filter.addAction(ACTION_BOOT_COMPLETED);
|
||||
//filter.addAction(Intent.ACTION_BATTERY_CHANGED);
|
||||
context.registerReceiver(this, filter);
|
||||
}
|
||||
}
|
@@ -1,139 +0,0 @@
|
||||
package cc.winboll.studio.contacts.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.Binder;
|
||||
import android.os.IBinder;
|
||||
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.SOS;
|
||||
import cc.winboll.studio.libappbase.bean.APPSOSBean;
|
||||
|
||||
public class AssistantService extends Service {
|
||||
|
||||
public static final String TAG = "AssistantService";
|
||||
|
||||
MainServiceBean mMainServiceBean;
|
||||
MyServiceConnection mMyServiceConnection;
|
||||
MainService mMainService;
|
||||
boolean isBound = false;
|
||||
volatile boolean isThreadAlive = false;
|
||||
|
||||
public synchronized void setIsThreadAlive(boolean isThreadAlive) {
|
||||
LogUtils.d(TAG, "setIsThreadAlive(...)");
|
||||
LogUtils.d(TAG, String.format("isThreadAlive %s", isThreadAlive));
|
||||
this.isThreadAlive = isThreadAlive;
|
||||
}
|
||||
|
||||
public boolean isThreadAlive() {
|
||||
return isThreadAlive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new MyBinder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
LogUtils.d(TAG, "onCreate");
|
||||
super.onCreate();
|
||||
|
||||
//mMyBinder = new MyBinder();
|
||||
if (mMyServiceConnection == null) {
|
||||
mMyServiceConnection = new MyServiceConnection();
|
||||
}
|
||||
// 设置运行参数
|
||||
setIsThreadAlive(false);
|
||||
assistantService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
LogUtils.d(TAG, "call onStartCommand(...)");
|
||||
assistantService();
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
//LogUtils.d(TAG, "onDestroy");
|
||||
setIsThreadAlive(false);
|
||||
// 解除绑定
|
||||
if (isBound) {
|
||||
unbindService(mMyServiceConnection);
|
||||
isBound = false;
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
// 运行服务内容
|
||||
//
|
||||
void assistantService() {
|
||||
LogUtils.d(TAG, "assistantService()");
|
||||
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||
LogUtils.d(TAG, String.format("mMainServiceBean.isEnable() %s", mMainServiceBean.isEnable()));
|
||||
if (mMainServiceBean.isEnable()) {
|
||||
LogUtils.d(TAG, String.format("mIsThreadAlive %s", isThreadAlive()));
|
||||
if (isThreadAlive() == false) {
|
||||
// 设置运行状态
|
||||
setIsThreadAlive(true);
|
||||
// 唤醒和绑定主进程
|
||||
wakeupAndBindMain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 唤醒和绑定主进程
|
||||
//
|
||||
void wakeupAndBindMain() {
|
||||
LogUtils.d(TAG, "wakeupAndBindMain()");
|
||||
// 绑定服务的Intent
|
||||
Intent intent = new Intent(this, MainService.class);
|
||||
startService(new Intent(this, MainService.class));
|
||||
bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
|
||||
|
||||
// startService(new Intent(this, MainService.class));
|
||||
// bindService(new Intent(AssistantService.this, MainService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
|
||||
}
|
||||
|
||||
// 主进程与守护进程连接时需要用到此类
|
||||
//
|
||||
class MyServiceConnection implements ServiceConnection {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
LogUtils.d(TAG, "onServiceConnected(...)");
|
||||
MainService.MyBinder binder = (MainService.MyBinder) service;
|
||||
mMainService = binder.getService();
|
||||
isBound = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
LogUtils.d(TAG, "onServiceDisconnected(...)");
|
||||
mMainServiceBean = MainServiceBean.loadBean(AssistantService.this, MainServiceBean.class);
|
||||
if (mMainServiceBean.isEnable()) {
|
||||
wakeupAndBindMain();
|
||||
}
|
||||
isBound = false;
|
||||
mMainService = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 用于返回服务实例的Binder
|
||||
public class MyBinder extends Binder {
|
||||
AssistantService getService() {
|
||||
LogUtils.d(TAG, "AssistantService MyBinder getService()");
|
||||
return AssistantService.this;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,247 +0,0 @@
|
||||
package cc.winboll.studio.contacts.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 cc.winboll.studio.contacts.listenphonecall.CallListenerService;
|
||||
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.contacts.beans.MainServiceBean;
|
||||
import cc.winboll.studio.contacts.handlers.MainServiceHandler;
|
||||
import cc.winboll.studio.contacts.receivers.MainReceiver;
|
||||
import cc.winboll.studio.contacts.services.MainService;
|
||||
import cc.winboll.studio.contacts.threads.MainServiceThread;
|
||||
import cc.winboll.studio.contacts.widgets.APPStatusWidget;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.SOS;
|
||||
import cc.winboll.studio.libappbase.bean.APPSOSBean;
|
||||
import cc.winboll.studio.contacts.dun.Rules;
|
||||
import android.media.AudioManager;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new MyBinder();
|
||||
}
|
||||
|
||||
public MainServiceThread getRemindThread() {
|
||||
return mMainServiceThread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
LogUtils.d(TAG, "onCreate()");
|
||||
_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();
|
||||
// 召唤 WinBoll APP 绑定本服务
|
||||
SOS.bindToAPPService(this, new APPSOSBean(getPackageName(), MainService.class.getName()));
|
||||
|
||||
if (mMainReceiver == null) {
|
||||
// 注册广播接收器
|
||||
mMainReceiver = new MainReceiver(this);
|
||||
mMainReceiver.registerAction(this);
|
||||
}
|
||||
|
||||
Rules.getInstance(this);
|
||||
//Rules.getInstance(this).add("18888888888", true);
|
||||
//Rules.getInstance(this).add("16769764848", true);
|
||||
|
||||
startPhoneCallListener();
|
||||
|
||||
MainServiceThread.getInstance(this, mMainServiceHandler).start();
|
||||
|
||||
LogUtils.i(TAG, "Main Service Is Start.");
|
||||
}
|
||||
}
|
||||
|
||||
// 唤醒和绑定守护进程
|
||||
//
|
||||
void wakeupAndBindAssistant() {
|
||||
LogUtils.d(TAG, "wakeupAndBindAssistant()");
|
||||
// if (ServiceUtils.isServiceAlive(getApplicationContext(), AssistantService.class.getName()) == false) {
|
||||
// startService(new Intent(MainService.this, AssistantService.class));
|
||||
// //LogUtils.d(TAG, "call wakeupAndBindAssistant() : Binding... AssistantService");
|
||||
// bindService(new Intent(MainService.this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
|
||||
// }
|
||||
Intent intent = new Intent(this, AssistantService.class);
|
||||
startService(intent);
|
||||
// 绑定服务的Intent
|
||||
//Intent intent = new Intent(this, AssistantService.class);
|
||||
bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
|
||||
|
||||
// Intent intent = new Intent(this, AssistantService.class);
|
||||
// startService(intent);
|
||||
// LogUtils.d(TAG, "startService(intent)");
|
||||
// bindService(new Intent(this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
|
||||
}
|
||||
|
||||
void startPhoneCallListener() {
|
||||
Intent callListener = new Intent(this, CallListenerService.class);
|
||||
startService(callListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
//LogUtils.d(TAG, "onDestroy");
|
||||
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||
//LogUtils.d(TAG, "onDestroy done");
|
||||
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);
|
||||
|
||||
// 停止主要进程
|
||||
MainServiceThread.getInstance(this, mMainServiceHandler).setIsExit(true);
|
||||
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
// 主进程与守护进程连接时需要用到此类
|
||||
//
|
||||
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();
|
||||
SOS.sosWinBollService(getApplicationContext(), new APPSOSBean(getPackageName(), MainService.class.getName()));
|
||||
}
|
||||
isBound = false;
|
||||
mAssistantService = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 用于返回服务实例的Binder
|
||||
public class MyBinder extends Binder {
|
||||
MainService getService() {
|
||||
LogUtils.d(TAG, "MainService MyBinder getService()");
|
||||
return MainService.this;
|
||||
}
|
||||
}
|
||||
|
||||
// //
|
||||
// // 启动服务
|
||||
// //
|
||||
// public static void startControlCenterService(Context context) {
|
||||
// Intent intent = new Intent(context, MainService.class);
|
||||
// context.startForegroundService(intent);
|
||||
// }
|
||||
//
|
||||
// //
|
||||
// // 停止服务
|
||||
// //
|
||||
// public static void stopControlCenterService(Context context) {
|
||||
// Intent intent = new Intent(context, MainService.class);
|
||||
// context.stopService(intent);
|
||||
// }
|
||||
|
||||
public void appenMessage(String message) {
|
||||
LogUtils.d(TAG, String.format("Message : %s", message));
|
||||
}
|
||||
|
||||
public static void stopMainService(Context context) {
|
||||
LogUtils.d(TAG, "stopMainService");
|
||||
MainServiceBean bean = new MainServiceBean();
|
||||
bean.setIsEnable(false);
|
||||
MainServiceBean.saveBean(context, bean);
|
||||
context.stopService(new Intent(context, MainService.class));
|
||||
}
|
||||
|
||||
public static void startMainService(Context context) {
|
||||
LogUtils.d(TAG, "startMainService");
|
||||
MainServiceBean bean = new MainServiceBean();
|
||||
bean.setIsEnable(true);
|
||||
MainServiceBean.saveBean(context, bean);
|
||||
context.startService(new Intent(context, MainService.class));
|
||||
}
|
||||
}
|
||||
|
@@ -1,77 +0,0 @@
|
||||
package cc.winboll.studio.contacts.threads;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/14 03:46:44
|
||||
*/
|
||||
import android.content.Context;
|
||||
import cc.winboll.studio.contacts.handlers.MainServiceHandler;
|
||||
import cc.winboll.studio.contacts.services.MainService;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.SOS;
|
||||
import cc.winboll.studio.libappbase.bean.APPSOSBean;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class MainServiceThread extends Thread {
|
||||
|
||||
public static final String TAG = "MainServiceThread";
|
||||
|
||||
volatile static MainServiceThread _MainServiceThread;
|
||||
// 控制线程是否退出的标志
|
||||
volatile boolean isExit = false;
|
||||
volatile boolean isStarted = false;
|
||||
Context mContext;
|
||||
// 服务Handler, 用于线程发送消息使用
|
||||
WeakReference<MainServiceHandler> mwrMainServiceHandler;
|
||||
|
||||
MainServiceThread(Context context, MainServiceHandler handler) {
|
||||
mContext = context;
|
||||
mwrMainServiceHandler = new WeakReference<MainServiceHandler>(handler);
|
||||
}
|
||||
|
||||
public void setIsExit(boolean isExit) {
|
||||
this.isExit = isExit;
|
||||
}
|
||||
|
||||
public boolean isExit() {
|
||||
return isExit;
|
||||
}
|
||||
|
||||
public void setIsStarted(boolean isStarted) {
|
||||
this.isStarted = isStarted;
|
||||
}
|
||||
|
||||
public boolean isStarted() {
|
||||
return isStarted;
|
||||
}
|
||||
|
||||
public static MainServiceThread getInstance(Context context, MainServiceHandler handler) {
|
||||
if (_MainServiceThread != null) {
|
||||
_MainServiceThread.setIsExit(true);
|
||||
}
|
||||
_MainServiceThread = new MainServiceThread(context, handler);
|
||||
return _MainServiceThread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (isStarted == false) {
|
||||
isStarted = true;
|
||||
LogUtils.d(TAG, "run()");
|
||||
|
||||
while (!isExit()) {
|
||||
//ToastUtils.show("run");
|
||||
//LogUtils.d(TAG, "run()");
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
_MainServiceThread = null;
|
||||
LogUtils.d(TAG, "run() exit");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
package cc.winboll.studio.contacts.widgets;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/17 14:49:31
|
||||
* @Describe APPStatusWidget
|
||||
*/
|
||||
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.contacts.R;
|
||||
import cc.winboll.studio.contacts.threads.MainServiceThread;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
|
||||
public class APPStatusWidget extends AppWidgetProvider {
|
||||
|
||||
public static final String TAG = "APPSOSReportWidget";
|
||||
|
||||
public static final String ACTION_STATUS_ACTIVE = "cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_ACTIVE";
|
||||
public static final String ACTION_STATUS_NOACTIVE = "cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_NOACTIVE";
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
super.onReceive(context, intent);
|
||||
if (intent.getAction().equals(ACTION_STATUS_ACTIVE)) {
|
||||
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPStatusWidget.class));
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId);
|
||||
}
|
||||
} else if (intent.getAction().equals(ACTION_STATUS_NOACTIVE)) {
|
||||
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPStatusWidget.class));
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
|
||||
LogUtils.d(TAG, "updateAppWidget(...)");
|
||||
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
|
||||
//设置按钮点击事件
|
||||
Intent intentAppButton = new Intent(context, APPStatusWidgetClickListener.class);
|
||||
intentAppButton.setAction(APPStatusWidgetClickListener.ACTION_APPICON_CLICK);
|
||||
PendingIntent pendingIntentAppButton = PendingIntent.getBroadcast(context, 0, intentAppButton, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
views.setOnClickPendingIntent(R.id.widgetlayoutImageView1, pendingIntentAppButton);
|
||||
|
||||
// boolean isActive = !MainServiceThread.isExist();
|
||||
// if (isActive) {
|
||||
// views.setImageViewResource(R.id.widgetlayoutImageView1, R.drawable.ic_launcher);
|
||||
// } else {
|
||||
// views.setImageViewResource(R.id.widgetlayoutImageView1, R.drawable.ic_launcher_disable);
|
||||
//
|
||||
// }
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
}
|
||||
|
||||
public static void onAPPStatusWidgetClick(Context context) {
|
||||
ToastUtils.show("onAPPStatusWidgetClick");
|
||||
}
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
package cc.winboll.studio.contacts.widgets;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/17 14:59:55
|
||||
* @Describe WidgetButtonClickListener
|
||||
*/
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
public class APPStatusWidgetClickListener extends BroadcastReceiver {
|
||||
|
||||
public static final String TAG = "APPStatusWidgetClickListener";
|
||||
|
||||
public static final String ACTION_APPICON_CLICK = "cc.winboll.studio.contacts.widgets.APPStatusWidgetClickListener.ACTION_APPICON_CLICK";
|
||||
|
||||
@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_APPICON_CLICK)) {
|
||||
LogUtils.d(TAG, "ACTION_APPICON_CLICK");
|
||||
Toast.makeText(context, "ACTION_APPICON_CLICK", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:clickable="true">
|
||||
<item android:drawable="@drawable/ic_launcher_background"/>
|
||||
<item
|
||||
android:left="15dp"
|
||||
android:top="15dp"
|
||||
android:right="15dp"
|
||||
android:bottom="15dp"
|
||||
android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</layer-list>
|
@@ -1,170 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="@color/colorPrimary"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:clickable="true">
|
||||
<item android:drawable="@drawable/ic_launcher_background"/>
|
||||
<item
|
||||
android:left="15dp"
|
||||
android:top="15dp"
|
||||
android:right="15dp"
|
||||
android:bottom="15dp"
|
||||
android:drawable="@drawable/ic_launcher_foreground_disable"/>
|
||||
</layer-list>
|
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M16.61,15.15C16.15,15.15 15.77,14.78 15.77,14.32S16.15,13.5 16.61,13.5H16.61C17.07,13.5 17.45,13.86 17.45,14.32C17.45,14.78 17.07,15.15 16.61,15.15M7.41,15.15C6.95,15.15 6.57,14.78 6.57,14.32C6.57,13.86 6.95,13.5 7.41,13.5H7.41C7.87,13.5 8.24,13.86 8.24,14.32C8.24,14.78 7.87,15.15 7.41,15.15M16.91,10.14L18.58,7.26C18.67,7.09 18.61,6.88 18.45,6.79C18.28,6.69 18.07,6.75 18,6.92L16.29,9.83C14.95,9.22 13.5,8.9 12,8.91C10.47,8.91 9,9.24 7.73,9.82L6.04,6.91C5.95,6.74 5.74,6.68 5.57,6.78C5.4,6.87 5.35,7.08 5.44,7.25L7.1,10.13C4.25,11.69 2.29,14.58 2,18H22C21.72,14.59 19.77,11.7 16.91,10.14H16.91Z"/>
|
||||
</vector>
|
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#FFAAAAAA"
|
||||
android:pathData="M16.61,15.15C16.15,15.15 15.77,14.78 15.77,14.32S16.15,13.5 16.61,13.5H16.61C17.07,13.5 17.45,13.86 17.45,14.32C17.45,14.78 17.07,15.15 16.61,15.15M7.41,15.15C6.95,15.15 6.57,14.78 6.57,14.32C6.57,13.86 6.95,13.5 7.41,13.5H7.41C7.87,13.5 8.24,13.86 8.24,14.32C8.24,14.78 7.87,15.15 7.41,15.15M16.91,10.14L18.58,7.26C18.67,7.09 18.61,6.88 18.45,6.79C18.28,6.69 18.07,6.75 18,6.92L16.29,9.83C14.95,9.22 13.5,8.9 12,8.91C10.47,8.91 9,9.24 7.73,9.82L6.04,6.91C5.95,6.74 5.74,6.68 5.57,6.78C5.4,6.87 5.35,7.08 5.44,7.25L7.1,10.13C4.25,11.69 2.29,14.58 2,18H22C21.72,14.59 19.77,11.7 16.91,10.14H16.91Z"/>
|
||||
</vector>
|
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="36dp"
|
||||
android:height="36dp"
|
||||
android:viewportHeight="1024"
|
||||
android:viewportWidth="1024">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M847.53,307.03L597.5,307.03c-8.54,0.17 -13.66,-11.26 -13.66,-19.97 0.17,-8.7 5.12,-14.17 13.66,-14.17h250.02c30.21,-0.34 31.41,34.14 0,34.14zM736.6,428.71c-6.14,6.32 -16.05,6.32 -22.02,0.17l-128.34,-130.56c-5.98,-6.14 -5.8,-16.22 0.17,-22.53l122.37,-124.76c6.14,-6.31 16.05,-6.31 22.02,-0.34 5.98,6.14 5.81,16.22 -0.34,22.36L623.1,289.62l113.5,116.22a16.33,16.33 0,0 1,0 22.87zM833.02,717.99c-18.78,-12.63 -122.37,-64.85 -141.99,-77.14 -2.05,0 -7.34,0.68 -14.85,4.43 -8.02,4.1 -31.23,16.9 -63.66,34.99l-25.26,14.16 -23.9,-16.38c-17.75,-12.11 -56.32,-41.3 -115.03,-98.64 -58.88,-57.69 -88.58,-95.23 -101.04,-112.47l-16.72,-23.38L345.09,418.82c13.99,-23.9 30.72,-52.9 35.84,-62.46 4.1,-7.51 4.44,-12.63 4.44,-14.17v-0.51C374.27,324.44 303.27,219.65 290.3,201.22c-1.88,-0.34 -5.8,0 -10.75,3.42 -40.28,28.5 -82.43,68.95 -88.41,84.65C195.24,373.42 270.85,491.52 404.48,622.25c133.8,130.73 251.22,205.49 336.9,209.41 16.22,-5.63 62.81,-52.05 91.65,-91.14 2.05,-2.9 3.24,-3.58 3.24,-6.99 0,-0.51 -3.07,-15.02 -3.24,-15.53zM867.5,770.73c-0.68,1.02 -81.58,108.03 -126.12,106.67 -125.1,-3.93 -265.73,-128.86 -366.94,-227.84 -101.38,-98.99 -225.45,-238.25 -229.38,-360.96v-1.02c0,-43.01 124.76,-128 125.78,-128.69 19.62,-13.14 36.35,-13.82 52.39,10.41 7.85,10.92 87.73,123.9 96.94,138.58 3.92,6.49 11.09,25.94 11.09,34.48 0,10.92 -3.24,23.21 -9.73,34.98 -6.31,11.61 -26.62,46.59 -36.69,63.83 10.92,15.19 46.76,61.44 103.94,117.25 56.83,55.63 85.16,72.19 100.69,82.77 17.58,-9.73 53.42,-29.7 65.2,-35.67 22.18,-11.44 44.37,-12.8 59.39,-3.42 14.16,8.7 136.02,88.41 147.79,96.6 10.93,7.68 16.9,21.67 16.9,36.69 0.17,11.78 -3.58,24.41 -11.26,35.33z" />
|
||||
</vector>
|
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="36dp"
|
||||
android:height="36dp"
|
||||
android:viewportHeight="1024"
|
||||
android:viewportWidth="1024">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M861.86,307.03L614.4,307.03c-31.06,-0.17 -29.86,-34.48 0,-34.14h247.46c8.54,0.17 13.31,5.46 13.49,14.17 0,8.87 -4.95,20.14 -13.49,19.97zM872.96,298.5l-126.98,130.56c-5.98,6.14 -15.7,5.98 -21.85,-0.17 -6.14,-6.32 -6.14,-16.38 -0.17,-22.36l112.3,-116.22L730.11,173.74c-6.14,-6.31 -6.14,-16.38 -0.17,-22.35 5.97,-6.14 15.7,-5.98 21.84,0.34L872.96,276.48c5.8,5.8 5.98,15.87 0,22.02zM833.02,717.99c-18.78,-12.63 -122.37,-64.85 -141.99,-77.14 -2.05,0 -7.34,0.68 -14.85,4.43 -8.02,4.1 -31.23,16.9 -63.66,34.99l-25.26,14.16 -23.9,-16.38c-17.75,-12.11 -56.32,-41.3 -115.03,-98.64 -58.88,-57.69 -88.41,-95.23 -100.86,-112.47L330.75,443.56l14.5,-24.74c14,-23.9 30.72,-52.9 35.84,-62.46 4.1,-7.51 4.44,-12.63 4.44,-14.17v-0.51C374.44,324.44 303.45,219.65 290.3,201.22c-1.88,-0.34 -5.8,0 -10.75,3.42 -40.28,28.5 -82.43,68.95 -88.41,84.65C195.24,373.42 270.85,491.52 404.48,622.25c133.63,130.73 251.05,205.49 336.9,209.41 16.22,-5.63 62.81,-52.05 91.65,-91.14 2.05,-2.9 3.24,-3.58 3.24,-6.99 0,-0.51 -3.07,-15.02 -3.24,-15.53zM867.5,770.73c-0.68,1.02 -81.58,108.03 -126.12,106.67 -125.1,-3.93 -265.56,-128.86 -366.94,-227.84 -101.38,-98.99 -225.45,-238.25 -229.38,-360.96v-1.02c0,-43.01 124.76,-128 125.78,-128.69 19.62,-13.14 36.35,-13.82 52.39,10.41 7.85,10.92 87.73,123.9 96.94,138.58 4.1,6.49 11.09,25.94 11.09,34.48 0,10.92 -3.24,23.21 -9.73,34.98 -6.14,11.61 -26.62,46.59 -36.69,63.83 10.75,15.19 46.76,61.44 103.94,117.25 56.83,55.63 85.16,72.19 100.69,82.77 17.58,-9.73 53.42,-29.7 65.2,-35.67 22.18,-11.44 44.37,-12.8 59.39,-3.42 14.16,8.7 136.19,88.41 147.79,96.6 10.93,7.68 16.9,21.67 16.9,36.69 0.17,11.78 -3.58,24.41 -11.26,35.33z" />
|
||||
</vector>
|
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportHeight="1024"
|
||||
android:viewportWidth="1024">
|
||||
<path
|
||||
android:fillColor="#201f1f"
|
||||
android:pathData="M841.22,856.06c185.86,-185.86 185.86,-487.42 0,-673.79C655.36,-3.58 353.79,-3.58 167.42,182.27c-185.86,185.86 -185.86,487.42 0,673.79 186.37,185.86 487.94,185.86 673.79,0zM218.62,495.1c0,-47.62 111.1,-113.66 285.7,-113.66 175.1,0 285.7,66.05 285.7,113.66 0,40.96 10.75,102.4 -73.73,93.18 -84.48,-9.22 -78.85,-40.96 -78.85,-83.46 0,-29.7 -68.61,-36.35 -133.12,-36.35 -65.02,0 -133.12,6.66 -133.12,36.35 0,42.5 5.63,74.24 -78.85,83.46 -84.99,9.22 -73.73,-51.71 -73.73,-93.18z" />
|
||||
</vector>
|
@@ -1,30 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportHeight="1024"
|
||||
android:viewportWidth="1024">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M277.9,742s167.9,-294.1 465.3,-465.3C575.7,577.8 277.9,742 277.9,742z" />
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M743.1,742S575.2,447.9 277.8,276.7C445.3,577.8 743.1,742 743.1,742z" />
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M284.1,343m-49,0a49,49 0,1 0,98 0,49 49,0 1,0 -98,0Z" />
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M284.1,515.5m-49,0a49,49 0,1 0,98 0,49 49,0 1,0 -98,0Z" />
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M284.1,683.5m-49,0a49,49 0,1 0,98 0,49 49,0 1,0 -98,0Z" />
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M808.4,341.6s-208.5,-61.2 -420,0c211.5,64.8 420,0 420,0zM808.4,516.6s-208.5,-61.2 -420,0c211.5,64.8 420,0 420,0zM808.4,684.6s-208.5,-61.2 -420,0c211.5,64.8 420,0 420,0z" />
|
||||
<path
|
||||
android:fillColor="#0EC469"
|
||||
android:pathData="M512,512m-448,0a448,448 0,1 0,896 0,448 448,0 1,0 -896,0Z" />
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M701.4,786.8h-0.3c-3.2,-0.3 -6.4,-0.8 -9.5,-1.4C290,725.2 237.2,331.5 237.2,331.5h0.1c-5.9,-27.8 0.8,-56.8 22.5,-78.5 9.5,-9.5 22.5,-18.1 31.3,-19.2l111.6,111.6c-1.1,8.8 -9.6,21.7 -19.1,31.2 -8.5,8.5 -17.9,14.4 -28.4,18.6 57.7,214.5 272.1,271.5 272.1,271.5l0.3,0.2c4.2,-10.5 9.6,-19.3 18.1,-27.8 9.5,-9.5 22.6,-18.1 31.4,-19.2l111.6,111.6c-1.1,8.8 -9.6,21.7 -19.1,31.2 -18.9,19 -43.4,26.3 -68.2,24.1z" />
|
||||
</vector>
|
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:angle="180"
|
||||
android:endColor="#FFFFFFFF"
|
||||
android:startColor="#FFFFFFFF"
|
||||
android:type="linear" />
|
||||
|
||||
<corners android:radius="10dp" />
|
||||
</shape>
|
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/phone_number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="请输入电话号码"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/dial_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="拨号"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/call_status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="16dp"/>
|
||||
|
||||
</LinearLayout>
|
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/phone_number_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter phone number" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/dial_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Dial" />
|
||||
|
||||
</LinearLayout>
|
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/activitymainToolbar1"/>
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0"
|
||||
android:id="@+id/activitymainViewPager1"/>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:id="@+id/activitymainTabLayout1"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
@@ -1,95 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".phonecallui.PhoneCallActivity">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/rl_user_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:background="@color/colorPrimaryDark">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_call_number_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@+id/tv_call_number"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="center"
|
||||
android:text="来电号码"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="18sp" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_call_number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_centerInParent="true"
|
||||
tools:text="133-9527-9527" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_phone_calling_time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="通话中:01:33"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="18sp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_phone_hang_up"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="16dp"
|
||||
android:drawableTop="@drawable/ic_phone_hang_up"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:text="挂 断"
|
||||
android:textColor="@android:color/black"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_phone_pick_up"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="50dp"
|
||||
android:layout_toRightOf="@id/tv_phone_hang_up"
|
||||
android:drawablePadding="16dp"
|
||||
android:drawableTop="@drawable/ic_phone_pick_up"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:text="接 听"
|
||||
android:textColor="@android:color/black"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
@@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/activitymainToolbar1"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="应用权限设置:"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right">
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="悬浮窗设置"
|
||||
android:id="@+id/activitysettingsButton2"
|
||||
android:onClick="onCanDrawOverlays"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="默认拨号设置"
|
||||
android:id="@+id/activitysettingsButton1"
|
||||
android:onClick="onDefaultPhone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<EditText
|
||||
android:layout_width="0dp"
|
||||
android:ems="10"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Text"
|
||||
android:id="@+id/page_text"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Text"
|
||||
android:id="@+id/page_text"/>
|
||||
|
||||
</LinearLayout>
|
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<cc.winboll.studio.libappbase.LogView
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/logview"/>
|
||||
</LinearLayout>
|
@@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/rl_user_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/colorPrimary">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_call_number"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="30dp"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="18sp"
|
||||
tools:text="133-8892-9579" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_open_app"
|
||||
style="@style/Base.Widget.AppCompat.Button.Borderless.Colored"
|
||||
android:layout_width="match_parent"
|
||||
android:textColor="@android:color/white"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/tv_call_number"
|
||||
android:gravity="center"
|
||||
android:text="打开 Phone Call App"
|
||||
android:textAlignment="center"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</RelativeLayout>
|
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/shape_gradient"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_launcher"/>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:textColor="#FF000000"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:id="@+id/widgetlayoutImageView1"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/item_call"
|
||||
android:title="CallActivity"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/item_settings"
|
||||
android:title="SettingsActivity"/>
|
||||
</menu>
|
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- WinBoll 默认方案 -->
|
||||
<color name="colorPrimary">#FF196ABC</color>
|
||||
<color name="colorPrimaryDark">#FF002B57</color>
|
||||
<color name="colorAccent">#FF80BFFF</color>
|
||||
</resources>
|
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Contacts</string>
|
||||
|
||||
</resources>
|
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="MyAppTheme" parent="APPBaseTheme">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
</style>
|
||||
|
||||
<style name="GlobalCrashActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
|
||||
<item name="colorTittle">@color/colorAccent</item>
|
||||
<item name="colorTittleBackgound">@color/colorPrimary</item>
|
||||
<item name="colorText">@color/colorAccent</item>
|
||||
<item name="colorTextBackgound">@color/colorPrimaryDark</item>
|
||||
</style>
|
||||
</resources>
|
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:minWidth="40dp"
|
||||
android:minHeight="40dp"
|
||||
android:updatePeriodMillis="0"
|
||||
android:initialLayout="@layout/widget_layout"
|
||||
android:resizeMode="none">
|
||||
</appwidget-provider>
|
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<external-path
|
||||
name="external_storage_root"
|
||||
path="." />
|
||||
<files-path
|
||||
name="files_path"
|
||||
path="." />
|
||||
<cache-path
|
||||
name="cache_path"
|
||||
path="." />
|
||||
<!--/storage/emulated/0/Android/data/...-->
|
||||
<external-files-path
|
||||
name="external_file_path"
|
||||
path="." />
|
||||
<!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的目录-->
|
||||
<external-cache-path
|
||||
name="external_cache_path"
|
||||
path="." />
|
||||
<!--配置root-path。这样子可以读取到sd卡和一些应用分身的目录,否则微信分身保存的图片,就会导致 java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/999/tencent/MicroMsg/WeiXin/export1544062754693.jpg,在小米6的手机上微信分身有这个crash,华为没有
|
||||
-->
|
||||
<root-path
|
||||
name="root_path"
|
||||
path="" />
|
||||
</paths>
|
@@ -1,12 +0,0 @@
|
||||
<?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>
|
||||
|
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- Put flavor specific strings here -->
|
||||
|
||||
</resources>
|
@@ -4,7 +4,7 @@ apply from: '../.winboll/winboll_lib_build.gradle'
|
||||
apply from: '../.winboll/winboll_lint_build.gradle'
|
||||
|
||||
android {
|
||||
namespace 'cc.winboll.studio'
|
||||
namespace 'cc.winboll.studio.libappbase'
|
||||
|
||||
compileSdkVersion 32
|
||||
buildToolsVersion "33.0.3"
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Sun Jan 05 07:55:11 HKT 2025
|
||||
stageCount=17
|
||||
libraryProject=winboll-shared
|
||||
baseVersion=1.8
|
||||
publishVersion=1.8.16
|
||||
#Tue Feb 11 13:11:17 HKT 2025
|
||||
stageCount=1
|
||||
libraryProject=libappbase
|
||||
baseVersion=1.3
|
||||
publishVersion=1.3.0
|
||||
buildCount=0
|
||||
baseBetaVersion=1.8.17
|
||||
baseBetaVersion=1.3.1
|
||||
|
@@ -1,18 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="cc.winboll.studio" >
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="cc.winboll.studio.libappbase">
|
||||
|
||||
<!-- 拥有完全的网络访问权限 -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application>
|
||||
|
||||
<activity
|
||||
android:name=".CrashHandler$CrashActiviy"
|
||||
android:label="CrashActiviy"
|
||||
android:name=".CrashHandler$CrashActivity"
|
||||
android:label="CrashActivity"
|
||||
android:launchMode="standard"/>
|
||||
|
||||
|
||||
<activity
|
||||
android:name=".libappbase.LibraryActivity"
|
||||
android:label="@string/lib_name" >
|
||||
</activity>
|
||||
android:name=".GlobalCrashActivity"
|
||||
android:label="GlobalCrashActivity"
|
||||
android:launchMode="standard"/>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
|
@@ -1,215 +0,0 @@
|
||||
package cc.winboll.studio;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
* @Date 2024/08/12 13:22:12
|
||||
* @Describe 异常处理类
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
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 java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class CrashHandler {
|
||||
|
||||
public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
|
||||
|
||||
public static void init(Application app) {
|
||||
init(app, null);
|
||||
}
|
||||
|
||||
public static void init(final Application app, final String crashDir) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(){
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable throwable) {
|
||||
try {
|
||||
tryUncaughtException(thread, throwable);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null)
|
||||
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryUncaughtException(Thread thread, Throwable throwable) {
|
||||
final String time = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss", Locale.getDefault()).format(new Date());
|
||||
File crashFile = new File(TextUtils.isEmpty(crashDir) ? new File(app.getExternalFilesDir(null), "crash")
|
||||
: new File(crashDir), "crash_" + time + ".txt");
|
||||
|
||||
String versionName = "unknown";
|
||||
long versionCode = 0;
|
||||
try {
|
||||
PackageInfo packageInfo = app.getPackageManager().getPackageInfo(app.getPackageName(), 0);
|
||||
versionName = packageInfo.versionName;
|
||||
versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode()
|
||||
: packageInfo.versionCode;
|
||||
} catch (PackageManager.NameNotFoundException ignored) {}
|
||||
|
||||
String fullStackTrace; {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
throwable.printStackTrace(pw);
|
||||
fullStackTrace = sw.toString();
|
||||
pw.close();
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("************* Crash Head ****************\n");
|
||||
sb.append("Time Of Crash : ").append(time).append("\n");
|
||||
sb.append("Device Manufacturer: ").append(Build.MANUFACTURER).append("\n");
|
||||
sb.append("Device Model : ").append(Build.MODEL).append("\n");
|
||||
sb.append("Android Version : ").append(Build.VERSION.RELEASE).append("\n");
|
||||
sb.append("Android SDK : ").append(Build.VERSION.SDK_INT).append("\n");
|
||||
sb.append("App VersionName : ").append(versionName).append("\n");
|
||||
sb.append("App VersionCode : ").append(versionCode).append("\n");
|
||||
sb.append("************* Crash Head ****************\n");
|
||||
sb.append("\n").append(fullStackTrace);
|
||||
|
||||
String errorLog = sb.toString();
|
||||
|
||||
try {
|
||||
writeFile(crashFile, errorLog);
|
||||
} catch (IOException ignored) {}
|
||||
|
||||
gotoCrashActiviy: {
|
||||
Intent intent = new Intent(app, CrashActiviy.class);
|
||||
intent.addFlags(
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
);
|
||||
intent.putExtra(CrashActiviy.EXTRA_CRASH_INFO, errorLog);
|
||||
try {
|
||||
app.startActivity(intent);
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(0);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null)
|
||||
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFile(File file, String content) throws IOException {
|
||||
File parentFile = file.getParentFile();
|
||||
if (parentFile != null && !parentFile.exists()) {
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
file.createNewFile();
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
fos.write(content.getBytes());
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public static final class CrashActiviy extends Activity implements MenuItem.OnMenuItemClickListener {
|
||||
|
||||
private static final String EXTRA_CRASH_INFO = "crashInfo";
|
||||
|
||||
private static final int MENUITEM_COPY = 0;
|
||||
private static final int MENUITEM_RESTART = 1;
|
||||
|
||||
private String mLog;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mLog = getIntent().getStringExtra(EXTRA_CRASH_INFO);
|
||||
setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
|
||||
setContentView: {
|
||||
ScrollView contentView = new ScrollView(this);
|
||||
contentView.setFillViewport(true);
|
||||
HorizontalScrollView hw = new HorizontalScrollView(this);
|
||||
hw.setBackgroundColor(Color.GRAY);
|
||||
TextView message = new TextView(this); {
|
||||
int padding = dp2px(16);
|
||||
message.setPadding(padding, padding, padding, padding);
|
||||
message.setText(mLog);
|
||||
message.setTextIsSelectable(true);
|
||||
}
|
||||
hw.addView(message);
|
||||
contentView.addView(hw, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
setContentView(contentView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
restart();
|
||||
}
|
||||
|
||||
private void restart() {
|
||||
PackageManager pm = getPackageManager();
|
||||
Intent intent = pm.getLaunchIntentForPackage(getPackageName());
|
||||
if (intent != null) {
|
||||
intent.addFlags(
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
);
|
||||
startActivity(intent);
|
||||
}
|
||||
finish();
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private int dp2px(final float dpValue) {
|
||||
final float scale = Resources.getSystem().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENUITEM_COPY:
|
||||
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
|
||||
break;
|
||||
case MENUITEM_RESTART:
|
||||
restart();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
menu.add(0, MENUITEM_COPY, 0, "Copy").setOnMenuItemClickListener(this)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
menu.add(0, MENUITEM_RESTART, 0, "Restart").setOnMenuItemClickListener(this)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,95 +0,0 @@
|
||||
package cc.winboll.studio;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
* @Date 2025/01/05 10:10:23
|
||||
* @Describe 全局应用类
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
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 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 GlobalApplication extends Application {
|
||||
|
||||
private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
CrashHandler.init(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) {}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,283 @@
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
* @Date 2025/01/15 11:11:52
|
||||
* @Describe Json Bean 基础类。
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public abstract class BaseBean<T extends BaseBean> {
|
||||
|
||||
public static final String TAG = "BaseBean";
|
||||
static final String BEAN_NAME = "BeanName";
|
||||
|
||||
public BaseBean() {}
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
public String getBeanJsonFilePath(Context context) {
|
||||
|
||||
return context.getExternalFilesDir(TAG) + "/" + getName() + ".json";
|
||||
}
|
||||
|
||||
public String getBeanListJsonFilePath(Context context) {
|
||||
|
||||
return context.getExternalFilesDir(TAG) + "/" + getName() + "_List.json";
|
||||
}
|
||||
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
jsonWriter.name(BEAN_NAME).value(getName());
|
||||
}
|
||||
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract public T readBeanFromJsonReader(JsonReader jsonReader) throws IOException;
|
||||
|
||||
public static <T extends BaseBean> String checkIsTheSameBeanListAndFile(String szFilePath, Class<T> clazz) {
|
||||
StringBuilder sbResult = new StringBuilder();
|
||||
String szErrorInfo = "Check Is The Same Bean List And File Error : ";
|
||||
|
||||
try {
|
||||
int nSameCount = 0;
|
||||
int nBeanListCout = 0;
|
||||
|
||||
T beanTemp = clazz.newInstance();
|
||||
String szBeanSimpleName = beanTemp.getName();
|
||||
String szListJson = UTF8FileUtils.readStringFromFile(szFilePath);
|
||||
StringReader stringReader = new StringReader(szListJson);
|
||||
JsonReader jsonReader = new JsonReader(stringReader);
|
||||
jsonReader.beginArray();
|
||||
while (jsonReader.hasNext()) {
|
||||
nBeanListCout++;
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (name.equals(BEAN_NAME)) {
|
||||
if (szBeanSimpleName.equals(jsonReader.nextString())) {
|
||||
nSameCount++;
|
||||
}
|
||||
} else {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
jsonReader.endObject();
|
||||
}
|
||||
jsonReader.endArray();
|
||||
|
||||
// 返回检查结果
|
||||
if (nSameCount == nBeanListCout) {
|
||||
// 检查一致直接返回空串
|
||||
return "";
|
||||
} else {
|
||||
// 检查不一致返回对比信息
|
||||
sbResult.append("Total : ");
|
||||
sbResult.append(nBeanListCout);
|
||||
sbResult.append(" Diff : ");
|
||||
sbResult.append(nBeanListCout - nSameCount);
|
||||
}
|
||||
} catch (InstantiationException e) {
|
||||
sbResult.append(szErrorInfo);
|
||||
sbResult.append(e);
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
} catch (IllegalAccessException e) {
|
||||
sbResult.append(szErrorInfo);
|
||||
sbResult.append(e);
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
} catch (IOException e) {
|
||||
sbResult.append(szErrorInfo);
|
||||
sbResult.append(e);
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return sbResult.toString();
|
||||
}
|
||||
|
||||
public static <T extends BaseBean> T parseStringToBean(String szBean, Class<T> clazz) throws IOException {
|
||||
// 创建 JsonWriter 对象
|
||||
StringReader stringReader = new StringReader(szBean);
|
||||
JsonReader jsonReader = new JsonReader(stringReader);
|
||||
try {
|
||||
T beanTemp = clazz.newInstance();
|
||||
return (T)beanTemp.readBeanFromJsonReader(jsonReader);
|
||||
} catch (InstantiationException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
} catch (IllegalAccessException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T extends BaseBean> boolean parseStringToBeanList(String szBeanList, ArrayList<T> beanList, Class<T> clazz) {
|
||||
try {
|
||||
beanList.clear();
|
||||
StringReader stringReader = new StringReader(szBeanList);
|
||||
JsonReader jsonReader = new JsonReader(stringReader);
|
||||
jsonReader.beginArray();
|
||||
while (jsonReader.hasNext()) {
|
||||
T beanTemp = clazz.newInstance();
|
||||
T bean = (T)beanTemp.readBeanFromJsonReader(jsonReader);
|
||||
if (bean != null) {
|
||||
beanList.add(bean);
|
||||
//LogUtils.d(TAG, "beanList.add(bean)");
|
||||
}
|
||||
}
|
||||
jsonReader.endArray();
|
||||
return true;
|
||||
//LogUtils.d(TAG, "beanList.size() is " + Integer.toString(beanList.size()));
|
||||
} catch (InstantiationException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
} catch (IllegalAccessException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// 创建 JsonWriter 对象
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||
jsonWriter.setIndent(" ");
|
||||
try {// 开始 JSON 对象
|
||||
jsonWriter.beginObject();
|
||||
// 写入键值对
|
||||
writeThisToJsonWriter(jsonWriter);
|
||||
// 结束 JSON 对象
|
||||
jsonWriter.endObject();
|
||||
return stringWriter.toString();
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
// 获取 JSON 字符串
|
||||
return "";
|
||||
}
|
||||
|
||||
public static <T extends BaseBean> String toStringByBeanList(ArrayList<T> beanList) {
|
||||
try {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||
jsonWriter.setIndent(" ");
|
||||
jsonWriter.beginArray();
|
||||
for (int i = 0; i < beanList.size(); i++) {
|
||||
// 开始 JSON 对象
|
||||
jsonWriter.beginObject();
|
||||
// 写入键值对
|
||||
beanList.get(i).writeThisToJsonWriter(jsonWriter);
|
||||
// 结束 JSON 对象
|
||||
jsonWriter.endObject();
|
||||
}
|
||||
jsonWriter.endArray();
|
||||
jsonWriter.close();
|
||||
return stringWriter.toString();
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public static <T extends BaseBean> T loadBean(Context context, Class<T> clazz) {
|
||||
try {
|
||||
T beanTemp = clazz.newInstance();
|
||||
return loadBeanFromFile(beanTemp.getBeanJsonFilePath(context), clazz);
|
||||
} catch (InstantiationException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
} catch (IllegalAccessException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T extends BaseBean> T loadBeanFromFile(String szFilePath, Class<T> clazz) {
|
||||
try {
|
||||
try {
|
||||
File fTemp = new File(szFilePath);
|
||||
if (fTemp.exists()) {
|
||||
T beanTemp = clazz.newInstance();String szJson = UTF8FileUtils.readStringFromFile(szFilePath);
|
||||
return beanTemp.parseStringToBean(szJson, clazz);
|
||||
}
|
||||
} catch (InstantiationException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
} catch (IllegalAccessException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T extends BaseBean> boolean saveBean(Context context, T bean) {
|
||||
return saveBeanToFile(bean.getBeanJsonFilePath(context), bean);
|
||||
}
|
||||
|
||||
public static <T extends BaseBean> boolean saveBeanToFile(String szFilePath, T bean) {
|
||||
try {
|
||||
String szJson = bean.toString();
|
||||
UTF8FileUtils.writeStringToFile(szFilePath, szJson);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T extends BaseBean> boolean loadBeanList(Context context, ArrayList<T> beanListDst, Class<T> clazz) {
|
||||
try {
|
||||
T beanTemp = clazz.newInstance();
|
||||
return loadBeanListFromFile(beanTemp.getBeanListJsonFilePath(context), beanListDst, clazz);
|
||||
} catch (InstantiationException e) {} catch (IllegalAccessException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T extends BaseBean> boolean loadBeanListFromFile(String szFilePath, ArrayList<T> beanList, Class<T> clazz) {
|
||||
try {
|
||||
File fTemp = new File(szFilePath);
|
||||
if (fTemp.exists()) {
|
||||
String szListJson = UTF8FileUtils.readStringFromFile(szFilePath);
|
||||
return parseStringToBeanList(szListJson, beanList, clazz);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T extends BaseBean> boolean saveBeanList(Context context, ArrayList<T> beanList, Class<T> clazz) {
|
||||
try {
|
||||
T beanTemp = clazz.newInstance();
|
||||
return saveBeanListToFile(beanTemp.getBeanListJsonFilePath(context), beanList);
|
||||
} catch (InstantiationException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
} catch (IllegalAccessException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T extends BaseBean> boolean saveBeanListToFile(String szFilePath, ArrayList<T> beanList) {
|
||||
try {
|
||||
String szJson = toStringByBeanList(beanList);
|
||||
UTF8FileUtils.writeStringToFile(szFilePath, szJson);
|
||||
//LogUtils.d(TAG, "FileUtil.writeFile beanList.size() is " + Integer.toString(beanList.size()));
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,404 @@
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
* @Date 2024/08/12 13:22:12
|
||||
* @Describe 异常处理类
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.Toolbar;
|
||||
import cc.winboll.studio.libappbase.R;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class CrashHandler {
|
||||
|
||||
public static final String TAG = "CrashHandler";
|
||||
|
||||
public static final String TITTLE = "CrashReport";
|
||||
|
||||
public static final String EXTRA_CRASH_INFO = "crashInfo";
|
||||
|
||||
final static String PREFS = CrashHandler.class.getName() + "PREFS";
|
||||
final static String PREFS_CRASHHANDLER_ISCRASHHAPPEN = "PREFS_CRASHHANDLER_ISCRASHHAPPEN";
|
||||
|
||||
public static String _CrashCountFilePath;
|
||||
|
||||
public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
|
||||
|
||||
public static void init(Application app) {
|
||||
_CrashCountFilePath = app.getExternalFilesDir("CrashHandler") + "/IsCrashHandlerCrashHappen.dat";
|
||||
LogUtils.d(TAG, String.format("_CrashCountFilePath %s", _CrashCountFilePath));
|
||||
init(app, null);
|
||||
}
|
||||
|
||||
public static void init(final Application app, final String crashDir) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(){
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable throwable) {
|
||||
try {
|
||||
tryUncaughtException(thread, throwable);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null)
|
||||
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryUncaughtException(Thread thread, Throwable throwable) {
|
||||
// 每到这里就燃烧一次保险丝
|
||||
AppCrashSafetyWire.getInstance().burnSafetyWire();
|
||||
|
||||
final String time = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss", Locale.getDefault()).format(new Date());
|
||||
File crashFile = new File(TextUtils.isEmpty(crashDir) ? new File(app.getExternalFilesDir(null), "crash")
|
||||
: new File(crashDir), "crash_" + time + ".txt");
|
||||
|
||||
String versionName = "unknown";
|
||||
long versionCode = 0;
|
||||
try {
|
||||
PackageInfo packageInfo = app.getPackageManager().getPackageInfo(app.getPackageName(), 0);
|
||||
versionName = packageInfo.versionName;
|
||||
versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode()
|
||||
: packageInfo.versionCode;
|
||||
} catch (PackageManager.NameNotFoundException ignored) {}
|
||||
|
||||
String fullStackTrace; {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
throwable.printStackTrace(pw);
|
||||
fullStackTrace = sw.toString();
|
||||
pw.close();
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("************* Crash Head ****************\n");
|
||||
sb.append("Time Of Crash : ").append(time).append("\n");
|
||||
sb.append("Device Manufacturer : ").append(Build.MANUFACTURER).append("\n");
|
||||
sb.append("Device Model : ").append(Build.MODEL).append("\n");
|
||||
sb.append("Android Version : ").append(Build.VERSION.RELEASE).append("\n");
|
||||
sb.append("Android SDK : ").append(Build.VERSION.SDK_INT).append("\n");
|
||||
sb.append("App VersionName : ").append(versionName).append("\n");
|
||||
sb.append("App VersionCode : ").append(versionCode).append("\n");
|
||||
sb.append("************* Crash Head ****************\n");
|
||||
sb.append("\n").append(fullStackTrace);
|
||||
|
||||
String errorLog = sb.toString();
|
||||
|
||||
try {
|
||||
writeFile(crashFile, errorLog);
|
||||
} catch (IOException ignored) {}
|
||||
|
||||
gotoCrashActiviy: {
|
||||
Intent intent = new Intent();
|
||||
LogUtils.d(TAG, "gotoCrashActiviy: ");
|
||||
if (AppCrashSafetyWire.getInstance().isAppCrashSafetyWireOK()) {
|
||||
LogUtils.d(TAG, "gotoCrashActiviy: isAppCrashSafetyWireOK");
|
||||
intent.setClass(app, GlobalCrashActivity.class);
|
||||
intent.putExtra(EXTRA_CRASH_INFO, errorLog);
|
||||
// 如果发生了 CrashHandler 内部崩溃, 就调用基础的应用崩溃显示类
|
||||
// intent.setClass(app, GlobalCrashActiviy.class);
|
||||
// intent.putExtra(GlobalCrashActiviy.EXTRA_CRASH_INFO, errorLog);
|
||||
} else {
|
||||
LogUtils.d(TAG, "gotoCrashActiviy: else");
|
||||
// 正常状态调用进阶的应用崩溃显示页
|
||||
intent.setClass(app, CrashActivity.class);
|
||||
intent.putExtra(EXTRA_CRASH_INFO, errorLog);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// intent.setClass(app, CrashActiviy.class);
|
||||
// intent.putExtra(CrashActiviy.EXTRA_CRASH_INFO, errorLog);
|
||||
|
||||
|
||||
intent.addFlags(
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
);
|
||||
|
||||
try {
|
||||
app.startActivity(intent);
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(0);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null)
|
||||
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null)
|
||||
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void writeFile(File file, String content) throws IOException {
|
||||
File parentFile = file.getParentFile();
|
||||
if (parentFile != null && !parentFile.exists()) {
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
file.createNewFile();
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
fos.write(content.getBytes());
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// 应用崩溃保险丝
|
||||
//
|
||||
public static final class AppCrashSafetyWire {
|
||||
|
||||
volatile static AppCrashSafetyWire _AppCrashSafetyWire;
|
||||
|
||||
volatile Integer currentSafetyLevel; // 熔断值,为 0 表示熔断了。
|
||||
private static final int _MINI = 1;
|
||||
private static final int _MAX = 2;
|
||||
|
||||
AppCrashSafetyWire() {
|
||||
LogUtils.d(TAG, "AppCrashSafetyWire()");
|
||||
currentSafetyLevel = loadCurrentSafetyLevel();
|
||||
}
|
||||
|
||||
public static synchronized AppCrashSafetyWire getInstance() {
|
||||
if (_AppCrashSafetyWire == null) {
|
||||
_AppCrashSafetyWire = new AppCrashSafetyWire();
|
||||
}
|
||||
return _AppCrashSafetyWire;
|
||||
}
|
||||
|
||||
public void setCurrentSafetyLevel(int currentSafetyLevel) {
|
||||
this.currentSafetyLevel = currentSafetyLevel;
|
||||
}
|
||||
|
||||
public int getCurrentSafetyLevel() {
|
||||
return currentSafetyLevel;
|
||||
}
|
||||
|
||||
public void saveCurrentSafetyLevel(int currentSafetyLevel) {
|
||||
LogUtils.d(TAG, "saveCurrentSafetyLevel()");
|
||||
this.currentSafetyLevel = currentSafetyLevel;
|
||||
try {
|
||||
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(_CrashCountFilePath));
|
||||
oos.writeInt(currentSafetyLevel);
|
||||
oos.flush();
|
||||
oos.close();
|
||||
LogUtils.d(TAG, String.format("saveCurrentSafetyLevel writeInt currentSafetyLevel %d", currentSafetyLevel));
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
public int loadCurrentSafetyLevel() {
|
||||
LogUtils.d(TAG, "loadCurrentSafetyLevel()");
|
||||
try {
|
||||
File f = new File(_CrashCountFilePath);
|
||||
if (f.exists()) {
|
||||
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(_CrashCountFilePath));
|
||||
currentSafetyLevel = ois.readInt();
|
||||
LogUtils.d(TAG, String.format("loadCurrentSafetyLevel() readInt currentSafetyLevel %d", currentSafetyLevel));
|
||||
} else {
|
||||
currentSafetyLevel = _MAX;
|
||||
LogUtils.d(TAG, String.format("loadCurrentSafetyLevel() currentSafetyLevel init to _MAX->%d", _MAX));
|
||||
saveCurrentSafetyLevel(currentSafetyLevel);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return currentSafetyLevel;
|
||||
}
|
||||
|
||||
boolean burnSafetyWire() {
|
||||
LogUtils.d(TAG, "burnSafetyWire()");
|
||||
// 崩溃计数进入崩溃保险值
|
||||
int safeLevel = loadCurrentSafetyLevel();
|
||||
if (isSafetyWireWorking(safeLevel)) {
|
||||
// 如果保险丝未熔断, 就增加一次熔断值
|
||||
LogUtils.d(TAG, "burnSafetyWire() use");
|
||||
saveCurrentSafetyLevel(safeLevel - 1);
|
||||
return isSafetyWireWorking(safeLevel - 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isSafetyWireWorking(int safetyLevel) {
|
||||
LogUtils.d(TAG, "isSafetyWireOK()");
|
||||
//safetyLevel = _MINI;
|
||||
//safetyLevel = _MINI - 1;
|
||||
//safetyLevel = _MINI + 1;
|
||||
//safetyLevel = _MAX;
|
||||
//safetyLevel = _MAX + 1;
|
||||
LogUtils.d(TAG, String.format("SafetyLevel %d", safetyLevel));
|
||||
|
||||
if (safetyLevel >= _MINI && safetyLevel <= _MAX) {
|
||||
// 如果在保险值之内
|
||||
LogUtils.d(TAG, String.format("In Safety Level"));
|
||||
return true;
|
||||
}
|
||||
LogUtils.d(TAG, String.format("Out of Safety Level"));
|
||||
return false;
|
||||
}
|
||||
|
||||
void resumeToMaximumImmediately() {
|
||||
LogUtils.d(TAG, "resumeToMaximumImmediately() call saveCurrentSafetyLevel(_MAX)");
|
||||
AppCrashSafetyWire.getInstance().saveCurrentSafetyLevel(_MAX);
|
||||
}
|
||||
|
||||
void off() {
|
||||
LogUtils.d(TAG, "off()");
|
||||
saveCurrentSafetyLevel(_MINI);
|
||||
}
|
||||
|
||||
boolean isAppCrashSafetyWireOK() {
|
||||
LogUtils.d(TAG, "isAppCrashSafetyWireOK()");
|
||||
currentSafetyLevel = loadCurrentSafetyLevel();
|
||||
return isSafetyWireWorking(currentSafetyLevel);
|
||||
}
|
||||
|
||||
// 调用函数以启用持续崩溃保险,从而调用 CrashHandler 内部崩溃处理窗口
|
||||
void postResumeCrashSafetyWireHandler(final Context context) {
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
LogUtils.d(TAG, "Handler run()");
|
||||
if (!AppCrashSafetyWire.getInstance().isSafetyWireWorking(currentSafetyLevel - 1)) {
|
||||
// 如果下一次应用崩溃时,保险丝熔断,则先恢复保险丝满能状态
|
||||
// 进程持续运行时,恢复保险丝熔断值
|
||||
//Resume to maximum
|
||||
AppCrashSafetyWire.getInstance().resumeToMaximumImmediately();
|
||||
LogUtils.d(TAG, "postResumeCrashSafetyWireHandler");
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class CrashActivity extends Activity implements MenuItem.OnMenuItemClickListener {
|
||||
private static final int MENUITEM_COPY = 0;
|
||||
private static final int MENUITEM_RESTART = 1;
|
||||
|
||||
private String mLog;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
AppCrashSafetyWire.getInstance().postResumeCrashSafetyWireHandler(getApplicationContext());
|
||||
|
||||
mLog = getIntent().getStringExtra(EXTRA_CRASH_INFO);
|
||||
setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
|
||||
setContentView: {
|
||||
ScrollView contentView = new ScrollView(this);
|
||||
contentView.setFillViewport(true);
|
||||
|
||||
HorizontalScrollView hw = new HorizontalScrollView(this);
|
||||
hw.setBackgroundColor(Color.GRAY);
|
||||
TextView message = new TextView(this); {
|
||||
int padding = dp2px(16);
|
||||
message.setPadding(padding, padding, padding, padding);
|
||||
message.setText(mLog);
|
||||
message.setTextIsSelectable(true);
|
||||
}
|
||||
hw.addView(message);
|
||||
|
||||
contentView.addView(hw, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
setContentView(contentView);
|
||||
getActionBar().setTitle(TITTLE);
|
||||
getActionBar().setSubtitle(GlobalApplication.class.getSimpleName() + " Error");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
restart();
|
||||
}
|
||||
|
||||
private void restart() {
|
||||
PackageManager pm = getPackageManager();
|
||||
Intent intent = pm.getLaunchIntentForPackage(getPackageName());
|
||||
if (intent != null) {
|
||||
intent.addFlags(
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
);
|
||||
startActivity(intent);
|
||||
}
|
||||
finish();
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private int dp2px(final float dpValue) {
|
||||
final float scale = Resources.getSystem().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENUITEM_COPY:
|
||||
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
|
||||
Toast.makeText(getApplication(), "The text is copied.", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case MENUITEM_RESTART:
|
||||
AppCrashSafetyWire.getInstance().resumeToMaximumImmediately();
|
||||
restart();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
menu.add(0, MENUITEM_COPY, 0, "Copy").setOnMenuItemClickListener(this)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
menu.add(0, MENUITEM_RESTART, 0, "Restart").setOnMenuItemClickListener(this)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,85 @@
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
* @Date 2025/01/05 10:10:23
|
||||
* @Describe 全局应用类
|
||||
*/
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
public class GlobalApplication extends Application {
|
||||
|
||||
public static final String TAG = "GlobalApplication";
|
||||
|
||||
final static String PREFS = GlobalApplication.class.getName() + "PREFS";
|
||||
final static String PREFS_ISDEBUGING = "PREFS_ISDEBUGING";
|
||||
|
||||
|
||||
private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
|
||||
|
||||
// 是否处于调试状态
|
||||
volatile static boolean isDebuging = false;
|
||||
|
||||
public static void setIsDebuging(Context context, boolean isDebuging) {
|
||||
GlobalApplication.isDebuging = isDebuging;
|
||||
// 获取SharedPreferences实例
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
||||
// 获取编辑器
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
// 保存数据
|
||||
editor.putBoolean(PREFS_ISDEBUGING, GlobalApplication.isDebuging);
|
||||
// 提交更改
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static boolean isDebuging() {
|
||||
return isDebuging;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getApplicationContext() {
|
||||
return super.getApplicationContext();
|
||||
}
|
||||
|
||||
public Application getApplication() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
//GlobalApplication.isDebuging = true;
|
||||
//GlobalApplication.setIsDebuging(this, true);
|
||||
LogUtils.init(this);
|
||||
//LogUtils.setLogLevel(LogUtils.LOG_LEVEL.Debug);
|
||||
//LogUtils.setTAGListEnable(GlobalApplication.TAG, true);
|
||||
//LogUtils.setALlTAGListEnable(true);
|
||||
//LogUtils.d(TAG, "LogUtils init");
|
||||
|
||||
// 设置应用异常处理窗口
|
||||
CrashHandler.init(this);
|
||||
|
||||
// 设置应用调试状态
|
||||
//SharedPreferences sharedPreferences = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
||||
//GlobalApplication.isDebuging = sharedPreferences.getBoolean(PREFS_ISDEBUGING, GlobalApplication.isDebuging);
|
||||
|
||||
}
|
||||
|
||||
public static String getAppName(Context context) {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
try {
|
||||
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(
|
||||
context.getPackageName(), 0);
|
||||
return (String) packageManager.getApplicationLabel(applicationInfo);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,143 @@
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/11 00:14:05
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.Toolbar;
|
||||
import cc.winboll.studio.libappbase.R;
|
||||
|
||||
public final class GlobalCrashActivity extends Activity implements MenuItem.OnMenuItemClickListener {
|
||||
|
||||
private static final int MENUITEM_COPY = 0;
|
||||
private static final int MENUITEM_RESTART = 1;
|
||||
|
||||
String mLog;
|
||||
int colorTittle;
|
||||
int colorTittleBackground;
|
||||
int colorText;
|
||||
int colorTextBackground;
|
||||
|
||||
public static final String TAG = "GlobalCrashActivity";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
CrashHandler.AppCrashSafetyWire.getInstance().postResumeCrashSafetyWireHandler(getApplicationContext());
|
||||
|
||||
mLog = getIntent().getStringExtra(CrashHandler.EXTRA_CRASH_INFO);
|
||||
//setTheme(android.R.style.Theme_Holo_Light_NoActionBar);
|
||||
setTheme(R.style.APPBaseTheme);
|
||||
setContentView(R.layout.activity_globalcrash);
|
||||
|
||||
TypedArray a = obtainStyledAttributes(R.styleable.GlobalCrashActivity);
|
||||
colorTittle = a.getColor(R.styleable.GlobalCrashActivity_colorTittle, Color.WHITE);
|
||||
colorTittleBackground = a.getColor(R.styleable.GlobalCrashActivity_colorTittleBackgound, Color.BLACK);
|
||||
colorText = a.getColor(R.styleable.GlobalCrashActivity_colorText, Color.RED);
|
||||
colorTextBackground = a.getColor(R.styleable.GlobalCrashActivity_colorTextBackgound, Color.RED);
|
||||
// 返回一个绑定资源结束的信号给资源
|
||||
a.recycle();
|
||||
|
||||
LinearLayout llMain = findViewById(R.id.activityglobalcrashLinearLayout1);
|
||||
llMain.setBackgroundColor(colorTextBackground);
|
||||
Toolbar toolbar = findViewById(R.id.activityglobalcrashToolbar1);
|
||||
toolbar.setBackgroundColor(colorTittleBackground);
|
||||
toolbar.setTitleTextColor(colorTittle);
|
||||
toolbar.setSubtitleTextColor(colorTittle);
|
||||
setActionBar(toolbar);
|
||||
TextView tvLog = findViewById(R.id.activityglobalcrashTextView1);
|
||||
tvLog.setText(mLog);
|
||||
tvLog.setTextColor(colorText);
|
||||
tvLog.setBackgroundColor(colorTextBackground);
|
||||
|
||||
getActionBar().setTitle(CrashHandler.TITTLE);
|
||||
getActionBar().setSubtitle(GlobalApplication.getAppName(getApplicationContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
restart();
|
||||
}
|
||||
|
||||
private void restart() {
|
||||
PackageManager pm = getPackageManager();
|
||||
Intent intent = pm.getLaunchIntentForPackage(getPackageName());
|
||||
if (intent != null) {
|
||||
intent.addFlags(
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
);
|
||||
startActivity(intent);
|
||||
}
|
||||
finish();
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENUITEM_COPY:
|
||||
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
|
||||
Toast.makeText(getApplication(), "The text is copied.", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case MENUITEM_RESTART:
|
||||
CrashHandler.AppCrashSafetyWire.getInstance().resumeToMaximumImmediately();
|
||||
restart();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
menu.add(0, MENUITEM_COPY, 0, "Copy").setOnMenuItemClickListener(this)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
menu.add(0, MENUITEM_RESTART, 0, "Restart").setOnMenuItemClickListener(this)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
|
||||
|
||||
// 设置菜单文本颜色
|
||||
for (int i = 0; i < menu.size(); i++) {
|
||||
MenuItem item = menu.getItem(i);
|
||||
SpannableString spanString = new SpannableString(item.getTitle().toString());
|
||||
spanString.setSpan(new ForegroundColorSpan(colorTittle), 0, spanString.length(), 0);
|
||||
item.setTitle(spanString);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void joinQQGroup(String key) {
|
||||
// 创建Intent
|
||||
Intent intent = new Intent();
|
||||
// 设置动作
|
||||
intent.setAction("android.intent.action.VIEW");
|
||||
// 设置数据为网址的URI
|
||||
Uri content_url = Uri.parse("https://www.winboll.cc");
|
||||
intent.setData(content_url);
|
||||
// 添加标志
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
// 设置类名和活动名
|
||||
intent.setClassName("com.android.browser", "com.android.browser.BrowserActivity");
|
||||
// 启动Activity
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
import android.app.*;
|
||||
import android.os.*;
|
||||
import cc.winboll.studio.R;
|
||||
|
||||
public class LibraryActivity extends Activity
|
||||
{
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.library);
|
||||
}
|
||||
}
|
@@ -0,0 +1,370 @@
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
* @Date 2024/08/12 13:44:06
|
||||
* @Describe LogUtils
|
||||
* @Describe 应用日志类
|
||||
*/
|
||||
import android.content.Context;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import dalvik.system.DexFile;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class LogUtils {
|
||||
|
||||
public static final String TAG = "LogUtils";
|
||||
|
||||
public static enum LOG_LEVEL { Off, Error, Warn, Info, Debug, Verbose }
|
||||
|
||||
static volatile boolean _IsInited = false;
|
||||
static Context _mContext;
|
||||
// 日志显示时间格式
|
||||
static SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("[yyyyMMdd_HHmmSS]", Locale.getDefault());
|
||||
// 应用日志文件夹
|
||||
static File _mfLogCacheDir;
|
||||
static File _mfLogDataDir;
|
||||
// 应用日志文件
|
||||
static File _mfLogCatchFile;
|
||||
static File _mfLogUtilsBeanFile;
|
||||
static LogUtilsBean _mLogUtilsBean;
|
||||
public static Map<String, Boolean> mapTAGList = new HashMap<String, Boolean>();
|
||||
|
||||
//
|
||||
// 初始化函数
|
||||
//
|
||||
public static void init(Context context) {
|
||||
_mContext = context;
|
||||
init(context, LOG_LEVEL.Off);
|
||||
}
|
||||
|
||||
//
|
||||
// 初始化函数
|
||||
//
|
||||
public static void init(Context context, LOG_LEVEL logLevel) {
|
||||
if (GlobalApplication.isDebuging()) {
|
||||
// 初始化日志缓存文件路径
|
||||
_mfLogCacheDir = new File(context.getApplicationContext().getExternalCacheDir(), TAG);
|
||||
if (!_mfLogCacheDir.exists()) {
|
||||
_mfLogCacheDir.mkdirs();
|
||||
}
|
||||
_mfLogCatchFile = new File(_mfLogCacheDir, "log.txt");
|
||||
|
||||
// 初始化日志配置文件路径
|
||||
_mfLogDataDir = context.getApplicationContext().getExternalFilesDir(TAG);
|
||||
if (!_mfLogDataDir.exists()) {
|
||||
_mfLogDataDir.mkdirs();
|
||||
}
|
||||
_mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json");
|
||||
} else {
|
||||
// 初始化日志缓存文件路径
|
||||
_mfLogCacheDir = new File(context.getApplicationContext().getCacheDir(), TAG);
|
||||
if (!_mfLogCacheDir.exists()) {
|
||||
_mfLogCacheDir.mkdirs();
|
||||
}
|
||||
_mfLogCatchFile = new File(_mfLogCacheDir, "log.txt");
|
||||
|
||||
// 初始化日志配置文件路径
|
||||
_mfLogDataDir = new File(context.getApplicationContext().getFilesDir(), TAG);
|
||||
if (!_mfLogDataDir.exists()) {
|
||||
_mfLogDataDir.mkdirs();
|
||||
}
|
||||
_mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json");
|
||||
}
|
||||
|
||||
// Toast.makeText(context,
|
||||
// "_mfLogUtilsBeanFile : " + _mfLogUtilsBeanFile
|
||||
// + "\n_mfLogCatchFile : " + _mfLogCatchFile,
|
||||
// Toast.LENGTH_SHORT).show();
|
||||
//
|
||||
_mLogUtilsBean = LogUtilsBean.loadBeanFromFile(_mfLogUtilsBeanFile.getPath(), LogUtilsBean.class);
|
||||
if (_mLogUtilsBean == null) {
|
||||
_mLogUtilsBean = new LogUtilsBean();
|
||||
_mLogUtilsBean.saveBeanToFile(_mfLogUtilsBeanFile.getPath(), _mLogUtilsBean);
|
||||
}
|
||||
|
||||
// 加载当前应用下的所有类的 TAG
|
||||
addClassTAGList();
|
||||
loadTAGBeanSettings();
|
||||
_IsInited = true;
|
||||
LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString()));
|
||||
}
|
||||
|
||||
public static Map<String, Boolean> getMapTAGList() {
|
||||
return mapTAGList;
|
||||
}
|
||||
|
||||
static void loadTAGBeanSettings() {
|
||||
ArrayList<LogUtilsClassTAGBean> list = new ArrayList<LogUtilsClassTAGBean>();
|
||||
LogUtilsClassTAGBean.loadBeanList(_mContext, list, LogUtilsClassTAGBean.class);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
LogUtilsClassTAGBean beanSetting = list.get(i);
|
||||
for (Map.Entry<String, Boolean> entry : mapTAGList.entrySet()) {
|
||||
if (entry.getKey().equals(beanSetting.getTag())) {
|
||||
entry.setValue(beanSetting.getEnable());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void saveTAGBeanSettings() {
|
||||
ArrayList<LogUtilsClassTAGBean> list = new ArrayList<LogUtilsClassTAGBean>();
|
||||
for (Map.Entry<String, Boolean> entry : mapTAGList.entrySet()) {
|
||||
list.add(new LogUtilsClassTAGBean(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
LogUtilsClassTAGBean.saveBeanList(_mContext, list, LogUtilsClassTAGBean.class);
|
||||
}
|
||||
|
||||
static void addClassTAGList() {
|
||||
//ClassLoader classLoader = getClass().getClassLoader();
|
||||
try {
|
||||
//String packageName = context.getPackageName();
|
||||
String packageNamePrefix = "cc.winboll.studio";
|
||||
List<String> classNames = new ArrayList<>();
|
||||
String apkPath = _mContext.getPackageCodePath();
|
||||
//Log.d("APK_PATH", "The APK path is: " + apkPath);
|
||||
LogUtils.d(TAG, String.format("apkPath : %s", apkPath));
|
||||
//String apkPath = "/data/app/" + packageName + "-";
|
||||
|
||||
//DexFile dexfile = new DexFile(apkPath + "1/base.apk");
|
||||
DexFile dexfile = new DexFile(apkPath);
|
||||
|
||||
int countTemp = 0;
|
||||
Enumeration<String> entries = dexfile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
countTemp++;
|
||||
String className = entries.nextElement();
|
||||
if (className.startsWith(packageNamePrefix)) {
|
||||
classNames.add(className);
|
||||
}
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, String.format("countTemp : %d\nClassNames size : %d", countTemp, classNames.size()));
|
||||
|
||||
for (String className : classNames) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers()) && field.getType() == String.class && "TAG".equals(field.getName())) {
|
||||
String tagValue = (String) field.get(null);
|
||||
//Log.d("TAG_INFO", "Class: " + className + ", TAG value: " + tagValue);
|
||||
//LogUtils.d(TAG, String.format("Tag Value : %s", tagValue));
|
||||
//mapTAGList.put(tagValue, true);
|
||||
mapTAGList.put(tagValue, false);
|
||||
}
|
||||
}
|
||||
} catch (NoClassDefFoundError | ClassNotFoundException | IllegalAccessException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
//LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
//Toast.makeText(context, TAG + " : " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
//Toast.makeText(context, TAG + " : " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTAGListEnable(String tag, boolean isEnable) {
|
||||
Iterator<Map.Entry<String, Boolean>> iterator = mapTAGList.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, Boolean> entry = iterator.next();
|
||||
if (tag.equals(entry.getKey())) {
|
||||
entry.setValue(isEnable);
|
||||
//System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
saveTAGBeanSettings();
|
||||
LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString()));
|
||||
}
|
||||
|
||||
public static void setALlTAGListEnable(boolean isEnable) {
|
||||
Iterator<Map.Entry<String, Boolean>> iterator = mapTAGList.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, Boolean> entry = iterator.next();
|
||||
entry.setValue(isEnable);
|
||||
//System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
|
||||
}
|
||||
saveTAGBeanSettings();
|
||||
LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString()));
|
||||
}
|
||||
|
||||
public static void setLogLevel(LOG_LEVEL logLevel) {
|
||||
LogUtils._mLogUtilsBean.setLogLevel(logLevel);
|
||||
_mLogUtilsBean.saveBeanToFile(_mfLogUtilsBeanFile.getPath(), _mLogUtilsBean);
|
||||
}
|
||||
|
||||
public static LOG_LEVEL getLogLevel() {
|
||||
return LogUtils._mLogUtilsBean.getLogLevel();
|
||||
}
|
||||
|
||||
static boolean isLoggable(String tag, LOG_LEVEL logLevel) {
|
||||
return _IsInited && mapTAGList.get(tag) && isInTheLevel(logLevel);
|
||||
}
|
||||
|
||||
static boolean isInTheLevel(LOG_LEVEL logLevel) {
|
||||
return (LogUtils._mLogUtilsBean.getLogLevel().ordinal() == logLevel.ordinal()
|
||||
|| LogUtils._mLogUtilsBean.getLogLevel().ordinal() > logLevel.ordinal());
|
||||
}
|
||||
|
||||
//
|
||||
// 获取应用日志文件夹
|
||||
//
|
||||
public static File getLogCacheDir() {
|
||||
return _mfLogCacheDir;
|
||||
}
|
||||
|
||||
//
|
||||
// 调试日志写入函数
|
||||
//
|
||||
public static void e(String szTAG, String szMessage) {
|
||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Error)) {
|
||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Error, szMessage);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 调试日志写入函数
|
||||
//
|
||||
public static void w(String szTAG, String szMessage) {
|
||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Warn)) {
|
||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Warn, szMessage);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 调试日志写入函数
|
||||
//
|
||||
public static void i(String szTAG, String szMessage) {
|
||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Info)) {
|
||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Info, szMessage);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 调试日志写入函数
|
||||
//
|
||||
public static void d(String szTAG, String szMessage) {
|
||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, szMessage);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 调试日志写入函数
|
||||
// 包含线程调试堆栈信息
|
||||
//
|
||||
public static void d(String szTAG, String szMessage, StackTraceElement[] listStackTrace) {
|
||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
||||
StringBuilder sbMessage = new StringBuilder(szMessage);
|
||||
sbMessage.append(" \nAt ");
|
||||
sbMessage.append(listStackTrace[2].getMethodName());
|
||||
sbMessage.append(" (");
|
||||
sbMessage.append(listStackTrace[2].getFileName());
|
||||
sbMessage.append(":");
|
||||
sbMessage.append(listStackTrace[2].getLineNumber());
|
||||
sbMessage.append(")");
|
||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sbMessage.toString());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 调试日志写入函数
|
||||
// 包含异常信息和线程调试堆栈信息
|
||||
//
|
||||
public static void d(String szTAG, Exception e, StackTraceElement[] listStackTrace) {
|
||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
|
||||
StringBuilder sbMessage = new StringBuilder(e.getClass().toGenericString());
|
||||
sbMessage.append(" : ");
|
||||
sbMessage.append(e.getMessage());
|
||||
sbMessage.append(" \nAt ");
|
||||
sbMessage.append(listStackTrace[2].getMethodName());
|
||||
sbMessage.append(" (");
|
||||
sbMessage.append(listStackTrace[2].getFileName());
|
||||
sbMessage.append(":");
|
||||
sbMessage.append(listStackTrace[2].getLineNumber());
|
||||
sbMessage.append(")");
|
||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sbMessage.toString());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 调试日志写入函数
|
||||
//
|
||||
public static void v(String szTAG, String szMessage) {
|
||||
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Verbose)) {
|
||||
saveLog(szTAG, LogUtils.LOG_LEVEL.Verbose, szMessage);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 日志文件保存函数
|
||||
//
|
||||
static void saveLog(String szTAG, LogUtils.LOG_LEVEL logLevel, String szMessage) {
|
||||
try {
|
||||
BufferedWriter out = null;
|
||||
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(_mfLogCatchFile, true), "UTF-8"));
|
||||
out.write("[" + logLevel + "] " + mSimpleDateFormat.format(System.currentTimeMillis()) + " [" + szTAG + "]\n" + szMessage + "\n");
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, "IOException : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 历史日志加载函数
|
||||
//
|
||||
public static String loadLog() {
|
||||
if (_mfLogCatchFile.exists()) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
try {
|
||||
BufferedReader in = null;
|
||||
in = new BufferedReader(new InputStreamReader(new FileInputStream(_mfLogCatchFile), "UTF-8"));
|
||||
String line = "";
|
||||
while ((line = in.readLine()) != null) {
|
||||
sb.append(line);
|
||||
sb.append("\n");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, "IOException : " + e.getMessage());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//
|
||||
// 清理日志函数
|
||||
//
|
||||
public static void cleanLog() {
|
||||
if (_mfLogCatchFile.exists()) {
|
||||
try {
|
||||
UTF8FileUtils.writeStringToFile(_mfLogCatchFile.getPath(), "");
|
||||
//LogUtils.d(TAG, "cleanLog");
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,50 +1,52 @@
|
||||
package cc.winboll.studio.contacts.beans;
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/13 07:06:13
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
* @Date 2024/08/23 15:39:07
|
||||
* @Describe LogUtils 数据配置类。
|
||||
*/
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
public class LogUtilsBean extends BaseBean {
|
||||
|
||||
public class MainServiceBean extends BaseBean {
|
||||
public static final String TAG = "LogUtilsBean";
|
||||
|
||||
public static final String TAG = "MainServiceBean";
|
||||
LogUtils.LOG_LEVEL logLevel;
|
||||
|
||||
boolean isEnable;
|
||||
|
||||
public MainServiceBean() {
|
||||
this.isEnable = false;
|
||||
public LogUtilsBean() {
|
||||
this.logLevel = LogUtils.LOG_LEVEL.Off;
|
||||
}
|
||||
|
||||
public void setIsEnable(boolean isEnable) {
|
||||
this.isEnable = isEnable;
|
||||
public LogUtilsBean(LogUtils.LOG_LEVEL logLevel) {
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
public boolean isEnable() {
|
||||
return isEnable;
|
||||
public void setLogLevel(LogUtils.LOG_LEVEL logLevel) {
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
public LogUtils.LOG_LEVEL getLogLevel() {
|
||||
return logLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return MainServiceBean.class.getName();
|
||||
return LogUtilsBean.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
MainServiceBean bean = this;
|
||||
jsonWriter.name("isEnable").value(bean.isEnable());
|
||||
|
||||
LogUtilsBean bean = this;
|
||||
jsonWriter.name("logLevel").value(bean.getLogLevel().ordinal());
|
||||
}
|
||||
|
||||
@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());
|
||||
if (name.equals("logLevel")) {
|
||||
setLogLevel(LogUtils.LOG_LEVEL.values()[jsonReader.nextInt()]);
|
||||
} else {
|
||||
return false;
|
||||
}
|
@@ -0,0 +1,87 @@
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
* @Date 2025/01/04 14:17:02
|
||||
* @Describe 日志类class TAG 标签数据类
|
||||
*/
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LogUtilsClassTAGBean extends BaseBean {
|
||||
|
||||
public static final String TAG = "LogUtilsClassTAGBean";
|
||||
|
||||
// 标签名
|
||||
String tag;
|
||||
// 是否启用
|
||||
Boolean enable;
|
||||
|
||||
public LogUtilsClassTAGBean() {
|
||||
this.tag = TAG;
|
||||
this.enable = true;
|
||||
}
|
||||
|
||||
public LogUtilsClassTAGBean(String tag, Boolean enable) {
|
||||
this.tag = tag;
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
public void setTag(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void setEnable(Boolean enable) {
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
public Boolean getEnable() {
|
||||
return enable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return LogUtilsClassTAGBean.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
LogUtilsClassTAGBean bean = this;
|
||||
jsonWriter.name("tag").value(bean.getTag());
|
||||
jsonWriter.name("enable").value(bean.getEnable());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||
if (name.equals("tag")) {
|
||||
setTag(jsonReader.nextString());
|
||||
} else if (name.equals("enable")) {
|
||||
setEnable(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,49 @@
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
* @Date 2024/07/19 14:30:57
|
||||
* @Describe UTF-8编码文件工具类
|
||||
*/
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class UTF8FileUtils {
|
||||
|
||||
public static final String TAG = "FileUtils";
|
||||
|
||||
//
|
||||
// 把字符串写入文件,指定 UTF-8 编码
|
||||
//
|
||||
public static void writeStringToFile(String szFilePath, String szContent) throws IOException {
|
||||
File file = new File(szFilePath);
|
||||
if (!file.getParentFile().exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
FileOutputStream outputStream = new FileOutputStream(file);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
|
||||
writer.write(szContent);
|
||||
writer.close();
|
||||
}
|
||||
|
||||
//
|
||||
// 读取文件到字符串,指定 UTF-8 编码
|
||||
//
|
||||
public static String readStringFromFile(String szFilePath) throws IOException {
|
||||
File file = new File(szFilePath);
|
||||
FileInputStream inputStream = new FileInputStream(file);
|
||||
InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||
StringBuilder content = new StringBuilder();
|
||||
int character;
|
||||
while ((character = reader.read()) != -1) {
|
||||
content.append((char) character);
|
||||
}
|
||||
reader.close();
|
||||
return content.toString();
|
||||
}
|
||||
}
|
36
libappbase/src/main/res/layout/activity_globalcrash.xml
Normal file
36
libappbase/src/main/res/layout/activity_globalcrash.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/activityglobalcrashLinearLayout1"
|
||||
style="@style/APPBaseTheme">
|
||||
|
||||
<android.widget.Toolbar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/activityglobalcrashToolbar1"/>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/activityglobalcrashTextView1"
|
||||
android:background="#FFFFFFFF"/>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
@@ -1,11 +0,0 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:text="@string/hello_world"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="AppTheme" parent="@android:style/Theme.Material.Light">
|
||||
</style>
|
||||
</resources>
|
12
libappbase/src/main/res/values/attrs.xml
Normal file
12
libappbase/src/main/res/values/attrs.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<attr name="globalCrashActivity" format="reference"/>
|
||||
|
||||
<declare-styleable name="GlobalCrashActivity">
|
||||
<attr name="colorTittle" format="color" />
|
||||
<attr name="colorTittleBackgound" format="color" />
|
||||
<attr name="colorText" format="color" />
|
||||
<attr name="colorTextBackgound" format="color" />
|
||||
</declare-styleable>
|
||||
</resources>
|
7
libappbase/src/main/res/values/colors.xml
Normal file
7
libappbase/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#FFFFE200</color>
|
||||
<color name="colorPrimaryDark">#FFFFE200</color>
|
||||
<color name="colorAccent">#FFFFE200</color>
|
||||
<color name="colorText">#FFFFE200</color>
|
||||
</resources>
|
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="AppTheme" parent="@android:style/Theme.Holo.Light">
|
||||
<style name="APPBaseTheme" parent="@android:style/Theme.Holo.Light.NoActionBar">
|
||||
<item name="colorTittle">#FFFFF600</item>
|
||||
<item name="colorTittleBackgound">#FF00B322</item>
|
||||
<item name="colorText">#FF00B322</item>
|
||||
<item name="colorTextBackgound">#FF000000</item>
|
||||
</style>
|
||||
</resources>
|
||||
</resources>
|
||||
|
@@ -1,10 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- WinBoll 默认方案 -->
|
||||
<color name="colorPrimary">#FF196ABC</color>
|
||||
<color name="colorPrimaryDark">#FF002B57</color>
|
||||
<color name="colorAccent">#FF80BFFF</color>
|
||||
<color name="colorToastFrame">#FFA9A9A9</color>
|
||||
<color name="colorToastShadow">#FF000000</color>
|
||||
<color name="colorToastBackgroung">#FFFFFFFF</color>
|
||||
</resources>
|
||||
|
@@ -37,7 +37,3 @@
|
||||
//include ':aes'
|
||||
//include ':libaes'
|
||||
//rootProject.name = "aes"
|
||||
|
||||
// Contacts 项目编译设置
|
||||
//include ':contacts'
|
||||
//rootProject.name = "contacts"
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Tue Feb 25 00:17:29 HKT 2025
|
||||
stageCount=2
|
||||
#Fri Nov 29 11:55:20 GMT 2024
|
||||
stageCount=0
|
||||
libraryProject=winboll-shared
|
||||
baseVersion=1.0
|
||||
publishVersion=1.0.1
|
||||
baseVersion=1.8
|
||||
publishVersion=1.8.16
|
||||
buildCount=0
|
||||
baseBetaVersion=1.0.2
|
||||
baseBetaVersion=1.8.17
|
||||
|
Reference in New Issue
Block a user