You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
6.5 KiB
153 lines
6.5 KiB
/**
|
|
* TimeSafari Native Fetcher Implementation (Android/Kotlin)
|
|
*
|
|
* Example implementation of NativeNotificationContentFetcher for Android.
|
|
* This runs in background workers and does NOT require JavaScript bridge.
|
|
*
|
|
* @author Matthew Raymer
|
|
* @version 1.0.0
|
|
*/
|
|
|
|
package com.timesafari.notification
|
|
|
|
import com.timesafari.dailynotification.*
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.withContext
|
|
|
|
/**
|
|
* TimeSafari native content fetcher
|
|
*
|
|
* Implements the NativeNotificationContentFetcher SPI to provide
|
|
* TimeSafari-specific notification content fetching for background workers.
|
|
*/
|
|
class TimeSafariNativeFetcher(
|
|
private val api: TimeSafariApi,
|
|
private val tokenProvider: TokenProvider,
|
|
private val starredPlanIds: List<String>
|
|
) : NativeNotificationContentFetcher {
|
|
|
|
override suspend fun fetchContent(context: FetchContext): List<NotificationContent> {
|
|
return withContext(Dispatchers.IO) {
|
|
try {
|
|
// 1. Get fresh authentication token
|
|
val jwt = tokenProvider.freshToken()
|
|
|
|
// 2. Fetch TimeSafari data in parallel
|
|
val offersToPerson = async { api.fetchOffersToPerson(jwt) }
|
|
val offersToPlans = async { api.fetchOffersToPlans(jwt) }
|
|
val projectUpdates = async {
|
|
if (starredPlanIds.isNotEmpty()) {
|
|
api.fetchProjectsLastUpdated(starredPlanIds, jwt)
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
|
|
// Wait for all requests
|
|
val offersPersonResult = offersToPerson.await()
|
|
val offersPlansResult = offersToPlans.await()
|
|
val updatesResult = projectUpdates.await()
|
|
|
|
// 3. Convert to NotificationContent list
|
|
buildList {
|
|
// Add offers to person
|
|
offersPersonResult?.data?.forEach { offer ->
|
|
add(
|
|
NotificationContent(
|
|
id = "offer_person_${offer.id}",
|
|
title = "New Offer: ${offer.title}",
|
|
body = offer.description ?: "",
|
|
scheduledTime = context.scheduledTime
|
|
?: (System.currentTimeMillis() + 3_600_000),
|
|
fetchTime = context.fetchTime,
|
|
mediaUrl = offer.imageUrl,
|
|
ttlSeconds = 86_400, // 24 hours
|
|
dedupeKey = "offer_person_${offer.id}_${offer.updatedAt}",
|
|
priority = "default",
|
|
metadata = mapOf(
|
|
"offerId" to offer.id,
|
|
"issuerDid" to offer.issuerDid,
|
|
"source" to "offers_to_person"
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
// Add offers to plans
|
|
offersPlansResult?.data?.forEach { offer ->
|
|
add(
|
|
NotificationContent(
|
|
id = "offer_plan_${offer.id}",
|
|
title = "Offer for Your Project: ${offer.projectName}",
|
|
body = offer.description ?: "",
|
|
scheduledTime = context.scheduledTime
|
|
?: (System.currentTimeMillis() + 3_600_000),
|
|
fetchTime = context.fetchTime,
|
|
mediaUrl = offer.imageUrl,
|
|
ttlSeconds = 86_400,
|
|
dedupeKey = "offer_plan_${offer.id}_${offer.updatedAt}",
|
|
priority = "default",
|
|
metadata = mapOf(
|
|
"offerId" to offer.id,
|
|
"planHandleId" to offer.planHandleId,
|
|
"source" to "offers_to_plans"
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
// Add project updates
|
|
updatesResult?.data?.forEach { update ->
|
|
add(
|
|
NotificationContent(
|
|
id = "project_${update.planSummary.jwtId}",
|
|
title = "${update.planSummary.name} Updated",
|
|
body = "New updates for ${update.planSummary.name}",
|
|
scheduledTime = context.scheduledTime
|
|
?: (System.currentTimeMillis() + 3_600_000),
|
|
fetchTime = context.fetchTime,
|
|
mediaUrl = update.planSummary.image,
|
|
ttlSeconds = 86_400,
|
|
dedupeKey = "project_${update.planSummary.handleId}_${update.planSummary.jwtId}",
|
|
priority = "default",
|
|
metadata = mapOf(
|
|
"planHandleId" to update.planSummary.handleId,
|
|
"jwtId" to update.planSummary.jwtId,
|
|
"source" to "project_updates"
|
|
)
|
|
)
|
|
)
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
// Log error and return empty list
|
|
// Plugin will handle retry based on SchedulingPolicy
|
|
android.util.Log.e("TimeSafariFetcher", "Fetch failed", e)
|
|
emptyList()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registration in Application.onCreate()
|
|
*/
|
|
class TimeSafariApplication : Application() {
|
|
override fun onCreate() {
|
|
super.onCreate()
|
|
|
|
// Register native fetcher
|
|
val api = TimeSafariApiClient(context = this)
|
|
val tokenProvider = JWTokenProvider(activeDid = getActiveDid())
|
|
val starredPlanIds = getStarredPlanIds()
|
|
|
|
val fetcher = TimeSafariNativeFetcher(
|
|
api = api,
|
|
tokenProvider = tokenProvider,
|
|
starredPlanIds = starredPlanIds
|
|
)
|
|
|
|
DailyNotification.setNativeFetcher(fetcher)
|
|
}
|
|
}
|
|
|
|
|