0

Problem: I'm using an intent to allow the user to select photos. When they select the photos from images on the device, I am able to get the latitude and longitude using an ExifInterface. However when they choose photos from Google Photos I'm not able to get the geolocation from the uri returned.

Details: The intent I'm using looks like this:

Intent intent = new Intent();
    // Show only images, no videos or anything else
    intent.setType("image/*");
    intent.setAction(Intent.ACTION_GET_CONTENT);
    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
    // Always show the chooser (if there are multiple options available)
    startActivityForResult(Intent.createChooser(intent, "Select Pictures"), PICK_IMAGES_REQUEST);

When the user selects a photo from Google Photos that is not stored on the device, Google Photos first downloads the photo and returns a URI which does not include a location on the device. I'm using this to write the stream to a local file to get the photo. I then try to use a ContentResolver to get date taken, latitude and longitude from the stream like so:

Cursor cursor = context.getContentResolver().query(uri,
            new String[] {
                    MediaStore.Images.ImageColumns.DATE_TAKEN,
                    MediaStore.Images.ImageColumns.LATITUDE,
                    MediaStore.Images.ImageColumns.LONGITUDE
            }, null, null, null);

    if (null != cursor) {
        if (cursor.moveToFirst()) {
            int dateColumn = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);
            photoItem.date = new Date(cursor.getLong(dateColumn));

            int latitudeColumn = cursor.getColumnIndex(MediaStore.Images.ImageColumns.LATITUDE);
            double latitude = cursor.getDouble(latitudeColumn);

            int longitudeColumn = cursor.getColumnIndex(MediaStore.Images.ImageColumns.LONGITUDE);
            double longitude = cursor.getDouble(longitudeColumn);
            photoItem.photoGeoPoint = new LatLng(latitude, longitude);
        }
        cursor.close();
    }

This works for date taken. However latitude and longitude are always 0. I have verified that the photos I am trying this with have geo locations embedded in the exif. Any ideas?

--EDIT--

So using @CommonsWare's advice I updated my code to write directly from the stream to a file without converting it to a bitmap first. The code looks like this (where in is the InputStream from the Google Photos contentResolver):

try {
        File outputDir = AppState.getInstance().getCacheDir();
        File outputFile = File.createTempFile("tempImageFile", ".jpg", outputDir);
        OutputStream out = new FileOutputStream(outputFile);
        byte[] buf = new byte[1024];
        int len;
        while((len=in.read(buf))>0){
            out.write(buf,0,len);
        }
        out.close();
        in.close();
        ExifInterface exif = new ExifInterface(outputFile.getPath());
        Logger.d(LOG_TAG, "lat is: " + exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
        Logger.d(LOG_TAG, "lon is: " + exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
    } catch (Exception e) {
        e.printStackTrace();
    }

However Latitude and Longitude are still null (again, I've verified that in the photo the location data exists). The only values in the ExifInterface are LightSource = 0, Orientation = 1, ImageLength = 3264, MeteringMode = -1 and ImageWidth = 2448.

Community
  • 1
  • 1
odiggity
  • 1,496
  • 16
  • 29

1 Answers1

0

I'm using this to write the stream to a local file to get the photo.

Heavens, what awful code.

If you want to make a local file copy of content:

  1. Call openInputStream() on a ContentResolver, passing in the Uri that you got from Google Photos or wherever

  2. Get yourself an OutputStream on where you want to put your file

  3. Use ordinary Java I/O to copy from the InputStream to the OutputStream

Not only is this much faster and much less memory-intensive, but for your purposes, it retains the EXIF headers. You can then use ExifInterface to access those.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 1
    Not sure what the awful code part you're referring to is, could you elaborate? In the answer to the question in the link I provided they are doing what you suggest. Calling openInputStream() and using a ByteArrayOutputStream to write to file. I have also tried creating an ExifInterface from the file created but that does not have the geo location either. – odiggity May 11 '16 at 23:48
  • @odiggity: I was referring to the code that you linked to. They are not doing what I suggest. They are decoding the content to a `Bitmap`. They are then re-encoding the `Bitmap`. I did not tell you to do these things in my answer. – CommonsWare May 12 '16 at 00:48
  • @odiggity: Specifically with respect to your problem, a `Bitmap` does not have EXIF headers. Those are largely ignored by the decode process. The resulting encoded JPEG therefore does not have the original's headers. If, instead, you just copy the bytes, using ordinary I/O, the JPEG you wind up with has everything the original had, including the headers. Do not decode the image. Just copy the bytes. – CommonsWare May 12 '16 at 01:00
  • ah that makes sense, thank you @CommonsWare. Tried that though and still not getting Lat and Lon. I'm not even getting the date taken using this approach (but I was querying the ContentResolver for it)... Any other ideas? – odiggity May 13 '16 at 21:59
  • @odiggity: Compare your files. If your copy is a byte-for-byte match with the original, then your analysis is flawed. Either the original does not have the data that you think it does, or the copy does have the data that you think it does. If your copy is *not* a byte-for-byte match, then Google Photos must be messing with the files it serves up (privacy?), in which case I don't know if you have many options. – CommonsWare May 13 '16 at 22:03