BroadcastReceiver context registration not working

1k Views Asked by At

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());
}
2

There are 2 best solutions below

2
On BEST ANSWER

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"));
0
On

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