3

I have an application that contains four activities, within the application the user will find himself navigating through the 4 activities constantly. The application also has an ongoing service in the background, the service displays a statusbar notifications, and listens for changes to the content that will then appear on the notification.

Currently, the service displays the notification whenever the user starts an action that required the notification to show, therefore, the notification is shown even when you are still using the application. The desired scenario is to show the notification only when the user has navigated out of the application.

I attempted to override lifecycle methods like this:

    @Override
protected void onPause() {  
    Intent intent = new Intent();
    intent.setAction(MyService.ACTION_DISPLAY_PENDING_NOTIFICATION);
    sendBroadcast(intent);
    super.onPause();
}

@Override
protected void onResume() { 
    super.onResume();
    Intent intent = new Intent();
    intent.setAction(MyService.ACTION_CANCEL_NOTIFICATION);
    sendBroadcast(intent);      
}

And the service goes like this:

@Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (action.equals(ACTION_DISPLAY_PENDING_NOTIFICATION)) {
            showNotification();
        }

        else if (action.equals(ACTION_CANCEL_NOTIFICATION)) {
            mNotificationManager.cancel(mNotId);
        }
    }

This, works. But, since the intent is sent anytime the user navigates away from an activity , I am experiencing an undesired behaviour and slight performance hit when the user navigates through the 4 activities. The service will attempt to display the notification even when going from Activity A to Activiy B, or any combination within the 4 activities.

The notification is immediately cancelled, as when a new Activity B starts it will call mNotificationManager.cancel(mNotifId) during onResume, but the notification was built and shown for a fraction of a second as the service was told to do so when leaving Activity A. That is the behavior I want to address, rather than building and showing this notifications unnecessarily,

Is there any way I can know when the user is leaving the activity to another application, i.e the homepage, etc; but not within the application itself?

EDIT:

Just to clarify, there are two things the activity would have to check for during the onPause method,

a) Is there any previous activity on the foreground? Why? Because the user could navigate out of the activity by pressing back, meaning the last activity on the stack would be displayed. In order to check for this, the answer from DennisDrew would work, we can check like this:

if(!ForegroundHelper.activityExistsInForeground()){
//show your notification
}

But that is not the only way the user could navigate out of the activity, the user could also press the HomeKey, in which case, whether activityExistsInForeground() evaluates to true or false, the notification should be displayed.

b) Is the user going to another activity in the application? For instance, user is on Activity A, A is the only activity on the foreground for now, user clicks on an UI element that launches Activity B. Despite of activityExistsInForeground() evaluating to false, user is not leaving the application, he is launching a new instance of an activity that was previously not on the freground.

I've tried to add flags such as private boolean launchingNewActivity = false as default, and setting the flag to true when I know I am going to another activity, say for instance during the click of an item on my listview:

litview.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                long arg3) {
            launchingNewActivity = true
            startActivity2(arg2);
        }           
    });

and then checking for that during onPause:

@Override
protected void onPause() {
    if(!ForegroundHelper.activityExistsInForeground() && launchingNewActivity){
    //show your notification        
}

But doing this, it never shows the notification, somehow the double check always defaults to false.

daniel_c05
  • 11,438
  • 17
  • 60
  • 78
  • check this out: http://stackoverflow.com/questions/5975811/how-to-check-if-an-activity-is-the-last-one-in-the-activity-stack-for-an-applica – Waqas Feb 02 '13 at 14:35
  • That doesn't exactly apply, because that assumes user is pressing back. – daniel_c05 Feb 03 '13 at 07:22
  • @daniel_c05 in your "if" statement in onPause(), shouldn't your check be... if(!(ForegroundHelper.activityExistsInForeground() || launchingNewActivity)) – dennisdrew Feb 07 '13 at 16:45

2 Answers2

3

What if you used a singleton reference? You could make a class like:

public static class ForegroundHelper {
    public static boolean[] activityStates = new boolean{false, false, false, false};
    public static final int ACTIVITY_A = 0;
    public static final int ACTIVITY_B = 1;
    public static final int ACTIVITY_C = 2;
    public static final int ACTIVITY_D = 3;

    public static boolean activityExistsInForeground(){
        for(boolean b : activityStates){
            if(b)
                return true;
        }

        return false;
    }
}

Then, in each activity's onResume() do:

//For your first activity (Activity A)
ForegroundHelper.activityStates[ForegroundHelper.ACTIVITY_A] = true;

And in each activity's onPause() do:

ForegroundHelper.activityStates[ForegroundHelper.ACTIVITY_A] = false;

And then in the onStop() of each activity do:

if(!ForegroundHelper.activityExistsInForeground()){
    //show your notification
}

By the way, I haven't ever done something like this, so I have no idea if it will work exactly as I've coded it...but let me know if this helps.

dennisdrew
  • 4,399
  • 1
  • 25
  • 25
  • That actually works, but partially only. Why? Because this assumes the user is pressing the back key on the current activity, if the user is going from A to B, for instance, even if B, C, D and not even A are not on the foreground, the user is not navigating out, is in fact going to another activity. – daniel_c05 Feb 07 '13 at 06:33
  • I added more explanation to the question. – daniel_c05 Feb 07 '13 at 06:55
  • 1
    I guess this was the closest to an accurate answer. Thanks – daniel_c05 Feb 08 '13 at 14:38
2

What I did for that is that I extended the application class and kept there the "topActivity" that I would set in each onResume /onPause method like so:

public class myApp extends Application{

   private Activity topActivity;
}

Now in each activity

@Override
protected void onResume() {

((myApp) getApplication()).setOnTopActivity(this);
}

@Override
    protected void onPause() {
        ((myApp) getApplication()).setOnTopActivity(null);
        super.onPause();
    }

This way, when the app is not visible, topActivity is null. So, before showing your notification, just check whether or not topActivity==null. If it is, your app is not in the foreground, you can show your notification.

If you don't have access to your application class, you can always store this value in a static way :)

psykhi
  • 2,981
  • 2
  • 16
  • 22