OSDN Git Service

Select content uris vs file uris when open or send files
authorJorge Ruesga <jorge@ruesga.com>
Fri, 15 Nov 2013 00:41:32 +0000 (01:41 +0100)
committerJorge Ruesga <jorge@ruesga.com>
Fri, 15 Nov 2013 22:50:31 +0000 (23:50 +0100)
Change-Id: Ie0773d3b1922992dccb90b29cd136eff3397dfee
JIRA: CYAN-2649
Issue: https://jira.cyanogenmod.org/browse/CYAN-2649
Signed-off-by: Jorge Ruesga <jorge@ruesga.com>
src/com/cyanogenmod/filemanager/activities/EditorActivity.java
src/com/cyanogenmod/filemanager/activities/PickerActivity.java
src/com/cyanogenmod/filemanager/ui/policy/IntentsActionPolicy.java
src/com/cyanogenmod/filemanager/util/MediaHelper.java

index 2a6b9d8..68f91f3 100644 (file)
@@ -28,6 +28,7 @@ import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
@@ -78,6 +79,7 @@ import com.cyanogenmod.filemanager.util.DialogHelper;
 import com.cyanogenmod.filemanager.util.ExceptionUtil;
 import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult;
 import com.cyanogenmod.filemanager.util.FileHelper;
+import com.cyanogenmod.filemanager.util.MediaHelper;
 import com.cyanogenmod.filemanager.util.ResourcesHelper;
 
 import java.io.ByteArrayInputStream;
@@ -907,7 +909,7 @@ public class EditorActivity extends Activity implements TextWatcher {
         this.mReadOnly = false;
 
         // Read the intent and check that is has a valid request
-        String path = getIntent().getData().getPath();
+        String path = uriToPath(this, getIntent().getData());
         if (path == null || path.length() == 0) {
             DialogHelper.showToast(
                     this, R.string.editor_invalid_file_msg, Toast.LENGTH_SHORT);
@@ -1509,4 +1511,18 @@ public class EditorActivity extends Activity implements TextWatcher {
         theme.setTextColor(this, editor, "text_color"); //$NON-NLS-1$
     }
 
+    /**
+     * Method that resolves the content uri to a valid system path
+     *
+     * @param ctx The current context
+     * @param uri The content uri
+     * @return String The system path
+     */
+    private static String uriToPath(Context ctx, Uri uri) {
+        File file = MediaHelper.contentUriToFile(ctx.getContentResolver(), uri);
+        if (file == null) {
+            file = new File(uri.getPath());
+        }
+        return file.getAbsolutePath();
+    }
 }
index f548b41..fdf5099 100644 (file)
@@ -21,6 +21,7 @@ import android.app.AlertDialog;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnCancelListener;
@@ -60,6 +61,7 @@ import com.cyanogenmod.filemanager.ui.widgets.NavigationView.OnFilePickedListene
 import com.cyanogenmod.filemanager.util.DialogHelper;
 import com.cyanogenmod.filemanager.util.ExceptionUtil;
 import com.cyanogenmod.filemanager.util.FileHelper;
+import com.cyanogenmod.filemanager.util.MediaHelper;
 import com.cyanogenmod.filemanager.util.MimeTypeHelper;
 import com.cyanogenmod.filemanager.util.StorageHelper;
 
@@ -428,7 +430,7 @@ public class PickerActivity extends Activity
             // Return the picked file, as expected (this activity should fill the intent data
             // and return RESULT_OK result)
             Intent result = new Intent();
-            result.setData(getResultUriForFileFromIntent(src, getIntent()));
+            result.setData(getResultUriForFileFromIntent(getContentResolver(), src, getIntent()));
             setResult(Activity.RESULT_OK, result);
             finish();
 
@@ -490,8 +492,12 @@ public class PickerActivity extends Activity
         return file.getParentFile();
     }
 
-    private static Uri getResultUriForFileFromIntent(File src, Intent intent) {
-        Uri result = Uri.fromFile(src);
+    private static Uri getResultUriForFileFromIntent(ContentResolver cr, File src, Intent intent) {
+        // Try to find the preferred uri scheme
+        Uri result = MediaHelper.fileToContentUri(cr, src);
+        if (result == null) {
+            result = Uri.fromFile(src);
+        }
 
         if (Intent.ACTION_PICK.equals(intent.getAction()) && intent.getData() != null) {
             String scheme = intent.getData().getScheme();
index 4d56b37..4b9ec42 100644 (file)
@@ -17,6 +17,7 @@
 package com.cyanogenmod.filemanager.ui.policy;
 
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface.OnCancelListener;
 import android.content.DialogInterface.OnDismissListener;
@@ -37,6 +38,7 @@ import com.cyanogenmod.filemanager.ui.dialogs.AssociationsDialog;
 import com.cyanogenmod.filemanager.util.DialogHelper;
 import com.cyanogenmod.filemanager.util.ExceptionUtil;
 import com.cyanogenmod.filemanager.util.FileHelper;
+import com.cyanogenmod.filemanager.util.MediaHelper;
 import com.cyanogenmod.filemanager.util.MimeTypeHelper;
 import com.cyanogenmod.filemanager.util.MimeTypeHelper.MimeTypeCategory;
 import com.cyanogenmod.filemanager.util.ResourcesHelper;
@@ -99,9 +101,9 @@ public final class IntentsActionPolicy extends ActionsPolicy {
             String mime = MimeTypeHelper.getMimeType(ctx, fso);
             File file = new File(fso.getFullPath());
             if (mime != null) {
-                intent.setDataAndType(Uri.fromFile(file), mime);
+                intent.setDataAndType(getUriFromFile(ctx, file), mime);
             } else {
-                intent.setData(Uri.fromFile(file));
+                intent.setData(getUriFromFile(ctx, file));
             }
 
             // Resolve the intent
@@ -138,7 +140,7 @@ public final class IntentsActionPolicy extends ActionsPolicy {
             intent.setAction(android.content.Intent.ACTION_SEND);
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
             intent.setType(MimeTypeHelper.getMimeType(ctx, fso));
-            Uri uri = Uri.fromFile(new File(fso.getFullPath()));
+            Uri uri = getUriFromFile(ctx, new File(fso.getFullPath()));
             intent.putExtra(Intent.EXTRA_STREAM, uri);
 
             // Resolve the intent
@@ -199,7 +201,7 @@ public final class IntentsActionPolicy extends ActionsPolicy {
                 lastMimeType = mimeType;
 
                 // Add the uri
-                uris.add(Uri.fromFile(new File(fso.getFullPath())));
+                uris.add(getUriFromFile(ctx, new File(fso.getFullPath())));
             }
             if (sameMimeType) {
                 intent.setType(lastMimeType);
@@ -571,4 +573,19 @@ public final class IntentsActionPolicy extends ActionsPolicy {
         });
         return pref.get(0);
     }
+
+    /**
+     * Method that returns the best Uri for the file (content uri, file uri, ...)
+     *
+     * @param ctx The current context
+     * @param file The file to resolve
+     */
+    private static Uri getUriFromFile(Context ctx, File file) {
+        ContentResolver cr = ctx.getContentResolver();
+        Uri uri = MediaHelper.fileToContentUri(cr, file);
+        if (uri == null) {
+            uri = Uri.fromFile(file);
+        }
+        return uri;
+    }
 }
index db818db..bb6a9c6 100644 (file)
@@ -22,6 +22,7 @@ import android.net.Uri;
 import android.os.UserHandle;
 import android.provider.BaseColumns;
 import android.provider.MediaStore;
+import android.provider.MediaStore.MediaColumns;
 import android.text.TextUtils;
 
 import java.io.File;
@@ -33,6 +34,13 @@ import java.util.Map;
  */
 public final class MediaHelper {
 
+    private static final String EMULATED_STORAGE_SOURCE = System.getenv("EMULATED_STORAGE_SOURCE");
+    private static final String EMULATED_STORAGE_TARGET = System.getenv("EMULATED_STORAGE_TARGET");
+    private static final String EXTERNAL_STORAGE = System.getenv("EXTERNAL_STORAGE");
+
+    private static final String INTERNAL_VOLUME = "internal";
+    private static final String EXTERNAL_VOLUME = "external";
+
     /**
      * URIs that are relevant for determining album art;
      * useful for content observer registration
@@ -98,9 +106,117 @@ public final class MediaHelper {
         return null;
     }
 
-    private static final String EMULATED_STORAGE_SOURCE = System.getenv("EMULATED_STORAGE_SOURCE");
-    private static final String EMULATED_STORAGE_TARGET = System.getenv("EMULATED_STORAGE_TARGET");
-    private static final String EXTERNAL_STORAGE = System.getenv("EXTERNAL_STORAGE");
+    /**
+     * Method that converts a file reference to a content uri reference
+     *
+     * @param cr A content resolver
+     * @param file The file reference
+     * @return Uri The content uri or null if file not exists in the media database
+     */
+    public static Uri fileToContentUri(ContentResolver cr, File file) {
+        // Normalize the path to ensure media search
+        final String normalizedPath = normalizeMediaPath(file.getAbsolutePath());
+
+        // Check in external and internal storages
+        Uri uri = fileToContentUri(cr, normalizedPath, EXTERNAL_VOLUME);
+        if (uri != null) {
+            return uri;
+        }
+        uri = fileToContentUri(cr, normalizedPath, INTERNAL_VOLUME);
+        if (uri != null) {
+            return uri;
+        }
+        return null;
+    }
+
+    /**
+     * Method that converts a file reference to a content uri reference
+     *
+     * @param cr A content resolver
+     * @param path The path to search
+     * @param volume The volume
+     * @return Uri The content uri or null if file not exists in the media database
+     */
+    private static Uri fileToContentUri(ContentResolver cr, String path, String volume) {
+        final String[] projection = {BaseColumns._ID, MediaStore.Files.FileColumns.MEDIA_TYPE};
+        final String where = MediaColumns.DATA + " = ?";
+        Uri baseUri = MediaStore.Files.getContentUri(volume);
+        Cursor c = cr.query(baseUri, projection, where, new String[]{path}, null);
+        try {
+            if (c != null && c.moveToNext()) {
+                int type = c.getInt(c.getColumnIndexOrThrow(
+                        MediaStore.Files.FileColumns.MEDIA_TYPE));
+                if (type != 0) {
+                    // Do not force to use content uri for no media files
+                    long id = c.getLong(c.getColumnIndexOrThrow(BaseColumns._ID));
+                    return Uri.withAppendedPath(baseUri, String.valueOf(id));
+                }
+            }
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Method that converts a content uri to a file system path
+     *
+     * @param cr The content resolver
+     * @param uri The content uri
+     * @return File The file reference
+     */
+    public static File contentUriToFile(ContentResolver cr, Uri uri) {
+        // Sanity checks
+        if (uri == null || uri.getScheme() == null || uri.getScheme().compareTo("content") != 0) {
+            return null;
+        }
+
+        // Retrieve the request id
+        long id = 0;
+        try {
+            id = Long.parseLong(new File(uri.getPath()).getName());
+        } catch (NumberFormatException nfex) {
+            return null;
+        }
+
+        // Check in external and internal storages
+        File file = mediaIdToFile(cr, id, EXTERNAL_VOLUME);
+        if (file != null) {
+            return file;
+        }
+        file = mediaIdToFile(cr, id, INTERNAL_VOLUME);
+        if (file != null) {
+            return file;
+        }
+        return null;
+    }
+
+    /**
+     * Method that converts a content uri to a file system path
+     *
+     * @param cr The content resolver
+     * @param id The media database id
+     * @param volume The volume
+     * @return File The file reference
+     */
+    private static File mediaIdToFile(ContentResolver cr, long id, String volume) {
+        final String[] projection = {MediaColumns.DATA};
+        final String where = MediaColumns._ID + " = ?";
+        Uri baseUri = MediaStore.Files.getContentUri(volume);
+        Cursor c = cr.query(baseUri, projection, where, new String[]{String.valueOf(id)}, null);
+        try {
+            if (c != null && c.moveToNext()) {
+                return new File(c.getString(c.getColumnIndexOrThrow(MediaColumns.DATA)));
+            }
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        return null;
+    }
 
     /**
      * Method that converts a not standard media mount path to a standard media path