web-dev-qa-db-fra.com

Android filtre d'intention pour une extension de fichier particulière?

Je veux pouvoir télécharger un fichier avec une extension particulière à partir du 'net, et le faire passer à mon application pour y faire face, mais je n'ai pas pu comprendre le filtre d'intention. Le type de fichier n'est pas inclus dans les mimetypes, et j'ai essayé d'utiliser

<data Android:path="*.ext" />

mais je ne pouvais pas faire fonctionner cela.

88
Curyous

Voici comment j'ai défini mon activité dans mon AndroidManifest.xml pour que cela fonctionne.

<activity Android:name="com.keepassdroid.PasswordActivity">
    <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:mimeType="*/*" />
        <data Android:pathPattern=".*\\.kdb" />
        <data Android:Host="*" />
    </intent-filter>
</activity>

Le scheme de file indique que cela doit se produire lorsqu'un fichier local est ouvert (plutôt qu'un protocole comme HTTP).

mimeType peut être défini sur \*/\* pour correspondre à n'importe quel type de mime.

pathPattern est l'endroit où vous spécifiez l'extension à laquelle vous souhaitez faire correspondre (dans cet exemple .kdb). Le .* au début correspond à n'importe quelle séquence de caractères. Ces chaînes nécessitent un double échappement, donc \\\\. correspond à une période littérale. Ensuite, vous terminez avec votre extension de fichier. Une mise en garde avec pathPattern est que .* n'est pas une correspondance gourmande comme on pourrait s'y attendre s'il s'agissait d'une expression régulière. Ce modèle ne correspondra pas aux chemins d'accès qui contiennent un . avant le .kdb. Pour une discussion plus détaillée de ce problème et une solution de contournement, voir ici

Enfin, selon la documentation Android Android, les attributs Host et scheme sont requis pour que l'attribut pathPattern fonctionne, il suffit donc de définir au caractère générique pour correspondre à quoi que ce soit.

Maintenant, si vous sélectionnez un .kdb fichier dans une application comme Linda File Manager, mon application apparaît en option. Je dois noter que cela ne permet pas à lui seul de télécharger ce type de fichier dans un navigateur, car cela ne s'inscrit qu'avec le schéma de fichiers. Le fait d'avoir une application comme Linda File Manager sur votre téléphone résiste de manière générique vous permettant de télécharger n'importe quel type de fichier.

116
Brian Pellin

Il y a beaucoup de désinformation sur ce sujet, notamment de la propre documentation de Google. Le meilleur, et compte tenu de la logique étrange, peut-être la seule vraie documentation est le code source.

implémentation du filtre d'intention a une logique qui défie presque toute description. Le code de l'analyseur est l'autre pièce pertinente du puzzle.

Les filtres suivants se rapprochent assez d'un comportement sensé. Les modèles de chemin s'appliquent, pour les intentions de schéma de "fichier".

La correspondance de modèle de type mime global correspondra à tous les types tant que l'extension de fichier correspond. Ce n'est pas parfait, mais c'est le seul moyen de faire correspondre le comportement des gestionnaires de fichiers comme ES File Explorer, et il est limité aux intentions où l'URI/l'extension de fichier correspond.

Je n'ai pas inclus d'autres schémas comme "http" ici, mais ils fonctionneront probablement très bien sur tous ces filtres.

Le schéma étrange en sortie est le "contenu", pour lequel l'extension n'est pas disponible pour le filtre. Mais tant que le fournisseur indique votre type MIME (par exemple, Gmail transmettra le type MIME pour la pièce jointe sans entrave), le filtre correspondra.

Gotchas à connaître:

  1. Sachez que rien ne se comporte de manière cohérente dans les filtres, c'est un labyrinthe de cas particuliers et traite la violation du principe de la moindre surprise comme un objectif de conception. Aucun des algorithmes de correspondance de modèle ne suit la même syntaxe ou le même comportement. L'absence d'un champ est parfois un caractère générique et parfois non. Les attributs d'un élément de données doivent parfois aller de pair et parfois ignorer le regroupement. Cela aurait vraiment pu être mieux fait.
  2. Le schéma ET l'hôte doivent être spécifiés pour que les règles de chemin correspondent (contrairement au guide API de Google, actuellement).
  3. Au moins ES File Explorer génère des intentions avec un type MIME de "", qui est filtré très différemment de null, est impossible à faire correspondre explicitement et ne peut être mis en correspondance que par le filtre "*/*" risqué.
  4. Le filtre "*/*" ne correspondra PAS aux intentions avec un type MIME nul - ce qui nécessite un filtre distinct pour ce cas spécifique sans aucun type MIME.
  5. Le schéma de "contenu" ne peut être mis en correspondance que par type MIME, car le nom de fichier d'origine n'est pas disponible dans l'intention (au moins avec Gmail).
  6. Le regroupement d'attributs dans des éléments de "données" séparés est (presque) hors de propos pour l'interprétation, à l'exception spécifique de l'hôte et du port - qui s'associent. Tout le reste n'a pas d'association spécifique au sein d'un élément "data" ou entre des éléments "data".

Avec tout cela à l'esprit, voici un exemple avec des commentaires:

<!--
     Capture content by MIME type, which is how Gmail broadcasts
     attachment open requests.  pathPattern and file extensions
     are ignored, so the MIME type *MUST* be explicit, otherwise
     we will match absolutely every file opened.
-->
<intent-filter
    Android:icon="@drawable/icon"
    Android:label="@string/app_name"
    Android:priority="50" >
    <action Android:name="Android.intent.action.VIEW" />

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

    <data Android:scheme="file" />
    <data Android:scheme="content" />
    <data Android:mimeType="application/vnd.my-type" />
</intent-filter>

<!--
     Capture file open requests (pathPattern is honoured) where no
     MIME type is provided in the Intent.  An Intent with a null
     MIME type will never be matched by a filter with a set MIME
     type, so we need a second intent-filter if we wish to also
     match files with this extension and a non-null MIME type
     (even if it is non-null but zero length).
-->
<intent-filter
    Android:icon="@drawable/icon"
    Android:label="@string/app_name"
    Android:priority="50" >
    <action Android:name="Android.intent.action.VIEW" />

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

    <data Android:scheme="file" />
    <data Android:Host="*" />

    <!--
         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:pathPattern=".*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\..*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\..*\\..*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\..*\\..*\\..*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
</intent-filter>

<!--
     Capture file open requests (pathPattern is honoured) where a
     (possibly blank) MIME type is provided in the Intent.  This
     filter may only be necessary for supporting ES File Explorer,
     which has the probably buggy behaviour of using an Intent
     with a MIME type that is set but zero-length.  It's
     impossible to match such a type except by using a global
     wildcard.
-->
<intent-filter
    Android:icon="@drawable/icon"
    Android:label="@string/app_name"
    Android:priority="50" >
    <action Android:name="Android.intent.action.VIEW" />

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

    <data Android:scheme="file" />
    <data Android:Host="*" />
    <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:pathPattern=".*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\..*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\..*\\..*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\..*\\..*\\..*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
    <data Android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
</intent-filter>
26
David Sainty

Je dois admettre que la simple tâche d'ouvrir des pièces jointes à partir d'e-mails et de fichiers du système de fichiers sur Android a été l'une des expériences les plus exaspérantes de tous les temps. Il est facile de gérer trop de fichiers ou trop peu. Mais il est difficile de le faire correctement.La plupart des solutions publiées sur stackoverflow ne fonctionnaient pas correctement pour moi.

Mes exigences étaient:

  • que mon application gère les pièces jointes partagées par mon application
  • avoir mon application gérer des fichiers sur le stockage de fichiers qui ont été générés par mon application et avoir une extension particulière

La meilleure façon de procéder consiste probablement à spécifier un type MIME personnalisé pour vos pièces jointes. Et vous choisirez probablement également d'avoir une extension de fichier personnalisée. Supposons donc que notre application s'appelle "Cool App" et que nous générons des pièces jointes contenant ".cool" à la fin.

C'est le plus près de mon objectif et ça marche ... satisfaisant.

<!-- Register to handle email attachments -->
<!-- WARNING: Do NOT use Android:Host="*" for these as they will not work properly -->
<intent-filter>
    <!-- needed for properly formatted email messages -->
    <data
        Android:scheme="content"
        Android:mimeType="application/vnd.coolapp"
        Android:pathPattern=".*\\.cool" />
    <!-- needed for mangled email messages -->
    <data
        Android:scheme="content"
        Android:mimeType="application/coolapp"
        Android:pathPattern=".*\\.cool" />
    <!-- needed for mangled email messages -->
    <data
        Android:scheme="content"
        Android:mimeType="application/octet-stream"
        Android:pathPattern=".*\\.cool" />

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

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

<!-- Register to handle file opening -->
<intent-filter>
    <data Android:scheme="file"
          Android:mimeType="*/*"
          Android:pathPattern=".*\\.cool"
          Android:Host="*"/>

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

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

Remarques:

  • Le pathPattern semble être plus ou moins ignoré pour les pièces jointes (lors de l'utilisation de Android:scheme="content"). Si quelqu'un obtient le pathPattern pour répondre uniquement à certains modèles, je serais ravi de voir comment.
  • L'application Gmail a refusé de répertorier mon application dans le sélecteur si j'ai ajouté le Android:Host="*" attribut.
  • Cela fonctionne probablement encore si ces intent-filter les blocs sont fusionnés mais je n'ai pas vérifié cela.
  • Pour gérer les demandes d'un navigateur lors du téléchargement d'un fichier, le Android:scheme="http" peut être utilisé. Notez que certains navigateurs peuvent gâcher le Android:mimeType alors expérimentez avec Android:mimeType="*/*" et vérifiez dans le débogueur ce qui est réellement passé, puis resserrez le filtrage pour ne pas devenir cette application ennuyeuse qui gère tout.
  • Certains explorateurs de fichiers gâcheront également les types MIME pour vos fichiers. Ce qui précède intent-filter a été testé avec l'application "My Files" de Samsung sur un Galaxy S3. L'Explorateur FX refuse toujours d'ouvrir correctement le fichier et j'ai également remarqué que l'icône de l'application n'est pas utilisée pour les fichiers. Encore une fois, si quelqu'un réussit, veuillez commenter ci-dessous.

J'espère que vous trouverez cela utile et que vous n'aurez pas à perdre des jours à parcourir toutes les combinaisons possibles. Il y a place à amélioration, les commentaires sont donc les bienvenus.

23
omahena

La réponse de Brian ci-dessus m'a permis d'atteindre 90% du chemin. Pour finir, pour le type mime j'ai utilisé

Android:mimeType="*/*"

Je soupçonne que les affiches précédentes ont tenté de publier le même détail, mais le fait de ne pas citer l'étoile barre oblique comme code, stackoverflow la considère comme une barre oblique.

10
Dawson

Plutôt que Android:path, essayez Android:mimeType , avec une valeur du type MIME de ce contenu particulier. Également, Android:path n'accepte pas les caractères génériques - utilisez Android:pathPattern pour cela.

8
CommonsWare

J'ai essayé de faire fonctionner cela pendant des siècles et j'ai essayé fondamentalement toutes les solutions suggérées et je ne parviens toujours pas à obtenir Android pour reconnaître des extensions de fichier spécifiques. J'ai un filtre d'intention avec un "*/*" mimetype qui est la seule chose qui semble fonctionner et les navigateurs de fichiers répertorient maintenant mon application comme une option pour ouvrir des fichiers, mais mon application est maintenant affichée comme une option pour ouvrir N'IMPORTE QUEL TYPE de fichier même si j'ai spécifié un fichier spécifique extensions utilisant la balise pathPattern. Cela va si loin que même lorsque j'essaie d'afficher/modifier un contact dans ma liste de contacts Android me demande si je veux utiliser mon application pour afficher le contact, et ce n'est que l'un des nombreux situations où cela se produit, TRÈS TRÈS ennuyeux.

Finalement, j'ai trouvé ce message sur les groupes Google avec une question similaire à laquelle un véritable ingénieur cadre Android a répondu. Elle explique que Android ne sait tout simplement rien sur le fichier- extensions, uniquement les types MIME ( https://groups.google.com/forum/#!topic/Android-developers/a7qsSl3vQq ).

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 gigantesque perte de temps et d'énergie. Si vous avez la chance de ne besoin de fichiers d'un certain type MIME (par exemple, texte, vidéo ou audio), 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 de Android cependant, vous n'avez pas de chance.

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

Je pourrais écrire une autre page ou deux sur la façon dont ce genre de choses semble être courant dans Android et à quel point l'expérience du développeur est foirée, mais je vais vous éviter mes déclamations en colère;). J'espère que j'ai sauvé quelqu'un des ennuis.

5
PeeGee85

Aucun des éléments ci-dessus ne fonctionne correctement, pour les actions VIEW ou SEND, si le suffixe n'est pas enregistré avec un type MIME dans la base de données MIME system = wide d'Android. Les seuls paramètres que j'ai trouvés qui déclenchent le suffixe spécifié sont Android:mimeType="*/*", mais l'action se déclenche pour TOUS les fichiers. Clairement PAS ce que vous voulez!

Je ne peux pas trouver de solution appropriée sans ajouter le mime et le suffixe à la base de données Android mime, jusqu'à présent, je n'ai pas trouvé de moyen de le faire. Si quelqu'un sait, un pointeur serait être formidable.

3
Glenn Widener

Le Android 4 les règles sont devenues plus strictes qu'elles ne l'étaient auparavant. Utilisation:

    <data
      Android:Host=""
      Android:mimeType="*/*"
      Android:pathPattern=".*\\.ext"
      Android:scheme="file"
    ></data>
3
Martin

J'ai eu beaucoup de mal avec cela pour une extension de fichier personnalisée, moi-même. Après beaucoup de recherches, j'ai trouvé cette page Web où l'affiche a découvert que la classe patternMatcher d'Android (qui est utilisée pour la correspondance pathPattern dans Intent-Filters) a un comportement inattendu lorsque votre chemin contient le premier caractère de votre modèle de correspondance ailleurs dans le chemin (comme si vous essayez de faire correspondre "* .xyz", la classe patternMatcher s'arrête s'il y a un "x" plus tôt dans votre chemin). Voici ce qu'il a trouvé pour une solution de contournement et a fonctionné pour moi, bien que ce soit un peu un hack:

PatternMatcher est utilisé pour pathPattern à IntentFilter Mais, l'algorithme de PatternMatcher est assez étrange pour moi. Voici l'algorithme de Android PatternMatcher.

S'il y a 'caractère suivant' du motif '. *' Au milieu de la chaîne, PatternMatcher arrête la boucle à ce point. (Voir PatternMatcher.Java de Android framework.)

Ex. chaîne: "ceci est un modèle de ma pièce jointe": ". att. ". Android PatternMatcher entrez la boucle pour correspondre au motif '. ' jusqu'à rencontrer le caractère suivant du motif (dans cet exemple, 'a') Donc, '. 'la boucle de correspondance s'arrête à l'index 8 -' un 'entre' est 'et' mon '. Par conséquent, le résultat de cette correspondance renvoie' faux '.

Assez étrange, n'est-ce pas. Pour contourner ce problème - en fait, réduire les possibilités - le développeur devrait utiliser un pathPattern stupide et ennuyeux.

Ex. Objectif: faire correspondre le chemin uri qui inclut le "message".

<intent-filter>
...
<data Android:pathPattern=".*message.*" />
<data Android:pathPattern=".*m.*message.*" />
<data Android:pathPattern=".*m.*m.*message.*" />
<data Android:pathPattern=".*m.*m.*m.*message.*" />
<data Android:pathPattern=".*m.*m.*m.*m.*message.*" />
...
</intent-filter>

Ceci est spécialement émis lors de la correspondance avec une extension de fichier personnalisée.

3
benjamin davis

La réponse de Brian est très proche, mais voici un moyen propre et sans erreur d'invoquer votre application lorsque vous essayez d'ouvrir un fichier avec votre propre extension personnalisée (pas besoin de schéma ou d'hôte):

<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:mimeType="*/*" />
    <data Android:pathPattern="*.*\\.kdb" />
</intent-filter>
3
Kon

Quand une intention rencontre un intent-filter, Voici les intent-filter exigences: (imaginez une liste de contrôle).

  • Toute correspondance <action>
  • Toute correspondance <category>
  • Toute correspondance <data mimeType> (solution facile: "/")
  • En option:

    • Toute correspondance <data scheme> (solution facile: <data Android:scheme="file" /> <data Android:scheme="content" />)

    • Toute correspondance <data Host> (solution facile: "*")

    • Toute correspondance <data pathPattern/etc.> (par exemple .*\\.0cc)

Définition de plusieurs <data $type=""> elements vérifie la case $ type ssi tout <data $type=> correspond au Intent.

Omettre mimeType rompt votre intent-filter, même si cela semble redondant. Omettre <data scheme/Host/pathPattern> fait correspondre votre filtre à tout.

https://f-droid.org/en/packages/de.k3b.Android.intentintercept/ est une application conçue pour recevoir tout intentions, et vous permet d'inspecter l'intention. J'ai appris que les extensions de fichiers non reconnues ouvertes via Simple File Manager sont fournies avec le type MIME application/octet-stream.

https://stackoverflow.com/a/4621284/2683842 signale que <data pathPattern=>.*xyz s'interrompt au premier x qu'il voit et échouera immédiatement s'il n'est pas suivi de yz. Alors /sdcard/.hidden/foo.0cc ne passera pas .*\\.0cc sauf si vous essayez .*\\..*\\.0cc au lieu.

  • Je n'ai pas vérifié si cette solution de contournement est nécessaire.

Résultat final:

<activity Android:name=".Ft2NsfActivity">

    <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:Host="*" />
        <data Android:pathPattern=".*\\.ftm"/>
        <data Android:pathPattern=".*\\..*\\.ftm"/>
        <data Android:pathPattern=".*\\..*\\..*\\.ftm"/>
        <data Android:pathPattern=".*\\..*\\..*\\..*\\.ftm"/>
        <data Android:pathPattern=".*\\.0cc"/>
        <data Android:pathPattern=".*\\..*\\.0cc"/>
        <data Android:pathPattern=".*\\..*\\..*\\.0cc"/>
        <data Android:pathPattern=".*\\..*\\..*\\..*\\.0cc"/>
        <data Android:mimeType="*/*" />
    </intent-filter>

</activity>
2
jimbo1qaz

Utiliser le filtre comme ci-dessous pour ouvrir à partir du navigateur, de Gmail et du navigateur de fichiers (testé). REMARQUE: veuillez ne pas fusionner deux filtres, le navigateur ignorera votre application (testé).

        <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" Android:pathPattern=".*\\.ext" Android:mimeType="application/*"/>
            <data Android:scheme="content" Android:pathPattern=".*\\.ext" Android:mimeType="application/*"/>
        </intent-filter>

        <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="http"
                  Android:Host="*"
                  Android:pathPattern=".*\\.ext" />
            <data Android:scheme="https"
                  Android:Host="*"
                  Android:pathPattern=".*\\.ext" />
            <data Android:scheme="ftp"
                  Android:Host="*"
                  Android:pathPattern=".*\\.ext" />

        </intent-filter>
1
Tran Hieu

Si vous souhaitez que les fichiers soient ouverts directement depuis Gmail, dropbox ou l'un des outils de création de fichiers Android, utilisez le code suivant (supprimez 'Android: Host = "*"' qui a rendu le fichier inaccessible pour 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:scheme="content" Android:pathPattern=".*\\.kdb" 
          Android:mimeType="application/octet-stream"/>


</intent-filter>

<intent-filter>
    <action Android:name="Android.intent.action.VIEW"/>
    <category Android:name="Android.intent.category.DEFAULT"/>
    <data Android:scheme="file" Android:mimeType="*/*"     
          Android:pathPattern=".*\\.kdb"/>
</intent-filter>

Le filtre de données doit être écrit en une seule instruction selon Android version 4.x

1
AndreasReiff