Menu
Who Do Is
  • Home
  • What
  • How
  • Is
  • Can
  • Are
  • Does
  • Do
  • Why
  • Who
  • Where
  • Which
  • Which
  • Should
  • Will
  • When
  • What’s
  • Did
Who Do Is

[ANSWERED] android – Registering a ContentObserver in AppWidgetProvider to update AppWidget is not working

Posted on November 14, 2022

Solution 1 :

What could be the reason it’s not working as expected here?

An AppWidgetProvider is a subclass of BroadcastReceiver. Your instance of AppWidgetProvider will live for (hopefully) a very short time, best measured in milliseconds. Basically, you get one onUpdate() call (or other callback), and that instance is thrown away. The next callback gets a new instance.

As a result, doing anything in an AppWidgetProvider that requires it to be around for a period of time is doomed.

The most likely solution, taking modern versions of Android into account, is to adopt more of a push solution. Bear in mind that any of your code can update the RemoteViews for an app widget, simply by working with AppWidgetManager. So, some code that is already running and knows about the data updates needs to push a new RemoteViews, rather than expecting your AppWidgetProvider to be able to react to changes.

Problem :

I have a ContentProvider from a main app. The content will be shared with a consumer app. This consumer app has an app widget. I have tested the ContentProvider and ContentObserver to this consumer app in its Activity and all is well (meaning the RecyclerView of the Activity is updated whenever an update from the main app triggers changes to the database). However, registering the ContentObserver inside my AppWidgetProvider does not work as expected.

My AppWidgetProvider has the following code.

class StackWidgetProvider : AppWidgetProvider() {
    override fun onEnabled(context: Context) {
        Timber.i("Enabled")
        if (favoriteUserProviderObserver == null) {
            val appWidgetManager = AppWidgetManager.getInstance(context)
            val componentName = ComponentName(context, StackWidgetProvider::class.java)
            favoriteUserProviderObserver = FavoriteUserProviderObserver(appWidgetManager, componentName).let {
                context.contentResolver.registerContentObserver(CONTENT_URI, true, it)
                it
            }
        }
    }

    override fun onDisabled(context: Context) {
        Timber.i("Disabled")
        favoriteUserProviderObserver?.let {
            context.contentResolver.unregisterContentObserver(it)
        }
        favoriteUserProviderObserver = null
    }

    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        for (appWidgetId in appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId)
        }
    }
    ....

    companion object {
        private var favoriteUserProviderObserver: FavoriteUserProviderObserver? = null

        private fun updateAppWidget(
            context: Context,
            appWidgetManager: AppWidgetManager,
            appWidgetId: Int
        ) {
            val intent = Intent(context, StackWidgetService::class.java).apply {
                putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
                data = toUri(Intent.URI_INTENT_SCHEME).toUri()
            }

            val views = RemoteViews(context.packageName, R.layout.widget_favorite_user_stack).apply {
                setRemoteAdapter(R.id.widget_favorite_user_stack_view, intent)
                setEmptyView(R.id.widget_favorite_user_stack_view, R.id.widget_favorite_user_empty)
            }

            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }
}

I created a simple custom ContentObserver class like below.

class FavoriteUserProviderObserver(
    private val appWidgetManager: AppWidgetManager,
    private val componentName: ComponentName
) : ContentObserver(null) {
    override fun onChange(selfChange: Boolean) {
        Timber.i("Provider observer triggered")
        appWidgetManager.notifyAppWidgetViewDataChanged(
            appWidgetManager.getAppWidgetIds(componentName), R.id.widget_favorite_user_stack_view
        )
    }
}

The above observer class is never triggered (even when I change the data in my main app). For further clarity, here’s the code for my RemoteViewsService and its factory.

class StackWidgetService : RemoteViewsService() {
    override fun onGetViewFactory(intent: Intent): RemoteViewsFactory =
        StackRemoteViewsFactory(this.applicationContext)
}

class StackRemoteViewsFactory(private val context: Context) :
    RemoteViewsService.RemoteViewsFactory {
    private var widgetItems = listOf<UserProfileSummary>()
    private lateinit var repository: FavoriteUserRepository

    override fun onCreate() {
        repository = FavoriteUserRepository(
            FavoriteUserDataSource(context.contentResolver),
            Dispatchers.IO
        )
    }

    override fun onDataSetChanged() {
        GlobalScope.launch {
            widgetItems = repository.favoriteUsers().toList() // Tested; working on the Activity scope of the consumer app
            Timber.i(widgetItems.toString())
        }
    }

    override fun getViewAt(position: Int): RemoteViews =
        RemoteViews(context.packageName, R.layout.widget_favorite_user_item).apply {
            setTextViewText(R.id.widget_favorite_user_item_text, widgetItems[position].username)
        }

    override fun getLoadingView(): RemoteViews? = null

    override fun getItemId(position: Int): Long = 0

    override fun hasStableIds(): Boolean = false

    override fun getCount(): Int {
        return widgetItems.size
    }

    override fun getViewTypeCount(): Int = 1

    override fun onDestroy() {}
}

So the logic is to ask the ContentObserver to observe changes in the ContentProvider. The observer is registered on the onEnabled and onDisabled part of the AppWidgetProvider. Once the observer notices a change in ContentProvider, it will ask the AppWidgetProvider to update itself, thus calling onDataSetChanged and fetching a new list of data.

READ  [ANSWERED] Android fullscreen fragment does not show elements behind the navigation and status bars
Powered by Inline Related Posts

However, the observer is never called. What could be the reason it’s not working as expected here? (It can’t be because of a lack of permission, because the Activity part of the consumer app is able to fetch the data just fine.)

Comments

Comment posted by Richard

What does

Comment posted by CommonsWare

@Richard: “What does a push solution mean?” — see the last sentence. Basically, you get an

Comment posted by CommonsWare

@Richard: “Do you have a link to a sample?” — no, sorry. I have app widget samples, but they are old and do not demonstrate this particular approach. However, particularly since Apple’s recent updates are giving new interest to app widgets, I should write some new samples and hopefully will get to that over the next few months. I’ll specifically try to handle this scenario, as it is more relevant now.

Comment posted by Richard

I see. Please correct me if I’m wrong: I think that you’re suggesting utilizing my existing observer in

Comment posted by use

@Richard: “utilizing my existing observer in consumer app’s activity to also call updateAppWidget(); is this it?” — probably not, for the reasons you mentioned. “I’m not really sure at which part of my consumer app’s code I should register a long-lived observer” — to be honest, your setup is awkward. If your

Recent Posts

  • How can I play with my cat without toys?
  • What is a bag pipe band called?
  • Are Honda Civics actually fast?
  • Are Yankee candles toxic?
  • How do I pair my Michael Kors smartwatch with my Android?

Recent Comments

No comments to show.

Archives

  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022

Categories

  • ¿Cómo
  • ¿Cuál
  • ¿Cuántas
  • ¿Cuánto
  • ¿Que
  • ¿Quién
  • 90” and 108” so you may have to round up to the nearest size.
  • and delete any Spotify folders from it. Once this is done
  • Android
  • Are
  • At
  • Bei
  • blink
  • C'est
  • Can
  • carbs
  • Comment
  • Did
  • Do
  • Does
  • During
  • For
  • Has
  • How
  • In
  • Is
  • Ist
  • Kann
  • Können
  • nouveau
  • On
  • or 108 inches.2020-08-03
  • Où
  • owning
  • Pourquoi
  • Puis-je
  • Quand
  • Quante
  • Quel
  • Quelle
  • Quelles
  • Quels
  • Qui
  • Should
  • Sind
  • Sollte
  • spiritual
  • tap the downward-facing arrow on the top left. A downward-facing arrow will appear underneath each song in the album; they'll turn green as the download completes.2020-07-28
  • Uncategorized
  • Wann
  • Warum
  • Was
  • Welche
  • Welcher
  • Welches
  • Welke
  • Wer
  • Were
  • What
  • What's
  • When
  • Where
  • Which
  • Who
  • Whose
  • Why
  • Wie
  • Will
  • Wo
  • Woher
  • you will receive two curtains each with the same measurements of width 66"" (168cm) x drop 54""(137cm).
  • you'll see a green downward-facing arrow next to each song.2021-02-26
©2023 Who Do Is | Powered by SuperbThemes & WordPress