24 Commits
1.0 ... 1.35

Author SHA1 Message Date
ea962e1520 Update version to 1.35,fix more bugs. 2022-03-06 03:24:21 +08:00
3e2b8247c8 支持导入数组和非数组格式的 JSON 自定义规则,并加入支持合并非全部覆盖的功能 2022-03-06 03:02:37 +08:00
e529e75bc2 Update YukiHookAPI 2022-03-06 01:15:29 +08:00
cd5f9806a3 Update YukiHookAPI 2022-03-06 01:05:09 +08:00
4d7891f769 Update version to 1.3,fix more bugs. 2022-03-05 00:59:20 +08:00
cd4b9794c4 更换系统版本识别方案 2022-03-05 00:52:10 +08:00
efbd919482 更新新的在线规则地址、加入自定义功能、更换系统版本识别方案 2022-03-05 00:08:34 +08:00
Fankesyooni
e297dc0b96 Merge pull request #5 from pzcn/patch-1
Use CDN
2022-03-04 15:14:55 +08:00
pzcn
42c817c90e Update ConfigureActivity.kt 2022-03-04 14:59:07 +08:00
fe31b79d4d Update README.md 2022-03-03 03:44:27 +08:00
c8b13ce602 Update version to 1.2,fix more bugs. 2022-03-02 03:25:24 +08:00
9e7b92fdaa Update version to 1.2,fix more bugs. 2022-03-02 03:25:14 +08:00
dd293a7e33 更新 YukiHookAPI 2022-03-02 03:24:03 +08:00
ddda41ab13 优化代码,加入保姆级别网络问题解答 2022-03-02 01:07:50 +08:00
Fankesyooni
0c5c2daf74 Update issue templates 2022-03-01 21:34:02 +08:00
2b2da7bdb5 去除圆角修复后的强制设置图标 2022-03-01 21:02:49 +08:00
3c07fb40d5 Update version to 1.1,fix more bugs. 2022-03-01 00:35:04 +08:00
21ff3938e3 Merge code 2022-03-01 00:27:20 +08:00
5fe1598546 替换系统的彩色图标为 Android 原生样式 2022-03-01 00:26:27 +08:00
018c137d85 优化深色模式的 Android 12 通知图标颜色 2022-02-28 22:07:34 +08:00
f066382d4c 尝试修复部分系统的问题,适配 ColorOS 11 2022-02-28 20:31:30 +08:00
c4f79452d0 修正文案 2022-02-28 20:02:31 +08:00
4bdb454d3c 修正文案 2022-02-28 20:02:12 +08:00
e77f9bf494 修复 ColorOS 11 无法识别版本号的问题 2022-02-28 20:01:40 +08:00
21 changed files with 567 additions and 118 deletions

View File

@@ -13,7 +13,7 @@ assignees: fankes
**系统类型(请保留一个)** **系统类型(请保留一个)**
* ColorOS/RealmeOS/OxygenOS * ColorOS/RealmeUI/OxygenOS
**Android 版本(必填)** **Android 版本(必填)**

2
.idea/misc.xml generated
View File

@@ -8,6 +8,8 @@
<entry key="app/src/main/res/layout/activity_main.xml" value="0.335" /> <entry key="app/src/main/res/layout/activity_main.xml" value="0.335" />
<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.4375" />
<entry key="app/src/main/res/layout/dia_icon_filter.xml" value="0.4375" /> <entry key="app/src/main/res/layout/dia_icon_filter.xml" value="0.4375" />
<entry key="app/src/main/res/layout/dia_source_from.xml" value="0.4375" />
<entry key="app/src/main/res/layout/dia_source_from_string.xml" value="0.4375" />
</map> </map>
</option> </option>
</component> </component>

View File

@@ -2,17 +2,17 @@
![Eclipse Marketplace](https://img.shields.io/badge/build-passing-brightgreen) ![Eclipse Marketplace](https://img.shields.io/badge/build-passing-brightgreen)
![Eclipse Marketplace](https://img.shields.io/badge/license-AGPL3.0-blue) ![Eclipse Marketplace](https://img.shields.io/badge/license-AGPL3.0-blue)
![Eclipse Marketplace](https://img.shields.io/badge/version-v1.0-green) ![Eclipse Marketplace](https://img.shields.io/badge/version-v1.35-green)
<br/><br/> <br/><br/>
<img src="https://github.com/fankes/ColorOSNotifyIcon/blob/master/app/src/main/ic_launcher-playstore.png" width = "100" height = "100"/> <img src="https://github.com/fankes/ColorOSNotifyIcon/blob/master/app/src/main/ic_launcher-playstore.png" width = "100" height = "100"/>
<br/> <br/>
Optimize notification icons for ColorOS and adapt to native notification icon specifications.<br/> Optimize notification icons for ColorOS and adapt to native notification icon specifications.<br/>
为 ColorOS 优化通知图标以及适配原生通知图标规范,理论支持 OxygenOS 和 RealmeOS 为 ColorOS 优化通知图标以及适配原生通知图标规范,理论支持 OxygenOS 和 RealmeUI
# 开始使用 # 开始使用
点击下载最新版本 点击下载最新版本
<a href='https://github.com/fankes/ColorOSNotifyIcon/releases'>![Eclipse Marketplace](https://img.shields.io/badge/download-v1.0-green)</a> <a href='https://github.com/fankes/ColorOSNotifyIcon/releases'>![Eclipse Marketplace](https://img.shields.io/badge/download-v1.35-green)</a>
<br/><br/> <br/><br/>
⚠️ 适配说明<br/> ⚠️ 适配说明<br/>
@@ -31,7 +31,7 @@ Optimize notification icons for ColorOS and adapt to native notification icon sp
此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方。<br/> 此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方。<br/>
- [Android 通知图标规范适配](https://github.com/fankes/AndroidNotifyIconAdapt) - [Android 通知图标规范适配计划](https://github.com/fankes/AndroidNotifyIconAdapt)
# 历史背景 # 历史背景

View File

@@ -60,13 +60,12 @@ tasks.whenTaskAdded {
} }
dependencies { 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.tiann:FreeReflection:3.1.0'
implementation "com.github.topjohnwu.libsu:core:3.1.2" implementation "com.github.topjohnwu.libsu:core:3.1.2"
implementation 'androidx.annotation:annotation:1.3.0' 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.2'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.2'
implementation 'com.geyifeng.immersionbar:immersionbar:3.2.0' implementation 'com.geyifeng.immersionbar:immersionbar:3.2.0'
implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.0' implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'com.squareup.okhttp3:okhttp:4.9.3'

View File

@@ -40,6 +40,8 @@
-keep class android.support** -keep class android.support**
-keep class androidx** -keep class androidx**
-keep class me.weishu**{*;}
-keep public class * extends android.app.Activity -keep public class * extends android.app.Activity
-keep public class * extends android.app.Application -keep public class * extends android.app.Application
-keep public class * extends android.app.Service -keep public class * extends android.app.Service

View File

@@ -18,7 +18,7 @@
android:value="true" /> android:value="true" />
<meta-data <meta-data
android:name="xposeddescription" android:name="xposeddescription"
android:value="为 ColorOS 优化通知图标以及适配原生通知图标规范,理论支持 OxygenOS 和 RealmeOS\n开发者酷安 @星夜不荟" /> android:value="为 ColorOS 优化通知图标以及适配原生通知图标规范,理论支持 OxygenOS 和 RealmeUI\n开发者酷安 @星夜不荟" />
<meta-data <meta-data
android:name="xposedminversion" android:name="xposedminversion"
android:value="93" /> android:value="93" />

View File

@@ -25,7 +25,9 @@
package com.fankes.coloros.notify.application package com.fankes.coloros.notify.application
import android.app.Application import android.app.Application
import android.content.Context
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import me.weishu.reflection.Reflection
class CNNApplication : Application() { class CNNApplication : Application() {
@@ -38,6 +40,12 @@ class CNNApplication : Application() {
val appContext get() = context ?: error("App is death") val appContext get() = context ?: error("App is death")
} }
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
/** 解锁隐藏 API */
Reflection.unseal(base)
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
/** 设置静态实例 */ /** 设置静态实例 */

View File

@@ -47,4 +47,13 @@ data class IconDataBean(
) : Serializable { ) : Serializable {
fun toEnabledName() = ("$appName$packageName").base64 + "_enable" fun toEnabledName() = ("$appName$packageName").base64 + "_enable"
fun toEnabledAllName() = ("$appName$packageName").base64 + "_enable_all" 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" +
" }"
} }

View File

@@ -33,5 +33,12 @@ object HookConst {
const val REMOVE_CHANGECP_NOTIFY = "_remove_charge_complete_notify" const val REMOVE_CHANGECP_NOTIFY = "_remove_charge_complete_notify"
const val NOTIFY_ICON_DATAS = "_notify_icon_datas" 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" const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui"
} }

View File

@@ -84,16 +84,18 @@ class HookEntry : YukiHookXposedInitProxy {
/** 原生存在的类 */ /** 原生存在的类 */
private const val IconManagerClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.icon.IconManager" private const val IconManagerClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.icon.IconManager"
/** ColorOS 存在的类 */ /** ColorOS 存在的类 - 旧版本不存在 */
private const val OplusContrastColorUtilClass = "com.oplusos.util.OplusContrastColorUtil" private const val OplusContrastColorUtilClass = "com.oplusos.util.OplusContrastColorUtil"
/** ColorOS 存在的类 */
private const val OplusPowerNotificationWarningsClass =
"com.oplusos.systemui.notification.power.OplusPowerNotificationWarnings"
/** ColorOS 存在的类 */ /** ColorOS 存在的类 */
private const val SystemPromptControllerClass = "com.oplusos.systemui.statusbar.policy.SystemPromptController" private const val SystemPromptControllerClass = "com.oplusos.systemui.statusbar.policy.SystemPromptController"
/** 根据多个版本存在不同的包名相同的类 */
private val OplusPowerNotificationWarningsClass = VariousClass(
"com.oplusos.systemui.notification.power.OplusPowerNotificationWarnings",
"com.coloros.systemui.notification.power.ColorosPowerNotificationWarnings"
)
/** 根据多个版本存在不同的包名相同的类 */ /** 根据多个版本存在不同的包名相同的类 */
private val ExpandableNotificationRowClass = VariousClass( private val ExpandableNotificationRowClass = VariousClass(
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.ExpandableNotificationRow", "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.ExpandableNotificationRow",
@@ -174,8 +176,12 @@ class HookEntry : YukiHookXposedInitProxy {
*/ */
private fun PackageParam.compatCustomIcon(isGrayscaleIcon: Boolean, packageName: String): Pair<Bitmap?, Int> { private fun PackageParam.compatCustomIcon(isGrayscaleIcon: Boolean, packageName: String): Pair<Bitmap?, Int> {
var customPair: Pair<Bitmap?, Int>? = null var customPair: Pair<Bitmap?, Int>? = null
if (prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)) when {
run { /** 替换系统图标为 Android 默认 */
(packageName == "android" || packageName == "com.android.systemui") && !isGrayscaleIcon -> customPair =
Pair(if (isUpperOfAndroidS) IconPackParams.android12IconBitmap else IconPackParams.android11IconBitmap, 0)
/** 替换自定义通知图标 */
prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true) -> run {
if (iconDatas.isNotEmpty()) if (iconDatas.isNotEmpty())
iconDatas.forEach { iconDatas.forEach {
if (packageName == it.packageName && isAppNotifyHookOf(it)) { if (packageName == it.packageName && isAppNotifyHookOf(it)) {
@@ -185,6 +191,7 @@ class HookEntry : YukiHookXposedInitProxy {
} }
} }
} }
}
return customPair ?: Pair(null, 0) return customPair ?: Pair(null, 0)
} }
@@ -228,23 +235,28 @@ class HookEntry : YukiHookXposedInitProxy {
val oldStyle = (if (context.isSystemInDarkMode) 0xffdcdcdc else 0xff707173).toInt() val oldStyle = (if (context.isSystemInDarkMode) 0xffdcdcdc else 0xff707173).toInt()
/** 新版风格 */ /** 新版风格 */
val newStyle = (if (context.isSystemInDarkMode) 0xff2d2d2d else Color.WHITE).toInt() val newStyle = (if (context.isSystemInDarkMode) 0xffdcdcdc else Color.WHITE).toInt()
/** 图标着色 */ /** 优化风格 */
val applyColor = customPair.second.takeIf { it != 0 } ?: iconColor.takeIf { it != 0 } ?: oldStyle val fixStyle = (if (context.isSystemInDarkMode) 0xff707173 else oldStyle).toInt()
/** 旧版图标着色 */
val oldApplyColor = customPair.second.takeIf { it != 0 } ?: iconColor.takeIf { it != 0 } ?: oldStyle
/** 新版图标着色 */
val newApplyColor = customPair.second.takeIf { it != 0 } ?: iconColor.takeIf { it != 0 } ?: fixStyle
/** 判断风格并开始 Hook */ /** 判断风格并开始 Hook */
if (isA12Style) { if (isA12Style) {
background = DrawableBuilder().rounded().solidColor(applyColor).build() background = DrawableBuilder().rounded().solidColor(newApplyColor).build()
setColorFilter(if (isA12Style) newStyle else oldStyle) setColorFilter(newStyle)
setPadding(2.dp(context), 2.dp(context), 2.dp(context), 2.dp(context)) setPadding(2.dp(context), 2.dp(context), 2.dp(context), 2.dp(context))
} else { } else {
background = null background = null
setColorFilter(applyColor) setColorFilter(oldApplyColor)
setPadding(0, 0, 0, 0) setPadding(0, 0, 0, 0)
} }
} }
else -> iconView.apply { else -> iconView.apply {
setImageDrawable(drawable)
setPadding(0, 0, 0, 0) setPadding(0, 0, 0, 0)
background = null background = null
colorFilter = null colorFilter = null
@@ -255,11 +267,19 @@ class HookEntry : YukiHookXposedInitProxy {
} }
} }
override fun onHook() = encase { override fun onHook() {
configs { runConfig()
debugTag = "ColorOSNotify" runHook()
isDebug = false }
}
/** 配置 Hook */
private fun runConfig() = configs {
debugTag = "ColorOSNotify"
isDebug = false
}
/** 开始 Hook */
private fun runHook() = encase {
loadApp(SYSTEMUI_PACKAGE_NAME) { loadApp(SYSTEMUI_PACKAGE_NAME) {
when { when {
/** 不是 ColorOS 系统停止 Hook */ /** 不是 ColorOS 系统停止 Hook */
@@ -289,11 +309,15 @@ class HookEntry : YukiHookXposedInitProxy {
} }
beforeHook { beforeHook {
/** 是否移除 */ /** 是否移除 */
if (firstArgs as Int == 7 && prefs.getBoolean(REMOVE_CHANGECP_NOTIFY, default = false)) resultNull() if (firstArgs as Int == 7 && prefs.getBoolean(
REMOVE_CHANGECP_NOTIFY,
default = false
)
) resultNull()
} }
} }
} }
/** 修复并替换 ColorOS 原生灰度图标色彩判断 */ /** 修复并替换新版本 ColorOS 原生灰度图标色彩判断*/
NotificationUtilsClass.hook { NotificationUtilsClass.hook {
injectMember { injectMember {
method { method {
@@ -301,7 +325,7 @@ class HookEntry : YukiHookXposedInitProxy {
param(ImageViewClass, OplusContrastColorUtilClass.clazz) param(ImageViewClass, OplusContrastColorUtilClass.clazz)
} }
replaceAny { (firstArgs as? ImageView?)?.let { isGrayscaleIcon(it.context, it.drawable) } } replaceAny { (firstArgs as? ImageView?)?.let { isGrayscaleIcon(it.context, it.drawable) } }
} }.ignoredHookingFailure()
} }
/** 替换状态栏图标 */ /** 替换状态栏图标 */
IconManagerClass.hook { IconManagerClass.hook {

View File

@@ -20,15 +20,16 @@
* *
* This file is Created by fankes on 2022/1/24. * This file is Created by fankes on 2022/1/24.
*/ */
@file:Suppress("MemberVisibilityCanBePrivate")
package com.fankes.coloros.notify.param package com.fankes.coloros.notify.param
import android.content.Context import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import com.fankes.coloros.notify.bean.IconDataBean import com.fankes.coloros.notify.bean.IconDataBean
import com.fankes.coloros.notify.hook.HookConst.NOTIFY_ICON_DATAS import com.fankes.coloros.notify.hook.HookConst.NOTIFY_ICON_DATAS
import com.fankes.coloros.notify.utils.bitmap import com.fankes.coloros.notify.utils.*
import com.fankes.coloros.notify.utils.safeOf
import com.fankes.coloros.notify.utils.safeOfNan
import com.highcapable.yukihookapi.hook.factory.modulePrefs import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.PackageParam
import org.json.JSONArray import org.json.JSONArray
@@ -43,11 +44,64 @@ import org.json.JSONObject
*/ */
class IconPackParams(private val context: Context? = null, private val param: PackageParam? = null) { class IconPackParams(private val context: Context? = null, private val param: PackageParam? = null) {
companion object {
/**
* Android 11 系统默认图标
* @return [Bitmap]
*/
val android11IconBitmap by lazy {
("iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAAAXNSR0IArs4c6QAAAARzQklUCAgI\n" +
"CHwIZIgAAAPkSURBVHic7ZvfVdswFMY/9fS93qDZoNmg6QS4E8AGdAMYgQ1gg4QJkk5AOkGcCUgm\n" +
"+Pog27EdCa7+WDIn/J4gx77W/SzpyvdKwCefdFE5H06yAPCj/vefUuqQqy1JhCA5B3AFYFH/NAdQ\n" +
"WC4/ANjWf28APCultpZrozGaECQbx0sAs0BzFYAVgI1S6jm0baNDsiB5R/KV4/FK8i63r1ZIXpPc\n" +
"jSjAkB3J69x+t5AsEwtgEqTMLcJjRgGGPOYQoCC5zu25gTV1SE4iwpx5h8J77KjDtRNO4ZPkAsAS\n" +
"9jXAVDgA+K2U2khvEAtRq7zG9EVoOAD4JV2MfZFcRD3uXHvCEcBfh+vf429tU0oBYMmYcwbJF+H4\n" +
"3FKH09ng/jnJG5IHh7F+qO+ZD2zN6mdshXbWsUSQhsh7ga2C5Epga0XBmyR5L2xbWGilVl6C04KG\n" +
"5MMbth6m0MbhQyRhcuVhtyBZGWxV9BjTlPWynavdxviNwLhXw2v7C4M95/hf27IJO+TGx7CkNzh1\n" +
"Y8Nzuo0PyjmQfBK019orbOHzD2Q5hMqr1Se6zse0ZWNGy6RuE+I24sOl98e09RZG386EoJ5dU60e\n" +
"xUvgiBQ0RBBTj1gYfkuB10Tpef+ZjyYhrvzbEsT3wPtdhDjzsSdEHb5CE62+zEPCJ4CfDrfMhs8a\n" +
"9oi8KS//j6RHuL/A3vAYCpFrfmiYAXj3m6VLPfH5vMDePaLP8MTckhT1DOq0/jJ6C+hejwjqQTQv\n" +
"sxteqUsEps/wK8pTA1b7XbtfB22bUvapAPAEACQBveZ4q1ToY7+lHRpMlf31Z4HIL6rrc3eOCF3Q\n" +
"fERan6c4WWahK8TopfcJ0vrcCpFzk0Yuuj5f8tDolQaGQsSsQ0yd3lRwyT2ix1CIHImSXPSy70Mh\n" +
"nFPzH5jeS+8JURdM90mbk4f9sDhsmiMuoVec+WgS4hLmiTMfz4RQSq3gVn4PIUci6Fj72MMWPqUV\n" +
"rJSZ51jIq3OU1xJDS36p92K5V9MoKwLvGLcIPDZuReBOYyW9wjlnSHmROSb+tVXm2SgyFmGlCsrK\n" +
"7aRgozh1T1iO56uVpyAROg5shA98oc4wmzaTXXPcXfs2ROsi0T5L6glxC7f6ZHMAJWfRaA9gLkk6\n" +
"uW443QD4FtCwlBwBLKJuOAXaD7IS6VadIRwBlKMegarHuySs5qKiZ1XdR4yC8gk0JRvmKFRRHlpT\n" +
"ECdEBohRMu9QqZj7KFMX6m+TlIJU9P12SAH1RnGXXfiuHCjYAO/KmAdgS5wOwIZuFNvjdAB2lFRi\n" +
"yiPRjTCATsjYFmZHnIovjfMf90i0BOpQ18T87SXWXz+ZKv8BVnFXPlKejoIAAAAASUVORK5CYII=").bitmap
}
/**
* Android 12 系统默认图标
* @return [Bitmap]
*/
val android12IconBitmap by lazy {
("iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAAAXNSR0IArs4c6QAAAARzQklUCAgI\n" +
"CHwIZIgAAANkSURBVHic7ZvNkRoxEIWfXL4vIbBVvpsMTAhksIRgRwAZsBnAxWfjCMARGM4+MBkw\n" +
"RPB8kKh1zagljUYzwlv6qrZ2a0fo50lq9agboFAoFApeVI5GSX4xf07NDwDUAE4AbkqpU45+9Ybk\n" +
"hOTK8/yF5JbklX6uJH+QfPG0uyI5ST+iCMwgf5sBbC3PVoGDd4nSGrARlabtvGI0RLizNc9eegrQ\n" +
"5EJy0RDhTj4xBBHu/EkoQGjdvcT40EOLKYBn4dmnHvX6kOp+xpvh7UyvU4PkDMARwFOHj50B7KBP\n" +
"iEopVZmZnOHtFFkA+NyhzhuAedbThuSMZB2wpHckg2eM5JzkMaDem5mQ/JD86ehoTXLeo+6lR+jv\n" +
"KccSjZk5iRMTWHP6V1200MmgPtYGE+GfdlxiXFK1E9u5pdCxmh3sQYf2Jg4xlqnb69IxaTUsBmxz\n" +
"LbR5GKpNX4dmQoeOI7RdCW1Hnx59HCrJQL02/0FySnJD8kDtHovbJrDsumOfhoN297oWyjZnUDRu\n" +
"oWVptxX7VOMLRliaO0s56XhtLeOOZfe2grHj6bM1bFSJ63OR//KGsqG0nhZsL3dRsNCyJBehqyeE\n" +
"jzEfAiA5SlYbAW3EvkK/WFWQjV2XshWAX96eDoljL+d3dSNJbSP+W4oQhiKEoQhhiD01JDYUvMsR\n" +
"+RZzZZdaiEe4Mou6Aylbw1CEMBQhDLE2okZu91bbgi6xj/dJaje/bA1DEcJQhDAUIQxRp4a5BdpY\n" +
"HkW5t49AHxfbZp2nGO8uUQoJRN2bRm0Nx6yP+a5hFUIpNZ4QDrILMTq0xxWuI7ZvS1SL3pZ9VoQt\n" +
"xjnhCFFp6rCB7XW7FWAaHMqxjcFzFajjosliGik6dBI65Ipb9G1TCuyMGWVrdUpKFLlygARQ6ki5\n" +
"lMSaL1HEdE7KVbikXKp0J7jmd+LoTia7phDDIwL5KBE2kq8eMZxZ9p6653TndI9/UkiYGZMM551D\n" +
"l5mjtgfN5PMmydKUkn1xhdo4HuG/PjsB2EO/E1QAzkqpmnoLPUF7p0v4vdQzdNpx7jhKm8CVkYKk\n" +
"OZyDYMTYDSjC49iEEKgdH+lojaFmbl8hFurVsWZY9r5LgDUffSuEQu2F7gNFqam313IsAXJ93XEG\n" +
"/fZ4/w3o06QGAKXU4Nm7hUKhUIjgL/9/6dhvvYPfAAAAAElFTkSuQmCC").bitmap
}
}
/** /**
* 已存储的 JSON 数据 * 已存储的 JSON 数据
* @return [String] * @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 +113,32 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
if (it.isNotBlank()) runCatching { if (it.isNotBlank()) runCatching {
JSONArray(it).also { array -> JSONArray(it).also { array ->
for (i in 0 until array.length()) runCatching { for (i in 0 until array.length()) runCatching {
(array.get(i) as JSONObject).apply { add(convertToBean(array.get(i) as JSONObject)!!)
add( }.onFailure { context?.snake(msg = "部分规则加载失败") }
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")
)
)
}
}
} }
} }.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 * @param dataJson1 图标数据 JSON
@@ -88,6 +149,34 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
dataJson1.replace(oldValue = "]", newValue = "") + "," + dataJson2.replace(oldValue = "[", newValue = "") 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 * @param dataJson 图标数据 JSON

View File

@@ -25,8 +25,6 @@
package com.fankes.coloros.notify.ui package com.fankes.coloros.notify.ui
import android.app.ProgressDialog import android.app.ProgressDialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@@ -36,8 +34,14 @@ import android.widget.ListView
import android.widget.TextView import android.widget.TextView
import androidx.constraintlayout.utils.widget.ImageFilterView import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import com.fankes.coloros.notify.R import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.bean.IconDataBean import com.fankes.coloros.notify.bean.IconDataBean
import com.fankes.coloros.notify.hook.HookConst.SOURCE_SYNC_WAY
import com.fankes.coloros.notify.hook.HookConst.SOURCE_SYNC_WAY_CUSTOM_URL
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_1
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_2
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_3
import com.fankes.coloros.notify.hook.factory.isAppNotifyHookAllOf import com.fankes.coloros.notify.hook.factory.isAppNotifyHookAllOf
import com.fankes.coloros.notify.hook.factory.isAppNotifyHookOf import com.fankes.coloros.notify.hook.factory.isAppNotifyHookOf
import com.fankes.coloros.notify.hook.factory.putAppNotifyHookAllOf import com.fankes.coloros.notify.hook.factory.putAppNotifyHookAllOf
@@ -46,7 +50,9 @@ import com.fankes.coloros.notify.param.IconPackParams
import com.fankes.coloros.notify.ui.base.BaseActivity import com.fankes.coloros.notify.ui.base.BaseActivity
import com.fankes.coloros.notify.utils.* import com.fankes.coloros.notify.utils.*
import com.fankes.coloros.notify.view.MaterialSwitch import com.fankes.coloros.notify.view.MaterialSwitch
import com.google.android.material.radiobutton.MaterialRadioButton
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
class ConfigureActivity : BaseActivity() { class ConfigureActivity : BaseActivity() {
@@ -187,21 +193,23 @@ class ConfigureActivity : BaseActivity() {
lateinit var switchOpen: MaterialSwitch lateinit var switchOpen: MaterialSwitch
lateinit var switchAll: 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) } } onScrollEvent = { post { setSelection(if (it) iconDatas.lastIndex else 0) } }
} }
/** 设置点击事件 */ /** 设置点击事件 */
findViewById<View>(R.id.config_cbr_button).setOnClickListener { findViewById<View>(R.id.config_cbr_button).setOnClickListener {
runCatching { openBrowser(url = "https://github.com/fankes/AndroidNotifyIconAdapt/blob/main/CONTRIBUTING.md")
startActivity(Intent().apply {
action = "android.intent.action.VIEW"
data = Uri.parse("https://github.com/fankes/AndroidNotifyIconAdapt/blob/main/CONTRIBUTING.md")
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.onFailure {
toast(msg = "无法启动系统默认浏览器")
}
} }
/** 装载数据 */ /** 装载数据 */
mockLocalData() mockLocalData()
@@ -218,16 +226,119 @@ class ConfigureActivity : BaseActivity() {
/** 首次进入或更新数据 */ /** 首次进入或更新数据 */
private fun onStartRefresh() = private fun onStartRefresh() =
showDialog { showDialog {
title = if (iconAllDatas.isNotEmpty()) "同步列表" else "初始化" title = "同步列表"
msg = (if (iconAllDatas.isNotEmpty()) "建议定期从云端拉取数据以获得最新的通知图标优化名单适配数据。\n\n" var sourceType = modulePrefs.getInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_1)
else "首次装载需要从云端下载最新适配数据,后续可继续前往这里检查更新。\n\n") + var customUrl = modulePrefs.getString(SOURCE_SYNC_WAY_CUSTOM_URL)
"通过从 Github 同步最新数据,无法连接可能需要魔法上网。" addView(R.layout.dia_source_from).apply {
confirmButton(text = "开始同步") { onRefreshing() } 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() 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() { * 开始更新数据
* @param url
*/
private fun onRefreshing(url: String) = ClientRequestTool.checkingInternetConnect(context = this) {
ProgressDialog(this).apply { ProgressDialog(this).apply {
setDefaultStyle(context = this@ConfigureActivity) setDefaultStyle(context = this@ConfigureActivity)
setCancelable(false) setCancelable(false)
@@ -237,28 +348,75 @@ class ConfigureActivity : BaseActivity() {
}.also { }.also {
ClientRequestTool.wait( ClientRequestTool.wait(
context = this, context = this,
url = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main/OS/ColorOS/NotifyIconsSupportConfig.json" url = "$url/OS/ColorOS/NotifyIconsSupportConfig.json"
) { isDone1, ctOS -> ) { isDone1, ctOS ->
it.setMessage("正在同步 APP 数据") it.setMessage("正在同步 APP 数据")
ClientRequestTool.wait( ClientRequestTool.wait(
context = this, context = this,
url = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main/APP/NotifyIconsSupportConfig.json" url = "$url/APP/NotifyIconsSupportConfig.json"
) { isDone2, ctAPP -> ) { isDone2, ctAPP ->
it.cancel() it.cancel()
IconPackParams(context = this).also { params -> IconPackParams(context = this).also { params ->
if (isDone1 && isDone2) params.splicingJsonArray(ctOS, ctAPP).also { if (isDone1 && isDone2) params.splicingJsonArray(ctOS, ctAPP).also {
if (params.isCompareDifferent(it)) { when {
params.save(it) params.isHackString(it) -> snake(msg = "请求需要验证,请尝试魔法上网或关闭魔法")
params.isNotVaildJson(it) -> snake(msg = "在线规则发生问题,请稍后重试")
params.isCompareDifferent(it) -> {
params.save(it)
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this)
}
else -> snake(msg = "列表数据已是最新")
}
} else showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n${if (!isDone1) ctOS else ctAPP}"
confirmButton(text = "解决方案") {
openBrowser(url = "https://www.baidu.com/s?wd=github%2Braw%2B%E6%97%A0%E6%B3%95%E8%AE%BF%E9%97%AE")
}
cancelButton()
}
}
}
}
}
}
/**
* 开始更新数据
* @param url
*/
private fun onRefreshingCustom(url: String) = ClientRequestTool.checkingInternetConnect(context = this) {
ProgressDialog(this).apply {
setDefaultStyle(context = this@ConfigureActivity)
setCancelable(false)
setTitle("同步中")
setMessage("正在通过自定义地址同步数据")
show()
}.also {
ClientRequestTool.wait(
context = this,
url = url
) { isDone, content ->
it.cancel()
IconPackParams(context = this).also { params ->
if (isDone)
when {
params.isHackString(content) -> snake(msg = "请求需要验证,请尝试魔法上网或关闭魔法")
params.isNotVaildJson(content) -> snake(msg = "目标地址不是有效的 JSON 数据")
params.isCompareDifferent(content) -> {
params.save(content)
filterText = "" filterText = ""
mockLocalData() mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this) SystemUITool.showNeedUpdateApplySnake(context = this)
} else snake(msg = "列表数据已是最新")
} else
showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n${if (!isDone1) ctOS else ctAPP}"
confirmButton(text = "我知道了")
} }
else -> snake(msg = "列表数据已是最新")
}
else showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n$content"
confirmButton(text = "我知道了")
} }
} }
} }
@@ -286,4 +444,4 @@ class ConfigureActivity : BaseActivity() {
else iconAllDatas.filter { else iconAllDatas.filter {
it.appName.lowercase().contains(filterText.lowercase()) || it.packageName.lowercase().contains(filterText.lowercase()) it.appName.lowercase().contains(filterText.lowercase()) || it.packageName.lowercase().contains(filterText.lowercase())
} }
} }

View File

@@ -27,7 +27,6 @@ package com.fankes.coloros.notify.ui
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
@@ -61,7 +60,7 @@ class MainActivity : BaseActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
/** 设置文本 */ /** 设置文本 */
findViewById<TextView>(R.id.main_text_version).text = "当前版本:$moduleVersion" findViewById<TextView>(R.id.main_text_version).text = "模块版本:$moduleVersion"
findViewById<TextView>(R.id.main_text_coloros_version).text = "系统版本:$colorOSVersion" findViewById<TextView>(R.id.main_text_coloros_version).text = "系统版本:$colorOSVersion"
when { when {
/** 判断是否为 ColorOS 系统 */ /** 判断是否为 ColorOS 系统 */
@@ -147,10 +146,12 @@ class MainActivity : BaseActivity() {
devNotifyConfigSwitch.setOnCheckedChangeListener { btn, b -> devNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(REMOVE_DEV_NOTIFY, b) modulePrefs.putBoolean(REMOVE_DEV_NOTIFY, b)
SystemUITool.showNeedRestartSnake(context = this)
} }
crcpNotifyConfigSwitch.setOnCheckedChangeListener { btn, b -> crcpNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(REMOVE_CHANGECP_NOTIFY, b) modulePrefs.putBoolean(REMOVE_CHANGECP_NOTIFY, b)
SystemUITool.showNeedRestartSnake(context = this)
} }
a12StyleConfigSwitch.setOnCheckedChangeListener { btn, b -> a12StyleConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
@@ -163,30 +164,11 @@ class MainActivity : BaseActivity() {
findViewById<View>(R.id.title_restart_icon).setOnClickListener { SystemUITool.restartSystemUI(context = this) } findViewById<View>(R.id.title_restart_icon).setOnClickListener { SystemUITool.restartSystemUI(context = this) }
/** 恰饭! */ /** 恰饭! */
findViewById<View>(R.id.link_with_follow_me).setOnClickListener { findViewById<View>(R.id.link_with_follow_me).setOnClickListener {
runCatching { openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market")
startActivity(Intent().apply {
setPackage("com.coolapk.market")
action = "android.intent.action.VIEW"
data = Uri.parse("https://www.coolapk.com/u/876977")
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.onFailure {
toast(msg = "你可能没有安装酷安")
}
} }
/** 项目地址点击事件 */ /** 项目地址点击事件 */
findViewById<View>(R.id.link_with_project_address).setOnClickListener { findViewById<View>(R.id.link_with_project_address).setOnClickListener {
runCatching { openBrowser(url = "https://github.com/fankes/ColorOSNotifyIcon")
startActivity(Intent().apply {
action = "android.intent.action.VIEW"
data = Uri.parse("https://github.com/fankes/ColorOSNotifyIcon")
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.onFailure {
toast(msg = "无法启动系统默认浏览器")
}
} }
} }
} }

View File

@@ -20,11 +20,15 @@
* *
* This file is Created by fankes on 2022/2/25. * This file is Created by fankes on 2022/2/25.
*/ */
@file:Suppress("TrustAllX509TrustManager", "CustomX509TrustManager") @file:Suppress("TrustAllX509TrustManager", "CustomX509TrustManager", "DEPRECATION")
package com.fankes.coloros.notify.utils package com.fankes.coloros.notify.utils
import android.app.Activity import android.app.Activity
import android.app.ProgressDialog
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import com.highcapable.yukihookapi.hook.log.loggerD import com.highcapable.yukihookapi.hook.log.loggerD
import okhttp3.* import okhttp3.*
import java.io.IOException import java.io.IOException
@@ -37,13 +41,45 @@ import javax.net.ssl.*
*/ */
object ClientRequestTool { object ClientRequestTool {
/**
* 检查网络连接情况
* @param context 实例
* @param it 已连接回调
*/
fun checkingInternetConnect(context: Activity, it: () -> Unit) =
ProgressDialog(context).apply {
setDefaultStyle(context)
setCancelable(false)
setTitle("准备中")
setMessage("正在检查网络连接情况")
}.apply {
wait(context, url = "https://www.baidu.com") { isDone, _ ->
cancel()
if (isDone) it() else
context.showDialog {
title = "网络不可用"
msg = "无法连接到互联网,请检查你当前的设备是否可以上网,且没有在手机管家中禁用本模块的联网权限。"
confirmButton(text = "检查设置") {
runCatching {
context.startActivity(Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", context.packageName, null)
})
}.onFailure { context.snake(msg = "启动应用信息页面失败") }
}
cancelButton()
}
}
}.show()
/** /**
* 发送 GET 请求内容并等待 * 发送 GET 请求内容并等待
* @param context 实例 * @param context 实例
* @param url 请求地址 * @param url 请求地址
* @param it 回调 - ([Boolean] 是否成功,[String] 成功的内容或失败消息) * @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 { OkHttpClient().newBuilder().apply {
SSLSocketClient.sSLSocketFactory?.let { sslSocketFactory(it, SSLSocketClient.trustManager) } SSLSocketClient.sSLSocketFactory?.let { sslSocketFactory(it, SSLSocketClient.trustManager) }
hostnameVerifier(SSLSocketClient.hostnameVerifier) hostnameVerifier(SSLSocketClient.hostnameVerifier)
@@ -62,7 +98,7 @@ object ClientRequestTool {
context.runOnUiThread { it(true, bodyString) } context.runOnUiThread { it(true, bodyString) }
} }
}) })
} }.onFailure { it(false, "URL 无效") }
/** /**
* 自动信任 SSL 证书 * 自动信任 SSL 证书

View File

@@ -26,7 +26,10 @@ package com.fankes.coloros.notify.utils
import android.app.Activity import android.app.Activity
import android.app.AlertDialog import android.app.AlertDialog
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
@@ -34,17 +37,20 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.net.Uri
import android.os.Build import android.os.Build
import android.util.Base64 import android.util.Base64
import android.widget.Toast import android.widget.Toast
import com.fankes.coloros.notify.application.CNNApplication.Companion.appContext import com.fankes.coloros.notify.application.CNNApplication.Companion.appContext
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.factory.classOf import com.highcapable.yukihookapi.hook.factory.classOf
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.hasClass import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.loggerE import com.highcapable.yukihookapi.hook.log.loggerE
import com.highcapable.yukihookapi.hook.type.java.StringType import com.highcapable.yukihookapi.hook.type.java.StringType
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import java.io.ByteArrayOutputStream
/** /**
* 系统深色模式是否开启 * 系统深色模式是否开启
@@ -101,9 +107,12 @@ inline val isNotColorOS get() = !isColorOS
*/ */
val colorOSVersion val colorOSVersion
get() = safeOf(default = "无法获取") { get() = safeOf(default = "无法获取") {
findPropString(key = "ro.system.build.fingerprint", default = "无法获取") (classOf(name = "com.oplus.os.OplusBuild").let {
.split("ossi:")[1] it.field { name = "VERSIONS" }.ignoredError().of<Array<String>>()
.split("/")[0].trim() ?.get((it.method { name = "getOplusOSVERSION" }.ignoredError().get().invoke<Int>() ?: 23) - 1)
} ?: findPropString(key = "ro.system.build.fingerprint", default = "无法获取")
.split("ssi:")[1]
.split("/")[0].trim()) + " ${Build.DISPLAY}"
} }
/** /**
@@ -150,6 +159,17 @@ val Number.dp get() = (toFloat() * appContext.resources.displayMetrics.density).
*/ */
fun Number.dp(context: Context) = (toFloat() * context.resources.displayMetrics.density).toInt() fun Number.dp(context: Context) = (toFloat() * context.resources.displayMetrics.density).toInt()
/**
* Base64 加密
* @return [String]
*/
val Bitmap.base64
get() = safeOfNothing {
val baos = ByteArrayOutputStream()
compress(Bitmap.CompressFormat.PNG, 100, baos)
Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT)
}
/** /**
* Base64 加密 * Base64 加密
* @return [String] * @return [String]
@@ -233,6 +253,39 @@ fun Context.snake(msg: String, actionText: String = "", it: () -> Unit = {}) =
setAction(actionText) { it() } setAction(actionText) { it() }
}.show() }.show()
/**
* 启动系统浏览器
* @param url 网址
* @param packageName 指定包名 - 可不填
*/
fun Context.openBrowser(url: String, packageName: String = "") =
runCatching {
startActivity(Intent().apply {
if (packageName.isNotBlank()) setPackage(packageName)
action = Intent.ACTION_VIEW
data = Uri.parse(url)
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.onFailure {
if (packageName.isNotBlank())
snake(msg = "启动 $packageName 失败")
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 回调 - 如果异常为空 * @param it 回调 - 如果异常为空

View File

@@ -149,7 +149,7 @@
android:divider="@color/trans" android:divider="@color/trans"
android:dividerHeight="15dp" android:dividerHeight="15dp"
android:fadingEdgeLength="10dp" android:fadingEdgeLength="10dp"
android:listSelector="@null" android:listSelector="@color/trans"
android:padding="15dp" android:padding="15dp"
android:requiresFadingEdge="vertical" android:requiresFadingEdge="vertical"
android:scrollbars="none" /> android:scrollbars="none" />

View File

@@ -86,7 +86,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:alpha="0.8" android:alpha="0.8"
android:text="当前版本:%1" android:text="模块版本:%1"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="13sp" /> android:textSize="13sp" />
@@ -95,7 +95,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:alpha="0.8" android:alpha="0.8"
android:text="ColorOS 版本:%1" android:text="系统版本:%1"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="13sp" /> android:textSize="13sp" />
</LinearLayout> </LinearLayout>
@@ -447,7 +447,7 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.8" android:alpha="0.8"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="Q.哪些是已知问题?\nA.暂时只对 ColorOS 12 做了适配,其它版本情况未知,请自行进行测试。" android:text="Q.哪些是已知问题?\nA.问题如下:\n1.由于机型有限,仅对 ColorOS 12~12.1 测试正常运行,根据酷友的需要云调试修复 ColorOS 11 的问题,其它版本情况未知,请自行进行测试。\n2.OxygenOS 只支持 12 版本,其它类 ColorOS 魔改的 UI 理论没有问题,请自行测试。"
android:textColor="@color/colorTextDark" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />

View File

@@ -5,6 +5,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/bg_permotion_round" android:background="@drawable/bg_permotion_round"
android:baselineAligned="false" android:baselineAligned="false"
android:descendantFocusability="blocksDescendants"
android:gravity="center|start" android:gravity="center|start"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="15dp" android:padding="15dp"

View 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>

View 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>

View File

@@ -5,8 +5,8 @@ plugins {
} }
ext { ext {
appVersionName = "1.0" appVersionName = "1.35"
appVersionCode = 1 appVersionCode = 5
} }
task clean(type: Delete) { task clean(type: Delete) {