Reputation: 485
I have a list view present in the home screen widget of my android app. I am using a flutter plugin called home widget to enable communication between kotlin and dart code. There are two elements in every item of the listview: a checkbox and text. When the checkbox is clicked, I want to execute HomeWidgetBackgroundIntent.getBroadcast method to trigger a background callback in my dart code(this makes some changes in the database) and when the text is clicked I want to use HomeWidgetLaunchIntent.getActivity method to open my app with a specific URI. Both these methods are pending intents. The problem is that android doesn't allow you to add different pending intents for one single list item. They want developers to use fillInIntent for individual items and then set common pending intent template. How I am supposed to detect which element is clicked(checkbox or text) and set a different pending intent template for both cases? I have also thought of letting the pending intent template to trigger the receiver of the widget and then call the home_widget plugin methods here(based on data set in fillInIntent). But since the methods are pending intents, how can I trigger pending intents here(usually pending intents are triggered on click event but its not possible to trigger them in onReceive method right)?
Code pertaining to my widget:
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, widgetData: SharedPreferences) {
Log.d("debugging", "widget loaded")
val sharedPref: SharedPreferences = context.getSharedPreferences(
"ApplicationListener", Context.MODE_PRIVATE)
appWidgetIds.forEach { widgetId ->
val views = RemoteViews(context.packageName, R.layout.widget_layout).apply {
val todosStr = widgetData.getString("todos", "null")
val todos = ArrayList<HashMap<String, Any>>()
val todosRemoteView = RemoteViews.RemoteCollectionItems.Builder()
if(todosStr != "null"){
val jObj = JSONObject(todosStr)
val jsonArry = jObj.getJSONArray("todos")
for (i in 0 until jsonArry.length()) {
val todo = HashMap<String, Any>()
val obj = jsonArry.getJSONObject(i)
todo["id"] = obj.getInt("id")
todo["taskName"] = obj.getString("taskName")
todos.add(todo)
val view = RemoteViews(context.packageName, R.layout.each_todo).apply {
setTextViewText(R.id.each_todo_container_text, todo["taskName"].toString())
setCompoundButtonChecked(R.id.each_todo_container_checkbox, todo["finished"].toString().toBoolean())
//i want to have a separate pending intent for the text view and the check box
val backgroundIntent = HomeWidgetBackgroundIntent.getBroadcast(context,
Uri.parse("myAppWidget://todo_checked/${todo["id"].toString()}"))
setOnCheckedChangeResponse(
R.id.each_todo_container_checkbox,
RemoteViews.RemoteResponse.fromPendingIntent(backgroundIntent)
)
//this pending intent doesn't get triggered
val fillInIntent = Intent().apply {
Bundle().also { extras ->
extras.putInt(EXTRA_ITEM, todo["id"].toString().toInt())
putExtras(extras)
}
}
setOnClickFillInIntent(R.id.each_todo_container_text, fillInIntent)
}
todosRemoteView.addItem(todo["id"].toString().toInt().toLong(), view)
}
}
setRemoteAdapter(
R.id.todos_list,
todosRemoteView
.build()
)
val editTodoIntent = HomeWidgetLaunchIntent.getActivity(
context,
MainActivity::class.java,
Uri.parse("http://edit_todo/$timeType"))
setPendingIntentTemplate(R.id.todos_list, editTodoIntent)
//how to set two different pending intent templates here for the text view and checkbox?
}
appWidgetManager.updateAppWidget(widgetId, views)
}
}
override fun onReceive(context: Context?, intent: Intent?) {
val viewIndex: Int = intent!!.getIntExtra(EXTRA_ITEM, 0)
Log.d("debugging", "an item is clicked $viewIndex")
//i can make the pending intent template of the list view trigger this onReceive function and I can use the data inside intent(set by fillInIntent) to find out which element of the two was clicked
//but how can I trigger the pending intent here?
super.onReceive(context, intent)
}
}
Upvotes: 0
Views: 434
Reputation: 1
Feel free to read this android example to comprehend better what I wrote here. I'm going to explain what you could left to know after you read the website (maybe you already read it).
First you don't have to create the intent for setPendingIntentTemplate by HomeWidgetLaunchIntent.getActivity. You have to create its PendingIntent this way:
...
val pendingIntentTemplate = Intent(context, MyWidgetProvider::class.java).run {
action = "Some action name" // This is a constant in the android example
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
// This creates the PendingIntent you need. Also I had to add the PendingIntent.FLAG_MUTABLE flag to avoid errors, maybe is the OS version of my android emulator, feel free to test.
PendingIntent.getBroadcast(context, 0, this, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
}
setPendingIntentTemplate(R.id.listView_id, pendingIntentTemplate)
...
IMO you should use a RemoteViewsService.RemoteViewsFactory as an adapter and use setOnClickFillInIntent to set each element in the row in the getViewAt function you have to override, this is the way I found in android example and easier to understand. You can find more info in the android example, read carefully all the document. for getVewAt should have something like this:
class CustomAdapter(val context: Context, val intent: Intent) :
RemoteViewsService.RemoteViewsFactory {
...
override fun getViewAt(position: Int): RemoteViews {
val remoteViews = RemoteViews(context.packageName, R.layout.widget_layout).apply {
setTextViewText(R.id.textView_id, "Item $position")
val checkboxfillInIntent = Intent().apply {
Bundle().also { extras ->
extras.putInt("id", position)
extras.putBoolean("isCheckBox", true)
putExtras(extras)
}
}
val textfillInIntent = Intent().apply {
Bundle().also { extras ->
extras.putInt("id", position)
extras.putBoolean("isCheckBox", false)
putExtras(extras)
}
}
setOnClickFillInIntent(R.id.checkBox_id, checkboxfillInIntent)
setOnClickFillInIntent(R.id.textView_id, textfillInIntent)
}
return remoteViews
}
...
}
Maybe you can call the two setOnClickFillInIntent in the scoop of your RemoteViews.RemoteCollectionItems.Builder, I think you should be able to but I prefer to use a RemoteViewsService.RemoteViewsFactory to follow the android example.
In the android example they don't run the app, but you can run your app inside the onReceive function (now here you use HomeWidgetLaunchIntent.getActivity and run it using send() ):
override fun onReceive(context: Context, intent: Intent) {
...
if (intent.action == "Some action name") { // This is the action constant of the android example
val id = intent.getIntExtra("id")
val isCheckBox = intent.getBooleanExtra("isCheckBox")
...
if (isCheckBox) {
...
} else {
val pendingIntent = HomeWidgetLaunchIntent.getActivity(context, MainActivity::class.java)
pendingIntent.send()
}
}
...
}
Feel free to ask for if I missed something. I tried to adapt the code I have to match with what you need.
Upvotes: 0