blob: c1d6ccd582295c9887be4034763a4bb143458065 [file] [log] [blame]
Sascha Haeberling6f64b502013-08-14 16:23:18 -07001/*
2 * Copyright (C) 2013 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.camera.data;
18
Sascha Haeberlingb0a59292013-08-19 14:02:19 -070019import android.content.Context;
Sascha Haeberlinge2fb9942013-08-22 13:49:46 -070020import android.util.SparseIntArray;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070021
Angus Kong2bca2102014-03-11 16:27:30 -070022import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070023import com.android.camera.exif.ExifInterface;
24import com.android.camera.exif.ExifTag;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070025import com.android.camera2.R;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070026
27import java.io.FileNotFoundException;
28import java.io.IOException;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070029import java.util.Iterator;
30import java.util.Map.Entry;
31import java.util.TreeMap;
32
33public class MediaDetails implements Iterable<Entry<Integer, Object>> {
34 @SuppressWarnings("unused")
Angus Kong2bca2102014-03-11 16:27:30 -070035 private static final Log.Tag TAG = new Log.Tag("MediaDetails");
Sascha Haeberling6f64b502013-08-14 16:23:18 -070036
37 private TreeMap<Integer, Object> mDetails = new TreeMap<Integer, Object>();
Sascha Haeberlinge2fb9942013-08-22 13:49:46 -070038 private SparseIntArray mUnits = new SparseIntArray();
Sascha Haeberling6f64b502013-08-14 16:23:18 -070039
40 public static final int INDEX_TITLE = 1;
41 public static final int INDEX_DESCRIPTION = 2;
42 public static final int INDEX_DATETIME = 3;
43 public static final int INDEX_LOCATION = 4;
44 public static final int INDEX_WIDTH = 5;
45 public static final int INDEX_HEIGHT = 6;
46 public static final int INDEX_ORIENTATION = 7;
47 public static final int INDEX_DURATION = 8;
48 public static final int INDEX_MIMETYPE = 9;
49 public static final int INDEX_SIZE = 10;
50
51 // for EXIF
52 public static final int INDEX_MAKE = 100;
53 public static final int INDEX_MODEL = 101;
54 public static final int INDEX_FLASH = 102;
55 public static final int INDEX_FOCAL_LENGTH = 103;
56 public static final int INDEX_WHITE_BALANCE = 104;
57 public static final int INDEX_APERTURE = 105;
58 public static final int INDEX_SHUTTER_SPEED = 106;
59 public static final int INDEX_EXPOSURE_TIME = 107;
60 public static final int INDEX_ISO = 108;
61
62 // Put this last because it may be long.
63 public static final int INDEX_PATH = 200;
64
65 public static class FlashState {
66 private static int FLASH_FIRED_MASK = 1;
67 private static int FLASH_RETURN_MASK = 2 | 4;
68 private static int FLASH_MODE_MASK = 8 | 16;
69 private static int FLASH_FUNCTION_MASK = 32;
70 private static int FLASH_RED_EYE_MASK = 64;
71 private int mState;
72
73 public FlashState(int state) {
74 mState = state;
75 }
76
77 public boolean isFlashFired() {
78 return (mState & FLASH_FIRED_MASK) != 0;
79 }
80 }
81
82 public void addDetail(int index, Object value) {
83 mDetails.put(index, value);
84 }
85
86 public Object getDetail(int index) {
87 return mDetails.get(index);
88 }
89
90 public int size() {
91 return mDetails.size();
92 }
93
94 @Override
95 public Iterator<Entry<Integer, Object>> iterator() {
96 return mDetails.entrySet().iterator();
97 }
98
99 public void setUnit(int index, int unit) {
100 mUnits.put(index, unit);
101 }
102
103 public boolean hasUnit(int index) {
Sascha Haeberlinge2fb9942013-08-22 13:49:46 -0700104 return mUnits.indexOfKey(index) >= 0;
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700105 }
106
107 public int getUnit(int index) {
108 return mUnits.get(index);
109 }
110
111 private static void setExifData(MediaDetails details, ExifTag tag,
112 int key) {
113 if (tag != null) {
114 String value = null;
115 int type = tag.getDataType();
116 if (type == ExifTag.TYPE_UNSIGNED_RATIONAL || type == ExifTag.TYPE_RATIONAL) {
117 value = String.valueOf(tag.getValueAsRational(0).toDouble());
118 } else if (type == ExifTag.TYPE_ASCII) {
119 value = tag.getValueAsString();
120 } else {
121 value = String.valueOf(tag.forceGetValueAsLong(0));
122 }
123 if (key == MediaDetails.INDEX_FLASH) {
124 MediaDetails.FlashState state = new MediaDetails.FlashState(
125 Integer.valueOf(value.toString()));
126 details.addDetail(key, state);
127 } else {
128 details.addDetail(key, value);
129 }
130 }
131 }
132
133 /**
134 * Extracts data from the EXIF of the given file and stores it in the
135 * MediaDetails instance.
136 */
137 public static void extractExifInfo(MediaDetails details, String filePath) {
138 ExifInterface exif = new ExifInterface();
139 try {
140 exif.readExif(filePath);
141 } catch (FileNotFoundException e) {
142 Log.w(TAG, "Could not find file to read exif: " + filePath, e);
143 } catch (IOException e) {
144 Log.w(TAG, "Could not read exif from file: " + filePath, e);
145 }
146
147 setExifData(details, exif.getTag(ExifInterface.TAG_FLASH),
148 MediaDetails.INDEX_FLASH);
149 setExifData(details, exif.getTag(ExifInterface.TAG_IMAGE_WIDTH),
150 MediaDetails.INDEX_WIDTH);
151 setExifData(details, exif.getTag(ExifInterface.TAG_IMAGE_LENGTH),
152 MediaDetails.INDEX_HEIGHT);
153 setExifData(details, exif.getTag(ExifInterface.TAG_MAKE),
154 MediaDetails.INDEX_MAKE);
155 setExifData(details, exif.getTag(ExifInterface.TAG_MODEL),
156 MediaDetails.INDEX_MODEL);
157 setExifData(details, exif.getTag(ExifInterface.TAG_APERTURE_VALUE),
158 MediaDetails.INDEX_APERTURE);
159 setExifData(details, exif.getTag(ExifInterface.TAG_ISO_SPEED_RATINGS),
160 MediaDetails.INDEX_ISO);
161 setExifData(details, exif.getTag(ExifInterface.TAG_WHITE_BALANCE),
162 MediaDetails.INDEX_WHITE_BALANCE);
163 setExifData(details, exif.getTag(ExifInterface.TAG_EXPOSURE_TIME),
164 MediaDetails.INDEX_EXPOSURE_TIME);
165 ExifTag focalTag = exif.getTag(ExifInterface.TAG_FOCAL_LENGTH);
166 if (focalTag != null) {
167 details.addDetail(MediaDetails.INDEX_FOCAL_LENGTH,
168 focalTag.getValueAsRational(0).toDouble());
169 details.setUnit(MediaDetails.INDEX_FOCAL_LENGTH, R.string.unit_mm);
170 }
171 }
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700172
173 /**
174 * Returns a (localized) string for the given duration (in seconds).
175 */
176 public static String formatDuration(final Context context, long seconds) {
177 long h = seconds / 3600;
178 long m = (seconds - h * 3600) / 60;
179 long s = seconds - (h * 3600 + m * 60);
180 String durationValue;
181 if (h == 0) {
182 durationValue = String.format(context.getString(R.string.details_ms), m, s);
183 } else {
184 durationValue = String.format(context.getString(R.string.details_hms), h, m, s);
185 }
186 return durationValue;
187 }
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700188}