Files
crowd-funder-for-time-pwa/doc/plugin-feedback-android-parseUserNotificationConfig-optional-fields.md
Jose Olarte III e121db5fcf fix(notifications): align dual schedule config with Android plugin + bump DNP
- buildDualScheduleConfig: set contentFetch timeout/retryAttempts/retryDelay
  (match capacitor DailyNotification networkConfig), userNotification.vibration,
  return type DualScheduleConfiguration
- @timesafari/daily-notification-plugin 2.1.1 → 2.1.3 (package-lock)
- doc: plugin feedback (contentFetch JSON, parseUserNotificationConfig optional
  fields) and Android DailyNotificationWorker duplicate scheduleId note
2026-03-20 21:13:50 +08:00

5.5 KiB

Plugin feedback: Android parseUserNotificationConfig — optional fields vs getBoolean / getString

Date: 2026-03-20 21:11 PST
Target repo: @timesafari/daily-notification-plugin (daily-notification-plugin)
Consuming app: crowd-funder-for-time-pwa (TimeSafari)
Platform: Android (Kotlin)
Related: Same class of issue as plugin-feedback-android-scheduleDualNotification-contentFetch-json.md (contentFetch / parseContentFetchConfig).


Summary

DailyNotificationPlugin.parseUserNotificationConfig() uses JSObject / JSONObject strict getters for fields that the published TypeScript UserNotificationConfig marks as optional (sound?, vibration?, priority?, title?, body?). If a key is omitted, Android throws JSONException (e.g. No value for vibration), and scheduleDualNotification fails before scheduling.

Recommended direction (plugin): Align Kotlin parsing with dist/esm/definitions.d.ts by using optional reads + defaults, consistent with the fix already applied for parseContentFetchConfig (e.g. optIntOrNull, or Capacitor/JSON equivalents for booleans and strings).

Recommended direction (app / already done in TimeSafari): Send explicit sound, vibration, and priority (and title/body) in buildDualScheduleConfig() so older plugin builds that still use strict getters continue to work.

Does it make sense to change both sides? Yes — same reasoning as for contentFetch: the plugin should match its public contract; the app can stay explicit for compatibility and clarity.


Symptoms (consuming app)

  • In-app toast: “Could not schedule New Activity notification. Please try again.” (generic catch after scheduleDualNotification rejects.)
  • Logcat:
E DNP-PLUGIN: Schedule dual notification error
E DNP-PLUGIN: org.json.JSONException: No value for vibration
E DNP-PLUGIN:   at org.json.JSONObject.getBoolean(JSONObject.java:419)
E DNP-PLUGIN:   at org.timesafari.dailynotification.DailyNotificationPlugin.parseUserNotificationConfig(DailyNotificationPlugin.kt:2428)
E DNP-PLUGIN:   at org.timesafari.dailynotification.DailyNotificationPlugin.scheduleDualNotification(DailyNotificationPlugin.kt:1392)

(First failure observed after contentFetch timeouts were fixed was vibration; the same pattern can affect sound or priority if those keys are omitted.)


Root cause

Published TypeScript contract (UserNotificationConfig)

From definitions.d.ts (representative):

  • title?, body?, sound?, vibration?, priority? — all optional.

Current Android implementation (strict)

In DailyNotificationPlugin.kt, parseUserNotificationConfig (line numbers approximate; search for parseUserNotificationConfig):

private fun parseUserNotificationConfig(configJson: JSObject): UserNotificationConfig {
    return UserNotificationConfig(
        enabled = configJson.getBoolean("enabled") ?: true,
        schedule = configJson.getString("schedule") ?: "0 9 * * *",
        title = configJson.getString("title"),
        body = configJson.getString("body"),
        sound = configJson.getBoolean("sound"),
        vibration = configJson.getBoolean("vibration"),
        priority = configJson.getString("priority")
    )
}
  • getBoolean("vibration") (and getBoolean("sound")) throw if the key is missing — optional in TS, required at runtime on Android.
  • getString("title"), getString("body"), getString("priority") likewise throw if missing (depending on JSObject / JSONObject behavior for absent keys).

So minimal or TS-faithful payloads omit vibration → immediate JSONException.


Plugin-side recommendations

  1. Treat UserNotificationConfig optional fields as optional on Android, mirroring definitions.d.ts:

    • vibration: e.g. optBoolean / nullable + default true (or false if that matches product default — document the default).
    • sound: same pattern; default true is typical for notifications.
    • priority: optional string with default "normal" (or map from TS union).
    • title / body: if TS allows omission, use optional reads + defaults consistent with dual-schedule UX (or reject with a clear call.reject message instead of a raw JSONException).
  2. Reuse the same helper style as parseContentFetchConfig after the timeout fix (optIntOrNull, etc.) so one codebase convention applies to all dual-schedule JSON parsing.

  3. Tests: Unit or integration test that calls scheduleDualNotification with a minimal userNotification object (only what TS strictly requires, if anything) and asserts scheduling succeeds on Android.

  4. iOS parity: If iOS already accepts omitted vibration / sound, Android should match; if not, align both platforms to the same UserNotificationConfig rules.


App-side note (TimeSafari)

src/services/notifications/dualScheduleConfig.tsbuildDualScheduleConfig() now includes vibration: true (with sound: true) so current native code paths succeed. Keeping this explicit is still recommended even after the plugin is fixed.


References

  • Plugin: android/.../DailyNotificationPlugin.ktparseUserNotificationConfig
  • TS: dist/esm/definitions.d.tsUserNotificationConfig, DualScheduleConfiguration
  • App: src/services/notifications/dualScheduleConfig.tsbuildDualScheduleConfig