fix(android): improve channel status detection and UI refresh

- Fix isChannelEnabled() to create channel if missing and re-fetch from system
  to get actual state (handles previously blocked channels)
- Use correct channel ID 'timesafari.daily' instead of 'daily_notification_channel'
- Add detailed logging for channel status checks
- Fix UI to refresh channel status after notification permissions are granted
- Channel status now correctly reflects both app-level and channel-level settings
This commit is contained in:
Matthew Raymer
2025-11-18 09:50:23 +00:00
parent 8f20da7e8d
commit a8039d072d
2 changed files with 248 additions and 286 deletions

View File

@@ -95,7 +95,7 @@ open class DailyNotificationPlugin : Plugin() {
Log.e(TAG, "Context is null, cannot initialize database")
return
}
db = DailyNotificationDatabase.getDatabase(context)
db = DailyNotificationDatabase.getDatabase(context)
Log.i(TAG, "Daily Notification Plugin loaded successfully")
} catch (e: Exception) {
Log.e(TAG, "Failed to initialize Daily Notification Plugin", e)
@@ -1048,21 +1048,73 @@ open class DailyNotificationPlugin : Plugin() {
@PluginMethod
fun isChannelEnabled(call: PluginCall) {
try {
val channelId = call.getString("channelId") ?: "daily_notification_channel"
val enabled = NotificationManagerCompat.from(context).areNotificationsEnabled()
if (context == null) {
return call.reject("Context not available")
}
// Use the actual channel ID that matches what's used in notifications
val channelId = call.getString("channelId") ?: "timesafari.daily"
// Check app-level notifications first
val appNotificationsEnabled = NotificationManagerCompat.from(context).areNotificationsEnabled()
// Get notification channel importance if available
var importance = 0
var channelEnabled = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as android.app.NotificationManager?
val channel = notificationManager?.getNotificationChannel(channelId)
importance = channel?.importance ?: android.app.NotificationManager.IMPORTANCE_DEFAULT
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? android.app.NotificationManager
var channel = notificationManager?.getNotificationChannel(channelId)
if (channel == null) {
// Channel doesn't exist - create it first (same as ChannelManager does)
Log.i(TAG, "Channel $channelId doesn't exist, creating it")
val newChannel = android.app.NotificationChannel(
channelId,
"Daily Notifications",
android.app.NotificationManager.IMPORTANCE_HIGH
).apply {
description = "Daily notifications from TimeSafari"
enableLights(true)
enableVibration(true)
setShowBadge(true)
}
notificationManager?.createNotificationChannel(newChannel)
Log.i(TAG, "Channel $channelId created with HIGH importance")
// Re-fetch the channel from the system to get actual state
// (in case it was previously blocked by user)
channel = notificationManager?.getNotificationChannel(channelId)
}
// Now check the channel (re-fetched from system to get actual state)
if (channel != null) {
importance = channel.importance
// Channel is enabled if importance is not IMPORTANCE_NONE
// IMPORTANCE_NONE = 0 means blocked/disabled
channelEnabled = importance != android.app.NotificationManager.IMPORTANCE_NONE
Log.d(TAG, "Channel $channelId status: importance=$importance, enabled=$channelEnabled")
} else {
// Channel still doesn't exist after creation attempt - should not happen
Log.w(TAG, "Channel $channelId still doesn't exist after creation attempt")
importance = android.app.NotificationManager.IMPORTANCE_NONE
channelEnabled = false
}
} else {
// Pre-Oreo: channels don't exist, use app-level check
channelEnabled = appNotificationsEnabled
importance = android.app.NotificationManager.IMPORTANCE_DEFAULT
}
val finalEnabled = appNotificationsEnabled && channelEnabled
Log.i(TAG, "Channel status check complete: channelId=$channelId, appNotificationsEnabled=$appNotificationsEnabled, channelEnabled=$channelEnabled, importance=$importance, finalEnabled=$finalEnabled")
val result = JSObject().apply {
put("enabled", enabled)
// Channel is enabled if both app notifications are enabled AND channel importance is not NONE
put("enabled", finalEnabled)
put("channelId", channelId)
put("importance", importance)
put("appNotificationsEnabled", appNotificationsEnabled)
put("channelBlocked", importance == android.app.NotificationManager.IMPORTANCE_NONE)
}
call.resolve(result)
} catch (e: Exception) {
@@ -1074,28 +1126,80 @@ open class DailyNotificationPlugin : Plugin() {
@PluginMethod
fun openChannelSettings(call: PluginCall) {
try {
val channelId = call.getString("channelId") ?: "daily_notification_channel"
val intent = Intent(android.provider.Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply {
putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, context?.packageName)
putExtra(android.provider.Settings.EXTRA_CHANNEL_ID, channelId)
if (context == null) {
return call.reject("Context not available")
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
// Use the actual channel ID that matches what's used in notifications
val channelId = call.getString("channelId") ?: "timesafari.daily"
// Ensure channel exists before trying to open settings
// This ensures the channel-specific settings page can be opened
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? android.app.NotificationManager
val channel = notificationManager?.getNotificationChannel(channelId)
if (channel == null) {
// Channel doesn't exist - create it first
Log.i(TAG, "Channel $channelId doesn't exist, creating it")
val newChannel = android.app.NotificationChannel(
channelId,
"Daily Notifications",
android.app.NotificationManager.IMPORTANCE_HIGH
).apply {
description = "Daily notifications from TimeSafari"
enableLights(true)
enableVibration(true)
setShowBadge(true)
}
notificationManager?.createNotificationChannel(newChannel)
Log.i(TAG, "Channel $channelId created")
}
}
// Try to open channel-specific settings first
try {
activity?.startActivity(intent)
val intent = Intent(android.provider.Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply {
putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, context.packageName)
putExtra(android.provider.Settings.EXTRA_CHANNEL_ID, channelId)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
activity?.startActivity(intent) ?: context.startActivity(intent)
Log.i(TAG, "Channel settings opened for channel: $channelId")
val result = JSObject().apply {
put("opened", true)
put("channelId", channelId)
}
call.resolve(result)
} catch (e: Exception) {
Log.e(TAG, "Failed to start activity", e)
val result = JSObject().apply {
put("opened", false)
put("channelId", channelId)
put("error", e.message)
// Fallback to general app notification settings if channel-specific fails
Log.w(TAG, "Failed to open channel-specific settings, trying app notification settings", e)
try {
val fallbackIntent = Intent(android.provider.Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, context.packageName)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
activity?.startActivity(fallbackIntent) ?: context.startActivity(fallbackIntent)
Log.i(TAG, "App notification settings opened (fallback)")
val result = JSObject().apply {
put("opened", true)
put("channelId", channelId)
put("fallback", true)
put("message", "Opened app notification settings (channel-specific unavailable)")
}
call.resolve(result)
} catch (e2: Exception) {
Log.e(TAG, "Failed to open notification settings", e2)
val result = JSObject().apply {
put("opened", false)
put("channelId", channelId)
put("error", e2.message)
}
call.resolve(result)
}
call.resolve(result)
}
} catch (e: Exception) {
Log.e(TAG, "Failed to open channel settings", e)