Merge code

This commit is contained in:
2022-03-20 01:11:41 +08:00
parent d3053a725b
commit 1834803a5c
14 changed files with 754 additions and 551 deletions

View File

@@ -25,12 +25,20 @@ android {
versionCode rootProject.ext.appVersionCode versionCode rootProject.ext.appVersionCode
versionName rootProject.ext.appVersionName versionName rootProject.ext.appVersionName
kotlinOptions {
freeCompilerArgs = [
'-Xno-param-assertions',
'-Xno-call-assertions',
'-Xno-receiver-assertions'
]
}
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
release { release {
minifyEnabled true minifyEnabled false
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
@@ -42,6 +50,9 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
} }
buildFeatures {
viewBinding true
}
} }
/** 移除无效耗时 lint Task */ /** 移除无效耗时 lint Task */

View File

@@ -27,24 +27,26 @@
-dontoptimize -dontoptimize
-verbose -verbose
-overloadaggressively -overloadaggressively
-repackageclasses o
-allowaccessmodification -allowaccessmodification
-adaptclassstrings -adaptclassstrings
-adaptresourcefilenames -adaptresourcefilenames
-adaptresourcefilecontents -adaptresourcefilecontents
#-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* -renamesourcefileattribute P
-renamesourcefileattribute H
-keepattributes SourceFile,LineNumberTable -keepattributes SourceFile,LineNumberTable
-keep class android.support**
-keep class androidx**
-keep class me.weishu**{*;}
-keep public class * extends android.app.Activity -keep public class * extends android.app.Activity
-keep public class * extends android.app.Application -keep public class * extends android.app.Application
-keep public class * extends android.app.Service -keep public class * extends android.app.Service
-keep public class * extends android.content.ContentProvider -keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper -keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference -keep public class * extends android.preference.Preference
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
public static *** throwUninitializedProperty(...);
public static *** throwUninitializedPropertyAccessException(...);
}
-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
*** inflate(android.view.LayoutInflater);
}

View File

@@ -101,7 +101,7 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
* 已存储的 JSON 数据 * 已存储的 JSON 数据
* @return [String] * @return [String]
*/ */
internal val storageDataJson get() = (context?.modulePrefs ?: param?.prefs)?.getString(NOTIFY_ICON_DATAS) internal val storageDataJson get() = (context?.modulePrefs ?: param?.prefs)?.direct()?.getString(NOTIFY_ICON_DATAS)
/** /**
* 获取图标数据 * 获取图标数据

View File

@@ -24,40 +24,28 @@
package com.fankes.coloros.notify.ui.activity package com.fankes.coloros.notify.ui.activity
import android.app.ProgressDialog
import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.BaseAdapter import android.widget.BaseAdapter
import android.widget.ListView
import android.widget.TextView
import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import com.fankes.coloros.notify.R import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.bean.IconDataBean import com.fankes.coloros.notify.bean.IconDataBean
import com.fankes.coloros.notify.hook.HookConst.SOURCE_SYNC_WAY import com.fankes.coloros.notify.databinding.ActivityConfigBinding
import com.fankes.coloros.notify.hook.HookConst.SOURCE_SYNC_WAY_CUSTOM_URL import com.fankes.coloros.notify.databinding.AdapterConfigBinding
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_1 import com.fankes.coloros.notify.databinding.DiaIconFilterBinding
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_2
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_3
import com.fankes.coloros.notify.hook.factory.isAppNotifyHookAllOf import com.fankes.coloros.notify.hook.factory.isAppNotifyHookAllOf
import com.fankes.coloros.notify.hook.factory.isAppNotifyHookOf import com.fankes.coloros.notify.hook.factory.isAppNotifyHookOf
import com.fankes.coloros.notify.hook.factory.putAppNotifyHookAllOf import com.fankes.coloros.notify.hook.factory.putAppNotifyHookAllOf
import com.fankes.coloros.notify.hook.factory.putAppNotifyHookOf import com.fankes.coloros.notify.hook.factory.putAppNotifyHookOf
import com.fankes.coloros.notify.param.IconPackParams import com.fankes.coloros.notify.param.IconPackParams
import com.fankes.coloros.notify.ui.activity.base.BaseActivity import com.fankes.coloros.notify.ui.activity.base.BaseActivity
import com.fankes.coloros.notify.ui.view.MaterialSwitch
import com.fankes.coloros.notify.utils.factory.* import com.fankes.coloros.notify.utils.factory.*
import com.fankes.coloros.notify.utils.tool.ClientRequestTool import com.fankes.coloros.notify.utils.tool.IconRuleManagerTool
import com.fankes.coloros.notify.utils.tool.SystemUITool import com.fankes.coloros.notify.utils.tool.SystemUITool
import com.google.android.material.radiobutton.MaterialRadioButton
import com.google.android.material.textfield.TextInputEditText
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
class ConfigureActivity : BaseActivity() { class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
/** 当前筛选条件 */ /** 当前筛选条件 */
private var filterText = "" private var filterText = ""
@@ -71,9 +59,7 @@ class ConfigureActivity : BaseActivity() {
/** 全部的通知优化图标数据 */ /** 全部的通知优化图标数据 */
private var iconAllDatas = ArrayList<IconDataBean>() private var iconAllDatas = ArrayList<IconDataBean>()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_config)
/** 检查激活状态 */ /** 检查激活状态 */
if (!YukiHookModuleStatus.isActive()) { if (!YukiHookModuleStatus.isActive()) {
showDialog { showDialog {
@@ -85,25 +71,23 @@ class ConfigureActivity : BaseActivity() {
return return
} }
/** 返回按钮点击事件 */ /** 返回按钮点击事件 */
findViewById<View>(R.id.title_back_icon).setOnClickListener { onBackPressed() } binding.titleBackIcon.setOnClickListener { onBackPressed() }
/** 刷新适配器结果相关 */ /** 刷新适配器结果相关 */
refreshAdapterResult() refreshAdapterResult()
/** 设置上下按钮点击事件 */ /** 设置上下按钮点击事件 */
findViewById<View>(R.id.config_title_up).setOnClickListener { binding.configTitleUp.setOnClickListener {
snake(msg = "滚动到顶部") snake(msg = "滚动到顶部")
onScrollEvent?.invoke(false) onScrollEvent?.invoke(false)
} }
findViewById<View>(R.id.config_title_down).setOnClickListener { binding.configTitleDown.setOnClickListener {
snake(msg = "滚动到底部") snake(msg = "滚动到底部")
onScrollEvent?.invoke(true) onScrollEvent?.invoke(true)
} }
/** 设置过滤按钮点击事件 */ /** 设置过滤按钮点击事件 */
findViewById<View>(R.id.config_title_filter).setOnClickListener { binding.configTitleFilter.setOnClickListener {
showDialog { showDialog {
title = "按条件过滤" title = "按条件过滤"
var editText: TextInputEditText val editText = bind<DiaIconFilterBinding>().diaIconFilterInputEdit.apply {
addView(R.layout.dia_icon_filter).apply {
editText = findViewById<TextInputEditText>(R.id.dia_icon_filter_input_edit).apply {
requestFocus() requestFocus()
invalidate() invalidate()
if (filterText.isNotBlank()) { if (filterText.isNotBlank()) {
@@ -111,7 +95,6 @@ class ConfigureActivity : BaseActivity() {
setSelection(filterText.length) setSelection(filterText.length)
} }
} }
}
confirmButton { confirmButton {
if (editText.text.toString().isNotBlank()) { if (editText.text.toString().isNotBlank()) {
filterText = editText.text.toString().trim() filterText = editText.text.toString().trim()
@@ -130,13 +113,11 @@ class ConfigureActivity : BaseActivity() {
} }
} }
/** 设置同步列表按钮点击事件 */ /** 设置同步列表按钮点击事件 */
findViewById<View>(R.id.config_title_sync).setOnClickListener { onStartRefresh() } binding.configTitleSync.setOnClickListener { onStartRefresh() }
/** 设置列表元素和 Adapter */ /** 设置列表元素和 Adapter */
findViewById<ListView>(R.id.config_list_view).apply { binding.configListView.apply {
adapter = object : BaseAdapter() { adapter = object : BaseAdapter() {
private val inflater = LayoutInflater.from(context)
override fun getCount() = iconDatas.size override fun getCount() = iconDatas.size
override fun getItem(position: Int) = iconDatas[position] override fun getItem(position: Int) = iconDatas[position]
@@ -145,40 +126,33 @@ class ConfigureActivity : BaseActivity() {
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
var cView = convertView var cView = convertView
val holder: ViewHolder val holder: AdapterConfigBinding
if (convertView == null) { if (convertView == null) {
holder = ViewHolder() holder = AdapterConfigBinding.inflate(LayoutInflater.from(context))
cView = inflater.inflate(R.layout.adapter_config, null).also { cView = holder.root
holder.appIcon = it.findViewById(R.id.adp_app_icon)
holder.appName = it.findViewById(R.id.adp_app_name)
holder.pkgName = it.findViewById(R.id.adp_app_pkg_name)
holder.cbrName = it.findViewById(R.id.adp_cbr_name)
holder.switchOpen = it.findViewById(R.id.adp_app_open_switch)
holder.switchAll = it.findViewById(R.id.adp_app_all_switch)
}
cView.tag = holder cView.tag = holder
} else holder = convertView.tag as ViewHolder } else holder = convertView.tag as AdapterConfigBinding
getItem(position).also { getItem(position).also {
holder.appIcon.setImageBitmap(it.iconBitmap) holder.adpAppIcon.setImageBitmap(it.iconBitmap)
(if (it.iconColor != 0) it.iconColor else resources.getColor(R.color.colorTextGray)).also { color -> (if (it.iconColor != 0) it.iconColor else resources.getColor(R.color.colorTextGray)).also { color ->
holder.appIcon.setColorFilter(color) holder.adpAppIcon.setColorFilter(color)
holder.appName.setTextColor(color) holder.adpAppName.setTextColor(color)
} }
holder.appName.text = it.appName holder.adpAppName.text = it.appName
holder.pkgName.text = it.packageName holder.adpAppPkgName.text = it.packageName
holder.cbrName.text = "贡献者:" + it.contributorName holder.adpCbrName.text = "贡献者:" + it.contributorName
isAppNotifyHookOf(it).also { e -> isAppNotifyHookOf(it).also { e ->
holder.switchOpen.isChecked = e holder.adpAppOpenSwitch.isChecked = e
holder.switchAll.isEnabled = e holder.adpAppAllSwitch.isEnabled = e
} }
holder.switchOpen.setOnCheckedChangeListener { btn, b -> holder.adpAppOpenSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
putAppNotifyHookOf(it, b) putAppNotifyHookOf(it, b)
holder.switchAll.isEnabled = b holder.adpAppAllSwitch.isEnabled = b
SystemUITool.showNeedRestartSnake(context = this@ConfigureActivity) SystemUITool.showNeedRestartSnake(context = this@ConfigureActivity)
} }
holder.switchAll.isChecked = isAppNotifyHookAllOf(it) holder.adpAppAllSwitch.isChecked = isAppNotifyHookAllOf(it)
holder.switchAll.setOnCheckedChangeListener { btn, b -> holder.adpAppAllSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
putAppNotifyHookAllOf(it, b) putAppNotifyHookAllOf(it, b)
SystemUITool.showNeedRestartSnake(context = this@ConfigureActivity) SystemUITool.showNeedRestartSnake(context = this@ConfigureActivity)
@@ -186,15 +160,6 @@ class ConfigureActivity : BaseActivity() {
} }
return cView!! return cView!!
} }
inner class ViewHolder {
lateinit var appIcon: ImageFilterView
lateinit var appName: TextView
lateinit var pkgName: TextView
lateinit var cbrName: TextView
lateinit var switchOpen: MaterialSwitch
lateinit var switchAll: MaterialSwitch
}
}.apply { }.apply {
setOnItemLongClickListener { _, _, p, _ -> setOnItemLongClickListener { _, _, p, _ ->
showDialog { showDialog {
@@ -210,13 +175,22 @@ class ConfigureActivity : BaseActivity() {
onScrollEvent = { post { setSelection(if (it) iconDatas.lastIndex else 0) } } onScrollEvent = { post { setSelection(if (it) iconDatas.lastIndex else 0) } }
} }
/** 设置点击事件 */ /** 设置点击事件 */
findViewById<View>(R.id.config_cbr_button).setOnClickListener { binding.configCbrButton.setOnClickListener {
openBrowser(url = "https://github.com/fankes/AndroidNotifyIconAdapt/blob/main/CONTRIBUTING.md") openBrowser(url = "https://github.com/fankes/AndroidNotifyIconAdapt/blob/main/CONTRIBUTING.md")
} }
/** 装载数据 */ /** 装载数据 */
mockLocalData() mockLocalData()
/** 更新数据 */ /** 更新数据 */
onStartRefresh() if (intent?.getBooleanExtra("isShowUpdDialog", true) == true) onStartRefresh()
}
/** 开始手动同步 */
private fun onStartRefresh() {
IconRuleManagerTool.syncByHand(context = this) {
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this)
}
} }
/** 装载或刷新本地数据 */ /** 装载或刷新本地数据 */
@@ -225,213 +199,13 @@ class ConfigureActivity : BaseActivity() {
refreshAdapterResult() refreshAdapterResult()
} }
/** 首次进入或更新数据 */
private fun onStartRefresh() =
showDialog {
title = "同步列表"
var sourceType = modulePrefs.getInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_1)
var customUrl = modulePrefs.getString(SOURCE_SYNC_WAY_CUSTOM_URL)
addView(R.layout.dia_source_from).apply {
val radio1 = findViewById<MaterialRadioButton>(R.id.dia_sf_rd1)
val radio2 = findViewById<MaterialRadioButton>(R.id.dia_sf_rd2)
val radio3 = findViewById<MaterialRadioButton>(R.id.dia_sf_rd3)
val edLin = findViewById<View>(R.id.dia_sf_text_lin)
findViewById<TextInputEditText>(R.id.dia_sf_text).apply {
if (customUrl.isNotBlank()) {
setText(customUrl)
setSelection(customUrl.length)
}
doOnTextChanged { text, _, _, _ ->
customUrl = text.toString()
modulePrefs.putString(SOURCE_SYNC_WAY_CUSTOM_URL, text.toString())
}
}
edLin.isVisible = sourceType == TYPE_SOURCE_SYNC_WAY_3
radio1.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_1
radio2.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_2
radio3.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_3
radio1.setOnClickListener {
radio2.isChecked = false
radio3.isChecked = false
edLin.isVisible = false
sourceType = TYPE_SOURCE_SYNC_WAY_1
modulePrefs.putInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_1)
}
radio2.setOnClickListener {
radio1.isChecked = false
radio3.isChecked = false
edLin.isVisible = false
sourceType = TYPE_SOURCE_SYNC_WAY_2
modulePrefs.putInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_2)
}
radio3.setOnClickListener {
radio1.isChecked = false
radio2.isChecked = false
edLin.isVisible = true
sourceType = TYPE_SOURCE_SYNC_WAY_3
modulePrefs.putInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_3)
}
}
confirmButton {
when (sourceType) {
TYPE_SOURCE_SYNC_WAY_1 -> onRefreshing(url = "https://raw.fastgit.org/fankes/AndroidNotifyIconAdapt/main")
TYPE_SOURCE_SYNC_WAY_2 -> onRefreshing(url = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main")
TYPE_SOURCE_SYNC_WAY_3 ->
if (customUrl.isNotBlank())
if (customUrl.startsWith("http://") || customUrl.startsWith("https://"))
onRefreshingCustom(customUrl)
else snake(msg = "同步地址不是一个合法的 URL")
else snake(msg = "同步地址不能为空")
else -> snake(msg = "同步类型错误")
}
}
cancelButton()
neutralButton(text = "自定义规则") {
showDialog {
title = "自定义规则"
var editText: TextInputEditText
addView(R.layout.dia_source_from_string).apply {
editText = findViewById<TextInputEditText>(R.id.dia_sfs_input_edit).apply {
requestFocus()
invalidate()
}
}
IconPackParams(context = this@ConfigureActivity).also { params ->
confirmButton(text = "合并") {
editText.text.toString().also { jsonString ->
when {
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> snake(msg = "不是有效的 JSON 数据")
jsonString.isNotBlank() -> {
params.save(
params.splicingJsonArray(
dataJson1 = params.storageDataJson ?: "[]",
dataJson2 = jsonString.takeIf { params.isJsonArray(it) } ?: "[$jsonString]"
)
)
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this@ConfigureActivity)
}
else -> snake(msg = "请输入有效内容")
}
}
}
cancelButton(text = "覆盖") {
editText.text.toString().also { jsonString ->
when {
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> snake(msg = "不是有效的 JSON 数据")
jsonString.isNotBlank() -> {
params.save(dataJson = jsonString.takeIf { params.isJsonArray(it) } ?: "[$jsonString]")
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this@ConfigureActivity)
}
else -> snake(msg = "请输入有效内容")
}
}
}
}
neutralButton(text = "取消")
}
}
}
/**
* 开始更新数据
* @param url
*/
private fun onRefreshing(url: String) = ClientRequestTool.checkingInternetConnect(context = this) {
ProgressDialog(this).apply {
setDefaultStyle(context = this@ConfigureActivity)
setCancelable(false)
setTitle("同步中")
setMessage("正在同步 OS 数据")
show()
}.also {
ClientRequestTool.wait(
context = this,
url = "$url/OS/ColorOS/NotifyIconsSupportConfig.json"
) { isDone1, ctOS ->
it.setMessage("正在同步 APP 数据")
ClientRequestTool.wait(
context = this,
url = "$url/APP/NotifyIconsSupportConfig.json"
) { isDone2, ctAPP ->
it.cancel()
IconPackParams(context = this).also { params ->
if (isDone1 && isDone2) params.splicingJsonArray(ctOS, ctAPP).also {
when {
params.isHackString(it) -> snake(msg = "请求需要验证,请尝试魔法上网或关闭魔法")
params.isNotVaildJson(it) -> snake(msg = "在线规则发生问题,请稍后重试")
params.isCompareDifferent(it) -> {
params.save(it)
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this)
}
else -> snake(msg = "列表数据已是最新")
}
} else showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n${if (!isDone1) ctOS else ctAPP}"
confirmButton(text = "解决方案") {
openBrowser(url = "https://www.baidu.com/s?wd=github%2Braw%2B%E6%97%A0%E6%B3%95%E8%AE%BF%E9%97%AE")
}
cancelButton()
}
}
}
}
}
}
/**
* 开始更新数据
* @param url
*/
private fun onRefreshingCustom(url: String) = ClientRequestTool.checkingInternetConnect(context = this) {
ProgressDialog(this).apply {
setDefaultStyle(context = this@ConfigureActivity)
setCancelable(false)
setTitle("同步中")
setMessage("正在通过自定义地址同步数据")
show()
}.also {
ClientRequestTool.wait(
context = this,
url = url
) { isDone, content ->
it.cancel()
IconPackParams(context = this).also { params ->
if (isDone)
when {
params.isHackString(content) -> snake(msg = "请求需要验证,请尝试魔法上网或关闭魔法")
params.isNotVaildJson(content) -> snake(msg = "目标地址不是有效的 JSON 数据")
params.isCompareDifferent(content) -> {
params.save(content)
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this)
}
else -> snake(msg = "列表数据已是最新")
}
else showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n$content"
confirmButton(text = "我知道了")
}
}
}
}
}
/** 刷新适配器结果相关 */ /** 刷新适配器结果相关 */
private fun refreshAdapterResult() { private fun refreshAdapterResult() {
onChanged?.invoke() onChanged?.invoke()
findViewById<TextView>(R.id.config_title_count_text).text = binding.configTitleCountText.text =
if (filterText.isBlank()) "已适配 ${iconDatas.size} 个 APP 的通知图标" if (filterText.isBlank()) "已适配 ${iconDatas.size} 个 APP 的通知图标"
else "${filterText}” 匹配到 ${iconDatas.size} 个结果" else "${filterText}” 匹配到 ${iconDatas.size} 个结果"
findViewById<TextView>(R.id.config_list_no_data_view).apply { binding.configListNoDataView.apply {
text = if (iconAllDatas.isEmpty()) "噫,竟然什么都没有~\n请点击右上角同步按钮获取云端数据" else "噫,竟然什么都没找到~" text = if (iconAllDatas.isEmpty()) "噫,竟然什么都没有~\n请点击右上角同步按钮获取云端数据" else "噫,竟然什么都没找到~"
isVisible = iconDatas.isEmpty() isVisible = iconDatas.isEmpty()
} }

View File

@@ -24,18 +24,15 @@
package com.fankes.coloros.notify.ui.activity package com.fankes.coloros.notify.ui.activity
import android.app.Notification
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.provider.Settings
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.widget.SwitchCompat
import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.fankes.coloros.notify.BuildConfig import com.fankes.coloros.notify.BuildConfig
import com.fankes.coloros.notify.R import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.databinding.ActivityMainBinding
import com.fankes.coloros.notify.hook.HookConst.ENABLE_ANDROID12_STYLE import com.fankes.coloros.notify.hook.HookConst.ENABLE_ANDROID12_STYLE
import com.fankes.coloros.notify.hook.HookConst.ENABLE_HIDE_ICON import com.fankes.coloros.notify.hook.HookConst.ENABLE_HIDE_ICON
import com.fankes.coloros.notify.hook.HookConst.ENABLE_MODULE import com.fankes.coloros.notify.hook.HookConst.ENABLE_MODULE
@@ -51,7 +48,7 @@ import com.fankes.coloros.notify.utils.tool.SystemUITool
import com.highcapable.yukihookapi.hook.factory.modulePrefs import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
class MainActivity : BaseActivity() { class MainActivity : BaseActivity<ActivityMainBinding>() {
companion object { companion object {
@@ -59,12 +56,10 @@ class MainActivity : BaseActivity() {
private const val moduleVersion = BuildConfig.VERSION_NAME private const val moduleVersion = BuildConfig.VERSION_NAME
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/** 设置文本 */ /** 设置文本 */
findViewById<TextView>(R.id.main_text_version).text = "模块版本:$moduleVersion" binding.mainTextVersion.text = "模块版本:$moduleVersion"
findViewById<TextView>(R.id.main_text_coloros_version).text = "系统版本:$colorOSVersion" binding.mainTextColorOsVersion.text = "系统版本:$colorOSVersion"
when { when {
/** 判断是否为 ColorOS 系统 */ /** 判断是否为 ColorOS 系统 */
isNotColorOS -> isNotColorOS ->
@@ -77,16 +72,33 @@ class MainActivity : BaseActivity() {
} }
/** 判断是否 Hook */ /** 判断是否 Hook */
YukiHookModuleStatus.isActive() -> { YukiHookModuleStatus.isActive() -> {
findViewById<LinearLayout>(R.id.main_lin_status).setBackgroundResource(R.drawable.bg_green_round) binding.mainLinStatus.setBackgroundResource(R.drawable.bg_green_round)
findViewById<ImageFilterView>(R.id.main_img_status).setImageResource(R.mipmap.ic_success) binding.mainImgStatus.setImageResource(R.mipmap.ic_success)
findViewById<TextView>(R.id.main_text_status).text = "模块已激活" binding.mainTextStatus.text = "模块已激活"
if (IconPackParams(context = this).iconDatas.isEmpty() if (IconPackParams(context = this).iconDatas.isEmpty()
&& modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true) && modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)
) showDialog { ) showDialog {
title = "配置通知图标优化名单" title = "配置通知图标优化名单"
msg = "模块需要获取在线规则以更新“通知图标优化名单”,它现在是空的,这看起来是你第一次使用模块,请首先进行配置才可以使用相关功能。\n" + msg = "模块需要获取在线规则以更新“通知图标优化名单”,它现在是空的,这看起来是你第一次使用模块,请首先进行配置才可以使用相关功能。\n" +
"你可以随时在本页面下方找到“配置通知图标优化名单”手动前往。" "你可以随时在本页面下方找到“配置通知图标优化名单”手动前往。"
confirmButton(text = "前往") { startActivity(Intent(this@MainActivity, ConfigureActivity::class.java)) } confirmButton(text = "前往") { navigate<ConfigureActivity>() }
cancelButton()
noCancelable()
}
if (isNotNoificationEnabled && modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true))
showDialog {
title = "模块的通知权限已关闭"
msg = "请开启通知权限,以确保你能收到通知优化图标在线规则的更新。"
confirmButton {
runCatching {
Intent().also { intent ->
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
intent.putExtra(Notification.EXTRA_CHANNEL_ID, applicationInfo.uid)
startActivity(intent)
}
}.onFailure { snake(msg = "跳转通知设置失败") }
}
cancelButton() cancelButton()
noCancelable() noCancelable()
} }
@@ -102,47 +114,34 @@ class MainActivity : BaseActivity() {
noCancelable() noCancelable()
} }
} }
/** 初始化 View */
val moduleEnableSwitch = findViewById<SwitchCompat>(R.id.module_enable_switch)
val moduleEnableLogSwitch = findViewById<SwitchCompat>(R.id.module_enable_log_switch)
val devNotifyConfigItem = findViewById<View>(R.id.config_item_dev)
val a12StyleConfigItem = findViewById<View>(R.id.config_item_a12)
val notifyIconConfigItem = findViewById<View>(R.id.config_item_notify)
val devNotifyConfigSwitch = findViewById<SwitchCompat>(R.id.remove_dev_n_enable_switch)
val crcpNotifyConfigSwitch = findViewById<SwitchCompat>(R.id.remove_chargecp_n_enable_switch)
val dndNotifyConfigSwitch = findViewById<SwitchCompat>(R.id.remove_dndalert_n_enable_switch)
val a12StyleConfigSwitch = findViewById<SwitchCompat>(R.id.a12_style_enable_switch)
val hideIconInLauncherSwitch = findViewById<SwitchCompat>(R.id.hide_icon_in_launcher_switch)
val notifyIconFixSwitch = findViewById<SwitchCompat>(R.id.notify_icon_fix_switch)
val notifyIconFixButton = findViewById<View>(R.id.config_notify_app_button)
/** 获取 Sp 存储的信息 */ /** 获取 Sp 存储的信息 */
devNotifyConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true) binding.devNotifyConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
a12StyleConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true) binding.a12StyleConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
notifyIconConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true) binding.notifyIconConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
notifyIconFixButton.isVisible = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true) binding.notifyIconFixButton.isVisible = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)
devNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_DEV_NOTIFY, default = true) binding.devNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_DEV_NOTIFY, default = true)
crcpNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_CHANGECP_NOTIFY, default = false) binding.crcpNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_CHANGECP_NOTIFY, default = false)
dndNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_DNDALERT_NOTIFY, default = false) binding.dndNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_DNDALERT_NOTIFY, default = false)
a12StyleConfigSwitch.isChecked = modulePrefs.getBoolean(ENABLE_ANDROID12_STYLE, isUpperOfAndroidS) binding.a12StyleConfigSwitch.isChecked = modulePrefs.getBoolean(ENABLE_ANDROID12_STYLE, isUpperOfAndroidS)
moduleEnableSwitch.isChecked = modulePrefs.getBoolean(ENABLE_MODULE, default = true) binding.moduleEnableSwitch.isChecked = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
moduleEnableLogSwitch.isChecked = modulePrefs.getBoolean(ENABLE_MODULE_LOG, default = false) binding.moduleEnableLogSwitch.isChecked = modulePrefs.getBoolean(ENABLE_MODULE_LOG, default = false)
hideIconInLauncherSwitch.isChecked = modulePrefs.getBoolean(ENABLE_HIDE_ICON) binding.hideIconInLauncherSwitch.isChecked = modulePrefs.getBoolean(ENABLE_HIDE_ICON)
notifyIconFixSwitch.isChecked = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true) binding.notifyIconFixSwitch.isChecked = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)
moduleEnableSwitch.setOnCheckedChangeListener { btn, b -> binding.moduleEnableSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_MODULE, b) modulePrefs.putBoolean(ENABLE_MODULE, b)
moduleEnableLogSwitch.isVisible = b binding.moduleEnableLogSwitch.isVisible = b
notifyIconConfigItem.isVisible = b binding.notifyIconConfigItem.isVisible = b
devNotifyConfigItem.isVisible = b binding.devNotifyConfigItem.isVisible = b
a12StyleConfigItem.isVisible = b binding.a12StyleConfigItem.isVisible = b
SystemUITool.showNeedRestartSnake(context = this) SystemUITool.showNeedRestartSnake(context = this)
} }
moduleEnableLogSwitch.setOnCheckedChangeListener { btn, b -> binding.moduleEnableLogSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_MODULE_LOG, b) modulePrefs.putBoolean(ENABLE_MODULE_LOG, b)
SystemUITool.showNeedRestartSnake(context = this) SystemUITool.showNeedRestartSnake(context = this)
} }
hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b -> binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_HIDE_ICON, b) modulePrefs.putBoolean(ENABLE_HIDE_ICON, b)
packageManager.setComponentEnabledSetting( packageManager.setComponentEnabledSetting(
@@ -151,42 +150,42 @@ class MainActivity : BaseActivity() {
PackageManager.DONT_KILL_APP PackageManager.DONT_KILL_APP
) )
} }
notifyIconFixSwitch.setOnCheckedChangeListener { btn, b -> binding.notifyIconFixSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_NOTIFY_ICON_FIX, b) modulePrefs.putBoolean(ENABLE_NOTIFY_ICON_FIX, b)
notifyIconFixButton.isVisible = b binding.notifyIconFixButton.isVisible = b
SystemUITool.showNeedRestartSnake(context = this) SystemUITool.showNeedRestartSnake(context = this)
} }
devNotifyConfigSwitch.setOnCheckedChangeListener { btn, b -> binding.devNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(REMOVE_DEV_NOTIFY, b) modulePrefs.putBoolean(REMOVE_DEV_NOTIFY, b)
SystemUITool.showNeedRestartSnake(context = this) SystemUITool.showNeedRestartSnake(context = this)
} }
crcpNotifyConfigSwitch.setOnCheckedChangeListener { btn, b -> binding.crcpNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(REMOVE_CHANGECP_NOTIFY, b) modulePrefs.putBoolean(REMOVE_CHANGECP_NOTIFY, b)
SystemUITool.showNeedRestartSnake(context = this) SystemUITool.showNeedRestartSnake(context = this)
} }
dndNotifyConfigSwitch.setOnCheckedChangeListener { btn, b -> binding.dndNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(REMOVE_DNDALERT_NOTIFY, b) modulePrefs.putBoolean(REMOVE_DNDALERT_NOTIFY, b)
SystemUITool.showNeedRestartSnake(context = this) SystemUITool.showNeedRestartSnake(context = this)
} }
a12StyleConfigSwitch.setOnCheckedChangeListener { btn, b -> binding.a12StyleConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_ANDROID12_STYLE, b) modulePrefs.putBoolean(ENABLE_ANDROID12_STYLE, b)
SystemUITool.showNeedRestartSnake(context = this) SystemUITool.showNeedRestartSnake(context = this)
} }
/** 通知图标优化名单按钮点击事件 */ /** 通知图标优化名单按钮点击事件 */
notifyIconFixButton.setOnClickListener { startActivity(Intent(this, ConfigureActivity::class.java)) } binding.notifyIconFixButton.setOnClickListener { navigate<ConfigureActivity>() }
/** 重启按钮点击事件 */ /** 重启按钮点击事件 */
findViewById<View>(R.id.title_restart_icon).setOnClickListener { SystemUITool.restartSystemUI(context = this) } binding.titleRestartIcon.setOnClickListener { SystemUITool.restartSystemUI(context = this) }
/** 项目地址按钮点击事件 */ /** 项目地址按钮点击事件 */
findViewById<View>(R.id.title_github_icon).setOnClickListener { binding.titleGithubIcon.setOnClickListener {
openBrowser(url = "https://github.com/fankes/ColorOSNotifyIcon") openBrowser(url = "https://github.com/fankes/ColorOSNotifyIcon")
} }
/** 恰饭! */ /** 恰饭! */
findViewById<View>(R.id.link_with_follow_me).setOnClickListener { binding.linkWithFollowMe.setOnClickListener {
openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market") openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market")
} }
} }

View File

@@ -20,18 +20,36 @@
* *
* This file is Created by fankes on 2022/1/30. * This file is Created by fankes on 2022/1/30.
*/ */
@file:Suppress("UNCHECKED_CAST")
package com.fankes.coloros.notify.ui.activity.base package com.fankes.coloros.notify.ui.activity.base
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.fankes.coloros.notify.R import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.utils.factory.isNotSystemInDarkMode import com.fankes.coloros.notify.utils.factory.isNotSystemInDarkMode
import com.gyf.immersionbar.ktx.immersionBar import com.gyf.immersionbar.ktx.immersionBar
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
import java.lang.reflect.ParameterizedType
abstract class BaseActivity : AppCompatActivity() { abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
/** 获取绑定布局对象 */
lateinit var binding: VB
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
javaClass.genericSuperclass.also { type ->
if (type is ParameterizedType) {
binding = (type.actualTypeArguments[0] as Class<VB>).method {
name = "inflate"
param(LayoutInflaterClass)
}.get().invoke<VB>(layoutInflater) ?: error("binding failed")
setContentView(binding.root)
} else error("binding but got wrong type")
}
/** 隐藏系统的标题栏 */ /** 隐藏系统的标题栏 */
supportActionBar?.hide() supportActionBar?.hide()
/** 初始化沉浸状态栏 */ /** 初始化沉浸状态栏 */
@@ -43,5 +61,10 @@ abstract class BaseActivity : AppCompatActivity() {
navigationBarDarkIcon(isNotSystemInDarkMode) navigationBarDarkIcon(isNotSystemInDarkMode)
fitsSystemWindows(true) fitsSystemWindows(true)
} }
/** 装载子类 */
onCreate()
} }
/** 回调 [onCreate] 方法 */
abstract fun onCreate()
} }

View File

@@ -20,94 +20,182 @@
* *
* This file is Created by fankes on 2022/1/7. * This file is Created by fankes on 2022/1/7.
*/ */
@file:Suppress("unused", "DEPRECATION") @file:Suppress("unused", "OPT_IN_USAGE", "EXPERIMENTAL_API_USAGE")
package com.fankes.coloros.notify.utils.factory package com.fankes.coloros.notify.utils.factory
import android.app.AlertDialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.util.DisplayMetrics import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.WindowManager import android.view.ViewGroup
import kotlin.math.round import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.highcapable.yukihookapi.annotation.DoNotUseField
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
/** /**
* 构造对话框 * 构造对话框
* @param isUseBlackTheme 是否使用深色主题
* @param it 对话框方法体 * @param it 对话框方法体
*/ */
fun Context.showDialog(it: DialogBuilder.() -> Unit) = DialogBuilder(this).apply(it).show() fun Context.showDialog(isUseBlackTheme: Boolean = false, it: DialogBuilder.() -> Unit) =
DialogBuilder(this, isUseBlackTheme).apply(it).show()
/** /**
* 对话框构造器 * 对话框构造器
* @param context 实例 * @param context 实例
* @param isUseBlackTheme 是否使用深色主题 - 对 AndroidX 风格无效
*/ */
class DialogBuilder(private val context: Context) { class DialogBuilder(val context: Context, private val isUseBlackTheme: Boolean) {
private var instance: AlertDialog.Builder? = null // 实例对象 private var instanceAndroidX: androidx.appcompat.app.AlertDialog.Builder? = null // 实例对象
private var instanceAndroid: android.app.AlertDialog.Builder? = null // 实例对象
private var customLayoutView: View? = null // 自定义布局 private var dialogInstance: Dialog? = null // 对话框实例
@DoNotUseField
var customLayoutView: View? = null // 自定义布局
/**
* 是否需要使用 AndroidX 风格对话框
* @return [Boolean]
*/
private val isUsingAndroidX get() = runCatching { context is AppCompatActivity }.getOrNull() ?: false
init { init {
instance = AlertDialog.Builder(context, android.R.style.Theme_Material_Light_Dialog) if (isUsingAndroidX)
runCatching { instanceAndroidX = MaterialAlertDialogBuilder(context) }
else runCatching {
instanceAndroid = android.app.AlertDialog.Builder(
context,
if (isUseBlackTheme) android.R.style.Theme_Material_Dialog else android.R.style.Theme_Material_Light_Dialog
)
}
} }
/** 设置对话框不可关闭 */ /** 设置对话框不可关闭 */
fun noCancelable() = instance?.setCancelable(false) fun noCancelable() {
if (isUsingAndroidX)
runCatching { instanceAndroidX?.setCancelable(false) }
else runCatching { instanceAndroid?.setCancelable(false) }
}
/** 设置对话框标题 */ /** 设置对话框标题 */
var title var title
get() = "" get() = ""
set(value) { set(value) {
instance?.setTitle(value) if (isUsingAndroidX)
runCatching { instanceAndroidX?.setTitle(value) }
else runCatching { instanceAndroid?.setTitle(value) }
} }
/** 设置对话框消息内容 */ /** 设置对话框消息内容 */
var msg var msg
get() = "" get() = ""
set(value) { set(value) {
instance?.setMessage(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<TextView>("progressContent")?.text = value
} }
/** /**
* 设置对话框自定义布局 * 设置对话框自定义布局
* @param resId 属性资源 Id * @return [ViewBinding]
* @return [View]
*/ */
fun addView(resId: Int): View { inline fun <reified T : ViewBinding> bind() =
customLayoutView = LayoutInflater.from(context).inflate(resId, null) T::class.java.method {
return customLayoutView ?: error("Inflate $resId failed") name = "inflate"
} param(LayoutInflaterClass)
}.get().invoke<T>(LayoutInflater.from(context))?.apply {
customLayoutView = root
} ?: error("binding failed")
/** /**
* 设置对话框确定按钮 * 设置对话框确定按钮
* @param text 按钮文本内容 * @param text 按钮文本内容
* @param it 点击事件 * @param it 点击事件
*/ */
fun confirmButton(text: String = "确定", it: () -> Unit = {}) = fun confirmButton(text: String = "确定", it: () -> Unit = {}) {
instance?.setPositiveButton(text) { _, _ -> it() } if (isUsingAndroidX)
runCatching { instanceAndroidX?.setPositiveButton(text) { _, _ -> it() } }
else runCatching { instanceAndroid?.setPositiveButton(text) { _, _ -> it() } }
}
/** /**
* 设置对话框取消按钮 * 设置对话框取消按钮
* @param text 按钮文本内容 * @param text 按钮文本内容
* @param it 点击事件 * @param it 点击事件
*/ */
fun cancelButton(text: String = "取消", it: () -> Unit = {}) = fun cancelButton(text: String = "取消", it: () -> Unit = {}) {
instance?.setNegativeButton(text) { _, _ -> it() } if (isUsingAndroidX)
runCatching { instanceAndroidX?.setNegativeButton(text) { _, _ -> it() } }
else runCatching { instanceAndroid?.setNegativeButton(text) { _, _ -> it() } }
}
/** /**
* 设置对话框第三个按钮 * 设置对话框第三个按钮
* @param text 按钮文本内容 * @param text 按钮文本内容
* @param it 点击事件 * @param it 点击事件
*/ */
fun neutralButton(text: String = "更多", it: () -> Unit = {}) = fun neutralButton(text: String = "更多", it: () -> Unit = {}) {
instance?.setNeutralButton(text) { _, _ -> it() } if (isUsingAndroidX)
runCatching { instanceAndroidX?.setNeutralButton(text) { _, _ -> it() } }
else runCatching { instanceAndroid?.setNeutralButton(text) { _, _ -> it() } }
}
/** 取消对话框 */
fun cancel() = dialogInstance?.cancel()
/** 显示对话框 */ /** 显示对话框 */
internal fun show() = instance?.create()?.apply { internal fun show() {
val dm = DisplayMetrics() if (isUsingAndroidX) runCatching {
(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.getMetrics(dm) instanceAndroidX?.create()?.apply {
customLayoutView?.let { setView(it.apply { minimumWidth = round(x = dm.widthPixels / 1.3).toInt() }) } customLayoutView?.let { setView(it) }
setDefaultStyle(context = this@DialogBuilder.context) dialogInstance = this
}?.show() }?.show()
} else runCatching {
instanceAndroid?.create()?.apply {
customLayoutView?.let { setView(it) }
window?.setBackgroundDrawable(
GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
if (isUseBlackTheme) 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)
})
dialogInstance = this
}?.show()
}
}
} }

View File

@@ -25,7 +25,6 @@
package com.fankes.coloros.notify.utils.factory package com.fankes.coloros.notify.utils.factory
import android.app.Activity import android.app.Activity
import android.app.AlertDialog
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
@@ -36,11 +35,11 @@ import android.content.res.Configuration
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.util.Base64 import android.util.Base64
import android.widget.Toast import android.widget.Toast
import androidx.core.app.NotificationManagerCompat
import com.fankes.coloros.notify.application.CNNApplication.Companion.appContext import com.fankes.coloros.notify.application.CNNApplication.Companion.appContext
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.factory.classOf import com.highcapable.yukihookapi.hook.factory.classOf
@@ -144,6 +143,12 @@ val Context.versionName get() = packageInfo.versionName ?: ""
*/ */
val Context.versionCode get() = packageInfo.versionCode val Context.versionCode get() = packageInfo.versionCode
/**
* 是否关闭了通知权限
* @return [Boolean]
*/
val isNotNoificationEnabled get() = !NotificationManagerCompat.from(appContext).areNotificationsEnabled()
/** /**
* dp 转换为 pxInt * dp 转换为 pxInt
* @param context 使用的实例 * @param context 使用的实例
@@ -158,6 +163,18 @@ fun Number.dp(context: Context) = dpFloat(context).toInt()
*/ */
fun Number.dpFloat(context: Context) = toFloat() * context.resources.displayMetrics.density fun Number.dpFloat(context: Context) = toFloat() * context.resources.displayMetrics.density
/**
* 是否为白色
* @return [Boolean]
*/
val Int.isWhite
get() = safeOfTrue {
val r = this and 0xff0000 shr 16
val g = this and 0x00ff00 shr 8
val b = this and 0x0000ff
(0.2126 * r + 0.7152 * g + 0.0722 * b) >= 128
}
/** /**
* Base64 加密 * Base64 加密
* @return [String] * @return [String]
@@ -193,18 +210,6 @@ val ByteArray.bitmap: Bitmap get() = BitmapFactory.decodeByteArray(this, 0, size
*/ */
val String.bitmap: Bitmap get() = unbase64.bitmap val String.bitmap: Bitmap get() = unbase64.bitmap
/**
* 设置对话框默认风格
* @param context 使用的实例
*/
fun AlertDialog.setDefaultStyle(context: Context) = window?.setBackgroundDrawable(GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, intArrayOf(Color.WHITE, Color.WHITE)
).apply {
shape = GradientDrawable.RECTANGLE
gradientType = GradientDrawable.LINEAR_GRADIENT
cornerRadius = 15.dp(context).toFloat()
})
/** /**
* 获取系统 Prop 值 * 获取系统 Prop 值
* @param key Key * @param key Key
@@ -235,6 +240,13 @@ fun execShellSu(cmd: String) = safeOfNothing {
*/ */
fun toast(msg: String) = Toast.makeText(appContext, msg, Toast.LENGTH_SHORT).show() fun toast(msg: String) = Toast.makeText(appContext, msg, Toast.LENGTH_SHORT).show()
/**
* 跳转到指定页面
*
* [T] 为指定的 [Activity]
*/
inline fun <reified T : Activity> Context.navigate() = startActivity(Intent(this, T::class.java))
/** /**
* 弹出 [Snackbar] * 弹出 [Snackbar]
* @param msg 提示内容 * @param msg 提示内容

View File

@@ -1,150 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* 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.
* <p>
*
* 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/2/25.
*/
@file:Suppress("TrustAllX509TrustManager", "CustomX509TrustManager", "DEPRECATION")
package com.fankes.coloros.notify.utils.tool
import android.app.Activity
import android.app.ProgressDialog
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import com.fankes.coloros.notify.utils.factory.safeOfNull
import com.fankes.coloros.notify.utils.factory.setDefaultStyle
import com.fankes.coloros.notify.utils.factory.showDialog
import com.fankes.coloros.notify.utils.factory.snake
import com.highcapable.yukihookapi.hook.log.loggerD
import okhttp3.*
import java.io.IOException
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.*
/**
* 网络请求管理类
*/
object ClientRequestTool {
/**
* 检查网络连接情况
* @param context 实例
* @param it 已连接回调
*/
fun checkingInternetConnect(context: Activity, it: () -> Unit) =
ProgressDialog(context).apply {
setDefaultStyle(context)
setCancelable(false)
setTitle("准备中")
setMessage("正在检查网络连接情况")
}.apply {
wait(context, url = "https://www.baidu.com") { isDone, _ ->
cancel()
if (isDone) it() else
context.showDialog {
title = "网络不可用"
msg = "无法连接到互联网,请检查你当前的设备是否可以上网,且没有在手机管家中禁用本模块的联网权限。"
confirmButton(text = "检查设置") {
runCatching {
context.startActivity(Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", context.packageName, null)
})
}.onFailure { context.snake(msg = "启动应用信息页面失败") }
}
cancelButton()
}
}
}.show()
/**
* 发送 GET 请求内容并等待
* @param context 实例
* @param url 请求地址
* @param it 回调 - ([Boolean] 是否成功,[String] 成功的内容或失败消息)
*/
fun wait(context: Activity, url: String, it: (Boolean, String) -> Unit) = runCatching {
OkHttpClient().newBuilder().apply {
SSLSocketClient.sSLSocketFactory?.let { sslSocketFactory(it, SSLSocketClient.trustManager) }
hostnameVerifier(SSLSocketClient.hostnameVerifier)
}.build().newCall(
Request.Builder()
.url(url)
.get()
.build()
).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
context.runOnUiThread { it(false, e.toString()) }
}
override fun onResponse(call: Call, response: Response) {
val bodyString = response.body?.string() ?: ""
context.runOnUiThread { it(true, bodyString) }
}
})
}.onFailure { it(false, "URL 无效") }
/**
* 自动信任 SSL 证书
*
* 放行全部加密 SSL 请求
*/
object SSLSocketClient {
/**
* 格式化实例
* @return [SSLSocketFactory] or null
*/
val sSLSocketFactory
get() = safeOfNull {
SSLContext.getInstance("TLS").let {
it.init(null, arrayOf<TrustManager>(trustManager), SecureRandom())
it.socketFactory
}
}
/**
* 使用的实例
* @return [HostnameVerifier]
*/
val hostnameVerifier get() = HostnameVerifier { _, _ -> true }
/**
* 信任管理者
* @return [X509TrustManager]
*/
val trustManager
get() = object : X509TrustManager {
override fun checkClientTrusted(chain: Array<X509Certificate?>?, authType: String?) {
loggerD(msg = "TrustX509 --> $authType")
}
override fun checkServerTrusted(chain: Array<X509Certificate?>?, authType: String?) {
loggerD(msg = "TrustX509 --> $authType")
}
override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
}
}
}

View File

@@ -0,0 +1,439 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* 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.
* <p>
*
* 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/2/25.
*/
@file:Suppress("TrustAllX509TrustManager", "CustomX509TrustManager", "DEPRECATION", "IMPLICIT_CAST_TO_ANY")
package com.fankes.coloros.notify.utils.tool
import android.app.Activity
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import androidx.core.content.getSystemService
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.databinding.DiaSourceFromBinding
import com.fankes.coloros.notify.databinding.DiaSourceFromStringBinding
import com.fankes.coloros.notify.hook.HookConst
import com.fankes.coloros.notify.param.IconPackParams
import com.fankes.coloros.notify.ui.activity.ConfigureActivity
import com.fankes.coloros.notify.utils.factory.openBrowser
import com.fankes.coloros.notify.utils.factory.safeOfNull
import com.fankes.coloros.notify.utils.factory.showDialog
import com.fankes.coloros.notify.utils.factory.snake
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.log.loggerD
import okhttp3.*
import java.io.IOException
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.*
/**
* 通知图标在线规则管理类
*/
object IconRuleManagerTool {
/** 当前规则的系统名称 */
private const val OS_TAG = "ColorOS"
/** 当前规则的通知图标颜色 */
private const val OS_COLOR = 0xFF4E8A5A
/**
* 从在线地址手动同步规则
* @param context 实例
* @param it 成功后回调
*/
fun syncByHand(context: Context, it: () -> Unit) =
context.showDialog {
title = "同步列表"
var sourceType = context.modulePrefs.getInt(HookConst.SOURCE_SYNC_WAY, HookConst.TYPE_SOURCE_SYNC_WAY_1)
var customUrl = context.modulePrefs.getString(HookConst.SOURCE_SYNC_WAY_CUSTOM_URL)
bind<DiaSourceFromBinding>().apply {
diaSfText.apply {
if (customUrl.isNotBlank()) {
setText(customUrl)
setSelection(customUrl.length)
}
doOnTextChanged { text, _, _, _ ->
customUrl = text.toString()
context.modulePrefs.putString(HookConst.SOURCE_SYNC_WAY_CUSTOM_URL, text.toString())
}
}
diaSfTextLin.isVisible = sourceType == HookConst.TYPE_SOURCE_SYNC_WAY_3
diaSfRd1.isChecked = sourceType == HookConst.TYPE_SOURCE_SYNC_WAY_1
diaSfRd2.isChecked = sourceType == HookConst.TYPE_SOURCE_SYNC_WAY_2
diaSfRd3.isChecked = sourceType == HookConst.TYPE_SOURCE_SYNC_WAY_3
diaSfRd1.setOnClickListener {
diaSfRd2.isChecked = false
diaSfRd3.isChecked = false
diaSfTextLin.isVisible = false
sourceType = HookConst.TYPE_SOURCE_SYNC_WAY_1
context.modulePrefs.putInt(HookConst.SOURCE_SYNC_WAY, HookConst.TYPE_SOURCE_SYNC_WAY_1)
}
diaSfRd2.setOnClickListener {
diaSfRd1.isChecked = false
diaSfRd3.isChecked = false
diaSfTextLin.isVisible = false
sourceType = HookConst.TYPE_SOURCE_SYNC_WAY_2
context.modulePrefs.putInt(HookConst.SOURCE_SYNC_WAY, HookConst.TYPE_SOURCE_SYNC_WAY_2)
}
diaSfRd3.setOnClickListener {
diaSfRd1.isChecked = false
diaSfRd2.isChecked = false
diaSfTextLin.isVisible = true
sourceType = HookConst.TYPE_SOURCE_SYNC_WAY_3
context.modulePrefs.putInt(HookConst.SOURCE_SYNC_WAY, HookConst.TYPE_SOURCE_SYNC_WAY_3)
}
}
confirmButton { sync(context, it) }
cancelButton()
neutralButton(text = "自定义规则") {
context.showDialog {
title = "自定义规则"
val editText = bind<DiaSourceFromStringBinding>().diaSfsInputEdit.apply {
requestFocus()
invalidate()
}
IconPackParams(context).also { params ->
confirmButton(text = "合并") {
editText.text.toString().also { jsonString ->
when {
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> context.snake(msg = "不是有效的 JSON 数据")
jsonString.isNotBlank() -> {
params.save(
params.splicingJsonArray(
dataJson1 = params.storageDataJson ?: "[]",
dataJson2 = jsonString.takeIf { params.isJsonArray(it) } ?: "[$jsonString]"
)
)
it()
}
else -> context.snake(msg = "请输入有效内容")
}
}
}
cancelButton(text = "覆盖") {
editText.text.toString().also { jsonString ->
when {
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> context.snake(msg = "不是有效的 JSON 数据")
jsonString.isNotBlank() -> {
params.save(dataJson = jsonString.takeIf { params.isJsonArray(it) } ?: "[$jsonString]")
it()
}
else -> context.snake(msg = "请输入有效内容")
}
}
}
}
neutralButton(text = "取消")
}
}
}
/**
* 从在线地址同步规则
* @param context 实例
* @param it 成功后回调
*/
fun sync(context: Context, it: () -> Unit) {
val sourceType = context.modulePrefs.getInt(HookConst.SOURCE_SYNC_WAY, HookConst.TYPE_SOURCE_SYNC_WAY_1)
val customUrl = context.modulePrefs.getString(HookConst.SOURCE_SYNC_WAY_CUSTOM_URL)
when (sourceType) {
HookConst.TYPE_SOURCE_SYNC_WAY_1 ->
onRefreshing(context, url = "https://raw.fastgit.org/fankes/AndroidNotifyIconAdapt/main", it)
HookConst.TYPE_SOURCE_SYNC_WAY_2 ->
onRefreshing(context, url = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main", it)
HookConst.TYPE_SOURCE_SYNC_WAY_3 ->
if (customUrl.isNotBlank())
if (customUrl.startsWith("http://") || customUrl.startsWith("https://"))
onRefreshingCustom(context, customUrl, it)
else context.snakeOrNotify(title = "同步失败", msg = "同步地址不是一个合法的 URL")
else context.snakeOrNotify(title = "同步失败", msg = "同步地址不能为空")
else -> context.snakeOrNotify(title = "同步异常", msg = "同步类型错误")
}
}
/**
* 开始更新数据
* @param context 实例
* @param url
* @param it 成功后回调
*/
private fun onRefreshing(context: Context, url: String, it: () -> Unit) = checkingInternetConnect(context) {
fun doParse(callback: (Boolean) -> Unit = {}) {
wait(context, url = "$url/OS/$OS_TAG/NotifyIconsSupportConfig.json") { isDone1, ctOS ->
callback(true)
wait(context, url = "$url/APP/NotifyIconsSupportConfig.json") { isDone2, ctAPP ->
callback(false)
IconPackParams(context).also { params ->
when {
isDone1 && isDone2 -> params.splicingJsonArray(ctOS, ctAPP).also {
when {
params.isHackString(it) ->
context.snakeOrNotify(title = "同步错误", msg = "请求需要验证,请尝试魔法上网或关闭魔法")
params.isNotVaildJson(it) ->
context.snakeOrNotify(title = "同步错误", msg = "目标地址不是有效的 JSON 数据")
params.isCompareDifferent(it) -> {
params.save(it)
pushNotify(context, title = "同步完成", msg = "已更新通知图标优化名单,点击查看")
it()
}
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新"))
}
}
context is AppCompatActivity ->
context.showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n${if (!isDone1) ctOS else ctAPP}"
confirmButton(text = "解决方案") {
context.openBrowser(url = "https://www.baidu.com/s?wd=github%2Braw%2B%E6%97%A0%E6%B3%95%E8%AE%BF%E9%97%AE")
}
cancelButton()
}
else -> pushNotify(context, title = "同步地址不可用", msg = if (!isDone1) ctOS else ctAPP)
}
}
}
}
}
if (context is AppCompatActivity)
context.showDialog {
title = "同步中"
progressContent = "正在同步 OS 数据"
noCancelable()
doParse { if (it) progressContent = "正在同步 APP 数据" else cancel() }
}
else doParse()
}
/**
* 开始更新数据
* @param context 实例
* @param url
* @param it 成功后回调
*/
private fun onRefreshingCustom(context: Context, url: String, it: () -> Unit) = checkingInternetConnect(context) {
fun doParse(callback: () -> Unit = {}) {
wait(context, url) { isDone, content ->
callback()
IconPackParams(context).also { params ->
when {
isDone -> when {
params.isHackString(content) ->
context.snakeOrNotify(title = "同步错误", msg = "请求需要验证,请尝试魔法上网或关闭魔法")
params.isNotVaildJson(content) ->
context.snakeOrNotify(title = "同步错误", msg = "目标地址不是有效的 JSON 数据")
params.isCompareDifferent(content) -> {
params.save(content)
pushNotify(context, title = "同步完成", msg = "已更新通知图标优化名单,点击查看")
it()
}
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新"))
}
context is AppCompatActivity ->
context.showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n$content"
confirmButton(text = "我知道了")
}
else -> pushNotify(context, title = "同步地址不可用", msg = content)
}
}
}
}
if (context is AppCompatActivity)
context.showDialog {
title = "同步中"
progressContent = "正在通过自定义地址同步数据"
noCancelable()
doParse { cancel() }
}
else doParse()
}
/**
* 检查网络连接情况
* @param context 实例
* @param it 已连接回调
*/
private fun checkingInternetConnect(context: Context, it: () -> Unit) =
if (context is AppCompatActivity) context.showDialog {
title = "准备中"
progressContent = "正在检查网络连接情况"
noCancelable()
baseCheckingInternetConnect(context) { isDone ->
cancel()
if (isDone) it() else
context.showDialog {
title = "网络不可用"
msg = "无法连接到互联网,请检查你当前的设备是否可以上网,且没有在手机管家中禁用本模块的联网权限。"
confirmButton(text = "检查设置") {
runCatching {
context.startActivity(Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", context.packageName, null)
})
}.onFailure { context.snake(msg = "启动应用信息页面失败") }
}
cancelButton()
}
}
} else baseCheckingInternetConnect(context) { isDone ->
if (isDone) it() else pushNotify(context, title = "网络不可用", msg = "无法连接到互联网,无法更新通知图标规则")
}
/**
* 检查网络连接情况
* @param context 实例
* @param it 已连接回调
*/
private fun baseCheckingInternetConnect(context: Context, it: (Boolean) -> Unit) =
wait(context, url = "https://www.baidu.com") { isDone, _ -> it(isDone) }
/**
* 发送 GET 请求内容并等待
* @param context 实例
* @param url 请求地址
* @param it 回调 - ([Boolean] 是否成功,[String] 成功的内容或失败消息)
*/
private fun wait(context: Context, url: String, it: (Boolean, String) -> Unit) = runCatching {
OkHttpClient().newBuilder().apply {
SSLSocketClient.sSLSocketFactory?.let { sslSocketFactory(it, SSLSocketClient.trustManager) }
hostnameVerifier(SSLSocketClient.hostnameVerifier)
}.build().newCall(
Request.Builder()
.url(url)
.get()
.build()
).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
(context as? Activity?)?.runOnUiThread { it(false, e.toString()) } ?: it(false, e.toString())
}
override fun onResponse(call: Call, response: Response) {
val bodyString = response.body?.string() ?: ""
(context as? Activity?)?.runOnUiThread { it(true, bodyString) } ?: it(true, bodyString)
}
})
}.onFailure { it(false, "URL 无效") }
/**
* 根据实例类型决定推送通知还是弹出提示
* @param title 标题 - 仅对通知生效
* @param msg 消息内容
*/
private fun Context.snakeOrNotify(title: String, msg: String) {
if (this !is AppCompatActivity)
pushNotify(context = this, title, msg)
else snake(msg)
}
/**
* 推送通知
* @param context 实例 - 类型为 [AppCompatActivity] 时将不会推送通知
* @param title 通知标题
* @param msg 通知消息
*/
private fun pushNotify(context: Context, title: String, msg: String) {
if (context !is AppCompatActivity)
context.getSystemService<NotificationManager>()?.apply {
areNotificationsEnabled()
createNotificationChannel(
NotificationChannel(
"notifyRuleUpdateId", "通知图标优化规则",
NotificationManager.IMPORTANCE_DEFAULT
)
)
notify(0, NotificationCompat.Builder(context, "notifyRuleUpdateId").apply {
setContentTitle(title)
setContentText(msg)
color = OS_COLOR.toInt()
setAutoCancel(true)
setSmallIcon(R.drawable.ic_nf_icon_update)
setSound(null)
setDefaults(NotificationCompat.DEFAULT_ALL)
setContentIntent(
PendingIntent.getActivity(
context, 1,
Intent(context, ConfigureActivity::class.java).apply { putExtra("isShowUpdDialog", false) },
if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE
)
)
}.build())
}
}
/**
* 自动信任 SSL 证书
*
* 放行全部加密 SSL 请求
*/
private object SSLSocketClient {
/**
* 格式化实例
* @return [SSLSocketFactory] or null
*/
val sSLSocketFactory
get() = safeOfNull {
SSLContext.getInstance("TLS").let {
it.init(null, arrayOf<TrustManager>(trustManager), SecureRandom())
it.socketFactory
}
}
/**
* 使用的实例
* @return [HostnameVerifier]
*/
val hostnameVerifier get() = HostnameVerifier { _, _ -> true }
/**
* 信任管理者
* @return [X509TrustManager]
*/
val trustManager
get() = object : X509TrustManager {
override fun checkClientTrusted(chain: Array<X509Certificate?>?, authType: String?) {
loggerD(msg = "TrustX509 --> $authType")
}
override fun checkServerTrusted(chain: Array<X509Certificate?>?, authType: String?) {
loggerD(msg = "TrustX509 --> $authType")
}
override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
}
}
}

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM14,13v4h-4v-4H7l5,-5 5,5h-3z"/>
</vector>

View File

@@ -102,7 +102,7 @@
android:textSize="13sp" /> android:textSize="13sp" />
<TextView <TextView
android:id="@+id/main_text_coloros_version" android:id="@+id/main_text_color_os_version"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:alpha="0.8" android:alpha="0.8"
@@ -201,7 +201,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/config_item_dev" android:id="@+id/dev_notify_config_item"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
@@ -216,7 +216,7 @@
android:paddingRight="15dp"> android:paddingRight="15dp">
<com.fankes.coloros.notify.ui.view.MaterialSwitch <com.fankes.coloros.notify.ui.view.MaterialSwitch
android:id="@+id/remove_dev_n_enable_switch" android:id="@+id/dev_notify_config_switch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
@@ -235,7 +235,7 @@
android:textSize="12sp" /> android:textSize="12sp" />
<com.fankes.coloros.notify.ui.view.MaterialSwitch <com.fankes.coloros.notify.ui.view.MaterialSwitch
android:id="@+id/remove_chargecp_n_enable_switch" android:id="@+id/crcp_notify_config_switch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="35dp" android:layout_height="35dp"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
@@ -254,7 +254,7 @@
android:textSize="12sp" /> android:textSize="12sp" />
<com.fankes.coloros.notify.ui.view.MaterialSwitch <com.fankes.coloros.notify.ui.view.MaterialSwitch
android:id="@+id/remove_dndalert_n_enable_switch" android:id="@+id/dnd_notify_config_switch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="35dp" android:layout_height="35dp"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
@@ -274,7 +274,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/config_item_a12" android:id="@+id/a12_style_config_item"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
@@ -289,7 +289,7 @@
android:paddingRight="15dp"> android:paddingRight="15dp">
<com.fankes.coloros.notify.ui.view.MaterialSwitch <com.fankes.coloros.notify.ui.view.MaterialSwitch
android:id="@+id/a12_style_enable_switch" android:id="@+id/a12_style_config_switch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
@@ -310,7 +310,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/config_item_notify" android:id="@+id/notify_icon_config_item"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
@@ -335,7 +335,7 @@
android:textSize="15sp" /> android:textSize="15sp" />
<TextView <TextView
android:id="@+id/config_notify_app_button" android:id="@+id/notify_icon_fix_button"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"

View File

@@ -1,4 +1,4 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.ColorOSNotifyIcon" parent="Theme.Material3.DayNight"> <style name="Theme.ColorOSNotifyIcon" parent="Theme.Material3.DayNight">
<!-- Primary brand color. --> <!-- Primary brand color. -->
@@ -10,7 +10,7 @@
<item name="colorSecondaryVariant">@color/teal_200</item> <item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item> <item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
</style> </style>
</resources> </resources>

View File

@@ -1,4 +1,4 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.ColorOSNotifyIcon" parent="Theme.Material3.DayNight"> <style name="Theme.ColorOSNotifyIcon" parent="Theme.Material3.DayNight">
<!-- Primary brand color. --> <!-- Primary brand color. -->
@@ -10,7 +10,7 @@
<item name="colorSecondaryVariant">@color/teal_700</item> <item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item> <item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
</style> </style>
</resources> </resources>