/*
* Copyright (C) 2017 Team Gateship-One
* (Hendrik Borghorst & Frederik Luetkes)
*
* The AUTHORS.md file contains a detailed contributors list:
* <https://github.com/gateship-one/odyssey/blob/master/AUTHORS.md>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.gateshipone.odyssey.playbackservice.statemanager;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import org.gateshipone.odyssey.BuildConfig;
import org.gateshipone.odyssey.models.BookmarkModel;
import org.gateshipone.odyssey.models.TrackModel;
import org.gateshipone.odyssey.playbackservice.OdysseyServiceState;
import org.gateshipone.odyssey.playbackservice.PlaybackService;
import java.util.ArrayList;
import java.util.List;
public class OdysseyDatabaseManager extends SQLiteOpenHelper {
public static final String TAG = "OdysseyStateManager";
/**
* The name of the database
*/
private static final String DATABASE_NAME = "OdysseyStatesDB";
/**
* The version of the database
*/
private static final int DATABASE_VERSION = BuildConfig.VERSION_CODE;
/**
* Array of returned columns from the StateTracks table
*/
private String[] projectionTrackModels = {StateTracksTable.COLUMN_TRACKNUMBER, StateTracksTable.COLUMN_TRACKTITLE, StateTracksTable.COLUMN_TRACKALBUM, StateTracksTable.COLUMN_TRACKALBUMKEY,
StateTracksTable.COLUMN_TRACKDURATION, StateTracksTable.COLUMN_TRACKARTIST, StateTracksTable.COLUMN_TRACKURL, StateTracksTable.COLUMN_TRACKID};
/**
* Array of returned columns from the State table
*/
private String[] projectionState = {StateTable.COLUMN_BOOKMARK_TIMESTAMP, StateTable.COLUMN_TRACKNUMBER, StateTable.COLUMN_TRACKPOSITION, StateTable.COLUMN_RANDOM_STATE, StateTable.COLUMN_REPEAT_STATE};
public OdysseyDatabaseManager(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* Called when the database is created for the first time.
* This method creates the StateTracks and the State table
*/
@Override
public void onCreate(SQLiteDatabase db) {
StateTracksTable.onCreate(db);
StateTable.onCreate(db);
}
/**
* Called when the database needs to be upgraded.
* This method is currently not implemented.
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// FIXME if database schema change provide update path here
}
/**
* Save a given state in the database, including the related playlist.
* If an auto generated state is saved, all previous auto states will be deleted.
*
* @param playList The list of tracks for the current state
* @param state The current state
* @param title The title of this state
* @param autosave True if it's an auto generated state
*/
public void saveState(List<TrackModel> playList, OdysseyServiceState state, String title, boolean autosave) {
Log.v(TAG, "save state");
if (autosave) {
// delete previous auto saved states if this save is an auto generated save
clearAutoSaveState();
} else {
// delete the state with the same name from the database if exists
clearDuplicateState(title);
}
long timeStamp = System.currentTimeMillis();
savePlaylist(playList, timeStamp);
saveCurrentPlayState(state, timeStamp, autosave, title, playList.size());
}
/**
* Save the playlist in the database.
*
* @param playList The list of tracks
* @param timeStamp The timestamp as an additional identifier
*/
private void savePlaylist(List<TrackModel> playList, long timeStamp) {
SQLiteDatabase odysseyStateDB = getWritableDatabase();
ContentValues values = new ContentValues();
odysseyStateDB.beginTransaction();
for (TrackModel item : playList) {
values.clear();
// set TrackModel parameters
values.put(StateTracksTable.COLUMN_TRACKTITLE, item.getTrackName());
values.put(StateTracksTable.COLUMN_TRACKDURATION, item.getTrackDuration());
values.put(StateTracksTable.COLUMN_TRACKNUMBER, item.getTrackNumber());
values.put(StateTracksTable.COLUMN_TRACKARTIST, item.getTrackArtistName());
values.put(StateTracksTable.COLUMN_TRACKALBUM, item.getTrackAlbumName());
values.put(StateTracksTable.COLUMN_TRACKURL, item.getTrackURL());
values.put(StateTracksTable.COLUMN_TRACKALBUMKEY, item.getTrackAlbumKey());
values.put(StateTracksTable.COLUMN_TRACKID, item.getTrackId());
values.put(StateTracksTable.COLUMN_BOOKMARK_TIMESTAMP, timeStamp);
odysseyStateDB.insert(StateTracksTable.TABLE_NAME, null, values);
}
odysseyStateDB.setTransactionSuccessful();
odysseyStateDB.endTransaction();
// close the connection
odysseyStateDB.close();
}
/**
* Return the playlist for the given timestamp
*/
public List<TrackModel> readPlaylist(long timeStamp) {
SQLiteDatabase odysseyStateDB = getReadableDatabase();
List<TrackModel> playList = new ArrayList<>();
Cursor cursor = odysseyStateDB.query(StateTracksTable.TABLE_NAME, projectionTrackModels, StateTracksTable.COLUMN_BOOKMARK_TIMESTAMP + "=?", new String[]{Long.toString(timeStamp)},
"", "", StateTracksTable.COLUMN_ID);
if (cursor.moveToFirst()) {
do {
String trackName = cursor.getString(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKTITLE));
long duration = cursor.getLong(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKDURATION));
int number = cursor.getInt(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKNUMBER));
String artistName = cursor.getString(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKARTIST));
String albumName = cursor.getString(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKALBUM));
String url = cursor.getString(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKURL));
String albumKey = cursor.getString(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKALBUMKEY));
long id = cursor.getLong(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKID));
TrackModel item = new TrackModel(trackName, artistName, albumName, albumKey, duration, number, url, id);
playList.add(item);
} while (cursor.moveToNext());
}
cursor.close();
odysseyStateDB.close();
return playList;
}
/**
* Returns the playlist for the most recent timestamp
*/
public List<TrackModel> readPlaylist() {
SQLiteDatabase odysseyStateDB = getReadableDatabase();
List<TrackModel> playList = new ArrayList<>();
// query the most recent timestamp
Cursor stateCursor = odysseyStateDB.query(StateTable.TABLE_NAME, new String[]{StateTable.COLUMN_BOOKMARK_TIMESTAMP}, "", null, "", "", StateTable.COLUMN_BOOKMARK_TIMESTAMP + " DESC", "1");
if (stateCursor.moveToFirst()) {
long timeStamp = stateCursor.getLong(stateCursor.getColumnIndex(StateTable.COLUMN_BOOKMARK_TIMESTAMP));
// get the playlist tracks for the queried timestamp
Cursor cursor = odysseyStateDB.query(StateTracksTable.TABLE_NAME, projectionTrackModels, StateTracksTable.COLUMN_BOOKMARK_TIMESTAMP + "=?", new String[]{Long.toString(timeStamp)},
"", "", StateTracksTable.COLUMN_ID);
if (cursor.moveToFirst()) {
do {
String trackName = cursor.getString(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKTITLE));
long duration = cursor.getLong(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKDURATION));
int number = cursor.getInt(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKNUMBER));
String artistName = cursor.getString(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKARTIST));
String albumName = cursor.getString(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKALBUM));
String url = cursor.getString(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKURL));
String albumKey = cursor.getString(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKALBUMKEY));
long id = cursor.getLong(cursor.getColumnIndex(StateTracksTable.COLUMN_TRACKID));
TrackModel item = new TrackModel(trackName, artistName, albumName, albumKey, duration, number, url, id);
playList.add(item);
} while (cursor.moveToNext());
}
cursor.close();
}
stateCursor.close();
odysseyStateDB.close();
return playList;
}
/**
* Save the current state in the database.
*
* @param state The state object
* @param timeStamp The given timestamp
* @param autosave True if it's an auto generated state
* @param title The title of the state
* @param numberOfTracks The number of tracks related to this state
*/
private void saveCurrentPlayState(OdysseyServiceState state, long timeStamp, boolean autosave, String title, int numberOfTracks) {
SQLiteDatabase odysseyStateDB = getWritableDatabase();
ContentValues values = new ContentValues();
odysseyStateDB.beginTransaction();
// set state parameters
values.put(StateTable.COLUMN_BOOKMARK_TIMESTAMP, timeStamp);
values.put(StateTable.COLUMN_TRACKNUMBER, state.mTrackNumber);
values.put(StateTable.COLUMN_TRACKPOSITION, state.mTrackPosition);
values.put(StateTable.COLUMN_RANDOM_STATE, state.mRandomState.ordinal());
values.put(StateTable.COLUMN_REPEAT_STATE, state.mRepeatState.ordinal());
values.put(StateTable.COLUMN_AUTOSAVE, autosave);
values.put(StateTable.COLUMN_TITLE, title);
values.put(StateTable.COLUMN_TRACKS, numberOfTracks);
odysseyStateDB.insert(StateTable.TABLE_NAME, null, values);
odysseyStateDB.setTransactionSuccessful();
odysseyStateDB.endTransaction();
odysseyStateDB.close();
}
/**
* Return a state object for the given timestamp
*/
public OdysseyServiceState getState(long timeStamp) {
SQLiteDatabase odysseyStateDB = getReadableDatabase();
OdysseyServiceState state = new OdysseyServiceState();
Cursor cursor = odysseyStateDB.query(StateTable.TABLE_NAME, projectionState, StateTable.COLUMN_BOOKMARK_TIMESTAMP + "=?", new String[]{Long.toString(timeStamp)}, "", "", "");
if (cursor.moveToFirst()) {
state.mTrackNumber = cursor.getInt(cursor.getColumnIndex(StateTable.COLUMN_TRACKNUMBER));
state.mTrackPosition = cursor.getInt(cursor.getColumnIndex(StateTable.COLUMN_TRACKPOSITION));
state.mRandomState = PlaybackService.RANDOMSTATE.values()[cursor.getInt(cursor.getColumnIndex(StateTable.COLUMN_RANDOM_STATE))];
state.mRepeatState = PlaybackService.REPEATSTATE.values()[cursor.getInt(cursor.getColumnIndex(StateTable.COLUMN_REPEAT_STATE))];
}
cursor.close();
odysseyStateDB.close();
return state;
}
/**
* Return the most recent state object
*/
public OdysseyServiceState getState() {
SQLiteDatabase odysseyStateDB = getReadableDatabase();
OdysseyServiceState state = new OdysseyServiceState();
Cursor cursor = odysseyStateDB.query(StateTable.TABLE_NAME, projectionState, "", null, "", "", StateTable.COLUMN_BOOKMARK_TIMESTAMP + " DESC", "1");
if (cursor.moveToFirst()) {
state.mTrackNumber = cursor.getInt(cursor.getColumnIndex(StateTable.COLUMN_TRACKNUMBER));
state.mTrackPosition = cursor.getInt(cursor.getColumnIndex(StateTable.COLUMN_TRACKPOSITION));
state.mRandomState = PlaybackService.RANDOMSTATE.values()[cursor.getInt(cursor.getColumnIndex(StateTable.COLUMN_RANDOM_STATE))];
state.mRepeatState = PlaybackService.REPEATSTATE.values()[cursor.getInt(cursor.getColumnIndex(StateTable.COLUMN_REPEAT_STATE))];
}
cursor.close();
odysseyStateDB.close();
return state;
}
/**
* Return all custom saved states as Bookmark objects
*/
public List<BookmarkModel> getBookmarks() {
SQLiteDatabase odysseyStateDB = getReadableDatabase();
ArrayList<BookmarkModel> bookmarks = new ArrayList<>();
String whereVal[] = {"0"};
String where = StateTable.COLUMN_AUTOSAVE + "=?";
Cursor bookmarkCursor = odysseyStateDB.query(StateTable.TABLE_NAME, new String[]{StateTable.COLUMN_BOOKMARK_TIMESTAMP, StateTable.COLUMN_TITLE, StateTable.COLUMN_TRACKS, StateTable.COLUMN_AUTOSAVE},
where, whereVal, "", "", StateTable.COLUMN_BOOKMARK_TIMESTAMP + " DESC");
if (bookmarkCursor != null) {
if (bookmarkCursor.moveToFirst()) {
do {
long timeStamp = bookmarkCursor.getLong(bookmarkCursor.getColumnIndex(StateTable.COLUMN_BOOKMARK_TIMESTAMP));
String title = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(StateTable.COLUMN_TITLE));
int numberOfTracks = bookmarkCursor.getInt(bookmarkCursor.getColumnIndex(StateTable.COLUMN_TRACKS));
bookmarks.add(new BookmarkModel(timeStamp, title, numberOfTracks));
} while (bookmarkCursor.moveToNext());
}
bookmarkCursor.close();
}
odysseyStateDB.close();
return bookmarks;
}
/**
* Remove the state from the database related to the given timestamp
*/
public void removeState(long timestamp) {
SQLiteDatabase odysseyStateDB = getWritableDatabase();
// delete playlist
odysseyStateDB.delete(StateTracksTable.TABLE_NAME, StateTracksTable.COLUMN_BOOKMARK_TIMESTAMP + "=?", new String[]{Long.toString(timestamp)});
// delete state
odysseyStateDB.delete(StateTable.TABLE_NAME, StateTable.COLUMN_BOOKMARK_TIMESTAMP + "=?", new String[]{Long.toString(timestamp)});
odysseyStateDB.close();
}
/**
* Remove all states marked as auto generated, including their related tracks
*/
private void clearAutoSaveState() {
SQLiteDatabase odysseyStateDB = getWritableDatabase();
Cursor stateCursor = odysseyStateDB.query(StateTable.TABLE_NAME, new String[]{StateTable.COLUMN_BOOKMARK_TIMESTAMP, StateTable.COLUMN_AUTOSAVE}, StateTable.COLUMN_AUTOSAVE + "=?", new String[]{"1"},
"", "", StateTable.COLUMN_BOOKMARK_TIMESTAMP + " DESC");
if (stateCursor.moveToFirst()) {
do {
long timeStamp = stateCursor.getLong(stateCursor.getColumnIndex(StateTable.COLUMN_BOOKMARK_TIMESTAMP));
// delete playlist
odysseyStateDB.delete(StateTracksTable.TABLE_NAME, StateTracksTable.COLUMN_BOOKMARK_TIMESTAMP + "=?", new String[]{Long.toString(timeStamp)});
// delete state
odysseyStateDB.delete(StateTable.TABLE_NAME, StateTable.COLUMN_BOOKMARK_TIMESTAMP + "=?", new String[]{Long.toString(timeStamp)});
} while (stateCursor.moveToNext());
}
stateCursor.close();
odysseyStateDB.close();
}
/**
* Search the database for a state with the given title and remove that state, including the related tracks.
*
* @param title The title of the state
*/
private void clearDuplicateState(String title) {
SQLiteDatabase odysseyStateDB = getWritableDatabase();
Cursor stateCursor = odysseyStateDB.query(StateTable.TABLE_NAME, new String[]{StateTable.COLUMN_BOOKMARK_TIMESTAMP, StateTable.COLUMN_TITLE}, StateTable.COLUMN_TITLE + "=?", new String[]{title},
"", "", "");
if (stateCursor.moveToFirst()) {
long timeStamp = stateCursor.getLong(stateCursor.getColumnIndex(StateTable.COLUMN_BOOKMARK_TIMESTAMP));
// delete playlist
odysseyStateDB.delete(StateTracksTable.TABLE_NAME, StateTracksTable.COLUMN_BOOKMARK_TIMESTAMP + "=?", new String[]{Long.toString(timeStamp)});
// delete state
odysseyStateDB.delete(StateTable.TABLE_NAME, StateTable.COLUMN_BOOKMARK_TIMESTAMP + "=?", new String[]{Long.toString(timeStamp)});
}
stateCursor.close();
odysseyStateDB.close();
}
}