1 /* 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 17 package com.android.camera.data; 18 19 import android.content.Context; 20 import android.util.SparseIntArray; 21 22 import com.android.camera.debug.Log; 23 import com.android.camera.exif.ExifInterface; 24 import com.android.camera.exif.ExifTag; 25 import com.android.camera2.R; 26 27 import java.io.FileNotFoundException; 28 import java.io.IOException; 29 import java.util.Iterator; 30 import java.util.Map.Entry; 31 import java.util.TreeMap; 32 33 public class MediaDetails implements Iterable<Entry<Integer, Object>> { 34 @SuppressWarnings("unused") 35 private static final Log.Tag TAG = new Log.Tag("MediaDetails"); 36 37 private TreeMap<Integer, Object> mDetails = new TreeMap<Integer, Object>(); 38 private SparseIntArray mUnits = new SparseIntArray(); 39 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) { 104 return mUnits.indexOfKey(index) >= 0; 105 } 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)); 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 } 172 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 } 188 } 189