mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2025-09-01 08:45:16 +08:00
Compare commits
123 Commits
1.2
...
c301d5aab7
Author | SHA1 | Date | |
---|---|---|---|
c301d5aab7
|
|||
2c673280cc
|
|||
036a3f90e9
|
|||
5b6caf4769
|
|||
140b74fcab
|
|||
e59b7f501a
|
|||
ab03c74659
|
|||
a17e823a46
|
|||
5a60ef47b5
|
|||
|
9092e4d17a | ||
d70cf11a42
|
|||
8b73ad6610
|
|||
652c8383c1
|
|||
fbabb41f33
|
|||
f0b52d54c5
|
|||
40747a81ea | |||
cabf025082
|
|||
248daa5c80
|
|||
d253b26294
|
|||
d7c26180ae
|
|||
0facd0bcbf
|
|||
32855f4b61
|
|||
183999ca30
|
|||
0ef91d8578
|
|||
|
ff948fd1ae | ||
|
7c237dd024 | ||
28749a9226
|
|||
46b810e409
|
|||
b497173e02
|
|||
18c49405a8
|
|||
7d96a5097e
|
|||
|
773a5258df | ||
|
59b835dd2e | ||
|
69a96cb819 | ||
53c6f8a204
|
|||
d1480ae8f3
|
|||
|
91cfda35bc | ||
c774dbebf2
|
|||
e81ca4ec10
|
|||
cafb664c1a
|
|||
7939d25bb6
|
|||
1b6bc71e51
|
|||
9483cd7940
|
|||
d1bd76221d
|
|||
b13df7179a
|
|||
10a51c800b
|
|||
308afa7d6a
|
|||
7775c82caa
|
|||
60b3963df1
|
|||
b7ffdf3231
|
|||
5f96817d8c
|
|||
063b093eed
|
|||
fb388ee783
|
|||
389f3a69ee
|
|||
e0b8799a9b
|
|||
a64ad86e64
|
|||
2d42581e36
|
|||
936d66a81e
|
|||
66b9407b34
|
|||
c8a0631034
|
|||
32ca130da0
|
|||
273dca6042
|
|||
315dfd7e22
|
|||
b5baf8243e
|
|||
2064c01350
|
|||
72d76a486c
|
|||
d215440e83
|
|||
52367b5c41
|
|||
061b73fef7
|
|||
13e63a8fb3
|
|||
7eacb56857
|
|||
684456bb5b
|
|||
3b5aee9fb8
|
|||
86ba9749be
|
|||
4fff1d7c17
|
|||
c1e584d739
|
|||
ccc50d720e
|
|||
8df2fd5c14
|
|||
99472dedc4
|
|||
d22c5801b2
|
|||
0a87f13af7
|
|||
fd7bb9bf77
|
|||
b0a6c71300
|
|||
00512d6f95
|
|||
4bc2d84e7d
|
|||
|
75e6f1c16c | ||
886e8d1e37 | |||
a876854010 | |||
9834b6c8dd | |||
3a5a1df270 | |||
4afe549a8d
|
|||
3a7c97a1ac
|
|||
|
2b8b769aa9 | ||
|
fa793d764b | ||
4f9ef060ea
|
|||
47be6b10b7
|
|||
340034b531
|
|||
58aa7bc498
|
|||
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
|
33
.editorconfig
Normal file
33
.editorconfig
Normal file
@@ -0,0 +1,33 @@
|
||||
# noinspection EditorConfigKeyCorrectness
|
||||
[{*.kt,*.kts}]
|
||||
ktlint_standard_annotation = disabled
|
||||
ktlint_standard_filename = disabled
|
||||
ktlint_standard_wrapping = disabled
|
||||
ktlint_standard_import-ordering = enabled
|
||||
ktlint_standard_max-line-length = disabled
|
||||
ktlint_standard_multiline-if-else = disabled
|
||||
ktlint_standard_argument-list-wrapping = disabled
|
||||
ktlint_standard_parameter-list-wrapping = disabled
|
||||
ktlint_standard_trailing-comma-on-declaration-site = disabled
|
||||
ktlint_function_signature_body_expression_wrapping = multiline
|
||||
ktlint_standard_string-template-indent = disabled
|
||||
ktlint_standard_function-signature = disabled
|
||||
ktlint_standard_trailing-comma-on-call-site = disabled
|
||||
ktlint_standard_multiline-expression-wrapping = disabled
|
||||
ktlint_standard_no-empty-first-line-in-class-body = disabled
|
||||
ktlint_standard_if-else-wrapping = disabled
|
||||
ktlint_standard_if-else-bracing = disabled
|
||||
ktlint_standard_statement-wrapping = disabled
|
||||
ktlint_standard_blank-line-before-declaration = disabled
|
||||
ktlint_standard_no-empty-file = disabled
|
||||
ktlint_standard_property-naming = disabled
|
||||
ktlint_standard_function-naming = disabled
|
||||
ktlint_standard_chain-method-continuation = disabled
|
||||
ktlint_standard_class-signature = disabled
|
||||
ktlint_standard_condition-wrapping = disabled
|
||||
ktlint_standard_class-signature = disabled
|
||||
ij_continuation_indent_size = 2
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = false
|
||||
max_line_length = 150
|
101
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
101
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
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:
|
||||
- 14
|
||||
- 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
77
.github/workflows/commit_ci.yml
vendored
@@ -13,17 +13,36 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build CI
|
||||
if: ${{ success() }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MODULE_APK_OUTPUT_PATH: 'module-app/build/outputs/apk'
|
||||
DEMO_APK_OUTPUT_PATH: 'demo-app/build/outputs/apk'
|
||||
APP_CENTER_SECRET: ${{ secrets.APP_CENTER_SECRET }}
|
||||
TG_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
TG_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
COMMIT_MESSAGE: |+
|
||||
New push to GitHub\!
|
||||
```
|
||||
${{ github.event.head_commit.message }}
|
||||
```by `${{ github.event.head_commit.author.name }}`
|
||||
See commit detail [here](${{ github.event.head_commit.url }})
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare GitHub Env
|
||||
run: |
|
||||
GITHUB_SHA=${{ github.sha }}
|
||||
GITHUB_CI_COMMIT_ID=${GITHUB_SHA:0:7}
|
||||
echo "GITHUB_CI_COMMIT_ID=$GITHUB_CI_COMMIT_ID" >> $GITHUB_ENV
|
||||
- name: Setup cmake
|
||||
uses: jwlawson/actions-setup-cmake@v1
|
||||
with:
|
||||
cmake-version: '3.22.1'
|
||||
- name: Prepare Java 11
|
||||
uses: actions/setup-java@v3
|
||||
- name: Prepare Java 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 11
|
||||
java-version: 21
|
||||
java-package: jdk
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
@@ -34,7 +53,7 @@ jobs:
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
!~/.gradle/caches/build-cache-*
|
||||
key: gradle-deps-core-${{ hashFiles('**/build.gradle') }}
|
||||
key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }}
|
||||
restore-keys: |
|
||||
gradle-deps
|
||||
- name: Cache Gradle Build
|
||||
@@ -47,17 +66,43 @@ jobs:
|
||||
gradle-builds
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
./gradlew :app:assembleRelease
|
||||
./gradlew :module-app:assembleDebug
|
||||
./gradlew :module-app:assembleRelease
|
||||
./gradlew :demo-app:assembleDebug
|
||||
./gradlew :demo-app:assembleRelease
|
||||
echo "APK_FILE=$(find app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_APK_FILE=$(find demo-app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
- name: Upload Artifacts(module)
|
||||
uses: actions/upload-artifact@v3
|
||||
echo "MODULE_DEBUG_APK_PATH=$(find ${{ env.MODULE_APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "MODULE_RELEASE_APK_PATH=$(find ${{ env.MODULE_APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_DEBUG_APK_PATH=$(find ${{ env.DEMO_APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_RELEASE_APK_PATH=$(find ${{ env.DEMO_APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV
|
||||
- name: Upload Artifacts (Module-Debug)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.APK_FILE }}
|
||||
name: module-release
|
||||
- name: Upload Artifacts(demo-app)
|
||||
uses: actions/upload-artifact@v3
|
||||
path: ${{ env.MODULE_DEBUG_APK_PATH }}
|
||||
name: AppErrorsTracking-module-debug-${{ github.event.head_commit.id }}
|
||||
- name: Upload Artifacts (Module-Release)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.DEMO_APK_FILE }}
|
||||
name: demo-release
|
||||
path: ${{ env.MODULE_RELEASE_APK_PATH }}
|
||||
name: AppErrorsTracking-module-release-${{ github.event.head_commit.id }}
|
||||
- name: Upload Artifacts (Demo-Debug)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.DEMO_DEBUG_APK_PATH }}
|
||||
name: AppErrorsTracking-demo-debug-${{ github.event.head_commit.id }}
|
||||
- name: Upload Artifacts (Demo-Release)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.DEMO_RELEASE_APK_PATH }}
|
||||
name: AppErrorsTracking-demo-release-${{ github.event.head_commit.id }}
|
||||
- name: Post Artifacts to Telegram
|
||||
run: |
|
||||
export module_debug=$(find ${{ env.MODULE_APK_OUTPUT_PATH }}/debug -name "*.apk")
|
||||
export module_release=$(find ${{ env.MODULE_APK_OUTPUT_PATH }}/release -name "*.apk")
|
||||
export demo_debug=$(find ${{ env.DEMO_APK_OUTPUT_PATH }}/debug -name "*.apk")
|
||||
export demo_release=$(find ${{ env.DEMO_APK_OUTPUT_PATH }}/release -name "*.apk")
|
||||
ESCAPED=`python3 -c 'import json,os,urllib.parse; msg = json.dumps(os.environ["COMMIT_MESSAGE"]); print(urllib.parse.quote(msg if len(msg) <= 1024 else json.dumps(os.environ["COMMIT_URL"])))'`
|
||||
curl -v "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMediaGroup?chat_id=${TG_CHAT_ID}&media=%5B%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Fmodule_debug%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Fmodule_release%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Fdemo_debug%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Fdemo_release%22%2C%22parse_mode%22%3A%22MarkdownV2%22%2C%22caption%22:${ESCAPED}%7D%5D" \
|
||||
-F module_debug="@$module_debug" \
|
||||
-F module_release="@$module_release" \
|
||||
-F demo_debug="@$demo_debug" \
|
||||
-F demo_release="@$demo_release"
|
57
.github/workflows/pull_request_ci.yml
vendored
57
.github/workflows/pull_request_ci.yml
vendored
@@ -11,18 +11,27 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Pull request check
|
||||
name: Pull Request Check
|
||||
if: ${{ success() }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MODULE_APK_OUTPUT_PATH: 'module-app/build/outputs/apk'
|
||||
DEMO_APK_OUTPUT_PATH: 'demo-app/build/outputs/apk'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare GitHub Env
|
||||
run: |
|
||||
GITHUB_SHA=${{ github.sha }}
|
||||
GITHUB_CI_COMMIT_ID=${GITHUB_SHA:0:7}
|
||||
echo "GITHUB_CI_COMMIT_ID=$GITHUB_CI_COMMIT_ID" >> $GITHUB_ENV
|
||||
- name: Setup cmake
|
||||
uses: jwlawson/actions-setup-cmake@v1
|
||||
with:
|
||||
cmake-version: '3.22.1'
|
||||
- name: Prepare Java 11
|
||||
uses: actions/setup-java@v3
|
||||
- name: Prepare Java 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 11
|
||||
java-version: 21
|
||||
java-package: jdk
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
@@ -33,7 +42,7 @@ jobs:
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
!~/.gradle/caches/build-cache-*
|
||||
key: gradle-deps-core-${{ hashFiles('**/build.gradle') }}
|
||||
key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }}
|
||||
restore-keys: |
|
||||
gradle-deps
|
||||
- name: Cache Gradle Build
|
||||
@@ -46,17 +55,31 @@ jobs:
|
||||
gradle-builds
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
./gradlew :app:assembleRelease
|
||||
./gradlew :module-app:assembleDebug
|
||||
./gradlew :module-app:assembleRelease
|
||||
./gradlew :demo-app:assembleDebug
|
||||
./gradlew :demo-app:assembleRelease
|
||||
echo "APK_FILE=$(find app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_APK_FILE=$(find demo-app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
|
||||
- name: Upload Artifacts(module)
|
||||
uses: actions/upload-artifact@v3
|
||||
echo "MODULE_DEBUG_APK_PATH=$(find ${{ env.MODULE_APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "MODULE_RELEASE_APK_PATH=$(find ${{ env.MODULE_APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_DEBUG_APK_PATH=$(find ${{ env.DEMO_APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "DEMO_RELEASE_APK_PATH=$(find ${{ env.DEMO_APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV
|
||||
- name: Upload Artifacts (Module-Debug)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.APK_FILE }}
|
||||
name: module-release
|
||||
- name: Upload Artifacts(demo-app)
|
||||
uses: actions/upload-artifact@v3
|
||||
path: ${{ env.MODULE_DEBUG_APK_PATH }}
|
||||
name: AppErrorsTracking-module-debug-${{ github.event.head_commit.id }}
|
||||
- name: Upload Artifacts (Module-Release)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.DEMO_APK_FILE }}
|
||||
name: demo-release
|
||||
path: ${{ env.MODULE_RELEASE_APK_PATH }}
|
||||
name: AppErrorsTracking-module-release-${{ github.event.head_commit.id }}
|
||||
- name: Upload Artifacts (Demo-Debug)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.DEMO_DEBUG_APK_PATH }}
|
||||
name: AppErrorsTracking-demo-debug-${{ github.event.head_commit.id }}
|
||||
- name: Upload Artifacts (Demo-Release)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.DEMO_RELEASE_APK_PATH }}
|
||||
name: AppErrorsTracking-demo-release-${{ github.event.head_commit.id }}
|
122
.gitignore
vendored
122
.gitignore
vendored
@@ -1,20 +1,110 @@
|
||||
# Project exclude paths
|
||||
## Fully .gtignore for IntelliJ, Android Studio and Gradle based Java projects
|
||||
## References:
|
||||
## - https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
## - https://github.com/android/platform-samples/blob/main/.gitignore
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
.idea/.name
|
||||
.idea/artifacts
|
||||
.idea/compiler.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/modules.xml
|
||||
.idea/*.iml
|
||||
.idea/modules
|
||||
.idea/caches
|
||||
.idea/material_theme**
|
||||
.idea/other.xml
|
||||
*.iml
|
||||
.gradle
|
||||
.secret
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
*.ipr
|
||||
|
||||
# Kotlin
|
||||
.kotlin
|
||||
|
||||
# Misc
|
||||
.idea/misc.xml
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
# Android studio 3.1+ additional
|
||||
.idea/deployment*.xml
|
||||
.idea/assetWizardSettings.xml
|
||||
.idea/androidTestResultsUserPreferences.xml
|
||||
|
||||
# Android projects
|
||||
**/local.properties
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
/app/releaseHasController/
|
||||
/app/debug/
|
||||
/app/release/
|
||||
|
||||
# Gradle projects
|
||||
.gradle
|
||||
build/
|
||||
|
||||
# Mkdocs temporary serving folder
|
||||
docs-gen
|
||||
site
|
||||
*.bak
|
||||
.idea/appInsightsSettings.xml
|
||||
|
||||
# Mac OS
|
||||
.DS_Store
|
3
.idea/.gitignore
generated
vendored
3
.idea/.gitignore
generated
vendored
@@ -1,3 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
6
.idea/AndroidProjectSystem.xml
generated
Normal file
6
.idea/AndroidProjectSystem.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
6
.idea/compiler.xml
generated
6
.idea/compiler.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
17
.idea/deploymentTargetDropDown.xml
generated
17
.idea/deploymentTargetDropDown.xml
generated
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<targetSelectedWithDropDown>
|
||||
<Target>
|
||||
<type value="QUICK_BOOT_TARGET" />
|
||||
<deviceKey>
|
||||
<Key>
|
||||
<type value="VIRTUAL_DEVICE_PATH" />
|
||||
<value value="$USER_HOME$/.android/avd/Pixel_2_API_29.avd" />
|
||||
</Key>
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</targetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2022-05-19T02:00:42.222889Z" />
|
||||
</component>
|
||||
</project>
|
21
.idea/gradle.xml
generated
21
.idea/gradle.xml
generated
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="Embedded JDK" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
<option value="$PROJECT_DIR$/demo-app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
2
.idea/inspectionProfiles/Project_Default.xml
generated
2
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,10 +1,12 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="CheckImageSize" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="YAMLSchemaValidation" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
6
.idea/kotlinc.xml
generated
Normal file
6
.idea/kotlinc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.1.10" />
|
||||
</component>
|
||||
</project>
|
7
.idea/ktlint-plugin.xml
generated
Normal file
7
.idea/ktlint-plugin.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KtLint plugin">
|
||||
<ktlintMode>MANUAL</ktlintMode>
|
||||
<formatOnSave>false</formatOnSave>
|
||||
</component>
|
||||
</project>
|
6
.idea/ktlint.xml
generated
Normal file
6
.idea/ktlint.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KtlintProjectConfiguration">
|
||||
<treatAsErrors>false</treatAsErrors>
|
||||
</component>
|
||||
</project>
|
10
.idea/migrations.xml
generated
Normal file
10
.idea/migrations.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
48
.idea/misc.xml
generated
48
.idea/misc.xml
generated
@@ -1,48 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="app/src/main/res/drawable-night/bg_black_round.xml" value="0.2425" />
|
||||
<entry key="app/src/main/res/drawable-night/bg_dark_round.xml" value="0.256" />
|
||||
<entry key="app/src/main/res/drawable-night/bg_green_round.xml" value="0.2425" />
|
||||
<entry key="app/src/main/res/drawable-night/bg_orange_round.xml" value="0.2425" />
|
||||
<entry key="app/src/main/res/drawable-night/bg_yellow_round.xml" value="0.2425" />
|
||||
<entry key="app/src/main/res/drawable/bg_black_round.xml" value="0.2425" />
|
||||
<entry key="app/src/main/res/drawable/bg_dark_round.xml" value="0.2425" />
|
||||
<entry key="app/src/main/res/drawable/bg_green_round.xml" value="0.2425" />
|
||||
<entry key="app/src/main/res/drawable/bg_orange_round.xml" value="0.2425" />
|
||||
<entry key="app/src/main/res/drawable/ic_debug.xml" value="0.2555" />
|
||||
<entry key="app/src/main/res/drawable/ic_exception.xml" value="0.256" />
|
||||
<entry key="app/src/main/res/drawable/ic_filter.xml" value="0.256" />
|
||||
<entry key="app/src/main/res/drawable/ic_share.xml" value="0.241" />
|
||||
<entry key="app/src/main/res/drawable/ic_statistics.xml" value="0.256" />
|
||||
<entry key="app/src/main/res/layout/activity_app_errors_detail.xml" value="0.43697916666666664" />
|
||||
<entry key="app/src/main/res/layout/activity_app_errors_display.xml" value="0.4359375" />
|
||||
<entry key="app/src/main/res/layout/activity_app_errors_ignored.xml" value="0.43697916666666664" />
|
||||
<entry key="app/src/main/res/layout/activity_app_errors_muted.xml" value="0.43697916666666664" />
|
||||
<entry key="app/src/main/res/layout/activity_app_errors_record.xml" value="0.43697916666666664" />
|
||||
<entry key="app/src/main/res/layout/activity_config.xml" value="0.4359375" />
|
||||
<entry key="app/src/main/res/layout/activity_main.xml" value="0.4359375" />
|
||||
<entry key="app/src/main/res/layout/adapter_app_errors_ignored.xml" value="0.43697916666666664" />
|
||||
<entry key="app/src/main/res/layout/adapter_app_errors_muted.xml" value="0.43697916666666664" />
|
||||
<entry key="app/src/main/res/layout/adapter_app_errors_record.xml" value="0.43697916666666664" />
|
||||
<entry key="app/src/main/res/layout/adapter_app_info.xml" value="0.43697916666666664" />
|
||||
<entry key="app/src/main/res/layout/dia_app_config.xml" value="0.43697916666666664" />
|
||||
<entry key="app/src/main/res/layout/dia_app_errors_display.xml" value="0.4359375" />
|
||||
<entry key="app/src/main/res/layout/dia_app_errors_statistics.xml" value="0.43697916666666664" />
|
||||
<entry key="app/src/main/res/layout/dia_apps_filter.xml" value="0.43697916666666664" />
|
||||
<entry key="app/src/main/res/menu/menu_list_detail_action.xml" value="0.43697916666666664" />
|
||||
<entry key="demo-app/src/main/res/layout/activity_main.xml" value="0.4083333333333333" />
|
||||
<entry key="demo-app/src/main/res/layout/activity_multi_process.xml" value="0.4359375" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
17
.idea/runConfigurations.xml
generated
Normal file
17
.idea/runConfigurations.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
1
.secret/.gitignore
vendored
Normal file
1
.secret/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/secret.properties
|
121
README-ja-JP.md
Normal file
121
README-ja-JP.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# AppErrorsTracking
|
||||
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/blob/master/LICENSE)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/actions/workflows/commit_ci.yml)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||

|
||||

|
||||
|
||||
[](https://t.me/AppErrorsTracking_CI)
|
||||
[](https://t.me/XiaofangInternet)
|
||||
[](https://qm.qq.com/cgi-bin/qm/qr?k=dp2h5YhWiga9WWb_Oh7kSHmx01X8I8ii&jump_from=webapi&authKey=Za5CaFP0lk7+Zgsk2KpoBD7sSaYbeXbsDgFjiWelOeH4VSionpxFJ7V0qQBSqvFM)
|
||||
[](https://pd.qq.com/s/44gcy28h)
|
||||
|
||||
<img src="img-src/icon.png" width = "100" height = "100" alt="LOGO"/>
|
||||
|
||||
[English](README.md) | [简体中文](README-zh-CN.md) | 日本語
|
||||
|
||||
アプリのエラーダイアログに機能を追加し、カスタムROMによって削除されたダイアログを修正することで、Android開発者に最高の体験を提供します。
|
||||
|
||||
このプロジェクトは、どのAndroidシステム上でも使用できるXposedモジュールであり、現在は**LSPosed**でのみテストされています。
|
||||
|
||||
このXposedモジュールは、Android開発者のために特別に設計されています。
|
||||
|
||||
PCに接続できない、ADBが実行できない状態である場合にこのモジュールを使用して、インストールされているアプリのエラーをキャプチャする事で問題を迅速に特定することができます。
|
||||
|
||||
アプリがクラッシュしたときのエラーログは、開発者にとって貴重な財産です。もしあなたが開発者でなくても、このモジュールをインストールする事で開発者への貢献に繋がるでしょう。
|
||||
|
||||
> 最小サポート Android 7.0
|
||||
|
||||
## プロジェクトの理由
|
||||
|
||||
本当に理解不能ですが、中国本土のAndroid ROMは、MIUI(安定版を除く)を除いて、アプリのクラッシュ時のダイアログボックス(強制終了ダイアログ)を削除しています。私はシステムフレームワークを逆コンパイルして本当に削除されていることを確認するまで、これは当たり前の機能だと思っていました。
|
||||
|
||||
プロダクトマネージャーは、ユーザーにエラーを表示させずにアプリをクラッシュさせて直接終了する事が最善の解決策と考えているのでしょうか?
|
||||
それとも **隠された秘密** があるのでしょうか?
|
||||
|
||||
## 動作の原理
|
||||
|
||||
`Thread.UncaughtExceptionHandler`とは異なり、システムフレームワークをインジェクトする事でアプリのエラーを全方向からキャプチャするネイティブメソッドを使用します。これは、元の例外監視よりもパフォーマンスに優れています。
|
||||
|
||||
同時に、システムレベルの例外のキャプチャは、ネイティブプラットフォームの`スタックトレース`もキャプチャ可能です。
|
||||
|
||||
## 注意事項
|
||||
|
||||
システムによってネイティブにキャプチャされるエラーは、アプリ自体によって処理されないエラーのみです。アプリ自体に**Bugly**のような、エラーを自動的に収集するためのカスタムの `Thread.UncaughtExceptionHandler` がある場合、システムはアプリが実際にクラッシュ **(強制終了)** したかどうかを取得できません。
|
||||
|
||||
## 機能のリスト
|
||||
|
||||
- [x] システムのアプリエラーダイアログを完全に置き換え
|
||||
|
||||
- [x] 各アプリの例外をログに記録して再起動まで保持
|
||||
|
||||
- [x] エラーのスタックトレース関数のコピー、共有、エクスポート
|
||||
|
||||
- [x] エラー履歴記録機能、これは通知バータイルの「エラー履歴の記録」およびモジュールのメインインターフェースから入力
|
||||
|
||||
- [x] アプリのエラー統計機能
|
||||
|
||||
- [x] マルチプロセスアプリのエラー表示機能
|
||||
|
||||
## 翻訳の貢献
|
||||
|
||||
このプロジェクトは、あなたの国の言語に翻訳する事を歓迎します。
|
||||
|
||||
## リリースチャンネル
|
||||
|
||||
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub CI](https://github.com/KitsunePie/AppErrorsTracking/actions/workflows/commit_ci.yml) | CI 自動ビルド (テスト版) |
|
||||
|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-----------------------------------|
|
||||
|
||||
| <img src="https://github.com/peter-iakovlev/Telegram/blob/public/Icon.png?raw=true" width = "30" height = "30" alt="LOGO"/> | [Telegram CI チャンネル](https://t.me/AppErrorsTracking_CI) | CI 自動ビルド (テスト版) |
|
||||
|-----------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------|-----------------------------------|
|
||||
|
||||
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub リリース](https://github.com/KitsunePie/AppErrorsTracking/releases) | 正式版 (安定版) |
|
||||
|------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|---------------------------------|
|
||||
|
||||
| <img src="https://avatars.githubusercontent.com/u/78217009?s=200&v=4?raw=true" width = "30" height = "30" alt="LOGO"/> | [Xposed モジュールのリポジトリ](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases) | 正式版 (安定版) |
|
||||
|------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|---------------------------------|
|
||||
|
||||
このXposedモジュールのリリースは上記のURLに限定されています。
|
||||
|
||||
他の非公式チャンネルからダウンロードされたバージョンで及ぼした問題は一切関係はありません。
|
||||
|
||||
## プロモーション
|
||||
|
||||
<!--suppress HtmlDeprecatedAttribute -->
|
||||
<div align="center">
|
||||
<h2>ねぇ、きいて! 👋</h2>
|
||||
<h3>ここでは、Androidの開発ツールやUIデザイン、Gradleプラグイン、Xposedモジュール、実用的なソフトウェアなどの関連プロジェクトを紹介します。</h3>
|
||||
<h3>もしも以下のプロジェクトであなたのお役に立てたのであれば、私にStarを付けてください!</h3>
|
||||
<h3>すべてのプロジェクトは無料でオープンソースであり、対応するオープンソースライセンス契約に基づいています。</h3>
|
||||
<h1><a href="https://github.com/fankes/fankes/blob/main/project-promote/README.md">→ 私のプロジェクトについてはこちらをクリック ←</a></h1>
|
||||
</div>
|
||||
|
||||
## Starの推移
|
||||
|
||||

|
||||
|
||||
## ライセンス
|
||||
|
||||
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
```
|
||||
Copyright (C) 2017 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
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program 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
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
```
|
||||
|
||||
Powered by [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI)
|
||||
|
||||
Copyright © 2017 Fankes Studio(qzmmcn@163.com)
|
@@ -1,12 +1,19 @@
|
||||
# AppErrorsTracking
|
||||
|
||||
[](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/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
|
||||
<br/><br/>
|
||||
[English](https://github.com/KitsunePie/AppErrorsTracking/blob/master/README.md) | 简体中文
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/blob/master/LICENSE)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/actions/workflows/commit_ci.yml)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||

|
||||

|
||||
|
||||
[](https://t.me/AppErrorsTracking_CI)
|
||||
[](https://t.me/XiaofangInternet)
|
||||
[](https://qm.qq.com/cgi-bin/qm/qr?k=dp2h5YhWiga9WWb_Oh7kSHmx01X8I8ii&jump_from=webapi&authKey=Za5CaFP0lk7+Zgsk2KpoBD7sSaYbeXbsDgFjiWelOeH4VSionpxFJ7V0qQBSqvFM)
|
||||
[](https://pd.qq.com/s/44gcy28h)
|
||||
|
||||
<img src="img-src/icon.png" width = "100" height = "100" alt="LOGO"/>
|
||||
|
||||
[English](README.md) | 简体中文 | [日本語](README-ja-JP.md)
|
||||
|
||||
为原生 FC 对话框增加更多功能并修复国内定制 ROM 删除 FC 对话框的问题,给 Android 开发者带来更好的体验。
|
||||
|
||||
@@ -39,53 +46,72 @@
|
||||
|
||||
## 功能列表
|
||||
|
||||
- 完全取代系统的应用错误对话框
|
||||
- [x] 完全取代系统的应用错误对话框
|
||||
|
||||
- 记录每个应用的异常,直到重新启动前持续保留
|
||||
- [x] 记录每个应用的异常,直到重新启动前持续保留
|
||||
|
||||
- 复制、分享、导出异常堆栈功能
|
||||
- [x] 复制、分享、导出异常堆栈功能
|
||||
|
||||
- 异常历史记录功能,可通过通知栏磁贴“异常历史记录”进入和模块主界面进入
|
||||
- [x] 异常历史记录功能,可通过通知栏磁贴“异常历史记录”进入和模块主界面进入
|
||||
|
||||
- 应用异常统计功能
|
||||
- [x] 应用异常统计功能
|
||||
|
||||
- 多进程应用的异常显示功能
|
||||
- [x] 多进程应用的异常显示功能
|
||||
|
||||
## 翻译贡献
|
||||
|
||||
欢迎为此项目做出贡献,将其翻译为您国家的语言。
|
||||
|
||||
## 发行渠道说明
|
||||
## 发行渠道
|
||||
|
||||
- [Automatic Build on Commit](https://github.com/KitsunePie/AppErrorsTracking/actions/workflows/commit_ci.yml)
|
||||
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub CI](https://github.com/KitsunePie/AppErrorsTracking/actions/workflows/commit_ci.yml) | CI 自动构建 (测试版) |
|
||||
|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|---------------|
|
||||
|
||||
上述更新为代码 `commit` 后自动触发,具体更新内容可点击上方的文字前往 **Github Actions** 进行查看,本更新由开源的流程自动编译发布,**不保证其稳定性**,所发布的版本**仅供测试**,且不会特殊说明甚至可能会变更版本号或保持与当前稳定版相同的版本号。
|
||||
| <img src="https://github.com/peter-iakovlev/Telegram/blob/public/Icon.png?raw=true" width = "30" height = "30" alt="LOGO"/> | [Telegram CI 频道](https://t.me/AppErrorsTracking_CI) | CI 自动构建 (测试版) |
|
||||
|-----------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|---------------|
|
||||
|
||||
- [Release](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
- [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
|
||||
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub Releases](https://github.com/KitsunePie/AppErrorsTracking/releases) | 正式版 (稳定版) |
|
||||
|------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|-----------|
|
||||
|
||||
上述更新为手动发布的稳定版,具体更新内容可点击上方的文字前往指定的发布页面查看,稳定版的更新将会同时发布到上述地址中,同步更新。
|
||||
| <img src="https://avatars.githubusercontent.com/u/78217009?s=200&v=4?raw=true" width = "30" height = "30" alt="LOGO"/> | [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases) | 正式版 (稳定版) |
|
||||
|------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|-----------|
|
||||
|
||||
## 发行状态说明
|
||||
本模块发布地址仅限于上述所列出的地址,从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
|
||||
|
||||

|
||||
## 注意事项
|
||||
|
||||
上述状态为当前稳定版与自动构建版本一致或当前代码改动与稳定版无功能差异。
|
||||
<h3>1. 本软件免费、由兴趣驱动开发,仅供学习交流使用。如果你是从其他非官方渠道付费获得本软件,可能已遭遇欺诈,欢迎向我们举报可疑行为。</h3>
|
||||
|
||||

|
||||
<h3>2. 本软件采用 <strong>GNU Affero General Public License (AGPL 3.0)</strong> 许可证。根据该许可证的要求:</h3>
|
||||
|
||||
上述状态为存在自动构建版本和新功能的更新但当前并未发布稳定版,处于预发行状态。
|
||||
- 任何衍生作品必须采用相同的 AGPL 许可证
|
||||
- 分发本软件或其修改版本时,必须提供完整的源代码
|
||||
- 必须保留原始的版权声明及许可证信息
|
||||
- 不得额外施加限制来限制他人对本软件的自由使用
|
||||
|
||||

|
||||
<h3>3. 我们鼓励在遵守 AGPL 3.0 条款的前提下进行自由传播和改进,但请尊重作者署名权,勿冒用原作者名义。</h3>
|
||||
|
||||
上述状态为当前发行的稳定版可能存在严重问题但并未及时进行修复且并未发布稳定版。
|
||||
## 项目推广
|
||||
|
||||
<!--suppress HtmlDeprecatedAttribute -->
|
||||
<div align="center">
|
||||
<h2>嘿,还请君留步!👋</h2>
|
||||
<h3>这里有 Android 开发工具、UI 设计、Gradle 插件、Xposed 模块和实用软件等相关项目。</h3>
|
||||
<h3>如果下方的项目能为你提供帮助,不妨为我点个 star 吧!</h3>
|
||||
<h3>所有项目免费、开源,遵循对应开源许可协议。</h3>
|
||||
<h1><a href="https://github.com/fankes/fankes/blob/main/project-promote/README-zh-CN.md">→ 查看更多关于我的项目,请点击这里 ←</a></h1>
|
||||
</div>
|
||||
|
||||
## Star History
|
||||
|
||||

|
||||
|
||||
## 许可证
|
||||
|
||||
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
```
|
||||
Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
Copyright (C) 2017 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
|
||||
@@ -98,9 +124,9 @@ 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
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
```
|
||||
|
||||
Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI)
|
||||
Powered by [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI)
|
||||
|
||||
版权所有 © 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
版权所有 © 2017 Fankes Studio(qzmmcn@163.com)
|
||||
|
99
README.md
99
README.md
@@ -1,21 +1,28 @@
|
||||
# AppErrorsTracking
|
||||
|
||||
[](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/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
|
||||
<br/><br/>
|
||||
English | [简体中文](https://github.com/KitsunePie/AppErrorsTracking/blob/master/README-zh-CN.md)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/blob/master/LICENSE)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/actions/workflows/commit_ci.yml)
|
||||
[](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||

|
||||

|
||||
|
||||
[](https://t.me/AppErrorsTracking_CI)
|
||||
[](https://t.me/XiaofangInternet)
|
||||
[](https://qm.qq.com/cgi-bin/qm/qr?k=dp2h5YhWiga9WWb_Oh7kSHmx01X8I8ii&jump_from=webapi&authKey=Za5CaFP0lk7+Zgsk2KpoBD7sSaYbeXbsDgFjiWelOeH4VSionpxFJ7V0qQBSqvFM)
|
||||
[](https://pd.qq.com/s/44gcy28h)
|
||||
|
||||
<img src="img-src/icon.png" width = "100" height = "100" alt="LOGO"/>
|
||||
|
||||
English | [简体中文](README-zh-CN.md) | [日本語](README-ja-JP.md)
|
||||
|
||||
Added more features to app's errors dialog, fixed custom rom deleted dialog, the best experience to Android developer.
|
||||
|
||||
This project is an Xposed module that can be used in any Android system, currently only tested in **LSPosed**.
|
||||
This project is a Xposed Module that can be used in any Android system, currently only tested in **LSPosed**.
|
||||
|
||||
This module is specially designed for Android developers.
|
||||
This Xposed Module is specially designed for Android developers.
|
||||
|
||||
When it is possible that the computer cannot be connected and ADB cannot be performed, this module can be used to quickly capture any errors of
|
||||
any installed apps, so as to quickly locate the problem.
|
||||
any installed apps, 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.
|
||||
@@ -25,7 +32,7 @@ developers with more exception information to quickly solve problems.
|
||||
## Project Reason
|
||||
|
||||
I really can't understand, except for MIUI (except stable version), Android ROMs in mainland China have chosen to delete the dialog box (FC
|
||||
dialog) of apps crashes. I thought this was always a feature until I decompiled the system. frame, only to confirm that it was indeed deleted.
|
||||
dialog) of apps crashes. I thought this was always a feature until I decompiled the system framework, only to confirm that it was indeed deleted.
|
||||
|
||||
Does the product manager think that it is the best solution to let the user not see the error, and the apps will crash and exit directly, or is
|
||||
there another **hidden secret**?
|
||||
@@ -45,66 +52,62 @@ Similar to **Bugly** to automatically collect errors, the system cannot obtain w
|
||||
|
||||
## Features List
|
||||
|
||||
- Completely replaces the system's apps errors dialog
|
||||
- [x] Completely replaces the system's apps errors dialog
|
||||
|
||||
- Logs exceptions for each apps and persists until restart
|
||||
- [x] Logs exceptions for each app and persists until restart
|
||||
|
||||
- Copy, share, export errors stack trace functions
|
||||
- [x] Copy, share, export errors stack trace functions
|
||||
|
||||
- Errors history record function, which can be entered through the notification bar tile "errors history record" and the main interface of the
|
||||
module
|
||||
- [x] Errors history record function,
|
||||
which can be entered through the notification bar tile "errors history record" and the main interface of the module
|
||||
|
||||
- Apps errors statistics function
|
||||
- [x] Apps errors statistics function
|
||||
|
||||
- Errors display function for multi-process apps
|
||||
- [x] Errors display function for multi-process apps
|
||||
|
||||
## Translation Contribution
|
||||
|
||||
Contributions to this project are welcome to translate it into your country's language.
|
||||
|
||||
## Release Channel Description
|
||||
## Release Channel
|
||||
|
||||
- [Automatic Build on Commit](https://github.com/KitsunePie/AppErrorsTracking/actions/workflows/commit_ci.yml)
|
||||
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub CI](https://github.com/KitsunePie/AppErrorsTracking/actions/workflows/commit_ci.yml) | CI automatic build (test version) |
|
||||
|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-----------------------------------|
|
||||
|
||||
The above update is automatically triggered after the code `commit`.
|
||||
| <img src="https://github.com/peter-iakovlev/Telegram/blob/public/Icon.png?raw=true" width = "30" height = "30" alt="LOGO"/> | [Telegram CI Channel](https://t.me/AppErrorsTracking_CI) | CI automatic build (test version) |
|
||||
|-----------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------|-----------------------------------|
|
||||
|
||||
The specific update content can be viewed by clicking the text above and going to **Github Actions**.
|
||||
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub Releases](https://github.com/KitsunePie/AppErrorsTracking/releases) | Formal edition (stable version) |
|
||||
|------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|---------------------------------|
|
||||
|
||||
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.
|
||||
| <img src="https://avatars.githubusercontent.com/u/78217009?s=200&v=4?raw=true" width = "30" height = "30" alt="LOGO"/> | [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases) | Formal edition (stable version) |
|
||||
|------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|---------------------------------|
|
||||
|
||||
- [Release](https://github.com/KitsunePie/AppErrorsTracking/releases)
|
||||
- [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.apperrorstracking/releases)
|
||||
The releases of this Xposed Module is limited to the urls listed above.
|
||||
|
||||
The above update is a manually released stable version.
|
||||
We have nothing to do with versions downloaded from other informal channels or any impact on you.
|
||||
|
||||
For the specific update content, you can click the text above to go to the designated release page to view.
|
||||
## Promotion
|
||||
|
||||
The update of the stable version will be released to the above address at the same time and updated synchronously.
|
||||
<!--suppress HtmlDeprecatedAttribute -->
|
||||
<div align="center">
|
||||
<h2>Hey, please stay! 👋</h2>
|
||||
<h3>Here are related projects such as Android development tools, UI design, Gradle plugins, Xposed Modules and practical software. </h3>
|
||||
<h3>If the project below can help you, please give me a star! </h3>
|
||||
<h3>All projects are free, open source, and follow the corresponding open source license agreement. </h3>
|
||||
<h1><a href="https://github.com/fankes/fankes/blob/main/project-promote/README.md">→ To see more about my projects, please click here ←</a></h1>
|
||||
</div>
|
||||
|
||||
## Release Status Description
|
||||
## Star History
|
||||
|
||||

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

|
||||
|
||||
## License
|
||||
|
||||
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
```
|
||||
Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
Copyright (C) 2017 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
|
||||
@@ -117,9 +120,9 @@ 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
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
```
|
||||
|
||||
Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI)
|
||||
Powered by [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI)
|
||||
|
||||
Copyright © 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
Copyright © 2017 Fankes Studio(qzmmcn@163.com)
|
||||
|
15
app/.gitignore
vendored
15
app/.gitignore
vendored
@@ -1,15 +0,0 @@
|
||||
*.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
|
@@ -1,91 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'com.google.devtools.ksp' version '1.7.22-1.0.8'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.fankes.apperrorstracking'
|
||||
compileSdk 33
|
||||
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('../keystore/public')
|
||||
storePassword '123456'
|
||||
keyAlias 'public'
|
||||
keyPassword '123456'
|
||||
v1SigningEnabled true
|
||||
v2SigningEnabled true
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.fankes.apperrorstracking"
|
||||
minSdk 24
|
||||
targetSdk 33
|
||||
versionCode rootProject.ext.appVersionCode
|
||||
versionName rootProject.ext.appVersionName
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
/** 添加 App Center Secret 到 BuildConfig */
|
||||
buildConfigField("String", "APP_CENTER_SECRET", "\"${getAppCenterSecret()}\"")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled rootProject.ext.enableR8
|
||||
shrinkResources rootProject.ext.enableR8
|
||||
zipAlignEnabled rootProject.ext.enableR8
|
||||
signingConfig signingConfigs.debug
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '11'
|
||||
freeCompilerArgs = [
|
||||
'-Xno-param-assertions',
|
||||
'-Xno-call-assertions',
|
||||
'-Xno-receiver-assertions'
|
||||
]
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 App Center Secret
|
||||
* @return [String]
|
||||
*/
|
||||
String getAppCenterSecret() {
|
||||
def fileName = '../.secret/APP_CENTER_SECRET'
|
||||
def content = ''
|
||||
if (file(fileName).exists()) file(fileName).eachLine { content = it }
|
||||
return content
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'de.robv.android.xposed:api:82'
|
||||
implementation 'com.highcapable.yukihookapi:api:1.1.6'
|
||||
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.1.6'
|
||||
implementation 'com.microsoft.appcenter:appcenter-analytics:4.4.5'
|
||||
implementation 'com.microsoft.appcenter:appcenter-crashes:4.4.5'
|
||||
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.7'
|
||||
implementation 'com.google.code.gson:gson:2.9.0'
|
||||
implementation 'com.github.duanhong169:drawabletoolbox:1.0.7'
|
||||
implementation 'com.github.topjohnwu.libsu:core:3.1.2'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.0'
|
||||
implementation 'com.google.android.material:material:1.7.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
}
|
@@ -1 +0,0 @@
|
||||
com.fankes.apperrorstracking.hook.AppErrorsTracking
|
@@ -1 +0,0 @@
|
||||
com.fankes.apperrorstracking.hook.HookEntry
|
@@ -1,559 +0,0 @@
|
||||
/*
|
||||
* 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 2022/5/11.
|
||||
*/
|
||||
@file:Suppress("MemberVisibilityCanBePrivate", "StaticFieldLeak", "unused")
|
||||
|
||||
package com.fankes.apperrorstracking.locale
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
|
||||
/**
|
||||
* I18n 字符串实例
|
||||
*/
|
||||
object LocaleString {
|
||||
|
||||
/** 当前的 [Context] */
|
||||
private var baseContext: Context? = null
|
||||
|
||||
/** 当前的 [PackageParam] */
|
||||
private var basePackageParam: PackageParam? = null
|
||||
|
||||
/** 当前的 [Resources] */
|
||||
private var baseResources: Resources? = null
|
||||
|
||||
/**
|
||||
* 当前的 [Resources]
|
||||
* @return [Resources]
|
||||
* @throws IllegalStateException 如果 [LocaleString] 没有被绑定
|
||||
*/
|
||||
private val resources
|
||||
get() = baseContext?.resources ?: basePackageParam?.moduleAppResources ?: baseResources
|
||||
?: error("LocaleString must bind an instance first")
|
||||
|
||||
/**
|
||||
* 绑定并初始化
|
||||
* @param instance 可以是 [Context]、[PackageParam]、[Resources]
|
||||
*/
|
||||
fun bind(instance: Any) {
|
||||
when (instance) {
|
||||
is Context -> baseContext = instance
|
||||
is PackageParam -> basePackageParam = instance
|
||||
is Resources -> baseResources = instance
|
||||
else -> error("LocaleString bind an unknown instance")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据资源 Id 获取字符串
|
||||
* @param objArrs 格式化实例
|
||||
* @return [String]
|
||||
*/
|
||||
private fun Int.bind(vararg objArrs: Any) = resources.getString(this, *objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val appName get() = appName()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun appName(vararg objArrs: Any) = R.string.app_name.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val copied get() = copied()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun copied(vararg objArrs: Any) = R.string.copied.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val copyFail get() = copyFail()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun copyFail(vararg objArrs: Any) = R.string.copy_fail.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val printToLogcatSuccess get() = printToLogcatSuccess()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun printToLogcatSuccess(vararg objArrs: Any) = R.string.print_to_logcat_success.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val outputStackSuccess get() = outputStackSuccess()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun outputStackSuccess(vararg objArrs: Any) = R.string.output_stack_success.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val outputStackFail get() = outputStackFail()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun outputStackFail(vararg objArrs: Any) = R.string.output_stack_fail.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val aerrTitle get() = aerrTitle()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun aerrTitle(vararg objArrs: Any) = R.string.aerr_title.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val aerrRepeatedTitle get() = aerrRepeatedTitle()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun aerrRepeatedTitle(vararg objArrs: Any) = R.string.aerr_repeated_title.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val appInfo get() = appInfo()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun appInfo(vararg objArrs: Any) = R.string.app_info.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val closeApp get() = closeApp()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun closeApp(vararg objArrs: Any) = R.string.close_app.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val reopenApp get() = reopenApp()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun reopenApp(vararg objArrs: Any) = R.string.reopen_app.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val errorDetail get() = errorDetail()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun errorDetail(vararg objArrs: Any) = R.string.error_detail.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val muteIfUnlock get() = muteIfUnlock()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun muteIfUnlock(vararg objArrs: Any) = R.string.mute_if_unlock.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val muteIfRestart get() = muteIfRestart()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun muteIfRestart(vararg objArrs: Any) = R.string.mute_if_restart.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val muteIfUnlockTip get() = muteIfUnlockTip()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun muteIfUnlockTip(vararg objArrs: Any) = R.string.mute_if_unlock_tip.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val muteIfRestartTip get() = muteIfRestartTip()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun muteIfRestartTip(vararg objArrs: Any) = R.string.mute_if_restart_tip.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val noListData get() = noListData()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun noListData(vararg objArrs: Any) = R.string.no_list_data.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val confirm get() = confirm()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun confirm(vararg objArrs: Any) = R.string.confirm.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val cancel get() = cancel()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun cancel(vararg objArrs: Any) = R.string.cancel.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val more get() = more()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun more(vararg objArrs: Any) = R.string.more.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val notice get() = notice()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun notice(vararg objArrs: Any) = R.string.notice.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val warning get() = warning()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun warning(vararg objArrs: Any) = R.string.warning.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val areYouSureClearErrors get() = areYouSureClearErrors()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun areYouSureClearErrors(vararg objArrs: Any) = R.string.are_you_sure_clear_errors.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val allErrorsClearSuccess get() = allErrorsClearSuccess()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun allErrorsClearSuccess(vararg objArrs: Any) = R.string.all_errors_clear_success.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val areYouSureExportAllErrors get() = areYouSureExportAllErrors()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun areYouSureExportAllErrors(vararg objArrs: Any) = R.string.are_you_sure_export_all_errors.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val exportAllErrorsSuccess get() = exportAllErrorsSuccess()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun exportAllErrorsSuccess(vararg objArrs: Any) = R.string.export_all_errors_success.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val exportAllErrorsFail get() = exportAllErrorsFail()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun exportAllErrorsFail(vararg objArrs: Any) = R.string.export_all_errors_fail.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val noCpuAbi get() = noCpuAbi()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun noCpuAbi(vararg objArrs: Any) = R.string.no_cpu_abi.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val areYouSureRemoveRecord get() = areYouSureRemoveRecord()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun areYouSureRemoveRecord(vararg objArrs: Any) = R.string.are_you_sure_remove_record.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val gotIt get() = gotIt()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun gotIt(vararg objArrs: Any) = R.string.got_it.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val moduleVersion get() = moduleVersion()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun moduleVersion(vararg objArrs: Any) = R.string.module_version.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val systemVersion get() = systemVersion()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun systemVersion(vararg objArrs: Any) = R.string.system_version.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val areYouSureRestartSystem get() = areYouSureRestartSystem()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun areYouSureRestartSystem(vararg objArrs: Any) = R.string.are_your_sure_restart_system.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val fastRestart get() = fastRestart()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun fastRestart(vararg objArrs: Any) = R.string.fast_restart.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val accessRootFail get() = accessRootFail()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun accessRootFail(vararg objArrs: Any) = R.string.access_root_fail.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val moduleNotActivated get() = moduleNotActivated()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun moduleNotActivated(vararg objArrs: Any) = R.string.module_not_activated.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val moduleIsActivated get() = moduleIsActivated()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun moduleIsActivated(vararg objArrs: Any) = R.string.module_is_activated.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val moduleNotFullyActivated get() = moduleNotFullyActivated()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun moduleNotFullyActivated(vararg objArrs: Any) = R.string.module_not_fully_activated.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val momentAgo get() = momentAgo()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun momentAgo(vararg objArrs: Any) = R.string.moment_ago.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val secondAgo get() = secondAgo()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun secondAgo(vararg objArrs: Any) = R.string.second_ago.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val minuteAgo get() = minuteAgo()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun minuteAgo(vararg objArrs: Any) = R.string.minute_ago.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val hourAgo get() = hourAgo()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun hourAgo(vararg objArrs: Any) = R.string.hour_ago.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val dayAgo get() = dayAgo()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun dayAgo(vararg objArrs: Any) = R.string.day_ago.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val monthAgo get() = monthAgo()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun monthAgo(vararg objArrs: Any) = R.string.month_ago.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val yearAgo get() = yearAgo()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun yearAgo(vararg objArrs: Any) = R.string.year_ago.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val crashProcess get() = crashProcess()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun crashProcess(vararg objArrs: Any) = R.string.crash_process.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val shareErrorStack get() = shareErrorStack()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun shareErrorStack(vararg objArrs: Any) = R.string.share_error_stack.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val areYouSureUnmuteAll get() = areYouSureUnmuteAll()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun areYouSureUnmuteAll(vararg objArrs: Any) = R.string.are_you_sure_unmute_all.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val noListResult get() = noListResult()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun noListResult(vararg objArrs: Any) = R.string.no_list_result.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val filterByCondition get() = filterByCondition()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun filterByCondition(vararg objArrs: Any) = R.string.filter_by_condition.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val clearFilters get() = clearFilters()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun clearFilters(vararg objArrs: Any) = R.string.clear_filters.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val resultCount get() = resultCount()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun resultCount(vararg objArrs: Any) = R.string.result_count.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val loading get() = loading()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun loading(vararg objArrs: Any) = R.string.loading.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val showErrorsDialog get() = showErrorsDialog()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun showErrorsDialog(vararg objArrs: Any) = R.string.show_errors_dialog.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val showErrorsToast get() = showErrorsToast()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun showErrorsToast(vararg objArrs: Any) = R.string.show_errors_toast.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val showNothing get() = showNothing()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun showNothing(vararg objArrs: Any) = R.string.show_nothing.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val appErrorsStatistics get() = appErrorsStatistics()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun appErrorsStatistics(vararg objArrs: Any) = R.string.app_errors_statistics.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val totalErrorsUnit get() = totalErrorsUnit()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun totalErrorsUnit(vararg objArrs: Any) = R.string.total_errors_unit.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val totalAppsUnit get() = totalAppsUnit()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun totalAppsUnit(vararg objArrs: Any) = R.string.total_apps_unit.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val generatingStatistics get() = generatingStatistics()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun generatingStatistics(vararg objArrs: Any) = R.string.generating_statistics.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val moduleNotFullyActivatedTip get() = moduleNotFullyActivatedTip()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun moduleNotFullyActivatedTip(vararg objArrs: Any) = R.string.module_not_fully_activated_tip.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val showErrorsNotify get() = showErrorsNotify()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun showErrorsNotify(vararg objArrs: Any) = R.string.show_errors_notify.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val appErrorsTip get() = appErrorsTip()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun appErrorsTip(vararg objArrs: Any) = R.string.app_errors_tip.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val batchOperations get() = batchOperations()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun batchOperations(vararg objArrs: Any) = R.string.batch_operations.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val areYouSureApplySiteApps get() = areYouSureApplySiteApps()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun areYouSureApplySiteApps(vararg objArrs: Any) = R.string.are_you_sure_apply_site_apps.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val developerNoticeTip get() = developerNoticeTip()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun developerNoticeTip(vararg objArrs: Any) = R.string.developer_notice_tip.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val developerNotice get() = developerNotice()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun developerNotice(vararg objArrs: Any) = R.string.developer_notice.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val fastRestartProblem get() = fastRestartProblem()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun fastRestartProblem(vararg objArrs: Any) = R.string.fast_restart_problem.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val userId get() = userId()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun userId(vararg objArrs: Any) = R.string.user_id.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val unableGetAppErrorsRecordTip get() = unableGetAppErrorsRecordTip()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun unableGetAppErrorsRecordTip(vararg objArrs: Any) = R.string.unable_get_app_errors_record_tip.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val exportAllLogsSuccess get() = exportAllLogsSuccess()
|
||||
|
||||
/** @string Automatic generated */
|
||||
fun exportAllLogsSuccess(vararg objArrs: Any) = R.string.export_all_logs_success.bind(*objArrs)
|
||||
|
||||
/** @string Automatic generated */
|
||||
val exportAllLogsFail get() = exportAllLogsFail()
|
||||
|
||||
/** @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)
|
||||
}
|
@@ -1,148 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">エラー追跡</string>
|
||||
<string name="xposed_desc">アプリのエラーダイアログに機能を追加し、カスタム ROM の削除されたダイアログを修正し、Android 開発者にとって最高のエクスペリエンスを実現しました。</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="close_app">アプリを閉じる</string>
|
||||
<string name="aerr_title">%1$s がエラー</string>
|
||||
<string name="aerr_repeated_title">%1$s が再びエラー</string>
|
||||
<string name="mute_if_restart_tip">システイムがリスタートするまで、「%1$s」のエラーを無視します</string>
|
||||
<string name="mute_if_unlock_tip">ロックが解除されるまで、「%1$s」のエラーを無視します</string>
|
||||
<string name="back">戻る</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">投擲クラス</string>
|
||||
<string name="error_throw_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="clear_all">すべてクリア</string>
|
||||
<string name="errors_record">エラー履歴記錄</string>
|
||||
<string name="no_list_data">一時的にデータレコードはありません</string>
|
||||
<string name="confirm">確認</string>
|
||||
<string name="cancel">キャンセル</string>
|
||||
<string name="more">モア</string>
|
||||
<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="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="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="are_your_sure_restart_system">リスタートシステムしてもよろしいですか</string>
|
||||
<string name="fast_restart">高速リスタート</string>
|
||||
<string name="access_root_fail">ルート権限を取得できませんでした</string>
|
||||
<string name="moment_ago">現在</string>
|
||||
<string name="second_ago">秒前</string>
|
||||
<string name="minute_ago">分前</string>
|
||||
<string name="hour_ago">時間前</string>
|
||||
<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="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">有効にすると、アプリがメインプロセス (最初の 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>
|
||||
<string name="unmute_all">無視を全部解除</string>
|
||||
<string name="are_you_sure_unmute_all">エラーを無視したすべてのアプリを解除してもよろしいですか</string>
|
||||
<string name="apps_config_template">アプリ設定テンプレート</string>
|
||||
<string name="filter_by_condition">条件でフィルタリング</string>
|
||||
<string name="no_list_result">表示結果はありません</string>
|
||||
<string name="typo_app_name_pkg_name">アプリ名とパッケージ名を入力できます</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="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="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="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="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="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="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="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>
|
||||
</resources>
|
11
build.gradle
11
build.gradle
@@ -1,11 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application' version '7.4.0' apply false
|
||||
id 'com.android.library' version '7.4.0' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.7.22' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
appVersionName = "1.2"
|
||||
appVersionCode = 4
|
||||
enableR8 = true
|
||||
}
|
21
build.gradle.kts
Normal file
21
build.gradle.kts
Normal file
@@ -0,0 +1,21 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
|
||||
|
||||
plugins {
|
||||
autowire(libs.plugins.android.application) apply false
|
||||
autowire(libs.plugins.kotlin.android) apply false
|
||||
autowire(libs.plugins.kotlin.ksp) apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
tasks.withType<KotlinJvmCompile>().configureEach {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_17
|
||||
freeCompilerArgs.addAll(
|
||||
"-Xno-param-assertions",
|
||||
"-Xno-call-assertions",
|
||||
"-Xno-receiver-assertions"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
1
demo-app/.gitignore
vendored
1
demo-app/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
@@ -1,78 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.fankes.apperrorsdemo'
|
||||
compileSdk 33
|
||||
ndkVersion '24.0.8215888'
|
||||
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('../keystore/public')
|
||||
storePassword '123456'
|
||||
keyAlias 'public'
|
||||
keyPassword '123456'
|
||||
v1SigningEnabled true
|
||||
v2SigningEnabled true
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdk 24
|
||||
targetSdk 33
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
signingConfig signingConfigs.debug
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "src/main/cpp/CMakeLists.txt"
|
||||
version "3.22.1"
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '11'
|
||||
freeCompilerArgs = [
|
||||
'-Xno-param-assertions',
|
||||
'-Xno-call-assertions',
|
||||
'-Xno-receiver-assertions'
|
||||
]
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.highcapable.yukihookapi:api:1.1.6'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.0'
|
||||
implementation 'com.google.android.material:material:1.7.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
}
|
87
demo-app/build.gradle.kts
Normal file
87
demo-app/build.gradle.kts
Normal file
@@ -0,0 +1,87 @@
|
||||
plugins {
|
||||
autowire(libs.plugins.android.application)
|
||||
autowire(libs.plugins.kotlin.android)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = property.project.demo.app.packageName
|
||||
compileSdk = property.project.android.compileSdk
|
||||
ndkVersion = property.project.android.ndk.version
|
||||
|
||||
signingConfigs {
|
||||
create("universal") {
|
||||
keyAlias = property.project.demo.app.signing.keyAlias
|
||||
keyPassword = property.project.demo.app.signing.keyPassword
|
||||
storeFile = rootProject.file(property.project.demo.app.signing.storeFilePath)
|
||||
storePassword = property.project.demo.app.signing.storePassword
|
||||
enableV1Signing = true
|
||||
enableV2Signing = true
|
||||
}
|
||||
}
|
||||
defaultConfig {
|
||||
applicationId = property.project.demo.app.packageName
|
||||
minSdk = property.project.android.minSdk
|
||||
targetSdk = property.project.android.targetSdk
|
||||
versionName = property.project.demo.app.versionName
|
||||
versionCode = property.project.demo.app.versionCode
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
all { signingConfig = signingConfigs.getByName("universal") }
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path("src/main/cpp/CMakeLists.txt")
|
||||
version = property.project.android.cmake.version
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
viewBinding = true
|
||||
}
|
||||
lint { checkReleaseBuilds = false }
|
||||
androidResources.additionalParameters += listOf("--allow-reserved-package-id", "--package-id", "0x37")
|
||||
}
|
||||
|
||||
androidComponents {
|
||||
onVariants(selector().all()) {
|
||||
it.outputs.forEach { output ->
|
||||
val currentType = it.buildType
|
||||
|
||||
// Workaround for GitHub Actions.
|
||||
// Why? I don't know, but it works.
|
||||
// Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
|
||||
// public inline fun CharSequence.isNotBlank(): Boolean defined in kotlin.text.
|
||||
@Suppress("UNNECESSARY_SAFE_CALL", "RemoveRedundantCallsOfConversionMethods")
|
||||
val currentSuffix = property.github.ci.commit.id?.let { suffix ->
|
||||
// Workaround for GitHub Actions.
|
||||
// Strongly transfer type to [String].
|
||||
val sSuffix = suffix.toString()
|
||||
if (sSuffix.isNotBlank()) "-$sSuffix" else ""
|
||||
}
|
||||
val currentVersion = "${output.versionName.get()}$currentSuffix(${output.versionCode.get()})"
|
||||
if (output is com.android.build.api.variant.impl.VariantOutputImpl)
|
||||
output.outputFileName.set("${property.project.name}-demo-v$currentVersion-$currentType.apk")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(com.fankes.projectpromote.project.promote)
|
||||
implementation(com.highcapable.kavaref.kavaref.core)
|
||||
implementation(com.highcapable.kavaref.kavaref.extension)
|
||||
implementation(androidx.core.core.ktx)
|
||||
implementation(androidx.appcompat.appcompat)
|
||||
implementation(com.google.android.material.material)
|
||||
testImplementation(junit.junit)
|
||||
androidTestImplementation(androidx.test.ext.junit)
|
||||
androidTestImplementation(androidx.test.espresso.espresso.core)
|
||||
}
|
@@ -1,14 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission
|
||||
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||
tools:node="remove" />
|
||||
<permission
|
||||
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||
tools:node="remove" />
|
||||
|
||||
<application
|
||||
android:name=".application.DemoApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.AppErrorsDemo">
|
||||
android:theme="@style/Theme.AppErrorsDemo"
|
||||
tools:targetApi="tiramisu">
|
||||
|
||||
<activity
|
||||
android:name=".ui.activity.MainActivity"
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/10.
|
||||
* This file is created by fankes on 2022/5/10.
|
||||
*/
|
||||
package com.fankes.apperrorsdemo.application
|
||||
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/10.
|
||||
* This file is created by fankes on 2022/5/10.
|
||||
*/
|
||||
package com.fankes.apperrorsdemo.native
|
||||
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,21 +17,27 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/10.
|
||||
* This file is created by fankes on 2022/5/10.
|
||||
*/
|
||||
@file:Suppress("SetTextI18n")
|
||||
|
||||
package com.fankes.apperrorsdemo.ui.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.SystemClock
|
||||
import com.fankes.apperrorsdemo.R
|
||||
import com.fankes.apperrorsdemo.databinding.ActivityMainBinding
|
||||
import com.fankes.apperrorsdemo.databinding.ActivityMultiProcessBinding
|
||||
import com.fankes.apperrorsdemo.generated.DemoAppProperties
|
||||
import com.fankes.apperrorsdemo.native.Channel
|
||||
import com.fankes.apperrorsdemo.ui.activity.base.BaseActivity
|
||||
|
||||
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
override fun onCreate() {
|
||||
binding.titleBackIcon.setOnClickListener { finish() }
|
||||
DemoAppProperties.GITHUB_CI_COMMIT_ID.takeIf(String::isNotBlank)?.also {
|
||||
binding.titleText.text = "${getString(R.string.app_name)} ($it)"
|
||||
}; binding.titleBackIcon.setOnClickListener { finish() }
|
||||
binding.throwRuntimeButton.setOnClickListener { Channel.throwRuntimeException() }
|
||||
binding.throwIllegalStateButton.setOnClickListener { Channel.throwIllegalStateException() }
|
||||
binding.throwNullPointerButton.setOnClickListener { Channel.throwNullPointerException() }
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,23 +17,22 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/10.
|
||||
* This file is created by fankes on 2022/5/10.
|
||||
*/
|
||||
@file:Suppress("UNCHECKED_CAST")
|
||||
|
||||
package com.fankes.apperrorsdemo.ui.activity.base
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
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.kavaref.KavaRef.Companion.resolve
|
||||
import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
|
||||
import com.highcapable.kavaref.extension.toClassOrNull
|
||||
|
||||
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
|
||||
@@ -42,10 +41,12 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = current().generic()?.argument()?.method {
|
||||
val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
|
||||
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
|
||||
name = "inflate"
|
||||
param(LayoutInflaterClass)
|
||||
}?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed")
|
||||
parameters(LayoutInflater::class)
|
||||
}?.invoke<VB>(layoutInflater) ?: error("binding failed")
|
||||
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
|
||||
setContentView(binding.root)
|
||||
/** 隐藏系统的标题栏 */
|
||||
supportActionBar?.hide()
|
||||
@@ -54,6 +55,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
isAppearanceLightStatusBars = isNotSystemInDarkMode
|
||||
isAppearanceLightNavigationBars = isNotSystemInDarkMode
|
||||
}
|
||||
@Suppress("DEPRECATION")
|
||||
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
|
||||
window?.statusBarColor = it
|
||||
window?.navigationBarColor = 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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/10.
|
||||
* This file is created by fankes on 2022/5/10.
|
||||
*/
|
||||
package com.fankes.apperrorsdemo.utils.factory
|
||||
|
||||
|
@@ -30,6 +30,7 @@
|
||||
android:tooltipText="@string/back" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="2.5dp"
|
||||
|
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">エラーの例</string>
|
||||
<string name="back">戻る</string>
|
||||
<string name="function_desc">一般的なシナリオでのビジネスエラーロジックをここに示します。Xposedモジュールをアクティブ化した後、ここでエラーをキャッチできるかどうかをテストできます。Xposedモジュールの有効性を確認するために、現在のアプリには手動用のロジックコードがありません。エラーの処理。</string>
|
||||
<string name="throw_native_error">ネイティブレイヤーのエラーをスロー</string>
|
||||
<string name="throw_exception">Exception をスロー</string>
|
||||
<string name="throw_nullpointer">NullPointerException をスロー</string>
|
||||
<string name="throw_illegalstate">IllegalStateException をスロー</string>
|
||||
<string name="throw_runtime">RuntimeException をスロー</string>
|
||||
<string name="throw_multi_process_error">マルチプロセスエラーをスロー</string>
|
||||
<string name="suicide_in_progress">自殺中</string>
|
||||
</resources>
|
||||
<string name="app_name">AppErrorsDemo</string>
|
||||
<string name="back">戻る</string>
|
||||
<string name="function_desc">一般的なシナリオにおけるビジネス例外のロジックを提供します。\nXposed モジュールを有効化後に例外を取得できるかどうかをテストする事ができます。\nXposed モジュールの有効性を検証するため、現在のアプリには手動で例外を処理するロジックコードはありません。</string>
|
||||
<string name="throw_runtime">RuntimeException をスロー</string>
|
||||
<string name="throw_illegalstate">IllegalStateException をスロー</string>
|
||||
<string name="throw_nullpointer">NullPointerException をスロー</string>
|
||||
<string name="throw_exception">Exception をスロー</string>
|
||||
<string name="throw_native_error">ネイティブエラーをスロー</string>
|
||||
<string name="throw_multi_process_error">マルチプロセスの例外をスロー</string>
|
||||
<string name="suicide_in_progress">プロセスを強制停止中</string>
|
||||
</resources>
|
||||
|
9
demo-app/src/main/res/xml/locales_config.xml
Normal file
9
demo-app/src/main/res/xml/locales_config.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<locale android:name="en" />
|
||||
<locale android:name="ja" />
|
||||
<locale android:name="zh-Hans-CN" />
|
||||
<locale android:name="zh-Hant-HK" />
|
||||
<locale android:name="zh-Hant-MO" />
|
||||
<locale android:name="zh-Hant-TW" />
|
||||
</locale-config>
|
@@ -1,25 +1,26 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-XX:+UseParallelGC
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app"s APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
# Compiler Configuration
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
# Incremental
|
||||
kotlin.incremental.useClasspathSnapshot=true
|
||||
kotlin.code.style=official
|
||||
# Project Configuration
|
||||
project.name=AppErrorsTracking
|
||||
project.android.compileSdk=36
|
||||
project.android.minSdk=24
|
||||
project.android.targetSdk=36
|
||||
project.android.ndk.version="24.0.8215888"
|
||||
project.android.cmake.version="3.22.1"
|
||||
project.module-app.packageName=com.fankes.apperrorstracking
|
||||
project.module-app.versionName="1.3"
|
||||
project.module-app.versionCode=6
|
||||
project.module-app.signing.keyAlias=public
|
||||
project.module-app.signing.keyPassword="123456"
|
||||
project.module-app.signing.storePassword="123456"
|
||||
project.module-app.signing.storeFilePath=.secret/universal.p12
|
||||
project.demo-app.packageName=com.fankes.apperrorsdemo
|
||||
project.demo-app.versionName="${project.module-app.versionName}"
|
||||
project.demo-app.versionCode=${project.module-app.versionCode}
|
||||
project.demo-app.signing.keyAlias="${project.module-app.signing.keyAlias}"
|
||||
project.demo-app.signing.keyPassword="${project.module-app.signing.keyPassword}"
|
||||
project.demo-app.signing.storePassword="${project.module-app.signing.storePassword}"
|
||||
project.demo-app.signing.storeFilePath="${project.module-app.signing.storeFilePath}"
|
95
gradle/sweet-dependency/sweet-dependency-config.yaml
Normal file
95
gradle/sweet-dependency/sweet-dependency-config.yaml
Normal file
@@ -0,0 +1,95 @@
|
||||
preferences:
|
||||
autowire-on-sync-mode: UPDATE_OPTIONAL_DEPENDENCIES
|
||||
repositories-mode: FAIL_ON_PROJECT_REPOS
|
||||
|
||||
repositories:
|
||||
gradle-plugin-portal:
|
||||
scope: PLUGINS
|
||||
google:
|
||||
maven-central:
|
||||
jit-pack:
|
||||
sonatype-oss-releases:
|
||||
rovo89-xposed-api:
|
||||
scope: LIBRARIES
|
||||
url: https://api.xposed.info/
|
||||
content:
|
||||
include:
|
||||
group:
|
||||
de.robv.android.xposed
|
||||
fankes-maven-releases:
|
||||
url: https://raw.githubusercontent.com/fankes/maven-repository/main/repository/releases
|
||||
|
||||
plugins:
|
||||
com.android.application:
|
||||
alias: android-application
|
||||
version: 8.12.1
|
||||
org.jetbrains.kotlin.android:
|
||||
alias: kotlin-android
|
||||
version: 2.2.10
|
||||
com.highcapable.flexilocale:
|
||||
alias: flexi-locale
|
||||
version: 1.0.2
|
||||
com.google.devtools.ksp:
|
||||
alias: kotlin-ksp
|
||||
version: 2.2.10-2.0.2
|
||||
|
||||
libraries:
|
||||
com.fankes.projectpromote:
|
||||
project-promote:
|
||||
version: 1.0.0
|
||||
repositories:
|
||||
fankes-maven-releases
|
||||
de.robv.android.xposed:
|
||||
api:
|
||||
version: 82
|
||||
repositories:
|
||||
rovo89-xposed-api
|
||||
com.highcapable.yukihookapi:
|
||||
api:
|
||||
version: 1.3.0
|
||||
ksp-xposed:
|
||||
version-ref: <this>::api
|
||||
com.highcapable.kavaref:
|
||||
kavaref-core:
|
||||
version: 1.0.1
|
||||
kavaref-extension:
|
||||
version: 1.0.1
|
||||
com.microsoft.appcenter:
|
||||
appcenter-analytics:
|
||||
version: 5.0.6
|
||||
appcenter-crashes:
|
||||
version-ref: <this>::appcenter-analytics
|
||||
com.github.topjohnwu.libsu:
|
||||
core:
|
||||
version: 5.2.2
|
||||
auto-update: false
|
||||
com.github.duanhong169:
|
||||
drawabletoolbox:
|
||||
version: 1.0.7
|
||||
com.google.code.gson:
|
||||
gson:
|
||||
version: 2.13.1
|
||||
com.squareup.okhttp3:
|
||||
okhttp:
|
||||
version: 5.1.0
|
||||
androidx.core:
|
||||
core-ktx:
|
||||
version: 1.17.0
|
||||
androidx.appcompat:
|
||||
appcompat:
|
||||
version: 1.7.1
|
||||
com.google.android.material:
|
||||
material:
|
||||
version: 1.12.0
|
||||
androidx.constraintlayout:
|
||||
constraintlayout:
|
||||
version: 2.2.1
|
||||
androidx.test.ext:
|
||||
junit:
|
||||
version: 1.3.0
|
||||
androidx.test.espresso:
|
||||
espresso-core:
|
||||
version: 3.7.0
|
||||
junit:
|
||||
junit:
|
||||
version: 4.13.2
|
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
||||
#Wed May 04 08:35:13 CST 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStoreBase=GRADLE_USER_HOME
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
2
module-app/.gitignore
vendored
Normal file
2
module-app/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/src/main/assets/xposed_init
|
||||
/src/main/resources/META-INF/yukihookapi_init
|
93
module-app/build.gradle.kts
Normal file
93
module-app/build.gradle.kts
Normal file
@@ -0,0 +1,93 @@
|
||||
plugins {
|
||||
autowire(libs.plugins.android.application)
|
||||
autowire(libs.plugins.kotlin.android)
|
||||
autowire(libs.plugins.kotlin.ksp)
|
||||
autowire(libs.plugins.flexi.locale)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = property.project.module.app.packageName
|
||||
compileSdk = property.project.android.compileSdk
|
||||
|
||||
signingConfigs {
|
||||
create("universal") {
|
||||
keyAlias = property.project.module.app.signing.keyAlias
|
||||
keyPassword = property.project.module.app.signing.keyPassword
|
||||
storeFile = rootProject.file(property.project.module.app.signing.storeFilePath)
|
||||
storePassword = property.project.module.app.signing.storePassword
|
||||
enableV1Signing = true
|
||||
enableV2Signing = true
|
||||
}
|
||||
}
|
||||
defaultConfig {
|
||||
applicationId = property.project.module.app.packageName
|
||||
minSdk = property.project.android.minSdk
|
||||
targetSdk = property.project.android.targetSdk
|
||||
versionName = property.project.module.app.versionName
|
||||
versionCode = property.project.module.app.versionCode
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
all { signingConfig = signingConfigs.getByName("universal") }
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
viewBinding = true
|
||||
}
|
||||
lint { checkReleaseBuilds = false }
|
||||
androidResources.additionalParameters += listOf("--allow-reserved-package-id", "--package-id", "0x37")
|
||||
}
|
||||
|
||||
androidComponents {
|
||||
onVariants(selector().all()) {
|
||||
it.outputs.forEach { output ->
|
||||
val currentType = it.buildType
|
||||
|
||||
// Workaround for GitHub Actions.
|
||||
// Why? I don't know, but it works.
|
||||
// Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
|
||||
// public inline fun CharSequence.isNotBlank(): Boolean defined in kotlin.text.
|
||||
@Suppress("UNNECESSARY_SAFE_CALL", "RemoveRedundantCallsOfConversionMethods")
|
||||
val currentSuffix = property.github.ci.commit.id?.let { suffix ->
|
||||
// Workaround for GitHub Actions.
|
||||
// Strongly transfer type to [String].
|
||||
val sSuffix = suffix.toString()
|
||||
if (sSuffix.isNotBlank()) "-$sSuffix" else ""
|
||||
}
|
||||
val currentVersion = "${output.versionName.get()}$currentSuffix(${output.versionCode.get()})"
|
||||
if (output is com.android.build.api.variant.impl.VariantOutputImpl)
|
||||
output.outputFileName.set("${property.project.name}-module-v$currentVersion-$currentType.apk")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(de.robv.android.xposed.api)
|
||||
implementation(com.highcapable.yukihookapi.api)
|
||||
ksp(com.highcapable.yukihookapi.ksp.xposed)
|
||||
implementation(com.highcapable.kavaref.kavaref.core)
|
||||
implementation(com.highcapable.kavaref.kavaref.extension)
|
||||
implementation(com.fankes.projectpromote.project.promote)
|
||||
implementation(com.microsoft.appcenter.appcenter.analytics)
|
||||
implementation(com.microsoft.appcenter.appcenter.crashes)
|
||||
implementation(com.github.topjohnwu.libsu.core)
|
||||
implementation(com.github.duanhong169.drawabletoolbox)
|
||||
implementation(com.google.code.gson.gson)
|
||||
implementation(com.squareup.okhttp3.okhttp)
|
||||
implementation(androidx.core.core.ktx)
|
||||
implementation(androidx.appcompat.appcompat)
|
||||
implementation(com.google.android.material.material)
|
||||
implementation(androidx.constraintlayout.constraintlayout)
|
||||
testImplementation(junit.junit)
|
||||
androidTestImplementation(androidx.test.ext.junit)
|
||||
androidTestImplementation(androidx.test.espresso.espresso.core)
|
||||
}
|
@@ -32,31 +32,36 @@
|
||||
-adaptresourcefilecontents
|
||||
|
||||
-renamesourcefileattribute P
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
-keepattributes SourceFile,Signature,LineNumberTable
|
||||
|
||||
## ---------------Begin: proguard configuration for Gson ----------
|
||||
# Gson uses generic type information stored in a class file when working with fields. Proguard
|
||||
# removes such information by default, so configure it to keep all of it.
|
||||
|
||||
# Explicitly preserve all serialization members. The Serializable interface
|
||||
# is only a marker interface, so it wouldn't save them.
|
||||
-keepclassmembers class * implements java.io.Serializable {
|
||||
static final long serialVersionUID;
|
||||
private static final java.io.ObjectStreamField[] serialPersistentFields;
|
||||
private void writeObject(java.io.ObjectOutputStream);
|
||||
private void readObject(java.io.ObjectInputStream);
|
||||
java.lang.Object writeReplace();
|
||||
java.lang.Object readResolve();
|
||||
}
|
||||
|
||||
# Gson specific classes
|
||||
-dontwarn sun.misc**
|
||||
-keep class com.google.gson.stream**{*;}
|
||||
|
||||
# Application classes that will be serialized/deserialized over Gson
|
||||
-keep class com.google.gson.examples.android.model** { <fields>; }
|
||||
|
||||
# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
|
||||
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
|
||||
-keep class * implements com.google.gson.TypeAdapter
|
||||
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||
-keep class * implements com.google.gson.JsonSerializer
|
||||
-keep class * implements com.google.gson.JsonDeserializer
|
||||
-dontwarn sun.misc.**
|
||||
-keep class com.google.gson.stream.** { *; }
|
||||
|
||||
# Prevent R8 from leaving Data object members always null
|
||||
-keepclassmembers,allowobfuscation class * {
|
||||
@com.google.gson.annotations.SerializedName <fields>;
|
||||
}
|
||||
|
||||
# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
|
||||
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
|
||||
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
|
||||
|
||||
## ---------------End: proguard configuration for Gson ----------
|
||||
|
||||
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
||||
@@ -64,4 +69,8 @@
|
||||
public static *** throwUninitializedPropertyAccessException(...);
|
||||
}
|
||||
|
||||
-keep class com.fankes.apperrorstracking.databinding**{*;}
|
||||
-keep class * extends android.app.Activity
|
||||
-keep class * implements androidx.viewbinding.ViewBinding {
|
||||
<init>();
|
||||
*** inflate(android.view.LayoutInflater);
|
||||
}
|
@@ -10,14 +10,23 @@
|
||||
android:name="android.permission.INTERACT_ACROSS_USERS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
|
||||
<uses-permission
|
||||
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||
tools:node="remove" />
|
||||
<permission
|
||||
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||
tools:node="remove" />
|
||||
|
||||
<application
|
||||
android:name=".application.AppErrorsApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.AppErrorsTracking">
|
||||
android:theme="@style/Theme.AppErrorsTracking"
|
||||
tools:targetApi="tiramisu">
|
||||
|
||||
<meta-data
|
||||
android:name="xposedmodule"
|
||||
@@ -108,5 +117,15 @@
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
BIN
module-app/src/main/ic_launcher-playstore.png
Normal file
BIN
module-app/src/main/ic_launcher-playstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,13 +17,14 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/10.
|
||||
* This file is created by fankes on 2022/5/10.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.application
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.fankes.apperrorstracking.data.ConfigData
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.generated.locale.ModuleAppLocale
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.fankes.apperrorstracking.utils.tool.AppAnalyticsTool
|
||||
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
|
||||
|
||||
@@ -31,13 +32,13 @@ class AppErrorsApplication : ModuleApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
/** 绑定 I18n */
|
||||
locale = ModuleAppLocale.attach(this)
|
||||
/** 跟随系统夜间模式 */
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
/** 绑定 I18n */
|
||||
LocaleString.bind(instance = this)
|
||||
/** 装载存储控制类 */
|
||||
ConfigData.init(instance = this)
|
||||
ConfigData.init(this)
|
||||
/** 装载 App Center */
|
||||
AppAnalyticsTool.init(instance = this)
|
||||
AppAnalyticsTool.init(this)
|
||||
}
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/6/1.
|
||||
* This file is created by fankes on 2022/6/1.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.bean
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,19 +17,27 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/10.
|
||||
* This file is created by fankes on 2022/5/10.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.bean
|
||||
|
||||
import android.app.ApplicationErrorReport
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.utils.factory.*
|
||||
import com.fankes.apperrorstracking.const.ModuleVersion
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.fankes.apperrorstracking.utils.factory.appCpuAbiOf
|
||||
import com.fankes.apperrorstracking.utils.factory.appMinSdkOf
|
||||
import com.fankes.apperrorstracking.utils.factory.appTargetSdkOf
|
||||
import com.fankes.apperrorstracking.utils.factory.appVersionCodeOf
|
||||
import com.fankes.apperrorstracking.utils.factory.appVersionNameOf
|
||||
import com.fankes.apperrorstracking.utils.factory.difference
|
||||
import com.fankes.apperrorstracking.utils.factory.toUtcTime
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import java.io.Serializable
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 应用异常信息 bean
|
||||
@@ -39,6 +47,8 @@ import java.util.*
|
||||
* @param packageName 包名
|
||||
* @param versionName 版本名称
|
||||
* @param versionCode 版本号
|
||||
* @param targetSdk 目标 SDK 版本
|
||||
* @param minSdk 最低 SDK 版本
|
||||
* @param isNativeCrash 是否为原生层异常
|
||||
* @param exceptionClassName 异常类名
|
||||
* @param exceptionMessage 异常信息
|
||||
@@ -62,6 +72,10 @@ data class AppErrorsInfoBean(
|
||||
var versionName: String = "",
|
||||
@SerializedName("versionCode")
|
||||
var versionCode: Long = -1L,
|
||||
@SerializedName("targetSdk")
|
||||
var targetSdk: Int = -1,
|
||||
@SerializedName("minSdk")
|
||||
var minSdk: Int = -1,
|
||||
@SerializedName("isNativeCrash")
|
||||
var isNativeCrash: Boolean = false,
|
||||
@SerializedName("exceptionClassName")
|
||||
@@ -100,8 +114,10 @@ data class AppErrorsInfoBean(
|
||||
userId = userId,
|
||||
cpuAbi = packageName?.let { context.appCpuAbiOf(it) } ?: "",
|
||||
packageName = packageName ?: "unknown",
|
||||
versionName = packageName?.let { context.appVersionNameOf(it) } ?: "",
|
||||
versionName = packageName?.let { context.appVersionNameOf(it).ifBlank { "unknown" } } ?: "",
|
||||
versionCode = packageName?.let { context.appVersionCodeOf(it) } ?: -1L,
|
||||
targetSdk = packageName?.let { context.appTargetSdkOf(it) } ?: -1,
|
||||
minSdk = packageName?.let { context.appMinSdkOf(it) } ?: -1,
|
||||
isNativeCrash = isNativeCrash,
|
||||
exceptionClassName = crashInfo?.exceptionClassName ?: "unknown",
|
||||
exceptionMessage = if (isNativeCrash) crashInfo?.stackTrace.let {
|
||||
@@ -130,7 +146,7 @@ data class AppErrorsInfoBean(
|
||||
* 获取生成的 Json 文件名
|
||||
* @return [String]
|
||||
*/
|
||||
val jsonFileName get() = "${packageName}_${pid}_${timestamp}.json"
|
||||
val jsonFileName get() = "${packageName}_${pid}_$timestamp.json"
|
||||
|
||||
/**
|
||||
* 获取 APP 版本信息与版本号
|
||||
@@ -150,13 +166,13 @@ data class AppErrorsInfoBean(
|
||||
*/
|
||||
val crossTime
|
||||
get() = timestamp.difference(
|
||||
now = LocaleString.momentAgo,
|
||||
second = LocaleString.secondAgo,
|
||||
minute = LocaleString.minuteAgo,
|
||||
hour = LocaleString.hourAgo,
|
||||
day = LocaleString.dayAgo,
|
||||
month = LocaleString.monthAgo,
|
||||
year = LocaleString.yearAgo
|
||||
now = locale.momentAgo,
|
||||
second = locale.secondAgo,
|
||||
minute = locale.minuteAgo,
|
||||
hour = locale.hourAgo,
|
||||
day = locale.dayAgo,
|
||||
month = locale.monthAgo,
|
||||
year = locale.yearAgo
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -167,42 +183,80 @@ data class AppErrorsInfoBean(
|
||||
|
||||
/**
|
||||
* 获取异常堆栈分享模板
|
||||
* @param sDeviceBrand
|
||||
* @param sDeviceModel
|
||||
* @param sDisplay
|
||||
* @param sPackageName
|
||||
* @return [String]
|
||||
*/
|
||||
val stackOutputShareContent
|
||||
get() = "Generated by AppErrorsTracking\n" +
|
||||
"Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
|
||||
"===============\n$environmentInfo"
|
||||
fun stackOutputShareContent(
|
||||
sDeviceBrand: Boolean = true,
|
||||
sDeviceModel: Boolean = true,
|
||||
sDisplay: Boolean = true,
|
||||
sPackageName: Boolean = true
|
||||
) = """
|
||||
Generated by AppErrorsTracking $ModuleVersion
|
||||
Project URL: https://github.com/KitsunePie/AppErrorsTracking
|
||||
===============
|
||||
""".trimIndent() + "\n${environmentInfo(sDeviceBrand, sDeviceModel, sDisplay, sPackageName)}"
|
||||
|
||||
/**
|
||||
* 获取异常堆栈文件模板
|
||||
* @param sDeviceBrand
|
||||
* @param sDeviceModel
|
||||
* @param sDisplay
|
||||
* @param sPackageName
|
||||
* @return [String]
|
||||
*/
|
||||
val stackOutputFileContent
|
||||
get() = "================================================================\n" +
|
||||
" Generated by AppErrorsTracking\n" +
|
||||
" Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
|
||||
"================================================================\n" +
|
||||
environmentInfo
|
||||
fun stackOutputFileContent(
|
||||
sDeviceBrand: Boolean = true,
|
||||
sDeviceModel: Boolean = true,
|
||||
sDisplay: Boolean = true,
|
||||
sPackageName: Boolean = true
|
||||
) = """
|
||||
================================================================
|
||||
Generated by AppErrorsTracking $ModuleVersion
|
||||
Project URL: https://github.com/KitsunePie/AppErrorsTracking
|
||||
================================================================
|
||||
""".trimIndent() + "\n${environmentInfo(sDeviceBrand, sDeviceModel, sDisplay, sPackageName)}"
|
||||
|
||||
/**
|
||||
* 获取运行环境信息
|
||||
* @param sDeviceBrand
|
||||
* @param sDeviceModel
|
||||
* @param sDisplay
|
||||
* @param sPackageName
|
||||
* @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" +
|
||||
"[Android API Level]: ${Build.VERSION.SDK_INT}\n" +
|
||||
"[System Locale]: ${Locale.getDefault()}\n" +
|
||||
"[Process ID]: $pid\n" +
|
||||
(if (userId > 0) "[User Id]: $userId\n" else "") +
|
||||
"[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
|
||||
private fun environmentInfo(
|
||||
sDeviceBrand: Boolean,
|
||||
sDeviceModel: Boolean,
|
||||
sDisplay: Boolean,
|
||||
sPackageName: Boolean
|
||||
) = """
|
||||
[Device Brand]: ${Build.BRAND.by(sDeviceBrand)}
|
||||
[Device Model]: ${Build.MODEL.by(sDeviceModel)}
|
||||
[Display]: ${Build.DISPLAY.by(sDisplay)}
|
||||
[Android Version]: ${Build.VERSION.RELEASE}
|
||||
[Android API Level]: ${Build.VERSION.SDK_INT}
|
||||
[System Locale]: ${Locale.getDefault()}
|
||||
[Process ID]: $pid
|
||||
[User ID]: $userId
|
||||
[CPU ABI]: ${cpuAbi.ifBlank { "none" }}
|
||||
[Package Name]: ${packageName.by(sPackageName)}
|
||||
[Version Name]: ${versionName.ifBlank { "unknown" }}
|
||||
[Version Code]: ${versionCode.takeIf { it != -1L } ?: "unknown"}
|
||||
[Target SDK]: ${targetSdk.takeIf { it != -1 } ?: "unknown"}
|
||||
[Min SDK]: ${minSdk.takeIf { it != -1 } ?: "unknown"}
|
||||
[Error Type]: ${if (isNativeCrash) "Native" else "JVM"}
|
||||
[Crash Time]: $utcTime
|
||||
[Stack Trace]:
|
||||
""".trimIndent() + "\n$stackTrace"
|
||||
|
||||
/**
|
||||
* 判断字符串是否需要显示
|
||||
* @param isDisplay 是否需要显示
|
||||
* @return [String]
|
||||
*/
|
||||
private fun String.by(isDisplay: Boolean) = if (isDisplay) this else "***"
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/6/4.
|
||||
* This file is created by fankes on 2022/6/4.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.bean
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/6/8.
|
||||
* This file is created by fankes on 2022/6/8.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.bean
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/6/3.
|
||||
* This file is created by fankes on 2022/6/3.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.bean
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2023/1/22.
|
||||
* This file is created by fankes on 2023/1/22.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.bean.enum
|
||||
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
|
||||
* Copyright (C) 2017 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/9/19.
|
||||
*/
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.apperrorstracking.const
|
||||
|
||||
import com.fankes.apperrorstracking.generated.ModuleAppProperties
|
||||
import com.fankes.apperrorstracking.wrapper.BuildConfigWrapper
|
||||
|
||||
/**
|
||||
* 包名常量定义类
|
||||
*/
|
||||
object PackageName {
|
||||
|
||||
/** 系统框架 */
|
||||
const val SYSTEM_FRAMEWORK = "android"
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块版本常量定义类
|
||||
*/
|
||||
object ModuleVersion {
|
||||
|
||||
/** 当前 GitHub 提交的 ID (CI 自动构建) */
|
||||
const val GITHUB_COMMIT_ID = ModuleAppProperties.GITHUB_CI_COMMIT_ID
|
||||
|
||||
/** 版本名称 */
|
||||
const val NAME = BuildConfigWrapper.VERSION_NAME
|
||||
|
||||
/** 版本号 */
|
||||
const val CODE = BuildConfigWrapper.VERSION_CODE
|
||||
|
||||
/** 是否为 CI 自动构建版本 */
|
||||
val isCiMode = GITHUB_COMMIT_ID.isNotBlank()
|
||||
|
||||
/** 当前版本名称后缀 */
|
||||
val suffix = GITHUB_COMMIT_ID.let { if (it.isNotBlank()) "-$it" else "" }
|
||||
|
||||
override fun toString() = "$NAME$suffix($CODE)"
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2023/1/20.
|
||||
* This file is created by fankes on 2023/1/20.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.data
|
||||
|
||||
@@ -74,9 +74,9 @@ object AppErrorsConfigData {
|
||||
if (packageName.isNotBlank()) when (type) {
|
||||
AppErrorsConfigType.GLOBAL ->
|
||||
showDialogApps.contains(packageName).not() &&
|
||||
showNotifyApps.contains(packageName).not() &&
|
||||
showToastApps.contains(packageName).not() &&
|
||||
showNothingApps.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)
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2023/1/17.
|
||||
* This file is created by fankes on 2023/1/17.
|
||||
*/
|
||||
@file:Suppress("StaticFieldLeak")
|
||||
|
||||
@@ -26,8 +26,12 @@ 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 com.fankes.apperrorstracking.utils.factory.appCpuAbiOf
|
||||
import com.fankes.apperrorstracking.utils.factory.appVersionCodeOf
|
||||
import com.fankes.apperrorstracking.utils.factory.appVersionNameOf
|
||||
import com.fankes.apperrorstracking.utils.factory.toEntityOrNull
|
||||
import com.fankes.apperrorstracking.utils.factory.toJsonOrNull
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import java.io.File
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
@@ -72,7 +76,7 @@ object AppErrorsRecordData {
|
||||
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)
|
||||
YLog.error("Can't create directory \"$FOLDER_PATH\", there will be problems with the app errors records function", it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,15 +91,15 @@ object AppErrorsRecordData {
|
||||
?.toEntityOrNull<CopyOnWriteArrayList<AppErrorsInfoBean>>()
|
||||
?.onEach { e ->
|
||||
e.cpuAbi = it.appCpuAbiOf(e.packageName)
|
||||
e.versionName = it.appVersionNameOf(e.packageName)
|
||||
e.versionName = it.appVersionNameOf(e.packageName).ifBlank { "unknown" }
|
||||
e.versionCode = it.appVersionCodeOf(e.packageName)
|
||||
e.toJsonOrNull()?.also { json -> File(errorsInfoDataFolder.absolutePath, e.jsonFileName).writeText(json) }
|
||||
}.let { result ->
|
||||
if (result != null) {
|
||||
Settings.Secure.putString(it.contentResolver, keyName, "")
|
||||
result
|
||||
} else null
|
||||
}
|
||||
if (result != null) {
|
||||
Settings.Secure.putString(it.contentResolver, keyName, "")
|
||||
result
|
||||
} else null
|
||||
}
|
||||
}.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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/10/1.
|
||||
* This file is created by fankes on 2022/10/1.
|
||||
*/
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
@@ -25,9 +25,8 @@ package com.fankes.apperrorstracking.data
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.widget.CompoundButton
|
||||
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
||||
import com.highcapable.yukihookapi.hook.log.loggerW
|
||||
import com.highcapable.yukihookapi.hook.factory.prefs
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||
|
||||
@@ -54,9 +53,15 @@ object ConfigData {
|
||||
/** 启用应用配置模板 */
|
||||
val ENABLE_APP_CONFIG_TEMPLATE = PrefsData("_enable_app_config_template", false)
|
||||
|
||||
/** 启用对话框防误触 */
|
||||
val ENABLE_PREVENT_MISOPERATION_FOR_DIALOG = PrefsData("_enable_prevent_misoperation_for_dialog", false)
|
||||
|
||||
/** 禁止异常堆栈内容自动换行 */
|
||||
val DISABLE_AUTO_WRAP_ERROR_STACK_TRACE = PrefsData("_disable_auto_wrap_error_stack_trace", false)
|
||||
|
||||
/** 分享时使用文件 */
|
||||
val SHARE_WITH_FILE = PrefsData("_share_with_file", false)
|
||||
|
||||
/** 当前实例 - [Context] or [PackageParam] */
|
||||
private var instance: Any? = null
|
||||
|
||||
@@ -79,7 +84,7 @@ object ConfigData {
|
||||
* @return [Set]<[String]>
|
||||
*/
|
||||
internal fun getStringSet(key: String) = when (instance) {
|
||||
is Context -> (instance as Context).modulePrefs.getStringSet(key, setOf())
|
||||
is Context -> (instance as Context).prefs().getStringSet(key, setOf())
|
||||
is PackageParam -> (instance as PackageParam).prefs.getStringSet(key, setOf())
|
||||
else -> error("Unknown type for get prefs data")
|
||||
}
|
||||
@@ -91,8 +96,8 @@ object ConfigData {
|
||||
*/
|
||||
internal fun putStringSet(key: String, value: Set<String>) {
|
||||
when (instance) {
|
||||
is Context -> (instance as Context).modulePrefs.putStringSet(key, value)
|
||||
is PackageParam -> loggerW(msg = "Not support for this method")
|
||||
is Context -> (instance as Context).prefs().edit { putStringSet(key, value) }
|
||||
is PackageParam -> YLog.warn("Not support for this method")
|
||||
else -> error("Unknown type for put prefs data")
|
||||
}
|
||||
}
|
||||
@@ -103,7 +108,7 @@ object ConfigData {
|
||||
* @return [Int]
|
||||
*/
|
||||
internal fun getInt(data: PrefsData<Int>) = when (instance) {
|
||||
is Context -> (instance as Context).modulePrefs.get(data)
|
||||
is Context -> (instance as Context).prefs().get(data)
|
||||
is PackageParam -> (instance as PackageParam).prefs.get(data)
|
||||
else -> error("Unknown type for get prefs data")
|
||||
}
|
||||
@@ -115,8 +120,8 @@ object ConfigData {
|
||||
*/
|
||||
internal fun putInt(data: PrefsData<Int>, value: Int) {
|
||||
when (instance) {
|
||||
is Context -> (instance as Context).modulePrefs.put(data, value)
|
||||
is PackageParam -> loggerW(msg = "Not support for this method")
|
||||
is Context -> (instance as Context).prefs().edit { put(data, value) }
|
||||
is PackageParam -> YLog.warn("Not support for this method")
|
||||
else -> error("Unknown type for put prefs data")
|
||||
}
|
||||
}
|
||||
@@ -126,8 +131,8 @@ object ConfigData {
|
||||
* @param data 键值数据模板
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private fun getBoolean(data: PrefsData<Boolean>) = when (instance) {
|
||||
is Context -> (instance as Context).modulePrefs.get(data)
|
||||
internal fun getBoolean(data: PrefsData<Boolean>) = when (instance) {
|
||||
is Context -> (instance as Context).prefs().get(data)
|
||||
is PackageParam -> (instance as PackageParam).prefs.get(data)
|
||||
else -> error("Unknown type for get prefs data")
|
||||
}
|
||||
@@ -137,29 +142,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 PackageParam -> loggerW(msg = "Not support for this method")
|
||||
is Context -> (instance as Context).prefs().edit { put(data, value) }
|
||||
is PackageParam -> YLog.warn("Not support for this method")
|
||||
else -> error("Unknown type for put prefs data")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定到 [CompoundButton] 自动设置选中状态
|
||||
* @param data 键值数据模板
|
||||
* @param onChange 当改变时回调
|
||||
*/
|
||||
fun CompoundButton.bind(data: PrefsData<Boolean>, onChange: (Boolean) -> Unit = {}) {
|
||||
isChecked = getBoolean(data).also(onChange)
|
||||
setOnCheckedChangeListener { button, isChecked ->
|
||||
if (button.isPressed) {
|
||||
putBoolean(data, isChecked)
|
||||
onChange(isChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否显示开发者提示
|
||||
* @return [Boolean]
|
||||
@@ -210,6 +200,16 @@ object ConfigData {
|
||||
putBoolean(ENABLE_APP_CONFIG_TEMPLATE, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用对话框防误触
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnablePreventMisoperation
|
||||
get() = getBoolean(ENABLE_PREVENT_MISOPERATION_FOR_DIALOG)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_PREVENT_MISOPERATION_FOR_DIALOG, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用 Material 3 风格的错误对话框
|
||||
* @return [Boolean]
|
||||
@@ -219,4 +219,13 @@ object ConfigData {
|
||||
set(value) {
|
||||
putBoolean(ENABLE_MATERIAL3_STYLE_APP_ERRORS_DIALOG, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否以文件方式分享
|
||||
*/
|
||||
var isShareWithFile
|
||||
get() = getBoolean(SHARE_WITH_FILE)
|
||||
set(value) {
|
||||
putBoolean(SHARE_WITH_FILE, value)
|
||||
}
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2023/1/20.
|
||||
* This file is created by fankes on 2023/1/20.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.data.enum
|
||||
|
@@ -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 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,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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,13 +17,14 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/7.
|
||||
* This file is created by fankes on 2022/5/7.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.hook
|
||||
|
||||
import com.fankes.apperrorstracking.data.ConfigData
|
||||
import com.fankes.apperrorstracking.generated.locale.ModuleAppLocale
|
||||
import com.fankes.apperrorstracking.hook.entity.FrameworkHooker
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
|
||||
import com.highcapable.yukihookapi.hook.factory.configs
|
||||
import com.highcapable.yukihookapi.hook.factory.encase
|
||||
@@ -38,13 +39,12 @@ object HookEntry : IYukiHookXposedInit {
|
||||
isRecord = true
|
||||
}
|
||||
isDebug = false
|
||||
isEnableModulePrefsCache = false
|
||||
}
|
||||
|
||||
override fun onHook() = encase {
|
||||
loadSystem {
|
||||
LocaleString.bind(instance = this)
|
||||
ConfigData.init(instance = this)
|
||||
locale = ModuleAppLocale.attach { moduleAppResources }
|
||||
ConfigData.init(this)
|
||||
loadHooker(FrameworkHooker)
|
||||
}
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,8 +17,10 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/7.
|
||||
* This file is created by fankes on 2022/5/7.
|
||||
*/
|
||||
@file:Suppress("ConstPropertyName")
|
||||
|
||||
package com.fankes.apperrorstracking.hook.entity
|
||||
|
||||
import android.app.ApplicationErrorReport
|
||||
@@ -28,12 +30,12 @@ import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
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
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.fankes.apperrorstracking.bean.AppErrorsDisplayBean
|
||||
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
|
||||
@@ -44,47 +46,52 @@ import com.fankes.apperrorstracking.data.AppErrorsConfigData
|
||||
import com.fankes.apperrorstracking.data.AppErrorsRecordData
|
||||
import com.fankes.apperrorstracking.data.ConfigData
|
||||
import com.fankes.apperrorstracking.data.enum.AppErrorsConfigType
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsDisplayActivity
|
||||
import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsRecordActivity
|
||||
import com.fankes.apperrorstracking.utils.factory.*
|
||||
import com.fankes.apperrorstracking.utils.factory.appNameOf
|
||||
import com.fankes.apperrorstracking.utils.factory.drawableOf
|
||||
import com.fankes.apperrorstracking.utils.factory.isAppCanOpened
|
||||
import com.fankes.apperrorstracking.utils.factory.listOfPackages
|
||||
import com.fankes.apperrorstracking.utils.factory.openApp
|
||||
import com.fankes.apperrorstracking.utils.factory.pushNotify
|
||||
import com.fankes.apperrorstracking.utils.factory.toArrayList
|
||||
import com.fankes.apperrorstracking.utils.factory.toast
|
||||
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
|
||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||
import com.fankes.apperrorstracking.wrapper.BuildConfigWrapper
|
||||
import com.highcapable.kavaref.KavaRef.Companion.resolve
|
||||
import com.highcapable.kavaref.extension.VariousClass
|
||||
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.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
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
|
||||
object FrameworkHooker : YukiBaseHooker() {
|
||||
|
||||
private const val ActivityManagerServiceClass = "com.android.server.am.ActivityManagerService"
|
||||
private const val UserControllerClass = "com.android.server.am.UserController"
|
||||
private const val AppErrorsClass = "com.android.server.am.AppErrors"
|
||||
private const val AppErrorDialogClass = "com.android.server.am.AppErrorDialog"
|
||||
private const val AppErrorDialog_DataClass = "com.android.server.am.AppErrorDialog\$Data"
|
||||
private const val ProcessRecordClass = "com.android.server.am.ProcessRecord"
|
||||
private const val ActivityTaskManagerService_LocalServiceClass = "com.android.server.wm.ActivityTaskManagerService\$LocalService"
|
||||
private val UserControllerClass by lazyClass("com.android.server.am.UserController")
|
||||
private val AppErrorsClass by lazyClass("com.android.server.am.AppErrors")
|
||||
private val AppErrorDialogClass by lazyClass("com.android.server.am.AppErrorDialog")
|
||||
private val AppErrorDialog_DataClass by lazyClass("com.android.server.am.AppErrorDialog\$Data")
|
||||
private val ProcessRecordClass by lazyClass("com.android.server.am.ProcessRecord")
|
||||
private val ActivityManagerServiceClass by lazyClassOrNull("com.android.server.am.ActivityManagerService")
|
||||
private val ActivityTaskManagerService_LocalServiceClass by lazyClassOrNull("com.android.server.wm.ActivityTaskManagerService\$LocalService")
|
||||
|
||||
private val PackageListClass = VariousClass(
|
||||
"com.android.server.am.ProcessRecord\$PackageList",
|
||||
"com.android.server.am.PackageList"
|
||||
private val PackageListClass by lazyClassOrNull(
|
||||
VariousClass(
|
||||
"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"
|
||||
private val ErrorDialogControllerClass by lazyClassOrNull(
|
||||
VariousClass(
|
||||
"com.android.server.am.ProcessRecord\$ErrorDialogController",
|
||||
"com.android.server.am.ErrorDialogController"
|
||||
)
|
||||
)
|
||||
|
||||
/** 已忽略错误的 APP 数组 - 直到重新解锁 */
|
||||
private var mutedErrorsIfUnlockApps = HashSet<String>()
|
||||
private var mutedErrorsIfUnlockApps = mutableSetOf<String>()
|
||||
|
||||
/** 已忽略错误的 APP 数组 - 直到重新启动 */
|
||||
private var mutedErrorsIfRestartApps = HashSet<String>()
|
||||
private var mutedErrorsIfRestartApps = mutableSetOf<String>()
|
||||
|
||||
/**
|
||||
* APP 进程异常数据定义类
|
||||
@@ -98,40 +105,73 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
* 获取当前包列表实例
|
||||
* @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()
|
||||
private val pkgList by lazy {
|
||||
ProcessRecordClass.resolve().optional(silent = true)
|
||||
.firstMethodOrNull {
|
||||
name = "getPkgList"
|
||||
emptyParameters()
|
||||
}?.of(proc)?.invoke()
|
||||
?: ProcessRecordClass.resolve().optional(silent = true)
|
||||
.firstFieldOrNull {
|
||||
name { it.endsWith("pkgList", true) }
|
||||
}?.of(proc)?.get()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前包列表数组大小
|
||||
* @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
|
||||
private val pkgListSize by lazy {
|
||||
PackageListClass?.resolve()?.optional(silent = true)
|
||||
?.firstMethodOrNull {
|
||||
name = "size"
|
||||
emptyParameters()
|
||||
}?.of(pkgList)?.invoke()
|
||||
?: ProcessRecordClass.resolve().optional(silent = true)
|
||||
.firstFieldOrNull { name = "pkgList" }
|
||||
?.of(proc)?.get<ArrayMap<*, *>>()?.size ?: -1
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 pid 信息
|
||||
* @return [Int]
|
||||
*/
|
||||
val pid = ProcessRecordClass.toClass().field { name { it == "mPid" || it == "pid" } }.get(proc).int()
|
||||
val pid by lazy {
|
||||
ProcessRecordClass.resolve().optional()
|
||||
.firstFieldOrNull {
|
||||
name { it == "mPid" || it == "pid" }
|
||||
}?.of(proc)?.get<Int>() ?: 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户 ID 信息
|
||||
* @return [Int]
|
||||
*/
|
||||
val userId = ProcessRecordClass.toClass().field { name = "userId" }.get(proc).int()
|
||||
val userId by lazy {
|
||||
ProcessRecordClass.resolve().optional()
|
||||
.firstFieldOrNull { name = "userId" }
|
||||
?.of(proc)?.get<Int>() ?: 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 APP 信息
|
||||
* @return [ApplicationInfo] or null
|
||||
*/
|
||||
val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(proc).cast<ApplicationInfo>()
|
||||
val appInfo by lazy {
|
||||
ProcessRecordClass.resolve().optional()
|
||||
.firstFieldOrNull { name = "info" }
|
||||
?.of(proc)?.get<ApplicationInfo>()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前进程名称
|
||||
* @return [String]
|
||||
*/
|
||||
val processName = ProcessRecordClass.toClass().field { name = "processName" }.get(proc).string()
|
||||
val processName by lazy {
|
||||
ProcessRecordClass.resolve().optional()
|
||||
.firstFieldOrNull { name = "processName" }
|
||||
?.of(proc)?.get<String>() ?: ""
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 APP、进程 包名
|
||||
@@ -155,17 +195,25 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
* 获取当前进程是否为后台进程
|
||||
* @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
|
||||
val isBackgroundProcess by lazy {
|
||||
UserControllerClass.resolve().optional()
|
||||
.firstMethodOrNull { name { it == "getCurrentProfileIds" || it == "getCurrentProfileIdsLocked" } }
|
||||
?.of(ActivityManagerServiceClass?.resolve()?.optional()?.firstFieldOrNull { name = "mUserController" }
|
||||
?.of(AppErrorsClass.resolve().optional().firstFieldOrNull { name = "mService" }?.of(errors)?.get())?.getQuietly())
|
||||
?.invokeQuietly<IntArray>()?.takeIf { it.isNotEmpty() }?.any { it != userId } ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前进程是否短时内重复崩溃
|
||||
* @return [Boolean]
|
||||
*/
|
||||
val isRepeatingCrash = resultData?.let { AppErrorDialog_DataClass.toClass().field { name = "repeating" }.get(it).boolean() } ?: false
|
||||
val isRepeatingCrash by lazy {
|
||||
resultData?.let {
|
||||
AppErrorDialog_DataClass.resolve().optional()
|
||||
.firstFieldOrNull { name = "repeating" }
|
||||
?.of(it)?.get<Boolean>() == true
|
||||
} ?: false
|
||||
}
|
||||
}
|
||||
|
||||
/** 注册生命周期 */
|
||||
@@ -184,34 +232,34 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
SystemClock.sleep(100)
|
||||
/** 刷新存储类 */
|
||||
AppErrorsConfigData.refresh()
|
||||
if (prefs.isPreferencesAvailable.not()) loggerW(msg = "Cannot refreshing app errors config data, preferences is not available")
|
||||
if (prefs.isPreferencesAvailable.not()) YLog.warn("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" } ?: ""}")
|
||||
YLog.info("Opened \"${it.first}\"${it.second.takeIf { e -> e > 0 }?.let { e -> " --user $e" } ?: ""}")
|
||||
}
|
||||
onPushAppErrorInfoData {
|
||||
AppErrorsRecordData.allData.firstOrNull { e -> e.pid == it } ?: run {
|
||||
loggerW(msg = "Cannot received crash application data --pid $it")
|
||||
YLog.warn("Cannot received crash application data --pid $it")
|
||||
AppErrorsInfoBean()
|
||||
}
|
||||
}
|
||||
onPushAppErrorsInfoData { AppErrorsRecordData.allData.toArrayList() }
|
||||
onRemoveAppErrorsInfoData {
|
||||
loggerI(msg = "Removed app errors info data for package \"${it.packageName}\"")
|
||||
YLog.info("Removed app errors info data for package \"${it.packageName}\"")
|
||||
AppErrorsRecordData.remove(it)
|
||||
}
|
||||
onClearAppErrorsInfoData {
|
||||
loggerI(msg = "Cleared all app errors info data, size ${AppErrorsRecordData.allData.size}")
|
||||
YLog.info("Cleared all app errors info data, size ${AppErrorsRecordData.allData.size}")
|
||||
AppErrorsRecordData.clearAll()
|
||||
}
|
||||
onMutedErrorsIfUnlock {
|
||||
mutedErrorsIfUnlockApps.add(it)
|
||||
loggerI(msg = "Muted \"$it\" until unlocks")
|
||||
YLog.info("Muted \"$it\" until unlocks")
|
||||
}
|
||||
onMutedErrorsIfRestart {
|
||||
mutedErrorsIfRestartApps.add(it)
|
||||
loggerI(msg = "Muted \"$it\" until restarts")
|
||||
YLog.info("Muted \"$it\" until restarts")
|
||||
}
|
||||
onPushMutedErrorsAppsData {
|
||||
arrayListOf<MutedErrorsAppBean>().apply {
|
||||
@@ -224,24 +272,24 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
onUnmuteErrorsApp {
|
||||
when (it.type) {
|
||||
MutedErrorsAppBean.MuteType.UNTIL_UNLOCKS -> {
|
||||
loggerI(msg = "Unmuted if unlocks errors app \"${it.packageName}\"")
|
||||
YLog.info("Unmuted if unlocks errors app \"${it.packageName}\"")
|
||||
mutedErrorsIfUnlockApps.remove(it.packageName)
|
||||
}
|
||||
MutedErrorsAppBean.MuteType.UNTIL_REBOOTS -> {
|
||||
loggerI(msg = "Unmuted if restarts errors app \"${it.packageName}\"")
|
||||
YLog.info("Unmuted if restarts errors app \"${it.packageName}\"")
|
||||
mutedErrorsIfRestartApps.remove(it.packageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
onUnmuteAllErrorsApps {
|
||||
loggerI(msg = "Unmute all errors apps --unlocks ${mutedErrorsIfUnlockApps.size} --restarts ${mutedErrorsIfRestartApps.size}")
|
||||
YLog.info("Unmute all errors apps --unlocks ${mutedErrorsIfUnlockApps.size} --restarts ${mutedErrorsIfRestartApps.size}")
|
||||
mutedErrorsIfUnlockApps.clear()
|
||||
mutedErrorsIfRestartApps.clear()
|
||||
}
|
||||
onPushAppListData { filters ->
|
||||
appContext?.let { context ->
|
||||
context.listOfPackages()
|
||||
.filter { it.packageName.let { e -> e != "android" && e != BuildConfig.APPLICATION_ID } }
|
||||
.filter { it.packageName.let { e -> e != "android" && e != BuildConfigWrapper.APPLICATION_ID } }
|
||||
.let { info ->
|
||||
arrayListOf<AppInfoBean>().apply {
|
||||
if (info.isNotEmpty())
|
||||
@@ -252,7 +300,9 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
* 是否为系统应用
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun PackageInfo.isSystemApp() = (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0
|
||||
fun PackageInfo.isSystemApp() = applicationInfo?.let {
|
||||
(it.flags and ApplicationInfo.FLAG_SYSTEM) != 0
|
||||
} ?: false
|
||||
when (filters.type) {
|
||||
AppFiltersType.USER -> result.filter { it.isSystemApp().not() }
|
||||
AppFiltersType.SYSTEM -> result.filter { it.isSystemApp() }
|
||||
@@ -260,7 +310,7 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
}
|
||||
}.sortedByDescending { it.lastUpdateTime }
|
||||
.forEach { add(AppInfoBean(name = context.appNameOf(it.packageName), packageName = it.packageName)) }
|
||||
else loggerW(msg = "Fetched installed packages but got empty list")
|
||||
else YLog.warn("Fetched installed packages but got empty list")
|
||||
}
|
||||
}
|
||||
} ?: arrayListOf()
|
||||
@@ -274,21 +324,21 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
*/
|
||||
private fun AppErrorsProcessData.handleShowAppErrorUi(context: Context) {
|
||||
/** 当前 APP 名称 */
|
||||
val appName = appInfo?.let { context.appNameOf(it.packageName) } ?: packageName
|
||||
val appName = appInfo?.let { context.appNameOf(it.packageName).ifBlank { it.packageName } } ?: packageName
|
||||
|
||||
/** 当前 APP 名称 (包含用户 ID) */
|
||||
val appNameWithUserId = if (userId != 0) "$appName (${LocaleString.userId(userId)})" else appName
|
||||
val appNameWithUserId = if (userId != 0) "$appName (${locale.userId(userId)})" else appName
|
||||
|
||||
/** 崩溃标题 */
|
||||
val errorTitle = if (isRepeatingCrash) LocaleString.aerrRepeatedTitle(appNameWithUserId) else LocaleString.aerrTitle(appNameWithUserId)
|
||||
val errorTitle = if (isRepeatingCrash) locale.aerrRepeatedTitle(appNameWithUserId) else locale.aerrTitle(appNameWithUserId)
|
||||
|
||||
/** 使用通知推送异常信息 */
|
||||
fun showAppErrorsWithNotify() =
|
||||
context.pushNotify(
|
||||
channelId = "APPS_ERRORS",
|
||||
channelName = LocaleString.appName,
|
||||
channelName = locale.appName,
|
||||
title = errorTitle,
|
||||
content = LocaleString.appErrorsTip,
|
||||
content = locale.appErrorsTip,
|
||||
icon = IconCompat.createWithBitmap(moduleAppResources.drawableOf(R.drawable.ic_notify).toBitmap()),
|
||||
color = 0xFFFF6200.toInt(),
|
||||
intent = AppErrorsRecordActivity.intent()
|
||||
@@ -309,9 +359,9 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
title = errorTitle,
|
||||
isShowAppInfoButton = isActualApp,
|
||||
isShowReopenButton = isActualApp &&
|
||||
(isRepeatingCrash.not() || ConfigData.isEnableAlwaysShowsReopenAppOptions) &&
|
||||
context.isAppCanOpened(packageName) &&
|
||||
isMainProcess,
|
||||
(isRepeatingCrash.not() || ConfigData.isEnableAlwaysShowsReopenAppOptions) &&
|
||||
context.isAppCanOpened(packageName) &&
|
||||
isMainProcess,
|
||||
isShowCloseAppButton = isActualApp
|
||||
)
|
||||
)
|
||||
@@ -322,9 +372,9 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
/** 判断是否为主进程 */
|
||||
if (isMainProcess.not() && ConfigData.isEnableOnlyShowErrorsInMain) return
|
||||
when {
|
||||
packageName == BuildConfig.APPLICATION_ID -> {
|
||||
packageName == BuildConfigWrapper.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")
|
||||
YLog.error("AppErrorsTracking has crashed itself, please see the Android Runtime Exception in console")
|
||||
}
|
||||
ConfigData.isEnableAppConfigTemplate -> when {
|
||||
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.GLOBAL, packageName) -> when {
|
||||
@@ -341,11 +391,11 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
else -> showAppErrorsWithDialog()
|
||||
}
|
||||
/** 打印错误日志 */
|
||||
if (isActualApp) loggerE(
|
||||
if (isActualApp) YLog.error(
|
||||
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")
|
||||
(if (packageName != processName) " --process \"$processName\"" else "") +
|
||||
"${if (userId != 0) " --user $userId" else ""} --pid $pid"
|
||||
) else YLog.error("Process \"$processName\" ${if (isRepeatingCrash) "keeps stopping" else "has stopped"} --pid $pid")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,101 +405,102 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
*/
|
||||
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")
|
||||
YLog.info("Received crash application data${if (userId != 0) " --user $userId" else ""} --pid $pid")
|
||||
}
|
||||
|
||||
override fun onHook() {
|
||||
/** 注册生命周期 */
|
||||
registerLifecycle()
|
||||
/** 干掉原生错误对话框 - 如果有 */
|
||||
ErrorDialogControllerClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "hasCrashDialogs"
|
||||
emptyParam()
|
||||
ErrorDialogControllerClass?.resolve()?.optional(silent = true)?.apply {
|
||||
val hasCrashDialogs = firstMethodOrNull {
|
||||
name = "hasCrashDialogs"
|
||||
emptyParameters()
|
||||
}?.hook()?.replaceToTrue() != null
|
||||
if (!hasCrashDialogs)
|
||||
firstConstructorOrNull {
|
||||
parameterCount = 1
|
||||
}?.hook()?.after {
|
||||
firstFieldOrNull { name = "mCrashDialogs" }?.of(instance)?.set(emptyList<Any>())
|
||||
}
|
||||
replaceToTrue()
|
||||
}
|
||||
injectMember {
|
||||
method {
|
||||
name = "showCrashDialogs"
|
||||
paramCount = 1
|
||||
}
|
||||
intercept()
|
||||
}
|
||||
}.ignoredHookClassNotFoundFailure()
|
||||
firstMethodOrNull {
|
||||
name = "showCrashDialogs"
|
||||
parameterCount = 1
|
||||
}?.hook()?.intercept()
|
||||
}
|
||||
/** 干掉原生错误对话框 - API 30 以下 */
|
||||
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()
|
||||
ActivityTaskManagerService_LocalServiceClass?.resolve()?.optional()?.firstMethodOrNull {
|
||||
name = "canShowErrorDialogs"
|
||||
emptyParameters()
|
||||
}?.hook()?.replaceToFalse()
|
||||
ActivityManagerServiceClass?.resolve()?.optional()?.firstMethodOrNull {
|
||||
name = "canShowErrorDialogs"
|
||||
emptyParameters()
|
||||
}?.hook()?.replaceToFalse()
|
||||
}
|
||||
/** 干掉原生错误对话框 - 如果上述方法全部失效则直接结束对话框 */
|
||||
AppErrorDialogClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
}
|
||||
afterHook { instance<Dialog>().cancel() }
|
||||
}.ignoredNoSuchMemberFailure()
|
||||
injectMember {
|
||||
method {
|
||||
name = "onStart"
|
||||
emptyParam()
|
||||
}
|
||||
afterHook { instance<Dialog>().cancel() }
|
||||
}.ignoredNoSuchMemberFailure()
|
||||
AppErrorDialogClass.resolve().optional(silent = true).apply {
|
||||
firstMethodOrNull {
|
||||
name = "onCreate"
|
||||
parameters(Bundle::class)
|
||||
}?.hook()?.after { instance<Dialog>().cancel() }
|
||||
firstMethodOrNull {
|
||||
name = "onStart"
|
||||
emptyParameters()
|
||||
}?.hook()?.after { instance<Dialog>().cancel() }
|
||||
}
|
||||
/** 注入自定义错误对话框 */
|
||||
AppErrorsClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "handleShowAppErrorUi"
|
||||
param(MessageClass)
|
||||
AppErrorsClass.resolve().optional().apply {
|
||||
when {
|
||||
Build.VERSION.SDK_INT > Build.VERSION_CODES.R -> {
|
||||
firstMethodOrNull {
|
||||
name = "handleAppCrashLSPB"
|
||||
parameterCount = 6
|
||||
}?.hook()?.after {
|
||||
/** 如果为用户终止则不展示异常 */
|
||||
if (args(index = 1).string() == "user-terminated") return@after
|
||||
/** 当前实例 */
|
||||
val context = appContext ?: firstFieldOrNull { name = "mContext" }?.of(instance)?.get<Context>() ?: return@after
|
||||
|
||||
/** 当前进程信息 */
|
||||
val proc = args().first().any() ?: return@after YLog.warn("Received but got null ProcessRecord (Show UI failed)")
|
||||
|
||||
/** 当前错误数据 */
|
||||
val resultData = args().last().any()
|
||||
/** 创建 APP 进程异常数据类 */
|
||||
AppErrorsProcessData(instance, proc, resultData).handleShowAppErrorUi(context)
|
||||
}
|
||||
}
|
||||
afterHook {
|
||||
/** 当前实例 */
|
||||
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: return@afterHook
|
||||
else -> {
|
||||
firstMethodOrNull {
|
||||
name = "handleShowAppErrorUi"
|
||||
parameters(Message::class)
|
||||
}?.hook()?.after {
|
||||
/** 当前实例 */
|
||||
val context = appContext ?: firstFieldOrNull { name = "mContext" }?.of(instance)?.get<Context>() ?: return@after
|
||||
|
||||
/** 当前错误数据 */
|
||||
val resultData = args().first().cast<Message>()?.obj
|
||||
/** 当前错误数据 */
|
||||
val resultData = args().first().cast<Message>()?.obj
|
||||
|
||||
/** 当前进程信息 */
|
||||
val proc = AppErrorDialog_DataClass.toClass().field { name = "proc" }.get(resultData).any()
|
||||
/** 创建 APP 进程异常数据类 */
|
||||
AppErrorsProcessData(instance, proc, resultData).handleShowAppErrorUi(context)
|
||||
/** 当前进程信息 */
|
||||
val proc = AppErrorDialog_DataClass.resolve().optional().firstFieldOrNull { name = "proc" }?.of(resultData)?.get()
|
||||
/** 创建 APP 进程异常数据类 */
|
||||
AppErrorsProcessData(instance, proc, resultData).handleShowAppErrorUi(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
injectMember {
|
||||
method {
|
||||
name = "handleAppCrashInActivityController"
|
||||
returnType = BooleanType
|
||||
}
|
||||
afterHook {
|
||||
/** 当前实例 */
|
||||
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: return@afterHook
|
||||
firstMethodOrNull {
|
||||
name = "handleAppCrashInActivityController"
|
||||
returnType = Boolean::class
|
||||
}?.hook()?.after {
|
||||
/** 当前实例 */
|
||||
val context = appContext ?: firstFieldOrNull { name = "mContext" }?.of(instance)?.get<Context>() ?: return@after
|
||||
|
||||
/** 当前进程信息 */
|
||||
val proc = args().first().any() ?: return@afterHook loggerW(msg = "Received but got null ProcessRecord")
|
||||
/** 创建 APP 进程异常数据类 */
|
||||
AppErrorsProcessData(instance, proc).handleAppErrorsInfo(context, args(index = 1).cast())
|
||||
}
|
||||
/** 当前进程信息 */
|
||||
val proc = args().first().any() ?: return@after YLog.warn("Received but got null ProcessRecord")
|
||||
/** 创建 APP 进程异常数据类 */
|
||||
AppErrorsProcessData(instance, proc).handleAppErrorsInfo(context, args(index = 1).cast())
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
|
||||
* Copyright (C) 2017 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/10/13.
|
||||
*/
|
||||
@file:Suppress("StaticFieldLeak")
|
||||
|
||||
package com.fankes.apperrorstracking.locale
|
||||
|
||||
import com.fankes.apperrorstracking.generated.locale.ModuleAppLocale
|
||||
|
||||
/** I18ns 实例 */
|
||||
lateinit var locale: ModuleAppLocale
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/11.
|
||||
* This file is created by fankes on 2022/5/11.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.service
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,16 +17,15 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/7.
|
||||
* This file is created by fankes on 2022/5/7.
|
||||
*/
|
||||
@file:Suppress("UNCHECKED_CAST")
|
||||
|
||||
package com.fankes.apperrorstracking.ui.activity.base
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
@@ -34,9 +33,9 @@ import androidx.viewbinding.ViewBinding
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.fankes.apperrorstracking.utils.factory.isNotSystemInDarkMode
|
||||
import com.fankes.apperrorstracking.utils.factory.toast
|
||||
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.kavaref.KavaRef.Companion.resolve
|
||||
import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
|
||||
import com.highcapable.kavaref.extension.toClassOrNull
|
||||
|
||||
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
|
||||
@@ -45,10 +44,12 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = current().generic()?.argument()?.method {
|
||||
val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
|
||||
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
|
||||
name = "inflate"
|
||||
param(LayoutInflaterClass)
|
||||
}?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed")
|
||||
parameters(LayoutInflater::class)
|
||||
}?.invoke<VB>(layoutInflater) ?: error("binding failed")
|
||||
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
|
||||
setContentView(binding.root)
|
||||
/** 隐藏系统的标题栏 */
|
||||
supportActionBar?.hide()
|
||||
@@ -57,6 +58,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
isAppearanceLightStatusBars = isNotSystemInDarkMode
|
||||
isAppearanceLightNavigationBars = isNotSystemInDarkMode
|
||||
}
|
||||
@Suppress("DEPRECATION")
|
||||
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
|
||||
window?.statusBarColor = it
|
||||
window?.navigationBarColor = 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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/10/4.
|
||||
* This file is created by fankes on 2022/10/4.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
|
||||
|
||||
@@ -31,20 +31,24 @@ import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import androidx.core.view.isVisible
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.fankes.apperrorstracking.const.PackageName
|
||||
import com.fankes.apperrorstracking.databinding.ActivitiyLoggerBinding
|
||||
import com.fankes.apperrorstracking.databinding.AdapterLoggerBinding
|
||||
import com.fankes.apperrorstracking.databinding.DiaLoggerFilterBinding
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
||||
import com.fankes.apperrorstracking.utils.factory.*
|
||||
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
|
||||
import com.fankes.apperrorstracking.utils.factory.bindAdapter
|
||||
import com.fankes.apperrorstracking.utils.factory.copyToClipboard
|
||||
import com.fankes.apperrorstracking.utils.factory.showDialog
|
||||
import com.fankes.apperrorstracking.utils.factory.toUtcTime
|
||||
import com.fankes.apperrorstracking.utils.factory.toast
|
||||
import com.highcapable.yukihookapi.hook.factory.dataChannel
|
||||
import com.highcapable.yukihookapi.hook.log.YukiHookLogger
|
||||
import com.highcapable.yukihookapi.hook.log.YukiLoggerData
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.log.data.YLogData
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.PrintStream
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
|
||||
class LoggerActivity : BaseActivity<ActivitiyLoggerBinding>() {
|
||||
|
||||
@@ -61,14 +65,14 @@ class LoggerActivity : BaseActivity<ActivitiyLoggerBinding>() {
|
||||
private var filters = arrayListOf("D", "I", "W", "E")
|
||||
|
||||
/** 全部的调试日志数据 */
|
||||
private val listData = ArrayList<YukiLoggerData>()
|
||||
private val listData = mutableListOf<YLogData>()
|
||||
|
||||
override fun onCreate() {
|
||||
binding.titleBackIcon.setOnClickListener { finish() }
|
||||
binding.refreshIcon.setOnClickListener { refreshData() }
|
||||
binding.filterIcon.setOnClickListener {
|
||||
showDialog<DiaLoggerFilterBinding> {
|
||||
title = LocaleString.filterByCondition
|
||||
title = locale.filterByCondition
|
||||
binding.configCheck0.isChecked = filters.any { it == "D" }
|
||||
binding.configCheck1.isChecked = filters.any { it == "I" }
|
||||
binding.configCheck2.isChecked = filters.any { it == "W" }
|
||||
@@ -124,7 +128,7 @@ class LoggerActivity : BaseActivity<ActivitiyLoggerBinding>() {
|
||||
|
||||
/** 更新列表数据 */
|
||||
private fun refreshData() {
|
||||
dataChannel(FrameworkTool.SYSTEM_FRAMEWORK_NAME).obtainLoggerInMemoryData {
|
||||
dataChannel(PackageName.SYSTEM_FRAMEWORK).obtainLoggerInMemoryData {
|
||||
listData.clear()
|
||||
it.takeIf { e -> e.isNotEmpty() }?.reversed()?.filter { filters.any { e -> it.priority == e } }?.forEach { e -> listData.add(e) }
|
||||
onChanged?.invoke()
|
||||
@@ -132,7 +136,7 @@ class LoggerActivity : BaseActivity<ActivitiyLoggerBinding>() {
|
||||
binding.exportAllIcon.isVisible = listData.isNotEmpty()
|
||||
binding.listView.isVisible = listData.isNotEmpty()
|
||||
binding.listNoDataView.isVisible = listData.isEmpty()
|
||||
binding.listNoDataView.text = if (filters.size < 4) LocaleString.noListResult else LocaleString.noListData
|
||||
binding.listNoDataView.text = if (filters.size < 4) locale.noListResult else locale.noListData
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,10 +176,10 @@ 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(YukiHookLogger.contents(listData).toByteArray()) }?.close()
|
||||
toast(LocaleString.exportAllLogsSuccess)
|
||||
} ?: toast(LocaleString.exportAllLogsFail)
|
||||
}.onFailure { toast(LocaleString.exportAllLogsFail) }
|
||||
contentResolver?.openOutputStream(it)?.apply { write(YLog.contents(listData).toByteArray()) }?.close()
|
||||
toast(locale.exportAllLogsSuccess)
|
||||
} ?: toast(locale.exportAllLogsFail)
|
||||
}.onFailure { toast(locale.exportAllLogsFail) }
|
||||
}
|
||||
|
||||
override fun onResume() {
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/7.
|
||||
* This file is created by fankes on 2022/5/7.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
|
||||
|
||||
@@ -27,17 +27,28 @@ import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.FileProvider
|
||||
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.ConfigData.bind
|
||||
import com.fankes.apperrorstracking.data.factory.bind
|
||||
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDetailBinding
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
||||
import com.fankes.apperrorstracking.utils.factory.*
|
||||
import com.fankes.apperrorstracking.utils.factory.appIconOf
|
||||
import com.fankes.apperrorstracking.utils.factory.appNameOf
|
||||
import com.fankes.apperrorstracking.utils.factory.copyToClipboard
|
||||
import com.fankes.apperrorstracking.utils.factory.dp
|
||||
import com.fankes.apperrorstracking.utils.factory.getSerializableExtraCompat
|
||||
import com.fankes.apperrorstracking.utils.factory.navigate
|
||||
import com.fankes.apperrorstracking.utils.factory.openSelfSetting
|
||||
import com.fankes.apperrorstracking.utils.factory.showDialog
|
||||
import com.fankes.apperrorstracking.utils.factory.toast
|
||||
import com.fankes.apperrorstracking.utils.tool.StackTraceShareHelper
|
||||
import com.highcapable.yukihookapi.hook.log.loggerE
|
||||
import java.io.File
|
||||
|
||||
class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
||||
|
||||
@@ -62,55 +73,104 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
||||
private var stackTrace = ""
|
||||
|
||||
override fun onCreate() {
|
||||
if (initUi(intent).not()) return
|
||||
binding.titleBackIcon.setOnClickListener { onBackPressed() }
|
||||
binding.disableAutoWrapErrorStackTraceSwitch.bind(ConfigData.DISABLE_AUTO_WRAP_ERROR_STACK_TRACE) {
|
||||
onInitialize {
|
||||
binding.errorStackTraceScrollView.isVisible = it
|
||||
binding.errorStackTraceFixedText.isGone = it
|
||||
}
|
||||
onChanged {
|
||||
reinitialize()
|
||||
resetScrollView()
|
||||
}
|
||||
}
|
||||
binding.detailTitleText.setOnClickListener { binding.appPanelScrollView.smoothScrollTo(0, 0) }
|
||||
resetScrollView()
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 [Intent] 中解析 [AppErrorsInfoBean] 并加载至界面
|
||||
*
|
||||
* @param intent 用于获取并解析 [AppErrorsInfoBean] 的 [Intent] 实例
|
||||
* @return [Boolean] 是否解析成功:true 为成功;false 为失败,可能是 [Intent] 为空或者 [AppErrorsInfoBean] 为空
|
||||
*/
|
||||
private fun initUi(intent: Intent?): Boolean {
|
||||
val appErrorsInfo = runCatching { intent?.getSerializableExtraCompat<AppErrorsInfoBean>(EXTRA_APP_ERRORS_INFO) }.getOrNull()
|
||||
?: return toastAndFinish(name = "AppErrorsInfo")
|
||||
if (appErrorsInfo == null) {
|
||||
toastAndFinish(name = "AppErrorsInfo")
|
||||
return false
|
||||
}
|
||||
if (appErrorsInfo.isEmpty) {
|
||||
binding.appPanelScrollView.isVisible = false
|
||||
showDialog {
|
||||
title = LocaleString.notice
|
||||
msg = LocaleString.unableGetAppErrorsRecordTip
|
||||
confirmButton(LocaleString.gotIt) {
|
||||
title = locale.notice
|
||||
msg = locale.unableGetAppErrorsRecordTip
|
||||
confirmButton(locale.gotIt) {
|
||||
cancel()
|
||||
finish()
|
||||
}
|
||||
cancelButton(LocaleString.goItNow) {
|
||||
cancelButton(locale.goItNow) {
|
||||
cancel()
|
||||
finish()
|
||||
navigate<AppErrorsRecordActivity>()
|
||||
}
|
||||
noCancelable()
|
||||
}
|
||||
return
|
||||
return false
|
||||
}
|
||||
binding.appInfoItem.setOnClickListener { openSelfSetting(appErrorsInfo.packageName) }
|
||||
binding.titleBackIcon.setOnClickListener { onBackPressed() }
|
||||
binding.printIcon.setOnClickListener {
|
||||
loggerE(msg = appErrorsInfo.stackTrace)
|
||||
toast(LocaleString.printToLogcatSuccess)
|
||||
toast(locale.printToLogcatSuccess)
|
||||
}
|
||||
binding.copyIcon.setOnClickListener {
|
||||
StackTraceShareHelper.showChoose(context = this, locale.copyErrorStack) { sDeviceBrand, sDeviceModel, sDisplay, sPackageName ->
|
||||
copyToClipboard(appErrorsInfo.stackOutputShareContent(sDeviceBrand, sDeviceModel, sDisplay, sPackageName))
|
||||
}
|
||||
}
|
||||
binding.copyIcon.setOnClickListener { copyToClipboard(appErrorsInfo.stackOutputShareContent) }
|
||||
binding.exportIcon.setOnClickListener {
|
||||
stackTrace = appErrorsInfo.stackOutputFileContent
|
||||
runCatching {
|
||||
startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "*/application"
|
||||
putExtra(Intent.EXTRA_TITLE, "${appErrorsInfo.packageName}_${appErrorsInfo.utcTime}.log")
|
||||
}, WRITE_REQUEST_CODE)
|
||||
}.onFailure { toast(msg = "Start Android SAF failed") }
|
||||
StackTraceShareHelper.showChoose(context = this, locale.exportToFile) { sDeviceBrand, sDeviceModel, sDisplay, sPackageName ->
|
||||
stackTrace = appErrorsInfo.stackOutputFileContent(sDeviceBrand, sDeviceModel, sDisplay, sPackageName)
|
||||
runCatching {
|
||||
startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "*/application"
|
||||
val packageName = if (sPackageName) appErrorsInfo.packageName else "anonymous"
|
||||
putExtra(Intent.EXTRA_TITLE, "${packageName}_${appErrorsInfo.utcTime}.log")
|
||||
}, WRITE_REQUEST_CODE)
|
||||
}.onFailure { toast(msg = "Start Android SAF failed") }
|
||||
}
|
||||
}
|
||||
binding.shareIcon.setOnClickListener {
|
||||
startActivity(Intent.createChooser(Intent(Intent.ACTION_SEND).apply {
|
||||
type = "text/plain"
|
||||
putExtra(Intent.EXTRA_TEXT, appErrorsInfo.stackOutputShareContent)
|
||||
}, LocaleString.shareErrorStack))
|
||||
StackTraceShareHelper.showChoose(context = this, locale.shareErrorStack) { sDeviceBrand, sDeviceModel, sDisplay, sPackageName ->
|
||||
startActivity(Intent.createChooser(Intent(Intent.ACTION_SEND).apply {
|
||||
val content = appErrorsInfo.stackOutputShareContent(sDeviceBrand, sDeviceModel, sDisplay, sPackageName)
|
||||
if (ConfigData.isShareWithFile) {
|
||||
type = "application/octet-stream"
|
||||
runCatching {
|
||||
val file = File.createTempFile("app_errors_stacktrace_", ".log", cacheDir)
|
||||
file.deleteOnExit()
|
||||
file.writeText(content)
|
||||
putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(this@AppErrorsDetailActivity, "$packageName.provider", file))
|
||||
}.onFailure {
|
||||
toast(msg = "Create temp file failed")
|
||||
}
|
||||
} else {
|
||||
type = "text/plain"
|
||||
putExtra(Intent.EXTRA_TEXT, content)
|
||||
}
|
||||
}, locale.shareErrorStack))
|
||||
}
|
||||
}
|
||||
binding.appIcon.setImageDrawable(appIconOf(appErrorsInfo.packageName))
|
||||
binding.appNameText.text = appNameOf(appErrorsInfo.packageName)
|
||||
binding.appNameText.text = appNameOf(appErrorsInfo.packageName).ifBlank { appErrorsInfo.packageName }
|
||||
binding.appVersionText.text = appErrorsInfo.versionBrand
|
||||
binding.appUserIdText.isVisible = appErrorsInfo.userId > 0
|
||||
binding.appUserIdText.text = LocaleString.userId(appErrorsInfo.userId)
|
||||
binding.appCpuAbiText.text = appErrorsInfo.cpuAbi.ifBlank { LocaleString.noCpuAbi }
|
||||
binding.appUserIdText.text = locale.userId(appErrorsInfo.userId)
|
||||
binding.appCpuAbiText.text = appErrorsInfo.cpuAbi.ifBlank { locale.noCpuAbi }
|
||||
binding.appTargetSdkText.text = locale.appTargetSdk(appErrorsInfo.targetSdk)
|
||||
binding.appMinSdkText.text = locale.appMinSdk(appErrorsInfo.minSdk)
|
||||
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
|
||||
@@ -122,16 +182,12 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
||||
binding.errorRecordTimeText.text = appErrorsInfo.dateTime
|
||||
binding.errorStackTraceMovableText.text = appErrorsInfo.stackTrace
|
||||
binding.errorStackTraceFixedText.text = appErrorsInfo.stackTrace
|
||||
binding.disableAutoWrapErrorStackTraceSwitch.bind(ConfigData.DISABLE_AUTO_WRAP_ERROR_STACK_TRACE) {
|
||||
binding.errorStackTraceScrollView.isVisible = it
|
||||
binding.errorStackTraceFixedText.isGone = it
|
||||
resetScrollView()
|
||||
}
|
||||
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@AppErrorsDetailActivity))
|
||||
appNameOf(appErrorsInfo.packageName).ifBlank { appErrorsInfo.packageName }
|
||||
else locale.appName
|
||||
}
|
||||
binding.detailTitleText.setOnClickListener { binding.appPanelScrollView.smoothScrollTo(0, 0) }
|
||||
resetScrollView()
|
||||
return true
|
||||
}
|
||||
|
||||
/** 修复在一些小屏设备上设置了 [TextView.setTextIsSelectable] 后布局自动上滑问题 */
|
||||
@@ -147,13 +203,18 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
||||
if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) runCatching {
|
||||
data?.data?.let {
|
||||
contentResolver?.openOutputStream(it)?.apply { write(stackTrace.toByteArray()) }?.close()
|
||||
toast(LocaleString.outputStackSuccess)
|
||||
} ?: toast(LocaleString.outputStackFail)
|
||||
}.onFailure { toast(LocaleString.outputStackFail) }
|
||||
toast(locale.outputStackSuccess)
|
||||
} ?: toast(locale.outputStackFail)
|
||||
}.onFailure { toast(locale.outputStackFail) }
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
intent?.removeExtra(EXTRA_APP_ERRORS_INFO)
|
||||
finish()
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
if (initUi(intent)) binding.appPanelScrollView.scrollTo(0, 0)
|
||||
}
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/6/1.
|
||||
* This file is created by fankes on 2022/6/1.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.ui.activity.errors
|
||||
|
||||
@@ -29,9 +29,14 @@ import com.fankes.apperrorstracking.bean.AppErrorsDisplayBean
|
||||
import com.fankes.apperrorstracking.data.ConfigData
|
||||
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDisplayBinding
|
||||
import com.fankes.apperrorstracking.databinding.DiaAppErrorsDisplayBinding
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
||||
import com.fankes.apperrorstracking.utils.factory.*
|
||||
import com.fankes.apperrorstracking.utils.factory.colorOf
|
||||
import com.fankes.apperrorstracking.utils.factory.getSerializableExtraCompat
|
||||
import com.fankes.apperrorstracking.utils.factory.navigate
|
||||
import com.fankes.apperrorstracking.utils.factory.openSelfSetting
|
||||
import com.fankes.apperrorstracking.utils.factory.showDialog
|
||||
import com.fankes.apperrorstracking.utils.factory.toast
|
||||
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
|
||||
|
||||
class AppErrorsDisplayActivity : BaseActivity<ActivityAppErrorsDisplayBinding>() {
|
||||
@@ -73,7 +78,7 @@ class AppErrorsDisplayActivity : BaseActivity<ActivityAppErrorsDisplayBinding>()
|
||||
binding.appInfoItem.isVisible = appErrorsDisplay.isShowAppInfoButton
|
||||
binding.closeAppItem.isVisible = appErrorsDisplay.isShowReopenButton.not() && appErrorsDisplay.isShowCloseAppButton
|
||||
binding.reopenAppItem.isVisible = appErrorsDisplay.isShowReopenButton
|
||||
binding.processNameText.text = LocaleString.crashProcess(appErrorsDisplay.processName)
|
||||
binding.processNameText.text = locale.crashProcess(appErrorsDisplay.processName)
|
||||
binding.appInfoItem.setOnClickListener {
|
||||
cancel()
|
||||
openSelfSetting(appErrorsDisplay.packageName)
|
||||
@@ -91,13 +96,13 @@ class AppErrorsDisplayActivity : BaseActivity<ActivityAppErrorsDisplayBinding>()
|
||||
}
|
||||
binding.mutedIfUnlockItem.setOnClickListener {
|
||||
FrameworkTool.mutedErrorsIfUnlock(context, appErrorsDisplay.packageName) {
|
||||
toast(LocaleString.muteIfUnlockTip(appErrorsDisplay.appName))
|
||||
toast(locale.muteIfUnlockTip(appErrorsDisplay.appName))
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
binding.mutedIfRestartItem.setOnClickListener {
|
||||
FrameworkTool.mutedErrorsIfRestart(context, appErrorsDisplay.packageName) {
|
||||
toast(LocaleString.muteIfRestartTip(appErrorsDisplay.appName))
|
||||
toast(locale.muteIfRestartTip(appErrorsDisplay.appName))
|
||||
cancel()
|
||||
}
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,17 +17,15 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/6/3.
|
||||
* This file is created by fankes on 2022/6/3.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
|
||||
|
||||
package com.fankes.apperrorstracking.ui.activity.errors
|
||||
|
||||
import androidx.core.view.isVisible
|
||||
import com.fankes.apperrorstracking.bean.MutedErrorsAppBean
|
||||
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsMutedBinding
|
||||
import com.fankes.apperrorstracking.databinding.AdapterAppErrorsMutedBinding
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
||||
import com.fankes.apperrorstracking.utils.factory.appIconOf
|
||||
import com.fankes.apperrorstracking.utils.factory.appNameOf
|
||||
@@ -47,8 +45,8 @@ class AppErrorsMutedActivity : BaseActivity<ActivityAppErrorsMutedBinding>() {
|
||||
binding.titleBackIcon.setOnClickListener { onBackPressed() }
|
||||
binding.unmuteAllIcon.setOnClickListener {
|
||||
showDialog {
|
||||
title = LocaleString.notice
|
||||
msg = LocaleString.areYouSureUnmuteAll
|
||||
title = locale.notice
|
||||
msg = locale.areYouSureUnmuteAll
|
||||
confirmButton { FrameworkTool.unmuteAllErrorsApps(context) { refreshData() } }
|
||||
cancelButton()
|
||||
}
|
||||
@@ -59,10 +57,10 @@ 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
|
||||
MutedErrorsAppBean.MuteType.UNTIL_UNLOCKS -> locale.muteIfUnlock
|
||||
MutedErrorsAppBean.MuteType.UNTIL_REBOOTS -> locale.muteIfRestart
|
||||
}
|
||||
binding.unmuteButton.setOnClickListener { FrameworkTool.unmuteErrorsApp(context, bean) { refreshData() } }
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/11.
|
||||
* This file is created by fankes on 2022/5/11.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION", "SetTextI18n")
|
||||
|
||||
@@ -31,7 +31,6 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo
|
||||
import androidx.core.view.isVisible
|
||||
import com.fankes.apperrorstracking.BuildConfig
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
|
||||
import com.fankes.apperrorstracking.bean.AppFiltersBean
|
||||
@@ -39,11 +38,21 @@ 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
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
||||
import com.fankes.apperrorstracking.utils.factory.*
|
||||
import com.fankes.apperrorstracking.utils.factory.appIconOf
|
||||
import com.fankes.apperrorstracking.utils.factory.appNameOf
|
||||
import com.fankes.apperrorstracking.utils.factory.bindAdapter
|
||||
import com.fankes.apperrorstracking.utils.factory.decimal
|
||||
import com.fankes.apperrorstracking.utils.factory.newThread
|
||||
import com.fankes.apperrorstracking.utils.factory.openSelfSetting
|
||||
import com.fankes.apperrorstracking.utils.factory.showDialog
|
||||
import com.fankes.apperrorstracking.utils.factory.toUtcTime
|
||||
import com.fankes.apperrorstracking.utils.factory.toast
|
||||
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
|
||||
import com.fankes.apperrorstracking.utils.tool.StackTraceShareHelper
|
||||
import com.fankes.apperrorstracking.utils.tool.ZipFileTool
|
||||
import com.fankes.apperrorstracking.wrapper.BuildConfigWrapper
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
|
||||
@@ -58,7 +67,7 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
||||
* 获取 [Intent]
|
||||
* @return [Intent]
|
||||
*/
|
||||
fun intent() = Intent().apply { component = ComponentName(BuildConfig.APPLICATION_ID, AppErrorsRecordActivity::class.java.name) }
|
||||
fun intent() = Intent().apply { component = ComponentName(BuildConfigWrapper.APPLICATION_ID, AppErrorsRecordActivity::class.java.name) }
|
||||
}
|
||||
|
||||
/** 当前导出文件的路径 */
|
||||
@@ -74,8 +83,8 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
||||
binding.titleBackIcon.setOnClickListener { onBackPressed() }
|
||||
binding.appErrorSisIcon.setOnClickListener {
|
||||
showDialog {
|
||||
title = LocaleString.notice
|
||||
progressContent = LocaleString.generatingStatistics
|
||||
title = locale.notice
|
||||
progressContent = locale.generatingStatistics
|
||||
noCancelable()
|
||||
FrameworkTool.fetchAppListData(context, AppFiltersBean(type = AppFiltersType.ALL)) {
|
||||
newThread {
|
||||
@@ -92,14 +101,14 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
||||
runOnUiThread {
|
||||
cancel()
|
||||
showDialog<DiaAppErrorsStatisticsBinding> {
|
||||
title = LocaleString.appErrorsStatistics
|
||||
binding.totalErrorsUnitText.text = LocaleString.totalErrorsUnit(listData.size)
|
||||
binding.totalAppsUnitText.text = LocaleString.totalAppsUnit(it.size)
|
||||
title = locale.appErrorsStatistics
|
||||
binding.totalErrorsUnitText.text = locale.totalErrorsUnit(listData.size)
|
||||
binding.totalAppsUnitText.text = locale.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)
|
||||
confirmButton(locale.gotIt)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,12 +117,12 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
||||
}
|
||||
binding.clearAllIcon.setOnClickListener {
|
||||
showDialog {
|
||||
title = LocaleString.notice
|
||||
msg = LocaleString.areYouSureClearErrors
|
||||
title = locale.notice
|
||||
msg = locale.areYouSureClearErrors
|
||||
confirmButton {
|
||||
FrameworkTool.clearAppErrorsInfoData(context) {
|
||||
refreshData()
|
||||
toast(LocaleString.allErrorsClearSuccess)
|
||||
toast(locale.allErrorsClearSuccess)
|
||||
}
|
||||
}
|
||||
cancelButton()
|
||||
@@ -121,8 +130,8 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
||||
}
|
||||
binding.exportAllIcon.setOnClickListener {
|
||||
showDialog {
|
||||
title = LocaleString.notice
|
||||
msg = LocaleString.areYouSureExportAllErrors
|
||||
title = locale.notice
|
||||
msg = locale.areYouSureExportAllErrors
|
||||
confirmButton { exportAll() }
|
||||
cancelButton()
|
||||
}
|
||||
@@ -134,9 +143,9 @@ 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.appUserIdText.text = locale.userId(bean.userId)
|
||||
binding.errorsTimeText.text = bean.crossTime
|
||||
binding.errorTypeIcon.setImageResource(if (bean.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
|
||||
binding.errorTypeText.text = if (bean.isNativeCrash) "Native crash" else bean.exceptionClassName.simpleThwName()
|
||||
@@ -152,34 +161,40 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
||||
/** 更新列表数据 */
|
||||
private fun refreshData() {
|
||||
FrameworkTool.fetchAppErrorsInfoData(context = this) {
|
||||
binding.titleCountText.text = locale.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()
|
||||
}
|
||||
}
|
||||
|
||||
/** 打包导出全部 */
|
||||
private fun exportAll() {
|
||||
clearAllExportTemp()
|
||||
("${cacheDir.absolutePath}/temp").also { path ->
|
||||
File(path).mkdirs()
|
||||
listData.takeIf { it.isNotEmpty() }?.forEach {
|
||||
File("$path/${it.packageName}_${it.utcTime}.log").writeText(it.stackOutputFileContent)
|
||||
StackTraceShareHelper.showChoose(context = this, locale.exportAll) { sDeviceBrand, sDeviceModel, sDisplay, sPackageName ->
|
||||
("${cacheDir.absolutePath}/temp").also { path ->
|
||||
File(path).mkdirs()
|
||||
listData.takeIf { it.isNotEmpty() }?.forEachIndexed { index, bean ->
|
||||
val packageName = if (sPackageName) bean.packageName else "anonymous_$index"
|
||||
File("$path/${packageName}_${bean.utcTime}.log")
|
||||
.writeText(bean.stackOutputFileContent(sDeviceBrand, sDeviceModel, sDisplay, sPackageName))
|
||||
}
|
||||
outPutFilePath = "${cacheDir.absolutePath}/temp_${System.currentTimeMillis()}.zip"
|
||||
ZipFileTool.zipMultiFile(path, outPutFilePath)
|
||||
runCatching {
|
||||
startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "*/application"
|
||||
putExtra(Intent.EXTRA_TITLE, "app_errors_info_${System.currentTimeMillis().toUtcTime()}.zip")
|
||||
}, WRITE_REQUEST_CODE)
|
||||
}.onFailure { toast(msg = "Start Android SAF failed") }
|
||||
}
|
||||
outPutFilePath = "${cacheDir.absolutePath}/temp_${System.currentTimeMillis()}.zip"
|
||||
ZipFileTool.zipMultiFile(path, outPutFilePath)
|
||||
runCatching {
|
||||
startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "*/application"
|
||||
putExtra(Intent.EXTRA_TITLE, "app_errors_info_${System.currentTimeMillis().toUtcTime()}.zip")
|
||||
}, WRITE_REQUEST_CODE)
|
||||
}.onFailure { toast(msg = "Start Android SAF failed") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,8 +224,8 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
||||
R.id.aerrors_app_info -> openSelfSetting(listData[it.position].packageName)
|
||||
R.id.aerrors_remove_record ->
|
||||
showDialog {
|
||||
title = LocaleString.notice
|
||||
msg = LocaleString.areYouSureRemoveRecord
|
||||
title = locale.notice
|
||||
msg = locale.areYouSureRemoveRecord
|
||||
confirmButton { FrameworkTool.removeAppErrorsInfoData(context, listData[it.position]) { refreshData() } }
|
||||
cancelButton()
|
||||
}
|
||||
@@ -225,9 +240,9 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
||||
data?.data?.let {
|
||||
contentResolver?.openOutputStream(it)?.apply { write(FileInputStream(outPutFilePath).readBytes()) }?.close()
|
||||
clearAllExportTemp()
|
||||
toast(LocaleString.exportAllErrorsSuccess)
|
||||
} ?: toast(LocaleString.exportAllErrorsFail)
|
||||
}.onFailure { toast(LocaleString.exportAllErrorsFail) }
|
||||
toast(locale.exportAllErrorsSuccess)
|
||||
} ?: toast(locale.exportAllErrorsFail)
|
||||
}.onFailure { toast(locale.exportAllErrorsFail) }
|
||||
}
|
||||
|
||||
override fun onResume() {
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/6/4.
|
||||
* This file is created by fankes on 2022/6/4.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.ui.activity.main
|
||||
|
||||
@@ -31,7 +31,7 @@ import com.fankes.apperrorstracking.databinding.ActivityConfigBinding
|
||||
import com.fankes.apperrorstracking.databinding.AdapterAppInfoBinding
|
||||
import com.fankes.apperrorstracking.databinding.DiaAppConfigBinding
|
||||
import com.fankes.apperrorstracking.databinding.DiaAppsFilterBinding
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
||||
import com.fankes.apperrorstracking.utils.factory.appIconOf
|
||||
import com.fankes.apperrorstracking.utils.factory.bindAdapter
|
||||
@@ -53,16 +53,16 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
override fun onCreate() {
|
||||
binding.titleBackIcon.setOnClickListener { finish() }
|
||||
binding.globalIcon.setOnClickListener {
|
||||
showAppConfigDialog(LocaleString.globalConfig, isShowGlobalConfig = false) { type ->
|
||||
showAppConfigDialog(locale.globalConfig, isShowGlobalConfig = false) { type ->
|
||||
AppErrorsConfigData.putAppShowingType(type)
|
||||
onChanged?.invoke()
|
||||
}
|
||||
}
|
||||
binding.batchIcon.setOnClickListener {
|
||||
showAppConfigDialog(LocaleString.batchOperationsNumber(listData.size), isNotSetDefaultValue = true) { type ->
|
||||
showAppConfigDialog(locale.batchOperationsNumber(listData.size), isNotSetDefaultValue = true) { type ->
|
||||
showDialog {
|
||||
title = LocaleString.notice
|
||||
msg = LocaleString.areYouSureApplySiteApps(listData.size)
|
||||
title = locale.notice
|
||||
msg = locale.areYouSureApplySiteApps(listData.size)
|
||||
confirmButton {
|
||||
listData.takeIf { it.isNotEmpty() }?.forEach { AppErrorsConfigData.putAppShowingType(type, it.packageName) }
|
||||
onChanged?.invoke()
|
||||
@@ -73,7 +73,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
}
|
||||
binding.filterIcon.setOnClickListener {
|
||||
showDialog<DiaAppsFilterBinding> {
|
||||
title = LocaleString.filterByCondition
|
||||
title = locale.filterByCondition
|
||||
binding.filtersRadioUser.isChecked = appFilters.type == AppFiltersType.USER
|
||||
binding.filtersRadioSystem.isChecked = appFilters.type == AppFiltersType.SYSTEM
|
||||
binding.filtersRadioAll.isChecked = appFilters.type == AppFiltersType.ALL
|
||||
@@ -101,7 +101,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
}
|
||||
cancelButton()
|
||||
if (appFilters.name.isNotBlank())
|
||||
neutralButton(LocaleString.clearFilters) {
|
||||
neutralButton(locale.clearFilters) {
|
||||
setAppFiltersType()
|
||||
appFilters.name = ""
|
||||
refreshData()
|
||||
@@ -116,11 +116,11 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
binding.appIcon.setImageDrawable(bean.icon)
|
||||
binding.appNameText.text = bean.name
|
||||
binding.configTypeText.text = when {
|
||||
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
|
||||
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.GLOBAL, bean.packageName) -> locale.followGlobalConfig
|
||||
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.DIALOG, bean.packageName) -> locale.showErrorsDialog
|
||||
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.NOTIFY, bean.packageName) -> locale.showErrorsNotify
|
||||
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.TOAST, bean.packageName) -> locale.showErrorsToast
|
||||
AppErrorsConfigData.isAppShowingType(AppErrorsConfigType.NOTHING, bean.packageName) -> locale.showNothing
|
||||
else -> "Unknown type"
|
||||
}
|
||||
}
|
||||
@@ -138,8 +138,8 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
/** 模块未完全激活将显示警告 */
|
||||
if (MainActivity.isModuleValied.not())
|
||||
showDialog {
|
||||
title = LocaleString.notice
|
||||
msg = LocaleString.moduleNotFullyActivatedTip
|
||||
title = locale.notice
|
||||
msg = locale.moduleNotFullyActivatedTip
|
||||
confirmButton { FrameworkTool.restartSystem(context) }
|
||||
cancelButton()
|
||||
noCancelable()
|
||||
@@ -199,7 +199,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
binding.filterIcon.isVisible = false
|
||||
binding.listView.isVisible = false
|
||||
binding.listNoDataView.isVisible = false
|
||||
binding.titleCountText.text = LocaleString.loading
|
||||
binding.titleCountText.text = locale.loading
|
||||
FrameworkTool.fetchAppListData(context = this, appFilters) {
|
||||
/** 设置一个临时变量用于更新列表数据 */
|
||||
val tempsData = ArrayList<AppInfoBean>()
|
||||
@@ -221,7 +221,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
binding.filterIcon.isVisible = true
|
||||
binding.listView.isVisible = listData.isNotEmpty()
|
||||
binding.listNoDataView.isVisible = listData.isEmpty()
|
||||
binding.titleCountText.text = LocaleString.resultCount(listData.size)
|
||||
binding.titleCountText.text = locale.resultCount(listData.size)
|
||||
} else tempsData.clear()
|
||||
}
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/14.
|
||||
* This file is created by fankes on 2022/5/14.
|
||||
*/
|
||||
@file:Suppress("SetTextI18n")
|
||||
|
||||
@@ -25,20 +25,28 @@ package com.fankes.apperrorstracking.ui.activity.main
|
||||
|
||||
import android.os.Build
|
||||
import androidx.core.view.isVisible
|
||||
import com.fankes.apperrorstracking.BuildConfig
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.fankes.apperrorstracking.const.ModuleVersion
|
||||
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.locale.locale
|
||||
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
||||
import com.fankes.apperrorstracking.ui.activity.debug.LoggerActivity
|
||||
import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsMutedActivity
|
||||
import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsRecordActivity
|
||||
import com.fankes.apperrorstracking.utils.factory.*
|
||||
import com.fankes.apperrorstracking.utils.factory.hideOrShowLauncherIcon
|
||||
import com.fankes.apperrorstracking.utils.factory.isLauncherIconShowing
|
||||
import com.fankes.apperrorstracking.utils.factory.isSystemLanguageSimplifiedChinese
|
||||
import com.fankes.apperrorstracking.utils.factory.navigate
|
||||
import com.fankes.apperrorstracking.utils.factory.openBrowser
|
||||
import com.fankes.apperrorstracking.utils.factory.showDialog
|
||||
import com.fankes.apperrorstracking.utils.factory.toast
|
||||
import com.fankes.apperrorstracking.utils.tool.AppAnalyticsTool
|
||||
import com.fankes.apperrorstracking.utils.tool.AppAnalyticsTool.bindAppAnalytics
|
||||
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
|
||||
import com.fankes.apperrorstracking.utils.tool.GithubReleaseTool
|
||||
import com.fankes.projectpromote.ProjectPromote
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
|
||||
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
@@ -55,36 +63,58 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
override fun onCreate() {
|
||||
checkingTopComponentName()
|
||||
/** 检查更新 */
|
||||
GithubReleaseTool.checkingForUpdate(context = this, BuildConfig.VERSION_NAME) { version, function ->
|
||||
GithubReleaseTool.checkingForUpdate(context = this, ModuleVersion.NAME) { version, function ->
|
||||
binding.mainTextReleaseVersion.apply {
|
||||
text = LocaleString.clickToUpdate(version)
|
||||
text = locale.clickToUpdate(version)
|
||||
isVisible = true
|
||||
setOnClickListener { function() }
|
||||
}
|
||||
}
|
||||
binding.mainTextVersion.text = LocaleString.moduleVersion(BuildConfig.VERSION_NAME)
|
||||
binding.mainTextSystemVersion.text = LocaleString.systemVersion(systemVersion)
|
||||
/** 推广、恰饭 */
|
||||
if (YukiHookAPI.Status.isXposedModuleActive) ProjectPromote.show(activity = this, ModuleVersion.toString())
|
||||
/** 显示开发者提示 */
|
||||
if (ConfigData.isShowDeveloperNotice)
|
||||
showDialog {
|
||||
title = locale.developerNotice
|
||||
msg = locale.developerNoticeTip
|
||||
confirmButton(locale.gotIt) { ConfigData.isShowDeveloperNotice = false }
|
||||
noCancelable()
|
||||
}
|
||||
/** 设置 CI 自动构建标识 */
|
||||
if (ModuleVersion.isCiMode)
|
||||
binding.mainTextReleaseVersion.apply {
|
||||
text = "CI ${ModuleVersion.GITHUB_COMMIT_ID}"
|
||||
isVisible = true
|
||||
setOnClickListener {
|
||||
showDialog {
|
||||
title = locale.ciNoticeDialogTitle
|
||||
msg = locale.ciNoticeDialogContent(ModuleVersion.GITHUB_COMMIT_ID)
|
||||
confirmButton(locale.gotIt)
|
||||
noCancelable()
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.mainTextVersion.text = locale.moduleVersion(ModuleVersion.NAME)
|
||||
binding.mainTextSystemVersion.text = locale.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.shareWithFile.bind(ConfigData.SHARE_WITH_FILE)
|
||||
binding.enableAppsConfigsTemplateSwitch.bind(ConfigData.ENABLE_APP_CONFIG_TEMPLATE) {
|
||||
binding.mgrAppsConfigsTemplateButton.isVisible = it
|
||||
onInitialize { binding.mgrAppsConfigsTemplateButton.isVisible = it }
|
||||
onChanged { reinitialize() }
|
||||
}
|
||||
binding.errorsDialogPreventMisoperationSwitch.bind(ConfigData.ENABLE_PREVENT_MISOPERATION_FOR_DIALOG)
|
||||
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.appAnalyticsConfigItem.isVisible = AppAnalyticsTool.isAvailable
|
||||
binding.enableAnonymousStatisticsSwitch.bindAppAnalytics()
|
||||
/** 系统版本点击事件 */
|
||||
binding.mainTextSystemVersion.setOnClickListener {
|
||||
showDialog {
|
||||
title = LocaleString.notice
|
||||
title = locale.notice
|
||||
msg = systemVersion
|
||||
confirmButton(LocaleString.gotIt)
|
||||
confirmButton(locale.gotIt)
|
||||
}
|
||||
}
|
||||
/** 管理应用配置模板按钮点击事件 */
|
||||
@@ -98,14 +128,17 @@ 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.paymentFollowingZhCnItem.isVisible = isSystemLanguageSimplifiedChinese
|
||||
binding.linkWithFollowMe.setOnClickListener {
|
||||
openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market")
|
||||
}
|
||||
/** 设置桌面图标显示隐藏 */
|
||||
binding.hideIconInLauncherSwitch.isChecked = isLauncherIconShowing.not()
|
||||
binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
hideOrShowLauncherIcon(b)
|
||||
}
|
||||
}
|
||||
|
||||
/** 刷新模块状态 */
|
||||
@@ -124,9 +157,9 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
}
|
||||
)
|
||||
binding.mainTextStatus.text = when {
|
||||
YukiHookAPI.Status.isXposedModuleActive && isModuleValied.not() -> LocaleString.moduleNotFullyActivated
|
||||
YukiHookAPI.Status.isXposedModuleActive -> LocaleString.moduleIsActivated
|
||||
else -> LocaleString.moduleNotActivated
|
||||
YukiHookAPI.Status.isXposedModuleActive && isModuleValied.not() -> locale.moduleNotFullyActivated
|
||||
YukiHookAPI.Status.isXposedModuleActive -> locale.moduleIsActivated
|
||||
else -> locale.moduleNotActivated
|
||||
}
|
||||
binding.mainTextApiWay.isVisible = YukiHookAPI.Status.isXposedModuleActive
|
||||
binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name} API ${YukiHookAPI.Status.Executor.apiLevel}"
|
||||
@@ -137,7 +170,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
* @param callback 激活后回调
|
||||
*/
|
||||
private inline fun whenActivated(callback: () -> Unit) {
|
||||
if (YukiHookAPI.Status.isXposedModuleActive) callback() else toast(LocaleString.moduleNotActivated)
|
||||
if (YukiHookAPI.Status.isXposedModuleActive) callback() else toast(locale.moduleNotActivated)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/6/1.
|
||||
* This file is created by fankes on 2022/6/1.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.ui.widget
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/14.
|
||||
* This file is created by fankes on 2022/5/14.
|
||||
*/
|
||||
@file:Suppress("SameParameterValue")
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,8 +17,10 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/6/3.
|
||||
* This file is created by fankes on 2022/6/3.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.fankes.apperrorstracking.utils.factory
|
||||
|
||||
import android.content.Context
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,28 +17,32 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/12.
|
||||
* This file is created by fankes on 2022/5/12.
|
||||
*/
|
||||
@file:Suppress("unused", "DEPRECATION", "OPT_IN_USAGE", "EXPERIMENTAL_API_USAGE")
|
||||
@file:Suppress("unused", "DEPRECATION")
|
||||
|
||||
package com.fankes.apperrorstracking.utils.factory
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.os.postDelayed
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.data.ConfigData
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
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
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
|
||||
|
||||
@@ -83,6 +87,8 @@ class DialogBuilder<VB : ViewBinding>(
|
||||
/** 自定义布局 */
|
||||
private var customLayoutView: View? = null
|
||||
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
/**
|
||||
* 获取 [DialogBuilder] 绑定布局对象
|
||||
* @return [VB]
|
||||
@@ -131,7 +137,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"
|
||||
@@ -147,7 +156,7 @@ class DialogBuilder<VB : ViewBinding>(
|
||||
* @param text 按钮文本内容
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun confirmButton(text: String = LocaleString.confirm, callback: () -> Unit = {}) {
|
||||
fun confirmButton(text: String = locale.confirm, callback: () -> Unit = {}) {
|
||||
instance?.setPositiveButton(text) { _, _ -> callback() }
|
||||
}
|
||||
|
||||
@@ -156,7 +165,7 @@ class DialogBuilder<VB : ViewBinding>(
|
||||
* @param text 按钮文本内容
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun cancelButton(text: String = LocaleString.cancel, callback: () -> Unit = {}) {
|
||||
fun cancelButton(text: String = locale.cancel, callback: () -> Unit = {}) {
|
||||
instance?.setNegativeButton(text) { _, _ -> callback() }
|
||||
}
|
||||
|
||||
@@ -165,7 +174,7 @@ class DialogBuilder<VB : ViewBinding>(
|
||||
* @param text 按钮文本内容
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun neutralButton(text: String = LocaleString.more, callback: () -> Unit = {}) {
|
||||
fun neutralButton(text: String = locale.more, callback: () -> Unit = {}) {
|
||||
instance?.setNeutralButton(text) { _, _ -> callback() }
|
||||
}
|
||||
|
||||
@@ -181,7 +190,6 @@ class DialogBuilder<VB : ViewBinding>(
|
||||
fun cancel() = dialogInstance?.cancel()
|
||||
|
||||
/** 显示对话框 */
|
||||
@CauseProblemsApi
|
||||
fun show() {
|
||||
/** 若当前自定义 View 的对话框没有调用 [binding] 将会对其手动调用一次以确保显示布局 */
|
||||
if (bindingClass != null) binding
|
||||
@@ -190,6 +198,16 @@ class DialogBuilder<VB : ViewBinding>(
|
||||
customLayoutView?.let { setView(it) }
|
||||
dialogInstance = this
|
||||
setOnCancelListener { onCancel?.invoke() }
|
||||
if (ConfigData.isEnablePreventMisoperation) {
|
||||
setOnShowListener {
|
||||
window?.run {
|
||||
addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
|
||||
mainHandler.postDelayed(1000) {
|
||||
clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}?.show()
|
||||
}
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,14 +17,22 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/7.
|
||||
* This file is created by fankes on 2022/5/7.
|
||||
*/
|
||||
@file:Suppress("unused", "NotificationPermission")
|
||||
@file:Suppress("unused", "NotificationPermission", "DEPRECATION")
|
||||
|
||||
package com.fankes.apperrorstracking.utils.factory
|
||||
|
||||
import android.app.*
|
||||
import android.content.*
|
||||
import android.app.Activity
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.PackageInfoFlags
|
||||
@@ -43,12 +51,13 @@ import androidx.core.content.getSystemService
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import com.fankes.apperrorstracking.BuildConfig
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.fankes.apperrorstracking.wrapper.BuildConfigWrapper
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.highcapable.yukihookapi.hook.factory.field
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.type.android.ApplicationInfoClass
|
||||
import com.highcapable.yukihookapi.hook.type.android.ContextClass
|
||||
import com.highcapable.yukihookapi.hook.type.android.IntentClass
|
||||
@@ -58,7 +67,18 @@ import java.io.Serializable
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 当前系统环境是否为简体中文
|
||||
* @return [Boolean]
|
||||
*/
|
||||
val isSystemLanguageSimplifiedChinese
|
||||
get(): Boolean {
|
||||
val locale = Locale.getDefault()
|
||||
return locale.language == "zh" && locale.country == "CN"
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统深色模式是否开启
|
||||
@@ -107,7 +127,7 @@ fun Resources.colorOf(@ColorRes resId: Int) = ResourcesCompat.getColor(this, res
|
||||
* @return [PackageInfo] or null
|
||||
*/
|
||||
private fun Context.getPackageInfoCompat(packageName: String, flag: Number = 0) = runCatching {
|
||||
@Suppress("DEPRECATION")
|
||||
@Suppress("DEPRECATION", "KotlinRedundantDiagnosticSuppress")
|
||||
if (Build.VERSION.SDK_INT >= 33)
|
||||
packageManager?.getPackageInfo(packageName, PackageInfoFlags.of(flag.toLong()))
|
||||
else packageManager?.getPackageInfo(packageName, flag.toInt())
|
||||
@@ -124,7 +144,7 @@ private val PackageInfo.versionCodeCompat get() = PackageInfoCompat.getLongVersi
|
||||
* @return [List]<[PackageInfo]>
|
||||
*/
|
||||
fun Context.listOfPackages() = runCatching {
|
||||
@Suppress("DEPRECATION")
|
||||
@Suppress("DEPRECATION", "KotlinRedundantDiagnosticSuppress")
|
||||
if (Build.VERSION.SDK_INT >= 33)
|
||||
packageManager?.getInstalledPackages(PackageInfoFlags.of(PackageManager.GET_CONFIGURATIONS.toLong()))
|
||||
else packageManager?.getInstalledPackages(PackageManager.GET_CONFIGURATIONS)
|
||||
@@ -133,24 +153,25 @@ fun Context.listOfPackages() = runCatching {
|
||||
/**
|
||||
* 得到 APP 名称
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [String]
|
||||
* @return [String] 无法获取时返回 ""
|
||||
*/
|
||||
fun Context.appNameOf(packageName: String = getPackageName()) =
|
||||
getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: "unknown"
|
||||
getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: ""
|
||||
|
||||
/**
|
||||
* 得到 APP 版本信息与版本号
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [String] 无法获取时返回 "unknown(-1)"
|
||||
* @return [String] 无法获取时返回 ""
|
||||
*/
|
||||
fun Context.appVersionBrandOf(packageName: String = getPackageName()) = "${appVersionNameOf(packageName)}(${appVersionCodeOf(packageName)})"
|
||||
fun Context.appVersionBrandOf(packageName: String = getPackageName()) =
|
||||
if (appVersionNameOf(packageName).isNotBlank()) "${appVersionNameOf(packageName)}(${appVersionCodeOf(packageName)})" else ""
|
||||
|
||||
/**
|
||||
* 得到 APP 版本名称
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [String] 无法获取时返回 "unknown"
|
||||
* @return [String] 无法获取时返回 ""
|
||||
*/
|
||||
fun Context.appVersionNameOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.versionName ?: "unknown"
|
||||
fun Context.appVersionNameOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.versionName ?: ""
|
||||
|
||||
/**
|
||||
* 得到 APP 版本号
|
||||
@@ -159,10 +180,24 @@ fun Context.appVersionNameOf(packageName: String = getPackageName()) = getPackag
|
||||
*/
|
||||
fun Context.appVersionCodeOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.versionCodeCompat ?: -1L
|
||||
|
||||
/**
|
||||
* 得到 APP 目标 SDK 版本
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [Int] 无法获取时返回 -1
|
||||
*/
|
||||
fun Context.appTargetSdkOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.applicationInfo?.targetSdkVersion ?: -1
|
||||
|
||||
/**
|
||||
* 得到 APP 最低 SDK 版本
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [Int] 无法获取时返回 -1
|
||||
*/
|
||||
fun Context.appMinSdkOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.applicationInfo?.minSdkVersion ?: -1
|
||||
|
||||
/**
|
||||
* 获取 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()
|
||||
@@ -253,7 +288,11 @@ fun Long.toUtcTime() = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ROOT
|
||||
* 弹出 [Toast]
|
||||
* @param msg 提示内容
|
||||
*/
|
||||
fun Context.toast(msg: String) = Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
|
||||
fun Context.toast(msg: String) {
|
||||
runCatching {
|
||||
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
|
||||
}.onFailure { YLog.warn(msg) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出 [Snackbar]
|
||||
@@ -305,7 +344,7 @@ inline fun <reified T : Activity> Context.navigate(isOutSide: Boolean = false, i
|
||||
startActivity((if (isOutSide) Intent() else Intent(if (this is Service) applicationContext else this, T::class.java)).apply {
|
||||
flags = if (this@navigate !is Activity) Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
else Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
if (isOutSide) component = ComponentName(BuildConfig.APPLICATION_ID, T::class.java.name)
|
||||
if (isOutSide) component = ComponentName(BuildConfigWrapper.APPLICATION_ID, T::class.java.name)
|
||||
initiate(this)
|
||||
})
|
||||
}.onFailure { toast(msg = "Start ${T::class.java.name} failed") }
|
||||
@@ -318,7 +357,7 @@ fun Context.copyToClipboard(content: String) = runCatching {
|
||||
(getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).apply {
|
||||
setPrimaryClip(ClipData.newPlainText(null, content))
|
||||
(primaryClip?.getItemAt(0)?.text ?: "").also {
|
||||
if (it != content) toast(LocaleString.copyFail) else toast(LocaleString.copied)
|
||||
if (it != content) toast(locale.copyFail) else toast(locale.copied)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -376,7 +415,10 @@ fun Context.openApp(packageName: String = getPackageName(), userId: Int = 0) = r
|
||||
* 是否有 Root 权限
|
||||
* @return [Boolean]
|
||||
*/
|
||||
val isRootAccess get() = runCatching { Shell.rootAccess() }.getOrNull() ?: false
|
||||
val isRootAccess get() = runCatching {
|
||||
@Suppress("DEPRECATION")
|
||||
Shell.rootAccess()
|
||||
}.getOrNull() ?: false
|
||||
|
||||
/**
|
||||
* 执行命令
|
||||
@@ -385,6 +427,7 @@ val isRootAccess get() = runCatching { Shell.rootAccess() }.getOrNull() ?: false
|
||||
* @return [String] 执行结果
|
||||
*/
|
||||
fun execShell(cmd: String, isSu: Boolean = true) = runCatching {
|
||||
@Suppress("DEPRECATION")
|
||||
(if (isSu) Shell.su(cmd) else Shell.sh(cmd)).exec().out.let {
|
||||
if (it.isNotEmpty()) it[0].trim() else ""
|
||||
}
|
||||
@@ -398,7 +441,7 @@ fun execShell(cmd: String, isSu: Boolean = true) = runCatching {
|
||||
*/
|
||||
fun Context.hideOrShowLauncherIcon(isShow: Boolean) {
|
||||
packageManager?.setComponentEnabledSetting(
|
||||
ComponentName(packageName, "${BuildConfig.APPLICATION_ID}.Home"),
|
||||
ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home"),
|
||||
if (isShow) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||
PackageManager.DONT_KILL_APP
|
||||
)
|
||||
@@ -410,5 +453,5 @@ fun Context.hideOrShowLauncherIcon(isShow: Boolean) {
|
||||
*/
|
||||
val Context.isLauncherIconShowing
|
||||
get() = packageManager?.getComponentEnabledSetting(
|
||||
ComponentName(packageName, "${BuildConfig.APPLICATION_ID}.Home")
|
||||
ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home")
|
||||
) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/10/3.
|
||||
* This file is created by fankes on 2022/10/3.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.utils.factory
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/10/3.
|
||||
* This file is created by fankes on 2022/10/3.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.utils.factory
|
||||
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/10/5.
|
||||
* This file is created by fankes on 2022/10/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
@@ -25,8 +25,8 @@ 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.fankes.apperrorstracking.generated.ModuleAppProperties
|
||||
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
|
||||
@@ -38,7 +38,7 @@ import com.microsoft.appcenter.crashes.Crashes
|
||||
object AppAnalyticsTool {
|
||||
|
||||
/** App Secret */
|
||||
private const val APP_CENTER_SECRET = BuildConfig.APP_CENTER_SECRET
|
||||
private const val APP_CENTER_SECRET = ModuleAppProperties.APP_CENTER_SECRET
|
||||
|
||||
/** 启用匿名统计收集使用情况功能 */
|
||||
private val ENABLE_APP_CENTER_ANALYTICS = PrefsData("_enable_app_center_analytics", true)
|
||||
@@ -51,11 +51,14 @@ 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) }
|
||||
}
|
||||
|
||||
/** 是否可用 */
|
||||
val isAvailable = APP_CENTER_SECRET.isNotBlank()
|
||||
|
||||
/** 绑定到 [CompoundButton] 自动设置选中状态 */
|
||||
fun CompoundButton.bindAppAnalytics() {
|
||||
isChecked = isEnableAppCenterAnalytics
|
||||
@@ -81,7 +84,7 @@ object AppAnalyticsTool {
|
||||
*/
|
||||
fun init(instance: Application) {
|
||||
this.instance = instance
|
||||
if (isEnableAppCenterAnalytics && APP_CENTER_SECRET.isNotBlank())
|
||||
if (isEnableAppCenterAnalytics && isAvailable)
|
||||
AppCenter.start(instance, APP_CENTER_SECRET, Analytics::class.java, Crashes::class.java)
|
||||
}
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,10 +17,8 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/12.
|
||||
* This file is created by fankes on 2022/5/12.
|
||||
*/
|
||||
@file:Suppress("UNCHECKED_CAST")
|
||||
|
||||
package com.fankes.apperrorstracking.utils.tool
|
||||
|
||||
import android.content.Context
|
||||
@@ -28,7 +26,8 @@ import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
|
||||
import com.fankes.apperrorstracking.bean.AppFiltersBean
|
||||
import com.fankes.apperrorstracking.bean.AppInfoBean
|
||||
import com.fankes.apperrorstracking.bean.MutedErrorsAppBean
|
||||
import com.fankes.apperrorstracking.locale.LocaleString
|
||||
import com.fankes.apperrorstracking.const.PackageName
|
||||
import com.fankes.apperrorstracking.locale.locale
|
||||
import com.fankes.apperrorstracking.utils.factory.execShell
|
||||
import com.fankes.apperrorstracking.utils.factory.isRootAccess
|
||||
import com.fankes.apperrorstracking.utils.factory.showDialog
|
||||
@@ -41,9 +40,6 @@ import com.highcapable.yukihookapi.hook.xposed.channel.data.ChannelData
|
||||
*/
|
||||
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"
|
||||
@@ -215,22 +211,22 @@ object FrameworkTool {
|
||||
/** 当 Root 权限获取失败时显示对话框 */
|
||||
fun showWhenAccessRootFail() =
|
||||
context.showDialog {
|
||||
title = LocaleString.accessRootFail
|
||||
msg = LocaleString.accessRootFailTip
|
||||
confirmButton(LocaleString.gotIt)
|
||||
title = locale.accessRootFail
|
||||
msg = locale.accessRootFailTip
|
||||
confirmButton(locale.gotIt)
|
||||
}
|
||||
context.showDialog {
|
||||
title = LocaleString.notice
|
||||
msg = LocaleString.areYouSureRestartSystem
|
||||
title = locale.notice
|
||||
msg = locale.areYourSureRestartSystem
|
||||
confirmButton {
|
||||
if (isRootAccess)
|
||||
execShell(cmd = "reboot")
|
||||
else showWhenAccessRootFail()
|
||||
}
|
||||
neutralButton(LocaleString.fastRestart) {
|
||||
neutralButton(locale.fastRestart) {
|
||||
context.showDialog {
|
||||
title = LocaleString.warning
|
||||
msg = LocaleString.fastRestartProblem
|
||||
title = locale.warning
|
||||
msg = locale.fastRestartProblem
|
||||
confirmButton {
|
||||
if (isRootAccess)
|
||||
execShell(cmd = "killall zygote")
|
||||
@@ -249,13 +245,13 @@ object FrameworkTool {
|
||||
* @param result 成功后回调
|
||||
*/
|
||||
fun checkingActivated(context: Context, result: (Boolean) -> Unit) =
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).checkingVersionEquals(result = result)
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).checkingVersionEquals(result = result)
|
||||
|
||||
/**
|
||||
* 通知系统框架刷新存储的数据
|
||||
* @param context 实例
|
||||
*/
|
||||
fun refreshFrameworkPrefsData(context: Context) = context.dataChannel(SYSTEM_FRAMEWORK_NAME).put(CALL_REFRESH_HOST_PREFS_DATA)
|
||||
fun refreshFrameworkPrefsData(context: Context) = context.dataChannel(PackageName.SYSTEM_FRAMEWORK).put(CALL_REFRESH_HOST_PREFS_DATA)
|
||||
|
||||
/**
|
||||
* 使用系统框架打开 [packageName]
|
||||
@@ -264,7 +260,7 @@ object FrameworkTool {
|
||||
* @param userId APP 用户 ID
|
||||
*/
|
||||
fun openAppUsedFramework(context: Context, packageName: String, userId: Int) =
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).put(CALL_OPEN_SPECIFY_APP, Pair(packageName, userId))
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).put(CALL_OPEN_SPECIFY_APP, Pair(packageName, userId))
|
||||
|
||||
/**
|
||||
* 获取指定 APP 异常信息
|
||||
@@ -273,7 +269,7 @@ object FrameworkTool {
|
||||
* @param result 回调数据
|
||||
*/
|
||||
fun fetchAppErrorInfoData(context: Context, pid: Int, result: (AppErrorsInfoBean) -> Unit) {
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).with {
|
||||
wait(CALL_APP_ERROR_DATA_GET_RESULT) { result(it) }
|
||||
put(CALL_APP_ERROR_DATA_GET, pid)
|
||||
}
|
||||
@@ -285,7 +281,7 @@ object FrameworkTool {
|
||||
* @param result 回调数据
|
||||
*/
|
||||
fun fetchAppErrorsInfoData(context: Context, result: (ArrayList<AppErrorsInfoBean>) -> Unit) {
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).with {
|
||||
wait(CALL_APP_ERRORS_DATA_GET_RESULT) { result(it) }
|
||||
put(CALL_APP_ERRORS_DATA_GET)
|
||||
}
|
||||
@@ -298,7 +294,7 @@ object FrameworkTool {
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
fun removeAppErrorsInfoData(context: Context, appErrorsInfo: AppErrorsInfoBean, callback: () -> Unit) {
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).with {
|
||||
wait(CALL_APP_ERRORS_DATA_REMOVE_RESULT) { callback() }
|
||||
put(CALL_APP_ERRORS_DATA_REMOVE, appErrorsInfo)
|
||||
}
|
||||
@@ -310,7 +306,7 @@ object FrameworkTool {
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
fun clearAppErrorsInfoData(context: Context, callback: () -> Unit) {
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).with {
|
||||
wait(CALL_APP_ERRORS_DATA_CLEAR_RESULT) { callback() }
|
||||
put(CALL_APP_ERRORS_DATA_CLEAR)
|
||||
}
|
||||
@@ -323,7 +319,7 @@ object FrameworkTool {
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
fun mutedErrorsIfUnlock(context: Context, packageName: String, callback: () -> Unit) {
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).with {
|
||||
wait(CALL_MUTED_ERRORS_IF_UNLOCK_RESULT) { callback() }
|
||||
put(CALL_MUTED_ERRORS_IF_UNLOCK, packageName)
|
||||
}
|
||||
@@ -336,7 +332,7 @@ object FrameworkTool {
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
fun mutedErrorsIfRestart(context: Context, packageName: String, callback: () -> Unit) {
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).with {
|
||||
wait(CALL_MUTED_ERRORS_IF_RESTART_RESULT) { callback() }
|
||||
put(CALL_MUTED_ERRORS_IF_RESTART, packageName)
|
||||
}
|
||||
@@ -348,7 +344,7 @@ object FrameworkTool {
|
||||
* @param result 回调数据
|
||||
*/
|
||||
fun fetchMutedErrorsAppsData(context: Context, result: (ArrayList<MutedErrorsAppBean>) -> Unit) {
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).with {
|
||||
wait(CALL_MUTED_ERRORS_APP_DATA_GET_RESULT) { result(it) }
|
||||
put(CALL_MUTED_ERRORS_APP_DATA_GET)
|
||||
}
|
||||
@@ -361,7 +357,7 @@ object FrameworkTool {
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
fun unmuteErrorsApp(context: Context, mutedErrorsApp: MutedErrorsAppBean, callback: () -> Unit) {
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).with {
|
||||
wait(CALL_UNMUTE_ERRORS_APP_DATA_RESULT) { callback() }
|
||||
put(CALL_UNMUTE_ERRORS_APP_DATA, mutedErrorsApp)
|
||||
}
|
||||
@@ -373,7 +369,7 @@ object FrameworkTool {
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
fun unmuteAllErrorsApps(context: Context, callback: () -> Unit) {
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).with {
|
||||
wait(CALL_UNMUTE_ALL_ERRORS_APPS_DATA_RESULT) { callback() }
|
||||
put(CALL_UNMUTE_ALL_ERRORS_APPS_DATA)
|
||||
}
|
||||
@@ -386,7 +382,7 @@ object FrameworkTool {
|
||||
* @param result 回调数据
|
||||
*/
|
||||
fun fetchAppListData(context: Context, appFilters: AppFiltersBean, result: (ArrayList<AppInfoBean>) -> Unit) {
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||
context.dataChannel(PackageName.SYSTEM_FRAMEWORK).with {
|
||||
wait(CALL_APP_LIST_DATA_GET_RESULT) { result(it) }
|
||||
put(CALL_APP_LIST_DATA_GET, appFilters)
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,7 +17,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2023/1/23.
|
||||
* This file is created by fankes on 2023/1/23.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.utils.tool
|
||||
|
||||
@@ -26,17 +26,21 @@ 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.locale.locale
|
||||
import com.fankes.apperrorstracking.utils.factory.openBrowser
|
||||
import com.fankes.apperrorstracking.utils.factory.showDialog
|
||||
import okhttp3.*
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 获取 Github Release 最新版本工具类
|
||||
* 获取 GitHub Release 最新版本工具类
|
||||
*/
|
||||
object GithubReleaseTool {
|
||||
|
||||
@@ -70,9 +74,9 @@ object GithubReleaseTool {
|
||||
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) }
|
||||
title = locale.latestVersion(name)
|
||||
msg = locale.latestVersionTip(date, content)
|
||||
confirmButton(locale.updateNow) { context.openBrowser(htmlUrl) }
|
||||
cancelButton()
|
||||
}
|
||||
if (name != version) (context as? Activity?)?.runOnUiThread {
|
||||
@@ -98,7 +102,7 @@ object GithubReleaseTool {
|
||||
}
|
||||
|
||||
/**
|
||||
* Github Release bean
|
||||
* GitHub Release bean
|
||||
* @param name 版本名称
|
||||
* @param htmlUrl 网页地址
|
||||
* @param content 更新日志
|
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
|
||||
* Copyright (C) 2017 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/11/3.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.utils.tool
|
||||
|
||||
import android.content.Context
|
||||
import com.fankes.apperrorstracking.databinding.DiaStackTraceShareBinding
|
||||
import com.fankes.apperrorstracking.utils.factory.showDialog
|
||||
|
||||
/**
|
||||
* 异常堆栈分享工具类
|
||||
*/
|
||||
object StackTraceShareHelper {
|
||||
|
||||
/**
|
||||
* 显示分享选择器
|
||||
* @param context 当前实例
|
||||
* @param title 对话框标题
|
||||
* @param onChoose 回调选择的结果
|
||||
*/
|
||||
fun showChoose(
|
||||
context: Context,
|
||||
title: String,
|
||||
onChoose: (sDeviceBrand: Boolean, sDeviceModel: Boolean, sDisplay: Boolean, sPackageName: Boolean) -> Unit
|
||||
) {
|
||||
context.showDialog<DiaStackTraceShareBinding> {
|
||||
this.title = title
|
||||
confirmButton {
|
||||
val sDeviceBrand = binding.configCheck0.isChecked
|
||||
val sDeviceModel = binding.configCheck1.isChecked
|
||||
val sDisplay = binding.configCheck2.isChecked
|
||||
val sPackageName = binding.configCheck3.isChecked
|
||||
onChoose(sDeviceBrand, sDeviceModel, sDisplay, sPackageName)
|
||||
cancel()
|
||||
}
|
||||
cancelButton()
|
||||
}
|
||||
}
|
||||
}
|
@@ -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) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -17,13 +17,19 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/13.
|
||||
* This file is created by fankes on 2022/5/13.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.fankes.apperrorstracking.utils.tool
|
||||
|
||||
import java.io.*
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import java.util.zip.ZipOutputStream
|
@@ -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 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/9/19.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.fankes.apperrorstracking.wrapper
|
||||
|
||||
import com.fankes.apperrorstracking.BuildConfig
|
||||
|
||||
/**
|
||||
* 对 [BuildConfig] 的包装
|
||||
*/
|
||||
object BuildConfigWrapper {
|
||||
const val APPLICATION_ID = BuildConfig.APPLICATION_ID
|
||||
const val VERSION_NAME = BuildConfig.VERSION_NAME
|
||||
const val VERSION_CODE = BuildConfig.VERSION_CODE
|
||||
val isDebug = BuildConfig.DEBUG
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user