{"id":50719951,"url":"https://github.com/meticha/triggerx","last_synced_at":"2026-06-27T00:01:03.766Z","repository":{"id":297181738,"uuid":"995552312","full_name":"meticha/triggerx","owner":"meticha","description":"TriggerX is a modular, developer-friendly alarm execution library for Android. ","archived":false,"fork":false,"pushed_at":"2026-06-25T06:25:21.000Z","size":5659,"stargazers_count":100,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-25T08:12:54.832Z","etag":null,"topics":["alarm","alarm-clock","alarmmanager","android","android-library","composer-library","jetpack-compose","kotlin","kotlin-android"],"latest_commit_sha":null,"homepage":"https://meticha.com/triggerx/docs","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/meticha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yaml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["meticha"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2025-06-03T16:49:44.000Z","updated_at":"2026-06-25T06:25:18.000Z","dependencies_parsed_at":"2025-06-04T14:03:55.471Z","dependency_job_id":"e8710b42-1e10-4c34-93d1-18334f82a8e1","html_url":"https://github.com/meticha/triggerx","commit_stats":null,"previous_names":["meticha/triggerx"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/meticha/triggerx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meticha%2Ftriggerx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meticha%2Ftriggerx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meticha%2Ftriggerx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meticha%2Ftriggerx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/meticha","download_url":"https://codeload.github.com/meticha/triggerx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meticha%2Ftriggerx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34835785,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alarm","alarm-clock","alarmmanager","android","android-library","composer-library","jetpack-compose","kotlin","kotlin-android"],"created_at":"2026-06-09T23:00:25.268Z","updated_at":"2026-06-27T00:01:03.746Z","avatar_url":"https://github.com/meticha.png","language":"Kotlin","funding_links":["https://github.com/sponsors/meticha"],"categories":["Android"],"sub_categories":["Android libraries"],"readme":"# TriggerX\n\n![triggerx-banner.png](banner/triggerx-banner.png)\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://central.sonatype.com/artifact/com.meticha/triggerx\"\u003e\n  \u003cimg src=\"https://img.shields.io/maven-central/v/com.meticha/triggerx.svg?label=Maven%20Central\" alt=\"Maven Central\"\u003e\n  \u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Jetpack%20Compose-Supported-4285F4?logo=android\" alt=\"Jetpack Compose Supported\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Kotlin-2.1.20-blueviolet?logo=kotlin\" alt=\"Kotlin Version\"\u003e\n  \u003ca href=\"https://github.com/Meticha/TriggerX/blob/main/LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/license/Meticha/TriggerX.svg\" alt=\"license\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://androidweekly.net/issues/issue-678\"\u003e\u003cimg alt=\"Android Weekly\" src=\"https://androidweekly.net/issues/issue-678/badge\" height=\"20px\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Meticha/TriggerX/stargazers\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/stars/Meticha/TriggerX.svg?style=social\" alt=\"GitHub stars\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://opensource.org/licenses/Apache-2.0\"\u003e\n    \u003cimg alt=\"License\" src=\"https://img.shields.io/badge/License-Apache%202.0-blue.svg\"/\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\nTriggerX is a modular, developer-friendly **alarm execution** library for Android.\n\n[See the full documentation](https://meticha.com/triggerx/docs)\n\nIt simplifies scheduling exact alarms and showing user-facing UIs at a specific time, even when\nyour app has been killed or without you managing foreground-service boilerplate, wake-locks, or\nlock-screen flags.\n\n## Example\n\n\u003cp\u003e\n  \u003cimg src=\"triggerx_example.gif\" alt=\"TriggerX Example\" height=\"500\"\u003e\n\u003c/p\u003e\n\n---\n\n## 📌 What does TriggerX do?\n\n|                                                                                   |                                                                      |\n|-----------------------------------------------------------------------------------|----------------------------------------------------------------------|\n| ⏰ **Exact alarms** that work in Doze (API 25+)                                    | 🔓 **Lock-screen activity** automatically shown when the alarm fires |\n| 🔑 Handles permissions: exact alarm, battery optimisations, overlay, notification | 📱 Wakes the device, starts a foreground service, then stops it      |\n| 🔄 Fetches fresh data at alarm time via `suspend` provider (Room, API, …)         | 🎨 Lets you build the UI in **Jetpack Compose**                      |\n\nThink of TriggerX as an execution layer that runs a piece of UI logic at the right time and hides\nthe system details.\n\n---\n\n## ✅ When should you use TriggerX?\n\n|                                                                                 |                                                                 |\n|---------------------------------------------------------------------------------|-----------------------------------------------------------------|\n| 📅 You need to show a UI (reminder, alert, action screen) **at a given time**   | 🔄 The UI needs **live data** from DB, cache, or API            |\n| 🧹 You want to avoid edge-case handling for Doze, foreground services, or flags | 🎯 You want a consistent alarm solution across Android versions |\n\n---\n\n### 📦 Installation\n\n\u003cdetails\u003e\n\u003csummary\u003eVersion catalogs\u003c/summary\u003e\n\n```toml\n[versions]\ntriggerx = \"latest-version\"\n\n[dependencies]\ntriggerx = { module = \"com.meticha:triggerx\", version.ref = \"triggerx\" }\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eGradle DSL\u003c/summary\u003e\n\n```kotlin\ndependencies {\n    implementation(\"com.meticha:triggerx:latest-version\")\n}\n```\n\n\u003c/details\u003e\n\n## 🚀 Quick Start\n\n### 0. Add these permission in your `AndroidManifest.xml` file:\n\n```xml\n\n\u003cuses-permission android:name=\"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS\" /\u003e\n\n    \u003c!-- Permissions for scheduling exact alarms --\u003e\n\u003cuses-permission android:name=\"android.permission.SCHEDULE_EXACT_ALARM\" /\u003e\n\n\u003cuses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\"/\u003e\n```\n\n### 1. Initialize in your Application class\n\n```kotlin\nclass MyApplication : Application() {\n    override fun onCreate() {\n        super.onCreate()\n\n        TriggerX.init(this) {\n\n            /* UI that opens when the alarm fires */\n            activityClass = MyAlarmActivity::class.java\n\n            /* Foreground-service notification */\n            useDefaultNotification(\n                title = \"Alarm running\",\n                message = \"Tap to open\",\n                channelName = \"Alarm Notifications\"\n            )\n\n            /* Optional: Provide up-to-date data right before the UI opens */\n            alarmDataProvider = object : TriggerXDataProvider {\n                override suspend fun provideData(alarmId: Int, alarmType: String): Bundle {\n                    return when (alarmType) {\n                        \"MEETING\" -\u003e {\n                            val meeting = meetingRepository.getMeeting(alarmId)\n                            return bundleOf(\n                                \"title\" to meeting?.title,\n                                \"location\" to meeting?.location\n                            )\n                        }\n\n                        else -\u003e bundleOf()\n                    }\n                }\n            }\n        }\n    }\n}\n```\n\n### 2. Ask for the permission\n\nThe library provides a composable helper to request permissions so that you don't have to manage\nthis manually. However, the library provides the functionality to request permissions manually if\nyou want to follow that path\n\n```kotlin\n@Composable\nfun HomeScreen(\n    viewModel: HomeViewModel = hiltViewModel()\n) {\n    val context = LocalContext.current\n    val permissionState = rememberAppPermissionState()\n    val coroutines = rememberCoroutineScope()\n\n    Scaffold { paddingValues -\u003e\n        Column(\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(paddingValues)\n                .padding(16.dp),\n            verticalArrangement = Arrangement.Center,\n            horizontalAlignment = Alignment.CenterHorizontally\n        ) {\n            ElevatedButton(\n                onClick = {\n                    coroutines.launch {\n                        if (permissionState.allRequiredGranted()) {\n                            viewModel.scheduleOneMinuteAlarm(context)\n                        } else {\n                            permissionState.requestPermission()\n                        }\n                    }\n                }\n            ) {\n                Text(\"Schedule Activity\")\n            }\n        }\n    }\n}\n```\n\n### 3. Schedule an alarm\n\n```kotlin\nval inFiveMinutes = Calendar.getInstance().apply {\n    add(Calendar.MINUTE, 5)\n}.timeInMillis\n\n\nTriggerXAlarmScheduler().scheduleAlarm(\n    context = this,\n    alarmId = 1,\n    type = \"MEETING\",\n    triggerAtMillis = inFiveMinutes\n)\n```\n\n💡 You can schedule many alarms with different alarmId / alarmType.\n\n## 🧩 Create your Alarm UI\n\n```kotlin\nclass AppAlarmActivity : TriggerXActivity() {\n\n    @Composable\n    override fun AlarmContent() {\n        val bundle = remember { intent?.getBundleExtra(\"ALARM_DATA\") }\n        val title = bundle?.getString(\"title\") ?: \"empty title\"\n        val location = bundle?.getString(\"location\")\n        Box(\n            modifier = Modifier.fillMaxSize()\n        ) {\n            Column(\n                modifier = Modifier\n                    .fillMaxSize()\n                    .background(\n                        color = Color.White,\n                        shape = RoundedCornerShape(32.dp)\n                    )\n                    .padding(32.dp),\n                verticalArrangement = Arrangement.Center,\n                horizontalAlignment = Alignment.CenterHorizontally\n            ) {\n                Icon(\n                    imageVector = Icons.Default.Notifications,\n                    contentDescription = \"Trigger Icon\",\n                    tint = Color(0xFF111111),\n                    modifier = Modifier.size(80.dp)\n                )\n\n                Spacer(modifier = Modifier.height(24.dp))\n\n                Text(\n                    text = title,\n                    fontSize = 42.sp,\n                    fontWeight = FontWeight.Bold,\n                    color = Color(0xFF111111)\n                )\n\n                Spacer(modifier = Modifier.height(12.dp))\n\n                Text(\n                    text = location ?: \"empty location\",\n                    fontSize = 20.sp,\n                    fontWeight = FontWeight.Medium,\n                    color = Color(0xFF333333),\n                    textAlign = TextAlign.Center\n                )\n            }\n        }\n    }\n}\n```\n\nWhat the base class handles for you:\n\n- 🔓 Shows over lock-screen\n- 📱 Turns screen on\n- ⚙️ Chooses correct flags per Android version\n- 📦 Receives \u0026 parses Bundle (“ALARM_DATA”)\n\n## 🔐 Permissions\n\nTriggerX includes a Composable helper to request what it needs.\n\n```kotlin\n@Composable\nfun HomeScreen(\n    viewModel: HomeViewModel = hiltViewModel()\n) {\n    val context = LocalContext.current\n    val permissionState = rememberAppPermissionState()\n    val coroutines = rememberCoroutineScope()\n\n\n    Scaffold { paddingValues -\u003e\n        Column(\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(paddingValues)\n                .padding(16.dp),\n            verticalArrangement = Arrangement.Center,\n            horizontalAlignment = Alignment.CenterHorizontally\n        ) {\n            ElevatedButton(\n                onClick = {\n                    coroutines.launch {\n                        if (permissionState.allRequiredGranted()) {\n                            viewModel.scheduleOneMinuteAlarm(\n                                context\n                            )\n                        } else {\n                            permissionState.requestPermission()\n                        }\n                    }\n                }\n            ) {\n                Text(\"Schedule Activity\")\n            }\n        }\n    }\n}\n```\n\nCovered automatically:\n\n| Permission                     | Version |\n|--------------------------------|---------|\n| ⏰ Exact alarm                 | API 31+ |\n| 🔋 Ignore battery optimisation | all     |\n| 🖼 Overlay (only if needed)    | all     |\n| 📢 Post-notification           | API 33+ |\n\n## 🧠 How TriggerX works\n\n1. AlarmManager schedules an exact alarm\n2. BroadcastReceiver fires when alarm time arrives\n3. ForegroundService starts (Play-Store-compliant)\n4. Your AlarmDataProvider supplies fresh data (suspend)\n5. Service launches your activity with the data bundle\n6. ForegroundService stops; activity shows over lock-screen\n\n## 🗂 Data Provider Interface\n\n```kotlin\ninterface TriggerXDataProvider {\n    /**\n     * Fetch or build data for a specific alarm.\n     * Called on a background dispatcher.\n     */\n    suspend fun provideData(alarmId: Int, alarmType: String): Bundle\n}\n```\n\nRoom example:\n\n```kotlin\nclass RoomProvider(private val dao: MeetingDao) : TriggerXDataProvider {\n    override suspend fun provideData(id: Int, type: String) = when (type) {\n        \"MEETING\" -\u003e dao.getMeeting(id)?.run {\n            bundleOf(\"title\" to title, \"location\" to location)\n        } ?: Bundle.EMPTY\n        else -\u003e Bundle.EMPTY\n    }\n}\n```\n\n## 📁 TriggerX Configuration Example\n\n```kotlin\nclass MyApplication : Application() {\n    override fun onCreate() {\n        super.onCreate()\n\n        TriggerX.init(this) {\n            notificationTitle = \"Reminder\"\n            notificationMessage = \"You have a scheduled event\"\n            notificationChannelName = \"Event Alarms\"\n            customLogger = object : TriggerXLogger {\n                override fun d(message: String) = Log.d(\"TriggerX\", message)\n                override fun e(message: String, throwable: Throwable?) =\n                    Log.e(\"TriggerX\", message, throwable)\n            }\n        }\n    }\n}\n```\n\n## 🔗 Retrofit or Network-Based Data Provider\n\n```kotlin\nclass ApiProvider(private val api: AlarmApi) : TriggerXDataProvider {\n    override suspend fun provideData(alarmId: Int, alarmType: String): Bundle {\n        return try {\n            val response = api.getAlarmDetails(alarmId)\n            bundleOf(\"title\" to response.title, \"description\" to response.description)\n        } catch (e: Exception) {\n            Bundle.EMPTY\n        }\n    }\n}\n```\n\n## 💾 In-Memory or Static Data Provider\n\n```kotlin\nclass StaticProvider : TriggerXDataProvider {\n    override suspend fun provideData(alarmId: Int, alarmType: String): Bundle {\n        return when (alarmType) {\n            \"WELCOME\" -\u003e bundleOf(\"title\" to \"Welcome!\", \"body\" to \"Thanks for installing our app.\")\n            else -\u003e Bundle.EMPTY\n        }\n    }\n}\n```\n\n## 🔁 Delegating to Multiple Providers\n\n```kotlin\nclass MultiSourceProvider(\n    private val roomProvider: TriggerXDataProvider,\n    private val networkProvider: TriggerXDataProvider\n) : TriggerXDataProvider {\n\n    override suspend fun provideData(alarmId: Int, alarmType: String): Bundle {\n        return when (alarmType) {\n            \"MEETING\" -\u003e roomProvider.provideData(alarmId, alarmType)\n            \"NEWS\" -\u003e networkProvider.provideData(alarmId, alarmType)\n            else -\u003e Bundle.EMPTY\n        }\n    }\n}\n```\n\n## 💾 Persistence\n\nTriggerX stores minimal info (ID, type, activity class) in Preferences DataStore so alarms still\nwork after the user swipes the app away.\n\n## 🗑 Cancel alarms\n\n```kotlin\nTriggerXAlarmScheduler().cancelAlarm(this, alarmId = 1)\n```\n\n## 🗨 Troubleshooting\n\n| Issue                | Check                           |\n|----------------------|---------------------------------|\n| Alarm doesn’t fire   | Exact-alarm permission granted? |\n| Activity not visible | Overlay / lock-screen flags?    |\n| Service killed early | Battery optimisation disabled?  |\n\n## 🤝 Contributing\n\nSee [CONTRIBUTING.MD](CONTRIBUTING.md) for more info on how you can contribute to this repository\n\n## Find this repository useful? :heart:\n\nSupport it by joining __[stargazers](https://github.com/meticha/triggerx/stargazers)__ for this\nrepository. :star: \u003cbr\u003e\nAlso __[follow](https://github.com/meticha)__ Meticha for the next creations! 🤩\n\n## 📄 License\n\n```\nCopyright 2025 Meticha\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeticha%2Ftriggerx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmeticha%2Ftriggerx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeticha%2Ftriggerx/lists"}