92 Commits
1.1 ... 1.25

Author SHA1 Message Date
06324d104a Update version to 1.25 2023-04-17 06:41:55 +08:00
0ed2cf4903 Modify merge to YukiHookAPI new usage 2023-04-17 06:38:05 +08:00
d278faf7c9 Modify merge to YukiHookAPI new usage 2023-04-17 06:06:35 +08:00
823c5c690f Update Gradle dependencies 2023-04-17 06:05:05 +08:00
a4e1c82ad0 Update YukiHookAPI 2023-04-17 06:03:55 +08:00
07e13ccf04 Modify merge contents of build.gradle into constant definitions 2023-04-15 23:01:55 +08:00
e19f8ea4af Update Gradle dependencies 2023-04-14 17:57:05 +08:00
45c337d475 Modify change uninstalled apps or unknown apps to show their package name for users 2023-04-09 20:52:55 +08:00
8690e8afdb Modify change apps related functions returned "" for default value in FunctionFactory 2023-04-09 20:51:55 +08:00
ec7dcabdb8 Added loading view and errors records count in AppErrorsRecordActivity, activity_app_errors_record 2023-04-09 20:31:55 +08:00
0db82b5630 Added i18n strings 2023-04-09 20:30:35 +08:00
1207dbb561 Modify replace YukiHookAPI to YukiReflection in demo-app 2023-04-08 00:06:05 +08:00
785208a5bb Update Gradle & Kotlin
- Update Kotlin version to 1.8.20
- Update Gradle version to 8.0.2
- Update Gradle dependencies
2023-04-08 00:03:55 +08:00
3dbd80e06e Added start history chart in README 2023-02-19 14:39:55 +08:00
f452bb2b32 Modify change ProgressBar to CircularProgressIndicator in activity_config 2023-02-08 13:42:55 +08:00
39b779eab9 Added new bug report issues template 2023-02-08 13:25:55 +08:00
948095c016 Fix CI compiler problem and add debug version on CI 2023-02-08 13:03:15 +08:00
1a7775d5cc Fix "GitHub" spelling in all files 2023-02-07 05:27:55 +08:00
00cfca12b0 Modify change ProgressBar to CircularProgressIndicator in DialogBuilderFactory 2023-02-03 23:17:15 +08:00
556951c27c Fix some problems in CompoundButtonFactory 2023-02-03 03:39:05 +08:00
b478654008 Modify merge to CompoundButtonDataBinder and optimize code in AppErrorsDetailActivity 2023-02-03 02:26:09 +08:00
b47543e71a Modify merge to CompoundButtonDataBinder and optimize code in MainActivity 2023-02-03 02:22:05 +08:00
6ffe4bc15d Update YukiHookAPI 2023-02-01 04:32:05 +08:00
bb94689baa Update .gitignore 2023-02-01 04:31:10 +08:00
6803b60dca Update Gradle dependencies 2023-01-31 23:50:05 +08:00
82946e55fe Update version to 1.2 2023-01-23 12:19:06 +08:00
910b3523a3 Update copyright date to 2023 for all existing files 2023-01-23 12:13:50 +08:00
a6fd0b699c Modify remove scrollbar and reset scroll view when view changed in AppErrorsDetailActivity, activity_app_errors_detail 2023-01-23 11:59:35 +08:00
6ddbfb5444 Added check for updates feature from GitHub Release 2023-01-23 11:43:55 +08:00
fe4f91291e Added i18n strings 2023-01-23 11:42:04 +08:00
5e0336a434 Added disable automatic wrapping error stack trace contents function in ConfigData, AppErrorsDetailActivity, activity_app_errors_detail 2023-01-22 15:51:31 +08:00
55a4d68a18 Added i18n strings 2023-01-22 15:49:31 +08:00
358ac848e2 Modify remove screenOrientation parameter for AppErrorsDetailActivity in AndroidManifest 2023-01-22 15:32:18 +08:00
567d0a7b6c Modify add cpuAbi, versionName, versionCode default value for legacy data transfer of AppErrorsInfoBean in AppErrorsRecordData 2023-01-22 14:55:16 +08:00
4d5dadae8b Modify make crashed apps info persistence and add more info to stackOutputShareContent, stackOutputFileContent in AppErrorsInfoBean, FrameworkHooker, AppErrorsDetailActivity 2023-01-22 14:51:15 +08:00
9dce54f326 Added appVersionNameOf, appVersionCodeOf functions and merge to appVersionBrandOf function in FunctionFactory 2023-01-22 14:23:41 +08:00
37af877a7d Modify change the judgment logic when fetched installed packages list is empty in FrameworkHooker 2023-01-22 14:11:05 +08:00
59f5b27d19 Modify change view id "app_api" to "app_cpu_abi" in AppErrorsDetailActivity, activity_app_errors_detail 2023-01-22 14:05:08 +08:00
1299779868 Modify change apps config filters function with 3 types such as user apps, system apps and all apps 2023-01-22 13:05:05 +08:00
27c5e92879 Added i18n strings and remove some unused translations 2023-01-22 13:03:02 +08:00
d22745f2ea Modify make radio button singleLine in dia_app_config 2023-01-22 12:59:38 +08:00
dfcee71168 Modify add @throws code note in putAppShowingType function in AppErrorsConfigData 2023-01-22 11:49:37 +08:00
5b30499cc3 Update YukiHookAPI 2023-01-21 00:53:45 +08:00
1889b848b0 Update Android Gradle Plugin to 7.4.0 2023-01-21 00:49:40 +08:00
c349ee5dc7 Added global app config template function and remove old implementations 2023-01-20 03:09:25 +08:00
72e99bd4f2 Added i18n strings and fix some translations 2023-01-20 02:59:15 +08:00
2d9550d5b9 Modify remove fetch installed packages list log and only print for empty list in FrameworkHooker 2023-01-20 02:33:25 +08:00
27e068898e Fix translations for i18n strings 2023-01-19 23:59:28 +08:00
92a0591d5b Update Gradle dependencies 2023-01-19 22:29:12 +08:00
2a97dcbc06 Added access root failed tips dialog in FrameworkTool 2023-01-19 21:53:58 +08:00
0dc12ae3e6 Added i18n strings 2023-01-19 21:53:51 +08:00
075850d239 Modify remove @Keep in all beans and add @SerializedName in AppErrorsInfoBean 2023-01-19 13:07:26 +08:00
c48a23f07d Modify change empty mark for AppErrorsInfoBean in AppErrorsInfoBean, FrameworkHooker 2023-01-19 12:57:02 +08:00
cf1796b7cb Modify optimize code in FrameworkHooker 2023-01-17 13:45:16 +08:00
5bb1f38146 Modify ignored "android" package name when getting app list data in FrameworkHooker 2023-01-17 13:13:21 +08:00
a055d7f53d Modify rename ui/view to ui/widget 2023-01-17 11:15:48 +08:00
2eae45a640 Modify change the way of getting app list data in FrameworkHooker 2023-01-17 05:12:23 +08:00
fd168f8810 Modify change multi-user app display name with suffix its user id in FrameworkHooker 2023-01-17 05:02:15 +08:00
37580519db Modify change appNameOf function returned default blank content to "unknown" in FunctionFactory 2023-01-17 04:56:37 +08:00
5144494f82 Modify make app errors record files sorted by last modified date in AppErrorsRecordData 2023-01-17 04:35:47 +08:00
01a6c1ffa5 Fix the previous fix caused the text could not be selected in AppErrorsDetailActivity, activity_app_errors_detail 2023-01-17 03:59:48 +08:00
2aaa422a56 Modify change AppErrorsData to AppErrorsProcessData in FrameworkHooker 2023-01-17 03:22:37 +08:00
7c0c1754e9 Modify merge to new way to save and read the app errors record data 2023-01-17 03:17:37 +08:00
587c718d0a Added List.toArrayList function in FunctionFactory 2023-01-17 03:03:27 +08:00
6b2b538047 Added Any?.toJsonOrNull, String.toEntityOrNull functions in GsonFormatFactory 2023-01-17 01:37:08 +08:00
6a5fee830c Fix code style in build.gradle 2023-01-16 23:36:20 +08:00
1d0cc1d24f Fix the central color problem of views such as CheckBox 2023-01-16 22:40:26 +08:00
eeaf386635 Fix the interface automatically slides up problem on Android versions lower than 10 in activity_app_errors_detail 2023-01-16 22:21:21 +08:00
a15b5b008d Added "Go It Now" button in app errors dialog for unable get app errors record in AppErrorsDetailActivity 2023-01-16 22:20:02 +08:00
37962fa12f Modify merge app errors functions implementation code to AppErrorsData in FrameworkHooker 2023-01-15 14:35:06 +08:00
3798479c23 Fix crashed apps user id mismatch problem in FrameworkHooker, AppErrorsInfoBean 2023-01-15 04:12:50 +08:00
a214f5773f Fix the app first crash report not responded problem in some customize ROMs in FrameworkHooker 2023-01-15 03:57:41 +08:00
aeda0f183e Modify support Android 7.0 2023-01-15 02:39:03 +08:00
c6d5f07b8c Fix PackageList class not exist problem in Android 8.1 and fix app errors dialog no show problem in FrameworkHooker 2023-01-15 02:37:16 +08:00
5991d976b9 Modify optimize code format in MainActivity 2023-01-15 01:35:02 +08:00
3fb4e4f375 Fix no onCreate method in AppErrorDialog class problem in Android 10 in FrameworkHooker 2023-01-14 01:55:52 +08:00
8c4a1ea5f0 Modify make HookEntry singleton 2023-01-14 01:55:29 +08:00
7749cb9aeb Modify merge to YukiHookAPI new usage 2023-01-14 01:55:22 +08:00
9d9cb473e8 Update Gradle & Kotlin
- Update Kotlin version to 1.7.22
- Update Gradle version to 7.6
- Update Gradle dependencies
2023-01-14 01:28:08 +08:00
afeb16e69d Update YukiHookAPI 2023-01-14 01:24:19 +08:00
211343a6e7 Modify remove "contains", "replace" method's param name statement 2023-01-14 01:22:16 +08:00
2c0cfb6863 Modify add release channel description, release status description in README 2022-11-26 00:12:48 +08:00
15293950a9 Modify change action file name for ci 2022-11-25 23:32:43 +08:00
88fe23ab9a Modify change action name for ci 2022-11-25 23:29:03 +08:00
Fankesyooni
150d2e8aa5 Merge pull request #24 from KitsunePie/ci
Upgrade ci deps
2022-11-14 13:13:57 +08:00
Howard Wu
dd6e971a34 gradlew chmod +x 2022-11-14 12:13:50 +08:00
Howard Wu
051da4df5f Update push_ci.yml 2022-11-14 12:10:41 +08:00
Howard Wu
368a4b347f Upgrade ci deps 2022-11-14 12:08:46 +08:00
30b92770e9 Update Gradle & PlatformSDK
- Update Android Gradle Plugin version to 7.3.1
- Update Kotlin version to 1.7.20
- Update YukiHookAPI version to 1.1.4 in demo-app
2022-10-20 00:18:46 +08:00
Fankesyooni
2491547e3e Merge pull request #10 from cracky5322/master
Update Traditional Chinese
2022-10-12 12:00:25 +08:00
Jia-Bin
182a65255f Update Traditional Chinese
Better quality and beautiful localization translation optimization
2022-10-12 11:42:17 +08:00
aef2e0814a Fix file naming bug 2022-10-05 09:57:39 +08:00
77 changed files with 2209 additions and 970 deletions

100
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View 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

77
.github/workflows/commit_ci.yml vendored Normal file
View File

@@ -0,0 +1,77 @@
name: Automatic Build on Commit
on:
workflow_dispatch:
push:
branches: [ master ]
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '!.github/workflows/**'
jobs:
build:
name: Build CI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1
with:
cmake-version: '3.22.1'
- name: Prepare Java 11
uses: actions/setup-java@v3
with:
java-version: 11
java-package: jdk
distribution: 'temurin'
cache: 'gradle'
- name: Cache Gradle Dependencies
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
!~/.gradle/caches/build-cache-*
key: gradle-deps-core-${{ hashFiles('**/build.gradle') }}
restore-keys: |
gradle-deps
- name: Cache Gradle Build
uses: actions/cache@v3
with:
path: |
~/.gradle/caches/build-cache-*
key: gradle-builds-core-${{ github.sha }}
restore-keys: |
gradle-builds
- name: Build with Gradle
run: |
./gradlew :app:assembleDebug
./gradlew :app:assembleRelease
./gradlew :demo-app:assembleDebug
./gradlew :demo-app:assembleRelease
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.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-release)
uses: actions/upload-artifact@v3
with:
path: ${{ env.DEMO_RELEASE_APK_FILE }}
name: demo-release

View File

@@ -1,62 +0,0 @@
name: main
on:
pull_request:
branches: [ master ]
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '!.github/workflows/**'
jobs:
build:
name: Pull request check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.12
with:
cmake-version: '3.22.1'
- name: Prepare Java 11
uses: actions/setup-java@v1
with:
java-version: 11
java-package: jdk
- name: Cache Gradle Dependencies
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
!~/.gradle/caches/build-cache-*
key: gradle-deps-core-${{ hashFiles('**/build.gradle') }}
restore-keys: |
gradle-deps
- name: Cache Gradle Build
uses: actions/cache@v2
with:
path: |
~/.gradle/caches/build-cache-*
key: gradle-builds-core-${{ github.sha }}
restore-keys: |
gradle-builds
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: |
./gradlew :app:assembleRelease
./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)
uses: actions/upload-artifact@v2
with:
path: ${{ env.APK_FILE }}
name: module-release
- name: Upload Artifacts(demo-app)
uses: actions/upload-artifact@v2
with:
path: ${{ env.DEMO_APK_FILE }}
name: demo-release

76
.github/workflows/pull_request_ci.yml vendored Normal file
View File

@@ -0,0 +1,76 @@
name: Pull Request Checker
on:
pull_request:
branches: [ master ]
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '!.github/workflows/**'
jobs:
build:
name: Pull request check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1
with:
cmake-version: '3.22.1'
- name: Prepare Java 11
uses: actions/setup-java@v3
with:
java-version: 11
java-package: jdk
distribution: 'temurin'
cache: 'gradle'
- name: Cache Gradle Dependencies
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
!~/.gradle/caches/build-cache-*
key: gradle-deps-core-${{ hashFiles('**/build.gradle') }}
restore-keys: |
gradle-deps
- name: Cache Gradle Build
uses: actions/cache@v3
with:
path: |
~/.gradle/caches/build-cache-*
key: gradle-builds-core-${{ github.sha }}
restore-keys: |
gradle-builds
- name: Build with Gradle
run: |
./gradlew :app:assembleDebug
./gradlew :app:assembleRelease
./gradlew :demo-app:assembleDebug
./gradlew :demo-app:assembleRelease
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.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-release)
uses: actions/upload-artifact@v3
with:
path: ${{ env.DEMO_RELEASE_APK_FILE }}
name: demo-release

View File

@@ -1,63 +0,0 @@
name: main
on:
workflow_dispatch:
push:
branches: [ master ]
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '!.github/workflows/**'
jobs:
build:
name: Build CI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.12
with:
cmake-version: '3.22.1'
- name: Prepare Java 11
uses: actions/setup-java@v1
with:
java-version: 11
java-package: jdk
- name: Cache Gradle Dependencies
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
!~/.gradle/caches/build-cache-*
key: gradle-deps-core-${{ hashFiles('**/build.gradle') }}
restore-keys: |
gradle-deps
- name: Cache Gradle Build
uses: actions/cache@v2
with:
path: |
~/.gradle/caches/build-cache-*
key: gradle-builds-core-${{ github.sha }}
restore-keys: |
gradle-builds
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: |
./gradlew :app:assembleRelease
./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)
uses: actions/upload-artifact@v2
with:
path: ${{ env.APK_FILE }}
name: module-release
- name: Upload Artifacts(demo-app)
uses: actions/upload-artifact@v2
with:
path: ${{ env.DEMO_APK_FILE }}
name: demo-release

2
.gitignore vendored
View File

@@ -1,7 +1,7 @@
# Project exclude paths
*.iml
.gradle
.secret
.secret/APP_CENTER_SECRET
/local.properties
/.idea/caches
/.idea/libraries

View File

@@ -0,0 +1,6 @@
{
"keyAlias": "public",
"keyPassword": "123456",
"storeFileName": "universal.p12",
"storePassword": "123456"
}

View File

@@ -2,7 +2,7 @@
[![Blank](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/KitsunePie/AppErrorsTracking)
[![Blank](https://img.shields.io/badge/license-AGPL3.0-blue)](https://github.com/KitsunePie/AppErrorsTracking/blob/master/LICENSE)
[![Blank](https://img.shields.io/badge/version-v1.1-green)](https://github.com/KitsunePie/AppErrorsTracking/releases)
[![Blank](https://img.shields.io/badge/version-v1.25-green)](https://github.com/KitsunePie/AppErrorsTracking/releases)
[![Blank](https://img.shields.io/github/downloads/KitsunePie/AppErrorsTracking/total?label=Release)](https://github.com/KitsunePie/AppErrorsTracking/releases)
[![Blank](https://img.shields.io/github/downloads/Xposed-Modules-Repo/com.fankes.apperrorstracking/total?label=LSPosed%20Repo&logo=Android&style=flat&labelColor=F48FB1&logoColor=ffffff)](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
<br/><br/>
@@ -18,7 +18,7 @@
应用发生崩溃的错误日志对开发者来说是无价的财富,若你不是开发者,你依然可以安装此模块,以便给开发者提供更多异常信息快速解决问题。
> 最低支持 Android 8.1
> 最低支持 Android 7.0
## 项目缘由
@@ -55,12 +55,41 @@
欢迎为此项目做出贡献,将其翻译为您国家的语言。
## 发行渠道说明
- [Automatic Build on Commit](https://github.com/KitsunePie/AppErrorsTracking/actions/workflows/commit_ci.yml)
上述更新为代码 `commit` 后自动触发,具体更新内容可点击上方的文字前往 **GitHub Actions** 进行查看,本更新由开源的流程自动编译发布,**不保证其稳定性**,所发布的版本**仅供测试**,且不会特殊说明甚至可能会变更版本号或保持与当前稳定版相同的版本号。
- [Release](https://github.com/KitsunePie/AppErrorsTracking/releases)
- [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
上述更新为手动发布的稳定版,具体更新内容可点击上方的文字前往指定的发布页面查看,稳定版的更新将会同时发布到上述地址中,同步更新。
## 发行状态说明
![Blank](https://img.shields.io/badge/build-passing-brightgreen)
上述状态为当前稳定版与自动构建版本一致或当前代码改动与稳定版无功能差异。
![Blank](https://img.shields.io/badge/build-pending-dbab09)
上述状态为存在自动构建版本和新功能的更新但当前并未发布稳定版,处于预发行状态。
![Blank](https://img.shields.io/badge/build-problem-red)
上述状态为当前发行的稳定版可能存在严重问题但并未及时进行修复且并未发布稳定版。
## Star History
![Star History Chart](https://api.star-history.com/svg?repos=KitsunePie/AppErrorsTracking&type=Date)
## 许可证
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
```
Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -78,4 +107,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI)
版权所有 © 2019-2022 Fankes Studio(qzmmcn@163.com)
版权所有 © 2017-2023 Fankes Studio(qzmmcn@163.com)

View File

@@ -2,7 +2,7 @@
[![Blank](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/KitsunePie/AppErrorsTracking)
[![Blank](https://img.shields.io/badge/license-AGPL3.0-blue)](https://github.com/KitsunePie/AppErrorsTracking/blob/master/LICENSE)
[![Blank](https://img.shields.io/badge/version-v1.1-green)](https://github.com/KitsunePie/AppErrorsTracking/releases)
[![Blank](https://img.shields.io/badge/version-v1.25-green)](https://github.com/KitsunePie/AppErrorsTracking/releases)
[![Blank](https://img.shields.io/github/downloads/KitsunePie/AppErrorsTracking/total?label=Release)](https://github.com/KitsunePie/AppErrorsTracking/releases)
[![Blank](https://img.shields.io/github/downloads/Xposed-Modules-Repo/com.fankes.apperrorstracking/total?label=LSPosed%20Repo&logo=Android&style=flat&labelColor=F48FB1&logoColor=ffffff)](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
<br/><br/>
@@ -20,7 +20,7 @@ any installed apps, so as to quickly locate the problem.
The error log of apps crashing is an invaluable asset for developers. If you are not a developer, you can still install this module to provide
developers with more exception information to quickly solve problems.
> Minimum support Android 8.1
> Minimum support Android 7.0
## Project Reason
@@ -58,16 +58,57 @@ Similar to **Bugly** to automatically collect errors, the system cannot obtain w
- Errors display function for multi-process apps
## Translation contribution
## Translation Contribution
Contributions to this project are welcome to translate it into your country's language.
## Release Channel Description
- [Automatic Build on Commit](https://github.com/KitsunePie/AppErrorsTracking/actions/workflows/commit_ci.yml)
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**.
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.
- [Release](https://github.com/KitsunePie/AppErrorsTracking/releases)
- [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
The above update is a manually released stable version.
For the specific update content, you can click the text above to go to the designated release page to view.
The update of the stable version will be released to the above address at the same time and updated synchronously.
## Release Status Description
![Blank](https://img.shields.io/badge/build-passing-brightgreen)
The above status is that the current stable version is consistent with the automatic build version or the current code changes and the stable
version have no functional difference.
![Blank](https://img.shields.io/badge/build-pending-dbab09)
The above state is that there are automatic build versions and updates with new features but no stable version is currently released, and it is
in a pre-release state.
![Blank](https://img.shields.io/badge/build-problem-red)
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
![Star History Chart](https://api.star-history.com/svg?repos=KitsunePie/AppErrorsTracking&type=Date)
## License
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
```
Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -85,4 +126,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI)
Copyright © 2019-2022 Fankes Studio(qzmmcn@163.com)
Copyright © 2017-2023 Fankes Studio(qzmmcn@163.com)

16
app/.gitignore vendored
View File

@@ -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

View File

@@ -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.10-1.0.6'
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 27
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,18 @@ String getAppCenterSecret() {
dependencies {
compileOnly 'de.robv.android.xposed:api:82'
implementation 'com.highcapable.yukihookapi:api:1.1.4'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.1.4'
implementation "com.microsoft.appcenter:appcenter-analytics:4.4.5"
implementation "com.microsoft.appcenter:appcenter-crashes:4.4.5"
implementation 'com.google.code.gson:gson:2.9.0'
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.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.5.1'
implementation 'com.google.android.material:material:1.6.1'
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.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

View File

@@ -27,7 +27,10 @@
android:value="@string/xposed_desc" />
<meta-data
android:name="xposedminversion"
android:value="93" />
android:value="89" />
<meta-data
android:name="xposedsharedprefs"
android:value="true" />
<meta-data
android:name="xposedscope"
android:resource="@array/module_scope" />
@@ -92,7 +95,6 @@
android:name=".ui.activity.errors.AppErrorsDetailActivity"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="behind"
android:taskAffinity=":detail" />
<service

View File

@@ -1 +0,0 @@
com.fankes.apperrorstracking.hook.AppErrorsTracking

View File

@@ -1 +0,0 @@
com.fankes.apperrorstracking.hook.HookEntry

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -21,7 +21,6 @@
*/
package com.fankes.apperrorstracking.bean
import androidx.annotation.Keep
import java.io.Serializable
/**
@@ -37,13 +36,13 @@ import java.io.Serializable
* @param isShowReopenButton 是否显示重新打开按钮
*/
data class AppErrorsDisplayBean(
@Keep var pid: Int,
@Keep var userId: Int,
@Keep var packageName: String,
@Keep var processName: String,
@Keep var appName: String,
@Keep var title: String,
@Keep var isShowAppInfoButton: Boolean,
@Keep var isShowCloseAppButton: Boolean,
@Keep var isShowReopenButton: Boolean
var pid: Int,
var userId: Int,
var packageName: String,
var processName: String,
var appName: String,
var title: String,
var isShowAppInfoButton: Boolean,
var isShowCloseAppButton: Boolean,
var isShowReopenButton: Boolean
) : Serializable

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -22,11 +22,11 @@
package com.fankes.apperrorstracking.bean
import android.app.ApplicationErrorReport
import android.content.Context
import android.os.Build
import androidx.annotation.Keep
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.utils.factory.difference
import com.fankes.apperrorstracking.utils.factory.toUtcTime
import com.fankes.apperrorstracking.utils.factory.*
import com.google.gson.annotations.SerializedName
import java.io.Serializable
import java.text.SimpleDateFormat
import java.util.*
@@ -35,7 +35,10 @@ import java.util.*
* 应用异常信息 bean
* @param pid 进程 ID
* @param userId 用户 ID
* @param cpuAbi CPU 架构类型
* @param packageName 包名
* @param versionName 版本名称
* @param versionCode 版本号
* @param isNativeCrash 是否为原生层异常
* @param exceptionClassName 异常类名
* @param exceptionMessage 异常信息
@@ -47,46 +50,62 @@ import java.util.*
* @param timestamp 记录时间戳
*/
data class AppErrorsInfoBean(
@Keep var pid: Int = -1,
@Keep var userId: Int = -1,
@Keep var packageName: String = "",
@Keep var isNativeCrash: Boolean = false,
@Keep var exceptionClassName: String = "",
@Keep var exceptionMessage: String = "",
@Keep var throwFileName: String = "",
@Keep var throwClassName: String = "",
@Keep var throwMethodName: String = "",
@Keep var throwLineNumber: Int = -1,
@Keep var stackTrace: String = "",
@Keep var timestamp: Long = -1L
@SerializedName("pid")
var pid: Int = -1,
@SerializedName("userId")
var userId: Int = -1,
@SerializedName("cpuAbi")
var cpuAbi: String = "",
@SerializedName("packageName")
var packageName: String = "",
@SerializedName("versionName")
var versionName: String = "",
@SerializedName("versionCode")
var versionCode: Long = -1L,
@SerializedName("isNativeCrash")
var isNativeCrash: Boolean = false,
@SerializedName("exceptionClassName")
var exceptionClassName: String = "",
@SerializedName("exceptionMessage")
var exceptionMessage: String = "",
@SerializedName("throwFileName")
var throwFileName: String = "",
@SerializedName("throwClassName")
var throwClassName: String = "",
@SerializedName("throwMethodName")
var throwMethodName: String = "",
@SerializedName("throwLineNumber")
var throwLineNumber: Int = -1,
@SerializedName("stackTrace")
var stackTrace: String = "",
@SerializedName("timestamp")
var timestamp: Long = -1L
) : Serializable {
companion object {
/**
* 创建一个空的 [AppErrorsInfoBean]
* @return [AppErrorsInfoBean]
*/
fun createEmpty() = AppErrorsInfoBean().apply { isEmpty = true }
/**
* 从 [ApplicationErrorReport.CrashInfo] 克隆
* @param context 当前实例
* @param pid APP 进程 ID
* @param packageName APP 包名
* @param userId APP 用户 ID
* @param packageName APP 包名
* @param crashInfo [ApplicationErrorReport.CrashInfo]
* @return [AppErrorsInfoBean]
*/
fun clone(pid: Int, packageName: String?, userId: Int?, crashInfo: ApplicationErrorReport.CrashInfo?) =
fun clone(context: Context, pid: Int, userId: Int, packageName: String?, crashInfo: ApplicationErrorReport.CrashInfo?) =
(crashInfo?.exceptionClassName?.lowercase() == "native crash").let { isNativeCrash ->
AppErrorsInfoBean(
pid = pid,
userId = userId ?: 0,
userId = userId,
cpuAbi = packageName?.let { context.appCpuAbiOf(it) } ?: "",
packageName = packageName ?: "unknown",
versionName = packageName?.let { context.appVersionNameOf(it).ifBlank { "unknown" } } ?: "",
versionCode = packageName?.let { context.appVersionCodeOf(it) } ?: -1L,
isNativeCrash = isNativeCrash,
exceptionClassName = crashInfo?.exceptionClassName ?: "unknown",
exceptionMessage = if (isNativeCrash) crashInfo?.stackTrace.let {
if (it?.contains(other = "Abort message: '") == true)
if (it?.contains("Abort message: '") == true)
runCatching { it.split("Abort message: '")[1].split("'")[0] }.getOrNull()
?: crashInfo?.exceptionMessage ?: "unknown"
else crashInfo?.exceptionMessage ?: "unknown"
@@ -101,8 +120,23 @@ data class AppErrorsInfoBean(
}
}
/** 标识当前内容是否为空 */
var isEmpty = false
/**
* 获取当前内容是否为空
* @return [Boolean]
*/
val isEmpty get() = pid == -1 && userId == -1 && timestamp == -1L
/**
* 获取生成的 Json 文件名
* @return [String]
*/
val jsonFileName get() = "${packageName}_${pid}_${timestamp}.json"
/**
* 获取 APP 版本信息与版本号
* @return [String]
*/
val versionBrand get() = if (versionName.isBlank()) "unknown" else "$versionName($versionCode)"
/**
* 获取异常本地化 UTC 时间
@@ -138,18 +172,7 @@ data class AppErrorsInfoBean(
val stackOutputShareContent
get() = "Generated by AppErrorsTracking\n" +
"Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
"===============\n" +
"[Device Brand]: ${Build.BRAND}\n" +
"[Device Model]: ${Build.MODEL}\n" +
"[Display]: ${Build.DISPLAY}\n" +
"[Android Version]: ${Build.VERSION.RELEASE}\n" +
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
"[System Locale]: ${Locale.getDefault()}\n" +
"[Package Name]: $packageName\n" +
(if (userId > 0) "[User Id]: $userId\n" else "") +
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
"[Crash Time]: $utcTime\n" +
"[Stack Trace]:\n" + stackTrace
"===============\n$environmentInfo"
/**
* 获取异常堆栈文件模板
@@ -160,15 +183,26 @@ data class AppErrorsInfoBean(
" Generated by AppErrorsTracking\n" +
" Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
"================================================================\n" +
"[Device Brand]: ${Build.BRAND}\n" +
environmentInfo
/**
* 获取运行环境信息
* @return [String]
*/
private val environmentInfo
get() = "[Device Brand]: ${Build.BRAND}\n" +
"[Device Model]: ${Build.MODEL}\n" +
"[Display]: ${Build.DISPLAY}\n" +
"[Android Version]: ${Build.VERSION.RELEASE}\n" +
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
"[Android API Level]: ${Build.VERSION.SDK_INT}\n" +
"[System Locale]: ${Locale.getDefault()}\n" +
"[Package Name]: $packageName\n" +
"[Process ID]: $pid\n" +
(if (userId > 0) "[User Id]: $userId\n" else "") +
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
"[CPU ABI]: ${cpuAbi.ifBlank { "none" }}\n" +
"[Package Name]: $packageName\n" +
"[Version Name]: ${versionName.ifBlank { "unknown" }}\n" +
"[Version Code]: ${versionCode.takeIf { it != -1L } ?: "unknown"}\n" +
"[Error Type]: ${if (isNativeCrash) "Native" else "JVM"}\n" +
"[Crash Time]: $utcTime\n" +
"[Stack Trace]:\n" + stackTrace
}

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -21,15 +21,15 @@
*/
package com.fankes.apperrorstracking.bean
import androidx.annotation.Keep
import com.fankes.apperrorstracking.bean.enum.AppFiltersType
import java.io.Serializable
/**
* 应用过滤条件 bean
* @param name 名称或包名
* @param isContainsSystem 是否包含系统应用
* @param type 过滤条件类型
*/
data class AppFiltersBean(
@Keep var name: String = "",
@Keep var isContainsSystem: Boolean = false
var name: String = "",
var type: AppFiltersType = AppFiltersType.USER
) : Serializable

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -22,7 +22,6 @@
package com.fankes.apperrorstracking.bean
import android.graphics.drawable.Drawable
import androidx.annotation.Keep
import java.io.Serializable
/**
@@ -32,7 +31,7 @@ import java.io.Serializable
* @param packageName APP 包名
*/
data class AppInfoBean(
@Keep var icon: Drawable? = null,
@Keep var name: String,
@Keep var packageName: String
var icon: Drawable? = null,
var name: String,
var packageName: String
) : Serializable

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -21,7 +21,6 @@
*/
package com.fankes.apperrorstracking.bean
import androidx.annotation.Keep
import java.io.Serializable
/**
@@ -30,8 +29,8 @@ import java.io.Serializable
* @param packageName 包名
*/
data class MutedErrorsAppBean(
@Keep var type: MuteType,
@Keep var packageName: String
var type: MuteType,
var packageName: String
) : Serializable {
/**

View File

@@ -0,0 +1,36 @@
/*
* 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/1/22.
*/
package com.fankes.apperrorstracking.bean.enum
/**
* 应用过滤条件类型定义类
*/
enum class AppFiltersType {
/** 用户 */
USER,
/** 系统 */
SYSTEM,
/** 全部 */
ALL
}

View File

@@ -0,0 +1,139 @@
/*
* 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/1/20.
*/
package com.fankes.apperrorstracking.data
import com.fankes.apperrorstracking.data.enum.AppErrorsConfigType
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
/**
* 应用配置模版存储控制类
*/
object AppErrorsConfigData {
/** 显示错误对话框键值名称 */
private const val SHOW_ERRORS_DIALOG_APPS = "_show_errors_dialog_apps"
/** 推送错误通知键值名称 */
private const val SHOW_ERRORS_NOTIFY_APPS = "_show_errors_notify_apps"
/** 显示错误 Toast 键值名称 */
private const val SHOW_ERRORS_TOAST_APPS = "_show_errors_toast_apps"
/** 什么也不显示键值名称 */
private const val SHOW_ERRORS_NOTHING_APPS = "_show_errors_nothing_apps"
/** 全局错误显示类型 */
private val GLOBAL_SHOW_ERRORS_TYPE = PrefsData("_global_show_errors_type", AppErrorsConfigType.DIALOG.ordinal)
/** 显示错误对话框的 APP 包名数组 */
private var showDialogApps = HashSet<String>()
/** 推送错误通知的 APP 包名数组 */
private var showNotifyApps = HashSet<String>()
/** 显示错误 Toast 的 APP 包名数组 */
private var showToastApps = HashSet<String>()
/** 什么也不显示的 APP 包名数组 */
private var showNothingApps = HashSet<String>()
/** 刷新存储控制类 */
fun refresh() {
showDialogApps = ConfigData.getStringSet(SHOW_ERRORS_DIALOG_APPS).toHashSet()
showNotifyApps = ConfigData.getStringSet(SHOW_ERRORS_NOTIFY_APPS).toHashSet()
showToastApps = ConfigData.getStringSet(SHOW_ERRORS_TOAST_APPS).toHashSet()
showNothingApps = ConfigData.getStringSet(SHOW_ERRORS_NOTHING_APPS).toHashSet()
}
/**
* 获取当前 APP 显示错误的类型是否为 [type]
* @param type 当前类型
* @param packageName 当前 APP 包名 - 不填为全局配置
* @return [Boolean]
*/
fun isAppShowingType(type: AppErrorsConfigType, packageName: String = "") =
if (packageName.isNotBlank()) when (type) {
AppErrorsConfigType.GLOBAL ->
showDialogApps.contains(packageName).not() &&
showNotifyApps.contains(packageName).not() &&
showToastApps.contains(packageName).not() &&
showNothingApps.contains(packageName).not()
AppErrorsConfigType.DIALOG -> showDialogApps.contains(packageName)
AppErrorsConfigType.NOTIFY -> showNotifyApps.contains(packageName)
AppErrorsConfigType.TOAST -> showToastApps.contains(packageName)
AppErrorsConfigType.NOTHING -> showNothingApps.contains(packageName)
} else ConfigData.getInt(GLOBAL_SHOW_ERRORS_TYPE) == type.ordinal
/**
* 写入当前 APP 显示错误的类型
* @param type 当前类型
* @param packageName 当前 APP 包名 - 不填为全局配置
* @throws IllegalStateException 如果 [packageName] 为空 [type] 为 [AppErrorsConfigType.GLOBAL]
*/
fun putAppShowingType(type: AppErrorsConfigType, packageName: String = "") {
if (packageName.isBlank() && type == AppErrorsConfigType.GLOBAL)
error("You can't still specify the \"follow global config\" type when saving the global config")
fun saveAllData() {
ConfigData.putStringSet(SHOW_ERRORS_DIALOG_APPS, showDialogApps)
ConfigData.putStringSet(SHOW_ERRORS_NOTIFY_APPS, showNotifyApps)
ConfigData.putStringSet(SHOW_ERRORS_TOAST_APPS, showToastApps)
ConfigData.putStringSet(SHOW_ERRORS_NOTHING_APPS, showNothingApps)
}
if (packageName.isNotBlank()) when (type) {
AppErrorsConfigType.GLOBAL -> {
showDialogApps.remove(packageName)
showNotifyApps.remove(packageName)
showToastApps.remove(packageName)
showNothingApps.remove(packageName)
saveAllData()
}
AppErrorsConfigType.DIALOG -> {
showDialogApps.add(packageName)
showNotifyApps.remove(packageName)
showToastApps.remove(packageName)
showNothingApps.remove(packageName)
saveAllData()
}
AppErrorsConfigType.NOTIFY -> {
showDialogApps.remove(packageName)
showNotifyApps.add(packageName)
showToastApps.remove(packageName)
showNothingApps.remove(packageName)
saveAllData()
}
AppErrorsConfigType.TOAST -> {
showDialogApps.remove(packageName)
showNotifyApps.remove(packageName)
showToastApps.add(packageName)
showNothingApps.remove(packageName)
saveAllData()
}
AppErrorsConfigType.NOTHING -> {
showDialogApps.remove(packageName)
showNotifyApps.remove(packageName)
showToastApps.remove(packageName)
showNothingApps.add(packageName)
saveAllData()
}
} else ConfigData.putInt(GLOBAL_SHOW_ERRORS_TYPE, type.ordinal)
}
}

View File

@@ -0,0 +1,134 @@
/*
* 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/1/17.
*/
@file:Suppress("StaticFieldLeak")
package com.fankes.apperrorstracking.data
import android.content.Context
import android.provider.Settings
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
import com.fankes.apperrorstracking.utils.factory.*
import com.highcapable.yukihookapi.hook.log.loggerE
import java.io.File
import java.util.concurrent.CopyOnWriteArrayList
/**
* [AppErrorsInfoBean] 存储控制类
*/
object AppErrorsRecordData {
/** 异常记录数据文件目录路径 */
private const val FOLDER_PATH = "/data/misc/app_errors_records/"
/** 当前实例 */
private var context: Context? = null
/**
* 获取当前异常记录数据目录
* @return [File]
*/
private val errorsInfoDataFolder by lazy { File(FOLDER_PATH) }
/**
* 获取当前全部异常记录数据文件
* @return [List]<[File]>
*/
private val errorsInfoDataFiles get() = errorsInfoDataFolder.listFiles()?.sortedByDescending { it.lastModified() } ?: emptyList()
/** 已记录的全部 APP 异常信息数组 */
var allData = CopyOnWriteArrayList<AppErrorsInfoBean>()
/**
* 初始化存储控制类
* @param context 实例
*/
fun init(context: Context) {
this.context = context
initializeDataDirectory()
allData = readAllDataFromFiles()
}
/** 初始化异常记录数据目录 */
private fun initializeDataDirectory() {
runCatching {
errorsInfoDataFolder.also { if (it.exists().not() || it.isFile) it.apply { delete(); mkdirs() } }
}.onFailure {
loggerE(msg = "Can't create directory \"$FOLDER_PATH\", there will be problems with the app errors records function", e = it)
}
}
/**
* 获取旧版异常记录数据并自动转换到新版
* @return [ArrayList]<[AppErrorsInfoBean]> or null
*/
private fun copyOldDataFromResolverString() = context?.let {
val keyName = "app_errors_data"
runCatching {
Settings.Secure.getString(it.contentResolver, keyName)
?.toEntityOrNull<CopyOnWriteArrayList<AppErrorsInfoBean>>()
?.onEach { e ->
e.cpuAbi = it.appCpuAbiOf(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 ->
if (result != null) {
Settings.Secure.putString(it.contentResolver, keyName, "")
result
} else null
}
}.getOrNull()
}
/**
* 从文件获取全部异常记录数据
* @return [ArrayList]<[AppErrorsInfoBean]>
*/
private fun readAllDataFromFiles() = copyOldDataFromResolverString() ?: CopyOnWriteArrayList<AppErrorsInfoBean>().apply {
errorsInfoDataFiles.takeIf { it.isNotEmpty() }?.forEach { it.readText().toEntityOrNull<AppErrorsInfoBean>()?.let { e -> add(e) } }
}
/**
* 添加新的异常记录数据
* @param bean [AppErrorsInfoBean] 实例
*/
fun add(bean: AppErrorsInfoBean) {
allData.add(0, bean)
bean.toJsonOrNull()?.runCatching { File(errorsInfoDataFolder.absolutePath, bean.jsonFileName).writeText(this) }
}
/**
* 移除指定的异常记录数据
* @param bean [AppErrorsInfoBean] 实例
*/
fun remove(bean: AppErrorsInfoBean) {
allData.remove(bean)
runCatching { File(errorsInfoDataFolder.absolutePath, bean.jsonFileName).delete() }
}
/** 清除全部异常记录数据 */
fun clearAll() {
allData.clear()
runCatching { errorsInfoDataFolder.deleteRecursively() }
initializeDataDirectory()
}
}

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -23,13 +23,9 @@
package com.fankes.apperrorstracking.data
import android.content.ContentResolver
import android.content.Context
import android.os.Build
import android.provider.Settings
import android.widget.CompoundButton
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.log.loggerE
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
@@ -39,9 +35,6 @@ import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
*/
object ConfigData {
/** 存取全部应用异常数据的键值名称 */
const val APP_ERRORS_DATA = "app_errors_data"
/** 显示开发者提示 */
val SHOW_DEVELOPER_NOTICE = PrefsData("_show_developer_notice", true)
@@ -60,6 +53,9 @@ object ConfigData {
/** 启用应用配置模板 */
val ENABLE_APP_CONFIG_TEMPLATE = PrefsData("_enable_app_config_template", false)
/** 禁止异常堆栈内容自动换行 */
val DISABLE_AUTO_WRAP_ERROR_STACK_TRACE = PrefsData("_disable_auto_wrap_error_stack_trace", false)
/** 当前实例 - [Context] or [PackageParam] */
private var instance: Any? = null
@@ -73,6 +69,55 @@ object ConfigData {
is Context, is PackageParam -> this.instance = instance
else -> error("Unknown type for init ConfigData")
}
AppErrorsConfigData.refresh()
}
/**
* 读取 [Set]<[String]> 数据
* @param key 键值名称
* @return [Set]<[String]>
*/
internal fun getStringSet(key: String) = when (instance) {
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")
}
/**
* 存入 [Set]<[String]> 数据
* @param key 键值名称
* @param value 键值内容
*/
internal fun putStringSet(key: String, value: Set<String>) {
when (instance) {
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")
}
}
/**
* 读取 [Int] 数据
* @param data 键值数据模板
* @return [Int]
*/
internal fun getInt(data: PrefsData<Int>) = 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")
}
/**
* 存入 [Int] 数据
* @param data 键值数据模板
* @param value 键值内容
*/
internal fun putInt(data: PrefsData<Int>, value: Int) {
when (instance) {
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")
}
}
/**
@@ -80,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")
}
@@ -91,50 +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)
}
}
}
/**
* 获取 [ContentResolver] 字符串数据 (仅限 Hook 进程)
* @param key 键值名称
* @return [String]
*/
fun getResolverString(key: String) =
runCatching { (instance as? PackageParam)?.appContext?.let { Settings.Secure.getString(it.contentResolver, key) } }.getOrNull() ?: ""
/**
* 存入 [ContentResolver] 字符串数据 (仅限 Hook 进程)
* @param key 键值名称
* @param value 键值数据
*/
fun putResolverString(key: String, value: String) {
runCatching {
(instance as? PackageParam)?.appContext?.also { Settings.Secure.putString(it.contentResolver, key, value) }
}.onFailure {
loggerE(msg = "Write secure settings failed", e = it)
}
}
/**
* 是否显示开发者提示
* @return [Boolean]

View File

@@ -0,0 +1,42 @@
/*
* 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/1/20.
*/
package com.fankes.apperrorstracking.data.enum
/**
* 应用配置模版类型定义类
*/
enum class AppErrorsConfigType {
/** 跟随全局配置 */
GLOBAL,
/** 对话框 */
DIALOG,
/** 通知 */
NOTIFY,
/** Toast */
TOAST,
/** 什么也不显示 */
NOTHING
}

View File

@@ -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()
}
}

View File

@@ -1,104 +0,0 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 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 2022/6/8.
*/
@file:Suppress("unused")
package com.fankes.apperrorstracking.data.factory
import android.content.Context
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.param.PackageParam
/**
* 获取此 APP 是否配置显示错误对话框
* @param packageName APP 包名
*/
fun PackageParam.isAppShowErrorsDialog(packageName: String) = prefs.getBoolean("${packageName}_show_errors_dialog", true)
/**
* 获取此 APP 是否配置显示错误通知推送
* @param packageName APP 包名
*/
fun PackageParam.isAppShowErrorsNotify(packageName: String) = prefs.getBoolean("${packageName}_show_errors_notify", false)
/**
* 获取此 APP 是否配置显示错误 Toast 提示
* @param packageName APP 包名
*/
fun PackageParam.isAppShowErrorsToast(packageName: String) = prefs.getBoolean("${packageName}_show_errors_toast", false)
/**
* 获取此 APP 是否配置不显示任何提示
* @param packageName APP 包名
*/
fun PackageParam.isAppShowNothing(packageName: String) = prefs.getBoolean("${packageName}_show_nothing", false)
/**
* 获取此 APP 是否配置显示错误对话框
* @param packageName APP 包名
*/
fun Context.isAppShowErrorsDialog(packageName: String) = modulePrefs.getBoolean("${packageName}_show_errors_dialog", true)
/**
* 获取此 APP 是否配置显示错误通知推送
* @param packageName APP 包名
*/
fun Context.isAppShowErrorsNotify(packageName: String) = modulePrefs.getBoolean("${packageName}_show_errors_notify", false)
/**
* 获取此 APP 是否配置显示错误 Toast 提示
* @param packageName APP 包名
*/
fun Context.isAppShowErrorsToast(packageName: String) = modulePrefs.getBoolean("${packageName}_show_errors_toast", false)
/**
* 获取此 APP 是否配置不显示任何提示
* @param packageName APP 包名
*/
fun Context.isAppShowNothing(packageName: String) = modulePrefs.getBoolean("${packageName}_show_nothing", false)
/**
* 设置此 APP 是否配置显示错误对话框
* @param packageName APP 包名
* @param isApply 是否设置
*/
fun Context.putAppShowErrorsDialog(packageName: String, isApply: Boolean) = modulePrefs.putBoolean("${packageName}_show_errors_dialog", isApply)
/**
* 设置此 APP 是否配置显示错误通知推送
* @param packageName APP 包名
* @param isApply 是否设置
*/
fun Context.putAppShowErrorsNotify(packageName: String, isApply: Boolean) = modulePrefs.putBoolean("${packageName}_show_errors_notify", isApply)
/**
* 设置此 APP 是否配置显示错误 Toast 提示
* @param packageName APP 包名
* @param isApply 是否设置
*/
fun Context.putAppShowErrorsToast(packageName: String, isApply: Boolean) = modulePrefs.putBoolean("${packageName}_show_errors_toast", isApply)
/**
* 设置此 APP 是否配置不显示任何提示
* @param packageName APP 包名
* @param isApply 是否设置
*/
fun Context.putAppShowNothing(packageName: String, isApply: Boolean) = modulePrefs.putBoolean("${packageName}_show_nothing", isApply)

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -30,7 +30,7 @@ import com.highcapable.yukihookapi.hook.factory.encase
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
@InjectYukiHookWithXposed(entryClassName = "AppErrorsTracking", isUsingResourcesHook = false)
class HookEntry : IYukiHookXposedInit {
object HookEntry : IYukiHookXposedInit {
override fun onInit() = configs {
debugLog {
@@ -38,7 +38,7 @@ class HookEntry : IYukiHookXposedInit {
isRecord = true
}
isDebug = false
isEnableModulePrefsCache = false
isEnablePrefsBridgeCache = false
}
override fun onHook() = encase {

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -19,16 +19,18 @@
*
* This file is Created by fankes on 2022/5/7.
*/
@file:Suppress("UseCompatLoadingForDrawables")
package com.fankes.apperrorstracking.hook.entity
import android.app.ApplicationErrorReport
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.os.Build
import android.os.Message
import android.os.SystemClock
import android.util.ArrayMap
import androidx.core.graphics.drawable.IconCompat
import androidx.core.graphics.drawable.toBitmap
import com.fankes.apperrorstracking.BuildConfig
@@ -37,10 +39,11 @@ import com.fankes.apperrorstracking.bean.AppErrorsDisplayBean
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
import com.fankes.apperrorstracking.bean.AppInfoBean
import com.fankes.apperrorstracking.bean.MutedErrorsAppBean
import com.fankes.apperrorstracking.bean.enum.AppFiltersType
import com.fankes.apperrorstracking.data.AppErrorsConfigData
import com.fankes.apperrorstracking.data.AppErrorsRecordData
import com.fankes.apperrorstracking.data.ConfigData
import com.fankes.apperrorstracking.data.factory.isAppShowErrorsNotify
import com.fankes.apperrorstracking.data.factory.isAppShowErrorsToast
import com.fankes.apperrorstracking.data.factory.isAppShowNothing
import com.fankes.apperrorstracking.data.enum.AppErrorsConfigType
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsDisplayActivity
import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsRecordActivity
@@ -51,12 +54,12 @@ import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.hasMethod
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.loggerD
import com.highcapable.yukihookapi.hook.log.loggerE
import com.highcapable.yukihookapi.hook.log.loggerI
import com.highcapable.yukihookapi.hook.log.loggerW
import com.highcapable.yukihookapi.hook.type.android.BundleClass
import com.highcapable.yukihookapi.hook.type.android.MessageClass
import com.highcapable.yukihookapi.hook.type.java.BooleanType
object FrameworkHooker : YukiBaseHooker() {
@@ -72,23 +75,98 @@ object FrameworkHooker : YukiBaseHooker() {
"com.android.server.am.ProcessRecord\$PackageList",
"com.android.server.am.PackageList"
)
private val ErrorDialogControllerClass = VariousClass(
"com.android.server.am.ProcessRecord\$ErrorDialogController",
"com.android.server.am.ErrorDialogController"
)
/** 已记录的 APP 用户 ID */
private var appUserIdRecords = HashMap<Int, Int>()
/** 已忽略错误的 APP 数组 - 直到重新解锁 */
private var mutedErrorsIfUnlockApps = HashSet<String>()
/** 已忽略错误的 APP 数组 - 直到重新启动 */
private var mutedErrorsIfRestartApps = HashSet<String>()
/** 已记录的 APP 异常信息数组 */
private var appErrorsRecords = ArrayList<AppErrorsInfoBean>()
/**
* APP 进程异常数据定义类
* @param errors [AppErrorsClass] 实例
* @param proc [ProcessRecordClass] 实例
* @param resultData [AppErrorDialog_DataClass] 实例 - 默认空
*/
private class AppErrorsProcessData(errors: Any?, proc: Any?, resultData: Any? = null) {
/**
* 获取当前包列表实例
* @return [Any] or null
*/
private val pkgList = if (ProcessRecordClass.toClass().hasMethod { name = "getPkgList"; emptyParam() })
ProcessRecordClass.toClass().method { name = "getPkgList"; emptyParam() }.get(proc).call()
else ProcessRecordClass.toClass().field { name = "pkgList" }.get(proc).any()
/**
* 获取当前包列表数组大小
* @return [Int]
*/
private val pkgListSize = PackageListClass.toClassOrNull()?.method { name = "size"; emptyParam() }?.get(pkgList)?.int()
?: ProcessRecordClass.toClass().field { name = "pkgList" }.get(proc).cast<ArrayMap<*, *>>()?.size ?: -1
/**
* 获取当前 pid 信息
* @return [Int]
*/
val pid = ProcessRecordClass.toClass().field { name { it == "mPid" || it == "pid" } }.get(proc).int()
/**
* 获取当前用户 ID 信息
* @return [Int]
*/
val userId = ProcessRecordClass.toClass().field { name = "userId" }.get(proc).int()
/**
* 获取当前 APP 信息
* @return [ApplicationInfo] or null
*/
val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(proc).cast<ApplicationInfo>()
/**
* 获取当前进程名称
* @return [String]
*/
val processName = ProcessRecordClass.toClass().field { name = "processName" }.get(proc).string()
/**
* 获取当前 APP、进程 包名
* @return [String]
*/
val packageName = appInfo?.packageName ?: processName
/**
* 获取当前进程是否为可被启动的 APP - 非框架 APP
* @return [Boolean]
*/
val isActualApp = pkgListSize == 1 && appInfo != null
/**
* 获取当前进程是否为主进程
* @return [Boolean]
*/
val isMainProcess = packageName == processName
/**
* 获取当前进程是否为后台进程
* @return [Boolean]
*/
val isBackgroundProcess = UserControllerClass.toClass()
.method { name { it == "getCurrentProfileIds" || it == "getCurrentProfileIdsLocked" } }
.get(ActivityManagerServiceClass.toClass().field { name = "mUserController" }
.get(AppErrorsClass.toClass().field { name = "mService" }.get(errors).any()).any())
.invoke<IntArray>()?.takeIf { it.isNotEmpty() }?.any { it != userId } ?: false
/**
* 获取当前进程是否短时内重复崩溃
* @return [Boolean]
*/
val isRepeatingCrash = resultData?.let { AppErrorDialog_DataClass.toClass().field { name = "repeating" }.get(it).boolean() } ?: false
}
/** 注册生命周期 */
private fun registerLifecycle() {
@@ -97,30 +175,35 @@ object FrameworkHooker : YukiBaseHooker() {
registerReceiver(Intent.ACTION_USER_PRESENT) { _, _ -> mutedErrorsIfUnlockApps.clear() }
/** 刷新模块 Resources 缓存 */
registerReceiver(Intent.ACTION_LOCALE_CHANGED) { _, _ -> refreshModuleAppResources() }
/** 启动时从本地获取异常记录 */
onCreate { appErrorsRecords = ConfigData.getResolverString(ConfigData.APP_ERRORS_DATA).toEntity() ?: arrayListOf() }
/** 启动时从本地获取异常记录数据 */
onCreate { AppErrorsRecordData.init(context = this) }
}
FrameworkTool.Host.with(instance = this) {
onRefreshFrameworkPrefsData {
/** 必要的延迟防止 Sp 存储不刷新 */
SystemClock.sleep(100)
/** 刷新存储类 */
AppErrorsConfigData.refresh()
if (prefs.isPreferencesAvailable.not()) loggerW(msg = "Cannot refreshing app errors config data, preferences is not available")
}
onOpenAppUsedFramework {
appContext?.openApp(it.first, it.second)
loggerI(msg = "Opened \"${it.first}\"${it.second.takeIf { e -> e > 0 }?.let { e -> " --user $e" } ?: ""}")
}
onPushAppErrorInfoData {
appErrorsRecords.firstOrNull { e -> e.pid == it } ?: run {
AppErrorsRecordData.allData.firstOrNull { e -> e.pid == it } ?: run {
loggerW(msg = "Cannot received crash application data --pid $it")
AppErrorsInfoBean.createEmpty()
AppErrorsInfoBean()
}
}
onPushAppErrorsInfoData { appErrorsRecords }
onPushAppErrorsInfoData { AppErrorsRecordData.allData.toArrayList() }
onRemoveAppErrorsInfoData {
loggerI(msg = "Removed app errors info data for package \"${it.packageName}\"")
appErrorsRecords.remove(it)
saveAllAppErrorsRecords()
AppErrorsRecordData.remove(it)
}
onClearAppErrorsInfoData {
loggerI(msg = "Cleared all app errors info data, size ${appErrorsRecords.size}")
appErrorsRecords.clear()
saveAllAppErrorsRecords()
loggerI(msg = "Cleared all app errors info data, size ${AppErrorsRecordData.allData.size}")
AppErrorsRecordData.clearAll()
}
onMutedErrorsIfUnlock {
mutedErrorsIfUnlockApps.add(it)
@@ -157,28 +240,123 @@ object FrameworkHooker : YukiBaseHooker() {
}
onPushAppListData { filters ->
appContext?.let { context ->
arrayListOf<AppInfoBean>().apply {
context.listOfPackages().also { info ->
(if (filters.name.isNotBlank())
info.filter { it.packageName.contains(filters.name) || context.appNameOf(it.packageName).contains(filters.name) }
else info).let { result ->
if (filters.isContainsSystem.not())
result.filter { (it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) == 0 }
else result
}.sortedByDescending { it.lastUpdateTime }
.forEach { add(AppInfoBean(name = context.appNameOf(it.packageName), packageName = it.packageName)) }
/** 移除模块自身 */
removeIf { it.packageName == BuildConfig.APPLICATION_ID }
context.listOfPackages()
.filter { it.packageName.let { e -> e != "android" && e != BuildConfig.APPLICATION_ID } }
.let { info ->
arrayListOf<AppInfoBean>().apply {
if (info.isNotEmpty())
(if (filters.name.isNotBlank()) info.filter {
it.packageName.contains(filters.name) || context.appNameOf(it.packageName).contains(filters.name)
} else info).let { result ->
/**
* 是否为系统应用
* @return [Boolean]
*/
fun PackageInfo.isSystemApp() = (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0
when (filters.type) {
AppFiltersType.USER -> result.filter { it.isSystemApp().not() }
AppFiltersType.SYSTEM -> result.filter { it.isSystemApp() }
AppFiltersType.ALL -> result
}
}.sortedByDescending { it.lastUpdateTime }
.forEach { add(AppInfoBean(name = context.appNameOf(it.packageName), packageName = it.packageName)) }
else loggerW(msg = "Fetched installed packages but got empty list")
}
}
loggerD(msg = "Fetched installed packages list, size $size")
}
} ?: arrayListOf()
}
}
}
/** 保存异常记录到本地 */
private fun saveAllAppErrorsRecords() = newThread { ConfigData.putResolverString(ConfigData.APP_ERRORS_DATA, appErrorsRecords.toJson()) }
/**
* 处理 APP 进程异常信息展示
* @param context 当前实例
*/
private fun AppErrorsProcessData.handleShowAppErrorUi(context: Context) {
/** 当前 APP 名称 */
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
/** 崩溃标题 */
val errorTitle = if (isRepeatingCrash) LocaleString.aerrRepeatedTitle(appNameWithUserId) else LocaleString.aerrTitle(appNameWithUserId)
/** 使用通知推送异常信息 */
fun showAppErrorsWithNotify() =
context.pushNotify(
channelId = "APPS_ERRORS",
channelName = LocaleString.appName,
title = errorTitle,
content = LocaleString.appErrorsTip,
icon = IconCompat.createWithBitmap(moduleAppResources.drawableOf(R.drawable.ic_notify).toBitmap()),
color = 0xFFFF6200.toInt(),
intent = AppErrorsRecordActivity.intent()
)
/** 使用 Toast 展示异常信息 */
fun showAppErrorsWithToast() = context.toast(errorTitle)
/** 使用对话框展示异常信息 */
fun showAppErrorsWithDialog() =
AppErrorsDisplayActivity.start(
context, AppErrorsDisplayBean(
pid = pid,
userId = userId,
packageName = packageName,
processName = processName,
appName = appName,
title = errorTitle,
isShowAppInfoButton = isActualApp,
isShowReopenButton = isActualApp &&
(isRepeatingCrash.not() || ConfigData.isEnableAlwaysShowsReopenAppOptions) &&
context.isAppCanOpened(packageName) &&
isMainProcess,
isShowCloseAppButton = isActualApp
)
)
/** 判断是否为已忽略的 APP */
if (mutedErrorsIfUnlockApps.contains(packageName) || mutedErrorsIfRestartApps.contains(packageName)) return
/** 判断是否为后台进程 */
if ((isBackgroundProcess || context.isAppCanOpened(packageName).not()) && ConfigData.isEnableOnlyShowErrorsInFront) return
/** 判断是否为主进程 */
if (isMainProcess.not() && ConfigData.isEnableOnlyShowErrorsInMain) return
when {
packageName == BuildConfig.APPLICATION_ID -> {
context.toast(msg = "AppErrorsTracking has crashed, please see the log in console")
loggerE(msg = "AppErrorsTracking has crashed itself, please see the Android Runtime Exception in console")
}
ConfigData.isEnableAppConfigTemplate -> when {
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.GLOBAL, packageName) -> when {
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.DIALOG) -> showAppErrorsWithDialog()
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.NOTIFY) -> showAppErrorsWithNotify()
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.TOAST) -> showAppErrorsWithToast()
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.NOTHING) -> {}
}
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.DIALOG, packageName) -> showAppErrorsWithDialog()
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.NOTIFY, packageName) -> showAppErrorsWithNotify()
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.TOAST, packageName) -> showAppErrorsWithToast()
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.NOTHING, packageName) -> {}
}
else -> showAppErrorsWithDialog()
}
/** 打印错误日志 */
if (isActualApp) loggerE(
msg = "Application \"$packageName\" ${if (isRepeatingCrash) "keeps stopping" else "has stopped"}" +
(if (packageName != processName) " --process \"$processName\"" else "") +
"${if (userId != 0) " --user $userId" else ""} --pid $pid"
) else loggerE(msg = "Process \"$processName\" ${if (isRepeatingCrash) "keeps stopping" else "has stopped"} --pid $pid")
}
/**
* 处理 APP 进程异常数据
* @param context 当前实例
* @param info 系统错误报告数据实例
*/
private fun AppErrorsProcessData.handleAppErrorsInfo(context: Context, info: ApplicationErrorReport.CrashInfo?) {
AppErrorsRecordData.add(AppErrorsInfoBean.clone(context, pid, userId, appInfo?.packageName, info))
loggerI(msg = "Received crash application data${if (userId != 0) " --user $userId" else ""} --pid $pid")
}
override fun onHook() {
/** 注册生命周期 */
@@ -201,15 +379,26 @@ object FrameworkHooker : YukiBaseHooker() {
}
}.ignoredHookClassNotFoundFailure()
/** 干掉原生错误对话框 - API 30 以下 */
ActivityTaskManagerService_LocalServiceClass.hook {
injectMember {
method {
name = "canShowErrorDialogs"
emptyParam()
}
replaceToFalse()
}
}.by { Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q }
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
ActivityTaskManagerService_LocalServiceClass.hook {
injectMember {
method {
name = "canShowErrorDialogs"
emptyParam()
}
replaceToFalse()
}.ignoredNoSuchMemberFailure()
}.ignoredHookClassNotFoundFailure()
ActivityManagerServiceClass.hook {
injectMember {
method {
name = "canShowErrorDialogs"
emptyParam()
}
replaceToFalse()
}.ignoredNoSuchMemberFailure()
}.ignoredHookClassNotFoundFailure()
}
/** 干掉原生错误对话框 - 如果上述方法全部失效则直接结束对话框 */
AppErrorDialogClass.hook {
injectMember {
@@ -218,7 +407,14 @@ object FrameworkHooker : YukiBaseHooker() {
param(BundleClass)
}
afterHook { instance<Dialog>().cancel() }
}
}.ignoredNoSuchMemberFailure()
injectMember {
method {
name = "onStart"
emptyParam()
}
afterHook { instance<Dialog>().cancel() }
}.ignoredNoSuchMemberFailure()
}
/** 注入自定义错误对话框 */
AppErrorsClass.hook {
@@ -231,143 +427,28 @@ object FrameworkHooker : YukiBaseHooker() {
/** 当前实例 */
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: return@afterHook
/** 错误数据 */
val errData = args().first().cast<Message>()?.obj
/** 当前错误数据 */
val resultData = args().first().cast<Message>()?.obj
/** 当前进程信息 */
val proc = AppErrorDialog_DataClass.toClass().field { name = "proc" }.get(errData).any()
/** 当前 pid 信息 */
val pid = ProcessRecordClass.toClass().field { name { it == "mPid" || it == "pid" } }.get(proc).int()
/** 当前用户 ID 信息 */
val userId = ProcessRecordClass.toClass().field { name = "userId" }.get(proc).int()
/** 当前 APP 信息 */
val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(proc).cast<ApplicationInfo>()
/** 当前进程名称 */
val processName = ProcessRecordClass.toClass().field { name = "processName" }.get(proc).string()
/** 当前 APP、进程 包名 */
val packageName = appInfo?.packageName ?: processName
/** 当前 APP 名称 */
val appName = appInfo?.let { context.appNameOf(it.packageName) } ?: packageName
/** 是否为 APP */
val isApp = (PackageListClass.toClass().method {
name = "size"
emptyParam()
}.get(if (ProcessRecordClass.toClass().hasMethod {
name = "getPkgList"
emptyParam()
}) ProcessRecordClass.toClass().method {
name = "getPkgList"
emptyParam()
}.get(proc).call() else ProcessRecordClass.toClass().field {
name = "pkgList"
}.get(proc).any()).int() == 1 && appInfo != null)
/** 是否为主进程 */
val isMainProcess = packageName == processName
/** 是否为后台进程 */
val isBackgroundProcess = UserControllerClass.toClass()
.method { name = "getCurrentProfileIds" }
.get(ActivityManagerServiceClass.toClass().field { name = "mUserController" }
.get(field { name = "mService" }.get(instance).any()).any())
.invoke<IntArray>()?.takeIf { it.isNotEmpty() }?.any { it != userId } ?: false
/** 是否短时内重复错误 */
val isRepeating = AppErrorDialog_DataClass.toClass().field { name = "repeating" }.get(errData).boolean()
/** 崩溃标题 */
val errorTitle = if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName)
/** 写入到用户 ID 记录 */
appUserIdRecords[pid] = userId
/** 打印错误日志 */
if (isApp) loggerE(
msg = "Application \"$packageName\" ${if (isRepeating) "keeps stopping" else "has stopped"}" +
(if (packageName != processName) " --process \"$processName\"" else "") +
"${if (userId != 0) " --user $userId" else ""} --pid $pid"
) else loggerE(msg = "Process \"$processName\" ${if (isRepeating) "keeps stopping" else "has stopped"} --pid $pid")
/** 判断是否为模块自身崩溃 */
if (packageName == BuildConfig.APPLICATION_ID) {
context.toast(msg = "AppErrorsTracking has crashed, please see the log in console")
loggerE(msg = "AppErrorsTracking has crashed itself, please see the Android Runtime Exception in console")
return@afterHook
}
/** 判断是否为已忽略的 APP */
if (mutedErrorsIfUnlockApps.contains(packageName) || mutedErrorsIfRestartApps.contains(packageName)) return@afterHook
/** 判断配置模块启用状态 */
if (ConfigData.isEnableAppConfigTemplate) {
if (isAppShowNothing(packageName)) return@afterHook
if (isAppShowErrorsNotify(packageName)) {
context.pushNotify(
channelId = "APPS_ERRORS",
channelName = LocaleString.appName,
title = errorTitle,
content = LocaleString.appErrorsTip,
icon = IconCompat.createWithBitmap(moduleAppResources.drawableOf(R.drawable.ic_notify).toBitmap()),
color = 0xFFFF6200.toInt(),
intent = AppErrorsRecordActivity.intent()
)
return@afterHook
}
if (isAppShowErrorsToast(packageName)) {
context.toast(errorTitle)
return@afterHook
}
}
/** 判断是否为后台进程 */
if ((isBackgroundProcess || context.isAppCanOpened(packageName).not()) && ConfigData.isEnableOnlyShowErrorsInFront)
return@afterHook
/** 判断是否为主进程 */
if (isMainProcess.not() && ConfigData.isEnableOnlyShowErrorsInMain) return@afterHook
/** 启动错误对话框显示窗口 */
AppErrorsDisplayActivity.start(
context, AppErrorsDisplayBean(
pid = pid,
userId = userId,
packageName = packageName,
processName = processName,
appName = appName,
title = errorTitle,
isShowAppInfoButton = isApp,
isShowReopenButton = isApp && (isRepeating.not() || ConfigData.isEnableAlwaysShowsReopenAppOptions)
&& context.isAppCanOpened(packageName) && isMainProcess,
isShowCloseAppButton = isApp
)
)
val proc = AppErrorDialog_DataClass.toClass().field { name = "proc" }.get(resultData).any()
/** 创建 APP 进程异常数据类 */
AppErrorsProcessData(instance, proc, resultData).handleShowAppErrorUi(context)
}
}
injectMember {
method {
name = "crashApplication"
paramCount = 2
name = "handleAppCrashInActivityController"
returnType = BooleanType
}
afterHook {
/** 当前实例 */
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: return@afterHook
/** 当前进程信息 */
val proc = args().first().any() ?: return@afterHook loggerW(msg = "Received but got null ProcessRecord")
/** 当前 pid 信息 */
val pid = ProcessRecordClass.toClass().field { name { it == "mPid" || it == "pid" } }.get(proc).int()
/** 当前 APP 信息 */
val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(proc).cast<ApplicationInfo>()
/** 启动新线程延迟防止方法执行顺序在前导致无法正确获取数据 */
newThread {
/** 延迟 50ms */
Thread.sleep(50)
/** 添加当前异常信息到第一位 */
appErrorsRecords.add(
0, AppErrorsInfoBean.clone(pid, appInfo?.packageName, appUserIdRecords[pid], args().last().cast())
)
loggerI(msg = "Received crash application data --pid $pid")
}
/** 保存异常记录到本地 */
saveAllAppErrorsRecords()
/** 创建 APP 进程异常数据类 */
AppErrorsProcessData(instance, proc).handleAppErrorsInfo(context, args(index = 1).cast())
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -502,4 +502,64 @@ object LocaleString {
/** @string Automatic generated */
fun exportAllLogsFail(vararg objArrs: Any) = R.string.export_all_logs_fail.bind(*objArrs)
/** @string Automatic generated */
val goItNow get() = goItNow()
/** @string Automatic generated */
fun goItNow(vararg objArrs: Any) = R.string.go_it_now.bind(*objArrs)
/** @string Automatic generated */
val accessRootFailTip get() = accessRootFailTip()
/** @string Automatic generated */
fun accessRootFailTip(vararg objArrs: Any) = R.string.access_root_fail_tip.bind(*objArrs)
/** @string Automatic generated */
val globalConfig get() = globalConfig()
/** @string Automatic generated */
fun globalConfig(vararg objArrs: Any) = R.string.global_config.bind(*objArrs)
/** @string Automatic generated */
val followGlobalConfig get() = followGlobalConfig()
/** @string Automatic generated */
fun followGlobalConfig(vararg objArrs: Any) = R.string.follow_global_config.bind(*objArrs)
/** @string Automatic generated */
val batchOperationsNumber get() = batchOperationsNumber()
/** @string Automatic generated */
fun batchOperationsNumber(vararg objArrs: Any) = R.string.batch_operations_number.bind(*objArrs)
/** @string Automatic generated */
val clickToUpdate get() = clickToUpdate()
/** @string Automatic generated */
fun clickToUpdate(vararg objArrs: Any) = R.string.click_to_update.bind(*objArrs)
/** @string Automatic generated */
val latestVersion get() = latestVersion()
/** @string Automatic generated */
fun latestVersion(vararg objArrs: Any) = R.string.latest_version.bind(*objArrs)
/** @string Automatic generated */
val latestVersionTip get() = latestVersionTip()
/** @string Automatic generated */
fun latestVersionTip(vararg objArrs: Any) = R.string.latest_version_tip.bind(*objArrs)
/** @string Automatic generated */
val updateNow get() = updateNow()
/** @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)
}

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -23,6 +23,8 @@
package com.fankes.apperrorstracking.ui.activity.base
import android.app.ActivityManager
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
@@ -67,6 +69,24 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
/** 回调 [onCreate] 方法 */
abstract fun onCreate()
/**
* 在旧版的 Android 系统中使用了 activity-alias 标签从启动器启动 Activity 会造成其组件名称 (完整类名) 为代理名称
*
* 为了获取真实的顶层 Activity 组件名称 (完整类名) - 如果名称不正确将自动执行一次结束并重新打开当前 Activity
*/
fun checkingTopComponentName() {
/** 当前顶层的 Activity 组件名称 (完整类名) */
val topComponentName = runCatching {
@Suppress("DEPRECATION")
(getSystemService(ACTIVITY_SERVICE) as? ActivityManager?)
?.getRunningTasks(9999)?.firstOrNull()?.topActivity?.className ?: ""
}.getOrNull() ?: ""
if (topComponentName.isNotBlank() && topComponentName != javaClass.name) {
finish()
startActivity(Intent(this, javaClass))
}
}
/**
* 弹出提示并退出
* @param name 名称

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -38,7 +38,6 @@ import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
import com.fankes.apperrorstracking.utils.factory.*
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.factory.dataChannel
import com.highcapable.yukihookapi.hook.log.YukiHookLogger
import com.highcapable.yukihookapi.hook.log.YukiLoggerData
@@ -137,26 +136,6 @@ class LoggerActivity : BaseActivity<ActivitiyLoggerBinding>() {
}
}
/**
* 获取当前所有日志写出文件的格式
*
* 复制自 [YukiHookLogger.contents]
* @return [String]
*/
private val loggerContents: String
get() {
var content = ""
listData.takeIf { it.isNotEmpty() }?.forEach {
val head = "${it.time} ------ "
content += "$head$it\n"
it.throwable?.also { e ->
content += "${head}Dump stack trace for \"${e.current().name}\":\n"
content += e.toStackTrace()
}
}
return content
}
/**
* 格式化为本地时间格式
* @return [String]
@@ -167,7 +146,7 @@ class LoggerActivity : BaseActivity<ActivitiyLoggerBinding>() {
* 格式化消息字符串样式
* @return [String]
*/
private fun String.format() = replace(oldValue = "--", newValue = "\n--")
private fun String.format() = replace("--", "\n--")
/**
* 获取完整的异常堆栈内容
@@ -193,7 +172,7 @@ class LoggerActivity : BaseActivity<ActivitiyLoggerBinding>() {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) runCatching {
data?.data?.let {
contentResolver?.openOutputStream(it)?.apply { write(loggerContents.toByteArray()) }?.close()
contentResolver?.openOutputStream(it)?.apply { write(YukiHookLogger.contents(listData).toByteArray()) }?.close()
toast(LocaleString.exportAllLogsSuccess)
} ?: toast(LocaleString.exportAllLogsFail)
}.onFailure { toast(LocaleString.exportAllLogsFail) }

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -26,10 +26,13 @@ package com.fankes.apperrorstracking.ui.activity.errors
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.widget.TextView
import androidx.core.view.isGone
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.factory.bind
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDetailBinding
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
@@ -70,6 +73,11 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
cancel()
finish()
}
cancelButton(LocaleString.goItNow) {
cancel()
finish()
navigate<AppErrorsRecordActivity>()
}
noCancelable()
}
return
@@ -98,11 +106,11 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
}, LocaleString.shareErrorStack))
}
binding.appIcon.setImageDrawable(appIconOf(appErrorsInfo.packageName))
binding.appNameText.text = appNameOf(appErrorsInfo.packageName)
binding.appVersionText.text = appVersionBrandOf(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)
binding.appAbiText.text = appCpuAbiOf(appErrorsInfo.packageName).ifBlank { LocaleString.noCpuAbi }
binding.appCpuAbiText.text = appErrorsInfo.cpuAbi.ifBlank { LocaleString.noCpuAbi }
binding.jvmErrorPanel.isGone = appErrorsInfo.isNativeCrash
binding.errorTypeIcon.setImageResource(if (appErrorsInfo.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
binding.errorInfoText.text = appErrorsInfo.exceptionMessage
@@ -112,11 +120,33 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
binding.errorThrowMethodText.text = appErrorsInfo.throwMethodName
binding.errorLineNumberText.text = appErrorsInfo.throwLineNumber.toString()
binding.errorRecordTimeText.text = appErrorsInfo.dateTime
binding.errorStackText.text = appErrorsInfo.stackTrace
binding.errorStackTraceMovableText.text = appErrorsInfo.stackTrace
binding.errorStackTraceFixedText.text = appErrorsInfo.stackTrace
binding.disableAutoWrapErrorStackTraceSwitch.bind(ConfigData.DISABLE_AUTO_WRAP_ERROR_STACK_TRACE) {
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()
}
/** 修复在一些小屏设备上设置了 [TextView.setTextIsSelectable] 后布局自动上滑问题 */
private fun resetScrollView() {
binding.rootView.post {
binding.appPanelScrollView.scrollTo(0, 0)
binding.errorStackTraceScrollView.scrollTo(0, 0)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -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

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -35,6 +35,7 @@ import com.fankes.apperrorstracking.BuildConfig
import com.fankes.apperrorstracking.R
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
import com.fankes.apperrorstracking.bean.AppFiltersBean
import com.fankes.apperrorstracking.bean.enum.AppFiltersType
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsRecordBinding
import com.fankes.apperrorstracking.databinding.AdapterAppErrorsRecordBinding
import com.fankes.apperrorstracking.databinding.DiaAppErrorsStatisticsBinding
@@ -76,7 +77,7 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
title = LocaleString.notice
progressContent = LocaleString.generatingStatistics
noCancelable()
FrameworkTool.fetchAppListData(context, AppFiltersBean(isContainsSystem = true)) {
FrameworkTool.fetchAppListData(context, AppFiltersBean(type = AppFiltersType.ALL)) {
newThread {
val errorsApps = listData.groupBy { it.packageName }
.map { it.key to it.value.size }
@@ -95,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)
@@ -133,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
@@ -151,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()
}
}
@@ -193,7 +196,7 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
* @return [String]
*/
private fun String.simpleThwName() =
let { text -> if (text.contains(other = ".")) text.split(".").let { e -> e[e.lastIndex] } else text }
let { text -> if (text.contains(".")) text.split(".").let { e -> e[e.lastIndex] } else text }
override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) {
menuInflater.inflate(R.menu.menu_list_detail_action, menu)

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -24,7 +24,9 @@ package com.fankes.apperrorstracking.ui.activity.main
import androidx.core.view.isVisible
import com.fankes.apperrorstracking.bean.AppFiltersBean
import com.fankes.apperrorstracking.bean.AppInfoBean
import com.fankes.apperrorstracking.data.factory.*
import com.fankes.apperrorstracking.bean.enum.AppFiltersType
import com.fankes.apperrorstracking.data.AppErrorsConfigData
import com.fankes.apperrorstracking.data.enum.AppErrorsConfigType
import com.fankes.apperrorstracking.databinding.ActivityConfigBinding
import com.fankes.apperrorstracking.databinding.AdapterAppInfoBinding
import com.fankes.apperrorstracking.databinding.DiaAppConfigBinding
@@ -50,36 +52,31 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
override fun onCreate() {
binding.titleBackIcon.setOnClickListener { finish() }
binding.globalIcon.setOnClickListener {
showAppConfigDialog(LocaleString.globalConfig, isShowGlobalConfig = false) { type ->
AppErrorsConfigData.putAppShowingType(type)
onChanged?.invoke()
}
}
binding.batchIcon.setOnClickListener {
showDialog<DiaAppConfigBinding> {
title = LocaleString.batchOperations
confirmButton {
val config0 = binding.configRadio0.isChecked
val config1 = binding.configRadio1.isChecked
val config2 = binding.configRadio2.isChecked
val config3 = binding.configRadio3.isChecked
showDialog {
title = LocaleString.notice
msg = LocaleString.areYouSureApplySiteApps(listData.size)
confirmButton {
listData.takeIf { it.isNotEmpty() }?.forEach {
putAppShowErrorsDialog(it.packageName, config0)
putAppShowErrorsNotify(it.packageName, config1)
putAppShowErrorsToast(it.packageName, config2)
putAppShowNothing(it.packageName, config3)
}
onChanged?.invoke()
}
cancelButton()
showAppConfigDialog(LocaleString.batchOperationsNumber(listData.size), isNotSetDefaultValue = true) { type ->
showDialog {
title = LocaleString.notice
msg = LocaleString.areYouSureApplySiteApps(listData.size)
confirmButton {
listData.takeIf { it.isNotEmpty() }?.forEach { AppErrorsConfigData.putAppShowingType(type, it.packageName) }
onChanged?.invoke()
}
cancelButton()
}
cancelButton()
}
}
binding.filterIcon.setOnClickListener {
showDialog<DiaAppsFilterBinding> {
title = LocaleString.filterByCondition
binding.containsSystemSwitch.isChecked = appFilters.isContainsSystem
binding.filtersRadioUser.isChecked = appFilters.type == AppFiltersType.USER
binding.filtersRadioSystem.isChecked = appFilters.type == AppFiltersType.SYSTEM
binding.filtersRadioAll.isChecked = appFilters.type == AppFiltersType.ALL
binding.appFiltersEdit.apply {
requestFocus()
invalidate()
@@ -88,15 +85,24 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
setSelection(appFilters.name.length)
}
}
/** 设置 [AppFiltersBean.type] */
fun setAppFiltersType() {
appFilters.type = when {
binding.filtersRadioUser.isChecked -> AppFiltersType.USER
binding.filtersRadioSystem.isChecked -> AppFiltersType.SYSTEM
binding.filtersRadioAll.isChecked -> AppFiltersType.ALL
else -> error("Invalid app filters type")
}
}
confirmButton {
appFilters.isContainsSystem = binding.containsSystemSwitch.isChecked
setAppFiltersType()
appFilters.name = binding.appFiltersEdit.text.toString().trim()
refreshData()
}
cancelButton()
if (appFilters.name.isNotBlank())
neutralButton(LocaleString.clearFilters) {
appFilters.isContainsSystem = binding.containsSystemSwitch.isChecked
setAppFiltersType()
appFilters.name = ""
refreshData()
}
@@ -110,10 +116,11 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
binding.appIcon.setImageDrawable(bean.icon)
binding.appNameText.text = bean.name
binding.configTypeText.text = when {
isAppShowErrorsDialog(bean.packageName) -> LocaleString.showErrorsDialog
isAppShowErrorsNotify(bean.packageName) -> LocaleString.showErrorsNotify
isAppShowErrorsToast(bean.packageName) -> LocaleString.showErrorsToast
isAppShowNothing(bean.packageName) -> LocaleString.showNothing
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.GLOBAL, bean.packageName) -> LocaleString.followGlobalConfig
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.DIALOG, bean.packageName) -> LocaleString.showErrorsDialog
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.NOTIFY, bean.packageName) -> LocaleString.showErrorsNotify
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.TOAST, bean.packageName) -> LocaleString.showErrorsToast
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.NOTHING, bean.packageName) -> LocaleString.showNothing
else -> "Unknown type"
}
}
@@ -121,20 +128,9 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
}.apply { onChanged = { notifyDataSetChanged() } }
setOnItemClickListener { _, _, p, _ ->
listData[p].also { bean ->
showDialog<DiaAppConfigBinding> {
title = bean.name
binding.configRadio0.isChecked = isAppShowErrorsDialog(bean.packageName)
binding.configRadio1.isChecked = isAppShowErrorsNotify(bean.packageName)
binding.configRadio2.isChecked = isAppShowErrorsToast(bean.packageName)
binding.configRadio3.isChecked = isAppShowNothing(bean.packageName)
confirmButton {
putAppShowErrorsDialog(bean.packageName, binding.configRadio0.isChecked)
putAppShowErrorsNotify(bean.packageName, binding.configRadio1.isChecked)
putAppShowErrorsToast(bean.packageName, binding.configRadio2.isChecked)
putAppShowNothing(bean.packageName, binding.configRadio3.isChecked)
onChanged?.invoke()
}
cancelButton()
showAppConfigDialog(bean.name, bean.packageName) { type ->
AppErrorsConfigData.putAppShowingType(type, bean.packageName)
onChanged?.invoke()
}
}
}
@@ -152,9 +148,53 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
refreshData()
}
/**
* 显示应用配置对话框
* @param title 对话框标题
* @param packageName APP 包名 - 默认空 (空时使用全局配置的默认值)
* @param isNotSetDefaultValue 是否不设置选项的默认值 - 默认否
* @param isShowGlobalConfig 是否显示跟随全局配置选项 - 默认是
* @param result 回调类型结果
*/
private fun showAppConfigDialog(
title: String,
packageName: String = "",
isNotSetDefaultValue: Boolean = false,
isShowGlobalConfig: Boolean = true,
result: (AppErrorsConfigType) -> Unit
) {
showDialog<DiaAppConfigBinding> {
this.title = title
binding.configRadio0.isVisible = isShowGlobalConfig
if (isNotSetDefaultValue.not()) {
if (isShowGlobalConfig) binding.configRadio0.isChecked =
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.GLOBAL, packageName)
binding.configRadio1.isChecked = AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.DIALOG, packageName)
binding.configRadio2.isChecked = AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.NOTIFY, packageName)
binding.configRadio3.isChecked = AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.TOAST, packageName)
binding.configRadio4.isChecked = AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.NOTHING, packageName)
}
confirmButton {
result(
when {
binding.configRadio0.isChecked -> AppErrorsConfigType.GLOBAL
binding.configRadio1.isChecked -> AppErrorsConfigType.DIALOG
binding.configRadio2.isChecked -> AppErrorsConfigType.NOTIFY
binding.configRadio3.isChecked -> AppErrorsConfigType.TOAST
binding.configRadio4.isChecked -> AppErrorsConfigType.NOTHING
else -> error("Invalid config type")
}
)
FrameworkTool.refreshFrameworkPrefsData(context)
}
cancelButton()
}
}
/** 刷新列表数据 */
private fun refreshData() {
binding.listProgressView.isVisible = true
binding.globalIcon.isVisible = false
binding.batchIcon.isVisible = false
binding.filterIcon.isVisible = false
binding.listView.isVisible = false
@@ -176,6 +216,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
onChanged?.invoke()
binding.listView.post { binding.listView.setSelection(0) }
binding.listProgressView.isVisible = false
binding.globalIcon.isVisible = true
binding.batchIcon.isVisible = listData.isNotEmpty()
binding.filterIcon.isVisible = true
binding.listView.isVisible = listData.isNotEmpty()

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -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
@@ -38,6 +38,7 @@ import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsRecordActivity
import com.fankes.apperrorstracking.utils.factory.*
import com.fankes.apperrorstracking.utils.tool.AppAnalyticsTool.bindAppAnalytics
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
import com.fankes.apperrorstracking.utils.tool.GithubReleaseTool
import com.highcapable.yukihookapi.YukiHookAPI
class MainActivity : BaseActivity<ActivityMainBinding>() {
@@ -52,21 +53,33 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
override fun onCreate() {
checkingTopComponentName()
/** 检查更新 */
GithubReleaseTool.checkingForUpdate(context = this, BuildConfig.VERSION_NAME) { version, function ->
binding.mainTextReleaseVersion.apply {
text = LocaleString.clickToUpdate(version)
isVisible = true
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()
/** 系统版本点击事件 */
@@ -88,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)
}
}
/** 刷新模块状态 */
@@ -113,14 +124,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
else -> R.drawable.ic_warn
}
)
binding.mainTextStatus.text =
when {
YukiHookAPI.Status.isXposedModuleActive && isModuleValied.not() -> LocaleString.moduleNotFullyActivated
YukiHookAPI.Status.isXposedModuleActive -> LocaleString.moduleIsActivated
else -> LocaleString.moduleNotActivated
}
binding.mainTextStatus.text = when {
YukiHookAPI.Status.isXposedModuleActive && isModuleValied.not() -> LocaleString.moduleNotFullyActivated
YukiHookAPI.Status.isXposedModuleActive -> LocaleString.moduleIsActivated
else -> LocaleString.moduleNotActivated
}
binding.mainTextApiWay.isVisible = YukiHookAPI.Status.isXposedModuleActive
binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.executorName} API ${YukiHookAPI.Status.executorVersion}"
binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name} API ${YukiHookAPI.Status.Executor.apiLevel}"
}
/**

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -19,7 +19,7 @@
*
* This file is Created by fankes on 2022/6/1.
*/
package com.fankes.apperrorstracking.ui.view
package com.fankes.apperrorstracking.ui.widget
import android.content.Context
import android.util.AttributeSet

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -21,7 +21,7 @@
*/
@file:Suppress("SameParameterValue")
package com.fankes.apperrorstracking.ui.view
package com.fankes.apperrorstracking.ui.widget
import android.content.Context
import android.content.res.ColorStateList

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -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"

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -19,7 +19,7 @@
*
* This file is Created by fankes on 2022/5/7.
*/
@file:Suppress("unused")
@file:Suppress("unused", "NotificationPermission")
package com.fankes.apperrorstracking.utils.factory
@@ -133,7 +133,7 @@ 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() ?: ""
@@ -141,15 +141,29 @@ fun Context.appNameOf(packageName: String = getPackageName()) =
/**
* 得到 APP 版本信息与版本号
* @param packageName APP 包名 - 默认为当前 APP
* @return [String] 无法获取时返回 "unknown"
* @return [String] 无法获取时返回 ""
*/
fun Context.appVersionBrandOf(packageName: String = getPackageName()) =
getPackageInfoCompat(packageName)?.let { "${it.versionName}(${it.versionCodeCompat})" } ?: "unknown"
if (appVersionNameOf(packageName).isNotBlank()) "${appVersionNameOf(packageName)}(${appVersionCodeOf(packageName)})" else ""
/**
* 得到 APP 版本名称
* @param packageName APP 包名 - 默认为当前 APP
* @return [String] 无法获取时返回 ""
*/
fun Context.appVersionNameOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.versionName ?: ""
/**
* 得到 APP 版本号
* @param packageName APP 包名 - 默认为当前 APP
* @return [Long] 无法获取时返回 -1
*/
fun Context.appVersionCodeOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.versionCodeCompat ?: -1L
/**
* 获取 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()
@@ -175,6 +189,12 @@ inline fun <reified T : Serializable> Intent.getSerializableExtraCompat(key: Str
else getSerializableExtra(key) as? T?
}
/**
* [List]<[T]> 转换为 [ArrayList]<[T]>
* @return [ArrayList]<[T]>
*/
fun <T> List<T>.toArrayList() = toMutableList() as ArrayList<T>
/**
* 计算与当前时间戳相差的友好时间
* @param now 刚刚
@@ -261,7 +281,8 @@ fun Context.snake(msg: String, actionText: String = "", callback: () -> Unit = {
*/
fun Context.pushNotify(channelId: String, channelName: String, title: String, content: String, icon: IconCompat, color: Int, intent: Intent) {
getSystemService<NotificationManager>()?.apply {
createNotificationChannel(NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
createNotificationChannel(NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH))
notify((0..999).random(), NotificationCompat.Builder(this@pushNotify, channelId).apply {
this.color = color
setAutoCancel(true)

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -23,6 +23,8 @@ package com.fankes.apperrorstracking.utils.factory
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonIOException
import com.google.gson.JsonSyntaxException
import com.google.gson.reflect.TypeToken
/**
@@ -34,11 +36,25 @@ val GSON by lazy { GsonBuilder().setLenient().create() ?: error("Gson create fai
/**
* 实体类转 Json 字符串
* @return [String]
* @throws [JsonIOException] 如果不是有效的 Json 数据
*/
fun Any?.toJson() = GSON.toJson(this) ?: ""
/**
* 实体类转 Json 字符串
* @return [String] or null
*/
fun Any?.toJsonOrNull() = runCatching { toJson() }.getOrNull()
/**
* Json 字符串转实体类
* @return [T] or null
* @throws [JsonSyntaxException] 如果 Json 格式不正确
*/
inline fun <reified T> String.toEntity(): T? = takeIf { it.isNotBlank() }.let { GSON.fromJson(this, object : TypeToken<T>() {}.type) }
/**
* Json 字符串转实体类
* @return [T] or null
*/
inline fun <reified T> String.toEntity(): T? = takeIf { it.isNotBlank() }.let { GSON.fromJson(this, object : TypeToken<T>() {}.type) }
inline fun <reified T> String.toEntityOrNull(): T? = runCatching { toEntity<T?>() }.getOrNull()

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -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] 自动设置选中状态 */

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -32,7 +32,6 @@ import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.utils.factory.execShell
import com.fankes.apperrorstracking.utils.factory.isRootAccess
import com.fankes.apperrorstracking.utils.factory.showDialog
import com.fankes.apperrorstracking.utils.factory.snake
import com.highcapable.yukihookapi.hook.factory.dataChannel
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.xposed.channel.data.ChannelData
@@ -45,6 +44,7 @@ object FrameworkTool {
/** 系统框架包名 */
const val SYSTEM_FRAMEWORK_NAME = "android"
private const val CALL_REFRESH_HOST_PREFS_DATA = "call_refresh_host_prefs_data"
private const val CALL_APP_ERRORS_DATA_GET = "call_app_errors_data_get"
private const val CALL_MUTED_ERRORS_APP_DATA_GET = "call_muted_app_errors_data_get"
private const val CALL_APP_ERRORS_DATA_CLEAR = "call_app_errors_data_clear"
@@ -84,6 +84,12 @@ object FrameworkTool {
*/
fun with(instance: PackageParam, initiate: Host.() -> Unit) = apply { this.instance = instance }.apply(initiate)
/**
* 通知系统框架刷新存储的数据
* @param callback 回调
*/
fun onRefreshFrameworkPrefsData(callback: () -> Unit) = instance?.dataChannel?.wait(CALL_REFRESH_HOST_PREFS_DATA) { callback() }
/**
* 监听使用系统框架打开 APP
* @param result 回调包名和用户 ID
@@ -205,14 +211,21 @@ object FrameworkTool {
* 重启系统
* @param context 实例
*/
fun restartSystem(context: Context) =
fun restartSystem(context: Context) {
/** 当 Root 权限获取失败时显示对话框 */
fun showWhenAccessRootFail() =
context.showDialog {
title = LocaleString.accessRootFail
msg = LocaleString.accessRootFailTip
confirmButton(LocaleString.gotIt)
}
context.showDialog {
title = LocaleString.notice
msg = LocaleString.areYouSureRestartSystem
confirmButton {
if (isRootAccess)
execShell(cmd = "reboot")
else context.snake(LocaleString.accessRootFail)
else showWhenAccessRootFail()
}
neutralButton(LocaleString.fastRestart) {
context.showDialog {
@@ -221,20 +234,28 @@ object FrameworkTool {
confirmButton {
if (isRootAccess)
execShell(cmd = "killall zygote")
else context.snake(LocaleString.accessRootFail)
else showWhenAccessRootFail()
}
cancelButton()
}
}
cancelButton()
}
}
/**
* 检查模块是否激活
* @param context 实例
* @param result 成功后回调
*/
fun checkingActivated(context: Context, result: (Boolean) -> Unit) = context.dataChannel(SYSTEM_FRAMEWORK_NAME).checkingVersionEquals(result)
fun checkingActivated(context: Context, result: (Boolean) -> Unit) =
context.dataChannel(SYSTEM_FRAMEWORK_NAME).checkingVersionEquals(result = result)
/**
* 通知系统框架刷新存储的数据
* @param context 实例
*/
fun refreshFrameworkPrefsData(context: Context) = context.dataChannel(SYSTEM_FRAMEWORK_NAME).put(CALL_REFRESH_HOST_PREFS_DATA)
/**
* 使用系统框架打开 [packageName]

View File

@@ -0,0 +1,113 @@
/*
* 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/1/23.
*/
package com.fankes.apperrorstracking.utils.tool
import android.app.Activity
import android.content.Context
import android.icu.text.SimpleDateFormat
import android.icu.util.Calendar
import android.icu.util.TimeZone
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.utils.factory.openBrowser
import com.fankes.apperrorstracking.utils.factory.showDialog
import okhttp3.*
import org.json.JSONObject
import java.io.IOException
import java.io.Serializable
import java.util.*
/**
* 获取 GitHub Release 最新版本工具类
*/
object GithubReleaseTool {
/** 仓库作者 */
private const val REPO_AUTHOR = "KitsunePie"
/** 仓库名称 */
private const val REPO_NAME = "AppErrorsTracking"
/**
* 获取最新版本信息
* @param context 实例
* @param version 当前版本
* @param result 成功后回调 - ([String] 最新版本,[Function] 更新对话框方法体)
*/
fun checkingForUpdate(context: Context, version: String, result: (String, () -> Unit) -> Unit) = runCatching {
OkHttpClient().newBuilder().build().newCall(
Request.Builder()
.url("https://api.github.com/repos/$REPO_AUTHOR/$REPO_NAME/releases/latest")
.get()
.build()
).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {}
override fun onResponse(call: Call, response: Response) = runCatching {
JSONObject(response.body.string()).apply {
GithubReleaseBean(
name = getString("name"),
htmlUrl = getString("html_url"),
content = getString("body"),
date = getString("published_at").localTime()
).apply {
fun showUpdate() = context.showDialog {
title = LocaleString.latestVersion(name)
msg = LocaleString.latestVersionTip(date, content)
confirmButton(LocaleString.updateNow) { context.openBrowser(htmlUrl) }
cancelButton()
}
if (name != version) (context as? Activity?)?.runOnUiThread {
showUpdate()
result(name) { showUpdate() }
}
}
}
}.getOrNull().let {}
})
}.getOrNull().let {}
/**
* 格式化时间为本地时区
* @return [String] 本地时区时间
*/
private fun String.localTime() = replace("T", " ").replace("Z", "").let {
runCatching {
val local = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = Calendar.getInstance().timeZone }
val current = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("GMT") }
local.format(current.parse(it))
}.getOrNull() ?: it
}
/**
* GitHub Release bean
* @param name 版本名称
* @param htmlUrl 网页地址
* @param content 更新日志
* @param date 发布时间
*/
private data class GithubReleaseBean(
var name: String,
var htmlUrl: String,
var content: String,
var date: String
) : Serializable
}

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#ffffff"
android:pathData="M18,6H8C6.895,6 6,6.895 6,8V18C6,19.105 6.895,20 8,20H18C19.105,20 20,19.105 20,18V8C20,6.895 19.105,6 18,6Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
<path
android:fillColor="#ffffff"
android:pathData="M18,28H8C6.895,28 6,28.895 6,30V40C6,41.105 6.895,42 8,42H18C19.105,42 20,41.105 20,40V30C20,28.895 19.105,28 18,28Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
<path
android:fillColor="#ffffff"
android:pathData="M40,6H30C28.895,6 28,6.895 28,8V18C28,19.105 28.895,20 30,20H40C41.105,20 42,19.105 42,18V8C42,6.895 41.105,6 40,6Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
<path
android:fillColor="#ffffff"
android:pathData="M40,28H30C28.895,28 28,28.895 28,30V40C28,41.105 28.895,42 30,42H40C41.105,42 42,41.105 42,40V30C42,28.895 41.105,28 40,28Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
</vector>

View File

@@ -6,7 +6,7 @@
android:background="@color/colorThemeBackground"
android:orientation="vertical"
tools:context=".ui.activity.debug.LoggerActivity"
tools:ignore="ContentDescription,UseCompoundDrawables">
tools:ignore="ContentDescription,UseCompoundDrawables,UnusedAttribute">
<LinearLayout
android:layout_width="match_parent"
@@ -42,7 +42,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:singleLine="true"
android:text="@string/debug_log"
android:text="@string/debug_logs"
android:textColor="@color/colorTextGray"
android:textSize="19sp"
android:textStyle="bold" />

View File

@@ -8,7 +8,7 @@
android:background="@color/colorThemeBackground"
android:orientation="vertical"
tools:context=".ui.activity.errors.AppErrorsDetailActivity"
tools:ignore="ContentDescription,UseCompoundDrawables,SmallSp">
tools:ignore="ContentDescription,UseCompoundDrawables,SmallSp,UnusedAttribute">
<LinearLayout
android:layout_width="match_parent"
@@ -182,7 +182,7 @@
android:orientation="horizontal">
<TextView
android:id="@+id/app_abi_text"
android:id="@+id/app_cpu_abi_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
@@ -437,8 +437,52 @@
</LinearLayout>
</LinearLayout>
<com.fankes.apperrorstracking.ui.widget.MaterialSwitch
android:id="@+id/disable_auto_wrap_error_stack_trace_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="@drawable/bg_permotion_round"
android:paddingLeft="15dp"
android:paddingTop="10dp"
android:paddingRight="15dp"
android:paddingBottom="10dp"
android:text="@string/disable_auto_wrap_error_stack_trace_content"
android:textAllCaps="false"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<HorizontalScrollView
android:id="@+id/error_stack_trace_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@drawable/bg_stack_round"
android:fadingEdgeLength="10dp"
android:fillViewport="true"
android:overScrollMode="never"
android:padding="15dp"
android:requiresFadingEdge="horizontal"
android:scrollbars="none"
android:visibility="gone">
<TextView
android:id="@+id/error_stack_trace_movable_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:lineSpacingExtra="5dp"
android:textColor="#B65B57"
android:textIsSelectable="true"
android:textSize="12sp"
android:typeface="monospace" />
</HorizontalScrollView>
<TextView
android:id="@+id/error_stack_text"
android:id="@+id/error_stack_trace_fixed_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"

View File

@@ -6,7 +6,7 @@
android:background="@color/colorThemeBackground"
android:orientation="vertical"
tools:context=".ui.activity.errors.AppErrorsRecordActivity"
tools:ignore="ContentDescription,UseCompoundDrawables">
tools:ignore="ContentDescription,UseCompoundDrawables,UnusedAttribute">
<LinearLayout
android:layout_width="match_parent"

View File

@@ -1,12 +1,13 @@
<?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"
android:background="@color/colorThemeBackground"
android:orientation="vertical"
tools:context=".ui.activity.errors.AppErrorsRecordActivity"
tools:ignore="ContentDescription,UseCompoundDrawables">
tools:ignore="ContentDescription,UseCompoundDrawables,UnusedAttribute">
<LinearLayout
android:layout_width="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"

View File

@@ -1,12 +1,13 @@
<?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"
android:background="@color/colorThemeBackground"
android:orientation="vertical"
tools:context=".ui.activity.main.ConfigureActivity"
tools:ignore="ContentDescription,UseCompoundDrawables">
tools:ignore="ContentDescription,UseCompoundDrawables,UnusedAttribute">
<LinearLayout
android:layout_width="match_parent"
@@ -57,6 +58,16 @@
android:textSize="11.5sp" />
</LinearLayout>
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/global_icon"
android:layout_width="23dp"
android:layout_height="23dp"
android:layout_marginEnd="15dp"
android:src="@drawable/ic_global"
android:tint="@color/colorTextGray"
android:tooltipText="@string/global_config"
android:visibility="gone" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/batch_icon"
android:layout_width="25dp"
@@ -84,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"

View File

@@ -7,7 +7,7 @@
android:background="@color/colorThemeBackground"
android:orientation="vertical"
tools:context=".ui.activity.main.MainActivity"
tools:ignore="UseCompoundDrawables,ContentDescription,SmallSp">
tools:ignore="UseCompoundDrawables,ContentDescription,SmallSp,UnusedAttribute">
<LinearLayout
android:layout_width="match_parent"
@@ -39,7 +39,7 @@
android:padding="0.5dp"
android:src="@drawable/ic_debug"
android:tint="@color/colorTextGray"
android:tooltipText="@string/debug_log" />
android:tooltipText="@string/debug_logs" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/title_restart_icon"
@@ -226,7 +226,7 @@
android:textSize="12sp" />
</LinearLayout>
<com.fankes.apperrorstracking.ui.view.MaterialSwitch
<com.fankes.apperrorstracking.ui.widget.MaterialSwitch
android:id="@+id/only_show_errors_in_front_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
@@ -250,7 +250,7 @@
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<com.fankes.apperrorstracking.ui.view.MaterialSwitch
<com.fankes.apperrorstracking.ui.widget.MaterialSwitch
android:id="@+id/only_show_errors_in_main_process_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
@@ -274,7 +274,7 @@
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<com.fankes.apperrorstracking.ui.view.MaterialSwitch
<com.fankes.apperrorstracking.ui.widget.MaterialSwitch
android:id="@+id/always_shows_reopen_app_options_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
@@ -298,7 +298,7 @@
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<com.fankes.apperrorstracking.ui.view.MaterialSwitch
<com.fankes.apperrorstracking.ui.widget.MaterialSwitch
android:id="@+id/enable_apps_configs_template_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
@@ -385,7 +385,7 @@
android:textSize="12sp" />
</LinearLayout>
<com.fankes.apperrorstracking.ui.view.MaterialSwitch
<com.fankes.apperrorstracking.ui.widget.MaterialSwitch
android:id="@+id/enable_material3_app_errors_dialog_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -551,7 +551,7 @@
android:textSize="12sp" />
</LinearLayout>
<com.fankes.apperrorstracking.ui.view.MaterialSwitch
<com.fankes.apperrorstracking.ui.widget.MaterialSwitch
android:id="@+id/hide_icon_in_launcher_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -626,7 +626,7 @@
android:textSize="12sp" />
</LinearLayout>
<com.fankes.apperrorstracking.ui.view.MaterialSwitch
<com.fankes.apperrorstracking.ui.widget.MaterialSwitch
android:id="@+id/enable_anonymous_statistics_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -27,27 +27,39 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/show_errors_dialog"
android:singleLine="true"
android:text="@string/follow_global_config"
app:buttonTint="@color/colorPrimaryAccent" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/config_radio_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/show_errors_notify"
android:singleLine="true"
android:text="@string/show_errors_dialog"
app:buttonTint="@color/colorPrimaryAccent" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/config_radio_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/show_errors_toast"
android:singleLine="true"
android:text="@string/show_errors_notify"
app:buttonTint="@color/colorPrimaryAccent" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/config_radio_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/show_errors_toast"
app:buttonTint="@color/colorPrimaryAccent" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/config_radio_4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/show_nothing"
app:buttonTint="@color/colorPrimaryAccent" />
</RadioGroup>

View File

@@ -32,7 +32,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
<com.fankes.apperrorstracking.ui.widget.ItemLinearLayout
android:id="@+id/app_info_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -54,9 +54,9 @@
android:text="@string/app_info"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
</com.fankes.apperrorstracking.ui.widget.ItemLinearLayout>
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
<com.fankes.apperrorstracking.ui.widget.ItemLinearLayout
android:id="@+id/close_app_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -77,9 +77,9 @@
android:text="@string/close_app"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
</com.fankes.apperrorstracking.ui.widget.ItemLinearLayout>
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
<com.fankes.apperrorstracking.ui.widget.ItemLinearLayout
android:id="@+id/reopen_app_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -100,9 +100,9 @@
android:text="@string/reopen_app"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
</com.fankes.apperrorstracking.ui.widget.ItemLinearLayout>
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
<com.fankes.apperrorstracking.ui.widget.ItemLinearLayout
android:id="@+id/error_detail_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -123,9 +123,9 @@
android:text="@string/error_detail"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
</com.fankes.apperrorstracking.ui.widget.ItemLinearLayout>
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
<com.fankes.apperrorstracking.ui.widget.ItemLinearLayout
android:id="@+id/muted_if_unlock_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -146,9 +146,9 @@
android:text="@string/mute_if_unlock"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
</com.fankes.apperrorstracking.ui.widget.ItemLinearLayout>
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
<com.fankes.apperrorstracking.ui.widget.ItemLinearLayout
android:id="@+id/muted_if_restart_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -169,7 +169,7 @@
android:text="@string/mute_if_restart"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
</com.fankes.apperrorstracking.ui.widget.ItemLinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@@ -21,11 +21,37 @@
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/contains_system_switch"
<RadioGroup
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/result_contains_system_apps"
app:buttonTint="@color/colorPrimaryAccent" />
android:orientation="horizontal">
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/filters_radio_user"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:checked="true"
android:singleLine="true"
android:text="@string/user_apps"
app:buttonTint="@color/colorPrimaryAccent" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/filters_radio_system"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:singleLine="true"
android:text="@string/system_apps"
app:buttonTint="@color/colorPrimaryAccent" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/filters_radio_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/all_apps"
app:buttonTint="@color/colorPrimaryAccent" />
</RadioGroup>
</LinearLayout>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">エラー追跡</string>
<string name="xposed_desc">ネイティブFCダイアログに機能を追加し、中国の国内カスタムROMがFCダイアログを削除する問題を修正し、Android開発者のエクスペリエンスを向上させます</string>
<string name="xposed_desc">アプリのエラーダイアログに機能を追加し、カスタム ROM の削除されたダイアログを修正し、Android 開発者にとって最高のエクスペリエンスを実現しました</string>
<string name="app_info">アプリ情報</string>
<string name="reopen_app">アプリをリスタート</string>
<string name="error_detail">エラーの詳細</string>
@@ -48,16 +48,16 @@
<string name="got_it">分かります</string>
<string name="restart_system">リスタートシステム</string>
<string name="project_address">プロジェクトアドレス</string>
<string name="module_not_activated">モジュールが無効化でした</string>
<string name="module_version">モジュールバージョン: %1$s</string>
<string name="system_version">システムバージョン: %1$s</string>
<string name="module_is_activated">モジュールが有効化でした</string>
<string name="module_not_activated">モジュールが無効化でした</string>
<string name="module_version">モジュールバージョン: %1$s</string>
<string name="system_version">システムバージョン: %1$s</string>
<string name="module_is_activated">モジュールが有効化でした</string>
<string name="display_settings">表示設定</string>
<string name="hide_app_icon_on_launcher">デスクトップ上のアプリアイコンを非表示</string>
<string name="hide_app_icon_on_launcher_tip">モジュールアイコンを非表示にすると、インターフェイスが閉じてデスクトップに表示されなくなります。モジュール設定は、EdXposedまたはLSPosedで検索して開くことができます。</string>
<string name="hide_app_icon_on_launcher_notice">LSPosedの「Force apps to show launcher icons」機能を必ずオフにしてください</string>
<string name="hide_app_icon_on_launcher">ランチャー上のアプリアイコンを非表示</string>
<string name="hide_app_icon_on_launcher_tip">モジュールアイコンを非表示にすると、インターフェイスが閉じてランチャーに表示されなくなります。モジュール設定は、EdXposedまたはLSPosedで検索して開くことができます。</string>
<string name="hide_app_icon_on_launcher_notice">LSPosedの「ランチャーアイコンを強制的に表示」機能を必ずオフにしてください</string>
<string name="about_module">このモジュールは、YukiHookAPIを使用して構築できます。 \n詳細 https://github.com/fankes/YukiHookAPI</string>
<string name="module_not_fully_activated">モジュールが不完全に有効化でした</string>
<string name="module_not_fully_activated">モジュールが不完全に有効化でした</string>
<string name="are_your_sure_restart_system">リスタートシステムしてもよろしいですか</string>
<string name="fast_restart">高速リスタート</string>
<string name="access_root_fail">ルート権限を取得できませんでした</string>
@@ -72,15 +72,15 @@
<string name="share_error_stack">エラースタックをシェアする</string>
<string name="preference_settings">好み設定</string>
<string name="function_mgr">機能管理</string>
<string name="only_show_errors_in_front">フロントアプリのエラーダイアログを表示</string>
<string name="only_show_errors_in_main_process">メインプロセスのエラーダイアログを表示</string>
<string name="enable_apps_config_template">アプリ設定テンプレートを有効</string>
<string name="mgr_apps_config_template">アプリ設定テンプレートを管理する</string>
<string name="view_errors_record">エラー履歴を表示する</string>
<string name="view_muted_errors_apps">エラーを無視したアプリを表示する</string>
<string name="only_show_errors_in_front_tip">有効にすると、エラーが発生したアプリがフォアグラウンド使用中の場合にのみエラーダイアログが表示されます。エラーが発生したバックグラウンドアプリではエラーダイアログは表示されませんが、すべてがに記録されます。エラー履歴。</string>
<string name="only_show_errors_in_main_process_tip">有効にすると、エラーダイアログは、アプリで発生したエラーがメインプロセス最初のアプリケーションインスタンスオブジェクトにある場合にのみ表示されます。</string>
<string name="apps_config_template_tip">ここでは、アプリごとに個別にエラーが発生したときにエラーダイアログおよびその他のエラーメッセージを表示するかどうかを設定できます。</string>
<string name="only_show_errors_in_front">フォアグラウンドアプリで発生したエラーを表示</string>
<string name="only_show_errors_in_main_process">アプリのメインプロセスで発生したエラーを表示</string>
<string name="enable_apps_config_template">アプリ設定テンプレートを有効</string>
<string name="mgr_apps_config_template">アプリ設定テンプレートを管理する</string>
<string name="view_errors_record">エラー履歴を表示する</string>
<string name="view_muted_errors_apps">エラーを無視したアプリを表示する</string>
<string name="only_show_errors_in_front_tip">有効にすると、アプリがフォアグラウンド (使用中) の場合にのみエラーが表示されます。エラーが発生したバックグラウンドアプリにはプロンプト情報はありませんが、エラー履歴に記録されます。</string>
<string name="only_show_errors_in_main_process_tip">有効にすると、アプリがメインプロセス (最初の Application インスタンスオブジェクト) にあるときに、アプリのエラーのみ表示されます。</string>
<string name="apps_config_template_tip">ここでは、アプリごとに個別にエラーが発生したときにエラーダイアログおよびその他のエラーメッセージを表示するかどうかを設定できます。\n無効にすると、すべてのアプリで常にエラーダイアログの形式でエラーメッセージが表示されます。</string>
<string name="view_muted_errors_apps_tip">ここでは、さまざまな形式のエラーを手動で無視したアプリを見つけることができます。このリストは、リスタート後に自動的にクリアされます。これらの無視されたアプリを管理し、無視リストから削除できます。</string>
<string name="muted_errors_apps">エラーを無視したアプリ</string>
<string name="unmute">無視を解除</string>
@@ -90,7 +90,6 @@
<string name="filter_by_condition">条件でフィルタリング</string>
<string name="no_list_result">表示結果はありません</string>
<string name="typo_app_name_pkg_name">アプリ名とパッケージ名を入力できます</string>
<string name="result_contains_system_apps">結果にはシステムアプリが含まれます</string>
<string name="clear_filters">条件をクリア</string>
<string name="result_count">%1$s の合計結果</string>
<string name="loading">ロード中</string>
@@ -112,25 +111,39 @@
<string name="app_errors_tip">アプリは未処理のエラーで崩壊しました。クリックして、アプリ開発者への詳細またはフィードバックを表示します。</string>
<string name="batch_operations">一括操作</string>
<string name="are_you_sure_apply_site_apps">一度に %1$s 個のアプリに設定を適用してもよろしいですか</string>
<string name="errors_dialog_always_show_reopen">エラーダイアログには常にリスタートが表示</string>
<string name="errors_dialog_always_show_reopen_tip">有効にした後、アプリが最初のエラーでない場合にも「アプリをリスタート」オプション表示されます。現在のエラーがメインプロセスでない場合、またはアプリを開くことができない場合でも、このオプションは表示されません。</string>
<string name="errors_dialog_always_show_reopen">エラーダイアログには常に「アプリをリスタート」が表示</string>
<string name="errors_dialog_always_show_reopen_tip">有効にした後、ダイアログボックスにアプリのエラーが表示された場合、エラーが初めてではない場合「アプリをリスタート」オプション表示されます。現在のエラーがメインプロセスでない場合、またはアプリを開ない場合、このオプションはまだ表示されません。</string>
<string name="developer_notice_tip">このモジュールは、Android開発者向けに作成されています。\n\nコンピューターに接続できず、ADBデバッグを実行できない可能性がある場合、このモジュールを使用して、インストールされているアプリのエラーをすばやくキャプチャし、問題をすばやく特定できます。\n\nアプリのクラッシュのエラーログは開発者にとって非常に貴重です。開発者でない場合でも、このモジュールをインストールして、問題をすばやく解決するためのより多くの例外情報を開発者に提供できます。</string>
<string name="developer_notice">使用説明書</string>
<string name="warning">警告</string>
<string name="fast_restart_problem">一部のカスタム システムでは、高速リスタートを使用した後にエラーが発生する場合があります。\\n続行しますか?</string>
<string name="view_errors_record_tip">ここでは、モジュールが記録を開始してからのすべてのアプリ エラー レコードを見つけることができます。エラー履歴は、手動で消去するか工場出荷時の設定に戻すまで保持され続けます。記録を表示、エクスポート、共有、消去できます。</string>
<string name="view_errors_record_tip">ここでは、モジュールが記録を開始してからのすべてのアプリエラーレコードを見つけることができます。エラー履歴は、手動で消去するか工場出荷時の設定に戻すまで保持され続けます。記録を表示、エクスポート、共有、消去できます。</string>
<string name="theme_settings">テーマ設定</string>
<string name="enable_md3_app_errors_dialog">MD3 スタイルのエラーダイアログを有効</string>
<string name="enable_md3_app_errors_dialog_tip">この機能は、Android のターゲット バージョンが 12 以降の場合にデフォルトで有効になり、動的テーマ カラー機能は Android 12 以降でのみ有効になります。</string>
<string name="enable_md3_app_errors_dialog">MD3 スタイルのエラータイアログを有効</string>
<string name="enable_md3_app_errors_dialog_tip">この機能は、Android のターゲットバージョンが 12 以降の場合にデフォルトで有効になり、動的テーマカラー機能は Android 12 以降でのみ有効になります。</string>
<string name="user_id">ユーザー %1$s</string>
<string name="unable_get_app_errors_record_tip">現在、エラー レコードを取得できません。後でエラー履歴で確認する必要があるかもしれません。問題が解決しない場合は、システムがエラー レポート収集機能をオフにしている可能性があります。</string>
<string name="debug_log">デバッグ ログ</string>
<string name="unable_get_app_errors_record_tip">現在、エラーレコードを取得できません。後でエラー履歴で確認する必要があるかもしれません。問題が解決しない場合は、システムがエラーレポート収集機能をオフにしている可能性があります。</string>
<string name="debug_logs">デバッグログ</string>
<string name="refresh">リフレッシュ</string>
<string name="copy">コピー</string>
<string name="export_all_logs_success">エクスポートされたすべてのデバッグ ログ</string>
<string name="export_all_logs_success">エクスポートされたすべてのデバッグログ</string>
<string name="export_all_logs_fail">すべてのデバッグ ログのエクスポートに失敗しました</string>
<string name="this_contents_clear_when_restarts_tip">ここのコンテンツは、デバイスのリスタート後に自動的に消去されます</string>
<string name="when_logger_how_to_show_tip">現在のデバッグ ログ表示の優先度フィルタ条件を設定できます。</string>
<string name="enable_anonymous_statistics">匿名統計を有効</string>
<string name="enable_anonymous_statistics_tip">有効にすると、Microsoft App Center を使用してアプリ関連の診断データを匿名で送信し、アプリの機能を向上させます。\n収集される情報は匿名であり、独立したデバイスまたはユーザーまで追跡することはできません。\nこの機能を有効または無効にするには、リスタートアプリが有効になります。</string>
<string name="go_it_now">今行く</string>
<string name="access_root_fail_tip">現在、ルート許可を取得できません。デバイスがルート化されていることを確認し、ルート許可を付与することに同意してください。\nMagisk を使用していて、Shamiko モジュールをインストールしている場合は、現在ホワイトリスト モードになっているかどうかを確認してください。(ホワイトリストモードでは、ルート権限の申請ができなくなります)</string>
<string name="global_config">グローバル構成</string>
<string name="follow_global_config">グローバル構成に従う</string>
<string name="batch_operations_number">一括操作 %1$s 個のアプリ</string>
<string name="user_apps">ユーザーアプリ</string>
<string name="all_apps">全てのアプリ</string>
<string name="system_apps">システムアプリ</string>
<string name="disable_auto_wrap_error_stack_trace_content">エラースタックコンテンツの自動ラッピングを無効</string>
<string name="click_to_update">更新 %1$s をクリック</string>
<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>

View File

@@ -4,11 +4,11 @@
<!-- Primary brand color. -->
<item name="colorPrimary">@color/colorPrimaryAccent</item>
<item name="colorPrimaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnPrimary">@color/colorPrimaryAccent</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/colorPrimaryAccent</item>
<item name="colorSecondaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnSecondary">@color/colorPrimaryAccent</item>
<item name="colorOnSecondary">@color/white</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">@color/black</item>
<item name="android:windowLightStatusBar">false</item>

View File

@@ -73,15 +73,15 @@
<string name="share_error_stack">分享异常堆栈</string>
<string name="preference_settings">偏好设置</string>
<string name="function_mgr">功能管理</string>
<string name="only_show_errors_in_front">前台应用显示错误对话框</string>
<string name="only_show_errors_in_main_process">应用主进程显示错误对话框</string>
<string name="only_show_errors_in_front">显示前台应用发生的异常</string>
<string name="only_show_errors_in_main_process">显示应用主进程发生的异常</string>
<string name="enable_apps_config_template">启用应用配置模板</string>
<string name="mgr_apps_config_template">管理应用配置模板</string>
<string name="view_errors_record">查看异常历史记录</string>
<string name="view_muted_errors_apps">查看已忽略异常的应用</string>
<string name="only_show_errors_in_front_tip">启用后,只有发生异常的应用处于前台(正在使用中)时才会显示错误对话框,发生异常的后台应用虽然不会显示错误对话框,但是它们都会被记录到异常历史记录中。</string>
<string name="only_show_errors_in_main_process_tip">启用后,只有应用发生的异常位于主进程(第一个 Application 实例对象)时才会显示错误对话框</string>
<string name="apps_config_template_tip">你可以在这里对每个应用发生异常时,单独配置其在发生异常时是否显示错误对话框以及其它错误提示。</string>
<string name="only_show_errors_in_front_tip">启用后,只有应用处于前台(正在使用中)时才会显示其发生的异常,发生异常的后台应用虽然不会有任何提示信息,但是它们都会被记录到异常历史记录中。</string>
<string name="only_show_errors_in_main_process_tip">启用后,只有应用位于主进程(第一个 Application 实例对象)时才会显示其发生的异常</string>
<string name="apps_config_template_tip">你可以在这里对每个应用发生异常时,单独配置其在发生异常时是否显示错误对话框以及其它错误提示。\n关闭后全部应用将始终以错误对话框的形式显示异常信息。</string>
<string name="view_errors_record_tip">在这里,你可以找到从模块开始记录以来的全部应用异常记录,异常历史记录会持续保留直到你手动清空或恢复出厂设置,你可以对记录进行查看、导出和分享以及清空。</string>
<string name="view_muted_errors_apps_tip">在这里,你可以找到已被你以不同形式手动忽略异常的应用,这个列表将会在重新启动后自动清空,你可以对这些已忽略的应用进行管理以及从忽略列表中移除它们。</string>
<string name="muted_errors_apps">已忽略异常的应用</string>
@@ -92,7 +92,6 @@
<string name="filter_by_condition">按条件过滤</string>
<string name="no_list_result">没有结果可以显示</string>
<string name="typo_app_name_pkg_name">可输入 APP 名称、包名</string>
<string name="result_contains_system_apps">结果包含系统应用</string>
<string name="clear_filters">清除条件</string>
<string name="result_count">共 %1$s 个结果</string>
<string name="loading">加载中</string>
@@ -115,7 +114,7 @@
<string name="batch_operations">批量操作</string>
<string name="are_you_sure_apply_site_apps">你确定要一次性应用设置给 %1$s 个应用吗?</string>
<string name="errors_dialog_always_show_reopen">错误对话框始终显示“重新打开”选项</string>
<string name="errors_dialog_always_show_reopen_tip">启用后,在应用非首次异常时也将显示“重新打开”选项,若当前异常非主进程或应用无法打开则依然不会显示此选项。</string>
<string name="errors_dialog_always_show_reopen_tip">启用后,在使用对话框显示应用异常时,非首次异常时也将显示“重新打开”选项,若当前异常非主进程或应用无法打开则依然不会显示此选项。</string>
<string name="developer_notice_tip">此模块专为 Android 开发者而打造。\n\n在可能的无法连接电脑不能进行 ADB 调试的时候,可通过此模块来快速捕获任意已安装应用的任意异常,以便快速定位问题。\n\n应用发生崩溃的错误日志对开发者来说是无价的财富若你不是开发者你依然可以安装此模块以便给开发者提供更多异常信息快速解决问题。</string>
<string name="developer_notice">使用说明</string>
<string name="fast_restart_problem">部分定制系统使用快速重启后可能会发生错误,仍然要继续吗?</string>
@@ -124,7 +123,7 @@
<string name="enable_md3_app_errors_dialog_tip">此功能在 Android 目标版本为 12 及以上时默认启用,动态主题色功能仅会在 Android 12 及以上版本生效。</string>
<string name="user_id">用户 %1$s</string>
<string name="unable_get_app_errors_record_tip">现在无法获取异常记录,你可能需要稍后在异常历史记录中查看,若问题持续出现,可能是你的系统关闭了错误报告收集功能。</string>
<string name="debug_log">调试日志</string>
<string name="debug_logs">调试日志</string>
<string name="refresh">刷新</string>
<string name="copy">复制</string>
<string name="export_all_logs_success">已导出全部调试日志</string>
@@ -133,4 +132,18 @@
<string name="when_logger_how_to_show_tip">你可以配置当前调试日志显示的优先级过滤条件。</string>
<string name="enable_anonymous_statistics">启用匿名统计</string>
<string name="enable_anonymous_statistics_tip">启用后,我们将会使用 Microsoft App Center 来匿名发送应用相关的诊断数据以帮助应用的功能更加完善,收集的信息是匿名的,我们无法追溯到独立的设备或用户,启用或关闭此功能需要重启应用方可生效。</string>
<string name="go_it_now">现在前往</string>
<string name="access_root_fail_tip">当前无法获取 Root 权限,请确认你的设备已经被 Root 且同意授予 Root 权限。\n如果你正在使用 Magisk 并安装了 Shamiko 模块,请确认当前是否正处于白名单模式。 (白名单模式将导致无法申请 Root 权限)</string>
<string name="global_config">全局配置</string>
<string name="follow_global_config">跟随全局配置</string>
<string name="batch_operations_number">批量操作 %1$s 个应用</string>
<string name="user_apps">用户应用</string>
<string name="system_apps">系统应用</string>
<string name="all_apps">全部应用</string>
<string name="disable_auto_wrap_error_stack_trace_content">禁止异常堆栈内容自动换行</string>
<string name="click_to_update">点击更新 %1$s</string>
<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>

View File

@@ -52,10 +52,10 @@
<string name="module_version">模組版本:%1$s</string>
<string name="system_version">系統版本:%1$s</string>
<string name="module_is_activated">模組已激活</string>
<string name="display_settings">示設置</string>
<string name="display_settings">示設置</string>
<string name="hide_app_icon_on_launcher">在桌面隱藏模組圖標</string>
<string name="hide_app_icon_on_launcher_tip">隱藏模組圖標後界面可能會被關閉,將不會再在桌面示,你可以在 EdXposed、LSPosed 中找到模組設置並打開。</string>
<string name="hide_app_icon_on_launcher_notice">注意:請務必在 LSPosed 中關閉“強制示桌面圖標”功能</string>
<string name="hide_app_icon_on_launcher_tip">隱藏模組圖標後界面可能會被關閉,將不會再在桌面示,你可以在 EdXposed、LSPosed 中找到模組設置並打開。</string>
<string name="hide_app_icon_on_launcher_notice">注意:請務必在 LSPosed 中關閉“強制示桌面圖標”功能</string>
<string name="about_module">此模組使用 YukiHookAPI 構建。 \n了解更多 https://github.com/fankes/YukiHookAPI</string>
<string name="module_not_fully_activated">模組未完全激活,請重新開機</string>
<string name="are_your_sure_restart_system">你確定要重新開機嗎?</string>
@@ -72,15 +72,15 @@
<string name="share_error_stack">分享異常堆棧</string>
<string name="preference_settings">偏好設置</string>
<string name="function_mgr">功能管理</string>
<string name="only_show_errors_in_front">前台程式展示錯誤對話框</string>
<string name="only_show_errors_in_main_process">程式主進程展示錯誤對話框</string>
<string name="only_show_errors_in_front">展示前台程式發生的異常</string>
<string name="only_show_errors_in_main_process">展示程式主進程發生的異常</string>
<string name="enable_apps_config_template">啟用程式配置模板</string>
<string name="mgr_apps_config_template">管理程式配置模板</string>
<string name="view_errors_record">查看異常歷史記錄</string>
<string name="view_muted_errors_apps">查看已忽略異常的程式</string>
<string name="only_show_errors_in_front_tip">啟用後,只有發生異常的程式處於前台(正在使用中)時才會展示錯誤對話框,發生異常的後台程式雖然不會展示錯誤對話框,但是它們都會被記錄到異常歷史記錄中。</string>
<string name="only_show_errors_in_main_process_tip">啟用後,只有程式發生的異常位於主進程(第一個 Application 實例對象)時才會展示錯誤對話框</string>
<string name="apps_config_template_tip">你可以在這裡對每個程式發生異常時,單獨配置其在發生異常時是否展示錯誤對話框以及其它錯誤提示。</string>
<string name="only_show_errors_in_front_tip">啟用後,只有程式處於前台(正在使用中)時才會展示其發生的異常,發生異常的後台程式雖然不會有任何提示信息,但是它們都會被記錄到異常歷史記錄中。</string>
<string name="only_show_errors_in_main_process_tip">啟用後,只有程式位於主進程(第一個 Application 實例對象)時才會展示其發生的異常</string>
<string name="apps_config_template_tip">你可以在這裡對每個程式發生異常時,單獨配置其在發生異常時是否展示錯誤對話框以及其它錯誤提示。\n關閉後全部程式將始終以錯誤對話框的形式展示異常信息。</string>
<string name="view_errors_record_tip">在這裡,你可以找到從模組開始記錄以來的全部程式異常記錄,異常歷史記錄會持續保留直到你手動清空或恢復出廠設置,你可以對記錄進行查看、導出和分享以及清空。</string>
<string name="view_muted_errors_apps_tip">在這裡,你可以找到已被你以不同形式手動忽略異常的程式,這個列表將會在重新啟動後自動清空,你可以對這些已忽略的程式進行管理以及從忽略列表中移除它們。</string>
<string name="muted_errors_apps">已忽略異常的程式</string>
@@ -91,14 +91,13 @@
<string name="filter_by_condition">按條件過濾</string>
<string name="no_list_result">沒有結果可以展示</string>
<string name="typo_app_name_pkg_name">可輸入 APP 名稱、包名</string>
<string name="result_contains_system_apps">結果包含系統程式</string>
<string name="clear_filters">清除條件</string>
<string name="result_count">共 %1$s 個結果</string>
<string name="loading">加載中</string>
<string name="when_errors_how_to_show_tip">你可以配置當程式崩潰時系統將如何向你展示錯誤提醒副案。</string>
<string name="show_errors_dialog">示錯誤對話框</string>
<string name="show_errors_toast">示錯誤 Toast</string>
<string name="show_nothing">什麼也不</string>
<string name="show_errors_dialog">示錯誤對話框</string>
<string name="show_errors_toast">示錯誤 Toast</string>
<string name="show_nothing">什麼也不</string>
<string name="app_errors_statistics">程式異常統計</string>
<string name="total_errors">異常總數</string>
<string name="total_apps">程式總數</string>
@@ -113,8 +112,8 @@
<string name="app_errors_tip">程式發生了未處理的異常而崩潰,點擊查看詳情或反饋給程式開發者。</string>
<string name="batch_operations">批量操作</string>
<string name="are_you_sure_apply_site_apps">你確認要一次性應用設置給 %1$s 個程式嗎?</string>
<string name="errors_dialog_always_show_reopen">錯誤對話框始終示“重新開啟”選項</string>
<string name="errors_dialog_always_show_reopen_tip">啟用後,在程式非首次異常時也將示“重新開啟”選項,若當前異常非主進程或程式無法打開則依然不會示此選項。</string>
<string name="errors_dialog_always_show_reopen">錯誤對話框始終示“重新開啟”選項</string>
<string name="errors_dialog_always_show_reopen_tip">啟用後,在使用對話框展示程式異常時,非首次異常時也將示“重新開啟”選項,若當前異常非主進程或程式無法打開則依然不會示此選項。</string>
<string name="developer_notice">使用說明</string>
<string name="developer_notice_tip">此模組專為 Android 開發者而打造。\n\n在可能的無法連接電腦不能進行 ADB 調試的時候,可通過此模組來快速捕獲任意已安裝程式的任意異常,以便快速定位問題。 \n\n程式發生崩潰的錯誤日誌對開發者來說是無價的財富若你不是開發者你依然可以安裝此模組以便給開發者提供更多異常信息快速解決問題。</string>
<string name="warning">警告</string>
@@ -124,13 +123,27 @@
<string name="enable_md3_app_errors_dialog_tip">此功能在 Android 目標版本為 12 及以上時默認啟用,動態主題色功能僅會在 Android 12 及以上版本生效。</string>
<string name="user_id">用户 %1$s</string>
<string name="unable_get_app_errors_record_tip">現在無法獲取異常記錄,你可能需要稍後在異常歷史記錄中查看,若問題持續出現,可能是你的系統關閉了錯誤報告收集功能。</string>
<string name="debug_log">調試日誌</string>
<string name="debug_logs">調試日誌</string>
<string name="refresh">刷新</string>
<string name="copy">複製</string>
<string name="export_all_logs_success">已導出全部調試日誌</string>
<string name="export_all_logs_fail">導出全部調試日誌失敗</string>
<string name="this_contents_clear_when_restarts_tip">這裡的內容會在設備重開後自動清空</string>
<string name="when_logger_how_to_show_tip">你可以配置當前調試日誌示的優先級過濾條件。</string>
<string name="when_logger_how_to_show_tip">你可以配置當前調試日誌示的優先級過濾條件。</string>
<string name="enable_anonymous_statistics">啟用匿名統計</string>
<string name="enable_anonymous_statistics_tip">啟用後,我們將會使用 Microsoft App Center 來匿名發送程式相關的診斷數據以幫助程式的功能更加完善,收集的信息是匿名的,我們無法追溯到獨立的裝置或用戶,啟用或關閉此功能需要重啟程式方可生效。</string>
<string name="go_it_now">現在前往</string>
<string name="access_root_fail_tip">當前無法取得 Root 權限,請確認你的裝置已經被 Root 且同意授予 Root 權限。\n如果你正在使用 Magisk 並安裝了 Shamiko 模組,請確認當前是否正處於白名單模式。 (白名單模式將導致無法申請 Root 權限)</string>
<string name="global_config">全局配置</string>
<string name="follow_global_config">跟隨全局配置</string>
<string name="batch_operations_number">批量操作 %1$s 個程式</string>
<string name="user_apps">用戶程式</string>
<string name="all_apps">全部程式</string>
<string name="system_apps">系統程式</string>
<string name="disable_auto_wrap_error_stack_trace_content">禁止異常堆棧內容自動換行</string>
<string name="click_to_update">點按升級 %1$s</string>
<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>

View File

@@ -52,10 +52,10 @@
<string name="module_version">模組版本:%1$s</string>
<string name="system_version">系統版本:%1$s</string>
<string name="module_is_activated">模組已激活</string>
<string name="display_settings">示設置</string>
<string name="display_settings">示設置</string>
<string name="hide_app_icon_on_launcher">在桌面隱藏模組圖標</string>
<string name="hide_app_icon_on_launcher_tip">隱藏模組圖標後界面可能會被關閉,將不會再在桌面示,你可以在 EdXposed、LSPosed 中找到模組設置並打開。</string>
<string name="hide_app_icon_on_launcher_notice">注意:請務必在 LSPosed 中關閉“強制示桌面圖標”功能</string>
<string name="hide_app_icon_on_launcher_tip">隱藏模組圖標後界面可能會被關閉,將不會再在桌面示,你可以在 EdXposed、LSPosed 中找到模組設置並打開。</string>
<string name="hide_app_icon_on_launcher_notice">注意:請務必在 LSPosed 中關閉“強制示桌面圖標”功能</string>
<string name="about_module">此模組使用 YukiHookAPI 構建。 \n了解更多 https://github.com/fankes/YukiHookAPI</string>
<string name="module_not_fully_activated">模組未完全激活,請重新開機</string>
<string name="are_your_sure_restart_system">你確定要重新開機嗎?</string>
@@ -72,15 +72,15 @@
<string name="share_error_stack">分享異常堆棧</string>
<string name="preference_settings">偏好設置</string>
<string name="function_mgr">功能管理</string>
<string name="only_show_errors_in_front">前台程式展示錯誤對話框</string>
<string name="only_show_errors_in_main_process">程式主進程展示錯誤對話框</string>
<string name="only_show_errors_in_front">展示前台程式發生的異常</string>
<string name="only_show_errors_in_main_process">展示程式主進程發生的異常</string>
<string name="enable_apps_config_template">啟用程式配置模板</string>
<string name="mgr_apps_config_template">管理程式配置模板</string>
<string name="view_errors_record">查看異常歷史記錄</string>
<string name="view_muted_errors_apps">查看已忽略異常的程式</string>
<string name="only_show_errors_in_front_tip">啟用後,只有發生異常的程式處於前台(正在使用中)時才會展示錯誤對話框,發生異常的後台程式雖然不會展示錯誤對話框,但是它們都會被記錄到異常歷史記錄中。</string>
<string name="only_show_errors_in_main_process_tip">啟用後,只有程式發生的異常位於主進程(第一個 Application 實例對象)時才會展示錯誤對話框</string>
<string name="apps_config_template_tip">你可以在這裡對每個程式發生異常時,單獨配置其在發生異常時是否展示錯誤對話框以及其它錯誤提示。</string>
<string name="only_show_errors_in_front_tip">啟用後,只有程式處於前台(正在使用中)時才會展示其發生的異常,發生異常的後台程式雖然不會有任何提示信息,但是它們都會被記錄到異常歷史記錄中。</string>
<string name="only_show_errors_in_main_process_tip">啟用後,只有程式位於主進程(第一個 Application 實例對象)時才會展示其發生的異常</string>
<string name="apps_config_template_tip">你可以在這裡對每個程式發生異常時,單獨配置其在發生異常時是否展示錯誤對話框以及其它錯誤提示。\n關閉後全部程式將始終以錯誤對話框的形式展示異常信息。</string>
<string name="view_errors_record_tip">在這裡,你可以找到從模組開始記錄以來的全部程式異常記錄,異常歷史記錄會持續保留直到你手動清空或恢復出廠設置,你可以對記錄進行查看、導出和分享以及清空。</string>
<string name="view_muted_errors_apps_tip">在這裡,你可以找到已被你以不同形式手動忽略異常的程式,這個列表將會在重新啟動後自動清空,你可以對這些已忽略的程式進行管理以及從忽略列表中移除它們。</string>
<string name="muted_errors_apps">已忽略異常的程式</string>
@@ -91,14 +91,13 @@
<string name="filter_by_condition">按條件過濾</string>
<string name="no_list_result">沒有結果可以展示</string>
<string name="typo_app_name_pkg_name">可輸入 APP 名稱、包名</string>
<string name="result_contains_system_apps">結果包含系統程式</string>
<string name="clear_filters">清除條件</string>
<string name="result_count">共 %1$s 個結果</string>
<string name="loading">加載中</string>
<string name="when_errors_how_to_show_tip">你可以配置當程式崩潰時系統將如何向你展示錯誤提醒副案。</string>
<string name="show_errors_dialog">示錯誤對話框</string>
<string name="show_errors_toast">示錯誤 Toast</string>
<string name="show_nothing">什麼也不</string>
<string name="show_errors_dialog">示錯誤對話框</string>
<string name="show_errors_toast">示錯誤 Toast</string>
<string name="show_nothing">什麼也不</string>
<string name="app_errors_statistics">程式異常統計</string>
<string name="total_errors">異常總數</string>
<string name="total_apps">程式總數</string>
@@ -113,8 +112,8 @@
<string name="app_errors_tip">程式發生了未處理的異常而崩潰,點擊查看詳情或反饋給程式開發者。</string>
<string name="batch_operations">批量操作</string>
<string name="are_you_sure_apply_site_apps">你確認要一次性應用設置給 %1$s 個程式嗎?</string>
<string name="errors_dialog_always_show_reopen">錯誤對話框始終示“重新開啟”選項</string>
<string name="errors_dialog_always_show_reopen_tip">啟用後,在程式非首次異常時也將示“重新開啟”選項,若當前異常非主進程或程式無法打開則依然不會示此選項。</string>
<string name="errors_dialog_always_show_reopen">錯誤對話框始終示“重新開啟”選項</string>
<string name="errors_dialog_always_show_reopen_tip">啟用後,在使用對話框展示程式異常時,非首次異常時也將示“重新開啟”選項,若當前異常非主進程或程式無法打開則依然不會示此選項。</string>
<string name="developer_notice">使用說明</string>
<string name="developer_notice_tip">此模組專為 Android 開發者而打造。 \n\n在可能的無法連接電腦不能進行 ADB 調試的時候,可通過此模組來快速捕獲任意已安裝程式的任意異常,以便快速定位問題。 \n\n程式發生崩潰的錯誤日誌對開發者來說是無價的財富若你不是開發者你依然可以安裝此模組以便給開發者提供更多異常信息快速解決問題。</string>
<string name="warning">警告</string>
@@ -124,13 +123,27 @@
<string name="enable_md3_app_errors_dialog_tip">此功能在 Android 目標版本為 12 及以上時默認啟用,動態主題色功能僅會在 Android 12 及以上版本生效。</string>
<string name="user_id">用户 %1$s</string>
<string name="unable_get_app_errors_record_tip">現在無法獲取異常記錄,你可能需要稍後在異常歷史記錄中查看,若問題持續出現,可能是你的系統關閉了錯誤報告收集功能。</string>
<string name="debug_log">調試日誌</string>
<string name="debug_logs">調試日誌</string>
<string name="refresh">刷新</string>
<string name="copy">複製</string>
<string name="export_all_logs_success">已導出全部調試日誌</string>
<string name="export_all_logs_fail">導出全部調試日誌失敗</string>
<string name="this_contents_clear_when_restarts_tip">這裡的內容會在設備重開後自動清空</string>
<string name="when_logger_how_to_show_tip">你可以配置當前調試日誌示的優先級過濾條件。</string>
<string name="when_logger_how_to_show_tip">你可以配置當前調試日誌示的優先級過濾條件。</string>
<string name="enable_anonymous_statistics">啟用匿名統計</string>
<string name="enable_anonymous_statistics_tip">啟用後,我們將會使用 Microsoft App Center 來匿名發送程式相關的診斷數據以幫助程式的功能更加完善,收集的信息是匿名的,我們無法追溯到獨立的裝置或用戶,啟用或關閉此功能需要重啟程式方可生效。</string>
<string name="go_it_now">現在前往</string>
<string name="access_root_fail_tip">當前無法取得 Root 權限,請確認你的裝置已經被 Root 且同意授予 Root 權限。\n如果你正在使用 Magisk 並安裝了 Shamiko 模組,請確認當前是否正處於白名單模式。 (白名單模式將導致無法申請 Root 權限)</string>
<string name="global_config">全局配置</string>
<string name="follow_global_config">跟隨全局配置</string>
<string name="batch_operations_number">批量操作 %1$s 個程式</string>
<string name="user_apps">用戶程式</string>
<string name="all_apps">全部程式</string>
<string name="system_apps">系統程式</string>
<string name="disable_auto_wrap_error_stack_trace_content">禁止異常堆棧內容自動換行</string>
<string name="click_to_update">點按升級 %1$s</string>
<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>

View File

@@ -1,34 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">異常調查</string>
<string name="xposed_desc">為原生 FC 對話增加更多功能並修復中國大陸定 ROM 刪除 FC 對話的問題,給 Android 開發者帶來更好的體驗。 \n開發者酷安 @星夜不薈</string>
<string name="xposed_desc">為原生 FC 對話視窗增加更多功能並修復中國大陸定 ROM 刪除 FC 對話視窗的問題,給 Android 開發者帶來更好的體驗。 \n開發者酷安 @星夜不薈</string>
<string name="app_info">程式情報</string>
<string name="reopen_app">重新開啟</string>
<string name="error_detail">異常詳解</string>
<string name="mute_if_unlock">忽略(直到設備重新開屏</string>
<string name="mute_if_restart">忽略(直到設備重新開機)</string>
<string name="mute_if_unlock">忽略(直到裝置重新展示</string>
<string name="mute_if_restart">忽略(直到裝置重新開機)</string>
<string name="close_app">結束程式</string>
<string name="aerr_title">%1$s 已停止運作</string>
<string name="aerr_repeated_title">%1$s 屢次停止運作</string>
<string name="mute_if_unlock_tip">忽略“%1$s”的錯誤直到設備重新開屏</string>
<string name="mute_if_restart_tip">忽略“%1$s”的錯誤直到設備重新開機</string>
<string name="mute_if_unlock_tip">忽略“%1$s”的錯誤直到裝置重新展示</string>
<string name="mute_if_restart_tip">忽略“%1$s”的錯誤直到裝置重新開機</string>
<string name="back">回退</string>
<string name="copy_error_stack">複製異常堆</string>
<string name="copy_error_stack">複製異常堆</string>
<string name="export_to_file">導出到副案</string>
<string name="error_info">異常訊息</string>
<string name="error_type">異常</string>
<string name="error_file_name">文件</string>
<string name="error_throw_class">拋出 Class</string>
<string name="error_throw_method">抛出 Method</string>
<string name="error_type">異常型</string>
<string name="error_file_name"></string>
<string name="error_throw_class">丟擲 Class</string>
<string name="error_throw_method">丟擲 Method</string>
<string name="error_line_number">行號</string>
<string name="error_record_time">記錄時間</string>
<string name="copied">已復制</string>
<string name="copy_fail">複製失敗</string>
<string name="print_to_logcat">打印到控制</string>
<string name="print_to_logcat_success">打印到控制</string>
<string name="output_stack_success">出異常堆</string>
<string name="output_stack_fail">出異常堆失敗</string>
<string name="export_all">出全部</string>
<string name="print_to_logcat">輸出到控制</string>
<string name="print_to_logcat_success">輸出到控制</string>
<string name="output_stack_success">出異常堆</string>
<string name="output_stack_fail">出異常堆失敗</string>
<string name="export_all">出全部</string>
<string name="clear_all">清空全部</string>
<string name="errors_record">異常歷史記錄</string>
<string name="no_list_data">暫時沒有紀錄</string>
@@ -38,29 +38,29 @@
<string name="notice">提醒</string>
<string name="are_you_sure_clear_errors">你確認要清空全部異常紀錄嗎?</string>
<string name="all_errors_clear_success">全部異常紀錄已清空</string>
<string name="are_you_sure_export_all_errors">你確認要出全部日誌文件嗎?打包過程可能會花費一點時間。</string>
<string name="view_detail">查看詳情</string>
<string name="export_all_errors_success">出全部異常紀錄</string>
<string name="export_all_errors_fail">出全部異常紀錄失敗</string>
<string name="are_you_sure_export_all_errors">你確認要出全部日誌檔案嗎?打包過程可能會花費一點時間。</string>
<string name="view_detail">檢視詳情</string>
<string name="export_all_errors_success">出全部異常紀錄</string>
<string name="export_all_errors_fail">出全部異常紀錄失敗</string>
<string name="no_cpu_abi">無原生庫</string>
<string name="remove_record">移除紀錄</string>
<string name="are_you_sure_remove_record">你確認要移除這條紀錄嗎?</string>
<string name="got_it">我懂了</string>
<string name="restart_system">重新開機</string>
<string name="project_address">項目地址</string>
<string name="module_not_activated">模組未激活</string>
<string name="project_address">專案地址</string>
<string name="module_not_activated">模組未啟用</string>
<string name="module_version">模組版本:%1$s</string>
<string name="system_version">系統版本:%1$s</string>
<string name="module_is_activated">模組已激活</string>
<string name="display_settings">示設</string>
<string name="hide_app_icon_on_launcher">在桌面隱藏模組圖</string>
<string name="hide_app_icon_on_launcher_tip">隱藏模組圖標後界面可能會被關閉,將不會再在桌面示,你可以在 EdXposed、LSPosed 中找到模組設置並打開</string>
<string name="hide_app_icon_on_launcher_notice">注意:請務必在 LSPosed 中關閉“強制示桌面圖”功能</string>
<string name="about_module">此模組使用 YukiHookAPI 構建。 \n解更多 https://github.com/fankes/YukiHookAPI</string>
<string name="module_not_fully_activated">模組未完全激活,請重新開機</string>
<string name="module_is_activated">模組已啟用</string>
<string name="display_settings">示設</string>
<string name="hide_app_icon_on_launcher">在桌面隱藏模組圖</string>
<string name="hide_app_icon_on_launcher_tip">隱藏模組圖示後介面可能會被關閉,將不會再在桌面示,你可以在 EdXposed、LSPosed 中找到模組設定並開啟</string>
<string name="hide_app_icon_on_launcher_notice">注意:請務必在 LSPosed 中關閉“強制示桌面圖”功能</string>
<string name="about_module">此模組使用 YukiHookAPI 構建。 \n解更多 https://github.com/fankes/YukiHookAPI</string>
<string name="module_not_fully_activated">模組未完全啟用,請重新開機</string>
<string name="are_your_sure_restart_system">你確定要重新開機嗎?</string>
<string name="fast_restart">急速重開</string>
<string name="access_root_fail">取得 Root 利失</string>
<string name="access_root_fail">取得 Root 利失</string>
<string name="moment_ago">剛剛</string>
<string name="second_ago">秒前</string>
<string name="minute_ago">分鐘前</string>
@@ -68,20 +68,20 @@
<string name="day_ago">天前</string>
<string name="month_ago">月前</string>
<string name="year_ago">年前</string>
<string name="crash_process">異常程 \"%1$s\"</string>
<string name="share_error_stack">分享異常堆</string>
<string name="preference_settings">偏好設</string>
<string name="crash_process">異常程 \"%1$s\"</string>
<string name="share_error_stack">分享異常堆</string>
<string name="preference_settings">偏好設</string>
<string name="function_mgr">功能管理</string>
<string name="only_show_errors_in_front">前台程式展示錯誤對話框</string>
<string name="only_show_errors_in_main_process">程式主進程展示錯誤對話框</string>
<string name="only_show_errors_in_front">展示前台程式發生的異常</string>
<string name="only_show_errors_in_main_process">展示程式主進程發生的異常</string>
<string name="enable_apps_config_template">啟用程式配置模板</string>
<string name="mgr_apps_config_template">管理程式配置模板</string>
<string name="view_errors_record">查看異常歷史記錄</string>
<string name="view_muted_errors_apps">查看已忽略異常的程式</string>
<string name="only_show_errors_in_front_tip">啟用後,只有發生異常的程式處於前台(正在使用中)時才會展示錯誤對話框,發生異常的後台程式雖然不會展示錯誤對話框,但是它們都會被記錄到異常歷史記錄中。</string>
<string name="only_show_errors_in_main_process_tip">啟用後,只有程式發生的異常位於主進程(第一個 Application 實例對象)時才會展示錯誤對話框</string>
<string name="apps_config_template_tip">你可以在這裡對每個程式發生異常時,單獨配置其在發生異常時是否展示錯誤對話以及其它錯誤提示。</string>
<string name="view_errors_record_tip">在這裡,你可以找到從模組開始記錄以來的全部程式異常記錄,異常歷史記錄會持續保留直到你手動清空或恢復出廠設,你可以對記錄進行查看、導出和分享以及清空。</string>
<string name="view_errors_record">檢視異常歷史記錄</string>
<string name="view_muted_errors_apps">檢視已忽略異常的程式</string>
<string name="only_show_errors_in_front_tip">啟用後,只有程式處於前台(正在使用中)時才會展示其發生的異常,發生異常的後台程式雖然不會有任何提示信息,但是它們都會被記錄到異常歷史記錄中。</string>
<string name="only_show_errors_in_main_process_tip">啟用後,只有程式位於主進程(第一個 Application 實例對象)時才會展示其發生的異常</string>
<string name="apps_config_template_tip">你可以在這裡對每個程式發生異常時,單獨配置其在發生異常時是否展示錯誤對話視窗以及其它錯誤提示。\n關閉後全部程式將始終以錯誤對話視窗的形式展示異常信息。</string>
<string name="view_errors_record_tip">在這裡,你可以找到從模組開始記錄以來的全部程式異常記錄,異常歷史記錄會持續保留直到你手動清空或恢復出廠設,你可以對記錄進行檢視、匯出和分享以及清空。</string>
<string name="view_muted_errors_apps_tip">在這裡,你可以找到已被你以不同形式手動忽略異常的程式,這個列表將會在重新啟動後自動清空,你可以對這些已忽略的程式進行管理以及從忽略列表中移除它們。</string>
<string name="muted_errors_apps">已忽略異常的程式</string>
<string name="unmute">取消忽略</string>
@@ -91,46 +91,59 @@
<string name="filter_by_condition">按條件過濾</string>
<string name="no_list_result">沒有結果可以展示</string>
<string name="typo_app_name_pkg_name">可輸入 APP 名稱、包名</string>
<string name="result_contains_system_apps">結果包含系統程式</string>
<string name="clear_filters">清除條件</string>
<string name="result_count">共 %1$s 個結果</string>
<string name="loading">載中</string>
<string name="loading"></string>
<string name="when_errors_how_to_show_tip">你可以配置當程式崩潰時系統將如何向你展示錯誤提醒副案。</string>
<string name="show_errors_dialog">示錯誤對話</string>
<string name="show_errors_toast">示錯誤 Toast</string>
<string name="show_nothing">什麼也不</string>
<string name="show_errors_dialog">示錯誤對話視窗</string>
<string name="show_errors_toast">示錯誤 Toast</string>
<string name="show_nothing">什麼也不</string>
<string name="app_errors_statistics">程式異常統計</string>
<string name="total_errors">異常總數</string>
<string name="total_apps">程式總數</string>
<string name="most_errors_app">最多異常的程式</string>
<string name="most_errors_type">最多異常的</string>
<string name="most_errors_type">最多異常的型</string>
<string name="total_proportion_of_errors">異常總佔比</string>
<string name="total_errors_unit">%1$s 條</string>
<string name="total_apps_unit">%1$s 個 (含系統程式)</string>
<string name="generating_statistics">統計數據生成中</string>
<string name="module_not_fully_activated_tip">模組未完全激活,可能無法載當前設定項,建議重新開機後重試。</string>
<string name="generating_statistics">統計資料生成中</string>
<string name="module_not_fully_activated_tip">模組未完全啟用,可能無法載當前設定項,建議重新開機後重試。</string>
<string name="show_errors_notify">推送錯誤通知</string>
<string name="app_errors_tip">程式發生了未處理的異常而崩潰,點擊查看詳情或反饋給程式開發者。</string>
<string name="batch_operations">操作</string>
<string name="are_you_sure_apply_site_apps">你確認要一次性應用設給 %1$s 個程式嗎?</string>
<string name="errors_dialog_always_show_reopen">錯誤對話始終示“重新開啟”選項</string>
<string name="errors_dialog_always_show_reopen_tip">啟用後,在程式非首次異常時也將示“重新開啟”選項,若當前異常非主進程或程式無法打開則依然不會示此選項。</string>
<string name="app_errors_tip">程式發生了未處理的異常而崩潰,點選檢視詳情或反饋給程式開發者。</string>
<string name="batch_operations">操作</string>
<string name="are_you_sure_apply_site_apps">你確認要一次性應用設給 %1$s 個程式嗎?</string>
<string name="errors_dialog_always_show_reopen">錯誤對話視窗始終示“重新開啟”選項</string>
<string name="errors_dialog_always_show_reopen_tip">啟用後,在使用對話視窗展示程式異常時,非首次異常時也將示“重新開啟”選項,若當前異常非主進程或程式無法打開則依然不會示此選項。</string>
<string name="developer_notice">使用說明</string>
<string name="developer_notice_tip">此模組專為 Android 開發者而打造。 \n\n在可能無法連接電腦,不能進行 ADB 調試的時候,可過此模組來快速捕獲任意已安裝程式的任意異常,以便快速定位問題。 \n\n程式發生崩潰的錯誤日誌對開發者來說是無價的財富若你不是開發者你依然可以安裝此模組以便給開發者提供更多異常信息快速解決問題。</string>
<string name="developer_notice_tip">此模組專為 Android 開發者而打造。 \n\n在可能無法與連線電腦,不能進行 ADB 除錯的時候,可過此模組來快速捕獲任意已安裝程式的任意異常,以便快速定位問題。 \n\n程式發生崩潰的錯誤日誌對開發者來說是無價的財富若你不是開發者你依然可以安裝此模組以便給開發者提供更多異常資訊快速解決問題。</string>
<string name="warning">警告</string>
<string name="fast_restart_problem">部分定系統使用急速重開後可能會發生錯誤,仍然要繼續嗎?</string>
<string name="theme_settings">主題設</string>
<string name="enable_md3_app_errors_dialog">啟用 Material 3 風格錯誤對話</string>
<string name="enable_md3_app_errors_dialog_tip">此功能在 Android 目標版本為 12 及以上時默認啟用,動態主題色功能僅會在 Android 12 及以上版本生效。</string>
<string name="user_id">用户 %1$s</string>
<string name="unable_get_app_errors_record_tip">現在無法獲取異常記錄,你可能需要稍後在異常歷史記錄中查看,若問題持續出現,可能是你的系統關閉了錯誤報告收集功能。</string>
<string name="debug_log">調試日誌</string>
<string name="refresh">刷新</string>
<string name="fast_restart_problem">部分定系統使用急速重開後可能會發生錯誤,仍然要繼續嗎?</string>
<string name="theme_settings">主題設</string>
<string name="enable_md3_app_errors_dialog">啟用 Material 3 風格錯誤對話視窗</string>
<string name="enable_md3_app_errors_dialog_tip">此功能在 Android 目標版本為 12 及以上時預設啟用,動態主題色功能僅會在 Android 12 及以上版本生效。</string>
<string name="user_id">使用者 %1$s</string>
<string name="unable_get_app_errors_record_tip">現在無法獲取異常記錄,你可能需要稍後在異常歷史記錄中檢視,若問題持續出現,可能是你的系統關閉了錯誤報告收集功能。</string>
<string name="debug_logs">除錯日誌</string>
<string name="refresh">重新整理</string>
<string name="copy">複製</string>
<string name="export_all_logs_success">出全部調試日誌</string>
<string name="export_all_logs_fail">出全部調試日誌失敗</string>
<string name="this_contents_clear_when_restarts_tip">這裡的內容會在設備重開後自動清空</string>
<string name="when_logger_how_to_show_tip">你可以配置當前調試日誌示的優先過濾條件。</string>
<string name="export_all_logs_success">出全部除錯日誌</string>
<string name="export_all_logs_fail">出全部除錯日誌失敗</string>
<string name="this_contents_clear_when_restarts_tip">這裡的內容會在裝置重開後自動清空</string>
<string name="when_logger_how_to_show_tip">你可以配置當前除錯日誌示的優先順序過濾條件。</string>
<string name="enable_anonymous_statistics">啟用匿名統計</string>
<string name="enable_anonymous_statistics_tip">啟用後,我們將會使用 Microsoft App Center 來匿名送程式相關的診斷數據以幫助程式的功能更加完善,收集的信息是匿名的,我們無法追溯到獨立的裝置或用戶,啟用或關閉此功能需要重啟程式方可生效。</string>
<string name="enable_anonymous_statistics_tip">啟用後,我們將會使用 Microsoft App Center 來匿名送程式相關的診斷資料以幫助程式的功能更加完善,收集的資訊是匿名的,我們無法追溯到獨立的裝置或使用者,啟用或關閉此功能需要重啟程式方可生效。</string>
<string name="go_it_now">現在前往</string>
<string name="access_root_fail_tip">當前無法取得 Root 權限,請確認你的裝置已經被 Root 且同意授予 Root 權限。\n如果你正在使用 Magisk 並安裝了 Shamiko 模組,請確認當前是否正處於白名單模式。 (白名單模式將導致無法申請 Root 權限)</string>
<string name="global_config">全局配置</string>
<string name="follow_global_config">跟隨全局配置</string>
<string name="batch_operations_number">批次操作 %1$s 個程式</string>
<string name="user_apps">用戶程式</string>
<string name="all_apps">全部程式</string>
<string name="system_apps">系統程式</string>
<string name="disable_auto_wrap_error_stack_trace_content">禁止異常堆棧內容自動換行</string>
<string name="click_to_update">點按升級 %1$s</string>
<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>

View File

@@ -72,15 +72,15 @@
<string name="share_error_stack">Share error stack</string>
<string name="preference_settings">Preference Settings</string>
<string name="function_mgr">Function Management</string>
<string name="only_show_errors_in_front">Show error dialogs only foreground apps</string>
<string name="only_show_errors_in_main_process">Show error dialogs only main process apps</string>
<string name="only_show_errors_in_front">Show errors only foreground apps</string>
<string name="only_show_errors_in_main_process">Show errors only main process apps</string>
<string name="enable_apps_config_template">Enable apps config template</string>
<string name="mgr_apps_config_template">Management apps config template</string>
<string name="view_errors_record">View app errors record</string>
<string name="view_muted_errors_apps">View muted errors apps</string>
<string name="only_show_errors_in_front_tip">After enabling, the error dialog will only be displayed when the abnormal apps is in the foreground (in use). Although the abnormal background application will not display the error dialog, they will be recorded in the errors history.</string>
<string name="only_show_errors_in_main_process_tip">After enabling, the error dialog will only be displayed if the exception occurred in the application is in the main process (the first Application instance object).</string>
<string name="apps_config_template_tip">Here you can individually configure whether to display an error dialog and other error tips when an exception occurs for each apps.</string>
<string name="mgr_apps_config_template">Management Apps Config Template</string>
<string name="view_errors_record">View Apps Errors Record</string>
<string name="view_muted_errors_apps">View Muted Errors Apps</string>
<string name="only_show_errors_in_front_tip">After enabling, the error information will only be displayed when the abnormal apps is in the foreground (in use). Although the abnormal background application will not showing the error information, they will be recorded in the errors history.</string>
<string name="only_show_errors_in_main_process_tip">After enabling, the error information will only be displayed if the exception occurred in the application is in the main process (the first Application instance object).</string>
<string name="apps_config_template_tip">Here you can individually configure whether to display an error dialog and other error tips when an exception occurs for each apps.\nAfter disabling, all apps will always display errors information in the form of error dialogs.</string>
<string name="view_muted_errors_apps_tip">Here you can find apps that you have manually muted errors in different forms. This list will be automatically cleared after restarting. You can manage these ignored applications and remove them from the mute list.</string>
<string name="muted_errors_apps">Muted Errors Apps</string>
<string name="unmute">Unmute</string>
@@ -90,7 +90,6 @@
<string name="filter_by_condition">Filter by Condition</string>
<string name="no_list_result">No result to display</string>
<string name="typo_app_name_pkg_name">Typo apps name or package name</string>
<string name="result_contains_system_apps">Result contains system apps</string>
<string name="clear_filters">Clear Filters</string>
<string name="result_count">%1$s results found</string>
<string name="loading">Loading</string>
@@ -113,7 +112,7 @@
<string name="batch_operations">Batch Operations</string>
<string name="are_you_sure_apply_site_apps">Are you sure you want to apply settings to %1$s apps at once?</string>
<string name="errors_dialog_always_show_reopen">Error dialog always shows \"Reopen App\"</string>
<string name="errors_dialog_always_show_reopen_tip">After enabling, the \"Reopen App\" option will also be shows when the app is not errors for the first time. If the current errors is not the main process or the app cannot be opened, this option will still not be showing.</string>
<string name="errors_dialog_always_show_reopen_tip">After enabling, when using the dialog to display application exceptions, the \"Reopen App\" option will also be displayed when the errors is not the first time. If the current errors is not the main process or the application cannot be opened, this option will still not be displayed.</string>
<string name="developer_notice_tip">This module is specially designed for Android developers.\n\nWhen it is possible that the computer cannot be connected and ADB cannot be performed, this module can be used to quickly capture any exception of any installed apps, so as to quickly locate the problem.\n\nThe error log of apps crashing is an invaluable asset for developers. If you are not a developer, you can still install this module to provide developers with more exception information to quickly solve problems.</string>
<string name="developer_notice">Instructions</string>
<string name="warning">Warning</string>
@@ -124,7 +123,7 @@
<string name="enable_md3_app_errors_dialog_tip">This feature is enabled by default when the Android target version is 12 and above, and the theme dynamic colors function will only take effect on Android 12 and above.</string>
<string name="user_id">User %1$s</string>
<string name="unable_get_app_errors_record_tip">The errors record cannot be obtained now, you may need to check the errors history record later.\n\nIf the problem persists, it may be that your system has turned off the errors report collection function.</string>
<string name="debug_log">Debug Log</string>
<string name="debug_logs">Debug Logs</string>
<string name="refresh">Refresh</string>
<string name="copy">Copy</string>
<string name="export_all_logs_success">All debug logs exported</string>
@@ -134,4 +133,18 @@
<string name="microsoft_app_center" translatable="false">Microsoft App Center</string>
<string name="enable_anonymous_statistics">Enable anonymous statistics</string>
<string name="enable_anonymous_statistics_tip">After enabling, we will use the Microsoft App Center to anonymously send application-related diagnostic data to help the application function better.\nThe information collected is anonymous, and we cannot trace it back to an independent device or user.\nEnabling or disabling this feature requires restarting the app to take effect.</string>
<string name="go_it_now">Got It Now</string>
<string name="access_root_fail_tip">Root permission cannot be obtained currently, please confirm that your device has been rooted and agree to grant Root permission.\nIf you are using Magisk and installed the Shamiko module, please confirm whether it is currently in whitelist mode. (Whitelist mode will make it impossible to apply for Root permission)</string>
<string name="global_config">Global Config</string>
<string name="follow_global_config">Follow global config</string>
<string name="batch_operations_number">Batch Operations %1$s Apps</string>
<string name="user_apps">User apps</string>
<string name="system_apps">System apps</string>
<string name="all_apps">All apps</string>
<string name="disable_auto_wrap_error_stack_trace_content">Disable error stack trace automatic wrapping</string>
<string name="click_to_update">Click to update %1$s</string>
<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>

View File

@@ -4,11 +4,11 @@
<!-- Primary brand color. -->
<item name="colorPrimary">@color/colorPrimaryAccent</item>
<item name="colorPrimaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnPrimary">@color/colorPrimaryAccent</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/colorPrimaryAccent</item>
<item name="colorSecondaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnSecondary">@color/colorPrimaryAccent</item>
<item name="colorOnSecondary">@color/white</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">@color/white</item>
<item name="android:windowLightStatusBar">true</item>

View File

@@ -1,11 +1,23 @@
plugins {
id 'com.android.application' version '7.3.0' apply false
id 'com.android.library' version '7.3.0' apply false
id 'org.jetbrains.kotlin.android' version '1.7.10' 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.1"
appVersionCode = 3
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"
]
]
}

View File

@@ -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 27
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.3'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.6.1'
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.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -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() {

View File

@@ -1,6 +1,6 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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

View File

@@ -1,6 +1,6 @@
#Wed May 04 08:35:13 CST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME