diff --git a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt index 9e3b3d6..10ef196 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt @@ -19,11 +19,10 @@ * * This file is Created by fankes on 2022/5/7. */ -@file:Suppress("DEPRECATION", "UseCompatLoadingForDrawables") +@file:Suppress("UseCompatLoadingForDrawables") package com.fankes.apperrorstracking.hook.entity -import android.app.AlertDialog import android.app.ApplicationErrorReport import android.content.BroadcastReceiver import android.content.Context @@ -36,10 +35,10 @@ import android.text.TextUtils import android.view.Gravity import android.view.View import android.view.ViewGroup -import android.view.WindowManager import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView +import androidx.core.content.res.ResourcesCompat import com.fankes.apperrorstracking.R import com.fankes.apperrorstracking.bean.AppErrorsInfoBean import com.fankes.apperrorstracking.locale.LocaleString @@ -77,7 +76,7 @@ object FrameworkHooker : YukiBaseHooker() { ) /** 已打开的错误对话框数组 */ - private var openedErrorsDialogs = HashMap() + private var openedErrorsDialogs = HashMap() /** 已忽略错误的 APP 数组 - 直到重新解锁 */ private var ignoredErrorsIfUnlockApps = HashSet() @@ -145,7 +144,7 @@ object FrameworkHooker : YukiBaseHooker() { layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) addView(ImageView(context).apply { - setImageDrawable(moduleAppResources.getDrawable(drawableId)) + setImageDrawable(ResourcesCompat.getDrawable(moduleAppResources, drawableId, null)) layoutParams = ViewGroup.LayoutParams(25.dp(context), 25.dp(context)) setColorFilter(if (context.isSystemInDarkMode) Color.WHITE else Color.BLACK) }) @@ -240,13 +239,9 @@ object FrameworkHooker : YukiBaseHooker() { /** 关闭重复的对话框 */ openedErrorsDialogs[packageName]?.cancel() /** 创建自定义对话框 */ - AlertDialog.Builder( - context, if (context.isSystemInDarkMode) - android.R.style.Theme_Material_Dialog - else android.R.style.Theme_Material_Light_Dialog - ).create().apply { - setTitle(if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName)) - setView(LinearLayout(context).apply { + context.showDialog { + title = if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName) + view = LinearLayout(context).apply { orientation = LinearLayout.VERTICAL /** 应用信息按钮 */ val appInfoButton = @@ -301,14 +296,14 @@ object FrameworkHooker : YukiBaseHooker() { addView(ignoredUntilRestartButton) /** 设置边距 */ setPadding(6.dp(context), 15.dp(context), 6.dp(context), 6.dp(context)) - }) - /** 只有 SystemUid 才能响应系统级别的对话框 */ - window?.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT) + } + /** 设置取消对话框监听 */ + onCancel { openedErrorsDialogs.remove(packageName) } /** 记录实例 */ openedErrorsDialogs[packageName] = this - /** 设置取消对话框监听 */ - setOnCancelListener { openedErrorsDialogs.remove(packageName) } - }.show() + /** 只有 SystemUid 才能响应系统级别的对话框 */ + makeSystemAlert() + } } } injectMember { diff --git a/app/src/main/java/com/fankes/apperrorstracking/utils/factory/DialogBuilderFactory.kt b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/DialogBuilderFactory.kt new file mode 100644 index 0000000..24ac5b3 --- /dev/null +++ b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/DialogBuilderFactory.kt @@ -0,0 +1,223 @@ +/* + * AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer. + * Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) + * https://github.com/KitsunePie/AppErrorsTracking + * + * This software is non-free but opensource software: you can redistribute it + * and/or modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * and eula along with this software. If not, see + * + * + * This file is Created by fankes on 2022/5/12. + */ +@file:Suppress("unused", "OPT_IN_USAGE", "EXPERIMENTAL_API_USAGE", "DEPRECATION") + +package com.fankes.apperrorstracking.utils.factory + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.graphics.drawable.InsetDrawable +import android.view.Gravity +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.AppCompatActivity +import com.google.android.material.dialog.MaterialAlertDialogBuilder + +/** + * 构造对话框 + * @param it 对话框方法体 + */ +fun Context.showDialog(it: DialogBuilder.() -> Unit) = + DialogBuilder(this).apply(it).show() + +/** + * 对话框构造器 + * @param context 实例 + */ +class DialogBuilder(private val context: Context) { + + private var instanceAndroidX: androidx.appcompat.app.AlertDialog.Builder? = null // 实例对象 + private var instanceAndroid: android.app.AlertDialog.Builder? = null // 实例对象 + + private var isSystemAlert = false // 标识为系统级别的对话框 + + private var onCancel: (() -> Unit)? = null // 对话框取消监听 + + private var dialogInstance: Dialog? = null // 对话框实例 + + private var customLayoutView: View? = null // 自定义布局 + + /** + * 是否需要使用 AndroidX 风格对话框 + * @return [Boolean] + */ + private val isUsingAndroidX get() = runCatching { context is AppCompatActivity }.getOrNull() ?: false + + init { + if (isUsingAndroidX) + runCatching { instanceAndroidX = MaterialAlertDialogBuilder(context) } + else runCatching { + instanceAndroid = android.app.AlertDialog.Builder( + context, + if (context.isSystemInDarkMode) android.R.style.Theme_Material_Dialog else android.R.style.Theme_Material_Light_Dialog + ) + } + } + + /** 设置对话框不可关闭 */ + fun noCancelable() { + if (isUsingAndroidX) + runCatching { instanceAndroidX?.setCancelable(false) } + else runCatching { instanceAndroid?.setCancelable(false) } + } + + /** + * 设置为系统级别对话框 + * + * - ❗仅可在系统级别的 APP 中生效 + */ + fun makeSystemAlert() { + isSystemAlert = true + } + + /** 设置对话框标题 */ + var title + get() = "" + set(value) { + if (isUsingAndroidX) + runCatching { instanceAndroidX?.setTitle(value) } + else runCatching { instanceAndroid?.setTitle(value) } + } + + /** 设置对话框消息内容 */ + var msg + get() = "" + set(value) { + if (isUsingAndroidX) + runCatching { instanceAndroidX?.setMessage(value) } + else runCatching { instanceAndroid?.setMessage(value) } + } + + /** 设置进度条对话框消息内容 */ + var progressContent + get() = "" + set(value) { + if (customLayoutView == null) + customLayoutView = LinearLayout(context).apply { + orientation = LinearLayout.HORIZONTAL + gravity = Gravity.CENTER or Gravity.START + addView(ProgressBar(context)) + addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(20.dp(context), 5) }) + addView(TextView(context).apply { + tag = "progressContent" + text = value + }) + setPadding(20.dp(context), 20.dp(context), 20.dp(context), 20.dp(context)) + } + else customLayoutView?.findViewWithTag("progressContent")?.text = value + } + + /** + * 设置对话框自定义布局 + * @return [customLayoutView] + */ + var view + get() = customLayoutView + set(value) { + customLayoutView = value + } + + /** + * 设置对话框确定按钮 + * @param text 按钮文本内容 + * @param it 点击事件 + */ + fun confirmButton(text: String = "确定", it: () -> Unit = {}) { + if (isUsingAndroidX) + runCatching { instanceAndroidX?.setPositiveButton(text) { _, _ -> it() } } + else runCatching { instanceAndroid?.setPositiveButton(text) { _, _ -> it() } } + } + + /** + * 设置对话框取消按钮 + * @param text 按钮文本内容 + * @param it 点击事件 + */ + fun cancelButton(text: String = "取消", it: () -> Unit = {}) { + if (isUsingAndroidX) + runCatching { instanceAndroidX?.setNegativeButton(text) { _, _ -> it() } } + else runCatching { instanceAndroid?.setNegativeButton(text) { _, _ -> it() } } + } + + /** + * 设置对话框第三个按钮 + * @param text 按钮文本内容 + * @param it 点击事件 + */ + fun neutralButton(text: String = "更多", it: () -> Unit = {}) { + if (isUsingAndroidX) + runCatching { instanceAndroidX?.setNeutralButton(text) { _, _ -> it() } } + else runCatching { instanceAndroid?.setNeutralButton(text) { _, _ -> it() } } + } + + /** + * 当对话框关闭时 + * @param it 回调 + */ + fun onCancel(it: () -> Unit) { + onCancel = it + } + + /** 取消对话框 */ + fun cancel() = dialogInstance?.cancel() + + /** 显示对话框 */ + internal fun show() { + if (isUsingAndroidX) runCatching { + instanceAndroidX?.create()?.apply { + customLayoutView?.let { setView(it) } + dialogInstance = this + setOnCancelListener { onCancel?.invoke() } + /** 只有 SystemUid 才能响应系统级别的对话框 */ + if (isSystemAlert) runCatching { window?.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT) } + }?.show() + } else runCatching { + instanceAndroid?.create()?.apply { + customLayoutView?.let { setView(it) } + window?.attributes + window?.setBackgroundDrawable( + InsetDrawable( + GradientDrawable( + GradientDrawable.Orientation.TOP_BOTTOM, + if (context.isSystemInDarkMode) intArrayOf(0xFF2D2D2D.toInt(), 0xFF2D2D2D.toInt()) + else intArrayOf(Color.WHITE, Color.WHITE) + ).apply { + shape = GradientDrawable.RECTANGLE + gradientType = GradientDrawable.LINEAR_GRADIENT + cornerRadius = 15.dpFloat(this@DialogBuilder.context) + }, 30.dp(context), 0, 30.dp(context), 0 + ) + ) + dialogInstance = this + setOnCancelListener { onCancel?.invoke() } + /** 只有 SystemUid 才能响应系统级别的对话框 */ + if (isSystemAlert) runCatching { window?.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT) } + }?.show() + } + } +} \ No newline at end of file