1104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/*
2104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Copyright (C) 2012 The Android Open Source Project
3104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
4104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * you may not use this file except in compliance with the License.
6104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * You may obtain a copy of the License at
7104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
8104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
10104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Unless required by applicable law or agreed to in writing, software
11104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * See the License for the specific language governing permissions and
14104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * limitations under the License.
15104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
16104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
17104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapackage com.android.gallery3d.exif;
18104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
19104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.util.Log;
20104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
21104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.UnsupportedEncodingException;
22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteOrder;
23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.ArrayList;
24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.Arrays;
25104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.List;
26104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
27104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/**
28104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * This class stores the EXIF header in IFDs according to the JPEG
29104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * specification. It is the result produced by {@link ExifReader}.
30104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
31104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * @see ExifReader
32104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * @see IfdData
33104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
34104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaclass ExifData {
35104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String TAG = "ExifData";
36104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final byte[] USER_COMMENT_ASCII = {
37104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00
38104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    };
39104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final byte[] USER_COMMENT_JIS = {
40104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            0x4A, 0x49, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00
41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    };
42104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final byte[] USER_COMMENT_UNICODE = {
43104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00
44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    };
45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final IfdData[] mIfdDatas = new IfdData[IfdId.TYPE_IFD_COUNT];
47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private byte[] mThumbnail;
48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ArrayList<byte[]> mStripBytes = new ArrayList<byte[]>();
49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final ByteOrder mByteOrder;
50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    ExifData(ByteOrder order) {
52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mByteOrder = order;
53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Gets the compressed thumbnail. Returns null if there is no compressed
57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * thumbnail.
58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #hasCompressedThumbnail()
60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected byte[] getCompressedThumbnail() {
62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mThumbnail;
63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Sets the compressed thumbnail.
67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void setCompressedThumbnail(byte[] thumbnail) {
69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mThumbnail = thumbnail;
70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Returns true it this header contains a compressed thumbnail.
74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected boolean hasCompressedThumbnail() {
76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mThumbnail != null;
77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Adds an uncompressed strip.
81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void setStripBytes(int index, byte[] strip) {
83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (index < mStripBytes.size()) {
84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mStripBytes.set(index, strip);
85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            for (int i = mStripBytes.size(); i < index; i++) {
87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mStripBytes.add(null);
88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mStripBytes.add(strip);
90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Gets the strip count.
95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int getStripCount() {
97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mStripBytes.size();
98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Gets the strip at the specified index.
102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @exceptions #IndexOutOfBoundException
104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected byte[] getStrip(int index) {
106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mStripBytes.get(index);
107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Returns true if this header contains uncompressed strip.
111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected boolean hasUncompressedStrip() {
113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mStripBytes.size() != 0;
114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
115104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
116104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
117104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Gets the byte order.
118104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
119104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ByteOrder getByteOrder() {
120104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mByteOrder;
121104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
122104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
123104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
124104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Returns the {@link IfdData} object corresponding to a given IFD if it
125104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * exists or null.
126104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected IfdData getIfdData(int ifdId) {
128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (ExifTag.isValidIfd(ifdId)) {
129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return mIfdDatas[ifdId];
130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return null;
132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Adds IFD data. If IFD data of the same type already exists, it will be
136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * replaced by the new data.
137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void addIfdData(IfdData data) {
139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mIfdDatas[data.getId()] = data;
140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Returns the {@link IfdData} object corresponding to a given IFD or
144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * generates one if none exist.
145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected IfdData getOrCreateIfdData(int ifdId) {
147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData ifdData = mIfdDatas[ifdId];
148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (ifdData == null) {
149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifdData = new IfdData(ifdId);
150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mIfdDatas[ifdId] = ifdData;
151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return ifdData;
153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Returns the tag with a given TID in the given IFD if the tag exists.
157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Otherwise returns null.
158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ExifTag getTag(short tag, int ifd) {
160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData ifdData = mIfdDatas[ifd];
161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return (ifdData == null) ? null : ifdData.getTag(tag);
162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Adds the given ExifTag to its default IFD and returns an existing ExifTag
166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * with the same TID or null if none exist.
167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ExifTag addTag(ExifTag tag) {
169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (tag != null) {
170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int ifd = tag.getIfd();
171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return addTag(tag, ifd);
172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return null;
174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Adds the given ExifTag to the given IFD and returns an existing ExifTag
178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * with the same TID or null if none exist.
179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ExifTag addTag(ExifTag tag, int ifdId) {
181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (tag != null && ExifTag.isValidIfd(ifdId)) {
182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            IfdData ifdData = getOrCreateIfdData(ifdId);
183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return ifdData.setTag(tag);
184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return null;
186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void clearThumbnailAndStrips() {
189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mThumbnail = null;
190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mStripBytes.clear();
191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Removes the thumbnail and its related tags. IFD1 will be removed.
195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
196104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void removeThumbnailData() {
197104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        clearThumbnailAndStrips();
198104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mIfdDatas[IfdId.TYPE_IFD_1] = null;
199104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
200104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
201104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
202104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Removes the tag with a given TID and IFD.
203104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
204104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void removeTag(short tagId, int ifdId) {
205104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData ifdData = mIfdDatas[ifdId];
206104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (ifdData == null) {
207104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
208104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
209104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ifdData.removeTag(tagId);
210104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
211104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
212104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
213104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Decodes the user comment tag into string as specified in the EXIF
214104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * standard. Returns null if decoding failed.
215104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
216104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected String getUserComment() {
217104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData ifdData = mIfdDatas[IfdId.TYPE_IFD_0];
218104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (ifdData == null) {
219104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
220104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
221104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ExifTag tag = ifdData.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_USER_COMMENT));
222104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (tag == null) {
223104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
224104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
225104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (tag.getComponentCount() < 8) {
226104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
227104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
228104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
229104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        byte[] buf = new byte[tag.getComponentCount()];
230104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        tag.getBytes(buf);
231104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
232104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        byte[] code = new byte[8];
233104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        System.arraycopy(buf, 0, code, 0, 8);
234104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
235104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        try {
236104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (Arrays.equals(code, USER_COMMENT_ASCII)) {
237104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return new String(buf, 8, buf.length - 8, "US-ASCII");
238104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else if (Arrays.equals(code, USER_COMMENT_JIS)) {
239104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return new String(buf, 8, buf.length - 8, "EUC-JP");
240104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else if (Arrays.equals(code, USER_COMMENT_UNICODE)) {
241104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return new String(buf, 8, buf.length - 8, "UTF-16");
242104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else {
243104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return null;
244104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
245104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } catch (UnsupportedEncodingException e) {
246104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.w(TAG, "Failed to decode the user comment");
247104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
248104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
249104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
250104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
251104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
252104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Returns a list of all {@link ExifTag}s in the ExifData or null if there
253104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * are none.
254104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
255104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected List<ExifTag> getAllTags() {
256104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ArrayList<ExifTag> ret = new ArrayList<ExifTag>();
257104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for (IfdData d : mIfdDatas) {
258104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (d != null) {
259104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ExifTag[] tags = d.getAllTags();
260104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (tags != null) {
261104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    for (ExifTag t : tags) {
262104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        ret.add(t);
263104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
264104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
265104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
266104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
267104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (ret.size() == 0) {
268104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
269104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
270104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return ret;
271104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
272104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
273104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
274104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Returns a list of all {@link ExifTag}s in a given IFD or null if there
275104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * are none.
276104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
277104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected List<ExifTag> getAllTagsForIfd(int ifd) {
278104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData d = mIfdDatas[ifd];
279104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (d == null) {
280104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
281104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
282104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ExifTag[] tags = d.getAllTags();
283104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (tags == null) {
284104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
285104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
286104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ArrayList<ExifTag> ret = new ArrayList<ExifTag>(tags.length);
287104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for (ExifTag t : tags) {
288104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ret.add(t);
289104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
290104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (ret.size() == 0) {
291104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
292104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
293104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return ret;
294104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
295104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
296104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
297104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Returns a list of all {@link ExifTag}s with a given TID or null if there
298104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * are none.
299104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
300104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected List<ExifTag> getAllTagsForTagId(short tag) {
301104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ArrayList<ExifTag> ret = new ArrayList<ExifTag>();
302104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for (IfdData d : mIfdDatas) {
303104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (d != null) {
304104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ExifTag t = d.getTag(tag);
305104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (t != null) {
306104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    ret.add(t);
307104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
308104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
309104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
310104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (ret.size() == 0) {
311104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
312104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
313104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return ret;
314104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
315104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
316104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
317104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public boolean equals(Object obj) {
318104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (this == obj) {
319104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return true;
320104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
321104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (obj == null) {
322104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return false;
323104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
324104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (obj instanceof ExifData) {
325104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifData data = (ExifData) obj;
326104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (data.mByteOrder != mByteOrder ||
327104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    data.mStripBytes.size() != mStripBytes.size() ||
328104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    !Arrays.equals(data.mThumbnail, mThumbnail)) {
329104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return false;
330104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
331104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            for (int i = 0; i < mStripBytes.size(); i++) {
332104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (!Arrays.equals(data.mStripBytes.get(i), mStripBytes.get(i))) {
333104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    return false;
334104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
335104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
336104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
337104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                IfdData ifd1 = data.getIfdData(i);
338104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                IfdData ifd2 = getIfdData(i);
339104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (ifd1 != ifd2 && ifd1 != null && !ifd1.equals(ifd2)) {
340104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    return false;
341104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
342104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
343104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return true;
344104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
345104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return false;
346104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
347104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
348104c45677660586026a7e74ef8c47d396403d50eMichael Jurka}
349