MiniThumbFilepublic class MiniThumbFile extends Object This class handles the mini-thumb file. A mini-thumb file consists
of blocks, indexed by id. Each block has BYTES_PER_MINTHUMB bytes in the
following format:
1 byte status (0 = empty, 1 = mini-thumb available)
8 bytes magic (a magic number to match what's in the database)
4 bytes data length (LEN)
LEN bytes jpeg data
(the remaining bytes are unused) |
Fields Summary |
---|
private static final String | TAG | private static final int | MINI_THUMB_DATA_FILE_VERSION | public static final int | BYTES_PER_MINTHUMB | private static final int | HEADER_SIZE | private android.net.Uri | mUri | private RandomAccessFile | mMiniThumbFile | private FileChannel | mChannel | private ByteBuffer | mBuffer | private static final Hashtable | sThumbFiles |
Constructors Summary |
---|
public MiniThumbFile(android.net.Uri uri)
mUri = uri;
mBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB);
|
Methods Summary |
---|
public synchronized void | deactivate()
if (mMiniThumbFile != null) {
try {
mMiniThumbFile.close();
mMiniThumbFile = null;
} catch (IOException ex) {
// ignore exception
}
}
| public synchronized long | getMagic(long id)
// check the mini thumb file for the right data. Right is
// defined as having the right magic number at the offset
// reserved for this "id".
RandomAccessFile r = miniThumbDataFile();
if (r != null) {
long pos = id * BYTES_PER_MINTHUMB;
FileLock lock = null;
try {
mBuffer.clear();
mBuffer.limit(1 + 8);
lock = mChannel.lock(pos, 1 + 8, true);
// check that we can read the following 9 bytes
// (1 for the "status" and 8 for the long)
if (mChannel.read(mBuffer, pos) == 9) {
mBuffer.position(0);
if (mBuffer.get() == 1) {
return mBuffer.getLong();
}
}
} catch (IOException ex) {
Log.v(TAG, "Got exception checking file magic: ", ex);
} catch (RuntimeException ex) {
// Other NIO related exception like disk full, read only channel..etc
Log.e(TAG, "Got exception when reading magic, id = " + id +
", disk full or mount read-only? " + ex.getClass());
} finally {
try {
if (lock != null) lock.release();
}
catch (IOException ex) {
// ignore it.
}
}
}
return 0;
| public synchronized byte[] | getMiniThumbFromFile(long id, byte[] data)Gallery app can use this method to retrieve mini-thumbnail. Full size
images share the same IDs with their corresponding thumbnails.
RandomAccessFile r = miniThumbDataFile();
if (r == null) return null;
long pos = id * BYTES_PER_MINTHUMB;
FileLock lock = null;
try {
mBuffer.clear();
lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, true);
int size = mChannel.read(mBuffer, pos);
if (size > 1 + 8 + 4) { // flag, magic, length
mBuffer.position(0);
byte flag = mBuffer.get();
long magic = mBuffer.getLong();
int length = mBuffer.getInt();
if (size >= 1 + 8 + 4 + length && length != 0 && magic != 0 && flag == 1 &&
data.length >= length) {
mBuffer.get(data, 0, length);
return data;
}
}
} catch (IOException ex) {
Log.w(TAG, "got exception when reading thumbnail id=" + id + ", exception: " + ex);
} catch (RuntimeException ex) {
// Other NIO related exception like disk full, read only channel..etc
Log.e(TAG, "Got exception when reading thumbnail, id = " + id +
", disk full or mount read-only? " + ex.getClass());
} finally {
try {
if (lock != null) lock.release();
}
catch (IOException ex) {
// ignore it.
}
}
return null;
| public static synchronized android.media.MiniThumbFile | instance(android.net.Uri uri)
String type = uri.getPathSegments().get(1);
MiniThumbFile file = sThumbFiles.get(type);
// Log.v(TAG, "get minithumbfile for type: "+type);
if (file == null) {
file = new MiniThumbFile(
Uri.parse("content://media/external/" + type + "/media"));
sThumbFiles.put(type, file);
}
return file;
| private java.io.RandomAccessFile | miniThumbDataFile()
if (mMiniThumbFile == null) {
removeOldFile();
String path = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION);
File directory = new File(path).getParentFile();
if (!directory.isDirectory()) {
if (!directory.mkdirs()) {
Log.e(TAG, "Unable to create .thumbnails directory "
+ directory.toString());
}
}
File f = new File(path);
try {
mMiniThumbFile = new RandomAccessFile(f, "rw");
} catch (IOException ex) {
// Open as read-only so we can at least read the existing
// thumbnails.
try {
mMiniThumbFile = new RandomAccessFile(f, "r");
} catch (IOException ex2) {
// ignore exception
}
}
if (mMiniThumbFile != null) {
mChannel = mMiniThumbFile.getChannel();
}
}
return mMiniThumbFile;
| private java.lang.String | randomAccessFilePath(int version)
String directoryName =
Environment.getExternalStorageDirectory().toString()
+ "/DCIM/.thumbnails";
return directoryName + "/.thumbdata" + version + "-" + mUri.hashCode();
| private void | removeOldFile()
String oldPath = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION - 1);
File oldFile = new File(oldPath);
if (oldFile.exists()) {
try {
oldFile.delete();
} catch (SecurityException ex) {
// ignore
}
}
| public static synchronized void | reset()We store different types of thumbnails in different files. To remain backward compatibility,
we should hashcode of content://media/external/images/media remains the same.
for (MiniThumbFile file : sThumbFiles.values()) {
file.deactivate();
}
sThumbFiles.clear();
| public synchronized void | saveMiniThumbToFile(byte[] data, long id, long magic)
RandomAccessFile r = miniThumbDataFile();
if (r == null) return;
long pos = id * BYTES_PER_MINTHUMB;
FileLock lock = null;
try {
if (data != null) {
if (data.length > BYTES_PER_MINTHUMB - HEADER_SIZE) {
// not enough space to store it.
return;
}
mBuffer.clear();
mBuffer.put((byte) 1);
mBuffer.putLong(magic);
mBuffer.putInt(data.length);
mBuffer.put(data);
mBuffer.flip();
lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false);
mChannel.write(mBuffer, pos);
}
} catch (IOException ex) {
Log.e(TAG, "couldn't save mini thumbnail data for "
+ id + "; ", ex);
throw ex;
} catch (RuntimeException ex) {
// Other NIO related exception like disk full, read only channel..etc
Log.e(TAG, "couldn't save mini thumbnail data for "
+ id + "; disk full or mount read-only? " + ex.getClass());
} finally {
try {
if (lock != null) lock.release();
}
catch (IOException ex) {
// ignore it.
}
}
|
|