Restoring an activity stack from a notification? - android

I have a notification that create from an activity in a stack, and it's important that the stack be restored. Each activity in the stack should only be there a single time.
So I have this:
A -> B, B starts notification
For my intent, I need to restore A -> B on the stack. I tried starting A, but the intent didn't seem to have any extras to start startActivity immediately.
What combination of flags and XML attributes do I need to restore a stack?
If the user is on B and taps the notification, it should not start a new stack. Reloading everything is fine, but it should not go A -> B -> B.

In your notification intent use
i.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Since you can't/shouldn't change the back button function of backing back across different Apps. In B activity use an 'Up' button to navigate back up the stack using something like the following
Intent upIntent = new Intent(this,ParentActivity.class);
upIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(upIntent);
finish();
Intent.FLAG_ACTIVITY_REORDER_TO_FRONT will bring an existing activity to the front, else it will create it keeping the stack free from duplicated activities

Related

Android - Keep bunch of activities in each task

I have few activities.For example A,B,C,D,E,F.
My Home screen is activity A.Now I start Activity B from that and C from Activity B.
So now my stack is A->B->C.
Now if I press back I should be able to navigate in reverse Order.
But If I start Activity D from C.
I want my stack to be A->D as I want to kill B and C.
My expectations is A is my Home screen which should be always there.B and C on having one task and D,E,F having other task.
There is one more catch - I can even start activity D->E->F from A and B from F. In that case when I start B from F, D,E,F should be removed from stack
There are several ways to accomplish this, depending on your application requireents. You should look at using startActivityForResult() in order to start one (or more) activities which can then return a result to the Activity that launched it. This may be a good solution for you.
The other alternative is to use your ActivityA as a kind of "dispatcher. In this case, when C wants to launch D but also clear the task stack back to A first, you can do something like this in C:
Intent intent = new Intent(this, ActivityA.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra("launchActivity", "ActivityD");
Using the flags CLEAR_TOP and SINGLE_TOP will cause all activities on top of the existing instance of ActivityA to be finished and cause the Intent to be routed to onNewIntent() of the existing instance of ActivityA.
Now, in ActivityA, override onNewIntent() with something like this:
if (intent.hasExtra("launchActivity)) {
if (intent.getExtra("launchActivity").equals("ActivityD")) {
Intent launchIntent = new Intent(this, ActivityD.class);
startActivity(launchIntent);
} else ...
}

How to set no History for all Activities

My Android Application is based on four Activities. The last activity starts an Intent Service and set itself to background.
Before I sent the last Activity to background I would like to remove all four Activities from backstack history
I tried already to set the no history = true attribute for the activities. This causes errors when I use startActivityForResult, so I need a different solution
I don't really think there is an "good" way to do this, I would try something like this:
First create a SelfClosingActivity which only job is to close itself after opening:
public void onCreate(Bundle savedInstanceState()) {
super.onCreate(savedInstanceState);
finish();
}
When you want to close all the Activities (after you start your IntentService I guess) run the SelfClosingActivity and add flags to clear the current stack.
Intent intent = new Intent(this, SelfClosingActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
This will clear the current stack before opening SelfClosingActivity and SelfClosingActivity will close itself leaving the stack completely empty.
FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_NEW_TASK will do what you want.
FLAG_ACTIVITY_CLEAR_TOP
Added in API level 1 int FLAG_ACTIVITY_CLEAR_TOP If set, and the
activity being launched is already running in the current task, then
instead of launching a new instance of that activity, all of the other
activities on top of it will be closed and this Intent will be
delivered to the (now on top) old activity as a new Intent.
For example, consider a task consisting of the activities: A, B, C, D.
If D calls startActivity() with an Intent that resolves to the
component of activity B, then C and D will be finished and B receive
the given Intent, resulting in the stack now being: A, B.
This launch mode can also be used to good effect in conjunction with
FLAG_ACTIVITY_NEW_TASK: if used to start the root activity of a task,
it will bring any currently running instance of that task to the
foreground, and then clear it to its root state. This is especially
useful, for example, when launching an activity from the notification
manager.
Refer to Tasks and Back Stack for more info

How to keep layout and its data

I need your help, I have 2 activities A and B where A is the main activity.
Now B starts the activities from A using startActivityForResult() and from B when I finish it will go back to activity A.
This works fine but the actual purpose was like the Gmail application when you go back from B to A and then start activity B again from A, then A need to start activity with its last screen as i left it.
For example: from inbox->label->draft in gmail how to achieve this to keep data/layout as it is.
Maybe android:launchMode="singleInstance" on activity A and B will get it done?
How to switch Activity from 2 activities without losing its data and current state of data
Ok I have got solution that when every time start your application that time only activity will be created and view and put in stack so the next time you start again this activity just open from background so the activity will available in stack and just brought to front using start activity
From A to start ActivityB
Intent intent = new Intent(context,ActivityB.class);
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
Now for start ActivityA from ActivityB
Intent intent = new Intent(getInstance(), ActivityA.class);
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
It just brought front while activity start and put the current activity into background

Android:Delete a activity in backstack

I have got 4 activity let it be A->B->C->D.In every A,B,C activity user need to enter data all data will sent to server in C activity if the user data is correct he will move on to D activity and all the activity A,B,C removed from stack.If the data is in correct i need give the user to reenter data i.e is on back press it has to move C->B->A.My question is How to remove A,B,C activity when user enter D activity.
Use FLAG_ACTIVITY_CLEAR_TOP this shall solve your problem
From the Android documentation:
If set, and the activity being launched is already running in the
current task, then instead of launching a new instance of that
activity, all of the other activities on top of it will be closed and
this Intent will be delivered to the (now on top) old activity as a
new Intent.
For example, consider a task consisting of the activities: A, B, C, D.
If D calls startActivity() with an Intent that resolves to the
component of activity B, then C and D will be finished and B receive
the given Intent, resulting in the stack now being: A, B.
Use it like
Intent intent = new Intent(getApplicationContext(),
yourActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
and also, take a look at this question:
Activity with Flag FLAG_ACTIVITY_CLEAR_TOP (android)
Edit : I thought you want to move to your home activity from D and want to remove all activities from stack
Your stack would be like homeactivity , A , B , C , D
so i gave you this solution as this shall remove all activities in stack on top of your home activity.
If you want to clear the the stack while going to D, for that you can use FLAG_ACTIVITY_TASK_ON_HOME or FLAG_ACTIVITY_CLEAR_TASK
But both of these for api level 11.
The correct answer interesting for me also, but I can offer a solution:
For example you start activity A from O: O->A->B->C->D.
On activity O you can put in android manifest android:launchMode="singleTop"
Then, when data are ok, you can start activity O with flag "FLAG_ACTIVITY_CLEAR_TOP" - it remove from stack A,B,C and will be called method onNewIntent (Intent intent) http://developer.android.com/reference/android/app/Activity.html#onNewIntent(android.content.Intent) in O, where you can start activity D.
You can start activities with startActivityForResult, and call setResult from for example activity D, in C activity you can listen activity result, and related of this result finish activity or not, or call setResalt from C activity ...
When you want to move D activity there you need to check your
conditions and if your condition is satisfied then you need to enter
into your Next activity(i.e., D) .In that case you need to use the
following code..
Intent intent = new Intent(this,D.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
Suppose In on backpress you need to use finish(). to move back i.e., C
-> B -> A.
Try this piece of code with some modifications:
Intent intent = new Intent(this, D.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // To clean up all activities
startActivity(intent);
Try looking for Activity's public method startActivity(Intent i) and finish() here
In usage wise, it should look like this.
Intent i = new Intent(MainActivity.this, MainActivity.class);
startActivity(i);
finish();
Hope this helps :D

SINGLE_TOP | CLEAR_TOP seem to work 95% of the time. Why the 5%?

I have a nearly-finished application with a non-trivial activity structure. There are push notifications associated with this app, and selecting the notification entry is supposed to bring up a specific activity regardless of whether the app is foreground/background/not active.
If the app is not active, I have been able to successfully start the app and auto-navigate to the appropriate part. However, when the app is active, I have a problem. I will present a simplified version of the issue, to communicate the nature of the problem, and I will post the details of my app's activity structure and relevant code as needed (actually, working on that now).
So, my app's activity stack (greatly simplified) looks like this:
A -> B -> X
Where A, the root activity, is a login page; B is something of a "home page" and X is one of several activities that can be started from the home page (but only one instance active at a time; as these can only be started from B).
When the notification is selected, I need the application to automatically navigate to B, regardless of what state it was in beforehand - whether [A], [A -> B], [A -> B -> X] or [ ] (app not active).
My notification passes an Intent to activity A. I have tried using CLEAR_TOP and NEW_TASK flags, and none. A currently has launchmode=singleTask. Doing this, I figure I am addressing all possible existing stack configurations and reducing them to [A]. The Intent also carries an extra which identifies it as coming from a notification, as opposed to a usual launch.
Activity A, upon identifying the Intent as being sent from the notification (it can do this in both onCreate() and onNewIntent() ), sends an Intent to Activity B. This Intent contains CLEAR_TOP and SINGLE_TOP. B has launchmode=singleTop.
95% of the time, this works as desired, and after pressing the notification, the app's stack is [A -> B].
Roughly 5% of the time, the app somehow ends up with a stack of [A -> B -> B].
Any ideas on what's happening here, or anything I'm doing wrong?
I'll post more detail if this turns out to be a non-trivial problem. In fact, posting more details now...
~~~~~~~~~~~~~~~~~~~~~~~MORE DETAIL~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Stepping through the debugger shows that, every time A sends its intent to B, the existing instance of B is onDestroy()'d before being onCreate()'d and then also having its onNewIntent() called. This seems odd to me, and suggests that either I misunderstand the flags I am using (CLEAR_TOP and SINGLE_TOP), or something else is interfering with them.
I have not successfully reproduced the erroneous stack structure in debug. Not sure if it's because it doesn't happen in debug, or I just haven't tried enough times.
Code for Intents being made:
In the C2DM receiver service:
protected void onMessage(Context context, Intent intent) {
int icon = R.drawable.some_drawable;
CharSequence tickerText = "blah";
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, when);
//Context context = getApplicationContext(); //Don't need this; using the context passed by the message.
CharSequence contentTitle = intent.getStringExtra("payload");
CharSequence contentText = "Lorem ipsum dolor si amet,";
Intent notificationIntent = new Intent(this, LoginPage.class);
//notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); //Tried with and without
notificationIntent.putExtra(PushManager.PUSH_INTENT, PushManager.PUSH_INTENT); //Indicator that this was send from notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
notificationManager.notify(PushManager.ALARM_NOTIFICATION_ID, notification);
}
In LoginPage (Activity A), after successful login:
Intent i = new Intent(LoginPage.this, TabHomePage.class);
// (If we're automatically going to tab 2, inform next activity)
if(fromNotification) {
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
i.putExtra(TabHomePage.TAB_NUMBER, TabHomePage.TAB_2);
}
startActivity(i);
For more detail on the activity stack structure, here's the picture:
http://i89.photobucket.com/albums/k207/cephron/ActivityStack.png
And here's the thousand words:
Activity A is a login page; upon successful login, it starts B.
B is a TabActivity, which contains three Activities within it (represented by C, D, E).
Each of C, D, and E is actually an ActivityGroup whose child Activities mimic the usual stack behaviour of Activities.
So, each tab contains its own stack of activities, and switching between tabs changes which of these stacks is currently being pushed to/popped from by the user's navigation (each tab contains a stack of ListActivities browsing through a hierarchical structure of entities). These can also start new activities beyond the giant 'B' TabActivity (represented by X).
So, upon logging in from Activity A, at least three activities are created before more user input is accepted:
-B is created (and is seen, because of the TabWidget now at the top of the screen)
-One of the ActivityGroups is created: whichever one belongs to the default tab. This ActivityGroup remains unrepresented on screen, however...it only shows the top activity of its stack of child activities.
-So, finally, the "root" activity of that ActivityGroup's stack is created (in the picture, F is an example of such an Activity). This Activity shows itself below the TabWidget.
After each tab has been visited once, no more Activities are created/destroyed by switching between tabs (not counting memory kills).
Pressing back in any tab finish()es the Activity at the top of that stack, showing the one beneath it.
Pressing back from the root activity (like F) in any tab finishes the whole TabActivity, sending the user back to A.
The intent passed to B also instructs it to automatically navigate to a different tab than the default. In the case where we end up with a stack of [A -> B -> B], the first B is navigated to the correct tab, and the second is at the default.
TL;DR; Don't use CLEAR_TOP with SINGLE_TOP at the same time
If it only produces an error 5% of the time, it is likely to be a concurrency issue. You said you have SINGLE_TOP | CLEAR_TOP for calling Activity B. CLEAR_TOP destroys the current instance of Activity B and the intent is delivered to onCreate(). SINGLE_TOP doesn't destroy the current instance of Activity B, and delivers the intent to onNewIntent().
When the SINGLE_TOP flag is read first, the intent is delivered to the current instance of Activity B calling onNewIntent(). Then CLEAR_TOP is read and Activity B is destroyed and a new instance is created with onCreate() and everything works fine.
When CLEAR_TOP is read first, the existing instance of Activity B is destroyed and a new one is created with onCreate(). Then SINGLE_TOP is read and the intent is delivered to onNewIntent() as well. Again, it works out.
When CLEAR_TOP and SINGLE_TOP are read at the same time, the current instance of activity is destroyed and CLEAR_TOP calls onCreate() and SINGLE_TOP calls onCreate() as well, because no instance of Activity B exists at the moment. Thus, you end up with A->B->B.

Categories

Resources