3

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.

Nitzan Daloomy
  • 166
  • 5
  • 24

2 Answers2

3

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.

Umesh P
  • 228
  • 2
  • 7
Sambhav Khandelwal
  • 3,585
  • 2
  • 7
  • 38
  • it gives me a red line under `startActivity()`, saying "`This intent launches a Service which launches activities; this indirection is bad for performance, and activities should be launched directly from the notification`" – Nitzan Daloomy Apr 18 '22 at 15:44
  • Let me edit this and give another answer – Sambhav Khandelwal Apr 18 '22 at 15:45
  • Does my answer help you now @NitzanDaloomy? – Sambhav Khandelwal Apr 18 '22 at 15:55
  • After reading your suggestion, I don't understand - where's the code for opening the activity? – Nitzan Daloomy Apr 18 '22 at 19:47
  • not only this, I find it really hard to understand what's going on - what's the `createNotificationActionButton()` method is for, why is it better than how I did it, and how it actually works... where did you register your broadcast? I really got lost in here – Nitzan Daloomy Apr 18 '22 at 19:53
  • @NitzanDaloomy. I will answer your questions here ~~~ `where's the code for opening the activity?`: I forgot to add the part to open the activity. I have added it now. `where did you register your broadcast?`: Also I had forgotten to register it in the service. I have added it. `what's the createNotificationActionButton() method is for?` It is used to make an action button for the notification. `why is it better than how I did I`: I can't say that. Your method is good for u and my method is good for me. But, I'd prefer never to open activities from the service. Instead use Broadcast Receivers. – Sambhav Khandelwal Apr 19 '22 at 04:12
  • And lastly `and how it actually works`: It works with a easy method. First we have an intent in the activity which will be used to start and stop the service. When, we click the button in the notification, it sends a broadcast to the `StopwatchNotificationActionReceiver` class. There, when it receives a broadcast, it stops the service from the intent of the main activity and then opens the main activity. **Hope I have answered all your questions** – Sambhav Khandelwal Apr 19 '22 at 04:16
  • Tried what you suggested again, and again a red line jumped, with the same error, this tome under `addAction()` method: "`This intent launches a BroadcastReceiver which launches activities; this indirection is bad for performance, and activities should be launched directly from the notification`" – Nitzan Daloomy Apr 19 '22 at 14:35
  • also tried to run this although there's a red line, and the activity just didn't open – Nitzan Daloomy Apr 19 '22 at 14:40
  • Now. I want to know whether or not you want to stop the timer on click of the stop button? If you want to stop, then use the broadcast receiver as mentioned by me or else just use a pending intent for that. – Sambhav Khandelwal Apr 20 '22 at 09:39
  • I want to stop the timer, and open the app so I can do some other stuff that I can only do inside my app. That's why I need to be able to open the activity intent from outside the app. stopping the service was never a problem... – Nitzan Daloomy Apr 20 '22 at 10:06
  • Sorry for the late reply. Yes. My answer is edited according to that. Try that code – Sambhav Khandelwal Apr 20 '22 at 11:06
  • Or even try the simple method given by me at the bottom – Sambhav Khandelwal Apr 20 '22 at 11:09
  • Both of them show the same red lines I mentioned before :( (and they don't work) – Nitzan Daloomy Apr 20 '22 at 11:21
  • Is it possible for you to open the activity and in the activity stop the timer – Sambhav Khandelwal Apr 20 '22 at 11:36
  • In the meantime, if I cannot find a way to open the app using the stop button, I just won't show a button, and the user will have to go into the app and then stop the service. It would be better if they could just click the stop button and then it will all happen automatically for the users though, and that's why I brought up this question – Nitzan Daloomy Apr 20 '22 at 11:39
  • found half a solution (: check out my answer to see it. – Nitzan Daloomy Apr 20 '22 at 13:28
  • I have edited my answer. Look into the `Edit` section] – Sambhav Khandelwal Apr 21 '22 at 07:13
  • Hi @NitzanDaloomy. No response yet. Is there any error still? – Sambhav Khandelwal Apr 22 '22 at 09:54
  • Hi, sorry to answer just now, a busy weekend. I've seen your solution but didn't see anything that's supposed to solve my problem... check my answer (the one below yours), it explains why the activity won't open... if you have anything to solve what I referred to at the end of my answer that'll be great (: thanks for the help! – Nitzan Daloomy Apr 22 '22 at 14:31
  • @NitzanDaloomy NP. Yes. I did see your answer. I have also solved ur problem. What I have done is that open the activity with an extra. In the `onResume()` I check if the extra is `r`. If yes, I stop the service and also stop the timer. You can view the result below it. That will do the task for you i guess. – Sambhav Khandelwal Apr 22 '22 at 14:38
  • But I don't get it. in order to get to `onResume()`, you have to open the app, and this requires the permissions, as I mentioned... Do you claim to solve the problem without the required permissions? and also to be able to open the app even if it's killed? – Nitzan Daloomy Apr 22 '22 at 15:45
  • Yes indeed @NitzanDaloomy. Try it out. It works like charm. But, did u check the sample video? You can see the result there – Sambhav Khandelwal Apr 22 '22 at 16:58
  • Oh, great, I'll try that out as soon as I can! Thanks(: – Nitzan Daloomy Apr 22 '22 at 21:02
  • Also, instead of stopping the service when I'm in the app, I'm not stopping it - the service is the main and only timer that runs, so I guess there's no need to use `onResume()` anymore? – Nitzan Daloomy Apr 22 '22 at 21:05
  • hmmmm. So, you can remove the `stopService()` method in the `onResume()`. That will do the task – Sambhav Khandelwal Apr 23 '22 at 04:24
  • Any updates @NitzanDaloomy? – Sambhav Khandelwal Apr 24 '22 at 14:50
  • tried your code, it makes sense that it'll work, and it does. but it's not what I tried to do. I'm trying to open the activity from the service at any time. In addition to the regular timer, I have a `CountdownTimer`, and in its `onFinish()` I'm opening the activity, so opening the app from the service is required, and not from the notification. Anyway, it's a great way for opening the app using the notifications button. So I'll give you an upvote for that, but still, my solution solves better my problem. hope it'll get figured out how to solve it completely. – Nitzan Daloomy Apr 24 '22 at 17:22
  • Now. That becomes another question i guess. I have answered your question of opening activity from action button. What u r asking now is different. Why not ask another question? – Sambhav Khandelwal Apr 24 '22 at 17:40
  • I'll upload another answer then. I'll update you when I do it (: – Nitzan Daloomy Apr 24 '22 at 21:14
  • I've uploaded another [question](https://stackoverflow.com/q/71998322/8099601) – Nitzan Daloomy Apr 25 '22 at 10:49
  • are you the one who upvoted to it? – Nitzan Daloomy Apr 25 '22 at 11:11
  • No. Not me. @NitzanDaloomy – Sambhav Khandelwal Apr 26 '22 at 07:27
  • Man :/ so who did it... I just can't understand why would someone do it without at least explaining... what's the point of it if it doesn't come with an explanation? – Nitzan Daloomy Apr 26 '22 at 15:05
-1

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.

Nitzan Daloomy
  • 166
  • 5
  • 24