Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Publish
on:
release:
types: [released]
jobs:
publish:
name: Release build and publish
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 21
- name: Publish to MavenCentral
run: ./gradlew publishToMavenCentral --no-configuration-cache
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY_CONTENTS }}
4 changes: 2 additions & 2 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
- main

env:
VERSION_NAME: 0.0.8 # Update this value for each release
VERSION_NAME: 0.0.9 # Update this value for each release

jobs:
release:
Expand All @@ -16,7 +16,7 @@ jobs:

steps:
- name: Checkout Repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
token: ${{ secrets.TOKEN }}
persist-credentials: true
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@
.externalNativeBuild
.cxx
local.properties
/gradle.properties
4 changes: 4 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8

android.useAndroidX=true
kotlin.code.style=official
android.nonTransitiveRClass=true

6 changes: 3 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ coreKtx = "1.16.0"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.9.1"
lifecycleRuntimeKtx = "2.9.2"
activityCompose = "1.10.1"
composeBom = "2025.06.01"
composeBom = "2025.07.00"
appcompat = "1.7.1"
kotlinSerialization = "2.1.21"
hiltNavigation = "1.2.0"
hilt = "2.56.2"
hilt = "2.56.1"
ksp = "2.1.21-2.0.1"
spotless = "7.0.4"

Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Tue Jun 03 22:26:27 IST 2025
#Thu Jul 10 22:22:40 IST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
39 changes: 38 additions & 1 deletion triggerx/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ plugins {
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.spotless)
id("com.vanniktech.maven.publish") version "0.32.0"
id("com.vanniktech.maven.publish") version "0.34.0"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid mixing version-catalog aliases with an explicit plugin declaration

Every other plugin is declared through the version-catalog (alias(libs.plugins.…​)), whereas com.vanniktech.maven.publish is hard-coded with a version string.
Mixing the two styles makes upgrades easy to miss.

-    id("com.vanniktech.maven.publish") version "0.34.0"
+    alias(libs.plugins.vanniktech.maven.publish)

Add the plugin to libs.versions.toml and keep all versions in a single place.

🤖 Prompt for AI Agents
In triggerx/build.gradle.kts at line 21, the plugin
"com.vanniktech.maven.publish" is declared with a hard-coded version string,
which is inconsistent with other plugins declared via version-catalog aliases.
To fix this, add the plugin and its version to the libs.versions.toml file and
then replace the explicit declaration with the corresponding alias from the
version catalog to centralize version management.

}

android {
Expand Down Expand Up @@ -92,4 +92,41 @@ dependencies {
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
}

mavenPublishing {
publishToMavenCentral()

signAllPublications()


coordinates("com.meticha", "triggerx", "0.0.9")

Comment on lines +97 to +104
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Hard-coding coordinates duplicates existing Gradle properties

group and version are usually supplied via gradle.properties or a root‐level versionCatalog.
Duplicating them here risks divergence on the next bump.

-    coordinates("com.meticha", "triggerx", "0.0.9")
+    // Re-use the project’s canonical values
+    coordinates(project.group.toString(), "triggerx", project.version.toString())

This keeps the build single-sourced and avoids accidental mismatches between publishing and build metadata.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mavenPublishing {
publishToMavenCentral()
signAllPublications()
coordinates("com.meticha", "triggerx", "0.0.9")
mavenPublishing {
publishToMavenCentral()
signAllPublications()
// Re-use the project’s canonical values
coordinates(
project.group.toString(),
"triggerx",
project.version.toString()
)
}
🤖 Prompt for AI Agents
In triggerx/build.gradle.kts around lines 97 to 104, the Maven publishing
coordinates are hard-coded, duplicating values that should come from existing
Gradle properties like group and version. Remove the explicit coordinates call
and instead reference the group and version properties already defined in
gradle.properties or the root version catalog to keep the build configuration
single-sourced and consistent.

pom {
name = "triggerx"
description =
"A lightweight Android library for scheduling exact alarms with custom UIs. No foreground services or wake-lock hassle."
inceptionYear = "2025"
version = "0.0.9"
url = "https://github.com/meticha/triggerx.git"
licenses {
license {
name = "The Apache License, Version 2.0"
url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt"
}
}
developers {
developer {
id = "Cavin"
name = "Cavin Macwan"
url = "https://github.com/cavin-macwan/"
}
}
scm {
url = "https://github.com/meticha/triggerx.git"
connection = "scm:git:git://github.com/meticha/triggerx.git"
developerConnection = "scm:git:ssh://[email protected]/meticha/triggerx.git"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import kotlinx.coroutines.launch


/**
* A Composable side-effect that observes lifecycle events to re-evaluate and request permissions.
* A Composable side effect that observes lifecycle events to re-evaluate and request permissions.
*
* This is primarily used to detect when the user returns to the app after potentially changing
* permissions in the system settings. When the specified [lifecycleEvent] (defaulting to `ON_RESUME`)
Expand All @@ -35,7 +35,7 @@ import kotlinx.coroutines.launch
*
* @param permissionState The current [PermissionState] to be monitored and updated.
* @param lifecycleEvent The [Lifecycle.Event] to observe for triggering the permission check.
* Defaults to [Lifecycle.Event.ON_RESUME].
* Defaults to [Lifecycle.Event.ON_RESUME].
*/
@Composable
internal fun PermissionLifeCycleCheckEffect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,7 @@ fun rememberAppPermissionState(): PermissionState {
permissionState.next()
}

else -> handlePermissionDenial(
permissionState
)
else -> handlePermissionDenial(permissionState)
}
}
}
Expand All @@ -117,16 +115,14 @@ fun rememberAppPermissionState(): PermissionState {
)
}

permissionState.showRationalePopUp -> {
permissionState.showRationalePopUp -> {
ShowPopup(
message = "Permissions are required to proceed further",
onConfirm = {
permissionState.showRationalePopUp = false
coroutineScope.launch { permissionState.requestPermission() }
},
onDismiss = {
permissionState.showRationalePopUp = false
}
onDismiss = { permissionState.showRationalePopUp = false }
)
}

Expand All @@ -150,11 +146,11 @@ private fun ShowPermissionGuidanceDialog(
remember { context.applicationInfo.loadLabel(context.packageManager).toString() }
ShowManualPermissionDialog(
message = "For alarms to reliably appear when the app is in the background, " +
"'$appName' needs an additional permission on some phones (like Xiaomi, Oppo, Vivo, etc.).\n\n" +
"Please go to your phone's Settings -> Apps -> Manage Apps (or similar) -> Find '$appName' -> " +
"Other permissions (or App permissions) -> And ensure 'Display pop-up windows while running in the background' " +
"(or a similar sounding option like 'Start in background') is ENABLED.\n\n" +
"This reminder is shown once if you click 'Acknowledge'.",
"'$appName' needs an additional permission on some phones (like Xiaomi, Oppo, Vivo, etc.).\n\n" +
"Please go to your phone's Settings -> Apps -> Manage Apps (or similar) -> Find '$appName' -> " +
"Other permissions (or App permissions) -> And ensure 'Display pop-up windows while running in the background' " +
"(or a similar sounding option like 'Start in background') is ENABLED.\n\n" +
"This reminder is shown once if you click 'Acknowledge'.",
onDismiss = {
permissionState.showPermissionGuidanceDialog = false
coroutineScope.launch {
Expand All @@ -179,7 +175,7 @@ private fun ShowPermissionGuidanceDialog(
)
}

else -> {}
else -> {}
}
}

Expand Down Expand Up @@ -229,8 +225,8 @@ internal fun ShowManualPermissionDialog(
* or to inform them about the necessity of a permission if it has been denied.
*
* @param message The main message to be displayed in the dialog.
* @param onConfirm A lambda function to be executed when the confirm button (e.g., "Grant") is clicked.
* @param onDismiss A lambda function to be executed when the dismiss button (e.g., "Cancel") is clicked
* @param onConfirm A lambda function to be executed when the confirmation button (e.g., "Grant") is clicked.
* @param onDismiss A lambda function to be executed when the dismissed button (e.g., "Cancel") is clicked
* or when the dialog is dismissed by tapping outside or pressing the back button.
*/
@Composable
Expand All @@ -242,7 +238,7 @@ internal fun ShowPopup(
AlertDialog(
onDismissRequest = { onDismiss() },
title = {
Text(text = "Permission")
Text(text = "Permission is required")
},
text = {
Text(text = message)
Expand All @@ -258,7 +254,7 @@ internal fun ShowPopup(
Button(onClick = {
onConfirm()
}) {
Text(text = "Grant")
Text(text = "Grant Permission")
}
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,11 @@ internal object AlarmPermissionManager {
*/
suspend fun isGranted(context: Context, permission: PermissionType): Boolean {
return when (permission) {
PermissionType.ALARM -> hasExactAlarmPermission(context)
PermissionType.OVERLAY -> hasOverlayPermission(context)
PermissionType.BATTERY_OPTIMIZATION -> hasBatteryOptimizationPermission(context)
PermissionType.LOCK_SCREEN -> isShowOnLockScreenPermissionEnable(context)
PermissionType.NOTIFICATION -> isNotificationPermissionEnabled(context)
PermissionType.ALARM -> hasExactAlarmPermission(context)
PermissionType.OVERLAY -> hasOverlayPermission(context)
PermissionType.BATTERY_OPTIMIZATION -> hasBatteryOptimizationPermission(context)
PermissionType.LOCK_SCREEN -> isShowOnLockScreenPermissionEnable(context)
PermissionType.NOTIFICATION -> isNotificationPermissionEnabled(context)
PermissionType.OVERLAY_WHILE_BACKGROUND -> isOverlayBackgroundPermissionEnabled(context)
}
}
Expand All @@ -163,7 +163,7 @@ internal object AlarmPermissionManager {
*/
fun createPermissionIntent(context: Context, permissionType: PermissionType): Intent? {
when (permissionType) {
PermissionType.ALARM -> {
PermissionType.ALARM -> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
data = "package:${context.packageName}".toUri()
Expand All @@ -173,7 +173,7 @@ internal object AlarmPermissionManager {
}
}

PermissionType.OVERLAY -> {
PermissionType.OVERLAY -> {
return Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION).apply {
data = "package:${context.packageName}".toUri()
}
Expand All @@ -185,7 +185,7 @@ internal object AlarmPermissionManager {
}
}

PermissionType.LOCK_SCREEN -> {
PermissionType.LOCK_SCREEN -> {
// This intent is specific to MIUI and might not work on other devices.
return Intent("miui.intent.action.APP_PERM_EDITOR").apply {
setClassName(
Expand All @@ -196,7 +196,7 @@ internal object AlarmPermissionManager {
}
}

PermissionType.NOTIFICATION -> {
PermissionType.NOTIFICATION -> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
Expand All @@ -206,7 +206,7 @@ internal object AlarmPermissionManager {
}
}

else -> {
else -> {
return null
}
}
Expand Down Expand Up @@ -240,7 +240,7 @@ enum class PermissionType(val isManualPermissionType: Boolean = false) {
}

/**
* Manages the state of permission requests and their UI flows, particularly for a list
* Manages the state of permission requests, and their UI flows, particularly for a list
* of required permissions. This class is typically used in conjunction with Jetpack Compose.
*
* @param permissionList The initial list of [PermissionType]s that this state manager will handle.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ internal object TriggerXPreferences {

/**
* Saves the provided [TriggerXConfig] to DataStore.
* This includes the activity class name, and optionally the notification title and message.
* This includes the activity class name and optionally the notification title and message.
*
* @param context The [Context] used to access DataStore.
* @param config The [TriggerXConfig] instance containing the settings to save.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ internal class TriggerXForegroundService : Service() {
channelName,
NotificationManager.IMPORTANCE_DEFAULT
).apply {
description = "Notifications for TriggerX alarms"
description = "Notifications for scheduling alarms"
}
val nm = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nm.createNotificationChannel(channel)
Expand Down
Loading