/*
* Copyright 2014 Yaroslav Mytkalyk
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.docd.purefm.utils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Pair;
import com.docd.purefm.Environment;
import com.docd.purefm.file.GenericFile;
import android.support.annotation.NonNull;
public final class MediaStoreUtils {
private MediaStoreUtils() {}
public static void requestMediaScanner(@NonNull final Context context,
@NonNull final GenericFile... files) {
final String[] paths = new String[files.length];
int i = 0;
for (final GenericFile file : files) {
paths[i] = PFMFileUtils.fullPath(file);
i++;
}
MediaScannerConnection.scanFile(context, paths, null, null);
}
public static void renameFileOrDirectory(@NonNull final Context context,
@NonNull final GenericFile oldFile,
@NonNull final GenericFile newFile) {
if (oldFile.isDirectory()) {
deleteFileOrDirectory(context.getContentResolver(), oldFile);
requestMediaScanner(context, newFile);
} else {
renameFile(context.getContentResolver(), oldFile, newFile);
}
}
private static void renameFile(@NonNull final ContentResolver contentResolver,
@NonNull final GenericFile oldFile,
@NonNull final GenericFile newFile) {
final Uri uri = getContentUri(oldFile);
final long id = fileId(contentResolver, uri, oldFile.toFile());
if (id != -1) {
final ContentValues values = new ContentValues(2);
values.put(MediaStore.Files.FileColumns.DATA, PFMFileUtils.fullPath(newFile));
values.put(MediaStore.Files.FileColumns.DISPLAY_NAME, newFile.getName());
contentResolver.update(ContentUris.withAppendedId(uri, id),
values, null, null);
}
}
public static void moveFiles(@NonNull final Context context,
@NonNull final List<Pair<GenericFile, GenericFile>> files) {
for (final Pair<GenericFile, GenericFile> pair : files) {
deleteFileOrDirectory(context.getContentResolver(), pair.first);
requestMediaScanner(context, pair.second);
}
}
public static void copyFiles(@NonNull final Context context,
@NonNull final List<Pair<GenericFile, GenericFile>> files) {
final int size = files.size();
final String[] paths = new String[size];
for (int i = 0; i < size; i++) {
final Pair<GenericFile, GenericFile> pair = files.get(i);
final String newPath = PFMFileUtils.fullPath(pair.second);
paths[i] = newPath;
}
MediaScannerConnection.scanFile(context, paths, null, null);
}
public static void addEmptyFileOrDirectory(final ContentResolver resolver, final GenericFile file) {
final ContentValues values = new ContentValues(2);
final String fullPath = PFMFileUtils.fullPath(file);
values.put(MediaStore.Files.FileColumns.DATA, fullPath);
values.put(MediaStore.Files.FileColumns.DISPLAY_NAME, file.getName());
final String mimeType = MimeTypes.getMimeType(file.toFile());
if (mimeType != null) {
values.put(MediaStore.Files.FileColumns.MIME_TYPE, mimeType);
}
resolver.insert(getContentUri(file), values);
}
public static Uri getContentUri(@NonNull final GenericFile file) {
return getContentUri(PFMFileUtils.fullPath(file));
}
public static Uri getContentUri(@NonNull final String path) {
final boolean isExternal = isExternal(path);
// general file content uri is just fine, so the code below is commented
// if (mimeType != null) {
// if (mimeType.startsWith("image/")) {
// return isExternal ? MediaStore.Images.Media.EXTERNAL_CONTENT_URI :
// MediaStore.Images.Media.INTERNAL_CONTENT_URI;
// } else if (mimeType.startsWith("audio/")) {
// return isExternal ? MediaStore.Audio.Media.EXTERNAL_CONTENT_URI :
// MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
// } else if (mimeType.startsWith("video/")) {
// return isExternal ? MediaStore.Video.Media.EXTERNAL_CONTENT_URI :
// MediaStore.Video.Media.INTERNAL_CONTENT_URI;
// }
// }
return MediaStore.Files.getContentUri(isExternal ?
"external" : "internal");
}
public static boolean isExternal(@NonNull final String path) {
for (final StorageHelper.StorageVolume volume : Environment.getStorageVolumes()) {
if (path.startsWith(volume.file.getAbsolutePath())) {
return true;
}
}
return false;
}
private static long fileId(@NonNull final ContentResolver contentResolver,
@NonNull final Uri uri,
@NonNull final File file) {
final Pair<String, String[]> selection = dataSelection(file);
final Cursor c = contentResolver.query(uri,
new String[] {MediaStore.Files.FileColumns._ID},
selection.first, selection.second, null);
if (c != null) {
try {
if (c.moveToFirst()) {
return c.getLong(0);
}
} finally {
c.close();
}
}
return -1;
}
@NonNull
public static Pair<String, String[]> dataSelection(final File file) {
final String canonicalPath = PFMFileUtils.fullPath(file);
final String absolutePath = file.getAbsolutePath();
final String selection;
final String[] selectionArgs;
if (canonicalPath.equals(absolutePath)) {
selection = MediaStore.Files.FileColumns.DATA + "=?";
selectionArgs = new String[] {canonicalPath};
} else {
selection = MediaStore.Files.FileColumns.DATA + "=? OR " +
MediaStore.Files.FileColumns.DATA + "=?";
selectionArgs = new String[] {canonicalPath, absolutePath};
}
return new Pair<>(selection, selectionArgs);
}
public static void deleteFile(@NonNull final ContentResolver contentResolver,
@NonNull final GenericFile file) {
final String canonicalPath = PFMFileUtils.fullPath(file);
final int result = contentResolver.delete(getContentUri(canonicalPath),
MediaStore.Files.FileColumns.DATA + "=?", new String[] {canonicalPath});
if (result == 0) {
final String absolutePath = file.getAbsolutePath();
if (!absolutePath.equals(canonicalPath)) {
contentResolver.delete(getContentUri(absolutePath),
MediaStore.Files.FileColumns.DATA + "=?", new String[]{absolutePath});
}
}
}
public static void deleteFileOrDirectory(@NonNull final ContentResolver contentResolver,
@NonNull final GenericFile file) {
if (file.isDirectory()) {
deleteAllFromDirectory(contentResolver, file);
} else {
deleteFile(contentResolver, file);
}
}
public static void deleteFilesOrDirectories(@NonNull final ContentResolver contentResolver,
@NonNull final List<GenericFile> files) {
final List<GenericFile> regular = new ArrayList<>();
for (final GenericFile file : files) {
if (file.isDirectory()) {
deleteAllFromDirectory(contentResolver, file);
} else {
regular.add(file);
}
}
if (!regular.isEmpty()) {
final String selection = filesToSelection(regular);
contentResolver.delete(getContentUri(regular.get(0)), selection, null);
}
}
@NonNull
private static Pair<String, String[]> listDirectoryRecursiveSelelction(
@NonNull final GenericFile dir) {
if (dir.exists() && !dir.isDirectory()) {
throw new IllegalArgumentException("\'dir\' is not a directory");
}
final String canonicalPath = PFMFileUtils.fullPath(dir);
boolean pathEndsWithSeparator = canonicalPath.endsWith(File.separator);
String pathNoSeparator = pathEndsWithSeparator ? canonicalPath.substring(0, canonicalPath.length() - 1) : canonicalPath;
String pathWithSeparator = pathEndsWithSeparator ? canonicalPath : canonicalPath.concat(File.separator);
final String absolutePath = dir.getAbsolutePath();
final String selection;
final String[] selectionArgs;
if (canonicalPath.equals(absolutePath)) {
selection = MediaStore.Files.FileColumns.DATA + "=? OR " +
MediaStore.Files.FileColumns.DATA + " LIKE \'" + pathWithSeparator + "%\'";
selectionArgs = new String[] {pathNoSeparator};
} else {
pathEndsWithSeparator = absolutePath.endsWith(File.separator);
String absolutePathNoSeparator = pathEndsWithSeparator ? absolutePath.substring(0, absolutePath.length() - 1) : absolutePath;
String absolutePathWithSeparator = pathEndsWithSeparator ? absolutePath : absolutePath.concat(File.separator);
selection = MediaStore.Files.FileColumns.DATA + "=? OR " +
MediaStore.Files.FileColumns.DATA + "=? OR " +
MediaStore.Files.FileColumns.DATA + " LIKE \'" + pathWithSeparator + "%\'" + " OR " +
MediaStore.Files.FileColumns.DATA + " LIKE \'" + absolutePathWithSeparator + "%\'";
selectionArgs = new String[] {pathNoSeparator, absolutePathNoSeparator};
}
return new Pair<>(selection, selectionArgs);
}
public static void deleteAllFromDirectory(@NonNull final ContentResolver contentResolver,
@NonNull final GenericFile dir) {
final String canonicalPath = PFMFileUtils.fullPath(dir);
final Uri uri = MediaStoreUtils.getContentUri(canonicalPath);
final Pair<String, String[]> selection = listDirectoryRecursiveSelelction(dir);
final Cursor c = contentResolver.query(uri, new String[] {
MediaStore.Files.FileColumns._ID},
selection.first, selection.second, null);
if (c != null){
try {
for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
final long id = c.getLong(0);
contentResolver.delete(ContentUris.withAppendedId(uri, id), null, null);
}
} finally {
c.close();
}
}
}
@NonNull
private static String filesToSelection(final List<GenericFile> files) {
final StringBuilder selection = new StringBuilder(MediaStore.Files.FileColumns.DATA);
selection.append(" IN ");
selection.append('(');
final int size = files.size();
for (int i = 0; i < size; i++) {
final GenericFile file = files.get(i);
final String canonicalPath = PFMFileUtils.fullPath(file);
final String absolutePath = file.getAbsolutePath();
selection.append('\'');
selection.append(canonicalPath);
selection.append('\'');
if (!absolutePath.equals(canonicalPath)) {
selection.append(',');
selection.append('\'');
selection.append(absolutePath);
selection.append('\'');
}
if (i != size - 1) {
selection.append(',');
}
}
selection.append(')');
return selection.toString();
}
}