From 3fa167cba08d9bcf60b0825b32e8b71386b0578a Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Mon, 10 Nov 2025 06:12:22 +0000 Subject: [PATCH] fix(android): improve exact alarm permission check with fallback strategies Fix reflection-based permission check that was failing with NoSuchMethodException. Add multiple fallback strategies to ensure permission check works reliably. Changes: - Add getDeclaredMethod() fallback when getMethod() fails - Add heuristic fallback: if exact alarms not allowed, assume they can be requested - Improve error handling: catch NoSuchMethodException separately from other exceptions - Add debug logging to track which reflection path is taken - Change reflection failure log level from ERROR to WARNING (we have fallback) The heuristic fallback is safe because: - If exact alarms are not currently allowed, we should try to request them - Only edge case is permanently denied (rare), worst case is unnecessary Settings redirect - Better than failing silently or blocking permission requests Fixes reflection failures seen in logcat where Settings.canRequestScheduleExactAlarms() method lookup was failing, causing unnecessary Settings redirects. --- .../DailyNotificationPlugin.kt | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.kt b/android/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.kt index 10a6fcf..6f0846c 100644 --- a/android/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.kt +++ b/android/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.kt @@ -823,8 +823,10 @@ open class DailyNotificationPlugin : Plugin() { * Check if exact alarm permission can be requested * Helper method that handles API level differences * - * Uses reflection to call Settings.canRequestScheduleExactAlarms() on Android 13+ - * to avoid compilation issues with newer APIs. + * On Android 12 (API 31-32): Permission can always be requested + * On Android 13+ (API 33+): Uses reflection to call Settings.canRequestScheduleExactAlarms() + * If reflection fails, falls back to heuristic: if exact alarms are not currently allowed, + * we assume they can be requested (safe default). * * @param context Application context * @return true if permission can be requested, false if permanently denied @@ -833,17 +835,39 @@ open class DailyNotificationPlugin : Plugin() { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12+ (API 31+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - // Android 13+ (API 33+) - use reflection to call canRequestScheduleExactAlarms + // Android 13+ (API 33+) - try reflection to call canRequestScheduleExactAlarms try { + // Try getMethod first (for public static methods) val method = Settings::class.java.getMethod( "canRequestScheduleExactAlarms", Context::class.java ) - method.invoke(null, context) as Boolean + val result = method.invoke(null, context) as Boolean + Log.d(TAG, "canRequestScheduleExactAlarms() returned: $result") + return result + } catch (e: NoSuchMethodException) { + // Method not found - try getDeclaredMethod as fallback + try { + val method = Settings::class.java.getDeclaredMethod( + "canRequestScheduleExactAlarms", + Context::class.java + ) + method.isAccessible = true + val result = method.invoke(null, context) as Boolean + Log.d(TAG, "canRequestScheduleExactAlarms() (via getDeclaredMethod) returned: $result") + return result + } catch (e2: Exception) { + Log.w(TAG, "Failed to check exact alarm permission using reflection, using heuristic", e2) + // Fallback heuristic: if exact alarms are not currently allowed, + // assume we can request them (safe default) + // Only case where we can't request is if permanently denied, which is rare + return !canScheduleExactAlarms(context) + } } catch (e: Exception) { - Log.e(TAG, "Failed to check exact alarm permission using reflection", e) - // Fallback to allowing request (safe default) - true + Log.w(TAG, "Failed to invoke canRequestScheduleExactAlarms(), using heuristic", e) + // Fallback heuristic: if exact alarms are not currently allowed, + // assume we can request them (safe default) + return !canScheduleExactAlarms(context) } } else { // Android 12 (API 31-32) - permission can always be requested