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

Commit 839cc347 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Load EXIF data into new view."

parents bce3247a 0404bdca
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -393,6 +393,9 @@
    <!-- Title of inspector's metadata info section. Note that this is probably camera EXIF data. -->
    <string name="inspector_metadata_section">Metadata</string>

    <!-- Title of inspector section displaying image specific information (like size and embedded camera details, like lat/long). -->
    <string name="inspector_exif_section">Image information</string>

    <!--The height and width of a photo. Note that this is probably camera EXIF data.-->
    <string name="metadata_dimensions">Dimensions</string>
    <!--The location of where a photo was taken. (i.e. latitude, longitude) Note that this is probably camera EXIF data.-->
+5 −0
Original line number Diff line number Diff line
@@ -223,6 +223,7 @@ public class DocumentInfo implements Durable, Parcelable {
                + ", isDeleteSupported=" + isDeleteSupported()
                + ", isCreateSupported=" + isCreateSupported()
                + ", isRenameSupported=" + isRenameSupported()
                + ", isMetadataSupported=" + isMetadataSupported()
                + "} @ "
                + derivedUri;
    }
@@ -255,6 +256,10 @@ public class DocumentInfo implements Durable, Parcelable {
        return (flags & Document.FLAG_SUPPORTS_RENAME) != 0;
    }

    public boolean isMetadataSupported() {
        return (flags & Document.FLAG_SUPPORTS_METADATA) != 0;
    }

    public boolean isThumbnailSupported() {
        return (flags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
    }
+1 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ public class DebugView extends TableView implements Consumer<DocumentInfo> {
        put("Is virtual", info.isVirtual());
        put("Supports create", info.isCreateSupported());
        put("Supports delete", info.isDeleteSupported());
        put("Supports metadata", info.isMetadataSupported());
        put("Supports rename", info.isRenameSupported());
        put("Supports settings", info.isSettingsSupported());
        put("Supports thumbnail", info.isThumbnailSupported());
+75 −23
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import static com.android.internal.util.Preconditions.checkArgument;

import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.CursorLoader;
import android.database.ContentObserver;
@@ -27,32 +28,37 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;

import android.provider.DocumentsContract;
import android.support.annotation.Nullable;
import android.util.Log;

import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.inspector.InspectorController.Loader;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * Asynchronously loads a document's metadata for the inspector.
 * Asynchronously loads a document data for the inspector.
 */
public class DocumentLoader implements Loader {

    private static final String TAG = "DocumentLoader";

    private final Context mContext;
    private final LoaderManager mLoader;
    private List<Integer> loaderIds;
    private final LoaderManager mLoaderMgr;
    private final List<Integer> loaderIds = new ArrayList<>();
    private @Nullable Callbacks mDocCallbacks;
    private @Nullable Callbacks mDirCallbacks;
    private @Nullable LoaderCallbacks<Bundle> mMetadataCallbacks;

    public DocumentLoader(Context context, LoaderManager loader) {
    public DocumentLoader(Context context, LoaderManager loaderMgr) {
        checkArgument(context != null);
        checkArgument(loader != null);
        checkArgument(loaderMgr != null);
        mContext = context;
        mLoader = loader;
        loaderIds = new ArrayList<>();
        mLoaderMgr = loaderMgr;
    }

    /**
@@ -63,11 +69,6 @@ public class DocumentLoader implements Loader {
        //Check that we have correct Uri type and that the loader is not already created.
        checkArgument(uri.getScheme().equals("content"));

        //get a new loader id.
        int loadId = getNextLoaderId();
        checkArgument(mLoader.getLoader(loadId) == null);
        loaderIds.add(loadId);

        Consumer<Cursor> callback = new Consumer<Cursor>() {
            @Override
            public void accept(Cursor cursor) {
@@ -81,7 +82,7 @@ public class DocumentLoader implements Loader {
        };

        mDocCallbacks = new Callbacks(mContext, uri, callback);
        mLoader.restartLoader(loadId, null, mDocCallbacks);
        mLoaderMgr.restartLoader(getNextLoaderId(), null, mDocCallbacks);
    }

    /**
@@ -93,11 +94,6 @@ public class DocumentLoader implements Loader {
        Uri children = DocumentsContract.buildChildDocumentsUri(
                directory.authority, directory.documentId);

        //get a new loader id.
        int loadId = getNextLoaderId();
        checkArgument(mLoader.getLoader(loadId) == null);
        loaderIds.add(loadId);

        Consumer<Cursor> callback = new Consumer<Cursor>() {
            @Override
            public void accept(Cursor cursor) {
@@ -108,19 +104,74 @@ public class DocumentLoader implements Loader {
        };

        mDirCallbacks = new Callbacks(mContext, children, callback);
        mLoader.restartLoader(loadId, null, mDirCallbacks);
        mLoaderMgr.restartLoader(getNextLoaderId(), null, mDirCallbacks);
    }

    @Override
    public void getDocumentMetadata(Uri uri, Consumer<Bundle> callback) {

        // TODO: For some reason the async loading of metadata isn't working.
        // This is a hackaround. Tracking bug @ b/63925015
        try {
            Bundle syncData = DocumentsContract.getDocumentMetadata(
                    mContext.getContentResolver(),
                    uri,
                    null);
            callback.accept(syncData);
        } catch (FileNotFoundException e) {
            callback.accept(Bundle.EMPTY);
        }

        Log.d(TAG, "Loading document metadata.");

        mMetadataCallbacks = new LoaderCallbacks<Bundle>() {
            @Override
            public android.content.Loader<Bundle> onCreateLoader(int id, Bundle unused) {
                Log.d(TAG, "Creating loader for metadata.");
                return new AsyncTaskLoader<Bundle>(mContext) {
                    @Override
                    public Bundle loadInBackground() {
                        try {
                            Log.d(TAG, "Executing call to load metadata.");
                            return DocumentsContract.getDocumentMetadata(
                                    mContext.getContentResolver(),
                                    uri, null);
                        } catch (FileNotFoundException e) {
                            Log.e(TAG, "Failed to load metadata for doc: " + uri, e);
                        }

                        return null;
                    }
                };
            }

            @Override
            public void onLoadFinished(android.content.Loader<Bundle> loader, Bundle data) {
                Log.d(TAG, "Received document metadata. Relaying to callback.");
                callback.accept(data);
            }

            @Override
            public void onLoaderReset(android.content.Loader<Bundle> loader) {
                Log.d(TAG, "Document metadata reset. Yerp!");
            }
        };

        // TODO: Listen for changes on content URI.
        mLoaderMgr.restartLoader(getNextLoaderId(), null, mMetadataCallbacks);
    }

    @Override
    public void reset() {
        for (Integer id : loaderIds) {
            mLoader.destroyLoader(id);
            mLoaderMgr.destroyLoader(id);
        }
        loaderIds.clear();

        if (mDocCallbacks != null && mDocCallbacks.getObserver() != null) {
            mContext.getContentResolver().unregisterContentObserver(mDocCallbacks.getObserver());
        }

        if (mDirCallbacks != null && mDirCallbacks.getObserver() != null) {
            mContext.getContentResolver().unregisterContentObserver(mDocCallbacks.getObserver());
        }
@@ -128,10 +179,11 @@ public class DocumentLoader implements Loader {

    private int getNextLoaderId() {
        int id = 0;
        while(mLoader.getLoader(id) != null) {
        while(mLoaderMgr.getLoader(id) != null) {
            id++;
            checkArgument(id <= Integer.MAX_VALUE);
        }
        loaderIds.add(id);
        return id;
    }

+61 −30
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
 */
package com.android.documentsui.inspector;

import static android.provider.DocumentsContract.Document.FLAG_SUPPORTS_SETTINGS;
import static com.android.internal.util.Preconditions.checkArgument;

import android.annotation.StringRes;
@@ -32,6 +31,7 @@ import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.view.View;
import android.view.View.OnClickListener;

import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.ProviderExecutor;
import com.android.documentsui.R;
@@ -42,6 +42,7 @@ import com.android.documentsui.inspector.actions.ClearDefaultAppAction;
import com.android.documentsui.inspector.actions.ShowInProviderAction;
import com.android.documentsui.roots.ProvidersAccess;
import com.android.documentsui.ui.Snackbars;

import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -172,34 +173,53 @@ public final class InspectorController {
                        });
                }
            }

            if (docInfo.isMetadataSupported()) {
                mLoader.getDocumentMetadata(
                        docInfo.derivedUri,
                        (Bundle bundle) -> {
                            onDocumentMetadataLoaded(docInfo.displayName, bundle);
                        });
            } else {
                mMetadata.setVisible(false);
            }

            if (mShowDebug) {
                mDebugView.accept(docInfo);
            }
        }
    }

    @VisibleForTesting
    public void onDocumentMetadataLoaded(String displayName, Bundle bundle) {
        Bundle exif = bundle.getBundle(DocumentsContract.METADATA_EXIF);
        if (exif != null) {
            showExifData(displayName, exif);
        }
        mMetadata.setVisible(exif != null);
    }

    /**
     * Updates a files metadata to the view.
     * @param docName - the name of the doc. needed for launching a geo intent.
     * @param args - bundle of metadata.
     * @param bundle - bundle of metadata.
     */
    @VisibleForTesting
    public void updateMetadata(String docName, Bundle args) {

        mMetadata.setTitle(R.string.inspector_metadata_section);
    public void showExifData(String docName, Bundle bundle) {
        mMetadata.setTitle(R.string.inspector_exif_section);

        if (args.containsKey(ExifInterface.TAG_IMAGE_WIDTH)
            && args.containsKey(ExifInterface.TAG_IMAGE_LENGTH)) {
            int width = args.getInt(ExifInterface.TAG_IMAGE_WIDTH);
            int height = args.getInt(ExifInterface.TAG_IMAGE_LENGTH);
        if (bundle.containsKey(ExifInterface.TAG_IMAGE_WIDTH)
            && bundle.containsKey(ExifInterface.TAG_IMAGE_LENGTH)) {
            int width = bundle.getInt(ExifInterface.TAG_IMAGE_WIDTH);
            int height = bundle.getInt(ExifInterface.TAG_IMAGE_LENGTH);
            mMetadata.put(R.string.metadata_dimensions, String.valueOf(width) + " x "
                + String.valueOf(height));
        }

        if (args.containsKey(ExifInterface.TAG_GPS_LATITUDE)
            && args.containsKey(ExifInterface.TAG_GPS_LONGITUDE) ) {
            double latitude = args.getDouble(ExifInterface.TAG_GPS_LATITUDE);
            double longitude = args.getDouble(ExifInterface.TAG_GPS_LONGITUDE);
        if (bundle.containsKey(ExifInterface.TAG_GPS_LATITUDE)
                && bundle.containsKey(ExifInterface.TAG_GPS_LONGITUDE) ) {
            double latitude = bundle.getDouble(ExifInterface.TAG_GPS_LATITUDE);
            double longitude = bundle.getDouble(ExifInterface.TAG_GPS_LONGITUDE);

            Intent intent = createGeoIntent(latitude, longitude, docName);

@@ -214,28 +234,28 @@ public final class InspectorController {
            }
        }

        if (args.containsKey(ExifInterface.TAG_GPS_ALTITUDE)) {
            double altitude = args.getDouble(ExifInterface.TAG_GPS_ALTITUDE);
        if (bundle.containsKey(ExifInterface.TAG_GPS_ALTITUDE)) {
            double altitude = bundle.getDouble(ExifInterface.TAG_GPS_ALTITUDE);
            mMetadata.put(R.string.metadata_altitude, String.valueOf(altitude));
        }

        if (args.containsKey(ExifInterface.TAG_MAKE)) {
            String make = args.getString(ExifInterface.TAG_MAKE);
        if (bundle.containsKey(ExifInterface.TAG_MAKE)) {
            String make = bundle.getString(ExifInterface.TAG_MAKE);
            mMetadata.put(R.string.metadata_make, make);
        }

        if (args.containsKey(ExifInterface.TAG_MODEL)) {
            String model = args.getString(ExifInterface.TAG_MODEL);
        if (bundle.containsKey(ExifInterface.TAG_MODEL)) {
            String model = bundle.getString(ExifInterface.TAG_MODEL);
            mMetadata.put(R.string.metadata_model, model);
        }

        if (args.containsKey(ExifInterface.TAG_APERTURE)) {
            String aperture = String.valueOf(args.get(ExifInterface.TAG_APERTURE));
        if (bundle.containsKey(ExifInterface.TAG_APERTURE)) {
            String aperture = String.valueOf(bundle.get(ExifInterface.TAG_APERTURE));
            mMetadata.put(R.string.metadata_aperture, aperture);
        }

        if (args.containsKey(ExifInterface.TAG_SHUTTER_SPEED_VALUE)) {
            String shutterSpeed = String.valueOf(args.get(ExifInterface.TAG_SHUTTER_SPEED_VALUE));
        if (bundle.containsKey(ExifInterface.TAG_SHUTTER_SPEED_VALUE)) {
            String shutterSpeed = String.valueOf(bundle.get(ExifInterface.TAG_SHUTTER_SPEED_VALUE));
            mMetadata.put(R.string.metadata_shutter_speed, shutterSpeed);
        }
    }
@@ -327,12 +347,28 @@ public final class InspectorController {
         * Deletes all loader id's when android lifecycle ends.
         */
        void reset();

        /**
         * @param uri
         * @param callback
         */
        void getDocumentMetadata(Uri uri, Consumer<Bundle> callback);
    }

    /**
     * This interface is for unit testing.
     */
    public interface ActionDisplay {
    public interface Display {
        /**
         * Makes the action visible.
         */
        void setVisible(boolean visible);
    }

    /**
     * This interface is for unit testing.
     */
    public interface ActionDisplay extends Display {

        /**
         * Initializes the view based on the action.
@@ -341,11 +377,6 @@ public final class InspectorController {
         */
        void init(Action action, OnClickListener listener);

        /**
         * Makes the action visible.
         */
        void setVisible(boolean visible);

        void setActionHeader(String header);

        void setAppIcon(Drawable icon);
@@ -368,7 +399,7 @@ public final class InspectorController {
    /**
     * Displays a table of image metadata.
     */
    public interface TableDisplay {
    public interface TableDisplay extends Display {

        /**
         * Sets the title of the data.
Loading