I have a main Activity A that can kick off sub-Activities B, C, or D. I start them using startActivityForResult(newIntent, REQUEST_CODE);
The user can choose to navigate between B, C, and D once one is displayed. Activity A controls that navigation. When they choose swap between screens 'A' first calls finishActivity(REQUEST_CODE); on the one displayed and then call startActivityForResult(newIntent, REQUEST_CODE); for the next one.
In my onActivityResult I've got
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
// these logs are just for my debugging
Log.w(this.toString(), "onActivityResult returned to by " + requestCode);
Log.w(this.toString(), "result code = " + resultCode );
// the Activity set this value if it was returning normally
if (RESULT_OK == resultCode)
{
Log.i(this.toString(), "---- the sub-activity returned OK ----");
// do some stuff
}// end if (RESULT_OK == resultCode)
// Else we got here because the user hit the back button or something went
// went wrong (RESULT_CANCELED). Either way we do not want to display this
// blank screen to the user. Take them back to the device connection screen.
else if (RESULT_CANCELED == resultCode)
{
finish();
}
}
For some reason I'm getting back a zero or RESULT_CANCELED everytime even though in my sub-activities B,C,D I only set that result if the user chooses to hit the back button (I display an "are you sure" dialog and they choose to exit. In B,C,D I have the following in onPause. The userExiting is a flag I set from the Exit dialog.
protected void onPause()
{
Log.i(this.toString(), "onPause");
// hack to try to setResult for an activity whose finishActivty was called
if ( !this.exiting )
{
Log.i(this.toString(), "======== RESULT_OK ========");
Intent returnIntent = new Intent();
setResult(RESULT_OK, returnIntent);
finish();
}
displayed = false;
super.onPause();
}
Any ideas?
Edit - to be clear the issue is that after calling finishActivty on the old Activity, since a result was never set, Activity A always thinks it should exit. One weird side items. This doesn't happen until the user navigates two times. In other words B is displayed and they choose to go to C. Works, but I can see in the log that onActivityResult hasn't been called. User selects to go from C to D, boom, I see two calls to onActivtyResult, 'A; exits and D is left displayed and the user is unable to nav away.
Update: Since this has been getting a lot of views I thought I'd post this update. The project evolved such that sometimes a single Activity is displays and sometimes a group are displayed as tabs. When the user packs out of the tab in some cases data needed to be returned to the Activity that kicked off the tabs. Before I added the code below RESULT_CANCELED would always be returned. Also make sure that setResult is called before finish.
if (getParent() == null)
{
setResult(Activity.RESULT_OK, intent);
}
else
{
getParent().setResult(Activity.RESULT_OK, intent);
}
I'm not quite following everything you are doing here, but it doesn't sound like you are navigating between activities properly. I would highly suggest reading through the notepad example thoroughly so that you understand the android way of moving between Activities.
Here is what I understand regarding what you are trying to accomplish: Activity A can navigate to B, C or D. From B, C or D you can navigate to any of the others.
You are somehow trying to using Activity A as a controller, having it finish other Activities. Don't do this. Each Activity should be responsible for finishing itself at the proper point.
If you want to go from having A -> B on the stack to A -> C, then I would do this:
A starts B for result. When user selects something in B asking to go to C you have a few choices. You could have B start C not for result, then finish. This would have the effect of B being removed from the stack when C exits. Or, you could have B finish itself, passing back a note that C should be loaded in the results intent. A could then load C for result.
I'm really not sure why you want this behavior though. As an Android user, if I go from A -> B -> C, I expect hitting back to take me back to B.
As an aside, your "is exiting" hack doesn't make any sense. By the time you get to onPause, the result has been set. You cannot change it here. You need to properly set the result when you are finishing, before onPause.
Related
My Android app is for a hiking club and starts with a Home screen, which is the main activity.
The home screen shows the house-style and has some buttons to the core functionalities: list of upcoming hikes, history of hikes, last reactions and also a message board.
THe activity flow is pretty straightforward, you can i.e. navigate from:
Home -> Hike list -> Hike Details
And back using the Back button. Going back from the Home activity will ask for closing the app.
I already use the FLAG_ACTIVITY_CLEAR_TOP flag to prevent several instance of the same activity.
But my problem is I have also implemented a Menu to navigate to the core functionalities directly.
So for example, when in the Hike Detail screen, one can choose to go to the Message board. But I do not want to keep the Hike List -> Hike Detail activities on the stack.
So when pressing Back from the message board, I always want to return to the Home activity.
IS there a clean possibility to pop the stack and only keep the Home activity before launching a new activity? I guess that would solve my issue.
I found something similar on SO that might work.
Check out: FLAG_ACTIVITY_TASK_ON_HOME
Pre API 11:
Start all of your activities from home using startActivityForResult(). When you navigate to a parallel stack via the global menu. Call this on your current activity:
// startParallelActivity();
setResult(KILL_YOURSELF);
finish;
Where every activity on top of home implements onActivityResult() like so:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == KILL_YOURSELF) {
setResult(KILL_YOURSELF);
finish();
}
}
This will destroy all the activities in the current stack, leaving just the home activity that will be there when the user hits "back"
I have 2 Activities: DetailsBuildingActivity and EditBuildingActivity. DetailsActivity has a button to go to the EditBuildingActivity, to edit properties of the building (like name, size, etc). After editing and saving, you the DetailsBuildingActivity opens up with the new adjusted data. Doing this a few times back and forth in 1 go, gives me the following problem:
When doing 5 times in a row, but then I press the backbutton, I have to press it 10 times before I can get to my previous Activity. (10 times - 5 times DetailsBuildingActivity and 5 times EditBuildingActivity). Is there a way to solve this? I constantly make an intent to open each other op.
You need to use the activity stack.
When you save in the EditBuildingActivity you need to call finish() and not create a new intent.
If you would like DetailsBuildingActivity to know about EditBuildingActivities status, you can use
startActivityForResult(intent, requestCode) to start the activity.
setResult() && finish() to complete the launched activity
and
--- ANDROID DOCS
protected void onActivityResult (int requestCode, int resultCode, Intent data)
Since: API Level 1 Called when an activity you launched exits, giving you the requestCode you started it with, the resultCode it returned, and any additional data from it. The resultCode will be RESULT_CANCELED if the activity explicitly returned that, didn't return any result, or crashed during its operation. You will receive this call immediately before onResume() when your activity is re-starting.
Remember that activities can have multiple instances, and every time you start a new one with a intent they are typically added to the top of your activity stack.
in the process of building an app, I have an Activity lets call it A, and i open in multiple times, as A(1) -> A(2) -> A(3) -> ... -> A(m) -> ... -> A(n).
In the app I want to pop all the way back to A(m), is there a way to pop activities all the way back to a chosen one?
i already saw a few answers that had the FLAG_ACTIVITY_CLEAR_TOP but it seems to me like that is only to clear the whole stack and go to the first element in the stack and that is not what I'm trying to get.
update:
what I'm trying to achieve here is as follows: I have a list of questions, and the user can choose a different answer for each one.
once he chooses, the question and the answer go into a list of history, and another question is prompted. at any given point, the user can click a history button and go back to the question he chooses.
all the Q&A activities are the same, so going back to a question means popping the stack until getting there.
If I understand you correctly you are saying that you have N number of the SAME activity and wish to go back to some arbitrary starting point activity in the stack?
I am not totally clear on whether you know ahead of time that all activities forward of your "sticky" activity should be finished or if this is determined somewhere along the way
I don't know of any arbitrary way of popping N activities off the stack when they are duplicate activities but you could roll your own, such as (not finished code)
If you know ahead of time that A(M) will be sticky before it launches the next activity(s) you could just tell each forward activity that it needs to kill itself once it launches it's new task by passing it a flag then when the last activity in the chain ends you are back to A(M)
in A(...)
startSelf()
{
Intent I = new Intent(this,MyActivity.class);
if (bFinishSelf || bFinishForward)
I.putExtra("finishself", 1);
if (Finishelf)
finish();
startActivity(I);
in ... all A(...) onCreate( ...
Bundle b = getIntent().getExtras();
if(b !=null && extras.getInt("finishself");
bFinishSelf = true;
Then when you start a new activity check if the finishself flag is set and if so call finish() after starting new activity ...
If on the other hand A(z) is the one that determines you need to return to A(...) then I guess in A(z) you could tell the calling parent(s) that they need to kill themselves until reaching some ID contained in data ...
A(M)
startActivityForResult(...)
A(Z) somehow you determine that A(M) should be the sticky activity ...
finshAndStopAt(int ID) {
Intent I = new Intent();
I.putExtra("finish",ID);
if (getParent() == null) {
setResult(Activity.RESULT_OK, I);
} else {
getParent().setResult(Activity.RESULT_OK, I);
}
finish();
}
All A(z ... m) (parent's) would monitor for the return data
#Override void onActivityResult(int requestCode, int resultCode, Intent data) {
// If we are asked to finish ourselves, pass it on ...
if (null != data && data.getInt("finish",stopAt) != myID) {
if (getParent() == null) {
setResult(Activity.RESULT_OK, data);
} else {
getParent().setResult(Activity.RESULT_OK, data);
finish();
}
// else we can hang around and are the sticky activity
}
It's a bit of pain and maybe someone knows of an easier method
As a generic response, I would say to keep popping until the Activity returned by the pop is the one you want, then maybe push it back (if you need to).
Have you considered using Fragments? In this way you can use an instance of the FragmentManager class to handle the fragments history that you have generated through methods of the class FragmentTransaction. You can also tag your transactions and then pop all back stack states up to a transaction with a given name.
I don't know if there is a similar way to do this with Activities.
Take a look here:
http://developer.android.com/reference/android/support/v4/app/FragmentManager.html
http://developer.android.com/reference/android/support/v4/app/FragmentTransaction.html
If you use the support library you don't have to worry about you target SDK.
I'm trying to implement an efficent navigation for my app; basically it's a product catalog, I mean:
list Product -> detail
list Product (serach button in interface - startActivityForResult ) -> searchAct (cal finish) -> back to list Product (filtered)
but i need to open searchAct by the HW search button, and also from detail!
is it possible to use startActivityForResult from detail to open searchAct and when searchAct finish forward to list product?
Also, if i open searchAct then press back i need to get back to detail!
if i use always:
startActivity()
then the user needs to press BACK button repeatedly, see all the steps...
for example if the usage is like this:
list (startActivity) ->detail (startActivity)->search (startActivity)->list (startActivity) ->detail
the back button works fine but i have to BACK 5 TIMES FOR EXIT!!
i try:
list (startActivity) ->detail (startActivity and finish() )->search (call finish()) ->list
BUT this way Back button in search is 'broken' because i got to list instead detail....
maybe i can try this:
detail (startActivityForResult) -> search
in detail if i got result_ok i finish() and get back to the list, if i got result_cancel i stay in detail?
i think i got it!!! in list activity i have 'classic' startActivityForResult then 'classic' onActivityResult
in detail activity i launch serach with startActivityForResult then:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
finish();
}
}
finishing the detail i got back to list.... i don't know if it's ok but i like it!
It's quite hard to understand whats your problem about, so im just guessing your need.
In your searchAct activity you can return data like this:
Intent returnIntent = new Intent();
String strData = "your data";
returnIntent.putExtra("data", strData);
setResult(RESULT_OK, returnIntent);
finish();
Then in your detail activity, from where you are starting searchAct, you have a function which is triggered when searchAct finish.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {}
From the product detail activity, you are starting the search activity via startActivityForResult. In which case you can call finish() in the onActivityResult of the product detail activity when the search activity returns the correct code.
This way, the product detail will finish after a search and you'll end up back at the list activity.
If you start the product detail with startActivityForResult as well, you can return the data that your product activity gets given from the search activity.
I guessing this is what you want?
What Is Working Fine
I have 2 activities in my app. First activity calls second actifity for results.
Second activity shows a new layout and lets user perform certain actions. There is a "OK" button. When user presses this button, second activity is finished and user goes back to first activity.
Under the hood, first activity calls second ativity like this:
Intent intent = new Intent(this, NextAct.class);
intent.putExtra("input", input);
this.startActivityForResult(intent, 99);
On "OK" button press, second activity returns with result like this:
Intent intent = new Intent();
intent.putExtra("output", output);
setResult(RESULT_OK, intent);
finish();
After that, first activity's onActivityResult is called with results successfully:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// requestCode is 99
// resultCode is -1
// data holds my output
}
Above setup is working perfectly
What Is Not Working
Now I had a simple requirement, I wanted the user to close the second activity not with an "OK" button, but in a natural way with "HARDWARE BACK" button.
I tried moving the setResult logic in onStop and onDestroy methods of second activity, but it turned out that onActivityResult of first activity is called before onStop or onDestroy methods of second activity and as a result setResult logic does not get a chance to run at all.
Then I tried moving the setResult logic in onPause method of second activity like this
protected void onPause() {
super.onPause();
Intent intent = new Intent();
intent.putExtra("output", output);
setResult(RESULT_OK, intent);
//finish(); enabling or disabling this does not work
}
But although onPause is being called well before onActivityResult and setResult logic runs properly, still I get all null values in onActivityResult
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// requestCode is 99
// resultCode is 0
// data comes as null
}
I need to know why this is happening and if onResume is not a proper place to put setResult logic, what is the most natural way to do it?
Thanks much.
Now I had a simple requirement, I wanted the user to close the second activity not with an "OK" button, but in a natural way with "HARDWARE BACK" button.
That is not usually "natural" for a startActivityForResult() scenario. The BACK button should allow the user to tell you "No, it is not OK" (i.e., cancel).
This does not mean you necessarily have to have an OK button. For example, if NextAct were a ListActivity, a typical pattern is for clicking on a list item to be considered "OK" (i.e., call setResult() and finish() from onListItemClick()), and BACK to mean "I didn't really mean to start NextAct, sorry".
I need to know why this is happening
You are calling setResult() too late. If the user presses BACK, by the time of onPause(), the result (RESULT_CANCELED) has already been determined.
if onResume is not a proper place to put setResult logic
onResume() would not be a proper place to "put setResult logic"
what is the most natural way to do it?
Possibly what you have with the OK button is the "most natural way to do it".
However, if for some strange reason you really do want the BACK button to mean "OK", override onBackPressed() and call setResult() there before chaining to the superclass with super.onBackPressed().
The best way I found is to override finish() method:
#Override
public void finish() {
Intent data = new Intent();
data.putExtra(INTENT_PARAM_OPTIONS, options);
setResult(RESULT_OK, data);
super.finish();
}
In that case, your activity will always return data, even if the user presses back button (or activity bar up button).
And I think its perfectly natural to do this in some circumstances. If you have an activity that is made to edit one big entity (shipping order for instance), and a smaller activity inside that activity that modifies 1 small part of big entity (shipping address), you might aswell always save any changes inside smaller activity, because user can cancel all the changes by cancelling the big activity.
You can try to override onBackPressed or onKeyDown, and setResult(RESULT_OK, intent) there.