28

I am sending push notification to users which when clicking on it opens the app.

My problem is that when the app is already open, clicking on the notification start the app again.

I only want it to start the app if its not already running.

I am using Pending Intent in the notification:

PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Splash.class), 0);

I saw posts which say use:

<activity 
android:name=".Splash"
android:launchMode="singleTask"

but the thing is that my running app is running other activity then the splash which is finished after 7 seconds from app start, so when the app is running Splash is not the current activity

Michael A
  • 5,444
  • 15
  • 62
  • 115

11 Answers11

27

Use a "launch Intent" for your app, like this:

PackageManager pm = getPackageManager();
Intent launchIntent = pm.getLaunchIntentForPackage("your.package.name");
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, launchIntent, 0);

Replace "your.package.name" with the name of your package from the Android manifest.

Also, you should remove the special launchMode="singleTask" from your manifest. Standard Android behaviour will do what you want.

David Wasser
  • 85,616
  • 15
  • 182
  • 239
  • 1
    David Wasser will try it. can you please explain how this will work? is the PendingIntent will not launch if the app is running? – Michael A Jun 01 '15 at 14:28
  • 4
    Using a launch Intent, Android will start the application by launching the root activity if it is not running. If it is already running, using a launch Intent will just bring the existing task from the background to the foreground. This does exactly the same thing when you run an app, press HOME (pushes the task to the background) and then click on the app's icon on the HOME screen (thereby "launching" the app). This doesn't start the app again, it just brings the existing task (in whatever state it was in) to the foreground. – David Wasser Jun 01 '15 at 14:56
  • Have you tried this? Is there some problem with this solution? – David Wasser Jun 05 '15 at 12:01
  • quick question sir, about your solution, what if there a no Activities in that particular Task? are you going to have a blank screen? because a task can exist without having Activities in them, so how will that work out? – Elltz Jun 05 '15 at 16:51
  • @Elltz No, there is no such thing as a task with no activities. When the last `Activity` of a task finishes, the task is dead. If you then try to start an `Activity` that would belong in that task, Android will create a new task and start the `Activity` in the new task. – David Wasser Jun 05 '15 at 18:02
  • It seems like this does the exact same thing as `new Intent(this, Splash.class)` – tachyonflux Jun 08 '15 at 03:36
  • @karaokyo No, it doesn't. The launch `Intent` will also contain ACTION=MAIN, CATEGORY=LAUNCHER and has flag `Intent.FLAG_ACTIVITY_NEW_TASK` set – David Wasser Jun 08 '15 at 08:11
  • So what is suppose to happen if this intent is invoked while the activity is in the foreground? – tachyonflux Jun 08 '15 at 15:58
  • @karaokyo absolutely nothing. – David Wasser Jun 08 '15 at 16:02
  • @DavidWasser I tried this code, and it doesn't seem to just bring the task to the foreground. Instead it always start the main activity, even if the app is currently running with a different activity in the foreground. In fact `PackageManager.getLaunchIntentForPackage` is documented to do just that - get the main (front door) activity. Any idea how I can get this to simply bring the task to the foreground if the app is already running, without reopening the main activity? – M.S Jan 05 '17 at 05:13
  • 1
    @M.S You may be seeing this nasty Android bug: http://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508#16447508 Try force stopping your app and starting it again by clicking the app icon on the HOME screen, then see if you can bring it to the foreground as I've described in this post. If you still have problems, please open a new question! – David Wasser Jan 05 '17 at 08:53
  • 1
    @DavidWasser Your answer and that link which you have provided seems to work. – Wojtek Oct 01 '17 at 17:03
3
String appPackageName = "";

private void isApplicationInForeground() throws Exception {
    ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        final List<ActivityManager.RunningAppProcessInfo> processInfos = am
                .getRunningAppProcesses();
        ActivityManager.RunningAppProcessInfo processInfo = processInfos
                .get(0);
        // for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) {
        if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
            // getting process at 0th index means our application is on top on all apps or currently open 
            appPackageName = (Arrays.asList(processInfo.pkgList).get(0));
        }
        // }
    }
    else {
        List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
        ComponentName componentInfo = null;
        componentInfo = taskInfo.get(0).topActivity;
        appPackageName = componentInfo.getPackageName();
    }
}

private void notifyMessage(String text) {
    if (appPackageName.contains("com.example.test")) {
        // do not notify
    }
    else {          
        // create notification and notify user  
    }
}
Haris Qureshi
  • 2,076
  • 1
  • 10
  • 26
3

For those who use Xamarin.Android. The Xamarin version of David Wasser's answer is below:

        //Create notification
        var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
        Intent uiIntent = PackageManager.GetLaunchIntentForPackage("com.company.app");

        //Create the notification
        var notification = new Notification(Android.Resource.Drawable.SymActionEmail, title);

        //Auto-cancel will remove the notification once the user touches it
        notification.Flags = NotificationFlags.AutoCancel;

        //Set the notification info
        //we use the pending intent, passing our ui intent over, which will get called
        //when the notification is tapped.
        notification.SetLatestEventInfo(this, title, desc, PendingIntent.GetActivity(this, 0, uiIntent, PendingIntentFlags.OneShot));

        //Show the notification
        notificationManager.Notify(0, notification);
chris hu
  • 408
  • 3
  • 9
1

Instead of showing the Splash activity on notification click, show your MainActivity because your splash activity will closed after some time but MainActivity will be remain open and

<activity 
android:name=".MainActivity"
android:launchMode="singleTask"
Kartheek
  • 6,611
  • 3
  • 24
  • 42
1

Use Splash as Fragment instead of Activity. Keep Splash fragment(7 seconds), replace the same with the desired one(landing page).

Add launchMode="singleTask" to the manifest.

As already stated by Rahul, onNewIntent() get called if application is already running else onCreate()

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

OR

Go with David's answer, seems promising.

Community
  • 1
  • 1
Mohammed Azharuddin Shaikh
  • 39,642
  • 13
  • 90
  • 114
0

when notification clicked and your code that redirect to your desire screen just replace that code by calling this method and redirect to particular screen on "true/false" result basis.

    private boolean isAppOnForeground(Context context) {
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
    if (appProcesses == null) {
      return false;
    }
    final String packageName = context.getPackageName();
    for (RunningAppProcessInfo appProcess : appProcesses) {
      if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
        return true;
      }
    }
    return false;
  }
krunal shah
  • 344
  • 2
  • 14
  • Code like this is overkill for this problem. There are simple ways to solve OP's problem instead of using hackey workarounds like this trying to figure out if your process is in the foreground. – David Wasser Jun 05 '15 at 12:15
  • @DavidWasser so by suggested your code, if i am on third screen of app and if i clicked on notification than i redirected on third screen not to the first screen, am i right ? – krunal shah Jun 08 '15 at 04:41
  • @DavidWasser ok, so many thanks for suggest me quality code for this issue. i keep note of this. – krunal shah Jun 08 '15 at 08:14
0
Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT

And maybe don't start the Splash Activity and reopen (bring to front) the MainActivity and update the UI with a listener that tells you, that you have a new notification (with a flag - boolean or with an Interface to make a listener).

Tazz
  • 711
  • 1
  • 7
  • 23
0

You can use an ordered broadcast to accomplish this.

1) Change your PendingIntent to start a BroadcastReceiver which will decide whether to start the activity or do nothing:

PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(this, DecisionReceiver.class), 0);

2) Create the decision BroadcastReceiver:

public class DecisionReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        context.sendOrderedBroadcast(new Intent(MainActivity.NOTIFICATION_ACTION), null, new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (getResultCode() == MainActivity.IS_ALIVE) {
                    // Activity is in the foreground
                }
                else {
                    // Activity is not in the foreground
                }
            }
        }, null, 0, null, null);
    }
}

3) Create a BroadcastReceiver in your activity that will signal that it is alive:

public static final String NOTIFICATION_ACTION = "com.mypackage.myapplication.NOTIFICATION";
public static final int IS_ALIVE = 1;
private BroadcastReceiver mAliveReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        setResultCode(IS_ALIVE);
    }
};

// Register onResume, unregister onPause
// Essentially receiver only responds if the activity is the foreground activity
@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mAliveReceiver, new IntentFilter(NOTIFICATION_ACTION));
}

@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(mAliveReceiver);
}
tachyonflux
  • 19,305
  • 6
  • 43
  • 65
0

notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK );

notificationIntent.putExtras(bundle);
PendingIntent pintent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Rahul
  • 481
  • 3
  • 18
-1

try adding this to your intent to bring activity to front if it is running in the background

Intent intent = new Intent(this, Splash.class); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);

ponnex
  • 818
  • 5
  • 17
  • This won't help. OP has stated that his `Splash` activity has already finished 7 seconds after app start, so it can't be reordered to the front because it isn't active any more. Not only that, he doesn't want to bring his `Splash` actvity to the front. He just wants to to bring the existing task to the foreground without starting any activities (if the app is already running). – David Wasser Jun 05 '15 at 12:12
  • Ohh I guess I misunderstood the situation hmm thanks for explaining it to me. Then i guess it should be `Intent intent = new Intent(this, DesiredActivity.class);` ? Since if that desired activity is already running then it wont be created again, it will just be reodered to front – ponnex Jun 05 '15 at 13:41
  • No. He wants the app started if it isnt already running, but if it is already running he just wants the app brought to the foreground again in whatever state it is in (ie: whatever activity was on the top when it went to the background should be on top when it comes to the foreground). In this case he doesn't want to **start any** activities. – David Wasser Jun 05 '15 at 14:17
-2

first of all set a default Task android:taskAffinity="com.example.testp.yourPreferredName" in your Application element in the Manifest file. Maintain your android:launchMode="singleTask" on your SplashActivity. Now since your SplashActivity is your main entry add this code to both onResume(), onNewIntent() and onCreate() (on a second thought onResume() is not recomended) -follow the comments in the code

//Note these following lines of code will work like magic only if its UPVOTED.
//so upvote before you try it.-or it will crash with SecurityException
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);

List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1000);    
    for(int i =0; i< taskInfo.size(); i++){
        String PackageName = taskInfo.get(i).baseActivity.getPackageName();
        if(PackageName.equals("packagename.appname")){// suppose stackoverflow.answerer.Elltz
            //if the current runing actiivity is not the splash activity. it will be 1
            //only if this is the first time your <taskAffinity> is be called as a task
            if(taskInfo.get(i).numActivities >1){
                //other activities are running, so kill this splash dead!! reload!!                 
                finish();
                // i am dying in onCreate..(the user didnt see nothing, that's the good part)
                //about this code. its a silent assassin
            }
            //Operation kill the Splash is done so retreat to base.
            break;
        }
    }

This code will not work on api 21+; to make it work you need to use AppTask, this will save you extra lines of code as you will not be in a Loop to find your Task.

Hope it helps

Elltz
  • 10,073
  • 3
  • 26
  • 55
  • This is completely silly. First of all, you haven't explained what this code is doing. Secondly, you are using a bazooka to make a pinhole. Thirdly, the method `ActivityManager.getRunningTasks()` isn't intended for this. Fourthly, you can accomplish exactly the same thing by calling `isTaskRoot()` in `onCreate()`: This will tell you if this `Activity` is the root activity (ie: the first one) in the task, which is what you are trying to do with all of this other code. Lastly, it is a hacky workaround because it doesn't solve the original problem. Better answers actually solve the problem. – David Wasser Jun 05 '15 at 12:09
  • im young sir, silliness is one of my very few creative activities, 1) this is an answer to a problem,it solves the problem **_Starting app only if its not currently running_**, 2).. 3) SIr i feel it is intended for what it can do,not just debugging 4) also Sir, there is **`singleInstance`** `launchMode` which will make `isTaskRoot()` return true on that particular activity even if its not,since it lives in another task, so all that code is really needed- i feel it solves the question, i even tried it, and it did, you should try it sir, its a bazooka work-around a pinhole ):- @DavidWasser – Elltz Jun 05 '15 at 16:27
  • Then we just will have to agree to disagree. – David Wasser Jun 05 '15 at 18:05
  • 1
    The documentation explicitely reads that `getRunningTasks()` *is only intended for debugging and presenting task management user interfaces.* Using it as this answerr does is an unnecessary hack. – rds Jun 07 '15 at 21:33
  • and i did not know that? im not trying to be rude sir, but i feel this question is about task management, activities runs on a task, launchMode's change how task are created, so i do not see how its unnecessary by your definition, anyways i feel it solves the problem-you can always try it, and Sir, thanks for your reply pointing out its usage, you can always downvote (: @rds btw nobody is getting the bounty so no hard feelings jkn – Elltz Jun 08 '15 at 20:13