package com.mehmetakiftutuncu.eshotroid.database; import java.util.ArrayList; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.mehmetakiftutuncu.eshotroid.BuildConfig; import com.mehmetakiftutuncu.eshotroid.model.Bus; import com.mehmetakiftutuncu.eshotroid.model.BusTime; /** A database class using singleton design pattern for all the bus information * * @author mehmetakiftutuncu */ public class BusDatabase implements IDatabaseOperations<Bus> { /** Key for the number of a bus */ public static final String KEY_BUSSES_NUMBER = "busNumber"; /** Key for the source of a bus */ public static final String KEY_BUSSES_SOURCE = "busSource"; /** Key for the destination of a bus */ public static final String KEY_BUSSES_DESTINATION = "busDestination"; /** Key for the route of a bus */ public static final String KEY_BUSSES_ROUTE = "busRoute"; /** Key for the favorited flag of a bus */ public static final String KEY_BUSSES_ISFAVORITED = "busIsFavorited"; /** Key for the weekday times of a bus */ public static final String KEY_BUSSES_TIMESH = "busTimesH"; /** Key for the saturday times of a bus */ public static final String KEY_BUSSES_TIMESC = "busTimesC"; /** Key for the sunday times of a bus */ public static final String KEY_BUSSES_TIMESP = "busTimesP"; /** Key for the flag if weekday times of a bus exists */ public static final String KEY_BUSSES_TIMESH_EXISTS = "busTimesHExists"; /** Key for the flag if saturday times of a bus exists */ public static final String KEY_BUSSES_TIMESC_EXISTS = "busTimesCExists"; /** Key for the flag if sunday times of a bus exists */ public static final String KEY_BUSSES_TIMESP_EXISTS = "busTimesPExists"; /** Name of the database */ private static final String DATABASE_NAME = "eshotroid_database"; /** Version of the database */ private static final int DATABASE_VERSION = 2; /** Name of the busses table */ private static final String TABLE_NAME_BUSSES = "busses"; /** SQL string for creating busses table */ private static final String CREATE_SQL = "CREATE TABLE " + TABLE_NAME_BUSSES + " (" + KEY_BUSSES_NUMBER + " INTEGER PRIMARY KEY NOT NULL, " + KEY_BUSSES_SOURCE + " TEXT NOT NULL, " + KEY_BUSSES_DESTINATION + " TEXT NOT NULL, " + KEY_BUSSES_ROUTE + " TEXT, " + KEY_BUSSES_ISFAVORITED + " INTEGER NOT NULL, " + KEY_BUSSES_TIMESH + " TEXT, " + KEY_BUSSES_TIMESC + " TEXT, " + KEY_BUSSES_TIMESP + " TEXT," + KEY_BUSSES_TIMESH_EXISTS + " INTEGER NOT NULL," + KEY_BUSSES_TIMESC_EXISTS + " INTEGER NOT NULL," + KEY_BUSSES_TIMESP_EXISTS + " INTEGER NOT NULL" + ");"; /** {@link SQLiteDatabase} object which is the actual database object to do * database operations */ private static SQLiteDatabase myDatabase; /** Flag if the database is opened */ protected static boolean isOpened = false; /** {@link BusDatabaseHelper} instance for creating/opening the database */ private static BusDatabaseHelper myHelper; /** An instance of this database */ private static BusDatabase myInstance; /** Tag for debugging */ private static final String LOG_TAG = "Eshotroid_BusDatabase"; /** A database helper class for creating database * * @author mehmetakiftutuncu */ private static class BusDatabaseHelper extends SQLiteOpenHelper { public BusDatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { if(BuildConfig.DEBUG) Log.d(LOG_TAG, "Running create bus database SQL..."); db.execSQL(CREATE_SQL); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if(BuildConfig.DEBUG) Log.d(LOG_TAG, "Upgrading bus database..."); db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME_BUSSES); onCreate(db); } } /** Private constructor for singleton */ private BusDatabase() {} /** Creates and/or opens a writable database * * @param Context * * @return An instance of this class * * @throws SQLException <li>If database cannot be opened */ public static synchronized BusDatabase getDatabase(Context context) throws SQLException { if(myInstance == null) { if(BuildConfig.DEBUG) Log.d(LOG_TAG, "Creating a new BusDatabase instance..."); myInstance = new BusDatabase(); } if(myHelper == null) { if(BuildConfig.DEBUG) Log.d(LOG_TAG, "Creating a new BusDatabaseHelper instance..."); myHelper = new BusDatabaseHelper(context); } if(!isOpened) { if(BuildConfig.DEBUG) Log.d(LOG_TAG, "Opening bus database..."); isOpened = true; myDatabase = myHelper.getWritableDatabase(); } return myInstance; } /** Closes the opened database */ public synchronized void closeDatabase() { if(isOpened) { if(BuildConfig.DEBUG) Log.d(LOG_TAG, "Closing bus database..."); myHelper.close(); } isOpened = false; } @Override public synchronized boolean addOrUpdate(Bus entry) { /* Result flag */ boolean result = true; /* Set the row */ ContentValues values = new ContentValues(); values.put(KEY_BUSSES_NUMBER, entry.getNumber()); values.put(KEY_BUSSES_SOURCE, entry.getSource()); values.put(KEY_BUSSES_DESTINATION, entry.getDestination()); values.put(KEY_BUSSES_ROUTE, (entry.getRoute() != null ? entry.getRoute() : "")); values.put(KEY_BUSSES_ISFAVORITED, (entry.isFavorited() ? 1 : 0)); values.put(KEY_BUSSES_TIMESH, (entry.getTimesH() != null ? new Gson().toJson(entry.getTimesH()) : "")); values.put(KEY_BUSSES_TIMESC, (entry.getTimesC() != null ? new Gson().toJson(entry.getTimesC()) : "")); values.put(KEY_BUSSES_TIMESP, (entry.getTimesP() != null ? new Gson().toJson(entry.getTimesP()) : "")); values.put(KEY_BUSSES_TIMESH_EXISTS, (entry.timesHExists() ? 1 : 0)); values.put(KEY_BUSSES_TIMESC_EXISTS, (entry.timesCExists() ? 1 : 0)); values.put(KEY_BUSSES_TIMESP_EXISTS, (entry.timesPExists() ? 1 : 0)); try { /* Get the entry from the database */ Bus bus = get(entry.getNumber()); /* If the entry is already in the database */ if(bus != null) { /* Updating */ myDatabase.update(TABLE_NAME_BUSSES, values, KEY_BUSSES_NUMBER + "=" + entry.getNumber(), null); } else { /* Adding */ myDatabase.insert(TABLE_NAME_BUSSES, null, values); } } catch(Exception e) { Log.e(LOG_TAG, "Error occurred while adding to/updating database!", e); result = false; } /* Return the result */ return result; } @Override public synchronized ArrayList<Bus> get() { /* Resulting list */ ArrayList<Bus> list = new ArrayList<Bus>(); /* Columns to select which are all except for route and times because they are not needed in the bus list */ String[] columns = new String[] { KEY_BUSSES_NUMBER, KEY_BUSSES_SOURCE, KEY_BUSSES_DESTINATION, KEY_BUSSES_ISFAVORITED }; /* Cursor to query the database */ Cursor cursor = myDatabase.query(TABLE_NAME_BUSSES, columns, null, null, null, null, null); /* For every item that the cursor finds until the end */ for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { /* Fields of the item */ int number; String source; String destination; boolean isFavorited; /* Get values for all fields */ number = cursor.getInt(cursor.getColumnIndex(KEY_BUSSES_NUMBER)); source = cursor.getString(cursor.getColumnIndex(KEY_BUSSES_SOURCE)); destination = cursor.getString(cursor.getColumnIndex(KEY_BUSSES_DESTINATION)); isFavorited = (cursor.getInt(cursor.getColumnIndex(KEY_BUSSES_ISFAVORITED)) == 1); /* Add the item to the resulting list */ list.add(new Bus(number, source, destination, null, isFavorited, null, null, null, true, true, true)); } // Close the cursor cursor.close(); /* Return the result */ return list; } @Override public synchronized Bus get(int number) { /* Resulting item */ Bus item = null; /* Columns to select which are all except for number because we know number */ String[] columns = new String[] { KEY_BUSSES_SOURCE, KEY_BUSSES_DESTINATION, KEY_BUSSES_ROUTE, KEY_BUSSES_ISFAVORITED, KEY_BUSSES_TIMESH, KEY_BUSSES_TIMESC, KEY_BUSSES_TIMESP, KEY_BUSSES_TIMESH_EXISTS, KEY_BUSSES_TIMESC_EXISTS, KEY_BUSSES_TIMESP_EXISTS }; /* Cursor to query the database */ Cursor cursor = myDatabase.query(TABLE_NAME_BUSSES, columns, KEY_BUSSES_NUMBER + "=" + number, null, null, null, null); /* If successfully queried */ if(cursor != null) { /* If any match is found */ if(cursor.getCount() > 0) { /* Go to the first match */ cursor.moveToFirst(); /* Fields of the item */ String source; String destination; String route; boolean isFavorited; ArrayList<BusTime> timesH; ArrayList<BusTime> timesC; ArrayList<BusTime> timesP; boolean timesHExists; boolean timesCExists; boolean timesPExists; /* Get values for all fields */ source = cursor.getString(cursor.getColumnIndex(KEY_BUSSES_SOURCE)); destination = cursor.getString(cursor.getColumnIndex(KEY_BUSSES_DESTINATION)); route = cursor.getString(cursor.getColumnIndex(KEY_BUSSES_ROUTE)); isFavorited = (cursor.getInt(cursor.getColumnIndex(KEY_BUSSES_ISFAVORITED)) == 1); timesH = new Gson().fromJson(cursor.getString(cursor.getColumnIndex(KEY_BUSSES_TIMESH)), new TypeToken<ArrayList<BusTime>>(){}.getType()); timesC = new Gson().fromJson(cursor.getString(cursor.getColumnIndex(KEY_BUSSES_TIMESC)), new TypeToken<ArrayList<BusTime>>(){}.getType()); timesP = new Gson().fromJson(cursor.getString(cursor.getColumnIndex(KEY_BUSSES_TIMESP)), new TypeToken<ArrayList<BusTime>>(){}.getType()); timesHExists = (cursor.getInt(cursor.getColumnIndex(KEY_BUSSES_TIMESH_EXISTS)) == 1); timesCExists = (cursor.getInt(cursor.getColumnIndex(KEY_BUSSES_TIMESC_EXISTS)) == 1); timesPExists = (cursor.getInt(cursor.getColumnIndex(KEY_BUSSES_TIMESP_EXISTS)) == 1); /* Generate the resulting item */ item = new Bus(number, source, destination, route, isFavorited, timesH, timesC, timesP, timesHExists, timesCExists, timesPExists); } // Close the cursor cursor.close(); } /* Return the result */ return item; } @Override public synchronized boolean delete(int number) { /* Result flag */ boolean result = true; /* Delete */ int affectedRows = myDatabase.delete(TABLE_NAME_BUSSES, KEY_BUSSES_NUMBER + "=" + number, null); if(affectedRows == 0) { result = false; } /* Return the result */ return result; } }