π Getting Started with TriggerX



Introduction
As Android developers, we've all faced it. You schedule an alarm, expect a screen to pop up, and... nothing happens. The app was killed, the phone was in Doze mode, or the system quietly denied your foreground service.
You might've searched on various LLMs, StackOverflow, looked for same GitHub repositories, but nothing worked out. Because there're not a lot of resources that are available online for this kind of stuff.
We hit this wall one too many times. Our apps needed time-sensitive UI triggers that worked every time, under any condition. We wanted a way to reliably show UI, without wrestling with wake locks, foreground services, or permission gymnastics.
Thatβs why we built TriggerX. A modern, Compose-first Android library designed to make time-triggered UI reliable, modular, and dead simple.
In this blog, you'll learn how can you integrate this library in your existing compose application. So without further a do, let's get started!
π¦ 1. Add the Dependency
To get started, first add the dependency to your project's `build.gradle.kts` like this and sync your project:
1dependencies {
2 implementation("com.meticha:triggerx:0.0.4")
3}
After that, add this permissions in your AndroidManifest.xml file:
1<!-- To ensure our activity gets opneded at a scheduled time -->
2<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
3
4<!-- For displaying an Activity over other apps/lock screen -->
5<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
6
7<!-- Permissions for scheduling exact alarms -->
8<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
βοΈ 2. Initialize TriggerX
Call TriggerX.init() in your Application class like this:
1class TriggerXApplication : Application() {
2 override fun onCreate() {
3 super.onCreate()
4 TriggerX.init(applicationContext) {
5 useDefaultNotification(
6 title = "My App Alarm",
7 message = "Your alarm is ringing!",
8 channelName = "My Awesome App Notifications"
9 )
10 activityClass = AppAlarmActivity::class.java
11 }
12 }
13}
Now, you might be getting an error because we haven't created the AppAlarmActivity composable yet. So let's do that in the next step.
π§© 3. Create Your Alarm UI
Once we extend the class by TriggerXActivity, we'll need to override one method called AlarmContent that accepts an composable. So you can create any kind of UI you want to show it to the user.
1class AppAlarmActivity : TriggerXActivity() {
2
3 @Composable
4 override fun AlarmContent() {
5 Box(
6 modifier = Modifier.fillMaxSize()
7 ) {
8 Column(
9 modifier = Modifier
10 .fillMaxSize()
11 .background(
12 color = Color.White,
13 shape = RoundedCornerShape(32.dp)
14 )
15 .padding(32.dp),
16 verticalArrangement = Arrangement.Center,
17 horizontalAlignment = Alignment.CenterHorizontally
18 ) {
19 Icon(
20 imageVector = Icons.Default.Notifications,
21 contentDescription = "Trigger Icon",
22 tint = Color(0xFF111111),
23 modifier = Modifier.size(80.dp)
24 )
25
26 Spacer(modifier = Modifier.height(24.dp))
27
28 Text(
29 text = "TriggerX",
30 fontSize = 42.sp,
31 fontWeight = FontWeight.Bold,
32 color = Color(0xFF111111)
33 )
34 }
35 }
36 }
37}
38
Since this is an activity, you can also do all kind of stuff like playing the music when this activity is displayed. But let's save that for the next blog π
π 4. Request Permissions & Schedule Alarm
Once we create the activity that we want to display to the users at a specific time, we need to ask for the permission via our App. TriggerX provides requesting and managing the permissions out of the box. So that you can focus on the core logic.
Here's how you can ask the permission and schedule the alarm:
1@Composable
2fun HomeScreen() {
3 val context = LocalContext.current
4 val permissionState = rememberAppPermissionState()
5 val alarmScheduler = remember { TriggerXAlarmScheduler() }
6
7 Scaffold { paddingValues ->
8 Column(
9 modifier = Modifier
10 .fillMaxSize()
11 .padding(paddingValues)
12 .padding(16.dp),
13 verticalArrangement = Arrangement.Center,
14 horizontalAlignment = Alignment.CenterHorizontally
15 ) {
16 ElevatedButton(
17 onClick = {
18 if (permissionState.allRequiredGranted()) {
19 val triggerTime = Calendar.getInstance().apply {
20 add(Calendar.MINUTE, 1)
21 }.timeInMillis
22 alarmScheduler.scheduleAlarm(
23 context,
24 triggerTime,
25 "MEETING",
26 1
27 )
28 } else {
29 permissionState.requestPermission()
30 }
31 }
32 ) {
33 Text("Schedule Activity")
34 }
35 }
36 }
37}
- First, we're using rememberAppPermissionState() provided by the library to request the permissions in the elevated button.
- In the onClick of the elevated button, we're checking if the permissions are granted or not. If they're not granted then ask for the permission, else we're scheduling the alarm of one minute by using TriggerXAlarmScheduler
That's it! Now you can close the App and chill. Your phone will show your custom activity after one minute even if your App is closed or you've cleared it from the recents menu.
π 5. Pass Dynamic Data Using DataProvider
TriggerX also offers a bunch of customization out of the box. Like you can show dynamic content on your Activity by utilizing TriggerXDataProvider when initializing TriggerX like this:
1class TriggerXApplication : Application() {
2 override fun onCreate() {
3 super.onCreate()
4 TriggerX.init(applicationContext) {
5 useDefaultNotification(
6 title = "My App Alarm",
7 message = "Your alarm is ringing!",
8 channelName = "My Awesome App Notifications"
9 )
10 activityClass = AppAlarmActivity::class.java
11 alarmDataProvider = object : TriggerXDataProvider {
12 override suspend fun provideData(alarmId: Int, alarmType: String): Bundle {
13 return when (alarmType) {
14 "MEETING" -> {
15 bundleOf("title" to "TriggerX", "location" to "Compose")
16 }
17
18 else -> bundleOf()
19 }
20 }
21 }
22 }
23 }
24}
- In this code, we're providing a custom implementation of alarmDataProvider. In that, we've to override the function named provideData()
- In the provideData(), we're checking if the alarm type is equals to "MEETING" or not, and based on that we're returning the bundle.
But the question is, where will we pass alarmType, the answer is when you schedule the alarm itself π
1TriggerXAlarmScheduler().scheduleAlarm(
2 context = context,
3 triggerAtMillis = triggerTime,
4 type = "MEETING",
5 alarmId = 1
6 )
7
β¨ That's a Wrap!

And just like that, youβve added robust, time-triggered UI to your Compose app π
In our next blog, weβll explore how it all works under the hood. Until then, star β the repo, try it out, and feel free to contribute!
π GitHub: https://github.com/meticha/triggerx
Explore TriggerX's source code on Github
https://github.com/meticha/triggerx
Made with β€οΈ by Meticha