Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
0b54c8e
Add Brazilian Portuguese translation (#14) (#16)
D4vidDf Nov 29, 2025
1bbf626
feat(nav): add layout customization and fix text alignment (#18)
D4vidDf Nov 29, 2025
9a65b03
Add Brazilian Portuguese translation (#14) (#22)
D4vidDf Nov 29, 2025
6723b69
feat(a11y): improve TalkBack support and semantic navigation (#24)
D4vidDf Nov 30, 2025
93cb584
fix(service): ignore notifications showing package name as content (#28)
D4vidDf Nov 30, 2025
b97abec
fix(deps): downgrade DataStore to resolve boot persistence issues (#29)
D4vidDf Nov 30, 2025
ec11071
Updating PTBR translation (#30)
NIICKTCHUNS Dec 1, 2025
4bc9b92
Added Korean translation (#26)
alexkoala Dec 1, 2025
2107287
fix(service): ignore placeholder notifications containing package nam…
D4vidDf Dec 1, 2025
3d751d7
feat(blocklist): add spoiler and privacy protection and redesign conf…
D4vidDf Dec 1, 2025
f2b44ed
fix(ui): update library and clarify settings UX
D4vidDf Dec 1, 2025
d9fe40c
feat(onboarding): enforce HyperOS 3 requirement and improve device info
D4vidDf Dec 1, 2025
8a1e45d
docs(release): update changelog resources for v0.3.0 "The Global Update"
D4vidDf Dec 2, 2025
c560ea9
Update PL translation for 0.3.0 (#34)
kacskrz Dec 2, 2025
4106a24
Add Ukrainian translation (#37)
ItzDFPlayer Dec 2, 2025
2846b44
Updating PTBR translation for v0.3.0 (#38)
NIICKTCHUNS Dec 2, 2025
a444c03
docs(release): update changelog resources for v0.3.0 "The Global Update"
D4vidDf Dec 2, 2025
aec3abb
Add missing 0.3.0 Ukrainian strings (#42)
ItzDFPlayer Dec 3, 2025
2855ba5
feat(data): migrate preference storage from DataStore to Room (#43)
D4vidDf Dec 3, 2025
d1168de
fix(service): enable blocked terms filtering logic
D4vidDf Dec 3, 2025
bdc1c16
Updated changelog to add support for Ukrainian
D4vidDf Dec 3, 2025
c72dd77
Merge branch 'master' into dev
D4vidDf Dec 3, 2025
da19c07
Fixed text buttons and custom actions.
D4vidDf Dec 4, 2025
01ce4ed
Merge branch 'master' into dev
D4vidDf Dec 4, 2025
f553b19
feat(app): add backup system, language selector, and UI refresh
D4vidDf Dec 8, 2025
ff32f6f
Feature/ru de language (#52)
kilo3528 Dec 9, 2025
63da4f4
Added Russian and German to the language selector.
D4vidDf Dec 9, 2025
d8d60b0
Feature/ru de language (#54)
kilo3528 Dec 11, 2025
0aaa687
fix(app): Fixed bold tags on russian strings.xml and updated the AppL…
D4vidDf Dec 11, 2025
41dcb12
fix(app): Fixed language
D4vidDf Dec 11, 2025
215f1b8
fix(app): fixed log modal
D4vidDf Dec 11, 2025
af8e9e3
update dev branch (#59)
D4vidDf Dec 15, 2025
70a74ca
feat(app): Updated library to version 0.4.3
D4vidDf Dec 20, 2025
d0eae7e
fix(app): Show firstisland expand
D4vidDf Dec 21, 2025
1d1e292
feat: add snapshot mode, per-widget settings, and revamp design screen
D4vidDf Dec 21, 2025
7fc2ef3
feat(widget): overhaul config UI, add search, and improve performance
D4vidDf Dec 22, 2025
a56217c
feat(widget): add dedicated WidgetOverlayService and improve logic
D4vidDf Dec 23, 2025
7765ef1
fix(app): Fixed timeout for closing island and animation.
D4vidDf Dec 23, 2025
6959da5
fix(:app): Added qtty strings
D4vidDf Dec 23, 2025
7f6143b
fix(:app): Fixed duplicated notifications and formatArgs for pluralSt…
D4vidDf Dec 23, 2025
1e92d25
feature/polish-update: updated Polish translation (#65)
kacskrz Dec 23, 2025
046d664
feat(app): support uninstalled apps persistence and refresh UI
D4vidDf Dec 24, 2025
f713733
Merge remote-tracking branch 'origin/dev' into dev
D4vidDf Dec 24, 2025
24d00af
fix(app): Change `AppListItem` click behavior to open settings
D4vidDf Dec 24, 2025
e3d810a
refactor(design): overhaul widget management and navigation flow
D4vidDf Dec 24, 2025
9673d89
feat(service): enhance notification sync and update translators
D4vidDf Dec 27, 2025
c553fe6
fix(service): Prevent ghost notifications and optimize processing
D4vidDf Dec 27, 2025
f707867
fix(service): enhance notification content resolution and lifecycle h…
D4vidDf Dec 27, 2025
2a1abb6
fix(service): improve title resolution and add state fallback
D4vidDf Dec 28, 2025
3d5493a
Updated Brazilian Portuguese Translation to 0.4.0-dev (#75)
NIICKTCHUNS Dec 30, 2025
c9a20c2
Feat/theme engine (#79)
D4vidDf Dec 30, 2025
5b290c4
Updated Brazilian Portuguese translation to 0.4.0-dev08 (#81)
NIICKTCHUNS Dec 31, 2025
3a38bd4
feat(ui): redesign Design hub and refactor navigation structure (#83)
D4vidDf Jan 1, 2026
17420da
feat(i18n): add Crowdin configuration for localization
D4vidDf Jan 2, 2026
84ca1f3
fix(ui): repair language dialog and overhaul localizations
D4vidDf Jan 2, 2026
3f2d87d
feat(i18n): complete Brazilian Portuguese localization
D4vidDf Jan 2, 2026
d9cc82d
feat(service): implement dynamic icon and label loading in WidgetTran…
D4vidDf Jan 3, 2026
83481fc
feat(theme): add global action styling and call shape customization (…
D4vidDf Jan 3, 2026
c183030
feat(theme): add advanced metadata and custom icons
D4vidDf Jan 10, 2026
057528a
Feat/app theme creator (#90)
D4vidDf Jan 12, 2026
b62ac8d
feat(i18n): add Indonesian and Turkish translations and sync strings
D4vidDf Jan 16, 2026
f5f6948
feat(changelog): improve HTML parsing and introduce bottom sheet
D4vidDf Jan 16, 2026
6958acd
docs(changelog): add version 0.4.0 update notes
D4vidDf Jan 16, 2026
2e5ce36
fix(nav): correct notification layout and improve state handling
D4vidDf Jan 16, 2026
768b239
Feat/update readme (#95)
D4vidDf Jan 20, 2026
88a198a
chore(l10n): update translations for Spanish and Indonesian and bump …
D4vidDf Jan 22, 2026
1c6a68c
Merge remote-tracking branch 'refs/remotes/origin/master' into dev
D4vidDf Jan 22, 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
42 changes: 30 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</p>

<p align="center">
Hyper Bridge bridges standard Android notifications into the pill-shaped UI around the camera cutout, offering a seamless, iOS-like experience on Xiaomi phones.
Hyper Bridge bridges standard Android notifications into the pill-shaped UI around the camera cutout, offering a seamless, iOS-like experience on Xiaomi phones. Now with full theme customization and widget support.
</p>

<p align="center">
Expand All @@ -19,52 +19,71 @@
</p>

<p align="center">
<img src="https://img.shields.io/badge/version-0.4.0-blue?style=for-the-badge&logo=github" alt="Version 0.4.0" />
<img src="https://img.shields.io/badge/kotlin-%237F52FF.svg?style=for-the-badge&logo=kotlin&logoColor=white" alt="Kotlin" />
<img src="https://img.shields.io/badge/Android-3DDC84?style=for-the-badge&logo=android&logoColor=white" alt="Android" />
<img src="https://img.shields.io/badge/Material%20Design-757575?style=for-the-badge&logo=material-design&logoColor=white" alt="Material Design" />
<a href="https://crowdin.com/project/hyper-bridge"><img src="https://badges.crowdin.net/hyper-bridge/localized.svg" alt="Crowdin" /></a>
</p>

<br>

## 🚀 Features

* **Native Visuals:** Transforms notifications into HyperOS system-style islands.
* **🎨 Theme Engine (New):** Customize every pixel.
* **Theme Creator:** Built-in editor to design your own themes with real-time previews.
* **Smart Colors:** Automatically extract vibrant brand colors from app icons.
* **Icon Shaping:** Choose between shapes like *Squircle*, *Clover*, *Arch*, and *Cookie*.
* **Granular Control:** Per-app overrides for colors, icons, and action styles.
* **🧩 Widgets (New):** Pin standard Android widgets to the island layer for quick access—even on the Lockscreen!
* **Smart Integration:**
* **🎵 Media:** Show album art and "Now Playing" status with visualizer support.
* **🧭 Navigation:** Real-time turn-by-turn instructions (Google Maps, Waze). **New:** Customize the split layout (Distance, ETA, or Instruction).
* **🧭 Navigation:** Real-time turn-by-turn instructions (Google Maps, Waze).
* **⬇️ Downloads:** Circular progress ring with a satisfying "Green Tick" animation upon completion.
* **📞 Calls:** Dedicated layout for incoming and active calls with timers.
* **🛡️ Spoiler Protection:** Define blocked terms globally or per-app to prevent specific notifications (e.g., message spoilers) from popping up on the Island.
* **👻 Ghost Mode:** Option to hide the persistent service notification from the system shade while keeping the Island fully active.
* **Total Control:** Choose exactly which apps trigger the island, customize timeouts, and toggle floating behavior per app.

## 👩‍💻 For Developers: Create Themes

HyperBridge supports an open theming standard (`.hbr` packages). You can create themes and distribute them, or integrate a "Apply Theme" button directly into your own app (Launcher, Icon Pack, etc.).

* **Documentation:** [Full Guide on Creating & Distributing Themes](https://github.com/D4vidDf/HyperBridge/discussions/78)
* **Intent API:** Send themes programmatically using `com.d4viddf.hyperbridge.APPLY_THEME`.

## 🌐 Supported Languages

HyperBridge is fully localized thanks to our amazing community contributors!
HyperBridge is fully localized thanks to our amazing community. **Want to add your language?** We now use Crowdin for easy translation management.

👉 **[Help translate HyperBridge on Crowdin](https://crowdin.com/project/hyper-bridge)**

* 🇺🇸 **English** (Default)
* 🇪🇸 **Spanish** (Español)
* 🇧🇷 **Portuguese** (Português Brasileiro) — Thanks to [@NIICKTCHUNS](https://github.com/NIICKTCHUNS)
* 🇵🇱 **Polish** (Polski) — Thanks to [@kacskrz](https://github.com/kacskrz)
* 🇸🇰 **Slovak** (Slovenčina)
* 🇰🇷 **Korean** (한국어) — Thanks to [@alexkoala](https://github.com/alexkoala)
* 🇺🇦 **Ukrainian** (Українська) — Thanks to [@ItzDFPlayer](https://github.com/ItzDFPlayer)

> **Special Thanks:** A huge shoutout to everyone who contributed translations for this release. You make HyperBridge accessible to the world! ❤️
* 🇷🇺 **Russian** (Русский) — Thanks to [@kilo3528](https://github.com/kilo3528)
* 🇩🇪 **German** (Deutsch) — Thanks to [@kilo3528](https://github.com/kilo3528)
* 🇮🇩 **Indonesian** (Bahasa Indonesia)
* 🇹🇷 **Turkish** (Türkçe)

## 🛠️ Tech Stack

* **Language:** Kotlin
* **UI:** Jetpack Compose (Material 3 Expressive)
* **Architecture:** MVVM
* **Storage:** Room Database (SQLite)
* **Services:** NotificationListenerService, BroadcastReceiver
* **Services:** NotificationListenerService, WidgetOverlayService
* **Concurrency:** Kotlin Coroutines & Flow

## 📸 Screenshots

| Home Screen | Settings | Active Island |
|:---:|:---:|:---:|
| ![Home](./screenshots/home.png) | ![Settings](./screenshots/settings.png) | ![Island](./screenshots/island_example.png) |
| Home Screen | Active Island | Theme Creator | Widget Picker |
|:---:|:---:|:---:|:---:|
| ![Home](./screenshots/home.png) | ![Island](./screenshots/island_example.png) | ![Creator](./screenshots/theme_creator.png) | ![Widgets](./screenshots/widget_picker.png) |

## 📥 Installation

Expand All @@ -91,7 +110,6 @@ Contributions are welcome! Please read our [Contributing Guidelines](CONTRIBUTIN
4. Push to the branch (`git push origin feature/AmazingFeature`).
5. Open a **Pull Request**.


## 💖 Support the Project

Hyper Bridge is an open-source project developed in my free time. If this app has improved your daily experience, please consider supporting its development!
Expand All @@ -108,4 +126,4 @@ Distributed under the Apache 2.0 License. See `LICENSE` for more information.

**D4vidDf**
* Website: [d4viddf.com](https://d4viddf.com)
* GitHub: [@D4vidDf](https://github.com/D4vidDf)
* GitHub: [@D4vidDf](https://github.com/D4vidDf)
19 changes: 13 additions & 6 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ plugins {
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.ksp)
id("org.jetbrains.kotlin.plugin.serialization") version "2.3.0"
}

android {
Expand All @@ -15,8 +16,8 @@ android {
applicationId = "com.d4viddf.hyperbridge"
minSdk = 35
targetSdk = 36
versionCode = 10
versionName = "0.3.1"
versionCode = 14
versionName = "0.4.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
Expand All @@ -34,14 +35,17 @@ android {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
compose = true
}
}

kotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
}
}

dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
Expand All @@ -54,6 +58,8 @@ dependencies {
implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.androidx.ui)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.palette.ktx)
implementation(libs.androidx.material3)
ksp(libs.androidx.room.compiler)
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
Expand All @@ -69,7 +75,8 @@ dependencies {
implementation(libs.androidx.datastore.preferences)
implementation(libs.androidx.lifecycle.viewmodel.compose)

implementation("com.google.code.gson:gson:2.10.1")
implementation(libs.gson)
implementation(libs.kotlinx.serialization.json)
}

configurations.all {
Expand Down
67 changes: 62 additions & 5 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<queries>
<intent>
<action android:name="android.appwidget.action.APPWIDGET_PICK" />
</intent>

<intent>
<action android:name="com.d4viddf.hyperbridge.THEME_PROVIDER" />
</intent>
</queries>

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -20,17 +30,18 @@
android:supportsRtl="true"
android:enableOnBackInvokedCallback="true"
android:theme="@style/Theme.HyperBridge">

<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:stateNotNeeded="true"
android:theme="@style/Theme.HyperBridge">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<receiver
android:name=".receiver.BootReceiver"
android:enabled="true"
Expand All @@ -46,10 +57,56 @@
android:label="HyperBridge Listener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:exported="true"
android:directBootAware="true"> <intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
android:directBootAware="true">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>

<service
android:name=".service.WidgetOverlayService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="shortService" />

<activity
android:name=".ui.screens.theme.ThemeInstallerActivity"
android:exported="true"
android:theme="@style/Theme.HyperBridge.Transparent"
android:excludeFromRecents="true">

<intent-filter>
<action android:name="com.d4viddf.hyperbridge.APPLY_THEME" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/zip" />
<data android:mimeType="application/octet-stream" />
<data android:scheme="content" />
<data android:scheme="file" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="content" />
<data android:scheme="file" />

<data android:mimeType="application/zip" />
<data android:mimeType="application/octet-stream" />
<data android:mimeType="application/x-zip-compressed" />
</intent-filter>
</activity>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>

</manifest>
20 changes: 12 additions & 8 deletions app/src/main/java/com/d4viddf/hyperbridge/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import androidx.compose.ui.res.stringResource
import com.d4viddf.hyperbridge.data.AppPreferences
import com.d4viddf.hyperbridge.data.db.AppDatabase
import com.d4viddf.hyperbridge.data.model.HyperBridgeBackup
import com.d4viddf.hyperbridge.ui.components.ChangelogDialog
import com.d4viddf.hyperbridge.ui.components.ChangelogSheet
import com.d4viddf.hyperbridge.ui.components.PriorityEducationDialog
import com.d4viddf.hyperbridge.ui.screens.home.HomeScreen
import com.d4viddf.hyperbridge.ui.screens.onboarding.OnboardingScreen
Expand Down Expand Up @@ -86,7 +86,7 @@ fun MainRootNavigation() {
val packageInfo = remember { try { context.packageManager.getPackageInfo(context.packageName, 0) } catch (e: Exception) { null } }
@Suppress("DEPRECATION")
val currentVersionCode = packageInfo?.longVersionCode?.toInt() ?: 0
val currentVersionName = packageInfo?.versionName ?: "0.3.1"
val currentVersionName = packageInfo?.versionName ?: "0.4.0"

// --- 2. ROBUST DATA COLLECTION ---
val isSetupComplete by produceState<Boolean?>(initialValue = null) {
Expand Down Expand Up @@ -232,13 +232,17 @@ fun MainRootNavigation() {
}

if (showChangelog) {
ChangelogDialog(currentVersionName = currentVersionName, changelogText = stringResource(R.string.changelog_0_3_1)) {
showChangelog = false
scope.launch {
preferences.setLastSeenVersion(currentVersionCode)
if (!isPriorityEduShown) showPriorityEdu = true
ChangelogSheet (
currentVersionName = currentVersionName,
changelogText = stringResource(R.string.changelog_0_4_0),
onDismiss = {
showChangelog = false
scope.launch {
preferences.setLastSeenVersion(currentVersionCode)
if (!isPriorityEduShown) showPriorityEdu = true
}
}
}
)
}

if (showPriorityEdu) {
Expand Down
60 changes: 60 additions & 0 deletions app/src/main/java/com/d4viddf/hyperbridge/data/AppCacheManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.d4viddf.hyperbridge.data

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Log
import java.io.File
import java.io.FileOutputStream
import androidx.core.content.edit

/**
* Persists App Names and Icons so they can be displayed even after the app is uninstalled.
*/
class AppCacheManager(private val context: Context) {

private val prefs = context.getSharedPreferences("app_metadata_cache", Context.MODE_PRIVATE)
private val iconsDir = File(context.filesDir, "cached_icons").apply { mkdirs() }

fun cacheAppInfo(packageName: String, name: String, icon: Bitmap?) {
// 1. Save Name
prefs.edit { putString(packageName, name) }

// 2. Save Icon to Disk
if (icon != null) {
try {
val file = File(iconsDir, "$packageName.png")
// Only save if it doesn't exist or we want to update it (optional optimization)
if (!file.exists()) {
FileOutputStream(file).use { out ->
icon.compress(Bitmap.CompressFormat.PNG, 100, out)
}
}
} catch (e: Exception) {
Log.e("AppCacheManager", "Failed to cache icon for $packageName", e)
}
}
}

fun getCachedAppName(packageName: String): String {
return prefs.getString(packageName, null) ?: packageName
}

fun getCachedAppIcon(packageName: String): Bitmap? {
val file = File(iconsDir, "$packageName.png")
return if (file.exists()) {
try {
BitmapFactory.decodeFile(file.absolutePath)
} catch (e: Exception) {
null
}
} else {
null
}
}

fun removeCachedData(packageName: String) {
prefs.edit { remove(packageName) }
File(iconsDir, "$packageName.png").delete()
}
}
Loading