Pergunta

I am developing an app with numerous Activities. I would like to create a persistent notification that (more or less) says, "AppName - Return to AppName" that will be present whenever my background services are running. Creating and disposing of the notification was no problem.

Now, the user could be on any of several screens/Activities, leave the application, then want to re-enter the app via the notification. The problem is, the notification must have an intent, which launches a predetermined Activity. I want the notification to re-enter the app in whatever Activity is at the top of the history stack.

My first attempt at an ugly workaround was to make an activity (let's call it "returnFromNotify") whose only job was to "finish" itself in it's "onCreate". The notification would open "returnFromNotify" in the scope of the applications history, which would then immediately remove itself, sending the user back to the previous history state in the application stack. This seems to work... unless the user has used "back" to completely back out of the app. Then, when they hit the notification, "returnFromNotify" loads, then finishes, sending them back out to the home screen (as there are no activities in the history stack for the app).

I considered trying to detect if there was anything in the history stack before "returnFromNotify", and if not, fire up my main Activity. I can't seem to find a way to do this, either.

Any input or suggestions for a Java/Android novice? FYI, My primary history is with script-based languages.

Foi útil?

Solução

I like your original idea of creating a "returnFromNotify" activity better than your proposed workaround, as it is possible to detect if the ResumeActivity is at the bottom of the stack (and therefore the only activity in the stack).

Here's how you can do it:

Add your ResumeActivity to the manifest and specify the noHistory attribute:

<activity android:name=".ResumeActivity" android:noHistory="true" />

Specifying noHistory will make sure this Activity won't stay in the stack as soon as it finishes. This way you know that only a currently running instance of the ResumeActivity will show up in the stack.

In order to check the application stack, you'll also have to ask for the GET_TASKS permission:

<uses-permission android:name="android.permission.GET_TASKS" />

Now you can use ActivityManager::getRunningTasks() to determine if ResumeActivity is the only activity in the stack:

public class ResumeActivity extends Activity {

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

        if(isOnlyActivityInStack()) { //check the application stack
            //This activity is the only activity in the application stack, so we need to launch the main activity
            Intent main = new Intent(this, MainActivity.class);
            main.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(main);
        } else {
            //Return the user to the last activity they had open
            this.finish();
        }
    }

    /**
     * Checks the currently running tasks. If this activity is the base activity, we know it's the only activity in the stack
     *
     * @return  boolean  This activity is the only activity in the stack?
     **/
    private boolean isOnlyActivityInStack() {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        boolean onlyActivityInStack = false;

        for(RunningTaskInfo tasks : manager.getRunningTasks(Integer.MAX_VALUE)) {
            if(tasks.baseActivity.getPackageName().equals(this.getPackageName())) { //find this package's application stack
                if(tasks.baseActivity.getClassName().equals(this.getClass().getName())) {
                    //If the ResumeActivity is the base activity, we know that it is the only activity in the stack
                    onlyActivityInStack = true;
                    break;
                }
            }
        }

        return onlyActivityInStack;
    }

}

I know you asked this question over 2 years ago, but I'm providing this answer in case anyone else runs in to this particular situation (as I did). I think you were on the right track with the solution you were originally working towards.

Outras dicas

Okay, I believe that I have found a satisfactory work-around for my specific case. I've added a static integer to my "mainActivity", and each time it's "onCreate" is fired, it increments the integer. Each time it's "onDestroy" is fired, it decrements.

In my "returnFromNotify", I look at the static integer to see if it is greater than 0. If so, I assume there is an active "mainActivity", and that running "finish" inside "returnFromNotify" will return there. Otherwise, it assumes the users has "backed" out, finishes itself, then uses "startActivity" to fire up a new instance of "mainActivity".

This is not a universal solution, but for my purposes, I think it will suffice. I am still open to other answers, and if someone can punch a hole in my logic, please do so - constructive criticism is welcome. Thanks.

I guess there is no easy way to do this but instead of adding a counter in the mainActivity I would extend Application:

Base class for those who need to maintain global application state. You can provide your own implementation by specifying its name in your AndroidManifest.xml's tag, which will cause that class to be instantiated for you when the process for your application/package is created.

I would mantein the logic there and have a method like:

public Intent getIntentForLastActivityShown();

to be called when the notification item is clicked.

My first approach would be to use SharedPreferences and store a key value pair called something like lastDisplayedActivity. Then in each Activity's onResume (and possibly `onCreate') you would have a line like this:

sharedPreferences.edit().putInteger("lastDisplayedActivity", ReturnFromNotify.THIS_ACTIVITY_NAME);

In other words, you store an application-wide variable indicating which activity was last displayed. Then you just grab this variable from SharedPreferences and launch the corresponding activity.

I usually use activity named "Launcher" that checks state of my application model and starts activities (or does other things) depending on model rules. I put Model object in my Application class. Model can use Preferences to store its state. I do it to avoid static fields in activities.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top