This is not a duplicate question. I only found this question relevant but it doesn't have any working solution.
What I Have
I have two apps both created by me, App A and App B. My requirement is to open App B from App A on click of a button. Now, App B will do some processing and return a small status to App A. This should be possible isn't it?
What I Have Done
Intent intent = getActivity().getPackageManager().getLaunchIntentForPackage(Config._PACKAGE);
getActivity().startActivityForResult(intent, 6699);
This opens the App B perfectly. Now in App B,
Intent returnIntent = new Intent();
returnIntent.putExtra("STATUS", statusCode);
setResult(RESULT_OK, returnIntent);
finish();
And this is my onActivityResult() method on the Activity. I am starting the Activity from a Fragment though.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "ON ACTIVITY RESULT");
if (requestCode == 6699) {
if (resultCode == RESULT_OK) {
Log.d(TAG, "RESULT OK");
}
if (resultCode == RESULT_CANCELED) {
Log.d(TAG, "RESULT NOT OK");
}
}
}
Problem
The onActivityResult() gets called just when I start the activity of App B with RESULT_CANCELLED. How is this possible? Is it because its a new task?
But when I call finish() on App B and it closes and takes me back to App A, but the onActivityResult() is not called this time? How can I solve this?
This is a very basic requirement and should have a solution.
I finally solved this problem.
if the activity you are launching uses the singleTask launch mode, it
will not run in your task and thus you will immediately receive a
cancel result
This is what the docs says. Now the idea is that if the called activity is started on a new task, the result will be cancelled immediately. In my case, I am starting an activity from a different app altogether, which is always started in a new task, regardless of any launchMode or taskAffinity or flag.
Whenever I do something like this,
Intent intent = getActivity().getPackageManager().getLaunchIntentForPackage(Config._PACKAGE);
getActivity().startActivityForResult(intent, 6699);
this flag Intent.FLAG_ACTIVITY_NEW_TASK is automatically added by default. So, we need to clear all flags before starting the activity using,
intent.setFlags(0);
Use it before calling startActivity(). The solution was simple and tricky at the same time.
You really should add in a custom Intent Filter to make this happen. This will allow you to direct the flow for your task that you want Application B to do, separate from how you might use Application B. See Android docs Intent Filter for how to do this. Basically, you add a blurb into the manifest for Application B, something like this:
<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
Application A will use something like this:
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
These of course will need to be customized for your application.
Related
I have the following situation:
when I launch my app the following scenario takes place:
I start the activity StartActivity
If I never did a login (I know it based on some shared preference) launch LoginActivity and show a login layout (username and password)
If I already did a login I start LoginActivity anyway, but I use (crypted) data saved on shared preferences without asking again credentials to the user.
When the login succeed I launch the activity StartActivity
Now in my manifest file I have the following IntentFilter for MainActivity:
<intent-filter android:label="Conio" >
<action android:name="android.intent.action.VIEW" />
<data android:scheme="myprotocol" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
And everything works well with a big "BUT":
everything works only when my app is already opened: my app is brought to front, the onCreate method is called and with getIntent.getData() I can access the URI which started my app.
THE BIG PROBLEM:
WHAT I NEED IS :
When my app is not already started everything should begin from StartActivity as per previous explanation
When my app is running the behaviour must be the current one
To accomplish this I made the following attempt:
I applied that IntentFilter to a new Activity , in this Activity's onCreate method I put the following code
Intent intent = getPackageManager().getLaunchIntentForPackage("my.package.name");
if (intent != null) {
intent.setData(intent.getData());
intent.putExtra("test", "test");
startActivity(intent);
finish();
}
but what happens here is that when app is not running the app correctly starts from StartActivity, but if if the app is already running, MainActivity is brought to front but I can't access the intent extras, in fact this code do not works
#Override
protected void onResume() {
super.onResume();
//this code returns null
String test=getIntent.getStringExtra("test");
}
how can I make all this to work?
if the app is already running, MainActivity is brought to front but I can't access the intent extras:
To get Intent deliver when Activity is running either use LocalBroadcastManager sending new Intent to running Activity instead of calling startActivity method.
OR
Without LocalBroadcastManager add FLAG_ACTIVITY_SINGLE_TOP in Intent or add android:launchMode="singleTask" in AndroidManifest.xml when starting MainActivity :
intent.setData(intent.getData());
intent.putExtra("test", "test");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
and override onNewIntent method in MainActivity:
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//get data from intent
}
I'm launching an activity using startActivityForResult() from a button handler, and my onActivityResult() is being called instantly, even before the onCreate() for the target activity is being hit.
public void onGraphNext (View target) {
Intent i = new Intent(this, AddCommentActivity.class);
startActivityForResult(i,6); // 6 is arbitrary request code
}
. . .
protected void onActivityResult(int requestCode, int resultCode,
Intent returnData) {
if ( (resultCode == RESULT_OK) && (requestCode == 6) ) {
Bundle extras = returnData.getExtras();
comment = extras.getString("comment");
}
}
The result code returned is 0 and the request code is 6. Elsewhere on StackOverflow I've seen people report this problem and the solution was to not use singeInstance for the launchMode in the manifest. But I'm using standard . . .
<activity android:name="AddCommentActivity"
android:configChanges="orientation"
android:screenOrientation="portrait"
android:launchMode="standard"></activity>
Thanks in advance for any insights!
EDIT: I made a simple test program and I can reproduce the problem reliably when the caller ("launcher") - the activity with the onActivityResult - is a singleInstance and the Activity being invoked ("launchee") is standard. i.e.,
<activity android:name="Launcher"
android:screenOrientation="portrait"
android:launchMode="singleInstance"></activity>
<activity android:name="Launchee"
android:screenOrientation="portrait"
android:launchMode="standard"></activity>
This is a problem for me because in the real app, the called must be a singleInstance for other reasons, but it wants to have buttons to start up other activities to request user input. How else to do this if I can't use startActivityForResult?
You cannot use startActivityForResult() if the activity being started is not running in the same task as the activity that starts it. This means that neither of the activities can have launchMode="singleInstance". If you require that these 2 activities run in different tasks, then you need to use a different mechanism. The documentation for startActivityForResult() sorta-kinda hints at this:
"For example, if the activity you are launching uses the singleTask
launch mode, it will not run in your task and thus you will
immediately receive a cancel result."
What you can do is to simply start the activity using startActivity() and have the called activity call startActivity() to return to your activity, sending back data as necessary as extras in the Intent it uses.
However, you might consider whether you really need these special launchModes. In general they are only necessary for HOME-screen replacements and other very special applications. Most developers who use singleInstance or singleTask launch modes are using them incorrectly.
What I would like to do is the following:
User selects a shortcut from a list of all available shortcuts in the system;
The relevant info is stored;
User performs an action and the selected shortcut is executed, like if it was an icon on the home screen.
So far I am able to populate and present a list with all the shortcuts, using
getPackageManager().queryIntentActivities(new Intent(Intent.ACTION_CREATE_SHORTCUT), 0);. Upon selecting a shortcut, I start the ACTION_CREATE_SHORTCUT intent to customize the shortcut parameters - it presents the proper UI and seems to work. I use this code to start the intent:
ActivityInfo activity = resolveInfo.activityInfo;
ComponentName name = new ComponentName(activity.applicationInfo.packageName, activity.name);
Intent i = new Intent(Intent.ACTION_CREATE_SHORTCUT);
i.addCategory(Intent.CATEGORY_DEFAULT);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
i.setComponent(name);
startActivityForResult(i, 1);
Here is my onActivityResult:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode==1 && resultCode == RESULT_OK) {
try {
startActivity((Intent) data.getExtras().get(Intent.EXTRA_SHORTCUT_INTENT));
} catch (Exception e) {
e.printStackTrace();
}
Toast.makeText(getApplicationContext(), "Success!!", Toast.LENGTH_LONG).show();
finish();
}else{
Toast.makeText(getApplicationContext(), "Fail: "+resultCode+" "+resultCode, Toast.LENGTH_LONG).show();
}
super.onActivityResult(requestCode, resultCode, data);
}
Now, the problem is that the onActivityResult always gets triggered immediately after startActivityForResult with requestCode=0, resultCode=0 and with no data. It does not trigger when the ACTION_CREATE_SHORTCUT activity actually ends. I really don't get it. I think that after the activity ends it should return the requestCode I sent it and the data intent, containing the Intent.EXTRA_SHORTCUT_INTENT which I could then use somehow to actually start the shortcut.
The second part of the question is how do I actually store the necessary information for the shortcut the user selected, preferably in SharedPreferences, so I could later execute this shortcut with the specific parameters. I couldn't find any example of this.
Any help would be much appreciated! Thanks!
More then 2 years later, here is the answer to my question:
The proper functioning of the startActivityForResult/onActivityResult system obviously depends on both the calling and the called Activities being part of the same Task. Therefore any action which would cause the two activities to be launched in separate Tasks would break this functionality. Such actions include setting any exclusive launchMode for any of the Activities in the AndroidManifest.xml or using flags such as Intent.FLAG_ACTIVITY_NEW_TASK when launching any of the two Activities.
Upvoted the answer of user2427931 for the Intent.parseUri() solution.
I had the same behavior when my calling activity had launchMode="singleInstance". Do you have that defined as well?
Regarding saving the Intents. You could turn the intent into an URI and save it as a string in SharedPreferences. You could then use Intent.pareseUri() once you have retrieved it.
//Erty
I have 2 Activities, each in seperate applications. Activity1 has a button the user can click and it calls the second activity using an intent in its onClick() method:
Intent myIntent = getPackageManager().getLaunchIntentForPackage(com.myProject.Activity2);
startActivityForResult(myIntent, 600);
This correctly launches Activity2 from Activity1, but onActivityResult gets called in Activity1 before onCreate gets called in Activity2, instead of in onBackPressed() where I set up the return intent.
Here is the onCreate method for Activity2:
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
Here is the current version of onBackPressed method for Activity2:
#Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("Stuff", someStuff);
if(getParent()==null){
setResult(Activity.RESULT_OK, intent);
}else{
getParent().setResult(Activity.RESULT_OK, intent);
}
finish();
super.onBackPressed();
}
My AndroidManifest.xml has the following intent filter for Activity2:
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
I verified that my launchMode is standard (and not singleTask, etc) as advised here and my request code is not negative as warned here. I also tried android:launchMode="singleTop", but that was a no-go also.
I also tried not calling finish() in onBackPressed() for Activity2 as mentioned here (also with just super.onBackPressed() as suggested here) and again calling it as suggested here.
Additionally I tried commenting out the line intent.putExtra("Stuff", someStuff); as it seemed to cause trouble for this person.
Any ideas as to what I might be doing wrong?
So here is the final solution that took care of it:
I changed the intent for Activity1 to the following:
Intent myIntent = new Intent();
myIntent.setClassName("com.myProject", "com.myProject.Activity2");
startActivityForResult(myIntent, 600);
For some reason Android requires the fully qualified name for the second parameter in addition to the package name given by the first parameter. Now it works! :)
It will happen if "singleInstance" flag is set when you launch the activity.
Not certain what your problem is. The way you're creating the Intent in Activity1 is odd; that method isn't meant for creating intents that launch another activity in the same app. Some developers use the Intent(Context, Class<>) constructor. I prefer to use Intent(String action) with a custom action string defined only in my app (which is easier to code correctly).
Also, the intent filter you've specified for Activity2 is usually used for an activity that's launched directly from the Home screen.
Where's the onCreate() code for activity2? Where's the code for onBackPressed()? Can you prove to me that setResult() is called before some other code in Activity2? You should run the activities in debug. Ensure that Activity2 is receiving the intent you think it should, then trace step by step the statements that are executed until setResult(). The thing not to do is throw solutions at the code before you understand what the underlying problem is.
As far as I can tell so far, Activity1 is sending out an Intent, and then onActivityResult is being called. Nothing else is proven so far.
I 'm calling getParent().setResult(0) in TabActivity. This is called when the user hits previous button. I want the current activity to close and get deleted from the stack.
getparent() returns null. Can someone tell me why does that happen??
getParent().setResult(0);
finish();
Thanks
UPDATE: This is the definition of getParent()...What does embedded child mean. And secondly is the TabActivity an embedded child if it is called from another Activity??
public final Activity getParent ()
Return the parent activity if this view is an embedded child.
You haven't clearly stated you're question. You want to understand how to pass a result back from an Activity to the Activity which called it. You must first understand that Activities aren't hierarchical even though they are kept on a back stack. Later activities do not belong to the Activity they are called from.
However, here is the answer the question you meant to ask:
You are using startActivityForResult(Intent, int) (Which you can read up on here)
When Activity A calls startActivityForResult() method Activity B is started, this should do whatever processing is required and then when it exits either call:
setResult(RESULT_OK)
when a yes/no is required or
setResult(RESULT_OK, intent)
where intent is an Intent which contains bundled information you want to use in Activity A to act upon.
After Activity B exits Activity A will resume and call the method:
protected void onActivityResult(int requestCode, int resultCode, Intent data)
This is where you will process the result.
You can read all about this here:
http://developer.android.com/reference/android/app/Activity.html#StartingActivities
If you don't want to keep this Activity in the history stack do one of the following:
a) when starting the TabActivity in the parent, add flag: FLAG_ACTIVITY_NO_HISTORY
Intent intent = new Intent(this, TabActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivityForResult(intent, requestCode);
b) OR add android:noHistory="true" in the activity's Manifest like so
<activity
android:name=".TabActivity"
android:noHistory="true"
...
>
...
</activity>
Then to return the result to the parent and finish TabActivity
Intent result = new Intent();
result.putExtra("somevalue", requestCode);
setResult(Activity.RESULT_OK, result); // or setResult(Activity.RESULT_CANCELED, result);
finish();
When you finish(); the TabActivity activity it will not be remembered