开源版本:
MediaStoreHack.java
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.kk.kkfilemanager.R;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Locale;
/**
* Created by Luxuan on 2016/11/15.
*/
public class MediaStoreHackOnKitkat {
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(Context context, File file){
String where=MediaStore.MediaColumns.DATA+"=?";
String[] selectionArgs=new String[]{
file.getAbsolutePath()
};
ContentResolver contentResolver=context.getContentResolver();
// Delete the entry from the media database. This will actually delete media files.
Uri fileUri=MediaStore.Files.getContentUri("external");
// If the file is not a media file, create a new entry.
contentResolver.delete(fileUri,where,selectionArgs);
if(file.exists()){
ContentValues values=new ContentValues();
values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values);
contentResolver.delete(fileUri,where,selectionArgs);
}
return !file.exists();
}
private static File getExternalFilesDir(Context context){
return context.getExternalFilesDir(null);
}
public static InputStream getInputStream(Context context, File file, long size) throws IOException{
try{
String where=MediaStore.MediaColumns.DATA+"=?";
String[] selectionArgs=new String[]{
file.getAbsolutePath()
};
ContentResolver contentResolver=context.getContentResolver();
Uri fileUri=MediaStore.Files.getContentUri("external");
contentResolver.delete(fileUri,where,selectionArgs);
ContentValues values=new ContentValues();
values.put(MediaStore.MediaColumns.DATA,file.getAbsolutePath());
values.put(MediaStore.MediaColumns.SIZE,size);
Uri uri=contentResolver.insert(fileUri,values);
if (uri == null) {
// Should not occur.
throw new IOException("Internal error.");
}
return contentResolver.openInputStream(uri);
}catch(FileNotFoundException e){
return null;
}
}
public static OutputStream getOutputStream(String path, Context context) throws IOException{
ContentResolver resolver=context.getContentResolver();
File file=new File(path);
if (file.exists() && file.isDirectory()) {
throw new IOException("File exists and is a directory.");
}
// Delete any existing entry from the media database.
// This may also delete the file (for media types), but that is irrelevant as it will be truncated momentarily in any case.
String where = MediaStore.MediaColumns.DATA + "=?";
String[] selectionArgs = new String[] { file.getAbsolutePath() };
Uri filesUri = MediaStore.Files.getContentUri("external");
resolver.delete(filesUri, where, selectionArgs);
ContentValues values = new ContentValues();
values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath());
Uri uri = resolver.insert(filesUri, values);
if (uri == null) {
// Should not occur.
throw new IOException("Internal error.");
}
return resolver.openOutputStream(uri);
}
/**
* Returns an OutputStream to write to the file. The file will be truncated immediately.
*/
private static int getTemporaryAlbumId(Context context){
File temporaryTrack;
try{
temporaryTrack=installTemporaryTrack(context);
}catch(IOException ex){
return 0;
}
Uri fileUri=MediaStore.Files.getContentUri("external");
String[] selectionArgs={
temporaryTrack.getAbsolutePath()
};
ContentResolver contentResolver=context.getContentResolver();
Cursor cursor=contentResolver.query(fileUri,ALBUM_PROJECTION,
MediaStore.MediaColumns.DATA+" =?",selectionArgs,null);
if(cursor==null||!cursor.moveToFirst()){
if(cursor!=null){
cursor.close();
}
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(fileUri,values);
}
cursor=contentResolver.query(fileUri,ALBUM_PROJECTION,MediaStore.MediaColumns.DATA+" =?",selectionArgs,null);
if(cursor==null){
return 0;
}
if(!cursor.moveToNext()){
cursor.close();
return 0;
}
int id=cursor.getInt(0);
int albumId=cursor.getInt(1);
int mediaType=cursor.getInt(2);
cursor.close();
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(fileUri,values,BaseColumns._ID+"="+id,null);
}
cursor=contentResolver.query(fileUri,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(Context context) throws IOException{
File externalFilesDir=getExternalFilesDir(context);
if(externalFilesDir==null){
return null;
}
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);
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(Context context, File file) throws IOException {
if (file.exists()) {
return file.isDirectory();
}
File tmpFile = new File(file, ".MediaWriteTemp");
int albumId = getTemporaryAlbumId(context);
if (albumId == 0) {
throw new IOException("Failed to create temporary album id.");
}
Uri albumUri = Uri.parse(String.format(Locale.US, ALBUM_ART_URI + "/%d", albumId));
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, tmpFile.getAbsolutePath());
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 {
ParcelFileDescriptor fd = contentResolver.openFileDescriptor(albumUri, "r");
fd.close();
}catch(Exception e){
Log.d("Operation On Kitkat","Permission Denial.");
}finally{
delete(context,tmpFile);
}
return file.exists();
}
}
复制功能:
public static String copyFile(Context context, String src, String dest){
File file = new File(src);
if (!file.exists() || file.isDirectory()) {
Log.v(LOG_TAG, "copyFile: file not exist or is directory, " + src);
return null;
}
InputStream input = null;
OutputStream output = null;
try {
String destPath = Util.makePath(dest, file.getName());
File destFile = new File(destPath);
int i = 1;
while (destFile.exists()) {
String destName = Util.getNameFromFilename(file.getName()) + " " + i++ + "."
+ Util.getExtFromFilename(file.getName());
destPath = Util.makePath(dest, destName);
destFile = new File(destPath);
}
input=MediaStoreHackOnKitkat.getInputStream(context,file,file.length());
output = MediaStoreHackOnKitkat.getOutputStream(destPath, context);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
}catch(IOException e){
Log.e(LOG_TAG, "copyFile: " + e.toString());
}finally{
try {
if (input != null)
input.close();
if (output != null)
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
目前问题是:三星会有安全异常(Security Exception)。
三星和小米有Security Exception解决方法就是只能ROOT。