Android App, Activity State (Running, Not Running, Foreground/ Background)
Asked Answered
S

4

9

I have come across a requirement but I am not able to get the correct way of implementation and hence need your help.

What I want to do? - I want to perform an action depending upon notification I get as follows:

  1. When app is open and in foreground i.e. visible to user and I get notification I simply show a popup to start my Activity B
  2. When app is closed i.e neither in background nor in foreground and I get the notification I will start my application first and then start Activity B
  3. When the app is running BUT in background i.e in recents but not visible to user then I want to start my Activity B without re-starting the application. Also, in this case when user presses back on Activity B, they should see the screen they left off before sending it to background.

What I have done? I have achieved point #1 and #2. I want to achieve point #3. I have tried the below

public static boolean isApplicationBroughtToBackground(final Activity activity) {
  ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
  List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);

  // Check the top Activity against the list of Activities contained in the Application's package.
  if (!tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      return false; // Never happens.
    }
  }
  return true;
}

However this returns true in both the cases, point #2 and #3, so I am not able to distinguish only #3.

Also I tried the below in every Activity I have,

@Override
protected void onPause() {
    super.onPause();
    saveIsPausedInPref(true);
}

@Override
protected void onResume() {
    super.onResume();
    saveIsPausedInPref(false);
}   

But, it also doesn't give the desired result coz if the app is sent to background by pressing the Home button my Preference will have isPaused = true and if the user removes the app from recent then it will stay true and again I will not be able to differentiate Point #2 and #3 when the notification arrives.

Apologies for the whole story, but I hope I am able to explain my requirement.

Thanks in advance. :)

Edit:

        <activity
            android:name=".HomeActivity"
            android:screenOrientation="portrait" >
        </activity>
        <activity
            android:name=".ChatProfileActivity"
            android:screenOrientation="portrait" >
        </activity>
Scylla answered 2/6, 2015 at 10:36 Comment(4)
So as I able to understand, you need to switch to the app which was on the foreground when user pressed back button on Activity 3?Pentahedron
Yes may be if that's a possible way. Ideally I would need to recall my App from background and start my Activity B on top of it.Scylla
show your manifest file, I want to see Activity A and B declarations.Pentahedron
@MurtazaKhursheedHussain - Please see my edit. Nothing special there I guess.Scylla
T
2

To differentiate between case #2 and case #3, you can do the following:

Launch ActivityB if it is not case #1. In ActivityB.onCreate() do this:

super.onCreate(...);
if (isTaskRoot()) {
    // ActivityB has been started when the app is not running,
    //  start the app from the beginning
    Intent restartIntent = new Intent(this, MyRootActivity.class);
    startActivity(restartIntent);
    finish();
    return;
}
... rest of onCreate() code here...
Thuggee answered 11/6, 2015 at 16:46 Comment(2)
Thanks David, I have used this only for now. :) Was looking for a better answer. Until I find will stick with this.Scylla
What do you mean by a "better answer". What is wrong with this? Maybe your architecture is broken because you are trying to differentiate between all these cases. What is it exactly you are trying to achieve?Thuggee
D
4

Below code works for me

In AndroidManifest.xml

<application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:name=".MyApplication"
            >

MyApplication.java

public class MyApplication extends Application {
private ActivityLifecycleCallbacks myLifecycleHandler;

@Override
    public void onCreate() {
        super.onCreate();
        myLifecycleHandler=new MyLifecycleHandler();
        registerActivityLifecycleCallbacks(myLifecycleHandler);
    }
}

MyLifecycleHandler.java

public class MyLifecycleHandler implements Application.ActivityLifecycleCallbacks {
    private static final String TAG = MyLifecycleHandler.class.getSimpleName();

private static int resumed;
private static int paused;
private static int started;
private static int stopped;

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}

@Override
public void onActivityDestroyed(Activity activity) {
}

@Override
public void onActivityResumed(Activity activity) {
    ++resumed;
}

@Override
public void onActivityPaused(Activity activity) {
    ++paused;
    Log.d(TAG, "application is in foreground: " + (resumed > paused));
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}

@Override
public void onActivityStarted(Activity activity) {
    ++started;
}

@Override
public void onActivityStopped(Activity activity) {
    ++stopped;
    Log.d(TAG, "application is visible: " + (started > stopped));
}

public static boolean isApplicationVisible() {
    return started > stopped;
}

public static boolean isApplicationInForeground() {
    return resumed > paused;
}
}

Now using myLifecycleHandler methods you can get all states you need.

isApplicationInForeground means -> Atleast one activity is in visible state.

isApplicationVisible means -> Atleast one activity is there which got started but not stopped, means application is running state

if isApplicationInForeground is true isApplicationVisible will be always true, but vice versa is not true

Divertissement answered 10/6, 2015 at 19:8 Comment(1)
Whats the difference here between isApplicationVisible and isApplicationInForeground?Scylla
T
2

To differentiate between case #2 and case #3, you can do the following:

Launch ActivityB if it is not case #1. In ActivityB.onCreate() do this:

super.onCreate(...);
if (isTaskRoot()) {
    // ActivityB has been started when the app is not running,
    //  start the app from the beginning
    Intent restartIntent = new Intent(this, MyRootActivity.class);
    startActivity(restartIntent);
    finish();
    return;
}
... rest of onCreate() code here...
Thuggee answered 11/6, 2015 at 16:46 Comment(2)
Thanks David, I have used this only for now. :) Was looking for a better answer. Until I find will stick with this.Scylla
What do you mean by a "better answer". What is wrong with this? Maybe your architecture is broken because you are trying to differentiate between all these cases. What is it exactly you are trying to achieve?Thuggee
I
1

I know that u figured out 1 & 2 but ill right my own toughts in case u did something else and u want to take a look.

  1. How to know whether your app is in foreground?

    this should be the correct way, if its in foreground then post an event via eventbus, eventbus is my preffered way.

  2. Start the app at a specific activity with stack? take a look at TaskStackBuilder with this answer, it will provide information how to this properly.
  3. Resume app with a certain activity while retaining the current stack? Check the state of the app with the callbacks, then when receiving the notification open the activity without FLAG_ACTIVITY_NEW_TASK. This should do the tricl.
Inane answered 9/6, 2015 at 12:47 Comment(2)
Could you please stress on 'Check the state of the app with the callbacks'?Scylla
At the activity callbacks check if it hit onActivityPaused and not onActivityDestroyd. This will allow u to understand the state of your app.Inane
F
1

If you change to not show any pop up, just to go to the Activity B always (destroyed, background or foreground), you don't need any of this check. Just to work with the flags FLAG_ACTIVITY_X on your notification (it's GCM this notification?).

LaunchMode on Manifest

Intent Flags

You can use the onNewIntent method to check the intent if comes from a notification and start Activity B.

Notification Proper BackStack

Fielder answered 10/6, 2015 at 12:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.