diff options
Diffstat (limited to 'docs/html/training/secure-file-sharing/share-file.jd')
-rw-r--r-- | docs/html/training/secure-file-sharing/share-file.jd | 298 |
1 files changed, 298 insertions, 0 deletions
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> +<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> |