mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2025-09-04 02:05:16 +08:00
Compare commits
92 Commits
Author | SHA1 | Date | |
---|---|---|---|
06324d104a
|
|||
0ed2cf4903
|
|||
d278faf7c9
|
|||
823c5c690f
|
|||
a4e1c82ad0
|
|||
07e13ccf04
|
|||
e19f8ea4af
|
|||
45c337d475
|
|||
8690e8afdb
|
|||
ec7dcabdb8
|
|||
0db82b5630
|
|||
1207dbb561
|
|||
785208a5bb
|
|||
3dbd80e06e
|
|||
f452bb2b32
|
|||
39b779eab9
|
|||
948095c016
|
|||
1a7775d5cc
|
|||
00cfca12b0
|
|||
556951c27c
|
|||
b478654008
|
|||
b47543e71a
|
|||
6ffe4bc15d
|
|||
bb94689baa
|
|||
6803b60dca
|
|||
82946e55fe
|
|||
910b3523a3
|
|||
a6fd0b699c
|
|||
6ddbfb5444
|
|||
fe4f91291e
|
|||
5e0336a434
|
|||
55a4d68a18
|
|||
358ac848e2
|
|||
567d0a7b6c
|
|||
4d5dadae8b
|
|||
9dce54f326
|
|||
37af877a7d
|
|||
59f5b27d19
|
|||
1299779868
|
|||
27c5e92879
|
|||
d22745f2ea
|
|||
dfcee71168
|
|||
5b30499cc3
|
|||
1889b848b0
|
|||
c349ee5dc7
|
|||
72e99bd4f2
|
|||
2d9550d5b9
|
|||
27e068898e
|
|||
92a0591d5b
|
|||
2a97dcbc06
|
|||
0dc12ae3e6
|
|||
075850d239
|
|||
c48a23f07d
|
|||
cf1796b7cb
|
|||
5bb1f38146
|
|||
a055d7f53d
|
|||
2eae45a640
|
|||
fd168f8810
|
|||
37580519db
|
|||
5144494f82
|
|||
01a6c1ffa5
|
|||
2aaa422a56
|
|||
7c0c1754e9
|
|||
587c718d0a
|
|||
6b2b538047
|
|||
6a5fee830c
|
|||
1d0cc1d24f
|
|||
eeaf386635
|
|||
a15b5b008d
|
|||
37962fa12f
|
|||
3798479c23
|
|||
a214f5773f
|
|||
aeda0f183e
|
|||
c6d5f07b8c
|
|||
5991d976b9
|
|||
3fb4e4f375
|
|||
8c4a1ea5f0
|
|||
7749cb9aeb
|
|||
9d9cb473e8
|
|||
afeb16e69d
|
|||
211343a6e7
|
|||
2c0cfb6863
|
|||
15293950a9
|
|||
88fe23ab9a
|
|||
|
150d2e8aa5 | ||
|
dd6e971a34 | ||
|
051da4df5f | ||
|
368a4b347f | ||
30b92770e9
|
|||
|
2491547e3e | ||
|
182a65255f | ||
aef2e0814a
|
100
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
100
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
name: Issues and Bugs Report / 问题与 BUG 反馈
|
||||
description: 问题反馈必须使用此模板进行提交 / Issues and bugs report must be submitted using this template
|
||||
labels: [ bug ]
|
||||
title: "[Issues and Bugs Report] (Briefly describe the cause of the problem)"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Please fill in the specific reason and steps to reproduce the problem below.
|
||||
|
||||
In the event of an exception, crash functional problem, you must submit a problem log, if not, your issues will be closed directly.
|
||||
|
||||
### 请在下方填写问题发生的具体原因和复现步骤。
|
||||
|
||||
发生异常、崩溃、闪退或功能性问题,必须提交问题 Log (日志),没有 Log 的 issues 将直接被关闭。
|
||||
- type: input
|
||||
attributes:
|
||||
label: Module App version / 模块版本
|
||||
description: |
|
||||
Please fill in the complete version of the Module App currently in use, for example: **1.0**
|
||||
请填写当前使用的模块完整版本号,例如:**1.0**
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Device model and system in used / 正在使用的设备型号以及使用的系统
|
||||
description: |
|
||||
Fill in the current device model and system here, the system such as (MIUI, ColorOS, OxygenOS, PE/Native)
|
||||
这里填写当前使用的设备型号以及使用的系统,系统例如 (MIUI、ColorOS、OxygenOS、PE/原生)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Android version / Android 版本
|
||||
options:
|
||||
- 13
|
||||
- 12L/12.1
|
||||
- 12
|
||||
- 11
|
||||
- 10
|
||||
- 9
|
||||
- 8.1
|
||||
- 8.0.0
|
||||
- 7.1.2
|
||||
- 7.1.1
|
||||
- 7.0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Xposed Framework name and version / Xposed 框架名称与版本号
|
||||
description: |
|
||||
Please fill in the currently used Xposed Framework, for example: **LSPosed 1.8.4 (version code)**
|
||||
请填写当前使用的 Xposed 框架,例如:**LSPosed 1.8.4 (次版本号)**
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Xposed Modules with the same scope / 与当前同作用域的 Xposed 模块
|
||||
description: |
|
||||
The scope of this module is the System Framework (Android System).
|
||||
To ensure that the problem is not caused by conflicts with other modules, please be sure to fill in the relevant modules that you are currently activating at the same time.
|
||||
If not, please fill in "none" directly below.
|
||||
此模块的作用域为系统框架 (Android 系统),为确保非其它模块冲突造成的问题,请一定要填写当前你同时激活的相关模块。
|
||||
若没有,请直接在下方填写“无”。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe in detail why the problem occurred / 详细描述问题发生的具体原因
|
||||
description: 请在下方详细描述问题发生的具体场景、复现步骤和经过,以便我们能够按照你所描述的步骤复现这个问题。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Provide module problem logs or necessary logs / 提供模块问题 Log 或必要 Log
|
||||
description: |
|
||||
If you are using LSPosed, you can view and filter the logs containing `AppErrorsTracking` in the log management.
|
||||
LSPosed 可在日志管理中查看并筛选包含 `AppErrorsTracking` 的日志。
|
||||
value: |
|
||||
<details><summary>Click to expand / 展开查看</summary><pre><code>
|
||||
|
||||
(Paste problem log here / 此处粘贴问题 Log)
|
||||
|
||||
</code></pre></details>
|
||||
<!-- When submitting, please delete the parentheses including the parentheses, paste the logs you copied, and do not break the code format -->
|
||||
<!-- 提交时请将括号内容包括括号全部删除,粘贴你复制的日志,不要破坏代码格式 -->
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Confirm the contents you submitted / 确认一下你提交的信息
|
||||
description: |
|
||||
In order to ensure the quality of issues and avoid wasting unnecessary time, issues that do not check the options below will be closed directly.
|
||||
Please make sure you have **checked the option below** before submitting.
|
||||
为了确保 issues 的质量和避免浪费不必要的时间,未勾选下方选项的 issues 将直接被关闭。
|
||||
请一定确保你已经**勾选下方的选项**后再提交。
|
||||
options:
|
||||
- label: I certify that the above contents is correct / 我确保上述信息准确无误
|
||||
required: false
|
77
.github/workflows/commit_ci.yml
vendored
Normal file
77
.github/workflows/commit_ci.yml
vendored
Normal 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
|
62
.github/workflows/pr_ci.yml
vendored
62
.github/workflows/pr_ci.yml
vendored
@@ -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
76
.github/workflows/pull_request_ci.yml
vendored
Normal 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
|
63
.github/workflows/push_ci.yml
vendored
63
.github/workflows/push_ci.yml
vendored
@@ -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
2
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
# Project exclude paths
|
||||
*.iml
|
||||
.gradle
|
||||
.secret
|
||||
.secret/APP_CENTER_SECRET
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
|
6
.secret/key_store_secret.json
Normal file
6
.secret/key_store_secret.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"keyAlias": "public",
|
||||
"keyPassword": "123456",
|
||||
"storeFileName": "universal.p12",
|
||||
"storePassword": "123456"
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/blob/master/LICENSE)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
|
||||
<br/><br/>
|
||||
@@ -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)
|
||||
|
||||
上述更新为手动发布的稳定版,具体更新内容可点击上方的文字前往指定的发布页面查看,稳定版的更新将会同时发布到上述地址中,同步更新。
|
||||
|
||||
## 发行状态说明
|
||||
|
||||

|
||||
|
||||
上述状态为当前稳定版与自动构建版本一致或当前代码改动与稳定版无功能差异。
|
||||
|
||||

|
||||
|
||||
上述状态为存在自动构建版本和新功能的更新但当前并未发布稳定版,处于预发行状态。
|
||||
|
||||

|
||||
|
||||
上述状态为当前发行的稳定版可能存在严重问题但并未及时进行修复且并未发布稳定版。
|
||||
|
||||
## Star History
|
||||
|
||||

|
||||
|
||||
## 许可证
|
||||
|
||||
- [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)
|
51
README.md
51
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/blob/master/LICENSE)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
[](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
|
||||
<br/><br/>
|
||||
@@ -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
|
||||
|
||||

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

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

|
||||
|
||||
The above status is that the currently released stable version may have serious problems but have not been fixed in time and the stable version
|
||||
has not been released.
|
||||
|
||||
## Star History
|
||||
|
||||

|
||||
|
||||
## License
|
||||
|
||||
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
```
|
||||
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
16
app/.gitignore
vendored
@@ -1,15 +1,3 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
/src/main/assets/xposed_init
|
||||
/src/main/resources/META-INF/yukihookapi_init
|
@@ -1,43 +1,54 @@
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'com.google.devtools.ksp' version '1.7.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'
|
||||
}
|
@@ -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
|
||||
|
@@ -1 +0,0 @@
|
||||
com.fankes.apperrorstracking.hook.AppErrorsTracking
|
@@ -1 +0,0 @@
|
||||
com.fankes.apperrorstracking.hook.HookEntry
|
@@ -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
|
||||
|
@@ -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
|
@@ -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
|
||||
}
|
@@ -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
|
@@ -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
|
@@ -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 {
|
||||
|
||||
/**
|
||||
|
@@ -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
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
@@ -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()
|
||||
}
|
||||
}
|
@@ -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]
|
||||
|
@@ -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
|
||||
}
|
@@ -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()
|
||||
}
|
||||
}
|
@@ -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)
|
@@ -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 {
|
||||
|
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
@@ -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
|
||||
|
@@ -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 名称
|
||||
|
@@ -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) }
|
||||
|
@@ -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?) {
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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()
|
||||
|
@@ -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}"
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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
|
@@ -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
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
@@ -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)
|
||||
|
@@ -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()
|
@@ -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
|
@@ -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] 自动设置选中状态 */
|
||||
|
@@ -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]
|
||||
|
@@ -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
|
||||
}
|
@@ -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
app/src/main/res/drawable/ic_global.xml
Normal file
30
app/src/main/res/drawable/ic_global.xml
Normal 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>
|
@@ -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" />
|
||||
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
||||
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
||||
|
24
build.gradle
24
build.gradle
@@ -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"
|
||||
]
|
||||
]
|
||||
}
|
@@ -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'
|
||||
}
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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() {
|
||||
|
||||
|
@@ -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
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Wed May 04 08:35:13 CST 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.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
|
||||
|
Reference in New Issue
Block a user