Filtro intent Android: associa l'app all'estensione del file


92

Ho un tipo / estensione di file personalizzato a cui voglio associare la mia app.

Per quanto ne so, l'elemento data è fatto per questo scopo, ma non riesco a farlo funzionare. http://developer.android.com/guide/topics/manifest/data-element.html Secondo i documenti e molti post del forum, dovrebbe funzionare in questo modo:

<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>

Ebbene, non funziona. Cos'ho fatto di sbagliato? Voglio semplicemente dichiarare il mio tipo di file.


Aggiungi android: label = "@ string / app_name" nel filtro intent
Prashant

Risposte:


116

Hai bisogno di più filtri di intenti per affrontare le diverse situazioni che desideri gestire.

Esempio 1, gestisci le richieste http senza mimetype:

  <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>

Gestisci con mimetypes, dove il suffisso è irrilevante:

  <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>

Gestisci l'intento da un'app browser di file:

  <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>

Pensavo che host = "*" potesse essere omesso, ma ha iniziato a essere troppo ampio.
Tè Phyrum

@Phyrum Tea: esiste un modo per associare un'estensione di file ad ALTRE app utilizzando la mia app? La mia app genera file .list che voglio dire ad Android che sono file di testo e per aprirli con un editor di testo (la mia app NON è un editor di testo).
Luis A. Florit

@ LuisA.Florit hai provato a chiamare i tuoi file * .txt? Potrebbe funzionare.
Gavriel

l'estensione personalizzata non funziona senza MimeType. Se fornisco il tipo MIME come / la mia app viene visualizzata quando faccio clic anche sulla notifica di Gmail.
madan V

1
Non funziona .. Sono stufo ora che la ricerca degli ultimi 2 giorni non ha funzionato. Sto usando Android 9
Ahmad Arslan

48

Le altre soluzioni non funzionavano in modo affidabile fino a quando non ho aggiunto:

android:mimeType="*/*" 

Prima funzionava in alcune applicazioni, in altre no ...

soluzione completa per me:

<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>

1
Wow! questo mi ha aiutato. Sembra che la documentazione di Android non sia corretta. Tutti (schema, host, percorso [Pattern], mimeType) devono essere dichiarati per funzionare
Gavriel

Dovresti avere sia una regola con il mimeTypeche una senza se vuoi essere completo. Vedi developer.android.com/guide/components/…
Andrew Sun

1
E se volessi aprire la tua app per i file da Gmail?
IgorGanapolsky

30

Le risposte fornite da Phyrum Tea e yuku sono già molto istruttive.

Voglio aggiungere che a partire da Android 7.0 Nougat c'è una modifica al modo in cui viene gestita la condivisione di file tra le app:

Dalle modifiche ufficiali di Android 7.0 :

Per le app destinate ad Android 7.0, il framework Android applica il criterio API StrictMode che vieta di esporre file: // URI all'esterno della tua app. Se un intento contenente un URI di file lascia l'app, l'app ha esito negativo con un'eccezione FileUriExposedException.

Per condividere file tra le applicazioni, è necessario inviare un content: // URI e concedere un'autorizzazione di accesso temporanea sull'URI. Il modo più semplice per concedere questa autorizzazione è utilizzare la classe FileProvider. Per ulteriori informazioni sulle autorizzazioni e sulla condivisione di file, vedere Condivisione di file.

Se hai il tuo file personalizzato che termina senza uno specifico mime-type(o immagino anche con uno) potresti dover aggiungere un secondo schemevalore al tuo intent-filterper farlo funzionare conFileProviders anche .

Esempio:

<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 cosa importante qui è l'aggiunta di

<data android:scheme="content" />

al filtro.

Ho avuto difficoltà a scoprire questo piccolo cambiamento che ha impedito l'apertura della mia attività sui dispositivi Android 7.0 mentre tutto andava bene sulle versioni precedenti. Spero che aiuti qualcuno.


Senza questo suggerimento e informazioni aggiuntive non sono riuscito a registrare la mia app con Esplora file. Questo è cruciale e dovrebbe essere migliorato
u2tall

Hai salvato un giorno! Big tnx. Questa dovrebbe essere una risposta accettata!
Andris

⚠️ Se provi a ottenere il file fornito tramite lo schema dei contenuti, ad esempio File(uri.path), si bloccherà a causa di No such file or directory- dovrà gestire lo scenario in modo diverso durante l'aggiornamento per supportare Nougat +!
Jordan H

19

I miei risultati:

Sono necessari diversi filtri per gestire i diversi modi di recuperare un file. cioè, tramite allegato gmail, tramite esploratore di file, HTTP, FTP ... Tutti inviano intenti molto diversi.

E devi filtrare l'intento che attiva la tua attività nel tuo codice attività.

Per l'esempio seguente, ho creato un falso tipo di file new.mrz. E l'ho recuperato dall'allegato di Gmail e dal file explorer.

Codice attività aggiunto in 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!
            }
        }

Filtro allegati 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: Intento di contenuto rilevato: android.intent.action.VIEW: content: //gmail-ls/l.foul@gmail.com/messages/2950/attachments/0.1/BEST/false: application / octet-stream: new. mrz

Filtro Esplora file:

        <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: Intento file rilevato: android.intent.action.VIEW: file: ///storage/sdcard0/My%20Documents/new.mrz: null: new.mrz

Filtro 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>

Funzioni private usate sopra:

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());
    }
}

L'attività deve essere lanciatore o tipo principale? Perché ho provato il filtro degli allegati gmail e non funziona.
Amit

15

Il pathPattern

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

non funziona se il percorso del file contiene uno o più punti prima di ".pdf".

Questo funzionerà:

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

Aggiungine altri se desideri supportare più punti.


1
A partire da ora questa è l'unica opzione per
accedere

Sto usando lo stesso codice, ma IDE darmi un avvertimento, vedere questa domanda stackoverflow.com/questions/35833776/...
AndreaF

5

Ho provato a farlo funzionare per anni e ho provato praticamente tutte le soluzioni suggerite e ancora non riesco a far riconoscere ad Android estensioni di file specifiche. Ho un filtro di intenti con un tipo "*/*"MIME che è l'unica cosa che sembra funzionare e i browser di file ora elencano la mia app come un'opzione per l'apertura dei file, tuttavia la mia app viene ora mostrata come un'opzione per aprire QUALSIASI TIPO di file anche se Ho specificato estensioni di file specifiche utilizzando il tag pathPattern. Questo va così lontano che anche quando provo a visualizzare / modificare un contatto nel mio elenco di contatti, Android mi chiede se voglio usare la mia app per visualizzare il contatto, e questa è solo una delle tante situazioni in cui ciò si verifica, MOLTO MOLTO fastidioso.

Alla fine ho trovato questo post di Google Gruppi con una domanda simile a cui ha risposto un vero ingegnere del framework Android. Spiega che Android semplicemente non sa nulla delle estensioni di file, solo dei tipi MIME ( https://groups.google.com/forum/#!topic/android-developers/a7qsSl3vQq0 ).

Quindi da quello che ho visto, provato e letto, Android semplicemente non riesce a distinguere tra estensioni di file e il tag pathPattern è fondamentalmente una gigantesca perdita di tempo ed energia. Se sei abbastanza fortunato da aver bisogno solo di file di un certo tipo MIME (diciamo testo, video o audio), puoi usare un filtro di intenti con un tipo MIME. Se hai bisogno di un'estensione di file specifica o di un tipo MIME non conosciuto da Android, allora sei sfortunato.

Se mi sbaglio su qualcosa per favore dimmelo, finora ho letto tutti i post e ho provato tutte le soluzioni proposte che sono riuscito a trovare ma nessuna ha funzionato.

Potrei scrivere un'altra o due pagine su quanto sembrano essere comuni questo tipo di cose in Android e su quanto sia incasinata l'esperienza degli sviluppatori, ma ti risparmierò le mie rabbiose invettive;). Spero di aver salvato qualcuno dei guai.


5

Markus Ressel ha ragione. Android 7.0 Nougat non consente più la condivisione di file tra app utilizzando un URI di file. È necessario utilizzare un URI di contenuto. Tuttavia, un URI di contenuto non consente la condivisione di un percorso di file, solo un tipo MIME. Quindi non puoi utilizzare un URI di contenuto per associare la tua app alla tua estensione di file.

Drobpox ha un comportamento interessante su Android 7.0. Quando incontra un'estensione di file sconosciuta, sembra che formi un intento URI di file ma invece di avviare l'intento chiama il sistema operativo per scoprire quali app possono accettare l'intento. Se è presente una sola app in grado di accettare l'URI del file, invia un URI del contenuto esplicito direttamente a tale app. Quindi, per lavorare con Dropbox non è necessario modificare i filtri di intenti sulla tua app. Non richiede un filtro intento URI di contenuto. Assicurati solo che l'app possa ricevere un URI di contenuto e che la tua app con la tua estensione di file funzioni con Dropbox proprio come faceva prima di Android 7.0.

Ecco un esempio del mio codice di caricamento del file modificato per accettare un URI di contenuto:

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);
        ...
    }
}

1

Prova ad aggiungere

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

Ora che funziona con i tipi di file impostati in fabbrica come application / pdf, come posso dichiarare il mio tipo di file? E quando dico tipo di file, intendo mimeType;)
Tamas

Che tipo di file vuoi che questo tipo MIME catturi? Inoltre, questo file viene aperto dal browser o dal file manager o inviato da un'altra applicazione che hai creato?
magaio

potrebbe provenire dal browser, dal client di posta, dal file manager o da qualsiasi altra posizione .. O la mia app ofc :) l'estensione del file è personalizzata, specificata dal client.
Tamas,

Beh, sono ancora bloccato ... qualcuno potrebbe aiutarmi per favore?
Tamas

@Tamas avete capito tutto questo. Sono bloccato su questo!
StuStirling

1

Per l'allegato di Gmail, puoi utilizzare:

<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++ / ... -->

Aggiungi tutti i tipi MIME necessari. Mi servono solo per il mio progetto.


1
         <!--
            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>

Tieni presente che in questo modo la tua app aprirà tutti i file allegati di Gmail, non c'è modo di aggirarlo


Questo gestirà ogni singolo file inviato all'utente, non solo .teamz
IgorGanapolsky

1

Coloro che hanno problemi con altre app File Manager \ Explorer, come hanno risposto @yuku e @ phyrum-tea

Funziona con l'app di gestione file predefinita di 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>

ma non poteva funzionare con ES File Explorer e altri file manager, quindi ho aggiunto

 android:mimeType="*/*"

quindi funziona con ES Explorer ma il file manager LG non è stato in grado di rilevare il tipo di file, quindi la mia soluzione è

     <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

Content URI ftw e con il filtro intent nel manifest ... se i tuoi file hanno un'estensione personalizzata .xyz, aggiungi un tipo MIME corrispondente:

        <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>

Alcune app come la posta elettronica sembrano convertire l'estensione in un tipo MIME. Ora posso fare clic sull'allegato nell'email e aprirlo nella mia app.


1

Prova questo ti aiuterà. Invece di pdf puoi usare anche altre estensioni. Per prima cosa devi aggiungere il permesso di lettura dell'archiviazione esterna nel file androidmanifest.xml .

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

Quindi nel file androidmanifest nel tag attività, aggiungi un filtro di intenti come mostrato di seguito.

            <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>

Infine nel codice, ottieni il percorso del file pdf come mostrato di seguito:

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();

        }

Per questo Intent.ACTION_VIEW, l'impossibilità di acquisire il percorso del file causa l'errore: java.io.FileNotFoundException: autorizzazione negata. È solo da alcune applicazioni specifiche, non tutte le volte. qualche soluzione?
Hardik Joshi

@HardikJoshi Le app potrebbero non impostare l'autorizzazione GRANT_READ_URI_PERMISSION.
Mira_Cole

1

Leggi il file di apertura in 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) 
        }
    }
}

-1

Inserisci questo filtro di intenzione all'interno del tag attività nel file manifest che desideri aprire toccando il file:

<intent-filter android:priority="999">
    <action android:name="android.intent.action.VIEW" />

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

    <data android:host="*" />
    <data android:mimeType="application/octet-stream" />
    <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\.yourextension" />
    <data android:scheme="content" />
</intent-filter>

-1

// Ho provato questo codice. E funziona bene. È possibile utilizzare questo codice per accettare il file 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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.