1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.documentsui.inspector; 17 18 import static androidx.core.util.Preconditions.checkNotNull; 19 20 import android.media.ExifInterface; 21 import android.os.Bundle; 22 import android.provider.DocumentsContract; 23 24 import com.android.documentsui.base.Shared; 25 26 import javax.annotation.Nullable; 27 28 final class MetadataUtils { 29 MetadataUtils()30 private MetadataUtils() {} 31 hasGeoCoordinates(@ullable Bundle metadata)32 static boolean hasGeoCoordinates(@Nullable Bundle metadata) { 33 if (metadata == null) { 34 return false; 35 } 36 return hasVideoCoordinates(metadata.getBundle(Shared.METADATA_KEY_VIDEO)) 37 || hasExifGpsFields(metadata.getBundle(DocumentsContract.METADATA_EXIF)); 38 } 39 hasVideoCoordinates(@ullable Bundle data)40 static boolean hasVideoCoordinates(@Nullable Bundle data) { 41 return data != null && (data.containsKey(Shared.METADATA_VIDEO_LATITUDE) 42 && data.containsKey(Shared.METADATA_VIDEO_LONGITUTE)); 43 } 44 hasExifGpsFields(@ullable Bundle exif)45 static boolean hasExifGpsFields(@Nullable Bundle exif) { 46 return exif != null && (exif.containsKey(ExifInterface.TAG_GPS_LATITUDE) 47 && exif.containsKey(ExifInterface.TAG_GPS_LONGITUDE) 48 && exif.containsKey(ExifInterface.TAG_GPS_LATITUDE_REF) 49 && exif.containsKey(ExifInterface.TAG_GPS_LONGITUDE_REF)); 50 } 51 getGeoCoordinates(Bundle metadata)52 static float[] getGeoCoordinates(Bundle metadata) { 53 assert hasGeoCoordinates(metadata); 54 checkNotNull(metadata); 55 56 Bundle bundle = metadata.getBundle(DocumentsContract.METADATA_EXIF); 57 if (hasExifGpsFields(bundle)) { 58 return getExifGpsCoords(bundle); 59 } 60 61 bundle = metadata.getBundle(Shared.METADATA_KEY_VIDEO); 62 if (hasVideoCoordinates(bundle)) { 63 return getVideoCoords(bundle); 64 } 65 66 // This should never happen, because callers should always check w/ hasGeoCoordinates first. 67 throw new IllegalArgumentException("Invalid metadata bundle: " + metadata); 68 } 69 getExifGpsCoords(Bundle exif)70 static float[] getExifGpsCoords(Bundle exif) { 71 assert hasExifGpsFields(exif); 72 73 String lat = exif.getString(ExifInterface.TAG_GPS_LATITUDE); 74 String lon = exif.getString(ExifInterface.TAG_GPS_LONGITUDE); 75 String latRef = exif.getString(ExifInterface.TAG_GPS_LATITUDE_REF); 76 String lonRef = exif.getString(ExifInterface.TAG_GPS_LONGITUDE_REF); 77 78 return new float[] { 79 convertRationalLatLonToFloat(lat, latRef), convertRationalLatLonToFloat(lon, lonRef) 80 }; 81 } 82 getVideoCoords(Bundle data)83 static float[] getVideoCoords(Bundle data) { 84 assert hasVideoCoordinates(data); 85 return new float[] { 86 data.getFloat(Shared.METADATA_VIDEO_LATITUDE), 87 data.getFloat(Shared.METADATA_VIDEO_LONGITUTE) 88 }; 89 } 90 91 /** This founction is copied from {@link ExifInterface} */ convertRationalLatLonToFloat(String rationalString, String ref)92 private static float convertRationalLatLonToFloat(String rationalString, String ref) { 93 try { 94 String [] parts = rationalString.split(","); 95 96 String [] pair; 97 pair = parts[0].split("/"); 98 double degrees = Double.parseDouble(pair[0].trim()) 99 / Double.parseDouble(pair[1].trim()); 100 101 pair = parts[1].split("/"); 102 double minutes = Double.parseDouble(pair[0].trim()) 103 / Double.parseDouble(pair[1].trim()); 104 105 pair = parts[2].split("/"); 106 double seconds = Double.parseDouble(pair[0].trim()) 107 / Double.parseDouble(pair[1].trim()); 108 109 double result = degrees + (minutes / 60.0) + (seconds / 3600.0); 110 if ((ref.equals("S") || ref.equals("W"))) { 111 return (float) -result; 112 } 113 return (float) result; 114 } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { 115 // Not valid 116 throw new IllegalArgumentException(); 117 } 118 } 119 } 120