/* * Copyright (C) 2012 mixi, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jp.mixi.compatibility.android.media; import android.media.ExifInterface; import android.text.TextUtils; import android.util.Log; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; import static jp.mixi.compatibility.android.media.ExifInterfaceCompat.EXIF_DEGREE_FALLBACK_VALUE; import static jp.mixi.compatibility.android.media.ExifInterfaceCompat.getExifDateTime; /** * Bug fixture for ExifInterface constructor. */ public final class ExifInterfaceCompat { public static final String TAG = ExifInterfaceCompat.class.getSimpleName(); public static final int EXIF_DEGREE_FALLBACK_VALUE = -1; /** * Do not instantiate this class. */ private ExifInterfaceCompat() {} /** * Creates new instance of {@link ExifInterface}. * Original constructor won't check filename value, so if null value has been passed, * the process will be killed because of SIGSEGV. * Google Play crash report system cannot perceive this crash, so this method will throw {@link NullPointerException} when the filename is null. * * @param filename a JPEG filename. * @return {@link ExifInterface} instance. * @throws IOException something wrong with I/O. */ public static final ExifInterface newInstance(String filename) throws IOException { if (filename == null) throw new NullPointerException("filename should not be null"); return new ExifInterface(filename); } public static final Date getExifDateTime(String filepath) { ExifInterface exif = null; try { // ExifInterface does not check whether file path is null or not, // so passing null file path argument to its constructor causing SIGSEGV. // We should avoid such a situation by checking file path string. exif = ExifInterfaceCompat.newInstance(filepath); } catch (IOException ex) { Log.e(TAG, "cannot read exif", ex); return null; } String date = exif.getAttribute(ExifInterface.TAG_DATETIME); if (TextUtils.isEmpty(date)) { return null; } try { SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); formatter.setTimeZone(TimeZone.getTimeZone("UTC")); return formatter.parse(date); } catch (ParseException e) { Log.d(TAG, "failed to parse date taken", e); } return null; } /** * Read exif info and get datetime value of the photo. * @param filepath to get datetime * @return when a photo taken. */ public static final long getExifDateTimeInMillis(String filepath) { Date datetime = getExifDateTime(filepath); if (datetime == null) { return -1; } return datetime.getTime(); } /** * Read exif info and get orientation value of the photo. * @param filepath to get exif. * @return exif orientation value */ public static final int getExifOrientation(String filepath) { ExifInterface exif = null; try { // ExifInterface does not check whether file path is null or not, // so passing null file path argument to its constructor causing SIGSEGV. // We should avoid such a situation by checking file path string. exif = ExifInterfaceCompat.newInstance(filepath); } catch (IOException ex) { Log.e(TAG, "cannot read exif", ex); return EXIF_DEGREE_FALLBACK_VALUE; } int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, EXIF_DEGREE_FALLBACK_VALUE); if (orientation == EXIF_DEGREE_FALLBACK_VALUE) { return 0; } // We only recognize a subset of orientation tag values. switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: return 90; case ExifInterface.ORIENTATION_ROTATE_180: return 180; case ExifInterface.ORIENTATION_ROTATE_270: return 270; default: return 0; } } }