Selecting and launching shortcuts from a non-launcher app - android

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

Related

startActivityForResult() not working for external Activity

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.

Retrieving putExtra values from onActivityResult intent

I am trying to append certain data to an intent, before using StartActivityForResult on it.
When the intent returns in OnActivityForResult, I would like to access the data I appended in the intent. So I can correlate the data retrieved in the intent, with things like database entries, container ids, etc.
Unfortunately the intent that returns does not seem to be the same one I started. I tried comparing (==) the old and the new intent in a test case, and the result failed, and not surprisingly then the data I am trying append is not there. Is there any link back to the original intent?
Basic idea of what I've tried:
Code to StartActivityForResult in psuedo code:
Intent i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
i.putExtra([-Key-], [int]);
i.putExtra([-Key-], [int]);
....
getParentFragment().startActivityForResult(i, requestCode);
Pseudo Code for OnActivityResult
#Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
....
switch(requestcode){
case RESULT_LOAD_IMAGE :
//These always evaluate to default. The intent returns with the picture,
//and I process it fine (with default values), but any extra data i try to append
//to the intent is lost.
int rowId = intent.getIntExtra([-Key-], [-def_value-]);
....
....
break;
default:
throw new RuntimeException();
}
}
When you launch an Activity using implicit Intent resolution, which is what you are doing when you do this:
Intent i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
....
getParentFragment().startActivityForResult(i, requestCode);
you don't have any guarantees what Activity will actually be chosen to perform the action. Because of this, there isn't any "contract" between your Activity and the Activity that will be used to perform the desired action. This is, unfortunately, one of the disavantages of using implicit Intent resolution. Because there is no contract between the 2 Activities, you can't be sure what you are going to get in the result that is returned to you in onActivityResult().
If you look at the documentation for ACTION_PICK, it at least indicates what "should" happen (if the selected Activity actually behaves the way the documentation reads):
Input: getData() is URI containing a directory of data
(vnd.android.cursor.dir/*) from which to pick an item.
Output: The URI of the item that was picked.
This indicates that you should provide a URI that contains a directory of data and that you will be returned an Intent containing the URI of the item that was picked. That's it. That's all you can expect to get. You can put lots of other "extras" in the Intent that you pass to the Activity with ACTION_PICK, but that Activity doesn't care about those extras and will just ignore them. The Activity that performs the ACTION_PICK will create a new Intent containing the URI of the selected item and pass that back to you. It doesn't pass your original Intent back. The "input Intent" and the "output Intent" are completely different and don't have anything to do with each other.
To solve your problem, I'd suggest that you create a unique integer requestCode and save your "extras" in a table or map in your Activity associated with that requestCode. Then you can launch the ACTION_PICK activity using the requestCode. In onActivityResult() you can use the requestCode argument that comes back to find your "extras" that you saved and you'll be able to associate the returned URI with them.
NOTE: One more thing: When you call startActivityForResult() your Activity will be paused and the launched Activity will run. Your Activity won't be resumed until onActivityResult() is called. This means that you will only ever be able to have one ACTION_PICK pending at any given time. For this reason you may not need a way to associate a specific PICK action with any given data.

Android Start Activity for Result returns different resultCode

There are a lot of questions out there on similar topics, but after searching around I haven't found one that matches my issue.
I know about starting intents for result, and overriding onActivityResult() to handle these intents, but for some reason, I'm having issues when I'm coming back from activity b to activity a. So, for example, in the last of 3 activities (AddDirections class) I start in my project, I call this method to return back to the previous activity:
public void finish(View v){
Intent intent = new Intent(getApplicationContext(), AddIngredients.class);
intent.putExtra(Home.RECIPE_INTENT, (Parcelable)recipe);
intent.putExtra(Home.RECIPE_ID_INTENT, recipe.getId());
setResult(RESULT_OK, intent);
finish();
}
In the AddIngredients class, I have this method:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK && requestCode == DIRECTIONS_REQUEST){
Intent intent = new Intent(getApplicationContext(), NameRecipe.class);
Recipe recipe = data.getParcelableExtra(Home.RECIPE_INTENT);
intent.putExtra(Home.RECIPE_INTENT, (Parcelable)recipe);
setResult(RESULT_OK, intent);
finish();
}
}
This should accept the returned result from the AddDirections class, and pass it off to NameRecipe, where I have the exact same method (Except for in the new Intent method it says Home.class). In the Home class, I have basically the same method again to receive the intent as it backs all the way out of the app.
Now, I will say that this works if I go straight through the steps from beginning to end. But if I use the up navigation to go the current activity's parent activity, then it messes everything up. Then when I click finish in the final step it messes up the resultCodes that I set for each intent. I make sure to explicitly set the correct result to RESULT_OK (which equals 1) but then for some reason, sometimes it changes what I've set to be the resultCode to be 0 instead.
Here's what I do in an activity if the user clicks the up navigation:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent intent = NavUtils.getParentActivityIntent(this);
intent.putExtra(Home.RECIPE_INTENT, (Parcelable)recipe);
setResult(RESULT_OK, intent);
NavUtils.navigateUpTo(this, intent);
finish(); //Have tried with and without this
return true;
}
return super.onOptionsItemSelected(item);
}
Like I said, I've searched for a lot of reasons why the resultCode gets overwritten after using the up navigation, but I haven't found a single reason why.
Any help would be greatly appreciated.
It looks like the underlying question is really how do I navigate between multiple activities through the stack using the up navigation that is built in. I am wondering if the project is setup correctly for this type of navigation. From the android developer resources, up navigation from google, you have to setup the navigation buttons with a parent activity. Then the application can traverse between the different activities. From what it looks like this should allow A->B B->C c->D and back to A if needed. I would also add in a button that would allow a direct path back to A if the user so chooses.
If you have more information on what you are trying to accomplish between activities whether it is data passed back and forth or just returning to the previous activity that would be helpful. Hopefully the link will help your issue with this.
your problem seems to be the NavUp, this util class tends to clear everything to navigate to the parent including your resultCode and the instance of the activity, resulting in a Canceled result (if started for result) to a new instance of the parent activity, in order to fix that a quick fix would be to declare android:launchMode="singleTop" in your manifest, but I wouldn't recommend to use that unless you are 100% that you want that, instead I would go into my navigator class, or my method for navigate and overwrite that specific navigation to something like:
Intent parentActivityIntent = NavUtils.getParentActivityIntent(this);
parentActivityIntent.setFlags(
Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP
);
NavUtils.navigateUpTo(this, parentActivityIntent);
Hope this helps!!
Sorry for the late answer :p
if by 'up navigation' you mean back key pressed then you need to override a function
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
}
if from what it seems to be you are trying to get back when clicking an item from the Options Menu to your parent activity directly then i think you will need to go back step by step as at this moment there is already an activity running and waiting for a result so when you go directly to home activity its already waiting an answer from activity B and not activity C i hope i explained it right :)

How to call contact picker from home screen?

I'm experimenting with creating a custom Android Home Screen. I've used the sample Home Screen application and have adapted it. Something I would like to do, is open the contact picker from a button in the Home Screen and use the contact that the user chose in a next action. I've stumbled on the same problem that is mentioned in this question.
How can I work around this so that the home screen stays "singleInstance" and I can also call startActivityForResult()?
Is the contacts picker an activity that I can subclass (I've searched but can't find any) so that I can use the solution that David Wasser proposes in the aforementioned question?
I've found an elegant solution after all:
My main activity launches an intermediate, invisible activity that has android:theme="#android:style/Theme.NoDisplay"
This intermediate activity calls the contact picker in its onCreate
Intent phoneContactIntent = new Intent(Intent.ACTION_PICK,
ContactsContract.Contacts.CONTENT_URI);
// Show user only contacts w/ phone numbers
phoneContactIntent.setType(Phone.CONTENT_TYPE);
startActivityForResult(phoneContactIntent, CHOOSE_CONTACT_TO_CALL);
Then, in onActivityResult, it creates a new intent for the main application, with the data that the contact picker returned.
switch (requestCode) {
case (CHOOSE_CONTACT_TO_CALL):
if (resultCode == Activity.RESULT_OK) {
Intent resultIntent = new Intent(this, Home.class);
resultIntent.putExtras(data);
Uri contactData = data.getData();
if (contactData != null)
{
resultIntent.setData(contactData);
}
startActivity(resultIntent);
}
}
finish();
and in my Home class, in onCreate I call getIntent() and inspect the data in the intent that launched the main activity.

Cannot start market activity with CLEAR_TOP flag

Consider this code snippet that prompts the user to rate the app on the market:
private void backToMainActivity() {
Intent i = new Intent(mActivity, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
mActivity.startActivity(i);
mActivity.finish();
}
...
if (mRateToast.leftClicked(x, y)) {
setRate(true);
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + GameGlobals.PACKAGE_NAME));
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | Intent.FLAG_ACTIVITY_NEW_TASK);
mActivity.startActivity(i);
backToMainActivity();
} else if (mRateToast.rightClicked(x, y)) {
setRate(false);
backToMainActivity();
}
What this does is detect what button was pressed (Rate or Later), set a flag accordingly and run the market app if needed. No matter which button is pressed, I want to return to the main menu (this is a game activity, 1 activity away from the main menu activity).
On Android 4.1, this works fine - the market opens to the app's page, and on return from the market, I am on the main menu. On android <= 2.3, the market does not open, I am just returned directly to the main menu. It does, however, open if I comment the i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); in the backToMainActivity method. This causes other problems though, such as the back button going back to the wrong activity instead of exiting the app, so is not a solution.
How can I run the market, return to main menu and have the main menu activity be at the top of the activity stack?
Note: I haven't tried it, but removing the finish is also not really an option. My game activity can also be started by external applications directly, and in that case, if I remove the finish call, for some reason CLEAR_TOP will not work, and pressing the back button on the main menu activity will return to the game activity instead of exiting the application.
try this
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + GameGlobals.PACKAGE_NAME));
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | Intent.FLAG_ACTIVITY_NEW_TASK);
mActivity.startActivityForResult(i,1552);
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==1552){
backToMainActivity();
}
}
I solved this by removing Intent.FLAG_ACTIVITY_NEW_TASK and using startActivityForResult as described by MoshErsan in his answer. Apparently starting the market as a new task does not play well with CLEAR_TOP in android <= 2.3.
If someone can explain why the original approach wasn't working or offer a better solution, I will accept that answer in one or two days, if not I will accept mine.

Categories

Resources