package com.amaze.filemanager.filesystem;
/**
* Created by Arpit on 29-06-2015.
*/
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.util.Log;
import com.amaze.filemanager.R;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Locale;
/**
* Wrapper for manipulating files via the Android Media Content Provider. As of Android 4.4 KitKat,
* applications can no longer write to the "secondary storage" of a device. Write operations using
* the java.io.File API will thus fail. This class restores access to those write operations by way
* of the Media Content Provider.</p>
*
* Note that this class relies on the internal operational characteristics of the media content
* provider API, and as such is not guaranteed to be future-proof. Then again, we did all think the
* java.io.File API was going to be future-proof for media card access, so all bets are off.</p>
*
* If you're forced to use this class, it's because Google/AOSP made a very poor API decision in
* Android 4.4 KitKat. Read more at https://plus.google.com/+TodLiebeck/posts/gjnmuaDM8sn</p>
*
* Your application must declare the permission "android.permission.WRITE_EXTERNAL_STORAGE".</p>
*
* Adapted from: http://forum.xda-developers.com/showpost.php?p=52151865&postcount=20</p>
*
* @author Jared Rummler <jared.rummler@gmail.com>
*/
public class MediaStoreHack {
private static final String ALBUM_ART_URI = "content://media/external/audio/albumart";
private static final String[] ALBUM_PROJECTION = {
BaseColumns._ID, MediaStore.Audio.AlbumColumns.ALBUM_ID, "media_type"
};
/**
* Deletes the file. Returns true if the file has been successfully deleted or otherwise does
* not exist. This operation is not recursive.
*/
public static boolean delete(final Context context, final File file) {
final String where = MediaStore.MediaColumns.DATA + "=?";
final String[] selectionArgs = new String[]{
file.getAbsolutePath()
};
final ContentResolver contentResolver = context.getContentResolver();
final Uri filesUri = MediaStore.Files.getContentUri("external");
// Delete the entry from the media database. This will actually delete media files.
contentResolver.delete(filesUri, where, selectionArgs);
// If the file is not a media file, create a new entry.
if (file.exists()) {
final ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
// Delete the created entry, such that content provider will delete the file.
contentResolver.delete(filesUri, where, selectionArgs);
}
return !file.exists();
}
private static File getExternalFilesDir(final Context context) {
return context.getExternalFilesDir(null);
}
public static InputStream
getInputStream(final Context context, final File file, final long size) {
try {
final String where = MediaStore.MediaColumns.DATA + "=?";
final String[] selectionArgs = new String[]{
file.getAbsolutePath()
};
final ContentResolver contentResolver = context.getContentResolver();
final Uri filesUri = MediaStore.Files.getContentUri("external");
contentResolver.delete(filesUri, where, selectionArgs);
final ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
values.put(MediaStore.MediaColumns.SIZE, size);
final Uri uri = contentResolver.insert(filesUri, values);
return contentResolver.openInputStream(uri);
} catch (final Throwable t) {
return null;
}
}
public static OutputStream getOutputStream(Context context, String str) {
OutputStream outputStream = null;
Uri fileUri = getUriFromFile(str, context);
if (fileUri != null) {
try {
outputStream = context.getContentResolver().openOutputStream(fileUri);
} catch (Throwable th) {
}
}
return outputStream;
}
public static Uri getUriFromFile(final String path, Context context) {
ContentResolver resolver = context.getContentResolver();
Cursor filecursor = resolver.query(MediaStore.Files.getContentUri("external"),
new String[]{BaseColumns._ID}, MediaStore.MediaColumns.DATA + " = ?",
new String[]{path}, MediaStore.MediaColumns.DATE_ADDED + " desc");
filecursor.moveToFirst();
if (filecursor.isAfterLast()) {
filecursor.close();
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, path);
return resolver.insert(MediaStore.Files.getContentUri("external"), values);
} else {
int imageId = filecursor.getInt(filecursor.getColumnIndex(BaseColumns._ID));
Uri uri = MediaStore.Files.getContentUri("external").buildUpon().appendPath(
Integer.toString(imageId)).build();
filecursor.close();
return uri;
}
}
/**
* Returns an OutputStream to write to the file. The file will be truncated immediately.
*/
private static int getTemporaryAlbumId(final Context context) {
final File temporaryTrack;
try {
temporaryTrack = installTemporaryTrack(context);
} catch (final IOException ex) {
Log.w("MediaFile", "Error installing tempory track.", ex);
return 0;
}
final Uri filesUri = MediaStore.Files.getContentUri("external");
final String[] selectionArgs = {
temporaryTrack.getAbsolutePath()
};
final ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(filesUri, ALBUM_PROJECTION,
MediaStore.MediaColumns.DATA + "=?", selectionArgs, null);
if (cursor == null || !cursor.moveToFirst()) {
if (cursor != null) {
cursor.close();
cursor = null;
}
final ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, temporaryTrack.getAbsolutePath());
values.put(MediaStore.MediaColumns.TITLE, "{MediaWrite Workaround}");
values.put(MediaStore.MediaColumns.SIZE, temporaryTrack.length());
values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mpeg");
values.put(MediaStore.Audio.AudioColumns.IS_MUSIC, true);
contentResolver.insert(filesUri, values);
}
cursor = contentResolver.query(filesUri, ALBUM_PROJECTION, MediaStore.MediaColumns.DATA
+ "=?", selectionArgs, null);
if (cursor == null) {
return 0;
}
if (!cursor.moveToFirst()) {
cursor.close();
return 0;
}
final int id = cursor.getInt(0);
final int albumId = cursor.getInt(1);
final int mediaType = cursor.getInt(2);
cursor.close();
final ContentValues values = new ContentValues();
boolean updateRequired = false;
if (albumId == 0) {
values.put(MediaStore.Audio.AlbumColumns.ALBUM_ID, 13371337);
updateRequired = true;
}
if (mediaType != 2) {
values.put("media_type", 2);
updateRequired = true;
}
if (updateRequired) {
contentResolver.update(filesUri, values, BaseColumns._ID + "=" + id, null);
}
cursor = contentResolver.query(filesUri, ALBUM_PROJECTION, MediaStore.MediaColumns.DATA
+ "=?", selectionArgs, null);
if (cursor == null) {
return 0;
}
try {
if (!cursor.moveToFirst()) {
return 0;
}
return cursor.getInt(1);
} finally {
cursor.close();
}
}
private static File installTemporaryTrack(final Context context) throws IOException {
final File externalFilesDir = getExternalFilesDir(context);
if (externalFilesDir == null) {
return null;
}
final File temporaryTrack = new File(externalFilesDir, "temptrack.mp3");
if (!temporaryTrack.exists()) {
InputStream in = null;
OutputStream out = null;
try {
in = context.getResources().openRawResource(R.raw.temptrack);
out = new FileOutputStream(temporaryTrack);
final byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
} finally {
out.close();
in.close();
}
}
return temporaryTrack;
}
public static boolean mkdir(final Context context, final File file) throws IOException {
if (file.exists()) {
return file.isDirectory();
}
final File tmpFile = new File(file, ".MediaWriteTemp");
final int albumId = getTemporaryAlbumId(context);
if (albumId == 0) {
throw new IOException("Failed to create temporary album id.");
}
final Uri albumUri = Uri.parse(String.format(Locale.US, ALBUM_ART_URI + "/%d", albumId));
final ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, tmpFile.getAbsolutePath());
final ContentResolver contentResolver = context.getContentResolver();
if (contentResolver.update(albumUri, values, null, null) == 0) {
values.put(MediaStore.Audio.AlbumColumns.ALBUM_ID, albumId);
contentResolver.insert(Uri.parse(ALBUM_ART_URI), values);
}
try {
final ParcelFileDescriptor fd = contentResolver.openFileDescriptor(albumUri, "r");
fd.close();
} finally {
delete(context, tmpFile);
}
return file.exists();
}
public static boolean mkfile(final Context context, final File file) {
final OutputStream outputStream = getOutputStream(context, file.getPath());
if (outputStream == null) {
return false;
}
try {
outputStream.close();
return true;
} catch (final IOException e) {
}
return false;
}
}