Blackmoon File Browser Intents & Providers

Blackmoon File Browser publishes Intents and Providers for other apps to use so that they don’t need to implement a file browser themselves.  Android app developers might be interested in how to use these Intents for their own apps and would like some documentation on how to do so.  Please note that this code pertains to Blackmoon File Browser and is not considered generic code that is guaranteed to work with any other file browser app. I have gone out of my way to make use of standard Android actions and also handle many of the Open Intents actions as well.  Comments or questions can be left on this page, on my blog post or via email.

Intent & Provider List since version 5.2

  1. Pick File
  2. Pick Multiple Files
  3. Pick Folder
  4. Save To New File
  5. Save To New Folder
  6. Get Image Content since version 5.7
  7. Playlist Creator
  8. Create Zip File
  9. Unpack Zip File
  10. MIME Type Provider
  11. Browse File since version 6.6

Pick File

The Pick File intent uses the existing File Browser app UI to allow the user to pick an existing file.  The advantage of this approach is that the user is already quite familiar with the browser app and locating the file they wish to pick should be quick and easy.  All the normal file browser actions are still readily available so that complex interactions like creating a new folder and copying in a bunch of files and then picking one of those new files is entirely within the realm of possibility.

void pickFile(File aFile) {
    Intent theIntent = new Intent(Intent.ACTION_PICK);
    theIntent.setData(Uri.fromFile(aFile));  //default file / jump directly to this file/folder
    theIntent.putExtra(Intent.EXTRA_TITLE,"A Custom Title"); //optional
    theIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); //optional
    try {
        startActivityForResult(theIntent,PICK_FILE_RESULT_CODE);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    switch (requestCode) {
        case PICK_FILE_RESULT_CODE: {
            if (resultCode==RESULT_OK && data!=null && data.getData()!=null) {
                String theFilePath = data.getData().getPath();
                ...
            }
            break;
        }
    }
}

NOTES:

  • Intent.ACTION_PICK can be replaced with org.openintents.action.PICK_FILE.
  • Uri.fromFile(aFile) can be replaced with Uri.parse(“file://”+aFile.getPath())
  • PICK_FILE_RESULT_CODE is user defined and can be any int >= Activity.RESULT_FIRST_USER
  • The Intent created in pickFile() is returned in onActivityResult so that other information required for onActivityResult to perform it’s task is returned intact to the calling application.

Pick Multiple Files

While similar to the Pick File intent, the resulting data returned from a Pick Multiple Files intent requires special handling.

void pickFiles(File aFolder) {
    Intent theIntent = new Intent(Intent.ACTION_PICK);
    theIntent.setData(Uri.fromFile(aFolder));  //jump directly to this folder
    theIntent.putExtra("com.blackmoonit.extra.ALLOW_MULTIPLE",true); //required
    theIntent.putExtra(Intent.EXTRA_TITLE,"A Custom Title"); //optional
    theIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); //optional
    try {
        startActivityForResult(theIntent,PICK_FILES_RESULT_CODE);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    switch (requestCode) {
        case PICK_FILES_RESULT_CODE: {
            if (resultCode==RESULT_OK && data!=null && data.getExtras()!=null) {
                ArrayList<Uri> theFileUriList = data.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
                ...
            }
            break;
        }
    }
}

NOTES:

  • Intent.ACTION_PICK can be replaced with org.openintents.action.PICK_FILE or Intent.GET_CONTENT
  • Uri.fromFile(aFolder) can be replaced with Uri.parse(“file://”+aFolder.getPath())
  • PICK_FILES_RESULT_CODE is user defined and can be any int >= Activity.RESULT_FIRST_USER
  • The Intent created in pickFiles() is returned in onActivityResult so that other information required for onActivityResult to perform it’s task is returned intact to the calling application.

Pick Folder

Very similar to the Pick File intent, except that you are limited to picking a folder instead of a file. The Scheme encoded in the Uri must be either “folder” or “directory”.

void pickFolder(File aFolder) {
    Intent theIntent = new Intent(Intent.ACTION_PICK);
    theIntent.setData(Uri.parse("folder://"+aFolder.getPath()));  //default folder / jump directly to this folder
    theIntent.putExtra(Intent.EXTRA_TITLE,"A Custom Title"); //optional
    theIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); //optional
    try {
        startActivityForResult(theIntent,PICK_FOLDER_RESULT_CODE);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    switch (requestCode) {
        case PICK_FOLDER_RESULT_CODE: {
            if (resultCode==RESULT_OK && data!=null && data.getData()!=null) {
                String theFolderPath = data.getData().getPath();
                ...
            }
            break;
        }
    }
}

NOTES:

  • Intent.ACTION_PICK can be replaced with org.openintents.action.PICK_FILE
  • “folder://” can be replaced with “directory://”
  • PICK_FOLDER_RESULT_CODE is user defined and can be any int >= Activity.RESULT_FIRST_USER
  • The Intent created in pickFolder() is returned in onActivityResult so that other information required for onActivityResult to perform it’s task is returned intact to the calling application.

Save To New File

The Save To New File intent differs from the Pick File intent in that instead of using the File Browser application to navigate and pick a file, a dialog pops up allowing the user to input a folder name as well as a file name in seperate textviews.  A browse folder button is available that will use the File Browser application to pick a folder just as the Pick Folder intent currently works.  The goal of this intent is to allow people that prefer to type or to copy/paste text a chance to enter the information with their keyboard rather than use finger navigation that the Pick Folder typically uses.  A button is provided so that finger navigation is still possible, if desired.  The benefit of this approach is for saving files that usually do not require a change in folder can be easily done in a more visually compact fashion and still look as if you are in the original calling app. The major technical difference between this intent and Pick File is the Uri scheme of “file.new”.

void saveToFile(File aFile) {
    Uri theUri = Uri.fromFile(aFile).buildUpon().scheme("file.new").build();
    Intent theIntent = new Intent(Intent.ACTION_PICK);
    theIntent.setData(theUri);
    theIntent.putExtra(Intent.EXTRA_TITLE,"A Custom Title"); //optional
    theIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); //optional
    try {
        startActivityForResult(theIntent,SAVE_FILE_RESULT_CODE);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    switch (requestCode) {
        case SAVE_FILE_RESULT_CODE: {
            if (resultCode==RESULT_OK && data!=null && data.getData()!=null) {
                String theFilePath = data.getData().getPath();
                ...
            }
            break;
        }
    }
}

NOTES:

  • SAVE_FILE_RESULT_CODE is user defined and can be any int >= Activity.RESULT_FIRST_USER
  • The Intent created in pickFolder() is returned in onActivityResult so that other information required for onActivityResult to perform it’s task is returned intact to the calling application.

Save To New Folder

The Save To New Folder intent differs from the Pick Folder intent in that instead of using the application to navigate and pick a folder, a dialog pops up allowing the user to input a folder name in a textview.  A browse folder button is available that will use the File Browser application to pick a folder just as the Pick Folder intent currently works.  The goal of this intent is to allow people that prefer to type or to copy/paste text a chance to enter the information with their keyboard rather than use finger navigation that the Pick Folder typically uses.  A button is provided so that finger navigation is still possible, if desired.  The benefit of this approach is that the dialog still looks as if you are in the original calling app and it has a similar look and feel to the Save To New File dialog. The major technical difference between this intent and Pick Folder is the Uri scheme of “folder.new” (which can also be “directory.new” in case you prefer to use that terminology).

void saveToFolder(File aFolder) {
    Uri theUri = Uri.fromFile(aFolder).buildUpon().scheme("folder.new").build();
    Intent theIntent = new Intent(Intent.ACTION_PICK);
    theIntent.setData(theUri);
    theIntent.putExtra(Intent.EXTRA_TITLE,"A Custom Title"); //optional
    theIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); //optional
    try {
        startActivityForResult(theIntent,SAVE_FOLDER_RESULT_CODE);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    switch (requestCode) {
        case SAVE_FOLDER_RESULT_CODE: {
            if (resultCode==RESULT_OK && data!=null && data.getData()!=null) {
                String theFolderPath = data.getData().getPath();
                ...
            }
            break;
        }
    }
}

NOTES:

  • SAVE_FOLDER_RESULT_CODE is user defined and can be any int >= Activity.RESULT_FIRST_USER
  • “folder.new” can be replaced with “directory.new”
  • The Intent created in pickFolder() is returned in onActivityResult so that other information required for onActivityResult to perform it’s task is returned intact to the calling application.

Get Image Content

The Get Image Content intent uses the existing File Browser app UI to allow the user to pick an existing image file.  The difference between this intent and the Pick File intent is that instead of just returning the Uri of the file picked, the Bitmap of the Image is also returned in the Intent.

void getImageFile(File aFile) {
    Intent theIntent = new Intent(Intent.ACTION_GET_CONTENT);
    theIntent.setDataAndType(Uri.fromFile(aFile),"image/*");  //default file / jump directly to this file/folder, limited to only images
    theIntent.putExtra("return-data",true);  //required
    //optional - scale the return image data to be these dimensions, both values must be present and greater than 0.
    theIntent.putExtra("outputX",96);  //width
    theIntent.putExtra("outputY",96);  //height
   

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
theIntent.putExtra(Intent.EXTRA_TITLE,"A Custom Title"); //optional
    theIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); //optional
    try {
        startActivityForResult(theIntent,GET_IMAGE_RESULT_CODE);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    switch (requestCode) {
        case GET_IMAGE_RESULT_CODE: {
            if (resultCode==RESULT_OK &amp;&amp; data!=null &amp;&amp; data.getData()!=null) {
                String theFilePath = data.getData().getPath();
                Bitmap theImage = data.getParcelableExtra("data");
                ...
            }
            break;
        }
    }
}

NOTES:

  • Uri.fromFile(aFile) can be replaced with Uri.parse(“file://”+aFile.getPath())
  • GET_IMAGE_RESULT_CODE is user defined and can be any int >= Activity.RESULT_FIRST_USER
  • The Intent created in getImageFile() is returned in onActivityResult so that other information required for onActivityResult to perform it’s task is returned intact to the calling application.

Playlist Creator

Takes a playlist consisting of an ArrayList<Uri> of file Uri’s and creates a playlist out of them in the same order as the list.

private static final String EXTRA_DIRECTORY = "com.blackmoonit.intent.extra.DIRECTORY";
void createPlaylist(ArrayList<Uri> aFileList) {
    String theDefaultFolderPath = "/sdcard/playlists";
    Intent theIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
    theIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM,aFileList);
    theIntent.setType("audio/*");
    theIntent.putExtra(EXTRA_DIRECTORY,theDefaultFolderPath); //optional
    try {
        startActivity(theIntent);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Create Zip file

Takes a file list consisting of an ArrayList<Uri> and creates a Zip file of all the files in the ArrayList in the same order as the list. All nested folders in the list will also be traversed.  Activity name is “com.blackmoonit.android.FileBrowser.ZipPackActivity”. Setting the type should be a good faith attempt at figuring out the overall MIME type of the list of files so that other apps get a chance to work with it (like Create Playlist for a list of audio files).  I go into detail on how to do that over here.  If you specifically want to just create a Zip file, then setting the type to “multipart/mixed” is fine.

private static final String EXTRA_DIRECTORY = "com.blackmoonit.intent.extra.DIRECTORY";
void createZipFile(ArrayList<Uri> aFileList) {
    Intent theIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
    theIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM,aFileList);
    theIntent.setType("multipart/mixed");  //this should be a good faith attempt at determining the MIME type
    String theFolderPath = "/sdcard/some_folder";  //all files in the Zip will be stored relative to this path
    theIntent.putExtra(EXTRA_DIRECTORY,theFolderPath);
    try {
        startActivity(theIntent);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Alternatively, it can take a single file instead of a list of multiple files.

void createZipFile(File aFile) {
    Intent theIntent = new Intent(Intent.ACTION_SEND);
    theIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(aFile));
    try {
        startActivity(theIntent);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

NOTES:

  • Unlike using the File Browser app, the process will only work in the background with no user interface for progress bars or cancel buttons.
  • When creating the Intent to send to startActivity(), you can specify a default Zip filename in Uri form as the Data part of the Intent. So instead of using setType(), you would use setDataAndType(Uri.fromFile(new File(myZipFilePath)),”multipart/mixed”);  (version 6.4+ supports this optional feature)
  • GMail behavior now conflicts with setting the Data part of the Intent (Oct 2013).  Instead of using the Data portion, set a string Extra with the URI of the desired filename: putExtra(“com.blackmoonit.extra.DEFAULT_URI”, myDefaultZipFile.toURI().toString() ); (version 7.8i+ supports this optional feature)

Unpack a Zip file

Two approaches to this feature. The simplest is to just view it like any other file.

private static final String EXTRA_DIRECTORY = "com.blackmoonit.intent.extra.DIRECTORY";
void unpackZipFile(File aFile) {
    Intent theIntent = new Intent(Intent.ACTION_VIEW);
    theIntent.setDataAndType(Uri.fromFile(aFile),"application/zip");
    theIntent.putExtra(EXTRA_DIRECTORY,"/my/default/folder"); //optional default location (version 7.4+)
    try {
        startActivity(theIntent);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

NOTES:

  • Unlike using the File Browser app, the process will only work in the background with no user interface for progress bars or cancel buttons.
  • Optional in version 7.5+: using startActivityForResult(theIntent) will return the Intent with EXTRA_DIRECTORY modified to contain what the user chose as a location.

The other approach, new to version 7.6, is to use a BroadcastReceiver to receive a notification upon completion of the unpack process. The code is a bit more involved, but if you really need to make sure the unpack process has been completed, you should use it.

private static final String EXTRA_DIRECTORY = "com.blackmoonit.intent.extra.DIRECTORY";
final String ACTION_UNPACK_FINISHED = "com.blackmoonit.filebrowser.ACTION_UNPACK_FINISHED";
void unpackZipFile(final File theZipFile) {
    final BroadcastReceiver theUnpackBR = new BroadcastReceiver() {
        @Override
        public void onReceive(Context aContext, Intent aIntent) {
            //data of the intent will always have the zip file being unpacked, make sure it's ours
            if (!aIntent.getData().equals(Uri.fromFile(theZipFile)))
                return;
            String theAction = aIntent.getAction();

            //do stuff here, CANNOT show a dialog or bind to a service.
            //http://developer.android.com/reference/android/content/BroadcastReceiver.html

            if (theAction.equals(ACTION_UNPACK_FINISHED)) {
                //finished unpacking archive ==YOUR CODE HERE INSTEAD OF TOAST==
                Toast.makeText(myActivity.this,"finished unzipping!",Toast.LENGTH_LONG).show();

                //since this message means the unpacking is finished, unregister ourselves
                myActivity.unregisterReceiver(this);
            }
        }
    };

    //tell Android OS we want to know if certain actions take place
    IntentFilter theIF = new IntentFilter();
    theIF.addAction(ACTION_UNPACK_FINISHED); //not optional, will always be sent
    theIF.addDataScheme("file"); //data is non-blank, so need to specify a scheme
    theIF.addDataType("*/*"); //type is non-blank, so need to specify wildcard MIMEType
    myActivity.registerReceiver(theUnpackBR,theIF);//unpack zip file intent
    Intent theIntent = new Intent(Intent.ACTION_VIEW);
    theIntent.setDataAndType(Uri.fromFile(theZipFile),"application/zip");
    theIntent.putExtra(EXTRA_DIRECTORY,"/my/default/folder"); //optional default location
    try {
        myActivity.startActivityForResult(theIntent,MY_UNPACK_RESULT_CODE);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    switch (requestCode) {
        case MY_UNPACK_RESULT_CODE: {
            if (resultCode==RESULT_OK && data!=null && data.getData()!=null) {
                String theUnpackPath = data.getStringExtra(EXTRA_DIRECTORY,"/my/default/folder");
                ...
            }
            break;
        }
    }
}

NOTES:

  • The section of code with the Toast will run when the zip file finishes unpacking.
  • File Browser Version 7.6+ required.
  • Several ACTION_UNPACK_% events are defined and functional, including start, finish, start file, end file, update file, and thrown. Please email me if you wish more information regarding these other events.
  • Optional: using startActivityForResult(theIntent) will return the Intent with EXTRA_DIRECTORY modified to contain what the user chose as a location.

MIME Type Provider

You can ask the MIME type provider for converting filenames to mimetypes.

public static final String MIME_AUTHORITY = "com.blackmoonit.FileBrowser.mimeType";
public static final Uri MIMETYPE_URI = Uri.parse("content://" + MIME_AUTHORITY );
private String getMIMEtypeFromProvider(String aFilename) {
    Uri theContentTypeRequest = Uri.withAppendedPath(MIMETYPE_URI,aFilename);
    Cursor theTypeResult = managedQuery(theContentTypeRequest, null, null, null, null);
    theTypeResult.moveToFirst();
    if (!theTypeResult.isNull(0)) {
        return theTypeResult.getString(0);
    } else {
        return null;
    }
}

NOTES (as of version 5.6):

  • aFilename can be a fully qualified path such as “/sdcard/download/somefile.mp3”
  • If aFilename is a fully qualified path that points to an existing directory, the MIME type returned is “container/directory”
  • If aFilename ends in “.bak”, the next possible extension will be used instead.  e.g. “somefile.mp3.bak” will return “audio/mpeg”

Browse File

The Browse File intent is really just a special way to launch the app so that it displays a particular file instead of viewing their last location. Consider this a way to launch your very own Jump Point.

void browseFile(File aFile) {
    Intent theIntent = new Intent(Intent.ACTION_VIEW);
    //jump directly to the following file/folder
    Uri theFileUri = Uri.fromFile(aFile);
    theIntent.setDataAndType(theFileUri,"vnd.android.cursor.item/file");
    theIntent.putExtra(Intent.EXTRA_TITLE,"A Custom Title"); //optional
    theIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); //optional
    try {
        startActivity(theIntent);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

If you are wanting to emulate this behavior in your own file browsing app, just make sure you place the following filter into your AndroidManifest.xml and have your code handle it similar to how you would handle the ACTION_PICK intent.

<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:mimeType="vnd.android.cursor.item/file" />
</intent-filter>

NOTES:

  • Uri.fromFile(aFile) can be replaced with Uri.parse(“file://”+aFile.getPath())

19 thoughts on “Blackmoon File Browser Intents & Providers

  1. Hi,

    I have a document file stored in my application’s data folder, which is a kind of private folder to the application. It is a .doc file. I have doc viewer application installed in the device. Now, how can I open the file with the Action_view intent. As the viewer application can’t access file stored in my application private folder, it is throwing an error saying file can’t be accessed. I have no interest to copy the file to phone public folders like sdcard. Is there anyway to open that doc file. Any clue on embeded apk in android.

    Thanks & regards,
    Suman

    • Files stored in the private folder of the application are only accessible by that application and root. There is no other way to get at it unless the app itself saves the file in public folder or specifically shares it somehow. Nothing can be done by other apps to open/view/modify those private folders. This was done for security purposes in Android itself.

  2. Just wanting to let you know that the “Browse File” file does not work with Blackmoon V.7.8e.
    CODE:
    Intent myIntent = new Intent(Intent.ACTION_VIEW);
    File file = this.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
    Uri fileUri = Uri.fromFile(file);
    myIntent.setDataAndType(fileUri, “vnd.android.cursor.item/file”);
    try {
    this.startActivity(myIntent);
    } catch (Exception e) {
    e.printStackTrace();
    }

    • The Create Jump Point shortcut code uses this feature, the only time that code would not work is if your Environment.DIRECTORY_DOWNLOADS folder does not exist. File Browser app cannot navigate to something that does not exist. Ensure file exists by following the getExternalFilesDir() call with a file.mkdirs() call. Folders defined by Environment are not guaranteed to exist.

  3. I want to save an image from my app’s drawable to user’s phone and user should select the directory to save the image means he/she can browse the directory to save the image. Some one can help me, please. Thanks in advance.

  4. I’am try Pick Folder and recive android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.PICK dat=folder:///mnt/sdcard/ flg=0x800000 (has extras) }

    Using android 4.0, can help me ?

  5. Hi all,
    Which File browser are u using for the pick folder example??, The File browser that comes with my Galaxy Tab does not listen the intent “Intent.ACTION_PICK”. I had to install ES File explorer and change the intent to “Intent.ACTION_GET_CONTENT”.

  6. Just wanting to let you know that the “Browse File” file does not work with Blackmoon V.7.8e.
    CODE:
    Intent myIntent = new Intent(Intent.ACTION_VIEW);
    File file = this.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
    Uri fileUri = Uri.fromFile(file);
    myIntent.setDataAndType(fileUri, “vnd.android.cursor.item/file”);
    try {
    this.startActivity(myIntent);
    } catch (Exception e) {
    e.printStackTrace();
    }

  7. Hi,

    I have a document file stored in my application’s data folder, which is a kind of private folder to the application. It is a .doc file. I have doc viewer application installed in the device. Now, how can I open the file with the Action_view intent. As the viewer application can’t access file stored in my application private folder, it is throwing an error saying file can’t be accessed. I have no interest to copy the file to phone public folders like sdcard. Is there anyway to open that doc file. Any clue on embeded apk in android.

  8. Just wanting to let you know that the “Browse File” file does not work with Blackmoon V.7.8e.
    CODE:
    Intent myIntent = new Intent(Intent.ACTION_VIEW);
    File file = this.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
    Uri fileUri = Uri.fromFile(file);
    myIntent.setDataAndType(fileUri, “vnd.android.cursor.item/file”);
    try {
    this.startActivity(myIntent);
    } catch (Exception e) {
    e.printStackTrace();
    }

Leave a Reply

Your email address will not be published. Required fields are marked *