• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Sharing a File
2
3trainingnavtop=true
4@jd:body
5
6
7<div id="tb-wrapper">
8<div id="tb">
9
10<h2>This lesson teaches you to</h2>
11<ol>
12  <li><a href="#ReceiveRequests">Receive File Requests</a></li>
13  <li><a href="#CreateFileSelection">Create a File Selection Activity</a></li>
14  <li><a href="#RespondToRequest">Respond to a File Selection</a></li>
15  <li><a href="#GrantPermissions">Grant Permissions for the File</a></li>
16  <li><a href="#ShareFile">Share the File with the Requesting App</a>
17</ol>
18
19<h2>You should also read</h2>
20<ul>
21    <li>
22        <a href="{@docRoot}guide/topics/providers/content-provider-creating.html#ContentURI"
23        >Designing Content URIs</a>
24    </li>
25    <li>
26        <a href="{@docRoot}guide/topics/providers/content-provider-creating.html#Permissions"
27        >Implementing Content Provider Permissions</a>
28    </li>
29    <li>
30        <a href="{@docRoot}guide/topics/security/permissions.html">Permissions</a>
31    </li>
32    <li>
33        <a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent Filters</a>
34    </li>
35</ul>
36
37</div>
38</div>
39<p>
40    Once you have set up your app to share files using content URIs, you can respond to other apps'
41    requests for those files. One way to respond to these requests is to provide a file selection
42    interface from the server app that other applications can invoke. This approach allows a client
43    application to let users select a file from the server app and then receive the selected file's
44    content URI.
45</p>
46<p>
47    This lesson shows you how to create a file selection {@link android.app.Activity} in your app
48    that responds to requests for files.
49</p>
50<h2 id="ReceiveRequests">Receive File Requests</h2>
51<p>
52    To receive requests for files from client apps and respond with a content URI, your app should
53    provide a file selection {@link android.app.Activity}. Client apps start this
54    {@link android.app.Activity} by calling {@link android.app.Activity#startActivityForResult
55    startActivityForResult()} with an {@link android.content.Intent} containing the action
56    {@link android.content.Intent#ACTION_PICK ACTION_PICK}. When the client app calls
57    {@link android.app.Activity#startActivityForResult startActivityForResult()}, your app can
58    return a result to the client app, in the form of a content URI for the file the user selected.
59</p>
60<p>
61    To learn how to implement a request for a file in a client app, see the lesson
62    <a href="request-file.html">Requesting a Shared File</a>.
63</p>
64<h2 id="CreateFileSelection">Create a File Selection Activity</h2>
65<p>
66    To set up the file selection {@link android.app.Activity}, start by specifying the
67    {@link android.app.Activity} in your manifest, along with an intent filter
68    that matches the action {@link android.content.Intent#ACTION_PICK ACTION_PICK} and the
69    categories {@link android.content.Intent#CATEGORY_DEFAULT CATEGORY_DEFAULT} and
70    {@link android.content.Intent#CATEGORY_OPENABLE CATEGORY_OPENABLE}.  Also add MIME type filters
71    for the files your app serves to other apps. The following snippet shows you how to specify the
72    new {@link android.app.Activity} and intent filter:
73</p>
74<pre>
75&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"&gt;
76    ...
77        &lt;application&gt;
78        ...
79            &lt;activity
80                android:name=".FileSelectActivity"
81                android:label="&#64;File Selector" &gt;
82                &lt;intent-filter&gt;
83                    &lt;action
84                        android:name="android.intent.action.PICK"/&gt;
85                    &lt;category
86                        android:name="android.intent.category.DEFAULT"/&gt;
87                    &lt;category
88                        android:name="android.intent.category.OPENABLE"/&gt;
89                    &lt;data android:mimeType="text/plain"/&gt;
90                    &lt;data android:mimeType="image/*"/&gt;
91                &lt;/intent-filter&gt;
92            &lt;/activity&gt;</pre>
93<h3>Define the file selection Activity in code</h3>
94<p>
95    Next, define an {@link android.app.Activity} subclass that displays the files available from
96    your app's <code>files/images/</code> directory in internal storage and allows the user to pick
97    the desired file. The following snippet demonstrates how to define this
98    {@link android.app.Activity} and respond to the user's selection:
99</p>
100<pre>
101public class MainActivity extends Activity {
102    // The path to the root of this app's internal storage
103    private File mPrivateRootDir;
104    // The path to the "images" subdirectory
105    private File mImagesDir;
106    // Array of files in the images subdirectory
107    File[] mImageFiles;
108    // Array of filenames corresponding to mImageFiles
109    String[] mImageFilenames;
110    // Initialize the Activity
111    &#64;Override
112    protected void onCreate(Bundle savedInstanceState) {
113        ...
114        // Set up an Intent to send back to apps that request a file
115        mResultIntent =
116                new Intent("com.example.myapp.ACTION_RETURN_FILE");
117        // Get the files/ subdirectory of internal storage
118        mPrivateRootDir = getFilesDir();
119        // Get the files/images subdirectory;
120        mImagesDir = new File(mPrivateRootDir, "images");
121        // Get the files in the images subdirectory
122        mImageFiles = mImagesDir.listFiles();
123        // Set the Activity's result to null to begin with
124        setResult(Activity.RESULT_CANCELED, null);
125        /*
126         * Display the file names in the ListView mFileListView.
127         * Back the ListView with the array mImageFilenames, which
128         * you can create by iterating through mImageFiles and
129         * calling File.getAbsolutePath() for each File
130         */
131         ...
132    }
133    ...
134}</pre>
135<h2 id="RespondToRequest">Respond to a File Selection</h2>
136<p>
137    Once a user selects a shared file, your application must determine what file was selected and
138    then generate a content URI for the file. Since the {@link android.app.Activity} displays the
139    list of available files in a {@link android.widget.ListView}, when the user clicks a file name
140    the system calls the method {@link android.widget.AdapterView.OnItemClickListener#onItemClick
141    onItemClick()}, in which you can get the selected file.
142</p>
143<p>
144    In {@link android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()}, get a
145    {@link java.io.File} object for the file name of the selected file and pass it as an argument to
146    {@link android.support.v4.content.FileProvider#getUriForFile getUriForFile()}, along with the
147    authority that you specified in the
148    <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"
149    >&lt;provider&gt;</a></code> element for the {@link android.support.v4.content.FileProvider}.
150    The resulting content URI contains the authority, a path segment corresponding to the file's
151    directory (as specified in the XML meta-data), and the name of the file including its
152    extension. How {@link android.support.v4.content.FileProvider} maps directories to path
153    segments based on XML meta-data is described in the section
154    <a href="setup-sharing.html#DefineMetaData">Specify Sharable Directories</a>.
155</p>
156<p>
157    The following snippet shows you how to detect the selected file and get a content URI for it:
158</p>
159<pre>
160    protected void onCreate(Bundle savedInstanceState) {
161        ...
162        // Define a listener that responds to clicks on a file in the ListView
163        mFileListView.setOnItemClickListener(
164                new AdapterView.OnItemClickListener() {
165            &#64;Override
166            /*
167             * When a filename in the ListView is clicked, get its
168             * content URI and send it to the requesting app
169             */
170            public void onItemClick(AdapterView&lt;?&gt; adapterView,
171                    View view,
172                    int position,
173                    long rowId) {
174                /*
175                 * Get a File for the selected file name.
176                 * Assume that the file names are in the
177                 * mImageFilename array.
178                 */
179                File requestFile = new File(mImageFilename[position]);
180                /*
181                 * Most file-related method calls need to be in
182                 * try-catch blocks.
183                 */
184                // Use the FileProvider to get a content URI
185                try {
186                    fileUri = FileProvider.getUriForFile(
187                            MainActivity.this,
188                            "com.example.myapp.fileprovider",
189                            requestFile);
190                } catch (IllegalArgumentException e) {
191                    Log.e("File Selector",
192                          "The selected file can't be shared: " +
193                          clickedFilename);
194                }
195                ...
196            }
197        });
198        ...
199    }</pre>
200<p>
201    Remember that you can only generate content URIs for files that reside in a directory
202    you've specified in the meta-data file that contains the <code>&lt;paths&gt;</code> element, as
203    described in the section <a href="setup-sharing.html#DefineMetaData"
204    >Specify Sharable Directories</a>. If you call
205    {@link android.support.v4.content.FileProvider#getUriForFile getUriForFile()} for a
206    {@link java.io.File} in a path that you haven't specified, you receive an
207    {@link java.lang.IllegalArgumentException}.
208</p>
209<h2 id="GrantPermissions">Grant Permissions for the File</h2>
210<p>
211    Now that you have a content URI for the file you want to share with another app, you need to
212    allow the client app to access the file. To allow access, grant permissions to the client app by
213    adding the content URI to an {@link android.content.Intent} and then setting permission flags on
214    the {@link android.content.Intent}. The permissions you grant are temporary and expire
215    automatically when the receiving app's task stack is finished.
216</p>
217<p>
218    The following code snippet shows you how to set read permission for the file:
219</p>
220<pre>
221    protected void onCreate(Bundle savedInstanceState) {
222        ...
223        // Define a listener that responds to clicks in the ListView
224        mFileListView.setOnItemClickListener(
225                new AdapterView.OnItemClickListener() {
226            &#64;Override
227            public void onItemClick(AdapterView&lt;?&gt; adapterView,
228                    View view,
229                    int position,
230                    long rowId) {
231                ...
232                if (fileUri != null) {
233                    // Grant temporary read permission to the content URI
234                    mResultIntent.addFlags(
235                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
236                }
237                ...
238             }
239             ...
240        });
241    ...
242    }</pre>
243<p class="caution">
244    <strong>Caution:</strong> Calling {@link android.content.Intent#setFlags setFlags()} is the only
245    way to securely grant access to your files using temporary access permissions. Avoid calling
246    {@link android.content.Context#grantUriPermission Context.grantUriPermission()} method for a
247    file's content URI, since this method grants access that you can only revoke by
248    calling {@link android.content.Context#revokeUriPermission Context.revokeUriPermission()}.
249</p>
250<h2 id="ShareFile">Share the File with the Requesting App</h2>
251<p>
252    To share the file with the app that requested it, pass the {@link android.content.Intent}
253    containing the content URI and permissions to {@link android.app.Activity#setResult
254    setResult()}. When the {@link android.app.Activity} you have just defined is finished, the
255    system sends the {@link android.content.Intent} containing the content URI to the client app.
256    The following code snippet shows you how to do this:
257</p>
258<pre>
259    protected void onCreate(Bundle savedInstanceState) {
260        ...
261        // Define a listener that responds to clicks on a file in the ListView
262        mFileListView.setOnItemClickListener(
263                new AdapterView.OnItemClickListener() {
264            &#64;Override
265            public void onItemClick(AdapterView&lt;?&gt; adapterView,
266                    View view,
267                    int position,
268                    long rowId) {
269                ...
270                if (fileUri != null) {
271                    ...
272                    // Put the Uri and MIME type in the result Intent
273                    mResultIntent.setDataAndType(
274                            fileUri,
275                            getContentResolver().getType(fileUri));
276                    // Set the result
277                    MainActivity.this.setResult(Activity.RESULT_OK,
278                            mResultIntent);
279                    } else {
280                        mResultIntent.setDataAndType(null, "");
281                        MainActivity.this.setResult(RESULT_CANCELED,
282                                mResultIntent);
283                    }
284                }
285        });</pre>
286<p>
287    Provide users with an way to return immediately to the client app once they have chosen a file.
288    One way to do this is to provide a checkmark or <b>Done</b> button. Associate a method with
289    the button using the button's
290    <code><a href="{@docRoot}reference/android/view/View.html#attr_android:onClick"
291    >android:onClick</a></code> attribute. In the method, call
292    {@link android.app.Activity#finish finish()}. For example:
293</p>
294<pre>
295    public void onDoneClick(View v) {
296        // Associate a method with the Done button
297        finish();
298    }</pre>
299