/* * Copyright (c) 2013 Andrew Fontaine, James Finlay, Jesse Tucker, Jacob Viau, and * Evan DeGraff * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package ca.cmput301f13t03.adventure_datetime.model; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Bitmap; import android.graphics.Color; import android.provider.BaseColumns; import android.util.Log; import ca.cmput301f13t03.adventure_datetime.model.Interfaces.ILocalStorage; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.*; /** * The local database containing all stories, fragments, bookmarks, and subscriptions * * @author Andrew Fontaine * @version 1.0 * @since 28/10/13 */ public class StoryDB implements BaseColumns, ILocalStorage { private static final String TAG = "StoryDB"; public static final String COLUMN_GUID = "GUID"; public static final String STORY_TABLE_NAME = "Story"; public static final String STORY_COLUMN_AUTHOR = "Author"; public static final String STORY_COLUMN_HEAD_FRAGMENT = "HeadFragment"; public static final String STORY_COLUMN_TIMESTAMP = "Timestamp"; public static final String STORY_COLUMN_SYNOPSIS = "Synopsis"; public static final String STORY_COLUMN_THUMBNAIL = "Thumbnail"; public static final String STORY_COLUMN_TITLE = "Title"; public static final String STORYFRAGMENT_TABLE_NAME = "StoryFragment"; public static final String STORYFRAGMENT_COLUMN_STORYID = "StoryID"; public static final String STORYFRAGMENT_COLUMN_CONTENT = "Content"; public static final String STORYFRAGMENT_COLUMN_CHOICES = "Choices"; public static final String STORYFRAGMENT_COLUMN_IMAGES = "Images"; public static final String BOOKMARK_TABLE_NAME = "Bookmark"; public static final String BOOKMARK_COLUMN_STORYID = "StoryID"; public static final String BOOKMARK_COLUMN_FRAGMENTID = "FragmentID"; public static final String BOOKMARK_COLUMN_DATE = "Date"; public static final String AUTHORED_STORY_TABLE_NAME = "AuthoredStory"; public static final String STORY_IMAGE_TABLE_NAME = "StoryImage"; public static final String STORY_IMAGE_COLUMN_IMAGE = "Image"; private StoryDBHelper mDbHelper; public StoryDB(Context context) { mDbHelper = new StoryDBHelper(context); } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#getStory(java.util.UUID) */ @Override public Story getStory(UUID id) { SQLiteDatabase db = mDbHelper.getReadableDatabase(); Cursor cursor = db.query(STORY_TABLE_NAME, new String[]{_ID, COLUMN_GUID, STORY_COLUMN_TITLE, STORY_COLUMN_AUTHOR, STORY_COLUMN_TITLE, STORY_COLUMN_HEAD_FRAGMENT, STORY_COLUMN_SYNOPSIS, STORY_COLUMN_TIMESTAMP, STORY_COLUMN_THUMBNAIL}, COLUMN_GUID + " = ?", new String[] {id.toString()}, null, null, null, "1"); Story story; if (cursor.moveToFirst()) { Log.v(TAG, "Story with UUID " + id + " retrieved"); story = createStory(cursor); } else { story = null; Log.v(TAG, "No story found"); } cursor.close(); db.close(); return story; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#getStories() */ @Override public ArrayList<Story> getStories() { SQLiteDatabase db = mDbHelper.getReadableDatabase(); Cursor cursor = db.query(STORY_TABLE_NAME, new String[] {_ID, COLUMN_GUID, STORY_COLUMN_AUTHOR, STORY_COLUMN_TITLE, STORY_COLUMN_HEAD_FRAGMENT, STORY_COLUMN_SYNOPSIS, STORY_COLUMN_TIMESTAMP, STORY_COLUMN_THUMBNAIL}, null, null, null, null, null); ArrayList<Story> stories = new ArrayList<Story>(); if(cursor.moveToFirst()) { do { Log.v(TAG, "Story with id " + cursor.getString(cursor.getColumnIndex(COLUMN_GUID)) + " retrieved"); stories.add(createStory(cursor)); } while(cursor.moveToNext()); } Log.v(TAG, stories.size() + " stories retrieved"); cursor.close(); db.close(); return stories; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#getStoriesAuthoredBy(java.lang.String) */ @Override public ArrayList<Story> getStoriesAuthoredBy(String author) { SQLiteDatabase db = mDbHelper.getReadableDatabase(); Cursor cursor = db.query(STORY_TABLE_NAME, new String[]{_ID, COLUMN_GUID, STORY_COLUMN_AUTHOR, STORY_COLUMN_HEAD_FRAGMENT, STORY_COLUMN_SYNOPSIS, STORY_COLUMN_TIMESTAMP, STORY_COLUMN_THUMBNAIL}, STORY_COLUMN_AUTHOR + " = ?", new String[] {author}, null, null, null); ArrayList<Story> stories = new ArrayList<Story>(); if(cursor.moveToFirst()) { do { Log.v(TAG, "Story with id " + cursor.getString(cursor.getColumnIndex(COLUMN_GUID)) + " retrieved"); stories.add(createStory(cursor)); } while(cursor.moveToNext()); } Log.v(TAG, stories.size() + " stories retrieved"); cursor.close(); db.close(); return stories; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#getStoryFragment(java.util.UUID) */ @Override public StoryFragment getStoryFragment(UUID id) { SQLiteDatabase db = mDbHelper.getReadableDatabase(); Cursor cursor = db.query(STORYFRAGMENT_TABLE_NAME, new String[] {_ID, COLUMN_GUID, STORYFRAGMENT_COLUMN_STORYID, STORYFRAGMENT_COLUMN_CHOICES, STORYFRAGMENT_COLUMN_CONTENT, STORYFRAGMENT_COLUMN_IMAGES}, COLUMN_GUID + " = ?", new String[] {id.toString()}, null, null, "1"); StoryFragment frag; if(cursor.moveToFirst()) { Log.v(TAG, "StoryFragment " + id + " retrieved"); frag = createStoryFragment(cursor); } else { frag = null; Log.v(TAG, "No fragment found"); } cursor.close(); db.close(); return frag; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#getStoryFragments(java.util.UUID) */ @Override public ArrayList<StoryFragment> getStoryFragments(UUID storyid) { SQLiteDatabase db = mDbHelper.getReadableDatabase(); Cursor cursor = db.query(STORYFRAGMENT_TABLE_NAME, new String[]{_ID, COLUMN_GUID, STORYFRAGMENT_COLUMN_STORYID, STORYFRAGMENT_COLUMN_CHOICES, STORYFRAGMENT_COLUMN_CONTENT, STORYFRAGMENT_COLUMN_IMAGES}, STORYFRAGMENT_COLUMN_STORYID + " = ?", new String[]{storyid.toString()}, null, null, null); ArrayList<StoryFragment> fragments = new ArrayList<StoryFragment>(); if(cursor.moveToFirst()) { do { Log.v(TAG, "StoryFragment with id " + cursor.getString(cursor.getColumnIndex(COLUMN_GUID)) + " retrieved"); fragments.add(createStoryFragment(cursor)); } while(cursor.moveToNext()); } Log.v(TAG, fragments.size() + " StoryFragments retrieved"); cursor.close(); db.close(); return fragments; } private ArrayList<UUID> getStoryFragmentIDs(UUID storyID) { SQLiteDatabase db = mDbHelper.getReadableDatabase(); Cursor cursor = db.query(STORYFRAGMENT_TABLE_NAME, new String[]{COLUMN_GUID}, STORYFRAGMENT_COLUMN_STORYID + " = ?", new String[]{storyID.toString()}, null, null, null); ArrayList<UUID> fragmentIDs = new ArrayList<UUID>(); if(cursor.moveToFirst()) { do { Log.v(TAG, "StoryFragment with id " + cursor.getString(cursor.getColumnIndex(COLUMN_GUID)) + " retrieved"); fragmentIDs.add(UUID.fromString(cursor.getString(cursor.getColumnIndex(COLUMN_GUID)))); } while(cursor.moveToNext()); } Log.v(TAG, fragmentIDs.size() + " StoryFragments retrieved"); cursor.close(); db.close(); return fragmentIDs; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#getBookmark(java.util.UUID) */ @Override public Bookmark getBookmark(UUID storyid) { SQLiteDatabase db = mDbHelper.getReadableDatabase(); Cursor cursor = db.query(BOOKMARK_TABLE_NAME, new String[] {_ID, BOOKMARK_COLUMN_STORYID, BOOKMARK_COLUMN_FRAGMENTID, BOOKMARK_COLUMN_DATE}, BOOKMARK_COLUMN_STORYID + " = ?", new String[] {storyid.toString()}, null, null, null); Bookmark bookmark; if(cursor.moveToFirst()) { Log.v(TAG, "Bookmark with Story id " + cursor.getString(cursor.getColumnIndex(BOOKMARK_COLUMN_STORYID)) + "retrieved"); bookmark = createBookmark(cursor); } else { Log.v(TAG, "No bookmark found"); bookmark = null; } cursor.close(); db.close(); return bookmark; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#getAllBookmarks() */ @Override public ArrayList<Bookmark> getAllBookmarks() { SQLiteDatabase db = mDbHelper.getReadableDatabase(); Cursor cursor = db.query(BOOKMARK_TABLE_NAME, new String[] {BOOKMARK_COLUMN_STORYID, BOOKMARK_COLUMN_FRAGMENTID, BOOKMARK_COLUMN_DATE}, null, null, null, null, null); ArrayList<Bookmark> bookmarks = new ArrayList<Bookmark>(); if(cursor.moveToFirst()) { do { Log.v(TAG, "Bookmark with Story id " + cursor.getString(cursor.getColumnIndex(BOOKMARK_COLUMN_STORYID)) + " retrieved"); bookmarks.add(createBookmark(cursor)); } while(cursor.moveToNext()); } Log.v(TAG, bookmarks.size() + " bookmarks retrieved"); return bookmarks; } @Override public boolean getAuthoredStory(UUID storyId) { SQLiteDatabase db = mDbHelper.getReadableDatabase(); Cursor cursor = db.query(AUTHORED_STORY_TABLE_NAME, new String[] {COLUMN_GUID}, COLUMN_GUID + " = ?", new String[] {storyId.toString()}, null, null, null); boolean authoredStory = cursor.moveToFirst(); cursor.close(); db.close(); return authoredStory; } @Override public ArrayList<UUID> getAuthoredStories() { SQLiteDatabase db = mDbHelper.getReadableDatabase(); Cursor cursor = db.query(AUTHORED_STORY_TABLE_NAME, new String[] {COLUMN_GUID}, null, null, null, null, null); ArrayList<UUID> authoredStories = new ArrayList<UUID>(); if(cursor.moveToFirst()) { do { authoredStories.add(UUID.fromString(cursor.getString(cursor.getColumnIndex(COLUMN_GUID)))); } while(cursor.moveToNext()); } cursor.close(); db.close(); return authoredStories; } public Image getImage(UUID imageID) { Cursor cursor = getImageCursor(imageID); Image image; if(cursor.moveToFirst()) { image = createImage(cursor); } else { image = null; } cursor.close(); return image; } private Cursor getImageCursor(UUID imageID) { SQLiteDatabase db = mDbHelper.getReadableDatabase(); return db.query(STORY_IMAGE_TABLE_NAME, new String[] {COLUMN_GUID, STORY_IMAGE_COLUMN_IMAGE}, COLUMN_GUID + " = ?", new String[] {imageID.toString()}, null, null, null); } public ArrayList<Image> getImages(ArrayList<UUID> imageIDs) { ArrayList<Image> images = new ArrayList<Image>(); if(imageIDs == null) return images; Cursor cursor; for(UUID imageID : imageIDs) { cursor = getImageCursor(imageID); if(cursor.moveToFirst()) images.add(createImage(cursor)); cursor.close(); } return images; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#setBookmark(ca.cmput301f13t03.adventure_datetime.model.Bookmark) */ @Override public boolean setBookmark(Bookmark bookmark) { SQLiteDatabase db = mDbHelper.getWritableDatabase(); Cursor cursor = db.query(BOOKMARK_TABLE_NAME, new String[] {_ID, BOOKMARK_COLUMN_FRAGMENTID, BOOKMARK_COLUMN_STORYID, BOOKMARK_COLUMN_DATE}, BOOKMARK_COLUMN_STORYID + " = ?", new String[] {bookmark.getStoryID().toString()}, null, null, null); ContentValues values = new ContentValues(); values.put(BOOKMARK_COLUMN_STORYID, bookmark.getStoryID().toString()); values.put(BOOKMARK_COLUMN_FRAGMENTID, bookmark.getFragmentID().toString()); values.put(BOOKMARK_COLUMN_DATE, bookmark.getTimestamp() / 1000); long updated; if(cursor.moveToFirst()) { Bookmark bookmark1 = createBookmark(cursor); if(bookmark.getTimestamp() > bookmark1.getTimestamp()) { updated = db.update(BOOKMARK_TABLE_NAME,values,BOOKMARK_COLUMN_STORYID + " = ?", new String[] {BOOKMARK_COLUMN_STORYID}); Log.v(TAG, updated + " Bookmarks updated"); cursor.close(); db.close(); return updated == 1; } Log.v(TAG, "No Bookmarks updated"); cursor.close(); db.close(); return false; } updated = db.insert(BOOKMARK_TABLE_NAME, null, values); Log.v(TAG, updated + " Bookmark inserted"); cursor.close(); db.close(); return updated != -1; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#setStory(ca.cmput301f13t03.adventure_datetime.model.Story) */ @Override public boolean setStory(Story story) { SQLiteDatabase db = mDbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(STORY_COLUMN_TITLE, story.getTitle()); values.put(STORY_COLUMN_AUTHOR, story.getAuthor()); values.put(STORY_COLUMN_HEAD_FRAGMENT, story.getHeadFragmentId().toString()); values.put(STORY_COLUMN_SYNOPSIS, story.getSynopsis()); values.put(STORY_COLUMN_TIMESTAMP, story.getTimestamp()); values.put(STORY_COLUMN_THUMBNAIL, (story.getThumbnail() == null ? null : story.getThumbnail().getId().toString())); values.put(COLUMN_GUID, story.getId().toString()); Cursor cursor = db.query(STORY_TABLE_NAME, new String[] {_ID}, COLUMN_GUID + " = ?", new String[] {story.getId().toString()}, null, null, null); if(cursor.moveToFirst()) { int updated; updated = db.update(STORY_TABLE_NAME, values, COLUMN_GUID + " = ?", new String [] {story.getId().toString()}); Log.v(TAG, updated + " stories updated"); cursor.close(); db.close(); return updated == 1 && setImage(story.getThumbnail()); } cursor.close(); long inserted; inserted = db.insert(STORY_TABLE_NAME, null, values); Log.v(TAG, inserted + " story inserted"); db.close(); return inserted != -1 && setImage(story.getThumbnail()); } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#setStoryFragment(ca.cmput301f13t03.adventure_datetime.model.StoryFragment) */ @Override public boolean setStoryFragment(StoryFragment frag) { SQLiteDatabase db = mDbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(STORYFRAGMENT_COLUMN_STORYID, frag.getStoryID().toString()); values.put(STORYFRAGMENT_COLUMN_CONTENT, frag.getStoryText()); values.put(STORYFRAGMENT_COLUMN_CHOICES, frag.getChoicesInJson()); values.put(COLUMN_GUID, frag.getFragmentID().toString()); values.put(STORYFRAGMENT_COLUMN_IMAGES, frag.getStoryMediaInJson()); Cursor cursor = db.query(STORYFRAGMENT_TABLE_NAME, new String[] {_ID, STORYFRAGMENT_COLUMN_STORYID}, COLUMN_GUID + " = ? AND " + STORYFRAGMENT_COLUMN_STORYID + " = ?", new String[] {frag.getFragmentID().toString(), frag.getStoryID().toString()}, null, null, null); if(cursor.moveToFirst()) { int updated; updated = db.update(STORYFRAGMENT_TABLE_NAME, values, COLUMN_GUID + " = ? AND " + STORYFRAGMENT_COLUMN_STORYID + " = ?", new String[] {frag.getFragmentID().toString(), frag.getStoryID().toString()}); Log.v(TAG, updated + " fragments updated"); cursor.close(); db.close(); return updated == 1 && setImages(frag.getStoryMedia()); } long inserted; inserted = db.insert(STORYFRAGMENT_TABLE_NAME, null, values); Log.v(TAG, inserted + " fragment inserted"); db.close(); return inserted != -1 && setImages(frag.getStoryMedia()); } @Override public boolean setAuthoredStory(Story story) { Story story2 = getStory(story.getId()); if(story2 == null) { Log.v(TAG, "Story doesn't exist in local DB!"); return false; } if(getAuthoredStory(story.getId())) { Log.v(TAG, "Story already in AuthoredStory table"); return true; } SQLiteDatabase db = mDbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(COLUMN_GUID, story.getId().toString()); long insert = db.insert(AUTHORED_STORY_TABLE_NAME, null, values); db.close(); return insert != -1; } public boolean setImage(Image image) { SQLiteDatabase db = mDbHelper.getWritableDatabase(); Image image2 = getImage(image.getId()); ContentValues values = new ContentValues(); values.put(COLUMN_GUID, image.getId().toString()); values.put(STORY_IMAGE_COLUMN_IMAGE, image.getEncodedBitmap()); long inserted; if(image2 == null) { inserted = db.insert(STORY_IMAGE_TABLE_NAME, null, values); db.close(); return inserted != -1; } else { inserted = db.update(STORY_IMAGE_TABLE_NAME, values, COLUMN_GUID + " = ?", new String[] {image.getId().toString()}); db.close(); return inserted == 1; } } public boolean setImages(ArrayList<Image> images) { boolean result = true; for(Image image : images) { result &= setImage(image); } return result; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#deleteStory(java.util.UUID) */ public boolean deleteStory(UUID id) { boolean fragments; fragments = deleteStoryFragments(id); deleteBookmarkByStory(id); deleteAuthoredStory(id); SQLiteDatabase db = mDbHelper.getWritableDatabase(); int story; story = db.delete(STORY_TABLE_NAME, COLUMN_GUID + " = ?", new String[] {id.toString()}); Log.v(TAG, story + " deleted, had UUID " + id); db.close(); return story == 1 && fragments; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#deleteStoryFragments(java.util.UUID) */ public boolean deleteStoryFragments(UUID storyID) { int fragments; deleteBookmarkByStory(storyID); SQLiteDatabase db = mDbHelper.getWritableDatabase(); fragments = db.delete(STORYFRAGMENT_TABLE_NAME, STORYFRAGMENT_COLUMN_STORYID + " = ?", new String[] {storyID.toString()}); Log.v(TAG, fragments + " deleted from DB, all with StoryID " + storyID); db.close(); return fragments > 0; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#deleteStoryFragment(java.util.UUID) */ public boolean deleteStoryFragment(UUID fragmentID) { int fragment; deleteBookmarkByFragment(fragmentID); SQLiteDatabase db = mDbHelper.getWritableDatabase(); fragment = db.delete(STORYFRAGMENT_TABLE_NAME, COLUMN_GUID + " = ?", new String[] {fragmentID.toString()}); Log.v(TAG, fragment + " fragment deleted, with fragmentID " + fragmentID); db.close(); return fragment == 1; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#deleteBookmarkByStory(java.util.UUID) */ public boolean deleteBookmarkByStory(UUID storyID) { int bookmark; SQLiteDatabase db = mDbHelper.getWritableDatabase(); bookmark = db.delete(BOOKMARK_TABLE_NAME, BOOKMARK_COLUMN_STORYID + " = ?", new String[] {storyID.toString()}); Log.v(TAG, bookmark + " bookmark deleted, with storyID " + storyID); db.close(); return bookmark == 1; } /* (non-Javadoc) * @see ca.cmput301f13t03.adventure_datetime.model.ILocalDatabase#deleteBookmarkByFragment(java.util.UUID) */ public boolean deleteBookmarkByFragment(UUID fragmentID) { int bookmark; SQLiteDatabase db = mDbHelper.getWritableDatabase(); bookmark = db.delete(BOOKMARK_TABLE_NAME, BOOKMARK_COLUMN_FRAGMENTID + " = ?", new String[] {fragmentID.toString()}); Log.v(TAG, bookmark + " bookmark deleted, with fragmentID " + fragmentID); db.close(); return bookmark == 1; } @Override public boolean deleteAuthoredStory(UUID storyID) { int authoredStory; SQLiteDatabase db = mDbHelper.getWritableDatabase(); authoredStory = db.delete(AUTHORED_STORY_TABLE_NAME, COLUMN_GUID + " = ?", new String[] {storyID.toString()}); Log.v(TAG, authoredStory + " authored story deleted, with storyId " + storyID); db.close(); return authoredStory == 1; } public boolean deleteImage(UUID id) { int images; SQLiteDatabase db = mDbHelper.getWritableDatabase(); images = db.delete(STORY_IMAGE_TABLE_NAME, COLUMN_GUID + " = ?", new String[] {id.toString()}); Log.v(TAG, images + " images deleted, with UUID " + id); return images != 0; } /** * Creates story from a cursor * * @param cursor A Cursor pointing to a Story * * @return A Story instance from the Database */ private Story createStory(Cursor cursor) { String title, author, synopsis; UUID headFragmentId, id; long timestamp; Image thumbnail; id = UUID.fromString(cursor.getString(cursor.getColumnIndex(StoryDB.COLUMN_GUID))); title = cursor.getString(cursor.getColumnIndex(StoryDB.STORY_COLUMN_TITLE)); headFragmentId = UUID.fromString(cursor.getString(cursor.getColumnIndex(StoryDB.STORY_COLUMN_HEAD_FRAGMENT))); author = cursor.getString(cursor.getColumnIndex(StoryDB.STORY_COLUMN_AUTHOR)); synopsis = cursor.getString(cursor.getColumnIndex(StoryDB.STORY_COLUMN_SYNOPSIS)); timestamp = cursor.getLong(cursor.getColumnIndex(StoryDB.STORY_COLUMN_TIMESTAMP)); thumbnail = getImage(id); Story newStory = new Story(headFragmentId, id, author, timestamp, synopsis, thumbnail, title); ArrayList<UUID> referencedFragments = this.getStoryFragmentIDs(id); for(UUID frag : referencedFragments) { newStory.addFragment(frag); } return newStory; } /** * Creates a StoryFragment from a cursor * * @param cursor A Cursor pointing to a StoryFragment * * @return A StoryFragment instance from the Database */ private StoryFragment createStoryFragment(Cursor cursor) { UUID storyID, fragmentID; String storyText; ArrayList<Choice> choices; ArrayList<Image> images; ArrayList<UUID> uuids; storyID = UUID.fromString(cursor.getString(cursor.getColumnIndex(StoryDB.STORYFRAGMENT_COLUMN_STORYID))); fragmentID = UUID.fromString(cursor.getString(cursor.getColumnIndex(StoryDB.COLUMN_GUID))); storyText = cursor.getString(cursor.getColumnIndex(StoryDB.STORYFRAGMENT_COLUMN_CONTENT)); String json = cursor.getString(cursor.getColumnIndex(StoryDB.STORYFRAGMENT_COLUMN_CHOICES)); Gson gson = new Gson(); Type collectionType = new TypeToken<Collection<Choice>>(){}.getType(); choices = gson.fromJson(json, collectionType); json = cursor.getString(cursor.getColumnIndex(STORYFRAGMENT_COLUMN_IMAGES)); collectionType = new TypeToken<Collection<UUID>>(){}.getType(); uuids = gson.fromJson(json, collectionType); images = getImages(uuids); return new StoryFragment(storyID, fragmentID, storyText, images, choices); } /** * Creates a Bookmark from a cursor * * @param cursor A Cursor pointing to a Bookmark * * @return A Bookmark instance from the Database */ private Bookmark createBookmark(Cursor cursor) { UUID fragmentID, storyID; Date date; fragmentID = UUID.fromString(cursor.getString(cursor.getColumnIndex(StoryDB.BOOKMARK_COLUMN_FRAGMENTID))); storyID = UUID.fromString(cursor.getString(cursor.getColumnIndex(StoryDB.BOOKMARK_COLUMN_STORYID))); long unix = cursor.getLong(cursor.getColumnIndex(StoryDB.BOOKMARK_COLUMN_DATE)); Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(unix); date = cal.getTime(); return new Bookmark(fragmentID, storyID, date); } private Image createImage(Cursor cursor) { UUID id; String bitmap; id = UUID.fromString(cursor.getString(cursor.getColumnIndex(COLUMN_GUID))); bitmap = cursor.getString(cursor.getColumnIndex(STORY_IMAGE_COLUMN_IMAGE)); return new Image(id, bitmap); } public class StoryDBHelper extends SQLiteOpenHelper { public static final int DATABASE_VERSION = 7; public static final String DATABASE_NAME = "adventure.database"; private static final String TAG = "StoryDBHelper"; private static final String CREATE_STORY_TABLE = "CREATE TABLE " + STORY_TABLE_NAME + " (" + _ID + " INTEGER PRIMARY KEY, " + COLUMN_GUID + " TEXT, " + STORY_COLUMN_TITLE + " TEXT, " + STORY_COLUMN_AUTHOR + " TEXT, " + STORY_COLUMN_SYNOPSIS + " TEXT, " + STORY_COLUMN_HEAD_FRAGMENT + " INTEGER, " + STORY_COLUMN_TIMESTAMP + " INTEGER, " + STORY_COLUMN_THUMBNAIL + " TEXT, " + "FOREIGN KEY(" + STORY_COLUMN_HEAD_FRAGMENT + ") REFERENCES " + STORYFRAGMENT_TABLE_NAME + "(" + COLUMN_GUID + ") )"; private static final String CREATE_STORYFRAGMENT_TABLE = "CREATE TABLE " + STORYFRAGMENT_TABLE_NAME + " (" + _ID + " INTEGER PRIMARY KEY, " + COLUMN_GUID + " TEXT, " + STORYFRAGMENT_COLUMN_STORYID + " INTEGER, " + STORYFRAGMENT_COLUMN_CONTENT + " TEXT, " + STORYFRAGMENT_COLUMN_CHOICES + " BLOB, " + STORYFRAGMENT_COLUMN_IMAGES + " BLOB, " + "FOREIGN KEY(" + STORYFRAGMENT_COLUMN_STORYID + ") REFERENCES " + STORY_TABLE_NAME + "(" + COLUMN_GUID + "))"; private static final String CREATE_BOOKMARK_TABLE = "CREATE TABLE " + BOOKMARK_TABLE_NAME + " (" + _ID + " INTEGER PRIMARY KEY, " + BOOKMARK_COLUMN_FRAGMENTID + " INTEGER, " + BOOKMARK_COLUMN_STORYID + " INTEGER, " + BOOKMARK_COLUMN_DATE + " INTEGER, " + "FOREIGN KEY(" + BOOKMARK_COLUMN_FRAGMENTID + ") REFERENCES " + STORYFRAGMENT_TABLE_NAME + "(" + COLUMN_GUID + "), FOREIGN KEY (" + BOOKMARK_COLUMN_STORYID + ") REFERENCES " + STORY_TABLE_NAME + "(" + COLUMN_GUID + "))"; private static final String CREATE_AUTHORED_STORY_TABLE = "CREATE TABLE " + AUTHORED_STORY_TABLE_NAME + " (" + _ID + " INTEGER PRIMARY KEY, " + COLUMN_GUID + " TEXT, " + "FOREIGN KEY(" + COLUMN_GUID + ") REFERENCES " + STORY_TABLE_NAME + "(" + COLUMN_GUID + "))"; private static final String CREATE_STORY_IMAGE_TABLE = "CREATE TABLE " + STORY_IMAGE_TABLE_NAME + " (" + _ID + " INTEGER PRIMARY KEY, " + COLUMN_GUID + " TEXT, " + STORY_IMAGE_COLUMN_IMAGE + " TEXT)"; private static final String DELETE_STORY_TABLE = "DROP TABLE IF EXISTS " + STORY_TABLE_NAME; private static final String DELETE_STORYFRAGMENT_TABLE = "DROP TABLE IF EXISTS " + STORYFRAGMENT_TABLE_NAME; private static final String DELETE_BOOKMARK_TABLE = "DROP TABLE IF EXISTS " + BOOKMARK_TABLE_NAME; private static final String DELETE_AUTHORED_STORY_TABLE = "DROP TABLE IF EXISTS " + AUTHORED_STORY_TABLE_NAME; private static final String DELETE_STORY_IMAGE_TABLE = "DROP TABLE IF EXISTS " + STORY_IMAGE_TABLE_NAME; public StoryDBHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { Log.v(TAG, "Creating DB"); db.execSQL(CREATE_STORY_TABLE); db.execSQL(CREATE_STORYFRAGMENT_TABLE); db.execSQL(CREATE_BOOKMARK_TABLE); db.execSQL(CREATE_AUTHORED_STORY_TABLE); db.execSQL(CREATE_STORY_IMAGE_TABLE); populateDB(db); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(DELETE_STORYFRAGMENT_TABLE); db.execSQL(DELETE_STORY_TABLE); db.execSQL(DELETE_BOOKMARK_TABLE); db.execSQL(DELETE_AUTHORED_STORY_TABLE); db.execSQL(DELETE_STORY_IMAGE_TABLE); } @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { onUpgrade(db, oldVersion, newVersion); } private void populateDB(SQLiteDatabase db) { Bitmap bit = Bitmap.createBitmap(new int[]{Color.BLACK}, 1, 1, Bitmap.Config.ARGB_8888); Story story = new Story(UUID.fromString("5582f797-29b8-4d9d-83bf-88c434c1944a"), UUID.fromString("fc662870-5d6a-4ae2-98f6-0cdfe36013bb"), "Andrew", 706232100, "A guide to playing choose your own adventures\nPress 'Play Story' below to begin!", bit, "Play me!"); String storyText = "Welcome to Adventure.DateTime! To progress, press 'Actions' and select 'Next Tutorial'."; StoryFragment frag = new StoryFragment(story.getId(), UUID.fromString("5582f797-29b8-4d9d-83bf-88c434c1944a"), storyText, new ArrayList<Image>(), new ArrayList<Choice>()); StoryFragment frag2 = new StoryFragment(story.getId(), UUID.fromString("b10ef8ca-1180-44f6-b11b-170fef5ec071"), "Great job! " + "A new bookmark has automatically been created so you can pick up from where you left off later.\n" + "Playing these is as simple as that! A few things to note:\n" + "\tSometimes, these little story fragments,as we call them, contain pictures. " + "If they do, you'll see this above all this text. Press one, and it'll open up a " + "full-screen view." + "\t 'I'm feeling lucky' is an option in every story fragment. If you can't decide which option to choose," + " it'll pick one for you at random.\n\n" + "That's really about it. Now get playing! Hit 'The End' and 'Change Adventures' to find something else " + "to play. I suggest looking in Online.", new ArrayList<Image>(), new ArrayList<Choice>()); Choice choice = new Choice("Next tutorial", frag2.getFragmentID()); frag.addChoice(choice); story.addFragment(frag); story.setHeadFragmentId(frag); story.addFragment(frag2); Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(1383652800L * 1000L); db.beginTransaction(); long inserted; ContentValues values = new ContentValues(); values.put(STORY_COLUMN_TITLE, story.getTitle()); values.put(STORY_COLUMN_AUTHOR, story.getAuthor()); values.put(STORY_COLUMN_HEAD_FRAGMENT, story.getHeadFragmentId().toString()); values.put(STORY_COLUMN_SYNOPSIS, story.getSynopsis()); values.put(STORY_COLUMN_TIMESTAMP, story.getTimestamp()); values.put(STORY_COLUMN_THUMBNAIL, story.getThumbnail().getId().toString()); values.put(COLUMN_GUID, story.getId().toString()); inserted = db.insert(STORY_TABLE_NAME, null, values); Log.d(TAG, String.valueOf(inserted)); values = new ContentValues(); values.put(COLUMN_GUID, story.getThumbnail().getId().toString()); values.put(STORY_IMAGE_COLUMN_IMAGE, story.getThumbnail().getEncodedBitmap()); inserted = db.insert(STORY_IMAGE_TABLE_NAME, null, values); Log.d(TAG, String.valueOf(inserted)); values = new ContentValues(); values.put(STORYFRAGMENT_COLUMN_STORYID, frag.getStoryID().toString()); values.put(STORYFRAGMENT_COLUMN_CONTENT, frag.getStoryText()); values.put(STORYFRAGMENT_COLUMN_CHOICES, frag.getChoicesInJson()); values.put(COLUMN_GUID, frag.getFragmentID().toString()); inserted = db.insert(STORYFRAGMENT_TABLE_NAME, null, values); Log.d(TAG, String.valueOf(inserted)); values = new ContentValues(); values.put(STORYFRAGMENT_COLUMN_STORYID, frag2.getStoryID().toString()); values.put(STORYFRAGMENT_COLUMN_CONTENT, frag2.getStoryText()); values.put(STORYFRAGMENT_COLUMN_CHOICES, frag2.getChoicesInJson()); values.put(COLUMN_GUID, frag2.getFragmentID().toString()); inserted = db.insert(STORYFRAGMENT_TABLE_NAME, null, values); Log.d(TAG, String.valueOf(inserted)); db.setTransactionSuccessful(); db.endTransaction(); story = new Story(UUID.fromString("9b135456-ecc9-4619-89ca-94f21aefd5b5"), UUID.fromString("6395015f-d63d-416f-8be6-eb4ff5c99c4c"), "Andrew", 706232100, "A guide to creating choose your own adventures\nPress the pencil in the top right, and meet me over in " + "Author!\n\nI'm going to assume you're there now. If you are, pressing on that little black square next to the title" + " will allow you to change this " + "story's thumbnail to something a little more suitable. You should also notice that the author's name is now you! Unless this " + "isn't your phone, that is. The pencil, which is where we're going next, allows us to edit the interior of the story. " + "The floppy disk saves your changes, although we generally handle the saving for you. The arrow uploads your story to our" + " servers to share with everyone. The trash can deletes your story, but beware, if you upload your story, there's no eliminating it." + "\n\n When you're ready, hit that pencil!", bit, "Edit me!"); storyText = "Scroll over to Edit...\n\n" + "Hey there! You're editing the head fragment of your story. This is the first part your readers will encounter. " + "We have a few things you can do here, namely, editing this text, as well as editing the choices connecting it to " + "other fragments. There should already be a choice sitting there. " + "There isn't much here... let's change that. We want to add a new choice. " + "To add a choice, we need a new fragment to " + "connect it to. Go back to 'Overview,' and press the 'Add' button in the top right. " + "REMEMBER! To change the fragment you're editing, press on its node in Overview. Press the other node once " + "you have a new fragment."; frag = new StoryFragment(story.getId(), UUID.fromString("6395015f-d63d-416f-8be6-eb4ff5c99c4c"), storyText, new ArrayList<Image>(), new ArrayList<Choice>()); storyText = "Good you made it back. I was worried for a moment. To connect your new fragment to this one, hit " + "the add choice button. You'll have to fill out what you want your choice's text to be. When you hit " + "'Okay', you'll jump over to the Overview, where you can press on the node the choice will lead to. " + "Easy, right? To add pictures to your super cool story, press the Organize Media button above this. " + "You'll be able to select pictures from your gallery or take new ones. To take a look at what your fragment " + "will look like, head over to the Preview! Have fun creating!"; frag2 = new StoryFragment(story.getId(), UUID.fromString("dd868b19-d3bc-4c3b-beff-2c5bdad4026c"), storyText, new ArrayList<Image>(), new ArrayList<Choice>()); choice = new Choice("Next tutorial", frag2.getFragmentID()); frag.addChoice(choice); story.addFragment(frag); story.addFragment(frag2); story.setHeadFragmentId(frag); cal = Calendar.getInstance(); cal.setTimeInMillis(1383652800L * 1000L); db.beginTransaction(); values = new ContentValues(); values.put(STORY_COLUMN_TITLE, story.getTitle()); values.put(STORY_COLUMN_AUTHOR, story.getAuthor()); values.put(STORY_COLUMN_HEAD_FRAGMENT, story.getHeadFragmentId().toString()); values.put(STORY_COLUMN_SYNOPSIS, story.getSynopsis()); values.put(STORY_COLUMN_TIMESTAMP, story.getTimestamp()); values.put(STORY_COLUMN_THUMBNAIL, story.getThumbnail().getId().toString()); values.put(COLUMN_GUID, story.getId().toString()); inserted = db.insert(STORY_TABLE_NAME, null, values); Log.d(TAG, String.valueOf(inserted)); values = new ContentValues(); values.put(COLUMN_GUID, story.getThumbnail().getId().toString()); values.put(STORY_IMAGE_COLUMN_IMAGE, story.getThumbnail().getEncodedBitmap()); inserted = db.insert(STORY_IMAGE_TABLE_NAME, null, values); Log.d(TAG, String.valueOf(inserted)); values = new ContentValues(); values.put(STORYFRAGMENT_COLUMN_STORYID, frag.getStoryID().toString()); values.put(STORYFRAGMENT_COLUMN_CONTENT, frag.getStoryText()); values.put(STORYFRAGMENT_COLUMN_CHOICES, frag.getChoicesInJson()); values.put(COLUMN_GUID, frag.getFragmentID().toString()); inserted = db.insert(STORYFRAGMENT_TABLE_NAME, null, values); Log.d(TAG, String.valueOf(inserted)); values = new ContentValues(); values.put(STORYFRAGMENT_COLUMN_STORYID, frag2.getStoryID().toString()); values.put(STORYFRAGMENT_COLUMN_CONTENT, frag2.getStoryText()); values.put(STORYFRAGMENT_COLUMN_CHOICES, frag2.getChoicesInJson()); values.put(COLUMN_GUID, frag2.getFragmentID().toString()); inserted = db.insert(STORYFRAGMENT_TABLE_NAME, null, values); Log.d(TAG, String.valueOf(inserted)); db.setTransactionSuccessful(); db.endTransaction(); } } }