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