Skip to content

Instantly share code, notes, and snippets.

@jaredrummler
Last active June 25, 2019 21:01
Show Gist options
  • Save jaredrummler/e7e33963a794c02d163d5bf11fabb5ba to your computer and use it in GitHub Desktop.
Save jaredrummler/e7e33963a794c02d163d5bf11fabb5ba to your computer and use it in GitHub Desktop.
Setup a FileProvider for Android with paths for root, external storage, and internal app files
<manifest package="com.jaredrummler.android.common"
xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<provider
android:name="com.jaredrummler.android.files.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/root_paths"/>
</provider>
</application>
</manifest>
package com.jaredrummler.android.files;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.provider.MediaStore;
import java.util.Arrays;
/**
* A {@link FileProvider} that supports apps which rely on {@link MediaStore.MediaColumns#DATA}
*
* @see <a href="http://stackoverflow.com/a/25020642">http://stackoverflow.com/a/25020642</a>
*/
public class FileProvider extends android.support.v4.content.FileProvider {
public static final String AUTHORITY = App.getContext().getPackageName() + ".fileprovider";
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
final Cursor source = super.query(uri, projection, selection, selectionArgs, sortOrder);
/*
It's not necessary to overwrite
public Cursor query(final Uri uri, final String[] projection, final String selection, final String[]
selectionArgs, final String sortOrder, final CancellationSignal cancellationSignal);
cause it calls this method.
== What's happening here? ==
Some apps rely on the MediaStore.MediaColumns.DATA column in the cursor. To prevent them
from crashing, we have to copy the initial cursor and add the MediaStore.MediaColumns.DATA
column. The values of this column will always be null, but we assume that they are able
to handle that case.
*/
if (source == null) {
android.util.Log.w("TAG", "Not able to apply workaround, super.query(...) returned null");
return null;
}
String[] columnNames = source.getColumnNames();
String[] newColumnNames = columnNamesWithData(columnNames);
MatrixCursor cursor = new MatrixCursor(newColumnNames, source.getCount());
if (cursor.moveToFirst()) {
do {
MatrixCursor.RowBuilder row = cursor.newRow();
for (int i = 0; i < columnNames.length; i++) {
row.add(source.getString(i));
}
} while (cursor.moveToNext());
}
source.close();
return cursor;
}
private String[] columnNamesWithData(String[] columnNames) {
for (String columnName : columnNames) {
if (MediaStore.MediaColumns.DATA.equals(columnName)) {
return columnNames;
}
}
String[] newColumnNames = Arrays.copyOf(columnNames, columnNames.length + 1);
newColumnNames[columnNames.length] = MediaStore.MediaColumns.DATA;
return newColumnNames;
}
}
<?xml version="1.0" encoding="utf-8"?>
<paths>
<root-path
name="root"
path="."/>
<files-path
name="files"
path="."/>
<cache-path
name="cache"
path="."/>
<external-path
name="external"
path="."/>
<external-files-path
name="external-files"
path="."/>
<external-cache-path
name="external-cache"
path="."/>
</paths>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment