16f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling/*
26f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling * Copyright (C) 2013 The Android Open Source Project
36f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling *
46f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling * Licensed under the Apache License, Version 2.0 (the "License");
56f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling * you may not use this file except in compliance with the License.
66f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling * You may obtain a copy of the License at
76f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling *
86f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling *      http://www.apache.org/licenses/LICENSE-2.0
96f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling *
106f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling * Unless required by applicable law or agreed to in writing, software
116f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling * distributed under the License is distributed on an "AS IS" BASIS,
126f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling * See the License for the specific language governing permissions and
146f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling * limitations under the License.
156f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling */
166f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
176f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberlingpackage com.android.camera.data;
186f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
19b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberlingimport android.content.Context;
20e2fb9943fb2d5a51a651f138d88473bf7f62e0b9Sascha Haeberlingimport android.util.SparseIntArray;
216f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
222bca210e5fc8a77685775ffb403096167b017dceAngus Kongimport com.android.camera.debug.Log;
23a16e7b50f3148f581439509279f242092e254309ztenghuiimport com.android.camera.exif.ExifInterface;
24a16e7b50f3148f581439509279f242092e254309ztenghuiimport com.android.camera.exif.ExifTag;
256f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberlingimport com.android.camera2.R;
266f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
276f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberlingimport java.io.FileNotFoundException;
286f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberlingimport java.io.IOException;
296f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberlingimport java.util.Iterator;
306f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberlingimport java.util.Map.Entry;
316f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberlingimport java.util.TreeMap;
326f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
336f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberlingpublic class MediaDetails implements Iterable<Entry<Integer, Object>> {
346f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    @SuppressWarnings("unused")
352bca210e5fc8a77685775ffb403096167b017dceAngus Kong    private static final Log.Tag TAG = new Log.Tag("MediaDetails");
366f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
376f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    private TreeMap<Integer, Object> mDetails = new TreeMap<Integer, Object>();
38e2fb9943fb2d5a51a651f138d88473bf7f62e0b9Sascha Haeberling    private SparseIntArray mUnits = new SparseIntArray();
396f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
406f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_TITLE = 1;
416f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_DESCRIPTION = 2;
426f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_DATETIME = 3;
436f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_LOCATION = 4;
446f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_WIDTH = 5;
456f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_HEIGHT = 6;
466f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_ORIENTATION = 7;
476f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_DURATION = 8;
486f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_MIMETYPE = 9;
496f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_SIZE = 10;
506f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
516f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    // for EXIF
526f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_MAKE = 100;
536f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_MODEL = 101;
546f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_FLASH = 102;
556f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_FOCAL_LENGTH = 103;
566f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_WHITE_BALANCE = 104;
576f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_APERTURE = 105;
586f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_SHUTTER_SPEED = 106;
596f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_EXPOSURE_TIME = 107;
606f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_ISO = 108;
616f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
626f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    // Put this last because it may be long.
636f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static final int INDEX_PATH = 200;
646f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
656f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static class FlashState {
666f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        private static int FLASH_FIRED_MASK = 1;
676f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        private static int FLASH_RETURN_MASK = 2 | 4;
686f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        private static int FLASH_MODE_MASK = 8 | 16;
696f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        private static int FLASH_FUNCTION_MASK = 32;
706f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        private static int FLASH_RED_EYE_MASK = 64;
716f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        private int mState;
726f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
736f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        public FlashState(int state) {
746f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            mState = state;
756f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        }
766f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
776f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        public boolean isFlashFired() {
786f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            return (mState & FLASH_FIRED_MASK) != 0;
796f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        }
806f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    }
816f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
826f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public void addDetail(int index, Object value) {
836f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        mDetails.put(index, value);
846f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    }
856f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
866f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public Object getDetail(int index) {
876f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        return mDetails.get(index);
886f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    }
896f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
906f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public int size() {
916f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        return mDetails.size();
926f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    }
936f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
946f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    @Override
956f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public Iterator<Entry<Integer, Object>> iterator() {
966f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        return mDetails.entrySet().iterator();
976f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    }
986f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
996f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public void setUnit(int index, int unit) {
1006f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        mUnits.put(index, unit);
1016f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    }
1026f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
1036f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public boolean hasUnit(int index) {
104e2fb9943fb2d5a51a651f138d88473bf7f62e0b9Sascha Haeberling        return mUnits.indexOfKey(index) >= 0;
1056f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    }
1066f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
1076f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public int getUnit(int index) {
1086f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        return mUnits.get(index);
1096f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    }
1106f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
1116f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    private static void setExifData(MediaDetails details, ExifTag tag,
1126f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            int key) {
1136f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        if (tag != null) {
1146f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            String value = null;
1156f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            int type = tag.getDataType();
1166f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            if (type == ExifTag.TYPE_UNSIGNED_RATIONAL || type == ExifTag.TYPE_RATIONAL) {
1176f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                value = String.valueOf(tag.getValueAsRational(0).toDouble());
1186f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            } else if (type == ExifTag.TYPE_ASCII) {
1196f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                value = tag.getValueAsString();
1206f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            } else {
1216f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                value = String.valueOf(tag.forceGetValueAsLong(0));
1226f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            }
1236f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            if (key == MediaDetails.INDEX_FLASH) {
1246f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                MediaDetails.FlashState state = new MediaDetails.FlashState(
1258ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde                        Integer.valueOf(value));
1266f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                details.addDetail(key, state);
1276f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            } else {
1286f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                details.addDetail(key, value);
1296f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            }
1306f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        }
1316f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    }
1326f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
1336f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    /**
1346f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling     * Extracts data from the EXIF of the given file and stores it in the
1356f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling     * MediaDetails instance.
1366f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling     */
1376f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    public static void extractExifInfo(MediaDetails details, String filePath) {
1386f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        ExifInterface exif = new ExifInterface();
1396f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        try {
1406f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            exif.readExif(filePath);
1416f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        } catch (FileNotFoundException e) {
1426f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            Log.w(TAG, "Could not find file to read exif: " + filePath, e);
1436f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        } catch (IOException e) {
1446f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            Log.w(TAG, "Could not read exif from file: " + filePath, e);
1456f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        }
1466f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling
1476f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        setExifData(details, exif.getTag(ExifInterface.TAG_FLASH),
1486f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                MediaDetails.INDEX_FLASH);
1496f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        setExifData(details, exif.getTag(ExifInterface.TAG_IMAGE_WIDTH),
1506f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                MediaDetails.INDEX_WIDTH);
1516f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        setExifData(details, exif.getTag(ExifInterface.TAG_IMAGE_LENGTH),
1526f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                MediaDetails.INDEX_HEIGHT);
1536f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        setExifData(details, exif.getTag(ExifInterface.TAG_MAKE),
1546f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                MediaDetails.INDEX_MAKE);
1556f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        setExifData(details, exif.getTag(ExifInterface.TAG_MODEL),
1566f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                MediaDetails.INDEX_MODEL);
1576f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        setExifData(details, exif.getTag(ExifInterface.TAG_APERTURE_VALUE),
1586f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                MediaDetails.INDEX_APERTURE);
1596f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        setExifData(details, exif.getTag(ExifInterface.TAG_ISO_SPEED_RATINGS),
1606f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                MediaDetails.INDEX_ISO);
1616f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        setExifData(details, exif.getTag(ExifInterface.TAG_WHITE_BALANCE),
1626f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                MediaDetails.INDEX_WHITE_BALANCE);
1636f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        setExifData(details, exif.getTag(ExifInterface.TAG_EXPOSURE_TIME),
1646f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                MediaDetails.INDEX_EXPOSURE_TIME);
1656f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        ExifTag focalTag = exif.getTag(ExifInterface.TAG_FOCAL_LENGTH);
1666f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        if (focalTag != null) {
1676f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            details.addDetail(MediaDetails.INDEX_FOCAL_LENGTH,
1686f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling                    focalTag.getValueAsRational(0).toDouble());
1696f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling            details.setUnit(MediaDetails.INDEX_FOCAL_LENGTH, R.string.unit_mm);
1706f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling        }
1716f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling    }
172b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling
173b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling    /**
174b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling     * Returns a (localized) string for the given duration (in seconds).
175b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling     */
176b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling    public static String formatDuration(final Context context, long seconds) {
177b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling        long h = seconds / 3600;
178b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling        long m = (seconds - h * 3600) / 60;
179b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling        long s = seconds - (h * 3600 + m * 60);
180b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling        String durationValue;
181b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling        if (h == 0) {
182b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling            durationValue = String.format(context.getString(R.string.details_ms), m, s);
183b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling        } else {
184b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling            durationValue = String.format(context.getString(R.string.details_hms), h, m, s);
185b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling        }
186b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling        return durationValue;
187b0a5929b1dd0ddc2729e23ba95afdb43a1aecfd5Sascha Haeberling    }
1886f64b50db6565bc6454304fd20c0c4020b297bb7Sascha Haeberling}
189