Skip to content

Commit

Permalink
Update 2.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
YuKongA committed Feb 7, 2024
1 parent 752de17 commit c5379c5
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 171 deletions.
14 changes: 10 additions & 4 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ plugins {
}

android {
namespace = "top.yukonga.MediaControlBlur"
namespace = "top.yukonga.mediaControlBlur"
compileSdk = 34

defaultConfig {
applicationId = namespace
minSdk = 33
minSdk = 34
targetSdk = 34
versionCode = 1001
versionName = "1.0.1"
versionCode = 2000
versionName = "2.0.0"
}

buildTypes {
Expand Down Expand Up @@ -50,9 +50,15 @@ android {
}
}
}

androidResources {
additionalParameters += arrayOf("--allow-reserved-package-id", "--package-id", "0x62")
}
}

dependencies {
compileOnly(libs.xposed)
implementation(libs.coreKtx)
implementation(libs.ezXHelper)
implementation(libs.hiddenapibypass)
}
278 changes: 111 additions & 167 deletions app/src/main/kotlin/top/yukonga/mediaControlBlur/MainHook.kt
Original file line number Diff line number Diff line change
@@ -1,136 +1,152 @@
package top.yukonga.mediaControlBlur

import android.graphics.Bitmap
import android.graphics.Canvas
import android.app.AndroidAppHelper
import android.graphics.Color
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.HardwareRenderer
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.PixelFormat
import android.graphics.RenderEffect
import android.graphics.RenderNode
import android.graphics.Shader
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Icon
import android.hardware.HardwareBuffer
import android.media.ImageReader
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
import com.github.kyuubiran.ezxhelper.ClassUtils.loadClassOrNull
import com.github.kyuubiran.ezxhelper.EzXHelper
import com.github.kyuubiran.ezxhelper.EzXHelper.moduleRes
import com.github.kyuubiran.ezxhelper.HookFactory.`-Static`.createHook
import com.github.kyuubiran.ezxhelper.Log
import com.github.kyuubiran.ezxhelper.ObjectHelper.Companion.objectHelper
import com.github.kyuubiran.ezxhelper.finders.MethodFinder.`-Static`.methodFinder
import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.IXposedHookZygoteInit
import de.robv.android.xposed.XposedHelpers
import de.robv.android.xposed.callbacks.XC_LoadPackage
import kotlin.math.sqrt
import kotlin.random.Random
import top.yukonga.mediaControlBlur.utils.AppUtils.GREY
import top.yukonga.mediaControlBlur.utils.AppUtils.colorFilterCompat
import top.yukonga.mediaControlBlur.utils.AppUtils.isDarkMode
import top.yukonga.mediaControlBlur.utils.blur.MiBlurUtils.clearMiBackgroundBlendColor
import top.yukonga.mediaControlBlur.utils.blur.MiBlurUtils.setBlurRoundRect
import top.yukonga.mediaControlBlur.utils.blur.MiBlurUtils.setMiBackgroundBlendColors
import top.yukonga.mediaControlBlur.utils.blur.MiBlurUtils.setMiViewBlurMode
import top.yukonga.mediaControlBlur.utils.blur.MiBlurUtils.setPassWindowBlurEnabled

private const val TAG = "MediaControlBlur"
private var artwork: Icon? = null

class MainHook : IXposedHookLoadPackage {
class MainHook : IXposedHookLoadPackage, IXposedHookZygoteInit {

override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
EzXHelper.initZygote(startupParam)
}

override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
EzXHelper.initHandleLoadPackage(lpparam)
EzXHelper.setLogTag(TAG)
EzXHelper.setToastTag(TAG)
when (lpparam.packageName) {

"com.android.systemui" -> {

// 部分代码来自 Hyper Helper (https://github.com/HowieHChen/XiaomiHelper/blob/master/app/src/main/kotlin/dev/lackluster/mihelper/hook/rules/systemui/CustomMusicControl.kt)
try {
val miuiMediaControlPanel = loadClassOrNull("com.android.systemui.statusbar.notification.mediacontrol.MiuiMediaControlPanel")
val playerTwoCircleView = loadClassOrNull("com.android.systemui.statusbar.notification.mediacontrol.PlayerTwoCircleView")

// 获取 Icon
miuiMediaControlPanel?.methodFinder()?.filterByName("bindPlayer")?.first()?.createHook {
before {
artwork = it.args[0].objectHelper().getObjectOrNullAs<Icon>("artwork") ?: return@before
}
}
after {
val context = AndroidAppHelper.currentApplication().applicationContext

val mMediaViewHolder = it.thisObject.objectHelper().getObjectOrNullUntilSuperclass("mMediaViewHolder") ?: return@after

val titleText = mMediaViewHolder.objectHelper().getObjectOrNullAs<TextView>("titleText")
val artistText = mMediaViewHolder.objectHelper().getObjectOrNullAs<TextView>("artistText")
val seamlesIcon = mMediaViewHolder.objectHelper().getObjectOrNullAs<ImageView>("seamlessIcon")
val action0 = mMediaViewHolder.objectHelper().getObjectOrNullAs<ImageButton>("action0")
val action1 = mMediaViewHolder.objectHelper().getObjectOrNullAs<ImageButton>("action1")
val action2 = mMediaViewHolder.objectHelper().getObjectOrNullAs<ImageButton>("action2")
val action3 = mMediaViewHolder.objectHelper().getObjectOrNullAs<ImageButton>("action3")
val action4 = mMediaViewHolder.objectHelper().getObjectOrNullAs<ImageButton>("action4")
val seekBar = mMediaViewHolder.objectHelper().getObjectOrNullAs<SeekBar>("seekBar")
val elapsedTimeView = mMediaViewHolder.objectHelper().getObjectOrNullAs<TextView>("elapsedTimeView")
val totalTimeView = mMediaViewHolder.objectHelper().getObjectOrNullAs<TextView>("totalTimeView")

if (!isDarkMode(context)) {
titleText?.setTextColor(Color.BLACK)
artistText?.setTextColor(GREY)
elapsedTimeView?.setTextColor(GREY)
totalTimeView?.setTextColor(GREY)
seamlesIcon?.setColorFilter(Color.BLACK)
action0?.setColorFilter(Color.BLACK)
action1?.setColorFilter(Color.BLACK)
action2?.setColorFilter(Color.BLACK)
action3?.setColorFilter(Color.BLACK)
action4?.setColorFilter(Color.BLACK)
seekBar?.progressDrawable?.colorFilter = colorFilterCompat(Color.BLACK)
seekBar?.thumb?.colorFilter = colorFilterCompat(Color.BLACK)
} else {
titleText?.setTextColor(Color.WHITE)
seamlesIcon?.setColorFilter(Color.WHITE)
action0?.setColorFilter(Color.WHITE)
action1?.setColorFilter(Color.WHITE)
action2?.setColorFilter(Color.WHITE)
action3?.setColorFilter(Color.WHITE)
action4?.setColorFilter(Color.WHITE)
seekBar?.progressDrawable?.colorFilter = colorFilterCompat(Color.WHITE)
seekBar?.thumb?.colorFilter = colorFilterCompat(Color.WHITE)
}

// 重写 onDraw
playerTwoCircleView?.methodFinder()?.filterByName("onDraw")?.first()?.createHook {
before {
(it.thisObject.objectHelper().getObjectOrNullAs<Paint>("mPaint1"))?.alpha = 0
(it.thisObject.objectHelper().getObjectOrNullAs<Paint>("mPaint2"))?.alpha = 0
it.thisObject.objectHelper().setObject("mRadius", 0.0f)
}
}

// 重写 setBackground
val playerTwoCircleView = loadClassOrNull("com.android.systemui.statusbar.notification.mediacontrol.PlayerTwoCircleView")
playerTwoCircleView?.methodFinder()?.filterByName("setBackground")?.first()?.createHook {
replace {
if (artwork == null) return@replace it
val imageView = it.thisObject as ImageView
val backgroundColors = it.args[0] as IntArray

// 获取 Bitmap
var artworkLayer = artwork?.loadDrawable(imageView.context) ?: return@replace it
val artworkBitmap = Bitmap.createBitmap(artworkLayer.intrinsicWidth, artworkLayer.intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(artworkBitmap)
artworkLayer.setBounds(0, 0, artworkLayer.intrinsicWidth, artworkLayer.intrinsicHeight)
artworkLayer.draw(canvas)

// 缩小图片
val tmpBitmap = Bitmap.createScaledBitmap(artworkBitmap, 132, 132, true)
val tmpBitmapXS = Bitmap.createScaledBitmap(artworkBitmap, tmpBitmap.width / 2, tmpBitmap.height / 2, true)

// 创建混合图
val bigBitmap = Bitmap.createBitmap(tmpBitmap.width * 2, tmpBitmap.height * 2, Bitmap.Config.ARGB_8888)
val canvas2 = Canvas(bigBitmap)

// 生成随机图
val rotImages = mutableListOf<Bitmap>()
for (i in 1..5) {

// 中心点随机旋转 90°
val rotateMatrix = Matrix()
val pivotX = tmpBitmap.width / 2f
val pivotY = tmpBitmap.height / 2f
val rotationAngle = Random.nextInt(4) * 90f
rotateMatrix.postRotate(rotationAngle, pivotX, pivotY)

// 随机进行翻转和镜像
val flipHorizontal = Random.nextBoolean()
val flipVertical = Random.nextBoolean()
rotateMatrix.postScale(if (flipHorizontal) -1f else 1f, if (flipVertical) -1f else 1f, pivotX, pivotY)

val rotatedImage = if (i <= 4) {
Bitmap.createBitmap(tmpBitmap, 0, 0, tmpBitmap.width, tmpBitmap.height, rotateMatrix, true)
val mediaBg = it.thisObject as ImageView
mediaBg.background = null
mediaBg.setImageDrawable(null)
}
}

val miuiExpandableNotificationRow = loadClassOrNull("com.android.systemui.statusbar.notification.row.MiuiExpandableNotificationRow")
miuiExpandableNotificationRow?.methodFinder()?.filterByName("updateBlurBg")?.first()?.createHook {
after {
val context = AndroidAppHelper.currentApplication().applicationContext

val isHeadsUpState = it.thisObject.objectHelper().invokeMethodBestMatch("isHeadsUpState") as Boolean
val mExpandedParamsUpdating = it.thisObject.objectHelper().getObjectOrNullUntilSuperclassAs<Boolean>("mExpandedParamsUpdating")!!
val getViewState = it.thisObject.objectHelper().invokeMethodBestMatch("getViewState")
val animatingMiniWindowEnter = getViewState?.objectHelper()?.getObjectOrNullUntilSuperclassAs<Boolean>("animatingMiniWindowEnter")!!
val z2 = !(!mExpandedParamsUpdating && !animatingMiniWindowEnter)

val intArray = if (isHeadsUpState) {
if (isDarkMode(context)) moduleRes.getIntArray(R.array.notification_element_blend_headsUp_colors_night) else moduleRes.getIntArray(R.array.notification_element_blend_headsUp_colors_light)
} else {
if (z2) {
if (isDarkMode(context)) moduleRes.getIntArray(R.array.notification_element_blend_keyguard_colors_night) else moduleRes.getIntArray(
R.array.notification_element_blend_keyguard_colors_light
)
} else {
Bitmap.createBitmap(tmpBitmapXS, 0, 0, tmpBitmapXS.width, tmpBitmapXS.height, rotateMatrix, true)
if (isDarkMode(context)) moduleRes.getIntArray(R.array.notification_element_blend_shade_colors_night) else moduleRes.getIntArray(
R.array.notification_element_blend_shade_colors_light
)
}
rotImages.add(rotatedImage)
}

// 将随机图绘制到混合大图上
canvas2.drawBitmap(rotImages[0], 0f, 0f, null) // 左上角
canvas2.drawBitmap(rotImages[1], tmpBitmap.width.toFloat(), 0f, null) // 右上角
canvas2.drawBitmap(rotImages[2], 0f, tmpBitmap.height.toFloat(), null) // 左下角
canvas2.drawBitmap(rotImages[3], tmpBitmap.width.toFloat(), tmpBitmap.height.toFloat(), null) // 右下角
canvas2.drawBitmap(rotImages[4], tmpBitmap.width / 4f * 3f, tmpBitmap.height / 4f * 3f, null) // 中心

// 颜色处理
val brightness = bigBitmap.brightness()
val colorMatrix = brightness.colorMatrix()
val paint = Paint()
paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
canvas2.drawBitmap(bigBitmap, 0f, 0f, paint)
canvas2.drawColor(backgroundColors[0] and 0x6FFFFFFF)

// 模糊处理
artworkLayer = BitmapDrawable(imageView.resources, bigBitmap.blur(40f))

// 绘制到 ImageView 上
imageView.setImageDrawable(artworkLayer)
val mBackgroundNormal = it.thisObject.objectHelper().getObjectOrNullUntilSuperclass("mBackgroundNormal")
val notificationUtil = loadClassOrNull("com.android.systemui.statusbar.notification.NotificationUtil")
XposedHelpers.callStaticMethod(notificationUtil, "applyElementViewBlend", context, mBackgroundNormal, intArray, z2)

playerTwoCircleView?.methodFinder()?.filterByName("onDraw")?.first()?.createHook {
before {
(it.thisObject.objectHelper().getObjectOrNullAs<Paint>("mPaint1"))?.alpha = 0
(it.thisObject.objectHelper().getObjectOrNullAs<Paint>("mPaint2"))?.alpha = 0
it.thisObject.objectHelper().setObject("mRadius", 0.0f)

val mediaBg = it.thisObject as ImageView

mediaBg.apply {
clearMiBackgroundBlendColor()
setPassWindowBlurEnabled(true)
setMiViewBlurMode(1)
setBlurRoundRect(60)
setMiBackgroundBlendColors(intArray, 1.0f)
}
}
}
}

}

} catch (t: Throwable) {
Log.ex(t)
}
Expand All @@ -140,76 +156,4 @@ class MainHook : IXposedHookLoadPackage {
}
}

}

private fun Bitmap.blur(radius: Float): Bitmap {

// 该部分来自 Google (https://developer.android.google.cn/guide/topics/renderscript/migrate)
val imageReader =
ImageReader.newInstance(this.width, this.height, PixelFormat.RGBA_8888, 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
val renderNode = RenderNode("BlurEffect")
val hardwareRenderer = HardwareRenderer()

hardwareRenderer.setSurface(imageReader.surface)
hardwareRenderer.setContentRoot(renderNode)
renderNode.setPosition(0, 0, imageReader.width, imageReader.height)
val blurRenderEffect = RenderEffect.createBlurEffect(
radius, radius, Shader.TileMode.MIRROR
)
renderNode.setRenderEffect(blurRenderEffect)

val renderCanvas = renderNode.beginRecording()
renderCanvas.drawBitmap(this, 0f, 0f, null)
renderNode.endRecording()
hardwareRenderer.createRenderRequest().setWaitForPresent(true).syncAndDraw()

val image = imageReader.acquireNextImage() ?: throw RuntimeException("No Image")
val hardwareBuffer = image.hardwareBuffer ?: throw RuntimeException("No HardwareBuffer")
val bitmap = Bitmap.wrapHardwareBuffer(hardwareBuffer, null) ?: throw RuntimeException("Create Bitmap Failed")

hardwareBuffer.close()
image.close()
imageReader.close()
renderNode.discardDisplayList()
hardwareRenderer.destroy()

return bitmap
}

fun Bitmap.brightness(): Float {
var totalBrightness = 0f
val totalPixels = this.width * this.height

for (x in 0 until this.width) {
for (y in 0 until this.height) {
val pixel = this.getPixel(x, y)
val red = Color.red(pixel)
val green = Color.green(pixel)
val blue = Color.blue(pixel)
val brightness = sqrt(0.299f * red * red + 0.587f * green * green + 0.114f * blue * blue)
totalBrightness += brightness
}
}

return totalBrightness / totalPixels
}

fun Float.colorMatrix(): ColorMatrix {
val colorMatrix = ColorMatrix()
val adjustment = when {
this < 50 -> 40f
this < 100 -> 20f
this > 200 -> -40f
this > 150 -> -20f
else -> 0f
}
colorMatrix.set(
floatArrayOf(
1f, 0f, 0f, 0f, adjustment, // red
0f, 1f, 0f, 0f, adjustment, // green
0f, 0f, 1f, 0f, adjustment, // blue
0f, 0f, 0f, 1f, 0f // alpha
)
)
return colorMatrix
}
17 changes: 17 additions & 0 deletions app/src/main/kotlin/top/yukonga/mediaControlBlur/utils/AppUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package top.yukonga.mediaControlBlur.utils

import android.content.Context
import android.content.res.Configuration
import android.graphics.BlendMode
import android.graphics.BlendModeColorFilter
import android.graphics.Color

object AppUtils {

val GREY = Color.parseColor("#857772")

fun colorFilterCompat(colorInt: Int) = BlendModeColorFilter(colorInt, BlendMode.SRC_IN)

fun isDarkMode(context: Context): Boolean = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES

}
Loading

0 comments on commit c5379c5

Please sign in to comment.