diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/alltime/AllTimeWidgetBlockListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/alltime/AllTimeWidgetBlockListViewModel.kt index 131116add589..ac6c71ca7b69 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/alltime/AllTimeWidgetBlockListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/alltime/AllTimeWidgetBlockListViewModel.kt @@ -1,7 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.widget.alltime import android.content.Context -import kotlinx.coroutines.runBlocking import org.wordpress.android.R import org.wordpress.android.fluxc.model.stats.InsightsAllTimeModel import org.wordpress.android.fluxc.store.SiteStore @@ -10,6 +9,7 @@ import org.wordpress.android.ui.prefs.AppPrefsWrapper import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetBlockListProvider.BlockItemUiModel import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetBlockListProvider.WidgetBlockListViewModel import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color +import org.wordpress.android.ui.stats.refresh.lists.widget.utils.runBlockingForWidget import org.wordpress.android.ui.stats.refresh.utils.MILLION import org.wordpress.android.ui.stats.refresh.utils.StatsUtils import org.wordpress.android.viewmodel.ResourceProvider @@ -39,7 +39,7 @@ class AllTimeWidgetBlockListViewModel siteId?.apply { val site = siteStore.getSiteByLocalId(this) if (site != null) { - runBlocking { + runBlockingForWidget { allTimeStore.fetchAllTimeInsights(site) } allTimeStore.getAllTimeInsights(site)?.let { visitsAndViewsModel -> diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/alltime/AllTimeWidgetListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/alltime/AllTimeWidgetListViewModel.kt index 0c26465db1bf..0e1f83265dd3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/alltime/AllTimeWidgetListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/alltime/AllTimeWidgetListViewModel.kt @@ -1,13 +1,13 @@ package org.wordpress.android.ui.stats.refresh.lists.widget.alltime import androidx.annotation.LayoutRes -import kotlinx.coroutines.runBlocking import org.wordpress.android.R import org.wordpress.android.fluxc.model.stats.InsightsAllTimeModel import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.fluxc.store.stats.insights.AllTimeInsightsStore import org.wordpress.android.ui.prefs.AppPrefsWrapper import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color +import org.wordpress.android.ui.stats.refresh.lists.widget.utils.runBlockingForWidget import org.wordpress.android.ui.stats.refresh.utils.ONE_THOUSAND import org.wordpress.android.ui.stats.refresh.utils.StatsUtils import org.wordpress.android.viewmodel.ResourceProvider @@ -36,7 +36,7 @@ class AllTimeWidgetListViewModel siteId?.apply { val site = siteStore.getSiteByLocalId(this) if (site != null) { - runBlocking { + runBlockingForWidget { allTimeStore.fetchAllTimeInsights(site) } allTimeStore.getAllTimeInsights(site)?.let { visitsAndViewsModel -> diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModel.kt index b811c79a9c1a..578aeca13f2a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModel.kt @@ -1,7 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.widget.today import android.content.Context -import kotlinx.coroutines.runBlocking import org.wordpress.android.R import org.wordpress.android.fluxc.model.stats.VisitsModel import org.wordpress.android.fluxc.store.SiteStore @@ -11,6 +10,7 @@ import org.wordpress.android.ui.stats.StatsTimeframe import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetBlockListProvider.BlockItemUiModel import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetBlockListProvider.WidgetBlockListViewModel import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color +import org.wordpress.android.ui.stats.refresh.lists.widget.utils.runBlockingForWidget import org.wordpress.android.ui.stats.refresh.utils.MILLION import org.wordpress.android.ui.stats.refresh.utils.StatsUtils import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig @@ -46,7 +46,7 @@ class TodayWidgetBlockListViewModel siteId?.apply { val site = siteStore.getSiteByLocalId(this) if (site != null) { - runBlocking { + runBlockingForWidget { todayInsightsStore.fetchTodayInsights(site) } todayInsightsStore.getTodayInsights(site)?.let { visitsAndViewsModel -> diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetListViewModel.kt index 9b05b9fad60f..6adca39d4131 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetListViewModel.kt @@ -1,13 +1,13 @@ package org.wordpress.android.ui.stats.refresh.lists.widget.today import androidx.annotation.LayoutRes -import kotlinx.coroutines.runBlocking import org.wordpress.android.R import org.wordpress.android.fluxc.model.stats.VisitsModel import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.fluxc.store.stats.insights.TodayInsightsStore import org.wordpress.android.ui.prefs.AppPrefsWrapper import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color +import org.wordpress.android.ui.stats.refresh.lists.widget.utils.runBlockingForWidget import org.wordpress.android.ui.stats.refresh.utils.ONE_THOUSAND import org.wordpress.android.ui.stats.refresh.utils.StatsUtils import org.wordpress.android.viewmodel.ResourceProvider @@ -36,7 +36,7 @@ class TodayWidgetListViewModel siteId?.let { nonNullSiteId -> val site = siteStore.getSiteByLocalId(nonNullSiteId) if (site != null) { - runBlocking { + runBlockingForWidget { todayInsightsStore.fetchTodayInsights(site) } todayInsightsStore.getTodayInsights(site)?.let { visitsAndViewsModel -> diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/utils/WidgetCoroutineHelper.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/utils/WidgetCoroutineHelper.kt new file mode 100644 index 000000000000..84cb0c22c46f --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/utils/WidgetCoroutineHelper.kt @@ -0,0 +1,28 @@ +package org.wordpress.android.ui.stats.refresh.lists.widget.utils + +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.runBlocking +import org.wordpress.android.util.AppLog + +/** + * Runs [block] via [runBlocking] from inside a `RemoteViewsService.onDataSetChanged()` + * call on a stats widget. Swallows [InterruptedException] / [CancellationException] + * thrown when the widget host kills the service mid-fetch — the VM can still fall + * through to its cached read and render whatever data is already on disk. + * + * Only safe because this is a root [runBlocking] on the widget worker thread with no + * parent [kotlinx.coroutines.Job] above it — there is nothing upstream that needs to + * observe the cancellation. Do NOT call this from inside an existing coroutine: there + * the swallow would hide a cancellation signal from a surviving parent. + */ +internal fun runBlockingForWidget(block: suspend () -> Unit) { + try { + runBlocking { block() } + } catch (_: InterruptedException) { + AppLog.w(AppLog.T.STATS, "Widget data fetch interrupted") + Thread.currentThread().interrupt() + } catch (_: CancellationException) { + AppLog.w(AppLog.T.STATS, "Widget data fetch cancelled") + Thread.currentThread().interrupt() + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/views/ViewsWidgetListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/views/ViewsWidgetListViewModel.kt index 91aa5dbf2d35..b2ebf83dc17e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/views/ViewsWidgetListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/views/ViewsWidgetListViewModel.kt @@ -1,7 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.widget.views import androidx.annotation.LayoutRes -import kotlinx.coroutines.runBlocking import org.wordpress.android.R import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.LimitMode.Top @@ -16,6 +15,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Value import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.OVERVIEW_ITEMS_TO_LOAD import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.OverviewMapper import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color +import org.wordpress.android.ui.stats.refresh.lists.widget.utils.runBlockingForWidget import org.wordpress.android.ui.stats.refresh.utils.MILLION import org.wordpress.android.ui.stats.refresh.utils.ONE_THOUSAND import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter @@ -50,7 +50,7 @@ class ViewsWidgetListViewModel siteId?.apply { val site = siteStore.getSiteByLocalId(this) if (site != null) { - runBlocking { + runBlockingForWidget { visitsAndViewsStore.fetchVisits(site, DAYS, Top(OVERVIEW_ITEMS_TO_LOAD)) } val visitsAndViewsModel = visitsAndViewsStore.getVisits( diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetListViewModel.kt index 121c74fec8b2..b2714a8e5453 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetListViewModel.kt @@ -1,7 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.widget.weeks import androidx.annotation.LayoutRes -import kotlinx.coroutines.runBlocking import org.wordpress.android.R import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.time.VisitsAndViewsModel @@ -10,6 +9,7 @@ import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.fluxc.store.stats.time.VisitsAndViewsStore import org.wordpress.android.ui.prefs.AppPrefsWrapper import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color +import org.wordpress.android.ui.stats.refresh.lists.widget.utils.runBlockingForWidget import org.wordpress.android.ui.stats.refresh.utils.ONE_THOUSAND import org.wordpress.android.ui.stats.refresh.utils.StatsUtils import org.wordpress.android.viewmodel.ResourceProvider @@ -38,7 +38,7 @@ class WeekViewsWidgetListViewModel @Inject constructor( siteId?.let { nonNullSiteId -> val site = siteStore.getSiteByLocalId(nonNullSiteId) if (site != null) { - runBlocking { + runBlockingForWidget { visitsAndViewsStore.fetchVisits(site, WEEKS, LimitMode.Top(1)) } visitsAndViewsStore.getVisits(site, WEEKS, LimitMode.All)?.let { visitsAndViewsModel -> diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekWidgetBlockListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekWidgetBlockListViewModel.kt index 30b198f7dda4..9bd5b7b77ded 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekWidgetBlockListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekWidgetBlockListViewModel.kt @@ -1,7 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.widget.weeks import android.content.Context -import kotlinx.coroutines.runBlocking import org.wordpress.android.R import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.time.VisitsAndViewsModel @@ -12,6 +11,7 @@ import org.wordpress.android.ui.prefs.AppPrefsWrapper import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetBlockListProvider.BlockItemUiModel import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetBlockListProvider.WidgetBlockListViewModel import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color +import org.wordpress.android.ui.stats.refresh.lists.widget.utils.runBlockingForWidget import org.wordpress.android.ui.stats.refresh.utils.MILLION import org.wordpress.android.ui.stats.refresh.utils.StatsUtils import org.wordpress.android.viewmodel.ResourceProvider @@ -46,7 +46,7 @@ class WeekWidgetBlockListViewModel siteId?.let { nonNullSiteId -> val site = siteStore.getSiteByLocalId(nonNullSiteId) if (site != null) { - runBlocking { + runBlockingForWidget { visitsAndViewsStore.fetchVisits(site, WEEKS, LimitMode.Top(1)) } visitsAndViewsStore.getVisits(site, WEEKS, LimitMode.All)?.let { visitsAndViewsModel ->