├── SCOPE
├── SUMMARY
├── SOURCE_URL
├── code
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ ├── xposed_init
│ │ │ │ └── easygo.json
│ │ │ ├── res
│ │ │ │ ├── values-w1240dp
│ │ │ │ │ └── dimens.xml
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── off.png
│ │ │ │ │ ├── snhj.png
│ │ │ │ │ ├── ysxb.png
│ │ │ │ │ ├── caixing.png
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ ├── ic_menu_send.png
│ │ │ │ │ ├── notepaad_win11.png
│ │ │ │ │ ├── sunglass_huaji.png
│ │ │ │ │ ├── ic_launcher_round.webp
│ │ │ │ │ └── ic_lockscreen_answer_active.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── jinfan.png
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── values-land
│ │ │ │ │ └── dimens.xml
│ │ │ │ ├── values-w600dp
│ │ │ │ │ └── dimens.xml
│ │ │ │ ├── values
│ │ │ │ │ ├── dimens.xml
│ │ │ │ │ ├── arrays.xml
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── themes.xml
│ │ │ │ │ └── strings.xml
│ │ │ │ ├── xml
│ │ │ │ │ └── network_security_config.xml
│ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ ├── drawable
│ │ │ │ │ ├── ic_baseline_keyboard_arrow_up_24.xml
│ │ │ │ │ ├── ic_baseline_keyboard_arrow_down_24.xml
│ │ │ │ │ ├── ic_baseline_polymer_24.xml
│ │ │ │ │ ├── ic_baseline_music_note_24.xml
│ │ │ │ │ ├── ic_baseline_privacy_tip_24.xml
│ │ │ │ │ ├── ic_baseline_verified_user_24.xml
│ │ │ │ │ ├── bg_stroke_corner_grey.xml
│ │ │ │ │ ├── ic_baseline_double_arrow_24.xml
│ │ │ │ │ ├── ic_baseline_dynamic_form_24_notify_merge.xml
│ │ │ │ │ ├── ic_baseline_refresh_24.xml
│ │ │ │ │ ├── ic_baseline_ac_unit_24.xml
│ │ │ │ │ ├── ic_baseline_follow_the_signs_24.xml
│ │ │ │ │ ├── ic_baseline_policy_24.xml
│ │ │ │ │ ├── ic_baseline_dynamic_feed_24_notify_filter.xml
│ │ │ │ │ ├── ic_baseline_nat_24.xml
│ │ │ │ │ ├── ic_baseline_qr_code_scanner_24.xml
│ │ │ │ │ ├── ic_baseline_alt_route_24.xml
│ │ │ │ │ ├── ic_baseline_grass_24.xml
│ │ │ │ │ ├── github.xml
│ │ │ │ │ ├── ic_baseline_sports_kabaddi_24.xml
│ │ │ │ │ ├── magisk.xml
│ │ │ │ │ └── ic_launcher_background.xml
│ │ │ │ ├── values-v31
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ └── themes.xml
│ │ │ │ ├── menu
│ │ │ │ │ ├── menu_about.xml
│ │ │ │ │ └── menu_main.xml
│ │ │ │ ├── values-night
│ │ │ │ │ ├── themes.xml
│ │ │ │ │ └── colors.xml
│ │ │ │ ├── drawable-v24
│ │ │ │ │ └── ic_launcher_foreground.xml
│ │ │ │ └── layout
│ │ │ │ │ ├── still_not_done.xml
│ │ │ │ │ ├── content_scrolling.xml
│ │ │ │ │ ├── activity_about.xml
│ │ │ │ │ └── activity_main.xml
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── coolest
│ │ │ │ │ └── toolbox
│ │ │ │ │ ├── utils
│ │ │ │ │ ├── MainThreadHelper.kt
│ │ │ │ │ ├── TimeUtil.kt
│ │ │ │ │ ├── ShowHideAnimUtil.java
│ │ │ │ │ ├── BingDailyWallpaper.kt
│ │ │ │ │ ├── CoolConfigHelper.kt
│ │ │ │ │ ├── ReleaseNotesUtil.kt
│ │ │ │ │ ├── CoolConfigHelper_28.kt
│ │ │ │ │ ├── GithubUpdateUtils.kt
│ │ │ │ │ └── ViewUtils.java
│ │ │ │ │ ├── MyApplication.kt
│ │ │ │ │ ├── xposed
│ │ │ │ │ ├── ContextHelper.kt
│ │ │ │ │ └── HookEntry.kt
│ │ │ │ │ └── ui
│ │ │ │ │ ├── KotlinUtils.kt
│ │ │ │ │ ├── CoolUtils.java
│ │ │ │ │ └── AboutActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── test
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── coolest
│ │ │ │ └── toolbox
│ │ │ │ └── ExampleUnitTest.kt
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── coolest
│ │ │ └── toolbox
│ │ │ └── ExampleInstrumentedTest.kt
│ ├── release
│ │ ├── app-release.apk
│ │ └── output-metadata.json
│ ├── proguard-rules.pro
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── settings.gradle
├── build.gradle
├── gradle.properties
├── gradlew.bat
└── gradlew
└── README.md
/SCOPE:
--------------------------------------------------------------------------------
1 | [".android.settings","com.xiaomi.mirror"]
2 |
--------------------------------------------------------------------------------
/SUMMARY:
--------------------------------------------------------------------------------
1 | 解锁小米平板MIUI+连电脑, 同时让不支持MIUI+的手机支持MIUI+
2 |
--------------------------------------------------------------------------------
/SOURCE_URL:
--------------------------------------------------------------------------------
1 | https://github.com/CoolestEnoch/MIUI_Tools
2 |
--------------------------------------------------------------------------------
/code/app/src/main/assets/xposed_init:
--------------------------------------------------------------------------------
1 | com.coolest.toolbox.xposed.HookEntry
--------------------------------------------------------------------------------
/code/app/release/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/release/app-release.apk
--------------------------------------------------------------------------------
/code/app/src/main/res/values-w1240dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 200dp
3 |
--------------------------------------------------------------------------------
/code/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxhdpi/off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxhdpi/off.png
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxhdpi/snhj.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxhdpi/snhj.png
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxhdpi/ysxb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxhdpi/ysxb.png
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxhdpi/caixing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxhdpi/caixing.png
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxxhdpi/jinfan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxxhdpi/jinfan.png
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxhdpi/ic_menu_send.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxhdpi/ic_menu_send.png
--------------------------------------------------------------------------------
/code/app/src/main/res/values-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 | 48dp
4 |
--------------------------------------------------------------------------------
/code/app/src/main/res/values-w600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 | 48dp
4 |
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxhdpi/notepaad_win11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxhdpi/notepaad_win11.png
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxhdpi/sunglass_huaji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxhdpi/sunglass_huaji.png
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-xxhdpi/ic_lockscreen_answer_active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolestEnoch/MIUI_Tools/HEAD/code/app/src/main/res/mipmap-xxhdpi/ic_lockscreen_answer_active.png
--------------------------------------------------------------------------------
/code/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 180dp
3 | 16dp
4 | 16dp
5 |
--------------------------------------------------------------------------------
/code/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/code/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Mar 16 11:39:32 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/code/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/code/settings.gradle:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
3 | repositories {
4 | google()
5 | mavenCentral()
6 | jcenter() // Warning: this repository is going to shut down soon
7 | }
8 | }
9 | rootProject.name = "MIUI工具箱"
10 | include ':app'
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MIUI工具箱
2 |
3 | 可解锁平板通过MIUI+连接电脑
4 |
5 | 可修改MIUI+内电脑名字和电脑小尾巴
6 |
7 | 还有一些其他功能
8 |
9 |
10 |
11 | **更新随缘**
12 |
13 | [LSPosed仓库链接](https://github.com/Xposed-Modules-Repo/com.coolest.toolbox)
14 |
15 | [最新版下载](https://github.com/CoolestEnoch/MIUI_Tools/releases/latest)
16 |
17 | [手机版MIUI+下载链接](https://github.com/CoolestEnoch/MIUI_Tools/releases/download/V1.6/MIUI%2B_phone_3.5.21.apk)
18 | (其实从手机上提取一个装平板上不就得了(受虐滑稽))
19 |
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/utils/MainThreadHelper.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.utils
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 |
6 |
7 | val mainHandler: Handler by lazy {
8 | Handler(Looper.getMainLooper())
9 | }
10 |
11 | fun runOnMainThread(r: Runnable) {
12 | if (Looper.myLooper() == Looper.getMainLooper()) {
13 | r.run()
14 | } else {
15 | mainHandler.post(r)
16 | }
17 | }
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/main/res/values-v31/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @color/material_dynamic_primary40
4 | @color/material_dynamic_neutral70
5 | @color/material_dynamic_neutral_variant70
6 | @color/material_dynamic_tertiary70
7 | @color/material_dynamic_tertiary90
8 |
--------------------------------------------------------------------------------
/code/app/src/test/java/com/coolest/toolbox/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/release/output-metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "artifactType": {
4 | "type": "APK",
5 | "kind": "Directory"
6 | },
7 | "applicationId": "com.coolest.toolbox",
8 | "variantName": "release",
9 | "elements": [
10 | {
11 | "type": "SINGLE",
12 | "filters": [],
13 | "attributes": [],
14 | "versionCode": 1,
15 | "versionName": "3.01",
16 | "outputFile": "app-release.apk"
17 | }
18 | ],
19 | "elementType": "File"
20 | }
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_polymer_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_music_note_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_privacy_tip_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | - com.xiaomi.mirror
7 | - com.android.settings
8 | - com.miui.misound
9 | - android
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_verified_user_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/bg_stroke_corner_grey.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
15 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_double_arrow_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/code/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 | dependencies {
8 | classpath "com.android.tools.build:gradle:7.0.1"
9 | classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | task clean(type: Delete) {
17 | delete rootProject.buildDir
18 | }
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_dynamic_form_24_notify_merge.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_refresh_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/MyApplication.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import android.graphics.Bitmap
6 | import android.graphics.BitmapFactory
7 | import com.coolest.toolbox.utils.getTodayWallpaperURL
8 | import java.io.DataInputStream
9 | import java.net.URL
10 | import kotlin.concurrent.thread
11 |
12 | class MyApplication : Application() {
13 |
14 | companion object {
15 | var dailyBingPaper: Bitmap? = null
16 | var appContext: Context? = null
17 | }
18 |
19 | override fun onCreate() {
20 | super.onCreate()
21 |
22 | }
23 | }
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/xposed/ContextHelper.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.xposed
2 |
3 | import android.app.AndroidAppHelper
4 | import android.content.Context
5 | import com.github.kyuubiran.ezxhelper.utils.findMethod
6 | import com.github.kyuubiran.ezxhelper.utils.hookMethod
7 | import de.robv.android.xposed.XC_MethodHook
8 | import de.robv.android.xposed.XposedBridge
9 | import de.robv.android.xposed.XposedHelpers.findAndHookMethod
10 | import de.robv.android.xposed.XposedHelpers.findClass
11 | import de.robv.android.xposed.callbacks.XC_LoadPackage
12 |
13 |
14 | fun getContext() = AndroidAppHelper.currentApplication().applicationContext
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_ac_unit_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/utils/TimeUtil.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.utils
2 |
3 | import java.text.ParsePosition
4 | import java.text.SimpleDateFormat
5 |
6 | /**
7 | * Timestamp to String
8 | * @param Timestamp
9 | * @return String
10 | */
11 | public fun transToString(time: Long): String {
12 | return SimpleDateFormat("YYYY年MM月DD日 hh时mm分ss秒").format(time)
13 | }
14 |
15 | /**
16 | * String to Timestamp
17 | * @param String
18 | * @return Timestamp
19 | */
20 |
21 | public fun transToTimeStamp(date: String): Long {
22 | //2022-03-19T15:03:55Z
23 | return SimpleDateFormat("YYYY-MM-DD'T'hh:mm:ss'Z'").parse(date, ParsePosition(0)).time
24 | }
25 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_follow_the_signs_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/main/res/menu/menu_about.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_policy_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_dynamic_feed_24_notify_filter.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_nat_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_qr_code_scanner_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/androidTest/java/com/coolest/toolbox/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.coolest.toolbox", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/code/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_alt_route_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_grass_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/code/app/src/main/res/values-v31/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/code/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF4EAFF5
9 | #ff444444
10 | #FF000000
11 | #FFFFFFFF
12 |
13 | #FFFF0000
14 | #FFFF7F00
15 | #FFFFFF00
16 | #FF00FF00
17 | #FF00FFFF
18 | #FF0000FF
19 | #FF8B00FF
20 |
21 |
22 | @color/teal_200
23 |
24 |
25 | @color/black_icon
26 |
--------------------------------------------------------------------------------
/code/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/github.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
--------------------------------------------------------------------------------
/code/app/src/main/assets/easygo.json:
--------------------------------------------------------------------------------
1 | {
2 | "easyGoVersion": "1.0",
3 | "client": "com.coolest.toolbox",
4 | "logicEntities": [
5 | {
6 | "head": {
7 | "function": "magicwindow",
8 | "required": "true"
9 | },
10 | "body": {
11 | "mode": "1",
12 | "defaultDualActivities": {
13 | "mainPages": "net.micode.notes.ui.NotesListActivity",
14 | "relatedPage": ""
15 | },
16 | "Activities": [
17 | {
18 | "name": "net.micode.notes.ui.NoteEditActivity",
19 | "defaultFullScreen": "false"
20 | }
21 | ],
22 | "activityPairs": [
23 | {
24 | "from": "net.micode.notes.ui.NotesListActivity",
25 | "to": "*"
26 | }
27 | ],
28 | "UX": {
29 | "windowsRatio": [
30 | {
31 | "device": "PAD",
32 | "ratio": "900|1654"
33 | }
34 | ],
35 | "supportRotationUxCompat": "true",
36 | "isDraggable": "true"
37 | }
38 | }
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/code/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF4EAFF5
9 | #ff444444
10 | #FF000000
11 | #FFFFFFFF
12 |
13 | #FFFF0000
14 | #FFFF7F00
15 | #FFFFFF00
16 | #FF00FF00
17 | #FF00FFFF
18 | #FF0000FF
19 | #FF8B00FF
20 |
21 |
22 | @color/black
23 |
24 | @color/purple_500
25 | @color/light_blue
26 | @color/light_blue
27 | #00aaaaaa
28 |
29 | #e8e8e8
30 |
--------------------------------------------------------------------------------
/code/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/ui/KotlinUtils.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.ui
2 |
3 | import android.Manifest
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.google.android.material.snackbar.Snackbar
6 | import com.permissionx.guolindev.PermissionX
7 |
8 | class KotlinUtils {
9 |
10 | companion object {
11 |
12 | fun request_permissions(activity: AppCompatActivity) {
13 | PermissionX.init(activity)
14 | .permissions(
15 | Manifest.permission.WRITE_EXTERNAL_STORAGE,
16 | Manifest.permission.READ_EXTERNAL_STORAGE,
17 | Manifest.permission.MANAGE_EXTERNAL_STORAGE
18 | )
19 | .explainReasonBeforeRequest()
20 | .onExplainRequestReason { scope, deniedList ->
21 | scope.showRequestReasonDialog(deniedList, "配置文件存取权限", "好", "取消")
22 | }
23 | .onForwardToSettings { scope, deniedList ->
24 | scope.showForwardToSettingsDialog(
25 | deniedList,
26 | "不授予权限则可能出现闪退!",
27 | "好",
28 | "取消"
29 | )
30 | }
31 | .request { allGranted, grantedList, deniedList ->
32 | if (allGranted) {
33 | Snackbar.make(
34 | activity.window.decorView,
35 | "权限状态正常",
36 | Snackbar.LENGTH_LONG
37 | ).show()
38 | } else {
39 | Snackbar.make(
40 | activity.window.decorView,
41 | "以下权限被拒绝: $deniedList",
42 | Snackbar.LENGTH_LONG
43 | ).show()
44 | }
45 | }
46 |
47 | /*//判断能否在在安卓11以上的设备免root管理Android/data
48 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
49 | //表明已经有这个权限了
50 | val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
51 | startActivity(intent)
52 | }*/
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_baseline_sports_kabaddi_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/code/app/src/main/res/layout/still_not_done.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
13 |
14 |
17 |
18 |
21 |
22 |
26 |
27 |
34 |
35 |
43 |
44 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/code/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
12 |
13 |
23 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
38 |
41 |
44 |
47 |
50 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/utils/ShowHideAnimUtil.java:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.utils;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 |
7 | public class ShowHideAnimUtil {
8 | /**
9 | * 将view从可见变为不可见的动画,原理:动态改变其LayoutParams.height的值
10 | * @param view 要展示动画的view
11 | */
12 | public static void hideAnimator_height(final View view){
13 | if(view!=null){
14 | int viewHeight=view.getHeight();
15 | if(viewHeight==0){
16 | int width=View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
17 | int height=View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
18 | view.measure(width,height);
19 | viewHeight=view.getMeasuredHeight();
20 | }
21 | ValueAnimator animator=ValueAnimator.ofInt(viewHeight,0);
22 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
23 | @Override
24 | public void onAnimationUpdate(ValueAnimator animation) {
25 | ViewGroup.LayoutParams params=view.getLayoutParams();
26 | params.height= (int) animation.getAnimatedValue();
27 | view.setLayoutParams(params);
28 | }
29 | });
30 | animator.start();
31 | }
32 | }
33 |
34 | /**
35 | * 动态改变view的高度动画效果,动画时长300毫秒[android属性动画默认时长]
36 | * 原理:动画改变view LayoutParams.height的值
37 | * @param view 要进行高度改变动画的view
38 | * @param startHeight 动画前的view的高度
39 | * @param endHeight 动画后的view的高度
40 | */
41 | public static void showAnimator_height(final View view, final int startHeight, final int endHeight){
42 | if(view!=null&&startHeight>=0&&endHeight>=0){
43 | ValueAnimator animator=ValueAnimator.ofInt(startHeight,endHeight);
44 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
45 | @Override
46 | public void onAnimationUpdate(ValueAnimator animation) {
47 | ViewGroup.LayoutParams params=view.getLayoutParams();
48 | params.height= (int) animation.getAnimatedValue();
49 | view.setLayoutParams(params);
50 | }
51 | });
52 | animator.start();
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/code/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | compileSdk 31
8 |
9 | defaultConfig {
10 | applicationId "com.coolest.toolbox"
11 | minSdk 25
12 | targetSdk 31
13 | versionCode 1
14 | versionName "3.01"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildFeatures{
20 | viewBinding true
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 | compileOptions {
30 | sourceCompatibility JavaVersion.VERSION_1_8
31 | targetCompatibility JavaVersion.VERSION_1_8
32 | }
33 | kotlinOptions {
34 | jvmTarget = '1.8'
35 | }
36 | }
37 |
38 | dependencies {
39 |
40 | // implementation 'androidx.core:core-ktx:1.3.2'
41 | implementation 'androidx.appcompat:appcompat:1.2.0'
42 | implementation 'com.google.android.material:material:1.3.0'
43 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
44 | testImplementation 'junit:junit:4.+'
45 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
47 |
48 | compileOnly 'de.robv.android.xposed:api:82'
49 | compileOnly 'de.robv.android.xposed:api:82:sources'
50 | implementation 'com.github.kyuubiran:EzXHelper:0.6.3'
51 |
52 | implementation 'com.guolindev.permissionx:permissionx:1.6.1'
53 |
54 | api 'com.google.android.material:material:1.5.0'
55 | implementation 'com.afollestad.material-dialogs:core:3.2.1'
56 | implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0'
57 |
58 | implementation 'com.github.bumptech.glide:glide:4.9.0'
59 | implementation 'com.squareup.okhttp3:okhttp:4.8.1'
60 |
61 | implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
62 | implementation 'jp.wasabeef:glide-transformations:4.0.1'
63 | }
--------------------------------------------------------------------------------
/code/app/src/main/res/layout/content_scrolling.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
20 |
23 |
24 |
28 |
29 |
38 |
39 |
48 |
49 |
56 |
57 |
58 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/code/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/utils/BingDailyWallpaper.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.utils
2 |
3 | import android.content.Context
4 | import android.text.Spannable
5 | import android.text.SpannableStringBuilder
6 | import android.text.style.AbsoluteSizeSpan
7 | import android.util.Log
8 | import android.widget.ImageView
9 | import com.bumptech.glide.Glide
10 | import com.google.android.material.snackbar.Snackbar
11 | import de.robv.android.xposed.XposedBridge
12 | import jp.wasabeef.glide.transformations.BlurTransformation
13 | import okhttp3.OkHttpClient
14 | import okhttp3.Request
15 | import org.json.JSONObject
16 | import kotlin.concurrent.thread
17 |
18 |
19 | //implementation 'com.github.bumptech.glide:glide:4.9.0'
20 | //implementation 'com.squareup.okhttp3:okhttp:4.8.1'
21 |
22 | fun getTodayWallpaperURL(): String {
23 | //调用微软必应壁纸API获取json
24 | var imgUrl: String = ""
25 | var microsoftApiResponseJson: String? = ""
26 | val client = OkHttpClient()
27 | val request = Request.Builder()
28 | .url("https://cn.bing.com/HPImageArchive.aspx?n=1&format=js&idx=0")
29 | .build()
30 | val response = client.newCall(request).execute()
31 | microsoftApiResponseJson = response.body?.string()
32 | Log.e("okhttp", "$microsoftApiResponseJson")
33 |
34 | //解析json获取图片地址
35 | val jsonArray = JSONObject(microsoftApiResponseJson).getJSONArray("images")
36 | for (i in 0 until jsonArray.length()) {
37 | val jsonObject = jsonArray.get(i) as JSONObject
38 | if (jsonObject.has("url")) {
39 | imgUrl = jsonObject.getString("url")
40 | }
41 | }
42 |
43 | return "https://cn.bing.com/$imgUrl"
44 | }
45 |
46 | fun setBingDailyWallpaper(imageView: ImageView, context: Context, blurRadius: Int) {
47 | thread {
48 | try {
49 | val imgUrl = getTodayWallpaperURL()
50 | Log.e("okhttp", imgUrl)
51 | runOnMainThread {
52 | try {
53 | when {
54 | blurRadius < 1 -> Glide.with(context)
55 | .load(imgUrl)
56 | .into(imageView)
57 | else -> Glide.with(context).load(imgUrl)
58 | .transform(BlurTransformation(blurRadius))
59 | .into(imageView)
60 | }
61 | // val text = "关于 壁纸来源: Bing每日壁纸"
62 | // val sb = SpannableStringBuilder(text)
63 | // sb.setSpan(
64 | // AbsoluteSizeSpan(ViewUtils_Kotlin.dp2px(context, 34).toInt()),
65 | // 0,
66 | // 4,
67 | // Spannable.SPAN_INCLUSIVE_INCLUSIVE
68 | // )
69 | // sb.setSpan(
70 | // AbsoluteSizeSpan(ViewUtils_Kotlin.dp2px(context, 12).toInt()),
71 | // 4,
72 | // text.length,
73 | // Spannable.SPAN_INCLUSIVE_INCLUSIVE
74 | // )
75 |
76 | // binding.toolbarLayout.title = sb
77 | } catch (e: java.lang.Exception) {
78 | e.printStackTrace()
79 | XposedBridge.log(e)
80 | }
81 | }
82 | } catch (e: Exception) {
83 | if (e.toString().contains("Unable to resolve host \"api.github.com\"")) {
84 | Snackbar.make(
85 | imageView.rootView,
86 | "无法连接到必应服务器, 请检查网络",
87 | Snackbar.LENGTH_LONG
88 | ).show()
89 | } else {
90 | e.printStackTrace()
91 | XposedBridge.log(e)
92 | }
93 | }
94 | }
95 | }
--------------------------------------------------------------------------------
/code/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
31 |
32 |
41 |
42 |
52 |
53 |
64 |
65 |
72 |
73 |
77 |
78 |
84 |
85 |
88 |
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/utils/CoolConfigHelper.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.utils
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.ContentValues
5 | import android.content.Context
6 | import android.graphics.Bitmap
7 | import android.net.Uri
8 | import android.os.Build
9 | import android.os.Environment
10 | import android.provider.MediaStore
11 | import androidx.annotation.RequiresApi
12 | import com.coolest.toolbox.ui.CoolUtils
13 | import com.github.kyuubiran.ezxhelper.utils.Log
14 | import de.robv.android.xposed.XposedBridge
15 | import org.json.JSONArray
16 | import org.json.JSONObject
17 | import java.io.*
18 |
19 | class CoolConfigHelper {
20 | companion object {
21 | //const val configTxtFile = File(Context().externalCacheDir, "config.txt")
22 | private var configFileStr =
23 | Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
24 | .toString() + "/config.txt"
25 |
26 | // private var configFileStr = "/storage/emulated/0/Download/config.txt"
27 |
28 | /* fun initJsonConfig(): Boolean {
29 | try {
30 | //保存为json
31 | val obj = JSONObject().apply {
32 | put("miui_plus_regard_as_phone", false)
33 | }
34 |
35 | //把json转字符串并保存到本地
36 | val content = obj.toString()
37 | val file = File(configTxtFile)
38 |
39 | // if file doesnt exists, then create it
40 | if (!file.exists()) {
41 | file.createNewFile()
42 | }
43 | val fw = FileWriter(file.absoluteFile)
44 | val bw = BufferedWriter(fw)
45 | bw.write(content)
46 | bw.close()
47 | } catch (e: Exception) {
48 | e.printStackTrace()
49 |
50 | return false
51 | }
52 |
53 | return true
54 | }*/
55 |
56 | fun initJsonConfig(context: Context): Boolean {
57 | try {
58 | //保存为json
59 | val obj = JSONObject().apply {
60 | put("miui_plus_connect_pc", !CoolUtils.isPad())
61 | put("miui_plus_cust_pc_name_switch", false)
62 | put("miui_plus_pc_name", "")
63 | put("miui_plus_pc_tail", "")
64 | put("dolby_enabled", CoolUtils.isPad())
65 | put("harmanKardon_enabled", true)
66 | }
67 | Log.e(obj.toString())
68 |
69 | val myFile = File(configFileStr)
70 | myFile.printWriter().use { out ->
71 | out.println(obj.toString())
72 | }
73 | } catch (e: Exception) {
74 | e.printStackTrace()
75 | return false
76 | }
77 | return true
78 | }
79 |
80 |
81 | fun modifyJsonConfig(context: Context, key: String, value: Any): Boolean {
82 | try {
83 | //文件不存在就创建
84 | if (!File(configFileStr).exists()) {
85 | initJsonConfig(context)
86 | }
87 |
88 |
89 | //读取配置文件
90 | val jsonContent = StringBuilder()
91 | val input = FileInputStream(File(configFileStr))
92 | val reader = BufferedReader(InputStreamReader(input))
93 | reader.use {
94 | reader.forEachLine {
95 | jsonContent.append(it)
96 | }
97 | }
98 |
99 | //改配置
100 | val jsonString = jsonContent.toString()
101 | val obj = JSONObject(jsonString)
102 | obj.put(key, value)//修改json配置文件的配置项。put会覆盖旧值
103 |
104 | //保存文件
105 | val myFile = File(configFileStr)
106 | myFile.printWriter().use { out ->
107 | out.println(obj.toString())
108 | }
109 |
110 | } catch (e: Exception) {
111 | e.printStackTrace()
112 | return false
113 | }
114 |
115 | return true
116 | }
117 |
118 |
119 | fun getJsonConfig(context: Context, key: String): Any? {
120 | //文件不存在就创建
121 | val jsonContent = StringBuilder()
122 | try {
123 | val input = FileInputStream(File(configFileStr))
124 | val reader = BufferedReader(InputStreamReader(input))
125 | reader.use {
126 | reader.forEachLine {
127 | jsonContent.append(it)
128 | }
129 | }
130 |
131 | val jsonString = jsonContent.toString()
132 | val json = JSONObject(jsonString)//拿到的配置文件json object
133 | return json.get(key)
134 | } catch (e: Exception) {
135 | e.printStackTrace()
136 | return null
137 | }
138 | }
139 | }
140 | }
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/magisk.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
19 |
22 |
25 |
28 |
31 |
34 |
37 |
40 |
43 |
46 |
49 |
52 |
--------------------------------------------------------------------------------
/code/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MIUI工具箱
3 | ScrollingActivity
4 |
5 | "Material is the metaphor.\n\n"
6 |
7 | "A material metaphor is the unifying theory of a rationalized space and a system of motion."
8 | "The material is grounded in tactile reality, inspired by the study of paper and ink, yet "
9 | "technologically advanced and open to imagination and magic.\n"
10 | "Surfaces and edges of the material provide visual cues that are grounded in reality. The "
11 | "use of familiar tactile attributes helps users quickly understand affordances. Yet the "
12 | "flexibility of the material creates new affordances that supercede those in the physical "
13 | "world, without breaking the rules of physics.\n"
14 | "The fundamentals of light, surface, and movement are key to conveying how objects move, "
15 | "interact, and exist in space and in relation to each other. Realistic lighting shows "
16 | "seams, divides space, and indicates moving parts.\n\n"
17 |
18 | "Bold, graphic, intentional.\n\n"
19 |
20 | "The foundational elements of print based design typography, grids, space, scale, color, "
21 | "and use of imagery guide visual treatments. These elements do far more than please the "
22 | "eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge "
23 | "imagery, large scale typography, and intentional white space create a bold and graphic "
24 | "interface that immerse the user in the experience.\n"
25 | "An emphasis on user actions makes core functionality immediately apparent and provides "
26 | "waypoints for the user.\n\n"
27 |
28 | "Motion provides meaning.\n\n"
29 |
30 | "Motion respects and reinforces the user as the prime mover. Primary user actions are "
31 | "inflection points that initiate motion, transforming the whole design.\n"
32 | "All action takes place in a single environment. Objects are presented to the user without "
33 | "breaking the continuity of experience even as they transform and reorganize.\n"
34 | "Motion is meaningful and appropriate, serving to focus attention and maintain continuity. "
35 | "Feedback is subtle yet clear. Transitions are efficient yet coherent.\n\n"
36 |
37 | "3D world.\n\n"
38 |
39 | "The material environment is a 3D space, which means all objects have x, y, and z "
40 | "dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the "
41 | "positive z-axis extending towards the viewer. Every sheet of material occupies a single "
42 | "position along the z-axis and has a standard 1dp thickness.\n"
43 | "On the web, the z-axis is used for layering and not for perspective. The 3D world is "
44 | "emulated by manipulating the y-axis.\n\n"
45 |
46 | "Light and shadow.\n\n"
47 |
48 | "Within the material environment, virtual lights illuminate the scene. Key lights create "
49 | "directional shadows, while ambient light creates soft shadows from all angles.\n"
50 | "Shadows in the material environment are cast by these two light sources. In Android "
51 | "development, shadows occur when light sources are blocked by sheets of material at "
52 | "various positions along the z-axis. On the web, shadows are depicted by manipulating the "
53 | "y-axis only. The following example shows the card with a height of 6dp.\n\n"
54 |
55 | "Resting elevation.\n\n"
56 |
57 | "All material objects, regardless of size, have a resting elevation, or default elevation "
58 | "that does not change. If an object changes elevation, it should return to its resting "
59 | "elevation as soon as possible.\n\n"
60 |
61 | "Component elevations.\n\n"
62 |
63 | "The resting elevation for a component type is consistent across apps (e.g., FAB elevation "
64 | "does not vary from 6dp in one app to 16dp in another app).\n"
65 | "Components may have different resting elevations across platforms, depending on the depth "
66 | "of the environment (e.g., TV has a greater depth than mobile or desktop).\n\n"
67 |
68 | "Responsive elevation and dynamic elevation offsets.\n\n"
69 |
70 | "Some component types have responsive elevation, meaning they change elevation in response "
71 | "to user input (e.g., normal, focused, and pressed) or system events. These elevation "
72 | "changes are consistently implemented using dynamic elevation offsets.\n"
73 | "Dynamic elevation offsets are the goal elevation that a component moves towards, relative "
74 | "to the component’s resting state. They ensure that elevation changes are consistent "
75 | "across actions and component types. For example, all components that lift on press have "
76 | "the same elevation change relative to their resting elevation.\n"
77 | "Once the input event is completed or cancelled, the component will return to its resting "
78 | "elevation.\n\n"
79 |
80 | "Avoiding elevation interference.\n\n"
81 |
82 | "Components with responsive elevations may encounter other components as they move between "
83 | "their resting elevations and dynamic elevation offsets. Because material cannot pass "
84 | "through other material, components avoid interfering with one another any number of ways, "
85 | "whether on a per component basis or using the entire app layout.\n"
86 | "On a component level, components can move or be removed before they cause interference. "
87 | "For example, a floating action button (FAB) can disappear or move off screen before a "
88 | "user picks up a card, or it can move if a snackbar appears.\n"
89 | "On the layout level, design your app layout to minimize opportunities for interference. "
90 | "For example, position the FAB to one side of stream of a cards so the FAB won’t interfere "
91 | "when a user tries to pick up one of cards.\n\n"
92 |
93 | Settings
94 | AboutActivity
95 |
--------------------------------------------------------------------------------
/code/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/code/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/utils/ReleaseNotesUtil.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.utils
2 |
3 | import android.content.Context
4 | import android.util.Log
5 | import android.widget.LinearLayout
6 | import com.coolest.toolbox.R
7 | import android.widget.ScrollView
8 | import android.view.ViewGroup
9 | import androidx.appcompat.app.AlertDialog
10 | import java.lang.StringBuilder
11 | import java.util.*
12 |
13 | object ReleaseNotesUtil {
14 | var noteList: MutableList = ArrayList()
15 | var alert: AlertDialog? = null
16 | var builder: AlertDialog.Builder? = null
17 | const val tab_and_new_line = "\n"
18 | fun showAlertDialog(title: String?, str: String?, context: Context?) {
19 | builder = AlertDialog.Builder(context!!)
20 | alert = builder!!
21 | .setTitle(title)
22 | .setMessage(str)
23 | .setPositiveButton("确定") { dialog, which -> }.create() //创建AlertDialog对象
24 | alert!!.show() //显示对话框
25 | /*alert = builder!!
26 | .setTitle(title)
27 | .setMessage(str)
28 | .setNegativeButton("取消") { dialog, which ->
29 | Toast.makeText(
30 | context,
31 | "你点击了取消按钮~",
32 | Toast.LENGTH_SHORT
33 | ).show()
34 | }
35 | .setPositiveButton("确定", object : DialogInterface.OnClickListener {
36 | fun onClck(dialog: DialogInterface?, which: Int) {
37 | Toast.makeText(context, "你点击了确定按钮~", Toast.LENGTH_SHORT).show()
38 | }
39 | })
40 | .setNeutralButton("中立") { dialog, which ->
41 | Toast.makeText(
42 | context,
43 | "你点击了中立按钮~",
44 | Toast.LENGTH_SHORT
45 | ).show()
46 | }
47 | .create() //创建AlertDialog对象
48 | alert!!.show() //显示对话框*/
49 | }
50 |
51 | fun setReleaseNotes() {
52 | noteList.apply {
53 | add(Version(1.0, "第一个版本"))
54 | add(
55 | Version(
56 | 1.1, "[优化]在已root设备上的启动速度"
57 | + tab_and_new_line + "[新增]root授权防误触"
58 | + tab_and_new_line + "[新增]能看更新日志啦~"
59 | )
60 | )
61 | add(Version(1.2, "[新增]适配MIUI设备小白条沉浸" + tab_and_new_line + "[新增]适配MIUI平行世界"))
62 | add(
63 | Version(
64 | 1.3,
65 | "[新增]添加XPosed模块支持" + tab_and_new_line + "[新增]支持解锁米板5 MIUI+连电脑" + tab_and_new_line + "[新增]让运行MIUI12.5及以上系统的不支持MIUI+的手机支持MIUI+"
66 | )
67 | )
68 | add(Version(1.4, "[新增]连接MIUI+之后可修改本机显示的电脑名"))
69 | add(
70 | Version(
71 | 1.5,
72 | "[优化]大幅重构了UI" + tab_and_new_line + "[新增]支持修改MIUI+设置里电脑的小尾巴" + tab_and_new_line + "[新增]关于作者页及其每日壁纸背景图(需联网)" + tab_and_new_line + "[新增]支持在线检查更新和查询所有历史版本"
73 | )
74 | )
75 | add(Version(1.6, "[修复]操作过快导致的几率性闪退问题" + tab_and_new_line + "[修复]安卓12上检查更新可能引发的闪退"))
76 | add(
77 | Version(
78 | 1.7,
79 | "[修复]非MIUI设备上的显示问题" + tab_and_new_line + "[新增]更新渠道切换" + tab_and_new_line + "[新增]部分按钮高斯模糊" + tab_and_new_line + "[修复]部分场景下的闪退问题" + tab_and_new_line + "[修复]时间显示错误"
80 | )
81 | )
82 | add(Version(1.71, "[优化]部分场景的流畅度"))
83 | add(Version(1.72, "[优化]重构部分底层代码"))
84 | add(Version(2.0, "[适配]平板上版本号为3.5.17c的MIUI+"))
85 | add(Version(2.5, "[新增]解锁系统小窗应用个数从2个改成20个"))
86 | add(Version(2.51, "[修复]在非MIUI for Pad系统上会莫名其妙出现一堆FC的bug"))
87 | add(
88 | Version(
89 | 2.6,
90 | "[新增]解锁哈曼卡顿(仅在米10U和米板5的第一个MIUI13稳定版上测试过可用)" + tab_and_new_line + "[修复]深色模式显示异常"
91 | )
92 | )
93 | add(Version(2.61, "[改动]哈曼卡顿默认自动开启" + tab_and_new_line + "[优化]日志系统优化"))
94 | add(
95 | Version(
96 | 3.0,
97 | "[新增]MIUI13手机也能开好几个小窗啦" + tab_and_new_line + "[新增]任何程序皆可小窗" + tab_and_new_line + "[新增]锁定手机管家分数100分" + tab_and_new_line + "[新增]适配Material You动态配色(需Android 12或更高版本)"
98 | )
99 | )
100 | add(Version(3.01, "[修复]JoyUI无法使用的问题"))
101 | }
102 | Collections.reverse(noteList)
103 | }
104 |
105 | val releaseNotes: String
106 | get() {
107 | if (noteList.isEmpty()) setReleaseNotes()
108 | val sb = StringBuilder()
109 | for (v in noteList) {
110 | sb.append(v)
111 | }
112 | return sb.toString()
113 | }
114 |
115 | fun showReleaseNotes(context: Context) {
116 | // showAlertDialog("更新日志" + " " + "\n当前版本" + CoolUtils.getAppVersionName(context), getReleaseNotes(), context);
117 | releaseNotes
118 | val linearLayout = LinearLayout(context)
119 | val params = LinearLayout.LayoutParams(
120 | LinearLayout.LayoutParams.MATCH_PARENT,
121 | LinearLayout.LayoutParams.WRAP_CONTENT
122 | )
123 | linearLayout.layoutParams = params
124 | linearLayout.orientation = LinearLayout.VERTICAL
125 | Log.e("for", "before")
126 | for (version in noteList) {
127 | val list = LinkedList()
128 | list.add(version!!.version.toString())
129 | val detail = version.notes.split(tab_and_new_line.toRegex()).toTypedArray()
130 | for (str in detail) {
131 | list.add(str)
132 | }
133 | linearLayout.addView(
134 | ViewUtils_Kotlin.createBigButton(
135 | context,
136 | R.drawable.ic_baseline_grass_24,
137 | R.color.item_card_bg,
138 | list, null, R.color.white
139 | ) {}
140 | )
141 | Log.e("for", "run in")
142 | }
143 | Log.e("for", "after")
144 | val scrollView = ScrollView(context)
145 | val scrollParams: ViewGroup.LayoutParams = LinearLayout.LayoutParams(
146 | LinearLayout.LayoutParams.MATCH_PARENT,
147 | LinearLayout.LayoutParams.WRAP_CONTENT
148 | )
149 | scrollView.layoutParams = scrollParams
150 | scrollView.addView(linearLayout)
151 | ViewUtils.getBigCardFromBottom(
152 | context,
153 | "更新日志\t当前版本${
154 | context.packageManager.getPackageInfo(
155 | context.packageName,
156 | 0
157 | ).versionName
158 | }",
159 | scrollView
160 | ).show()
161 | }
162 |
163 | fun showLastReleaseNotes(context: Context?) {
164 | // showAlertDialog("更新日志" + " " + "\n当前版本" + CoolUtils.getAppVersionName(context), getReleaseNotes(), context);
165 | releaseNotes
166 | val linearLayout = LinearLayout(context)
167 | val params = LinearLayout.LayoutParams(
168 | LinearLayout.LayoutParams.MATCH_PARENT,
169 | LinearLayout.LayoutParams.WRAP_CONTENT
170 | )
171 | linearLayout.layoutParams = params
172 | linearLayout.orientation = LinearLayout.VERTICAL
173 | val version = noteList[0]
174 | val list = LinkedList()
175 | list.add(version!!.version.toString())
176 | val detail = version.notes.split(tab_and_new_line.toRegex()).toTypedArray()
177 | for (str in detail) {
178 | list.add(str)
179 | }
180 | linearLayout.addView(
181 | ViewUtils.createCardView(
182 | context,
183 | R.color.item_card_bg,
184 | R.drawable.ic_baseline_grass_24,
185 | list
186 | )
187 | )
188 | Log.e("for", "run in")
189 | val scrollView = ScrollView(context)
190 | val scrollParams: ViewGroup.LayoutParams = LinearLayout.LayoutParams(
191 | LinearLayout.LayoutParams.MATCH_PARENT,
192 | LinearLayout.LayoutParams.WRAP_CONTENT
193 | )
194 | scrollView.layoutParams = scrollParams
195 | scrollView.addView(linearLayout)
196 | ViewUtils.getBigCardFromBottom(context, "这个版本更新了啥", scrollView).show()
197 | }
198 |
199 | class Version(var version: Double, var notes: String) {
200 | override fun toString(): String {
201 | return "$version $notes\n"
202 | }
203 | }
204 | }
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/ui/CoolUtils.java:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.ui;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageInfo;
5 | import android.content.pm.PackageManager;
6 | import android.os.Build;
7 | import android.os.Environment;
8 | import android.text.TextUtils;
9 | import android.util.Log;
10 |
11 | import java.io.BufferedReader;
12 | import java.io.DataOutputStream;
13 | import java.io.File;
14 | import java.io.FileInputStream;
15 | import java.io.IOException;
16 | import java.io.InputStreamReader;
17 | import java.util.Properties;
18 |
19 | import de.robv.android.xposed.XposedBridge;
20 |
21 | public class CoolUtils {
22 |
23 | public final String config_path = "/sdcard";
24 |
25 | /**
26 | * 判断是否为MIUI系统
27 | *
28 | * @return
29 | */
30 | public static boolean isMIUI() {
31 | String manufacturer = Build.MANUFACTURER.toLowerCase();
32 |
33 | return !TextUtils.isEmpty(manufacturer) && (manufacturer.equals("xiaomi") || manufacturer.equals("blackshark"));
34 | }
35 |
36 | /**
37 | * 判断是否为平板
38 | *
39 | * @return
40 | */
41 | public static boolean isPad() {
42 | return adbExec_without_root_stable("getprop ro.build.characteristics").contains("tablet");
43 | }
44 |
45 | /**
46 | * 获取当前apk版本号
47 | *
48 | * @param context
49 | * @return
50 | */
51 | public static String getAppVersionName(Context context) {
52 | String versionName = "";
53 | try {
54 | // ---get the package info---
55 | PackageManager pm = context.getPackageManager();
56 | PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
57 | versionName = pi.versionName;
58 | // versioncode = pi.versionCode;
59 | if (versionName == null || versionName.length() <= 0) {
60 | return "";
61 | }
62 | } catch (Exception e) {
63 | Log.e("VersionInfo", "Exception", e);
64 | XposedBridge.log(e);
65 | }
66 | return versionName;
67 | }
68 |
69 | /**
70 | * 判断手机是否已root
71 | *
72 | * @return
73 | */
74 | public static boolean get_root_state() {
75 | boolean res = false;
76 | try {
77 | if ((!new File("/system/bin/su").exists()) &&
78 | (!new File("/system/xbin/su").exists())) {
79 | res = false;
80 | } else {
81 | res = true;
82 | }
83 | ;
84 | } catch (Exception e) {
85 |
86 | }
87 | return res;
88 | }
89 |
90 | /**
91 | * root执行adb 稳定版
92 | *
93 | * @param cmd
94 | * @return
95 | */
96 | public static String adbExec_with_root_stable(String cmd) {
97 | String content = "";
98 | Process process = null;
99 | DataOutputStream os = null;
100 | try {
101 | process = Runtime.getRuntime().exec("su"); //切换到root帐号
102 | os = new DataOutputStream(process.getOutputStream());
103 | os.writeBytes(cmd + "\n");
104 | os.writeBytes("exit\n");
105 | os.flush();
106 | process.waitFor();
107 | } catch (Exception e) {
108 | e.printStackTrace();
109 | XposedBridge.log(e);
110 | // return "Failed, permission denied.";
111 | return content;
112 | } finally {
113 | try {
114 | if (os != null) {
115 | os.close();
116 | }
117 | process.destroy();
118 | } catch (Exception e) {
119 | e.printStackTrace();
120 | XposedBridge.log(e);
121 | }
122 | }
123 | // return "Success.";
124 | return content;
125 | }
126 |
127 | /**
128 | * 无root执行adb 稳定版
129 | *
130 | * @param cmd
131 | * @return
132 | */
133 | public static String adbExec_without_root_stable(String cmd) {
134 | String content = "";
135 | Process process = null;
136 | BufferedReader reader = null;
137 | DataOutputStream os = null;
138 | try {
139 | process = Runtime.getRuntime().exec(cmd); //切换到root帐号
140 | os = new DataOutputStream(process.getOutputStream());
141 | reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
142 | // os.writeBytes(cmd + "\n");
143 | // os.writeBytes("exit\n");
144 | os.flush();
145 | process.waitFor();
146 | StringBuffer output = new StringBuffer();
147 | int read;
148 | char[] buffer = new char[4096];
149 | while ((read = reader.read(buffer)) > 0) {
150 | output.append(buffer, 0, read);
151 | }
152 | reader.close();
153 | content = output.toString();
154 | } catch (Exception e) {
155 | e.printStackTrace();
156 | XposedBridge.log(e);
157 | // return "Failed, permission denied.";
158 | return content;
159 | } finally {
160 | try {
161 | if (os != null) {
162 | os.close();
163 | }
164 | process.destroy();
165 | } catch (Exception e) {
166 | e.printStackTrace();
167 | XposedBridge.log(e);
168 | }
169 | }
170 | // return "Success.";
171 | return content;
172 | }
173 |
174 | //TODO 提权函数
175 | public static boolean get_root_access(String pkgCodePath) {
176 | Process process = null;
177 | DataOutputStream os = null;
178 | try {
179 | String cmd = "chmod 777 " + pkgCodePath;
180 | process = Runtime.getRuntime().exec("su"); //切换到root帐号
181 | os = new DataOutputStream(process.getOutputStream());
182 | os.writeBytes(cmd + "\n");
183 | os.writeBytes("exit\n");
184 | os.flush();
185 | process.waitFor();
186 |
187 | } catch (Exception e) {
188 | e.printStackTrace();
189 | XposedBridge.log(e);
190 | return false;
191 | } finally {
192 | try {
193 | if (os != null) {
194 | os.close();
195 | }
196 | process.destroy();
197 | } catch (Exception e) {
198 | e.printStackTrace();
199 | XposedBridge.log(e);
200 | }
201 | }
202 | return true;
203 | }
204 |
205 | public static String adbExec_with_root(String cmd) {
206 | String content = "";
207 | Process process = null;
208 | BufferedReader reader = null;
209 | DataOutputStream os = null;
210 | try {
211 | process = Runtime.getRuntime().exec("su"); //切换到root帐号
212 | os = new DataOutputStream(process.getOutputStream());
213 | reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
214 | os.writeBytes(cmd + "\n");
215 | os.writeBytes("exit\n");
216 | os.flush();
217 | process.waitFor();
218 | StringBuffer output = new StringBuffer();
219 | int read;
220 | char[] buffer = new char[4096];
221 | while ((read = reader.read(buffer)) > 0) {
222 | output.append(buffer, 0, read);
223 | }
224 | reader.close();
225 | content = output.toString();
226 | } catch (Exception e) {
227 | e.printStackTrace();
228 | XposedBridge.log(e);
229 | // return "Failed, permission denied.";
230 | return content;
231 | } finally {
232 | try {
233 | if (os != null) {
234 | os.close();
235 | }
236 | process.destroy();
237 | } catch (Exception e) {
238 | e.printStackTrace();
239 | XposedBridge.log(e);
240 | }
241 | }
242 | // return "Success.";
243 | return content;
244 | }
245 |
246 | /**
247 | * 执行adb命令_旧
248 | *
249 | * @param cmd
250 | * @return
251 | */
252 | public static String adbExec_no_root(String cmd) {
253 | BufferedReader reader = null;
254 | String content = "";
255 | try {
256 | //("ps -P|grep bg")执行失败,PC端adb shell ps -P|grep bg执行成功
257 | //Process process = Runtime.getRuntime().exec("ps -P|grep tv");
258 | //-P 显示程序调度状态,通常是bg或fg,获取失败返回un和er
259 | // Process process = Runtime.getRuntime().exec("ps -P");
260 | //打印进程信息,不过滤任何条件
261 | Process process = Runtime.getRuntime().exec(cmd);
262 | reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
263 | StringBuffer output = new StringBuffer();
264 | int read;
265 | char[] buffer = new char[4096];
266 | while ((read = reader.read(buffer)) > 0) {
267 | output.append(buffer, 0, read);
268 | }
269 | reader.close();
270 | content = output.toString();
271 | process.destroy();
272 | } catch (Exception e) {
273 | e.printStackTrace();
274 | XposedBridge.log(e);
275 | }
276 | return content;
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/utils/CoolConfigHelper_28.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.utils
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.ContentValues
5 | import android.content.Context
6 | import android.graphics.Bitmap
7 | import android.net.Uri
8 | import android.os.Build
9 | import android.os.Environment
10 | import android.provider.MediaStore
11 | import androidx.annotation.RequiresApi
12 | import com.coolest.toolbox.ui.CoolUtils
13 | import com.github.kyuubiran.ezxhelper.utils.Log
14 | import de.robv.android.xposed.XposedBridge
15 | import org.json.JSONArray
16 | import org.json.JSONObject
17 | import java.io.*
18 |
19 | class CoolConfigHelper_28 {
20 | companion object {
21 | //const val configTxtFile = File(Context().externalCacheDir, "config.txt")
22 | private var configFileStr =
23 | Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
24 | .toString() + "/config.txt"
25 |
26 | // private var configFileStr = "/storage/emulated/0/Download/config.txt"
27 | private var configFileUri: Uri? = null
28 |
29 | /* fun initJsonConfig(): Boolean {
30 | try {
31 | //保存为json
32 | val obj = JSONObject().apply {
33 | put("miui_plus_regard_as_phone", false)
34 | }
35 |
36 | //把json转字符串并保存到本地
37 | val content = obj.toString()
38 | val file = File(configTxtFile)
39 |
40 | // if file doesnt exists, then create it
41 | if (!file.exists()) {
42 | file.createNewFile()
43 | }
44 | val fw = FileWriter(file.absoluteFile)
45 | val bw = BufferedWriter(fw)
46 | bw.write(content)
47 | bw.close()
48 | } catch (e: Exception) {
49 | e.printStackTrace()
50 |
51 | return false
52 | }
53 |
54 | return true
55 | }*/
56 |
57 | @SuppressLint("InlinedApi")
58 | fun initJsonConfig(context: Context): Boolean {
59 | try {
60 | //保存为json
61 | val obj = JSONObject().apply {
62 | put("miui_plus_connect_pc", !CoolUtils.isPad())
63 | put("miui_plus_cust_pc_name_switch", false)
64 | put("miui_plus_pc_name", "")
65 | put("miui_plus_pc_tail", "")
66 | put("dolby_enabled", CoolUtils.isPad())
67 | put("harmanKardon_enabled", true)
68 | }
69 | Log.e(obj.toString())
70 |
71 | when (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
72 | //安卓10以下
73 | false -> {
74 | val myFile = File(configFileStr)
75 | myFile.printWriter().use { out ->
76 | out.println(obj.toString())
77 | }
78 | }
79 | //安卓10及以上
80 | true -> {
81 | val resolver = context.applicationContext.contentResolver
82 | // Find all audio files on the primary external storage device.
83 | val collection =
84 | MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
85 | val details = ContentValues().apply {
86 | put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
87 | put(MediaStore.Downloads.DISPLAY_NAME, "config.txt")
88 | put(MediaStore.Downloads.IS_PENDING, 1)
89 | }
90 | configFileUri = resolver.insert(collection, details)
91 | resolver.openFileDescriptor(configFileUri!!, "w", null).use { pfd ->
92 | // Write data into the pending audio file.
93 | val bos =
94 | BufferedOutputStream(FileOutputStream(pfd!!.fileDescriptor))
95 | val pw = PrintWriter(bos)
96 | pw.println(obj.toString())
97 | pw.flush()
98 | pw.close()
99 | bos.close()
100 | }
101 | details.clear()
102 | details.put(MediaStore.Downloads.IS_PENDING, 0)
103 | resolver.update(configFileUri!!, details, null, null)
104 | }
105 | }
106 |
107 | } catch (e: Exception) {
108 | e.printStackTrace()
109 |
110 | return false
111 | }
112 |
113 | return true
114 | }
115 |
116 |
117 | fun modifyJsonConfig(context: Context, key: String, value: Any): Boolean {
118 | try {
119 | //文件不存在就创建
120 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
121 | if (!File(configFileStr).exists()) {
122 | initJsonConfig(context)
123 | }
124 | } else {
125 | try {
126 | val resolver = context.applicationContext.contentResolver
127 | resolver.openInputStream(configFileUri!!).use { stream ->
128 |
129 | }
130 | } catch (e: java.lang.Exception) {
131 | // e.printStackTrace()
132 | initJsonConfig(context)
133 | }
134 | }
135 |
136 |
137 | //读取配置文件
138 | val jsonContent = StringBuilder()
139 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
140 | // Open a specific media item using InputStream.
141 | val resolver = context.applicationContext.contentResolver
142 | resolver.openInputStream(configFileUri!!).use { stream ->
143 | // Perform operations on "stream".
144 | val reader = BufferedReader(InputStreamReader(stream))
145 | reader.use {
146 | reader.forEachLine {
147 | jsonContent.append(it)
148 | }
149 | }
150 | }
151 | } else {
152 | val input = FileInputStream(File(configFileStr))
153 | val reader = BufferedReader(InputStreamReader(input))
154 | reader.use {
155 | reader.forEachLine {
156 | jsonContent.append(it)
157 | }
158 | }
159 | }
160 | //改配置
161 | val jsonString = jsonContent.toString()
162 | val obj = JSONObject(jsonString)
163 | obj.put(key, value)//修改json配置文件的配置项。put会覆盖旧值
164 |
165 | //保存文件
166 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
167 | val myFile = File(configFileStr)
168 | myFile.printWriter().use { out ->
169 | out.println(obj.toString())
170 | }
171 | } else {
172 | val resolver = context.applicationContext.contentResolver
173 | // Find all audio files on the primary external storage device.
174 | val collection =
175 | MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
176 | val details = ContentValues().apply {
177 | put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
178 | put(MediaStore.Downloads.DISPLAY_NAME, "config.txt")
179 | put(MediaStore.Downloads.IS_PENDING, 1)
180 | }
181 | configFileUri = resolver.insert(collection, details)
182 | resolver.openFileDescriptor(configFileUri!!, "w", null).use { pfd ->
183 | // Write data into the pending audio file.
184 | val bos =
185 | BufferedOutputStream(FileOutputStream(pfd!!.fileDescriptor))
186 | val pw = PrintWriter(bos)
187 | pw.println(obj.toString())
188 | pw.flush()
189 | pw.close()
190 | bos.close()
191 | }
192 | details.clear()
193 | details.put(MediaStore.Downloads.IS_PENDING, 0)
194 | resolver.update(configFileUri!!, details, null, null)
195 | }
196 | } catch (e: Exception) {
197 | e.printStackTrace()
198 | return false
199 | }
200 |
201 | return true
202 | }
203 |
204 |
205 | fun getJsonConfig(context: Context, key: String): Any? {
206 | //文件不存在就创建
207 | val jsonContent = StringBuilder()
208 | try {
209 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
210 | val input = FileInputStream(File(configFileStr))
211 | val reader = BufferedReader(InputStreamReader(input))
212 | reader.use {
213 | reader.forEachLine {
214 | jsonContent.append(it)
215 | }
216 | }
217 | } else {
218 | // Open a specific media item using InputStream.
219 | val resolver = context.applicationContext.contentResolver
220 | resolver.openInputStream(configFileUri!!).use { stream ->
221 | // Perform operations on "stream".
222 | val reader = BufferedReader(InputStreamReader(stream))
223 | reader.use {
224 | reader.forEachLine {
225 | jsonContent.append(it)
226 | }
227 | }
228 | }
229 | }
230 | val jsonString = jsonContent.toString()
231 | val json = JSONObject(jsonString)//拿到的配置文件json object
232 | return json.get(key)
233 | } catch (e: Exception) {
234 | e.printStackTrace()
235 | return null
236 | }
237 | }
238 | }
239 | }
--------------------------------------------------------------------------------
/code/app/src/main/res/layout/activity_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
15 |
16 |
22 |
23 |
32 |
33 |
40 |
41 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
57 |
58 |
63 |
64 |
68 |
69 |
77 |
78 |
83 |
84 |
88 |
89 |
93 |
94 |
103 |
104 |
113 |
114 |
121 |
122 |
123 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
147 |
148 |
156 |
157 |
162 |
163 |
167 |
168 |
172 |
173 |
182 |
183 |
192 |
193 |
201 |
202 |
203 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/ui/AboutActivity.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.ui
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.net.Uri
6 | import android.os.Bundle
7 | import android.text.TextUtils
8 | import android.util.Log
9 | import android.view.Menu
10 | import android.view.MenuItem
11 | import android.view.ViewGroup
12 | import android.widget.LinearLayout
13 | import android.widget.TextView
14 | import android.widget.Toast
15 | import androidx.appcompat.app.AppCompatActivity
16 | import com.afollestad.materialdialogs.MaterialDialog
17 | import com.coolest.toolbox.MyApplication
18 | import com.coolest.toolbox.R
19 | import com.coolest.toolbox.databinding.ActivityAboutBinding
20 | import com.coolest.toolbox.utils.*
21 | import com.google.android.material.card.MaterialCardView
22 | import com.google.android.material.dialog.MaterialDialogs
23 | import com.google.android.material.snackbar.Snackbar
24 | import de.robv.android.xposed.XposedBridge
25 | import okhttp3.OkHttpClient
26 | import okhttp3.Request
27 | import org.json.JSONObject
28 | import java.util.*
29 | import java.util.regex.Matcher
30 | import java.util.regex.Pattern
31 | import kotlin.concurrent.thread
32 |
33 | class AboutActivity : AppCompatActivity() {
34 |
35 | private lateinit var binding: ActivityAboutBinding
36 |
37 | var todayBingPicUrl = ""
38 |
39 | private var githubName = "CoolestEnoch"
40 | private var githubRepo = "MIUI_Tools"
41 |
42 | override fun onCreate(savedInstanceState: Bundle?) {
43 | super.onCreate(savedInstanceState)
44 | binding = ActivityAboutBinding.inflate(layoutInflater)
45 | setContentView(binding.root)
46 |
47 | setSupportActionBar(findViewById(R.id.toolbar))
48 |
49 | binding.toolbarLayout.title = "关于"
50 |
51 | binding.cardAuthor.setOnClickListener {
52 | Snackbar.make(window.decorView, "作者: Coolest Enoch", Snackbar.LENGTH_LONG)
53 | .setAction("Action", null).show()
54 | try {
55 | Thread.sleep(100)
56 | } catch (e: InterruptedException) {
57 | e.printStackTrace()
58 | }
59 | Toast.makeText(this, "作者: Coolest Enoch", Toast.LENGTH_SHORT).show()
60 | startActivity(Intent().apply {
61 | action = "android.intent.action.VIEW"
62 | data = Uri.parse("https://github.com/coolestenoch")
63 | })
64 | }
65 |
66 | //背景获取
67 | thread {
68 | try {
69 | todayBingPicUrl = getTodayWallpaperURL()
70 | } catch (e: Exception) {
71 | e.printStackTrace()
72 | XposedBridge.log(e)
73 | }
74 | }
75 |
76 | //背景图
77 | when (MyApplication.dailyBingPaper) {
78 | null -> setBingDailyWallpaper(binding.aboutBgImg, this, 0)
79 | else -> binding.aboutBgImg.setImageBitmap(MyApplication.dailyBingPaper)
80 | }
81 |
82 |
83 | //mapOf(信息list1 to 职位1, 信息list2 to 职位2)
84 | //如果map里的key list里只有一个值, 那么就会被解析为github用户名并直接去github上查找这个人的信息
85 | //否则list格式为:{用户名:String, 个性签名:String, 头像URL, 网页URL}
86 | //空值不要用null, 请使用空字符串""代替
87 |
88 | //作者列表
89 | val list = mapOf(listOf("coolestenoch") to "作者")
90 | processDeveloperInfo(list, binding.authorListView, null, this)
91 |
92 | //贡献者列表
93 | val list2 = mapOf(listOf("mikotwa") to "", listOf("桜谷暮枫", "", "http://avatar.coolapk.com/data/002/37/46/44_avatar_middle.jpg", "http://www.coolapk.com/u/2374644") to "为修复JoyUI上的bug提供帮助")
94 | processDeveloperInfo(list2, binding.contributorListView, binding.contributorListTip, this)
95 |
96 | //下拉刷新检查更新
97 | binding.swipeRefresh.apply {
98 | setColorSchemeColors(
99 | resources.getColor(R.color.green),
100 | resources.getColor(R.color.blue),
101 | resources.getColor(R.color.yellow),
102 | resources.getColor(R.color.purple),
103 | resources.getColor(R.color.orange),
104 | resources.getColor(R.color.red),
105 | resources.getColor(R.color.cyan)
106 | )
107 | setOnRefreshListener {
108 | checkUpdate(
109 | packageManager.getPackageInfo(packageName, 0).versionName,
110 | binding.swipeRefresh,
111 | githubName,
112 | githubRepo
113 | )
114 | }
115 | }
116 | }
117 |
118 | override fun onCreateOptionsMenu(menu: Menu?): Boolean {
119 | menuInflater.inflate(R.menu.menu_about, menu)
120 | return super.onCreateOptionsMenu(menu)
121 | }
122 |
123 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
124 | when (item.itemId) {
125 | R.id.action_update -> {
126 | //更新渠道设置
127 | getSharedPreferences("update_channel", Context.MODE_PRIVATE).apply {
128 | githubName = "${getString("username", "CoolestEnoch")}"
129 | githubRepo = "${getString("repo", "MIUI_Tools")}"
130 | }
131 | checkUpdate(
132 | packageManager.getPackageInfo(packageName, 0).versionName,
133 | binding.swipeRefresh,
134 | githubName,
135 | githubRepo
136 | )
137 | }
138 | R.id.release_history -> {
139 | //更新渠道设置
140 | getSharedPreferences("update_channel", Context.MODE_PRIVATE).apply {
141 | githubName = "${getString("username", "CoolestEnoch")}"
142 | githubRepo = "${getString("repo", "MIUI_Tools")}"
143 | }
144 | showAllRelease(
145 | binding.swipeRefresh, when (githubName) {
146 | "CoolestEnoch" -> "所有版本: 官方渠道"
147 | "Xposed-Modules-Repo" -> "所有版本: LSPosed仓库"
148 | else -> "所有版本: 其他渠道"
149 | }, githubName, githubRepo
150 | )
151 | }
152 | R.id.update_channel -> {
153 | changeUpdateChannel(this)
154 | }
155 | }
156 | return super.onOptionsItemSelected(item)
157 | }
158 |
159 | fun isHttpUrl(urls: String): Boolean {
160 | var isurl = false
161 | val regex = ("(((https|http)?://)?([a-z0-9]+[.])|(www.))"
162 | + "\\w+[.|\\/]([a-z0-9]{0,})?[[.]([a-z0-9]{0,})]+((/[\\S&&[^,;\u4E00-\u9FA5]]+)+)?([.][a-z0-9]{0,}+|/?)") //设置正则表达式
163 | val pat = Pattern.compile(regex.trim { it <= ' ' }) //对比
164 | val mat = pat.matcher(urls.trim { it <= ' ' })
165 | isurl = mat.matches() //判断是否匹配
166 | if (isurl) {
167 | isurl = true
168 | }
169 | return isurl
170 | }
171 |
172 | fun processDeveloperInfo(
173 | userInfoMap: Map, String>?,
174 | developersView: ViewGroup,
175 | tipView: TextView?,
176 | context: Context
177 | ) {
178 | try {
179 | thread {
180 | val tempViewList = LinkedList()
181 | if (userInfoMap != null) {
182 | for ((userInfo, description) in userInfoMap) {
183 | if (userInfo.size == 1) {
184 | //是个github用户名
185 | val username = userInfo[0]
186 |
187 | try {
188 | //调用GitHub API获取json
189 | var githubResponseJson: String? = ""
190 | var avatar_url: String? = ""
191 | var nickName: String? = ""
192 | var bio: String? = ""
193 | val client = OkHttpClient()
194 | val request = Request.Builder()
195 | .url("https://api.github.com/users/$username")
196 | .build()
197 | val response = client.newCall(request).execute()
198 | githubResponseJson = response.body?.string()
199 | Log.e("github", "$githubResponseJson")
200 |
201 | //解析json获取图片地址
202 | val responseJson = JSONObject(githubResponseJson)
203 | avatar_url = "${responseJson.get("avatar_url")}"
204 | nickName = "${responseJson.get("login")}"
205 | bio = "${responseJson.get("bio")}"
206 |
207 | //添加卡片
208 | val textList = mutableListOf("")
209 | textList.clear()
210 | if(!username.equals(""))
211 | textList.add(nickName)
212 | if(!bio.equals(""))
213 | textList.add(bio)
214 | if(!description.equals(""))
215 | textList.add(description)
216 |
217 | runOnUiThread {
218 | tempViewList.add(
219 | ViewUtils_Kotlin.createBigButton(
220 | context,
221 | avatar_url,
222 | R.color.item_card_bg,
223 | textList,
224 | null, R.color.white
225 | ) {
226 | startActivity(Intent().apply {
227 | action = "android.intent.action.VIEW"
228 | data = Uri.parse("https://github.com/$username")
229 | })
230 | }
231 | )
232 | }
233 | runOnUiThread {
234 | developersView.removeAllViews()
235 | for (view in tempViewList) {
236 | developersView.addView(view)
237 | }
238 | }
239 | } catch (e: Exception) {
240 | if (e.toString()
241 | .contains("Unable to resolve host \"api.github.com\"")
242 | ) {
243 | Snackbar.make(
244 | window.decorView,
245 | "无法连接到GitHub服务器, 请检查网络",
246 | Snackbar.LENGTH_LONG
247 | ).show()
248 | } else {
249 | e.printStackTrace()
250 | XposedBridge.log(e)
251 | }
252 | }
253 | } else {
254 | //不是github用户名
255 | val username = userInfo[0]
256 | val bio = userInfo[1]
257 | val aviatorUrl = userInfo[2]
258 | val webPageUrl = userInfo[3]
259 | //还有一个description
260 |
261 | try {
262 | //添加卡片
263 | val textList = mutableListOf("")
264 | textList.clear()
265 | if(!username.equals(""))
266 | textList.add(username)
267 | if(!bio.equals(""))
268 | textList.add(bio)
269 | if(!description.equals(""))
270 | textList.add(description)
271 |
272 | runOnUiThread {
273 | tempViewList.add(
274 | ViewUtils_Kotlin.createBigButton(
275 | context,
276 | aviatorUrl,
277 | R.color.item_card_bg,
278 | textList,
279 | null, R.color.white
280 | ) {
281 | when (TextUtils.isEmpty(webPageUrl)) {
282 | false -> startActivity(Intent().apply {
283 | action = "android.intent.action.VIEW"
284 | data = Uri.parse(webPageUrl)
285 | })
286 | true -> {
287 | Snackbar.make(
288 | window.decorView,
289 | "这个人很神秘, 没有留下可访问的页面。",
290 | Snackbar.LENGTH_LONG
291 | ).show()
292 | }
293 | }
294 | }
295 | )
296 | }
297 | runOnUiThread {
298 | developersView.removeAllViews()
299 | for (view in tempViewList) {
300 | developersView.addView(view)
301 | }
302 | }
303 | } catch (e: Exception) {
304 | if (e.toString()
305 | .contains("Unable to resolve host \"api.github.com\"")
306 | ) {
307 | Snackbar.make(
308 | window.decorView,
309 | "无法连接到GitHub服务器, 请检查网络",
310 | Snackbar.LENGTH_LONG
311 | ).show()
312 | } else {
313 | e.printStackTrace()
314 | XposedBridge.log(e)
315 | }
316 | }
317 | }
318 | }
319 | } else {
320 | tipView?.text = "暂无"
321 | }
322 | }
323 | } catch (e: Exception) {
324 | e.printStackTrace()
325 | }
326 | }
327 |
328 |
329 | private fun changeUpdateChannel(context: Context) {
330 | val picURL = when (todayBingPicUrl) {
331 | "" -> null
332 | else -> todayBingPicUrl
333 | }
334 | var bottomBar: MaterialDialog? = null
335 | runOnMainThread {
336 | val linearLayout = LinearLayout(context).apply {
337 | layoutParams = LinearLayout.LayoutParams(
338 | LinearLayout.LayoutParams.MATCH_PARENT,
339 | LinearLayout.LayoutParams.WRAP_CONTENT
340 | )
341 | orientation = LinearLayout.VERTICAL
342 | }
343 | val btnOfficial = ViewUtils_Kotlin.createBigButton(
344 | context, R.drawable.ic_baseline_double_arrow_24,
345 | R.color.teal_200, listOf("官方"), MyApplication.dailyBingPaper, R.color.white
346 | ) {
347 | context.getSharedPreferences("update_channel", Context.MODE_PRIVATE).edit()
348 | .apply {
349 | putString("username", "CoolestEnoch")
350 | putString("repo", "MIUI_Tools")
351 | commit()
352 | }
353 | bottomBar?.cancel()
354 | Unit
355 | }
356 | val btnLsp = ViewUtils_Kotlin.createBigButton(
357 | context, R.drawable.ic_baseline_double_arrow_24,
358 | R.color.teal_200, listOf("LSPosed"), MyApplication.dailyBingPaper, R.color.white
359 | ) {
360 | context.getSharedPreferences("update_channel", Context.MODE_PRIVATE).edit()
361 | .apply {
362 | putString("username", "Xposed-Modules-Repo")
363 | putString("repo", "com.coolest.toolbox")
364 | commit()
365 | }
366 | bottomBar?.cancel()
367 | Unit
368 | }
369 | linearLayout.addView(btnOfficial)
370 | linearLayout.addView(btnLsp)
371 | bottomBar = ViewUtils_Kotlin.getBigCardFromBottom(context, "更新渠道", linearLayout)
372 | bottomBar?.show()
373 | }
374 |
375 |
376 | }
377 |
378 |
379 | }
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/utils/GithubUpdateUtils.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.utils
2 |
3 | import android.content.Intent
4 | import android.net.Uri
5 | import android.util.Log
6 | import android.widget.LinearLayout
7 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
8 | import com.coolest.toolbox.R
9 | import com.github.kyuubiran.ezxhelper.utils.runOnMainThread
10 | import com.google.android.material.snackbar.Snackbar
11 | import de.robv.android.xposed.XposedBridge
12 | import okhttp3.OkHttpClient
13 | import okhttp3.Request
14 | import org.json.JSONArray
15 | import org.json.JSONObject
16 | import java.text.DecimalFormat
17 | import java.util.*
18 | import kotlin.concurrent.thread
19 | import kotlin.math.min
20 |
21 |
22 | /**
23 | * 结构:
24 | * 版本号Tag
25 | * 描述
26 | * 资源列表: 列表[文件名 - 下载链接 - 创建时间]
27 | */
28 | class Release(
29 | val relTag: String,
30 | val relName: String,
31 | val relDescription: String,
32 | val relTime: String,
33 | val relAssets: List>
34 | )
35 |
36 | /**
37 | * 有更新就弹窗, 没更新就弹SnackBar提示是最新版本
38 | */
39 | fun checkUpdate(
40 | currentVersion: String,
41 | swipeRefreshLayout: SwipeRefreshLayout,
42 | githubUserName: String,
43 | githubRepoName: String
44 | ) {
45 | swipeRefreshLayout.isRefreshing = true
46 | Snackbar.make(swipeRefreshLayout.rootView, "正在检查更新...", Snackbar.LENGTH_LONG).show()
47 | val context = swipeRefreshLayout.rootView.context
48 | //开始检查更新
49 |
50 | thread {
51 | try {
52 | //调用github API获取json
53 | var githubApiResponse: String? = ""
54 | val client = OkHttpClient()
55 | val request = Request.Builder()
56 | .url("https://api.github.com/repos/$githubUserName/$githubRepoName/releases/latest")
57 | .build()
58 | val response = client.newCall(request).execute()
59 | githubApiResponse = response.body?.string()
60 | Log.e("github repo", "$githubApiResponse")
61 |
62 | //解析json获取release信息
63 | val releaseObjectJson = JSONObject(githubApiResponse)
64 | if (releaseObjectJson.has("message") && releaseObjectJson.getString("message")
65 | .equals("Not Found")
66 | ) {
67 | //没有最新release或没有上传
68 | Snackbar.make(
69 | swipeRefreshLayout.rootView,
70 | "没有找到新版本, 可能是还没有上传?",
71 | Snackbar.LENGTH_LONG
72 | ).show()
73 | } else {
74 | val tag = releaseObjectJson.getString("tag_name")
75 | if (!needToUpdate(currentVersion, tag.substring(1))) {
76 | Snackbar.make(swipeRefreshLayout.rootView, "已是最新版本", Snackbar.LENGTH_LONG)
77 | .show()
78 | } else {
79 | val name = releaseObjectJson.getString("name")
80 | val description = "\n${releaseObjectJson.getString("body")}"
81 | val relTime = adjustTime(releaseObjectJson.getString("published_at"))
82 | val fileList = LinkedList>()
83 | //获取文件信息
84 | val filesJsonArray = releaseObjectJson.getJSONArray("assets")
85 | for (i in 0 until filesJsonArray.length()) {
86 | //获取单个文件信息
87 | val fileJsonObject = filesJsonArray.getJSONObject(i)
88 | val fileName = fileJsonObject.getString("name")
89 | val downloadUrl = fileJsonObject.getString("browser_download_url")
90 | val time = adjustTime(fileJsonObject.getString("updated_at"))
91 | val size = adjustFileSize(fileJsonObject.getLong("size"))
92 | val fileInfo = LinkedList().apply {
93 | add(fileName)
94 | add(downloadUrl)
95 | add(time)
96 | add(size)
97 | }
98 | fileList.add(fileInfo)
99 | }
100 | val latestRelease = Release(tag, name, description, relTime, fileList)
101 |
102 | //从latestRelease转换为底栏弹出的更新
103 | //最外层的LinearLayout
104 | val baseLinearLayout = LinearLayout(context).apply {
105 | layoutParams = LinearLayout.LayoutParams(
106 | LinearLayout.LayoutParams.MATCH_PARENT,
107 | LinearLayout.LayoutParams.WRAP_CONTENT
108 | )
109 | orientation = LinearLayout.VERTICAL
110 | }
111 | //往最外层的LinearLayout里塞小卡片
112 | //一个release
113 | val cardView = ViewUtils_Kotlin.createBigButton(
114 | context, null, R.color.item_card_bg,
115 | listOf(
116 | latestRelease.relName,
117 | latestRelease.relTag,
118 | latestRelease.relTime,
119 | latestRelease.relDescription
120 | ),
121 | null,
122 | R.color.white
123 | ) {//点击release卡片后弹出一个新的小卡片, 上面记载着所有的文件
124 | val relLinearLayout = LinearLayout(context).apply {
125 | layoutParams = LinearLayout.LayoutParams(
126 | LinearLayout.LayoutParams.MATCH_PARENT,
127 | LinearLayout.LayoutParams.WRAP_CONTENT
128 | )
129 | orientation = LinearLayout.VERTICAL
130 | }
131 | //从assets列表里取出所有文件清单
132 | for (file in latestRelease.relAssets) {
133 | //拿到单个文件
134 | val fileCard = ViewUtils_Kotlin.createBigButton(
135 | context, null, R.color.item_card_bg,
136 | listOf(file[0], file[2], file[3]),
137 | null,
138 | R.color.white
139 | ) {
140 | context.startActivity(Intent().apply {
141 | action = "android.intent.action.VIEW"
142 | data = Uri.parse(file[1])
143 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
144 | })
145 | }
146 | relLinearLayout.addView(fileCard)
147 | }
148 | runOnMainThread {
149 | ViewUtils_Kotlin.getBigCardFromBottom(context, "文件", relLinearLayout)
150 | .show()
151 | }
152 |
153 | Unit
154 | }
155 | baseLinearLayout.addView(cardView)
156 | runOnMainThread {
157 | ViewUtils_Kotlin.getBigCardFromBottom(
158 | context,
159 | "发现新版本! 当前版本:$currentVersion",
160 | baseLinearLayout
161 | )
162 | .show()
163 | }
164 | }
165 | }
166 | } catch (e: Exception) {
167 | if (e.toString().contains("Unable to resolve host \"api.github.com\"")) {
168 | Snackbar.make(
169 | swipeRefreshLayout.rootView,
170 | "无法连接到GitHub服务器, 请检查网络",
171 | Snackbar.LENGTH_LONG
172 | ).show()
173 | } else {
174 | e.printStackTrace()
175 | XposedBridge.log(e)
176 | }
177 | }
178 | //检查更新完成, 结束刷新动画
179 | runOnMainThread {
180 | swipeRefreshLayout.isRefreshing = false
181 | }
182 | }
183 |
184 | // ViewUtils_Kotlin.codeNotDone(swipeRefreshLayout.context)
185 | }
186 |
187 | fun showAllRelease(
188 | swipeRefreshLayout: SwipeRefreshLayout,
189 | title: String?,
190 | githubUserName: String,
191 | githubRepoName: String
192 | ) {
193 | swipeRefreshLayout.isRefreshing = true
194 | Snackbar.make(swipeRefreshLayout.rootView, "正在查询历史版本...", Snackbar.LENGTH_LONG).show()
195 | val context = swipeRefreshLayout.rootView.context
196 | //开始检查更新
197 |
198 | thread {
199 | try {
200 | val releaseList = mutableListOf()
201 |
202 | //调用github API获取json
203 | var githubApiResponse: String? = ""
204 | val client = OkHttpClient()
205 | val request = Request.Builder()
206 | .url("https://api.github.com/repos/$githubUserName/$githubRepoName/releases")
207 | .build()
208 | val response = client.newCall(request).execute()
209 | githubApiResponse = response.body?.string()
210 | Log.e("github repo", "$githubApiResponse")
211 |
212 | //解析json获取所有release并存入releaseList
213 | //releaseList首元素为最新release
214 | val responseArray = JSONArray(githubApiResponse)
215 | if (responseArray.length() == 0) {
216 | Snackbar.make(
217 | swipeRefreshLayout.rootView,
218 | "没有找到任何历史版本, 可能是还没上传?",
219 | Snackbar.LENGTH_LONG
220 | ).show()
221 | } else {
222 | //所有的release
223 | for (i in 0 until responseArray.length()) {
224 | //一个release
225 | val relObject = responseArray.get(i) as JSONObject
226 | val tag = relObject.getString("tag_name")
227 | val name = relObject.getString("name")
228 | val description = "\n${relObject.getString("body")}"
229 | val relTime = adjustTime(relObject.getString("published_at"))
230 | val assetsList = LinkedList>()
231 | val assetsArray = relObject.getJSONArray("assets")
232 | //一个release里所有文件
233 | for (j in 0 until assetsArray.length()) {
234 | //一个文件
235 | val assetsObj = assetsArray.get(j) as JSONObject
236 | val fileName = assetsObj.getString("name")
237 | val downloadURL = assetsObj.getString("browser_download_url")
238 | val time = adjustTime(assetsObj.getString("updated_at"))
239 | val size = adjustFileSize(assetsObj.getLong("size"))
240 | assetsList.add(LinkedList().apply {
241 | add(fileName)
242 | add(downloadURL)
243 | add(time)
244 | add(size)
245 | })
246 | }
247 | releaseList.add(Release(tag, name, description, relTime, assetsList))
248 | }
249 |
250 | //从releaseList转换为底栏弹出的更新列表
251 | //最外层的LinearLayout
252 | val baseLinearLayout = LinearLayout(context).apply {
253 | layoutParams = LinearLayout.LayoutParams(
254 | LinearLayout.LayoutParams.MATCH_PARENT,
255 | LinearLayout.LayoutParams.WRAP_CONTENT
256 | )
257 | orientation = LinearLayout.VERTICAL
258 | }
259 | //往最外层的LinearLayout里塞小卡片
260 | for (release in releaseList) {
261 | //一个release
262 | val cardView = ViewUtils_Kotlin.createBigButton(
263 | context, null, R.color.item_card_bg,
264 | listOf(
265 | release.relName,
266 | release.relTag,
267 | release.relTime,
268 | release.relDescription
269 | ),
270 | null,
271 | R.color.white
272 | ) {//点击release卡片后弹出一个新的小卡片, 上面记载着所有的文件
273 | val relLinearLayout = LinearLayout(context).apply {
274 | layoutParams = LinearLayout.LayoutParams(
275 | LinearLayout.LayoutParams.MATCH_PARENT,
276 | LinearLayout.LayoutParams.WRAP_CONTENT
277 | )
278 | orientation = LinearLayout.VERTICAL
279 | }
280 | //从assets列表里取出所有文件清单
281 | for (file in release.relAssets) {
282 | //拿到单个文件
283 | val fileCard = ViewUtils_Kotlin.createBigButton(
284 | context, null, R.color.item_card_bg,
285 | listOf(file[0], file[2], file[3]),
286 | null,
287 | R.color.white
288 | ) {
289 | context.startActivity(Intent().apply {
290 | action = "android.intent.action.VIEW"
291 | data = Uri.parse(file[1])
292 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
293 | })
294 | }
295 | relLinearLayout.addView(fileCard)
296 | }
297 | runOnMainThread {
298 | ViewUtils_Kotlin.getBigCardFromBottom(context, "文件", relLinearLayout)
299 | .show()
300 | }
301 |
302 | Unit
303 | }
304 | baseLinearLayout.addView(cardView)
305 | }
306 | runOnMainThread {
307 | try {
308 | ViewUtils_Kotlin.getBigCardFromBottom(
309 | context, when (title) {
310 | null -> "所有版本"
311 | else -> title
312 | }, baseLinearLayout
313 | )
314 | .show()
315 | } catch (e: Exception) {
316 | e.printStackTrace()
317 | XposedBridge.log(e)
318 | }
319 | }
320 | }
321 |
322 | } catch (e: Exception) {
323 | if (e.toString().contains("Unable to resolve host \"api.github.com\"")) {
324 | Snackbar.make(
325 | swipeRefreshLayout.rootView,
326 | "无法连接到GitHub服务器, 请检查网络",
327 | Snackbar.LENGTH_LONG
328 | ).show()
329 | } else {
330 | e.printStackTrace()
331 | XposedBridge.log(e)
332 | }
333 | }
334 | //检查更新完成, 结束刷新动画
335 | runOnMainThread {
336 | swipeRefreshLayout.isRefreshing = false
337 | }
338 | }
339 |
340 | // ViewUtils_Kotlin.codeNotDone(swipeRefreshLayout.context)
341 | }
342 |
343 | fun adjustFileSize(size: Long): String {
344 | if (size <= 0) return "0"
345 | val units = arrayOf("B", "KB", "MB", "GB", "TB")
346 | val digitGroups = (Math.log10(size.toDouble()) / Math.log10(1024.0)).toInt()
347 | val ret = DecimalFormat("#,##0.#").format(size / Math.pow(1024.0, digitGroups.toDouble()))
348 | .toString() + " " + units[digitGroups]
349 | return "文件大小: $ret"
350 | }
351 |
352 | fun adjustTime(githubTime: String): String {
353 | val year = githubTime.substring(0, 4)
354 | val month = githubTime.substring(6, 7)
355 | val day = githubTime.substring(8, 10)
356 | val hour = githubTime.substring(11, 13)
357 | val min = githubTime.substring(14, 16)
358 | val sec = githubTime.substring(17, 19)
359 |
360 | val calendar = Calendar.getInstance()
361 | calendar.set(year.toInt(), month.toInt(), day.toInt(), hour.toInt(), min.toInt(), sec.toInt())
362 | calendar.add(Calendar.HOUR, 8)
363 | return "发布于: ${calendar.get(Calendar.YEAR)}年${calendar.get(Calendar.MONTH)}月${
364 | calendar.get(
365 | Calendar.DATE
366 | )
367 | }日 ${calendar.get(Calendar.HOUR_OF_DAY)}时${calendar.get(Calendar.MINUTE)}分${
368 | calendar.get(
369 | Calendar.SECOND
370 | )
371 | }秒"
372 |
373 |
374 | /*return "发布于: ${year}年${month}月${day}日 ${hour}时${min}分${sec}秒"*/
375 |
376 | // 2022-03-20T08:24:33Z
377 | /*val timeStampUTC = transToTimeStamp(githubTime)
378 | val timeStampChn = timeStampUTC + 3600 * 8
379 |
380 | return "发布于: ${transToString(timeStampChn)}"*/
381 | }
382 |
383 |
384 | fun needToUpdate(currentVersion: String, remoteVersion: String): Boolean {
385 | /* val versionArray1 = currentVersion.split("\\.")
386 | val versionArray2 = remoteVersion.split("\\.")
387 | var idx = 0
388 | val minLength = min(versionArray1.size, versionArray2.size)
389 | var diff = 0
390 | while (idx < minLength
391 | && (versionArray1[idx].length == versionArray2[idx].length)
392 | && (versionArray1[idx].compareTo(versionArray2[idx]) == 0)
393 | ) {
394 | ++idx
395 | }
396 | when (diff) {
397 | 0 -> versionArray1.size - versionArray2.size
398 | else -> diff
399 | }
400 | return diff <= 0*/
401 |
402 | var ret = false
403 | try {
404 | ret = currentVersion.toDouble() < remoteVersion.toDouble()
405 | } catch (e: java.lang.Exception) {
406 | e.printStackTrace()
407 | XposedBridge.log(e)
408 | }
409 | return ret
410 | }
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/utils/ViewUtils.java:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.utils;
2 |
3 |
4 | import android.app.Activity;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.pm.PackageManager;
9 | import android.graphics.Typeface;
10 | import android.util.DisplayMetrics;
11 | import android.util.TypedValue;
12 | import android.view.Gravity;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.view.WindowManager;
16 | import android.widget.ImageView;
17 | import android.widget.LinearLayout;
18 | import android.widget.TextView;
19 |
20 | import com.afollestad.materialdialogs.LayoutMode;
21 | import com.afollestad.materialdialogs.MaterialDialog;
22 | import com.afollestad.materialdialogs.bottomsheets.BottomSheet;
23 | import com.afollestad.materialdialogs.customview.DialogCustomViewExtKt;
24 | import com.bumptech.glide.Glide;
25 | import com.coolest.toolbox.R;
26 | import com.google.android.material.card.MaterialCardView;
27 | import com.google.android.material.shape.CornerFamily;
28 | import com.google.android.material.textfield.TextInputEditText;
29 | import com.google.android.material.textfield.TextInputLayout;
30 |
31 | import java.util.List;
32 |
33 | import kotlin.Unit;
34 | import kotlin.jvm.functions.Function1;
35 |
36 | public class ViewUtils {
37 |
38 | //以下内容需要在build.gradle里添加依赖:
39 | // api 'com.google.android.material:material:1.1.0-alpha06'
40 | // implementation 'com.afollestad.material-dialogs:core:3.2.1'
41 | // implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0'
42 |
43 |
44 | public static int dp2px(Context context, float dpValue) {
45 | //获取屏幕分辨率
46 | final float scale = context.getResources().getDisplayMetrics().density;
47 | return (int) (dpValue * scale + 0.5f);
48 | }
49 |
50 | public static int px2dp(Context context, float pxValue) {
51 | //获取屏幕分辨率
52 | final float scale = context.getResources().getDisplayMetrics().density;
53 | return (int) (pxValue / scale + 0.5f);
54 | }
55 |
56 | //获取屏幕的宽度
57 | public static int getScreenWidth(Context ctx) {
58 | //从系统服务中获取窗口管理器
59 | WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
60 | DisplayMetrics dm = new DisplayMetrics();
61 | //从默认显示器中获取显示参数保存到dm中
62 | wm.getDefaultDisplay().getMetrics(dm);
63 | return dm.widthPixels;
64 | }
65 |
66 | //获取屏幕的高度
67 | public static int getScreenHeight(Context ctx) {
68 | WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
69 | DisplayMetrics dm = new DisplayMetrics();
70 | wm.getDefaultDisplay().getMetrics(dm);
71 | return dm.heightPixels;
72 | }
73 |
74 | //获取屏幕像素密度
75 | public static float getScreenDensity(Context ctx) {
76 | WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
77 | DisplayMetrics dm = new DisplayMetrics();
78 | wm.getDefaultDisplay().getMetrics(dm);
79 | return dm.density;
80 | }
81 |
82 | /**
83 | * 计算线性布局的实际高度
84 | * @param child
85 | * @return
86 | */
87 | public static float getRealHeight(View child){
88 | LinearLayout linearLayout = (LinearLayout) child;
89 | //获取线性布局的参数
90 | ViewGroup.LayoutParams params = linearLayout.getLayoutParams();
91 | if(params==null){
92 | params=new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
93 | }
94 | //获取布局参数里的参数规则
95 | int widthSpec = ViewGroup.getChildMeasureSpec(0,0,params.width);
96 | int heightSpec;
97 | if(params.height>0){
98 | //高度大于0说明这是明确的dp值
99 | //按照精确值的情况计算高度规格
100 | heightSpec=View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
101 | }else{
102 | //MATCH_PARENT = -1, WRAP_CONTENT = -2进入此分支
103 | //按照不确定的情况计算高度规则
104 | heightSpec=View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
105 | }
106 | //重新进行线性布局的宽高测量
107 | linearLayout.measure(widthSpec,heightSpec);
108 | //获取并返回线性布局测量后的高度值,用getMeasuredWidth方法获得宽度数值
109 | return linearLayout.getMeasuredHeight();
110 | }
111 |
112 | /**
113 | * 拖更弹窗
114 | * @param context
115 | */
116 | public static void codeNotDone(Context context){
117 | MaterialDialog dialog = new MaterialDialog(context, new BottomSheet(LayoutMode.WRAP_CONTENT));
118 | dialog.title(null, "啊哦");
119 | DialogCustomViewExtKt.customView(dialog, R.layout.still_not_done, null, true, true, true, true);
120 | dialog.positiveButton(null, "确定", new Function1() {
121 | @Override
122 | public Unit invoke(MaterialDialog materialDialog) {
123 | Activity a = (Activity) context;
124 | //判断调用此方法的是否是主界面
125 | PackageManager packageManager = a.getApplication().getPackageManager();
126 | Intent intent = packageManager.getLaunchIntentForPackage(a.getPackageName());
127 | ComponentName launchComponentName = intent.getComponent();
128 | ComponentName componentName = a.getComponentName();
129 | if(componentName.toString().equals(launchComponentName.toString())){
130 | // Log.i("min77",componentName.getClassName()+"是第一个启动的activity");
131 | }else {
132 | // Log.i("min77",componentName.getClassName()+"不是第一个启动的activity");
133 | a.finish();
134 | }
135 |
136 | return null;
137 | }
138 | });
139 | dialog.show();
140 | }
141 |
142 | /**
143 | * 从屏幕底部弹出一张大卡片
144 | * 在build.gradle里添加以下依赖然后再使用:
145 | * implementation 'com.afollestad.material-dialogs:core:3.2.1'
146 | * implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0'
147 | *
148 | * @param context
149 | * @return
150 | */
151 | public static MaterialDialog getBigCardFromBottom(Context context, String title, View view) {
152 | MaterialDialog dialog = new MaterialDialog(context, new BottomSheet(LayoutMode.WRAP_CONTENT));
153 | dialog.title(null, title);
154 | DialogCustomViewExtKt.customView(dialog, null, view, true, true, true, true);
155 | dialog.positiveButton(null, "确定", new Function1() {
156 | @Override
157 | public Unit invoke(MaterialDialog materialDialog) {
158 | Activity a = (Activity) context;
159 | // a.finish();
160 | return null;
161 | }
162 | });
163 | return dialog;
164 | }
165 |
166 |
167 |
168 | /**
169 | * 创建一张Material Design风格卡片,其中StringList的首项将作为标题,会被加粗
170 | *
171 | * @param context 这个不用解释了吧2333
172 | * @param bg_color_id 背景颜色
173 | * @param icon_url 左边提示图标的资源url
174 | * @param strList 首项作为标题,会被加粗
175 | * @return
176 | */
177 | public static MaterialCardView createCardView(Context context, int bg_color_id, String icon_url, List strList) {
178 | if (strList == null) {
179 | return null;
180 | }
181 | //创建卡片 - mainCard
182 | MaterialCardView mainCard = new MaterialCardView(context);
183 | LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
184 | cardParams.setMargins(ViewUtils.dp2px(context, 18), ViewUtils.dp2px(context, 10), ViewUtils.dp2px(context, 18), 0);
185 | mainCard.setLayoutParams(cardParams);
186 | mainCard.setBackgroundColor(context.getResources().getColor(bg_color_id));
187 | mainCard.setClickable(true);
188 | mainCard.setFocusable(true);
189 | mainCard.setShapeAppearanceModel(mainCard.getShapeAppearanceModel()
190 | .toBuilder()
191 | .setBottomLeftCorner(CornerFamily.ROUNDED, (float) ViewUtils.dp2px(context, 10))
192 | .setBottomRightCorner(CornerFamily.ROUNDED, (float) ViewUtils.dp2px(context, 10))
193 | .setTopLeftCorner(CornerFamily.ROUNDED, (float) ViewUtils.dp2px(context, 10))
194 | .setTopRightCorner(CornerFamily.ROUNDED, (float) ViewUtils.dp2px(context, 10))
195 | .build()
196 | );
197 | //创建卡片内的框架LinearLayout - frameworkLinearLayout
198 | LinearLayout frameworkLinearLayout = new LinearLayout(context);
199 | LinearLayout.LayoutParams frameworkLinearLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
200 | frameworkLinearLayout.setLayoutParams(frameworkLinearLayoutParams);
201 | frameworkLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
202 | //创建卡片内的图标ImageView - icon
203 | ImageView icon = new ImageView(context);
204 | LinearLayout.LayoutParams iconParams = new LinearLayout.LayoutParams(ViewUtils.dp2px(context, 28), ViewUtils.dp2px(context, 28));
205 | iconParams.gravity = Gravity.CENTER_VERTICAL;
206 | iconParams.setMargins(ViewUtils.dp2px(context, 26), 0, ViewUtils.dp2px(context, 20), 0);//卡片图标左右边距
207 | icon.setLayoutParams(iconParams);
208 | Glide.with(context).load(icon_url).into(icon);
209 | //创建在图标右边的LinearLayout - infoLinearLayout
210 | LinearLayout infoLinearLayout = new LinearLayout(context);
211 | LinearLayout.LayoutParams infoLinearLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
212 | infoLinearLayout.setLayoutParams(infoLinearLayoutParams);
213 | infoLinearLayout.setOrientation(LinearLayout.VERTICAL);
214 | infoLinearLayout.setPadding(0, ViewUtils.dp2px(context, 25), 0, ViewUtils.dp2px(context, 25));
215 | //往infoLinearLayout里添加文本框
216 | boolean firstEle = true;//实现第一个文本框加粗
217 | for (String str : strList) {
218 | TextView tv = new TextView(context);
219 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
220 | tv.setLayoutParams(layoutParams);
221 | tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
222 | tv.setTextColor(context.getResources().getColor(R.color.white));
223 | if (firstEle) {//实现第一个文本框加粗
224 | firstEle = false;
225 | tv.setTypeface(null, Typeface.BOLD);
226 | tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 22);
227 | tv.setPadding(0, 0, 0, ViewUtils.dp2px(context, 5));
228 | } else {
229 | tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
230 | }
231 | tv.setAllCaps(false);
232 | tv.setText(str);
233 | infoLinearLayout.addView(tv);
234 | }
235 | //添加视图
236 | frameworkLinearLayout.addView(icon);
237 | frameworkLinearLayout.addView(infoLinearLayout);
238 | mainCard.addView(frameworkLinearLayout);
239 | return mainCard;
240 | }
241 |
242 | /**
243 | * 创建一张Material Design风格卡片,其中StringList的首项将作为标题,会被加粗
244 | *
245 | * @param context 这个不用解释了吧2333
246 | * @param bg_color_id 背景颜色
247 | * @param icon_id 左边提示图标的资源id
248 | * @param strList 首项作为标题,会被加粗
249 | * @return
250 | */
251 | public static MaterialCardView createCardView(Context context, int bg_color_id, int icon_id, List strList) {
252 | if (strList == null) {
253 | return null;
254 | }
255 | //创建卡片 - mainCard
256 | MaterialCardView mainCard = new MaterialCardView(context);
257 | LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
258 | cardParams.setMargins(ViewUtils.dp2px(context, 18), ViewUtils.dp2px(context, 10), ViewUtils.dp2px(context, 18), 0);
259 | mainCard.setLayoutParams(cardParams);
260 | mainCard.setBackgroundColor(context.getResources().getColor(bg_color_id));
261 | mainCard.setClickable(true);
262 | mainCard.setFocusable(true);
263 | mainCard.setShapeAppearanceModel(mainCard.getShapeAppearanceModel()
264 | .toBuilder()
265 | .setBottomLeftCorner(CornerFamily.ROUNDED, (float) ViewUtils.dp2px(context, 10))
266 | .setBottomRightCorner(CornerFamily.ROUNDED, (float) ViewUtils.dp2px(context, 10))
267 | .setTopLeftCorner(CornerFamily.ROUNDED, (float) ViewUtils.dp2px(context, 10))
268 | .setTopRightCorner(CornerFamily.ROUNDED, (float) ViewUtils.dp2px(context, 10))
269 | .build()
270 | );
271 | //创建卡片内的框架LinearLayout - frameworkLinearLayout
272 | LinearLayout frameworkLinearLayout = new LinearLayout(context);
273 | LinearLayout.LayoutParams frameworkLinearLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
274 | frameworkLinearLayout.setLayoutParams(frameworkLinearLayoutParams);
275 | frameworkLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
276 | //创建卡片内的图标ImageView - icon
277 | ImageView icon = new ImageView(context);
278 | LinearLayout.LayoutParams iconParams = new LinearLayout.LayoutParams(ViewUtils.dp2px(context, 28), ViewUtils.dp2px(context, 28));
279 | iconParams.gravity = Gravity.CENTER_VERTICAL;
280 | iconParams.setMargins(ViewUtils.dp2px(context, 26), 0, ViewUtils.dp2px(context, 20), 0);//卡片图标左右边距
281 | icon.setLayoutParams(iconParams);
282 | icon.setBackground(context.getResources().getDrawable(icon_id));
283 | //创建在图标右边的LinearLayout - infoLinearLayout
284 | LinearLayout infoLinearLayout = new LinearLayout(context);
285 | LinearLayout.LayoutParams infoLinearLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
286 | infoLinearLayout.setLayoutParams(infoLinearLayoutParams);
287 | infoLinearLayout.setOrientation(LinearLayout.VERTICAL);
288 | infoLinearLayout.setPadding(0, ViewUtils.dp2px(context, 25), 0, ViewUtils.dp2px(context, 25));
289 | //往infoLinearLayout里添加文本框
290 | boolean firstEle = true;//实现第一个文本框加粗
291 | for (String str : strList) {
292 | TextView tv = new TextView(context);
293 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
294 | tv.setLayoutParams(layoutParams);
295 | tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
296 | tv.setTextColor(context.getResources().getColor(R.color.white));
297 | if (firstEle) {//实现第一个文本框加粗
298 | firstEle = false;
299 | tv.setTypeface(null, Typeface.BOLD);
300 | tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 22);
301 | tv.setPadding(0, 0, 0, ViewUtils.dp2px(context, 5));
302 | } else {
303 | tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
304 | }
305 | tv.setAllCaps(false);
306 | tv.setText(str);
307 | infoLinearLayout.addView(tv);
308 | }
309 | //添加视图
310 | frameworkLinearLayout.addView(icon);
311 | frameworkLinearLayout.addView(infoLinearLayout);
312 | mainCard.addView(frameworkLinearLayout);
313 | return mainCard;
314 | }
315 |
316 | /**
317 | * 创建Material Designed风格大按钮
318 | *
319 | * @param context 这个不用解释了吧2333
320 | * @param icon_id 左边提示图标的资源id
321 | * @param color_id 背景颜色
322 | * @param str 按钮文本
323 | * @return
324 | */
325 | public static MaterialCardView createBigButton(Context context, int icon_id, int color_id, String str) {
326 | //创建大卡片 - cardMain
327 | MaterialCardView cardMain = new MaterialCardView(context);
328 | LinearLayout.LayoutParams cardMainLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
329 | cardMainLayoutParams.setMargins(dp2px(context, 18), dp2px(context, 10), dp2px(context, 18), 0);
330 | cardMain.setLayoutParams(cardMainLayoutParams);
331 | cardMain.setCardBackgroundColor(context.getResources().getColor(color_id));
332 | // cardMain.setCardBackgroundColor(0xFF018786);
333 | cardMain.setFocusable(true);
334 | cardMain.setClickable(true);
335 | //创建框架LinearLayout - frameworkLinearLayout
336 | LinearLayout frameworkLinearLayout = new LinearLayout(context);
337 | LinearLayout.LayoutParams frameworkLinearLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
338 | frameworkLinearLayout.setLayoutParams(frameworkLinearLayoutParams);
339 | frameworkLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
340 | //创建小的图标 - icon
341 | ImageView icon = new ImageView(context);
342 | LinearLayout.LayoutParams iconParams = new LinearLayout.LayoutParams(dp2px(context, 28), dp2px(context, 28));
343 | iconParams.gravity = Gravity.CENTER_VERTICAL;
344 | iconParams.setMargins(dp2px(context, 20), 0, dp2px(context, 16), 0);
345 | icon.setLayoutParams(iconParams);
346 | // icon.setBackground(context.getResources().getDrawable(icon_id));//弃用了,用下面这句话
347 | icon.setBackgroundResource(icon_id);
348 | //创建标题 - title
349 | TextView title = new TextView(context);
350 | LinearLayout.LayoutParams tvParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
351 | title.setLayoutParams(tvParams);
352 | title.setPadding(0, dp2px(context, 21), 0, dp2px(context, 21));
353 | title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
354 | title.setTextColor(context.getResources().getColor(R.color.black));
355 | // title.setTextAppearance(R.attr.textAppearanceHeadline6);
356 | title.setText(str);
357 | //添加布局
358 | frameworkLinearLayout.addView(icon);
359 | frameworkLinearLayout.addView(title);
360 | cardMain.addView(frameworkLinearLayout);
361 | return cardMain;
362 | }
363 |
364 | /**
365 | * 创建一个MD输入框
366 | *
367 | * @param width_ViewGroup_LayoutParams_WHAT
368 | * @param height_ViewGroup_LayoutParams_WHAT
369 | * @return
370 | */
371 | public static TextInputLayout createTextInputDefault(Context context, int width_ViewGroup_LayoutParams_WHAT, int height_ViewGroup_LayoutParams_WHAT) {
372 | /**
373 | * width和height从ViewGroup.LayoutParams里选MATCH_PARENT或WRAP_CONTENT
374 | */
375 | TextInputLayout inputLayout = new TextInputLayout(context, null, R.style.Widget_MaterialComponents_TextInputLayout_OutlinedBox);
376 | LinearLayout.LayoutParams inputLayoutParams = new LinearLayout.LayoutParams(width_ViewGroup_LayoutParams_WHAT, height_ViewGroup_LayoutParams_WHAT);
377 | inputLayoutParams.setMargins(dp2px(context, 18), dp2px(context, 10), dp2px(context, 18), 0);
378 | inputLayout.setLayoutParams(inputLayoutParams);
379 |
380 | TextInputEditText inputEditText = new TextInputEditText(context);
381 | LinearLayout.LayoutParams inputEditTextParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
382 | inputEditText.setLayoutParams(inputEditTextParams);
383 |
384 | inputLayout.addView(inputEditText);
385 | return inputLayout;
386 | }
387 | }
388 |
--------------------------------------------------------------------------------
/code/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
15 |
16 |
20 |
21 |
24 |
25 |
29 |
30 |
37 |
38 |
47 |
48 |
57 |
58 |
59 |
60 |
61 |
64 |
65 |
69 |
70 |
71 |
72 |
85 |
86 |
91 |
92 |
104 |
105 |
122 |
123 |
124 |
125 |
130 |
131 |
132 |
137 |
138 |
151 |
152 |
165 |
166 |
178 |
179 |
191 |
192 |
193 |
194 |
195 |
200 |
201 |
208 |
209 |
221 |
222 |
231 |
232 |
241 |
242 |
251 |
252 |
253 |
259 |
260 |
270 |
271 |
280 |
281 |
285 |
286 |
291 |
292 |
293 |
294 |
299 |
300 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
332 |
333 |
338 |
339 |
351 |
352 |
369 |
370 |
371 |
372 |
377 |
378 |
379 |
384 |
385 |
398 |
399 |
412 |
413 |
426 |
427 |
428 |
429 |
430 |
435 |
436 |
441 |
442 |
454 |
455 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
483 |
484 |
492 |
493 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
--------------------------------------------------------------------------------
/code/app/src/main/java/com/coolest/toolbox/xposed/HookEntry.kt:
--------------------------------------------------------------------------------
1 | package com.coolest.toolbox.xposed
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.AndroidAppHelper
5 | import android.content.Context
6 | import android.content.res.XResources
7 | import android.view.View
8 | import com.coolest.toolbox.BuildConfig
9 | import com.coolest.toolbox.ui.CoolUtils
10 | import com.coolest.toolbox.utils.CoolConfigHelper
11 | import com.github.kyuubiran.ezxhelper.init.EzXHelperInit
12 | import com.github.kyuubiran.ezxhelper.utils.*
13 | import de.robv.android.xposed.*
14 | import de.robv.android.xposed.callbacks.XC_InitPackageResources
15 | import de.robv.android.xposed.callbacks.XC_LoadPackage
16 |
17 | class HookEntry : IXposedHookLoadPackage, IXposedHookZygoteInit, IXposedHookInitPackageResources {
18 |
19 | override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
20 | EzXHelperInit.initZygote(startupParam)
21 | XposedBridge.log("FM: init zygote")
22 | }
23 |
24 | override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
25 | when (resparam.packageName) {
26 | "com.xiaomi.mirror" -> {
27 | try {
28 | val enabled = try {
29 | CoolConfigHelper.getJsonConfig(
30 | getContext(),
31 | "miui_plus_cust_pc_name_switch"
32 | ) as Boolean
33 | } catch (e: Exception) {
34 | false
35 | }
36 | if (enabled) {
37 | val computerTail =
38 | CoolConfigHelper.getJsonConfig(getContext(), "miui_plus_pc_tail")
39 | when (computerTail) {
40 | "" -> Unit
41 | else -> {
42 | XposedBridge.log("电脑小尾巴替换成功: $computerTail")
43 | resparam.res.setReplacement(
44 | "com.xiaomi.mirror",
45 | "string",
46 | "default_boss_terminal_name",
47 | computerTail
48 | )
49 | }
50 | }
51 | }
52 | } catch (e: Exception) {
53 | XposedBridge.log(e)
54 | }
55 | }
56 | }
57 | }
58 |
59 | @SuppressLint("PrivateApi", "ObsoleteSdkInt", "NewApi")
60 | override fun handleLoadPackage(lparam: XC_LoadPackage.LoadPackageParam?) {
61 | if (lparam != null) {
62 |
63 | EzXHelperInit.initHandleLoadPackage(lparam)
64 | EzXHelperInit.setLogTag("Fuck Miui Xposed")
65 | EzXHelperInit.setToastTag("FM")
66 |
67 | when (lparam.packageName) {
68 | BuildConfig.APPLICATION_ID -> {
69 | try {
70 | XposedBridge.log("FM: Hook self succeed!")
71 |
72 | findMethod("com.coolest.toolbox.ui.MainActivity") {
73 | name == "isXposedActivated" && isNotStatic && returnType == Boolean::class.java
74 | }.hookMethod {
75 | before {
76 | XposedBridge.log("FM: detecting activated")
77 | }
78 | after { param ->
79 | XposedBridge.log("FM: set module active state to activated")
80 | param.result = true
81 | }
82 | }
83 | } catch (e: Exception) {
84 | XposedBridge.log(e)
85 | }
86 | }
87 |
88 | "android" -> {
89 | //miui-services.jar
90 | try {
91 | if (CoolUtils.isMIUI()) {
92 | findMethod("com.android.server.wm.MiuiFreeFormStackDisplayStrategy") {
93 | name == "getMaxMiuiFreeFormStackCount" && isNotStatic && returnType == Int::class.java
94 | }.hookMethod {
95 | before { lparam ->
96 | lparam.result = 256
97 | XposedBridge.log("[FM] 已将无限小窗个数改为${lparam.result}")
98 | }
99 | after { param ->
100 |
101 | }
102 | }
103 | }
104 | } catch (e: Exception) {
105 | XposedBridge.log("[FM] 无限小窗出现错误!")
106 | XposedBridge.log(e)
107 | }
108 |
109 | //services.jar
110 | try {
111 | if (CoolUtils.isMIUI()) {
112 | findMethod("com.android.server.wm.Task") {
113 | name == "isResizeable" && isNotStatic && returnType == Boolean::class.java
114 | }.hookMethod {
115 | before { lparam ->
116 | lparam.result = true
117 | XposedBridge.log("[FM] 强制设置所有程序允许小窗${lparam.result}")
118 | }
119 | after { param ->
120 |
121 | }
122 | }
123 | }
124 | } catch (e: Exception) {
125 | XposedBridge.log("[FM] 强制小窗出现错误!")
126 | XposedBridge.log(e)
127 | }
128 |
129 | //miui-framework.jar
130 | try {
131 | if (CoolUtils.isMIUI()) {
132 | findMethod("android.util.MiuiMultiWindowAdapter") {
133 | name == "getFreeformBlackList" && isStatic
134 | }.hookMethod {
135 | before { lparam ->
136 | lparam.result = listOf("")
137 | XposedBridge.log("[FM] 小窗黑名单=空列表")
138 | }
139 | after { param ->
140 |
141 | }
142 | }
143 | }
144 | } catch (e: Exception) {
145 | XposedBridge.log("[FM] 小窗黑名单出现错误!")
146 | XposedBridge.log(e)
147 | }
148 |
149 | //miui-framework.jar
150 | try {
151 | if (CoolUtils.isMIUI()) {
152 | findMethod("android.util.MiuiMultiWindowAdapter") {
153 | name == "getFreeformBlackListFromCloud" && isStatic
154 | }.hookMethod {
155 | before { lparam ->
156 | lparam.result = listOf("")
157 | XposedBridge.log("[FM] 小窗云控黑名单=空列表")
158 | }
159 | after { param ->
160 |
161 | }
162 | }
163 | }
164 | } catch (e: Exception) {
165 | XposedBridge.log("[FM] 小窗云控黑名单出现错误!")
166 | XposedBridge.log(e)
167 | }
168 |
169 | //miui-framework.jar
170 | try {
171 | if (CoolUtils.isMIUI()) {
172 | findMethod("android.util.MiuiMultiWindowUtils") {
173 | name == "supportFreeform" && isStatic && returnType == Boolean::class.java
174 | }.hookMethod {
175 | before { lparam ->
176 | XposedBridge.log("[FM] 云控设备不支持小窗 = false")
177 | lparam.result = true
178 | }
179 | after { param ->
180 |
181 | }
182 | }
183 | }
184 | } catch (e: Exception) {
185 | XposedBridge.log("[FM] 云控不支持小窗设备列表出现错误!")
186 | XposedBridge.log(e)
187 | }
188 | }
189 |
190 | // "com.miui.misound" -> hookMiSound()
191 | "com.miui.misound" -> try {
192 | if (CoolUtils.isMIUI()) {
193 |
194 | findMethod("com.miui.misound.util.Utils") {
195 | name == "isSupportHarmanKardon" && isStatic && returnType == Boolean::class.java
196 | }.hookMethod {
197 | before { lparam ->
198 | /*val jsonResult =
199 | CoolConfigHelper.getJsonConfig(getContext(),"harmanKardon_enabled")
200 | XposedBridge.log("FM: 哈曼卡顿启用状态: $jsonResult")
201 | lparam.result = try {
202 | jsonResult as Boolean
203 | } catch (e: Exception) {
204 | false
205 | }*/
206 | lparam.result = true
207 | }
208 | after { param ->
209 |
210 | }
211 | }
212 | /*findMethod("com.miui.misound.util.Utils") {
213 | name == "isSupportDolbyDax" && isStatic && returnType == Boolean::class.java
214 | }.hookMethod {
215 | before { lparam ->
216 | *//*val jsonResult = when(CoolUtils.isPad()) {
217 | true -> true
218 | false -> CoolConfigHelper.getJsonConfig("dolby_enabled")
219 | }*//*
220 | *//*val jsonResult = CoolConfigHelper.getJsonConfig(getContext(),"dolby_enabled")
221 | XposedBridge.log("FM: 杜比全景声启用状态: $jsonResult")
222 | lparam.result = try {
223 | jsonResult as Boolean
224 | } catch (e: Exception) {
225 | false
226 | }*//*
227 | lparam.result = true
228 | }
229 | after { param ->
230 |
231 | }
232 | }*/
233 |
234 | }
235 | } catch (e: Exception) {
236 | XposedBridge.log("[FM] 哈曼卡顿出现错误!")
237 | XposedBridge.log(e)
238 | }
239 |
240 | "com.miui.home" -> {
241 | if (CoolUtils.isMIUI()) {
242 |
243 | try {
244 | findMethod("com.miui.home.launcher.RecentsAndFSGestureUtils") {
245 | name == "canTaskEnterSmallWindow" && isStatic && returnType == Boolean::class.java
246 | }.hookMethod {
247 | before { param ->
248 | XposedBridge.log("[FM]: 允许进程进入小窗")
249 | param.result = true
250 | }
251 | after { param ->
252 | }
253 | }
254 | } catch (e: Exception) {
255 | XposedBridge.log(e)
256 | }
257 |
258 | try {
259 | findMethod("com.miui.home.launcher.RecentsAndFSGestureUtils") {
260 | name == "canTaskEnterMiniSmallWindow" && isStatic && returnType == Boolean::class.java
261 | }.hookMethod {
262 | before {
263 | XposedBridge.log("FM: detecting activated")
264 | }
265 | after { param ->
266 | XposedBridge.log("FM: set module active state to activated")
267 | param.result = true
268 | }
269 | }
270 | } catch (e: Exception) {
271 | XposedBridge.log(e)
272 | }
273 | }
274 | }
275 |
276 | "com.miui.securitycenter" -> {
277 | if (CoolUtils.isMIUI()) {
278 | val jsonResult =
279 | CoolConfigHelper.getJsonConfig(
280 | getContext(),
281 | "miui_secure_center_lock_100"
282 | )
283 | when (jsonResult as Boolean) {
284 | true -> {
285 | //防止点击重新检测
286 | findMethod("com.miui.securityscan.ui.main.MainContentFrame") {
287 | name == "onClick" && parameterTypes[0] == View::class.java
288 | }.hookMethod {
289 | before { param -> param.result = null }
290 | }
291 |
292 | //锁定100分
293 | findMethod("com.miui.securityscan.scanner.ScoreManager") {
294 | name == "B"
295 | }.hookMethod {
296 | before { param -> param.result = 0 }
297 | }
298 | }
299 | false -> {}
300 | }
301 | }
302 | }
303 |
304 | "com.xiaomi.mirror" -> {
305 | XposedBridge.log("FM: MIUI+ app has found!")
306 |
307 | //解锁平板MIUI+
308 | try {
309 | findMethod("com.xiaomi.mirror.utils.DeviceUtils") {
310 | name == "isPadDevice" && isStatic && returnType == Boolean::class.java
311 | }.hookMethod {
312 | before {
313 | // XposedBridge.log("FM: Found method in [com.xiaomi.mirror.utils.DeviceUtils]")
314 | XposedBridge.log("FM: Found method @isPad in [com.xiaomi.mirror]")
315 | }
316 | after { param ->
317 | // XposedBridge.log("FM: com.xiaomi.mirror.utils.DeviceUtils.isPadDevice() returns false")
318 | val jsonResult =
319 | CoolConfigHelper.getJsonConfig(
320 | getContext(),
321 | "miui_plus_connect_pc"
322 | )
323 | XposedBridge.log("FM: Hooked com.xiaomi.mirror @isPad, user's setting is $jsonResult")
324 | param.result = !try {
325 | jsonResult as Boolean
326 | } catch (e: Exception) {
327 | false
328 | }
329 | }
330 | }
331 | } catch (e: Exception) {
332 | XposedBridge.log(e)
333 | }
334 |
335 | //解锁平板MIUI+
336 | //3.5.17c
337 | try {
338 | findMethod("com.xiaomi.mirror.device.DeviceUtils") {
339 | name == "isPadDevice" && isStatic && returnType == Boolean::class.java
340 | }.hookMethod {
341 | before {
342 | // XposedBridge.log("FM: Found method in [com.xiaomi.mirror.utils.DeviceUtils]")
343 | XposedBridge.log("FM: Found method @isPad in [com.xiaomi.mirror] v3.5.17c")
344 | }
345 | after { param ->
346 | // XposedBridge.log("FM: com.xiaomi.mirror.utils.DeviceUtils.isPadDevice() returns false")
347 | val jsonResult =
348 | CoolConfigHelper.getJsonConfig(
349 | getContext(),
350 | "miui_plus_connect_pc"
351 | )
352 | XposedBridge.log("FM: Hooked com.xiaomi.mirror @isPad, user's setting is $jsonResult v3.5.17c")
353 | param.result = !try {
354 | jsonResult as Boolean
355 | } catch (e: Exception) {
356 | false
357 | }
358 | }
359 | }
360 | } catch (e: Exception) {
361 | XposedBridge.log(e)
362 | }
363 |
364 | try {
365 | findMethod("com.xiaomi.onetrack.util.DeviceUtil") {
366 | name == "p" && isStatic && returnType == Boolean::class.java
367 | }.hookMethod {
368 | before {
369 | // XposedBridge.log("FM: Found method in [com.xiaomi.mirror.utils.DeviceUtils]")
370 | XposedBridge.log("FM: Found method @isPad in [com.xiaomi.mirror]")
371 | }
372 | after { param ->
373 | // XposedBridge.log("FM: com.xiaomi.mirror.utils.DeviceUtils.isPadDevice() returns false")
374 | val jsonResult =
375 | CoolConfigHelper.getJsonConfig(
376 | getContext(),
377 | "miui_plus_connect_pc"
378 | )
379 | XposedBridge.log("FM: Hooked com.xiaomi.mirror @isPad, user's setting is $jsonResult")
380 | param.result = !try {
381 | jsonResult as Boolean
382 | } catch (e: Exception) {
383 | false
384 | }
385 | }
386 | }
387 | } catch (e: Exception) {
388 | XposedBridge.log(e)
389 | }
390 |
391 | /* try {
392 | findMethod("com.xiaomi.mirror.ak") {
393 | name == "a" && returnType == Boolean::class.java
394 | }.hookMethod {
395 | before {
396 | // XposedBridge.log("FM: Found method in [com.xiaomi.mirror.ak]")
397 | XposedBridge.log("FM: Found method @a in [com.xiaomi.mirror]")
398 | }
399 | after { param ->
400 | // XposedBridge.log("FM: com.xiaomi.mirror.ak.a() returns true")
401 | XposedBridge.log("FM: Hooked com.xiaomi.mirror @a")
402 | param.result = true
403 | }
404 | }
405 | } catch (e: Exception) {
406 | XposedBridge.log(e)
407 | }*/
408 |
409 | //解锁不支持得的设备的MIUI+
410 | try {
411 | findMethod("com.xiaomi.mirror.utils.SystemUtils") {
412 | name == "isModelSupport" && returnType == Boolean::class.java
413 | }.hookMethod {
414 | before {
415 | // XposedBridge.log("FM: Found method in [com.xiaomi.mirror.utils.SystemUtils]")
416 | XposedBridge.log("FM: Found method @SystemUtils in [com.xiaomi.mirror]")
417 | }
418 | after { param ->
419 | // XposedBridge.log("FM: com.xiaomi.mirror.utils.SystemUtils.isModelSupport() returns true")
420 | XposedBridge.log("FM: Hooked com.xiaomi.mirror @SystemUtils!")
421 | param.result = true
422 | }
423 | }
424 |
425 | /*
426 | //======================================
427 | //修改类中的变量值示例代码
428 | val field = findAllFields("com.miui.player.effect.dirac.DiracUtils", false){
429 | true
430 | }
431 | lateinit var myObject: Any;
432 | lateinit var myField: Field;
433 | field.forEach {
434 | XposedBridge.log("[debug] " + it.name)
435 |
436 | if (it.name == "VAL_OFF") {
437 | myField = it
438 | myObject = it.get(it.javaClass)!!
439 | }
440 | }
441 | myField.set(myField.javaClass, false)
442 | //======================================
443 |
444 | //======================================
445 | //调用某个类中的方法
446 | val method = findMethod("com.miui.player.effect.dirac.DiracUtils"){
447 | name == "isSupportEarcompensation" && isStatic && returnType == Boolean::class.java
448 | }
449 | val bool = method.invoke(Boolean::class.java)//这个方法返回值是boolean所以参数传的是Boolean::class.java
450 | //======================================
451 | */
452 |
453 | } catch (e: Exception) {
454 | XposedBridge.log(e)
455 | }
456 |
457 | //自定义电脑名
458 | try {
459 | val enabled = try {
460 | CoolConfigHelper.getJsonConfig(
461 | getContext(),
462 | "miui_plus_cust_pc_name_switch"
463 | ) as Boolean
464 | } catch (e: Exception) {
465 | false
466 | }
467 | if (enabled) {
468 | findMethod("com.xiaomi.mirror.MirrorAppService") {
469 | name == "getMasterName" && isNotStatic && returnType == String::class.java
470 | }.hookMethod {
471 | before {
472 | // XposedBridge.log("FM: Found method in [com.xiaomi.mirror.utils.SystemUtils]")
473 | XposedBridge.log("FM: Found method @masterName in [com.xiaomi.mirror]")
474 | }
475 | after { param ->
476 | // XposedBridge.log("FM: com.xiaomi.mirror.utils.SystemUtils.isModelSupport() returns true")
477 | val computerName =
478 | CoolConfigHelper.getJsonConfig(
479 | getContext(),
480 | "miui_plus_pc_name"
481 | )
482 | XposedBridge.log("自定义电脑名 $enabled -> $computerName")
483 | param.result = when (computerName) {
484 | "" -> param.result
485 | else -> computerName
486 | }
487 | }
488 | }
489 | }
490 | } catch (e: Exception) {
491 | XposedBridge.log(e)
492 | }
493 | }
494 |
495 | "com.android.settings" -> {
496 | XposedBridge.log("FM: Settings Found!")
497 |
498 | try {
499 | findMethod("com.android.settings.connection.MiMirrorController") {
500 | name == "isMirrorSupported" && isNotStatic && returnType == Boolean::class.java
501 | }.hookMethod {
502 | before {
503 | // XposedBridge.log("FM: Found method in [com.android.settings.connection.MiMirrorController]")
504 | XposedBridge.log("FM: Found method in [com.android.settings]")
505 | }
506 | after { param ->
507 | // XposedBridge.log("FM: com.android.settings.connection.MiMirrorController.isMirrorSupport() returns true")
508 | XposedBridge.log("FM: Hooked com.android.settings!")
509 | param.result = true
510 | }
511 | }
512 | } catch (e: Exception) {
513 | XposedBridge.log(e)
514 | }
515 | }
516 |
517 | //平板流转手机
518 | /*"com.xiaomi.mi_connect_service" -> {
519 | XposedBridge.log("FM: mi_connect_service app has found!")
520 |
521 | try {
522 | findMethod("b.h.q.e2.q") {
523 | name == "p" && isStatic && returnType == Boolean::class.java
524 | }.hookMethod {
525 | before {
526 | XposedBridge.log("FM: Found method in [mi_connect_service {p}]")
527 | }
528 | after { param ->
529 | val jsonResult =
530 | CoolConfigHelper.getJsonConfig("miui_plus_connect_pc")
531 | XposedBridge.log("FM: Hooked com.xiaomi.mi_connect_service {p} @isPad, user's setting is $jsonResult")
532 | param.result = !try {
533 | jsonResult as Boolean
534 | } catch (e: Exception) {
535 | false
536 | }
537 | }
538 | }
539 | findMethod("b.h.q.e2.q") {
540 | name == "q" && isStatic && returnType == Boolean::class.java
541 | }.hookMethod {
542 | before {
543 | XposedBridge.log("FM: Found method in [mi_connect_service {q}]")
544 | }
545 | after { param ->
546 | val jsonResult =
547 | CoolConfigHelper.getJsonConfig("miui_plus_connect_pc")
548 | XposedBridge.log("FM: Hooked com.xiaomi.mi_connect_service {q} @isPad, user's setting is $jsonResult")
549 | param.result = !try {
550 | jsonResult as Boolean
551 | } catch (e: Exception) {
552 | false
553 | }
554 | }
555 | }
556 | findMethod("b.h.e.e") {
557 | name == "e" && isStatic && returnType == Boolean::class.java
558 | }.hookMethod {
559 | before {
560 | XposedBridge.log("FM: Found method in [mi_connect_service {e}]")
561 | }
562 | after { param ->
563 | val jsonResult =
564 | CoolConfigHelper.getJsonConfig("miui_plus_connect_pc")
565 | XposedBridge.log("FM: Hooked com.xiaomi.mi_connect_service {e} @isPad, user's setting is $jsonResult")
566 | param.result = !try {
567 | jsonResult as Boolean
568 | } catch (e: Exception) {
569 | false
570 | }
571 | }
572 | }
573 | findMethod("com.xiaomi.onetrack.h.h") {
574 | name == "o" && isStatic && returnType == Boolean::class.java
575 | }.hookMethod {
576 | before {
577 | XposedBridge.log("FM: Found method in [mi_connect_service {o}]")
578 | }
579 | after { param ->
580 | val jsonResult =
581 | CoolConfigHelper.getJsonConfig("miui_plus_connect_pc")
582 | XposedBridge.log("FM: Hooked com.xiaomi.mi_connect_service {o} @isPad, user's setting is $jsonResult")
583 | param.result = !try {
584 | jsonResult as Boolean
585 | } catch (e: Exception) {
586 | false
587 | }
588 | }
589 | }
590 | } catch (e: Exception) {
591 | XposedBridge.log(e)
592 | }
593 | }*/
594 |
595 | //解锁通话
596 | /*"com.android.contacts" -> {
597 | XposedBridge.log("FM: Contacts app has found!")
598 |
599 | try {
600 | findMethod("com.android.contacts.activities.PeopleActivityV2") {
601 | name == "processIntent" && isNotStatic && returnType == Boolean::class.java && paramCount == 1
602 | }.hookMethod {
603 | before {
604 | XposedBridge.log("FM: Found method in [com.android.contacts.activities.PeopleActivityV2]")
605 | }
606 | after { param ->
607 | XposedBridge.log("FM: com.android.contacts.activities.PeopleActivityV2.processIntent() returns true")
608 | param.result = true
609 | }
610 | }
611 | } catch (e: Exception) {
612 | // XposedBridge.log(e)
613 | }
614 |
615 | try {
616 | findMethod("com.miui.contacts.common.SystemUtil") {
617 | name == "B" && isStatic && returnType == Boolean::class.java
618 | }.hookMethod {
619 | before {
620 | XposedBridge.log("FM: Found method in [com.miui.contacts.common.SystemUtil]")
621 | }
622 | after { param ->
623 | XposedBridge.log("FM: com.miui.contacts.common.SystemUtil.B() returns true")
624 | param.result = false
625 | }
626 | }
627 | } catch (e: Exception) {
628 | // XposedBridge.log(e)
629 | }
630 |
631 | try {
632 | findMethod("com.miui.contacts.common.SystemUtil") {
633 | name == "a" && isStatic && returnType == Boolean::class.java
634 | }.hookMethod {
635 | before {
636 | XposedBridge.log("FM: Found method in [com.miui.contacts.common.SystemUtil]")
637 | }
638 | after { param ->
639 | XposedBridge.log("FM: com.miui.contacts.common.SystemUtil.a() returns true")
640 | param.result = false
641 | }
642 | }
643 | } catch (e: Exception) {
644 | // XposedBridge.log(e)
645 | }
646 | }
647 |
648 | "com.android.server.telecom" -> {
649 | XposedBridge.log("FM: ContactServer app has found!")
650 |
651 | try {
652 | findMethod("com.android.server.telecom.components.UserCallIntentProcessor") {
653 | name == "isVoiceCapable" && isNotStatic && returnType == Boolean::class.java
654 | }.hookMethod {
655 | before {
656 | XposedBridge.log("FM: Found method in [com.android.server.telecom.components.UserCallIntentProcessor]")
657 | }
658 | after { param ->
659 | XposedBridge.log("FM: com.android.server.telecom.components.UserCallIntentProcessor.isVoiceCapable() returns true")
660 | param.result = true
661 | }
662 | }
663 | } catch (e: Exception) {
664 | XposedBridge.log(e)
665 | }
666 | }*/
667 |
668 | }
669 | }
670 | }
671 | }
--------------------------------------------------------------------------------