https://github.com/meticha/triggerx
TriggerX is a modular, developer-friendly alarm execution library for Android.
https://github.com/meticha/triggerx
alarm alarm-clock alarmmanager android android-library composer-library jetpack-compose kotlin kotlin-android
Last synced: 5 days ago
JSON representation
TriggerX is a modular, developer-friendly alarm execution library for Android.
- Host: GitHub
- URL: https://github.com/meticha/triggerx
- Owner: meticha
- License: apache-2.0
- Created: 2025-06-03T16:49:44.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2026-06-25T06:25:21.000Z (7 days ago)
- Last Synced: 2026-06-25T08:12:54.832Z (7 days ago)
- Topics: alarm, alarm-clock, alarmmanager, android, android-library, composer-library, jetpack-compose, kotlin, kotlin-android
- Language: Kotlin
- Homepage: https://meticha.com/triggerx/docs
- Size: 5.4 MB
- Stars: 100
- Watchers: 1
- Forks: 4
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yaml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
- mobile-awesome - triggerx - TriggerX is a modular, developer-friendly alarm execution library for Android. (Android / Android libraries)
README
# TriggerX

TriggerX is a modular, developer-friendly **alarm execution** library for Android.
[See the full documentation](https://meticha.com/triggerx/docs)
It simplifies scheduling exact alarms and showing user-facing UIs at a specific time, even when
your app has been killed or without you managing foreground-service boilerplate, wake-locks, or
lock-screen flags.
## Example
---
## π What does TriggerX do?
| | |
|-----------------------------------------------------------------------------------|----------------------------------------------------------------------|
| β° **Exact alarms** that work in Doze (API 25+) | π **Lock-screen activity** automatically shown when the alarm fires |
| π Handles permissions: exact alarm, battery optimisations, overlay, notification | π± Wakes the device, starts a foreground service, then stops it |
| π Fetches fresh data at alarm time via `suspend` provider (Room, API, β¦) | π¨ Lets you build the UI in **Jetpack Compose** |
Think of TriggerX as an execution layer that runs a piece of UI logic at the right time and hides
the system details.
---
## β
When should you use TriggerX?
| | |
|---------------------------------------------------------------------------------|-----------------------------------------------------------------|
| π
You need to show a UI (reminder, alert, action screen) **at a given time** | π The UI needs **live data** from DB, cache, or API |
| π§Ή You want to avoid edge-case handling for Doze, foreground services, or flags | π― You want a consistent alarm solution across Android versions |
---
### π¦ Installation
Version catalogs
```toml
[versions]
triggerx = "latest-version"
[dependencies]
triggerx = { module = "com.meticha:triggerx", version.ref = "triggerx" }
```
Gradle DSL
```kotlin
dependencies {
implementation("com.meticha:triggerx:latest-version")
}
```
## π Quick Start
### 0. Add these permission in your `AndroidManifest.xml` file:
```xml
```
### 1. Initialize in your Application class
```kotlin
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
TriggerX.init(this) {
/* UI that opens when the alarm fires */
activityClass = MyAlarmActivity::class.java
/* Foreground-service notification */
useDefaultNotification(
title = "Alarm running",
message = "Tap to open",
channelName = "Alarm Notifications"
)
/* Optional: Provide up-to-date data right before the UI opens */
alarmDataProvider = object : TriggerXDataProvider {
override suspend fun provideData(alarmId: Int, alarmType: String): Bundle {
return when (alarmType) {
"MEETING" -> {
val meeting = meetingRepository.getMeeting(alarmId)
return bundleOf(
"title" to meeting?.title,
"location" to meeting?.location
)
}
else -> bundleOf()
}
}
}
}
}
}
```
### 2. Ask for the permission
The library provides a composable helper to request permissions so that you don't have to manage
this manually. However, the library provides the functionality to request permissions manually if
you want to follow that path
```kotlin
@Composable
fun HomeScreen(
viewModel: HomeViewModel = hiltViewModel()
) {
val context = LocalContext.current
val permissionState = rememberAppPermissionState()
val coroutines = rememberCoroutineScope()
Scaffold { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
ElevatedButton(
onClick = {
coroutines.launch {
if (permissionState.allRequiredGranted()) {
viewModel.scheduleOneMinuteAlarm(context)
} else {
permissionState.requestPermission()
}
}
}
) {
Text("Schedule Activity")
}
}
}
}
```
### 3. Schedule an alarm
```kotlin
val inFiveMinutes = Calendar.getInstance().apply {
add(Calendar.MINUTE, 5)
}.timeInMillis
TriggerXAlarmScheduler().scheduleAlarm(
context = this,
alarmId = 1,
type = "MEETING",
triggerAtMillis = inFiveMinutes
)
```
π‘ You can schedule many alarms with different alarmId / alarmType.
## π§© Create your Alarm UI
```kotlin
class AppAlarmActivity : TriggerXActivity() {
@Composable
override fun AlarmContent() {
val bundle = remember { intent?.getBundleExtra("ALARM_DATA") }
val title = bundle?.getString("title") ?: "empty title"
val location = bundle?.getString("location")
Box(
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(
color = Color.White,
shape = RoundedCornerShape(32.dp)
)
.padding(32.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
imageVector = Icons.Default.Notifications,
contentDescription = "Trigger Icon",
tint = Color(0xFF111111),
modifier = Modifier.size(80.dp)
)
Spacer(modifier = Modifier.height(24.dp))
Text(
text = title,
fontSize = 42.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF111111)
)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = location ?: "empty location",
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF333333),
textAlign = TextAlign.Center
)
}
}
}
}
```
What the base class handles for you:
- π Shows over lock-screen
- π± Turns screen on
- βοΈ Chooses correct flags per Android version
- π¦ Receives & parses Bundle (βALARM_DATAβ)
## π Permissions
TriggerX includes a Composable helper to request what it needs.
```kotlin
@Composable
fun HomeScreen(
viewModel: HomeViewModel = hiltViewModel()
) {
val context = LocalContext.current
val permissionState = rememberAppPermissionState()
val coroutines = rememberCoroutineScope()
Scaffold { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
ElevatedButton(
onClick = {
coroutines.launch {
if (permissionState.allRequiredGranted()) {
viewModel.scheduleOneMinuteAlarm(
context
)
} else {
permissionState.requestPermission()
}
}
}
) {
Text("Schedule Activity")
}
}
}
}
```
Covered automatically:
| Permission | Version |
|--------------------------------|---------|
| β° Exact alarm | API 31+ |
| π Ignore battery optimisation | all |
| πΌ Overlay (only if needed) | all |
| π’ Post-notification | API 33+ |
## π§ How TriggerX works
1. AlarmManager schedules an exact alarm
2. BroadcastReceiver fires when alarm time arrives
3. ForegroundService starts (Play-Store-compliant)
4. Your AlarmDataProvider supplies fresh data (suspend)
5. Service launches your activity with the data bundle
6. ForegroundService stops; activity shows over lock-screen
## π Data Provider Interface
```kotlin
interface TriggerXDataProvider {
/**
* Fetch or build data for a specific alarm.
* Called on a background dispatcher.
*/
suspend fun provideData(alarmId: Int, alarmType: String): Bundle
}
```
Room example:
```kotlin
class RoomProvider(private val dao: MeetingDao) : TriggerXDataProvider {
override suspend fun provideData(id: Int, type: String) = when (type) {
"MEETING" -> dao.getMeeting(id)?.run {
bundleOf("title" to title, "location" to location)
} ?: Bundle.EMPTY
else -> Bundle.EMPTY
}
}
```
## π TriggerX Configuration Example
```kotlin
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
TriggerX.init(this) {
notificationTitle = "Reminder"
notificationMessage = "You have a scheduled event"
notificationChannelName = "Event Alarms"
customLogger = object : TriggerXLogger {
override fun d(message: String) = Log.d("TriggerX", message)
override fun e(message: String, throwable: Throwable?) =
Log.e("TriggerX", message, throwable)
}
}
}
}
```
## π Retrofit or Network-Based Data Provider
```kotlin
class ApiProvider(private val api: AlarmApi) : TriggerXDataProvider {
override suspend fun provideData(alarmId: Int, alarmType: String): Bundle {
return try {
val response = api.getAlarmDetails(alarmId)
bundleOf("title" to response.title, "description" to response.description)
} catch (e: Exception) {
Bundle.EMPTY
}
}
}
```
## πΎ In-Memory or Static Data Provider
```kotlin
class StaticProvider : TriggerXDataProvider {
override suspend fun provideData(alarmId: Int, alarmType: String): Bundle {
return when (alarmType) {
"WELCOME" -> bundleOf("title" to "Welcome!", "body" to "Thanks for installing our app.")
else -> Bundle.EMPTY
}
}
}
```
## π Delegating to Multiple Providers
```kotlin
class MultiSourceProvider(
private val roomProvider: TriggerXDataProvider,
private val networkProvider: TriggerXDataProvider
) : TriggerXDataProvider {
override suspend fun provideData(alarmId: Int, alarmType: String): Bundle {
return when (alarmType) {
"MEETING" -> roomProvider.provideData(alarmId, alarmType)
"NEWS" -> networkProvider.provideData(alarmId, alarmType)
else -> Bundle.EMPTY
}
}
}
```
## πΎ Persistence
TriggerX stores minimal info (ID, type, activity class) in Preferences DataStore so alarms still
work after the user swipes the app away.
## π Cancel alarms
```kotlin
TriggerXAlarmScheduler().cancelAlarm(this, alarmId = 1)
```
## π¨ Troubleshooting
| Issue | Check |
|----------------------|---------------------------------|
| Alarm doesnβt fire | Exact-alarm permission granted? |
| Activity not visible | Overlay / lock-screen flags? |
| Service killed early | Battery optimisation disabled? |
## π€ Contributing
See [CONTRIBUTING.MD](CONTRIBUTING.md) for more info on how you can contribute to this repository
## Find this repository useful? :heart:
Support it by joining __[stargazers](https://github.com/meticha/triggerx/stargazers)__ for this
repository. :star:
Also __[follow](https://github.com/meticha)__ Meticha for the next creations! π€©
## π License
```
Copyright 2025 Meticha
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```