Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
129b7cf
Trying to fix Gson circular reference issue
SherlockChiang Dec 12, 2025
487d393
feat: align notifications, Miuix UI, and idle behavior
SherlockChiang Aug 13, 2026
b2abfad
feat: expand HyperOS notifications and idle efficiency
SherlockChiang Aug 15, 2026
7bd7159
feat: align HyperOS metadata and notification efficiency
SherlockChiang Aug 16, 2026
39befca
feat: unify package and refine Miuix navigation
SherlockChiang Aug 17, 2026
12b4c32
feat: match KernelSU floating navigation interactions
SherlockChiang Aug 17, 2026
517612d
feat: deepen HyperOS notifications and idle recovery
SherlockChiang Aug 17, 2026
81e8d29
fix: keep colorful image aliases consistent
SherlockChiang Aug 17, 2026
72c4738
feat: harden focus notifications and explicit registration UI
SherlockChiang Aug 18, 2026
ed737b8
fix: parse activity click flags and diagnose config references
SherlockChiang Aug 18, 2026
c43f2a3
fix: constrain notification click activities to target app
SherlockChiang Aug 19, 2026
42a6d01
fix: prevent notification settings launch crashes
SherlockChiang Aug 19, 2026
fc44858
feat(ui): modernize application and event lists
SherlockChiang Aug 19, 2026
6f4f9d0
feat(settings): add configuration reference diagnostics
SherlockChiang Aug 19, 2026
2dfcc14
feat(ui): card application details guidance
SherlockChiang Aug 19, 2026
d626c24
feat: publish configuration diagnostics snapshots
SherlockChiang Aug 19, 2026
be3de59
fix: catch settings resolver launch failures
SherlockChiang Aug 19, 2026
a3dbf0c
chore: normalize application list sources
SherlockChiang Aug 19, 2026
47314e9
fix: keep published configuration lists immutable
SherlockChiang Aug 19, 2026
a9cfd34
test: provide JSON implementation for local parser tests
SherlockChiang Aug 19, 2026
44f1420
ui: distinguish unavailable MiPush service probes
SherlockChiang Aug 19, 2026
32be647
fix: preserve unknown manifest probe failures
SherlockChiang Aug 19, 2026
e5e7eb2
fix: honor legacy service probe flags
SherlockChiang Aug 19, 2026
7bebe16
Isolate notification dispatch phases
SherlockChiang Aug 19, 2026
e4edb59
test: initialize logging for service probe mapping
SherlockChiang Aug 19, 2026
044f6b7
merge: synchronize personal master
SherlockChiang Aug 20, 2026
3ed0c90
merge: integrate MIUIX focus notification rewrite
SherlockChiang Aug 20, 2026
b707f06
perf: throttle duplicate screen wakes per package
SherlockChiang Aug 20, 2026
43bee1b
ui: add swipe navigation between main pages
SherlockChiang Aug 20, 2026
bd4ea7f
feat(ui): add safe manual notification replay
SherlockChiang Aug 20, 2026
fd6eac4
fix: restore HyperOS conversation mini-window hint
SherlockChiang Aug 20, 2026
c4d0d3a
fix: preserve explicit notification click setting
SherlockChiang Aug 20, 2026
19c4c05
fix: use Miuix action buttons and open app permissions
SherlockChiang Aug 20, 2026
cec00ad
fix: dismiss event dialog before opening app permissions
SherlockChiang Aug 20, 2026
aa71fc5
perf: bound notification queue and trim icon caches
SherlockChiang Aug 21, 2026
ba83d71
fix: restore focus payload and observable test notifications
SherlockChiang Aug 21, 2026
b4bdf7f
fix: forward HyperOS custom focus payload
SherlockChiang Aug 21, 2026
a744631
feat: split Xiaomi system focus from portable notifications
SherlockChiang Aug 21, 2026
bcc3e6c
fix: keep bootstrap notification failure from crashing XMSF
SherlockChiang Aug 22, 2026
bfff421
fix: route ordinary notification clicks to target apps
SherlockChiang Aug 22, 2026
600490a
fix: preserve MiPush click routing and encrypted deep links
SherlockChiang Aug 22, 2026
c5fd91f
fix: preserve official click bridges and parse long deep links
SherlockChiang Aug 22, 2026
1472f67
fix: prefer payload deep links over generic launchers
SherlockChiang Aug 22, 2026
903c0ce
fix: route private notification clicks safely
SherlockChiang Aug 22, 2026
7187f2d
fix: avoid duplicate service wake on activity clicks
SherlockChiang Aug 22, 2026
a16c6c8
docs: standardize JDK 17 build workflow
SherlockChiang Aug 23, 2026
76184f3
fix: route focus notification deep links
SherlockChiang Aug 23, 2026
0fb6df7
fix: keep inferred notification deep links clean
SherlockChiang Aug 24, 2026
deabf64
fix: preserve sender notification navigation
SherlockChiang Aug 24, 2026
79d5630
refactor: replace package lists with capability checks
SherlockChiang Aug 24, 2026
ab22f2c
fix: derive app status from runtime evidence
SherlockChiang Aug 24, 2026
14eb516
fix: replay notification clicks through target SDK
SherlockChiang Aug 26, 2026
b3fd532
fix: hand off notification clicks after target foreground
SherlockChiang Aug 27, 2026
a4e7a79
build: use project-owned signing key for app and LSP
SherlockChiang Aug 27, 2026
778aa8d
ci: enforce project signing certificate
SherlockChiang Aug 27, 2026
9ae0860
docs: attribute KernelSU floating navigation reference
SherlockChiang Aug 27, 2026
fccba9d
fix: keep notification bridge vendor-specific
SherlockChiang Aug 27, 2026
6fd44a0
fix: keep floating Miuix dock clear of navigation bar
SherlockChiang Aug 28, 2026
a642a80
fix: enable portable notification package attribution
SherlockChiang Aug 28, 2026
2e078fb
fix: preserve target package notification routing and ordering
SherlockChiang Aug 29, 2026
43c14e3
feat: add portable focus delivery fallback
SherlockChiang Aug 29, 2026
3fd5dd7
feat: add portable delivery focus notifications
SherlockChiang Aug 29, 2026
29b25aa
fix: provide safe notification icon fallback
SherlockChiang Aug 31, 2026
86a49f7
perf: optimize battery and unify cache trimming
SherlockChiang Aug 31, 2026
eb739b2
refactor: centralize memory pressure cleanup
SherlockChiang Aug 31, 2026
84e4654
fix: dispatch private notification clicks before launcher fallback
SherlockChiang Sep 2, 2026
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
76 changes: 46 additions & 30 deletions .github/workflows/test_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,24 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: set up JDK 11
- name: set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'
cache: gradle

- name: Write key
run: |
if [ ! -z "${{ secrets.SIGNING_KEY }}" ]; then
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' >> local.properties
echo KEYSTORE_ALIAS='${{ secrets.KEYSTORE_ALIAS }}' >> local.properties
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' >> local.properties
if [ ! -z "${{ secrets.MIPUSH_SIGNING_KEY }}" ]; then
echo HAS_SIGNING_KEY=true >> $GITHUB_ENV
echo KEYSTORE_PASSWORD='${{ secrets.MIPUSH_KEYSTORE_PASSWORD }}' >> local.properties
echo KEYSTORE_ALIAS='${{ secrets.MIPUSH_KEY_ALIAS }}' >> local.properties
echo KEY_PASSWORD='${{ secrets.MIPUSH_KEY_PASSWORD }}' >> local.properties
echo KEY_LOCATE='../release.keystore' >> local.properties
echo ${{ secrets.SIGNING_KEY }} | base64 --decode > release.keystore
echo ${{ secrets.MIPUSH_SIGNING_KEY }} | base64 --decode > release.keystore
else
echo HAS_SIGNING_KEY=false >> $GITHUB_ENV
fi

- name: Git describe
Expand All @@ -40,56 +43,69 @@ jobs:

- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build -P versionName=${{ steps.ghd.outputs.describe }}

- name: Run Tests and Build Test Artifacts
if: github.event_name != 'workflow_dispatch' || env.HAS_SIGNING_KEY != 'true'
run: ./gradlew test assembleNormalDebug -P versionName=${{ steps.ghd.outputs.describe }}

- name: Build Official Release (Explicit Workflow Dispatch with Key)
if: github.event_name == 'workflow_dispatch' && env.HAS_SIGNING_KEY == 'true'
run: ./gradlew build -P versionName=${{ steps.ghd.outputs.describe }}

- name: Verify project signature
if: github.event_name == 'workflow_dispatch' && env.HAS_SIGNING_KEY == 'true'
shell: bash
run: |
apk=(push/build/outputs/apk/normal/release/*.apk)
apksigner_path=$(find "$ANDROID_HOME/build-tools" -type f -name apksigner | sort -V | tail -n 1)
test -n "$apksigner_path"
actual=$("$apksigner_path" verify --print-certs "${apk[0]}" 2>&1 \
| awk -F': ' '/certificate SHA-256 digest/ {print $2; exit}' \
| tr -d ':[:space:]' | tr '[:upper:]' '[:lower:]')
test "$actual" = "108209e30f644a23f43fe14aaae45f76e0639edaf079a765ff40aa18efcc6b83"

- name: Collect artifact name
run: |
for variant in vc105 normal; do
for build_type in debug release; do
artifact_name=$(basename -s .apk push/build/outputs/apk/$variant/$build_type/*.apk)
echo "${variant}_${build_type}_artifact=$artifact_name" >> $GITHUB_ENV
done
for build_type in debug release; do
if compgen -G "push/build/outputs/apk/normal/$build_type/*.apk" > /dev/null; then
artifact_name=$(basename -s .apk push/build/outputs/apk/normal/$build_type/*.apk)
echo "normal_${build_type}_artifact=$artifact_name" >> $GITHUB_ENV
fi
done

- name: Upload Release For Normal
if: env.normal_release_artifact != ''
uses: actions/upload-artifact@v4.6.0
with:
name: ${{ env.normal_release_artifact }}
path: push/build/outputs/apk/normal/release/*.apk

- name: Upload Release For VC105
uses: actions/upload-artifact@v4.6.0
with:
name: ${{ env.vc105_release_artifact }}
path: push/build/outputs/apk/vc105/release/*.apk

- name: Upload Debug For Normal
if: env.normal_debug_artifact != ''
uses: actions/upload-artifact@v4.6.0
with:
name: ${{ env.normal_debug_artifact }}
path: push/build/outputs/apk/normal/debug/*.apk

- name: Upload Debug For VC105
uses: actions/upload-artifact@v4.6.0
with:
name: ${{ env.vc105_debug_artifact }}
path: push/build/outputs/apk/vc105/debug/*.apk

- name: Get Version Name
id: gvn
run: |
artifact_name=(push/build/outputs/apk/*/*/*.apk)
version=$(echo $artifact_name | sed 's/.*(v.*)(-[^-]+){2}/\1/' -r)
echo "version=$version" >> $GITHUB_OUTPUT
if compgen -G "push/build/outputs/apk/normal/release/*.apk" > /dev/null; then
artifact_name=(push/build/outputs/apk/normal/release/*.apk)
version=$(echo $artifact_name | sed 's/.*(v.*)(-[^-]+){2}/\1/' -r)
echo "version=$version" >> $GITHUB_OUTPUT
fi

- name: Release
if: >-
github.event_name == 'workflow_dispatch' &&
env.HAS_SIGNING_KEY == 'true' &&
env.normal_release_artifact != '' &&
steps.gvn.outputs.version != ''
uses: softprops/action-gh-release@v2
with:
name: ${{ steps.gvn.outputs.version }}
tag_name: ${{ steps.gvn.outputs.version }}
target_commitish: ${{ steps.ghd.outputs.sha }}
prerelease: true
files: push/build/outputs/apk/*/*/*.apk
files: push/build/outputs/apk/normal/release/*.apk
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
*.iml
.gradle
.kotlin/
/local.properties
.DS_Store
/build
/captures
.externalNativeBuild
.yuuta.jks
*.jks
*.keystore
.mipush-project-signing.env
gradle.properties
gen/
debug/
Expand All @@ -14,3 +18,4 @@ release/
.project
org.eclipse.buildship.core.prefs
.idea
.analysis/
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
- 配置文件都有什么作用?我应该使用配置文件吗?
- 配置文件可以修改消息的标题、内容及样式等,控制收到消息时忽略、亮屏或自动弹出等
- 目前大部分“官方”配置都可以无脑使用,部分配置是否要使用,参见[仓库说明](https://github.com/NihilityT/MiPushConfigurations)、配置名或配置中的 description 字段
- 应用原始推送中可用的点击路由默认优先于配置文件对 `url`、`notify_effect`、`intent_uri`、`class_name`、`web_uri` 或 `intent_flag` 的改写,以保留应用自己的页面跳转与返回栈
- 如果配置确实需要替换点击路由,请同时在 `newMetaInfo.extra` 中写入 `"__mi_push_allow_click_route_rewrite": "true"`;框架仍会校验目标 Activity 属于通知对应的应用


- 是否应该安装为系统应用?
Expand Down Expand Up @@ -118,6 +120,20 @@
* 锤子 ROM 下,Push 可以正确收到通知,但是通知栏没有提示 #143
* 一些通知 Feature 可能无法使用(如通知都会显示为推送框架发出,而不是目标应用)

## 界面参考与第三方归因

悬浮底部导航栏的交互和几何布局参考了 KernelSU Manager 的公开实现,具体包括
`FloatingBottomBar.kt` 与 `BottomBarMiuix.kt`:

* [`FloatingBottomBar.kt`](https://github.com/tiann/KernelSU/blob/main/manager/app/src/main/java/me/weishu/kernelsu/ui/component/FloatingBottomBar.kt)
* [`BottomBarMiuix.kt`](https://github.com/tiann/KernelSU/blob/main/manager/app/src/main/java/me/weishu/kernelsu/ui/component/bottombar/BottomBarMiuix.kt)
* [KernelSU LICENSE](https://github.com/tiann/KernelSU/blob/main/LICENSE)(GPL-3.0)

本项目使用 Miuix 0.2.x API 对该交互进行了独立重写,没有复制 KernelSU 的源代码、资源或
模糊/液态玻璃依赖;因此这里记录的是设计与行为参考,而不是把 KernelSU 代码作为本项目代码
直接分发。KernelSU 的 `FloatingBottomBar.kt` 文件头另行注明其参考了 Apache-2.0 的
compose-miuix-ui 示例,本项目没有直接引入该示例。

## 感谢

* @Rachel030219 提供文件
Expand Down
59 changes: 59 additions & 0 deletions agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Agent build contract

This repository must be built with JDK 17. This is the Gradle runtime requirement; do not change the existing Android bytecode targets just to match it.

## Required toolchain

- JDK: 17.x (`java -version` must report major version 17)
- Android SDK: `C:\Users\vince\AppData\Local\Android\Sdk` unless `ANDROID_SDK_ROOT` points to another verified SDK
- Gradle: use `gradlew.bat` only; never use a globally installed Gradle
- MiPushFramework wrapper: Gradle 8.5, AGP 8.2.2, Kotlin 2.0.21
- Java/Kotlin compile target: 11, as defined by `push/build.gradle`; this is intentionally different from the JDK runtime

The currently verified local JDK 17 is:

```powershell
C:\Users\vince\MiPushFramework\.tmp-temurin17\jdk-17.0.20+8
```

Prefer a stable installed JDK 17 path when available. Set `JAVA_HOME` explicitly for every build and verify it before invoking Gradle:

```powershell
$env:JAVA_HOME = 'C:\path\to\jdk-17'
$env:ANDROID_SDK_ROOT = 'C:\Users\vince\AppData\Local\Android\Sdk'
& "$env:JAVA_HOME\bin\java.exe" -version
```

If the reported Java major version is not 17, stop; do not attempt a build with JDK 21 or another version.

## Stable build procedure

Run builds serially. Do not run Android Studio/Gradle builds concurrently with these commands, and do not switch repositories while a Gradle process is running.

```powershell
.\gradlew.bat --stop
.\gradlew.bat :push:testNormalDebugUnitTest :push:assembleNormalDebug `
--no-daemon --max-workers=1 --console=plain `
"-Dkotlin.compiler.execution.strategy=in-process"
```

Do not run `clean` routinely; it removes useful incremental outputs and makes the next build a full rebuild. If a Windows `AccessDeniedException` names a Gradle/Kotlin temporary file or build JAR, stop daemons first and retry the same command once. Only investigate/remove the specific locked build artifact after a repeated failure; never delete source, `.git`, or the whole Gradle cache.

## Dirty and reproducibility rules

- `dirty` in an APK name comes from `git describe --dirty` and means tracked source changes are not committed. It is a version label, not a build mode or performance setting.
- Before handing off an APK, run `git status --short --untracked-files=no`; commit tracked source changes so the artifact has a reproducible commit-based name.
- Do not stage `.tmp-*`, screenshots, device dumps, APKs, or other sampling artifacts.

## Verification

At minimum, report the exact JDK version, Gradle task, result, and APK path. A failed build caused by toolchain/ACL setup must be reported separately from a source compilation failure.

## Project signing contract

- The project-owned keystore is local-only at `C:\Users\vince\MiPushFramework\.mipush-project.jks`.
- Never stage or publish the keystore, `.mipush-project-signing.env`, `local.properties`, or any password.
- The main app and the LSP companion use the same alias and certificate. Expected certificate SHA-256 fingerprint:
`10:82:09:E3:0F:64:4A:23:F4:3F:E1:4A:AA:E4:5F:76:E0:63:9E:DA:F0:79:A7:65:FF:40:AA:18:EF:CC:6B:83`.
- CI uses repository secrets `MIPUSH_SIGNING_KEY`, `MIPUSH_KEYSTORE_PASSWORD`, `MIPUSH_KEY_ALIAS`, and `MIPUSH_KEY_PASSWORD`. The base64 keystore secret must be set independently on each fork.
- A release artifact is valid only when `apksigner verify --print-certs` reports the expected fingerprint. A debug artifact signed by another key must not be used for an upgrade.
40 changes: 24 additions & 16 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,39 +1,47 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.8.0'
ext.kotlin_version = '2.0.21'

repositories {
// Prefer the canonical repositories on CI. A transient 5xx from the
// Aliyun mirror is treated as a hard resolution failure by Gradle and
// prevents it from falling through to the repositories below.
google()
mavenCentral()
gradlePluginPortal()
maven { url 'https://jitpack.io' }

// Keep the mirrors as a fallback for users on networks where the
// canonical endpoints are unavailable.
maven { url = uri("https://maven.aliyun.com/repository/central") }
maven { url = uri("https://maven.aliyun.com/repository/public") }
maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") }
maven { url = uri("https://maven.aliyun.com/repository/google") }

mavenCentral()
maven { url 'https://jitpack.io' }
gradlePluginPortal()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
classpath 'com.android.tools.build:gradle:8.2.2'
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'io.github.wurensen:gradle-android-plugin-aspectjx:3.3.2'
classpath "org.jetbrains.kotlin:compose-compiler-gradle-plugin:$kotlin_version"
classpath 'io.freefair.gradle:android-gradle-plugins:8.2.2'
}

}

allprojects {
repositories {
// See buildscript.repositories above. The order is intentional:
// canonical sources first, mirrors only as a fallback.
google()
mavenCentral()
gradlePluginPortal()
maven { url 'https://jitpack.io' }

maven { url = uri("https://maven.aliyun.com/repository/central") }
maven { url = uri("https://maven.aliyun.com/repository/public") }
maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") }
maven { url = uri("https://maven.aliyun.com/repository/google") }

mavenCentral()
maven { url 'https://jitpack.io' }
gradlePluginPortal()
google()
}

gradle.taskGraph.whenReady {
Expand Down Expand Up @@ -100,8 +108,8 @@ private String getVersionNameFromGit() {
}

ext {
minSdkVersion = 21
compileSdkVersion = 33
minSdkVersion = 26
compileSdkVersion = 34
targetSdkVersion = 30
pushVersionCode = 7

Expand Down
3 changes: 3 additions & 0 deletions common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ android {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
buildFeatures {
buildConfig true
}
lintOptions {
abortOnError false
}
Expand Down
8 changes: 0 additions & 8 deletions common/src/main/java/top/trumeet/common/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,12 @@ private Constants () {
*/
public static final String LOG_FILE = "/file.log";

public static final String AUTHORITY_FILE_PROVIDER = "top.trumeet.mipushframework.fileprovider";

public static final String SERVICE_APP_NAME = "com.xiaomi.xmsf";

public static final String MANAGER_APP_NAME = "top.trumeet.mipush";

public static final int PUSH_SERVICE_VERSION_CODE = Integer.parseInt(BuildConfig.PUSH_VERSION_CODE);

public static final String SHARE_LOG_COMPONENT_NAME =
SERVICE_APP_NAME + ".ShareLogActivity";

public static final String REMOVE_DOZE_COMPONENT_NAME =
SERVICE_APP_NAME + ".RemoveDozeActivity";

public static final String INTENT_NOTIFICATION_ID = "mipush_notification_id";
public static final String INTENT_NOTIFICATION_GROUP = "mipush_notification_group";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,21 @@ CharSequence gen() {
}.get(pkg);
}

/** Names are cheap to reload and may be discarded when the process is under memory pressure. */
public void clearMemory() {
cacheInstance.evictAll();
}

/**
* Apply the process memory-pressure policy to this small, UI-oriented cache.
* Names are inexpensive to resolve, so dropping them is preferable to keeping
* stale objects alive while the process is backgrounded.
*/
public void trimMemory(int level) {
if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
clearMemory();
}
}


}
Loading