Sascha Haeberling | 6f64b50 | 2013-08-14 16:23:18 -0700 | [diff] [blame] | 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 | |
Sascha Haeberling | b0a5929 | 2013-08-19 14:02:19 -0700 | [diff] [blame] | 19 | import android.content.Context; |
Sascha Haeberling | e2fb994 | 2013-08-22 13:49:46 -0700 | [diff] [blame] | 20 | import android.util.SparseIntArray; |
Sascha Haeberling | 6f64b50 | 2013-08-14 16:23:18 -0700 | [diff] [blame] | 21 | |
Angus Kong | 2bca210 | 2014-03-11 16:27:30 -0700 | [diff] [blame^] | 22 | import com.android.camera.debug.Log; |
ztenghui | a16e7b5 | 2013-08-23 11:47:56 -0700 | [diff] [blame] | 23 | import com.android.camera.exif.ExifInterface; |
| 24 | import com.android.camera.exif.ExifTag; |
Sascha Haeberling | 6f64b50 | 2013-08-14 16:23:18 -0700 | [diff] [blame] | 25 | import com.android.camera2.R; |
Sascha Haeberling | 6f64b50 | 2013-08-14 16:23:18 -0700 | [diff] [blame] | 26 | |
| 27 | import java.io.FileNotFoundException; |
| 28 | import java.io.IOException; |
Sascha Haeberling | 6f64b50 | 2013-08-14 16:23:18 -0700 | [diff] [blame] | 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") |
Angus Kong | 2bca210 | 2014-03-11 16:27:30 -0700 | [diff] [blame^] | 35 | private static final Log.Tag TAG = new Log.Tag("MediaDetails"); |
Sascha Haeberling | 6f64b50 | 2013-08-14 16:23:18 -0700 | [diff] [blame] | 36 | |
| 37 | private TreeMap<Integer, Object> mDetails = new TreeMap<Integer, Object>(); |
Sascha Haeberling | e2fb994 | 2013-08-22 13:49:46 -0700 | [diff] [blame] | 38 | private SparseIntArray mUnits = new SparseIntArray(); |
Sascha Haeberling | 6f64b50 | 2013-08-14 16:23:18 -0700 | [diff] [blame] | 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) { |
Sascha Haeberling | e2fb994 | 2013-08-22 13:49:46 -0700 | [diff] [blame] | 104 | return mUnits.indexOfKey(index) >= 0; |
Sascha Haeberling | 6f64b50 | 2013-08-14 16:23:18 -0700 | [diff] [blame] | 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.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 Haeberling | b0a5929 | 2013-08-19 14:02:19 -0700 | [diff] [blame] | 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 | } |
Sascha Haeberling | 6f64b50 | 2013-08-14 16:23:18 -0700 | [diff] [blame] | 188 | } |