/*
* Copyright (C) 2006 The Android Open Source Project
* Copyright (C) 2013 YIXIA.COM
*
* 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 io.vov.vitamio.provider;
import android.net.Uri;
import android.os.Environment;
import io.vov.vitamio.provider.MediaStore.Video;
import io.vov.vitamio.utils.Log;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Hashtable;
public class MiniThumbFile {
protected static final int BYTES_PER_MINTHUMB = 10000;
private static final int MINI_THUMB_DATA_FILE_VERSION = 7;
private static final int HEADER_SIZE = 1 + 8 + 4;
private static Hashtable<String, MiniThumbFile> sThumbFiles = new Hashtable<String, MiniThumbFile>();
private Uri mUri;
private RandomAccessFile mMiniThumbFile;
private FileChannel mChannel;
private ByteBuffer mBuffer;
public MiniThumbFile(Uri uri) {
mUri = uri;
mBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB);
}
protected static synchronized void reset() {
for (MiniThumbFile file : sThumbFiles.values())
file.deactivate();
sThumbFiles.clear();
}
protected static synchronized MiniThumbFile instance(Uri uri) {
String type = uri.getPathSegments().get(0);
MiniThumbFile file = sThumbFiles.get(type);
if (file == null) {
file = new MiniThumbFile(Uri.parse(MediaStore.CONTENT_AUTHORITY_SLASH + type + "/media"));
sThumbFiles.put(type, file);
}
return file;
}
private String randomAccessFilePath(int version) {
String directoryName = Environment.getExternalStorageDirectory().toString() + "/" + Video.Thumbnails.THUMBNAILS_DIRECTORY;
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) {
}
}
}
private 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("Unable to create .thumbnails directory %s", directory.toString());
}
File f = new File(path);
try {
mMiniThumbFile = new RandomAccessFile(f, "rw");
} catch (IOException ex) {
try {
mMiniThumbFile = new RandomAccessFile(f, "r");
} catch (IOException ex2) {
}
}
if (mMiniThumbFile != null)
mChannel = mMiniThumbFile.getChannel();
}
return mMiniThumbFile;
}
protected synchronized void deactivate() {
if (mMiniThumbFile != null) {
try {
mMiniThumbFile.close();
mMiniThumbFile = null;
} catch (IOException ex) {
}
}
}
protected synchronized long getMagic(long 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);
if (mChannel.read(mBuffer, pos) == 9) {
mBuffer.position(0);
if (mBuffer.get() == 1)
return mBuffer.getLong();
}
} catch (IOException ex) {
Log.e("Got exception checking file magic: ", ex);
} catch (RuntimeException ex) {
Log.e("Got exception when reading magic, id = %d, disk full or mount read-only? %s", id, ex.getClass().toString());
} finally {
try {
if (lock != null)
lock.release();
} catch (IOException ex) {
}
}
}
return 0;
}
protected synchronized void saveMiniThumbToFile(byte[] data, long id, long magic) throws IOException {
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)
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("couldn't save mini thumbnail data for %d; %s", id, ex.getMessage());
throw ex;
} catch (RuntimeException ex) {
Log.e("couldn't save mini thumbnail data for %d, disk full or mount read-only? %s", id, ex.getClass().toString());
} finally {
try {
if (lock != null)
lock.release();
} catch (IOException ex) {
}
}
}
protected synchronized byte[] getMiniThumbFromFile(long id, byte[] data) {
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) {
mBuffer.position(9);
int length = mBuffer.getInt();
if (size >= 1 + 8 + 4 + length && data.length >= length) {
mBuffer.get(data, 0, length);
return data;
}
}
} catch (IOException ex) {
Log.e("got exception when reading thumbnail id = %d, exception: %s", id, ex.getMessage());
} catch (RuntimeException ex) {
Log.e("Got exception when reading thumbnail, id = %d, disk full or mount read-only? %s", id, ex.getClass().toString());
} finally {
try {
if (lock != null)
lock.release();
} catch (IOException ex) {
}
}
return null;
}
}