Back to project page LocalStorage.
The source code is released under:
BSD 3-Clause License Copyright (c) 2013, Ian Lake All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following cond...
If you think the Android project LocalStorage listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.ianhanniballake.localstorage; /*from w w w . ja v a 2s . co m*/ import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.database.MatrixCursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Point; import android.os.CancellationSignal; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.StatFs; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.provider.DocumentsProvider; import android.support.v4.os.EnvironmentCompat; import android.text.TextUtils; import android.util.Log; import android.webkit.MimeTypeMap; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class LocalStorageProvider extends DocumentsProvider { /** * Authority that matches the authority in the AndroidManifest.xml for LocalStorageProvider */ public final static String AUTHORITY = "com.ianhanniballake.localstorage.documents"; /** * Default root projection: everything but Root.COLUMN_MIME_TYPES */ private final static String[] DEFAULT_ROOT_PROJECTION = new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_SUMMARY, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_ICON, 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[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_FLAGS, Document.COLUMN_MIME_TYPE, Document.COLUMN_SIZE, 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(); if (TextUtils.equals(Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED)) { final MatrixCursor.RowBuilder row = result.newRow(); // These columns are required row.add(Root.COLUMN_ROOT_ID, homeDir.getAbsolutePath()); row.add(Root.COLUMN_DOCUMENT_ID, homeDir.getAbsolutePath()); row.add(Root.COLUMN_TITLE, getContext().getString(R.string.home)); row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD); row.add(Root.COLUMN_ICON, R.drawable.ic_launcher); // These columns are optional row.add(Root.COLUMN_SUMMARY, homeDir.getAbsolutePath()); row.add(Root.COLUMN_AVAILABLE_BYTES, new StatFs(homeDir.getAbsolutePath()).getAvailableBytes()); // 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) } // Add SD card directory File sdCard = new File("/storage/extSdCard"); String storageState = EnvironmentCompat.getStorageState(sdCard); if (TextUtils.equals(storageState, Environment.MEDIA_MOUNTED) || TextUtils.equals(storageState, Environment.MEDIA_MOUNTED_READ_ONLY)) { final MatrixCursor.RowBuilder row = result.newRow(); // These columns are required row.add(Root.COLUMN_ROOT_ID, sdCard.getAbsolutePath()); row.add(Root.COLUMN_DOCUMENT_ID, sdCard.getAbsolutePath()); row.add(Root.COLUMN_TITLE, getContext().getString(R.string.sd_card)); // Always assume SD Card is read-only row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY); row.add(Root.COLUMN_ICON, R.drawable.ic_sd_card); row.add(Root.COLUMN_SUMMARY, sdCard.getAbsolutePath()); row.add(Root.COLUMN_AVAILABLE_BYTES, new StatFs(sdCard.getAbsolutePath()).getAvailableBytes()); } 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 boolean isChildDocument(final String parentDocumentId, final String documentId) { return documentId.startsWith(parentDocumentId); } @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(Document.COLUMN_DOCUMENT_ID, file.getAbsolutePath()); row.add(Document.COLUMN_DISPLAY_NAME, file.getName()); String mimeType = getDocumentType(file.getAbsolutePath()); row.add(Document.COLUMN_MIME_TYPE, mimeType); int flags = file.canWrite() ? Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE | Document.FLAG_SUPPORTS_RENAME | (mimeType.equals(Document.MIME_TYPE_DIR) ? Document.FLAG_DIR_SUPPORTS_CREATE : 0) : 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 |= Document.FLAG_SUPPORTS_THUMBNAIL; row.add(Document.COLUMN_FLAGS, flags); // COLUMN_SIZE is required, but can be null row.add(Document.COLUMN_SIZE, file.length()); // These columns are optional row.add(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 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 String renameDocument(final String documentId, final String displayName) throws FileNotFoundException { File existingFile = new File(documentId); if (!existingFile.exists()) { throw new FileNotFoundException(documentId + " does not exist"); } if (existingFile.getName().equals(displayName)) { return null; } File parentDirectory = existingFile.getParentFile(); File newFile = new File(parentDirectory, displayName); int conflictIndex = 1; while (newFile.exists()) { newFile = new File(parentDirectory, displayName + "_" + conflictIndex++); } boolean success = existingFile.renameTo(newFile); if (!success) { throw new FileNotFoundException("Unable to rename " + documentId + " to " + existingFile.getAbsolutePath()); } return existingFile.getAbsolutePath(); } @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; } }