Nitzan Daloomy
Nitzan Daloomy

Reputation: 316

Open Activity on notification button click when app is closed

I'm trying to open the MainActivity when the user clicks a button in my notification, while the app is only running in the background with a service. When the button is clicked, these lines are triggered in the Service class:

Intent openApp = new Intent(this, MainActivity.class);
openApp.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openApp);

I've checked it, and the lines are triggered, so there's no problem in reacting to the button's click, the Activity won't open though.

Any suggestions? Why isn't this working for me and how can I make it work?

Edit

I was asked for some more code, so in my onStartCommand() inside my Service, if it starts with a stop-action within its intent, I call the killService() method, which kills the Service, starts the MainActivity and do some other stuff:

if (action != null && action.equals(ACTION_STOP_SERVICE)) {
    killService();
}

To set the Notifications button, I use this code:

Intent stopActionIntent = new Intent(this, TimerService.class);
        stopActionIntent.setAction(ACTION_STOP_SERVICE);
        PendingIntent stopActionPendingIntent = PendingIntent.getService(this, 1, stopActionIntent, PendingIntent.FLAG_IMMUTABLE);

timerNotificationBuilder.addAction(R.drawable.stop, "Stop", stopActionPendingIntent);

And as I said, the button already reacts to the user clicking on it, so that's not the problem.

Upvotes: 3

Views: 5423

Answers (2)

Sambhav Khandelwal
Sambhav Khandelwal

Reputation: 3765

You can try to receive the click in a BroadcastReceiver and then open activity from there.

  1. Try this to add a action button o your notification:
timerNotificationBuilder.addAction(createNotificationActionButton("STOP");

Where the createNotificationActionButton method is this:

public NotificationCompat.Action createNotificationActionButton(String text){
        Intent intent = new Intent(this, StopwatchNotificationActionReceiver.class);

        @SuppressLint("InlinedApi") PendingIntent pendingIntent = PendingIntent.getBroadcast(this, new Random().nextInt(100), intent, PendingIntent.FLAG_IMMUTABLE);

        return new NotificationCompat.Action(0, text, pendingIntent);
    }
  1. Create a class named StopwatchNotificationActionReceiver and make it extent a BroadcastReceiver. This is the code for that class:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class StopwatchNotificationActionReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        PrefUtil.setIsRunningInBackground(context, false);
        PrefUtil.setTimerSecondsPassed(context, 0);
        PrefUtil.setWasTimerRunning(context, false);
        context.stopService(MainActivity.serviceIntent);
        Intent activityIntent = new Intent(context, MainActivity.class);
        activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActvity(activityIntent);
    }
}

Also you need to register that receiver in your manifest like this:

<receiver android:name="StopwatchNotificationActionReceiver"/>
  1. Where the MainActivity.serviceIntent is a public static variable which looks like this:
public static Intent serviceIntent;

And this intent is only used to start the service like this:

//In onCreate
serviceIntent = new Intent(this, TimerService.class);

//In onPause
PrefUtil.setTimerSecondsPassed(this,seconds);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                startForegroundService(serviceIntent);
            }

Or you can try the simple method:

if (action != null && action.equals(ACTION_STOP_SERVICE)) {
    Context context = this;
    Intent activityIntent = new Intent(context, MainActivity.class);
        activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActvity(activityIntent);
    killService();
}

Edit


Another solution is here. Again. You need to refer to my repo as I have made changes to the files in order to complete your task. In the service class, refer to this method. There, I start the activity if the action is reset(r). Or else, it opens the broadcast receiver. Then, in the activity, I receive that extra in the onResume() method. If the reset button is not clicked, it opens the Receiver class.

And as always, you can view the result of the app from here.

I hope that code will do your work.

Upvotes: 4

Nitzan Daloomy
Nitzan Daloomy

Reputation: 316

I found it! See this answer.
This answer suggests enabling ScreeanOverlay settings because as of Android 10 and later you can no longer open an activity from the background just by calling the lines I've used.
To make it work, you'd have to add this permission through your Manifest.xml:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

And then the user would have to enable the Display over other apps setting.
I searched for an option to get the user to this setting more easily and found this answer.
This answer gives a code that redirects the user to the Display over other apps setting

if (!Settings.canDrawOverlays(this)) {
    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
    startActivityForResult(intent, 0);
}

and then I guide the user with the notification's content (text) on how to enable the setting.
Once the setting is enabled, The lines I've used before work.\

So, problem solved?

Not Completely Solved

this whole configuration described above works, but only if the app is not killed.
If the app was killed and I try the method listed above, the app joins the recent apps list, but won't open and show up.
A solution that solves this issue as well will be accepted as an answer instead of this one.

Upvotes: -1

Related Questions