Android 12 BroadcastReceiver onReceive intent.getExtras allways null
Asked Answered
B

1

5

I'm trying to build a stackview widget, which should display differnt data when swiping through it. So far everything is working, but when I want to receive the index of the clicked card in the widget, I always get an extra or action of 'null'. I already have tried several workarounds, but none of them is working in my application. I also set up a complete new application for only making this work completely isolated. But here I get the exact same behaviour.

Several workarounds I tried to pass the index:

What I observed is, that the getViewAt funtion is called several times with a position(p0) of "0" before it gets called by the right positions from the generated exampleData List... But I don't know if this causes the Nulls in the onReceive function, when an card is clicked.

Here is my coding:

Manifest.xml

<service android:name=".ExampleWidgetService"
        android:permission="android.permission.BIND_REMOTEVIEWS">
</service>

WidgetLayout

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <StackView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/stackview">
    </StackView>

    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/empty_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:textColor="#ffffff"
        android:textStyle="bold"
        android:text="empty"
        android:textSize="20sp" />
</FrameLayout>

WidgetItemEntry

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:text="Icon"
        android:id="@+id/newwidgetentry">

    </TextView>
</LinearLayout>

AppWidgetProvider

package com.example.widgettest
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.widget.RemoteViews
import android.widget.Toast

/**
 * Implementation of App Widget functionality.
 */
public const val clickedCategory: String = "com.example.myapplication.buttonpress.id"
public const val stackviewButton = "stackviewbutton"

class newwidget : AppWidgetProvider() {
    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray,
    ) {
        // There may be multiple widgets active, so update all of them
        for (appWidgetId in appWidgetIds) {

        /*    val widgetText = context.getString(R.string.appwidget_text)
            // Construct the RemoteViews object
            val views = RemoteViews(context.packageName, R.layout.newwidget)
            views.setTextViewText(R.id.newwidgetentry, widgetText)*/

            val serviceIntent: Intent = Intent(context, ExampleWidgetService::class.java).apply{
                putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
                data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
            }
            val views = RemoteViews(
                context.packageName,
                R.layout.newwidget
            ).apply{
                setRemoteAdapter(R.id.stackview, serviceIntent)
                setEmptyView(R.id.stackview, R.id.empty_view)
            }

            val toastIntent = Intent(context, newwidget::class.java).run {
                action= stackviewButton
                putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
                data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
                PendingIntent.getBroadcast(context,
                    0,
                    this,
                    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
            }
            views.setPendingIntentTemplate(R.id.stackview, toastIntent)


            // Instruct the widget manager to update the widget
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
        super.onUpdate(context, appWidgetManager, appWidgetIds)
    }

    override fun onEnabled(context: Context) {
        // Enter relevant functionality for when the first widget is created
    }

    override fun onDisabled(context: Context) {
        // Enter relevant functionality for when the last widget is disabled
    }

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


   var clickedCat2 = intent?.getBundleExtra("test")?.getInt(clickedCategory,-1)
             /*val clickedCat2: Int? = intent?.getIntExtra(clickedCategory, -1)*/

        Toast.makeText(context, "onreceive +$clickedCat2" , Toast.LENGTH_SHORT).show();
        if (stackviewButton.equals(intent?.getAction())){
            val clickedCat: Int? = intent?.getIntExtra(clickedCategory, 0)
            Toast.makeText(context, "widgetbuttonpressed id + $clickedCat", Toast.LENGTH_SHORT).show();
        }
        super.onReceive(context, intent)
    }
}

RemoteView Factory

package com.example.widgettest

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.RemoteViews
import android.widget.RemoteViewsService
import com.google.gson.Gson

private const val REMOTE_VIEW_COUNT: Int = 2

data class WidgetItem(
    val id: String,
)

class ExampleWidgetService : RemoteViewsService() {

    override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
        return ExampleWidgetService1(this.applicationContext, intent)
    }
}

class ExampleWidgetService1(private val context: Context, intent: Intent) :
    RemoteViewsService.RemoteViewsFactory {

    private lateinit var exampleData: List<WidgetItem>

    override fun onCreate() {
        exampleData = List(REMOTE_VIEW_COUNT) { index -> WidgetItem("$index!") }
        val test = 123
    }

    override fun onDataSetChanged() {
    }

    override fun onDestroy() {
    }

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

    override fun getItemId(p0: Int): Long {
        return p0.toLong()
    }

    override fun getViewAt(p0: Int): RemoteViews {

        return RemoteViews(context!!.packageName, R.layout.newwidgetentry).apply {

            setTextViewText(R.id.newwidgetentry, exampleData[p0].id)

       

     val fillIntent = Intent().apply {
                    Bundle().also { extras ->
                        extras.putInt(clickedCategory, p0)
                        putExtra("test",extras)
                    }
                }

  /*val fillIntent = Intent().apply {
                putExtra(clickedCategory, p0)
            }*/
            setOnClickFillInIntent(R.id.newwidgetentry, fillIntent)
        }
    }

    override fun getLoadingView(): RemoteViews? {
        return null
    }

    override fun getViewTypeCount(): Int {
        return 1
    }


    override fun hasStableIds(): Boolean {
        return true
    }

}

I really appreciate everykind of help!!! Thank you already in advance!!

Babble answered 11/3, 2022 at 12:48 Comment(17)
and where did you put extras? also where is setPendingIntentTemplateRub
It is commented out ... so it doesn't runRub
I know, I did it for testing purpose, doesn't matter when I comment it in. Already tried that, as I mentioned. The setPendingIntentTemplate I'm setting in the onUpdate of the class newwidgetBabble
this is all wrong ... you should pass into setPendingIntentTemplate intent obtained from PendingIntent.getXXX and you are passing your own new intent Intent().run {}Rub
for the PendingIntent I only get 4 options: getService, getBroadcast, readPendingIntentOrNullFromParcel, get Activity. How can I retrieve the PendingIntentTemplate intent, which I have created in the onUpdate function?Babble
nevermind ... i don't know kotlin does .run { } return last statment ? if so then this language is pretty stupid ... as you can read from code if you have () -> void or () -> somevalueRub
I have no idea, too.. I was first doing it a different way, without alle runs and applys... But since they are doing the same in the official google documentation, I thought that might be the cause, that it is not working... :D developer.android.com/guide/topics/appwidgets/collectionsBabble
from the example it's clear that you should set action where you have //action= stackviewButton and you should not provide action in intent passed to setOnClickFillInIntentRub
yes you are right, this was orginating from the many other tries to make it work. I just updated the code again. So that it is identical to the one in the documentationBabble
now test3 and clickedCat2 in the onReceive function are null when I click on the card...Babble
in example there is putExtras(extras) not putExtra("test",extras) ... if you use putExtra("test",extras) then you should take bundle extra called "test" and then clickedCategory from this bundle like intent?.getBundleExtra("test")?.getInt(clickedCategory, 0)Rub
even with this, clickedCat2 is still Null var clickedCat2 = intent?.getBundleExtra("test")?.getInt(clickedCategory,-1) And of course i already tried it without giving the bundle an additional descriptorBabble
or theoriticaly this should work also val fillInIntent = Intent().apply { putExtra(clickedCategory, p0) } with your val clickedCat: Int? = intent?.getIntExtra(clickedCategory, 0)Rub
with that what you just posted, it should be this in the onReceive (clickedCat2: Int? = intent?.getIntExtra(clickedCategory, -1)). But it still stays by the default value (-1) when I click on a card..Babble
are you sure that clickedCategory has the same value in both classes?Rub
I just have put the 2 options inside the code, the bundle option active the one without the bundle commented out. Yes clickedCatergory always stays "com.example.myapplication.buttonpress.id". I just proofed it via debugging.Babble
And I also remember that I tried it once just with an matching hardcoded string..Babble
C
11

I've the same issue, finally I resolve this issue!

Just change setPendingIntentTemplate's pending intent flags to PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE.

Critchfield answered 17/3, 2022 at 15:17 Comment(4)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Altimeter
wow, great! This is working now!!! Thank you very much for sharing the solution. I feel so free to add another question right away: Do you get an notification every single time you press on a card? In my case it seems like that the stackview widget is only recognizing every 5th click on a card or so... But the debugger in the onReceive function is getting called with every single click on a card...Babble
@Babble Sorry, in my case can recognize every click event.Critchfield
Just as explanation why it works (i had the same problem): For the listview in the widget, you will define a PendingIntent in advance and set this as a IntentTemplate. If there is no flag like PendingIntent. If you set a fillIntent for each list_item, whenever you click the item, the intent of this item will be merged with the PendingIntentTemplate. The problem is now, this merge will ONLY happen, if PendingIntent.FLAG_MUTABLE is set.Adolpho

© 2022 - 2024 — McMap. All rights reserved.