diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt index b81f0559983f..13f83da1d942 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt @@ -19,6 +19,8 @@ package com.ichi2.anki import android.app.Activity import android.app.Application +import android.appwidget.AppWidgetManager +import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.SharedPreferences @@ -34,6 +36,7 @@ import androidx.core.content.edit import androidx.fragment.app.FragmentActivity import androidx.lifecycle.MutableLiveData import androidx.work.Configuration +import anki.collection.OpChanges import com.ichi2.anki.CrashReportService.sendExceptionReport import com.ichi2.anki.analytics.UsageAnalytics import com.ichi2.anki.browser.SharedPreferencesLastDeckIdRepository @@ -53,11 +56,14 @@ import com.ichi2.anki.services.NotificationService import com.ichi2.anki.ui.dialogs.ActivityAgnosticDialogs import com.ichi2.annotations.NeedsTest import com.ichi2.compat.CompatHelper +import com.ichi2.libanki.ChangeManager import com.ichi2.utils.AdaptionUtil import com.ichi2.utils.ExceptionUtil import com.ichi2.utils.KotlinCleanup import com.ichi2.utils.LanguageUtil import com.ichi2.utils.Permissions +import com.ichi2.widget.DeckPickerWidget +import com.ichi2.widget.WidgetPreferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -117,6 +123,8 @@ open class AnkiDroidApp : Application(), Configuration.Provider { // Get preferences val preferences = this.sharedPrefs() + ChangeSubscriber.initialize(this) + CrashReportService.initialize(this) val logType = LogType.value when (logType) { @@ -280,6 +288,81 @@ open class AnkiDroidApp : Application(), Configuration.Provider { } } + /** + * `ChangeSubscriber` is a singleton object that subscribes to changes in the `ChangeManager` + * and updates the Deck Picker Widgets when relevant changes occur. + * + * This object is designed to be initialized with an `Application` context, allowing it + * to manage widget updates efficiently across the application lifecycle. + */ + object ChangeSubscriber : ChangeManager.Subscriber { + + /** + * Application context for accessing resources and system services. + */ + private lateinit var application: Application + + /** + * Initializes the `ChangeSubscriber` with the provided [app] context and subscribes to + * the `ChangeManager`. This method must be called before any changes are detected. + * + * @param app The application context used to manage widget updates. + */ + fun initialize(app: Application) { + Timber.d("Initializing ChangeSubscriber") + application = app + ChangeManager.subscribe(this) + } + + /** + * Callback method invoked when operations that affect the app state are executed. + * If relevant changes related to the study queues are detected, the Deck Picker Widgets + * are updated accordingly. + * + * @param changes The set of changes that occurred. + * @param handler An optional handler that can be used for custom processing (unused here). + */ + override fun opExecuted(changes: OpChanges, handler: Any?) { + Timber.d("ChangeSubscriber - opExecuted called with changes: $changes") + if (changes.studyQueues) { + updateDeckPickerWidgets() + } else { + Timber.d("No relevant changes to update the widget") + } + } + + /** + * Updates the Deck Picker Widgets based on the current state of the application. + * It fetches the App Widget IDs and updates each widget with the associated deck IDs. + */ + private fun updateDeckPickerWidgets() { + val context = application.applicationContext + val appWidgetManager = AppWidgetManager.getInstance(context) + + val provider = ComponentName(context, DeckPickerWidget::class.java) + Timber.d("Fetching appWidgetIds for provider: $provider") + + val appWidgetIds = appWidgetManager.getAppWidgetIds(provider) + Timber.d("AppWidgetIds to update: ${appWidgetIds.joinToString(", ")}") + + for (appWidgetId in appWidgetIds) { + val deckIds = fetchDeckIdsForWidget(appWidgetId) + DeckPickerWidget.updateWidget(context, appWidgetManager, appWidgetId, deckIds) + } + } + + /** + * Fetches the selected deck IDs for the specified [appWidgetId] from the widget preferences. + * + * @param appWidgetId The ID of the widget for which to fetch the deck IDs. + * @return A `LongArray` of deck IDs associated with the widget. + */ + private fun fetchDeckIdsForWidget(appWidgetId: Int): LongArray { + val widgetPreferences = WidgetPreferences(application.applicationContext) + return widgetPreferences.getSelectedDeckIdsFromPreferencesDeckPickerWidget(appWidgetId) + } + } + companion object { /**