Attempt to address Android 16 widget changes

pull/3881/head
Alex Baker 3 months ago
parent 04ab41f622
commit e6bbc8d361

@ -6,12 +6,10 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.media.RingtoneManager
import android.net.Uri import android.net.Uri
import android.os.Binder import android.os.Binder
import androidx.compose.material3.DisplayMode import androidx.compose.material3.DisplayMode
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.core.app.NotificationCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
@ -30,8 +28,6 @@ import org.tasks.extensions.Context.getResourceUri
import org.tasks.kmp.org.tasks.themes.ColorProvider.BLUE_500 import org.tasks.kmp.org.tasks.themes.ColorProvider.BLUE_500
import org.tasks.themes.ThemeBase import org.tasks.themes.ThemeBase
import org.tasks.time.DateTime import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.ONE_WEEK
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.net.URI import java.net.URI

@ -1,12 +1,15 @@
package org.tasks.widget package org.tasks.widget
import android.app.Activity
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.content.BroadcastReceiver
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.tasks.R import org.tasks.R
@ -25,23 +28,45 @@ class AppWidgetManager @Inject constructor(
?.getAppWidgetIds(ComponentName(context, TasksWidget::class.java)) ?.getAppWidgetIds(ComponentName(context, TasksWidget::class.java))
?: intArrayOf() ?: intArrayOf()
fun reconfigureWidgets(vararg appWidgetIds: Int) = scope.launch(Dispatchers.IO) { fun reconfigureWidgets(vararg appWidgetIds: Int) = scope.launch {
Timber.d("reconfigureWidgets(${appWidgetIds.joinToString()})") Timber.d("reconfigureWidgets(${appWidgetIds.joinToString()})")
val ids = appWidgetIds.takeIf { it.isNotEmpty() } ?: widgetIds
val intent = Intent(context, TasksWidget::class.java) val intent = Intent(context, TasksWidget::class.java)
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE .putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
intent.putExtra( .apply { action = AppWidgetManager.ACTION_APPWIDGET_UPDATE }
AppWidgetManager.EXTRA_APPWIDGET_IDS,
appWidgetIds.takeIf { it.isNotEmpty() } ?: widgetIds) context.sendOrderedBroadcast(
context.sendBroadcast(intent) intent,
null,
object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
scope.launch {
Timber.d("Update widgets after reconfigure: ${appWidgetIds.joinToString { it.toString() }}")
// I don't like it, but this seems to give Android enough time to update
// the cache, and I don't have time to rewrite this in Glance right now
delay(100)
notifyAppWidgetViewDataChanged(ids)
}
}
},
null,
Activity.RESULT_OK,
null,
null
)
} }
fun updateWidgets() = scope.launch(Dispatchers.IO) { fun updateWidgets() = scope.launch {
val appWidgetIds = widgetIds val appWidgetIds = widgetIds
Timber.d("updateWidgets: ${appWidgetIds.joinToString()}") Timber.d("updateWidgets: ${appWidgetIds.joinToString { it.toString() }}")
withContext(Dispatchers.Main) { notifyAppWidgetViewDataChanged(appWidgetIds)
appWidgetManager?.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.list_view)
}
} }
fun exists(id: Int) = appWidgetManager?.getAppWidgetInfo(id) != null fun exists(id: Int) = appWidgetManager?.getAppWidgetInfo(id) != null
}
private suspend fun notifyAppWidgetViewDataChanged(appWidgetIds: IntArray) = withContext(Dispatchers.Main) {
appWidgetManager?.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.list_view)
}
}

@ -5,10 +5,10 @@ import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider import android.appwidget.AppWidgetProvider
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.RemoteViews import android.widget.RemoteViews
import androidx.core.net.toUri
import com.todoroo.andlib.utility.AndroidUtilities.atLeastS import com.todoroo.andlib.utility.AndroidUtilities.atLeastS
import com.todoroo.astrid.activity.MainActivity.Companion.FINISH_AFFINITY import com.todoroo.astrid.activity.MainActivity.Companion.FINISH_AFFINITY
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -35,13 +35,11 @@ class TasksWidget : AppWidgetProvider() {
@Inject @ApplicationContext lateinit var context: Context @Inject @ApplicationContext lateinit var context: Context
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
appWidgetIds.forEach { appWidgetId -> Timber.d("onUpdate appWidgetIds=${appWidgetIds.joinToString { it.toString() }}")
appWidgetIds.forEach { id ->
try { try {
val options = appWidgetManager.getAppWidgetOptions(appWidgetId) val options = appWidgetManager.getAppWidgetOptions(id)
appWidgetManager.updateAppWidget( appWidgetManager.updateAppWidget(id, createWidget(context, id, options))
appWidgetId,
createWidget(context, appWidgetId, options)
)
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
} }
@ -54,10 +52,12 @@ class TasksWidget : AppWidgetProvider() {
appWidgetId: Int, appWidgetId: Int,
newOptions: Bundle newOptions: Bundle
) { ) {
Timber.d("onAppWidgetOptionsChanged appWidgetId=$appWidgetId")
appWidgetManager.updateAppWidget( appWidgetManager.updateAppWidget(
appWidgetId, appWidgetId,
createWidget(context, appWidgetId, newOptions) createWidget(context, appWidgetId, newOptions)
) )
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.list_view)
} }
private fun createWidget(context: Context, id: Int, options: Bundle): RemoteViews { private fun createWidget(context: Context, id: Int, options: Bundle): RemoteViews {
@ -69,6 +69,8 @@ class TasksWidget : AppWidgetProvider() {
val filter = runBlocking { val filter = runBlocking {
defaultFilterProvider.getFilterFromPreference(widgetPreferences.filterId) defaultFilterProvider.getFilterFromPreference(widgetPreferences.filterId)
} }
Timber.d("createWidget id=$id filter=$filter")
return RemoteViews(context.packageName, R.layout.scrollable_widget).apply { return RemoteViews(context.packageName, R.layout.scrollable_widget).apply {
if (settings.showHeader) { if (settings.showHeader) {
setViewVisibility(R.id.widget_header, View.VISIBLE) setViewVisibility(R.id.widget_header, View.VISIBLE)
@ -87,11 +89,12 @@ class TasksWidget : AppWidgetProvider() {
opacity = widgetPreferences.footerOpacity, opacity = widgetPreferences.footerOpacity,
) )
setOnClickPendingIntent(R.id.empty_view, getOpenListIntent(context, filter, id)) setOnClickPendingIntent(R.id.empty_view, getOpenListIntent(context, filter, id))
val cacheBuster = Uri.parse("tasks://widget/" + currentTimeMillis()) val cacheBuster = "tasks://widget/${currentTimeMillis()}".toUri()
setRemoteAdapter( setRemoteAdapter(
R.id.list_view, R.id.list_view,
Intent(context, TasksWidgetAdapter::class.java) Intent(context, TasksWidgetAdapter::class.java)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id) .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id)
.putExtra("extra_cache_buster", cacheBuster)
.setData(cacheBuster) .setData(cacheBuster)
) )
setPendingIntentTemplate(R.id.list_view, getPendingIntentTemplate(context)) setPendingIntentTemplate(R.id.list_view, getPendingIntentTemplate(context))
@ -232,4 +235,4 @@ class TasksWidget : AppWidgetProvider() {
private const val COMPACT_MAX = 275 private const val COMPACT_MAX = 275
private const val FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP private const val FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
} }
} }

@ -13,6 +13,7 @@ import org.tasks.markdown.MarkdownProvider
import org.tasks.preferences.DefaultFilterProvider import org.tasks.preferences.DefaultFilterProvider
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.tasklist.HeaderFormatter import org.tasks.tasklist.HeaderFormatter
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -32,6 +33,7 @@ class TasksWidgetAdapter : RemoteViewsService() {
val filter = runBlocking { val filter = runBlocking {
defaultFilterProvider.getFilterFromPreference(widgetPreferences.filterId) defaultFilterProvider.getFilterFromPreference(widgetPreferences.filterId)
} }
Timber.d("onGetViewFactory $filter")
return TasksWidgetViewFactory( return TasksWidgetViewFactory(
subtasksHelper, subtasksHelper,
widgetPreferences, widgetPreferences,

@ -66,9 +66,12 @@ internal class TasksWidgetViewFactory(
chipProvider.isDark = settings.isDark chipProvider.isDark = settings.isDark
} }
override fun onCreate() {} override fun onCreate() {
Timber.d("onCreate widgetId:$widgetId filter:$filter")
}
override fun onDataSetChanged() { override fun onDataSetChanged() {
Timber.v("onDataSetChanged $filter")
runBlocking { runBlocking {
val collapsed = widgetPreferences.collapsed val collapsed = widgetPreferences.collapsed
tasks = SectionedDataSource( tasks = SectionedDataSource(
@ -87,7 +90,9 @@ internal class TasksWidgetViewFactory(
} }
} }
override fun onDestroy() {} override fun onDestroy() {
Timber.d("onDestroy widgetId:$widgetId")
}
override fun getCount() = tasks.size.coerceAtMost(taskLimit) override fun getCount() = tasks.size.coerceAtMost(taskLimit)

Loading…
Cancel
Save