feat: lots of changes

- add BuildConfigWrapper
- add project promote
- add ci version tag support
- change app analytics config item show when available
- fix system api compat issues
This commit is contained in:
2023-09-19 05:12:44 +08:00
parent ccc50d720e
commit c1e584d739
136 changed files with 357 additions and 101 deletions

View File

@@ -66,7 +66,7 @@ androidComponents {
val currentSuffix = property.github.ci.commit.id.let { suffix -> if (suffix.isNotBlank()) "-$suffix" 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}-v$currentVersion-$currentType.apk")
output.outputFileName.set("${property.project.name}-demo-v$currentVersion-$currentType.apk")
}
}
}

View File

@@ -19,19 +19,25 @@
*
* 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() }

View File

@@ -19,8 +19,6 @@
*
* 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

View File

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

View File

@@ -61,7 +61,7 @@ androidComponents {
val currentSuffix = property.github.ci.commit.id.let { suffix -> if (suffix.isNotBlank()) "-$suffix" 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}-v$currentVersion-$currentType.apk")
output.outputFileName.set("${property.project.name}-module-v$currentVersion-$currentType.apk")
}
}
}

View File

@@ -64,4 +64,7 @@
public static *** throwUninitializedPropertyAccessException(...);
}
-keep class com.fankes.apperrorstracking.databinding**{*;}
-keep class * extends android.app.Activity
-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
*** inflate(android.view.LayoutInflater);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -25,11 +25,16 @@ 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.utils.factory.appCpuAbiOf
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
@@ -130,7 +135,7 @@ data class AppErrorsInfoBean(
* 获取生成的 Json 文件名
* @return [String]
*/
val jsonFileName get() = "${packageName}_${pid}_${timestamp}.json"
val jsonFileName get() = "${packageName}_${pid}_$timestamp.json"
/**
* 获取 APP 版本信息与版本号
@@ -170,39 +175,46 @@ data class AppErrorsInfoBean(
* @return [String]
*/
val stackOutputShareContent
get() = "Generated by AppErrorsTracking\n" +
"Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
"===============\n$environmentInfo"
get() = """
Generated by AppErrorsTracking
Project Url: https://github.com/KitsunePie/AppErrorsTracking
===============
$environmentInfo
""".trimIndent()
/**
* 获取异常堆栈文件模板
* @return [String]
*/
val stackOutputFileContent
get() = "================================================================\n" +
" Generated by AppErrorsTracking\n" +
" Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
"================================================================\n" +
environmentInfo
get() = """
================================================================
Generated by AppErrorsTracking
Project Url: https://github.com/KitsunePie/AppErrorsTracking
================================================================
""".trimIndent()
/**
* 获取运行环境信息
* @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
get() = """
[Device Brand]: ${Build.BRAND}
[Device Model]: ${Build.MODEL}
[Display]: ${Build.DISPLAY}
[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
[Version Name]: ${versionName.ifBlank { "unknown" }}
[Version Code]: ${versionCode.takeIf { it != -1L } ?: "unknown"}
[Error Type]: ${if (isNativeCrash) "Native" else "JVM"}
[Crash Time]: $utcTime
[Stack Trace]:
$stackTrace
""".trimIndent()
}

View File

@@ -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-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/KitsunePie/AppErrorsTracking
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2023/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)"
}

View File

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

View File

@@ -26,7 +26,11 @@ 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.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.loggerE
import java.io.File
import java.util.concurrent.CopyOnWriteArrayList
@@ -91,11 +95,11 @@ object AppErrorsRecordData {
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()
}

View File

@@ -19,6 +19,8 @@
*
* This file is Created by fankes on 2022/5/7.
*/
@file:Suppress("ConstPropertyName")
package com.fankes.apperrorstracking.hook.entity
import android.app.ApplicationErrorReport
@@ -33,7 +35,6 @@ 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
@@ -47,8 +48,16 @@ import com.fankes.apperrorstracking.data.enum.AppErrorsConfigType
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsDisplayActivity
import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsRecordActivity
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.fankes.apperrorstracking.wrapper.BuildConfigWrapper
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.field
@@ -241,7 +250,7 @@ object FrameworkHooker : YukiBaseHooker() {
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())
@@ -309,9 +318,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,7 +331,7 @@ 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")
}
@@ -343,8 +352,8 @@ object FrameworkHooker : YukiBaseHooker() {
/** 打印错误日志 */
if (isActualApp) loggerE(
msg = "Application \"$packageName\" ${if (isRepeatingCrash) "keeps stopping" else "has stopped"}" +
(if (packageName != processName) " --process \"$processName\"" else "") +
"${if (userId != 0) " --user $userId" else ""} --pid $pid"
(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")
}

View File

@@ -19,8 +19,6 @@
*
* 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

View File

@@ -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.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 java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.text.SimpleDateFormat
import java.util.*
import java.util.Date
class LoggerActivity : BaseActivity<ActivitiyLoggerBinding>() {
@@ -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()

View File

@@ -36,7 +36,15 @@ import com.fankes.apperrorstracking.data.factory.bind
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDetailBinding
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
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.highcapable.yukihookapi.hook.log.loggerE
class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {

View File

@@ -31,7 +31,12 @@ import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDisplayBinding
import com.fankes.apperrorstracking.databinding.DiaAppErrorsDisplayBinding
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
import com.fankes.apperrorstracking.utils.factory.*
import com.fankes.apperrorstracking.utils.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>() {

View File

@@ -19,8 +19,6 @@
*
* 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

View File

@@ -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
@@ -44,6 +43,7 @@ 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.tool.ZipFileTool
import com.fankes.apperrorstracking.wrapper.BuildConfigWrapper
import java.io.File
import java.io.FileInputStream
@@ -58,7 +58,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) }
}
/** 当前导出文件的路径 */

View File

@@ -25,8 +25,8 @@ 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.factory.bind
import com.fankes.apperrorstracking.databinding.ActivityMainBinding
@@ -35,10 +35,18 @@ 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,13 +63,15 @@ 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)
isVisible = true
setOnClickListener { function() }
}
}
/** 推广、恰饭 */
if (YukiHookAPI.Status.isXposedModuleActive) ProjectPromote.show(activity = this, ModuleVersion.toString())
/** 显示开发者提示 */
if (ConfigData.isShowDeveloperNotice)
showDialog {
@@ -70,7 +80,21 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
confirmButton(LocaleString.gotIt) { ConfigData.isShowDeveloperNotice = false }
noCancelable()
}
binding.mainTextVersion.text = LocaleString.moduleVersion(BuildConfig.VERSION_NAME)
/** 设置 CI 自动构建标识 */
if (ModuleVersion.isCiMode)
binding.mainTextReleaseVersion.apply {
text = "CI ${ModuleVersion.GITHUB_COMMIT_ID}"
isVisible = true
setOnClickListener {
showDialog {
title = LocaleString.ciNoticeDialogTitle
msg = LocaleString.ciNoticeDialogContent(ModuleVersion.GITHUB_COMMIT_ID)
confirmButton(LocaleString.gotIt)
noCancelable()
}
}
}
binding.mainTextVersion.text = LocaleString.moduleVersion(ModuleVersion.NAME)
binding.mainTextSystemVersion.text = LocaleString.systemVersion(systemVersion)
binding.onlyShowErrorsInFrontSwitch.bind(ConfigData.ENABLE_ONLY_SHOW_ERRORS_IN_FRONT)
binding.onlyShowErrorsInMainProcessSwitch.bind(ConfigData.ENABLE_ONLY_SHOW_ERRORS_IN_MAIN)
@@ -81,6 +105,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
binding.enableMaterial3AppErrorsDialogSwitch.bind(ConfigData.ENABLE_MATERIAL3_STYLE_APP_ERRORS_DIALOG)
/** 设置匿名统计 */
binding.appAnalyticsConfigItem.isVisible = AppAnalyticsTool.isAvailable
binding.enableAnonymousStatisticsSwitch.bindAppAnalytics()
/** 系统版本点击事件 */
binding.mainTextSystemVersion.setOnClickListener {
@@ -101,6 +126,11 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.titleRestartIcon.setOnClickListener { FrameworkTool.restartSystem(context = this) }
/** 项目地址按钮点击事件 */
binding.titleGithubIcon.setOnClickListener { openBrowser(url = "https://github.com/KitsunePie/AppErrorsTracking") }
/** 恰饭! */
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 ->

View File

@@ -19,7 +19,7 @@
*
* This file is Created by fankes on 2022/5/12.
*/
@file:Suppress("unused", "DEPRECATION", "OPT_IN_USAGE", "EXPERIMENTAL_API_USAGE")
@file:Suppress("unused", "OPT_IN_USAGE", "EXPERIMENTAL_API_USAGE")
package com.fankes.apperrorstracking.utils.factory

View File

@@ -23,8 +23,16 @@
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,9 +51,9 @@ 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.wrapper.BuildConfigWrapper
import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.method
@@ -58,7 +66,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 +126,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 +143,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)
@@ -306,7 +325,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") }
@@ -377,7 +396,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
/**
* 执行命令
@@ -386,6 +408,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 ""
}
@@ -399,7 +422,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
)
@@ -411,5 +434,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

View File

@@ -25,7 +25,7 @@ package com.fankes.apperrorstracking.utils.tool
import android.app.Application
import android.widget.CompoundButton
import com.fankes.apperrorstracking.BuildConfig
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
@@ -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)
@@ -56,6 +56,9 @@ object AppAnalyticsTool {
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)
}
}

View File

@@ -19,8 +19,6 @@
*
* 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,6 +26,7 @@ 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.const.PackageName
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.utils.factory.execShell
import com.fankes.apperrorstracking.utils.factory.isRootAccess
@@ -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"
@@ -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)
}

View File

@@ -29,11 +29,15 @@ import android.icu.util.TimeZone
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.utils.factory.openBrowser
import com.fankes.apperrorstracking.utils.factory.showDialog
import okhttp3.*
import 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 最新版本工具类

View File

@@ -23,7 +23,13 @@
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

View File

@@ -0,0 +1,36 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/KitsunePie/AppErrorsTracking
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2023/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