summaryrefslogtreecommitdiffstats
path: root/docs/html/training/secure-file-sharing
diff options
context:
space:
mode:
Diffstat (limited to 'docs/html/training/secure-file-sharing')
-rw-r--r--docs/html/training/secure-file-sharing/index.jd80
-rw-r--r--docs/html/training/secure-file-sharing/request-file.jd147
-rw-r--r--docs/html/training/secure-file-sharing/retrieve-info.jd110
-rw-r--r--docs/html/training/secure-file-sharing/setup-sharing.jd136
-rw-r--r--docs/html/training/secure-file-sharing/share-file.jd298
5 files changed, 771 insertions, 0 deletions
diff --git a/docs/html/training/secure-file-sharing/index.jd b/docs/html/training/secure-file-sharing/index.jd
new file mode 100644
index 0000000..19a1042
--- /dev/null
+++ b/docs/html/training/secure-file-sharing/index.jd
@@ -0,0 +1,80 @@
+page.title=Sharing Files
+
+trainingnavtop=true
+startpage=true
+
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+<ul>
+ <li>Android 1.6 (API Level 4) or higher</li>
+ <li>Familiarity with file operations such as opening, reading, and writing files</li>
+</ul>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/topics/data/data-storage.html">Storage Options</a></li>
+ <li><a href="{@docRoot}training/basics/data-storage/files.html">Saving Files</a>
+ <li><a href="{@docRoot}training/sharing/index.html">Sharing Simple Data</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>
+ Apps often have a need to offer one or more of their files to another app. For example, an image
+ gallery may want to offer files to image editors, or a file management app may want to allow
+ users to copy and paste files between areas in external storage. One way a sending app can
+ share a file is to respond to a request from the receiving app.
+</p>
+<p>
+ In all cases, the only secure way to offer a file from your app to another app is to send the
+ receiving app the file's content URI and grant temporary access permissions to that URI.
+ Content URIs with temporary URI access permissions are secure because they apply only to the
+ app that receives the URI, and they expire automatically. The Android
+ {@link android.support.v4.content.FileProvider} component provides the method
+ {@link android.support.v4.content.FileProvider#getUriForFile getUriForFile()} for
+ generating a file's content URI.
+</p>
+<p>
+ If you want to share small amounts of text or numeric data between apps, you should send an
+ {@link android.content.Intent} that contains the data. To learn how to send simple data with an
+ {@link android.content.Intent}, see the training class
+ <a href="{@docRoot}training/sharing/index.html">Sharing Simple Data</a>.
+</p>
+<p>
+ This class explains how to securely share files from your app to another app using content URIs
+ generated by the Android {@link android.support.v4.content.FileProvider} component and
+ temporary permissions that you grant to the receiving app for the content URI.
+</p>
+<h2>Lessons</h2>
+<dl>
+ <dt><b><a href="setup-sharing.html">Setting Up File Sharing</a></b></dt>
+ <dd>
+ Learn how to set up your app to share files.
+ </dd>
+ <dt><b><a href="share-file.html">Sharing a File</a></b></dt>
+ <dd>
+ Learn how to offer a file to another app by generating a content URI for the file,
+ granting access permissions to the URI, and sending the URI to the app.
+ </dd>
+ <dt><b><a href="request-file.html">Requesting a Shared File</a></b></dt>
+ <dd>
+ Learn how to request a file shared by another app, receive the content URI for the file,
+ and use the content URI to open the file.
+ </dd>
+ <dt>
+ <b><a href="retrieve-info.html">Retrieving File Information</a></b>
+ </dt>
+ <dd>
+ Learn how an app can use a content URI generated by a
+ {@link android.support.v4.content.FileProvider} to retrieve file information including
+ MIME type and file size.
+ </dd>
+</dl>
+
+
diff --git a/docs/html/training/secure-file-sharing/request-file.jd b/docs/html/training/secure-file-sharing/request-file.jd
new file mode 100644
index 0000000..116701d
--- /dev/null
+++ b/docs/html/training/secure-file-sharing/request-file.jd
@@ -0,0 +1,147 @@
+page.title=Requesting a Shared File
+
+trainingnavtop=true
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#SendRequest">Send a Request for the File</a></li>
+ <li><a href="#OpenFile">Access the Requested File</a>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li>
+ <a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent Filters</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/providers/content-provider-basics.html#SimpleQuery"
+ >Retrieving Data from the Provider</a>
+ </li>
+</ul>
+
+</div>
+</div>
+
+<p>
+ When an app wants to access a file shared by another app, the requesting app (the client)
+ usually sends a request to the app sharing the files (the server). In most cases, the request
+ starts an {@link android.app.Activity} in the server app that displays the files it can share.
+ The user picks a file, after which the server app returns the file's content URI to the
+ client app.
+</p>
+<p>
+ This lesson shows you how a client app requests a file from a server app, receives the file's
+ content URI from the server app, and opens the file using the content URI.
+</p>
+
+<h2 id="SendRequest">Send a Request for the File</h2>
+<p>
+ To request a file from the server app, the client app calls
+ {@link android.app.Activity#startActivityForResult startActivityForResult} with an
+ {@link android.content.Intent} containing the action such as
+ {@link android.content.Intent#ACTION_PICK ACTION_PICK} and a MIME type that the client app
+ can handle.
+</p>
+<p>
+ For example, the following code snippet demonstrates how to send an
+ {@link android.content.Intent} to a server app in order to start the
+ {@link android.app.Activity} described in <a href="share-file.html#SendURI"
+ >Sharing a File</a>:
+</p>
+<pre>
+public class MainActivity extends Activity {
+ private Intent mRequestFileIntent;
+ private ParcelFileDescriptor mInputPFD;
+ ...
+ &#64;Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ mRequestFileIntent = new Intent(Intent.ACTION_PICK);
+ mRequestFileIntent.setType("image/jpg");
+ ...
+ }
+ ...
+ protected void requestFile() {
+ /**
+ * When the user requests a file, send an Intent to the
+ * server app.
+ * files.
+ */
+ startActivityForResult(mRequestFileIntent, 0);
+ ...
+ }
+ ...
+}
+</pre>
+<h2 id="OpenFile">Access the Requested File</h2>
+<p>
+ The server app sends the file's content URI back to the client app in an
+ {@link android.content.Intent}. This {@link android.content.Intent} is passed to the client
+ app in its override of {@link android.app.Activity#onActivityResult onActivityResult()}. Once
+ the client app has the file's content URI, it can access the file by getting its
+ {@link java.io.FileDescriptor}.
+</p>
+<p>
+<p>
+ File security is preserved in this process because the content URI is the only piece of data
+ that the client app receives. Since this URI doesn't contain a directory path, the client app
+ can't discover and open any other files in the server app. Only the client app gets access to
+ the file, and only for the permissions granted by the server app. The permissions are temporary,
+ so once the client app's task stack is finished, the file is no longer accessible outside the
+ server app.
+</p>
+<p>
+ The next snippet demonstrates how the client app handles the
+ {@link android.content.Intent} sent from the server app, and how the client app gets the
+ {@link java.io.FileDescriptor} using the content URI:
+</p>
+<pre>
+ /*
+ * When the Activity of the app that hosts files sets a result and calls
+ * finish(), this method is invoked. The returned Intent contains the
+ * content URI of a selected file. The result code indicates if the
+ * selection worked or not.
+ */
+ &#64;Override
+ public void onActivityResult(int requestCode, int resultCode,
+ Intent returnIntent) {
+ // If the selection didn't work
+ if (resultCode != RESULT_OK) {
+ // Exit without doing anything else
+ return;
+ } else {
+ // Get the file's content URI from the incoming Intent
+ Uri returnUri = returnIntent.getData();
+ /*
+ * Try to open the file for "read" access using the
+ * returned URI. If the file isn't found, write to the
+ * error log and return.
+ */
+ try {
+ /*
+ * Get the content resolver instance for this context, and use it
+ * to get a ParcelFileDescriptor for the file.
+ */
+ mInputPFD = getContentResolver().openFileDescriptor(returnUri, "r");
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ Log.e("MainActivity", "File not found.");
+ return;
+ }
+ // Get a regular file descriptor for the file
+ FileDescriptor fd = mInputPFD.getFileDescriptor();
+ ...
+ }
+ }
+</pre>
+<p>
+ The method {@link android.content.ContentResolver#openFileDescriptor openFileDescriptor()}
+ returns a {@link android.os.ParcelFileDescriptor} for the file. From this object, the client
+ app gets a {@link java.io.FileDescriptor} object, which it can then use to read the file.
+</p>
diff --git a/docs/html/training/secure-file-sharing/retrieve-info.jd b/docs/html/training/secure-file-sharing/retrieve-info.jd
new file mode 100644
index 0000000..4a2b7d8
--- /dev/null
+++ b/docs/html/training/secure-file-sharing/retrieve-info.jd
@@ -0,0 +1,110 @@
+page.title=Retrieving File Information
+
+trainingnavtop=true
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#RetrieveMimeType">Retrieve a File's MIME Type</a></li>
+ <li><a href="#RetrieveFileInfo">Retrieve a File's Name and Size</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/topics/providers/content-provider-basics.html#SimpleQuery"
+ >Retrieving Data from the Provider</a></li>
+</ul>
+
+</div>
+</div>
+<p>
+ Before a client app tries to work with a file for which it has a content URI, the app can
+ request information about the file from the server app, including the file's data type and
+ file size. The data type helps the client app to determine if it can handle the file, and the
+ file size helps the client app set up buffering and caching for the file.
+</p>
+<p>
+ This lesson demonstrates how to query the server app's
+ {@link android.support.v4.content.FileProvider} to retrieve a file's MIME type and size.
+</p>
+<h2 id="RetrieveMimeType">Retrieve a File's MIME Type</h2>
+<p>
+ A file's data type indicates to the client app how it should handle the file's contents. To get
+ the data type of a shared file given its content URI, the client app calls
+ {@link android.content.ContentResolver#getType ContentResolver.getType()}. This method returns
+ the file's MIME type. By default, a
+ {@link android.support.v4.content.FileProvider} determines the file's MIME type from its
+ filename extension.
+</p>
+<p>
+ The following code snippet demonstrates how a client app retrieves the MIME type of a file once
+ the server app has returned the content URI to the client:
+</p>
+<pre>
+ ...
+ /*
+ * Get the file's content URI from the incoming Intent, then
+ * get the file's MIME type
+ */
+ Uri returnUri = returnIntent.getData();
+ String mimeType = getContentResolver().getType(returnUri);
+ ...
+</pre>
+<h2 id="RetrieveFileInfo">Retrieve a File's Name and Size</h2>
+<p>
+ The {@link android.support.v4.content.FileProvider} class has a default implementation of the
+ {@link android.support.v4.content.FileProvider#query query()} method that returns the
+ name and size of the file associated with a content URI in a
+ {@link android.database.Cursor}. The default implementation returns two columns:
+</p>
+<dl>
+ <dt>{@link android.provider.OpenableColumns#DISPLAY_NAME DISPLAY_NAME}</dt>
+ <dd>
+ The file's name, as a {@link java.lang.String}. This value is the same as the value returned
+ by {@link java.io.File#getName File.getName()}.
+ </dd>
+ <dt>{@link android.provider.OpenableColumns#SIZE SIZE}</dt>
+ <dd>
+ The size of the file in bytes, as a {@code long} This value is the same as the value
+ returned by {@link java.io.File#length File.length()}
+ </dd>
+</dl>
+<p>
+ The client app can get both the {@link android.provider.OpenableColumns#DISPLAY_NAME
+ DISPLAY_NAME} and {@link android.provider.OpenableColumns#SIZE SIZE} for a file by setting all
+ of the arguments of {@link android.support.v4.content.FileProvider#query query()} to
+ {@code null} except for the content URI. For example, this code snippet retrieves a file's
+ {@link android.provider.OpenableColumns#DISPLAY_NAME DISPLAY_NAME} and
+ {@link android.provider.OpenableColumns#SIZE SIZE} and displays each one in separate
+ {@link android.widget.TextView}:
+</p>
+<pre>
+ ...
+ /*
+ * Get the file's content URI from the incoming Intent,
+ * then query the server app to get the file's display name
+ * and size.
+ */
+ Uri returnUri = returnIntent.getData();
+ Cursor returnCursor =
+ getContentResolver().query(returnUri, null, null, null, null);
+ /*
+ * Get the column indexes of the data in the Cursor,
+ * move to the first row in the Cursor, get the data,
+ * and display it.
+ */
+ int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
+ int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
+ returnCursor.moveToFirst();
+ TextView nameView = (TextView) findViewById(R.id.filename_text);
+ TextView sizeView = (TextView) findViewById(R.id.filesize_text);
+ nameView.setText(returnCursor.getString(nameIndex));
+ sizeView.setText(Long.toString(returnCursor.getLong(sizeIndex)));
+ ...
+</pre>
diff --git a/docs/html/training/secure-file-sharing/setup-sharing.jd b/docs/html/training/secure-file-sharing/setup-sharing.jd
new file mode 100644
index 0000000..d1ab993
--- /dev/null
+++ b/docs/html/training/secure-file-sharing/setup-sharing.jd
@@ -0,0 +1,136 @@
+page.title=Setting Up File Sharing
+trainingnavtop=true
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#DefineProvider">Specify the FileProvider</a></li>
+ <li><a href="#DefineMetaData">Specify Sharable Directories</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/topics/data/data-storage.html">Storage Options</a></li>
+ <li><a href="{@docRoot}training/basics/data-storage/files.html">Saving Files</a>
+</ul>
+
+</div>
+</div>
+
+<p>
+ To securely offer a file from your app to another app, you need to configure your app to offer
+ a secure handle to the file, in the form of a content URI. The Android
+ {@link android.support.v4.content.FileProvider} component generates content URIs for
+ files, based on specifications you provide in XML. This lesson shows you how to add the default
+ implementation of {@link android.support.v4.content.FileProvider} to your app, and how to
+ specify the files you want to offer to other apps.
+</p>
+<h2 id="DefineProvider">Specify the FileProvider</h2>
+<p>
+ Defining a {@link android.support.v4.content.FileProvider} for your app requires an entry in
+ your manifest. This entry specifies the authority to use in generating content URIs, as well as
+ the name of an XML file that specifies the directories your app can share.
+</p>
+<p>
+ The following snippet shows you how to add to your manifest the
+ <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"
+ >&lt;provider&gt;</a></code> element that specifies the
+ {@link android.support.v4.content.FileProvider} class, the authority, and the
+ XML file name:
+</p>
+<pre>
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.myapp"&gt;
+ &lt;application
+ ...&gt;
+ &lt;provider
+ android:name="android.support.v4.content.FileProvider"
+ android:authorities="com.example.myapp.fileprovider"
+ android:grantUriPermissions="true"
+ android:exported="false"&gt;
+ &lt;meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="&#64;xml/filepaths" /&gt;
+ &lt;/provider&gt;
+ ...
+ &lt;/application&gt;
+&lt;/manifest&gt;</pre>
+<p>
+ In this example, the <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#auth"
+ >android:authorities</a></code> attribute specifies the URI authority
+ that you want to use for content URIs generated by the
+ {@link android.support.v4.content.FileProvider}.
+ In the example, the authority is <code>com.example.myapp.fileprovider</code>. For your own
+ app, specify an authority consisting of the app's
+ <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html#package"
+ >android:package</a></code> value with the string "fileprovider" appended to it. To learn more
+ about the authority value, see the topic
+ <a href="{@docRoot}guide/topics/providers/content-provider-basics.html#ContentURIs"
+ >Content URIs</a> and the documentation for the
+ <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#auth"
+ >android:authorities</a></code> attribute.
+</p>
+<p>
+ The <code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html"
+ >&lt;meta-data&gt;</a></code> child element of the
+ <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"
+ >&lt;provider&gt;</a></code> points to an XML file that specifies the directories you want to
+ share. The <code>android:resource</code> attribute is the path and name of the file, without
+ the <code>.xml</code> extension.The contents of this file are described in the next section.
+</p>
+<h2 id="DefineMetaData">Specify Sharable Directories</h2>
+<p>
+ Once you have added the {@link android.support.v4.content.FileProvider} to your app manifest,
+ you need to specify the directories that contain the files you want to share. To specify the
+ directories, start by creating the file <code>filepaths.xml</code> in the <code>res/xml/</code>
+ subdirectory of your project. In this file, specify the directories by adding an XML element for
+ each directory. The following snippet shows you an example of the contents of
+ <code>res/xml/filepaths.xml</code>. The snippet also demonstrates how to share a subdirectory
+ of the <code>files/</code> directory in your internal storage area:
+</p>
+<pre>
+&lt;paths&gt;
+ &lt;files-path path="images/" name="myimages" /&gt;
+&lt;/paths&gt;</pre>
+<p>
+ In this example, the <code>&lt;files-path&gt;</code> tag shares directories within the
+ <code>files/</code> directory of your app's internal storage. The <code>path</code> attribute
+ shares the <code>images/</code> subdirectory of <code>files/</code>. The <code>name</code>
+ attribute tells the {@link android.support.v4.content.FileProvider} to add the path segment
+ <code>myimages</code> to content URIs for files in the <code>files/images/</code> subdirectory.
+</p>
+<p>
+ The <code>&lt;paths&gt;</code> element can have multiple children, each specifying a different
+ directory to share. In addition to the <code>&lt;files-path&gt;</code> element, you can
+ use the <code>&lt;external-path&gt;</code> element to share directories in external storage, and
+ the <code>&lt;cache-path&gt;</code> element to share directories in your internal cache
+ directory. To learn more about the child elements that specify shared directories, see the
+ {@link android.support.v4.content.FileProvider} reference documentation.
+</p>
+<p class="note">
+ <strong>Note:</strong> The XML file is the only way you can specify the directories you want to
+ share; you can't programmatically add a directory.
+</p>
+<p>
+ You now have a complete specification of a {@link android.support.v4.content.FileProvider}
+ that generates content URIs for files in the <code>files/</code> directory of your app's
+ internal storage or for files in subdirectories of <code>files/</code>. When your app generates
+ a content URI for a file, it contains the authority specified in the
+ <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"
+ >&lt;provider&gt;</a></code> element (<code>com.example.myapp.fileprovider</code>),
+ the path <code>myimages/</code>, and the name of the file.
+</p>
+<p>
+ For example, if you define a {@link android.support.v4.content.FileProvider} according to the
+ snippets in this lesson, and you request a content URI for the file
+ <code>default_image.jpg</code>, {@link android.support.v4.content.FileProvider} returns the
+ following URI:
+</p>
+<pre>
+content://com.example.myapp.fileprovider/myimages/default_image.jpg</pre>
+
diff --git a/docs/html/training/secure-file-sharing/share-file.jd b/docs/html/training/secure-file-sharing/share-file.jd
new file mode 100644
index 0000000..6c52770
--- /dev/null
+++ b/docs/html/training/secure-file-sharing/share-file.jd
@@ -0,0 +1,298 @@
+page.title=Sharing a File
+
+trainingnavtop=true
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#ReceiveRequests">Receive File Requests</a></li>
+ <li><a href="#CreateFileSelection">Create a File Selection Activity</a></li>
+ <li><a href="#RespondToRequest">Respond to a File Selection</a></li>
+ <li><a href="#GrantPermissions">Grant Permissions for the File</a></li>
+ <li><a href="#ShareFile">Share the File with the Requesting App</a>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li>
+ <a href="{@docRoot}guide/topics/providers/content-provider-creating.html#ContentURI"
+ >Designing Content URIs</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/providers/content-provider-creating.html#Permissions"
+ >Implementing Content Provider Permissions</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/security/permissions.html">Permissions</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent Filters</a>
+ </li>
+</ul>
+
+</div>
+</div>
+<p>
+ Once you have set up your app to share files using content URIs, you can respond to other apps'
+ requests for those files. One way to respond to these requests is to provide a file selection
+ interface from the server app that other applications can invoke. This approach allows a client
+ application to let users select a file from the server app and then receive the selected file's
+ content URI.
+</p>
+<p>
+ This lesson shows you how to create a file selection {@link android.app.Activity} in your app
+ that responds to requests for files.
+</p>
+<h2 id="ReceiveRequests">Receive File Requests</h2>
+<p>
+ To receive requests for files from client apps and respond with a content URI, your app should
+ provide a file selection {@link android.app.Activity}. Client apps start this
+ {@link android.app.Activity} by calling {@link android.app.Activity#startActivityForResult
+ startActivityForResult()} with an {@link android.content.Intent} containing the action
+ {@link android.content.Intent#ACTION_PICK ACTION_PICK}. When the client app calls
+ {@link android.app.Activity#startActivityForResult startActivityForResult()}, your app can
+ return a result to the client app, in the form of a content URI for the file the user selected.
+</p>
+<p>
+ To learn how to implement a request for a file in a client app, see the lesson
+ <a href="request-file.html">Requesting a Shared File</a>.
+</p>
+<h2 id="CreateFileSelection">Create a File Selection Activity</h2>
+<p>
+ To set up the file selection {@link android.app.Activity}, start by specifying the
+ {@link android.app.Activity} in your manifest, along with an intent filter
+ that matches the action {@link android.content.Intent#ACTION_PICK ACTION_PICK} and the
+ categories {@link android.content.Intent#CATEGORY_DEFAULT CATEGORY_DEFAULT} and
+ {@link android.content.Intent#CATEGORY_OPENABLE CATEGORY_OPENABLE}. Also add MIME type filters
+ for the files your app serves to other apps. The following snippet shows you how to specify the
+ new {@link android.app.Activity} and intent filter:
+</p>
+<pre>
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ ...
+ &lt;application&gt;
+ ...
+ &lt;activity
+ android:name=".FileSelectActivity"
+ android:label="&#64;"File Selector" &gt;
+ &lt;intent-filter&gt;
+ &lt;action
+ android:name="android.intent.action.PICK"/&gt;
+ &lt;category
+ android:name="android.intent.category.DEFAULT"/&gt;
+ &lt;category
+ android:name="android.intent.category.OPENABLE"/&gt;
+ &lt;data android:mimeType="text/plain"/&gt;
+ &lt;data android:mimeType="image/*"/&gt;
+ &lt;/intent-filter&gt;
+ &lt;/activity&gt;</pre>
+<h3>Define the file selection Activity in code</h3>
+<p>
+ Next, define an {@link android.app.Activity} subclass that displays the files available from
+ your app's <code>files/images/</code> directory in internal storage and allows the user to pick
+ the desired file. The following snippet demonstrates how to define this
+ {@link android.app.Activity} and respond to the user's selection:
+</p>
+<pre>
+public class MainActivity extends Activity {
+ // The path to the root of this app's internal storage
+ private File mPrivateRootDir;
+ // The path to the "images" subdirectory
+ private File mImagesDir;
+ // Array of files in the images subdirectory
+ File[] mImageFiles;
+ // Array of filenames corresponding to mImageFiles
+ String[] mImageFilenames;
+ // Initialize the Activity
+ &#64;Override
+ protected void onCreate(Bundle savedInstanceState) {
+ ...
+ // Set up an Intent to send back to apps that request a file
+ mResultIntent =
+ new Intent("com.example.myapp.ACTION_RETURN_FILE");
+ // Get the files/ subdirectory of internal storage
+ mPrivateRootDir = getFilesDir();
+ // Get the files/images subdirectory;
+ mImagesDir = new File(mPrivateRootDir, "images");
+ // Get the files in the images subdirectory
+ mImageFiles = mImagesDir.listFiles();
+ // Set the Activity's result to null to begin with
+ setResult(Activity.RESULT_CANCELED, null);
+ /*
+ * Display the file names in the ListView mFileListView.
+ * Back the ListView with the array mImageFilenames, which
+ * you can create by iterating through mImageFiles and
+ * calling File.getAbsolutePath() for each File
+ */
+ ...
+ }
+ ...
+}</pre>
+<h2 id="RespondToRequest">Respond to a File Selection</h2>
+<p>
+ Once a user selects a shared file, your application must determine what file was selected and
+ then generate a content URI for the file. Since the {@link android.app.Activity} displays the
+ list of available files in a {@link android.widget.ListView}, when the user clicks a file name
+ the system calls the method {@link android.widget.AdapterView.OnItemClickListener#onItemClick
+ onItemClick()}, in which you can get the selected file.
+</p>
+<p>
+ In {@link android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()}, get a
+ {@link java.io.File} object for the file name of the selected file and pass it as an argument to
+ {@link android.support.v4.content.FileProvider#getUriForFile getUriForFile()}, along with the
+ authority that you specified in the
+ <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"
+ >&lt;provider&gt;</a></code> element for the {@link android.support.v4.content.FileProvider}.
+ The resulting content URI contains the authority, a path segment corresponding to the file's
+ directory (as specified in the XML meta-data), and the name of the file including its
+ extension. How {@link android.support.v4.content.FileProvider} maps directories to path
+ segments based on XML meta-data is described in the section
+ <a href="setup-sharing.html#DefineMetaData">Specify Sharable Directories</a>.
+</p>
+<p>
+ The following snippet shows you how to detect the selected file and get a content URI for it:
+</p>
+<pre>
+ protected void onCreate(Bundle savedInstanceState) {
+ ...
+ // Define a listener that responds to clicks on a file in the ListView
+ mFileListView.setOnItemClickListener(
+ new AdapterView.OnItemClickListener() {
+ &#64;Override
+ /*
+ * When a filename in the ListView is clicked, get its
+ * content URI and send it to the requesting app
+ */
+ public void onItemClick(AdapterView&lt;?&gt; adapterView,
+ View view,
+ int position,
+ long rowId) {
+ /*
+ * Get a File for the selected file name.
+ * Assume that the file names are in the
+ * mImageFilename array.
+ */
+ File requestFile = new File(mImageFilename[position]);
+ /*
+ * Most file-related method calls need to be in
+ * try-catch blocks.
+ */
+ // Use the FileProvider to get a content URI
+ try {
+ fileUri = FileProvider.getUriForFile(
+ MainActivity.this,
+ "com.example.myapp.fileprovider",
+ requestFile);
+ } catch (IllegalArgumentException e) {
+ Log.e("File Selector",
+ "The selected file can't be shared: " +
+ clickedFilename);
+ }
+ ...
+ }
+ });
+ ...
+ }</pre>
+<p>
+ Remember that you can only generate content URIs for files that reside in a directory
+ you've specified in the meta-data file that contains the <code>&lt;paths&gt;</code> element, as
+ described in the section <a href="setup-sharing.html#DefineMetaData"
+ >Specify Sharable Directories</a>. If you call
+ {@link android.support.v4.content.FileProvider#getUriForFile getUriForFile()} for a
+ {@link java.io.File} in a path that you haven't specified, you receive an
+ {@link java.lang.IllegalArgumentException}.
+</p>
+<h2 id="GrantPermissions">Grant Permissions for the File</h2>
+<p>
+ Now that you have a content URI for the file you want to share with another app, you need to
+ allow the client app to access the file. To allow access, grant permissions to the client app by
+ adding the content URI to an {@link android.content.Intent} and then setting permission flags on
+ the {@link android.content.Intent}. The permissions you grant are temporary and expire
+ automatically when the receiving app's task stack is finished.
+</p>
+<p>
+ The following code snippet shows you how to set read permission for the file:
+</p>
+<pre>
+ protected void onCreate(Bundle savedInstanceState) {
+ ...
+ // Define a listener that responds to clicks in the ListView
+ mFileListView.setOnItemClickListener(
+ new AdapterView.OnItemClickListener() {
+ &#64;Override
+ public void onItemClick(AdapterView&lt;?&gt; adapterView,
+ View view,
+ int position,
+ long rowId) {
+ ...
+ if (fileUri != null) {
+ // Grant temporary read permission to the content URI
+ mResultIntent.addFlags(
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ ...
+ }
+ ...
+ });
+ ...
+ }</pre>
+<p class="caution">
+ <strong>Caution:</strong> Calling {@link android.content.Intent#setFlags setFlags()} is the only
+ way to securely grant access to your files using temporary access permissions. Avoid calling
+ {@link android.content.Context#grantUriPermission Context.grantUriPermission()} method for a
+ file's content URI, since this method grants access that you can only revoke by
+ calling {@link android.content.Context#revokeUriPermission Context.revokeUriPermission()}.
+</p>
+<h2 id="ShareFile">Share the File with the Requesting App</h2>
+<p>
+ To share the file with the app that requested it, pass the {@link android.content.Intent}
+ containing the content URI and permissions to {@link android.app.Activity#setResult
+ setResult()}. When the {@link android.app.Activity} you have just defined is finished, the
+ system sends the {@link android.content.Intent} containing the content URI to the client app.
+ The following code snippet shows you how to do this:
+</p>
+<pre>
+ protected void onCreate(Bundle savedInstanceState) {
+ ...
+ // Define a listener that responds to clicks on a file in the ListView
+ mFileListView.setOnItemClickListener(
+ new AdapterView.OnItemClickListener() {
+ &#64;Override
+ public void onItemClick(AdapterView&lt;?&gt; adapterView,
+ View view,
+ int position,
+ long rowId) {
+ ...
+ if (fileUri != null) {
+ ...
+ // Put the Uri and MIME type in the result Intent
+ mResultIntent.setDataAndType(
+ fileUri,
+ getContentResolver().getType(fileUri));
+ // Set the result
+ MainActivity.this.setResult(Activity.RESULT_OK,
+ mResultIntent);
+ } else {
+ mResultIntent.setDataAndType(null, "");
+ MainActivity.this.setResult(RESULT_CANCELED,
+ mResultIntent);
+ }
+ }
+ });</pre>
+<p>
+ Provide users with an way to return immediately to the client app once they have chosen a file.
+ One way to do this is to provide a checkmark or <b>Done</b> button. Associate a method with
+ the button using the button's
+ <code><a href="{@docRoot}reference/android/view/View.html#attr_android:onClick"
+ >android:onClick</a></code> attribute. In the method, call
+ {@link android.app.Activity#finish finish()}. For example:
+</p>
+<pre>
+ public void onDoneClick(View v) {
+ // Associate a method with the Done button
+ finish();
+ }</pre>