package com.rubenvandeven.emotionhero; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.PointF; import android.util.Log; import java.util.ArrayList; import java.util.Date; /** * Created by ruben on 02/09/16. * * To test data: * adb -d shell "run-as com.rubenvandeven.emotionhero ls /data/data/com.rubenvandeven.emotionhero/databases/" * adb -d shell "run-as com.rubenvandeven.emotionhero cp /data/data/com.rubenvandeven.emotionhero/databases/emotionhero.db /sdcard/emotionhero.db" * adb pull /sdcard/emotionhero.db */ public class GameOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 7; private static final String GAME_TABLE_NAME = "games"; private static final String GAME_TABLE_CREATE = "CREATE TABLE " + GAME_TABLE_NAME + " (" + "id INTEGER NOT NULL," + "lvl_id INTEGER NOT NULL," + "score VARCHAR(255) NOT NULL," + "bonus VARCHAR(255) NOT NULL," + "time INTEGER NOT NULL," + "lost_face_time VARCHAR(255) NOT NULL," + "remoteId VARCHAR(255) DEFAULT NULL, " + "PRIMARY KEY(id));"; private static final String[] GAME_PROJECTION = { "id", "lvl_id", "score", "bonus", "time", "lost_face_time", "remoteId" }; private static final String HIT_TABLE_NAME = "hits"; public static final String HIT_TABLE_CREATE = "CREATE TABLE hits (" + "id INTEGER NOT NULL," + "game_id INTEGER NOT NULL," + "target_index INTEGER NOT_NULL," + "score VARCHAR(20)," + "bonus VARCHAR(20)," + "remoteId VARCHAR(20)," + "glasses VARCHAR(20)," + "ethnicity VARCHAR(20)," + "age VARCHAR(20)," + "gender VARCHAR(1)," + "anger VARCHAR(20)," + "contempt VARCHAR(20)," + "disgust VARCHAR(20)," + "fear VARCHAR(20)," + "joy VARCHAR(20)," + "sadness VARCHAR(20)," + "surprise VARCHAR(20)," + "roll VARCHAR(20)," + "pitch VARCHAR(20)," + "yaw VARCHAR(20)," + "inter_ocular_distance VARCHAR(20)," + "mouth_open VARCHAR(20)," + "lip_press VARCHAR(20)," + "brow_raise VARCHAR(20)," + "nose_wrinkler VARCHAR(20)," + "lip_depressor VARCHAR(20)," + "brow_furrow VARCHAR(20)," + "attention VARCHAR(20)," + "smile VARCHAR(20)," + "inner_brow_raiser VARCHAR(20)," + "chin_raiser VARCHAR(20)," + "smirk VARCHAR(20)," + "lip_suck VARCHAR(20)," + "upper_lip_raiser VARCHAR(20)," + "lip_pucker VARCHAR(20)," + "eye_closure VARCHAR(20)," + "engagement VARCHAR(20)," + "valence VARCHAR(20)," + "dimpler VARCHAR(20)," + "cheek_raise VARCHAR(20)," + "eye_widen VARCHAR(20)," + "jaw_drop VARCHAR(20)," + "lid_tighten VARCHAR(20)," + "lip_stretch VARCHAR(20)," + "point_0x VARCHAR(20)," + "point_0y VARCHAR(20)," + "point_1x VARCHAR(20)," + "point_1y VARCHAR(20)," + "point_2x VARCHAR(20)," + "point_2y VARCHAR(20)," + "point_3x VARCHAR(20)," + "point_3y VARCHAR(20)," + "point_4x VARCHAR(20)," + "point_4y VARCHAR(20)," + "point_5x VARCHAR(20)," + "point_5y VARCHAR(20)," + "point_6x VARCHAR(20)," + "point_6y VARCHAR(20)," + "point_7x VARCHAR(20)," + "point_7y VARCHAR(20)," + "point_8x VARCHAR(20)," + "point_8y VARCHAR(20)," + "point_9x VARCHAR(20)," + "point_9y VARCHAR(20)," + "point_10x VARCHAR(20)," + "point_10y VARCHAR(20)," + "point_11x VARCHAR(20)," + "point_11y VARCHAR(20)," + "point_12x VARCHAR(20)," + "point_12y VARCHAR(20)," + "point_13x VARCHAR(20)," + "point_13y VARCHAR(20)," + "point_14x VARCHAR(20)," + "point_14y VARCHAR(20)," + "point_15x VARCHAR(20)," + "point_15y VARCHAR(20)," + "point_16x VARCHAR(20)," + "point_16y VARCHAR(20)," + "point_17x VARCHAR(20)," + "point_17y VARCHAR(20)," + "point_18x VARCHAR(20)," + "point_18y VARCHAR(20)," + "point_19x VARCHAR(20)," + "point_19y VARCHAR(20)," + "point_20x VARCHAR(20)," + "point_20y VARCHAR(20)," + "point_21x VARCHAR(20)," + "point_21y VARCHAR(20)," + "point_22x VARCHAR(20)," + "point_22y VARCHAR(20)," + "point_23x VARCHAR(20)," + "point_23y VARCHAR(20)," + "point_24x VARCHAR(20)," + "point_24y VARCHAR(20)," + "point_25x VARCHAR(20)," + "point_25y VARCHAR(20)," + "point_26x VARCHAR(20)," + "point_26y VARCHAR(20)," + "point_27x VARCHAR(20)," + "point_27y VARCHAR(20)," + "point_28x VARCHAR(20)," + "point_28y VARCHAR(20)," + "point_29x VARCHAR(20)," + "point_29y VARCHAR(20)," + "point_30x VARCHAR(20)," + "point_30y VARCHAR(20)," + "point_31x VARCHAR(20)," + "point_31y VARCHAR(20)," + "point_32x VARCHAR(20)," + "point_32y VARCHAR(20)," + "point_33x VARCHAR(20)," + "point_33y VARCHAR(20)," + "image BLOB," + "PRIMARY KEY (id));" ; private static final String[] HIT_PROJECTION = { "id","game_id", "target_index","score","bonus","remoteId","glasses","ethnicity","age","gender","anger","contempt","disgust","fear","joy","sadness","surprise","roll","pitch","yaw","inter_ocular_distance","mouth_open","lip_press","brow_raise","nose_wrinkler","lip_depressor","brow_furrow","attention","smile","inner_brow_raiser","chin_raiser","smirk","lip_suck","upper_lip_raiser","lip_pucker","eye_closure","engagement","valence","dimpler","cheek_raise","eye_widen","jaw_drop","lid_tighten","lip_stretch","point_0x","point_0y","point_1x","point_1y","point_2x","point_2y","point_3x","point_3y","point_4x","point_4y","point_5x","point_5y","point_6x","point_6y","point_7x","point_7y","point_8x","point_8y","point_9x","point_9y","point_10x","point_10y","point_11x","point_11y","point_12x","point_12y","point_13x","point_13y","point_14x","point_14y","point_15x","point_15y","point_16x","point_16y","point_17x","point_17y","point_18x","point_18y","point_19x","point_19y","point_20x","point_20y","point_21x","point_21y","point_22x","point_22y","point_23x","point_23y","point_24x","point_24y","point_25x","point_25y","point_26x","point_26y","point_27x","point_27y","point_28x","point_28y","point_29x","point_29y","point_30x","point_30y","point_31x","point_31y","point_32x","point_32y","point_33x","point_33y" }; private static final String GAME_ACH_TABLE_NAME = "game_achievements"; private static final String GAME_ACH_TABLE_CREATE = "CREATE TABLE " + GAME_ACH_TABLE_NAME + " (" + "id INTEGER NOT NULL," + "game_id INTEGER NOT NULL," + "achievement_id INTEGER NOT NULL," + "PRIMARY KEY(id));"; private static final String[] GAME_ACH_PROJECTION = { "id", "game_id", "achievement_id", }; private Context context; GameOpenHelper(Context context) { super(context, "emotionhero.db", null, DATABASE_VERSION); this.context = context; } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(GAME_TABLE_CREATE); db.execSQL(HIT_TABLE_CREATE); db.execSQL(GAME_ACH_TABLE_CREATE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // until now there is only 1 database version.. so no alter table statements if(oldVersion == 1 && newVersion == 2) { // run alter statements db.execSQL("DROP TABLE hits"); db.execSQL("DROP TABLE games"); db.execSQL(GAME_TABLE_CREATE); db.execSQL(HIT_TABLE_CREATE); } if(oldVersion == 2 && newVersion == 3) { db.execSQL("ALTER TABLE hits ADD image BLOB;"); } if(oldVersion == 3 && newVersion == 4) { db.execSQL("ALTER TABLE hits ADD bonus VARCHAR(20);"); db.execSQL("ALTER TABLE games ADD bonus VARCHAR(255);"); } if(oldVersion == 4 && newVersion == 5) { db.execSQL(GAME_ACH_TABLE_CREATE); } if(oldVersion == 5 && newVersion == 6) { db.execSQL("ALTER TABLE games ADD lost_face_time VARCHAR(255);"); } if(oldVersion == 6 && newVersion == 7) { db.execSQL("ALTER TABLE games ADD dimpler VARCHAR(20);"); db.execSQL("ALTER TABLE games ADD cheek_raise VARCHAR(20);" ); db.execSQL("ALTER TABLE games ADD eye_widen VARCHAR(20);"); db.execSQL("ALTER TABLE games ADD jaw_drop VARCHAR(20);"); db.execSQL("ALTER TABLE games ADD lid_tighten VARCHAR(20);"); db.execSQL("ALTER TABLE games ADD lip_stretch VARCHAR(20);"); } } public Bitmap getImageForHit(Hit hit) { String[] params = { Long.toString(hit.id) }; Cursor c = getReadableDatabase().rawQuery("SELECT image FROM hits h WHERE h.id = ?", params); c.moveToFirst(); byte[] imageBytes= c.getBlob(0); if(imageBytes == null) { return null; } return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); } public void clearImageForHit(Hit hit) { String[] params = { Long.toString(hit.id) }; Cursor c = getWritableDatabase().rawQuery("UPDATE hits SET image = NULL WHERE id = ?", params); Log.v("GAME", "clear "+hit.id+" count: "+c.getCount()); } public void insertGame(Game game) { // Gets the data repository in write mode SQLiteDatabase db = getWritableDatabase(); // Create a new map of values, where column names are the keys ContentValues values = new ContentValues(); values.put("lvl_id", game.scenario.id); values.put("score", game.score); values.put("bonus", game.bonus); values.put("time", game.time.getTime()); values.put("lost_face_time", game.lostFaceTime); if(game.remoteId == null) { values.putNull("remoteId"); } else { values.put("remoteId", game.remoteId); } // Insert the new row, returning the primary key value of the new row long newRowId = db.insert(GAME_TABLE_NAME, null, values); game.id = newRowId; for(Hit hit: game.hits.values()) { insertHit(hit); } saveAchievementsForGame(game); } public Game[] getGamesForLevel(int lvl_id) { Scenario scenario = new Scenario(lvl_id, null); return getGamesForScenario(scenario, null); } public Game[] getGamesForScenario(Scenario s, Integer limit) { SQLiteDatabase db = getReadableDatabase(); String selection = "lvl_id = ?"; String[] selectionArgs = { Integer.toString(s.id) }; String sortOrder = "score+bonus DESC"; Cursor c = db.query( GAME_TABLE_NAME, // The table to query GAME_PROJECTION, // The columns to return selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder, // The sort order limit == null ? null : Integer.toString(limit) // no limit! ); return cursorToGames(c, s); } /** * Get the game that was played before this game * @param game * @return Game */ public Game getPreviousGame(Game game) { SQLiteDatabase db = getReadableDatabase(); String selection = "lvl_id = ? AND time < ?"; String[] selectionArgs = { Integer.toString(game.scenario.id), Long.toString(game.time.getTime()) }; String sortOrder = "time DESC"; Cursor c = db.query( GAME_TABLE_NAME, // The table to query GAME_PROJECTION, // The columns to return selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder, // The sort order "1" // only last item ); if(c.getCount() < 1) return null; Game[] games = cursorToGames(c, game.scenario); return games[0]; } public Game[] cursorToGames(Cursor c, Scenario s) { Game[] games = new Game[c.getCount()]; int i = 0; c.moveToFirst(); while (!c.isAfterLast()) { Scenario scenario = s == null ? new Scenario(c.getInt(c.getColumnIndex("lvl_id")), null) : s; Game game = new Game( c.getLong(c.getColumnIndex("id")), scenario, c.getFloat(c.getColumnIndex("score")), c.getFloat(c.getColumnIndex("bonus")), new Date(c.getLong(c.getColumnIndex("time"))), c.getString(c.getColumnIndex("remoteId")) ); game.lostFaceTime = c.getFloat(c.getColumnIndex("lost_face_time")); games[i] = game; this.getHitsForGame(game); // hits are appended to game object game.achievements = getAchievementsOfGame(game); i++; c.moveToNext(); } return games; } public void saveRemoteId(Game game) { SQLiteDatabase db = getWritableDatabase(); // New value for one column ContentValues values = new ContentValues(); values.put("remoteId", game.remoteId); // Which row to update, based on the title String selection = "id = ?"; String[] selectionArgs = { Long.toString(game.id) }; int count = db.update( GAME_TABLE_NAME, values, selection, selectionArgs); } public void saveAchievementsForGame(Game game) { SQLiteDatabase db = getWritableDatabase(); if(game.achievements.size() < 1) { return; } // New value for one column for(Achievement achievement: game.achievements) { ContentValues values = new ContentValues(); values.put("game_id", game.id); values.put("achievement_id", achievement.getId()); db.insert(GAME_ACH_TABLE_NAME, null, values); } } public void insertHit(Hit hit) { // Gets the data repository in write mode SQLiteDatabase db = getWritableDatabase(); // Create a new map of values, where column names are the keys ContentValues values = new ContentValues(); values.put("game_id", hit.game.id); values.put("target_index", hit.target.index); values.put("score", hit.score); values.put("bonus", hit.bonus); values.put("glasses", hit.glasses); values.put("ethnicity", hit.ethnicity); values.put("age", hit.age); values.put("gender", hit.gender); for(Emotion emotion: Emotion.values()) { values.put(emotion.name().toLowerCase(), hit.emotions.get(emotion)); } for(Expression exp: Expression.values()) { values.put(exp.getDbName(), hit.expressions.get(exp)); } int i=0; for(PointF point: hit.points) { values.put("point_"+i+"x", point.x); values.put("point_"+i+"y", point.y); i++; } if(hit.remoteId == null) { values.putNull("remoteId"); } else { values.put("remoteId", hit.remoteId); } byte[] frameBytes = hit.getFrameAsByteArray(); if(frameBytes != null) { values.put("image", frameBytes); } // Insert the new row, returning the primary key value of the new row long newRowId = db.insert(HIT_TABLE_NAME, null, values); hit.id = newRowId; } public Game getHighscoreGameForScenario(Scenario scenario) { Game[] games = getGamesForScenario(scenario, 1); if(games == null || games.length == 0) { return null; } return games[0]; } public ArrayList getHitsForGame(Game game) { SQLiteDatabase db = getReadableDatabase(); String selection = "game_id = ?"; String[] selectionArgs = { Long.toString(game.id) }; String sortOrder = "target_index ASC"; Cursor c = db.query( HIT_TABLE_NAME, // The table to query HIT_PROJECTION, // The columns to return selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder, // The sort order null // no limit! ); return cursorToHits(game, c); } public ArrayList cursorToHits(Game game, Cursor c) { ArrayList hits = new ArrayList(c.getCount()); c.moveToFirst(); while (!c.isAfterLast()) { hits.add(new Hit(game, c)); c.moveToNext(); } return hits; } public void saveRemoteId(Hit hit) { SQLiteDatabase db = getWritableDatabase(); // New value for one column ContentValues values = new ContentValues(); values.put("remoteId", hit.remoteId); // reset image after sync to avoid to much data used on phone // values.putNull("image"); // Which row to update, based on the title String selection = "id = ?"; String[] selectionArgs = { Long.toString(hit.id) }; int count = db.update( HIT_TABLE_NAME, values, selection, selectionArgs); } public Game getGameByid(long id) { SQLiteDatabase db = getReadableDatabase(); String selection = "id = ?"; String[] selectionArgs = { Long.toString(id) }; Cursor c = db.query( GAME_TABLE_NAME, // The table to query GAME_PROJECTION, // The columns to return selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups null, // The sort order null // no limit! ); Game[] games = cursorToGames(c, null); return games[0]; } public int getLocalRankOfGame(Game game) { String[] params = { Long.toString(game.scenario.id), Double.toString(game.score + game.bonus) }; // Log.e("GAME",params[0].toString()); // Log.e("GAME",params[1].toString()); Cursor c = getReadableDatabase().rawQuery("SELECT COUNT(id)+1 FROM games WHERE lvl_id = ? AND score+bonus > ?", params); c.moveToFirst(); return c.getInt(0); } public int countAchievementsForLevel(int lvl_id) { String[] params = { Long.toString(lvl_id) }; Cursor c = getReadableDatabase().rawQuery("SELECT COUNT(DISTINCT achievement_id) FROM games g INNER JOIN game_achievements ga ON ga.game_id = g.id WHERE g.lvl_id = ?", params); c.moveToFirst(); return c.getInt(0); } public ArrayList getAchievementsOfGame(Game game) { String[] params = { Long.toString(game.id) }; Cursor c = getReadableDatabase().rawQuery("SELECT achievement_id FROM games g INNER JOIN game_achievements ga ON ga.game_id = g.id WHERE g.id = ?", params); int count = c.getCount(); if(count < 1) return new ArrayList(); ArrayList achievements = new ArrayList<>(count); c.moveToFirst(); while (!c.isAfterLast()) { achievements.add(AchievementCollection.getInstance().get(c.getInt(0))); c.moveToNext(); } return achievements; } }