15

There are three different cases:

1) A user launches an app, navigates in it, pressed home and click on the app icon again to launch our app again.

2) A user launches an app, navigates in it, presses home, chooses recent and click on the app to launch our app again.

3) A user launches an app, navigates in it, click something in the app (TextView with a link), which calls another app (as example Email) and user clicks back button, which bring us back to our app.

I know about flag "clearTaskOnLaunch" flag, it solves case #1.

I know about about flag "excludeFromRecents", it solves case #2 (may be not the most user friendly solution, but it works).

What about case #3? I have a workaround right now. However, I will have to put it on all activities which can be lead to another app. I wonder, whether there is better way to solve it (without handling it in all such activities).

Victor Ronin
  • 22,758
  • 18
  • 92
  • 184
  • So you want the user to go back to start even though the user is in the middle of something? Sounds like something you need to manage yourself. – Warpzit May 04 '12 at 15:28
  • Out of curiosity, what use case are you realising that would require this functionality? – Gallal Jan 31 '13 at 23:03
  • 2Gallal: A password protection of an app. So, you can freely navigate inside of it, but as soon as you exited to any other app then you have to go through the password activity when you come back. Upvotes are appreciated :) – Victor Ronin Jan 31 '13 at 23:20

9 Answers9

6

This should be handled on the Application level.

For API level 14, you can register an ActivityLifeCycleCallback in your Application class

public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)

You can use it, to know on an Application level, which activities are destroyed, paused, resumed etc etc. Whenever, an activity is paused, without a new activity being created/resumed, you should clear the Activity stack, and re-launch your startActivity

If you target SDK versions < 14, you should implement your own method, to know which activities are created/resumed and paused, and do the same whenever an activity is paused, without a new activity being created/resumed

Entreco
  • 12,738
  • 8
  • 75
  • 95
  • Thank you. That's useful. However for right now, we have to support OS 2.1 and higher (API 7) – Victor Ronin May 10 '12 at 22:06
  • You can use android support library to get access to higher level apis in older versions... – Ron May 20 '12 at 04:56
  • @userSeven7s registerActivityLifecycleCallbacks is not supported by the android support library. You can use this project instead: https://github.com/BoD/android-activitylifecyclecallbacks-compat – poiuytrez Jul 18 '13 at 09:27
4

It seems a similar question has already been asked. It sounds like the OP came up with a working solution. How do I collapse "child activities"?

EDIT: Instead of using a button you can use a boolean to tell whether or not you need to collapse back to the main activity. Have your root activity extend from Activity and the child activities extend from CollapsableActivity. To get this to work in all cases I added startOutsideActivity() and startOutsideActivityForResult().

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class CollapsableActivity extends Activity {
    private boolean returnToRoot;
    public static final int COLLAPSE_BACK = -1; // something other than RESULT_CANEL (0)

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        returnToRoot = true;
    }

    @Override
    protected void onStart() {
        super.onStart();
        returnToRoot = true;
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        // start collapsing the stack
        if (returnToRoot) {
            setResult(COLLAPSE_BACK);

            finish();
        }
    }

    @Override
    public void startActivityForResult(Intent intent, int requestCode) {
        super.startActivityForResult(intent, requestCode);
        returnToRoot = false;
    }

    public void startOutsideActivityForResult(Intent intent, int requestCode) {
        super.startActivityForResult(intent, requestCode);
        returnToRoot = true;
    }

    @Override
    public void startActivity(Intent intent) {
        // call startActivityForResult to make sure and catch the collapse condition
        super.startActivityForResult(intent, 0);   
        returnToRoot = false;
    }

    public void startOutsideActivity(Intent intent) {
        super.startActivity(intent);    
        returnToRoot = true;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == COLLAPSE_BACK) {
            returnToRoot = true;
        }
   }
}

This worked properly for me in all cases you listed. The only difference is you need to call startOutsideActivity() or startOutsideActivityForResult() when you navigate away from you app. Personally, I think this adds clarity to your intentions. Hope it helps!

Community
  • 1
  • 1
Aholio
  • 273
  • 3
  • 8
  • It's not exactly the same. The difference is that in collapsing, a user explicitly says what he wants and in this problem, we should figure out what's going on (do we need to collapse or do we need just navigate). – Victor Ronin May 10 '12 at 22:13
4

1) define a public static normalPause = true variable in a Class.

2) in onPause method of all of your activities set it false (I am worry. We might not be in a normal pause)

2) in onCreate method of all of your activities set it true (Do not worry. We are in a normal pause)

3) in onResume of all of your Activities:

if(!Utilities.normalPause)
{
    this.finish()
}

Enjoy!

Bob
  • 22,810
  • 38
  • 143
  • 225
  • I need to try it out, but it looks really promising. For some reason, I was always thinking about onPause() and onResume() only and based on them, there is no way to determine whether we navigate inside of our app or outside of it. And using onCreate - it should work. I will try it. – Victor Ronin May 10 '12 at 22:23
  • seems to work. But not for multiple instances of acitivty. (when trying to figure out if activity was started from navigating back in the app) I cant distinguish in onResume if it is called because this instance set normalPause=true or another instance set it. So I cant tell if the Acitivty was started from navigating back or forward. – Jakob Aug 28 '12 at 12:56
1

I know you don't want to manage it in all activities but you can do this and still handle the code in one place with a super activity

public abstract class BlundellActivity extends Activity {
     @Override
     public void onPause(){
         // Whatever strategy you want
     }
}

public class SomeActivity extends BlundellActivity {
     // Do whatever you normally want to do
}

public class SomeActivity extends BlundellActivity {
     // Do whatever you normally want to do here as well
}
Blundell
  • 75,855
  • 30
  • 208
  • 233
1

Perhaps, android:noHistory is what you're looking for. If you declare all your activities except StartupActivity with this attribute, then they will be finished as the user navigates away from them and only StartupActivity will appear.

a.ch.
  • 8,285
  • 5
  • 40
  • 53
  • The issue of this method would be that it will screw up the navigation in our application. If we will have A->B->C->D in our app and we will try to use back button, while being on D, it won't go back to C, because it's no in the history – Victor Ronin May 10 '12 at 22:16
1

You can try this steps:

  1. use one boolean static flag isFinish in StartupActivity with default false value.
  2. in onCreate() of StartupActivity set isFinish value to false.
  3. write below code in onResume() method of all activities in your project.

    if(isFinish)
    {
       finish();
    }
    
  4. set isFinish value to true when you open any native app like email, browser etc.

or

5 . set isFinish value to true in onBackPress() method whenever you want to close application on back press.

Case 6: if android browser open on clicking on any link then use below code is onPause() method

if(isBrowserRunning("com.android.browser"))
{
   isFinish = true;
    finish();
}

////////////////

 private boolean isBrowserRunning(String processName)
        {
            ActivityManager manager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
            String packageName = manager.getRunningTasks(1).get(0).topActivity.getPackageName();
            Log.i("LogTest", "Current process package name: " + packageName);

            return processName.equalsIgnoreCase(packageName); 
        }

You can create a sample project to know other browser package name like opera mini, US browser etc.

add below permission in manifest:

<uses-permission 
        android:name="android.permission.GET_TASKS" />
Vivek Kumar Srivastava
  • 2,158
  • 1
  • 16
  • 23
0

You can call this.finish() on the onPause() of your Activity, that way the activity will be closed in the three cases.

ChristopheCVB
  • 7,269
  • 1
  • 29
  • 54
  • 1
    Let's say we have a following navigation StartupActivity->ActivityA->ActivityB->Email application. In the case, if we finish ActivityB, back button will just return to ActivityA. If we will start finishing all activities onPause(), it will screw up our internal application navigation big way (back button will stop working inside of our app). – Victor Ronin May 04 '12 at 21:13
0

Instead of using multiple solutions you can use a single one that solves all the problems.

Check this answer:

https://stackoverflow.com/a/8576529/327011

With a Broadcast and BroadcastReceivers in each activities of your application you can kill all activities whenever your application goes to background.

UPDATE:

To detect if your application when to background you can use onStop, check this to understand the theory: Activity side-by-side lifecycle

And this is the implementation: https://stackoverflow.com/a/5862048/327011

I think this is all you need :-)

Community
  • 1
  • 1
neteinstein
  • 17,529
  • 11
  • 93
  • 123
0

You need to use bundle and pass appropriate parameter/or parameters from the calling app (i.e. click something in the app (TextView with a link)).

Retrieve the parameter in the called app (Email app).

You can send the name of the activity in the parameter.

Now being in Email app(the called app) Click of back button navigate back to your calling application.

Optionally you can save the state of activity from the caller program, as required.

You need to use Bundle, and Intent to implement this logic.

Code snippet:

In the calling program, we need to store parameters/data required for back button functionality in the called program.

Bundle bndleData = new Bundle(); Use putString(), putInt() methods of Bundle class.

    String prefix = getPackageName().toString();
           (this prefix can be stored in application level constants.java file as applicable)

    bndleData.putString("ParentActivity", this.getLocalClassName());

Also store additional parameters if required bndleData.putString("paramName", valueofParamName); bndleData.putInt("IntChannelImage", chImageInt);

    Intent intent = new Intent(v.getContext(), AMRChannelPlayer.class);

    intent.putExtra(prefix + "bndleChnlData", bndleData);

    startActivity(intent);

Caller Program: Retrive the data, activity nae from bundle and use it in back button implementation:

prefix = getPackageName().toString(); Bundle extras = getIntent().getBundleExtra(prefix + "bndleData");

String parentActivity = extras.getString("ParentActivity"); extras.getString("paramName");

I hope this helps you.

Sree Rama
  • 1,207
  • 12
  • 25