/*
ThetaObjectStorage
Copyright (c) 2015 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.theta.data;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.StatFs;
import android.provider.BaseColumns;
import org.deviceconnect.android.deviceplugin.theta.BuildConfig;
import org.deviceconnect.android.deviceplugin.theta.core.ThetaDeviceException;
import org.deviceconnect.android.deviceplugin.theta.core.ThetaObject;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
/**
* Save the data of Theta in Storage of Android.
*
* @author NTT DOCOMO, INC.
*/
public class ThetaObjectStorage {
/** THETA Device Plug-in apk limit size. */
private static final int LIMIT_APK_SIZE = 100;
/**
* DB Name.
*/
private static final String DB_NAME = "theta_object_storage.db";
/**
* DB Version.
*/
private static final int DB_VERSION = 1;
/**
* Gallery Table.
*/
private static final String THETA_GALLERY_TBL_NAME = "theta_gallery_tbl";
/** File Name. */
private static final String THETA_FILE_NAME = "file_name";
/** Date Time. */
private static final String THETA_DATE_TIME = "date_time";
/** width. */
private static final String THETA_WIDTH = "width";
/** height. */
private static final String THETA_HEIGHT = "height";
/** MimeType. */
private static final String THETA_MIME_TYPE = "mime_type";
/** Thumbnail URL. */
private static final String THETA_THUMB_URL = "thumb_url";
/** Main Data URL. */
private static final String THETA_MAIN_URL = "main_url";
/** Theta's Image mimeType. */
private static final String MIMETYPE_IMAGE = "image/jpeg";
/** Theta's Movie mimeType. */
private static final String MIMETYPE_VIDEO = "video/mpeg";
/**
* DB Helper.
*/
private ThetaDBHelper mThetaDBHelper;
/** Context. */
private Context mContext;
/** Listener. */
private Listener mListener;
/**
* Logger.
*/
private static final Logger sLogger = Logger.getLogger("theta.sampleapp");
/** Storage's Listener. */
public interface Listener {
void onCompleted(final DBMode mode, final long result);
}
/** DBMode. */
public enum DBMode {
/** Add. */
Add,
/** Update. */
Update,
/** Delete. */
Delete
}
/**
* Constructor.
*/
public ThetaObjectStorage(final Context context) {
mContext = context;
mThetaDBHelper = new ThetaDBHelper(context);
}
/**
* Set Theta Object Storage Listener.
* @param l Listener
*/
public void setListener(final Listener l) {
mListener = l;
}
/**
* Add Theta Object's Cache.
* @param object Theta Object
*/
public synchronized void addThetaObjectCache(final ThetaObject object) {
ContentValues values = makeContentValue(object);
if (!object.isFetched(ThetaObject.DataType.MAIN)
|| !object.isFetched(ThetaObject.DataType.THUMBNAIL)) {
if (mListener != null) {
mListener.onCompleted(DBMode.Add, -1);
}
return;
}
SQLiteDatabase db = mThetaDBHelper.getWritableDatabase();
long result = -1;
try {
result = db.insert(THETA_GALLERY_TBL_NAME, null, values);
} finally {
db.close();
if (mListener != null) {
mListener.onCompleted(DBMode.Add, result);
}
}
}
/**
* Update Theta Object Cache.
* @param object Theta Object
*/
public synchronized void updateThetaObjectCache(final ThetaObject object) {
ContentValues values = makeContentValue(object);
String whereClause = THETA_FILE_NAME + "=?";
String[] whereArgs = {
object.getFileName()
};
SQLiteDatabase db = mThetaDBHelper.getWritableDatabase();
long result = -1;
try {
result = db.update(THETA_GALLERY_TBL_NAME, values, whereClause, whereArgs);
} finally {
db.close();
if (mListener != null) {
mListener.onCompleted(DBMode.Update, result);
}
}
}
/**
* Remove Theta Object Cache.
* @param object Theta Object
* @return Success or failure
*/
public synchronized void removeThetaObjectCache(final ThetaObject object) {
List<ThetaObject> removeObj = geThetaObjectCaches(object.getFileName());
ThetaObjectDB obj = (ThetaObjectDB) removeObj.get(0);
if (obj.getThumbnailURL() != null) {
File thumbFile = new File(obj.getThumbnailURL());
thumbFile.delete();
}
if (obj.getMainURL() != null) {
File dataFile = new File(obj.getMainURL());
dataFile.delete();
}
String whereClause = THETA_FILE_NAME + "=?";
String[] whereArgs = {
object.getFileName()
};
SQLiteDatabase db = mThetaDBHelper.getWritableDatabase();
int isDeleteCache = -1;
try {
isDeleteCache = db.delete(THETA_GALLERY_TBL_NAME, whereClause, whereArgs);
} finally {
db.close();
if (mListener != null) {
mListener.onCompleted(DBMode.Delete, isDeleteCache);
}
}
}
/**
* Get ThetaObject Caches.
* @return ThetaObjectList
*/
public synchronized List<ThetaObject> geThetaObjectCaches(final String name) {
String sql = "SELECT * FROM " + THETA_GALLERY_TBL_NAME;
if (name != null) {
sql += " WHERE " + THETA_FILE_NAME + "='" + name + "' ";
}
sql += " ORDER BY " + THETA_DATE_TIME + " DESC;";
String[] selectionArgs = {};
SQLiteDatabase db = null;
List<ThetaObject> objects = new ArrayList<ThetaObject>();
try {
db = mThetaDBHelper.getReadableDatabase();
Cursor cursor = db.rawQuery(sql, selectionArgs);
boolean next = cursor.moveToFirst();
while (next) {
String fileName = cursor.getString(cursor.getColumnIndex(THETA_FILE_NAME));
String dateTime = cursor.getString(cursor.getColumnIndex(THETA_DATE_TIME));
int width = cursor.getInt(cursor.getColumnIndex(THETA_WIDTH));
int height = cursor.getInt(cursor.getColumnIndex(THETA_HEIGHT));
String mimeType = cursor.getString(cursor.getColumnIndex(THETA_MIME_TYPE));
String thumbURL = cursor.getString(cursor.getColumnIndex(THETA_THUMB_URL));
String mainURL = cursor.getString(cursor.getColumnIndex(THETA_MAIN_URL));
ThetaObject object = new ThetaObjectDB(fileName, dateTime, width, height,
mimeType, thumbURL, mainURL);
objects.add(object);
next = cursor.moveToNext();
}
} finally {
db.close();
}
return objects;
}
/**
* THETA Data's index.
* @param name search data name
* @return index
*/
public synchronized int getThetaObjectCachesIndex(final String name) {
List<ThetaObject> objects = geThetaObjectCaches(name);
for (int i = 0; i < objects.size(); i++) {
if (objects.get(i).getFileName().equals(name)) {
return i;
}
}
return -1;
}
/** Make Content Value. */
private ContentValues makeContentValue(final ThetaObject object) {
ContentValues values = new ContentValues();
values.put(THETA_FILE_NAME, object.getFileName());
values.put(THETA_DATE_TIME, object.getCreationTime());
values.put(THETA_WIDTH, object.getWidth());
values.put(THETA_HEIGHT, object.getHeight());
values.put(THETA_MIME_TYPE, object.getMimeType());
if (object.isImage()) {
// check thumb url
byte[] thumbImage = object.getThumbnailData();
if (thumbImage == null) {
try {
object.fetch(ThetaObject.DataType.THUMBNAIL);
thumbImage = object.getThumbnailData();
} catch (ThetaDeviceException e) {
e.printStackTrace();
}
}
if (thumbImage != null) {
values.put(THETA_THUMB_URL, saveThetaImage("thumbnails",
object.getFileName(),
thumbImage));
}
// check main data url
byte[] dataImage = object.getMainData();
if (dataImage == null) {
try {
object.fetch(ThetaObject.DataType.MAIN);
dataImage = object.getMainData();
} catch (ThetaDeviceException e) {
e.printStackTrace();
}
}
if (dataImage != null) {
values.put(THETA_MAIN_URL, saveThetaImage("images",
object.getFileName(),
dataImage));
}
}
return values;
}
// save theta's image
private String saveThetaImage(final String cacheFoldar,
final String originalFileName,
final byte[] thetaImage) {
String root = Environment.getExternalStorageDirectory().getPath() + "/"
+ mContext.getPackageName() + "/" + cacheFoldar + "/";
File dir = new File(root);
if (!dir.exists()) {
dir.mkdir();
}
Date date = new Date();
SimpleDateFormat fileDate = new SimpleDateFormat("yyyyMMdd_HHmmss");
final String fileName = originalFileName + "." + fileDate.format(date);
final String filePath = root + fileName;
Uri u = Uri.parse("file://" + filePath);
ContentResolver contentResolver = mContext.getContentResolver();
OutputStream out = null;
try {
out = contentResolver.openOutputStream(u, "w");
out.write(thetaImage);
out.flush();
} catch (IOException e) {
throw new IOException("Failed to save a file." + fileName);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return filePath;
}
}
// load theta's image data.
private byte[] loadThetaImage(final String filePath) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
ContentResolver contentResolver = mContext.getContentResolver();
InputStream in = null;
int read;
try {
in = contentResolver.openInputStream(Uri.parse("file://" + filePath));
while ((read = in.read(buffer)) > 0) {
out.write(buffer, 0, read);
}
} catch (IOException e) {
throw new IOException("Failed to load a file." + filePath);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return out.toByteArray();
}
}
/** Theta Object Storage Model. */
private class ThetaObjectDB implements ThetaObject {
private final String mFilename;
private final String mDateTime;
private final int mWidth;
private final int mHeight;
private String mMimeType;
private byte[] mThumbnail;
private byte[] mMain;
private String mThumbURL;
private String mMainURL;
ThetaObjectDB(final String filename,
final String date,
final int width,
final int height,
final String mimeType,
final String thumbURL,
final String mainURL) {
mFilename = filename;
mWidth = width;
mHeight = height;
mMimeType = mimeType;
mDateTime = date;
mThumbURL = thumbURL;
mMainURL = mainURL;
}
@Override
public void fetch(final DataType type) {
switch (type) {
case THUMBNAIL:
mThumbnail = loadThetaImage(mThumbURL);
break;
case MAIN:
mMain = loadThetaImage(mMainURL);
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public boolean isFetched(final DataType type) {
switch (type) {
case THUMBNAIL:
return mThumbnail != null;
case MAIN:
return mMain != null;
default:
throw new IllegalArgumentException();
}
}
@Override
public void remove() {
removeThetaObjectCache(this);
}
@Override
public void clear(final DataType type) {
switch (type) {
case THUMBNAIL:
mThumbnail = null;
break;
case MAIN:
mMain = null;
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public String getMimeType() {
return mMimeType;
}
@Override
public Boolean isImage() {
return (mMimeType.equals(MIMETYPE_IMAGE));
}
@Override
public String getCreationTime() {
return mDateTime;
}
@Override
public long getCreationTimeWithUnixTime() {
return -1; // Unsupported method.
}
@Override
public String getFileName() {
return mFilename;
}
@Override
public Integer getWidth() {
return mWidth;
}
@Override
public Integer getHeight() {
return mHeight;
}
@Override
public byte[] getThumbnailData() {
return mThumbnail;
}
@Override
public byte[] getMainData() {
return mMain;
}
public String getThumbnailURL() {
return mThumbURL;
}
public String getMainURL() {
return mMainURL;
}
}
/**
* DB Helper to store the Theta storage
*/
private class ThetaDBHelper extends SQLiteOpenHelper {
/**
* Constructor.
* param context application context
*/
public ThetaDBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(final SQLiteDatabase db) {
createDB(db);
}
@Override
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + THETA_GALLERY_TBL_NAME);
createDB(db);
}
/**
* DB to store the Theta storage
* param db DB
*/
private void createDB(final SQLiteDatabase db) {
String thetaStorageSQL = "CREATE TABLE " + THETA_GALLERY_TBL_NAME + " ("
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ THETA_FILE_NAME + " TEXT NOT NULL,"
+ THETA_DATE_TIME + " TEXT NOT NULL,"
+ THETA_WIDTH + " INTEGER,"
+ THETA_HEIGHT + " INTEGER,"
+ THETA_MIME_TYPE + " TEXT NOT NULL,"
+ THETA_THUMB_URL + " TEXT,"
+ THETA_MAIN_URL + " TEXT"
+ ");";
db.execSQL(thetaStorageSQL);
}
}
/**
* Check Android Storage size.
* @return Return a false if true, otherwise there is a minimum required value or more free
*/
public static boolean hasEnoughStorageSize() {
StatFs stat = new StatFs(Environment.getDataDirectory().getPath());
float total = 1.0f;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
total = stat.getTotalBytes();
} else {
total = (float) stat.getBlockSize() * stat.getAvailableBlocks();
}
int v = (int) (total / (1024.f * 1024.f));
if(BuildConfig.DEBUG) {
if(v < LIMIT_APK_SIZE) {
sLogger.warning("hasEnoughStorageSize is less than " + LIMIT_APK_SIZE + ", rest size =" + v);
}
}
return v >= LIMIT_APK_SIZE;
}
}