45edbb701a
Change-Id: Idad8840dec02150733f23cfefdeaf152a260f63a
299 lines
13 KiB
Plaintext
299 lines
13 KiB
Plaintext
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>
|
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
...
|
|
<application>
|
|
...
|
|
<activity
|
|
android:name=".FileSelectActivity"
|
|
android:label="@"File Selector" >
|
|
<intent-filter>
|
|
<action
|
|
android:name="android.intent.action.PICK"/>
|
|
<category
|
|
android:name="android.intent.category.DEFAULT"/>
|
|
<category
|
|
android:name="android.intent.category.OPENABLE"/>
|
|
<data android:mimeType="text/plain"/>
|
|
<data android:mimeType="image/*"/>
|
|
</intent-filter>
|
|
</activity></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
|
|
@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"
|
|
><provider></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() {
|
|
@Override
|
|
/*
|
|
* When a filename in the ListView is clicked, get its
|
|
* content URI and send it to the requesting app
|
|
*/
|
|
public void onItemClick(AdapterView<?> 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><paths></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() {
|
|
@Override
|
|
public void onItemClick(AdapterView<?> 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() {
|
|
@Override
|
|
public void onItemClick(AdapterView<?> 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>
|