Skip to content

Commit

Permalink
优化 焦点歌词,并为手机端添加焦点歌词加载时隐藏状态栏时钟
Browse files Browse the repository at this point in the history
Co-authored-by: @wuyou-123
  • Loading branch information
lingqiqi5211 committed Nov 18, 2024
1 parent 025d466 commit 54a9712
Show file tree
Hide file tree
Showing 9 changed files with 340 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
import com.sevtinge.hyperceiler.module.hook.systemui.statusbar.icon.all.StatusBarIconPositionAdjust;
import com.sevtinge.hyperceiler.module.hook.systemui.statusbar.icon.all.WifiNetworkIndicator;
import com.sevtinge.hyperceiler.module.hook.systemui.statusbar.icon.v.FocusNotifLyric;
import com.sevtinge.hyperceiler.module.hook.systemui.statusbar.icon.v.HideFakeStatusBar;
import com.sevtinge.hyperceiler.module.hook.systemui.statusbar.layout.StatusBarLayout;
import com.sevtinge.hyperceiler.module.hook.systemui.statusbar.model.DualRowSignalHook;
import com.sevtinge.hyperceiler.module.hook.systemui.statusbar.model.MobileNetwork;
Expand Down Expand Up @@ -203,7 +204,10 @@ public void handleLoadPackage() {
// // initHook(new DisplayHardwareDetailForHyper(), true);

// 焦点歌词
initHook(FocusNotifLyric.INSTANCE, mPrefsMap.getBoolean("system_ui_statusbar_music_switch"));
if (mPrefsMap.getBoolean("system_ui_statusbar_music_switch")) {
initHook(FocusNotifLyric.INSTANCE);
initHook(HideFakeStatusBar.INSTANCE, mPrefsMap.getBoolean("system_ui_statusbar_music_hide_clock"));
}

// 灵动舞台
initHook(HideStrongToast.INSTANCE, mPrefsMap.getBoolean("system_ui_status_bar_hide_smart_strong_toast"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ abstract class MusicBaseHook : BaseHook() {
builder.setContentTitle(text)
builder.setSmallIcon(IconCompat.createWithBitmap(bitmap))
builder.setTicker(text).setPriority(NotificationCompat.PRIORITY_LOW)
builder.setOngoing(true) // 设置为常驻通知
builder.setContentIntent(
PendingIntent.getActivity(
context, 0, launchIntent, PendingIntent.FLAG_MUTABLE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@ import com.sevtinge.hyperceiler.module.base.*
import com.sevtinge.hyperceiler.utils.*
import de.robv.android.xposed.*

// author git@wuyou-123
// co-author git@lingqiqi5211
object FocusNotifLyric : MusicBaseHook() {
private val focusTextViewList = mutableListOf<TextView>()
private val textViewWidth by lazy {
mPrefsMap.getInt("system_ui_statusbar_music_width", 0)
}

override fun init() {
// 拦截构建通知的函数
loadClass("com.android.systemui.statusbar.notification.row.NotifBindPipeline").methodFinder()
Expand Down Expand Up @@ -85,7 +88,7 @@ object FocusNotifLyric : MusicBaseHook() {
.first().createHook {
// 允许全部应用发送焦点通知
returnConstant(true)
}
}

} catch (e: Exception) {
return
Expand All @@ -99,10 +102,14 @@ object FocusNotifLyric : MusicBaseHook() {
private val SPEED_INCREASE = mPrefsMap.getInt("system_ui_statusbar_music_speed", 18) * 0.1f

private val runnablePool = mutableMapOf<Int, Runnable>()
private var lastLyric = ""
override fun onUpdate(lyricData: LyricData) {
val lyric = lyricData.lyric
focusTextViewList.forEach {
it.text = lyric
if (lastLyric == it.text) {
it.text = lyric
lastLyric = lyric
}
if (XposedHelpers.getAdditionalStaticField(it, "is_scrolling") == 1) {
val m0 = it.getObjectFieldOrNull("mMarquee")
if (m0 != null) {
Expand Down Expand Up @@ -132,22 +139,22 @@ object FocusNotifLyric : MusicBaseHook() {
}
val width =
(textView.width - textView.compoundPaddingLeft - textView.compoundPaddingRight - textViewWidth).toFloat()
val lineWidth = textView.layout.getLineWidth(0)
// 重设最大滚动宽度,只能滚动到文本结束
m.setFloatField("mMaxScroll", lineWidth - width)
// 重设速度
m.setFloatField("mPixelsPerMs", speed)
// 移除回调,防止滚动结束之后重置滚动位置
m.setObjectField("mRestartCallback", Choreographer.FrameCallback {})
XposedHelpers.setAdditionalStaticField(textView, "is_scrolling", 1)
val lineWidth = textView.layout?.getLineWidth(0)
if (lineWidth != null) {
// 重设最大滚动宽度,只能滚动到文本结束
m.setFloatField("mMaxScroll", lineWidth - width)
// 重设速度
m.setFloatField("mPixelsPerMs", speed)
// 移除回调,防止滚动结束之后重置滚动位置
m.setObjectField("mRestartCallback", Choreographer.FrameCallback {})
XposedHelpers.setAdditionalStaticField(textView, "is_scrolling", 1)
}
} catch (e: Throwable) {
logE(TAG, lpparam.packageName, "error: $e")
}
}

override fun onStop() {
focusTextViewList.forEach {
it.visibility = View.GONE
}
logD(lpparam.packageName, "==========================================")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
* This file is part of HyperCeiler.
* HyperCeiler is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* Copyright (C) 2023-2024 HyperCeiler Contributions
*/
package com.sevtinge.hyperceiler.module.hook.systemui.statusbar.icon.v

import android.annotation.*
import android.graphics.*
import android.service.notification.*
import android.view.*
import android.widget.TextView
import cn.lyric.getter.api.data.*
import com.github.kyuubiran.ezxhelper.ClassUtils.loadClass
import com.github.kyuubiran.ezxhelper.HookFactory.`-Static`.createAfterHook
import com.github.kyuubiran.ezxhelper.HookFactory.`-Static`.createBeforeHook
import com.github.kyuubiran.ezxhelper.HookFactory.`-Static`.createHook
import com.github.kyuubiran.ezxhelper.finders.MethodFinder.`-Static`.methodFinder
import com.sevtinge.hyperceiler.module.base.*
import com.sevtinge.hyperceiler.module.base.MusicBaseHook.Companion.CHANNEL_ID
import com.sevtinge.hyperceiler.utils.*
import com.sevtinge.hyperceiler.utils.log.*
import de.robv.android.xposed.*
import kotlinx.coroutines.flow.*

@SuppressLint("StaticFieldLeak")
// author git@wuyou-123
// co-author git@lingqiqi5211
object HideFakeStatusBar : BaseHook() {
private var mStatusBarLeftContainer: View? = null
private var mFocusedNotLine: View? = null
private var mClockSeat: View? = null
private var mBigTime: TextView? = null
// ui上正在显示焦点通知(可能是其他应用的通知)
private val isShowingFocused = MutableStateFlow(false)

// 有歌词的通知(但是歌词可能没有显示在ui上,比如在音乐app中)
private val isLyric = MutableStateFlow(false)

// 正在显示焦点通知歌词(有歌词并且正在ui上显示)
private var isShowingFocusedLyric: Boolean = false

private fun updateLayout() {
if (isShowingFocused.value && isLyric.value) {
isShowingFocusedLyric = true
// 如果在显示歌词,就隐藏时钟,占位布局和竖线
mStatusBarLeftContainer?.visibility = View.INVISIBLE
mClockSeat?.visibility = View.GONE
mFocusedNotLine?.visibility = View.GONE
// 设置大时钟颜色
mBigTime?.setTextColor(Color.WHITE)
} else {
mStatusBarLeftContainer?.visibility = View.VISIBLE
mClockSeat?.visibility = View.VISIBLE
mFocusedNotLine?.visibility = View.VISIBLE
isShowingFocusedLyric = false
}

}

override fun init() {
loadClass("com.android.systemui.statusbar.phone.MiuiPhoneStatusBarView").methodFinder()
.filterByName("onFinishInflate").first().createAfterHook {
logI(lpparam.packageName, "onFinishInflate")
// 通知栏左边部分(包含时间和通知图标)
mStatusBarLeftContainer =
it.thisObject.getObjectField("mStatusBarLeftContainer") as View
// mStatusBarLeftContainer!!.visibility = View.INVISIBLE
}
loadClass("com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment").methodFinder()
.filterByName("onViewCreated").first().createAfterHook {
// 焦点通知左边竖线
mFocusedNotLine = it.thisObject.getObjectField("mFocusedNotLine") as View
// 焦点通知左边占位布局
mClockSeat = it.thisObject.getObjectField("mClockSeat") as View
}

loadClass("com.android.systemui.qs.MiuiNotificationHeaderView").methodFinder()
.filterByName("onFinishInflate").first().createAfterHook {
// 大时钟布局
mBigTime = it.thisObject.getObjectField("mBigTime") as TextView
}
loadClass("com.android.systemui.qs.MiuiNotificationHeaderView").methodFinder()
.filterByName("updateBigTimeColor").first().replaceMethod {
if (isShowingFocusedLyric) {
// 显示歌词的时候取消设置大时钟颜色(假时钟动画会设置颜色,显示歌词的时候取消了假时钟动画,所以可能会下拉通知栏之后时间是黑色)
null
} else {
it.invokeOriginalMethod()
}
}
var unhook0: XC_MethodHook.Unhook? = null
loadClass("com.android.systemui.controlcenter.shade.NotificationHeaderExpandController\$notificationCallback\$1").methodFinder()
.filterByName("onExpansionChanged").first().createHook {
before {
unhook0 = loadClass("com.miui.utils.configs.MiuiConfigs").methodFinder()
.filterByName("isVerticalMode").first().replaceMethod {
if (isShowingFocusedLyric) {
// 如果在显示歌词,就伪装成横屏,用来取消假时钟动画
false
} else {
it.invokeOriginalMethod()
}
}
}
after {
if (isShowingFocusedLyric) {
// 在显示歌词的时候固定通知栏顶部时间和日期的位置和缩放
val notificationHeaderExpandController =
it.thisObject.getObjectField("this\$0")
val combinedHeaderController =
notificationHeaderExpandController!!.getObjectField("headerController")!!
.callMethod("get")
val notificationBigTime =
combinedHeaderController!!.getObjectFieldAs<TextView>("notificationBigTime")
notificationBigTime.translationX = 0f
notificationBigTime.translationY = 0f
notificationBigTime.scaleX = 1f
notificationBigTime.scaleY = 1f
notificationBigTime.setTextColor(Color.WHITE)
val notificationDateTime =
combinedHeaderController.getObjectFieldAs<TextView>("notificationDateTime")
notificationDateTime.translationX = 0f
notificationDateTime.translationY = 0f
// 设置时钟的宽度
notificationHeaderExpandController.callMethod("updateWeight", 1.0f)
// 设置通知图标位置
combinedHeaderController.getObjectField("notificationShortcut")?.callMethod("setTranslationY", 0f)

}
unhook0?.unhook()
}
}
loadClass("com.android.systemui.controlcenter.shade.NotificationHeaderExpandController\$notificationCallback\$1").methodFinder()
.filterByName("onAppearanceChanged").first().createHook {
before {
}
after {
if (isShowingFocusedLyric) {
// 显示歌词的时候手动调用动画,防止大时钟突然出现
val notificationHeaderExpandController =
it.thisObject.getObjectField("this\$0")
val combinedHeaderController =
notificationHeaderExpandController!!.getObjectField("headerController")!!
.callMethod("get")
loadClass("com.android.systemui.controlcenter.shade.NotificationHeaderExpandController")
.callStaticMethod(
"access\$startFolmeAnimationAlpha",
notificationHeaderExpandController,
combinedHeaderController!!.getObjectField("notificationBigTime"),
combinedHeaderController.getObjectField("notificationBigTimeFolme"),
if (!(it.args[0] as Boolean)) 0f else 1f,
true
)
}
}
}

loadClass("com.android.systemui.statusbar.phone.FocusedNotifPromptController").methodFinder()
.filterByName("notifyNotifBeanChanged").first().createHook {
before {
// 焦点通知更新的事件,通过这个判断当前展示的焦点通知是不是歌词
val sbn = it.args[0]?.getObjectField("sbn") as StatusBarNotification?
isLyric.value = sbn?.notification?.channelId == CHANNEL_ID
updateLayout()
}
}

loadClass("com.android.systemui.recents.OverviewProxyService").methodFinder()
.filterByName("onFocusedNotifUpdate").first()
.createBeforeHook { m ->
// 代码中的动画目标位置
val rect = m.args[2] as Rect
/**
* 代码中的 20 是边距(可能在不同型号设备中会有不同的值,具体要去抓布局的资源文件,在小米 15pro 中是 20)
* @return 获取焦点通知正常情况下往右偏移的距离
*/
fun getWidth(): Int {
return (mClockSeat?.width?.plus(mFocusedNotLine?.width ?: 0) ?: 0) + 20
}

/**
* 动画的目标位置,只有两种情况,一种是隐藏时间之后的位置,一种是正常位置
* 如果目标位置左边减去时间那一块的宽度大于 0,则说明在右边
* @return 1: 当前动画目标位置在左边(隐藏时间之后的位置) 2:当前动画目标位置在右边(正常情况下的位置)
*/
fun getPos() = if (rect.left - getWidth() > 0) 2 else 1

if (isLyric.value) {
// 如果减去左边的位置大于 0,就说明当前位置是时间右边,因为现在是在显示歌词,所以把目标位置往左偏移
if (getPos() == 2) {
rect.left -= getWidth()
rect.right -= getWidth()
}
} else {
// 如果现在显示的不是歌词,并且隐藏了时钟,那么往右偏移到正常位置
// 当音乐在后台播放并且前台应用有焦点通知的时候会触发这种情况
if (mClockSeat?.visibility == 8) {
rect.left += getWidth()
rect.right += getWidth()
} else {
// 如果已经展示了时间,但是目标位置还是在左边,往右偏移
if (getPos() == 1) {
rect.left += getWidth()
rect.right += getWidth()
}
}
}
}

loadClass("com.android.systemui.statusbar.phone.MiuiCollapsedStatusBarFragment").methodFinder()
.filterByName("updateStatusBarVisibilities").first().createAfterHook {
// 获取是否在显示焦点通知
// 更新一次 isShowingFocused
isShowingFocused.value =
it.thisObject.getBooleanField("mIsFocusedNotifyViewShowing")
updateLayout()
}
}
}
Loading

0 comments on commit 54a9712

Please sign in to comment.