# Android Notification Implementation Comparison **Test App (Working)** vs **TimeSafari (Not Working)** This document identifies the critical differences between the test app where notifications work correctly and the TimeSafari app where notifications don't work at all. Use this as a checklist to fix TimeSafari. --- ## Critical Issues (Must Fix) ### 1. Missing Custom Application Class **This is likely the primary cause of failure.** **Test App (Working):** ```xml ``` ```java // TestApplication.java public class TestApplication extends Application { @Override public void onCreate() { super.onCreate(); Context context = getApplicationContext(); NativeNotificationContentFetcher testFetcher = new org.timesafari.dailynotification.test.TestNativeFetcher(context); DailyNotificationPlugin.setNativeFetcher(testFetcher); } } ``` **TimeSafari (Broken):** ```xml ``` - No custom Application class exists - No native fetcher is registered - Plugin cannot fetch notification content **Fix Required:** 1. Create `TimeSafariApplication.java` in `android/app/src/main/java/app/timesafari/` 2. Implement `NativeNotificationContentFetcher` specific to TimeSafari 3. Add `android:name=".TimeSafariApplication"` to AndroidManifest.xml --- ### 2. Missing Capacitor Plugin Configuration **Test App (Working):** ```typescript // capacitor.config.ts plugins: { DailyNotification: { debugMode: true, enableNotifications: true, timesafariConfig: { activeDid: "did:ethr:0x...", endpoints: { projectsLastUpdated: "http://..." }, starredProjectsConfig: { enabled: true, starredPlanHandleIds: [...], fetchInterval: '0 8 * * *' }, credentialConfig: { jwtSecret: '...', tokenExpirationMinutes: 1 } }, networkConfig: { timeout: 30000, retryAttempts: 3, retryDelay: 1000 }, contentFetch: { enabled: true, schedule: '0 00 * * *', fetchLeadTimeMinutes: 5 } } } ``` **TimeSafari (Broken):** ```typescript // capacitor.config.ts - NO DailyNotification configuration at all plugins: { App: { ... }, SplashScreen: { ... }, CapSQLite: { ... } // DailyNotification is MISSING } ``` **Fix Required:** Add `DailyNotification` configuration to `capacitor.config.ts` with appropriate values for TimeSafari. --- ### 3. Missing Permissions in AndroidManifest.xml **Test App has these permissions that TimeSafari is missing:** ```xml ``` **Current TimeSafari permissions (incomplete):** - ✅ `INTERNET` - ✅ `POST_NOTIFICATIONS` - ✅ `SCHEDULE_EXACT_ALARM` - ✅ `RECEIVE_BOOT_COMPLETED` - ✅ `WAKE_LOCK` - ❌ `ACCESS_NETWORK_STATE` - **MISSING** - ❌ `FOREGROUND_SERVICE` - **MISSING** - ❌ `SYSTEM_ALERT_WINDOW` - **MISSING** - ❌ `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` - **MISSING** --- ### 4. Missing Gradle Dependencies **Test App (Working):** ```gradle // android/app/build.gradle dependencies { // Capacitor annotation processor for automatic plugin discovery annotationProcessor project(':capacitor-android') // Required dependencies for the plugin implementation 'androidx.work:work-runtime:2.9.0' implementation 'androidx.lifecycle:lifecycle-service:2.7.0' implementation 'com.google.code.gson:gson:2.10.1' } ``` **TimeSafari (Broken):** ```gradle dependencies { // Missing: annotationProcessor project(':capacitor-android') implementation "androidx.work:work-runtime-ktx:2.9.0" // Using Kotlin version // Missing: androidx.lifecycle:lifecycle-service // Missing: com.google.code.gson:gson } ``` **Fix Required:** Add to TimeSafari's `android/app/build.gradle`: ```gradle annotationProcessor project(':capacitor-android') implementation 'androidx.lifecycle:lifecycle-service:2.7.0' implementation 'com.google.code.gson:gson:2.10.1' ``` --- ## Secondary Issues (Should Fix) ### 5. DailyNotificationReceiver Export Status **Test App (Working):** ```xml ``` **TimeSafari (Broken):** ```xml ``` The test app uses `exported="false"` because the plugin creates PendingIntents with explicit component targeting. Using `exported="true"` is unnecessary and a potential security concern. --- ### 6. Missing Network Security Config **Test App (Working):** ```xml ``` **TimeSafari (Broken):** ```xml ``` This may affect HTTP (non-HTTPS) requests during development. --- ### 7. Missing Java Compile Options **Test App (Working):** ```gradle android { compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } } ``` **TimeSafari (Broken):** No explicit compile options set. --- ## Complete Fix Checklist ### Step 1: Create Custom Application Class Create file: `android/app/src/main/java/app/timesafari/TimeSafariApplication.java` ```java package app.timesafari; import android.app.Application; import android.content.Context; import android.util.Log; import org.timesafari.dailynotification.DailyNotificationPlugin; import org.timesafari.dailynotification.NativeNotificationContentFetcher; public class TimeSafariApplication extends Application { private static final String TAG = "TimeSafariApplication"; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "Initializing TimeSafari notifications"); // Register native fetcher with application context Context context = getApplicationContext(); NativeNotificationContentFetcher fetcher = new TimeSafariNativeFetcher(context); DailyNotificationPlugin.setNativeFetcher(fetcher); Log.i(TAG, "Native fetcher registered"); } } ``` ### Step 2: Create Native Fetcher Implementation Create file: `android/app/src/main/java/app/timesafari/TimeSafariNativeFetcher.java` ```java package app.timesafari; import android.content.Context; import org.timesafari.dailynotification.NativeNotificationContentFetcher; import org.timesafari.dailynotification.NotificationContent; public class TimeSafariNativeFetcher implements NativeNotificationContentFetcher { private final Context context; public TimeSafariNativeFetcher(Context context) { this.context = context; } @Override public NotificationContent fetchContent(String scheduleId) { // TODO: Implement actual content fetching for TimeSafari // This should query the TimeSafari API for notification content return new NotificationContent( "timesafari_" + System.currentTimeMillis(), "TimeSafari Update", "Check your starred projects for updates!", System.currentTimeMillis(), null, System.currentTimeMillis() ); } } ``` ### Step 3: Update AndroidManifest.xml ```xml ``` ### Step 4: Update build.gradle Add to `android/app/build.gradle`: ```gradle android { // ... existing config ... compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } } dependencies { // ... existing dependencies ... // ADD these for notification plugin annotationProcessor project(':capacitor-android') implementation 'androidx.lifecycle:lifecycle-service:2.7.0' implementation 'com.google.code.gson:gson:2.10.1' } ``` ### Step 5: Update capacitor.config.ts Add DailyNotification configuration: ```typescript plugins: { // ... existing plugins ... DailyNotification: { debugMode: true, enableNotifications: true, timesafariConfig: { activeDid: '', // Will be set dynamically from user's DID endpoints: { projectsLastUpdated: 'https://api.endorser.ch/api/v2/report/plansLastUpdatedBetween' }, starredProjectsConfig: { enabled: true, starredPlanHandleIds: [], fetchInterval: '0 8 * * *' } }, networkConfig: { timeout: 30000, retryAttempts: 3, retryDelay: 1000 }, contentFetch: { enabled: true, schedule: '0 8 * * *', fetchLeadTimeMinutes: 5 } } } ``` ### Step 6: Rebuild ```bash npx cap sync android cd android && ./gradlew clean cd .. && npx cap build android ``` --- ## Verification After implementing fixes, verify: 1. **Check logs for Application initialization:** ```bash adb logcat | grep -E "TimeSafariApplication|Native fetcher" ``` 2. **Check alarm scheduling:** ```bash adb shell dumpsys alarm | grep -i timesafari ``` 3. **Test receiver manually:** ```bash adb shell am broadcast -a org.timesafari.daily.NOTIFICATION \ --es id "test_notification" \ -n app.timesafari.app/org.timesafari.dailynotification.DailyNotificationReceiver ``` 4. **Check notification permissions:** ```bash adb shell dumpsys package app.timesafari.app | grep -A 5 "granted=true" ``` --- ## Summary of Critical Differences | Component | Test App (Working) | TimeSafari (Broken) | |-----------|-------------------|---------------------| | Custom Application class | ✅ TestApplication.java | ❌ None | | Native fetcher registration | ✅ In Application.onCreate() | ❌ Not registered | | DailyNotification config | ✅ Full config in capacitor.config.ts | ❌ Not configured | | ACCESS_NETWORK_STATE | ✅ Present | ❌ Missing | | FOREGROUND_SERVICE | ✅ Present | ❌ Missing | | REQUEST_IGNORE_BATTERY_OPTIMIZATIONS | ✅ Present | ❌ Missing | | Gson dependency | ✅ Present | ❌ Missing | | lifecycle-service dependency | ✅ Present | ❌ Missing | | Capacitor annotation processor | ✅ Present | ❌ Missing | **The most critical missing piece is the custom Application class with native fetcher registration.** Without this, the plugin has no way to fetch notification content when the alarm fires.