添加新应用安装后自动提醒适配通知图标功能

This commit is contained in:
2022-03-21 03:33:07 +08:00
parent ee4da89104
commit 6bd3a5a78a
9 changed files with 251 additions and 22 deletions

View File

@@ -50,7 +50,7 @@
<activity
android:name=".ui.activity.ConfigureActivity"
android:exported="false"
android:exported="true"
android:screenOrientation="behind" />
</application>
</manifest>

View File

@@ -29,6 +29,7 @@ object HookConst {
const val ENABLE_HIDE_ICON = "_hide_icon"
const val ENABLE_ANDROID12_STYLE = "_notify_android12_style"
const val ENABLE_NOTIFY_ICON_FIX = "_notify_icon_fix"
const val ENABLE_NOTIFY_ICON_FIX_NOTIFY = "_notify_icon_fix_notify"
const val REMOVE_DEV_NOTIFY = "_remove_dev_notify"
const val REMOVE_CHANGECP_NOTIFY = "_remove_charge_complete_notify"
const val REMOVE_DNDALERT_NOTIFY = "_remove_dndalert_notify"

View File

@@ -23,6 +23,7 @@
package com.fankes.coloros.notify.hook
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.Drawable
@@ -36,6 +37,7 @@ import com.fankes.coloros.notify.hook.HookConst.ENABLE_ANDROID12_STYLE
import com.fankes.coloros.notify.hook.HookConst.ENABLE_MODULE
import com.fankes.coloros.notify.hook.HookConst.ENABLE_MODULE_LOG
import com.fankes.coloros.notify.hook.HookConst.ENABLE_NOTIFY_ICON_FIX
import com.fankes.coloros.notify.hook.HookConst.ENABLE_NOTIFY_ICON_FIX_NOTIFY
import com.fankes.coloros.notify.hook.HookConst.REMOVE_CHANGECP_NOTIFY
import com.fankes.coloros.notify.hook.HookConst.REMOVE_DEV_NOTIFY
import com.fankes.coloros.notify.hook.HookConst.REMOVE_DNDALERT_NOTIFY
@@ -45,6 +47,7 @@ import com.fankes.coloros.notify.hook.factory.isAppNotifyHookOf
import com.fankes.coloros.notify.param.IconPackParams
import com.fankes.coloros.notify.utils.drawable.drawabletoolbox.DrawableBuilder
import com.fankes.coloros.notify.utils.factory.*
import com.fankes.coloros.notify.utils.tool.IconAdaptationTool
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.factory.configs
@@ -54,10 +57,7 @@ import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.loggerD
import com.highcapable.yukihookapi.hook.log.loggerW
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.IconClass
import com.highcapable.yukihookapi.hook.type.android.ImageViewClass
import com.highcapable.yukihookapi.hook.type.android.*
import com.highcapable.yukihookapi.hook.type.java.BooleanType
import com.highcapable.yukihookapi.hook.type.java.IntType
import com.highcapable.yukihookapi.hook.type.java.LongType
@@ -89,6 +89,9 @@ class HookEntry : YukiHookXposedInitProxy {
/** ColorOS 存在的类 - 旧版本不存在 */
private const val OplusContrastColorUtilClass = "com.oplusos.util.OplusContrastColorUtil"
/** 原生存在的类 */
private const val PluginManagerImplClass = "$SYSTEMUI_PACKAGE_NAME.shared.plugins.PluginManagerImpl"
/** 根据多个版本存在不同的包名相同的类 */
private val SystemPromptControllerClass = VariousClass(
"com.oplusos.systemui.statusbar.policy.SystemPromptController",
@@ -129,15 +132,6 @@ class HookEntry : YukiHookXposedInitProxy {
/** 缓存的通知优化图标数组 */
private var iconDatas = ArrayList<IconDataBean>()
/**
* 获取推送通知的应用名称
* @param packageName APP 包名
* @return [String]
*/
private fun Context.findAppName(packageName: String) = safeOf(default = "<unknown>") {
packageManager.getPackageInfo(packageName, 0).applicationInfo.loadLabel(packageManager)
}
/**
* 打印日志
* @param tag 标识
@@ -403,6 +397,38 @@ class HookEntry : YukiHookXposedInitProxy {
}
}
}
/** 发送适配新的 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 ?: ""
)
}
}
}
}
}
}
}
}

View File

@@ -181,17 +181,37 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
/** 装载数据 */
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() {

View File

@@ -38,6 +38,7 @@ 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_LOG
import com.fankes.coloros.notify.hook.HookConst.ENABLE_NOTIFY_ICON_FIX
import com.fankes.coloros.notify.hook.HookConst.ENABLE_NOTIFY_ICON_FIX_NOTIFY
import com.fankes.coloros.notify.hook.HookConst.REMOVE_CHANGECP_NOTIFY
import com.fankes.coloros.notify.hook.HookConst.REMOVE_DEV_NOTIFY
import com.fankes.coloros.notify.hook.HookConst.REMOVE_DNDALERT_NOTIFY
@@ -128,6 +129,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.a12StyleConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
binding.notifyIconConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, 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.devNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_DEV_NOTIFY, default = true)
binding.crcpNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_CHANGECP_NOTIFY, default = false)
binding.dndNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_DNDALERT_NOTIFY, default = false)
@@ -136,6 +138,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.moduleEnableLogSwitch.isChecked = modulePrefs.getBoolean(ENABLE_MODULE_LOG, default = false)
binding.hideIconInLauncherSwitch.isChecked = modulePrefs.getBoolean(ENABLE_HIDE_ICON)
binding.notifyIconFixSwitch.isChecked = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)
binding.notifyIconFixNotifySwitch.isChecked = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY, default = true)
binding.moduleEnableSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_MODULE, b)
@@ -163,6 +166,12 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
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)
}
binding.devNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->

View File

@@ -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.coloros.notify.application.CNNApplication.Companion.appContext
import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.factory.classOf
@@ -170,6 +171,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]

View File

@@ -0,0 +1,129 @@
/*
* 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/3/20.
*/
package com.fankes.coloros.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.coloros.notify.BuildConfig
import com.fankes.coloros.notify.hook.HookEntry
import com.fankes.coloros.notify.utils.factory.bitmap
import com.fankes.coloros.notify.utils.factory.findAppIcon
import com.fankes.coloros.notify.utils.factory.findAppName
import com.fankes.coloros.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())
}
}

View File

@@ -48,7 +48,6 @@ 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.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
@@ -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)

View File

@@ -392,6 +392,36 @@
android:text="此选项默认开启,开启后将对优化名单内的 APP 通知小图标使用单色调进行修复,特别是通过系统级别推送的通知,它们始终是 APP 默认图标(彩色的 APP 图标),修复后使得它们的图标看起来更加符合 Android 原生的统一规范。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<LinearLayout
android:id="@+id/notify_icon_fix_notify_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.fankes.coloros.notify.ui.view.MaterialSwitch
android:id="@+id/notify_icon_fix_notify_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="5dp"
android:text="提醒未适配通知图标的新安装应用"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="此选项默认开启,在通知图标优化名单有数据时将自动检查新安装的应用是否存在适配数据,若没有将会发送通知提醒,如果你了解日后新安装的应用通知图标是遵守原生单色图标规范的,可以关闭此提醒。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout