19 Commits
1.2 ... 1.36

Author SHA1 Message Date
3c1a4798d0 Update version to 1.36,fix more bugs. 2022-03-13 02:06:36 +08:00
9faab60aa8 加入多版本适配支持 2022-03-13 02:02:09 +08:00
74eb55ab39 增加移除免打扰通知功能 2022-03-13 01:59:47 +08:00
f7e75931ed Merge code 2022-03-13 00:30:25 +08:00
856c1c5cab Merge code 2022-03-13 00:30:00 +08:00
fcbf3b0565 Merge code 2022-03-13 00:28:30 +08:00
fa3cddd75f Merge code 2022-03-13 00:23:09 +08:00
d11dfe5167 Merge remote-tracking branch 'origin/master' 2022-03-13 00:22:13 +08:00
b3be2789a9 Merge code 2022-03-13 00:21:55 +08:00
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
25 changed files with 588 additions and 203 deletions

2
.idea/gradle.xml generated
View File

@@ -7,13 +7,13 @@
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="Embedded JDK" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>

4
.idea/misc.xml generated
View File

@@ -8,10 +8,12 @@
<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/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>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@@ -2,7 +2,7 @@
![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/version-v1.2-green)
![Eclipse Marketplace](https://img.shields.io/badge/version-v1.36-green)
<br/><br/>
<img src="https://github.com/fankes/ColorOSNotifyIcon/blob/master/app/src/main/ic_launcher-playstore.png" width = "100" height = "100"/>
<br/>
@@ -12,7 +12,7 @@ Optimize notification icons for ColorOS and adapt to native notification icon sp
# 开始使用
点击下载最新版本
<a href='https://github.com/fankes/ColorOSNotifyIcon/releases'>![Eclipse Marketplace](https://img.shields.io/badge/download-v1.2-green)</a>
<a href='https://github.com/fankes/ColorOSNotifyIcon/releases'>![Eclipse Marketplace](https://img.shields.io/badge/download-v1.36-green)</a>
<br/><br/>
⚠️ 适配说明<br/>
@@ -31,7 +31,7 @@ Optimize notification icons for ColorOS and adapt to native notification icon sp
此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方。<br/>
- [Android 通知图标规范适配](https://github.com/fankes/AndroidNotifyIconAdapt)
- [Android 通知图标规范适配计划](https://github.com/fankes/AndroidNotifyIconAdapt)
# 历史背景

View File

@@ -60,13 +60,12 @@ tasks.whenTaskAdded {
}
dependencies {
compileOnly 'de.robv.android.xposed:api:82'
implementation 'com.highcapable.yukihookapi:api:1.0.4'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.4'
implementation 'com.github.tiann:FreeReflection:3.1.0'
implementation "com.github.topjohnwu.libsu:core:3.1.2"
implementation 'androidx.annotation:annotation:1.3.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
compileOnly 'de.robv.android.xposed:api:82'
implementation 'com.highcapable.yukihookapi:api:1.0.3'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.3'
implementation 'com.geyifeng.immersionbar:immersionbar:3.2.0'
implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.3'

View File

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

View File

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

View File

@@ -23,7 +23,7 @@
package com.fankes.coloros.notify.bean
import android.graphics.Bitmap
import com.fankes.coloros.notify.utils.base64
import com.fankes.coloros.notify.utils.factory.base64
import java.io.Serializable
/**
@@ -47,4 +47,13 @@ data class IconDataBean(
) : Serializable {
fun toEnabledName() = ("$appName$packageName").base64 + "_enable"
fun toEnabledAllName() = ("$appName$packageName").base64 + "_enable_all"
override fun toString() = "{\n" +
" \"appName\": \"$appName\",\n" +
" \"packageName\": \"$packageName\",\n" +
" \"iconBitmap\": \"${iconBitmap.base64}\",\n" +
" \"iconColor\": \"#${Integer.toHexString(iconColor)}\",\n" +
" \"contributorName\": \"$contributorName\",\n" +
" \"isEnabled\": $isEnabled,\n" +
" \"isEnabledAll\": $isEnabledAll\n" +
" }"
}

View File

@@ -31,7 +31,15 @@ object HookConst {
const val ENABLE_NOTIFY_ICON_FIX = "_notify_icon_fix"
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"
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"
}

View File

@@ -38,12 +38,13 @@ 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.REMOVE_CHANGECP_NOTIFY
import com.fankes.coloros.notify.hook.HookConst.REMOVE_DEV_NOTIFY
import com.fankes.coloros.notify.hook.HookConst.REMOVE_DNDALERT_NOTIFY
import com.fankes.coloros.notify.hook.HookConst.SYSTEMUI_PACKAGE_NAME
import com.fankes.coloros.notify.hook.factory.isAppNotifyHookAllOf
import com.fankes.coloros.notify.hook.factory.isAppNotifyHookOf
import com.fankes.coloros.notify.param.IconPackParams
import com.fankes.coloros.notify.utils.*
import com.fankes.coloros.notify.utils.drawable.drawabletoolbox.DrawableBuilder
import com.fankes.coloros.notify.utils.factory.*
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.factory.configs
@@ -59,6 +60,7 @@ import com.highcapable.yukihookapi.hook.type.android.IconClass
import com.highcapable.yukihookapi.hook.type.android.ImageViewClass
import com.highcapable.yukihookapi.hook.type.java.BooleanType
import com.highcapable.yukihookapi.hook.type.java.IntType
import com.highcapable.yukihookapi.hook.type.java.LongType
import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy
@InjectYukiHookWithXposed
@@ -87,8 +89,17 @@ class HookEntry : YukiHookXposedInitProxy {
/** ColorOS 存在的类 - 旧版本不存在 */
private const val OplusContrastColorUtilClass = "com.oplusos.util.OplusContrastColorUtil"
/** ColorOS 存在的类 */
private const val SystemPromptControllerClass = "com.oplusos.systemui.statusbar.policy.SystemPromptController"
/** 根据多个版本存在不同的包名相同的类 */
private val SystemPromptControllerClass = VariousClass(
"com.oplusos.systemui.statusbar.policy.SystemPromptController",
"com.coloros.systemui.statusbar.policy.SystemPromptController"
)
/** 根据多个版本存在不同的包名相同的类 */
private val DndAlertHelperClass = VariousClass(
"com.oplusos.systemui.notification.helper.DndAlertHelper",
"com.coloros.systemui.notification.helper.DndAlertHelper"
)
/** 根据多个版本存在不同的包名相同的类 */
private val OplusPowerNotificationWarningsClass = VariousClass(
@@ -267,11 +278,19 @@ class HookEntry : YukiHookXposedInitProxy {
}
}
override fun onHook() = encase {
configs {
override fun onHook() {
runConfig()
runHook()
}
/** 配置 Hook */
private fun runConfig() = configs {
debugTag = "ColorOSNotify"
isDebug = false
}
/** 开始 Hook */
private fun runHook() = encase {
loadApp(SYSTEMUI_PACKAGE_NAME) {
when {
/** 不是 ColorOS 系统停止 Hook */
@@ -305,6 +324,19 @@ class HookEntry : YukiHookXposedInitProxy {
}
}
}
/** 移除免打扰通知 */
DndAlertHelperClass.hook {
injectMember {
method {
name = "sendNotificationWithEndtime"
param(LongType)
}
beforeHook {
/** 是否移除 */
if (prefs.getBoolean(REMOVE_DNDALERT_NOTIFY, default = false)) resultNull()
}
}
}
/** 修复并替换新版本 ColorOS 原生灰度图标色彩判断*/
NotificationUtilsClass.hook {
injectMember {

View File

@@ -20,6 +20,8 @@
*
* This file is Created by fankes on 2022/1/24.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.fankes.coloros.notify.param
import android.content.Context
@@ -27,9 +29,7 @@ import android.graphics.Bitmap
import android.graphics.Color
import com.fankes.coloros.notify.bean.IconDataBean
import com.fankes.coloros.notify.hook.HookConst.NOTIFY_ICON_DATAS
import com.fankes.coloros.notify.utils.bitmap
import com.fankes.coloros.notify.utils.safeOf
import com.fankes.coloros.notify.utils.safeOfNan
import com.fankes.coloros.notify.utils.factory.*
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.param.PackageParam
import org.json.JSONArray
@@ -101,7 +101,7 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
* 已存储的 JSON 数据
* @return [String]
*/
private val storageDataJson get() = (context?.modulePrefs ?: param?.prefs)?.getString(NOTIFY_ICON_DATAS)
internal val storageDataJson get() = (context?.modulePrefs ?: param?.prefs)?.getString(NOTIFY_ICON_DATAS)
/**
* 获取图标数据
@@ -113,22 +113,29 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
if (it.isNotBlank()) runCatching {
JSONArray(it).also { array ->
for (i in 0 until array.length()) runCatching {
(array.get(i) as JSONObject).apply {
add(
add(convertToBean(array.get(i) as JSONObject)!!)
}.onFailure { context?.snake(msg = "部分规则加载失败") }
}
}.onFailure { context?.snake(msg = "规则加载发生错误") }
}
}
/**
* 转换为 [IconDataBean]
* @param jsonObject Json 实例
* @return [IconDataBean] or null
*/
private fun convertToBean(jsonObject: JSONObject) = safeOfNull {
jsonObject.let {
IconDataBean(
appName = getString("appName"),
packageName = getString("packageName"),
isEnabled = getBoolean("isEnabled"),
isEnabledAll = getBoolean("isEnabledAll"),
iconBitmap = getString("iconBitmap").bitmap,
iconColor = safeOfNan { Color.parseColor(getString("iconColor")) },
contributorName = getString("contributorName")
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")
)
)
}
}
}
}
}
}
@@ -142,6 +149,34 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
dataJson1.replace(oldValue = "]", newValue = "") + "," + dataJson2.replace(oldValue = "[", newValue = "")
}
/**
* 是否不为合法 JSON
* @param json 数据
* @return [Boolean]
*/
fun isNotVaildJson(json: String) = !isJsonArray(json) && !isJsonObject(json)
/**
* 是否为 JSON 数组
* @param json 数据
* @return [Boolean]
*/
fun isJsonArray(json: String) = json.trim().let { it.startsWith("[") && it.endsWith("]") }
/**
* 是否为 JSON 实例
* @param json 数据
* @return [Boolean]
*/
fun isJsonObject(json: String) = json.trim().let { it.startsWith("{") && it.endsWith("}") }
/**
* 是否为异常地址
* @param json 数据
* @return [Boolean]
*/
fun isHackString(json: String) = json.contains(other = "Checking your browser before accessing")
/**
* 比较图标数据不相等
* @param dataJson 图标数据 JSON

View File

@@ -34,24 +34,31 @@ import android.widget.ListView
import android.widget.TextView
import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import com.fankes.coloros.notify.R
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.isAppNotifyHookOf
import com.fankes.coloros.notify.hook.factory.putAppNotifyHookAllOf
import com.fankes.coloros.notify.hook.factory.putAppNotifyHookOf
import com.fankes.coloros.notify.param.IconPackParams
import com.fankes.coloros.notify.ui.base.BaseActivity
import com.fankes.coloros.notify.utils.*
import com.fankes.coloros.notify.utils.factory.*
import com.fankes.coloros.notify.utils.tool.ClientRequestTool
import com.fankes.coloros.notify.utils.tool.SystemUITool
import com.fankes.coloros.notify.view.MaterialSwitch
import com.google.android.material.radiobutton.MaterialRadioButton
import com.google.android.material.textfield.TextInputEditText
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
class ConfigureActivity : BaseActivity() {
/** 访问请求链接 */
private var rawGithubUrl = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main"
/** 当前筛选条件 */
private var filterText = ""
@@ -188,7 +195,18 @@ class ConfigureActivity : BaseActivity() {
lateinit var switchOpen: MaterialSwitch
lateinit var switchAll: MaterialSwitch
}
}.apply { onChanged = { notifyDataSetChanged() } }
}.apply {
setOnItemLongClickListener { _, _, p, _ ->
showDialog {
title = "复制“${iconDatas[p].appName}”的规则"
msg = "是否复制单条规则到剪贴板?"
confirmButton { copyToClipboard(iconDatas[p].toString()) }
cancelButton()
}
true
}
onChanged = { notifyDataSetChanged() }
}
onScrollEvent = { post { setSelection(if (it) iconDatas.lastIndex else 0) } }
}
/** 设置点击事件 */
@@ -210,16 +228,119 @@ class ConfigureActivity : BaseActivity() {
/** 首次进入或更新数据 */
private fun onStartRefresh() =
showDialog {
title = if (iconAllDatas.isNotEmpty()) "同步列表" else "初始化"
msg = (if (iconAllDatas.isNotEmpty()) "建议定期从云端拉取数据以获得最新的通知图标优化名单适配数据。\n\n"
else "首次装载需要从云端下载最新适配数据,后续可继续前往这里检查更新。\n\n") +
"通过从 Github 同步最新数据,无法连接可能需要魔法上网。"
confirmButton(text = "开始同步") { onRefreshing() }
title = "同步列表"
var sourceType = modulePrefs.getInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_1)
var customUrl = modulePrefs.getString(SOURCE_SYNC_WAY_CUSTOM_URL)
addView(R.layout.dia_source_from).apply {
val radio1 = findViewById<MaterialRadioButton>(R.id.dia_sf_rd1)
val radio2 = findViewById<MaterialRadioButton>(R.id.dia_sf_rd2)
val radio3 = findViewById<MaterialRadioButton>(R.id.dia_sf_rd3)
val edLin = findViewById<View>(R.id.dia_sf_text_lin)
findViewById<TextInputEditText>(R.id.dia_sf_text).apply {
if (customUrl.isNotBlank()) {
setText(customUrl)
setSelection(customUrl.length)
}
doOnTextChanged { text, _, _, _ ->
customUrl = text.toString()
modulePrefs.putString(SOURCE_SYNC_WAY_CUSTOM_URL, text.toString())
}
}
edLin.isVisible = sourceType == TYPE_SOURCE_SYNC_WAY_3
radio1.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_1
radio2.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_2
radio3.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_3
radio1.setOnClickListener {
radio2.isChecked = false
radio3.isChecked = false
edLin.isVisible = false
sourceType = TYPE_SOURCE_SYNC_WAY_1
modulePrefs.putInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_1)
}
radio2.setOnClickListener {
radio1.isChecked = false
radio3.isChecked = false
edLin.isVisible = false
sourceType = TYPE_SOURCE_SYNC_WAY_2
modulePrefs.putInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_2)
}
radio3.setOnClickListener {
radio1.isChecked = false
radio2.isChecked = false
edLin.isVisible = true
sourceType = TYPE_SOURCE_SYNC_WAY_3
modulePrefs.putInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_3)
}
}
confirmButton {
when (sourceType) {
TYPE_SOURCE_SYNC_WAY_1 -> onRefreshing(url = "https://raw.fastgit.org/fankes/AndroidNotifyIconAdapt/main")
TYPE_SOURCE_SYNC_WAY_2 -> onRefreshing(url = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main")
TYPE_SOURCE_SYNC_WAY_3 ->
if (customUrl.isNotBlank())
if (customUrl.startsWith("http://") || customUrl.startsWith("https://"))
onRefreshingCustom(customUrl)
else snake(msg = "同步地址不是一个合法的 URL")
else snake(msg = "同步地址不能为空")
else -> snake(msg = "同步类型错误")
}
}
cancelButton()
neutralButton(text = "自定义规则") {
showDialog {
title = "自定义规则"
var editText: TextInputEditText
addView(R.layout.dia_source_from_string).apply {
editText = findViewById<TextInputEditText>(R.id.dia_sfs_input_edit).apply {
requestFocus()
invalidate()
}
}
IconPackParams(context = this@ConfigureActivity).also { params ->
confirmButton(text = "合并") {
editText.text.toString().also { jsonString ->
when {
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> snake(msg = "不是有效的 JSON 数据")
jsonString.isNotBlank() -> {
params.save(
params.splicingJsonArray(
dataJson1 = params.storageDataJson ?: "[]",
dataJson2 = jsonString.takeIf { params.isJsonArray(it) } ?: "[$jsonString]"
)
)
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this@ConfigureActivity)
}
else -> snake(msg = "请输入有效内容")
}
}
}
cancelButton(text = "覆盖") {
editText.text.toString().also { jsonString ->
when {
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> snake(msg = "不是有效的 JSON 数据")
jsonString.isNotBlank() -> {
params.save(dataJson = jsonString.takeIf { params.isJsonArray(it) } ?: "[$jsonString]")
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this@ConfigureActivity)
}
else -> snake(msg = "请输入有效内容")
}
}
}
}
neutralButton(text = "取消")
}
}
}
/** 开始更新数据 */
private fun onRefreshing() = ClientRequestTool.checkingInternetConnect(context = this) {
/**
* 开始更新数据
* @param url
*/
private fun onRefreshing(url: String) = ClientRequestTool.checkingInternetConnect(context = this) {
ProgressDialog(this).apply {
setDefaultStyle(context = this@ConfigureActivity)
setCancelable(false)
@@ -229,22 +350,27 @@ class ConfigureActivity : BaseActivity() {
}.also {
ClientRequestTool.wait(
context = this,
url = "$rawGithubUrl/OS/ColorOS/NotifyIconsSupportConfig.json"
url = "$url/OS/ColorOS/NotifyIconsSupportConfig.json"
) { isDone1, ctOS ->
it.setMessage("正在同步 APP 数据")
ClientRequestTool.wait(
context = this,
url = "$rawGithubUrl/APP/NotifyIconsSupportConfig.json"
url = "$url/APP/NotifyIconsSupportConfig.json"
) { isDone2, ctAPP ->
it.cancel()
IconPackParams(context = this).also { params ->
if (isDone1 && isDone2) params.splicingJsonArray(ctOS, ctAPP).also {
if (params.isCompareDifferent(it)) {
when {
params.isHackString(it) -> snake(msg = "请求需要验证,请尝试魔法上网或关闭魔法")
params.isNotVaildJson(it) -> snake(msg = "在线规则发生问题,请稍后重试")
params.isCompareDifferent(it) -> {
params.save(it)
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this)
} else snake(msg = "列表数据已是最新")
}
else -> snake(msg = "列表数据已是最新")
}
} else showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n${if (!isDone1) ctOS else ctAPP}"
@@ -259,6 +385,46 @@ class ConfigureActivity : BaseActivity() {
}
}
/**
* 开始更新数据
* @param url
*/
private fun onRefreshingCustom(url: String) = ClientRequestTool.checkingInternetConnect(context = this) {
ProgressDialog(this).apply {
setDefaultStyle(context = this@ConfigureActivity)
setCancelable(false)
setTitle("同步中")
setMessage("正在通过自定义地址同步数据")
show()
}.also {
ClientRequestTool.wait(
context = this,
url = url
) { isDone, content ->
it.cancel()
IconPackParams(context = this).also { params ->
if (isDone)
when {
params.isHackString(content) -> snake(msg = "请求需要验证,请尝试魔法上网或关闭魔法")
params.isNotVaildJson(content) -> snake(msg = "目标地址不是有效的 JSON 数据")
params.isCompareDifferent(content) -> {
params.save(content)
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this)
}
else -> snake(msg = "列表数据已是最新")
}
else showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n$content"
confirmButton(text = "我知道了")
}
}
}
}
}
/** 刷新适配器结果相关 */
private fun refreshAdapterResult() {
onChanged?.invoke()

View File

@@ -43,8 +43,10 @@ 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.REMOVE_CHANGECP_NOTIFY
import com.fankes.coloros.notify.hook.HookConst.REMOVE_DEV_NOTIFY
import com.fankes.coloros.notify.hook.HookConst.REMOVE_DNDALERT_NOTIFY
import com.fankes.coloros.notify.ui.base.BaseActivity
import com.fankes.coloros.notify.utils.*
import com.fankes.coloros.notify.utils.factory.*
import com.fankes.coloros.notify.utils.tool.SystemUITool
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
@@ -60,7 +62,7 @@ class MainActivity : BaseActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/** 设置文本 */
findViewById<TextView>(R.id.main_text_version).text = "当前版本:$moduleVersion"
findViewById<TextView>(R.id.main_text_version).text = "模块版本:$moduleVersion"
findViewById<TextView>(R.id.main_text_coloros_version).text = "系统版本:$colorOSVersion"
when {
/** 判断是否为 ColorOS 系统 */
@@ -97,6 +99,7 @@ class MainActivity : BaseActivity() {
val notifyIconConfigItem = findViewById<View>(R.id.config_item_notify)
val devNotifyConfigSwitch = findViewById<SwitchCompat>(R.id.remove_dev_n_enable_switch)
val crcpNotifyConfigSwitch = findViewById<SwitchCompat>(R.id.remove_chargecp_n_enable_switch)
val dndNotifyConfigSwitch = findViewById<SwitchCompat>(R.id.remove_dndalert_n_enable_switch)
val a12StyleConfigSwitch = findViewById<SwitchCompat>(R.id.a12_style_enable_switch)
val hideIconInLauncherSwitch = findViewById<SwitchCompat>(R.id.hide_icon_in_launcher_switch)
val notifyIconFixSwitch = findViewById<SwitchCompat>(R.id.notify_icon_fix_switch)
@@ -109,6 +112,7 @@ class MainActivity : BaseActivity() {
notifyIconFixButton.isVisible = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)
devNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_DEV_NOTIFY, default = true)
crcpNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_CHANGECP_NOTIFY, default = false)
dndNotifyConfigSwitch.isChecked = modulePrefs.getBoolean(REMOVE_DNDALERT_NOTIFY, default = false)
a12StyleConfigSwitch.isChecked = modulePrefs.getBoolean(ENABLE_ANDROID12_STYLE, isUpperOfAndroidS)
moduleEnableSwitch.isChecked = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
moduleEnableLogSwitch.isChecked = modulePrefs.getBoolean(ENABLE_MODULE_LOG, default = false)
@@ -153,6 +157,11 @@ class MainActivity : BaseActivity() {
modulePrefs.putBoolean(REMOVE_CHANGECP_NOTIFY, b)
SystemUITool.showNeedRestartSnake(context = this)
}
dndNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(REMOVE_DNDALERT_NOTIFY, b)
SystemUITool.showNeedRestartSnake(context = this)
}
a12StyleConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_ANDROID12_STYLE, b)

View File

@@ -25,7 +25,7 @@ package com.fankes.coloros.notify.ui.base
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.utils.isNotSystemInDarkMode
import com.fankes.coloros.notify.utils.factory.isNotSystemInDarkMode
import com.gyf.immersionbar.ktx.immersionBar
abstract class BaseActivity : AppCompatActivity() {

View File

@@ -22,7 +22,7 @@
*/
@file:Suppress("unused", "DEPRECATION")
package com.fankes.coloros.notify.utils
package com.fankes.coloros.notify.utils.factory
import android.app.AlertDialog
import android.content.Context

View File

@@ -0,0 +1,83 @@
/*
* 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/13.
*/
@file:Suppress("unused")
package com.fankes.coloros.notify.utils.factory
import com.highcapable.yukihookapi.hook.log.loggerE
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为空
* @return [T] 发生异常时返回设定值否则返回正常值
*/
inline fun <T> safeOfNull(result: () -> T): T? = safeOf(default = null, result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 false
* @return [Boolean] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfFalse(result: () -> Boolean) = safeOf(default = false, result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 true
* @return [Boolean] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfTrue(result: () -> Boolean) = safeOf(default = true, result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 false
* @return [String] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfNothing(result: () -> String) = safeOf(default = "", result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 false
* @return [Int] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfNan(result: () -> Int) = safeOf(default = 0, result)
/**
* 忽略异常返回值
* @param default 异常返回值
* @param result 正常回调值
* @return [T] 发生异常时返回设定值否则返回正常值
*/
inline fun <T> safeOf(default: T, result: () -> T) = try {
result()
} catch (_: Throwable) {
default
}
/**
* 忽略异常运行
* @param msg 出错输出的消息 - 默认为空
* @param block 正常回调
*/
inline fun <T> T.runSafe(msg: String = "", block: () -> Unit) {
runCatching(block).onFailure { if (msg.isNotBlank()) loggerE(msg = msg, e = it) }
}

View File

@@ -22,10 +22,12 @@
*/
@file:Suppress("DEPRECATION", "PrivateApi", "unused", "ObsoleteSdkInt")
package com.fankes.coloros.notify.utils
package com.fankes.coloros.notify.utils.factory
import android.app.Activity
import android.app.AlertDialog
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo
@@ -42,11 +44,12 @@ import android.widget.Toast
import com.fankes.coloros.notify.application.CNNApplication.Companion.appContext
import com.google.android.material.snackbar.Snackbar
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.method
import com.highcapable.yukihookapi.hook.log.loggerE
import com.highcapable.yukihookapi.hook.type.java.StringType
import com.topjohnwu.superuser.Shell
import java.io.ByteArrayOutputStream
/**
* 系统深色模式是否开启
@@ -103,9 +106,12 @@ inline val isNotColorOS get() = !isColorOS
*/
val colorOSVersion
get() = safeOf(default = "无法获取") {
findPropString(key = "ro.system.build.fingerprint", default = "无法获取")
.split("ssi:")[1]
.split("/")[0].trim()
(classOf(name = "com.oplus.os.OplusBuild").let {
it.field { name = "VERSIONS" }.ignoredError().of<Array<String>>()
?.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}"
}
/**
@@ -121,8 +127,7 @@ val Context.packageInfo get() = packageManager?.getPackageInfo(packageName, 0) ?
val String.isInstall
get() = safeOfFalse {
appContext.packageManager.getPackageInfo(
this,
PackageManager.GET_UNINSTALLED_PACKAGES
this, PackageManager.GET_UNINSTALLED_PACKAGES
)
true
}
@@ -152,6 +157,17 @@ val Number.dp get() = (toFloat() * appContext.resources.displayMetrics.density).
*/
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 加密
* @return [String]
@@ -180,11 +196,8 @@ val String.bitmap: Bitmap get() = unbase64.bitmap
* 设置对话框默认风格
* @param context 使用的实例
*/
fun AlertDialog.setDefaultStyle(context: Context) =
window?.setBackgroundDrawable(
GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
intArrayOf(Color.WHITE, Color.WHITE)
fun AlertDialog.setDefaultStyle(context: Context) = window?.setBackgroundDrawable(GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, intArrayOf(Color.WHITE, Color.WHITE)
).apply {
shape = GradientDrawable.RECTANGLE
gradientType = GradientDrawable.LINEAR_GRADIENT
@@ -228,8 +241,7 @@ fun toast(msg: String) = Toast.makeText(appContext, msg, Toast.LENGTH_SHORT).sho
* @param it 按钮事件回调
*/
fun Context.snake(msg: String, actionText: String = "", it: () -> Unit = {}) =
Snackbar.make((this as Activity).findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG)
.apply {
Snackbar.make((this as Activity).findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG).apply {
if (actionText.isBlank()) return@apply
setActionTextColor(Color.WHITE)
setAction(actionText) { it() }
@@ -240,8 +252,7 @@ fun Context.snake(msg: String, actionText: String = "", it: () -> Unit = {}) =
* @param url 网址
* @param packageName 指定包名 - 可不填
*/
fun Context.openBrowser(url: String, packageName: String = "") =
runCatching {
fun Context.openBrowser(url: String, packageName: String = "") = runCatching {
startActivity(Intent().apply {
if (packageName.isNotBlank()) setPackage(packageName)
action = Intent.ACTION_VIEW
@@ -250,104 +261,19 @@ fun Context.openBrowser(url: String, packageName: String = "") =
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.onFailure {
if (packageName.isNotBlank())
snake(msg = "启动 $packageName 失败")
if (packageName.isNotBlank()) snake(msg = "启动 $packageName 失败")
else snake(msg = "启动系统浏览器失败")
}
/**
* 忽略异常返回值
* @param it 回调 - 如果异常为空
* @return [T] 发生异常时返回设定值否则返回正常值
* 复制到剪贴板
* @param content 要复制的文本
*/
inline fun <T> safeOfNull(it: () -> T): T? = safeOf(null, it)
/**
* 忽略异常返回值
* @param it 回调 - 如果异常为 false
* @return [Boolean] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfFalse(it: () -> Boolean) = safeOf(default = false, it)
/**
* 忽略异常返回值
* @param it 回调 - 如果异常为 true
* @return [Boolean] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfTrue(it: () -> Boolean) = safeOf(default = true, it)
/**
* 忽略异常返回值
* @param it 回调 - 如果异常为 false
* @return [String] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfNothing(it: () -> String) = safeOf(default = "", it)
/**
* 忽略异常返回值
* @param it 回调 - 如果异常为 false
* @return [Int] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfNan(it: () -> Int) = safeOf(default = 0, it)
/**
* 忽略异常返回值
* @param default 异常返回值
* @param it 正常回调值
* @return [T] 发生异常时返回设定值否则返回正常值
*/
inline fun <T> safeOf(default: T, it: () -> T): T {
return try {
it()
} catch (t: NullPointerException) {
default
} catch (t: UnsatisfiedLinkError) {
default
} catch (t: UnsupportedOperationException) {
default
} catch (t: ClassNotFoundException) {
default
} catch (t: IllegalStateException) {
default
} catch (t: NoSuchMethodError) {
default
} catch (t: NoSuchFieldError) {
default
} catch (t: Error) {
default
} catch (t: Exception) {
default
} catch (t: Throwable) {
default
fun Context.copyToClipboard(content: String) = runSafe {
(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 msg 出错输出的消息 - 默认为空
* @param it 正常回调
*/
inline fun safeRun(msg: String = "", it: () -> Unit) {
try {
it()
} catch (e: NullPointerException) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: UnsatisfiedLinkError) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: UnsupportedOperationException) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: ClassNotFoundException) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: IllegalStateException) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: NoSuchMethodError) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: NoSuchFieldError) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: Error) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: Exception) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: Throwable) {
}
}

View File

@@ -22,13 +22,17 @@
*/
@file:Suppress("TrustAllX509TrustManager", "CustomX509TrustManager", "DEPRECATION")
package com.fankes.coloros.notify.utils
package com.fankes.coloros.notify.utils.tool
import android.app.Activity
import android.app.ProgressDialog
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import com.fankes.coloros.notify.utils.factory.safeOfNull
import com.fankes.coloros.notify.utils.factory.setDefaultStyle
import com.fankes.coloros.notify.utils.factory.showDialog
import com.fankes.coloros.notify.utils.factory.snake
import com.highcapable.yukihookapi.hook.log.loggerD
import okhttp3.*
import java.io.IOException
@@ -79,7 +83,7 @@ object ClientRequestTool {
* @param url 请求地址
* @param it 回调 - ([Boolean] 是否成功,[String] 成功的内容或失败消息)
*/
fun wait(context: Activity, url: String, it: (Boolean, String) -> Unit) {
fun wait(context: Activity, url: String, it: (Boolean, String) -> Unit) = runCatching {
OkHttpClient().newBuilder().apply {
SSLSocketClient.sSLSocketFactory?.let { sslSocketFactory(it, SSLSocketClient.trustManager) }
hostnameVerifier(SSLSocketClient.hostnameVerifier)
@@ -98,7 +102,7 @@ object ClientRequestTool {
context.runOnUiThread { it(true, bodyString) }
}
})
}
}.onFailure { it(false, "URL 无效") }
/**
* 自动信任 SSL 证书

View File

@@ -20,9 +20,13 @@
*
* This file is Created by fankes on 2022/2/8.
*/
package com.fankes.coloros.notify.utils
package com.fankes.coloros.notify.utils.tool
import android.content.Context
import com.fankes.coloros.notify.utils.factory.execShellSu
import com.fankes.coloros.notify.utils.factory.showDialog
import com.fankes.coloros.notify.utils.factory.snake
import com.fankes.coloros.notify.utils.factory.toast
import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus

View File

@@ -29,7 +29,7 @@ import android.content.res.ColorStateList
import android.graphics.Color
import android.util.AttributeSet
import androidx.appcompat.widget.SwitchCompat
import com.fankes.coloros.notify.utils.dp
import com.fankes.coloros.notify.utils.factory.dp
import com.fankes.coloros.notify.utils.drawable.drawabletoolbox.DrawableBuilder
class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(context, attrs) {

View File

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

View File

@@ -86,7 +86,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:alpha="0.8"
android:text="当前版本:%1"
android:text="模块版本:%1"
android:textColor="@color/white"
android:textSize="13sp" />
@@ -95,7 +95,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.8"
android:text="ColorOS 版本:%1"
android:text="系统版本:%1"
android:textColor="@color/white"
android:textSize="13sp" />
</LinearLayout>
@@ -233,7 +233,6 @@
android:textSize="15sp" />
<TextView
android:id="@+id/color_icon_compat_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
@@ -242,6 +241,25 @@
android:text="充电完成后总是会推送一条通知,有时候甚至还不是静音状态,开启后将直接移除这个“你在教我做事”的通知功能。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<com.fankes.coloros.notify.view.MaterialSwitch
android:id="@+id/remove_dndalert_n_enable_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
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_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="开启免打扰后无论任何时长,总是会推送一条通知,开启后将直接移除这个“你在教我做事”的通知功能。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
@@ -447,7 +465,7 @@
android:layout_marginBottom="10dp"
android:alpha="0.8"
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:textSize="12sp" />

View File

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

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 {
appVersionName = "1.2"
appVersionCode = 3
appVersionName = "1.36"
appVersionCode = 6
}
task clean(type: Delete) {