Notifications with Countdown Interactive

This guide explains how to implement Notification permission handling and Exact Alarm in Storyly Android SDK using modern Activity Result APIs

Android documentation explains how to Set an exact alarm in newer versions of Android versions.

  • Starting from Android 12 (API level 31), apps need the SCHEDULE_EXACT_ALARM permission to set reminders at specific times.
  • Starting from Android 13 (API level 33), apps must explicitly request the POST_NOTIFICATIONS permission to show reminder notifications.

Manifest Permission Decleration

You need to add the following permissions to your AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yourapp">

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

</manifest>

Initialize Permission Launchers

You need to register the launchers to handle notification requests and the system alarm settings screen using the Activity Result API in your Activity

private var pendingCountdownComponent: StoryComponent? = null
private var pendingStorylyView: StorylyView? = null

private val notificationPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted ->
    if (!isGranted) {
        onFinishAlarmPermissionCheck()
    } else {
        checkAndRequestExactAlarmPermission()
    }
}

private val activityResultListener = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result ->
    onAlarmPermissionResult()
    onFinishAlarmPermissionCheck()
}

StoryCountdownReminderAdded Event

You need to handle the reminder event in your StorylyListener. It is best practice to pause the story immediately when the permission flow begins.

binding.storylyView.storylyListener = object : StorylyListener {
    override fun storylyEvent(
        storylyView: StorylyView,
        event: StorylyEvent,
        storyGroup: StoryGroup?,
        story: Story?,
        storyComponent: StoryComponent?
    ) {
        super.storylyEvent(storylyView, event, storyGroup, story, storyComponent)

        if (event == StorylyEvent.StoryCountdownReminderAdded) {
            checkPermissionsForCountdown(storyComponent, binding.storylyView)
        }
    }
}

Implement Permissions Logic

You need to add following methods orchestrate the check for both notification and exact alarm permissions.

private fun checkPermissionsForCountdown(storyComponent: StoryComponent?, storylyView: StorylyView) {
    pendingStorylyView = storylyView
    pendingCountdownComponent = storyComponent

    // Always pause before showing system dialogs
    storylyView.pauseStory()

    if (!NotificationManagerCompat.from(this).areNotificationsEnabled()) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            notificationPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
        } else {
            onFinishAlarmPermissionCheck()
        }
    } else {
        checkAndRequestExactAlarmPermission()
    }
}

private fun checkAndRequestExactAlarmPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val canScheduleExactAlarms = alarmManager.canScheduleExactAlarms()

        if (!canScheduleExactAlarms) {
            val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)
            intent.data = Uri.parse("package:${packageName}")
            activityResultListener.launch(intent)
        } else {
            onFinishAlarmPermissionCheck()
        }
    }
}

Check the Result

You need to trigger the Storyly Android SDKs internal reminder logic and resume the story after the user return from settings or dialog.

private fun onAlarmPermissionResult() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val canScheduleExactAlarms = alarmManager.canScheduleExactAlarms()

        if (canScheduleExactAlarms) {
            // This triggers the actual reminder scheduling inside the SDK
            (pendingCountdownComponent as? StoryCountDownComponent)?.onPermission?.invoke()
        }
    }
}

private fun onFinishAlarmPermissionCheck() {
    pendingCountdownComponent = null
    pendingStorylyView?.resumeStory()
    pendingStorylyView = null
}

 

📘

Best Practices

  • You must use pauseStory() before showing a permission dialog and resumeStory() after it is closed for a smooth experience.
  • You must check areNotificationsEnabled() before requesting alarm permissions, since reminders need notification visibility to be effective.
  • You must use Build.VERSION_CODES.S (API 31) for alarms and Build.VERSION_CODES.TIRAMISU (API 33) for notifications.
  • You must clear pendingCountdownComponent and pendingStorylyView after handling the permission result to avoid leaks.