Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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>
like image 720
Atul O Holic Avatar asked Jun 02 '15 10:06

Atul O Holic


3 Answers

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

like image 190
Akhil Avatar answered Nov 01 '22 10:11

Akhil


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...
like image 2
David Wasser Avatar answered Nov 01 '22 10:11

David Wasser


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.
like image 1
EE66 Avatar answered Nov 01 '22 10:11

EE66