refactor: migrate and update to YukiHookAPI 1.3.0

This commit is contained in:
2025-06-25 23:22:59 +08:00
parent ab03c74659
commit e59b7f501a
17 changed files with 170 additions and 94 deletions

View File

@@ -84,7 +84,8 @@ androidComponents {
dependencies { dependencies {
implementation(com.fankes.projectpromote.project.promote) implementation(com.fankes.projectpromote.project.promote)
implementation(com.highcapable.yukireflection.api) implementation(com.highcapable.kavaref.kavaref.core)
implementation(com.highcapable.kavaref.kavaref.extension)
implementation(androidx.core.core.ktx) implementation(androidx.core.core.ktx)
implementation(androidx.appcompat.appcompat) implementation(androidx.appcompat.appcompat)
implementation(com.google.android.material.material) implementation(com.google.android.material.material)

View File

@@ -19,21 +19,20 @@
* *
* This file is created by fankes on 2022/5/10. * This file is created by fankes on 2022/5/10.
*/ */
@file:Suppress("DEPRECATION")
package com.fankes.apperrorsdemo.ui.activity.base package com.fankes.apperrorsdemo.ui.activity.base
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.fankes.apperrorsdemo.R import com.fankes.apperrorsdemo.R
import com.fankes.apperrorsdemo.utils.factory.isNotSystemInDarkMode import com.fankes.apperrorsdemo.utils.factory.isNotSystemInDarkMode
import com.highcapable.yukireflection.factory.current import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.yukireflection.factory.method import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
import com.highcapable.yukireflection.type.android.LayoutInflaterClass import com.highcapable.kavaref.extension.toClassOrNull
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() { abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
@@ -42,10 +41,11 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = current().generic()?.argument()?.method { val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
name = "inflate" name = "inflate"
param(LayoutInflaterClass) parameters(LayoutInflater::class)
}?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed") }?.invoke<VB>(layoutInflater) ?: error("binding failed")
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
setContentView(binding.root) setContentView(binding.root)
/** 隐藏系统的标题栏 */ /** 隐藏系统的标题栏 */
@@ -55,6 +55,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
isAppearanceLightStatusBars = isNotSystemInDarkMode isAppearanceLightStatusBars = isNotSystemInDarkMode
isAppearanceLightNavigationBars = isNotSystemInDarkMode isAppearanceLightNavigationBars = isNotSystemInDarkMode
} }
@Suppress("DEPRECATION")
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also { ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
window?.statusBarColor = it window?.statusBarColor = it
window?.navigationBarColor = it window?.navigationBarColor = it

View File

@@ -22,7 +22,7 @@ repositories:
plugins: plugins:
com.android.application: com.android.application:
alias: android-application alias: android-application
version: 8.9.0 version: 8.9.3
org.jetbrains.kotlin.android: org.jetbrains.kotlin.android:
alias: kotlin-android alias: kotlin-android
version: 2.1.10 version: 2.1.10
@@ -46,12 +46,14 @@ libraries:
rovo89-xposed-api rovo89-xposed-api
com.highcapable.yukihookapi: com.highcapable.yukihookapi:
api: api:
version: 1.2.1 version: 1.3.0
ksp-xposed: ksp-xposed:
version-ref: <this>::api version-ref: <this>::api
com.highcapable.yukireflection: com.highcapable.kavaref:
api: kavaref-core:
version: 1.0.3 version: 1.0.0
kavaref-extension:
version: 1.0.0
com.microsoft.appcenter: com.microsoft.appcenter:
appcenter-analytics: appcenter-analytics:
version: 5.0.6 version: 5.0.6
@@ -69,13 +71,13 @@ libraries:
version: 2.12.1 version: 2.12.1
com.squareup.okhttp3: com.squareup.okhttp3:
okhttp: okhttp:
version: 5.0.0-alpha.14 version: 5.0.0-alpha.16
androidx.core: androidx.core:
core-ktx: core-ktx:
version: 1.15.0 version: 1.16.0
androidx.appcompat: androidx.appcompat:
appcompat: appcompat:
version: 1.7.0 version: 1.7.1
com.google.android.material: com.google.android.material:
material: material:
version: 1.12.0 version: 1.12.0

View File

@@ -82,6 +82,8 @@ dependencies {
compileOnly(de.robv.android.xposed.api) compileOnly(de.robv.android.xposed.api)
implementation(com.highcapable.yukihookapi.api) implementation(com.highcapable.yukihookapi.api)
ksp(com.highcapable.yukihookapi.ksp.xposed) 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.fankes.projectpromote.project.promote)
implementation(com.microsoft.appcenter.appcenter.analytics) implementation(com.microsoft.appcenter.appcenter.analytics)
implementation(com.microsoft.appcenter.appcenter.crashes) implementation(com.microsoft.appcenter.appcenter.crashes)

View File

@@ -30,6 +30,7 @@ import android.content.Intent
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.os.Build import android.os.Build
import android.os.Bundle
import android.os.Message import android.os.Message
import android.os.SystemClock import android.os.SystemClock
import android.util.ArrayMap import android.util.ArrayMap
@@ -58,16 +59,10 @@ import com.fankes.apperrorstracking.utils.factory.toArrayList
import com.fankes.apperrorstracking.utils.factory.toast import com.fankes.apperrorstracking.utils.factory.toast
import com.fankes.apperrorstracking.utils.tool.FrameworkTool import com.fankes.apperrorstracking.utils.tool.FrameworkTool
import com.fankes.apperrorstracking.wrapper.BuildConfigWrapper import com.fankes.apperrorstracking.wrapper.BuildConfigWrapper
import com.highcapable.yukihookapi.hook.bean.VariousClass 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.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.constructor
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.YLog import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.type.android.BundleClass
import com.highcapable.yukihookapi.hook.type.android.MessageClass
import com.highcapable.yukihookapi.hook.type.java.BooleanType
object FrameworkHooker : YukiBaseHooker() { object FrameworkHooker : YukiBaseHooker() {
@@ -110,40 +105,73 @@ object FrameworkHooker : YukiBaseHooker() {
* 获取当前包列表实例 * 获取当前包列表实例
* @return [Any] or null * @return [Any] or null
*/ */
private val pkgList = if (ProcessRecordClass.hasMethod { name = "getPkgList"; emptyParam() }) private val pkgList by lazy {
ProcessRecordClass.method { name = "getPkgList"; emptyParam() }.get(proc).call() ProcessRecordClass.resolve().optional(silent = true)
else ProcessRecordClass.field { name { it.endsWith("pkgList", true) } }.get(proc).any() .firstMethodOrNull {
name = "getPkgList"
emptyParameters()
}?.of(proc)?.invoke()
?: ProcessRecordClass.resolve().optional(silent = true)
.firstFieldOrNull {
name { it.endsWith("pkgList", true) }
}?.of(proc)?.get()
}
/** /**
* 获取当前包列表数组大小 * 获取当前包列表数组大小
* @return [Int] * @return [Int]
*/ */
private val pkgListSize = PackageListClass?.method { name = "size"; emptyParam() }?.get(pkgList)?.int() private val pkgListSize by lazy {
?: ProcessRecordClass.field { name = "pkgList" }.get(proc).cast<ArrayMap<*, *>>()?.size ?: -1 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 信息 * 获取当前 pid 信息
* @return [Int] * @return [Int]
*/ */
val pid = ProcessRecordClass.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 信息 * 获取当前用户 ID 信息
* @return [Int] * @return [Int]
*/ */
val userId = ProcessRecordClass.field { name = "userId" }.get(proc).int() val userId by lazy {
ProcessRecordClass.resolve().optional()
.firstFieldOrNull { name = "userId" }
?.of(proc)?.get<Int>() ?: 0
}
/** /**
* 获取当前 APP 信息 * 获取当前 APP 信息
* @return [ApplicationInfo] or null * @return [ApplicationInfo] or null
*/ */
val appInfo = ProcessRecordClass.field { name = "info" }.get(proc).cast<ApplicationInfo>() val appInfo by lazy {
ProcessRecordClass.resolve().optional()
.firstFieldOrNull { name = "info" }
?.of(proc)?.get<ApplicationInfo>()
}
/** /**
* 获取当前进程名称 * 获取当前进程名称
* @return [String] * @return [String]
*/ */
val processName = ProcessRecordClass.field { name = "processName" }.get(proc).string() val processName by lazy {
ProcessRecordClass.resolve().optional()
.firstFieldOrNull { name = "processName" }
?.of(proc)?.get<String>() ?: ""
}
/** /**
* 获取当前 APP、进程 包名 * 获取当前 APP、进程 包名
@@ -167,17 +195,25 @@ object FrameworkHooker : YukiBaseHooker() {
* 获取当前进程是否为后台进程 * 获取当前进程是否为后台进程
* @return [Boolean] * @return [Boolean]
*/ */
val isBackgroundProcess = UserControllerClass val isBackgroundProcess by lazy {
.method { name { it == "getCurrentProfileIds" || it == "getCurrentProfileIdsLocked" } } UserControllerClass.resolve().optional()
.get(ActivityManagerServiceClass?.field { name = "mUserController" } .firstMethodOrNull { name { it == "getCurrentProfileIds" || it == "getCurrentProfileIdsLocked" } }
?.get(AppErrorsClass.field { name = "mService" }.get(errors).any())?.any()) ?.of(ActivityManagerServiceClass?.resolve()?.optional()?.firstFieldOrNull { name = "mUserController" }
.invoke<IntArray>()?.takeIf { it.isNotEmpty() }?.any { it != userId } ?: false ?.of(AppErrorsClass.resolve().optional().firstFieldOrNull { name = "mService" }?.of(errors)?.get())?.getQuietly())
?.invokeQuietly<IntArray>()?.takeIf { it.isNotEmpty() }?.any { it != userId } ?: false
}
/** /**
* 获取当前进程是否短时内重复崩溃 * 获取当前进程是否短时内重复崩溃
* @return [Boolean] * @return [Boolean]
*/ */
val isRepeatingCrash = resultData?.let { AppErrorDialog_DataClass.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
}
} }
/** 注册生命周期 */ /** 注册生命周期 */
@@ -376,60 +412,56 @@ object FrameworkHooker : YukiBaseHooker() {
/** 注册生命周期 */ /** 注册生命周期 */
registerLifecycle() registerLifecycle()
/** 干掉原生错误对话框 - 如果有 */ /** 干掉原生错误对话框 - 如果有 */
ErrorDialogControllerClass?.apply { ErrorDialogControllerClass?.resolve()?.optional(silent = true)?.apply {
if (hasMethod { name = "hasCrashDialogs"; emptyParam() }) { val hasCrashDialogs = firstMethodOrNull {
method { name = "hasCrashDialogs"
name = "hasCrashDialogs" emptyParameters()
emptyParam() }?.hook()?.replaceToTrue() != null
}.hook().replaceToTrue() if (!hasCrashDialogs)
firstConstructorOrNull {
} else { parameterCount = 1
constructor { }?.hook()?.after {
paramCount = 1 firstFieldOrNull { name = "mCrashDialogs" }?.of(instance)?.set(emptyList<Any>())
}.hook().after {
field { name = "mCrashDialogs" }.get(instance).set(emptyList<Any>())
} }
} firstMethodOrNull {
method {
name = "showCrashDialogs" name = "showCrashDialogs"
paramCount = 1 parameterCount = 1
}.hook().intercept() }?.hook()?.intercept()
} }
/** 干掉原生错误对话框 - API 30 以下 */ /** 干掉原生错误对话框 - API 30 以下 */
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
ActivityTaskManagerService_LocalServiceClass?.method { ActivityTaskManagerService_LocalServiceClass?.resolve()?.optional()?.firstMethodOrNull {
name = "canShowErrorDialogs" name = "canShowErrorDialogs"
emptyParam() emptyParameters()
}?.ignored()?.hook()?.replaceToFalse() }?.hook()?.replaceToFalse()
ActivityManagerServiceClass?.method { ActivityManagerServiceClass?.resolve()?.optional()?.firstMethodOrNull {
name = "canShowErrorDialogs" name = "canShowErrorDialogs"
emptyParam() emptyParameters()
}?.ignored()?.hook()?.replaceToFalse() }?.hook()?.replaceToFalse()
} }
/** 干掉原生错误对话框 - 如果上述方法全部失效则直接结束对话框 */ /** 干掉原生错误对话框 - 如果上述方法全部失效则直接结束对话框 */
AppErrorDialogClass.apply { AppErrorDialogClass.resolve().optional(silent = true).apply {
method { firstMethodOrNull {
name = "onCreate" name = "onCreate"
param(BundleClass) parameters(Bundle::class)
}.ignored().hook().after { instance<Dialog>().cancel() } }?.hook()?.after { instance<Dialog>().cancel() }
method { firstMethodOrNull {
name = "onStart" name = "onStart"
emptyParam() emptyParameters()
}.ignored().hook().after { instance<Dialog>().cancel() } }?.hook()?.after { instance<Dialog>().cancel() }
} }
/** 注入自定义错误对话框 */ /** 注入自定义错误对话框 */
AppErrorsClass.apply { AppErrorsClass.resolve().optional().apply {
when { when {
Build.VERSION.SDK_INT > Build.VERSION_CODES.R -> { Build.VERSION.SDK_INT > Build.VERSION_CODES.R -> {
method { firstMethodOrNull {
name = "handleAppCrashLSPB" name = "handleAppCrashLSPB"
paramCount = 6 parameterCount = 6
}.hook().after { }?.hook()?.after {
/** 如果为用户终止则不展示异常 */ /** 如果为用户终止则不展示异常 */
if (args(index = 1).string() == "user-terminated") return@after if (args(index = 1).string() == "user-terminated") return@after
/** 当前实例 */ /** 当前实例 */
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: 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 proc = args().first().any() ?: return@after YLog.warn("Received but got null ProcessRecord (Show UI failed)")
@@ -441,29 +473,29 @@ object FrameworkHooker : YukiBaseHooker() {
} }
} }
else -> { else -> {
method { firstMethodOrNull {
name = "handleShowAppErrorUi" name = "handleShowAppErrorUi"
param(MessageClass) parameters(Message::class)
}.hook().after { }?.hook()?.after {
/** 当前实例 */ /** 当前实例 */
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: return@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.field { name = "proc" }.get(resultData).any() val proc = AppErrorDialog_DataClass.resolve().optional().firstFieldOrNull { name = "proc" }?.of(resultData)?.get()
/** 创建 APP 进程异常数据类 */ /** 创建 APP 进程异常数据类 */
AppErrorsProcessData(instance, proc, resultData).handleShowAppErrorUi(context) AppErrorsProcessData(instance, proc, resultData).handleShowAppErrorUi(context)
} }
} }
} }
method { firstMethodOrNull {
name = "handleAppCrashInActivityController" name = "handleAppCrashInActivityController"
returnType = BooleanType returnType = Boolean::class
}.hook().after { }?.hook()?.after {
/** 当前实例 */ /** 当前实例 */
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: 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") val proc = args().first().any() ?: return@after YLog.warn("Received but got null ProcessRecord")

View File

@@ -19,14 +19,13 @@
* *
* This file is created by fankes on 2022/5/7. * This file is created by fankes on 2022/5/7.
*/ */
@file:Suppress("DEPRECATION")
package com.fankes.apperrorstracking.ui.activity.base package com.fankes.apperrorstracking.ui.activity.base
import android.app.ActivityManager import android.app.ActivityManager
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
@@ -34,9 +33,9 @@ import androidx.viewbinding.ViewBinding
import com.fankes.apperrorstracking.R import com.fankes.apperrorstracking.R
import com.fankes.apperrorstracking.utils.factory.isNotSystemInDarkMode import com.fankes.apperrorstracking.utils.factory.isNotSystemInDarkMode
import com.fankes.apperrorstracking.utils.factory.toast import com.fankes.apperrorstracking.utils.factory.toast
import com.highcapable.yukihookapi.hook.factory.current import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass import com.highcapable.kavaref.extension.toClassOrNull
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() { abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
@@ -45,10 +44,11 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = current().generic()?.argument()?.method { val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
name = "inflate" name = "inflate"
param(LayoutInflaterClass) parameters(LayoutInflater::class)
}?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed") }?.invoke<VB>(layoutInflater) ?: error("binding failed")
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
setContentView(binding.root) setContentView(binding.root)
/** 隐藏系统的标题栏 */ /** 隐藏系统的标题栏 */
@@ -58,6 +58,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
isAppearanceLightStatusBars = isNotSystemInDarkMode isAppearanceLightStatusBars = isNotSystemInDarkMode
isAppearanceLightNavigationBars = isNotSystemInDarkMode isAppearanceLightNavigationBars = isNotSystemInDarkMode
} }
@Suppress("DEPRECATION")
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also { ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
window?.statusBarColor = it window?.statusBarColor = it
window?.navigationBarColor = it window?.navigationBarColor = it

View File

@@ -19,6 +19,8 @@
* *
* 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 package com.fankes.apperrorstracking.utils.factory
import android.content.Context import android.content.Context

View File

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

View File

@@ -19,7 +19,7 @@
* *
* 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 package com.fankes.apperrorstracking.utils.factory

View File

@@ -776,6 +776,35 @@
android:textColor="@color/colorTextGray" android:textColor="@color/colorTextGray"
android:textSize="11sp" /> android:textSize="11sp" />
</LinearLayout> </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp"
android:background="@drawable/bg_permotion_round"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_kavaref" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:ellipsize="end"
android:lineSpacingExtra="6dp"
android:maxLines="2"
android:text="@string/about_module_extension"
android:textColor="@color/colorTextGray"
android:textSize="11sp" />
</LinearLayout>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</LinearLayout> </LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -157,4 +157,5 @@
<string name="stack_trace_share_system_build_id">システムビルド ID</string> <string name="stack_trace_share_system_build_id">システムビルド ID</string>
<string name="stack_trace_share_package_name">アプリのパッケージ名</string> <string name="stack_trace_share_package_name">アプリのパッケージ名</string>
<string name="stack_trace_share_other">その他の要件</string> <string name="stack_trace_share_other">その他の要件</string>
<string name="about_module_extension">このモジュールは KavaRef を搭載しています。\n詳細はこちら https://github.com/HighCapable/KavaRef</string>
</resources> </resources>

View File

@@ -159,4 +159,5 @@
<string name="stack_trace_share_other">其它必要信息</string> <string name="stack_trace_share_other">其它必要信息</string>
<string name="share_with_file">以文件方式分享异常堆栈</string> <string name="share_with_file">以文件方式分享异常堆栈</string>
<string name="share_with_file_tip">使用文件的方式代替文本分享异常堆栈</string> <string name="share_with_file_tip">使用文件的方式代替文本分享异常堆栈</string>
<string name="about_module_extension">此模块使用 KavaRef 强力驱动。\n了解更多 https://github.com/HighCapable/KavaRef</string>
</resources> </resources>

View File

@@ -155,4 +155,5 @@
<string name="stack_trace_share_system_build_id">系統建置 ID</string> <string name="stack_trace_share_system_build_id">系統建置 ID</string>
<string name="stack_trace_share_package_name">應用程式包名稱</string> <string name="stack_trace_share_package_name">應用程式包名稱</string>
<string name="stack_trace_share_other">其它必要資訊</string> <string name="stack_trace_share_other">其它必要資訊</string>
<string name="about_module_extension">此模組使用 KavaRef 強力驅動。\n了解更多 https://github.com/HighCapable/KavaRef</string>
</resources> </resources>

View File

@@ -155,4 +155,5 @@
<string name="stack_trace_share_system_build_id">系統建置 ID</string> <string name="stack_trace_share_system_build_id">系統建置 ID</string>
<string name="stack_trace_share_package_name">應用程式包名稱</string> <string name="stack_trace_share_package_name">應用程式包名稱</string>
<string name="stack_trace_share_other">其它必要資訊</string> <string name="stack_trace_share_other">其它必要資訊</string>
<string name="about_module_extension">此模組使用 KavaRef 強力驅動。\n了解更多 https://github.com/HighCapable/KavaRef</string>
</resources> </resources>

View File

@@ -155,4 +155,5 @@
<string name="stack_trace_share_system_build_id">系統建置 ID</string> <string name="stack_trace_share_system_build_id">系統建置 ID</string>
<string name="stack_trace_share_package_name">應用程式包名稱</string> <string name="stack_trace_share_package_name">應用程式包名稱</string>
<string name="stack_trace_share_other">其它必要資訊</string> <string name="stack_trace_share_other">其它必要資訊</string>
<string name="about_module_extension">此模組使用 KavaRef 強力驅動。\n瞭解更多 https://github.com/HighCapable/KavaRef</string>
</resources> </resources>

View File

@@ -162,4 +162,5 @@
<string name="stack_trace_share_other">Other Requirement</string> <string name="stack_trace_share_other">Other Requirement</string>
<string name="share_with_file">Share errors stacktrace with file</string> <string name="share_with_file">Share errors stacktrace with file</string>
<string name="share_with_file_tip">Share errors stacktrace using files instead of text</string> <string name="share_with_file_tip">Share errors stacktrace using files instead of text</string>
<string name="about_module_extension">This Xposed Module is powered by KavaRef.\nLearn more https://github.com/HighCapable/KavaRef</string>
</resources> </resources>