mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2025-09-04 02:05:16 +08:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
06324d104a
|
|||
0ed2cf4903
|
|||
d278faf7c9
|
|||
823c5c690f
|
|||
a4e1c82ad0
|
|||
07e13ccf04
|
|||
e19f8ea4af
|
|||
45c337d475
|
|||
8690e8afdb
|
|||
ec7dcabdb8
|
|||
0db82b5630
|
|||
1207dbb561
|
|||
785208a5bb
|
|||
3dbd80e06e
|
|||
f452bb2b32
|
|||
39b779eab9
|
|||
948095c016
|
|||
1a7775d5cc
|
|||
00cfca12b0
|
|||
556951c27c
|
|||
b478654008
|
|||
b47543e71a
|
|||
6ffe4bc15d
|
|||
bb94689baa
|
|||
6803b60dca
|
100
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
100
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
name: Issues and Bugs Report / 问题与 BUG 反馈
|
||||
description: 问题反馈必须使用此模板进行提交 / Issues and bugs report must be submitted using this template
|
||||
labels: [ bug ]
|
||||
title: "[Issues and Bugs Report] (Briefly describe the cause of the problem)"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Please fill in the specific reason and steps to reproduce the problem below.
|
||||
|
||||
In the event of an exception, crash functional problem, you must submit a problem log, if not, your issues will be closed directly.
|
||||
|
||||
### 请在下方填写问题发生的具体原因和复现步骤。
|
||||
|
||||
发生异常、崩溃、闪退或功能性问题,必须提交问题 Log (日志),没有 Log 的 issues 将直接被关闭。
|
||||
- type: input
|
||||
attributes:
|
||||
label: Module App version / 模块版本
|
||||
description: |
|
||||
Please fill in the complete version of the Module App currently in use, for example: **1.0**
|
||||
请填写当前使用的模块完整版本号,例如:**1.0**
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Device model and system in used / 正在使用的设备型号以及使用的系统
|
||||
description: |
|
||||
Fill in the current device model and system here, the system such as (MIUI, ColorOS, OxygenOS, PE/Native)
|
||||
这里填写当前使用的设备型号以及使用的系统,系统例如 (MIUI、ColorOS、OxygenOS、PE/原生)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Android version / Android 版本
|
||||
options:
|
||||
- 13
|
||||
- 12L/12.1
|
||||
- 12
|
||||
- 11
|
||||
- 10
|
||||
- 9
|
||||
- 8.1
|
||||
- 8.0.0
|
||||
- 7.1.2
|
||||
- 7.1.1
|
||||
- 7.0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Xposed Framework name and version / Xposed 框架名称与版本号
|
||||
description: |
|
||||
Please fill in the currently used Xposed Framework, for example: **LSPosed 1.8.4 (version code)**
|
||||
请填写当前使用的 Xposed 框架,例如:**LSPosed 1.8.4 (次版本号)**
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Xposed Modules with the same scope / 与当前同作用域的 Xposed 模块
|
||||
description: |
|
||||
The scope of this module is the System Framework (Android System).
|
||||
To ensure that the problem is not caused by conflicts with other modules, please be sure to fill in the relevant modules that you are currently activating at the same time.
|
||||
If not, please fill in "none" directly below.
|
||||
此模块的作用域为系统框架 (Android 系统),为确保非其它模块冲突造成的问题,请一定要填写当前你同时激活的相关模块。
|
||||
若没有,请直接在下方填写“无”。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe in detail why the problem occurred / 详细描述问题发生的具体原因
|
||||
description: 请在下方详细描述问题发生的具体场景、复现步骤和经过,以便我们能够按照你所描述的步骤复现这个问题。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Provide module problem logs or necessary logs / 提供模块问题 Log 或必要 Log
|
||||
description: |
|
||||
If you are using LSPosed, you can view and filter the logs containing `AppErrorsTracking` in the log management.
|
||||
LSPosed 可在日志管理中查看并筛选包含 `AppErrorsTracking` 的日志。
|
||||
value: |
|
||||
<details><summary>Click to expand / 展开查看</summary><pre><code>
|
||||
|
||||
(Paste problem log here / 此处粘贴问题 Log)
|
||||
|
||||
</code></pre></details>
|
||||
<!-- When submitting, please delete the parentheses including the parentheses, paste the logs you copied, and do not break the code format -->
|
||||
<!-- 提交时请将括号内容包括括号全部删除,粘贴你复制的日志,不要破坏代码格式 -->
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Confirm the contents you submitted / 确认一下你提交的信息
|
||||
description: |
|
||||
In order to ensure the quality of issues and avoid wasting unnecessary time, issues that do not check the options below will be closed directly.
|
||||
Please make sure you have **checked the option below** before submitting.
|
||||
为了确保 issues 的质量和避免浪费不必要的时间,未勾选下方选项的 issues 将直接被关闭。
|
||||
请一定确保你已经**勾选下方的选项**后再提交。
|
||||
options:
|
||||
- label: I certify that the above contents is correct / 我确保上述信息准确无误
|
||||
required: false
|
26
.github/workflows/commit_ci.yml
vendored
26
.github/workflows/commit_ci.yml
vendored
@@ -47,17 +47,31 @@ jobs:
|
||||
gradle-builds
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
./gradlew :app:assembleDebug
|
||||
./gradlew :app:assembleRelease
|
||||
./gradlew :demo-app:assembleDebug
|
||||
./gradlew :demo-app:assembleRelease
|
||||
echo "APK_FILE=$(find app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_APK_FILE=$(find demo-app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
- name: Upload Artifacts(module)
|
||||
echo "MODULE_DEBUG_APK_FILE=$(find app/build/outputs/apk/debug -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_DEBUG_APK_FILE=$(find demo-app/build/outputs/apk/debug -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "MODULE_RELEASE_APK_FILE=$(find app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_RELEASE_APK_FILE=$(find demo-app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
- name: Upload Artifacts(module-debug)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ env.APK_FILE }}
|
||||
path: ${{ env.MODULE_DEBUG_APK_FILE }}
|
||||
name: module-debug
|
||||
- name: Upload Artifacts(demo-debug)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ env.DEMO_DEBUG_APK_FILE }}
|
||||
name: demo-debug
|
||||
- name: Upload Artifacts(module-release)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ env.MODULE_RELEASE_APK_FILE }}
|
||||
name: module-release
|
||||
- name: Upload Artifacts(demo-app)
|
||||
- name: Upload Artifacts(demo-release)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ env.DEMO_APK_FILE }}
|
||||
path: ${{ env.DEMO_RELEASE_APK_FILE }}
|
||||
name: demo-release
|
||||
|
26
.github/workflows/pull_request_ci.yml
vendored
26
.github/workflows/pull_request_ci.yml
vendored
@@ -46,17 +46,31 @@ jobs:
|
||||
gradle-builds
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
./gradlew :app:assembleDebug
|
||||
./gradlew :app:assembleRelease
|
||||
./gradlew :demo-app:assembleDebug
|
||||
./gradlew :demo-app:assembleRelease
|
||||
echo "APK_FILE=$(find app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_APK_FILE=$(find demo-app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
- name: Upload Artifacts(module)
|
||||
echo "MODULE_DEBUG_APK_FILE=$(find app/build/outputs/apk/debug -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_DEBUG_APK_FILE=$(find demo-app/build/outputs/apk/debug -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "MODULE_RELEASE_APK_FILE=$(find app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_RELEASE_APK_FILE=$(find demo-app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
- name: Upload Artifacts(module-debug)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ env.APK_FILE }}
|
||||
path: ${{ env.MODULE_DEBUG_APK_FILE }}
|
||||
name: module-debug
|
||||
- name: Upload Artifacts(demo-debug)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ env.DEMO_DEBUG_APK_FILE }}
|
||||
name: demo-debug
|
||||
- name: Upload Artifacts(module-release)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ env.MODULE_RELEASE_APK_FILE }}
|
||||
name: module-release
|
||||
- name: Upload Artifacts(demo-app)
|
||||
- name: Upload Artifacts(demo-release)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ env.DEMO_APK_FILE }}
|
||||
path: ${{ env.DEMO_RELEASE_APK_FILE }}
|
||||
name: demo-release
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
# Project exclude paths
|
||||
*.iml
|
||||
.gradle
|
||||
.secret
|
||||
.secret/APP_CENTER_SECRET
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
|
6
.secret/key_store_secret.json
Normal file
6
.secret/key_store_secret.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"keyAlias": "public",
|
||||
"keyPassword": "123456",
|
||||
"storeFileName": "universal.p12",
|
||||
"storePassword": "123456"
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/blob/master/LICENSE)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
|
||||
<br/><br/>
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
- [Automatic Build on Commit](https://github.com/KitsunePie/AppErrorsTracking/actions/workflows/commit_ci.yml)
|
||||
|
||||
上述更新为代码 `commit` 后自动触发,具体更新内容可点击上方的文字前往 **Github Actions** 进行查看,本更新由开源的流程自动编译发布,**不保证其稳定性**,所发布的版本**仅供测试**,且不会特殊说明甚至可能会变更版本号或保持与当前稳定版相同的版本号。
|
||||
上述更新为代码 `commit` 后自动触发,具体更新内容可点击上方的文字前往 **GitHub Actions** 进行查看,本更新由开源的流程自动编译发布,**不保证其稳定性**,所发布的版本**仅供测试**,且不会特殊说明甚至可能会变更版本号或保持与当前稳定版相同的版本号。
|
||||
|
||||
- [Release](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
- [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
|
||||
@@ -80,6 +80,10 @@
|
||||
|
||||
上述状态为当前发行的稳定版可能存在严重问题但并未及时进行修复且并未发布稳定版。
|
||||
|
||||
## Star History
|
||||
|
||||

|
||||
|
||||
## 许可证
|
||||
|
||||
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/blob/master/LICENSE)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
|
||||
<br/><br/>
|
||||
@@ -68,7 +68,7 @@ Contributions to this project are welcome to translate it into your country's la
|
||||
|
||||
The above update is automatically triggered after the code `commit`.
|
||||
|
||||
The specific update content can be viewed by clicking the text above and going to **Github Actions**.
|
||||
The specific update content can be viewed by clicking the text above and going to **GitHub Actions**.
|
||||
|
||||
This update is automatically compiled and released by the open source process, **no guarantee of its stability**, so the released version is
|
||||
**for testing only**, and there is no special explanation or even the version may change or remain the same as the current stable version.
|
||||
@@ -99,6 +99,10 @@ in a pre-release state.
|
||||
The above status is that the currently released stable version may have serious problems but have not been fixed in time and the stable version
|
||||
has not been released.
|
||||
|
||||
## Star History
|
||||
|
||||

|
||||
|
||||
## License
|
||||
|
||||
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
16
app/.gitignore
vendored
16
app/.gitignore
vendored
@@ -1,15 +1,3 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
/src/main/assets/xposed_init
|
||||
/src/main/resources/META-INF/yukihookapi_init
|
@@ -1,43 +1,54 @@
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'com.google.devtools.ksp' version '1.7.22-1.0.8'
|
||||
id 'com.google.devtools.ksp'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.fankes.apperrorstracking'
|
||||
compileSdk 33
|
||||
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('../keystore/public')
|
||||
storePassword '123456'
|
||||
keyAlias 'public'
|
||||
keyPassword '123456'
|
||||
universal {
|
||||
def dirPath = rootProject.ext.app.signingConfigs.secretConfigsDirPath
|
||||
def fileName = rootProject.ext.app.signingConfigs.secretConfigsFileName
|
||||
def configs = new JsonSlurper().parse(file("${dirPath}/${fileName}"))
|
||||
keyAlias configs.keyAlias
|
||||
keyPassword configs.keyPassword
|
||||
storeFile file("${dirPath}/${configs.storeFileName}")
|
||||
storePassword configs.storePassword
|
||||
v1SigningEnabled true
|
||||
v2SigningEnabled true
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.fankes.apperrorstracking"
|
||||
minSdk 24
|
||||
targetSdk 33
|
||||
versionCode rootProject.ext.appVersionCode
|
||||
versionName rootProject.ext.appVersionName
|
||||
namespace 'com.fankes.apperrorstracking'
|
||||
compileSdk rootProject.ext.android.compileSdk
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
defaultConfig {
|
||||
applicationId 'com.fankes.apperrorstracking'
|
||||
|
||||
minSdk rootProject.ext.android.minSdk
|
||||
targetSdk rootProject.ext.android.targetSdk
|
||||
|
||||
versionCode rootProject.ext.app.versionCode
|
||||
versionName rootProject.ext.app.versionName
|
||||
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
/** 添加 App Center Secret 到 BuildConfig */
|
||||
buildConfigField("String", "APP_CENTER_SECRET", "\"${getAppCenterSecret()}\"")
|
||||
buildConfigField('String', 'APP_CENTER_SECRET', "\"${getAppCenterSecret()}\"")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled false
|
||||
signingConfig signingConfigs.universal
|
||||
}
|
||||
release {
|
||||
minifyEnabled rootProject.ext.enableR8
|
||||
shrinkResources rootProject.ext.enableR8
|
||||
zipAlignEnabled rootProject.ext.enableR8
|
||||
signingConfig signingConfigs.debug
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
zipAlignEnabled true
|
||||
signingConfig signingConfigs.universal
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
@@ -74,17 +85,17 @@ String getAppCenterSecret() {
|
||||
|
||||
dependencies {
|
||||
compileOnly 'de.robv.android.xposed:api:82'
|
||||
implementation 'com.highcapable.yukihookapi:api:1.1.6'
|
||||
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.1.6'
|
||||
implementation 'com.microsoft.appcenter:appcenter-analytics:4.4.5'
|
||||
implementation 'com.microsoft.appcenter:appcenter-crashes:4.4.5'
|
||||
implementation 'com.highcapable.yukihookapi:api:1.1.9'
|
||||
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.1.9'
|
||||
implementation 'com.microsoft.appcenter:appcenter-analytics:5.0.0'
|
||||
implementation 'com.microsoft.appcenter:appcenter-crashes:5.0.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.7'
|
||||
implementation 'com.google.code.gson:gson:2.9.0'
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
implementation 'com.github.duanhong169:drawabletoolbox:1.0.7'
|
||||
implementation 'com.github.topjohnwu.libsu:core:3.1.2'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.0'
|
||||
implementation 'com.google.android.material:material:1.7.0'
|
||||
implementation 'com.github.topjohnwu.libsu:core:5.0.4'
|
||||
implementation 'androidx.core:core-ktx:1.10.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.8.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
|
@@ -1 +0,0 @@
|
||||
com.fankes.apperrorstracking.hook.AppErrorsTracking
|
@@ -1 +0,0 @@
|
||||
com.fankes.apperrorstracking.hook.HookEntry
|
@@ -100,7 +100,7 @@ data class AppErrorsInfoBean(
|
||||
userId = userId,
|
||||
cpuAbi = packageName?.let { context.appCpuAbiOf(it) } ?: "",
|
||||
packageName = packageName ?: "unknown",
|
||||
versionName = packageName?.let { context.appVersionNameOf(it) } ?: "",
|
||||
versionName = packageName?.let { context.appVersionNameOf(it).ifBlank { "unknown" } } ?: "",
|
||||
versionCode = packageName?.let { context.appVersionCodeOf(it) } ?: -1L,
|
||||
isNativeCrash = isNativeCrash,
|
||||
exceptionClassName = crashInfo?.exceptionClassName ?: "unknown",
|
||||
|
@@ -87,7 +87,7 @@ object AppErrorsRecordData {
|
||||
?.toEntityOrNull<CopyOnWriteArrayList<AppErrorsInfoBean>>()
|
||||
?.onEach { e ->
|
||||
e.cpuAbi = it.appCpuAbiOf(e.packageName)
|
||||
e.versionName = it.appVersionNameOf(e.packageName)
|
||||
e.versionName = it.appVersionNameOf(e.packageName).ifBlank { "unknown" }
|
||||
e.versionCode = it.appVersionCodeOf(e.packageName)
|
||||
e.toJsonOrNull()?.also { json -> File(errorsInfoDataFolder.absolutePath, e.jsonFileName).writeText(json) }
|
||||
}.let { result ->
|
||||
|
@@ -25,8 +25,7 @@ package com.fankes.apperrorstracking.data
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.widget.CompoundButton
|
||||
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
||||
import com.highcapable.yukihookapi.hook.factory.prefs
|
||||
import com.highcapable.yukihookapi.hook.log.loggerW
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||
@@ -79,7 +78,7 @@ object ConfigData {
|
||||
* @return [Set]<[String]>
|
||||
*/
|
||||
internal fun getStringSet(key: String) = when (instance) {
|
||||
is Context -> (instance as Context).modulePrefs.getStringSet(key, setOf())
|
||||
is Context -> (instance as Context).prefs().getStringSet(key, setOf())
|
||||
is PackageParam -> (instance as PackageParam).prefs.getStringSet(key, setOf())
|
||||
else -> error("Unknown type for get prefs data")
|
||||
}
|
||||
@@ -91,7 +90,7 @@ object ConfigData {
|
||||
*/
|
||||
internal fun putStringSet(key: String, value: Set<String>) {
|
||||
when (instance) {
|
||||
is Context -> (instance as Context).modulePrefs.putStringSet(key, value)
|
||||
is Context -> (instance as Context).prefs().edit { putStringSet(key, value) }
|
||||
is PackageParam -> loggerW(msg = "Not support for this method")
|
||||
else -> error("Unknown type for put prefs data")
|
||||
}
|
||||
@@ -103,7 +102,7 @@ object ConfigData {
|
||||
* @return [Int]
|
||||
*/
|
||||
internal fun getInt(data: PrefsData<Int>) = when (instance) {
|
||||
is Context -> (instance as Context).modulePrefs.get(data)
|
||||
is Context -> (instance as Context).prefs().get(data)
|
||||
is PackageParam -> (instance as PackageParam).prefs.get(data)
|
||||
else -> error("Unknown type for get prefs data")
|
||||
}
|
||||
@@ -115,7 +114,7 @@ object ConfigData {
|
||||
*/
|
||||
internal fun putInt(data: PrefsData<Int>, value: Int) {
|
||||
when (instance) {
|
||||
is Context -> (instance as Context).modulePrefs.put(data, value)
|
||||
is Context -> (instance as Context).prefs().edit { put(data, value) }
|
||||
is PackageParam -> loggerW(msg = "Not support for this method")
|
||||
else -> error("Unknown type for put prefs data")
|
||||
}
|
||||
@@ -126,8 +125,8 @@ object ConfigData {
|
||||
* @param data 键值数据模板
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private fun getBoolean(data: PrefsData<Boolean>) = when (instance) {
|
||||
is Context -> (instance as Context).modulePrefs.get(data)
|
||||
internal fun getBoolean(data: PrefsData<Boolean>) = when (instance) {
|
||||
is Context -> (instance as Context).prefs().get(data)
|
||||
is PackageParam -> (instance as PackageParam).prefs.get(data)
|
||||
else -> error("Unknown type for get prefs data")
|
||||
}
|
||||
@@ -137,29 +136,14 @@ object ConfigData {
|
||||
* @param data 键值数据模板
|
||||
* @param value 键值内容
|
||||
*/
|
||||
private fun putBoolean(data: PrefsData<Boolean>, value: Boolean) {
|
||||
internal fun putBoolean(data: PrefsData<Boolean>, value: Boolean) {
|
||||
when (instance) {
|
||||
is Context -> (instance as Context).modulePrefs.put(data, value)
|
||||
is Context -> (instance as Context).prefs().edit { put(data, value) }
|
||||
is PackageParam -> loggerW(msg = "Not support for this method")
|
||||
else -> error("Unknown type for put prefs data")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定到 [CompoundButton] 自动设置选中状态
|
||||
* @param data 键值数据模板
|
||||
* @param onChange 当改变时回调
|
||||
*/
|
||||
fun CompoundButton.bind(data: PrefsData<Boolean>, onChange: (Boolean) -> Unit = {}) {
|
||||
isChecked = getBoolean(data).also(onChange)
|
||||
setOnCheckedChangeListener { button, isChecked ->
|
||||
if (button.isPressed) {
|
||||
putBoolean(data, isChecked)
|
||||
onChange(isChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否显示开发者提示
|
||||
* @return [Boolean]
|
||||
|
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2023/2/3.
|
||||
*/
|
||||
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.apperrorstracking.data.factory
|
||||
|
||||
import android.widget.CompoundButton
|
||||
import com.fankes.apperrorstracking.data.ConfigData
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||
|
||||
/**
|
||||
* 绑定到 [CompoundButton] 自动设置选中状态
|
||||
* @param data 键值数据模板
|
||||
* @param initiate 方法体
|
||||
*/
|
||||
fun CompoundButton.bind(data: PrefsData<Boolean>, initiate: CompoundButtonDataBinder.(CompoundButton) -> Unit = {}) {
|
||||
val binder = CompoundButtonDataBinder(button = this).also { initiate(it, this) }
|
||||
isChecked = ConfigData.getBoolean(data).also { binder.initializeCallback?.invoke(it) }
|
||||
binder.applyChangesCallback = { ConfigData.putBoolean(data, it) }
|
||||
setOnCheckedChangeListener { button, isChecked ->
|
||||
if (button.isPressed) {
|
||||
if (binder.isAutoApplyChanges) binder.applyChangesCallback?.invoke(isChecked)
|
||||
binder.changedCallback?.invoke(isChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [CompoundButton] 数据绑定管理器实例
|
||||
* @param button 当前实例
|
||||
*/
|
||||
class CompoundButtonDataBinder(private val button: CompoundButton) {
|
||||
|
||||
/** 状态初始化回调事件 */
|
||||
internal var initializeCallback: ((Boolean) -> Unit)? = null
|
||||
|
||||
/** 状态改变回调事件 */
|
||||
internal var changedCallback: ((Boolean) -> Unit)? = null
|
||||
|
||||
/** 应用更改回调事件 */
|
||||
internal var applyChangesCallback: ((Boolean) -> Unit)? = null
|
||||
|
||||
/** 是否启用自动应用更改 */
|
||||
var isAutoApplyChanges = true
|
||||
|
||||
/**
|
||||
* 监听状态初始化
|
||||
* @param result 回调结果
|
||||
*/
|
||||
fun onInitialize(result: (Boolean) -> Unit) {
|
||||
initializeCallback = result
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听状态改变
|
||||
* @param result 回调结果
|
||||
*/
|
||||
fun onChanged(result: (Boolean) -> Unit) {
|
||||
changedCallback = result
|
||||
}
|
||||
|
||||
/** 重新初始化 */
|
||||
fun reinitialize() {
|
||||
initializeCallback?.invoke(button.isChecked)
|
||||
}
|
||||
|
||||
/** 应用更改并重新初始化 */
|
||||
fun applyChangesAndReinitialize() {
|
||||
applyChanges()
|
||||
reinitialize()
|
||||
}
|
||||
|
||||
/** 应用更改 */
|
||||
fun applyChanges() {
|
||||
applyChangesCallback?.invoke(button.isChecked)
|
||||
}
|
||||
|
||||
/** 取消更改 */
|
||||
fun cancelChanges() {
|
||||
button.isChecked = button.isChecked.not()
|
||||
}
|
||||
}
|
@@ -38,7 +38,7 @@ object HookEntry : IYukiHookXposedInit {
|
||||
isRecord = true
|
||||
}
|
||||
isDebug = false
|
||||
isEnableModulePrefsCache = false
|
||||
isEnablePrefsBridgeCache = false
|
||||
}
|
||||
|
||||
override fun onHook() = encase {
|
||||
|
@@ -274,7 +274,7 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
*/
|
||||
private fun AppErrorsProcessData.handleShowAppErrorUi(context: Context) {
|
||||
/** 当前 APP 名称 */
|
||||
val appName = appInfo?.let { context.appNameOf(it.packageName) } ?: packageName
|
||||
val appName = appInfo?.let { context.appNameOf(it.packageName).ifBlank { it.packageName } } ?: packageName
|
||||
|
||||
/** 当前 APP 名称 (包含用户 ID) */
|
||||
val appNameWithUserId = if (userId != 0) "$appName (${LocaleString.userId(userId)})" else appName
|
||||
|
@@ -556,4 +556,10 @@ object LocaleString {
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun updateNow(vararg objArrs: Any) = R.string.update_now.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val recordCount get() = recordCount()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun recordCount(vararg objArrs: Any) = R.string.record_count.bind(*objArrs)
|
||||
}
|
@@ -32,7 +32,7 @@ import androidx.core.view.isVisible
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
|
||||
import com.fankes.apperrorstracking.data.ConfigData
|
||||
import com.fankes.apperrorstracking.data.ConfigData.bind
|
||||
import com.fankes.apperrorstracking.data.factory.bind
|
||||
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDetailBinding
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
||||
@@ -106,7 +106,7 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
||||
}, LocaleString.shareErrorStack))
|
||||
}
|
||||
binding.appIcon.setImageDrawable(appIconOf(appErrorsInfo.packageName))
|
||||
binding.appNameText.text = appNameOf(appErrorsInfo.packageName)
|
||||
binding.appNameText.text = appNameOf(appErrorsInfo.packageName).ifBlank { appErrorsInfo.packageName }
|
||||
binding.appVersionText.text = appErrorsInfo.versionBrand
|
||||
binding.appUserIdText.isVisible = appErrorsInfo.userId > 0
|
||||
binding.appUserIdText.text = LocaleString.userId(appErrorsInfo.userId)
|
||||
@@ -123,12 +123,19 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
||||
binding.errorStackTraceMovableText.text = appErrorsInfo.stackTrace
|
||||
binding.errorStackTraceFixedText.text = appErrorsInfo.stackTrace
|
||||
binding.disableAutoWrapErrorStackTraceSwitch.bind(ConfigData.DISABLE_AUTO_WRAP_ERROR_STACK_TRACE) {
|
||||
binding.errorStackTraceScrollView.isVisible = it
|
||||
binding.errorStackTraceFixedText.isGone = it
|
||||
resetScrollView()
|
||||
onInitialize {
|
||||
binding.errorStackTraceScrollView.isVisible = it
|
||||
binding.errorStackTraceFixedText.isGone = it
|
||||
}
|
||||
onChanged {
|
||||
reinitialize()
|
||||
resetScrollView()
|
||||
}
|
||||
}
|
||||
binding.appPanelScrollView.setOnScrollChangeListener { _, _, y, _, _ ->
|
||||
binding.detailTitleText.text = if (y >= 30.dp(context = this)) appNameOf(appErrorsInfo.packageName) else LocaleString.appName
|
||||
binding.detailTitleText.text = if (y >= 30.dp(context = this))
|
||||
appNameOf(appErrorsInfo.packageName).ifBlank { appErrorsInfo.packageName }
|
||||
else LocaleString.appName
|
||||
}
|
||||
binding.detailTitleText.setOnClickListener { binding.appPanelScrollView.smoothScrollTo(0, 0) }
|
||||
resetScrollView()
|
||||
|
@@ -59,7 +59,7 @@ class AppErrorsMutedActivity : BaseActivity<ActivityAppErrorsMutedBinding>() {
|
||||
onBindViews<AdapterAppErrorsMutedBinding> { binding, position ->
|
||||
listData[position].also { bean ->
|
||||
binding.appIcon.setImageDrawable(appIconOf(bean.packageName))
|
||||
binding.appNameText.text = appNameOf(bean.packageName)
|
||||
binding.appNameText.text = appNameOf(bean.packageName).ifBlank { bean.packageName }
|
||||
binding.muteTypeText.text = when (bean.type) {
|
||||
MutedErrorsAppBean.MuteType.UNTIL_UNLOCKS -> LocaleString.muteIfUnlock
|
||||
MutedErrorsAppBean.MuteType.UNTIL_REBOOTS -> LocaleString.muteIfRestart
|
||||
|
@@ -96,7 +96,7 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
||||
binding.totalErrorsUnitText.text = LocaleString.totalErrorsUnit(listData.size)
|
||||
binding.totalAppsUnitText.text = LocaleString.totalAppsUnit(it.size)
|
||||
binding.mostErrorsAppIcon.setImageDrawable(appIconOf(mostAppPackageName))
|
||||
binding.mostErrorsAppText.text = appNameOf(mostAppPackageName)
|
||||
binding.mostErrorsAppText.text = appNameOf(mostAppPackageName).ifBlank { mostAppPackageName }
|
||||
binding.mostErrorsTypeText.text = mostErrorsType
|
||||
binding.totalPptOfErrorsText.text = "$pptCount%"
|
||||
confirmButton(LocaleString.gotIt)
|
||||
@@ -134,7 +134,7 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
||||
onBindViews<AdapterAppErrorsRecordBinding> { binding, position ->
|
||||
listData[position].also { bean ->
|
||||
binding.appIcon.setImageDrawable(appIconOf(bean.packageName))
|
||||
binding.appNameText.text = appNameOf(bean.packageName)
|
||||
binding.appNameText.text = appNameOf(bean.packageName).ifBlank { bean.packageName }
|
||||
binding.appUserIdText.isVisible = bean.userId > 0
|
||||
binding.appUserIdText.text = LocaleString.userId(bean.userId)
|
||||
binding.errorsTimeText.text = bean.crossTime
|
||||
@@ -152,14 +152,16 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
||||
/** 更新列表数据 */
|
||||
private fun refreshData() {
|
||||
FrameworkTool.fetchAppErrorsInfoData(context = this) {
|
||||
binding.titleCountText.text = LocaleString.recordCount(it.size)
|
||||
binding.listProgressView.isVisible = false
|
||||
binding.appErrorSisIcon.isVisible = it.size >= 5
|
||||
binding.clearAllIcon.isVisible = it.isNotEmpty()
|
||||
binding.exportAllIcon.isVisible = it.isNotEmpty()
|
||||
binding.listView.isVisible = it.isNotEmpty()
|
||||
binding.listNoDataView.isVisible = it.isEmpty()
|
||||
listData.clear()
|
||||
it.takeIf { e -> e.isNotEmpty() }?.forEach { e -> listData.add(e) }
|
||||
onChanged?.invoke()
|
||||
binding.appErrorSisIcon.isVisible = listData.size >= 5
|
||||
binding.clearAllIcon.isVisible = listData.isNotEmpty()
|
||||
binding.exportAllIcon.isVisible = listData.isNotEmpty()
|
||||
binding.listView.isVisible = listData.isNotEmpty()
|
||||
binding.listNoDataView.isVisible = listData.isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -28,7 +28,7 @@ import androidx.core.view.isVisible
|
||||
import com.fankes.apperrorstracking.BuildConfig
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.fankes.apperrorstracking.data.ConfigData
|
||||
import com.fankes.apperrorstracking.data.ConfigData.bind
|
||||
import com.fankes.apperrorstracking.data.factory.bind
|
||||
import com.fankes.apperrorstracking.databinding.ActivityMainBinding
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
||||
@@ -62,21 +62,24 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
setOnClickListener { function() }
|
||||
}
|
||||
}
|
||||
/** 显示开发者提示 */
|
||||
if (ConfigData.isShowDeveloperNotice)
|
||||
showDialog {
|
||||
title = LocaleString.developerNotice
|
||||
msg = LocaleString.developerNoticeTip
|
||||
confirmButton(LocaleString.gotIt) { ConfigData.isShowDeveloperNotice = false }
|
||||
noCancelable()
|
||||
}
|
||||
binding.mainTextVersion.text = LocaleString.moduleVersion(BuildConfig.VERSION_NAME)
|
||||
binding.mainTextSystemVersion.text = LocaleString.systemVersion(systemVersion)
|
||||
binding.onlyShowErrorsInFrontSwitch.bind(ConfigData.ENABLE_ONLY_SHOW_ERRORS_IN_FRONT)
|
||||
binding.onlyShowErrorsInMainProcessSwitch.bind(ConfigData.ENABLE_ONLY_SHOW_ERRORS_IN_MAIN)
|
||||
binding.alwaysShowsReopenAppOptionsSwitch.bind(ConfigData.ENABLE_ALWAYS_SHOWS_REOPEN_APP_OPTIONS)
|
||||
binding.enableAppsConfigsTemplateSwitch.bind(ConfigData.ENABLE_APP_CONFIG_TEMPLATE) {
|
||||
binding.mgrAppsConfigsTemplateButton.isVisible = it
|
||||
onInitialize { binding.mgrAppsConfigsTemplateButton.isVisible = it }
|
||||
onChanged { reinitialize() }
|
||||
}
|
||||
binding.enableMaterial3AppErrorsDialogSwitch.bind(ConfigData.ENABLE_MATERIAL3_STYLE_APP_ERRORS_DIALOG)
|
||||
/** 设置桌面图标显示隐藏 */
|
||||
binding.hideIconInLauncherSwitch.isChecked = isLauncherIconShowing.not()
|
||||
binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
hideOrShowLauncherIcon(b)
|
||||
}
|
||||
/** 设置匿名统计 */
|
||||
binding.enableAnonymousStatisticsSwitch.bindAppAnalytics()
|
||||
/** 系统版本点击事件 */
|
||||
@@ -98,14 +101,12 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
binding.titleRestartIcon.setOnClickListener { FrameworkTool.restartSystem(context = this) }
|
||||
/** 项目地址按钮点击事件 */
|
||||
binding.titleGithubIcon.setOnClickListener { openBrowser(url = "https://github.com/KitsunePie/AppErrorsTracking") }
|
||||
/** 显示开发者提示 */
|
||||
if (ConfigData.isShowDeveloperNotice)
|
||||
showDialog {
|
||||
title = LocaleString.developerNotice
|
||||
msg = LocaleString.developerNoticeTip
|
||||
confirmButton(LocaleString.gotIt) { ConfigData.isShowDeveloperNotice = false }
|
||||
noCancelable()
|
||||
}
|
||||
/** 设置桌面图标显示隐藏 */
|
||||
binding.hideIconInLauncherSwitch.isChecked = isLauncherIconShowing.not()
|
||||
binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
hideOrShowLauncherIcon(b)
|
||||
}
|
||||
}
|
||||
|
||||
/** 刷新模块状态 */
|
||||
|
@@ -30,12 +30,12 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.annotation.CauseProblemsApi
|
||||
@@ -131,7 +131,10 @@ class DialogBuilder<VB : ViewBinding>(
|
||||
customLayoutView = LinearLayout(context).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER or Gravity.START
|
||||
addView(ProgressBar(context))
|
||||
addView(CircularProgressIndicator(context).apply {
|
||||
isIndeterminate = true
|
||||
trackCornerRadius = 10.dp(context)
|
||||
})
|
||||
addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(20.dp(context), 5) })
|
||||
addView(TextView(context).apply {
|
||||
tag = "progressContent"
|
||||
|
@@ -133,24 +133,25 @@ fun Context.listOfPackages() = runCatching {
|
||||
/**
|
||||
* 得到 APP 名称
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [String]
|
||||
* @return [String] 无法获取时返回 ""
|
||||
*/
|
||||
fun Context.appNameOf(packageName: String = getPackageName()) =
|
||||
getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: "unknown"
|
||||
getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: ""
|
||||
|
||||
/**
|
||||
* 得到 APP 版本信息与版本号
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [String] 无法获取时返回 "unknown(-1)"
|
||||
* @return [String] 无法获取时返回 ""
|
||||
*/
|
||||
fun Context.appVersionBrandOf(packageName: String = getPackageName()) = "${appVersionNameOf(packageName)}(${appVersionCodeOf(packageName)})"
|
||||
fun Context.appVersionBrandOf(packageName: String = getPackageName()) =
|
||||
if (appVersionNameOf(packageName).isNotBlank()) "${appVersionNameOf(packageName)}(${appVersionCodeOf(packageName)})" else ""
|
||||
|
||||
/**
|
||||
* 得到 APP 版本名称
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [String] 无法获取时返回 "unknown"
|
||||
* @return [String] 无法获取时返回 ""
|
||||
*/
|
||||
fun Context.appVersionNameOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.versionName ?: "unknown"
|
||||
fun Context.appVersionNameOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.versionName ?: ""
|
||||
|
||||
/**
|
||||
* 得到 APP 版本号
|
||||
@@ -162,7 +163,7 @@ fun Context.appVersionCodeOf(packageName: String = getPackageName()) = getPackag
|
||||
/**
|
||||
* 获取 APP CPU ABI 名称
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [String]
|
||||
* @return [String] 无法获取时返回 ""
|
||||
*/
|
||||
fun Context.appCpuAbiOf(packageName: String = getPackageName()) = runCatching {
|
||||
ApplicationInfoClass.field { name = "primaryCpuAbi" }.get(getPackageInfoCompat(packageName)?.applicationInfo).string()
|
||||
|
@@ -26,7 +26,7 @@ package com.fankes.apperrorstracking.utils.tool
|
||||
import android.app.Application
|
||||
import android.widget.CompoundButton
|
||||
import com.fankes.apperrorstracking.BuildConfig
|
||||
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
||||
import com.highcapable.yukihookapi.hook.factory.prefs
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||
import com.microsoft.appcenter.AppCenter
|
||||
import com.microsoft.appcenter.analytics.Analytics
|
||||
@@ -51,9 +51,9 @@ object AppAnalyticsTool {
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private var isEnableAppCenterAnalytics
|
||||
get() = instance?.modulePrefs?.get(ENABLE_APP_CENTER_ANALYTICS) ?: true
|
||||
get() = instance?.prefs()?.get(ENABLE_APP_CENTER_ANALYTICS) ?: true
|
||||
set(value) {
|
||||
instance?.modulePrefs?.put(ENABLE_APP_CENTER_ANALYTICS, value)
|
||||
instance?.prefs()?.edit { put(ENABLE_APP_CENTER_ANALYTICS, value) }
|
||||
}
|
||||
|
||||
/** 绑定到 [CompoundButton] 自动设置选中状态 */
|
||||
|
@@ -36,7 +36,7 @@ import java.io.Serializable
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 获取 Github Release 最新版本工具类
|
||||
* 获取 GitHub Release 最新版本工具类
|
||||
*/
|
||||
object GithubReleaseTool {
|
||||
|
||||
@@ -98,7 +98,7 @@ object GithubReleaseTool {
|
||||
}
|
||||
|
||||
/**
|
||||
* Github Release bean
|
||||
* GitHub Release bean
|
||||
* @param name 版本名称
|
||||
* @param htmlUrl 网页地址
|
||||
* @param content 更新日志
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<?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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -14,9 +15,9 @@
|
||||
android:elevation="0dp"
|
||||
android:gravity="center|start"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingTop="13dp"
|
||||
android:paddingRight="15dp"
|
||||
android:paddingBottom="15dp">
|
||||
android:paddingBottom="5dp">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/title_back_icon"
|
||||
@@ -29,16 +30,33 @@
|
||||
android:tint="@color/colorTextGray"
|
||||
android:tooltipText="@string/back" />
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="2.5dp"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
android:text="@string/errors_record"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="19sp"
|
||||
android:textStyle="bold" />
|
||||
android:gravity="center|start"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/errors_record"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="19sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_count_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:text="@string/loading"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="11.5sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/app_error_sis_icon"
|
||||
@@ -74,7 +92,15 @@
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="5dp">
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/list_progress_view"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
app:trackCornerRadius="10dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_no_data_view"
|
||||
@@ -85,7 +111,8 @@
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="@string/no_list_data"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="17sp" />
|
||||
android:textSize="17sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/list_view"
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<?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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -94,11 +95,13 @@
|
||||
android:layout_marginTop="10dp"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<ProgressBar
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/list_progress_view"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_gravity="center" />
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
app:trackCornerRadius="10dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_no_data_view"
|
||||
|
@@ -145,4 +145,5 @@
|
||||
<string name="latest_version">最新バージョン %1$s</string>
|
||||
<string name="latest_version_tip">%1$s に投稿\n\n変更ログ\n\n%2$s</string>
|
||||
<string name="update_now">更新</string>
|
||||
<string name="record_count">%1$s の合計紀錄</string>
|
||||
</resources>
|
@@ -145,4 +145,5 @@
|
||||
<string name="latest_version">最新版本 %1$s</string>
|
||||
<string name="latest_version_tip">发布于 %1$s\n\n更新日志\n\n%2$s</string>
|
||||
<string name="update_now">更新</string>
|
||||
<string name="record_count">共 %1$s 条记录</string>
|
||||
</resources>
|
@@ -145,4 +145,5 @@
|
||||
<string name="latest_version">最新版本 %1$s</string>
|
||||
<string name="latest_version_tip">發佈於 %1$s\n\n更新日誌\n\n%2$s</string>
|
||||
<string name="update_now">升級</string>
|
||||
<string name="record_count">共 %1$s 條紀錄</string>
|
||||
</resources>
|
@@ -145,4 +145,5 @@
|
||||
<string name="latest_version">最新版本 %1$s</string>
|
||||
<string name="latest_version_tip">發佈於 %1$s\n\n更新日誌\n\n%2$s</string>
|
||||
<string name="update_now">升級</string>
|
||||
<string name="record_count">共 %1$s 條紀錄</string>
|
||||
</resources>
|
@@ -145,4 +145,5 @@
|
||||
<string name="latest_version">最新版本 %1$s</string>
|
||||
<string name="latest_version_tip">發佈於 %1$s\n\n更新日誌\n\n%2$s</string>
|
||||
<string name="update_now">升級</string>
|
||||
<string name="record_count">共 %1$s 條紀錄</string>
|
||||
</resources>
|
@@ -146,4 +146,5 @@
|
||||
<string name="latest_version">Latest Version %1$s</string>
|
||||
<string name="latest_version_tip">Released on %1$s\n\nChangelog\n\n%2$s</string>
|
||||
<string name="update_now">Update</string>
|
||||
<string name="record_count">%1$s records found</string>
|
||||
</resources>
|
24
build.gradle
24
build.gradle
@@ -1,11 +1,23 @@
|
||||
plugins {
|
||||
id 'com.android.application' version '7.4.0' apply false
|
||||
id 'com.android.library' version '7.4.0' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.7.22' apply false
|
||||
id 'com.android.application' version '7.4.1' apply false
|
||||
id 'com.android.library' version '7.4.1' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.8.20' apply false
|
||||
id 'com.google.devtools.ksp' version '1.8.20-1.0.10' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
appVersionName = "1.2"
|
||||
appVersionCode = 4
|
||||
enableR8 = true
|
||||
android = [
|
||||
compileSdk: 33,
|
||||
minSdk : 24,
|
||||
targetSdk : 33,
|
||||
ndkVersion: '24.0.8215888'
|
||||
]
|
||||
app = [
|
||||
versionName : '1.25',
|
||||
versionCode : 5,
|
||||
signingConfigs: [
|
||||
secretConfigsDirPath : "${projectDir.getAbsolutePath()}/.secret",
|
||||
secretConfigsFileName: "key_store_secret.json"
|
||||
]
|
||||
]
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
@@ -5,39 +7,44 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace 'com.fankes.apperrorsdemo'
|
||||
compileSdk 33
|
||||
ndkVersion '24.0.8215888'
|
||||
compileSdk rootProject.ext.android.compileSdk
|
||||
ndkVersion rootProject.ext.android.ndkVersion
|
||||
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('../keystore/public')
|
||||
storePassword '123456'
|
||||
keyAlias 'public'
|
||||
keyPassword '123456'
|
||||
universal {
|
||||
def dirPath = rootProject.ext.app.signingConfigs.secretConfigsDirPath
|
||||
def fileName = rootProject.ext.app.signingConfigs.secretConfigsFileName
|
||||
def configs = new JsonSlurper().parse(file("${dirPath}/${fileName}"))
|
||||
keyAlias configs.keyAlias
|
||||
keyPassword configs.keyPassword
|
||||
storeFile file("${dirPath}/${configs.storeFileName}")
|
||||
storePassword configs.storePassword
|
||||
v1SigningEnabled true
|
||||
v2SigningEnabled true
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdk 24
|
||||
targetSdk 33
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
applicationId 'com.fankes.apperrorsdemo'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags ""
|
||||
}
|
||||
}
|
||||
minSdk rootProject.ext.android.minSdk
|
||||
targetSdk rootProject.ext.android.targetSdk
|
||||
|
||||
versionCode rootProject.ext.app.versionCode
|
||||
versionName rootProject.ext.app.versionName
|
||||
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
consumerProguardFiles 'consumer-rules.pro'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled false
|
||||
signingConfig signingConfigs.universal
|
||||
}
|
||||
release {
|
||||
minifyEnabled false
|
||||
signingConfig signingConfigs.debug
|
||||
signingConfig signingConfigs.universal
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
@@ -68,11 +75,11 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.highcapable.yukihookapi:api:1.1.6'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.0'
|
||||
implementation 'com.google.android.material:material:1.7.0'
|
||||
implementation 'com.highcapable.yukireflection:api:1.0.1'
|
||||
implementation 'androidx.core:core-ktx:1.10.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.8.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
}
|
||||
}
|
@@ -31,9 +31,9 @@ import androidx.core.view.WindowCompat
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.fankes.apperrorsdemo.R
|
||||
import com.fankes.apperrorsdemo.utils.factory.isNotSystemInDarkMode
|
||||
import com.highcapable.yukihookapi.hook.factory.current
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
|
||||
import com.highcapable.yukireflection.factory.current
|
||||
import com.highcapable.yukireflection.factory.method
|
||||
import com.highcapable.yukireflection.type.android.LayoutInflaterClass
|
||||
|
||||
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Wed May 04 08:35:13 CST 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
Reference in New Issue
Block a user