16

I added a button inside a notification

but I don't know how to have it call a function when it's clicked.

I tried an approach like this https://code.google.com/p/languagepickerwidget/source/browse/trunk/trunk/src/org/gnvo/langpicker/LangPicker.java since it's also using a RemoteViews object but nothing happens when I click the button.

This is what I currently have:

private void createNotification(){
    String ns = Context.NOTIFICATION_SERVICE;
    NotificationManager notificationManager = (NotificationManager) getSystemService(ns);

    Notification notification = new Notification(R.drawable.ic_launcher, null, System.currentTimeMillis());
    RemoteViews notificationView = new RemoteViews(getPackageName(), R.layout.notification_switch);

    //the intent that is started when the notification is clicked (works)
    Intent notificationIntent = new Intent(this, SettingsActivity.class);
    PendingIntent pendingNotificationIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

    notification.contentView = notificationView;
    notification.contentIntent = pendingNotificationIntent;
    notification.flags |= Notification.FLAG_NO_CLEAR;

    //this is the intent that is supposed to be called when the button is clicked
    Intent switchIntent = new Intent(this, switchButtonListener.class);
    PendingIntent pendingSwitchIntent = PendingIntent.getBroadcast(this, 0, switchIntent, 0);

    notificationView.setOnClickPendingIntent(R.id.buttonswitch, pendingSwitchIntent);

    notificationManager.notify(1, notification);
}

public static class switchButtonListener extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("TAG", "test");
    }

}

I can start an activity with the button but I didn't succeed to have it call a simple function. What would be the best way to do this?

Edit: I found out that I had to register "switchButtonListener" in AndroidManifest.xml

<receiver android:name="SettingsActivity$switchButtonListener" />

Source: Android Activity with no GUI

It works now.

2
  • You should post what is in the EDIT section as an answer, that way if someone is looking for the answer to your question, they can find it more easily. Also welcome to stackoverflow! :) Commented Sep 16, 2012 at 18:23
  • 1
    Done. If I remember correctly, when I tried to post an answer I got a message saying that I can't answer my own questions before 6 hours have passed.
    – A E
    Commented Sep 16, 2012 at 21:53

3 Answers 3

33

I found out that I had to register "switchButtonListener" in AndroidManifest.xml

<receiver android:name="SettingsActivity$switchButtonListener" />

Source: Android Activity with no GUI


Later I found out that I can also use code like this to achieve the same thing without modifying the manifest.

switchButtonListener = new SwitchButtonListener();
registerReceiver(switchButtonListener, new IntentFilter(SWITCH_EVENT));

.

public class switchButtonListener extends BroadcastReceiver {
@Override
    public void onReceive(Context context, Intent intent) {
        Log.d("TAG", "test");
    }

}

.

Intent switchIntent = new Intent(LangService.SWITCH_EVENT);
PendingIntent pendingSwitchIntent = PendingIntent.getBroadcast(context, 0, switchIntent, 0);

notificationView.setOnClickPendingIntent(R.id.buttonswitch, pendingSwitchIntent);


Note that this way I can declare the switchButtonListener class without the static attribute (if not static, it would crash in the previous example) giving me much more flexibility.
Don't forget to call unregisterReceiver() later.

0
0

In Kotlin you can register a receiver with an anonymous class.

const val STOP_ALARM_ACTION = "STOP_ALARM"

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // ...
        registerReceiver(object : BroadcastReceiver() {
            override fun onReceive(p0: Context?, p1: Intent?) {
                stopAlarm();
            }
        }, IntentFilter(STOP_ALARM_ACTION))
    }
    private fun playAlarm() {
        ringtone.stop()
        val stopIntent = Intent(STOP_ALARM_ACTION)
        val stopPendingIntent = PendingIntent.getBroadcast(this, 0, stopIntent, 0)
        val notification = NotificationCompat.Builder(this, "Timer1_ALARM")
                // ...
                .addAction(android.R.drawable.ic_delete, "Stop", stopPendingIntent)
                .build()
        // ...
    }
    private fun stopAlarm() {
        ringtone.stop()
    }
}
0

I believe it is important to also unregister action. So my way of writing this nicely is:

val playButtonAction = register("play_button_action") {
    main.looper?.player?.asStarted { it.stop() }
}

so you can do:

override fun onDestroy() {
    super.onDestroy()
    unregister(playButtonAction)
}

using:

fun Context.register(action: String, function: () -> void): BroadcastReceiver =
    register(IntentFilter(action)) { _, _ -> function() }

fun Context.register(intent: IntentFilter,
                     function: (Intent, BroadcastReceiver) -> void): BroadcastReceiver {
    val receiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) = function(intent, this)
    }
    registerReceiver(receiver, intent)
    return receiver
}

fun Context.unregister(receiver: BroadcastReceiver) {
    unregisterReceiver(receiver)
}

And also u use playButtonAction:

val stopIntent = PendingIntent.getBroadcast(this, 0, Intent("play_button_action"),
            FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE);

This is my complete Service class:

class LooperPlayNotificationService : Service() {

    companion object {
        val NOTIFICATIONS_CHANNEL = "${app.packageName} notifications"
    }

    override fun onBind(intent: Intent): IBinder? = null

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)
        start()
        return START_STICKY
    }

    override fun onCreate() {
        super.onCreate()
        start()
    }

    private val playButtonActionId = "play_button_action"
    private lateinit var playButtonAction: BroadcastReceiver
    private var started = false

    // https://stackoverflow.com/questions/6619143/start-sticky-foreground-android-service-goes-away-without-notice
    // There's a bug in 2.3 (not sure if it was fixed yet) where when a Service is killed and restarted,
    // its onStartCommand() will NOT be called again. Instead you're going to have to do any setting up in onCreate()
    private fun start() {
        if (started) return
        started = true
        startForeground(647823876, createNotification())
        playButtonAction = register(playButtonActionId) {
            main.looper?.player?.asStarted { it.stop() }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        unregister(this.playButtonAction)
    }

private fun createNotification() = Builder(this, NOTIFICATIONS_CHANNEL)
    .setSmallIcon(outline_all_inclusive_24)
    .setContentIntent(getActivity(this, 0, Intent<InstrumentsActivity>(this),
        FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE))
    .setPriority(PRIORITY_DEFAULT)
    .setAutoCancel(false).setOngoing(true)
    .addAction(ic_stop_circle_black_24dp, "Stop",
        getBroadcast(this, 0, Intent(playButtonActionId),
            FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE))
    .setContentText(getString(R.string.app_name))
    .setContentText(main.looper?.preset?.item?.value?.title?.value).build()
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.