1/*
2 * Copyright (C) 2010 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
17package com.android.gallery3d.data;
18
19import com.android.gallery3d.R;
20import com.android.gallery3d.common.Utils;
21import com.android.gallery3d.exif.ExifInterface;
22import com.android.gallery3d.exif.ExifTag;
23import com.android.gallery3d.exif.Rational;
24
25import java.io.FileInputStream;
26import java.io.FileNotFoundException;
27import java.io.IOException;
28import java.io.InputStream;
29import java.util.HashMap;
30import java.util.Iterator;
31import java.util.Map.Entry;
32import java.util.TreeMap;
33
34public class MediaDetails implements Iterable<Entry<Integer, Object>> {
35    @SuppressWarnings("unused")
36    private static final String TAG = "MediaDetails";
37
38    private TreeMap<Integer, Object> mDetails = new TreeMap<Integer, Object>();
39    private HashMap<Integer, Integer> mUnits = new HashMap<Integer, Integer>();
40
41    public static final int INDEX_TITLE = 1;
42    public static final int INDEX_DESCRIPTION = 2;
43    public static final int INDEX_DATETIME = 3;
44    public static final int INDEX_LOCATION = 4;
45    public static final int INDEX_WIDTH = 5;
46    public static final int INDEX_HEIGHT = 6;
47    public static final int INDEX_ORIENTATION = 7;
48    public static final int INDEX_DURATION = 8;
49    public static final int INDEX_MIMETYPE = 9;
50    public static final int INDEX_SIZE = 10;
51
52    // for EXIF
53    public static final int INDEX_MAKE = 100;
54    public static final int INDEX_MODEL = 101;
55    public static final int INDEX_FLASH = 102;
56    public static final int INDEX_FOCAL_LENGTH = 103;
57    public static final int INDEX_WHITE_BALANCE = 104;
58    public static final int INDEX_APERTURE = 105;
59    public static final int INDEX_SHUTTER_SPEED = 106;
60    public static final int INDEX_EXPOSURE_TIME = 107;
61    public static final int INDEX_ISO = 108;
62
63    // Put this last because it may be long.
64    public static final int INDEX_PATH = 200;
65
66    public static class FlashState {
67        private static int FLASH_FIRED_MASK = 1;
68        private static int FLASH_RETURN_MASK = 2 | 4;
69        private static int FLASH_MODE_MASK = 8 | 16;
70        private static int FLASH_FUNCTION_MASK = 32;
71        private static int FLASH_RED_EYE_MASK = 64;
72        private int mState;
73
74        public FlashState(int state) {
75            mState = state;
76        }
77
78        public boolean isFlashFired() {
79            return (mState & FLASH_FIRED_MASK) != 0;
80        }
81    }
82
83    public void addDetail(int index, Object value) {
84        mDetails.put(index, value);
85    }
86
87    public Object getDetail(int index) {
88        return mDetails.get(index);
89    }
90
91    public int size() {
92        return mDetails.size();
93    }
94
95    @Override
96    public Iterator<Entry<Integer, Object>> iterator() {
97        return mDetails.entrySet().iterator();
98    }
99
100    public void setUnit(int index, int unit) {
101        mUnits.put(index, unit);
102    }
103
104    public boolean hasUnit(int index) {
105        return mUnits.containsKey(index);
106    }
107
108    public int getUnit(int index) {
109        return mUnits.get(index);
110    }
111
112    private static void setExifData(MediaDetails details, ExifTag tag,
113            int key) {
114        if (tag != null) {
115            String value = null;
116            int type = tag.getDataType();
117            if (type == ExifTag.TYPE_UNSIGNED_RATIONAL || type == ExifTag.TYPE_RATIONAL) {
118                value = String.valueOf(tag.getValueAsRational(0).toDouble());
119            } else if (type == ExifTag.TYPE_ASCII) {
120                value = tag.getValueAsString();
121            } else {
122                value = String.valueOf(tag.forceGetValueAsLong(0));
123            }
124            if (key == MediaDetails.INDEX_FLASH) {
125                MediaDetails.FlashState state = new MediaDetails.FlashState(
126                        Integer.valueOf(value.toString()));
127                details.addDetail(key, state);
128            } else {
129                details.addDetail(key, value);
130            }
131        }
132    }
133
134    public static void extractExifInfo(MediaDetails details, String filePath) {
135
136        ExifInterface exif = new ExifInterface();
137        try {
138            exif.readExif(filePath);
139        } catch (FileNotFoundException e) {
140            Log.w(TAG, "Could not find file to read exif: " + filePath, e);
141        } catch (IOException e) {
142            Log.w(TAG, "Could not read exif from file: " + filePath, e);
143        }
144
145        setExifData(details, exif.getTag(ExifInterface.TAG_FLASH),
146                MediaDetails.INDEX_FLASH);
147        setExifData(details, exif.getTag(ExifInterface.TAG_IMAGE_WIDTH),
148                MediaDetails.INDEX_WIDTH);
149        setExifData(details, exif.getTag(ExifInterface.TAG_IMAGE_LENGTH),
150                MediaDetails.INDEX_HEIGHT);
151        setExifData(details, exif.getTag(ExifInterface.TAG_MAKE),
152                MediaDetails.INDEX_MAKE);
153        setExifData(details, exif.getTag(ExifInterface.TAG_MODEL),
154                MediaDetails.INDEX_MODEL);
155        setExifData(details, exif.getTag(ExifInterface.TAG_APERTURE_VALUE),
156                MediaDetails.INDEX_APERTURE);
157        setExifData(details, exif.getTag(ExifInterface.TAG_ISO_SPEED_RATINGS),
158                MediaDetails.INDEX_ISO);
159        setExifData(details, exif.getTag(ExifInterface.TAG_WHITE_BALANCE),
160                MediaDetails.INDEX_WHITE_BALANCE);
161        setExifData(details, exif.getTag(ExifInterface.TAG_EXPOSURE_TIME),
162                MediaDetails.INDEX_EXPOSURE_TIME);
163        ExifTag focalTag = exif.getTag(ExifInterface.TAG_FOCAL_LENGTH);
164        if (focalTag != null) {
165            details.addDetail(MediaDetails.INDEX_FOCAL_LENGTH,
166                    focalTag.getValueAsRational(0).toDouble());
167            details.setUnit(MediaDetails.INDEX_FOCAL_LENGTH, R.string.unit_mm);
168        }
169    }
170}
171