3

I'm working on a project where I need to register a BroadcastReceiver and send a broadcast to it from a Notification Action. Please tell me if there is something glaring that I'm doing wrong. I don't want the receiver to be registered in the manifest because I want to have the custom onRecieve method that accesses several local variables.

Full Code available here: https://github.com/akirby/notificationTest

Edit: According to the Android documentation (https://developer.android.com/guide/components/broadcasts.html), this is possible, but I'm having trouble understanding why this is not working.

BroadcastReciever local variable

public BroadcastReceiver approveReceiver = new BroadcastReceiver(){
    @Override
    public void onReceive(Context context, Intent intent){
        notificationManager.cancel(notificationId);
        String data = intent.getAction();
        Toast.makeText(getApplicationContext(), data, Toast.LENGTH_LONG);
        if(data != null && data.equals("com.myapp.Approve")){
            mainText.setText("Approved");
        }
        else{
            mainText.setText("Denied");
        }
    }
};

Registration:

registerReceiver(approveReceiver, new IntentFilter("com.myapp.Approve"));

Notification:

public void showNotification(){

    Context appContext = getApplicationContext();
    Intent approveIntent = new Intent(appContext, ApprovalReceiver.class);
    approveIntent.setData(Uri.parse("Approve"));
    approveIntent.setAction("com.myapp.Approve");
    PendingIntent pendingIntent = PendingIntent.getBroadcast(appContext, 0, approveIntent, PendingIntent.FLAG_CANCEL_CURRENT);

    Intent denyIntent = new Intent(appContext, ApprovalReceiver.class);
    approveIntent.setData(Uri.parse("deny"));
    denyIntent.setAction("com.myapp.Deny");
    PendingIntent denyPendingIntent = PendingIntent.getBroadcast(appContext, 0, denyIntent, PendingIntent.FLAG_CANCEL_CURRENT);


    NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("Test Notification")
            .setContentText("Test notification details")
            .setAutoCancel(true)
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .addAction(R.drawable.ic_launcher_foreground, getString(R.string.Approved),
                    pendingIntent)
            .addAction(R.drawable.ic_launcher_foreground, getString(R.string.Deny),
                    denyPendingIntent);
    notificationManager.notify(notificationId, builder.build());
}
AKirby50
  • 174
  • 1
  • 13
  • have you added a receiver in manifest.xml – VIISHRUT MAVANII Mar 13 '19 at 03:27
  • Change the Ids for `PendingIntent` which is 0 in both case make it 1 and 2 (diffrent). – ADM Mar 13 '19 at 03:59
  • I updated `PendingIntent` requestCode to 1 and 2 and it still didnt work. `PendingIntent pendingIntent = PendingIntent.getBroadcast(appContext, 1, approveIntent, PendingIntent.FLAG_CANCEL_CURRENT);` – AKirby50 Mar 14 '19 at 15:31
  • Could you explain the use case a little bit? It is unusual to edit view contents via notification mechanism since notifications almost always appear when your app is not currently in use. And when your application is not in use than your activity doesn't exist, thus doesn't receive any intent. It would be better to make activity intent and modify the textView based on the contents of the intent. – Ernest Zamelczyk Mar 15 '19 at 15:20
  • @ErnestZamelczyk the example given is just an example. the use case is that I have a service registered in my application and when certain events happen in the service I raise the notification. I need the ability to call back into the existing service that is running in memory, however, I am not able to bind to the service based on security restrictions in Android. For this example i simplified it to send the broadcast to an existing Activity and it still doesnt work which suggests something is wrong with my broadcast code and is unrelated to the security portion of it in my main app. – AKirby50 Mar 15 '19 at 15:27
  • @ErnestZamelczyk the full code from the example is here: [link](https://github.com/akirby/notificationTest) – AKirby50 Mar 15 '19 at 15:29

2 Answers2

4

I figured out my problem. It was a conflict with how i was instantiating the Intent objects and my IntentFilter objects. The IntentFilters were being instantiated with an action, and while I was instantiating the Intents with a ".setAction" option, the fix is below:

Change this:

Intent approveIntent = new Intent(appContext, ApprovalReceiver.class);

To this:

Intent approveIntent = new Intent("com.myapp.Approve");

because my IntentFilter for the BroadcastReceiver registration is as such:

this.registerReceiver(approveReceiver, new IntentFilter("com.myapp.Approve"));
AKirby50
  • 174
  • 1
  • 13
  • I have been looking for a fix for this for days. Thanks! – Bill May 11 '21 at 18:57
  • This is very strange. It works but I looked through the source code and the `Intent(String action)` constructor just calls `Intent.setAction()`. I guess it's about not having a context or class and not which constructor you call. – SMMB Jan 07 '23 at 11:35
2

Unfortunately PendingIntent cannot be implicit so there's no way for you to receive it like that. There's a workaround though.

Your activity must have declared android:launchMode="singleInstance in manifest.

Create custom receiver which will start the activity:

public class ApprovalReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent){
        Intent activityIntent = new Intent(context, MainActivity.class);
        activityIntent.putExtra("action", intent.getAction());
        context.startActivity(activityIntent);
    }
}

Register it in the manifest:

<receiver android:name=".ApprovalReceiver">
    <intent-filter>
        <action android:name="ACTION_APPROVE"/>
        <action android:name="ACTION_DENY"/>
    </intent-filter>
</receiver>

And then handle intent in the activity:

public class MainActivity extends AppCompatActivity {

    private String CHANNEL_ID = "AlertChannel";
    private TextView mainText;
    private int notificationId = 1;
    private NotificationManagerCompat notificationManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mainText = (TextView) findViewById(R.id.mainText);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        notificationManager = NotificationManagerCompat.from(this);
        createNotificationChannel();

        handleIntent(getIntent());

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                showNotification();
            }
        });
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        handleIntent(intent);
    }

    private void handleIntent(Intent intent) {
        if(intent != null) {
            String action = intent.getStringExtra("action");
            if(action != null) {
                notificationManager.cancel(notificationId);
                setText(action);
            }
        }
    }

    private void setText(String action) {
        switch (action) {
            case "ACTION_APPROVE":
                mainText.setText("Approved");
                break;
            case "ACTION_DENY":
                mainText.setText("Denied");
                break;
        }
    }

    private void createNotificationChannel() {
        CharSequence name = getString(R.string.channel_name);
        String description = getString(R.string.channel_description);
        int importance = NotificationManager.IMPORTANCE_DEFAULT;
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
        channel.setDescription(description);
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(channel);
    }

    public void showNotification() {
        Intent approveIntent = new Intent(getApplicationContext(), ApprovalReceiver.class);
        approveIntent.setAction("ACTION_APPROVE");
        PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, approveIntent, 0);

        Intent denyIntent = new Intent(getApplicationContext(), ApprovalReceiver.class);
        denyIntent.setAction("ACTION_DENY");
        PendingIntent denyPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, denyIntent, 0);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setContentTitle("Test Notification")
                .setContentText("Test notification details")
                .setAutoCancel(true)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .addAction(R.drawable.ic_launcher_foreground, getString(R.string.Approved), pendingIntent)
                .addAction(R.drawable.ic_launcher_foreground, getString(R.string.Deny), denyPendingIntent);

        notificationManager.notify(notificationId, builder.build());
    }
}
Ernest Zamelczyk
  • 2,562
  • 2
  • 18
  • 32