Partager un fichier

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: