Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6493890e authored by Steve McKay's avatar Steve McKay
Browse files

Support for video lat/long.

Also:
- Replaced numeric rounding of coordinates with use of a format string in UI.
  ...which happens to move the layout of coords into a localizable string :).

Bug: 64267500
Test: Manual
Change-Id: Idf06a44611dc2cc1c78f596f0c252a08b0fac921
parent 7e4b4c5f
Loading
Loading
Loading
Loading
+34 −32
Original line number Diff line number Diff line
@@ -43,19 +43,21 @@
    <!-- File properties dialog user message if the default app is unknown-->
    <string name="handler_app_unknown">Unknown</string>

    <!--The height and width of a photo. Note that this is probably camera EXIF data.-->
    <!-- The height and width of a photo. -->
    <string name="metadata_dimensions">Dimensions</string>
    <!--The message that displays the width and height of a photo followed by a the
     "megapixel rating" this corresponds to. Cameras are frequently rated by "megapixels".
     In U.S. English this is denoted by a number followed by "MP". E.g. 12.2MP or 8MP. -->
    <string name="metadata_dimensions_display"><xliff:g id="width" example="1280">%1$d</xliff:g> x <xliff:g id="height" example="1024">%2$d</xliff:g> - <xliff:g id="megapixels" example="12.2">%3$,.1f</xliff:g>MP</string>
    <!--The location of where a photo was taken. (i.e. latitude, longitude) Note that this is probably camera EXIF data.-->
    <!-- The width and height of a photo, followed by a the "megapixel rating".
         Cameras are frequently rated by "megapixels". In U.S. English the "megapixels" rating
         of a camera is denoted by a number followed by "MP". E.g. 12.2MP or 8MP. -->
    <string name="metadata_dimensions_format"><xliff:g id="width" example="1280">%1$d</xliff:g> x <xliff:g id="height" example="1024">%2$d</xliff:g> - <xliff:g id="megapixels" example="12.2">%3$,.1f</xliff:g>MP</string>
    <!-- The location of where a photo was taken. -->
    <string name="metadata_coordinates">Coordinates</string>
    <!--The elevation the photo was taken. Note that this is probably camera EXIF data.-->
    <!-- The location a photo was taken in the form of latitude/longitude coordinates. -->
    <string name="metadata_coordinates_format"><xliff:g id="latitude" example="33.996">%1$,.3f</xliff:g>, <xliff:g id="longitude" example="-118.476">%2$,.3f</xliff:g></string>
    <!-- The elevation a photo was taken. -->
    <string name="metadata_altitude">Altitude</string>
    <!--The company that made the camera the photo was taken on. Note that this is probably camera EXIF data.-->
    <!-- The company that made the camera the photo was taken on. -->
    <string name="metadata_make">Make</string>
    <!--The camera model that the photo was taken on. Note that this is probably camera EXIF data.-->
    <!-- The camera model that the photo was taken on. -->
    <string name="metadata_model">Model</string>
    <!-- The value of a photos aperture. Note that this is probably camera EXIF data.-->
    <string name="metadata_aperture">Aperture</string>
+2 −2
Original line number Diff line number Diff line
@@ -39,8 +39,6 @@ import android.view.WindowManager;
import com.android.documentsui.R;
import com.android.documentsui.ui.MessageBuilder;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.Collator;
import java.util.ArrayList;
import java.util.List;
@@ -62,6 +60,8 @@ public final class Shared {
    // These values track values declared in MediaDocumentsProvider.
    public static final String METADATA_KEY_AUDIO = "android.media.metadata.audio";
    public static final String METADATA_KEY_VIDEO = "android.media.metadata.video";
    public static final String METADATA_VIDEO_LATITUDE = "android.media.metadata.video:latitude";
    public static final String METADATA_VIDEO_LONGITUTE = "android.media.metadata.video:longitude";

    /**
     * Extra boolean flag for {@link #ACTION_PICK_COPY_DESTINATION}, which
+6 −6
Original line number Diff line number Diff line
@@ -207,10 +207,9 @@ public final class InspectorController {
            return;
        }

        Bundle exif = metadata.getBundle(DocumentsContract.METADATA_EXIF);
        Runnable geoClickListener = null;
        if (exif != null && MetadataUtils.hasExifGpsFields(exif)) {
            double[] coords = MetadataUtils.getExifGpsCoords(exif);
        if (MetadataUtils.hasGeoCoordinates(metadata)) {
            float[] coords = MetadataUtils.getGeoCoordinates(metadata);
            final Intent intent = createGeoIntent(coords[0], coords[1], doc.displayName);
            if (hasHandler(intent)) {
                geoClickListener = () -> {
@@ -252,9 +251,10 @@ public final class InspectorController {
     *
     * @see https://developer.android.com/guide/components/intents-common.html#Maps
     */
    private static Intent createGeoIntent(double latitude, double longitude,
            @Nullable String label) {
        String data = "geo:0,0?q=" + latitude + " " + longitude + "(" + Uri.encode(label) + ")";
    private static Intent createGeoIntent(
            float latitude, float longitude, @Nullable String label) {
        label = Uri.encode(label == null ? "" : label);
        String data = "geo:0,0?q=" + latitude + " " + longitude + "(" + label + ")";
        Uri uri = Uri.parse(data);
        return new Intent(Intent.ACTION_VIEW, uri);
    }
+35 −15
Original line number Diff line number Diff line
@@ -66,7 +66,7 @@ public class MediaView extends TableView implements MediaDisplay {

        Bundle video = metadata.getBundle(Shared.METADATA_KEY_VIDEO);
        if (video != null) {
            showVideoData(this, mResources, doc, video);
            showVideoData(this, mResources, doc, video, geoClickListener);
        }

        setVisible(!isEmpty());
@@ -74,10 +74,19 @@ public class MediaView extends TableView implements MediaDisplay {

    @VisibleForTesting
    public static void showVideoData(
            TableDisplay table, Resources resources, DocumentInfo doc, Bundle tags) {
            TableDisplay table,
            Resources resources,
            DocumentInfo doc,
            Bundle tags,
            @Nullable Runnable geoClickListener) {

        addDimensionsRow(table, resources, tags);

        if (MetadataUtils.hasVideoCoordinates(tags)) {
            float[] coords = MetadataUtils.getVideoCoords(tags);
            showCoordiantes(table, resources, coords, geoClickListener);
        }

        if (tags.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
            int millis = tags.getInt(MediaMetadata.METADATA_KEY_DURATION);
            table.put(R.string.metadata_duration, DateUtils.formatElapsedTime(millis / 1000));
@@ -100,18 +109,8 @@ public class MediaView extends TableView implements MediaDisplay {
        }

        if (MetadataUtils.hasExifGpsFields(tags)) {
            double[] coords = MetadataUtils.getExifGpsCoords(tags);
            if (geoClickListener != null) {
                table.put(R.string.metadata_coordinates,
                        String.valueOf(coords[0]) + ",  " + String.valueOf(coords[1]),
                        view -> {
                            geoClickListener.run();
                        }
                );
            } else {
                table.put(R.string.metadata_coordinates,
                        String.valueOf(coords[0]) + ",  " + String.valueOf(coords[1]));
            }
            float[] coords = MetadataUtils.getExifGpsCoords(tags);
            showCoordiantes(table, resources, coords, geoClickListener);
        }

        if (tags.containsKey(ExifInterface.TAG_GPS_ALTITUDE)) {
@@ -141,6 +140,27 @@ public class MediaView extends TableView implements MediaDisplay {
        }
    }

    private static void showCoordiantes(
            TableDisplay table,
            Resources resources,
            float[] coords,
            @Nullable Runnable geoClickListener) {

        String value = resources.getString(
                R.string.metadata_coordinates_format, coords[0], coords[1]);
        if (geoClickListener != null) {
            table.put(
                    R.string.metadata_coordinates,
                    value,
                    view -> {
                        geoClickListener.run();
                    }
            );
        } else {
            table.put(R.string.metadata_coordinates, value);
        }
    }

    /**
     * @param speed a value n, where shutter speed equals 1/(2^n)
     * @return a String containing either a fraction that displays 1 over a positive integer, or a
@@ -172,7 +192,7 @@ public class MediaView extends TableView implements MediaDisplay {
            float megaPixels = height * width / 1000000f;
            table.put(R.string.metadata_dimensions,
                    resources.getString(
                            R.string.metadata_dimensions_display, width, height, megaPixels));
                            R.string.metadata_dimensions_format, width, height, megaPixels));
        }
    }
}
+54 −12
Original line number Diff line number Diff line
@@ -15,35 +15,77 @@
 */
package com.android.documentsui.inspector;

import static com.android.internal.util.Preconditions.checkNotNull;

import android.media.ExifInterface;
import android.os.Bundle;
import android.provider.DocumentsContract;

import com.android.documentsui.base.Shared;

import javax.annotation.Nullable;

final class MetadataUtils {

    private MetadataUtils() {}

    static boolean hasExifGpsFields(Bundle exif) {
        return (exif.containsKey(ExifInterface.TAG_GPS_LATITUDE)
    static boolean hasGeoCoordinates(@Nullable Bundle metadata) {
        if (metadata == null) {
            return false;
        }
        return hasVideoCoordinates(metadata.getBundle(Shared.METADATA_KEY_VIDEO))
                || hasExifGpsFields(metadata.getBundle(DocumentsContract.METADATA_EXIF));
    }

    static boolean hasVideoCoordinates(@Nullable Bundle data) {
        return data != null && (data.containsKey(Shared.METADATA_VIDEO_LATITUDE)
                && data.containsKey(Shared.METADATA_VIDEO_LONGITUTE));
    }

    static boolean hasExifGpsFields(@Nullable Bundle exif) {
        return exif != null && (exif.containsKey(ExifInterface.TAG_GPS_LATITUDE)
                && exif.containsKey(ExifInterface.TAG_GPS_LONGITUDE)
                && exif.containsKey(ExifInterface.TAG_GPS_LATITUDE_REF)
                && exif.containsKey(ExifInterface.TAG_GPS_LONGITUDE_REF));
    }

    static double[] getExifGpsCoords(Bundle exif) {
    static float[] getGeoCoordinates(Bundle metadata) {
        assert hasGeoCoordinates(metadata);
        checkNotNull(metadata);

        Bundle bundle = metadata.getBundle(DocumentsContract.METADATA_EXIF);
        if (hasExifGpsFields(bundle)) {
            return getExifGpsCoords(bundle);
        }

        bundle = metadata.getBundle(Shared.METADATA_KEY_VIDEO);
        if (hasVideoCoordinates(bundle)) {
            return getVideoCoords(bundle);
        }

        // This should never happen, because callers should always check w/ hasGeoCoordinates first.
        throw new IllegalArgumentException("Invalid metadata bundle: " + metadata);
    }

    static float[] getExifGpsCoords(Bundle exif) {
        assert hasExifGpsFields(exif);

        String lat = exif.getString(ExifInterface.TAG_GPS_LATITUDE);
        String lon = exif.getString(ExifInterface.TAG_GPS_LONGITUDE);
        String latRef = exif.getString(ExifInterface.TAG_GPS_LATITUDE_REF);
        String lonRef = exif.getString(ExifInterface.TAG_GPS_LONGITUDE_REF);

        double round = 1000.0;

        double[] coordinates = new double[2];

        coordinates[0] = Math.round(
                ExifInterface.convertRationalLatLonToFloat(lat, latRef) * round) / round;
        coordinates[1] = Math.round(
                ExifInterface.convertRationalLatLonToFloat(lon, lonRef) * round) / round;
        return new float[] {
            ExifInterface.convertRationalLatLonToFloat(lat, latRef),
            ExifInterface.convertRationalLatLonToFloat(lon, lonRef)
        };
    }

        return coordinates;
    static float[] getVideoCoords(Bundle data) {
        assert hasVideoCoordinates(data);
        return new float[] {
                data.getFloat(Shared.METADATA_VIDEO_LATITUDE),
                data.getFloat(Shared.METADATA_VIDEO_LONGITUTE)
        };
    }
}
Loading