Une fois que vous avez configuré votre application pour partager des fichiers à l'aide d'URI de contenu, vous pouvez répondre aux messages d'autres applications pour ces fichiers. Une façon de répondre à ces demandes consiste à fournir une sélection de fichiers de l'application serveur que d'autres applications peuvent appeler. Cette approche permet à un client pour permettre aux utilisateurs de sélectionner un fichier dans l'application de serveur et de recevoir les l'URI de contenu.
Cette leçon explique comment créer une sélection de fichiers Activity
dans votre application
qui répond aux demandes de fichiers.
Recevoir des demandes de fichiers
Pour recevoir des requêtes de fichiers provenant d'applications clientes et répondre avec un URI de contenu, votre application doit :
fournir une sélection de fichiers Activity
. Les applis clientes démarrent ceci
Activity
en appelant startActivityForResult()
avec un Intent
contenant l'action
ACTION_PICK
Lorsque l'application cliente appelle
startActivityForResult()
, votre application peut
renvoient un résultat à l'application cliente, sous la forme d'un URI de contenu pour le fichier sélectionné par l'utilisateur.
Pour découvrir comment implémenter une requête de fichier dans une application cliente, consultez la leçon Demander un fichier partagé
Créer une activité de sélection de fichiers
Pour configurer la sélection de fichiers Activity
, commencez par spécifier le
Activity
dans votre fichier manifeste, avec un filtre d'intent
qui correspond à l'action ACTION_PICK
et à
les catégories CATEGORY_DEFAULT
et
CATEGORY_OPENABLE
Ajouter également des filtres de types MIME
pour les fichiers que votre application diffuse vers d'autres applications. L'extrait de code suivant vous montre comment spécifier
Nouveau Activity
et filtre d'intent:
<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>
Définir l'activité de sélection de fichiers dans le code
Ensuite, définissez une sous-classe Activity
qui affiche les fichiers disponibles à partir de
le répertoire files/images/
de votre application dans la mémoire de stockage interne et permet à l'utilisateur de choisir
le fichier souhaité. L'extrait de code suivant montre comment définir
Activity
et répondre à la sélection de l'utilisateur:
Kotlin
class MainActivity : Activity() { // The path to the root of this app's internal storage private lateinit var privateRootDir: File // The path to the "images" subdirectory private lateinit var imagesDir: File // Array of files in the images subdirectory private lateinit var imageFiles: Array<File> // Array of filenames corresponding to imageFiles private lateinit var imageFilenames: Array<String> // Initialize the Activity override fun onCreate(savedInstanceState: Bundle?) { ... // Set up an Intent to send back to apps that request a file resultIntent = Intent("com.example.myapp.ACTION_RETURN_FILE") // Get the files/ subdirectory of internal storage privateRootDir = filesDir // Get the files/images subdirectory; imagesDir = File(privateRootDir, "images") // Get the files in the images subdirectory imageFiles = imagesDir.listFiles() // Set the Activity's result to null to begin with setResult(Activity.RESULT_CANCELED, null) /* * Display the file names in the ListView fileListView. * Back the ListView with the array imageFilenames, which * you can create by iterating through imageFiles and * calling File.getAbsolutePath() for each File */ ... } ... }
Java
public class MainActivity extends Activity { // The path to the root of this app's internal storage private File privateRootDir; // The path to the "images" subdirectory private File imagesDir; // Array of files in the images subdirectory File[] imageFiles; // Array of filenames corresponding to imageFiles String[] imageFilenames; // Initialize the Activity @Override protected void onCreate(Bundle savedInstanceState) { ... // Set up an Intent to send back to apps that request a file resultIntent = new Intent("com.example.myapp.ACTION_RETURN_FILE"); // Get the files/ subdirectory of internal storage privateRootDir = getFilesDir(); // Get the files/images subdirectory; imagesDir = new File(privateRootDir, "images"); // Get the files in the images subdirectory imageFiles = imagesDir.listFiles(); // Set the Activity's result to null to begin with setResult(Activity.RESULT_CANCELED, null); /* * Display the file names in the ListView fileListView. * Back the ListView with the array imageFilenames, which * you can create by iterating through imageFiles and * calling File.getAbsolutePath() for each File */ ... } ... }
Répondre à une sélection de fichiers
Lorsqu'un utilisateur sélectionne un fichier partagé, votre application doit déterminer le fichier sélectionné et
puis générez un URI de contenu pour le fichier. Étant donné que Activity
affiche
liste des fichiers disponibles dans un ListView
, lorsque l'utilisateur clique sur un nom de fichier
le système appelle la méthode onItemClick()
, dans laquelle vous pouvez obtenir le fichier sélectionné.
Lorsque vous utilisez un intent pour envoyer l'URI d'un fichier d'une application à une autre,
vous devez veiller à obtenir un URI
les applications peuvent lire. Effectuer cette opération sur des appareils équipés d'Android 6.0 (niveau d'API 23) ou version ultérieure
requiert des
en raison des modifications apportées au modèle d'autorisations dans cette version d'Android, notamment
pour READ_EXTERNAL_STORAGE
devient
une autorisation dangereuse, dont l'application réceptrice peut ne pas disposer.
En gardant ces considérations à l'esprit, nous
vous recommandons d'éviter d'utiliser
Uri.fromFile()
, qui
présente plusieurs inconvénients. Cette méthode:
- N'autorise pas le partage de fichiers entre profils.
- Votre application doit disposer
WRITE_EXTERNAL_STORAGE
autorisation sur les appareils équipés d'Android 4.4 (niveau d'API 19) ou version antérieure. - Nécessite que les applications réceptrices disposent des
L'autorisation
READ_EXTERNAL_STORAGE
, qui échouera sur les cibles de partage importantes, comme Gmail, qui ne disposent pas de cette autorisation.
Au lieu d'utiliser Uri.fromFile()
,
vous pouvez utiliser des autorisations d'URI pour accorder à d'autres applications
l'accès à des URI spécifiques. Bien que les autorisations d'URI ne fonctionnent pas sur les URI file://
généré par Uri.fromFile()
,
sur les URI associés à des fournisseurs de contenu. La
L'API FileProvider
peut
pour vous aider à créer ces URI. Cette approche fonctionne également
avec les fichiers qui ne sont pas
dans un espace de stockage externe, mais dans le stockage local de l'application qui envoie l'intent.
Dans onItemClick()
, obtenez une
File
pour le nom du fichier sélectionné et le transmettre en tant qu'argument à
getUriForFile()
, ainsi que les
que vous avez spécifiée dans le
<provider>
pour FileProvider
.
L'URI de contenu résultant contient l'autorité, un segment de chemin correspondant à l'attribut
(comme indiqué dans les métadonnées XML) et le nom du fichier, y compris ses
. Comment FileProvider
mappe les répertoires sur le chemin d'accès
basés sur des métadonnées XML est décrit dans la section
Spécifiez les répertoires partageables.
L'extrait de code suivant vous montre comment détecter le fichier sélectionné et obtenir un URI de contenu pour celui-ci:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> /* * Get a File for the selected file name. * Assume that the file names are in the * imageFilename array. */ val requestFile = File(imageFilenames[position]) /* * Most file-related method calls need to be in * try-catch blocks. */ // Use the FileProvider to get a content URI val fileUri: Uri? = try { FileProvider.getUriForFile( this@MainActivity, "com.example.myapp.fileprovider", requestFile) } catch (e: IllegalArgumentException) { Log.e("File Selector", "The selected file can't be shared: $requestFile") null } ... } ... }
Java
protected void onCreate(Bundle savedInstanceState) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.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 * imageFilename array. */ File requestFile = new File(imageFilename[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: " + requestFile.toString()); } ... } }); ... }
N'oubliez pas que vous ne pouvez générer des URI de contenu que pour les fichiers qui se trouvent dans un répertoire
que vous avez spécifié dans le fichier de métadonnées contenant l'élément <paths>
, comme suit :
décrit dans la section Spécifier les répertoires partageables. Si vous appelez
getUriForFile()
pour
File
dans un chemin d'accès que vous n'avez pas spécifié, vous recevez une
IllegalArgumentException
Accorder les autorisations pour le fichier
Maintenant que vous disposez d'un URI de contenu pour le fichier que vous souhaitez partager avec une autre application, vous devez
autoriser l'application cliente
à accéder au fichier. Pour autoriser l'accès, accordez des autorisations à l'application cliente
en ajoutant l'URI de contenu à une Intent
, puis en définissant des indicateurs d'autorisation sur
la Intent
. Les autorisations que vous accordez sont temporaires et expirent
automatiquement lorsque la pile de tâches
de l'application réceptrice est terminée.
L'extrait de code suivant vous montre comment définir l'autorisation de lecture pour le fichier:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> ... if (fileUri != null) { // Grant temporary read permission to the content URI resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) ... } ... } ... }
Java
protected void onCreate(Bundle savedInstanceState) { ... // Define a listener that responds to clicks in the ListView fileListView.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 resultIntent.addFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION); } ... } ... }); ... }
Attention:Appeler setFlags()
est le seul
d’accorder l’accès à vos fichiers en toute sécurité
à l’aide d’autorisations d’accès temporaires. Éviter d'appeler
Context.grantUriPermission()
pour une
l'URI de contenu du fichier, car cette méthode accorde un accès que vous ne pouvez révoquer
Appel de Context.revokeUriPermission()
en cours.
N'utilisez pas Uri.fromFile()
. Il force la réception d'applications
disposer de l'autorisation READ_EXTERNAL_STORAGE
,
ne fonctionneront pas du tout si vous essayez de partager des éléments entre utilisateurs,
d'Android antérieur à 4.4 (niveau d'API 19), nécessite votre
l'application pour avoir WRITE_EXTERNAL_STORAGE
.
Et les cibles de partage vraiment importantes, comme l'application Gmail, n'ont pas
le READ_EXTERNAL_STORAGE
, ce qui entraîne
pour que cet appel échoue.
À la place, vous pouvez utiliser des autorisations d'URI pour accorder à d'autres applications l'accès à des URI spécifiques.
Bien que les autorisations d'URI ne fonctionnent pas sur les URI file:// tels qu'ils sont générés par
Uri.fromFile()
, c'est le cas
sur les URI associés aux fournisseurs de contenu. Au lieu d'implémenter le vôtre
uniquement pour cela,
vous pouvez et devez utiliser FileProvider
comme expliqué dans la section Partager des fichiers.
Partager le fichier avec l'application à l'origine de la demande
Pour partager le fichier avec l'application à l'origine de la demande, transmettez Intent
contenant l'URI de contenu et les autorisations pour setResult()
. Lorsque la Activity
que vous venez de définir est terminée, la
le système envoie le Intent
contenant l'URI du contenu à l'application cliente.
L'extrait de code suivant vous montre comment procéder:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> ... if (fileUri != null) { ... // Put the Uri and MIME type in the result Intent resultIntent.setDataAndType(fileUri, contentResolver.getType(fileUri)) // Set the result setResult(Activity.RESULT_OK, resultIntent) } else { resultIntent.setDataAndType(null, "") setResult(RESULT_CANCELED, resultIntent) } } }
Java
protected void onCreate(Bundle savedInstanceState) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.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 resultIntent.setDataAndType( fileUri, getContentResolver().getType(fileUri)); // Set the result MainActivity.this.setResult(Activity.RESULT_OK, resultIntent); } else { resultIntent.setDataAndType(null, ""); MainActivity.this.setResult(RESULT_CANCELED, resultIntent); } } });
Offrez aux utilisateurs un moyen de revenir immédiatement à l'application cliente après avoir choisi un fichier.
Pour ce faire, vous pouvez cocher la case ou cliquer sur le bouton OK. Associer une méthode à
le bouton à l'aide de la flèche
android:onClick
. Dans la méthode, appelez
finish()
Exemple :
Kotlin
fun onDoneClick(v: View) { // Associate a method with the Done button finish() }
Java
public void onDoneClick(View v) { // Associate a method with the Done button finish(); }
Pour en savoir plus, consultez les ressources suivantes:
- Concevoir des URI de contenu
- Implémenter des autorisations de fournisseur de contenu
- Autorisations
- Intents et filtres d'intents