Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ HyperRefine 基于个人 Android 模块开发脚手架 MiuixGuiTemplate 创建

通知功能独立实现,未引入参考模块的代码、资源或原生库;参考项目的来源与许可证已登记到应用内参考项目分组。

状态栏的 Wi-Fi 活动指示器隐藏与移动网络类型隐藏同样独立实现,参考来源统一登记在应用内致谢页;功能范围、验证和暂未启用的双卡双行源码见 [状态栏功能说明](docs/STATUSBAR_PORT.md)。

<br>

# 赞助支持
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
android:name=".ui.screen.features.NotificationsActivity"
android:exported="false"
android:theme="@style/Theme.HyperRefine" />
<activity android:name=".ui.screen.features.NetworkIconsActivity"
android:exported="false" android:theme="@style/Theme.HyperRefine" />
<activity
android:name=".ui.screen.features.AodActivity"
android:exported="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ private val licenseSections: List<LicenseSection> = listOf(
LibraryInfo(
"Pengeek(米客)",
"OS4-26.10.13-test",
"GPL-3.0,仅通知功能思路参考(独立实现,未复用其代码或资源)",
"GPL-3.0,通知与状态栏功能思路参考(独立实现,未复用其代码或资源)",
"https://github.com/MonwF/customiuizer",
),
LibraryInfo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ fun FeaturesPageView(
HookSubPage(
titleRes = R.string.feature_system_ui,
specs = listOf(
specByKey(specs, KEY_STATUS_BAR),
specByKey(specs, KEY_GLASS),
specByKey(specs, KEY_CC_RADIUS),
specByKey(specs, PercentLocation.entryKey(PercentLocation.CC_BRIGHTNESS)),
Expand All @@ -88,6 +89,11 @@ fun FeaturesPageView(
),
onOpen = { context.startActivity(Intent(context, SystemUiActivity::class.java)) },
subPages = listOf(
HookSubPage(
titleRes = R.string.sb_title,
specs = statusBarSpecs(specs),
onOpen = { context.startActivity(Intent(context, NetworkIconsActivity::class.java)) },
),
HookSubPage(
titleRes = R.string.notifications_title,
specs = notificationSpecs(specs),
Expand Down Expand Up @@ -1223,6 +1229,7 @@ internal fun featureSpecs(): List<OptionSpec> {
specs += buildAutoBalanceSpecs(systemUi)
specs += ccRadiusSpecs(systemUi)
specs += notificationFeatureSpecs()
specs += statusBarFeatureSpecs()
return specs
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cn.ianzb.hyperrefine.ui.screen.features

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import cn.ianzb.hyperrefine.R
import cn.ianzb.hyperrefine.ui.component.pref.HookOptionView
import cn.ianzb.hyperrefine.ui.component.pref.HookSection
import cn.ianzb.hyperrefine.ui.component.pref.HookSectionCard
import cn.ianzb.hyperrefine.ui.screen.subpage.BaseSubPageActivity
import cn.ianzb.hyperrefine.ui.util.LocalSubPageScrollBehavior
import cn.ianzb.hyperrefine.ui.util.pageScrollModifiers

class NetworkIconsActivity : BaseSubPageActivity() {
override val titleRes = R.string.sb_title
@Composable
override fun SubPageContent(isBlurEnabled: Boolean, contentPadding: PaddingValues) {
val specs = statusBarSpecs(featureSpecs())
StatusBarSections(listOf(
HookSection(R.string.sb_network_section, specs = specs.take(1)),
HookSection(R.string.sb_mobile_type_section, specs = specs.drop(1)),
), contentPadding)
}
}

@Composable
private fun StatusBarSections(sections: List<HookSection>, padding: PaddingValues) {
val scroll = LocalSubPageScrollBehavior.current
LazyColumn(modifier = Modifier.fillMaxSize().then(
if (scroll != null) Modifier.pageScrollModifiers(showTopAppBar = true, topAppBarScrollBehavior = scroll) else Modifier,
), contentPadding = padding) {
sections.forEach { section -> item {
HookSectionCard(section) { section.specs.forEach { spec -> HookOptionView(spec) } }
} }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package cn.ianzb.hyperrefine.ui.screen.features

import cn.ianzb.hyperrefine.R
import cn.ianzb.hyperrefine.hook.systemui.statusbar.StatusBarKeys as Keys
import cn.ianzb.hyperrefine.prefs.OptionSpec
import cn.ianzb.hyperrefine.prefs.OptionType

const val KEY_STATUS_BAR = "feature_statusbar"

internal fun statusBarFeatureSpecs(): List<OptionSpec> {
fun toggle(key: String, title: Int) = OptionSpec(
key = key, type = OptionType.SWITCH, titleRes = title,
defaultBoolean = false, targetPackages = listOf(Keys.TARGET), showStatus = true,
)
return listOf(
OptionSpec(key = KEY_STATUS_BAR, type = OptionType.ARROW, titleRes = R.string.sb_title),
toggle(Keys.WIFI_ACTIVITY, R.string.sb_wifi_hide_activity),
toggle(Keys.HIDE_TYPE, R.string.sb_mobile_hide_type),
)
}

internal fun statusBarSpecs(specs: List<OptionSpec>) =
Keys.ALL.map { key -> specs.first { it.key == key } }
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class SystemUiActivity : BaseSubPageActivity() {
) {
val context = LocalContext.current
val scrollBehavior = LocalSubPageScrollBehavior.current
val statusBarSection = HookSection(
titleRes = R.string.sb_title,
specs = listOf(featureSpec(KEY_STATUS_BAR)),
)
val controlCenterSection = HookSection(
titleRes = R.string.feature_control_center,
specs = listOf(
Expand Down Expand Up @@ -81,6 +85,7 @@ class SystemUiActivity : BaseSubPageActivity() {

val openSubPage: (String) -> Unit = { key ->
when (key) {
KEY_STATUS_BAR -> context.startActivity(Intent(context, NetworkIconsActivity::class.java))
KEY_GLASS ->
context.startActivity(Intent(context, GlassActivity::class.java))
KEY_CC_RADIUS ->
Expand Down Expand Up @@ -125,7 +130,7 @@ class SystemUiActivity : BaseSubPageActivity() {
),
contentPadding = contentPadding,
) {
listOf(controlCenterSection, volumeSection, lockAodSection, wallpaperSection, windowSection).forEach { section ->
listOf(statusBarSection, controlCenterSection, volumeSection, lockAodSection, wallpaperSection, windowSection).forEach { section ->
item {
HookSectionCard(section) {
section.specs.forEach { spec ->
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/values-en/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -494,4 +494,9 @@

<string name="licenses_section_passkey">Passkey</string>
<string name="licenses_section_browser">Browser</string>
<string name="sb_title">Status bar</string>
<string name="sb_network_section">Network icons</string>
<string name="sb_wifi_hide_activity">Hide Wi-Fi activity indicators</string>
<string name="sb_mobile_type_section">Mobile network type</string>
<string name="sb_mobile_hide_type">Hide network type completely</string>
</resources>
5 changes: 5 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -494,4 +494,9 @@

<string name="licenses_section_passkey">通行密钥</string>
<string name="licenses_section_browser">浏览器</string>
<string name="sb_title">状态栏</string>
<string name="sb_network_section">网络图标</string>
<string name="sb_wifi_hide_activity">隐藏 Wi-Fi 网络活动指示器</string>
<string name="sb_mobile_type_section">移动网络类型</string>
<string name="sb_mobile_hide_type">完全隐藏网络类型</string>
</resources>
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

### 新增

- **状态栏 · 网络图标**(目标 `com.android.systemui`):新增「系统界面 → 状态栏」页,提供「隐藏 Wi-Fi 活动指示器」「完全隐藏网络类型」两项开关,默认关闭;双卡双行实现暂以注释源码保留,等待后续修复。
- **负一屏 · 过渡动画优化**(目标 `com.miui.personalassistant` / system_server):新增「负一屏」功能页。**过渡闪黑修复**消除打开 / 返回快速切换时的纯色闪黑;**上滑合并强制反向**修正返回动画被系统合并时先反向播一半的问题;**放行远程动画权限**(系统服务侧,需重启设备)让负一屏的远程过渡动画完整生效。思路参考 HyperAssistant(非开源,独立实现)。
- **负一屏 · 打开应用动画与过渡遮罩重绘**(目标 `com.miui.personalassistant`):**应用打开动画**让负一屏点开的应用从图标位置缩放展开(options 逐级剥离重试,失败回退框架动画);**过渡遮罩重绘**修复过渡中遮罩显示异常内容。
- **负一屏 · 返回缩回动画**(目标 `com.miui.personalassistant`):关闭应用回到负一屏时,部件快照从全屏缩回原位,接近末尾渐隐;需先开启「应用打开动画」。
Expand Down
2 changes: 2 additions & 0 deletions docs/CUSTOMIZE.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ HookOptionsPage(

通知功能的参考来源已登记到 `LicensePage.kt` 的参考项目分组,注明 GPL-3.0、仅思路参考、未复用代码或资源。新增七项配置和进程范围见 [通知功能说明](NOTIFICATION_PORT.md)。

状态栏功能的参考来源也在该分组登记;仅借鉴思路,未引入参考模块或小米 SystemUI 的代码、图片及原生库。两项开关的配置、目标进程和注释中的双卡双行实现见 [状态栏功能说明](STATUSBAR_PORT.md)。

本模板的定位是**个人 Android 模块项目的脚手架模板**。具体参考与致谢清单统一维护在应用内「第三方许可证与致谢」页面(`LicensePage.kt` → `licenses_section_refs` 分组),衍生项目**可增不可删**。

> 文档不展开对具体模块的借鉴说明;复用任何第三方代码前须先核对许可证,无许可证 / 非开源项目只能借鉴思路并致谢,完整流程见 [模块开发工作流](WORKFLOW.md)。
Expand Down
30 changes: 30 additions & 0 deletions docs/STATUSBAR_PORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 状态栏功能说明

入口:**功能 → 系统界面 → 状态栏**。当前仅开放以下两项开关,默认关闭,目标均为 `com.android.systemui`;修改后重新加载系统界面进程生效。

| 选项 | 配置键 | 效果 |
|---|---|---|
| 隐藏 Wi-Fi 网络活动指示器 | `statusbar_wifi_hide_activity` | 隐藏 Wi-Fi 的收发活动图标,保留信号与连接显示 |
| 完全隐藏网络类型 | `statusbar_mobile_hide_type` | 隐藏移动网络类型图片、独立文字和特殊 5G 标识,保留原生信号布局 |

## 实现与验证

Wi-Fi Hook 在原生绑定与活动更新之后将对应活动视图隐藏,避免后续收发更新再次显示。移动网络类型 Hook 在原生蜂窝模型创建后,将四个类型显示 Flow 替换为宿主自身的只读 false Flow;兼容宿主经过优化的一参数构造与普通二参数构造。

配置通过项目既有 `OptionSpec`、偏好与 Hook 状态机制接入;状态栏子页面继承 `BaseSubPageActivity`,登记到父页搜索,并使用 `NetworkIconsActivity`。没有新增权限、作用域或原生依赖。

接口静态核对基于提供的 HyperOS `OS4.0.22.0.XPNCNXM` ROM,SystemUI `17.03.260226.r`;其他 ROM 的私有接口兼容性尚未验证。用户已确认本次精简测试 APK 的两个开关均生效、双 SIM 保持原生布局,从状态栏页下拉通知中心/控制中心时背景显示正常。单元测试覆盖配置键、反射成员解析和两种只读 Flow 构造签名;没有新增临时诊断日志。

## 暂不启用双卡双行

`hook/.../systemui/statusbar/` 中的 `DualSimSignalHook.kt`、`DualSimRuntimeSupport.kt`、`DualSimArtworkPolicy.kt`、`StackedSignalDrawable.kt`、`StatusBarPolicy.kt`、`StatusBarViews.kt` 及对应双排测试以逐行注释保留,供后续修复参考。相关偏好键和宿主 Pair 解析辅助也已注释。

这些文件不生成运行时类;当前没有双排 Hook 注册、配置入口或搜索项,不读取该功能的旧偏好。系统继续使用原生 SIM 信号布局。以后恢复时须重新接入配置与注册,验证各卡信号强度、单双卡切换、数据卡切换、锁屏及主题/明暗表现;不能只取消某个文件的注释就视为可用。

此前电脑端导出的 PNG/SVG 不是运行时依赖,未进入仓库或 APK。当前构建没有图标解包、图片落盘或系统资源替换。

## 参考与安全边界

功能思路参考 Pengeek(米客)`OS4-26.10.13-test`,来源与许可在应用内「关于 → 第三方许可证与致谢」统一登记。实现独立编写,未复用参考模块或小米专有代码/资源;逆向材料仅保留在本地忽略的 `reverse_report/`。项目继续以 AGPL-3.0 分发。

新增 Hook 仅修改 SystemUI 进程中的视图可见性与显示 Flow,不请求 Root、不执行 shell、不挂载或重新挂载分区、不操作块设备/AVB,也不向受保护系统分区写文件。安装 APK、项目既有偏好及系统/框架日志仍可能保存普通应用数据,因此此说明不承诺整个模块或临时 Root 环境的所有组件均无写入。
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import cn.ianzb.hyperrefine.hook.systemui.radius.CcRadiusHook
import cn.ianzb.hyperrefine.hook.systemui.radius.CcRadiusKeys
import cn.ianzb.hyperrefine.hook.systemui.wallpaper.WallpaperDarkDimHook
import cn.ianzb.hyperrefine.hook.systemui.wallpaper.WallpaperDimKeys
import cn.ianzb.hyperrefine.hook.systemui.statusbar.HideWifiActivityHook
import cn.ianzb.hyperrefine.hook.systemui.statusbar.MobileIndicatorVisibilityHook
import cn.ianzb.hyperrefine.hook.systemui.statusbar.StatusBarKeys

/**
* 系统界面(com.android.systemui)目标 Load:
Expand All @@ -23,6 +26,8 @@ class SystemUiLoad : BaseLoad() {
override val targetPackages: List<String> = listOf(TARGET_PACKAGE)

override fun onPackageLoaded(target: PackageTarget) {
initHook(HideWifiActivityHook(), HookPrefs.getBoolean(StatusBarKeys.WIFI_ACTIVITY))
initHook(MobileIndicatorVisibilityHook(), HookPrefs.getBoolean(StatusBarKeys.HIDE_TYPE))
initHook(CcVolumePercentHook(), HookPrefs.getBoolean(CcVolumePercentHook.KEY, false))
initHook(CcBrightnessPercentHook(), HookPrefs.getBoolean(CcBrightnessPercentHook.KEY, false))
initHook(SideVolumePercentHook(), HookPrefs.getBoolean(SideVolumePercentHook.KEY, false))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// DORMANT: preserved from statusbar.6 (ddb0e59); not compiled or registered.
// Restore only after artwork and device regression testing. Runtime diagnostics removed.
// package cn.ianzb.hyperrefine.hook.systemui.statusbar
//
// import kotlin.math.min
// import kotlin.math.roundToInt
//
// /** Original four-cell artwork geometry and slot ordering, independent of the data SIM. */
// internal object DualSimArtworkPolicy {
// const val CELL_COUNT = 4
// private const val INACTIVE_ALPHA = 0.28f
//
// data class Levels(val top: Int, val bottom: Int)
// data class Cell(val row: Int, val column: Int, val left: Float, val top: Float,
// val right: Float, val bottom: Float, val radius: Float)
//
// /** HyperOS has six native levels (0..5); round proportionally to four visible cells. */
// fun fourCellLevel(nativeLevel: Int?): Int? = nativeLevel?.takeIf { it in 0..5 }
// ?.let { (it * CELL_COUNT / 5f).roundToInt() }
//
// fun levels(primarySlot: Int, secondarySlot: Int, primaryLevel: Int?, secondaryLevel: Int?): Levels? {
// val first = fourCellLevel(primaryLevel) ?: return null
// val second = fourCellLevel(secondaryLevel) ?: return null
// return when {
// primarySlot == 0 && secondarySlot == 1 -> Levels(first, second)
// primarySlot == 1 && secondarySlot == 0 -> Levels(second, first)
// else -> null // Unknown/duplicate slots must not invent a SIM 1 / SIM 2 assignment.
// }
// }
//
// fun cells(width: Float, height: Float, thick: Boolean): List<Cell> {
// if (!width.isFinite() || !height.isFinite() || width <= 0f || height <= 0f) return emptyList()
// val step = width / CELL_COUNT
// val barWidth = step * if (thick) 0.80f else 0.64f
// // Compact proportions independent of a tall Android signal drawable's intrinsic bounds.
// val artworkHeight = min(height * 0.72f, width * 0.80f)
// val artworkTop = height - artworkHeight
// val bottomHeight = min(artworkHeight * 0.24f, barWidth * 0.92f)
// val bottomTop = height - bottomHeight
// val upperBaseline = bottomTop - artworkHeight * 0.09f
// val upperHeight = upperBaseline - artworkTop
// return (0 until CELL_COUNT).map { column ->
// val left = column * step + (step - barWidth) / 2f
// val barHeight = upperHeight * (0.4f + column * 0.2f)
// val radius = min(barWidth, barHeight) * 0.48f
// Cell(0, column, left, upperBaseline - barHeight, left + barWidth, upperBaseline, radius)
// } + (0 until CELL_COUNT).map { column ->
// val left = column * step + (step - barWidth) / 2f
// val radius = min(barWidth, bottomHeight) * 0.48f
// Cell(1, column, left, bottomTop, left + barWidth, height, radius)
// }
// }
//
// /** Preserve the host's RGB and alpha; inactive cells use the same hue at lower opacity. */
// fun cellColor(foreground: Int, drawableAlpha: Int, filled: Boolean): Int {
// val hostAlpha = foreground ushr 24
// val strengthAlpha = if (filled) 1f else INACTIVE_ALPHA
// val alpha = (hostAlpha * drawableAlpha.coerceIn(0, 255) / 255f * strengthAlpha).roundToInt()
// return (foreground and 0x00ffffff) or (alpha shl 24)
// }
// }
Loading
Loading