Initial commit
15
.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
3
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
1
.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
||||
MIUINativeNotifyIcon
|
117
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,117 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
6
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
20
.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
40
.idea/jarRepositories.xml
generated
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenRepo" />
|
||||
<option name="name" value="MavenRepo" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven3" />
|
||||
<option name="name" value="maven3" />
|
||||
<option name="url" value="https://www.jitpack.io" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven" />
|
||||
<option name="name" value="maven" />
|
||||
<option name="url" value="https://maven.aliyun.com/nexus/content/groups/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven2" />
|
||||
<option name="name" value="maven2" />
|
||||
<option name="url" value="https://maven.aliyun.com/nexus/content/repositories/jcenter" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="Google" />
|
||||
<option name="name" value="Google" />
|
||||
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
18
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.44871794871794873" />
|
||||
<entry key="app/src/main/res/layout/activity_main.xml" value="0.4036346245815399" />
|
||||
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.44871794871794873" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
62
app/build.gradle
Normal file
@@ -0,0 +1,62 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 31
|
||||
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('../keystore/public')
|
||||
storePassword '123456'
|
||||
keyAlias 'public'
|
||||
keyPassword '123456'
|
||||
v1SigningEnabled true
|
||||
v2SigningEnabled true
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.fankes.miui.notify"
|
||||
minSdk 29
|
||||
targetSdk 26
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
signingConfig signingConfigs.debug
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'de.robv.android.xposed:api:82'
|
||||
// 基础依赖包
|
||||
implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
|
||||
// Fragment 快速实现
|
||||
implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
|
||||
// Kotlin 扩展
|
||||
implementation 'com.gyf.immersionbar:immersionbar-ktx:3.0.0'
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||
implementation 'com.google.android.material:material:1.5.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
//noinspection GradleDynamicVersion
|
||||
testImplementation 'junit:junit:4.+'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
}
|
48
app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-dontwarn
|
||||
-ignorewarnings
|
||||
-optimizationpasses 10
|
||||
-dontusemixedcaseclassnames
|
||||
-dontoptimize
|
||||
-verbose
|
||||
-overloadaggressively
|
||||
-repackageclasses o
|
||||
-allowaccessmodification
|
||||
-adaptclassstrings
|
||||
-adaptresourcefilenames
|
||||
-adaptresourcefilecontents
|
||||
|
||||
#-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
|
||||
-renamesourcefileattribute H
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
-keep class android.support**
|
||||
-keep class androidx**
|
||||
|
||||
-keep public class * extends android.app.Activity
|
||||
-keep public class * extends android.app.Application
|
||||
-keep public class * extends android.app.Service
|
||||
-keep public class * extends android.content.ContentProvider
|
||||
-keep public class * extends android.app.backup.BackupAgentHelper
|
||||
-keep public class * extends android.preference.Preference
|
@@ -0,0 +1,24 @@
|
||||
package com.fankes.miui.notify
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.fankes.miui.notify", appContext.packageName)
|
||||
}
|
||||
}
|
57
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.fankes.miui.notify">
|
||||
|
||||
<application
|
||||
android:name=".application.MNvApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.MIUINativeNotifyIcon"
|
||||
tools:ignore="AllowBackup">
|
||||
|
||||
<!-- 是否是xposed模块 -->
|
||||
<meta-data
|
||||
android:name="xposedmodule"
|
||||
android:value="true" />
|
||||
|
||||
<!-- 模块描述 -->
|
||||
<meta-data
|
||||
android:name="xposeddescription"
|
||||
android:value="MIUI 状态栏原生图标,修复 12.5、13 后期被破坏的彩色图标。\n开发者:酷安 @星夜不荟" />
|
||||
|
||||
<!-- 最低xposed版本号 -->
|
||||
<meta-data
|
||||
android:name="xposedminversion"
|
||||
android:value="82" />
|
||||
|
||||
<activity
|
||||
android:name="com.fankes.miui.notify.ui.MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="behind">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="de.robv.android.xposed.category.MODULE_SETTINGS" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity-alias
|
||||
android:name="com.fankes.miui.notify.Home"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="behind"
|
||||
android:targetActivity="com.fankes.miui.notify.ui.MainActivity">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
</application>
|
||||
</manifest>
|
1
app/src/main/assets/xposed_init
Normal file
@@ -0,0 +1 @@
|
||||
com.fankes.miui.notify.hook.HookMain
|
BIN
app/src/main/ic_launcher-playstore.png
Normal file
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/01/24.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.fankes.miui.notify.application
|
||||
|
||||
import android.app.Application
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
|
||||
class MNvApplication : Application() {
|
||||
|
||||
companion object {
|
||||
|
||||
/** 全局静态实例 */
|
||||
private var context: MNvApplication? = null
|
||||
|
||||
/** 调用全局静态实例 */
|
||||
val appContext get() = context ?: error("App is death")
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
/** 设置静态实例 */
|
||||
context = this
|
||||
/** 禁止系统夜间模式对自己造成干扰 - 模块要什么夜间模式?😅 (其实是我懒) */
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||
}
|
||||
}
|
323
app/src/main/java/com/fankes/miui/notify/hook/HookMain.kt
Normal file
@@ -0,0 +1,323 @@
|
||||
/**
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/01/24.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "SameParameterValue")
|
||||
|
||||
package com.fankes.miui.notify.hook
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Outline
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Build
|
||||
import android.service.notification.StatusBarNotification
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.ViewOutlineProvider
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.Keep
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import com.fankes.miui.notify.hook.HookMedium.QQ_PACKAGE_NAME
|
||||
import com.fankes.miui.notify.hook.HookMedium.SELF_PACKAGE_NAME
|
||||
import com.fankes.miui.notify.hook.HookMedium.SYSTEMUI_PACKAGE_NAME
|
||||
import com.fankes.miui.notify.utils.XPrefUtils
|
||||
import com.fankes.miui.notify.utils.dp
|
||||
import com.fankes.miui.notify.utils.isNotMIUI
|
||||
import com.fankes.miui.notify.utils.round
|
||||
import de.robv.android.xposed.*
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||
|
||||
@Keep
|
||||
class HookMain : IXposedHookLoadPackage {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val NotificationUtilClass =
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationUtil"
|
||||
|
||||
private const val NotificationHeaderViewWrapperInjectorClass =
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationHeaderViewWrapperInjector"
|
||||
|
||||
private const val ExpandedNotificationClass =
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.ExpandedNotification"
|
||||
|
||||
private const val SystemUIApplicationClass = "$SYSTEMUI_PACKAGE_NAME.SystemUIApplication"
|
||||
|
||||
private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil"
|
||||
}
|
||||
|
||||
/** 仅作用于替换的 Hook 方法体 */
|
||||
private val replaceToNull = object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam?): Any? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/** 仅作用于替换的 Hook 方法体 */
|
||||
private val replaceToTrue = object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam?): Any {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/** 仅作用于替换的 Hook 方法体 */
|
||||
private val replaceToFalse = object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam?): Any {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略异常运行
|
||||
* @param it 正常回调
|
||||
*/
|
||||
private fun runWithoutError(error: String, it: () -> Unit) {
|
||||
try {
|
||||
it()
|
||||
} catch (e: Error) {
|
||||
logE("hookFailed: $error", e)
|
||||
} catch (e: Exception) {
|
||||
logE("hookFailed: $error", e)
|
||||
} catch (e: Throwable) {
|
||||
logE("hookFailed: $error", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the log
|
||||
* @param content
|
||||
*/
|
||||
private fun logD(content: String) {
|
||||
XposedBridge.log("[MIUINativeNotifyIcon][D]>$content")
|
||||
Log.d("MIUINativeNotifyIcon", content)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the log
|
||||
* @param content
|
||||
*/
|
||||
private fun logE(content: String, e: Throwable? = null) {
|
||||
XposedBridge.log("[MIUINativeNotifyIcon][E]>$content")
|
||||
XposedBridge.log(e)
|
||||
Log.e("MIUINativeNotifyIcon", content, e)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找目标类
|
||||
* @param name 类名
|
||||
* @return [Class]
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.findClass(name: String) =
|
||||
classLoader.loadClass(name)
|
||||
|
||||
/**
|
||||
* ⚠️ 这个是修复彩色图标的关键核心代码判断
|
||||
* 判断是否为灰度图标 - 反射执行系统方法
|
||||
* @param context 实例
|
||||
* @param icon 要判断的图标
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.isGrayscaleIcon(context: Context, icon: Drawable) =
|
||||
findClass(ContrastColorUtilClass).let {
|
||||
val instance = it.getDeclaredMethod("getInstance", Context::class.java)
|
||||
.apply { isAccessible = true }.invoke(null, context)
|
||||
it.getDeclaredMethod("isGrayscaleIcon", Drawable::class.java)
|
||||
.apply { isAccessible = true }.invoke(instance, icon) as Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全局上下文
|
||||
* @return [Context]
|
||||
*/
|
||||
private val XC_LoadPackage.LoadPackageParam.globalContext
|
||||
get() = findClass(SystemUIApplicationClass)
|
||||
.getDeclaredMethod("getContext").apply { isAccessible = true }
|
||||
.invoke(null) as Context
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
|
||||
if (lpparam == null) return
|
||||
when (lpparam.packageName) {
|
||||
/** Hook 自身 */
|
||||
SELF_PACKAGE_NAME ->
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"$SELF_PACKAGE_NAME.hook.HookMedium",
|
||||
lpparam.classLoader,
|
||||
"isHooked",
|
||||
replaceToTrue
|
||||
)
|
||||
/** Hook 系统 UI */
|
||||
SYSTEMUI_PACKAGE_NAME -> {
|
||||
/** 若不是 MIUI 系统直接停止 Hook */
|
||||
if (isNotMIUI) return
|
||||
/** 若没开启模块直接停止 Hook */
|
||||
if (!XPrefUtils.getBoolean(HookMedium.ENABLE_MODULE, default = true)) return
|
||||
/** 强制回写系统的状态栏图标样式为原生 */
|
||||
runWithoutError("SubstituteSmallIcon") {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
NotificationUtilClass,
|
||||
lpparam.classLoader,
|
||||
"shouldSubstituteSmallIcon",
|
||||
lpparam.findClass(ExpandedNotificationClass),
|
||||
replaceToFalse
|
||||
)
|
||||
}
|
||||
/** 修复通知图标为彩色 */
|
||||
runWithoutError("IgnoreStatusBarIconColor") {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
NotificationUtilClass,
|
||||
lpparam.classLoader,
|
||||
"ignoreStatusBarIconColor",
|
||||
lpparam.findClass(ExpandedNotificationClass),
|
||||
object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam) =
|
||||
if (XPrefUtils.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true))
|
||||
try {
|
||||
/** 获取发送通知的 APP */
|
||||
val packageName = (param.args[0] as StatusBarNotification).opPkg
|
||||
|
||||
/** 获取通知小图标 */
|
||||
val iconDrawable = (param.args[0] as StatusBarNotification)
|
||||
.notification.smallIcon.loadDrawable(lpparam.globalContext)
|
||||
/** 如果开启了修复聊天 APP 的图标 */
|
||||
if (packageName == QQ_PACKAGE_NAME &&
|
||||
XPrefUtils.getBoolean(HookMedium.ENABLE_CHAT_ICON_HOOK, default = true)
|
||||
) false
|
||||
/** 只要不是灰度就返回彩色图标 */
|
||||
else !lpparam.isGrayscaleIcon(lpparam.globalContext, iconDrawable)
|
||||
} catch (e: Exception) {
|
||||
logE("Failed to hook ignoreStatusBarIconColor", e)
|
||||
false
|
||||
}
|
||||
else false
|
||||
}
|
||||
)
|
||||
}
|
||||
/** 强制回写系统的状态栏图标样式为原生 */
|
||||
runWithoutError("GetSmallIcon") {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
NotificationUtilClass,
|
||||
lpparam.classLoader,
|
||||
"getSmallIcon",
|
||||
lpparam.findClass(ExpandedNotificationClass),
|
||||
Int::class.java,
|
||||
object : XC_MethodHook() {
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam) {
|
||||
runWithoutError("GetSmallIconOnSet") {
|
||||
/** 获取通知小图标 */
|
||||
val iconDrawable = (param.result as Icon).loadDrawable(lpparam.globalContext)
|
||||
/** 判断要设置的图标 */
|
||||
when {
|
||||
/** 如果开启了修复聊天 APP 的图标 */
|
||||
(param.args[0] as StatusBarNotification).opPkg == QQ_PACKAGE_NAME &&
|
||||
XPrefUtils.getBoolean(HookMedium.ENABLE_CHAT_ICON_HOOK, default = true) ->
|
||||
param.result = Icon.createWithBitmap(IconPackParams.qqSmallIcon)
|
||||
/** 若不是灰度图标自动处理为圆角 */
|
||||
!lpparam.isGrayscaleIcon(lpparam.globalContext, iconDrawable) ->
|
||||
param.result = Icon.createWithBitmap(
|
||||
iconDrawable.toBitmap().round(15.dp(lpparam.globalContext))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
/** 干掉下拉通知图标自动设置回 APP 图标的方法 - Android 12 */
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R)
|
||||
runWithoutError("ResetIconBgAndPaddings") {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
NotificationHeaderViewWrapperInjectorClass,
|
||||
lpparam.classLoader,
|
||||
"resetIconBgAndPaddings",
|
||||
ImageView::class.java,
|
||||
lpparam.findClass(ExpandedNotificationClass),
|
||||
replaceToNull
|
||||
)
|
||||
}
|
||||
/** 修复下拉通知图标自动设置回 APP 图标的方法 - Android 12 */
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R)
|
||||
runWithoutError("AutoSetAppIcon") {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
NotificationHeaderViewWrapperInjectorClass,
|
||||
lpparam.classLoader,
|
||||
"setAppIcon",
|
||||
Context::class.java,
|
||||
ImageView::class.java,
|
||||
lpparam.findClass(ExpandedNotificationClass),
|
||||
object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam): Any? {
|
||||
runWithoutError("AutoSetAppIconOnSet") {
|
||||
/** 获取 [Context] */
|
||||
val context = param.args[0] as Context
|
||||
|
||||
/** 获取图标框 */
|
||||
val iconImageView = param.args[1] as ImageView
|
||||
|
||||
/** 获取通知小图标 */
|
||||
val iconDrawable = (param.args[2] as StatusBarNotification)
|
||||
.notification.smallIcon.loadDrawable(context)
|
||||
|
||||
/** 获取发送通知的 APP */
|
||||
val packageName = (param.args[2] as StatusBarNotification).opPkg
|
||||
/** 如果开启了修复聊天 APP 的图标 */
|
||||
if (packageName == QQ_PACKAGE_NAME &&
|
||||
XPrefUtils.getBoolean(HookMedium.ENABLE_CHAT_ICON_HOOK, default = true)
|
||||
)
|
||||
iconImageView.apply {
|
||||
/** 设置自定义小图标 */
|
||||
setImageDrawable(BitmapDrawable(IconPackParams.qqSmallIcon))
|
||||
/** 上色 */
|
||||
setColorFilter(Color.WHITE)
|
||||
}
|
||||
else {
|
||||
/** 重新设置图标 - 防止系统更改它 */
|
||||
iconImageView.setImageDrawable(iconDrawable)
|
||||
/** 判断如果是灰度图标就给他设置一个白色颜色遮罩 */
|
||||
if (lpparam.isGrayscaleIcon(context, iconDrawable))
|
||||
iconImageView.setColorFilter(Color.WHITE)
|
||||
else
|
||||
iconImageView.apply {
|
||||
clipToOutline = true
|
||||
/** 设置一个圆角轮廓裁切 */
|
||||
outlineProvider = object : ViewOutlineProvider() {
|
||||
override fun getOutline(view: View, out: Outline) {
|
||||
out.setRoundRect(0, 0, view.width, view.height, 5.dp(context))
|
||||
}
|
||||
}
|
||||
/** 清除原生的背景边距设置 */
|
||||
setPadding(0, 0, 0, 0)
|
||||
/** 清除原生的主题色背景圆圈颜色 */
|
||||
setBackgroundDrawable(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
logD("hook Completed!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
app/src/main/java/com/fankes/miui/notify/hook/HookMedium.kt
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/24.
|
||||
*/
|
||||
package com.fankes.miui.notify.hook
|
||||
|
||||
import android.util.Log
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
object HookMedium {
|
||||
|
||||
const val ENABLE_MODULE = "_enable_module"
|
||||
const val ENABLE_HIDE_ICON = "_hide_icon"
|
||||
const val ENABLE_COLOR_ICON_HOOK = "_color_icon_hook"
|
||||
const val ENABLE_CHAT_ICON_HOOK = "_chat_icon_hook"
|
||||
|
||||
const val SELF_PACKAGE_NAME = "com.fankes.miui.notify"
|
||||
const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui"
|
||||
const val QQ_PACKAGE_NAME = "com.tencent.mobileqq"
|
||||
|
||||
/**
|
||||
* 判断模块是否激活
|
||||
* 在 [HookMain] 中 Hook 掉此方法
|
||||
* @return [Boolean] 激活状态
|
||||
*/
|
||||
fun isHooked(): Boolean {
|
||||
Log.d("MIUINativeNotifyIcon", "isHooked: true")
|
||||
return false
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/24.
|
||||
*/
|
||||
package com.fankes.miui.notify.hook
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import com.fankes.miui.notify.utils.bitmap
|
||||
import com.fankes.miui.notify.utils.unbase64
|
||||
|
||||
object IconPackParams {
|
||||
|
||||
/**
|
||||
* QQ 通知栏小图标
|
||||
* @return [Bitmap]
|
||||
*/
|
||||
val qqSmallIcon by lazy {
|
||||
("iVBORw0KGgoAAAANSUhEUgAAAEwAAABMCAYAAADHl1ErAAAAAXNSR0IArs4c6QAAAARzQklUCAgI\n" +
|
||||
"CHwIZIgAAATZSURBVHic7ZxNjF5TGMd/T6uliBk1pSgtxQwlqo2gKghiU4ZaCOIjRGwIiY8QCRs7\n" +
|
||||
"S4suRJBYkUgXMiIipBYTiZmuKtRHW0biK8xrNE2l9be4d5LpeN/33nue877n3tHf5p259zzPfc4/\n" +
|
||||
"z7kf5wuOUglLeXFJw8BmYD0wApwBrACW5UUOANPAPuAb4HNg0sx29j/aREgakvSUpHGFMyHpBUln\n" +
|
||||
"p65Pz5C0VNJLkmYcQs3nkKRtkoZS1y8qkkYl/RBRqPlMS3oodT2jIOnlHgo1nzdT19eFpHf6KNYs\n" +
|
||||
"O1LXO4hEYs0ynrr+lZD0SkKxZtmeWodSSLoztVJzeC52/aK+uEoaBP6I6TMC55nZt7GcLYrlKOeN\n" +
|
||||
"yP5i8HZMZ9EEk7QRGI3lLyIbJG2N5Sxak1T2ZLoylr/ITJnZWTEcRckwSeuor1gAqyTdHMNRrCb5\n" +
|
||||
"TCQ/veT5GE7cTVLSUuBP4Fh/OD1npZn97HEQI8O20AyxAO71OoghWB2fjJ1w38diNMk9wBqvnz5x\n" +
|
||||
"EDjFzPaHOnBlmLIu5qaIBdmtY7PHgbdJXu60T8ElHmOvYCNO+xQkFWzYaZ+CCzzGXsHOddqnYK2k\n" +
|
||||
"4HoHG+YXPTXUPiHLycY+g/Bk2EnAoMM+FYtxxO0V7ESHfUqODzX0CLbEYZua40INY/e4LniOClYR\n" +
|
||||
"j2CtaFH0n99DDT2CneawTU3w969HsEcdtql5PNQwqHunYb2snQjqfQ3NsNtptlgAD4YYhWZYnYfU\n" +
|
||||
"yhI09FY5wyStpfliQTb0dm1Vo5Am+USATV15sqpBpSaZ91C0aO43ZDtWmNlvZQtXzbC7WVhiATxc\n" +
|
||||
"pXDVDNsBXFMpnPqz18zOKVu4tGDK5sTvCwqp/mwys1LTPKs0yXsCg2kC95UtWCXDdgPnB4VTf/4C\n" +
|
||||
"Bszsn6KCpTIsn860UMWC7EF2U5mCZZvkXeGxNIb7yxQq1SQXeHOcpWVmhYMjhRmWPx0XulgAA2U+\n" +
|
||||
"lco0ySZNZ/JyS1GBMoLdECGQpnB9UYGu97D82/EnHCPFDaTrt2VRhl3G/0ssgKu7nSwSbH3EQJrC\n" +
|
||||
"pd1OFgl2YYfjh4GpoHDqwRTZ9M12dJ0/ViTYRR2OPwK8WmBbZ94j66pqR6ckAYoFW93m2F4ze61M\n" +
|
||||
"VDVmwMzeBT5tc25VN8Miwdrd8Lfkvz+WCKyufJf/tlu0tbibYZFgy+b9/7SZ7cr//qREYHXlQ4D8\n" +
|
||||
"9eGOaF515B4T/1kSLGl3j1fU9oIZZQPRc+sxd8n1TDdNijLsQP47aWa3tTn/eoF9HXnLzP6ee8DM\n" +
|
||||
"HgPez//tOmZR9Ka/DVgH3Dj/Ivn5IeDXSuGmZ8TMvmp3QtIYcMjMbu3Z1VWPHQTKMuatb4y1RieQ\n" +
|
||||
"jVV2fbrUhNVm9r3HgXsGYr7Q6QGvnz7wolcsiLvmezv17TubNLONMRzFFGwR8CX1651tAWvMbDqG\n" +
|
||||
"s2iTgvMhqk3U66P8IHBFLLGgR1v6SZoANvTCdwW+Bi5u9zrk4ZiYziRdBVxHPWYnLgGelfSZmX2Q\n" +
|
||||
"OpgjkLRV0sfpXq8KmVAddrCTNChpLLEYVRhXNoMymOB7mLIX1p3U76lYRAsYDt2/wvOUXEnzxAIY\n" +
|
||||
"wLH02nPT30O2ccYoWVf2mYR/Hh0m23dsP9myltlXk9PJlhkuB052+v8F+AL4CGjWln9N5l/CCEZS\n" +
|
||||
"HWRiiAAAAABJRU5ErkJggg==").unbase64.bitmap
|
||||
}
|
||||
}
|
257
app/src/main/java/com/fankes/miui/notify/ui/MainActivity.kt
Normal file
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/01/24.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "SetWorldReadable", "SetTextI18n")
|
||||
|
||||
package com.fankes.miui.notify.ui
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
import com.fankes.miui.notify.BuildConfig
|
||||
import com.fankes.miui.notify.R
|
||||
import com.fankes.miui.notify.hook.HookMedium
|
||||
import com.fankes.miui.notify.utils.*
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import java.io.File
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
|
||||
/** 模块版本 */
|
||||
private const val moduleVersion = BuildConfig.VERSION_NAME
|
||||
|
||||
/** MIUI 版本 */
|
||||
private val miuiVersion by lazy {
|
||||
if (isMIUI)
|
||||
findPropString(key = "ro.miui.ui.version.code", default = "无法获取") +
|
||||
" " + findPropString(key = "ro.system.build.version.incremental")
|
||||
else "不是 MIUI 系统"
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
/** 隐藏系统的标题栏 */
|
||||
supportActionBar?.hide()
|
||||
/** 初始化沉浸状态栏 */
|
||||
ImmersionBar.with(this)
|
||||
.statusBarColor(R.color.white)
|
||||
.autoDarkModeEnable(false)
|
||||
.statusBarDarkFont(true)
|
||||
.navigationBarColor(R.color.white)
|
||||
.navigationBarDarkIcon(true)
|
||||
.fitsSystemWindows(true)
|
||||
.init()
|
||||
/** 设置文本 */
|
||||
findViewById<TextView>(R.id.main_text_version).text = "当前版本:$moduleVersion"
|
||||
findViewById<TextView>(R.id.main_text_miui_version).text = "MIUI 版本:$miuiVersion"
|
||||
/** 判断是否为 MIUI 系统 */
|
||||
if (isNotMIUI) {
|
||||
showDialog {
|
||||
title = "不是 MIUI 系统"
|
||||
msg = "此模块专为 MIUI 系统打造,当前无法识别你的系统为 MIUI,所以模块无法工作。\n" +
|
||||
"如有问题请联系 酷安 @星夜不荟"
|
||||
confirmButton(text = "退出") { finish() }
|
||||
noCancelable()
|
||||
}
|
||||
return
|
||||
}
|
||||
/** 判断 Hook 状态 */
|
||||
if (isHooked()) {
|
||||
findViewById<LinearLayout>(R.id.main_lin_status).setBackgroundResource(R.drawable.green_round)
|
||||
findViewById<ImageFilterView>(R.id.main_img_status).setImageResource(R.mipmap.succcess)
|
||||
findViewById<TextView>(R.id.main_text_status).text = "模块已激活"
|
||||
} else
|
||||
showDialog {
|
||||
title = "模块没有激活"
|
||||
msg = "检测到模块没有激活,模块需要 Xposed 环境依赖," +
|
||||
"同时需要系统拥有 Root 权限," +
|
||||
"请自行查看本页面使用帮助与说明第二条。\n" +
|
||||
"由于需要修改系统应用达到效果,模块不支持太极阴、应用转生。"
|
||||
confirmButton(text = "我知道了")
|
||||
noCancelable()
|
||||
}
|
||||
/** 初始化 View */
|
||||
val moduleEnableSwitch = findViewById<SwitchCompat>(R.id.module_enable_switch)
|
||||
val hideIconInLauncherSwitch = findViewById<SwitchCompat>(R.id.hide_icon_in_launcher_switch)
|
||||
val colorIconHookSwitch = findViewById<SwitchCompat>(R.id.color_icon_fix_switch)
|
||||
val chatIconHookSwitch = findViewById<SwitchCompat>(R.id.chat_icon_fix_switch)
|
||||
/** 获取 Sp 存储的信息 */
|
||||
moduleEnableSwitch.isChecked = getBoolean(HookMedium.ENABLE_MODULE, default = true)
|
||||
hideIconInLauncherSwitch.isChecked = getBoolean(HookMedium.ENABLE_HIDE_ICON)
|
||||
colorIconHookSwitch.isChecked = getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true)
|
||||
chatIconHookSwitch.isChecked = getBoolean(HookMedium.ENABLE_CHAT_ICON_HOOK, default = true)
|
||||
moduleEnableSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (!btn.isPressed) return@setOnCheckedChangeListener
|
||||
putBoolean(HookMedium.ENABLE_MODULE, b)
|
||||
}
|
||||
hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (!btn.isPressed) return@setOnCheckedChangeListener
|
||||
putBoolean(HookMedium.ENABLE_HIDE_ICON, b)
|
||||
packageManager.setComponentEnabledSetting(
|
||||
ComponentName(this@MainActivity, "com.fankes.miui.notify.Home"),
|
||||
if (b) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||
PackageManager.DONT_KILL_APP
|
||||
)
|
||||
}
|
||||
colorIconHookSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (!btn.isPressed) return@setOnCheckedChangeListener
|
||||
putBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, b)
|
||||
}
|
||||
chatIconHookSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (!btn.isPressed) return@setOnCheckedChangeListener
|
||||
putBoolean(HookMedium.ENABLE_CHAT_ICON_HOOK, b)
|
||||
}
|
||||
/** 重启按钮点击事件 */
|
||||
findViewById<View>(R.id.title_restart_icon).setOnClickListener {
|
||||
showDialog {
|
||||
title = "重启系统界面"
|
||||
msg = "你确定要立即重启系统界面吗?"
|
||||
confirmButton { restartSystemUI() }
|
||||
cancelButton()
|
||||
}
|
||||
}
|
||||
/** 恰饭! */
|
||||
findViewById<View>(R.id.link_with_follow_me).setOnClickListener {
|
||||
try {
|
||||
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
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "你可能没有安装酷安", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
/** 项目地址点击事件 */
|
||||
findViewById<View>(R.id.link_with_project_address).setOnClickListener {
|
||||
try {
|
||||
startActivity(Intent().apply {
|
||||
action = "android.intent.action.VIEW"
|
||||
data = Uri.parse("https://github.com/fankes/MIUINativeNotifyIcon")
|
||||
/** 防止顶栈一样重叠在自己的 APP 中 */
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "无法启动系统默认浏览器", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断模块是否激活
|
||||
* @return [Boolean] 激活状态
|
||||
*/
|
||||
private fun isHooked() = HookMedium.isHooked()
|
||||
|
||||
/** 重启系统界面 */
|
||||
private fun restartSystemUI() =
|
||||
execShellCmd(cmd = "pgrep systemui").also { pid ->
|
||||
if (pid.isNotBlank())
|
||||
execShellCmd(cmd = "kill -9 $pid")
|
||||
else Toast.makeText(this, "ROOT 权限获取失败", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
setWorldReadable()
|
||||
}
|
||||
|
||||
override fun onRestart() {
|
||||
super.onRestart()
|
||||
setWorldReadable()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
setWorldReadable()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取保存的值
|
||||
* @param key 名称
|
||||
* @param default 默认值
|
||||
* @return [Boolean] 保存的值
|
||||
*/
|
||||
private fun getBoolean(key: String, default: Boolean = false) =
|
||||
getSharedPreferences(
|
||||
packageName + "_preferences",
|
||||
Context.MODE_PRIVATE
|
||||
).getBoolean(key, default)
|
||||
|
||||
/**
|
||||
* 保存值
|
||||
* @param key 名称
|
||||
* @param bool 值
|
||||
*/
|
||||
private fun putBoolean(key: String, bool: Boolean) {
|
||||
getSharedPreferences(
|
||||
packageName + "_preferences",
|
||||
Context.MODE_PRIVATE
|
||||
).edit().putBoolean(key, bool).apply()
|
||||
setWorldReadable()
|
||||
/** 延迟继续设置强制允许 SP 可读可写 */
|
||||
Handler().postDelayed({ setWorldReadable() }, 500)
|
||||
Handler().postDelayed({ setWorldReadable() }, 1000)
|
||||
Handler().postDelayed({ setWorldReadable() }, 1500)
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制设置 Sp 存储为全局可读可写
|
||||
* 以供模块使用
|
||||
*/
|
||||
private fun setWorldReadable() {
|
||||
try {
|
||||
if (FileUtils.getDefaultPrefFile(this).exists()) {
|
||||
for (file in arrayOf<File>(
|
||||
FileUtils.getDataDir(this),
|
||||
FileUtils.getPrefDir(this),
|
||||
FileUtils.getDefaultPrefFile(this)
|
||||
)) {
|
||||
file.setReadable(true, false)
|
||||
file.setExecutable(true, false)
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
Toast.makeText(this, "无法写入模块设置,请检查权限\n如果此提示一直显示,请不要双开模块", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
setWorldReadable()
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
101
app/src/main/java/com/fankes/miui/notify/utils/DialogBuilder.kt
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/7.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.fankes.miui.notify.utils
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
|
||||
|
||||
/**
|
||||
* 构造对话框
|
||||
* @param it 对话框方法体
|
||||
*/
|
||||
fun Context.showDialog(it: DialogBuilder.() -> Unit) = DialogBuilder(this).apply(it).show()
|
||||
|
||||
/**
|
||||
* 对话框构造器
|
||||
* @param context 实例
|
||||
*/
|
||||
class DialogBuilder(private val context: Context) {
|
||||
|
||||
private var instance: AlertDialog.Builder? = null // 实例对象
|
||||
|
||||
init {
|
||||
instance = AlertDialog.Builder(context, android.R.style.Theme_Material_Light_Dialog)
|
||||
}
|
||||
|
||||
/** 设置对话框不可关闭 */
|
||||
fun noCancelable() = instance?.setCancelable(false)
|
||||
|
||||
/** 设置对话框标题 */
|
||||
var title
|
||||
get() = ""
|
||||
set(value) {
|
||||
instance?.setTitle(value)
|
||||
}
|
||||
|
||||
/** 设置对话框消息内容 */
|
||||
var msg
|
||||
get() = ""
|
||||
set(value) {
|
||||
instance?.setMessage(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对话框确定按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param it 点击事件
|
||||
*/
|
||||
fun confirmButton(text: String = "确定", it: () -> Unit = {}) =
|
||||
instance?.setPositiveButton(text) { _, _ -> it() }
|
||||
|
||||
/**
|
||||
* 设置对话框取消按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param it 点击事件
|
||||
*/
|
||||
fun cancelButton(text: String = "取消", it: () -> Unit = {}) =
|
||||
instance?.setNegativeButton(text) { _, _ -> it() }
|
||||
|
||||
/**
|
||||
* 设置对话框第三个按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param it 点击事件
|
||||
*/
|
||||
fun neutralButton(text: String = "更多", it: () -> Unit = {}) =
|
||||
instance?.setNeutralButton(text) { _, _ -> it() }
|
||||
|
||||
/** 显示对话框 */
|
||||
internal fun show() = instance?.create()?.apply {
|
||||
window?.setBackgroundDrawable(GradientDrawable(
|
||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
intArrayOf(Color.WHITE, Color.WHITE)
|
||||
).apply {
|
||||
shape = GradientDrawable.RECTANGLE
|
||||
gradientType = GradientDrawable.LINEAR_GRADIENT
|
||||
cornerRadius = 15.dp(this@DialogBuilder.context)
|
||||
})
|
||||
}?.show()
|
||||
}
|
85
app/src/main/java/com/fankes/miui/notify/utils/FileUtils.java
Executable file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by zpp0196 on 2019/2/9.
|
||||
*/
|
||||
package com.fankes.miui.notify.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.fankes.miui.notify.BuildConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class FileUtils {
|
||||
|
||||
private static final String FILE_PREF_NAME = BuildConfig.APPLICATION_ID + "_preferences.xml";
|
||||
|
||||
public static boolean copyFile(File srcFile, File targetFile) {
|
||||
FileInputStream ins = null;
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
if (targetFile.exists()) {
|
||||
targetFile.delete();
|
||||
}
|
||||
File targetParent = targetFile.getParentFile();
|
||||
if (!targetParent.exists()) {
|
||||
targetParent.mkdirs();
|
||||
}
|
||||
targetFile.createNewFile();
|
||||
ins = new FileInputStream(srcFile);
|
||||
out = new FileOutputStream(targetFile);
|
||||
byte[] b = new byte[1024];
|
||||
int n;
|
||||
while ((n = ins.read(b)) != -1) {
|
||||
out.write(b, 0, n);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
if (ins != null) {
|
||||
ins.close();
|
||||
}
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static File getDataDir(Context context) {
|
||||
return new File(context.getApplicationInfo().dataDir);
|
||||
}
|
||||
|
||||
public static File getPrefDir(Context context) {
|
||||
return new File(getDataDir(context), "shared_prefs");
|
||||
}
|
||||
|
||||
public static File getDefaultPrefFile(Context context) {
|
||||
return new File(getPrefDir(context), FILE_PREF_NAME);
|
||||
}
|
||||
}
|
179
app/src/main/java/com/fankes/miui/notify/utils/Utils.kt
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/7.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "PrivateApi", "unused")
|
||||
|
||||
package com.fankes.miui.notify.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.*
|
||||
import android.graphics.Bitmap.createBitmap
|
||||
import android.util.Base64
|
||||
import com.fankes.miui.notify.application.MNvApplication.Companion.appContext
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
|
||||
|
||||
/**
|
||||
* 当前设备是否是 MIUI 定制 Android 系统
|
||||
* @return [Boolean] 是否符合条件
|
||||
*/
|
||||
val isMIUI by lazy {
|
||||
try {
|
||||
Class.forName("android.miui.R")
|
||||
true
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前设备是否不是 MIUI 定制 Android 系统
|
||||
* @return [Boolean] 是否符合条件
|
||||
*/
|
||||
inline val isNotMIUI get() = !isMIUI
|
||||
|
||||
/**
|
||||
* 得到安装包信息
|
||||
* @return [PackageInfo]
|
||||
*/
|
||||
val Context.packageInfo get() = packageManager?.getPackageInfo(packageName, 0) ?: PackageInfo()
|
||||
|
||||
/**
|
||||
* 判断应用是否安装
|
||||
* @return [Boolean]
|
||||
*/
|
||||
val String.isInstall
|
||||
get() =
|
||||
try {
|
||||
appContext.packageManager.getPackageInfo(
|
||||
this,
|
||||
PackageManager.GET_UNINSTALLED_PACKAGES
|
||||
)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到版本信息
|
||||
* @return [String]
|
||||
*/
|
||||
val Context.versionName get() = packageInfo.versionName ?: ""
|
||||
|
||||
/**
|
||||
* 得到版本号
|
||||
* @return [Int]
|
||||
*/
|
||||
val Context.versionCode get() = packageInfo.versionCode
|
||||
|
||||
/**
|
||||
* dp 转换为 px
|
||||
* @return [Int]
|
||||
*/
|
||||
val Number.dp get() = (toFloat() * appContext.resources.displayMetrics.density).toInt()
|
||||
|
||||
/**
|
||||
* dp 转换为 px
|
||||
* @param context 使用的实例
|
||||
* @return [Float]
|
||||
*/
|
||||
fun Number.dp(context: Context) = toFloat() * context.resources.displayMetrics.density
|
||||
|
||||
/**
|
||||
* Base64 解密为字节流
|
||||
* @return [ByteArray]
|
||||
*/
|
||||
val String.unbase64 get() = Base64.decode(this, Base64.DEFAULT) ?: ByteArray(0)
|
||||
|
||||
/**
|
||||
* 字节流解析为位图
|
||||
* @return [Bitmap]
|
||||
*/
|
||||
val ByteArray.bitmap: Bitmap get() = BitmapFactory.decodeByteArray(this, 0, size)
|
||||
|
||||
/**
|
||||
* 圆角图片
|
||||
* @param radius 圆角度
|
||||
* @return [Bitmap] 圆角后的位图 - 失败会返回处理之前的位图
|
||||
*/
|
||||
fun Bitmap.round(radius: Float): Bitmap =
|
||||
createBitmap(width, height, Bitmap.Config.ARGB_8888).also { out ->
|
||||
Canvas(out).also { canvas ->
|
||||
Paint().also { paint ->
|
||||
paint.isAntiAlias = true
|
||||
canvas.drawARGB(0, 0, 0, 0)
|
||||
paint.color = Color.WHITE
|
||||
canvas.drawRoundRect(RectF(Rect(0, 0, width, height)), radius, radius, paint)
|
||||
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
|
||||
canvas.drawBitmap(this, Rect(0, 0, width, height), Rect(0, 0, width, height), paint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统 Prop 值
|
||||
* @param key Key
|
||||
* @param default 默认值
|
||||
* @return [String]
|
||||
*/
|
||||
fun findPropString(key: String, default: String = "") =
|
||||
try {
|
||||
(Class.forName("android.os.SystemProperties").getDeclaredMethod(
|
||||
"get",
|
||||
String::class.java,
|
||||
String::class.java
|
||||
).apply { isAccessible = true }.invoke(null, key, default)) as? String? ?: default
|
||||
} catch (_: Exception) {
|
||||
default
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行命令 - su
|
||||
* @param cmd 命令
|
||||
* @return [String] 执行结果
|
||||
*/
|
||||
fun execShellCmd(cmd: String): String {
|
||||
var result = ""
|
||||
var dos: DataOutputStream? = null
|
||||
var dis: DataInputStream? = null
|
||||
try {
|
||||
val p = Runtime.getRuntime().exec("su")
|
||||
dos = DataOutputStream(p.outputStream)
|
||||
dis = DataInputStream(p.inputStream)
|
||||
dos.writeBytes("$cmd\n")
|
||||
dos.flush()
|
||||
dos.writeBytes("exit\n")
|
||||
dos.flush()
|
||||
var line: String
|
||||
while (dis.readLine().also { line = it } != null) result += line
|
||||
p.waitFor()
|
||||
} catch (_: Exception) {
|
||||
} finally {
|
||||
try {
|
||||
dos?.close()
|
||||
dis?.close()
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
return result.trim()
|
||||
}
|
37
app/src/main/java/com/fankes/miui/notify/utils/XPrefUtils.kt
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by zpp0196 on 2018/4/11.
|
||||
*/
|
||||
package com.fankes.miui.notify.utils
|
||||
|
||||
import com.fankes.miui.notify.BuildConfig
|
||||
import de.robv.android.xposed.XSharedPreferences
|
||||
|
||||
object XPrefUtils {
|
||||
|
||||
fun getBoolean(key: String, default: Boolean = false) = pref.getBoolean(key, default)
|
||||
|
||||
private val pref: XSharedPreferences
|
||||
get() {
|
||||
val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID)
|
||||
preferences.makeWorldReadable()
|
||||
preferences.reload()
|
||||
return preferences
|
||||
}
|
||||
}
|
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
@file:Suppress("SameParameterValue")
|
||||
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.graphics.drawable.RippleDrawable
|
||||
import android.graphics.drawable.RotateDrawable
|
||||
import android.os.Build
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
|
||||
private val gradientState = resolveGradientState()
|
||||
|
||||
private fun resolveGradientState(): Class<*> {
|
||||
val classes = GradientDrawable::class.java.declaredClasses
|
||||
for (singleClass in classes) {
|
||||
if (singleClass.simpleName == "GradientState") return singleClass
|
||||
}
|
||||
throw RuntimeException("GradientState could not be found in thisAny GradientDrawable implementation")
|
||||
}
|
||||
|
||||
private val rotateState = resolveRotateState()
|
||||
|
||||
private fun resolveRotateState(): Class<*> {
|
||||
val classes = RotateDrawable::class.java.declaredClasses
|
||||
for (singleClass in classes) {
|
||||
if (singleClass.simpleName == "RotateState") return singleClass
|
||||
}
|
||||
throw RuntimeException("RotateState could not be found in thisAny RotateDrawable implementation")
|
||||
}
|
||||
|
||||
@Throws(SecurityException::class, NoSuchFieldException::class)
|
||||
private fun resolveField(source: Class<*>, fieldName: String): Field {
|
||||
val field = source.getDeclaredField(fieldName)
|
||||
field.isAccessible = true
|
||||
return field
|
||||
}
|
||||
|
||||
@Throws(SecurityException::class, NoSuchMethodException::class)
|
||||
private fun resolveMethod(
|
||||
source: Class<*>,
|
||||
methodName: String,
|
||||
vararg parameterTypes: Class<*>
|
||||
): Method {
|
||||
val method = source.getDeclaredMethod(methodName, *parameterTypes)
|
||||
method.isAccessible = true
|
||||
return method
|
||||
}
|
||||
|
||||
fun setInnerRadius(drawable: GradientDrawable, value: Int) {
|
||||
try {
|
||||
val innerRadius = resolveField(gradientState, "mInnerRadius")
|
||||
innerRadius.setInt(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setInnerRadiusRatio(drawable: GradientDrawable, value: Float) {
|
||||
try {
|
||||
val innerRadius = resolveField(gradientState, "mInnerRadiusRatio")
|
||||
innerRadius.setFloat(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setThickness(drawable: GradientDrawable, value: Int) {
|
||||
try {
|
||||
val innerRadius = resolveField(gradientState, "mThickness")
|
||||
innerRadius.setInt(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setThicknessRatio(drawable: GradientDrawable, value: Float) {
|
||||
try {
|
||||
val innerRadius = resolveField(gradientState, "mThicknessRatio")
|
||||
innerRadius.setFloat(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setUseLevelForShape(drawable: GradientDrawable, value: Boolean) {
|
||||
try {
|
||||
val useLevelForShape = resolveField(gradientState, "mUseLevelForShape")
|
||||
useLevelForShape.setBoolean(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
fun setOrientation(drawable: GradientDrawable, value: GradientDrawable.Orientation) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
drawable.orientation = value
|
||||
} else {
|
||||
try {
|
||||
val orientation = resolveField(gradientState, "mOrientation")
|
||||
orientation.set(drawable.constantState, value)
|
||||
val rectIdDirty = resolveField(GradientDrawable::class.java, "mRectIsDirty")
|
||||
rectIdDirty.setBoolean(drawable, true)
|
||||
drawable.invalidateSelf()
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
fun setColors(drawable: GradientDrawable, value: IntArray) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
drawable.colors = value
|
||||
} else {
|
||||
try {
|
||||
val colors = resolveField(gradientState, "mColors")
|
||||
colors.set(drawable.constantState, value)
|
||||
drawable.invalidateSelf()
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setGradientRadiusType(drawable: GradientDrawable, value: Int) {
|
||||
try {
|
||||
val type = resolveField(gradientState, "mGradientRadiusType")
|
||||
type.setInt(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setGradientRadius(drawable: GradientDrawable, value: Float) {
|
||||
try {
|
||||
val gradientRadius = resolveField(gradientState, "mGradientRadius")
|
||||
gradientRadius.setFloat(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setStrokeColor(drawable: GradientDrawable, value: Int) {
|
||||
try {
|
||||
val type = resolveField(gradientState, "mStrokeColor")
|
||||
type.setInt(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setDrawable(rotateDrawable: RotateDrawable, drawable: Drawable) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
rotateDrawable.drawable = drawable
|
||||
} else {
|
||||
try {
|
||||
val drawableField = resolveField(rotateState, "mDrawable")
|
||||
val stateField = resolveField(RotateDrawable::class.java, "mState")
|
||||
drawableField.set(stateField.get(rotateDrawable), drawable)
|
||||
drawable.callback = rotateDrawable
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setPivotX(rotateDrawable: RotateDrawable, value: Float) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
rotateDrawable.pivotX = value
|
||||
} else {
|
||||
try {
|
||||
val pivotXField = resolveField(rotateState, "mPivotX")
|
||||
pivotXField.setFloat(rotateDrawable.constantState, value)
|
||||
val pivotXRelField = resolveField(rotateState, "mPivotXRel")
|
||||
pivotXRelField.setBoolean(rotateDrawable.constantState, true)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setPivotY(rotateDrawable: RotateDrawable, value: Float) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
rotateDrawable.pivotY = value
|
||||
} else {
|
||||
try {
|
||||
val pivotYField = resolveField(rotateState, "mPivotY")
|
||||
pivotYField.setFloat(rotateDrawable.constantState, value)
|
||||
val pivotYRelField = resolveField(rotateState, "mPivotYRel")
|
||||
pivotYRelField.setBoolean(rotateDrawable.constantState, true)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setFromDegrees(rotateDrawable: RotateDrawable, value: Float) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
rotateDrawable.fromDegrees = value
|
||||
} else {
|
||||
try {
|
||||
val fromDegreesField = resolveField(rotateState, "mFromDegrees")
|
||||
fromDegreesField.setFloat(rotateDrawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setToDegrees(rotateDrawable: RotateDrawable, value: Float) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
rotateDrawable.toDegrees = value
|
||||
} else {
|
||||
try {
|
||||
val toDegreesField = resolveField(rotateState, "mToDegrees")
|
||||
toDegreesField.setFloat(rotateDrawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setRadius(rippleDrawable: RippleDrawable, value: Int) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
rippleDrawable.radius = value
|
||||
} else {
|
||||
try {
|
||||
val setRadiusMethod =
|
||||
resolveMethod(RippleDrawable::class.java, "setMaxRadius", Int::class.java)
|
||||
setRadiusMethod.invoke(rippleDrawable, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
class Constants {
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_COLOR = 0xFFBA68C8.toInt()
|
||||
}
|
||||
}
|
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.util.StateSet
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class DrawableBuilder {
|
||||
|
||||
private var properties = DrawableProperties()
|
||||
private var order: AtomicInteger = AtomicInteger(1)
|
||||
private var transformsMap = TreeMap<Int, (Drawable) -> Drawable>()
|
||||
private var baseDrawable: Drawable? = null
|
||||
|
||||
fun batch(properties: DrawableProperties) = apply { this.properties = properties.copy() }
|
||||
fun baseDrawable(drawable: Drawable) = apply { baseDrawable = drawable }
|
||||
|
||||
// <shape>
|
||||
fun shape(shape: Int) = apply { properties.shape = shape }
|
||||
|
||||
fun rectangle() = apply { shape(GradientDrawable.RECTANGLE) }
|
||||
fun oval() = apply { shape(GradientDrawable.OVAL) }
|
||||
fun line() = apply { shape(GradientDrawable.LINE) }
|
||||
fun ring() = apply { shape(GradientDrawable.RING) }
|
||||
fun innerRadius(innerRadius: Int) = apply { properties.innerRadius = innerRadius }
|
||||
fun innerRadiusRatio(innerRadiusRatio: Float) =
|
||||
apply { properties.innerRadiusRatio = innerRadiusRatio }
|
||||
|
||||
fun thickness(thickness: Int) = apply { properties.thickness = thickness }
|
||||
fun thicknessRatio(thicknessRatio: Float) = apply { properties.thicknessRatio = thicknessRatio }
|
||||
|
||||
fun useLevelForRing(use: Boolean = true) = apply { properties.useLevelForRing = use }
|
||||
|
||||
// <corner>
|
||||
fun cornerRadius(cornerRadius: Int) = apply { properties.cornerRadius = cornerRadius }
|
||||
|
||||
fun topLeftRadius(topLeftRadius: Int) = apply { properties.topLeftRadius = topLeftRadius }
|
||||
fun topRightRadius(topRightRadius: Int) = apply { properties.topRightRadius = topRightRadius }
|
||||
fun bottomRightRadius(bottomRightRadius: Int) =
|
||||
apply { properties.bottomRightRadius = bottomRightRadius }
|
||||
|
||||
fun bottomLeftRadius(bottomLeftRadius: Int) =
|
||||
apply { properties.bottomLeftRadius = bottomLeftRadius }
|
||||
|
||||
fun rounded() = apply { cornerRadius(Int.MAX_VALUE) }
|
||||
fun cornerRadii(
|
||||
topLeftRadius: Int,
|
||||
topRightRadius: Int,
|
||||
bottomRightRadius: Int,
|
||||
bottomLeftRadius: Int
|
||||
) = apply {
|
||||
topLeftRadius(topLeftRadius); topRightRadius(topRightRadius); bottomRightRadius(
|
||||
bottomRightRadius
|
||||
); bottomLeftRadius(bottomLeftRadius)
|
||||
}
|
||||
|
||||
// <gradient>
|
||||
|
||||
fun gradient(useGradient: Boolean = true) = apply { properties.useGradient = useGradient }
|
||||
|
||||
fun gradientType(type: Int) = apply { properties.type = type }
|
||||
fun linearGradient() = apply { gradientType(GradientDrawable.LINEAR_GRADIENT) }
|
||||
fun radialGradient() = apply { gradientType(GradientDrawable.RADIAL_GRADIENT) }
|
||||
fun sweepGradient() = apply { gradientType(GradientDrawable.SWEEP_GRADIENT) }
|
||||
fun angle(angle: Int) = apply { properties.angle = angle }
|
||||
fun centerX(centerX: Float) = apply { properties.centerX = centerX }
|
||||
fun centerY(centerY: Float) = apply { properties.centerY = centerY }
|
||||
fun center(centerX: Float, centerY: Float) = apply { centerX(centerX); centerY(centerY) }
|
||||
|
||||
fun useCenterColor(useCenterColor: Boolean = true) =
|
||||
apply { properties.useCenterColor = useCenterColor }
|
||||
|
||||
fun startColor(startColor: Int) = apply { properties.startColor = startColor }
|
||||
fun centerColor(centerColor: Int) = apply {
|
||||
properties.centerColor = centerColor
|
||||
useCenterColor(true)
|
||||
}
|
||||
|
||||
fun endColor(endColor: Int) = apply { properties.endColor = endColor }
|
||||
fun gradientColors(startColor: Int, endColor: Int, centerColor: Int?) = apply {
|
||||
startColor(startColor); endColor(endColor)
|
||||
useCenterColor(centerColor != null)
|
||||
centerColor?.let {
|
||||
centerColor(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun gradientRadiusType(gradientRadiusType: Int) =
|
||||
apply { properties.gradientRadiusType = gradientRadiusType }
|
||||
|
||||
fun gradientRadius(gradientRadius: Float) = apply { properties.gradientRadius = gradientRadius }
|
||||
fun gradientRadius(radius: Float, type: Int) =
|
||||
apply { gradientRadius(radius); gradientRadiusType(type) }
|
||||
|
||||
fun gradientRadiusInPixel(radius: Float) =
|
||||
apply { gradientRadius(radius); gradientRadiusType(DrawableProperties.RADIUS_TYPE_PIXELS) }
|
||||
|
||||
fun gradientRadiusInFraction(radius: Float) =
|
||||
apply { gradientRadius(radius); gradientRadiusType(DrawableProperties.RADIUS_TYPE_FRACTION) }
|
||||
|
||||
fun useLevelForGradient(use: Boolean) = apply { properties.useLevelForGradient = use }
|
||||
fun useLevelForGradient() = apply { useLevelForGradient(true) }
|
||||
|
||||
// <size>
|
||||
fun width(width: Int) = apply { properties.width = width }
|
||||
|
||||
fun height(height: Int) = apply { properties.height = height }
|
||||
fun size(width: Int, height: Int) = apply { width(width); height(height) }
|
||||
fun size(size: Int) = apply { width(size).height(size) }
|
||||
|
||||
// <solid>
|
||||
fun solidColor(solidColor: Int) = apply { properties.solidColor = solidColor }
|
||||
|
||||
private var solidColorPressed: Int? = null
|
||||
fun solidColorPressed(color: Int?) = apply { solidColorPressed = color }
|
||||
private var solidColorPressedWhenRippleUnsupported: Int? = null
|
||||
fun solidColorPressedWhenRippleUnsupported(color: Int?) =
|
||||
apply { solidColorPressedWhenRippleUnsupported = color }
|
||||
|
||||
private var solidColorDisabled: Int? = null
|
||||
fun solidColorDisabled(color: Int?) = apply { solidColorDisabled = color }
|
||||
private var solidColorSelected: Int? = null
|
||||
fun solidColorSelected(color: Int?) = apply { solidColorSelected = color }
|
||||
fun solidColorStateList(colorStateList: ColorStateList) =
|
||||
apply { properties.solidColorStateList = colorStateList }
|
||||
|
||||
// <stroke>
|
||||
fun strokeWidth(strokeWidth: Int) = apply { properties.strokeWidth = strokeWidth }
|
||||
|
||||
fun strokeColor(strokeColor: Int) = apply { properties.strokeColor = strokeColor }
|
||||
private var strokeColorPressed: Int? = null
|
||||
fun strokeColorPressed(color: Int?) = apply { strokeColorPressed = color }
|
||||
private var strokeColorDisabled: Int? = null
|
||||
fun strokeColorDisabled(color: Int?) = apply { strokeColorDisabled = color }
|
||||
private var strokeColorSelected: Int? = null
|
||||
fun strokeColorSelected(color: Int?) = apply { strokeColorSelected = color }
|
||||
fun strokeColorStateList(colorStateList: ColorStateList) =
|
||||
apply { properties.strokeColorStateList = colorStateList }
|
||||
|
||||
fun dashWidth(dashWidth: Int) = apply { properties.dashWidth = dashWidth }
|
||||
fun dashGap(dashGap: Int) = apply { properties.dashGap = dashGap }
|
||||
fun hairlineBordered() = apply { strokeWidth(1) }
|
||||
fun shortDashed() = apply { dashWidth(12).dashGap(12) }
|
||||
fun mediumDashed() = apply { dashWidth(24).dashGap(24) }
|
||||
fun longDashed() = apply { dashWidth(36).dashGap(36) }
|
||||
fun dashed() = apply { mediumDashed() }
|
||||
|
||||
// <rotate>
|
||||
private var rotateOrder = 0
|
||||
|
||||
|
||||
fun rotate(boolean: Boolean = true) = apply {
|
||||
properties.useRotate = boolean
|
||||
rotateOrder = if (boolean) {
|
||||
order.getAndIncrement()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fun pivotX(pivotX: Float) = apply { properties.pivotX = pivotX }
|
||||
fun pivotY(pivotY: Float) = apply { properties.pivotY = pivotY }
|
||||
fun pivot(pivotX: Float, pivotY: Float) = apply { pivotX(pivotX).pivotY(pivotY) }
|
||||
fun fromDegrees(degrees: Float) = apply { properties.fromDegrees = degrees }
|
||||
fun toDegrees(degrees: Float) = apply { properties.toDegrees = degrees }
|
||||
fun degrees(fromDegrees: Float, toDegrees: Float) =
|
||||
apply { fromDegrees(fromDegrees).toDegrees(toDegrees) }
|
||||
|
||||
fun degrees(degrees: Float) = apply { fromDegrees(degrees).toDegrees(degrees) }
|
||||
fun rotate(fromDegrees: Float, toDegrees: Float) =
|
||||
apply { rotate().fromDegrees(fromDegrees).toDegrees(toDegrees) }
|
||||
|
||||
fun rotate(degrees: Float) = apply { rotate().degrees(degrees) }
|
||||
|
||||
// <scale>
|
||||
private var scaleOrder = 0
|
||||
|
||||
|
||||
fun scale(boolean: Boolean = true) = apply {
|
||||
properties.useScale = boolean
|
||||
scaleOrder = if (boolean) {
|
||||
order.getAndIncrement()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fun scaleLevel(level: Int) = apply { properties.scaleLevel = level }
|
||||
fun scaleGravity(gravity: Int) = apply { properties.scaleGravity = gravity }
|
||||
fun scaleWidth(scale: Float) = apply { properties.scaleWidth = scale }
|
||||
fun scaleHeight(scale: Float) = apply { properties.scaleHeight = scale }
|
||||
fun scale(scale: Float) = apply { scale().scaleWidth(scale).scaleHeight(scale) }
|
||||
fun scale(scaleWidth: Float, scaleHeight: Float) =
|
||||
apply { scale().scaleWidth(scaleWidth).scaleHeight(scaleHeight) }
|
||||
|
||||
// flip
|
||||
|
||||
fun flip(boolean: Boolean = true) = apply { properties.useFlip = boolean }
|
||||
|
||||
fun orientation(orientation: Int) = apply { properties.orientation = orientation }
|
||||
fun flipVertical() = apply { flip().orientation(FlipDrawable.ORIENTATION_VERTICAL) }
|
||||
|
||||
// <ripple>
|
||||
|
||||
fun ripple(boolean: Boolean = true) = apply { properties.useRipple = boolean }
|
||||
|
||||
fun rippleColor(color: Int) = apply { properties.rippleColor = color }
|
||||
fun rippleColorStateList(colorStateList: ColorStateList) =
|
||||
apply { properties.rippleColorStateList = colorStateList }
|
||||
|
||||
fun rippleRadius(radius: Int) = apply { properties.rippleRadius = radius }
|
||||
|
||||
fun build(): Drawable {
|
||||
if (baseDrawable != null) {
|
||||
return wrap(baseDrawable!!)
|
||||
}
|
||||
|
||||
var drawable: Drawable
|
||||
|
||||
// fall back when ripple is unavailable on devices with API < 21
|
||||
if (shouldFallbackRipple()) {
|
||||
if (solidColorPressedWhenRippleUnsupported != null) {
|
||||
solidColorPressed(solidColorPressedWhenRippleUnsupported)
|
||||
} else {
|
||||
solidColorPressed(properties.rippleColor)
|
||||
}
|
||||
}
|
||||
|
||||
if (needStateListDrawable()) {
|
||||
drawable = StateListDrawableBuilder()
|
||||
.pressed(buildPressedDrawable())
|
||||
.disabled(buildDisabledDrawable())
|
||||
.selected(buildSelectedDrawable())
|
||||
.normal(buildNormalDrawable())
|
||||
.build()
|
||||
} else {
|
||||
drawable = GradientDrawable()
|
||||
setupGradientDrawable(drawable)
|
||||
}
|
||||
drawable = wrap(drawable)
|
||||
return drawable
|
||||
}
|
||||
|
||||
private fun getSolidColorStateList(): ColorStateList {
|
||||
if (properties.solidColorStateList != null) {
|
||||
return properties.solidColorStateList!!
|
||||
}
|
||||
|
||||
val states = mutableListOf<IntArray>()
|
||||
val colors = mutableListOf<Int>()
|
||||
|
||||
solidColorPressed?.let {
|
||||
states.add(intArrayOf(android.R.attr.state_pressed))
|
||||
colors.add(it)
|
||||
}
|
||||
solidColorDisabled?.let {
|
||||
states.add(intArrayOf(-android.R.attr.state_enabled))
|
||||
colors.add(it)
|
||||
}
|
||||
solidColorSelected?.let {
|
||||
states.add(intArrayOf(android.R.attr.state_selected))
|
||||
colors.add(it)
|
||||
}
|
||||
states.add(StateSet.WILD_CARD)
|
||||
colors.add(properties.solidColor)
|
||||
|
||||
return ColorStateList(states.toTypedArray(), colors.toIntArray())
|
||||
}
|
||||
|
||||
private fun getStrokeColorStateList(): ColorStateList {
|
||||
if (properties.strokeColorStateList != null) {
|
||||
return properties.strokeColorStateList!!
|
||||
}
|
||||
|
||||
val states = mutableListOf<IntArray>()
|
||||
val colors = mutableListOf<Int>()
|
||||
|
||||
strokeColorPressed?.let {
|
||||
states.add(intArrayOf(android.R.attr.state_pressed))
|
||||
colors.add(it)
|
||||
}
|
||||
strokeColorDisabled?.let {
|
||||
states.add(intArrayOf(-android.R.attr.state_enabled))
|
||||
colors.add(it)
|
||||
}
|
||||
strokeColorSelected?.let {
|
||||
states.add(intArrayOf(android.R.attr.state_selected))
|
||||
colors.add(it)
|
||||
}
|
||||
states.add(StateSet.WILD_CARD)
|
||||
colors.add(properties.strokeColor)
|
||||
|
||||
return ColorStateList(states.toTypedArray(), colors.toIntArray())
|
||||
}
|
||||
|
||||
private fun buildPressedDrawable(): Drawable? {
|
||||
if (solidColorPressed == null && strokeColorPressed == null) return null
|
||||
|
||||
val pressedDrawable = GradientDrawable()
|
||||
setupGradientDrawable(pressedDrawable)
|
||||
solidColorPressed?.let {
|
||||
pressedDrawable.setColor(it)
|
||||
}
|
||||
strokeColorPressed?.let {
|
||||
setStrokeColor(pressedDrawable, it)
|
||||
}
|
||||
return pressedDrawable
|
||||
}
|
||||
|
||||
private fun buildDisabledDrawable(): Drawable? {
|
||||
if (solidColorDisabled == null && strokeColorDisabled == null) return null
|
||||
|
||||
val disabledDrawable = GradientDrawable()
|
||||
setupGradientDrawable(disabledDrawable)
|
||||
solidColorDisabled?.let {
|
||||
disabledDrawable.setColor(it)
|
||||
}
|
||||
strokeColorDisabled?.let {
|
||||
setStrokeColor(disabledDrawable, it)
|
||||
}
|
||||
return disabledDrawable
|
||||
}
|
||||
|
||||
private fun buildSelectedDrawable(): Drawable? {
|
||||
if (solidColorSelected == null && strokeColorSelected == null) return null
|
||||
|
||||
val selectedDrawable = GradientDrawable()
|
||||
setupGradientDrawable(selectedDrawable)
|
||||
solidColorSelected?.let {
|
||||
selectedDrawable.setColor(it)
|
||||
}
|
||||
strokeColorSelected?.let {
|
||||
setStrokeColor(selectedDrawable, it)
|
||||
}
|
||||
return selectedDrawable
|
||||
}
|
||||
|
||||
private fun buildNormalDrawable(): Drawable {
|
||||
val pressedDrawable = GradientDrawable()
|
||||
setupGradientDrawable(pressedDrawable)
|
||||
return pressedDrawable
|
||||
}
|
||||
|
||||
private fun setupGradientDrawable(drawable: GradientDrawable) {
|
||||
properties.apply {
|
||||
drawable.shape = shape
|
||||
if (shape == GradientDrawable.RING) {
|
||||
setInnerRadius(drawable, innerRadius)
|
||||
setInnerRadiusRatio(drawable, innerRadiusRatio)
|
||||
setThickness(drawable, thickness)
|
||||
setThicknessRatio(drawable, thicknessRatio)
|
||||
setUseLevelForShape(drawable, useLevelForRing)
|
||||
}
|
||||
drawable.cornerRadii = getCornerRadii()
|
||||
if (useGradient) {
|
||||
drawable.gradientType = type
|
||||
setGradientRadiusType(drawable, gradientRadiusType)
|
||||
setGradientRadius(drawable, gradientRadius)
|
||||
drawable.setGradientCenter(centerX, centerY)
|
||||
setOrientation(drawable, getOrientation())
|
||||
setColors(drawable, getColors())
|
||||
drawable.useLevel = useLevelForGradient
|
||||
} else {
|
||||
drawable.color = getSolidColorStateList()
|
||||
}
|
||||
drawable.setSize(width, height)
|
||||
drawable.setStroke(
|
||||
strokeWidth,
|
||||
getStrokeColorStateList(),
|
||||
dashWidth.toFloat(),
|
||||
dashGap.toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun needStateListDrawable(): Boolean {
|
||||
return (hasStrokeColorStateList() || (!properties.useGradient && hasSolidColorStateList()))
|
||||
}
|
||||
|
||||
private fun needRotateDrawable(): Boolean {
|
||||
return properties.useRotate &&
|
||||
!(properties.pivotX == 0.5f && properties.pivotY == 0.5f
|
||||
&& properties.fromDegrees == 0f && properties.toDegrees == 0f)
|
||||
}
|
||||
|
||||
private fun needScaleDrawable(): Boolean {
|
||||
return properties.useScale
|
||||
}
|
||||
|
||||
private fun wrap(drawable: Drawable): Drawable {
|
||||
var wrappedDrawable = drawable
|
||||
|
||||
if (rotateOrder > 0) {
|
||||
transformsMap[rotateOrder] = ::wrapRotateIfNeeded
|
||||
}
|
||||
if (scaleOrder > 0) {
|
||||
transformsMap[scaleOrder] = ::wrapScaleIfNeeded
|
||||
}
|
||||
|
||||
for (action in transformsMap.values) {
|
||||
wrappedDrawable = action.invoke(wrappedDrawable)
|
||||
}
|
||||
|
||||
if (properties.useFlip) {
|
||||
wrappedDrawable = FlipDrawableBuilder()
|
||||
.drawable(wrappedDrawable)
|
||||
.orientation(properties.orientation)
|
||||
.build()
|
||||
}
|
||||
|
||||
if (isRippleSupported() && properties.useRipple) {
|
||||
wrappedDrawable = RippleDrawableBuilder()
|
||||
.drawable(wrappedDrawable)
|
||||
.color(properties.rippleColor)
|
||||
.colorStateList(properties.rippleColorStateList)
|
||||
.radius(properties.rippleRadius)
|
||||
.build()
|
||||
}
|
||||
|
||||
return wrappedDrawable
|
||||
}
|
||||
|
||||
private fun shouldFallbackRipple(): Boolean {
|
||||
return properties.useRipple && !isRippleSupported()
|
||||
}
|
||||
|
||||
private fun isRippleSupported() = true
|
||||
|
||||
private fun wrapRotateIfNeeded(drawable: Drawable): Drawable {
|
||||
if (!needRotateDrawable()) return drawable
|
||||
|
||||
with(properties) {
|
||||
return RotateDrawableBuilder()
|
||||
.drawable(drawable)
|
||||
.pivotX(pivotX)
|
||||
.pivotY(pivotY)
|
||||
.fromDegrees(fromDegrees)
|
||||
.toDegrees(toDegrees)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapScaleIfNeeded(drawable: Drawable): Drawable {
|
||||
if (!needScaleDrawable()) return drawable
|
||||
|
||||
with(properties) {
|
||||
return ScaleDrawableBuilder()
|
||||
.drawable(drawable)
|
||||
.level(scaleLevel)
|
||||
.scaleGravity(scaleGravity)
|
||||
.scaleWidth(scaleWidth)
|
||||
.scaleHeight(scaleHeight)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasSolidColorStateList(): Boolean {
|
||||
return solidColorPressed != null || solidColorDisabled != null || solidColorSelected != null
|
||||
}
|
||||
|
||||
private fun hasStrokeColorStateList(): Boolean {
|
||||
return strokeColorPressed != null || strokeColorDisabled != null || strokeColorSelected != null
|
||||
}
|
||||
}
|
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
@file:Suppress("SetterBackingFieldAssignment", "unused")
|
||||
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.view.Gravity
|
||||
import java.io.Serializable
|
||||
|
||||
data class DrawableProperties(
|
||||
|
||||
// <shape>
|
||||
@JvmField var shape: Int = GradientDrawable.RECTANGLE,
|
||||
@JvmField var innerRadius: Int = -1,
|
||||
@JvmField var innerRadiusRatio: Float = 9f,
|
||||
@JvmField var thickness: Int = -1,
|
||||
@JvmField var thicknessRatio: Float = 3f,
|
||||
@JvmField var useLevelForRing: Boolean = false,
|
||||
|
||||
// <corner>
|
||||
private var _cornerRadius: Int = 0,
|
||||
@JvmField var topLeftRadius: Int = 0,
|
||||
@JvmField var topRightRadius: Int = 0,
|
||||
@JvmField var bottomRightRadius: Int = 0,
|
||||
@JvmField var bottomLeftRadius: Int = 0,
|
||||
|
||||
// <gradient>
|
||||
@JvmField var useGradient: Boolean = false,
|
||||
@JvmField var type: Int = GradientDrawable.RADIAL_GRADIENT,
|
||||
@JvmField var angle: Int = 0,
|
||||
@JvmField var centerX: Float = 0.5f,
|
||||
@JvmField var centerY: Float = 0.5f,
|
||||
@JvmField var useCenterColor: Boolean = false,
|
||||
@JvmField var startColor: Int = Constants.DEFAULT_COLOR,
|
||||
@JvmField var centerColor: Int? = null,
|
||||
@JvmField var endColor: Int = 0x7FFFFFFF,
|
||||
@JvmField var gradientRadiusType: Int = RADIUS_TYPE_FRACTION,
|
||||
@JvmField var gradientRadius: Float = 0.5f,
|
||||
@JvmField var useLevelForGradient: Boolean = false,
|
||||
|
||||
// <size>
|
||||
@JvmField var width: Int = -1,
|
||||
@JvmField var height: Int = -1,
|
||||
|
||||
// <solid>
|
||||
@JvmField var solidColor: Int = Color.TRANSPARENT,
|
||||
@JvmField var solidColorStateList: ColorStateList? = null,
|
||||
|
||||
// <stroke>
|
||||
@JvmField var strokeWidth: Int = 0,
|
||||
@JvmField var strokeColor: Int = Color.DKGRAY,
|
||||
@JvmField var strokeColorStateList: ColorStateList? = null,
|
||||
@JvmField var dashWidth: Int = 0,
|
||||
@JvmField var dashGap: Int = 0,
|
||||
|
||||
// <rotate>
|
||||
@JvmField var useRotate: Boolean = false,
|
||||
@JvmField var pivotX: Float = 0.5f,
|
||||
@JvmField var pivotY: Float = 0.5f,
|
||||
@JvmField var fromDegrees: Float = 0f,
|
||||
@JvmField var toDegrees: Float = 0f,
|
||||
|
||||
// <scale>
|
||||
@JvmField var useScale: Boolean = false,
|
||||
@JvmField var scaleLevel: Int = 10000,
|
||||
@JvmField var scaleGravity: Int = Gravity.CENTER,
|
||||
@JvmField var scaleWidth: Float = 0f,
|
||||
@JvmField var scaleHeight: Float = 0f,
|
||||
|
||||
// flip
|
||||
@JvmField var useFlip: Boolean = false,
|
||||
@JvmField var orientation: Int = FlipDrawable.ORIENTATION_HORIZONTAL,
|
||||
|
||||
// ripple
|
||||
@JvmField var useRipple: Boolean = false,
|
||||
@JvmField var rippleColor: Int = Constants.DEFAULT_COLOR,
|
||||
@JvmField var rippleColorStateList: ColorStateList? = null,
|
||||
@JvmField var rippleRadius: Int = -1
|
||||
) : Serializable {
|
||||
|
||||
companion object {
|
||||
const val RADIUS_TYPE_PIXELS = 0
|
||||
const val RADIUS_TYPE_FRACTION = 1
|
||||
|
||||
@JvmField
|
||||
val CREATOR = object : Parcelable.Creator<DrawableProperties> {
|
||||
override fun createFromParcel(parcel: Parcel): DrawableProperties {
|
||||
return DrawableProperties(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<DrawableProperties?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cornerRadius: Int = _cornerRadius
|
||||
set(value) {
|
||||
_cornerRadius = value
|
||||
topLeftRadius = value
|
||||
topRightRadius = value
|
||||
bottomRightRadius = value
|
||||
bottomLeftRadius = value
|
||||
}
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readFloat(),
|
||||
parcel.readInt(),
|
||||
parcel.readFloat(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readFloat(),
|
||||
parcel.readFloat(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readFloat(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readParcelable(ColorStateList::class.java.classLoader),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readParcelable(ColorStateList::class.java.classLoader),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readFloat(),
|
||||
parcel.readFloat(),
|
||||
parcel.readFloat(),
|
||||
parcel.readFloat(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readFloat(),
|
||||
parcel.readFloat(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readParcelable(ColorStateList::class.java.classLoader),
|
||||
parcel.readInt()
|
||||
)
|
||||
|
||||
fun copy(): DrawableProperties {
|
||||
val parcel = Parcel.obtain()
|
||||
parcel.setDataPosition(0)
|
||||
val properties = CREATOR.createFromParcel(parcel)
|
||||
parcel.recycle()
|
||||
return properties
|
||||
}
|
||||
|
||||
fun getCornerRadii(): FloatArray {
|
||||
return floatArrayOf(
|
||||
topLeftRadius.toFloat(), topLeftRadius.toFloat(),
|
||||
topRightRadius.toFloat(), topRightRadius.toFloat(),
|
||||
bottomRightRadius.toFloat(), bottomRightRadius.toFloat(),
|
||||
bottomLeftRadius.toFloat(), bottomLeftRadius.toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
fun getOrientation(): GradientDrawable.Orientation {
|
||||
val orientation: GradientDrawable.Orientation = when (val angle = this.angle % 360) {
|
||||
0 -> GradientDrawable.Orientation.LEFT_RIGHT
|
||||
45 -> GradientDrawable.Orientation.BL_TR
|
||||
90 -> GradientDrawable.Orientation.BOTTOM_TOP
|
||||
135 -> GradientDrawable.Orientation.BR_TL
|
||||
180 -> GradientDrawable.Orientation.RIGHT_LEFT
|
||||
225 -> GradientDrawable.Orientation.TR_BL
|
||||
270 -> GradientDrawable.Orientation.TOP_BOTTOM
|
||||
315 -> GradientDrawable.Orientation.TL_BR
|
||||
else -> throw IllegalArgumentException("Unsupported angle: $angle")
|
||||
}
|
||||
return orientation
|
||||
}
|
||||
|
||||
fun getColors(): IntArray {
|
||||
return if (useCenterColor && centerColor != null) {
|
||||
intArrayOf(startColor, centerColor!!, endColor)
|
||||
} else intArrayOf(startColor, endColor)
|
||||
}
|
||||
|
||||
fun materialization(): Drawable = DrawableBuilder().batch(this).build()
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
|
||||
abstract class DrawableWrapperBuilder<T: DrawableWrapperBuilder<T>> {
|
||||
|
||||
protected var drawable: Drawable? = null
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun drawable(drawable: Drawable): T = apply { this.drawable = drawable } as T
|
||||
|
||||
abstract fun build(): Drawable
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "CanvasSize")
|
||||
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
|
||||
class FlipDrawable(
|
||||
private var drawable: Drawable,
|
||||
private var orientation: Int = ORIENTATION_HORIZONTAL
|
||||
) : Drawable() {
|
||||
|
||||
companion object {
|
||||
const val ORIENTATION_HORIZONTAL = 0
|
||||
const val ORIENTATION_VERTICAL = 1
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
val saveCount = canvas.save()
|
||||
if (orientation == ORIENTATION_VERTICAL) {
|
||||
canvas.scale(1f, -1f, (canvas.width / 2).toFloat(), (canvas.height / 2).toFloat())
|
||||
} else {
|
||||
canvas.scale(-1f, 1f, (canvas.width / 2).toFloat(), (canvas.height / 2).toFloat())
|
||||
}
|
||||
drawable.bounds = Rect(0, 0, canvas.width, canvas.height)
|
||||
drawable.draw(canvas)
|
||||
canvas.restoreToCount(saveCount)
|
||||
}
|
||||
|
||||
override fun onLevelChange(level: Int): Boolean {
|
||||
drawable.level = level
|
||||
invalidateSelf()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIntrinsicWidth(): Int {
|
||||
return drawable.intrinsicWidth
|
||||
}
|
||||
|
||||
override fun getIntrinsicHeight(): Int {
|
||||
return drawable.intrinsicHeight
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
drawable.alpha = alpha
|
||||
}
|
||||
|
||||
override fun getOpacity(): Int {
|
||||
return drawable.opacity
|
||||
}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {
|
||||
drawable.colorFilter = colorFilter
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
|
||||
class FlipDrawableBuilder : DrawableWrapperBuilder<FlipDrawableBuilder>() {
|
||||
|
||||
private var orientation: Int = FlipDrawable.ORIENTATION_HORIZONTAL
|
||||
|
||||
fun orientation(orientation: Int) = apply { this.orientation = orientation }
|
||||
|
||||
override fun build(): Drawable {
|
||||
return FlipDrawable(drawable!!, orientation)
|
||||
}
|
||||
}
|
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Build
|
||||
import android.view.Gravity
|
||||
import androidx.annotation.RequiresApi
|
||||
import java.util.*
|
||||
|
||||
class LayerDrawableBuilder {
|
||||
|
||||
companion object {
|
||||
const val DIMEN_UNDEFINED = Int.MIN_VALUE
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private var paddingMode = LayerDrawable.PADDING_MODE_NEST
|
||||
private var paddingLeft = 0
|
||||
private var paddingTop = 0
|
||||
private var paddingRight = 0
|
||||
private var paddingBottom = 0
|
||||
private var paddingStart = 0
|
||||
private var paddingEnd = 0
|
||||
private val layers = ArrayList<Layer>()
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
fun paddingMode(mode: Int) = apply { paddingMode = mode }
|
||||
fun paddingLeft(padding: Int) = apply { paddingLeft = padding }
|
||||
fun paddingTop(padding: Int) = apply { paddingTop = padding }
|
||||
fun paddingRight(padding: Int) = apply { paddingRight = padding }
|
||||
fun paddingBottom(padding: Int) = apply { paddingBottom = padding }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun paddingStart(padding: Int) = apply { paddingStart = padding }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun paddingEnd(padding: Int) = apply { paddingEnd = padding }
|
||||
fun padding(padding: Int) = apply {
|
||||
paddingLeft(padding).paddingTop(padding).paddingRight(padding).paddingBottom(padding)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun paddingRelative(padding: Int) = apply {
|
||||
paddingStart(padding).paddingTop(padding).paddingEnd(padding).paddingBottom(padding)
|
||||
}
|
||||
|
||||
fun add(drawable: Drawable) = apply { layers.add(Layer(drawable)) }
|
||||
|
||||
fun width(width: Int) = apply { layers.last().width = width }
|
||||
fun height(height: Int) = apply { layers.last().height = height }
|
||||
|
||||
fun insetLeft(inset: Int) = apply { layers.last().insetLeft = inset }
|
||||
fun insetTop(inset: Int) = apply { layers.last().insetTop = inset }
|
||||
fun insetRight(inset: Int) = apply { layers.last().insetRight = inset }
|
||||
fun insetBottom(inset: Int) = apply { layers.last().insetBottom = inset }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun insetStart(inset: Int) = apply { layers.last().insetStart = inset }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun insetEnd(inset: Int) = apply { layers.last().insetEnd = inset }
|
||||
fun inset(inset: Int) =
|
||||
apply { insetLeft(inset).insetTop(inset).insetRight(inset).insetBottom(inset) }
|
||||
|
||||
fun inset(insetLeft: Int, insetTop: Int, insetRight: Int, insetBottom: Int) = apply {
|
||||
insetLeft(insetLeft).insetTop(insetTop).insetRight(insetRight).insetBottom(insetBottom)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun insetRelative(inset: Int) =
|
||||
apply { insetStart(inset).insetTop(inset).insetEnd(inset).insetBottom(inset) }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun insetRelative(insetStart: Int, insetTop: Int, insetEnd: Int, insetBottom: Int) = apply {
|
||||
insetStart(insetStart).insetTop(insetTop).insetEnd(insetEnd).insetBottom(insetBottom)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun gravity(gravity: Int) = apply { layers.last().gravity = gravity }
|
||||
|
||||
fun modify(
|
||||
index: Int, drawable: Drawable,
|
||||
width: Int = DIMEN_UNDEFINED,
|
||||
height: Int = DIMEN_UNDEFINED,
|
||||
insetLeft: Int = DIMEN_UNDEFINED,
|
||||
insetTop: Int = DIMEN_UNDEFINED,
|
||||
insetRight: Int = DIMEN_UNDEFINED,
|
||||
insetBottom: Int = DIMEN_UNDEFINED,
|
||||
insetStart: Int = DIMEN_UNDEFINED,
|
||||
insetEnd: Int = DIMEN_UNDEFINED
|
||||
) =
|
||||
apply {
|
||||
val layer = layers[index]
|
||||
layer.drawable = drawable
|
||||
if (width != DIMEN_UNDEFINED) layer.width = width
|
||||
if (height != DIMEN_UNDEFINED) layer.height = height
|
||||
if (insetLeft != DIMEN_UNDEFINED) layer.insetLeft = insetLeft
|
||||
if (insetTop != DIMEN_UNDEFINED) layer.insetTop = insetTop
|
||||
if (insetRight != DIMEN_UNDEFINED) layer.insetRight = insetRight
|
||||
if (insetBottom != DIMEN_UNDEFINED) layer.insetBottom = insetBottom
|
||||
if (insetStart != DIMEN_UNDEFINED) layer.insetStart = insetStart
|
||||
if (insetEnd != DIMEN_UNDEFINED) layer.insetEnd = insetEnd
|
||||
}
|
||||
|
||||
fun build(): LayerDrawable {
|
||||
val layerDrawable = LayerDrawable(layers.map { it -> it.drawable }.toTypedArray())
|
||||
for (i in 0 until layers.size) {
|
||||
val layer = layers[i]
|
||||
layerDrawable.setLayerInset(
|
||||
i,
|
||||
layer.insetLeft,
|
||||
layer.insetTop,
|
||||
layer.insetRight,
|
||||
layer.insetBottom
|
||||
)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (layer.insetStart != DIMEN_UNDEFINED || layer.insetEnd != DIMEN_UNDEFINED) {
|
||||
layerDrawable.setLayerInsetRelative(
|
||||
i,
|
||||
layer.insetStart,
|
||||
layer.insetTop,
|
||||
layer.insetEnd,
|
||||
layer.insetBottom
|
||||
)
|
||||
}
|
||||
}
|
||||
layerDrawable.setId(i, i)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
layerDrawable.setLayerGravity(i, layer.gravity)
|
||||
layerDrawable.setLayerInsetStart(i, layer.insetStart)
|
||||
layerDrawable.setLayerInsetEnd(i, layer.insetEnd)
|
||||
}
|
||||
}
|
||||
layerDrawable.paddingMode = paddingMode
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
layerDrawable.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom)
|
||||
if (paddingStart != DIMEN_UNDEFINED || paddingEnd != DIMEN_UNDEFINED) {
|
||||
layerDrawable.setPaddingRelative(
|
||||
paddingStart,
|
||||
paddingTop,
|
||||
paddingEnd,
|
||||
paddingBottom
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return layerDrawable
|
||||
}
|
||||
|
||||
internal class Layer(var drawable: Drawable) {
|
||||
var gravity: Int = Gravity.NO_GRAVITY
|
||||
var width: Int = -1
|
||||
var height: Int = -1
|
||||
var insetLeft: Int = 0
|
||||
var insetTop: Int = 0
|
||||
var insetRight: Int = 0
|
||||
var insetBottom: Int = 0
|
||||
var insetStart: Int = DIMEN_UNDEFINED
|
||||
var insetEnd: Int = DIMEN_UNDEFINED
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.Path
|
||||
import android.graphics.drawable.ShapeDrawable
|
||||
import android.graphics.drawable.shapes.PathShape
|
||||
|
||||
class PathShapeDrawableBuilder {
|
||||
|
||||
private var path: Path? = null
|
||||
private var pathStandardWidth: Float = 100f
|
||||
private var pathStandardHeight: Float = 100f
|
||||
private var width: Int = -1
|
||||
private var height: Int = -1
|
||||
|
||||
fun path(path: Path, pathStandardWidth: Float, pathStandardHeight: Float) = apply {
|
||||
this.path = path
|
||||
this.pathStandardWidth = pathStandardWidth
|
||||
this.pathStandardHeight = pathStandardHeight
|
||||
}
|
||||
|
||||
fun width(width: Int) = apply { this.width = width }
|
||||
fun height(height: Int) = apply { this.height = height }
|
||||
fun size(size: Int) = apply { width(size).height(size) }
|
||||
|
||||
fun build(custom: ((shapeDrawable: ShapeDrawable) -> Unit)? = null): ShapeDrawable {
|
||||
val shapeDrawable = ShapeDrawable()
|
||||
if (path == null || width <= 0 || height <= 0) {
|
||||
return shapeDrawable
|
||||
}
|
||||
val pathShape = PathShape(path!!, pathStandardWidth, pathStandardHeight)
|
||||
|
||||
shapeDrawable.shape = pathShape
|
||||
shapeDrawable.intrinsicWidth = width
|
||||
shapeDrawable.intrinsicHeight = height
|
||||
if (custom != null) {
|
||||
custom(shapeDrawable)
|
||||
}
|
||||
return shapeDrawable
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.*
|
||||
import android.util.StateSet
|
||||
|
||||
class RippleDrawableBuilder : DrawableWrapperBuilder<RippleDrawableBuilder>() {
|
||||
|
||||
private var color: Int = Constants.DEFAULT_COLOR
|
||||
private var colorStateList: ColorStateList? = null
|
||||
private var radius: Int = -1
|
||||
|
||||
fun color(color: Int) = apply { this.color = color }
|
||||
fun colorStateList(colorStateList: ColorStateList?) =
|
||||
apply { this.colorStateList = colorStateList }
|
||||
|
||||
fun radius(radius: Int) = apply { this.radius = radius }
|
||||
|
||||
override fun build(): Drawable {
|
||||
var drawable = this.drawable!!
|
||||
val colorStateList = this.colorStateList ?: ColorStateList(
|
||||
arrayOf(StateSet.WILD_CARD),
|
||||
intArrayOf(color)
|
||||
)
|
||||
|
||||
var mask = if (drawable is DrawableContainer) drawable.getCurrent() else drawable
|
||||
if (mask is ShapeDrawable) {
|
||||
val state = mask.getConstantState()
|
||||
if (state != null) {
|
||||
val temp = state.newDrawable().mutate() as ShapeDrawable
|
||||
temp.paint.color = Color.BLACK
|
||||
mask = temp
|
||||
}
|
||||
} else if (mask is GradientDrawable) {
|
||||
val state = mask.getConstantState()
|
||||
if (state != null) {
|
||||
val temp = state.newDrawable().mutate() as GradientDrawable
|
||||
temp.setColor(Color.BLACK)
|
||||
mask = temp
|
||||
}
|
||||
} else {
|
||||
mask = ColorDrawable(Color.BLACK)
|
||||
}
|
||||
|
||||
val rippleDrawable = RippleDrawable(colorStateList, drawable, mask)
|
||||
setRadius(rippleDrawable, radius)
|
||||
rippleDrawable.invalidateSelf()
|
||||
drawable = rippleDrawable
|
||||
return drawable
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.RotateDrawable
|
||||
|
||||
class RotateDrawableBuilder : DrawableWrapperBuilder<RotateDrawableBuilder>() {
|
||||
|
||||
private var pivotX: Float = 0.5f
|
||||
private var pivotY: Float = 0.5f
|
||||
private var fromDegrees: Float = 0f
|
||||
private var toDegrees: Float = 360f
|
||||
|
||||
fun pivotX(x: Float) = apply { pivotX = x }
|
||||
fun pivotY(y: Float) = apply { pivotY = y }
|
||||
fun fromDegrees(degree: Float) = apply { fromDegrees = degree }
|
||||
fun toDegrees(degree: Float) = apply { toDegrees = degree }
|
||||
|
||||
override fun build(): Drawable {
|
||||
val rotateDrawable = RotateDrawable()
|
||||
drawable?.let {
|
||||
setDrawable(rotateDrawable, it)
|
||||
apply {
|
||||
setPivotX(rotateDrawable, pivotX)
|
||||
setPivotY(rotateDrawable, pivotY)
|
||||
setFromDegrees(rotateDrawable, fromDegrees)
|
||||
setToDegrees(rotateDrawable, toDegrees)
|
||||
}
|
||||
}
|
||||
return rotateDrawable
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.ScaleDrawable
|
||||
import android.view.Gravity
|
||||
|
||||
class ScaleDrawableBuilder : DrawableWrapperBuilder<ScaleDrawableBuilder>() {
|
||||
|
||||
private var level: Int = 10000
|
||||
private var scaleGravity = Gravity.CENTER
|
||||
private var scaleWidth: Float = 0f
|
||||
private var scaleHeight: Float = 0f
|
||||
|
||||
fun level(level: Int) = apply { this.level = level }
|
||||
fun scaleGravity(gravity: Int) = apply { this.scaleGravity = gravity }
|
||||
fun scaleWidth(scale: Float) = apply { this.scaleWidth = scale }
|
||||
fun scaleHeight(scale: Float) = apply { this.scaleHeight = scale }
|
||||
|
||||
override fun build(): Drawable {
|
||||
val scaleDrawable = ScaleDrawable(drawable, scaleGravity, scaleWidth, scaleHeight)
|
||||
scaleDrawable.level = level
|
||||
return scaleDrawable
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.miui.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.StateListDrawable
|
||||
import android.util.StateSet
|
||||
|
||||
class StateListDrawableBuilder {
|
||||
|
||||
private var pressed: Drawable? = null
|
||||
private var disabled: Drawable? = null
|
||||
private var selected: Drawable? = null
|
||||
private var normal: Drawable = ColorDrawable(Color.TRANSPARENT)
|
||||
|
||||
fun pressed(pressed: Drawable?) = apply { this.pressed = pressed }
|
||||
fun disabled(disabled: Drawable?) = apply { this.disabled = disabled }
|
||||
fun selected(selected: Drawable?) = apply { this.selected = selected }
|
||||
fun normal(normal: Drawable) = apply { this.normal = normal }
|
||||
|
||||
fun build(): StateListDrawable {
|
||||
val stateListDrawable = StateListDrawable()
|
||||
setupStateListDrawable(stateListDrawable)
|
||||
return stateListDrawable
|
||||
}
|
||||
|
||||
private fun setupStateListDrawable(stateListDrawable: StateListDrawable) {
|
||||
pressed?.let {
|
||||
stateListDrawable.addState(intArrayOf(android.R.attr.state_pressed), it)
|
||||
}
|
||||
disabled?.let {
|
||||
stateListDrawable.addState(intArrayOf(-android.R.attr.state_enabled), it)
|
||||
}
|
||||
selected?.let {
|
||||
stateListDrawable.addState(intArrayOf(android.R.attr.state_selected), it)
|
||||
}
|
||||
stateListDrawable.addState(StateSet.WILD_CARD, normal)
|
||||
}
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of MIUINativeNotifyIcon.
|
||||
*
|
||||
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
|
||||
@file:Suppress("SameParameterValue")
|
||||
|
||||
package com.fankes.miui.notify.view
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import com.fankes.miui.notify.utils.dp
|
||||
import com.fankes.miui.notify.utils.drawable.drawabletoolbox.DrawableBuilder
|
||||
|
||||
class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(context, attrs) {
|
||||
|
||||
private fun toColors(selected: Int, pressed: Int, normal: Int): ColorStateList {
|
||||
val colors = intArrayOf(selected, pressed, normal)
|
||||
val states = arrayOfNulls<IntArray>(3)
|
||||
states[0] = intArrayOf(android.R.attr.state_checked)
|
||||
states[1] = intArrayOf(android.R.attr.state_pressed)
|
||||
states[2] = intArrayOf()
|
||||
return ColorStateList(states, colors)
|
||||
}
|
||||
|
||||
init {
|
||||
trackDrawable = DrawableBuilder()
|
||||
.rectangle()
|
||||
.rounded()
|
||||
.solidColor(0xFF656565.toInt())
|
||||
.height(20.dp)
|
||||
.cornerRadius(15.dp)
|
||||
.build()
|
||||
thumbDrawable = DrawableBuilder()
|
||||
.rectangle()
|
||||
.rounded()
|
||||
.solidColor(Color.WHITE)
|
||||
.size(20.dp, 20.dp)
|
||||
.cornerRadius(20.dp)
|
||||
.strokeWidth(2.dp)
|
||||
.strokeColor(Color.TRANSPARENT)
|
||||
.build()
|
||||
trackTintList = toColors(
|
||||
0xFF656565.toInt(),
|
||||
0xFFCCCCCC.toInt(),
|
||||
0xFFCCCCCC.toInt()
|
||||
)
|
||||
}
|
||||
}
|
6
app/src/main/res/drawable/dark_round.xml
Executable file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#661B1B1B" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
6
app/src/main/res/drawable/green_round.xml
Executable file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#26A69A" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
6
app/src/main/res/drawable/permotion_round.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#66E4E4E4" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
6
app/src/main/res/drawable/red_round.xml
Executable file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#FF786F" />
|
||||
<corners android:radius="30dp" />
|
||||
</shape>
|
6
app/src/main/res/drawable/white_round.xml
Executable file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#FFFFFF" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
448
app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,448 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.MainActivity"
|
||||
tools:ignore="HardcodedText,UseCompoundDrawables,ContentDescription">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center|start"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="13dp"
|
||||
android:paddingRight="15dp"
|
||||
android:paddingBottom="5dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
android:text="MIUI 原生通知图标"
|
||||
android:textColor="#FF323B42"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/title_restart_icon"
|
||||
style="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:src="@mipmap/restart"
|
||||
android:tint="#FF323B42" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_lin_status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="@drawable/dark_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/main_img_status"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:layout_marginStart="25dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:src="@mipmap/warn"
|
||||
android:tint="@color/white" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:text="模块未激活"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_version"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:alpha="0.8"
|
||||
android:text="当前版本:%1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_miui_version"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.8"
|
||||
android:text="MIUI 版本:%1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="13sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:fadingEdgeLength="10dp"
|
||||
android:fillViewport="true"
|
||||
android:requiresFadingEdge="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:padding="15dp">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:alpha="0.85"
|
||||
android:src="@mipmap/about"
|
||||
android:tint="#FF777777" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="5dp"
|
||||
android:text="修改设置或开启关闭模块请点击右上角重启系统界面才能生效。"
|
||||
android:textColor="#FF323B42"
|
||||
android:textSize="11sp"
|
||||
tools:ignore="SmallSp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<com.fankes.miui.notify.view.MaterialSwitch
|
||||
android:id="@+id/module_enable_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="启用模块"
|
||||
android:textColor="#FF323B42"
|
||||
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="#777777"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<com.fankes.miui.notify.view.MaterialSwitch
|
||||
android:id="@+id/color_icon_fix_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="修复彩色通知图标"
|
||||
android:textColor="#FF323B42"
|
||||
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="此选项默认开启,开启后将自动对应用通知图标进行判断,保持彩色图标不被着色为白色或黑色色块,包括高版本 API 仍然不遵守原生通知图标规范的种种 APP 都将进行修复。"
|
||||
android:textColor="#777777"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<com.fankes.miui.notify.view.MaterialSwitch
|
||||
android:id="@+id/chat_icon_fix_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="优化社交应用通知图标"
|
||||
android:textColor="#FF323B42"
|
||||
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="此选项默认开启,开启后将对 QQ 和微信的消息图标进行色彩修复,使得它们的图标看起来更加美观。"
|
||||
android:textColor="#777777"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<com.fankes.miui.notify.view.MaterialSwitch
|
||||
android:id="@+id/hide_icon_in_launcher_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="在桌面隐藏模块图标"
|
||||
android:textColor="#FF323B42"
|
||||
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="隐藏模块图标后,模块不会再在桌面显示,你可以在 EdXposed、太极、LsPosed 中找到模块设置并打开,对原生系统无效。"
|
||||
android:textColor="#777777"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center|start">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:alpha="0.85"
|
||||
android:src="@mipmap/about"
|
||||
android:tint="#FF323B42" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.85"
|
||||
android:singleLine="true"
|
||||
android:text="使用帮助与说明"
|
||||
android:textColor="#FF323B42"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="Q.这个模块是如何诞生的?\nA.这个模块诞生来源于 MIUI 的乱改和不规范,本来 MIUI 9 之后,官方给出了原生通知图标样式,后面由于用户反应通知栏经常出现黑白块,这当然不是系统的错,而是国内 APP 极其不规范的通知图标设计,于是 MIUI 选择直接忽略这个问题把全部图标都改成了 APP 的彩色图标,使得之前拥有自有样式的原生图标也被破坏,通知中“setSmallIcon”不再有效,这个模块就是为了修复被 MIUI 开发组忽略的图标问题,并完美地给 MIUI 修复了黑白块图标的问题。"
|
||||
android:textColor="#777777"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="Q.如何使用?\nA.模块仅支持 EdXposed、LsPosed,由于涉及到修改系统应用,不支持太极(阴)框架,在 LsPosed 的作用域中,只需勾选“系统界面”即可,应用设置后需要重启系统界面。"
|
||||
android:textColor="#777777"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="Q.哪些是已知问题?\nA.MIUI 自己的系统应用的通知图标仍然可能出现黑白块的情况,暂时只能等官方修复。"
|
||||
android:textColor="#777777"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="Q.如何反馈问题?\nA.酷安关注 @星夜不荟"
|
||||
android:textColor="#777777"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/link_with_project_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center|start">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:alpha="0.85"
|
||||
android:src="@mipmap/about"
|
||||
android:tint="#FF323B42" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.85"
|
||||
android:singleLine="true"
|
||||
android:text="项目地址"
|
||||
android:textColor="#FF323B42"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="本软件是免费开源项目,遵循 GPL 协议,你可以点击这里前往 Github 查看源码以及获取模块更新。\n严禁以任何形式贩卖、商用本软件,否则开发者有权追究其法律责任。"
|
||||
android:textColor="#777777"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@drawable/permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/link_with_follow_me"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="恰饭时间\n点击前往酷安关注我,获取我的更多应用"
|
||||
android:textColor="#FF323B42"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:cardCornerRadius="15dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@mipmap/qr_pay" />
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="开发者 酷安 @星夜不荟\n未经允许不得转载、修改复制我的劳动成果"
|
||||
android:textColor="#FF323B42"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 775 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/about.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/icon_launcher.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/qr_pay.png
Executable file
After Width: | Height: | Size: 201 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/restart.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/succcess.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/warn.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 11 KiB |
16
app/src/main/res/values-night/themes.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.MIUINativeNotifyIcon" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
10
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#656565</color>
|
||||
<color name="purple_500">#656565</color>
|
||||
<color name="purple_700">#656565</color>
|
||||
<color name="teal_200">#656565</color>
|
||||
<color name="teal_700">#656565</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
4
app/src/main/res/values/ic_launcher_background.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#E06818</color>
|
||||
</resources>
|
3
app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">MIUI 原生通知图标</string>
|
||||
</resources>
|
16
app/src/main/res/values/themes.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.MIUINativeNotifyIcon" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/teal_700</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
@@ -0,0 +1,17 @@
|
||||
package com.fankes.miui.notify
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
32
build.gradle
Normal file
@@ -0,0 +1,32 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
ext.kotlin_version = "1.6.10"
|
||||
repositories {
|
||||
google()
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
|
||||
maven { url "https://www.jitpack.io" }
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:7.0.4"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
|
||||
maven { url "https://www.jitpack.io" }
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
21
gradle.properties
Normal file
@@ -0,0 +1,21 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app"s APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Mon Jan 24 04:31:03 CST 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
185
gradlew
vendored
Executable file
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
89
gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
BIN
keystore/public
Normal file
2
settings.gradle
Normal file
@@ -0,0 +1,2 @@
|
||||
rootProject.name = "MIUINativeNotifyIcon"
|
||||
include ':app'
|