更新通知图标优化名单功能并优化代码和相关功能

This commit is contained in:
2022-01-31 00:03:11 +08:00
parent 638f8f86fb
commit ae1c27aaec
14 changed files with 449 additions and 175 deletions

1
.idea/misc.xml generated
View File

@@ -8,6 +8,7 @@
<entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.44871794871794873" /> <entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.44871794871794873" />
<entry key="app/src/main/res/drawable/permotion_round.xml" value="0.256" /> <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/drawable/white_round.xml" value="0.256" />
<entry key="app/src/main/res/layout/activity_config.xml" value="0.42168674698795183" />
<entry key="app/src/main/res/layout/activity_main.xml" value="0.4036346245815399" /> <entry key="app/src/main/res/layout/activity_main.xml" value="0.4036346245815399" />
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.44871794871794873" /> <entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.44871794871794873" />
</map> </map>

View File

@@ -4,7 +4,7 @@
package="com.fankes.miui.notify"> package="com.fankes.miui.notify">
<application <application
android:name=".application.MNvApplication" android:name=".application.MNNApplication"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
@@ -31,7 +31,6 @@
<activity <activity
android:name="com.fankes.miui.notify.ui.MainActivity" android:name="com.fankes.miui.notify.ui.MainActivity"
android:exported="true" android:exported="true"
android:label="@string/app_name"
android:screenOrientation="behind"> android:screenOrientation="behind">
<intent-filter> <intent-filter>
@@ -53,5 +52,10 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity-alias> </activity-alias>
<activity
android:name="com.fankes.miui.notify.ui.ConfigureActivity"
android:exported="false"
android:screenOrientation="behind" />
</application> </application>
</manifest> </manifest>

View File

@@ -25,19 +25,24 @@ package com.fankes.miui.notify.application
import android.app.Application import android.app.Application
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
class MNvApplication : Application() { class MNNApplication : Application() {
companion object { companion object {
/** 全局静态实例 */ /** 全局静态实例 */
private var context: MNvApplication? = null private var context: MNNApplication? = null
/** 调用全局静态实例 */ /** 调用全局静态实例 */
val appContext get() = context ?: error("App is death") val appContext get() = context ?: error("App is death")
/** 自身 APP 是否已启动 */
var isMineStarted = false
} }
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
/** 设置状态 */
isMineStarted = true
/** 设置静态实例 */ /** 设置静态实例 */
context = this context = this
/** 跟随系统夜间模式 */ /** 跟随系统夜间模式 */

View File

@@ -0,0 +1,38 @@
/**
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com)
*
* This file is part of MIUINativeNotifyIcon.
*
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by fankes on 2022/1/30.
*/
package com.fankes.miui.notify.bean
import android.graphics.Bitmap
import java.io.Serializable
/**
* 通知栏小图标 bean
* @param appName APP 名称 - 仅限默认语言区域
* @param packageName 包名
* @param iconBitmap 图标位图
* @param contributorName 贡献者昵称
*/
data class IconDataBean(
var appName: String,
var packageName: String,
var iconBitmap: Bitmap,
var contributorName: String
) : Serializable

View File

@@ -23,6 +23,7 @@
package com.fankes.miui.notify.hook package com.fankes.miui.notify.hook
import android.content.Context import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import android.graphics.Outline import android.graphics.Outline
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
@@ -33,11 +34,10 @@ import android.view.View
import android.view.ViewOutlineProvider import android.view.ViewOutlineProvider
import android.widget.ImageView import android.widget.ImageView
import androidx.annotation.Keep import androidx.annotation.Keep
import androidx.core.app.NotificationCompat
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import com.fankes.miui.notify.hook.HookMedium.QQ_PACKAGE_NAME
import com.fankes.miui.notify.hook.HookMedium.SELF_PACKAGE_NAME import com.fankes.miui.notify.hook.HookMedium.SELF_PACKAGE_NAME
import com.fankes.miui.notify.hook.HookMedium.SYSTEMUI_PACKAGE_NAME import com.fankes.miui.notify.hook.HookMedium.SYSTEMUI_PACKAGE_NAME
import com.fankes.miui.notify.params.IconPackParams
import com.fankes.miui.notify.utils.* import com.fankes.miui.notify.utils.*
import de.robv.android.xposed.* import de.robv.android.xposed.*
import de.robv.android.xposed.callbacks.XC_LoadPackage import de.robv.android.xposed.callbacks.XC_LoadPackage
@@ -142,6 +142,17 @@ class HookMain : IXposedHookLoadPackage {
.apply { isAccessible = true }.invoke(instance, icon) as Boolean .apply { isAccessible = true }.invoke(instance, icon) as Boolean
} }
/**
* 获取 [ExpandedNotificationClass] 的应用名称
* @param instance 通知实例
* @return [String]
*/
private fun XC_LoadPackage.LoadPackageParam.findAppName(instance: Any?) =
findClass(ExpandedNotificationClass).getDeclaredMethod("getAppName").let {
it.isAccessible = true
it.invoke(instance) as? String ?: ""
}
/** /**
* 获取全局上下文 * 获取全局上下文
* @return [Context] * @return [Context]
@@ -162,15 +173,23 @@ class HookMain : IXposedHookLoadPackage {
val iconDrawable = (param.result as Icon).loadDrawable(globalContext) val iconDrawable = (param.result as Icon).loadDrawable(globalContext)
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */ /** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
(param.args?.get(0) as? StatusBarNotification?)?.also { notifyInstance -> (param.args?.get(0) as? StatusBarNotification?)?.also { notifyInstance ->
/** 判断要设置的图标 */ /** 目标彩色通知 APP 图标 */
var customIcon: Icon? = null
run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
HookMedium.isAppNotifyHookOf(it.packageName)
) {
customIcon = Icon.createWithBitmap(it.iconBitmap)
return@run
}
}
}
when { when {
/** 如果开启了修复聊天 APP 的图标 */ /** 如果开启了修复 APP 的彩色图标 */
notifyInstance.opPkgName == QQ_PACKAGE_NAME && customIcon != null && HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true) ->
XPrefUtils.getBoolean( param.result = customIcon
HookMedium.ENABLE_CHAT_ICON_HOOK,
default = true
) ->
param.result = Icon.createWithBitmap(IconPackParams.qqSmallIcon)
/** 若不是灰度图标自动处理为圆角 */ /** 若不是灰度图标自动处理为圆角 */
!isGrayscaleIcon(globalContext, iconDrawable) -> !isGrayscaleIcon(globalContext, iconDrawable) ->
param.result = Icon.createWithBitmap( param.result = Icon.createWithBitmap(
@@ -205,15 +224,24 @@ class HookMain : IXposedHookLoadPackage {
/** 获取通知小图标 */ /** 获取通知小图标 */
val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context) val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context)
/** 获取发送通知的 APP */ /** 自定义默认小图标 */
val packageName = notifyInstance.opPkgName var customIcon: Bitmap? = null
/** 如果开启了修复聊天 APP 的图标 */ run {
if (packageName == QQ_PACKAGE_NAME && IconPackParams.iconDatas.forEach {
XPrefUtils.getBoolean(HookMedium.ENABLE_CHAT_ICON_HOOK, default = true) if ((notifyInstance.opPkgName == it.packageName ||
) findAppName(notifyInstance) == it.appName) &&
HookMedium.isAppNotifyHookOf(it.packageName)
) {
customIcon = it.iconBitmap
return@run
}
}
}
/** 如果开启了修复 APP 的彩色图标 */
if (customIcon != null && HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true))
iconImageView.apply { iconImageView.apply {
/** 设置自定义小图标 */ /** 设置自定义小图标 */
setImageBitmap(IconPackParams.qqSmallIcon) setImageBitmap(customIcon)
/** 上色 */ /** 上色 */
setColorFilter(if (isUpperOfAndroidS) newStyle else oldStyle) setColorFilter(if (isUpperOfAndroidS) newStyle else oldStyle)
} }
@@ -265,7 +293,7 @@ class HookMain : IXposedHookLoadPackage {
/** 系统版本过低直接停止 Hook */ /** 系统版本过低直接停止 Hook */
if (isLowerAndroidP) return if (isLowerAndroidP) return
/** 若没开启模块直接停止 Hook */ /** 若没开启模块直接停止 Hook */
if (!XPrefUtils.getBoolean(HookMedium.ENABLE_MODULE, default = true)) return if (!HookMedium.getBoolean(HookMedium.ENABLE_MODULE, default = true)) return
/** 强制回写系统的状态栏图标样式为原生 */ /** 强制回写系统的状态栏图标样式为原生 */
runWithoutError(error = "SubstituteSmallIcon") { runWithoutError(error = "SubstituteSmallIcon") {
XposedHelpers.findAndHookMethod( XposedHelpers.findAndHookMethod(
@@ -285,19 +313,30 @@ class HookMain : IXposedHookLoadPackage {
lpparam.findClass(ExpandedNotificationClass), lpparam.findClass(ExpandedNotificationClass),
object : XC_MethodReplacement() { object : XC_MethodReplacement() {
override fun replaceHookedMethod(param: MethodHookParam) = override fun replaceHookedMethod(param: MethodHookParam) =
if (XPrefUtils.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true)) if (HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true))
try { try {
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */ /** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
(param.args?.get(0) as? StatusBarNotification?)?.let { notifyInstance -> (param.args?.get(0) as? StatusBarNotification?)?.let { notifyInstance ->
/** 获取发送通知的 APP */
val packageName = notifyInstance.opPkgName
NotificationCompat()
/** 获取通知小图标 */ /** 获取通知小图标 */
val iconDrawable = val iconDrawable =
notifyInstance.notification.smallIcon.loadDrawable(lpparam.globalContext) notifyInstance.notification.smallIcon.loadDrawable(lpparam.globalContext)
/** 如果开启了修复聊天 APP 的图标 */
if (packageName == QQ_PACKAGE_NAME && /** 获取目标修复彩色图标的 APP */
XPrefUtils.getBoolean(HookMedium.ENABLE_CHAT_ICON_HOOK, default = true) var isTargetApp = false
run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
lpparam.findAppName(notifyInstance) == it.appName) &&
HookMedium.isAppNotifyHookOf(it.packageName)
) {
isTargetApp = true
return@run
}
}
}
/** 如果开启了修复 APP 的彩色图标 */
if (isTargetApp &&
HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true)
) false ) false
/** 只要不是灰度就返回彩色图标 */ /** 只要不是灰度就返回彩色图标 */
else !lpparam.isGrayscaleIcon(lpparam.globalContext, iconDrawable) else !lpparam.isGrayscaleIcon(lpparam.globalContext, iconDrawable)

View File

@@ -18,10 +18,20 @@
* *
* This file is Created by fankes on 2022/1/24. * This file is Created by fankes on 2022/1/24.
*/ */
@file:Suppress("DEPRECATION", "SetWorldReadable")
package com.fankes.miui.notify.hook package com.fankes.miui.notify.hook
import android.content.Context
import android.os.Handler
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.annotation.Keep import androidx.annotation.Keep
import com.fankes.miui.notify.application.MNNApplication.Companion.appContext
import com.fankes.miui.notify.application.MNNApplication.Companion.isMineStarted
import com.fankes.miui.notify.utils.FileUtils
import com.fankes.miui.notify.utils.XPrefUtils
import java.io.File
@Keep @Keep
object HookMedium { object HookMedium {
@@ -29,11 +39,10 @@ object HookMedium {
const val ENABLE_MODULE = "_enable_module" const val ENABLE_MODULE = "_enable_module"
const val ENABLE_HIDE_ICON = "_hide_icon" const val ENABLE_HIDE_ICON = "_hide_icon"
const val ENABLE_COLOR_ICON_HOOK = "_color_icon_hook" const val ENABLE_COLOR_ICON_HOOK = "_color_icon_hook"
const val ENABLE_CHAT_ICON_HOOK = "_chat_icon_hook" const val ENABLE_NOTIFY_ICON_HOOK = "_notify_icon_hook"
const val SELF_PACKAGE_NAME = "com.fankes.miui.notify" const val SELF_PACKAGE_NAME = "com.fankes.miui.notify"
const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui" const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui"
const val QQ_PACKAGE_NAME = "com.tencent.mobileqq"
/** /**
* 判断模块是否激活 * 判断模块是否激活
@@ -44,4 +53,70 @@ object HookMedium {
Log.d("MIUINativeNotifyIcon", "isHooked: true") Log.d("MIUINativeNotifyIcon", "isHooked: true")
return false return false
} }
/**
* 获取此 APP 的通知图标是否被 Hook
* @param packageName 包名
*/
fun isAppNotifyHookOf(packageName: String) = getBoolean(key = "${packageName}_icon", default = true)
/**
* 设置 Hook 此 APP 的通知图标
* @param packageName 包名
* @param isHook 是否 Hook
*/
fun putAppNotifyHookOf(packageName: String, isHook: Boolean) = putBoolean(key = "${packageName}_icon", bool = isHook)
/**
* 获取保存的值
* @param key 名称
* @param default 默认值
* @return [Boolean] 保存的值
*/
fun getBoolean(key: String, default: Boolean = false) =
if (isMineStarted)
appContext.getSharedPreferences(
appContext.packageName + "_preferences",
Context.MODE_PRIVATE
).getBoolean(key, default)
else XPrefUtils.getBoolean(key, default)
/**
* 保存值
* @param key 名称
* @param bool 值
*/
fun putBoolean(key: String, bool: Boolean) {
appContext.getSharedPreferences(
appContext.packageName + "_preferences",
Context.MODE_PRIVATE
).edit().putBoolean(key, bool).apply()
setWorldReadable(appContext)
/** 延迟继续设置强制允许 SP 可读可写 */
Handler().postDelayed({ setWorldReadable(appContext) }, 500)
Handler().postDelayed({ setWorldReadable(appContext) }, 1000)
Handler().postDelayed({ setWorldReadable(appContext) }, 1500)
}
/**
* 强制设置 Sp 存储为全局可读可写
* 以供模块使用
* @param context 实例
*/
fun setWorldReadable(context: Context) {
try {
if (FileUtils.getDefaultPrefFile(context).exists()) {
for (file in arrayOf<File>(
FileUtils.getDataDir(context),
FileUtils.getPrefDir(context),
FileUtils.getDefaultPrefFile(context)
)) {
file.setReadable(true, false)
file.setExecutable(true, false)
}
}
} catch (_: Exception) {
Toast.makeText(context, "无法写入模块设置,请检查权限\n如果此提示一直显示,请不要双开模块", Toast.LENGTH_SHORT).show()
}
}
} }

View File

@@ -1,59 +0,0 @@
/**
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com)
*
* This file is part of MIUINativeNotifyIcon.
*
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by fankes on 2022/1/24.
*/
package com.fankes.miui.notify.hook
import android.graphics.Bitmap
import com.fankes.miui.notify.utils.bitmap
import com.fankes.miui.notify.utils.unbase64
object IconPackParams {
/**
* QQ 通知栏小图标
* @return [Bitmap]
*/
val qqSmallIcon by lazy {
("iVBORw0KGgoAAAANSUhEUgAAAEwAAABMCAYAAADHl1ErAAAAAXNSR0IArs4c6QAAAARzQklUCAgI\n" +
"CHwIZIgAAATZSURBVHic7ZxNjF5TGMd/T6uliBk1pSgtxQwlqo2gKghiU4ZaCOIjRGwIiY8QCRs7\n" +
"S4suRJBYkUgXMiIipBYTiZmuKtRHW0biK8xrNE2l9be4d5LpeN/33nue877n3tHf5p259zzPfc4/\n" +
"z7kf5wuOUglLeXFJw8BmYD0wApwBrACW5UUOANPAPuAb4HNg0sx29j/aREgakvSUpHGFMyHpBUln\n" +
"p65Pz5C0VNJLkmYcQs3nkKRtkoZS1y8qkkYl/RBRqPlMS3oodT2jIOnlHgo1nzdT19eFpHf6KNYs\n" +
"O1LXO4hEYs0ynrr+lZD0SkKxZtmeWodSSLoztVJzeC52/aK+uEoaBP6I6TMC55nZt7GcLYrlKOeN\n" +
"yP5i8HZMZ9EEk7QRGI3lLyIbJG2N5Sxak1T2ZLoylr/ITJnZWTEcRckwSeuor1gAqyTdHMNRrCb5\n" +
"TCQ/veT5GE7cTVLSUuBP4Fh/OD1npZn97HEQI8O20AyxAO71OoghWB2fjJ1w38diNMk9wBqvnz5x\n" +
"EDjFzPaHOnBlmLIu5qaIBdmtY7PHgbdJXu60T8ElHmOvYCNO+xQkFWzYaZ+CCzzGXsHOddqnYK2k\n" +
"4HoHG+YXPTXUPiHLycY+g/Bk2EnAoMM+FYtxxO0V7ESHfUqODzX0CLbEYZua40INY/e4LniOClYR\n" +
"j2CtaFH0n99DDT2CneawTU3w969HsEcdtql5PNQwqHunYb2snQjqfQ3NsNtptlgAD4YYhWZYnYfU\n" +
"yhI09FY5wyStpfliQTb0dm1Vo5Am+USATV15sqpBpSaZ91C0aO43ZDtWmNlvZQtXzbC7WVhiATxc\n" +
"pXDVDNsBXFMpnPqz18zOKVu4tGDK5sTvCwqp/mwys1LTPKs0yXsCg2kC95UtWCXDdgPnB4VTf/4C\n" +
"Bszsn6KCpTIsn860UMWC7EF2U5mCZZvkXeGxNIb7yxQq1SQXeHOcpWVmhYMjhRmWPx0XulgAA2U+\n" +
"lco0ySZNZ/JyS1GBMoLdECGQpnB9UYGu97D82/EnHCPFDaTrt2VRhl3G/0ssgKu7nSwSbH3EQJrC\n" +
"pd1OFgl2YYfjh4GpoHDqwRTZ9M12dJ0/ViTYRR2OPwK8WmBbZ94j66pqR6ckAYoFW93m2F4ze61M\n" +
"VDVmwMzeBT5tc25VN8Miwdrd8Lfkvz+WCKyufJf/tlu0tbibYZFgy+b9/7SZ7cr//qREYHXlQ4D8\n" +
"9eGOaF515B4T/1kSLGl3j1fU9oIZZQPRc+sxd8n1TDdNijLsQP47aWa3tTn/eoF9HXnLzP6ee8DM\n" +
"HgPez//tOmZR9Ka/DVgH3Dj/Ivn5IeDXSuGmZ8TMvmp3QtIYcMjMbu3Z1VWPHQTKMuatb4y1RieQ\n" +
"jVV2fbrUhNVm9r3HgXsGYr7Q6QGvnz7wolcsiLvmezv17TubNLONMRzFFGwR8CX1651tAWvMbDqG\n" +
"s2iTgvMhqk3U66P8IHBFLLGgR1v6SZoANvTCdwW+Bi5u9zrk4ZiYziRdBVxHPWYnLgGelfSZmX2Q\n" +
"OpgjkLRV0sfpXq8KmVAddrCTNChpLLEYVRhXNoMymOB7mLIX1p3U76lYRAsYDt2/wvOUXEnzxAIY\n" +
"wLH02nPT30O2ccYoWVf2mYR/Hh0m23dsP9myltlXk9PJlhkuB052+v8F+AL4CGjWln9N5l/CCEZS\n" +
"HWRiiAAAAABJRU5ErkJggg==").unbase64.bitmap
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com)
*
* This file is part of MIUINativeNotifyIcon.
*
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by fankes on 2022/1/24.
*/
package com.fankes.miui.notify.params
import com.fankes.miui.notify.bean.IconDataBean
import com.fankes.miui.notify.utils.bitmap
/**
* 通知栏小图标适配类
* 国内 APP 不规范的图标将由这里完成其自定义黑白小图标绘制
*/
object IconPackParams {
/**
* 通知栏小图标数组
* 默认使用包名进行匹配 - 若失败则使用 APP 名称进行匹配
* @return [Array]
*/
val iconDatas by lazy {
arrayOf(
IconDataBean(
appName = "QQ",
packageName = "com.tencent.mobileqq",
iconBitmap = ("iVBORw0KGgoAAAANSUhEUgAAAEwAAABMCAYAAADHl1ErAAAAAXNSR0IArs4c6QAAAARzQklUCAgI\n" +
"CHwIZIgAAATZSURBVHic7ZxNjF5TGMd/T6uliBk1pSgtxQwlqo2gKghiU4ZaCOIjRGwIiY8QCRs7\n" +
"S4suRJBYkUgXMiIipBYTiZmuKtRHW0biK8xrNE2l9be4d5LpeN/33nue877n3tHf5p259zzPfc4/\n" +
"z7kf5wuOUglLeXFJw8BmYD0wApwBrACW5UUOANPAPuAb4HNg0sx29j/aREgakvSUpHGFMyHpBUln\n" +
"p65Pz5C0VNJLkmYcQs3nkKRtkoZS1y8qkkYl/RBRqPlMS3oodT2jIOnlHgo1nzdT19eFpHf6KNYs\n" +
"O1LXO4hEYs0ynrr+lZD0SkKxZtmeWodSSLoztVJzeC52/aK+uEoaBP6I6TMC55nZt7GcLYrlKOeN\n" +
"yP5i8HZMZ9EEk7QRGI3lLyIbJG2N5Sxak1T2ZLoylr/ITJnZWTEcRckwSeuor1gAqyTdHMNRrCb5\n" +
"TCQ/veT5GE7cTVLSUuBP4Fh/OD1npZn97HEQI8O20AyxAO71OoghWB2fjJ1w38diNMk9wBqvnz5x\n" +
"EDjFzPaHOnBlmLIu5qaIBdmtY7PHgbdJXu60T8ElHmOvYCNO+xQkFWzYaZ+CCzzGXsHOddqnYK2k\n" +
"4HoHG+YXPTXUPiHLycY+g/Bk2EnAoMM+FYtxxO0V7ESHfUqODzX0CLbEYZua40INY/e4LniOClYR\n" +
"j2CtaFH0n99DDT2CneawTU3w969HsEcdtql5PNQwqHunYb2snQjqfQ3NsNtptlgAD4YYhWZYnYfU\n" +
"yhI09FY5wyStpfliQTb0dm1Vo5Am+USATV15sqpBpSaZ91C0aO43ZDtWmNlvZQtXzbC7WVhiATxc\n" +
"pXDVDNsBXFMpnPqz18zOKVu4tGDK5sTvCwqp/mwys1LTPKs0yXsCg2kC95UtWCXDdgPnB4VTf/4C\n" +
"Bszsn6KCpTIsn860UMWC7EF2U5mCZZvkXeGxNIb7yxQq1SQXeHOcpWVmhYMjhRmWPx0XulgAA2U+\n" +
"lco0ySZNZ/JyS1GBMoLdECGQpnB9UYGu97D82/EnHCPFDaTrt2VRhl3G/0ssgKu7nSwSbH3EQJrC\n" +
"pd1OFgl2YYfjh4GpoHDqwRTZ9M12dJ0/ViTYRR2OPwK8WmBbZ94j66pqR6ckAYoFW93m2F4ze61M\n" +
"VDVmwMzeBT5tc25VN8Miwdrd8Lfkvz+WCKyufJf/tlu0tbibYZFgy+b9/7SZ7cr//qREYHXlQ4D8\n" +
"9eGOaF515B4T/1kSLGl3j1fU9oIZZQPRc+sxd8n1TDdNijLsQP47aWa3tTn/eoF9HXnLzP6ee8DM\n" +
"HgPez//tOmZR9Ka/DVgH3Dj/Ivn5IeDXSuGmZ8TMvmp3QtIYcMjMbu3Z1VWPHQTKMuatb4y1RieQ\n" +
"jVV2fbrUhNVm9r3HgXsGYr7Q6QGvnz7wolcsiLvmezv17TubNLONMRzFFGwR8CX1651tAWvMbDqG\n" +
"s2iTgvMhqk3U66P8IHBFLLGgR1v6SZoANvTCdwW+Bi5u9zrk4ZiYziRdBVxHPWYnLgGelfSZmX2Q\n" +
"OpgjkLRV0sfpXq8KmVAddrCTNChpLLEYVRhXNoMymOB7mLIX1p3U76lYRAsYDt2/wvOUXEnzxAIY\n" +
"wLH02nPT30O2ccYoWVf2mYR/Hh0m23dsP9myltlXk9PJlhkuB052+v8F+AL4CGjWln9N5l/CCEZS\n" +
"HWRiiAAAAABJRU5ErkJggg==").bitmap,
contributorName = "fankes"
)
)
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com)
*
* This file is part of MIUINativeNotifyIcon.
*
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by fankes on 2022/01/30.
*/
package com.fankes.miui.notify.ui
import android.os.Bundle
import android.view.View
import com.fankes.miui.notify.R
import com.fankes.miui.notify.ui.base.BaseActivity
class ConfigureActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_config)
/** 返回按钮点击事件 */
findViewById<View>(R.id.title_back_icon).setOnClickListener { onBackPressed() }
}
}

View File

@@ -18,32 +18,28 @@
* *
* This file is Created by fankes on 2022/01/24. * This file is Created by fankes on 2022/01/24.
*/ */
@file:Suppress("DEPRECATION", "SetWorldReadable", "SetTextI18n") @file:Suppress("SetTextI18n")
package com.fankes.miui.notify.ui package com.fankes.miui.notify.ui
import android.content.ComponentName import android.content.ComponentName
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.SwitchCompat
import androidx.constraintlayout.utils.widget.ImageFilterView import androidx.constraintlayout.utils.widget.ImageFilterView
import com.fankes.miui.notify.BuildConfig import com.fankes.miui.notify.BuildConfig
import com.fankes.miui.notify.R import com.fankes.miui.notify.R
import com.fankes.miui.notify.hook.HookMedium import com.fankes.miui.notify.hook.HookMedium
import com.fankes.miui.notify.ui.base.BaseActivity
import com.fankes.miui.notify.utils.* import com.fankes.miui.notify.utils.*
import com.gyf.immersionbar.ktx.immersionBar
import java.io.File
class MainActivity : AppCompatActivity() { class MainActivity : BaseActivity() {
companion object { companion object {
@@ -54,17 +50,6 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
/** 隐藏系统的标题栏 */
supportActionBar?.hide()
/** 初始化沉浸状态栏 */
immersionBar {
statusBarColor(R.color.colorThemeBackground)
autoDarkModeEnable(true)
statusBarDarkFont(isNotSystemInDarkMode)
navigationBarColor(R.color.colorThemeBackground)
navigationBarDarkIcon(isNotSystemInDarkMode)
fitsSystemWindows(true)
}
/** 设置文本 */ /** 设置文本 */
findViewById<TextView>(R.id.main_text_version).text = "当前版本:$moduleVersion" findViewById<TextView>(R.id.main_text_version).text = "当前版本:$moduleVersion"
findViewById<TextView>(R.id.main_text_miui_version).text = "MIUI 版本:$miuiVersion" findViewById<TextView>(R.id.main_text_miui_version).text = "MIUI 版本:$miuiVersion"
@@ -108,12 +93,12 @@ class MainActivity : AppCompatActivity() {
val moduleEnableSwitch = findViewById<SwitchCompat>(R.id.module_enable_switch) val moduleEnableSwitch = findViewById<SwitchCompat>(R.id.module_enable_switch)
val hideIconInLauncherSwitch = findViewById<SwitchCompat>(R.id.hide_icon_in_launcher_switch) val hideIconInLauncherSwitch = findViewById<SwitchCompat>(R.id.hide_icon_in_launcher_switch)
val colorIconHookSwitch = findViewById<SwitchCompat>(R.id.color_icon_fix_switch) val colorIconHookSwitch = findViewById<SwitchCompat>(R.id.color_icon_fix_switch)
val chatIconHookSwitch = findViewById<SwitchCompat>(R.id.chat_icon_fix_switch) val notifyIconHookSwitch = findViewById<SwitchCompat>(R.id.notify_icon_fix_switch)
/** 获取 Sp 存储的信息 */ /** 获取 Sp 存储的信息 */
moduleEnableSwitch.isChecked = getBoolean(HookMedium.ENABLE_MODULE, default = true) moduleEnableSwitch.isChecked = getBoolean(HookMedium.ENABLE_MODULE, default = true)
hideIconInLauncherSwitch.isChecked = getBoolean(HookMedium.ENABLE_HIDE_ICON) hideIconInLauncherSwitch.isChecked = getBoolean(HookMedium.ENABLE_HIDE_ICON)
colorIconHookSwitch.isChecked = getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true) colorIconHookSwitch.isChecked = getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true)
chatIconHookSwitch.isChecked = getBoolean(HookMedium.ENABLE_CHAT_ICON_HOOK, default = true) notifyIconHookSwitch.isChecked = getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true)
moduleEnableSwitch.setOnCheckedChangeListener { btn, b -> moduleEnableSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
putBoolean(HookMedium.ENABLE_MODULE, b) putBoolean(HookMedium.ENABLE_MODULE, b)
@@ -131,9 +116,9 @@ class MainActivity : AppCompatActivity() {
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
putBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, b) putBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, b)
} }
chatIconHookSwitch.setOnCheckedChangeListener { btn, b -> notifyIconHookSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
putBoolean(HookMedium.ENABLE_CHAT_ICON_HOOK, b) putBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, b)
} }
/** 重启按钮点击事件 */ /** 重启按钮点击事件 */
findViewById<View>(R.id.title_restart_icon).setOnClickListener { findViewById<View>(R.id.title_restart_icon).setOnClickListener {
@@ -144,6 +129,10 @@ class MainActivity : AppCompatActivity() {
cancelButton() cancelButton()
} }
} }
/** 通知图标优化名单按钮点击事件 */
findViewById<View>(R.id.config_notify_app_button).setOnClickListener {
startActivity(Intent(this, ConfigureActivity::class.java))
}
/** 恰饭! */ /** 恰饭! */
findViewById<View>(R.id.link_with_follow_me).setOnClickListener { findViewById<View>(R.id.link_with_follow_me).setOnClickListener {
try { try {
@@ -187,73 +176,18 @@ class MainActivity : AppCompatActivity() {
else Toast.makeText(this, "ROOT 权限获取失败", Toast.LENGTH_SHORT).show() else Toast.makeText(this, "ROOT 权限获取失败", Toast.LENGTH_SHORT).show()
} }
override fun onResume() {
super.onResume()
setWorldReadable()
}
override fun onRestart() {
super.onRestart()
setWorldReadable()
}
override fun onPause() {
super.onPause()
setWorldReadable()
}
/** /**
* 获取保存的值 * 获取保存的值
* @param key 名称 * @param key 名称
* @param default 默认值 * @param default 默认值
* @return [Boolean] 保存的值 * @return [Boolean] 保存的值
*/ */
private fun getBoolean(key: String, default: Boolean = false) = private fun getBoolean(key: String, default: Boolean = false) = HookMedium.getBoolean(key, default)
getSharedPreferences(
packageName + "_preferences",
Context.MODE_PRIVATE
).getBoolean(key, default)
/** /**
* 保存值 * 保存值
* @param key 名称 * @param key 名称
* @param bool 值 * @param bool 值
*/ */
private fun putBoolean(key: String, bool: Boolean) { private fun putBoolean(key: String, bool: Boolean) = HookMedium.putBoolean(key, bool)
getSharedPreferences(
packageName + "_preferences",
Context.MODE_PRIVATE
).edit().putBoolean(key, bool).apply()
setWorldReadable()
/** 延迟继续设置强制允许 SP 可读可写 */
Handler().postDelayed({ setWorldReadable() }, 500)
Handler().postDelayed({ setWorldReadable() }, 1000)
Handler().postDelayed({ setWorldReadable() }, 1500)
}
/**
* 强制设置 Sp 存储为全局可读可写
* 以供模块使用
*/
private fun setWorldReadable() {
try {
if (FileUtils.getDefaultPrefFile(this).exists()) {
for (file in arrayOf<File>(
FileUtils.getDataDir(this),
FileUtils.getPrefDir(this),
FileUtils.getDefaultPrefFile(this)
)) {
file.setReadable(true, false)
file.setExecutable(true, false)
}
}
} catch (_: Exception) {
Toast.makeText(this, "无法写入模块设置,请检查权限\n如果此提示一直显示,请不要双开模块", Toast.LENGTH_SHORT).show()
}
}
override fun onBackPressed() {
setWorldReadable()
super.onBackPressed()
}
} }

View File

@@ -0,0 +1,66 @@
/**
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com)
*
* This file is part of MIUINativeNotifyIcon.
*
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by fankes on 2022/01/30.
*/
package com.fankes.miui.notify.ui.base
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.fankes.miui.notify.R
import com.fankes.miui.notify.hook.HookMedium
import com.fankes.miui.notify.utils.isNotSystemInDarkMode
import com.gyf.immersionbar.ktx.immersionBar
abstract class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/** 隐藏系统的标题栏 */
supportActionBar?.hide()
/** 初始化沉浸状态栏 */
immersionBar {
statusBarColor(R.color.colorThemeBackground)
autoDarkModeEnable(true)
statusBarDarkFont(isNotSystemInDarkMode)
navigationBarColor(R.color.colorThemeBackground)
navigationBarDarkIcon(isNotSystemInDarkMode)
fitsSystemWindows(true)
}
}
override fun onResume() {
super.onResume()
HookMedium.setWorldReadable(this)
}
override fun onRestart() {
super.onRestart()
HookMedium.setWorldReadable(this)
}
override fun onPause() {
super.onPause()
HookMedium.setWorldReadable(this)
}
override fun onBackPressed() {
HookMedium.setWorldReadable(this)
super.onBackPressed()
}
}

View File

@@ -31,7 +31,7 @@ import android.graphics.Bitmap.createBitmap
import android.os.Build import android.os.Build
import android.service.notification.StatusBarNotification import android.service.notification.StatusBarNotification
import android.util.Base64 import android.util.Base64
import com.fankes.miui.notify.application.MNvApplication.Companion.appContext import com.fankes.miui.notify.application.MNNApplication.Companion.appContext
import java.io.DataInputStream import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
@@ -177,6 +177,12 @@ val String.unbase64 get() = Base64.decode(this, Base64.DEFAULT) ?: ByteArray(0)
*/ */
val ByteArray.bitmap: Bitmap get() = BitmapFactory.decodeByteArray(this, 0, size) val ByteArray.bitmap: Bitmap get() = BitmapFactory.decodeByteArray(this, 0, size)
/**
* 字符串解析为位图
* @return [Bitmap]
*/
val String.bitmap: Bitmap get() = unbase64.bitmap
/** /**
* 圆角图片 * 圆角图片
* @param radius 圆角度 * @param radius 圆角度

View File

@@ -0,0 +1,42 @@
<?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="match_parent"
android:background="@color/colorThemeBackground"
android:orientation="vertical"
tools:context=".ui.MainActivity"
tools:ignore="HardcodedText,UseCompoundDrawables,ContentDescription">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="0dp"
android:gravity="center|start"
android:paddingLeft="15dp"
android:paddingTop="13dp"
android:paddingRight="15dp"
android:paddingBottom="5dp">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/title_back_icon"
style="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="15dp"
android:src="@mipmap/back"
android:tint="@color/colorTextGray" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:text="通知图标优化名单"
android:textColor="@color/colorTextGray"
android:textSize="25sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>

View File

@@ -210,9 +210,9 @@
<LinearLayout <LinearLayout
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="10dp"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:layout_marginRight="15dp" android:layout_marginRight="10dp"
android:background="@drawable/permotion_round" android:background="@drawable/permotion_round"
android:elevation="0dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
@@ -221,20 +221,37 @@
android:paddingRight="15dp"> android:paddingRight="15dp">
<com.fankes.miui.notify.view.MaterialSwitch <com.fankes.miui.notify.view.MaterialSwitch
android:id="@+id/chat_icon_fix_switch" android:id="@+id/notify_icon_fix_switch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="优化社交应用通知图标" android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:text="启用通知图标优化"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:id="@+id/config_notify_app_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:background="@drawable/button_round"
android:gravity="center"
android:padding="10dp"
android:singleLine="true"
android:text="配置通知图标优化名单"
android:textColor="@color/colorTextGray" android:textColor="@color/colorTextGray"
android:textSize="15sp" /> android:textSize="15sp" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.6" android:alpha="0.6"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="此选项默认开启,开启后将对 QQ 和微信的消息图标进行色彩修复,使得它们的图标看起来更加美观。" android:text="此选项默认开启,开启后将对优化名单内 APP 通知小图标进行色彩修复,使得它们的图标看起来更加美观。"
android:textColor="@color/colorTextDark" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>