1

My app widget (ListView showing game scores) works fine when created on the home screen, but when I call appWidgetManager.updateAppWidget(id, R.id.appwidget) inside the provider, there is no reaction from the widget or the log calls in the RemoteViewsFactory's onCreate() or onDataSetChanged() methods. I know that the updateAppWidget gets called because I can see the log call right before it.

Why is the widget not updating?

My AppWidgetProvider:

class ScoresAppWidgetProvider : AppWidgetProvider() {

override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
        updateAllWidgets(appWidgetIds, context, appWidgetManager)
        super.onUpdate(context, appWidgetManager, appWidgetIds)
    }

private fun updateAllWidgets(appWidgetIds: IntArray, context: Context, appWidgetManager: AppWidgetManager) {
        appWidgetIds.forEach { appWidgetId ->
            updateWidget(appWidgetId, context, appWidgetManager, false)
        }

    }

override fun onReceive(context: Context, intent: Intent) {
        super.onReceive(context, intent)

        Log.i("TAG", "onReceive appWidgetId $appWidgetId") // This is logged and id is correct      
        if (intent.action.equals(ACTION_CLICK_REFRESH)) {
            appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.appwidget_list_view) // Works fine and updates the widget list
        } else if (intent.action.equals((ACTION_CLICK_SPOILERS_ON))) { // This is received
            updateWidget(appWidgetId, context, appWidgetManager, true)
        } else if (intent.action.equals((ACTION_CLICK_SPOILERS_OFF))) { // This is received
            updateWidget(appWidgetId, context, appWidgetManager, false)
        }
    }

private fun updateWidget(appWidgetId: Int, context: Context, appWidgetManager: AppWidgetManager, spoilers: Boolean) {
        
        val intent = Intent(context, AppWidgetService::class.java).apply {
            putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            putExtra("testString", "Intent received")
            data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
        }

        // PendingIntent to refresh the appWidget contents
        val pendingRefreshClickIntent: PendingIntent = Intent(context, ScoresAppWidgetProvider::class.java).let {
            it.action = ScoresAppWidgetProvider.ACTION_CLICK_REFRESH
            it.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            return@let PendingIntent.getBroadcast(context, appWidgetId, it, PendingIntent.FLAG_UPDATE_CURRENT)
        }

        // PendingIntent to toggle score spoilers
        val pendingSpoilersClickIntent: PendingIntent = Intent(context, ScoresAppWidgetProvider::class.java).let {
            it.action = if (spoilers) ScoresAppWidgetProvider.ACTION_CLICK_SPOILERS_ON else ScoresAppWidgetProvider.ACTION_CLICK_SPOILERS_ON
            it.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            return@let PendingIntent.getBroadcast(context, appWidgetId, it, PendingIntent.FLAG_UPDATE_CURRENT)
        }

        // RemoteViews object for the app widget layout
        val remoteViews = RemoteViews(context.packageName, R.layout.appwidget).apply {
            if (spoilers) {
                setImageViewResource(R.id.btn_spoilers, R.drawable.avd_anim_turn_off_spoilers)
            } else {
                setImageViewResource(R.id.btn_spoilers, R.drawable.avd_anim_turn_on_spoilers)
            }
            
            setRemoteAdapter(R.id.appwidget_list_view, intent)
            setEmptyView(R.id.appwidget_list_view, R.id.empty_view)
            setOnClickPendingIntent(R.id.btn_refresh, pendingRefreshClickIntent)
            setOnClickPendingIntent(R.id.btn_spoilers, pendingSpoilersClickIntent)
        }

        Log.i(TAG, "updateWidget: Calling updateWidget!") // This is logged
        appWidgetManager.updateAppWidget(appWidgetId, remoteViews) // NO EFFECT FROM THIS CALL!!
    }

}

My RemoteViewsService and RemoteViewsFactory:

class AppWidgetService: RemoteViewsService() {
    override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
        return ListRemoteViewsFactory(this.applicationContext, intent)
    }
class ListRemoteViewsFactory(private val context: Context, val intent: Intent): RemoteViewsFactory {

...

        override fun onCreate() {
            Log.i(TAG, "onCreate: created!") // Logged when I add the widget first time, not logged when I call updateAppWidget() from AppWidgetProvider
            updateList()
        }

        override fun onDataSetChanged() {
            Log.i(TAG, "onDataSetChanged: changed!")
            updateList()
        }
...
    }
}

1 Answer 1

2

It seems the layout passed to the remoteViews is cached, and you have to use a different layout for it to work. I used two different layouts to update the widget based on a boolean, and it worked.

val remoteViews = if (spoilers) RemoteViews(context.packageName, R.layout.appwidget_spoilers) else RemoteViews(context.packageName, R.layout.appwidget_no_spoilers)

Layouts can be similar, but have to have some differences. In my case I used different drawables for an ImageView.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.