Android, Display alertDialog instead of notification when app is open
C

3

5

I followed this developer tutorial, and have Geofencing working within my app, as expected.

A notification is sent when a Geofence Transition occurs, from within an IntentService:

@Override
protected void onHandleIntent(Intent intent) {
    GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);

    ...        

    sendNotification(geofenceTransitionDetails);
}

private void sendNotification(String notificationDetails) {
    // Create an explicit content Intent that starts the main Activity.
    Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);

    // Construct a task stack.
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);

    // Add the main Activity to the task stack as the parent.
    stackBuilder.addParentStack(MainActivity.class);

    // Push the content Intent onto the stack.
    stackBuilder.addNextIntent(notificationIntent);

    // Get a PendingIntent containing the entire back stack.
    PendingIntent notificationPendingIntent =
            stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

    // Get a notification builder that's compatible with platform versions >= 4
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);

    // Define the notification settings.
    builder.setSmallIcon(R.mipmap.ic_launcher)
            // In a real app, you may want to use a library like Volley
            // to decode the Bitmap.
            .setLargeIcon(BitmapFactory.decodeResource(getResources(),
                    R.mipmap.ic_launcher))
            .setColor(Color.RED)
            .setContentTitle(notificationDetails)
            .setContentText("Return to app")
            .setContentIntent(notificationPendingIntent);

    // Dismiss notification once the user touches it.
    builder.setAutoCancel(true);

    // Get an instance of the Notification manager
    NotificationManager mNotificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    // Issue the notification
    mNotificationManager.notify(0, builder.build());
}

This is cookie-cutter from the tutorial. The intent is set-up in the Main activity:

private PendingIntent getGeofencePendingIntent() {
    // Reuse the PendingIntent if we already have it.
    if (mGeofencePendingIntent != null) {
        return mGeofencePendingIntent;
    }
    Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
    // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
    // addGeofences() and removeGeofences().
    return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}

How can I add functionality that suppresses the notifications if the app is open, and instead displays an AlertDialog to the user? Ideally, I'd like to be able to execute different tasks, depending on which view the user is currently in when the Geofence Transition occurs. Can I monitor/intercept the transition from within each view, or somehow globally?

Thanks in advance.

Ceto answered 16/9, 2015 at 2:8 Comment(0)
C
8

Some of the answers were incomplete, and so here is the complete solution to what I was looking for.

First off, set up MyApplication class, that implements ActivityLifecycleCallbacks:

public class MyApplication extends Application implements Application.ActivityLifecycleCallbacks {

    private static boolean isActive;

    @Override
    public void onCreate() {
        super.onCreate();

        registerActivityLifecycleCallbacks(this);
    }

    public static boolean isActivityVisible(){
        return isActive;
    }

    @Override
    public void onActivityResumed(Activity activity) {
        isActive = true;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        isActive = false;
    }

    ... no other methods need to be used, but there are more that 
    ... must be included for the ActivityLifecycleCallbacks
}

Be sure to name this in your manifest (only name line was added, rest is default):

<application
    android:name=".MyApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme"
    android:hardwareAccelerated="true">

What was done above is used to track the lifecycle of your app. You can use this to check if your app is currently in the foreground or not.

Next is to set up a BroadcastReceiver, wherever you would like code to run (in the event that the app is open when the trigger occurs). In this case, it is in my MainActivity:

protected BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        ... Do whatever you want here

        Toast.makeText(...).show();
    }
};

Register the receiver in your onCreate of the same activity:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...

    LocalBroadcastManager.getInstance(this).registerReceiver(mNotificationReceiver, new IntentFilter("some_custom_id"));
}

And don't forget to unregister it:

@Override
protected void onDestroy() {
    LocalBroadcastManager.getInstance(this).unregisterReceiver(mNotificationReceiver);
    super.onDestroy();
}

When a broadcast is received, the code within the receiver is executed.

Now, to check if the app is in the foreground, and send a broadcast if it is. Inside of the IntentService:

@Override
protected void onHandleIntent(Intent intent) {
    GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
    if (geofencingEvent.hasError()) {
        String errorMessage = getErrorString(this,
                geofencingEvent.getErrorCode());
        return;
    }

    int geofenceTransition = geofencingEvent.getGeofenceTransition();

    // Test that the reported transition was of interest.
    if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
            geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

        ...

        if(MyApplication.isActivityVisible()){
            Intent intnt = new Intent("some_custom_id");
            intnt.putExtra("message", geofenceTransitionDetails);
            LocalBroadcastManager.getInstance(this).sendBroadcast(intnt);
        }else{
            sendNotification(geofenceTransitionDetails);
        }

    } else {
        // Log the error.
    }
}

The important bit is the last nested if-statement:

if(MyApplication.isActivityVisible()){
    Intent intnt = new Intent("some_custom_id");
    intnt.putExtra("message", geofenceTransitionDetails);
    LocalBroadcastManager.getInstance(this).sendBroadcast(intnt);
}else{
    sendNotification(geofenceTransitionDetails);
}

Check if the app is in the foreground using MyApplication.isActivityVisible(), as defined above, and then either send the notification, or send a broadcast. Just make sure that your intent code (i.e. "some_custom_id") matches on your sender and receiver.

And that's about it. If the app is in the foreground (specifically the MainActivity), I execute some code. If the app is not in the foreground, I send a notification.

Ceto answered 18/9, 2015 at 4:56 Comment(0)
Z
1

The easiest way would be to use LocalBroadcastManager or some event bus.

So when transition happens you should send local broadcast from IntentService and catch it with some component X in between IntentService and any of your Activity's. Component X must track if any of your Activity's is in foreground and

  • if yes - pass other local broadcast up (to the foreground Activity),
  • if not - show notification.

Please note that in Android you cannot track easily if your app is in foreground or not (and if you have more than 1 Activity, you cannot do it properly in my opinion) but you can try.

Zoi answered 17/9, 2015 at 12:50 Comment(2)
Right now, the best I can think with the LocalBroadcastManager is to include a receiver in each of my activities. Is there a way to blanket all of the activities with a single receiver? Also, how do I suppress/stop notifications from within the IntentService when the app is open?Ceto
What I've found works best is a combination between your answer, which lets me show alerts and perform tasks, and this answer, which allows me to determine if the app is running or not.Ceto
F
0

a) You can notify your service of the activity's lifecycle events.

b) You can keep the current state of your UI in a static field in the activity and check it from the service before showing the notification.

Forborne answered 16/9, 2015 at 3:49 Comment(1)
I'm not sure if I'm getting this right, but those sound like they would only suppress the notifications. How can I use those to display alertDialogs when the geofence transition occurs? Or to update the view? Please provide some code with your answer.Ceto

© 2022 - 2024 — McMap. All rights reserved.