Skip to content

makise-ui/SamLiveNotification

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 

Repository files navigation

Samsung Live Notifications & Now Bar - Complete Developer Documentation

Table of Contents

  1. Overview
  2. Prerequisites
  3. Project Setup
  4. Manifest Configuration
  5. Notification Service Implementation
  6. Samsung-Specific Extras
  7. UI Integration
  8. Testing & Debugging
  9. Best Practices
  10. Troubleshooting
  11. API Reference

Overview

Samsung Live Notifications and Now Bar are enhanced notification features available in Samsung One UI 7+ that provide:

  • Live Notifications: Enhanced notifications displayed in a separate section of the notification drawer
  • Now Bar: Interactive notifications shown on the lock screen
  • Custom Styling: Chips, progress indicators, and custom colors
  • Rich Content: Primary/secondary text, icons, and progress tracking

Key Features

  • Real-time progress updates
  • Custom chip styling with colors and icons
  • Notification actions (pause, stop, etc.)
  • Lock screen integration (Now Bar)
  • Segmented progress indicators
  • Custom notification layouts

Important Limitations

⚠️ Samsung Whitelist Required: Apps must be whitelisted by Samsung for full Live Notifications functionality ⚠️ Device Compatibility: Only works on Samsung devices with One UI 7+ ⚠️ Future Availability: Will be available to all developers in Android 16 through standard APIs


Prerequisites

Development Environment

  • Android Studio: Arctic Fox or later
  • Android SDK: API 26+ (minimum), API 34+ (recommended)
  • Java/Kotlin: Java 8+ or Kotlin 1.8+
  • Samsung Device: One UI 7+ for testing (optional but recommended)

Required Permissions

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />

Project Setup

1. Create New Android Project

# Using Android Studio or command line
android create project \
  --target android-34 \
  --name SamsungLiveNotifications \
  --path ./samsung-live-notifications \
  --activity MainActivity \
  --package com.example.samsunglivenotifications

2. Update build.gradle (Module: app)

android {
    compileSdk 34
    
    defaultConfig {
        applicationId "com.example.samsunglivenotifications"
        minSdk 26
        targetSdk 34
        versionCode 1
        versionName "1.0"
    }
    
    buildFeatures {
        viewBinding true
    }
}

dependencies {
    implementation 'androidx.core:core-ktx:1.10.1'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.9.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'androidx.lifecycle:lifecycle-service:2.7.0'
}

Manifest Configuration

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- Required Permissions -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.SamsungLiveNotifications"
        tools:targetApi="31">

        <!-- CRITICAL: Samsung Live Notifications Metadata -->
        <meta-data
            android:name="com.samsung.android.support.ongoing_activity"
            android:value="true" />

        <!-- Main Activity -->
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- Foreground Service for Live Notifications -->
        <service
            android:name=".LiveNotificationService"
            android:enabled="true"
            android:exported="false"
            android:foregroundServiceType="specialUse">
            <property
                android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
                android:value="Samsung Live Notifications Demo" />
        </service>

    </application>
</manifest>

Key Manifest Elements

1. Samsung Metadata (REQUIRED)

<meta-data
    android:name="com.samsung.android.support.ongoing_activity"
    android:value="true" />

This metadata tells Samsung's system that your app supports ongoing activities and Live Notifications.

2. Foreground Service Configuration

<service
    android:name=".LiveNotificationService"
    android:foregroundServiceType="specialUse">
    <property
        android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
        android:value="Samsung Live Notifications Demo" />
</service>

Notification Service Implementation

1. Create LiveNotificationService.kt

package com.example.samsunglivenotifications

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Icon
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.os.bundleOf
import java.util.Timer
import java.util.TimerTask

class LiveNotificationService : Service() {
    
    companion object {
        const val ACTION_START = "ACTION_START"
        const val ACTION_STOP = "ACTION_STOP"
        const val ACTION_PAUSE = "ACTION_PAUSE"
        
        private const val NOTIFICATION_ID = 1001
        private const val CHANNEL_ID = "live_notifications_channel"
        private const val REQUEST_CODE_PAUSE = 100
        private const val REQUEST_CODE_STOP = 101
        private const val REQUEST_CODE_MAIN = 102
    }
    
    private var notificationManager: NotificationManager? = null
    private var timer: Timer? = null
    private var progress = 0
    private var isPaused = false
    
    override fun onCreate() {
        super.onCreate()
        notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        createNotificationChannel()
    }
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        when (intent?.action) {
            ACTION_START -> startLiveNotification()
            ACTION_PAUSE -> togglePause()
            ACTION_STOP -> stopLiveNotification()
        }
        return START_STICKY
    }
    
    override fun onBind(intent: Intent?): IBinder? = null
    
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID,
                "Live Notifications Channel",
                NotificationManager.IMPORTANCE_LOW
            ).apply {
                description = "Channel for Samsung Live Notifications demo"
                setShowBadge(false)
            }
            notificationManager?.createNotificationChannel(channel)
        }
    }
    
    private fun startLiveNotification() {
        startForeground(NOTIFICATION_ID, createLiveNotification())
        startProgressTimer()
    }
    
    private fun stopLiveNotification() {
        timer?.cancel()
        timer = null
        stopForeground(STOP_FOREGROUND_REMOVE)
        stopSelf()
    }
    
    private fun togglePause() {
        isPaused = !isPaused
        updateNotification()
    }
    
    private fun startProgressTimer() {
        timer?.cancel()
        timer = Timer()
        timer?.scheduleAtFixedRate(object : TimerTask() {
            override fun run() {
                if (!isPaused) {
                    progress = (progress + 1) % 101
                    updateNotification()
                }
            }
        }, 0, 1000) // Update every second
    }
    
    private fun updateNotification() {
        notificationManager?.notify(NOTIFICATION_ID, createLiveNotification())
    }
    
    private fun createLiveNotification(): Notification {
        // Create pending intents for actions
        val pauseIntent = Intent(this, LiveNotificationService::class.java).apply {
            action = ACTION_PAUSE
        }
        val pausePendingIntent = PendingIntent.getService(
            this, REQUEST_CODE_PAUSE, pauseIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        
        val stopIntent = Intent(this, LiveNotificationService::class.java).apply {
            action = ACTION_STOP
        }
        val stopPendingIntent = PendingIntent.getService(
            this, REQUEST_CODE_STOP, stopIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        
        val mainIntent = Intent(this, MainActivity::class.java)
        val mainPendingIntent = PendingIntent.getActivity(
            this, REQUEST_CODE_MAIN, mainIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        
        // Create Samsung Live Notifications extras bundle
        val extras = createSamsungLiveNotificationExtras()
        
        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Live Notification Demo")
            .setContentText("Samsung Live Notifications Active")
            .setSmallIcon(R.drawable.ic_notification)
            .setContentIntent(mainPendingIntent)
            .setOngoing(true)
            .setAutoCancel(false)
            .addAction(
                R.drawable.ic_pause,
                if (isPaused) "Resume" else "Pause",
                pausePendingIntent
            )
            .addAction(
                R.drawable.ic_stop,
                "Stop",
                stopPendingIntent
            )
            .setExtras(extras)
            .build()
    }
    
    private fun createSamsungLiveNotificationExtras(): Bundle {
        return bundleOf(
            // REQUIRED: Enable Samsung Live Notifications
            "android.ongoingActivityNoti.style" to 1,
            
            // Standard Style Configuration
            "android.ongoingActivityNoti.primaryInfo" to "Demo App",
            "android.ongoingActivityNoti.secondaryInfo" to if (isPaused) "Paused" else "Running",
            "android.ongoingActivityNoti.secondaryInfoIcon" to Icon.createWithResource(this, R.drawable.ic_celebration),
            
            // Chip Configuration
            "android.ongoingActivityNoti.chipBgColor" to getColor(R.color.chip_background),
            "android.ongoingActivityNoti.chipIcon" to Icon.createWithResource(this, R.drawable.ic_chip_location),
            "android.ongoingActivityNoti.chipExpandedText" to "Live Demo",
            
            // Progress Configuration
            "android.ongoingActivityNoti.progress" to progress,
            "android.ongoingActivityNoti.progressMax" to 100,
            "android.ongoingActivityNoti.progressSegments.icon" to Icon.createWithResource(this, R.drawable.ic_notification),
            "android.ongoingActivityNoti.progressSegments.progressColor" to getColor(R.color.progress_current),
            "android.ongoingActivityNoti.progressSegments" to arrayOf(
                bundleOf(
                    "android.ongoingActivityNoti.progressSegments.segmentStart" to 0.0f,
                    "android.ongoingActivityNoti.progressSegments.segmentColor" to getColor(R.color.progress_segment_1)
                ),
                bundleOf(
                    "android.ongoingActivityNoti.progressSegments.segmentStart" to 50.0f,
                    "android.ongoingActivityNoti.progressSegments.segmentColor" to getColor(R.color.progress_segment_2)
                )
            ),
            
            // Action Configuration for Live Notifications
            "android.ongoingActivityNoti.actionType" to 1,
            "android.ongoingActivityNoti.actionPrimarySet" to 0,
            
            // Now Bar Configuration (Lock Screen)
            "android.ongoingActivityNoti.nowbarPrimaryInfo" to "Demo",
            "android.ongoingActivityNoti.nowbarSecondaryInfo" to if (isPaused) "Paused" else "Active"
        )
    }
}

Samsung-Specific Extras

Core Configuration

1. Enable Live Notifications (REQUIRED)

"android.ongoingActivityNoti.style" to 1

This is the most important extra - it enables Samsung Live Notifications.

2. Standard Style Configuration

// Primary text displayed prominently
"android.ongoingActivityNoti.primaryInfo" to "Your App Name"

// Secondary text with optional icon
"android.ongoingActivityNoti.secondaryInfo" to "Status Text"
"android.ongoingActivityNoti.secondaryInfoIcon" to Icon.createWithResource(context, R.drawable.icon)

3. Chip Configuration

// Chip background color
"android.ongoingActivityNoti.chipBgColor" to Color.parseColor("#FF6B35")

// Chip icon
"android.ongoingActivityNoti.chipIcon" to Icon.createWithResource(context, R.drawable.chip_icon)

// Text shown when chip is expanded
"android.ongoingActivityNoti.chipExpandedText" to "Expanded Text"

4. Progress Configuration

// Current progress value
"android.ongoingActivityNoti.progress" to currentProgress

// Maximum progress value
"android.ongoingActivityNoti.progressMax" to 100

// Progress bar icon
"android.ongoingActivityNoti.progressSegments.icon" to Icon.createWithResource(context, R.drawable.progress_icon)

// Current progress color
"android.ongoingActivityNoti.progressSegments.progressColor" to Color.parseColor("#4CAF50")

// Progress segments with different colors
"android.ongoingActivityNoti.progressSegments" to arrayOf(
    bundleOf(
        "android.ongoingActivityNoti.progressSegments.segmentStart" to 0.0f,
        "android.ongoingActivityNoti.progressSegments.segmentColor" to Color.parseColor("#FF9800")
    ),
    bundleOf(
        "android.ongoingActivityNoti.progressSegments.segmentStart" to 50.0f,
        "android.ongoingActivityNoti.progressSegments.segmentColor" to Color.parseColor("#4CAF50")
    )
)

5. Action Configuration

// Action type for Live Notifications
"android.ongoingActivityNoti.actionType" to 1

// Primary action set
"android.ongoingActivityNoti.actionPrimarySet" to 0

6. Now Bar Configuration (Lock Screen)

// Primary text for Now Bar (shorter than main notification)
"android.ongoingActivityNoti.nowbarPrimaryInfo" to "App"

// Secondary text for Now Bar
"android.ongoingActivityNoti.nowbarSecondaryInfo" to "Status"

Complete Extras Reference

private fun createCompleteSamsungExtras(): Bundle {
    return bundleOf(
        // === CORE CONFIGURATION ===
        "android.ongoingActivityNoti.style" to 1, // REQUIRED: Enable Live Notifications
        
        // === STANDARD STYLE ===
        "android.ongoingActivityNoti.primaryInfo" to "Primary Text",
        "android.ongoingActivityNoti.secondaryInfo" to "Secondary Text",
        "android.ongoingActivityNoti.secondaryInfoIcon" to Icon.createWithResource(this, R.drawable.secondary_icon),
        
        // === CHIP STYLING ===
        "android.ongoingActivityNoti.chipBgColor" to Color.parseColor("#FF6B35"),
        "android.ongoingActivityNoti.chipIcon" to Icon.createWithResource(this, R.drawable.chip_icon),
        "android.ongoingActivityNoti.chipExpandedText" to "Expanded Chip Text",
        
        // === PROGRESS INDICATORS ===
        "android.ongoingActivityNoti.progress" to progress,
        "android.ongoingActivityNoti.progressMax" to 100,
        "android.ongoingActivityNoti.progressSegments.icon" to Icon.createWithResource(this, R.drawable.progress_icon),
        "android.ongoingActivityNoti.progressSegments.progressColor" to Color.parseColor("#4CAF50"),
        "android.ongoingActivityNoti.progressSegments" to arrayOf(
            bundleOf(
                "android.ongoingActivityNoti.progressSegments.segmentStart" to 0.0f,
                "android.ongoingActivityNoti.progressSegments.segmentColor" to Color.parseColor("#FF9800")
            ),
            bundleOf(
                "android.ongoingActivityNoti.progressSegments.segmentStart" to 33.0f,
                "android.ongoingActivityNoti.progressSegments.segmentColor" to Color.parseColor("#FFC107")
            ),
            bundleOf(
                "android.ongoingActivityNoti.progressSegments.segmentStart" to 66.0f,
                "android.ongoingActivityNoti.progressSegments.segmentColor" to Color.parseColor("#4CAF50")
            )
        ),
        
        // === ACTION CONFIGURATION ===
        "android.ongoingActivityNoti.actionType" to 1,
        "android.ongoingActivityNoti.actionPrimarySet" to 0,
        
        // === NOW BAR (LOCK SCREEN) ===
        "android.ongoingActivityNoti.nowbarPrimaryInfo" to "Lock Screen Primary",
        "android.ongoingActivityNoti.nowbarSecondaryInfo" to "Lock Screen Secondary"
    )
}

UI Integration

MainActivity.kt

package com.example.samsunglivenotifications

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.example.samsunglivenotifications.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    
    private lateinit var binding: ActivityMainBinding
    private var isServiceRunning = false
    
    private val requestPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted: Boolean ->
        if (isGranted) {
            startLiveNotificationService()
        } else {
            Toast.makeText(this, "Notification permission is required", Toast.LENGTH_SHORT).show()
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        setupClickListeners()
        updateUI()
    }
    
    private fun setupClickListeners() {
        binding.startButton.setOnClickListener {
            if (checkNotificationPermission()) {
                startLiveNotificationService()
            } else {
                requestNotificationPermission()
            }
        }
        
        binding.stopButton.setOnClickListener {
            stopLiveNotificationService()
        }
    }
    
    private fun checkNotificationPermission(): Boolean {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.POST_NOTIFICATIONS
            ) == PackageManager.PERMISSION_GRANTED
        } else {
            true
        }
    }
    
    private fun requestNotificationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
        }
    }
    
    private fun startLiveNotificationService() {
        val serviceIntent = Intent(this, LiveNotificationService::class.java)
        serviceIntent.action = LiveNotificationService.ACTION_START
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(serviceIntent)
        } else {
            startService(serviceIntent)
        }
        
        isServiceRunning = true
        updateUI()
        
        Toast.makeText(this, "Live Notification started", Toast.LENGTH_SHORT).show()
    }
    
    private fun stopLiveNotificationService() {
        val serviceIntent = Intent(this, LiveNotificationService::class.java)
        serviceIntent.action = LiveNotificationService.ACTION_STOP
        startService(serviceIntent)
        
        isServiceRunning = false
        updateUI()
        
        Toast.makeText(this, "Live Notification stopped", Toast.LENGTH_SHORT).show()
    }
    
    private fun updateUI() {
        binding.startButton.isEnabled = !isServiceRunning
        binding.stopButton.isEnabled = isServiceRunning
        binding.statusText.text = if (isServiceRunning) {
            "Status: Running - Check notification drawer and lock screen"
        } else {
            "Status: Stopped"
        }
    }
}

Layout (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Samsung Live Notifications Demo"
        android:textSize="24sp"
        android:textStyle="bold"
        android:gravity="center"
        android:layout_marginBottom="32dp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="This app demonstrates Samsung's Live Notifications and Now Bar features available in One UI 7."
        android:textSize="16sp"
        android:gravity="center"
        android:layout_marginBottom="32dp" />

    <Button
        android:id="@+id/startButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start Live Notification"
        android:textSize="18sp"
        android:layout_marginBottom="16dp" />

    <Button
        android:id="@+id/stopButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Stop Live Notification"
        android:textSize="18sp"
        android:enabled="false" />

    <TextView
        android:id="@+id/statusText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Status: Stopped"
        android:textSize="16sp"
        android:gravity="center"
        android:layout_marginTop="32dp" />

</LinearLayout>

Testing & Debugging

1. Testing on Samsung Devices

On Samsung One UI 7+ (Whitelisted Apps)

  • ✅ Live Notifications appear in separate section
  • ✅ Now Bar shows on lock screen
  • ✅ Custom chip styling visible
  • ✅ Progress indicators work
  • ✅ Enhanced notification layout

On Samsung One UI 7+ (Non-Whitelisted Apps)

  • ❌ Live Notifications fallback to standard notifications
  • ❌ Now Bar not available
  • ❌ Custom styling not applied
  • ✅ Basic notification functionality works

On Non-Samsung Devices

  • ❌ Samsung-specific features not available
  • ✅ Standard Android notifications work
  • ✅ Foreground service functions normally

2. Debugging Tips

Enable Detailed Logging

private fun logSamsungExtras(extras: Bundle) {
    Log.d("SamsungLiveNotif", "=== Samsung Live Notification Extras ===")
    for (key in extras.keySet()) {
        val value = extras.get(key)
        Log.d("SamsungLiveNotif", "$key: $value")
    }
    Log.d("SamsungLiveNotif", "==========================================")
}

Check Notification Channel

private fun debugNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = notificationManager?.getNotificationChannel(CHANNEL_ID)
        Log.d("SamsungLiveNotif", "Channel importance: ${channel?.importance}")
        Log.d("SamsungLiveNotif", "Channel can show badge: ${channel?.canShowBadge()}")
    }
}

Verify Manifest Metadata

private fun checkSamsungMetadata() {
    try {
        val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
        val metaData = appInfo.metaData
        val samsungSupport = metaData?.getBoolean("com.samsung.android.support.ongoing_activity", false)
        Log.d("SamsungLiveNotif", "Samsung ongoing activity support: $samsungSupport")
    } catch (e: Exception) {
        Log.e("SamsungLiveNotif", "Error checking metadata", e)
    }
}

3. Common Issues & Solutions

Issue: Notifications not showing as Live Notifications

Solution:

  • Verify Samsung metadata in manifest
  • Check if app is whitelisted by Samsung
  • Ensure android.ongoingActivityNoti.style is set to 1

Issue: Progress not updating

Solution:

  • Call notificationManager.notify() with same ID
  • Ensure progress values are within valid range
  • Check timer/update mechanism

Issue: Actions not working

Solution:

  • Verify PendingIntent flags (use FLAG_IMMUTABLE)
  • Check service action handling
  • Ensure unique request codes

Best Practices

1. Performance Optimization

Efficient Updates

// Update only when necessary
private var lastProgress = -1
private fun updateNotificationIfNeeded(newProgress: Int) {
    if (newProgress != lastProgress) {
        lastProgress = newProgress
        updateNotification()
    }
}

Batch Updates

// Avoid too frequent updates
private val updateHandler = Handler(Looper.getMainLooper())
private var pendingUpdate = false

private fun scheduleUpdate() {
    if (!pendingUpdate) {
        pendingUpdate = true
        updateHandler.postDelayed({
            updateNotification()
            pendingUpdate = false
        }, 500) // Update at most every 500ms
    }
}

2. Resource Management

Proper Cleanup

override fun onDestroy() {
    timer?.cancel()
    timer = null
    super.onDestroy()
}

Memory Efficient Icons

// Use vector drawables when possible
"android.ongoingActivityNoti.chipIcon" to Icon.createWithResource(this, R.drawable.ic_vector_icon)

// For bitmap icons, ensure proper sizing
private fun createOptimizedIcon(resourceId: Int): Icon {
    val bitmap = BitmapFactory.decodeResource(resources, resourceId)
    val scaledBitmap = Bitmap.createScaledBitmap(bitmap, 64, 64, true)
    return Icon.createWithBitmap(scaledBitmap)
}

3. User Experience

Meaningful Progress

// Show meaningful progress information
"android.ongoingActivityNoti.secondaryInfo" to when {
    progress < 25 -> "Starting..."
    progress < 50 -> "In Progress..."
    progress < 75 -> "Almost Done..."
    progress < 100 -> "Finishing..."
    else -> "Complete!"
}

Appropriate Colors

// Use colors that match your app theme
private fun getProgressColor(progress: Int): Int {
    return when {
        progress < 30 -> Color.parseColor("#FF5722") // Red for low progress
        progress < 70 -> Color.parseColor("#FF9800") // Orange for medium
        else -> Color.parseColor("#4CAF50") // Green for high progress
    }
}

Troubleshooting

Common Problems

1. Live Notifications Not Appearing

Symptoms: Notifications show as regular Android notifications Causes:

  • Missing Samsung metadata in manifest
  • App not whitelisted by Samsung
  • Incorrect extras bundle configuration
  • Not running on Samsung One UI 7+ device

Solutions:

// Verify metadata exists
private fun verifySamsungSupport(): Boolean {
    try {
        val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
        return appInfo.metaData?.getBoolean("com.samsung.android.support.ongoing_activity", false) == true
    } catch (e: Exception) {
        return false
    }
}

// Check if running on Samsung device
private fun isSamsungDevice(): Boolean {
    return Build.MANUFACTURER.equals("samsung", ignoreCase = true)
}

// Verify One UI version (if possible)
private fun checkOneUIVersion(): String? {
    return try {
        val version = Build.VERSION.RELEASE
        // Additional Samsung-specific version checking if needed
        version
    } catch (e: Exception) {
        null
    }
}

2. Progress Not Updating

Symptoms: Progress bar stuck or not visible Solutions:

// Ensure proper progress range
private fun updateProgress(newProgress: Int) {
    val clampedProgress = newProgress.coerceIn(0, 100)
    
    val extras = bundleOf(
        "android.ongoingActivityNoti.style" to 1,
        "android.ongoingActivityNoti.progress" to clampedProgress,
        "android.ongoingActivityNoti.progressMax" to 100,
        // ... other extras
    )
    
    // Update notification
    val notification = NotificationCompat.Builder(this, CHANNEL_ID)
        .setExtras(extras)
        .build()
        
    notificationManager?.notify(NOTIFICATION_ID, notification)
}

3. Actions Not Responding

Symptoms: Notification buttons don't work Solutions:

// Use proper PendingIntent flags
private fun createActionPendingIntent(action: String): PendingIntent {
    val intent = Intent(this, LiveNotificationService::class.java).apply {
        this.action = action
    }
    
    return PendingIntent.getService(
        this,
        action.hashCode(), // Unique request code
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )
}

4. Service Stops Unexpectedly

Symptoms: Notification disappears, service killed Solutions:

// Use START_STICKY for service restart
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    // Handle intent
    return START_STICKY // Service will be restarted if killed
}

// Handle low memory situations
override fun onLowMemory() {
    super.onLowMemory()
    // Reduce memory usage, pause non-essential updates
}

// Proper foreground service handling
private fun ensureForegroundService() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForeground(NOTIFICATION_ID, createLiveNotification())
    }
}

Debugging Checklist

  • Samsung metadata present in AndroidManifest.xml
  • Required permissions declared and granted
  • Foreground service properly configured
  • Samsung-specific extras bundle correctly formatted
  • Notification channel created with appropriate importance
  • PendingIntents use FLAG_IMMUTABLE
  • Progress values within valid range (0-100)
  • Icons exist and are accessible
  • Service handles all required actions
  • Testing on Samsung One UI 7+ device

API Reference

Samsung Live Notification Extras

Extra Key Type Required Description
android.ongoingActivityNoti.style int Must be 1 to enable Live Notifications
android.ongoingActivityNoti.primaryInfo String Primary text displayed prominently
android.ongoingActivityNoti.secondaryInfo String Secondary text with optional icon
android.ongoingActivityNoti.secondaryInfoIcon Icon Icon for secondary information
android.ongoingActivityNoti.chipBgColor int Background color for chip
android.ongoingActivityNoti.chipIcon Icon Icon displayed in chip
android.ongoingActivityNoti.chipExpandedText String Text shown when chip is expanded
android.ongoingActivityNoti.progress int Current progress value
android.ongoingActivityNoti.progressMax int Maximum progress value
android.ongoingActivityNoti.progressSegments.icon Icon Icon for progress bar
android.ongoingActivityNoti.progressSegments.progressColor int Color of current progress
android.ongoingActivityNoti.progressSegments Array Progress segments with colors
android.ongoingActivityNoti.actionType int Action type for Live Notifications
android.ongoingActivityNoti.actionPrimarySet int Primary action set identifier
android.ongoingActivityNoti.nowbarPrimaryInfo String Primary text for Now Bar (lock screen)
android.ongoingActivityNoti.nowbarSecondaryInfo String Secondary text for Now Bar

Progress Segment Bundle Structure

bundleOf(
    "android.ongoingActivityNoti.progressSegments.segmentStart" to 0.0f, // Start percentage (0.0-100.0)
    "android.ongoingActivityNoti.progressSegments.segmentColor" to Color.parseColor("#FF9800") // Segment color
)

Service Actions

Action Description
ACTION_START Start the Live Notification service
ACTION_STOP Stop the Live Notification service
ACTION_PAUSE Pause/Resume the notification

Required Permissions

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />

Required Manifest Metadata

<meta-data
    android:name="com.samsung.android.support.ongoing_activity"
    android:value="true" />

Conclusion

Samsung Live Notifications provide a powerful way to create enhanced notification experiences on Samsung devices. While currently requiring Samsung's whitelist approval, these features will become available to all developers with Android 16.

Key takeaways:

  • Always include the required Samsung metadata
  • Use android.ongoingActivityNoti.style = 1 to enable Live Notifications
  • Test on Samsung One UI 7+ devices for full functionality
  • Implement proper fallbacks for non-Samsung devices
  • Follow Android foreground service best practices

For the most up-to-date information, refer to Samsung's official developer documentation and the Android 16 Live Updates API when it becomes available.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors