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()
}
...
}
}
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.