/** * NativeNotificationContentFetcher.java * * Service Provider Interface (SPI) for native content fetchers. * * This interface is part of the Integration Point Refactor (PR1) that allows * host apps to provide their own content fetching logic without hardcoding * TimeSafari-specific code in the plugin. * * Host apps implement this interface in native code (Kotlin/Java) and register * it with the plugin. The plugin calls this interface from background workers * (WorkManager) to fetch notification content. * * @author Matthew Raymer * @version 1.0.0 */ package com.timesafari.dailynotification; import androidx.annotation.NonNull; import java.util.List; import java.util.concurrent.CompletableFuture; /** * Native content fetcher interface for host app implementations * * This interface enables the plugin to call host app's native code for * fetching notification content. This is the ONLY path used by background * workers, as JavaScript bridges are unreliable in background contexts. * * Implementation Requirements: * - Must be thread-safe (may be called from WorkManager background threads) * - Must complete within reasonable time (plugin enforces timeout) * - Should return empty list on failure rather than throwing exceptions * - Should handle errors gracefully and log for debugging * * Example Implementation: *
 * class TimeSafariNativeFetcher implements NativeNotificationContentFetcher {
 *     private final TimeSafariApi api;
 *     private final TokenProvider tokenProvider;
 *     
 *     @Override
 *     public CompletableFuture> fetchContent(
 *             FetchContext context) {
 *         return CompletableFuture.supplyAsync(() -> {
 *             try {
 *                 String jwt = tokenProvider.freshToken();
 *                 // Fetch from TimeSafari API
 *                 // Convert to NotificationContent[]
 *                 return notificationContents;
 *             } catch (Exception e) {
 *                 Log.e("Fetcher", "Fetch failed", e);
 *                 return Collections.emptyList();
 *             }
 *         });
 *     }
 * }
 * 
*/ public interface NativeNotificationContentFetcher { /** * Fetch notification content from external source * * This method is called by the plugin when: * - Background fetch work is triggered (WorkManager) * - Prefetch is scheduled before notification time * - Manual refresh is requested (if native fetcher enabled) * * The plugin will: * - Enforce a timeout (default 30 seconds, configurable via SchedulingPolicy) * - Handle empty lists gracefully (no notifications scheduled) * - Log errors for debugging * - Retry on failure based on SchedulingPolicy * * @param context Context about why fetch was triggered, including * trigger type, scheduled time, and optional metadata * @return CompletableFuture that resolves to list of NotificationContent. * Empty list indicates no content available (not an error). * The future should complete exceptionally only on unrecoverable errors. */ @NonNull CompletableFuture> fetchContent(@NonNull FetchContext context); /** * Optional: Configure the native fetcher with API credentials and settings * *

This method is called by the plugin when {@code configureNativeFetcher} is invoked * from TypeScript. It provides a cross-platform mechanism for passing configuration * from the JavaScript layer to native code without using platform-specific storage * mechanisms.

* *

When to implement:

* * *

When to skip (use default no-op):

* * *

Thread Safety: This method may be called from any thread. Implementations * must be thread-safe if storing configuration in instance variables.

* *

Implementation Pattern:

*
{@code
     * private volatile String apiBaseUrl;
     * private volatile String activeDid;
     * private volatile String jwtSecret;
     * 
     * @Override
     * public void configure(String apiBaseUrl, String activeDid, String jwtSecret) {
     *     this.apiBaseUrl = apiBaseUrl;
     *     this.activeDid = activeDid;
     *     this.jwtSecret = jwtSecret;
     *     Log.i(TAG, "Fetcher configured with API: " + apiBaseUrl);
     * }
     * }
* * @param apiBaseUrl Base URL for API server. Examples: * - Android emulator: "http://10.0.2.2:3000" (maps to host localhost:3000) * - iOS simulator: "http://localhost:3000" * - Production: "https://api.timesafari.com" * @param activeDid Active DID (Decentralized Identifier) for authentication. * Used as the JWT issuer/subject. Format: "did:ethr:0x..." * @param jwtToken Pre-generated JWT token (ES256K signed) from TypeScript. * This token is generated in the host app using TimeSafari's * {@code createEndorserJwtForKey()} function. The native fetcher * should use this token directly in the Authorization header as * "Bearer {jwtToken}". No JWT generation or signing is needed in Java. * * @see DailyNotificationPlugin#configureNativeFetcher(PluginCall) */ default void configure(String apiBaseUrl, String activeDid, String jwtToken) { // Default no-op implementation - fetchers that need config can override // This allows fetchers that don't need TypeScript-provided configuration // to ignore this method without implementing an empty body. } }