title: A final answer on how to get Exif data from URI
body: <p>This topic has been discussed in lots of questions here, with mostly different results and, due to API changes and different types of URIs, <strong>no definitive answer</strong>.</p>
  
  <p>I don’t have an answer myself, but let’s talk about it.
  The <code>ExifInterface</code> has a single constructor that accepts a <code>filePath</code>. That itself is annoying, as it is discouraged now to rely on paths - you should rather use <code>Uri</code>s and <code>ContentResolver</code>. OK.</p>
  
  <p>Our <code>Uri</code> named <code>uri</code> can be retrieved from the intent in <code>onActivityResult</code> (if you pick the picture from gallery with <code>ACTION_GET_CONTENT</code>) or can be an <code>Uri</code> that we previously had (if you pick the picture from camera and call <code>intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)</code>).</p>
  
  <h2>API&lt;19</h2>
  
  <p>Our <code>uri</code> can have two different schemas:</p>
  
  <ul>
  <li>Uris coming from cameras will mostly have a <code>file://</code> schema. Those are pretty easy to treat, because they hold the path. You can call <code>new ExifInterface(uri.getPath())</code> and you are done.</li>
  <li>Uris coming from gallery or other content providers usually have a <code>content://</code> interface. I personally don’t know what that is about, but is driving me mad.</li>
  </ul>
  
  <p>This second case, as far as I understand, should be treated with a <code>ContentResolver</code> that you can get with <code>Context.getContentResolver()</code>. The following <strong>works</strong> with all apps I have tested, in any case:</p>
  
  <pre><code>public static ExifInterface getPictureData(Context context, Uri uri) {
      String[] uriParts = uri.toString().split(":");
      String path = null;
  
      if (uriParts[0].equals("content")) {
          // we can use ContentResolver.
          // let’s query the DATA column which holds the path
          String col = MediaStore.Images.ImageColumns.DATA;
          Cursor c = context.getContentResolver().query(uri,
                  new String[]{col},
                  null, null, null);
  
          if (c != null &amp;&amp; c.moveToFirst()) {
              path = c.getString(c.getColumnIndex(col));
              c.close();
              return new ExifInterface(path);
          }
  
      } else if (uriParts[0].equals("file")) {
          // it's easy to get the path
          path = uri.getEncodedPath();
          return new ExifInterface(path);
      }
      return null;
  }
  </code></pre>
  
  <h2>API19+</h2>
  
  <p>My issues arise from Kitkat onward with <code>content://</code> URIs. Kitkat introduces the <code>Storage Access Framework</code> (see <a href="http://developer.android.com/guide/topics/providers/document-provider.html" rel="noreferrer">here</a>) along with a new intent, <code>ACTION_OPEN_DOCUMENT</code>, and a platform picker. However, it is said that</p>
  
  <blockquote>
    <p>On Android 4.4 and higher, you have the additional option of using the
    ACTION_OPEN_DOCUMENT intent, which displays a picker UI controlled by
    the system that allows the user to browse all files that other apps
    have made available. From this single UI, the user can pick a file
    from any of the supported apps.</p>
    
    <p>ACTION_OPEN_DOCUMENT is not intended to be a replacement for
    ACTION_GET_CONTENT. The one you should use depends on the needs of
    your app.</p>
  </blockquote>
  
  <p>So to keeps this very simple, let’s say that we are ok with the old <code>ACTION_GET_CONTENT</code>: it will fire a chooser dialog where you can choose a gallery app.</p>
  
  <p>However, the content approach doesn’t work anymore. Sometimes it works on Kitkat, but <strong>never works</strong> on Lollipop, for example. I don’t know what exactly has changed.</p>
  
  <p>I have searched and tried a lot; another approach taken for Kitkat specifically is:</p>
  
  <pre><code>String wholeId = DocumentsContract.getDocumentId(uri);
  String[] parts = wholeId.split(“:”);
  String numberId = parts[1];
  
  Cursor c = context.getContentResolver().query(
      // why external and not internal ?
      MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
      new String[]{ col },
      MediaStore.Images.Media._ID + “=?”,
      new String[]{ numberId },
      null);
  </code></pre>
  
  <p>This works sometimes, but others not. Specifically, it works when <code>wholeId</code> is something like <code>image:2839</code>, but obviously breaks when <code>wholeId</code> is simply a number.</p>
  
  <p>You can try this using the system picker (i.e. firing the gallery with <code>ACTION_OPEN_DOCUMENT</code>): if you choose an image from “Recents”, it works; if you choose an image from “Downloads”, it breaks.</p>
  
  <h2>So how to?!</h2>
  
  <p>The immediate answer is <strong>You don’t</strong>, you don’t find file paths from content uris in newer version of the OS. It could be said that not all content uris point to pictures or even files.</p>
  
  <p>That’s totally OK for me, and at first I worked to avoid this. But then, <strong>How are we supposed to use the ExifInterface class if we should not use paths?</strong> </p>
  
  <p>I don’t understand how modern apps do this - finding orientation and metadata is an issue you immediately face, and <code>ContentResolver</code> does not offer any API in that sense. You have <code>ContentResolver.openFileDescriptor()</code> and similar stuff, but no APIs to read metadata (which truly is in that file). There might be external libraries that read <code>Exif</code> stuff from a stream, but I’m wondering about the common/platform way to solve this.</p>
  
  <p>I have searched for similar code in google’s open source apps, but found nothing.</p>