diff options
Diffstat (limited to 'docs/html/training/secure-file-sharing')
| -rw-r--r-- | docs/html/training/secure-file-sharing/index.jd | 80 | ||||
| -rw-r--r-- | docs/html/training/secure-file-sharing/request-file.jd | 147 | ||||
| -rw-r--r-- | docs/html/training/secure-file-sharing/retrieve-info.jd | 110 | ||||
| -rw-r--r-- | docs/html/training/secure-file-sharing/setup-sharing.jd | 136 | ||||
| -rw-r--r-- | docs/html/training/secure-file-sharing/share-file.jd | 298 |
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; + ... + @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. + */ + @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" + ><provider></a></code> element that specifies the + {@link android.support.v4.content.FileProvider} class, the authority, and the + XML file name: +</p> +<pre> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.myapp"> + <application + ...> + <provider + android:name="android.support.v4.content.FileProvider" + android:authorities="com.example.myapp.fileprovider" + android:grantUriPermissions="true" + android:exported="false"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/filepaths" /> + </provider> + ... + </application> +</manifest></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" + ><meta-data></a></code> child element of the + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html" + ><provider></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> +<paths> + <files-path path="images/" name="myimages" /> +</paths></pre> +<p> + In this example, the <code><files-path></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><paths></code> element can have multiple children, each specifying a different + directory to share. In addition to the <code><files-path></code> element, you can + use the <code><external-path></code> element to share directories in external storage, and + the <code><cache-path></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" + ><provider></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> +<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> |
