package com.atridad.openclimb.widget import android.app.PendingIntent import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetProvider import android.content.ComponentName import android.content.Context import android.content.Intent import android.widget.RemoteViews import com.atridad.openclimb.MainActivity import com.atridad.openclimb.R import com.atridad.openclimb.data.database.OpenClimbDatabase import com.atridad.openclimb.data.repository.ClimbRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch class ClimbStatsWidgetProvider : AppWidgetProvider() { private val job = SupervisorJob() private val coroutineScope = CoroutineScope(Dispatchers.IO + job) override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { for (appWidgetId in appWidgetIds) { updateAppWidget(context, appWidgetManager, appWidgetId) } } override fun onEnabled(context: Context) {} override fun onDisabled(context: Context) { job.cancel() } private fun updateAppWidget( context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int ) { coroutineScope.launch { try { val database = OpenClimbDatabase.getDatabase(context) val repository = ClimbRepository(database, context) // Fetch stats data val sessions = repository.getAllSessions().first() val problems = repository.getAllProblems().first() val attempts = repository.getAllAttempts().first() val gyms = repository.getAllGyms().first() val activeSession = repository.getActiveSession() // Calculate stats val completedSessions = sessions.filter { it.endTime != null } // Count problems that have been completed (have at least one successful attempt) val completedProblems = problems .filter { problem -> attempts.any { attempt -> attempt.problemId == problem.id && (attempt.result == com.atridad.openclimb.data.model .AttemptResult.SUCCESS || attempt.result == com.atridad.openclimb.data.model .AttemptResult.FLASH) } } .size val favoriteGym = sessions.groupBy { it.gymId }.maxByOrNull { it.value.size }?.let { (gymId, _) -> gyms.find { it.id == gymId }?.name } ?: "No sessions yet" launch(Dispatchers.Main) { val views = RemoteViews(context.packageName, R.layout.widget_climb_stats) views.setTextViewText( R.id.widget_total_sessions, completedSessions.size.toString() ) views.setTextViewText( R.id.widget_problems_completed, completedProblems.toString() ) views.setTextViewText(R.id.widget_total_problems, problems.size.toString()) views.setTextViewText(R.id.widget_favorite_gym, favoriteGym) val intent = Intent(context, MainActivity::class.java) val pendingIntent = PendingIntent.getActivity( context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) views.setOnClickPendingIntent(R.id.widget_container, pendingIntent) appWidgetManager.updateAppWidget(appWidgetId, views) } } catch (e: Exception) { launch(Dispatchers.Main) { val views = RemoteViews(context.packageName, R.layout.widget_climb_stats) views.setTextViewText(R.id.widget_total_sessions, "0") views.setTextViewText(R.id.widget_problems_completed, "0") views.setTextViewText(R.id.widget_total_problems, "0") views.setTextViewText(R.id.widget_favorite_gym, "No data") val intent = Intent(context, MainActivity::class.java) val pendingIntent = PendingIntent.getActivity( context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) views.setOnClickPendingIntent(R.id.widget_container, pendingIntent) appWidgetManager.updateAppWidget(appWidgetId, views) } } } } companion object { fun updateAllWidgets(context: Context) { val appWidgetManager = AppWidgetManager.getInstance(context) val componentName = ComponentName(context, ClimbStatsWidgetProvider::class.java) val appWidgetIds = appWidgetManager.getAppWidgetIds(componentName) val intent = Intent(context, ClimbStatsWidgetProvider::class.java).apply { action = AppWidgetManager.ACTION_APPWIDGET_UPDATE putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds) } context.sendBroadcast(intent) } } }