Skip to main content
Glama

dhis2_android_setup_sync

Configure offline-first data synchronization for DHIS2 Android apps by setting sync strategies, scope, conflict resolution, and network conditions.

Instructions

Configure offline-first data synchronization patterns for DHIS2 Android app

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
syncStrategyYesData synchronization strategy
syncScopeNo
conflictResolutionNoStrategy for resolving sync conflicts
networkConditionsNo
progressTrackingNoInclude sync progress tracking UI

Implementation Reference

  • The core handler function `generateSyncConfiguration` that implements the tool logic. It takes configuration parameters and generates comprehensive Kotlin code for DHIS2 Android app synchronization, including SyncManager, background WorkManager jobs, network monitoring, conflict resolution strategies, and progress tracking.
    export function generateSyncConfiguration(args: any): string { const { syncStrategy, syncScope, conflictResolution, networkConditions, progressTracking } = args; return `# DHIS2 Android Sync Configuration ## Overview This configuration sets up ${syncStrategy} synchronization with ${conflictResolution} conflict resolution. ## Sync Manager Setup \`\`\`kotlin class SyncManager @Inject constructor( private val d2: D2, private val networkManager: NetworkManager, private val syncPreferences: SyncPreferences ) { suspend fun performSync(syncType: SyncType = SyncType.FULL): SyncResult { return when (syncStrategy) { SyncStrategy.MANUAL -> performManualSync(syncType) SyncStrategy.AUTOMATIC -> performAutomaticSync() SyncStrategy.SCHEDULED -> scheduleSync() SyncStrategy.SMART -> performSmartSync() } } private suspend fun performManualSync(syncType: SyncType): SyncResult { if (!canSync()) return SyncResult.Failed("Network conditions not met") return try { ${syncScope.metadata ? 'syncMetadata()' : ''} ${syncScope.dataValues ? 'syncDataValues()' : ''} ${syncScope.events ? 'syncEvents()' : ''} ${syncScope.enrollments ? 'syncEnrollments()' : ''} SyncResult.Success } catch (e: Exception) { handleSyncError(e) } } private suspend fun syncMetadata() { ${progressTracking ? 'updateProgress("Syncing metadata...", 10)' : ''} d2.metadataModule().download().blockingDownload() } private suspend fun syncDataValues() { ${progressTracking ? 'updateProgress("Syncing data values...", 40)' : ''} d2.dataValueModule().dataValueUploader().blockingUpload() } private suspend fun syncEvents() { ${progressTracking ? 'updateProgress("Syncing events...", 70)' : ''} d2.trackerModule().trackedEntityInstances().blockingUpload() d2.eventModule().events().blockingUpload() } private suspend fun syncEnrollments() { ${progressTracking ? 'updateProgress("Syncing enrollments...", 90)' : ''} d2.enrollmentModule().enrollments().blockingUpload() } private fun canSync(): Boolean { ${networkConditions.wifiOnly ? 'if (!networkManager.isWiFiConnected()) return false' : ''} if (!networkManager.isConnected()) return false return true } private fun handleConflict(conflict: ImportConflict): ConflictResolution { return when (conflictResolution) { ConflictResolution.SERVER_WINS -> ConflictResolution.SERVER_WINS ConflictResolution.CLIENT_WINS -> ConflictResolution.CLIENT_WINS ConflictResolution.MERGE -> mergeConflict(conflict) ConflictResolution.USER_PROMPT -> promptUserForResolution(conflict) } } } sealed class SyncResult { object Success : SyncResult() data class Failed(val error: String) : SyncResult() data class PartialSuccess(val details: String) : SyncResult() } enum class SyncStrategy { MANUAL, AUTOMATIC, SCHEDULED, SMART } \`\`\` ## Background Sync Service \`\`\`kotlin @HiltWorker class SyncWorker @AssistedInject constructor( @Assisted context: Context, @Assisted workerParams: WorkerParameters, private val syncManager: SyncManager ) : CoroutineWorker(context, workerParams) { override suspend fun doWork(): Result { return try { val result = syncManager.performSync() when (result) { is SyncResult.Success -> Result.success() is SyncResult.Failed -> Result.retry() is SyncResult.PartialSuccess -> Result.success() } } catch (e: Exception) { Result.failure() } } @AssistedFactory interface Factory { fun create(context: Context, params: WorkerParameters): SyncWorker } } // Schedule periodic sync class SyncScheduler @Inject constructor( private val workManager: WorkManager ) { fun schedulePeriodicSync() { val constraints = Constraints.Builder() ${networkConditions.wifiOnly ? '.setRequiredNetworkType(NetworkType.UNMETERED)' : '.setRequiredNetworkType(NetworkType.CONNECTED)'} ${networkConditions.backgroundSync ? '' : '.setRequiresBatteryNotLow(true)'} .build() val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>( repeatInterval = 15, // minutes repeatIntervalTimeUnit = TimeUnit.MINUTES ).setConstraints(constraints) .build() workManager.enqueueUniquePeriodicWork( "sync_work", ExistingPeriodicWorkPolicy.KEEP, syncRequest ) } } \`\`\` ## Network Monitoring \`\`\`kotlin @Singleton class NetworkManager @Inject constructor( @ApplicationContext private val context: Context ) { private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager fun isConnected(): Boolean { val network = connectivityManager.activeNetwork ?: return false val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) } fun isWiFiConnected(): Boolean { val network = connectivityManager.activeNetwork ?: return false val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) } fun getNetworkType(): NetworkType { val network = connectivityManager.activeNetwork ?: return NetworkType.NONE val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return NetworkType.NONE return when { capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> NetworkType.WIFI capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> NetworkType.CELLULAR else -> NetworkType.OTHER } } } enum class NetworkType { NONE, WIFI, CELLULAR, OTHER } \`\`\` ${progressTracking ? generateProgressTracking() : ''} ## Usage Example \`\`\`kotlin class MainActivity : AppCompatActivity() { @Inject lateinit var syncManager: SyncManager @Inject lateinit var syncScheduler: SyncScheduler override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Schedule background sync syncScheduler.schedulePeriodicSync() // Manual sync button binding.syncButton.setOnClickListener { lifecycleScope.launch { val result = syncManager.performSync() handleSyncResult(result) } } } private fun handleSyncResult(result: SyncResult) { when (result) { is SyncResult.Success -> { Toast.makeText(this, "Sync completed successfully", Toast.LENGTH_SHORT).show() } is SyncResult.Failed -> { Toast.makeText(this, "Sync failed: \${result.error}", Toast.LENGTH_LONG).show() } is SyncResult.PartialSuccess -> { Toast.makeText(this, "Partial sync: \${result.details}", Toast.LENGTH_SHORT).show() } } } } \`\`\` ## Configuration Summary - **Strategy**: ${syncStrategy} - **Scope**: ${Object.entries(syncScope).filter(([_, enabled]) => enabled).map(([key]) => key).join(', ')} - **Conflict Resolution**: ${conflictResolution} - **Network**: ${networkConditions.wifiOnly ? 'WiFi only' : 'Any connection'}${networkConditions.backgroundSync ? ', Background sync enabled' : ''} - **Progress Tracking**: ${progressTracking ? 'Enabled' : 'Disabled'} ${networkConditions.chunkSize ? `- **Chunk Size**: ${networkConditions.chunkSize}KB` : ''} `; }
  • src/index.ts:1261-1271 (registration)
    Tool registration in the main MCP server request handler. Dispatches calls to the `dhis2_android_setup_sync` tool by invoking the imported `generateSyncConfiguration` handler function from android-generators.ts.
    case 'dhis2_android_setup_sync': const syncArgs = args as any; const syncConfig = generateSyncConfiguration(syncArgs); return { content: [ { type: 'text', text: syncConfig, }, ], };
  • Permission registration: Maps the tool to the 'canConfigureMobile' permission in the TOOL_PERMISSIONS Map, used by PermissionSystem.filterToolsByPermissions to control access.
    ['dhis2_android_setup_sync', 'canConfigureMobile'],

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Dradebo/dhis2-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server