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