web-dev-qa-db-fra.com

Filtre d'intention Android: associer une application à une extension de fichier

Je souhaite associer mon application à un type/une extension de fichier personnalisé.

Autant que je sache, l'élément de données est créé à cet effet, mais je ne parviens pas à le faire fonctionner . http://developer.Android.com/guide/topics/manifest/data-element.html Selon la documentation et de nombreux messages de forum, cela devrait fonctionner comme suit:

<intent-filter>
    <action Android:name="Android.intent.action.MAIN" />
    <category Android:name="Android.intent.category.DEFAULT" />
    <category Android:name="Android.intent.category.BROWSABLE" />
    <data Android:mimeType="application/pdf" />
</intent-filter>

Ça ne marche pas. Qu'ai-je fait de mal? Je veux simplement déclarer mon propre type de fichier.

85
Tamas

Vous avez besoin de plusieurs filtres d'intention pour traiter les différentes situations que vous souhaitez gérer.

Exemple 1, gérer les requêtes http sans types MIME:

  <intent-filter>
    <action Android:name="Android.intent.action.VIEW" />
    <category Android:name="Android.intent.category.BROWSABLE" />
    <category Android:name="Android.intent.category.DEFAULT" />
    <data Android:scheme="http" />
    <data Android:Host="*" />
    <data Android:pathPattern=".*\\.pdf" />
  </intent-filter>

Manipulez avec les types MIME où le suffixe est sans importance:

  <intent-filter>
    <action Android:name="Android.intent.action.VIEW" />
    <category Android:name="Android.intent.category.BROWSABLE" />
    <category Android:name="Android.intent.category.DEFAULT" />
    <data Android:scheme="http" />
    <data Android:Host="*" />
    <data Android:mimeType="application/pdf" />
  </intent-filter>

Gérer l'intention depuis une application de navigateur de fichiers:

  <intent-filter>
    <action Android:name="Android.intent.action.VIEW" />
    <category Android:name="Android.intent.category.DEFAULT" />
    <data Android:scheme="file" />
    <data Android:Host="*" />
    <data Android:pathPattern=".*\\.pdf" />
  </intent-filter>
110
Phyrum Tea

Les autres solutions ne fonctionnaient pas de manière fiable pour moi jusqu'à ce que j'ajoute:

Android:mimeType="*/*" 

Avant cela, cela fonctionnait dans certaines applications, dans d'autres pas ...

solution complète pour moi:

<intent-filter>
  <action Android:name="Android.intent.action.VIEW" />
  <category Android:name="Android.intent.category.DEFAULT" />
  <data Android:scheme="file"  Android:Host="*" Android:pathPattern=".*\\.EXT" Android:mimeType="*/*"  />
</intent-filter>
45
Emmanuel Touzery

Les réponses données par Phyrum Tea et yuku sont déjà très instructives.

Je souhaite ajouter que, à partir de Android 7.0 Nougat, la manière dont le partage de fichiers entre les applications est géré est modifiée:

From official Changements Android 7.0 :

Pour les applications ciblant Android 7.0, le cadre Android applique le Stratégie d'API StrictMode interdisant l'exposition des fichiers: // URI en dehors de votre application. Si une intention contenant un fichier URI quitte votre application, l'application échoue avec une exception FileUriExposedException.

Pour partager des fichiers entre des applications, vous devez envoyer un contenu: // URI et accordez une autorisation d'accès temporaire sur l'URI. La meilleure façon de accorder cette permission est en utilisant la classe FileProvider. Pour plus Pour plus d'informations sur les autorisations et le partage de fichiers, voir Partage de fichiers.

Si vous avez votre propre fichier personnalisé se terminant sans un mime-type spécifique (ou même, je suppose, avec un seul), vous devrez peut-être ajouter une seconde valeur scheme à votre intent-filter pour que cela fonctionne également avec FileProviders.

Exemple:

<intent-filter>
    <action Android:name="Android.intent.action.VIEW" />

    <category Android:name="Android.intent.category.DEFAULT" />
    <category Android:name="Android.intent.category.BROWSABLE" />

    <data Android:scheme="file" />
    <data Android:scheme="content" />
    <data Android:mimeType="*/*" />
    <!--
        Work around Android's ugly primitive PatternMatcher
        implementation that can't cope with finding a . early in
        the path unless it's explicitly matched.
    -->
    <data Android:Host="*" />
    <data Android:pathPattern=".*\\.sfx" />
    <data Android:pathPattern=".*\\..*\\.sfx" />
    <data Android:pathPattern=".*\\..*\\..*\\.sfx" />
    <data Android:pathPattern=".*\\..*\\..*\\..*\\.sfx" />
    <!-- keep going if you need more -->

</intent-filter>

La chose importante ici est l'ajout de

<data Android:scheme="content" />

au filtre.

J'ai eu du mal à découvrir ce petit changement qui empêchait mon activité de s'ouvrir sur les appareils Android 7.0 alors que tout allait bien pour les versions antérieures. J'espère que ça aide quelqu'un.

18
Markus Ressel

Mes trouvailles:

Vous avez besoin de plusieurs filtres pour traiter les différentes manières de récupérer un fichier. C'est-à-dire, par pièce jointe gmail, par l'explorateur de fichiers, par HTTP, par FTP, etc., ils ont tous des intentions très différentes.

Et vous devez filtrer l'intention qui déclenche votre activité dans votre code d'activité.

Pour l'exemple ci-dessous, j'ai créé un faux type de fichier new.mrz. Et je l'ai récupéré de la pièce jointe gmail et de l'explorateur de fichiers.

Code d'activité ajouté dans onCreate ():

        Intent intent = getIntent();
        String action = intent.getAction();

        if (action.compareTo(Intent.ACTION_VIEW) == 0) {
            String scheme = intent.getScheme();
            ContentResolver resolver = getContentResolver();

            if (scheme.compareTo(ContentResolver.SCHEME_CONTENT) == 0) {
                Uri uri = intent.getData();
                String name = getContentName(resolver, uri);

                Log.v("tag" , "Content intent detected: " + action + " : " + intent.getDataString() + " : " + intent.getType() + " : " + name);
                InputStream input = resolver.openInputStream(uri);
                String importfilepath = "/sdcard/My Documents/" + name; 
                InputStreamToFile(input, importfilepath);
            }
            else if (scheme.compareTo(ContentResolver.SCHEME_FILE) == 0) {
                Uri uri = intent.getData();
                String name = uri.getLastPathSegment();

                Log.v("tag" , "File intent detected: " + action + " : " + intent.getDataString() + " : " + intent.getType() + " : " + name);
                InputStream input = resolver.openInputStream(uri);
                String importfilepath = "/sdcard/My Documents/" + name; 
                InputStreamToFile(input, importfilepath);
            }
            else if (scheme.compareTo("http") == 0) {
                // TODO Import from HTTP!
            }
            else if (scheme.compareTo("ftp") == 0) {
                // TODO Import from FTP!
            }
        }

Filtre de pièces jointes Gmail:

        <intent-filter Android:label="@string/app_name">
            <action Android:name="Android.intent.action.VIEW" />
            <category Android:name="Android.intent.category.DEFAULT" />
            <data Android:scheme="content" />
            <data Android:mimeType="application/octet-stream" />
        </intent-filter>
  • LOG: intention du contenu détectée: Android.intent.action.VIEW: contenu: //gmail-ls/[email protected]/messages/2950/attachments/0.1/BEST/faux: application/octet-stream: nouveau. mrz

Filtre de l'explorateur de fichiers:

        <intent-filter Android:label="@string/app_name">
            <action Android:name="Android.intent.action.VIEW" />
            <category Android:name="Android.intent.category.DEFAULT" />
            <data Android:scheme="file" />
            <data Android:pathPattern=".*\\.mrz" />
        </intent-filter>
  • LOG: intention du fichier détectée: Android.intent.action.VIEW: fichier: ///storage/sdcard0/My%20Documents/new.mrz: null: new.mrz

Filtre HTTP:

        <intent-filter Android:label="@string/rbook_viewer">
            <action Android:name="Android.intent.action.VIEW" />
            <category Android:name="Android.intent.category.DEFAULT" />
            <data Android:scheme="http" />
            <data Android:pathPattern=".*\\.mrz" />
        </intent-filter>

Fonctions privées utilisées ci-dessus:

private String getContentName(ContentResolver resolver, Uri uri){
    Cursor cursor = resolver.query(uri, null, null, null, null);
    cursor.moveToFirst();
    int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
    if (nameIndex >= 0) {
        return cursor.getString(nameIndex);
    } else {
        return null;
    }
}

private void InputStreamToFile(InputStream in, String file) {
    try {
        OutputStream out = new FileOutputStream(new File(file));

        int size = 0;
        byte[] buffer = new byte[1024];

        while ((size = in.read(buffer)) != -1) {
            out.write(buffer, 0, size);
        }

        out.close();
    }
    catch (Exception e) {
        Log.e("MainActivity", "InputStreamToFile exception: " + e.getMessage());
    }
}
17
LFOR

La pathPattern 

<data Android:pathPattern=".*\\.pdf" />

ne fonctionne pas si le chemin du fichier contient un ou plusieurs points avant ".pdf".

Cela fonctionnera:

<data Android:pathPattern=".*\\.pdf" />
<data Android:pathPattern=".*\\..*\\.pdf" />
<data Android:pathPattern=".*\\..*\\..*\\.pdf" />
<data Android:pathPattern=".*\\..*\\..*\\..*\\.pdf" />

Ajoutez plus si vous voulez supporter plus de points.

15

Markus Ressel a raison. Android 7.0 Nougat n'autorise plus le partage de fichiers entre applications utilisant un URI de fichier. Un URI de contenu doit être utilisé. Cependant, un URI de contenu ne permet pas de partager un chemin de fichier, mais uniquement un type mime. Vous ne pouvez donc pas utiliser un URI de contenu pour associer votre application à votre propre extension de fichier.

Drobpox a un comportement intéressant sur Android 7.0. Lorsqu'il rencontre une extension de fichier inconnue, il semble former une intention d'URI de fichier, mais au lieu de lancer l'intention, il appelle le système d'exploitation pour savoir quelles applications peuvent accepter l'intention. Si une seule application peut accepter cet URI de fichier, elle envoie alors un URI de contenu explicite directement à cette application. Donc, pour travailler avec Dropbox, vous n'avez pas besoin de changer les filtres d'intention de votre application. Il ne nécessite pas de filtre d'intention d'URI de contenu. Assurez-vous simplement que l'application peut recevoir un URI de contenu et que votre application avec votre propre extension de fichier fonctionnera avec Dropbox, comme elle le faisait avant Android 7.0.

Voici un exemple de code de chargement de fichier modifié pour accepter un URI de contenu:

Uri uri = getIntent().getData();
if (uri != null) {
    File myFile = null;
    String scheme = uri.getScheme();
    if (scheme.equals("file")) {
        String fileName = uri.getEncodedPath();
        myFile = new File(filename);
    }
    else if (!scheme.equals("content")) {
        //error
        return;
    }
    try {
        InputStream inStream;
        if (myFile != null) inStream = new FileInputStream(myFile);
        else inStream = getContentResolver().openInputStream(uri);
        InputStreamReader rdr = new InputStreamReader(inStream);
        ...
    }
}
3
Peter Newman

J'essaie de faire en sorte que cela fonctionne depuis des lustres et j'ai essentiellement essayé toutes les solutions suggérées et je ne parviens toujours pas à faire reconnaître par Android les extensions de fichiers spécifiques. J'ai un filtre d'intention avec un type de code "*/*" qui est la seule chose qui semble fonctionner et les navigateurs de fichiers listent maintenant mon application en tant qu'option pour ouvrir des fichiers. Bien que j'ai spécifié des extensions de fichier spécifiques en utilisant la balise pathPattern. Cela va tellement loin que même lorsque j'essaye de voir/modifier un contact dans ma liste de contacts, Android me demande si je veux utiliser mon application pour voir le contact, et ce n'est qu'une des nombreuses situations où cela se produit, TRÈS TRÈS ennuyeux.

Finalement, j'ai trouvé ce post de groupe de Google avec une question similaire à laquelle un ingénieur de framework Android a répondu. Elle explique qu'Android ignore tout simplement les extensions de fichiers, mais uniquement les types MIME ( https://groups.google.com/forum/#!topic/Android-developers/a7qsSl3vQq0 ).

Donc, d'après ce que j'ai vu, essayé et lu, Android ne peut tout simplement pas faire la distinction entre les extensions de fichier et la balise pathPattern est fondamentalement une énorme perte de temps et d'énergie. Si vous avez la chance de n'avoir besoin que de fichiers d'un certain type mime (texte, vidéo ou audio, par exemple), vous pouvez utiliser un filtre d'intention avec un type mime. Si vous avez besoin d'une extension de fichier spécifique ou d'un type mime inconnu d'Android, vous n'avez pas de chance.

Si je me trompe, dites-le-moi. Jusqu'à présent, j'ai lu tous les messages et essayé toutes les solutions proposées que je pouvais trouver, mais aucune n'a fonctionné.

Je pourrais écrire une ou deux pages sur la fréquence de ce genre de choses sur Android et sur la foutue expérience des développeurs, mais je vais vous épargner toute ma colère;). J'espère que j'ai sauvé des ennuis à quelqu'un.

3
PeeGee85

Essayez d'ajouter 

<action Android:name="Android.intent.action.VIEW"/>
2
magaio

Ceux qui rencontraient des problèmes avec d'autres applications du Gestionnaire de fichiers\Explorateur, comme @yuku et @ phyrum-tea, répondaient

Cela fonctionne avec l'application de gestion de fichiers par défaut LG

     <intent-filter Android:label="@string/app_name_decrypt">
            <action Android:name="Android.intent.action.VIEW" />
            <category Android:name="Android.intent.category.DEFAULT" />
            <data Android:scheme="file" />
            <data Android:pathPattern=".*\\.lock" />
            <data Android:pathPattern=".*\\..*\\.lock" />
            <data Android:pathPattern=".*\\..*\\..*\\.lock" />
        </intent-filter>

mais ne pouvait pas fonctionner avec ES File Explorer et d'autres gestionnaires de fichiers, j'ai donc ajouté 

 Android:mimeType="*/*"

cela fonctionne alors avec ES Explorer mais le gestionnaire de fichiers LG ne pouvant pas détecter le type de fichier, ma solution est la suivante: 

     <intent-filter Android:label="@string/app_name_decrypt">
            <action Android:name="Android.intent.action.VIEW" />
            <category Android:name="Android.intent.category.DEFAULT" />
            <data Android:scheme="file" />
            <data Android:pathPattern=".*\\.lock" />
            <data Android:pathPattern=".*\\..*\\.lock" />
            <data Android:pathPattern=".*\\..*\\..*\\.lock" />
        </intent-filter>
        <intent-filter Android:label="@string/app_name_decrypt">
            <action Android:name="Android.intent.action.VIEW" />
            <category Android:name="Android.intent.category.DEFAULT" />
            <data Android:scheme="file"/>
            <data Android:scheme="content" />
            <data Android:mimeType="*/*" />
            <data Android:pathPattern=".*\\.lock" />
            <data Android:pathPattern=".*\\..*\\.lock" />
            <data Android:pathPattern=".*\\..*\\..*\\.lock" />
        </intent-filter>
1
Sumit

Pour les pièces jointes Gmail, vous pouvez utiliser:

<intent-filter Android:label="@string/app_name">
  <action Android:name="Android.intent.action.VIEW" />
  <category Android:name="Android.intent.category.DEFAULT" />
  <data Android:scheme="content" />
  <data Android:mimeType="application/pdf" /> <!-- .pdf -->
  <data Android:mimeType="application/msword" /> <!-- .doc / .dot -->
  <data Android:mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document" /> <!-- .docx -->
  <data Android:mimeType="application/vnd.ms-Excel" />  <!-- .xls / .xlt / .xla -->
  <data Android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />  <!-- .xlsx -->
  <data Android:mimeType="application/vnd.ms-PowerPoint" />  <!-- .ppt / .pps / .pot / .ppa -->
  <data Android:mimeType="application/vnd.openxmlformats-officedocument.presentationml.presentation" /> <!-- .pptx -->
  <data Android:mimeType="application/vnd.openxmlformats-officedocument.presentationml.slideshow" /> <!-- .ppsx -->
  <data Android:mimeType="application/Zip" /> <!-- .Zip -->
  <data Android:mimeType="image/jpeg" /> <!-- .jpeg -->
  <data Android:mimeType="image/png" /> <!-- .png -->
  <data Android:mimeType="image/gif" /> <!-- .gif -->
  <data Android:mimeType="text/plain" /> <!-- .txt / .text / .log / .c / .c++ / ... -->

Ajoutez autant de types de mime que nécessaire. Je n'ai besoin que de ceux-ci pour mon projet.

1
Hrk
         <!--
            Works for Files, Drive and DropBox
        -->
        <intent-filter>
            <action Android:name="Android.intent.action.VIEW" />
            <category Android:name="Android.intent.category.DEFAULT" />
            <data Android:scheme="file" />
            <data Android:mimeType="*/*" />
            <data Android:Host="*" />
            <data Android:pathPattern=".*\\.teamz" />
        </intent-filter>

        <!--
            Works for Gmail
        -->
        <intent-filter>
            <action Android:name="Android.intent.action.VIEW"/>
            <category Android:name="Android.intent.category.BROWSABLE" />
            <category Android:name="Android.intent.category.DEFAULT"/>
            <data Android:Host="gmail-ls" Android:scheme="content" Android:mimeType="application/octet-stream"/>
        </intent-filter>

Notez que votre application ouvrira toutes les pièces jointes de gmail, il n'y a aucun moyen de les contourner.

1
Ofir Bitton

Vous essayez ceci cela vous aidera.Au lieu de pdf, vous pouvez utiliser d'autres extensions également . Vous devez d'abord ajouter l'autorisation de stockage externe en lecture dans androidmanifest.xml file.

<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE" />

Ensuite, dans le fichier androidmanifest de la balise Activity, vous ajoutez un filtre d'intention, comme indiqué ci-dessous . 

            <action Android:name="Android.intent.action.SEND" />

            <action Android:name="Android.intent.action.VIEW" />

             <category Android:name="Android.intent.category.DEFAULT" />

            <data Android:mimeType= "application/pdf" />

            <data Android:Host="*" />

        </intent-filter>

Enfin dans votre code, vous obtenez le chemin du fichier pdf comme indiqué ci-dessous:

Intent intent=getIntent();

if(intent!=null) {          

        String action=intent.getAction();

        String type=intent.getType();

        if(Intent.ACTION_VIEW.equals(action) && type.endsWith("pdf")) {

            // Get the file from the intent object

            Uri file_uri=intent.getData();

            if(file_uri!=null)

                filepath=file_uri.getPath();

            else

                filepath="No file";

        }

        else if(Intent.ACTION_SEND.equals(action) && type.endsWith("pdf")){

            Uri uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);

            filepath = uri.getPath();

        }
1
Arjun Othayoth

// J'ai essayé ce code. Et ça marche bien. Vous pouvez utiliser ce code pour accepter le fichier pdf.

<intent-filter>
   <action Android:name="Android.intent.action.SEND" />
   <category Android:name="Android.intent.category.DEFAULT" />
   <data Android:mimeType="application/pdf" />
   <data Android:pathPattern=".*\\.pdf" />
   <data Android:pathPattern=".*\\..*\\.pdf" />
   <data Android:pathPattern=".*\\..*\\..*\\.pdf" />
   <data Android:pathPattern=".*\\..*\\..*\\..*\\.pdf" />
</intent-filter>
0
Arslan Tahir

Content URI ftw, et avec le filtre d'intention dans le manifeste ... si vos fichiers ont une extension personnalisée .xyz, ajoutez un type mime correspondant:

        <intent-filter>
            <action Android:name="Android.intent.action.VIEW" />

            <category Android:name="Android.intent.category.DEFAULT" />

            <data
                Android:Host="*"
                Android:mimeType="application/xyz"
                Android:scheme="content" />
        </intent-filter>

Certaines applications telles que le courrier électronique semblent convertir l'extension en un type mime. Maintenant, je peux cliquer sur la pièce jointe dans un courrier électronique et la faire ouvrir dans mon application.

0
Sofi Software LLC

Lire le fichier d’ouverture dans kotlin:

private fun checkFileOpening(intent: Intent) {
    if (intent.action == Intent.ACTION_VIEW && (intent.scheme == ContentResolver.SCHEME_FILE
                    || intent.scheme == ContentResolver.SCHEME_CONTENT)) {

        val text = intent.data?.let {
            contentResolver.openInputStream(it)?.bufferedReader()?.use(BufferedReader::readText) 
        }
    }
}
0
brucemax