2

I have 2 custom file extensions *.abc and *.xyz.

I have written Intent filters for activities where file having *.abc extension should be opened in Activity1 and *.xyz should be open in Activity2

Those files having serialized java objects and not plain text files.

These are intent filters for those activities in AndroidManifest.xml

<activity
    android:name=".ui.Activity1">
    <intent-filter tools:ignore="AppLinkUrlError">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />

        <data android:mimeType="*/*" />
        <data android:pathPattern=".*\\.abc" />
    </intent-filter>
</activity>
<activity
    android:name=".ui.Activity2">
    <intent-filter tools:ignore="AppLinkUrlError">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />

        <data android:mimeType="*/*" />
        <data android:pathPattern=".*\\.xyz" />
    </intent-filter>
</activity>

I am able to register those extension in android system and my app activities are showing in app chooser of Android System UI

I am printing URI info in logcat as follows

Timber.i(uri.toString())
val mimeType = contentResolver.getType(uri)
Timber.i(mimeType)

Logcat when file opened from WhatsApp

content://com.whatsapp.provider.media/item/5237
application/octet-stream

Logcat when file opened from Solid Explorer

content://pl.solidexplorer2.files/storage/emulated/0/Download/File1.abc
application/*

But when tap either of file (custom extension) it shows both Activities as openable Intent, for eg. If click on file file1.abc it shows both Intent Filters of extensions as Activity1 and Activity2

I want to make those extensions Activity Specific like if click on file with extension *.abc it should only have Activity1 in Intent Chooser of Android System UI

I have tried few methods explained in other answers but those doesn't seems to work properly for both file format with content type URI

Some solutions only working when file opened from File Browsers like Solid Explorer but doesn't work when open from apps like WhatsApp which uses MIME type as media

Intent filter below properly worked with Solid Explorer. It is filtering activity based on custom file extension. But same Intent Filter won't work for Android System's Chooser dialog as it doesn't work in apps like WhatsApp or Gmail which uses Android System's Intent Chooser

<activity
    android:name=".ui.Activity1">
    <intent-filter>
        <data
            android:host="*"
            android:mimeType="*/*"
            android:pathPattern=".*\\.abc"
            android:scheme="content" />

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

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
    </intent-filter>
</activity>
<activity
    android:name=".ui.Activity2">
    <intent-filter>
        <data
            android:host="*"
            android:mimeType="*/*"
            android:pathPattern=".*\\.xyz"
            android:scheme="content" />

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

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

Any help will be appreciated. Thanks.

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
adityakamble49
  • 1,971
  • 18
  • 27
  • Filtering on file extensions will be increasingly useless. There is no requirement for a `Uri` to have a file extension, let alone one that is meaningful. Stop relying on file extensions. – CommonsWare Jan 21 '18 at 14:36
  • @CommonsWare so What do you suggest to do? How can I register those Activities for specific file type? – adityakamble49 Jan 21 '18 at 14:38
  • "How can I register those Activities for specific file type?" -- you don't. You cannot force other developers, let alone users, to use your custom file extensions, or even custom MIME types. IMHO, your decision to use Java serialization was **awful**. Had you used XML or JSON, you at least could have relied on some conventional MIME types. As it stands, whether you go with push (``) or pull (`ACTION_OPEN_DOCUMENT`, `ACTION_GET_CONTENT`), you will have to settle for requesting any type of file, in order to get any results. – CommonsWare Jan 21 '18 at 14:42
  • @CommonsWare Actually I'm developing text encryption application so can't directly use XML or JSON like normal plain text data holders. Yeah I need to settle for requesting any type of file. Isn't there another way to achieve this without depending on user to choose between them every time – adityakamble49 Jan 21 '18 at 14:47
  • Personally, in this situation, I would use pull: `ACTION_OPEN_DOCUMENT`, plus `ACTION_GET_CONTENT` if your `minSdkVersion` is below 19. These correspond to the "file-open" dialogs that you see in other GUI toolkits. That at least constrains the cases to where the user is in your app and is specifically wanting to load content into it. Using an accept-everything `` means that you keep showing up in places where the user may not want you, and it increases the chances that the user will accidentally choose your app. – CommonsWare Jan 21 '18 at 14:50
  • @CommonsWare Yeah I have already implemented that Pull based approach using Storage Access Framework https://developer.android.com/guide/topics/providers/document-provider.html . But to make it more user friendly I wanted to register those activities for Intent Filtering – adityakamble49 Jan 21 '18 at 14:57
  • IMHO, that will be more user-hostile, given that filtering on custom file extensions and custom MIME types is impractical. – CommonsWare Jan 21 '18 at 15:38
  • @CommonsWare Okay. Need to stick to pull based approach. Thanks for help – adityakamble49 Jan 21 '18 at 15:39

1 Answers1

2

There is no direct solution to this issue by using Intent Filters per Activity, So I have created FileIntentActivity which accepts following Intent Filter

<intent-filter
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    tools:ignore="AppLinkUrlError">
    <action android:name="android.intent.action.VIEW" />
    <action android:name="android.intent.action.EDIT" />

    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="*/*" />
</intent-filter>

This FileIntentActivity is used to accept any file format and then decides further action based on file extension converted from Content URI using Cursors referred from this answer

fun getFileName(uri: Uri): String {
    lateinit var cursor: Cursor
    lateinit var result: String
    try {
        cursor = appContext.contentResolver.query(uri, null, null, null, null)
        if(cursor!=null && cursor.moveToFirst()){
            result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
        }
        return result
    } finally {
        cursor.close()
    }
}

private fun getFileType(fileName: String): FileType {
    return when (fileExtension) {
        FileFormats.ABC_FILE_FORMAT -> FileType.ABC
        FileFormats.XYZ_FILE_FORMAT -> FileType.XYZ
        else -> FileType.UNKNOWN
    }
}

And open the Activity1 or Activity2 based on FileType

private fun openActivityByType(fileType: FileType) {
    when (fileType) {
        FileType.ABC -> startActivity1()
        FileType.XYZ -> startActivity2()
        FileType.UNKNOWN -> showFileUnsupportedMessage()
    }
}

This approach at least makes User Interaction less confusing by showing only 1 File Intent Activity instead of Multiple Activities of Single app in Android's Intent Chooser.

adityakamble49
  • 1,971
  • 18
  • 27