Created
November 9, 2017 05:47
-
-
Save piyush-malaviya/cf8077bfbe11641b89f8abe875a284ce to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LocalStorageProvider extends DocumentsProvider { | |
public static final String AUTHORITY = "com.app.android.localstorage.documents"; | |
/** | |
* Default root projection: everything but Root.COLUMN_MIME_TYPES | |
*/ | |
private final static String[] DEFAULT_ROOT_PROJECTION = new String[]{ | |
DocumentsContract.Root.COLUMN_ROOT_ID, | |
DocumentsContract.Root.COLUMN_FLAGS, | |
DocumentsContract.Root.COLUMN_TITLE, | |
DocumentsContract.Root.COLUMN_DOCUMENT_ID, | |
DocumentsContract.Root.COLUMN_ICON, | |
DocumentsContract.Root.COLUMN_AVAILABLE_BYTES | |
}; | |
/** | |
* Default document projection: everything but Document.COLUMN_ICON and | |
* Document.COLUMN_SUMMARY | |
*/ | |
private final static String[] DEFAULT_DOCUMENT_PROJECTION = new String[]{ | |
DocumentsContract.Document.COLUMN_DOCUMENT_ID, | |
DocumentsContract.Document.COLUMN_DISPLAY_NAME, | |
DocumentsContract.Document.COLUMN_FLAGS, | |
DocumentsContract.Document.COLUMN_MIME_TYPE, | |
DocumentsContract.Document.COLUMN_SIZE, | |
DocumentsContract.Document.COLUMN_LAST_MODIFIED | |
}; | |
@Override | |
public Cursor queryRoots(final String[] projection) throws FileNotFoundException { | |
// Create a cursor with either the requested fields, or the default | |
// projection if "projection" is null. | |
final MatrixCursor result = new MatrixCursor(projection != null ? projection | |
: DEFAULT_ROOT_PROJECTION); | |
// Add Home directory | |
File homeDir = Environment.getExternalStorageDirectory(); | |
final MatrixCursor.RowBuilder row = result.newRow(); | |
// These columns are required | |
row.add(DocumentsContract.Root.COLUMN_ROOT_ID, homeDir.getAbsolutePath()); | |
row.add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, homeDir.getAbsolutePath()); | |
row.add(DocumentsContract.Root.COLUMN_TITLE, "Internal storage"); | |
row.add(DocumentsContract.Root.COLUMN_FLAGS, DocumentsContract.Root.FLAG_LOCAL_ONLY | DocumentsContract.Root.FLAG_SUPPORTS_CREATE); | |
row.add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_provider); | |
// These columns are optional | |
row.add(DocumentsContract.Root.COLUMN_AVAILABLE_BYTES, homeDir.getFreeSpace()); | |
// Root.COLUMN_MIME_TYPE is another optional column and useful if you | |
// have multiple roots with different | |
// types of mime types (roots that don't match the requested mime type | |
// are automatically hidden) | |
return result; | |
} | |
@Override | |
public String createDocument(final String parentDocumentId, final String mimeType, | |
final String displayName) throws FileNotFoundException { | |
File newFile = new File(parentDocumentId, displayName); | |
try { | |
newFile.createNewFile(); | |
return newFile.getAbsolutePath(); | |
} catch (IOException e) { | |
Log.e(LocalStorageProvider.class.getSimpleName(), "Error creating new file " + newFile); | |
} | |
return null; | |
} | |
@Override | |
public AssetFileDescriptor openDocumentThumbnail(final String documentId, final Point sizeHint, | |
final CancellationSignal signal) throws FileNotFoundException { | |
// Assume documentId points to an image file. Build a thumbnail no | |
// larger than twice the sizeHint | |
BitmapFactory.Options options = new BitmapFactory.Options(); | |
options.inJustDecodeBounds = true; | |
BitmapFactory.decodeFile(documentId, options); | |
final int targetHeight = 2 * sizeHint.y; | |
final int targetWidth = 2 * sizeHint.x; | |
final int height = options.outHeight; | |
final int width = options.outWidth; | |
options.inSampleSize = 1; | |
if (height > targetHeight || width > targetWidth) { | |
final int halfHeight = height / 2; | |
final int halfWidth = width / 2; | |
// Calculate the largest inSampleSize value that is a power of 2 and | |
// keeps both | |
// height and width larger than the requested height and width. | |
while ((halfHeight / options.inSampleSize) > targetHeight | |
|| (halfWidth / options.inSampleSize) > targetWidth) { | |
options.inSampleSize *= 2; | |
} | |
} | |
options.inJustDecodeBounds = false; | |
Bitmap bitmap = BitmapFactory.decodeFile(documentId, options); | |
// Write out the thumbnail to a temporary file | |
File tempFile = null; | |
FileOutputStream out = null; | |
try { | |
tempFile = File.createTempFile("thumbnail", null, getContext().getCacheDir()); | |
out = new FileOutputStream(tempFile); | |
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out); | |
} catch (IOException e) { | |
Log.e(LocalStorageProvider.class.getSimpleName(), "Error writing thumbnail", e); | |
return null; | |
} finally { | |
if (out != null) | |
try { | |
out.close(); | |
} catch (IOException e) { | |
Log.e(LocalStorageProvider.class.getSimpleName(), "Error closing thumbnail", e); | |
} | |
} | |
// It appears the Storage Framework UI caches these results quite | |
// aggressively so there is little reason to | |
// write your own caching layer beyond what you need to return a single | |
// AssetFileDescriptor | |
return new AssetFileDescriptor(ParcelFileDescriptor.open(tempFile, | |
ParcelFileDescriptor.MODE_READ_ONLY), 0, | |
AssetFileDescriptor.UNKNOWN_LENGTH); | |
} | |
@Override | |
public Cursor queryChildDocuments(final String parentDocumentId, final String[] projection, | |
final String sortOrder) throws FileNotFoundException { | |
// Create a cursor with either the requested fields, or the default | |
// projection if "projection" is null. | |
final MatrixCursor result = new MatrixCursor(projection != null ? projection | |
: DEFAULT_DOCUMENT_PROJECTION); | |
final File parent = new File(parentDocumentId); | |
for (File file : parent.listFiles()) { | |
// Don't show hidden files/folders | |
if (!file.getName().startsWith(".")) { | |
// Adds the file's display name, MIME type, size, and so on. | |
includeFile(result, file); | |
} | |
} | |
return result; | |
} | |
@Override | |
public Cursor queryDocument(final String documentId, final String[] projection) | |
throws FileNotFoundException { | |
// Create a cursor with either the requested fields, or the default | |
// projection if "projection" is null. | |
final MatrixCursor result = new MatrixCursor(projection != null ? projection | |
: DEFAULT_DOCUMENT_PROJECTION); | |
includeFile(result, new File(documentId)); | |
return result; | |
} | |
private void includeFile(final MatrixCursor result, final File file) | |
throws FileNotFoundException { | |
final MatrixCursor.RowBuilder row = result.newRow(); | |
// These columns are required | |
row.add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, file.getAbsolutePath()); | |
row.add(DocumentsContract.Document.COLUMN_DISPLAY_NAME, file.getName()); | |
String mimeType = getDocumentType(file.getAbsolutePath()); | |
row.add(DocumentsContract.Document.COLUMN_MIME_TYPE, mimeType); | |
int flags = file.canWrite() ? DocumentsContract.Document.FLAG_SUPPORTS_DELETE | DocumentsContract.Document.FLAG_SUPPORTS_WRITE | |
: 0; | |
// We only show thumbnails for image files - expect a call to | |
// openDocumentThumbnail for each file that has | |
// this flag set | |
if (mimeType.startsWith("image/")) | |
flags |= DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL; | |
row.add(DocumentsContract.Document.COLUMN_FLAGS, flags); | |
// COLUMN_SIZE is required, but can be null | |
row.add(DocumentsContract.Document.COLUMN_SIZE, file.length()); | |
// These columns are optional | |
row.add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, file.lastModified()); | |
// Document.COLUMN_ICON can be a resource id identifying a custom icon. | |
// The system provides default icons | |
// based on mime type | |
// Document.COLUMN_SUMMARY is optional additional information about the | |
// file | |
} | |
@Override | |
public String getDocumentType(final String documentId) throws FileNotFoundException { | |
File file = new File(documentId); | |
if (file.isDirectory()) | |
return DocumentsContract.Document.MIME_TYPE_DIR; | |
// From FileProvider.getType(Uri) | |
final int lastDot = file.getName().lastIndexOf('.'); | |
if (lastDot >= 0) { | |
final String extension = file.getName().substring(lastDot + 1); | |
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); | |
if (mime != null) { | |
return mime; | |
} | |
} | |
return "application/octet-stream"; | |
} | |
@Override | |
public void deleteDocument(final String documentId) throws FileNotFoundException { | |
new File(documentId).delete(); | |
} | |
@Override | |
public ParcelFileDescriptor openDocument(final String documentId, final String mode, | |
final CancellationSignal signal) throws FileNotFoundException { | |
File file = new File(documentId); | |
final boolean isWrite = (mode.indexOf('w') != -1); | |
if (isWrite) { | |
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE); | |
} else { | |
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); | |
} | |
} | |
@Override | |
public boolean onCreate() { | |
return true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment