Android intent filter: associate app with file extension

后端 未结 16 1705
北海茫月
北海茫月 2020-11-22 11:06

I have a custom file type/extension that I want to associate my app with.

As far as I know, the data element is made for this purpose, but I can\'t get it working. h

相关标签:
16条回答
  • 2020-11-22 11:52

    You need multiple intent filters to address different situation you want to handle.

    Example 1, handle http requests without mimetypes:

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

    Handle with mimetypes, where the suffix is irrelevant:

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

    Handle intent from a file browser app:

      <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>
    
    0 讨论(0)
  • 2020-11-22 11:52

    The pathPattern

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

    does not work if the file path contains one or more dots before ".pdf".

    This will work:

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

    Add more if you want to support more dots.

    0 讨论(0)
  • 2020-11-22 11:54

    Read opening file 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) 
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 11:56

    My findings:

    You need several filters to deal with the different ways of retrieving a file. ie, by gmail attachment, by file explorer, by HTTP, by FTP... They all send very different intents.

    And you need to filter out the intent that trigger your activity in your activity code.

    For the example below, I created a fake file type new.mrz. And I retrieved it from gmail attachment and file explorer.

    Activity code added in the 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!
                }
            }
    

    Gmail attachement filter:

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

    File explorer filter:

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

    HTTP filter:

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

    Private functions used above:

    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());
        }
    }
    
    0 讨论(0)
提交回复
热议问题