mirror of
https://github.com/fankes/MIUINativeNotifyIcon.git
synced 2025-09-06 02:35:32 +08:00
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
00bf1acdb8 | |||
099886b0b2 | |||
4109a25b5d | |||
db93f8d48e | |||
e6cff940a7 | |||
3f7ea97812 | |||
8f8810e92a | |||
e65624c044 | |||
eabac2bd2d | |||
bde952c72c | |||
|
3e362591b6 | ||
|
ea29c089f6 | ||
cff1eb1958 |
6
.idea/misc.xml
generated
6
.idea/misc.xml
generated
@@ -10,11 +10,15 @@
|
||||
<entry key="app/src/main/res/drawable/bg_yellow_round.xml" value="0.255" />
|
||||
<entry key="app/src/main/res/drawable/permotion_round.xml" value="0.256" />
|
||||
<entry key="app/src/main/res/drawable/white_round.xml" value="0.256" />
|
||||
<entry key="app/src/main/res/layout-w1240dp/dia_source_from.xml" value="0.36484375" />
|
||||
<entry key="app/src/main/res/layout-w1240dp/dia_source_from_string.xml" value="0.36484375" />
|
||||
<entry key="app/src/main/res/layout/activity_config.xml" value="0.42168674698795183" />
|
||||
<entry key="app/src/main/res/layout/activity_login.xml" value="0.4375" />
|
||||
<entry key="app/src/main/res/layout/activity_main.xml" value="0.36160137752905724" />
|
||||
<entry key="app/src/main/res/layout/adapter_config.xml" value="0.4375" />
|
||||
<entry key="app/src/main/res/layout/adapter_config.xml" value="0.375" />
|
||||
<entry key="app/src/main/res/layout/dia_icon_filter.xml" value="0.4307692307692308" />
|
||||
<entry key="app/src/main/res/layout/dia_icon_search.xml" value="0.4307692307692308" />
|
||||
<entry key="app/src/main/res/layout/dia_source_from_string.xml" value="0.4307692307692308" />
|
||||
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.44871794871794873" />
|
||||
</map>
|
||||
</option>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
<br/><br/>
|
||||
<img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/app/src/main/ic_launcher-playstore.png" width = "100" height = "100"/>
|
||||
<br/>
|
||||
@@ -12,7 +12,7 @@ Fix the native notification bar icon function abandoned by the MIUI development
|
||||
# 开始使用
|
||||
|
||||
点击下载最新版本
|
||||
<a href='https://github.com/fankes/MIUINativeNotifyIcon/releases'></a>
|
||||
<a href='https://github.com/fankes/MIUINativeNotifyIcon/releases'></a>
|
||||
<br/><br/>
|
||||
⚠️ 适配说明<br/>
|
||||
|
||||
@@ -33,7 +33,7 @@ Fix the native notification bar icon function abandoned by the MIUI development
|
||||
|
||||
此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方。<br/>
|
||||
|
||||
- [Android 通知图标规范适配](https://github.com/fankes/AndroidNotifyIconAdapt)
|
||||
- [Android 通知图标规范适配计划](https://github.com/fankes/AndroidNotifyIconAdapt)
|
||||
|
||||
# 历史背景
|
||||
|
||||
|
@@ -63,13 +63,11 @@ tasks.whenTaskAdded {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'de.robv.android.xposed:api:82'
|
||||
implementation 'com.highcapable.yukihookapi:api:1.0.4'
|
||||
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.4'
|
||||
implementation "com.github.topjohnwu.libsu:core:3.1.2"
|
||||
implementation 'androidx.annotation:annotation:1.3.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
|
||||
compileOnly 'de.robv.android.xposed:api:82'
|
||||
implementation 'com.highcapable.yukihookapi:api:1.0.3'
|
||||
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.3'
|
||||
implementation 'com.geyifeng.immersionbar:immersionbar:3.2.0'
|
||||
implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
|
||||
|
@@ -47,4 +47,13 @@ data class IconDataBean(
|
||||
) : Serializable {
|
||||
fun toEnabledName() = ("$appName$packageName").base64 + "_enable"
|
||||
fun toEnabledAllName() = ("$appName$packageName").base64 + "_enable_all"
|
||||
override fun toString() = "{\n" +
|
||||
" \"appName\": \"$appName\",\n" +
|
||||
" \"packageName\": \"$packageName\",\n" +
|
||||
" \"iconBitmap\": \"${iconBitmap.base64}\",\n" +
|
||||
" \"iconColor\": \"#${Integer.toHexString(iconColor)}\",\n" +
|
||||
" \"contributorName\": \"$contributorName\",\n" +
|
||||
" \"isEnabled\": $isEnabled,\n" +
|
||||
" \"isEnabledAll\": $isEnabledAll\n" +
|
||||
" }"
|
||||
}
|
@@ -34,5 +34,12 @@ object HookConst {
|
||||
const val ENABLE_NOTIFY_ICON_FIX = "_notify_icon_fix"
|
||||
const val NOTIFY_ICON_DATAS = "_notify_icon_datas"
|
||||
|
||||
const val SOURCE_SYNC_WAY = "_source_sync_way"
|
||||
const val SOURCE_SYNC_WAY_CUSTOM_URL = "_source_sync_way_custom_url"
|
||||
|
||||
const val TYPE_SOURCE_SYNC_WAY_1 = 1000
|
||||
const val TYPE_SOURCE_SYNC_WAY_2 = 2000
|
||||
const val TYPE_SOURCE_SYNC_WAY_3 = 3000
|
||||
|
||||
const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui"
|
||||
}
|
@@ -75,6 +75,9 @@ class HookEntry : YukiHookXposedInitProxy {
|
||||
/** 原生存在的类 */
|
||||
private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val StatusBarIconViewClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.StatusBarIconView"
|
||||
|
||||
/** 根据多个版本存在不同的包名相同的类 */
|
||||
private val ExpandableNotificationRowClass = VariousClass(
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.ExpandableNotificationRow",
|
||||
@@ -130,17 +133,6 @@ class HookEntry : YukiHookXposedInitProxy {
|
||||
}
|
||||
} else BitmapCompatTool.isGrayscaleDrawable(drawable)
|
||||
|
||||
/**
|
||||
* 是否为新版本 MIUI 方案
|
||||
*
|
||||
* 拥有状态栏图标颜色检查功能
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private val PackageParam.hasIgnoreStatusBarIconColor
|
||||
get() = safeOfFalse {
|
||||
NotificationUtilClass.clazz.hasMethod(name = "ignoreStatusBarIconColor", ExpandedNotificationClass.clazz)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为旧版本 MIUI 方案
|
||||
*
|
||||
@@ -432,14 +424,14 @@ class HookEntry : YukiHookXposedInitProxy {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 通知栏小图标颜色
|
||||
* 判断状态栏小图标颜色以及反射的核心方法
|
||||
*
|
||||
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
|
||||
* @param context 实例
|
||||
* @param expandedNf 状态栏实例
|
||||
* @return [Boolean] 是否忽略通知图标颜色
|
||||
*/
|
||||
private fun PackageParam.hookIgnoreStatusBarIconColor(context: Context, expandedNf: StatusBarNotification?) =
|
||||
private fun PackageParam.hasIgnoreStatusBarIconColor(context: Context, expandedNf: StatusBarNotification?) =
|
||||
if (!context.isMiuiNotifyStyle)
|
||||
if (prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) safeOfFalse {
|
||||
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
|
||||
@@ -456,16 +448,26 @@ class HookEntry : YukiHookXposedInitProxy {
|
||||
* 只要不是灰度就返回彩色图标
|
||||
* 否则不对颜色进行反色处理防止一些系统图标出现异常
|
||||
*/
|
||||
if (isTargetFixApp) false else isNotGrayscaleIcon
|
||||
} ?: true
|
||||
} else false
|
||||
else true
|
||||
(if (isTargetFixApp) false else isNotGrayscaleIcon).also {
|
||||
printLogcat(tag = "IconColor", context, expandedNf, isTargetFixApp, !isNotGrayscaleIcon)
|
||||
}
|
||||
} ?: true.also { printLogcat(tag = "IconColor", context, expandedNf, isCustom = false, isGrayscale = false) }
|
||||
} else false.also { printLogcat(tag = "IconColor", context, expandedNf, isCustom = false, isGrayscale = true) }
|
||||
else true.also { printLogcat(tag = "IconColor", context, expandedNf, isCustom = false, isGrayscale = false) }
|
||||
|
||||
override fun onHook() = encase {
|
||||
configs {
|
||||
debugTag = "MIUINativeNotifyIcon"
|
||||
isDebug = false
|
||||
}
|
||||
override fun onHook() {
|
||||
runConfig()
|
||||
runHook()
|
||||
}
|
||||
|
||||
/** 配置 Hook */
|
||||
private fun runConfig() = configs {
|
||||
debugTag = "MIUINativeNotifyIcon"
|
||||
isDebug = false
|
||||
}
|
||||
|
||||
/** 开始 Hook */
|
||||
private fun runHook() = encase {
|
||||
loadApp(SYSTEMUI_PACKAGE_NAME) {
|
||||
when {
|
||||
/** 不是 MIUI 系统停止 Hook */
|
||||
@@ -494,19 +496,6 @@ class HookEntry : YukiHookXposedInitProxy {
|
||||
*/
|
||||
replaceAny { globalContext?.isMiuiNotifyStyle ?: isShowMiuiStyle }
|
||||
}
|
||||
if (hasIgnoreStatusBarIconColor)
|
||||
injectMember {
|
||||
method {
|
||||
name = "ignoreStatusBarIconColor"
|
||||
param(ExpandedNotificationClass.clazz)
|
||||
}
|
||||
replaceAny {
|
||||
hookIgnoreStatusBarIconColor(
|
||||
context = globalContext ?: error("GlobalContext got null"),
|
||||
expandedNf = firstArgs as? StatusBarNotification?
|
||||
)
|
||||
}
|
||||
}
|
||||
/** 强制回写系统的状态栏图标样式为原生 */
|
||||
injectMember {
|
||||
var isUseLegacy = false
|
||||
@@ -534,6 +523,19 @@ class HookEntry : YukiHookXposedInitProxy {
|
||||
}
|
||||
}
|
||||
}
|
||||
StatusBarIconViewClass.hook {
|
||||
/** Hook 状态栏图标的颜色 */
|
||||
injectMember {
|
||||
method { name = "updateIconColor" }
|
||||
afterHook {
|
||||
instance<ImageView>().also {
|
||||
if (hasIgnoreStatusBarIconColor(it.context, field { name = "mNotification" }
|
||||
.of<StatusBarNotification>(instance))) it.colorFilter = null
|
||||
else it.setColorFilter(field { name = "mCurrentSetColor" }.of<Int>(instance) ?: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NotificationHeaderViewWrapperClass.hook {
|
||||
/** 修复下拉通知图标自动设置回 APP 图标的方法 */
|
||||
injectMember {
|
||||
|
@@ -20,15 +20,15 @@
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/24.
|
||||
*/
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.miui.notify.params
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import com.fankes.miui.notify.bean.IconDataBean
|
||||
import com.fankes.miui.notify.hook.HookConst.NOTIFY_ICON_DATAS
|
||||
import com.fankes.miui.notify.utils.bitmap
|
||||
import com.fankes.miui.notify.utils.safeOf
|
||||
import com.fankes.miui.notify.utils.safeOfNan
|
||||
import com.fankes.miui.notify.utils.*
|
||||
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import org.json.JSONArray
|
||||
@@ -47,7 +47,7 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
|
||||
* 已存储的 JSON 数据
|
||||
* @return [String]
|
||||
*/
|
||||
private val storageDataJson get() = (context?.modulePrefs ?: param?.prefs)?.getString(NOTIFY_ICON_DATAS)
|
||||
internal val storageDataJson get() = (context?.modulePrefs ?: param?.prefs)?.getString(NOTIFY_ICON_DATAS)
|
||||
|
||||
/**
|
||||
* 获取图标数据
|
||||
@@ -59,25 +59,32 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
|
||||
if (it.isNotBlank()) runCatching {
|
||||
JSONArray(it).also { array ->
|
||||
for (i in 0 until array.length()) runCatching {
|
||||
(array.get(i) as JSONObject).apply {
|
||||
add(
|
||||
IconDataBean(
|
||||
appName = getString("appName"),
|
||||
packageName = getString("packageName"),
|
||||
isEnabled = getBoolean("isEnabled"),
|
||||
isEnabledAll = getBoolean("isEnabledAll"),
|
||||
iconBitmap = getString("iconBitmap").bitmap,
|
||||
iconColor = safeOfNan { Color.parseColor(getString("iconColor")) },
|
||||
contributorName = getString("contributorName")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
add(convertToBean(array.get(i) as JSONObject)!!)
|
||||
}.onFailure { context?.snake(msg = "部分规则加载失败") }
|
||||
}
|
||||
}
|
||||
}.onFailure { context?.snake(msg = "规则加载发生错误") }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为 [IconDataBean]
|
||||
* @param jsonObject Json 实例
|
||||
* @return [IconDataBean] or null
|
||||
*/
|
||||
private fun convertToBean(jsonObject: JSONObject) = safeOfNull {
|
||||
jsonObject.let {
|
||||
IconDataBean(
|
||||
appName = it.getString("appName"),
|
||||
packageName = it.getString("packageName"),
|
||||
isEnabled = it.getBoolean("isEnabled"),
|
||||
isEnabledAll = it.getBoolean("isEnabledAll"),
|
||||
iconBitmap = it.getString("iconBitmap").bitmap,
|
||||
iconColor = safeOfNan { Color.parseColor(it.getString("iconColor")) },
|
||||
contributorName = it.getString("contributorName")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接图标数组数据
|
||||
* @param dataJson1 图标数据 JSON
|
||||
@@ -88,6 +95,34 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
|
||||
dataJson1.replace(oldValue = "]", newValue = "") + "," + dataJson2.replace(oldValue = "[", newValue = "")
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否不为合法 JSON
|
||||
* @param json 数据
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun isNotVaildJson(json: String) = !isJsonArray(json) && !isJsonObject(json)
|
||||
|
||||
/**
|
||||
* 是否为 JSON 数组
|
||||
* @param json 数据
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun isJsonArray(json: String) = json.trim().let { it.startsWith("[") && it.endsWith("]") }
|
||||
|
||||
/**
|
||||
* 是否为 JSON 实例
|
||||
* @param json 数据
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun isJsonObject(json: String) = json.trim().let { it.startsWith("{") && it.endsWith("}") }
|
||||
|
||||
/**
|
||||
* 是否为异常地址
|
||||
* @param json 数据
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun isHackString(json: String) = json.contains(other = "Checking your browser before accessing")
|
||||
|
||||
/**
|
||||
* 比较图标数据不相等
|
||||
* @param dataJson 图标数据 JSON
|
||||
|
@@ -34,8 +34,14 @@ import android.widget.ListView
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import com.fankes.miui.notify.R
|
||||
import com.fankes.miui.notify.bean.IconDataBean
|
||||
import com.fankes.miui.notify.hook.HookConst.SOURCE_SYNC_WAY
|
||||
import com.fankes.miui.notify.hook.HookConst.SOURCE_SYNC_WAY_CUSTOM_URL
|
||||
import com.fankes.miui.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_1
|
||||
import com.fankes.miui.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_2
|
||||
import com.fankes.miui.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_3
|
||||
import com.fankes.miui.notify.hook.factory.isAppNotifyHookAllOf
|
||||
import com.fankes.miui.notify.hook.factory.isAppNotifyHookOf
|
||||
import com.fankes.miui.notify.hook.factory.putAppNotifyHookAllOf
|
||||
@@ -44,14 +50,13 @@ import com.fankes.miui.notify.params.IconPackParams
|
||||
import com.fankes.miui.notify.ui.base.BaseActivity
|
||||
import com.fankes.miui.notify.utils.*
|
||||
import com.fankes.miui.notify.view.MaterialSwitch
|
||||
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
|
||||
|
||||
class ConfigureActivity : BaseActivity() {
|
||||
|
||||
/** 访问请求链接 */
|
||||
private var rawGithubUrl = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main"
|
||||
|
||||
/** 当前筛选条件 */
|
||||
private var filterText = ""
|
||||
|
||||
@@ -188,7 +193,18 @@ class ConfigureActivity : BaseActivity() {
|
||||
lateinit var switchOpen: MaterialSwitch
|
||||
lateinit var switchAll: MaterialSwitch
|
||||
}
|
||||
}.apply { onChanged = { notifyDataSetChanged() } }
|
||||
}.apply {
|
||||
setOnItemLongClickListener { _, _, p, _ ->
|
||||
showDialog {
|
||||
title = "复制“${iconDatas[p].appName}”的规则"
|
||||
msg = "是否复制单条规则到剪贴板?"
|
||||
confirmButton { copyToClipboard(iconDatas[p].toString()) }
|
||||
cancelButton()
|
||||
}
|
||||
true
|
||||
}
|
||||
onChanged = { notifyDataSetChanged() }
|
||||
}
|
||||
onScrollEvent = { post { setSelection(if (it) iconDatas.lastIndex else 0) } }
|
||||
}
|
||||
/** 设置点击事件 */
|
||||
@@ -210,16 +226,119 @@ class ConfigureActivity : BaseActivity() {
|
||||
/** 首次进入或更新数据 */
|
||||
private fun onStartRefresh() =
|
||||
showDialog {
|
||||
title = if (iconAllDatas.isNotEmpty()) "同步列表" else "初始化"
|
||||
msg = (if (iconAllDatas.isNotEmpty()) "建议定期从云端拉取数据以获得最新的通知图标优化名单适配数据。\n\n"
|
||||
else "首次装载需要从云端下载最新适配数据,后续可继续前往这里检查更新。\n\n") +
|
||||
"通过从 Github 同步最新数据,无法连接可能需要魔法上网。"
|
||||
confirmButton(text = "开始同步") { onRefreshing() }
|
||||
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 = "取消")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 开始更新数据 */
|
||||
private fun onRefreshing() = ClientRequestTool.checkingInternetConnect(context = this) {
|
||||
/**
|
||||
* 开始更新数据
|
||||
* @param url
|
||||
*/
|
||||
private fun onRefreshing(url: String) = ClientRequestTool.checkingInternetConnect(context = this) {
|
||||
ProgressDialog(this).apply {
|
||||
setDefaultStyle(context = this@ConfigureActivity)
|
||||
setCancelable(false)
|
||||
@@ -229,22 +348,27 @@ class ConfigureActivity : BaseActivity() {
|
||||
}.also {
|
||||
ClientRequestTool.wait(
|
||||
context = this,
|
||||
url = "$rawGithubUrl/OS/MIUI/NotifyIconsSupportConfig.json"
|
||||
url = "$url/OS/MIUI/NotifyIconsSupportConfig.json"
|
||||
) { isDone1, ctOS ->
|
||||
it.setMessage("正在同步 APP 数据")
|
||||
ClientRequestTool.wait(
|
||||
context = this,
|
||||
url = "$rawGithubUrl/APP/NotifyIconsSupportConfig.json"
|
||||
url = "$url/APP/NotifyIconsSupportConfig.json"
|
||||
) { isDone2, ctAPP ->
|
||||
it.cancel()
|
||||
IconPackParams(context = this).also { params ->
|
||||
if (isDone1 && isDone2) params.splicingJsonArray(ctOS, ctAPP).also {
|
||||
if (params.isCompareDifferent(it)) {
|
||||
params.save(it)
|
||||
filterText = ""
|
||||
mockLocalData()
|
||||
SystemUITool.showNeedUpdateApplySnake(context = this)
|
||||
} else snake(msg = "列表数据已是最新")
|
||||
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}"
|
||||
@@ -259,6 +383,46 @@ class ConfigureActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始更新数据
|
||||
* @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() {
|
||||
onChanged?.invoke()
|
||||
@@ -280,4 +444,4 @@ class ConfigureActivity : BaseActivity() {
|
||||
else iconAllDatas.filter {
|
||||
it.appName.lowercase().contains(filterText.lowercase()) || it.packageName.lowercase().contains(filterText.lowercase())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -61,8 +61,8 @@ class MainActivity : BaseActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
/** 设置文本 */
|
||||
findViewById<TextView>(R.id.main_text_version).text = "当前版本:$moduleVersion"
|
||||
findViewById<TextView>(R.id.main_text_miui_version).text = "MIUI 版本:$miuiFullVersion"
|
||||
findViewById<TextView>(R.id.main_text_version).text = "模块版本:$moduleVersion"
|
||||
findViewById<TextView>(R.id.main_text_miui_version).text = "系统版本:$miuiFullVersion"
|
||||
when {
|
||||
/** 判断是否为 MIUI 系统 */
|
||||
isNotMIUI ->
|
||||
|
@@ -79,7 +79,7 @@ object ClientRequestTool {
|
||||
* @param url 请求地址
|
||||
* @param it 回调 - ([Boolean] 是否成功,[String] 成功的内容或失败消息)
|
||||
*/
|
||||
fun wait(context: Activity, url: String, it: (Boolean, String) -> Unit) {
|
||||
fun wait(context: Activity, url: String, it: (Boolean, String) -> Unit) = runCatching {
|
||||
OkHttpClient().newBuilder().apply {
|
||||
SSLSocketClient.sSLSocketFactory?.let { sslSocketFactory(it, SSLSocketClient.trustManager) }
|
||||
hostnameVerifier(SSLSocketClient.hostnameVerifier)
|
||||
@@ -98,7 +98,7 @@ object ClientRequestTool {
|
||||
context.runOnUiThread { it(true, bodyString) }
|
||||
}
|
||||
})
|
||||
}
|
||||
}.onFailure { it(false, "URL 无效") }
|
||||
|
||||
/**
|
||||
* 自动信任 SSL 证书
|
||||
|
@@ -26,6 +26,8 @@ package com.fankes.miui.notify.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInfo
|
||||
@@ -48,6 +50,7 @@ import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.log.loggerE
|
||||
import com.highcapable.yukihookapi.hook.type.java.StringType
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
/**
|
||||
* 系统深色模式是否开启
|
||||
@@ -148,8 +151,35 @@ val miuiVersion
|
||||
* @return [String]
|
||||
*/
|
||||
val miuiFullVersion
|
||||
get() = if (isMIUI) (miuiVersion + " " + findPropString(key = "ro.system.build.version.incremental"))
|
||||
else "不是 MIUI 系统"
|
||||
get() = if (isMIUI) findPropString(key = "ro.system.build.version.incremental").let {
|
||||
if (it.lowercase().contains("a") ||
|
||||
it.lowercase().contains("b") ||
|
||||
it.lowercase().contains("c") ||
|
||||
it.lowercase().contains("d") ||
|
||||
it.lowercase().contains("e") ||
|
||||
it.lowercase().contains("f") ||
|
||||
it.lowercase().contains("g") ||
|
||||
it.lowercase().contains("h") ||
|
||||
it.lowercase().contains("i") ||
|
||||
it.lowercase().contains("j") ||
|
||||
it.lowercase().contains("k") ||
|
||||
it.lowercase().contains("l") ||
|
||||
it.lowercase().contains("m") ||
|
||||
it.lowercase().contains("n") ||
|
||||
it.lowercase().contains("o") ||
|
||||
it.lowercase().contains("p") ||
|
||||
it.lowercase().contains("q") ||
|
||||
it.lowercase().contains("r") ||
|
||||
it.lowercase().contains("s") ||
|
||||
it.lowercase().contains("t") ||
|
||||
it.lowercase().contains("u") ||
|
||||
it.lowercase().contains("v") ||
|
||||
it.lowercase().contains("w") ||
|
||||
it.lowercase().contains("x") ||
|
||||
it.lowercase().contains("y") ||
|
||||
it.lowercase().contains("z")
|
||||
) "$it 稳定版" else "V$miuiVersion $it 开发版"
|
||||
} else "不是 MIUI 系统"
|
||||
|
||||
/**
|
||||
* 得到安装包信息
|
||||
@@ -195,6 +225,17 @@ val Number.dp get() = (toFloat() * appContext.resources.displayMetrics.density).
|
||||
*/
|
||||
fun Number.dp(context: Context) = toFloat() * context.resources.displayMetrics.density
|
||||
|
||||
/**
|
||||
* Base64 加密
|
||||
* @return [String]
|
||||
*/
|
||||
val Bitmap.base64
|
||||
get() = safeOfNothing {
|
||||
val baos = ByteArrayOutputStream()
|
||||
compress(Bitmap.CompressFormat.PNG, 100, baos)
|
||||
Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 加密
|
||||
* @return [String]
|
||||
@@ -298,6 +339,19 @@ fun Context.openBrowser(url: String, packageName: String = "") =
|
||||
else snake(msg = "启动系统浏览器失败")
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制到剪贴板
|
||||
* @param content 要复制的文本
|
||||
*/
|
||||
fun Context.copyToClipboard(content: String) = runCatching {
|
||||
(getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).apply {
|
||||
setPrimaryClip(ClipData.newPlainText(null, content))
|
||||
(primaryClip?.getItemAt(0)?.text ?: "").also {
|
||||
if (it != content) snake(msg = "复制失败") else snake(msg = "已复制")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略异常返回值
|
||||
* @param it 回调 - 如果异常为空
|
||||
|
@@ -149,7 +149,7 @@
|
||||
android:divider="@color/trans"
|
||||
android:dividerHeight="15dp"
|
||||
android:fadingEdgeLength="10dp"
|
||||
android:listSelector="@null"
|
||||
android:listSelector="@color/trans"
|
||||
android:padding="15dp"
|
||||
android:requiresFadingEdge="vertical"
|
||||
android:scrollbars="none" />
|
||||
|
@@ -86,7 +86,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:alpha="0.8"
|
||||
android:text="当前版本:%1"
|
||||
android:text="模块版本:%1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="13sp" />
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.8"
|
||||
android:text="MIUI 版本:%1"
|
||||
android:text="系统版本:%1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="13sp" />
|
||||
</LinearLayout>
|
||||
|
@@ -5,6 +5,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_permotion_round"
|
||||
android:baselineAligned="false"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal"
|
||||
android:padding="15dp"
|
||||
|
54
app/src/main/res/layout/dia_source_from.xml
Normal file
54
app/src/main/res/layout/dia_source_from.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="15dp"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8.5dp"
|
||||
android:layout_marginRight="8.5dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="在线规则将不定期更新,建议定期同步列表以适配更多 APP,若无法同步请自行寻找解决方法或魔法上网。"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/dia_sf_rd1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="从 FastGit 获取" />
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/dia_sf_rd2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="从 Github Raw 获取" />
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/dia_sf_rd3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="从自定义地址获取" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/dia_sf_text_lin"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/dia_sf_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:hint="请输入在线地址 URL"
|
||||
android:singleLine="true" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
25
app/src/main/res/layout/dia_source_from_string.xml
Normal file
25
app/src/main/res/layout/dia_source_from_string.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="15dp"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/dia_sfs_input_edit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="150dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center|start|top"
|
||||
android:hint="请粘贴 JSON 规则到此处"
|
||||
android:textSize="14sp" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
@@ -1,12 +1,12 @@
|
||||
plugins {
|
||||
id 'com.android.application' version '7.1.1' apply false
|
||||
id 'com.android.library' version '7.1.1' apply false
|
||||
id 'com.android.application' version '7.1.2' apply false
|
||||
id 'com.android.library' version '7.1.2' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
appVersionName = "1.87"
|
||||
appVersionCode = 20
|
||||
appVersionName = "1.95"
|
||||
appVersionCode = 22
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
|
Reference in New Issue
Block a user