Common Implicit Intents¶
Let's take a look at the most common implicit intents such as making a phone call, launching a web address, sending an email, etc.
Phone Call¶
Permissions:
<uses-permission android:name="android.permission.CALL_PHONE" /> ``` 0o9 Intent: ```java Intent callIntent = new Intent(Intent.ACTION_CALL); callIntent.setData(Uri.parse("tel:0377778888")); if (callIntent.resolveActivity(getPackageManager()) != null) { startActivity(callIntent); }
Danger
It's possible that a user won't have any apps that handle the implicit intent you send to startActivity(). If that happens, the call will fail and your app will crash. To verify that an activity will receive the intent, call resolveActivity() on your Intent object. If the result is non-null, then there is at least one app that can handle the intent and it's safe to call startActivity(). If the result is null, you should not use the intent and, if possible, you should disable the feature that issue the intent.
Send Email (to Phone Email Client)¶
Compose an email in the phone email client:
Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("plain/text"); intent.putExtra(Intent.EXTRA_EMAIL, new String[] { "[email protected]" }); intent.putExtra(Intent.EXTRA_SUBJECT, "subject"); intent.putExtra(Intent.EXTRA_TEXT, "mail body"); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(Intent.createChooser(intent, "")); }
Send Email (to Gmail)¶
Gmail does not examine the extra Intent fields, so in order to use this intent, you need to use the Intent.ACTION_SENDTO
and pass a mailto:
URI with the subject and body URL encoded.
String uriText = "mailto:[email protected]" + "?subject=" + Uri.encode("some subject text here") + "&body=" + Uri.encode("some text here"); Uri uri = Uri.parse(uriText); Intent sendIntent = new Intent(Intent.ACTION_SENDTO); sendIntent.setData(uri); if (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(Intent.createChooser(sendIntent, "Send email")); }
Launch Website¶
Launch a website in the phone browser:
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com")); if (browserIntent.resolveActivity(getPackageManager()) != null) { startActivity(browserIntent); }
You can also launch a Chrome tab if the app. Take a look at [[this guide|Chrome-Custom-Tabs#setup]] for how to launch this implicit intent.
Open Google Play Store¶
Open app page on Google Play:
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + context.getPackageName())); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); }
Compose SMS¶
Uri smsUri = Uri.parse("tel:" + to); Intent intent = new Intent(Intent.ACTION_VIEW, smsUri); intent.putExtra("address", to); intent.putExtra("sms_body", message); intent.setType("vnd.android-dir/mms-sms");//here setType will set the previous data null. if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); }
Google Maps¶
Show location in maps application:
Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); String data = String.format("geo:%s,%s", latitude, longitude); if (zoomLevel != null) { data = String.format("%s?z=%s", data, zoomLevel); } intent.setData(Uri.parse(data)); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); }
Capture Photo¶
Uri uri = Uri.fromFile(new File(file)); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); }
Sharing Content¶
Images or binary data:
Intent sharingIntent = new Intent(Intent.ACTION_SEND); sharingIntent.setType("image/jpg"); Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg")); sharingIntent.putExtra(Intent.EXTRA_STREAM, uri.toString()); if (sharingIntent.resolveActivity(getPackageManager()) != null) { startActivity(Intent.createChooser(sharingIntent, "Share image using")); }
or HTML:
Intent sharingIntent = new Intent(Intent.ACTION_SEND); sharingIntent.setType("text/html"); sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, Html.fromHtml("<p>This is the text shared.</p>")); if (sharingIntent.resolveActivity(getPackageManager()) != null) { startActivity(Intent.createChooser(sharingIntent,"Share using")); }
Sharing Content with Intents¶
Intents allow us to communicate data between Android apps and implicit intents can also accept actions. One of those actions is the ACTION_SEND
command which indicates we want to send data across apps. To send data, all you need to do is specify the data and its type, and the system will identify compatible receiving activities and display them to the user.
Sending and receiving data between applications with intents is most commonly used for social sharing of content. Intents allow users to share information quickly and easily, using their favorite applications.
Sharing Local Content¶
You can send content by invoking an implicit intent with ACTION_SEND
.
Sending HTML¶
Intent sharingIntent = new Intent(Intent.ACTION_SEND); sharingIntent.setType("text/html"); sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, Html.fromHtml("<p>This is the text shared.</p>")); startActivity(Intent.createChooser(sharingIntent, "Share using"));
Sending Images¶
To send images or binary data:
final Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("image/jpg"); final File photoFile = new File(getFilesDir(), "foo.jpg"); shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(photoFile)); startActivity(Intent.createChooser(shareIntent, "Share image using"));
Sending Links¶
Sending URL links should simply use text/plain
type:
Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("text/plain"); shareIntent.putExtra(Intent.EXTRA_TEXT, "http://codepath.com"); startActivity(Intent.createChooser(shareIntent, "Share link using"));
Sharing Multiple Types¶
In certain cases, we might want to send an image along with text. This can be done with:
String text = "Look at my awesome picture"; Uri pictureUri = Uri.parse("file://my_picture"); Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_TEXT, text); shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri); shareIntent.setType("image/*"); shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(Intent.createChooser(shareIntent, "Share images..."));
Sharing multiple images can be done with:
Intent shareIntent = new Intent(Intent.ACTION_SEND_MULTIPLE); shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris); shareIntent.setType("image/*");
See this stackoverflow post for more details.
Note: Facebook does not properly recognize multiple shared elements. See this facebook specific bug for more details and share using their SDK.
Share in Facebook¶
Facebook doesn't work well with normal sharing intents when sharing multiple content elements as discussed in this bug. To share posts with facebook, we need to:
- Create a new Facebook app here (follow the instructions)
- Add the Facebook SDK to your Android project
- Share using this code snippet:
public void setupFacebookShareIntent() { ShareDialog shareDialog; FacebookSdk.sdkInitialize(getApplicationContext()); shareDialog = new ShareDialog(this); ShareLinkContent linkContent = new ShareLinkContent.Builder() .setContentTitle("Title") .setContentDescription( "\"Body Of Test Post\"") .setContentUrl(Uri.parse("http://someurl.com/here")) .build(); shareDialog.show(linkContent); }
Sharing Remote Images¶
You may want to send an image that were loaded from a remote URL. Assuming you are using a third party library like Picasso
, here is how you might share an image that came from the network and was loaded into an ImageView. There are two ways to accomplish this. The first way, shown below, takes the bitmap from the view and loads it into a file.
// Get access to ImageView ImageView ivImage = (ImageView) findViewById(R.id.ivResult); // Fire async request to load image Picasso.with(context).load(imageUrl).into(ivImage);
and then later assuming after the image has completed loading, this is how you can trigger a share:
// Can be triggered by a view event such as a button press public void onShareItem(View v) { // Get access to bitmap image from view ImageView ivImage = (ImageView) findViewById(R.id.ivResult); // Get access to the URI for the bitmap Uri bmpUri = getLocalBitmapUri(ivImage); if (bmpUri != null) { // Construct a ShareIntent with link to image Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, bmpUri); shareIntent.setType("image/*"); // Launch sharing dialog for image startActivity(Intent.createChooser(shareIntent, "Share Image")); } else { // ...sharing failed, handle error } } // Returns the URI path to the Bitmap displayed in specified ImageView public Uri getLocalBitmapUri(ImageView imageView) { // Extract Bitmap from ImageView drawable Drawable drawable = imageView.getDrawable(); Bitmap bmp = null; if (drawable instanceof BitmapDrawable){ bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap(); } else { return null; } // Store image to default external storage directory Uri bmpUri = null; try { // Use methods on Context to access package-specific directories on external storage. // This way, you don't need to request external read/write permission. // See https://youtu.be/5xVh-7ywKpE?t=25m25s File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "share_image_" + System.currentTimeMillis() + ".png"); FileOutputStream out = new FileOutputStream(file); bmp.compress(Bitmap.CompressFormat.PNG, 90, out); out.close(); // **Warning:** This will fail for API >= 24, use a FileProvider as shown below instead. bmpUri = Uri.fromFile(file); } catch (IOException e) { e.printStackTrace(); } return bmpUri; }
Make sure to setup the "SD Card" within the emulator device settings:
Note that if you are using API 24 or above, see the section below on using a FileProvider
to work around new file restrictions.
Sharing Files with API 24 or higher¶
If you are using Android API 24 or higher, private File
URI resources (file:///) cannot be shared. You must instead wrap the File
object as a content provider (content://) using the FileProvider class.
First, you must declare this FileProvider in your AndroidManifest.xml
file within the <application>
tag:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.codepath.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/fileprovider" /> </provider>
Next, create a resource directory called xml
and create a fileprovider.xml
. Assuming you wish to grant access to the application's specific external storage directory, which requires requesting no additional permissions, you can declare this line as follows:
<?xml version="1.0" encoding="utf-8"?> <paths> <external-files-path name="images" path="Pictures" /> <!--Uncomment below to share the entire application specific directory --> <!--<external-path name="all_dirs" path="."/>--> </paths>
Finally, you will convert the File object into a content provider using the FileProvider class:
// getExternalFilesDir() + "/Pictures" should match the declaration in fileprovider.xml paths File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "share_image_" + System.currentTimeMillis() + ".png"); // wrap File object into a content provider. NOTE: authority here should match authority in manifest declaration bmpUri = FileProvider.getUriForFile(MyActivity.this, "com.codepath.fileprovider", file);
Note that there are other XML tags you can use in the fileprovider.xml
, which map to the File directory specified. In the example above, we use Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
, which corresponded to the <external-files-dir>
XML tag in the declaration with the Pictures
path explicitly specified. Here are all the options you can use too:
XML tag | Corresponding storage call | When to use |
---|---|---|
<files-path> | Context.getFilesDir() | data can only be viewed by app, deleted when uninstalled (/data/data/[packagename]/files ) |
<external-files-dir> | Context.getExternalFilesDir() | data can be read/write by the app, any apps granted with READ_STORAGE permission can read too, deleted when uninstalled (/Android/data/[packagename]/files ) |
<cache-path> | Context.getCacheDir() | temporary file storage |
<external-path> | Environment.getExternalStoragePublicDirectory() | data can be read/write by the app, any apps can view, files not deleted when uninstalled |
<external-cache-path> | Context.getExternalCacheDir() | temporary file storage with usually larger space |
If you are using API 23 or above, then you'll need to [[request runtime permissions|Managing-Runtime-Permissions-with-PermissionsDispatcher]] for Manifest.permission.READ_EXTERNAL_STORAGE
and Manifest.permission.WRITE_EXTERNAL_STORAGE
in order to share the image as shown above since newer versions require explicit permisions at runtime for accessing external storage.
Note: There is a common bug on emulators that will cause MediaStore.Images.Media.insertImage
to fail with E/MediaStore﹕ Failed to insert image
unless the media directory is first initialized as described in the link.
ShareActionProvider¶
This is how you can easily use an ActionBar share icon to activate a ShareIntent. The below focuses on the support ShareActionProvider for use with AppCompatActivity
.
Note: This is an alternative to using a sharing intent as described in the previous section. You either can use a sharing intent or the provider as described below.
First, we need to add an ActionBar menu item in res/menu/
in the XML specifying the ShareActionProvider
class.
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menu_item_share" app:showAsAction="ifRoom" android:title="Share" app:actionProviderClass="android.support.v7.widget.ShareActionProvider" /> ... </menu>
Next, get access to share provider menu item in the Activity so we can attach the share intent later:
private ShareActionProvider miShareAction; @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate menu resource file. getMenuInflater().inflate(R.menu.second_activity, menu); // Locate MenuItem with ShareActionProvider MenuItem item = menu.findItem(R.id.menu_item_share); // Fetch reference to the share action provider miShareAction = (ShareActionProvider) MenuItemCompat.getActionProvider(item); // Return true to display menu return true; }
Note: ShareActionProvider
does not respond to onOptionsItemSelected()
events, so you set the share action provider as soon as it is possible.
Attach Share Intent for Remote Image¶
Now, once you've setup the ShareActionProvider menu item, construct and attach the share intent for the provider but only after image has been loaded as shown below using the Callback
for Picasso
.
private Intent shareIntent; @Override protected void onCreate(Bundle savedInstanceState) { // ... // Get access to ImageView ImageView ivImage = (ImageView) findViewById(R.id.ivResult); // Load image async from remote URL, setup share when completed Picasso.with(this).load(result.getFullUrl()).into(ivImage, new Callback() { @Override public void onSuccess() { // Setup share intent now that image has loaded prepareShareIntent(); attachShareIntentAction(); } @Override public void onError() { // ... } }); } // Gets the image URI and setup the associated share intent to hook into the provider public void prepareShareIntent() { // Fetch Bitmap Uri locally ImageView ivImage = (ImageView) findViewById(R.id.ivResult); Uri bmpUri = getLocalBitmapUri(ivImage); // see previous remote images section // Construct share intent as described above based on bitmap shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, bmpUri); shareIntent.setType("image/*"); } // Attaches the share intent to the share menu item provider public void attachShareIntentAction() { if (miShareAction != null && shareIntent != null) miShareAction.setShareIntent(shareIntent); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate menu resource file. getMenuInflater().inflate(R.menu.second_activity, menu); // Locate MenuItem with ShareActionProvider MenuItem item = menu.findItem(R.id.menu_item_share); // Fetch reference to the share action provider miShareAction = (ShareActionProvider) MenuItemCompat.getActionProvider(item); attachShareIntentAction(); // call here in case this method fires second // Return true to display menu return true; }
Note: Be sure to call attachShareIntentAction
method both inside onCreateOptionsMenu
AND inside the onSuccess
for Picasso to ensure that the share attaches properly.
Attach Share for a WebView URL¶
We can use a similar approach if we wish to create a share action for the current URL that is being loaded in a [[WebView|Working-with-the-WebView]]:
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_article, menu); MenuItem item = menu.findItem(R.id.menu_item_share); ShareActionProvider miShare = (ShareActionProvider) MenuItemCompat.getActionProvider(item); Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("text/plain"); // get reference to WebView WebView wvArticle = (WebView) findViewById(R.id.wvArticle); // pass in the URL currently being used by the WebView shareIntent.putExtra(Intent.EXTRA_TEXT, wvArticle.getUrl()); miShare.setShareIntent(shareIntent); return super.onCreateOptionsMenu(menu); }
Check out the official guide for easy sharing for more information.