diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fb91119..b684820 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,7 +53,7 @@ \ No newline at end of file diff --git a/app/src/main/java/com/fankes/miui/notify/hook/HookConst.kt b/app/src/main/java/com/fankes/miui/notify/hook/HookConst.kt index 965004d..151c6e8 100644 --- a/app/src/main/java/com/fankes/miui/notify/hook/HookConst.kt +++ b/app/src/main/java/com/fankes/miui/notify/hook/HookConst.kt @@ -32,6 +32,7 @@ object HookConst { const val ENABLE_COLOR_ICON_HOOK = "_color_icon_hook" const val ENABLE_COLOR_ICON_COMPAT = "_color_icon_compat" const val ENABLE_NOTIFY_ICON_FIX = "_notify_icon_fix" + const val ENABLE_NOTIFY_ICON_FIX_NOTIFY = "_notify_icon_fix_notify" const val ENABLE_HOOK_STATUS_ICON_COUNT = "_enable_hook_status_icon_count" const val NOTIFY_ICON_DATAS = "_notify_icon_datas" const val HOOK_STATUS_ICON_COUNT = "_hook_status_icon_count" diff --git a/app/src/main/java/com/fankes/miui/notify/hook/HookEntry.kt b/app/src/main/java/com/fankes/miui/notify/hook/HookEntry.kt index 6c2eadb..31755fc 100644 --- a/app/src/main/java/com/fankes/miui/notify/hook/HookEntry.kt +++ b/app/src/main/java/com/fankes/miui/notify/hook/HookEntry.kt @@ -24,6 +24,7 @@ package com.fankes.miui.notify.hook import android.app.NotificationManager import android.content.Context +import android.content.Intent import android.graphics.Bitmap import android.graphics.Color import android.graphics.Outline @@ -43,6 +44,7 @@ import com.fankes.miui.notify.hook.HookConst.ENABLE_HOOK_STATUS_ICON_COUNT import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE_LOG import com.fankes.miui.notify.hook.HookConst.ENABLE_NOTIFY_ICON_FIX +import com.fankes.miui.notify.hook.HookConst.ENABLE_NOTIFY_ICON_FIX_NOTIFY import com.fankes.miui.notify.hook.HookConst.HOOK_STATUS_ICON_COUNT import com.fankes.miui.notify.hook.HookConst.SYSTEMUI_PACKAGE_NAME import com.fankes.miui.notify.hook.factory.isAppNotifyHookAllOf @@ -51,6 +53,7 @@ import com.fankes.miui.notify.params.IconPackParams import com.fankes.miui.notify.utils.drawable.drawabletoolbox.DrawableBuilder import com.fankes.miui.notify.utils.factory.* import com.fankes.miui.notify.utils.tool.BitmapCompatTool +import com.fankes.miui.notify.utils.tool.IconAdaptationTool import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed import com.highcapable.yukihookapi.hook.bean.VariousClass import com.highcapable.yukihookapi.hook.factory.* @@ -60,6 +63,7 @@ import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.type.android.ContextClass import com.highcapable.yukihookapi.hook.type.android.DrawableClass import com.highcapable.yukihookapi.hook.type.android.ImageViewClass +import com.highcapable.yukihookapi.hook.type.android.IntentClass import com.highcapable.yukihookapi.hook.type.java.BooleanType import com.highcapable.yukihookapi.hook.type.java.IntType import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy @@ -85,6 +89,9 @@ class HookEntry : YukiHookXposedInitProxy { /** 原生存在的类 */ private const val NotificationIconContainerClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.phone.NotificationIconContainer" + /** 原生存在的类 */ + private const val PluginManagerImplClass = "$SYSTEMUI_PACKAGE_NAME.shared.plugins.PluginManagerImpl" + /** 根据多个版本存在不同的包名相同的类 */ private val ExpandableNotificationRowClass = VariousClass( "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.ExpandableNotificationRow", @@ -187,9 +194,7 @@ class HookEntry : YukiHookXposedInitProxy { * @param context 实例 * @return [String] */ - private fun StatusBarNotification.findAppName(context: Context) = safeOf(default = "") { - context.packageManager.getPackageInfo(opPkgName, 0).applicationInfo.loadLabel(context.packageManager) - } + private fun StatusBarNotification.findAppName(context: Context) = context.findAppName(opPkgName) /** * 获取通知栏、状态栏 APP 图标 @@ -663,6 +668,38 @@ class HookEntry : YukiHookXposedInitProxy { intercept() }.ignoredNoSuchMemberFailure() }.ignoredHookClassNotFoundFailure() + /** 发送适配新的 APP 图标通知 */ + PluginManagerImplClass.hook { + injectMember { + method { + name = "onReceive" + param(ContextClass, IntentClass) + } + afterHook { + if (prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true) && + prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY, default = true) + ) (lastArgs as? Intent)?.also { + if (!it.action.equals(Intent.ACTION_PACKAGE_REPLACED) && + it.getBooleanExtra(Intent.EXTRA_REPLACING, false) + ) return@also + when (it.action) { + Intent.ACTION_PACKAGE_ADDED -> + it.data?.schemeSpecificPart?.also { newPkgName -> + if (iconDatas.takeIf { e -> e.isNotEmpty() } + ?.filter { e -> e.packageName == newPkgName } + .isNullOrEmpty() + ) IconAdaptationTool.pushNewAppSupportNotify(firstArgs as Context, newPkgName) + } + Intent.ACTION_PACKAGE_REMOVED -> + IconAdaptationTool.removeNewAppSupportNotify( + firstArgs as Context, + packageName = it.data?.schemeSpecificPart ?: "" + ) + } + } + } + } + } } } } diff --git a/app/src/main/java/com/fankes/miui/notify/ui/activity/ConfigureActivity.kt b/app/src/main/java/com/fankes/miui/notify/ui/activity/ConfigureActivity.kt index cad86f8..3a4a508 100644 --- a/app/src/main/java/com/fankes/miui/notify/ui/activity/ConfigureActivity.kt +++ b/app/src/main/java/com/fankes/miui/notify/ui/activity/ConfigureActivity.kt @@ -181,17 +181,37 @@ class ConfigureActivity : BaseActivity() { /** 装载数据 */ mockLocalData() /** 更新数据 */ - if (intent?.getBooleanExtra("isShowUpdDialog", true) == true) onStartRefresh() + when { + intent?.getBooleanExtra("isNewAppSupport", false) == true -> + showDialog { + val appName = intent?.getStringExtra("appName") ?: "" + val pkgName = intent?.getStringExtra("pkgName") ?: "" + title = "新安装应用通知图标适配" + msg = "你已安装 $appName($pkgName)\n\n" + + "此应用未在通知优化名单中发现适配数据,若此应用发送的通知为彩色图标," + + "可随时点击本页面下方的“贡献通知图标优化名单”按钮提交贡献或请求适配。\n\n" + + "你可以现在立即同步适配列表,以获取最新的适配数据。" + confirmButton(text = "同步列表") { onStartRefresh() } + cancelButton(text = "复制名称+包名") { copyToClipboard(content = "$appName($pkgName)") } + neutralButton(text = "取消") + noCancelable() + } + intent?.getBooleanExtra("isShowUpdDialog", true) == true -> onStartRefresh() + } + /** 清除数据 */ + intent?.apply { + removeExtra("isNewAppSupport") + removeExtra("isShowUpdDialog") + } } /** 开始手动同步 */ - private fun onStartRefresh() { + private fun onStartRefresh() = IconRuleManagerTool.syncByHand(context = this) { filterText = "" mockLocalData() SystemUITool.showNeedUpdateApplySnake(context = this) } - } /** 装载或刷新本地数据 */ private fun mockLocalData() { diff --git a/app/src/main/java/com/fankes/miui/notify/ui/activity/MainActivity.kt b/app/src/main/java/com/fankes/miui/notify/ui/activity/MainActivity.kt index 2852224..5b8f5ba 100644 --- a/app/src/main/java/com/fankes/miui/notify/ui/activity/MainActivity.kt +++ b/app/src/main/java/com/fankes/miui/notify/ui/activity/MainActivity.kt @@ -42,6 +42,7 @@ import com.fankes.miui.notify.hook.HookConst.ENABLE_HOOK_STATUS_ICON_COUNT import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE_LOG import com.fankes.miui.notify.hook.HookConst.ENABLE_NOTIFY_ICON_FIX +import com.fankes.miui.notify.hook.HookConst.ENABLE_NOTIFY_ICON_FIX_NOTIFY import com.fankes.miui.notify.hook.HookConst.HOOK_STATUS_ICON_COUNT import com.fankes.miui.notify.params.IconPackParams import com.fankes.miui.notify.ui.activity.base.BaseActivity @@ -152,6 +153,7 @@ class MainActivity : BaseActivity() { binding.notifyIconConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true) && modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true) binding.notifyIconFixButton.isVisible = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true) + binding.notifyIconFixNotifyItem.isVisible = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true) binding.statusIconCountSwitch.isChecked = modulePrefs.getBoolean(ENABLE_HOOK_STATUS_ICON_COUNT, default = true) binding.statusIconCountChildItem.isVisible = modulePrefs.getBoolean(ENABLE_HOOK_STATUS_ICON_COUNT, default = true) binding.moduleEnableSwitch.isChecked = modulePrefs.getBoolean(ENABLE_MODULE, default = true) @@ -160,6 +162,7 @@ class MainActivity : BaseActivity() { binding.colorIconHookSwitch.isChecked = modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true) binding.colorIconCompatSwitch.isChecked = modulePrefs.getBoolean(ENABLE_COLOR_ICON_COMPAT) binding.notifyIconFixSwitch.isChecked = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true) + binding.notifyIconFixNotifySwitch.isChecked = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY, default = true) binding.statusIconCountText.text = statusBarIconCount.toString() binding.moduleEnableSwitch.setOnCheckedChangeListener { btn, b -> if (!btn.isPressed) return@setOnCheckedChangeListener @@ -207,6 +210,12 @@ class MainActivity : BaseActivity() { if (!btn.isPressed) return@setOnCheckedChangeListener modulePrefs.putBoolean(ENABLE_NOTIFY_ICON_FIX, b) binding.notifyIconFixButton.isVisible = b + binding.notifyIconFixNotifyItem.isVisible = b + SystemUITool.showNeedRestartSnake(context = this) + } + binding.notifyIconFixNotifySwitch.setOnCheckedChangeListener { btn, b -> + if (!btn.isPressed) return@setOnCheckedChangeListener + modulePrefs.putBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY, b) SystemUITool.showNeedRestartSnake(context = this) } /** 通知图标优化名单按钮点击事件 */ diff --git a/app/src/main/java/com/fankes/miui/notify/utils/factory/FunctionFactory.kt b/app/src/main/java/com/fankes/miui/notify/utils/factory/FunctionFactory.kt index 77f4f28..12c12c4 100644 --- a/app/src/main/java/com/fankes/miui/notify/utils/factory/FunctionFactory.kt +++ b/app/src/main/java/com/fankes/miui/notify/utils/factory/FunctionFactory.kt @@ -43,6 +43,7 @@ import android.util.Base64 import android.widget.Toast import androidx.core.app.NotificationManagerCompat import androidx.core.content.getSystemService +import androidx.core.graphics.drawable.toBitmap import com.fankes.miui.notify.application.MNNApplication.Companion.appContext import com.google.android.material.snackbar.Snackbar import com.highcapable.yukihookapi.hook.factory.classOf @@ -217,6 +218,22 @@ val Context.versionName get() = packageInfo.versionName ?: "" */ val Context.versionCode get() = packageInfo.versionCode +/** + * 得到 APP 名称 + * @param name APP 包名 + * @return [String] + */ +fun Context.findAppName(name: String) = + safeOfNothing { packageManager.getPackageInfo(name, 0).applicationInfo.loadLabel(packageManager).toString() } + +/** + * 得到 APP 图标 + * @param name APP 包名 + * @return [Bitmap] or null + */ +fun Context.findAppIcon(name: String) = + safeOfNull { packageManager.getPackageInfo(name, 0).applicationInfo.loadIcon(packageManager).toBitmap() } + /** * 是否关闭了通知权限 * @return [Boolean] diff --git a/app/src/main/java/com/fankes/miui/notify/utils/tool/IconAdaptationTool.kt b/app/src/main/java/com/fankes/miui/notify/utils/tool/IconAdaptationTool.kt new file mode 100644 index 0000000..f37ca1b --- /dev/null +++ b/app/src/main/java/com/fankes/miui/notify/utils/tool/IconAdaptationTool.kt @@ -0,0 +1,129 @@ +/* + * MIUINativeNotifyIcon - Fix the native notification bar icon function abandoned by the MIUI development team. + * Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) + * https://github.com/fankes/MIUINativeNotifyIcon + * + * This software is non-free but opensource software: you can redistribute it + * and/or modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + *

+ * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * and eula along with this software. If not, see + * + * + * This file is Created by fankes on 2022/3/21. + */ +package com.fankes.miui.notify.utils.tool + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.drawable.Icon +import android.os.Build +import com.fankes.miui.notify.BuildConfig +import com.fankes.miui.notify.hook.HookEntry +import com.fankes.miui.notify.utils.factory.bitmap +import com.fankes.miui.notify.utils.factory.findAppIcon +import com.fankes.miui.notify.utils.factory.findAppName +import com.fankes.miui.notify.utils.factory.runInSafe + +/** + * 通知图标适配推送通知类 + * + * 这个类需要在 [HookEntry] 中调用 + */ +object IconAdaptationTool { + + /** 当前模块的包名 */ + private const val PACKAGE_NAME = BuildConfig.APPLICATION_ID + + /** + * 使用的小图标 + * @return [Bitmap] + */ + private val smallIcon by lazy { + ("iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ix\n" + + "AAAAAXNSR0IArs4c6QAAA7RJREFUaEPtmk2oVVUUx3//qYUDsaDUEISeqKEogVGkVjyzw\n" + + "GwmTlREJRSESupJolJRlNCgSUGkSPDImaL5hWip4GehEmFUKERSVgNJHS5Zsq8+r8/7zt\n" + + "pnn0uICw53cNfa6//b5569117ninvEdI9w0BiImQ0DetI1EZgC/AF8D/zkl6RLpSayERA\n" + + "zew14G3hsCKEfSuorAVMcxMx2AC8HxJ0FXpD0VyDmDteiIGZmNcQ8KulibnwxEDN7H1iT\n" + + "KwT4UdKk3PgiIGY2F9iWK2JA3FpJ7+WMUxvEzB4ATgPjgPPABuAgsA5YlCGqV9K+aFwJk\n" + + "CeB4ynxBknrWyLM7B9gRFDU65I+CcbU30fMzGd9U0q8VNIXA0B+B0YFRX0paUkwpgjIR8\n" + + "DqlPgbYIWk822AEV3HJE2PBLhviZ/WTuCltsT+rIyNikn+VyQ9GI0tAfI5sCyauIP/z5K\n" + + "8tAlZCZDlwGehrJ2d+yUtiI5XAuRZ4Nto4g7+ayR9EB2vBMhDQK06qU30PEnhzbU2iIsw\n" + + "M59Br3br2h5JL+YMUgQkwZwBnsgRMSBmjCTfe8JWEuRh4M+wglsBCyVtyY0vBpLuyiOA1\n" + + "0l+IoyYn0f2RwLafYuCtAY3s3eAdysI2wy8IenfCr4dXRoBSXdnNuClxlPpGp6U/AAc9k\n" + + "vS1roArfjGQNoFmtlU4IIkr4iLW20QM/OZ9lmfAYxPz8hJSSeKq+0wYC0QM3sTWAWMHiT\n" + + "HUaBPkh+yGrdsEDPbBVTZvNZL8lNjo5YFYmYrgU8DyiZI8qZcYxYGMbP5QH9QUXbpUTVP\n" + + "Dogfa3OaCuMlnasqLOqXA+KNBm84RO0VSdujQVX9c0D+A7wFFLW3JPn5vhHLAfGfx+MZa\n" + + "hZIij5bldPkgLgYf+CjNlWSlyeNWA6IN+C8ixgxP2P0SLoaCYr4hkF8cDM7AMwMJFotaW\n" + + "PAP+yaBZJgqr5C2C1pTkuZmb0KTE612V7glCT/rGXZIAnGXyP464TB7BqwWNLXAyBOAtM\n" + + "Gcf7Km9+SfsmlqQWSYLzeeh54LnXkvwMOAb6b+zn+hpnZHqC3g9Bdkto7lpW5aoNUyWRm\n" + + "vjjc7NJ3iLmtm19l7JZPt0CqLg4HJc2KAHQbpOrCgKSsyc0Kis5YYLn+39+RjwE/TQ5lG\n" + + "yW13rUM5Xvb9926IyOBX4FWJ2UwkZd91ZP0d4ggOXcFJC2/T6c20N10PiPpSA6Ex3QNJM\n" + + "FMAPzFkP+1w6/fAC8kl9dtE3UVJHe2q8TdB6kyS930uQ7NEhVCNv0J7AAAAABJRU5ErkJ\n" + + "ggg==").bitmap + } + + /** + * 推送新 APP 安装适配通知 + * @param context 实例 + * @param packageName 安装的 APP 包名 + */ + fun pushNewAppSupportNotify(context: Context, packageName: String) { + context.getSystemService(NotificationManager::class.java)?.apply { + createNotificationChannel( + NotificationChannel( + "notifyRuleAdaptId", "通知图标优化适配", + NotificationManager.IMPORTANCE_DEFAULT + ).apply { enableLights(false) } + ) + notify(packageName.hashCode(), Notification.Builder(context, "notifyRuleAdaptId").apply { + setShowWhen(true) + setContentTitle("您已安装 ${context.findAppName(packageName)}") + setContentText("尚未适配此应用,点按打开在线规则。") + setColor(0xFF2993F0.toInt()) + setAutoCancel(true) + setSmallIcon(Icon.createWithBitmap(smallIcon)) + setLargeIcon(context.findAppIcon(packageName)) + setContentIntent( + PendingIntent.getActivity( + context, packageName.hashCode(), + Intent().apply { + component = ComponentName( + PACKAGE_NAME, + "$PACKAGE_NAME.ui.activity.ConfigureActivity" + ) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + }.apply { + putExtra("isNewAppSupport", true) + putExtra("appName", context.findAppName(packageName)) + putExtra("pkgName", packageName) + }, if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE + ) + ) + }.build()) + } + } + + /** + * 检测 APP 卸载后移除相关通知 + * @param context 实例 + * @param packageName 卸载的 APP 包名 + */ + fun removeNewAppSupportNotify(context: Context, packageName: String) = runInSafe { + context.getSystemService(NotificationManager::class.java)?.cancel(packageName.hashCode()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fankes/miui/notify/utils/tool/IconRuleManagerTool.kt b/app/src/main/java/com/fankes/miui/notify/utils/tool/IconRuleManagerTool.kt index b72265a..3b27c2c 100644 --- a/app/src/main/java/com/fankes/miui/notify/utils/tool/IconRuleManagerTool.kt +++ b/app/src/main/java/com/fankes/miui/notify/utils/tool/IconRuleManagerTool.kt @@ -48,7 +48,6 @@ 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.params.IconPackParams import com.fankes.miui.notify.ui.activity.ConfigureActivity -import com.fankes.miui.notify.utils.factory.openBrowser import com.fankes.miui.notify.utils.factory.safeOfNull import com.fankes.miui.notify.utils.factory.showDialog import com.fankes.miui.notify.utils.factory.snake @@ -218,9 +217,7 @@ object IconRuleManagerTool { 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") - } + confirmButton(text = "再试一次") { syncByHand(context, it) } cancelButton() } else -> pushNotify(context, title = "同步地址不可用", msg = if (!isDone1) ctOS else ctAPP) diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 6fbd83b..373e396 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -447,6 +447,36 @@ android:text="此选项默认开启,开启后将对优化名单内的 APP 通知小图标使用单色调进行修复,特别是通过 MIPUSH 推送的通知,它们始终是 APP 默认图标(彩色的 APP 图标),修复后使得它们的图标看起来更加符合 Android 原生的统一规范。" android:textColor="@color/colorTextDark" android:textSize="12sp" /> + + + + + + +