Skip to main content

Implementing "Super Island" Dynamic Notifications in HyperOS 3 Apps

 Integrating live activities like food delivery tracking, ride-sharing status, or boarding passes into custom OEM status bars is a recurring challenge for Android developers. When targeting Xiaomi's ecosystem, engineers often find their carefully crafted ongoing notifications failing to render in the new multi-island top-screen system. Instead, the OS defaults to standard, intrusive heads-up notifications.

This failure breaks the intended user experience. To resolve this, UI/UX Developers and Front-end Android Engineers must bridge standard Android APIs with the proprietary metadata expected by the HyperOS SystemUI. This guide provides a definitive implementation for intercepting and populating the Super Island overlay.

The Root Cause: Why Standard Notifications Fail in HyperOS

To understand why standard ongoing notifications do not automatically convert into dynamic islands, we must look at how HyperOS modifies the Android Open Source Project (AOSP) SystemUI window manager.

Standard Android dynamic notifications design relies on Notification.Builder and standard NotificationCompat.DecoratedCustomViewStyle. However, HyperOS 3 intercepts incoming system notifications before they reach the standard rendering pipeline. The system's proprietary notification listener scans the Notification object's extras bundle for specific signature flags.

If the OS does not detect the exact keys defined by the HyperOS 3 Super Island API, it safely assumes the notification is not optimized for top-bar hardware cutout integration. Consequently, it forces a fallback to the AOSP heads-up display logic. Furthermore, many modern Mobile UI/UX frameworks abstract notification building, stripping away the custom bundle extras required to trigger the island injection.

The Fix: Implementing the Super Island Integration

To force the HyperOS window manager to promote your notification to the Super Island, you must construct a foreground service notification using RemoteViews while injecting specific Bundle parameters.

Step 1: Define Kotlin Custom Status Bar Views

Super Island requires two explicit layouts: a collapsed view (the pill shape around the camera cutout) and an expanded view (when the user long-presses the island).

Save this as res/layout/super_island_collapsed.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="40dp"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:paddingHorizontal="12dp"
    android:background="@android:color/transparent">

    <ImageView
        android:id="@+id/island_icon"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:src="@drawable/ic_delivery_car"
        android:contentDescription="Delivery Status" />

    <TextView
        android:id="@+id/island_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:text="Arriving in 5m"
        android:textColor="@android:color/white"
        android:textSize="14sp"
        android:textStyle="bold" />
</LinearLayout>

Step 2: The Kotlin Foreground Service

The following Kotlin implementation creates the notification, binds the custom views, and appends the critical metadata required by HyperOS.

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class LiveTrackingService : Service() {

    private val channelId = "super_island_tracking_channel"
    private val notificationId = 1042

    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        startForeground(notificationId, buildIslandNotification("Arriving in 5m"))
        simulateLiveUpdates()
        return START_STICKY
    }

    private fun buildIslandNotification(statusText: String): Notification {
        val collapsedView = RemoteViews(packageName, R.layout.super_island_collapsed).apply {
            setTextViewText(R.id.island_text, statusText)
        }

        // Expanded view layout omitted for brevity, follows same RemoteViews pattern
        val expandedView = RemoteViews(packageName, R.layout.super_island_expanded)

        val hyperOsExtras = Bundle().apply {
            // HyperOS 3 specific keys to trigger Super Island injection
            putBoolean("miui.super_island.enabled", true)
            putString("miui.super_island.type", "LIVE_ACTIVITY")
            putInt("miui.super_island.priority", 1)
        }

        return NotificationCompat.Builder(this, channelId)
            .setSmallIcon(R.drawable.ic_delivery_car)
            .setCustomContentView(collapsedView)
            .setCustomBigContentView(expandedView)
            .setStyle(NotificationCompat.DecoratedCustomViewStyle())
            .setOngoing(true)
            .setCategory(Notification.CATEGORY_SERVICE)
            .addExtras(hyperOsExtras)
            .build()
    }

    private fun simulateLiveUpdates() {
        CoroutineScope(Dispatchers.Default).launch {
            val minutesLeft = listOf("4m", "3m", "2m", "1m", "Arrived")
            val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            
            for (time in minutesLeft) {
                delay(60000) // 1 minute intervals
                val updatedNotification = buildIslandNotification("Arriving in $time")
                manager.notify(notificationId, updatedNotification)
            }
            stopSelf()
        }
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                "Live Tracking",
                NotificationManager.IMPORTANCE_HIGH
            ).apply {
                description = "Active delivery tracking"
                setSound(null, null) // Silent updates are required for islands
            }
            val manager = getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(channel)
        }
    }

    override fun onBind(intent: Intent?): IBinder? = null
}

Deep Dive: Architecture of the Solution

The success of this implementation relies on the intersection of standard NotificationCompat parameters and OEM-specific metadata.

The setOngoing(true) directive is non-negotiable. HyperOS will immediately reject any dismissible notification from entering the Super Island queue. The system prioritizes space in the status bar strictly for immutable, state-driven processes. By pairing this with NotificationManager.IMPORTANCE_HIGH and a null sound profile, we signal to the OS that this is an active UI update, not an alert.

The crucial link is the hyperOsExtras bundle. When miui.super_island.enabled is detected, the HyperOS SystemUI suppresses the standard heads-up display. It then parses your setCustomContentView (the Kotlin custom status bar views) and masks it within the pill-shaped hardware cutout. By providing both a collapsed and big content view, the OS automatically handles the transition animations when the user interacts with the island.

Common Pitfalls and Edge Cases

Hardware Cutout Clipping

Because HyperOS dynamically resizes the Super Island based on the device's physical camera cutout, hardcoding width dimensions in your RemoteViews will result in text truncation on different Xiaomi models. Always use wrap_content for root layouts and rely on paddingHorizontal to create safe zones around your text and icons.

Throttled Background Updates

Live activities require frequent UI updates. However, Android's battery management restricts how often a background service can update RemoteViews. Updating the notification manager faster than once every 500 milliseconds can cause SystemUI jank or lead to your foreground service being silently killed by MIUI battery optimizations. Throttle your state updates using a Coroutine StateFlow with a distinct-until-changed operator before pushing new notifications.

Handling Multiple Active Islands

The HyperOS 3 Super Island API supports splitting the island into two distinct nodes when multiple apps request priority. To ensure your app renders gracefully in the "split" state, keep your island_icon structurally independent of the text. If the OS forces your app into the secondary minimal node, it will automatically strip the TextView and only render the ImageView defined in your RemoteViews.

Conclusion

Successfully integrating live activities into HyperOS requires moving beyond standard mobile UI/UX frameworks and directly interfacing with OEM-specific notification rules. By constructing a robust foreground service, utilizing standard RemoteViews, and injecting the necessary Super Island metadata flags, developers can ensure their tracking features occupy the premium real estate at the top of the user's screen.