Skip to content

Commit b97e15f

Browse files
author
Nick Aiwazian
committed
Add more starter code for persistence
1 parent f325b0c commit b97e15f

File tree

1 file changed

+302
-4
lines changed

1 file changed

+302
-4
lines changed
Lines changed: 302 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,39 @@
11
package com.codepath.instagram.persistence;
22

3-
public class InstagramClientDatabase {
3+
import android.content.ContentValues;
4+
import android.content.Context;
5+
import android.database.Cursor;
6+
import android.database.sqlite.SQLiteDatabase;
7+
import android.database.sqlite.SQLiteOpenHelper;
8+
import android.util.Log;
9+
10+
import com.codepath.instagram.models.InstagramComment;
11+
import com.codepath.instagram.models.InstagramImage;
12+
import com.codepath.instagram.models.InstagramPost;
13+
import com.codepath.instagram.models.InstagramUser;
14+
15+
import java.util.ArrayList;
16+
import java.util.List;
17+
18+
public class InstagramClientDatabase extends SQLiteOpenHelper {
419
private static final String TAG = "InstagramClientDatabase";
520

21+
// Database info
622
private static final String DATABASE_NAME = "instagramClientDatabase";
723
private static final int DATABASE_VERSION = 1;
824

25+
// Tables
926
private static final String TABLE_POSTS = "posts";
1027
private static final String TABLE_USERS = "users";
1128
private static final String TABLE_IMAGES = "images";
1229
private static final String TABLE_COMMENTS = "comments";
1330
private static final String TABLE_POST_COMMENTS = "postComments";
1431

32+
// Constraints
1533
private static final String CONSTRAINT_POST_COMMENTS_PK = "postComments_pk";
1634

17-
private static final String KEY_ID = "_id";
18-
1935
// Posts table columns
36+
private static final String KEY_POST_ID = "id";
2037
private static final String KEY_POST_MEDIA_ID = "mediaId";
2138
private static final String KEY_POST_USER_ID_FK = "userId";
2239
private static final String KEY_POST_IMAGE_ID_FK = "imageId";
@@ -26,15 +43,18 @@ public class InstagramClientDatabase {
2643
private static final String KEY_POST_CREATED_TIME = "createdTime";
2744

2845
// Users table columns
46+
private static final String KEY_USER_ID = "id";
2947
private static final String KEY_USER_NAME = "userName";
3048
private static final String KEY_USER_PROFILE_PICTURE_URL = "profilePictureUrl";
3149

3250
// Images table columns
51+
private static final String KEY_IMAGE_ID = "id";
3352
private static final String KEY_IMAGE_URL = "imageUrl";
3453
private static final String KEY_IMAGE_HEIGHT = "imageHeight";
3554
private static final String KEY_IMAGE_WIDTH = "imageWidth";
3655

3756
// Comments table columns
57+
private static final String KEY_COMMENT_ID = "id";
3858
private static final String KEY_COMMENT_USER_ID_FK = "userId";
3959
private static final String KEY_COMMENT_TEXT = "text";
4060
private static final String KEY_COMMENT_CREATED_TIME = "createdTime";
@@ -43,6 +63,284 @@ public class InstagramClientDatabase {
4363
private static final String KEY_POST_COMMENT_POST_ID_FK = "postId";
4464
private static final String KEY_POST_COMMENT_COMMENT_ID_FK = "commentId";
4565

66+
// Singleton instance
67+
private static InstagramClientDatabase sInstance;
68+
69+
/**
70+
* Constructor should be private to prevent direct instantiation.
71+
* make call to static method "getInstance()" instead.
72+
*/
73+
private InstagramClientDatabase(Context context) {
74+
super(context, DATABASE_NAME, null, DATABASE_VERSION);
75+
}
76+
77+
public static synchronized InstagramClientDatabase getInstance(Context context) {
78+
if (sInstance == null) {
79+
// Use the application context, which will ensure that you
80+
// don't accidentally leak an Activity's context.
81+
// See this article for more information: http://bit.ly/6LRzfx
82+
sInstance = new InstagramClientDatabase(context.getApplicationContext());
83+
}
84+
85+
return sInstance;
86+
}
87+
88+
@Override
89+
public void onConfigure(SQLiteDatabase db) {
90+
super.onConfigure(db);
91+
db.setForeignKeyConstraintsEnabled(true);
92+
}
93+
94+
@Override
95+
public void onCreate(SQLiteDatabase db) {
96+
String CREATE_POSTS_TABLE = "CREATE TABLE " + TABLE_POSTS +
97+
"(" +
98+
KEY_POST_ID + " INTEGER PRIMARY KEY," +
99+
KEY_POST_MEDIA_ID + " TEXT," +
100+
KEY_POST_USER_ID_FK + " INTEGER REFERENCES " + TABLE_USERS + "," +
101+
KEY_POST_IMAGE_ID_FK + " INTEGER REFERENCES " + TABLE_IMAGES + "," +
102+
KEY_POST_CAPTION + " TEXT," +
103+
KEY_POST_LIKES_COUNT + " INTEGER," +
104+
KEY_POST_COMMENTS_COUNT + " INTEGER," +
105+
KEY_POST_CREATED_TIME + " INTEGER" +
106+
")";
107+
108+
String CREATE_USERS_TABLE = "CREATE TABLE " + TABLE_USERS +
109+
"(" +
110+
KEY_USER_ID + " INTEGER PRIMARY KEY," +
111+
KEY_USER_NAME + " TEXT UNIQUE ON CONFLICT ROLLBACK," +
112+
KEY_USER_PROFILE_PICTURE_URL + " TEXT" +
113+
")";
114+
115+
String CREATE_IMAGES_TABLE = "CREATE TABLE " + TABLE_IMAGES +
116+
"(" +
117+
KEY_IMAGE_ID + " INTEGER PRIMARY KEY," +
118+
KEY_IMAGE_URL + " TEXT," +
119+
KEY_IMAGE_HEIGHT + " INTEGER," +
120+
KEY_IMAGE_WIDTH + " INTEGER" +
121+
")";
122+
123+
String CREATE_COMMENTS_TABLE = "CREATE TABLE " + TABLE_COMMENTS +
124+
"(" +
125+
KEY_COMMENT_ID + " INTEGER PRIMARY KEY," +
126+
KEY_COMMENT_TEXT + " TEXT," +
127+
KEY_COMMENT_CREATED_TIME + " INTEGER," +
128+
KEY_COMMENT_USER_ID_FK + " INTEGER REFERENCES " + TABLE_USERS +
129+
")";
130+
131+
String CREATE_POST_COMMENTS_TABLE = "CREATE TABLE " + TABLE_POST_COMMENTS +
132+
"(" +
133+
KEY_POST_COMMENT_POST_ID_FK + " INTEGER REFERENCES " + TABLE_POSTS + "," +
134+
KEY_POST_COMMENT_COMMENT_ID_FK + " INTEGER REFERENCES " + TABLE_COMMENTS + "," +
135+
"constraint " + CONSTRAINT_POST_COMMENTS_PK +
136+
" PRIMARY KEY(" +
137+
KEY_POST_COMMENT_POST_ID_FK + "," +
138+
KEY_POST_COMMENT_COMMENT_ID_FK +
139+
")" +
140+
")";
141+
142+
db.execSQL(CREATE_USERS_TABLE);
143+
db.execSQL(CREATE_IMAGES_TABLE);
144+
db.execSQL(CREATE_COMMENTS_TABLE);
145+
db.execSQL(CREATE_POSTS_TABLE);
146+
db.execSQL(CREATE_POST_COMMENTS_TABLE);
147+
}
148+
149+
@Override
150+
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
151+
// TODO: Implement this method
152+
}
153+
154+
public void emptyAllTables() {
155+
// TODO: Implement this method to delete all rows from all tables
156+
}
157+
158+
public void addInstagramPosts(List<InstagramPost> posts) {
159+
// TODO: Implement this method
160+
// Take a look at the helper methods addImage, addComment, etc as you implement this method
161+
// It's also a good idea to do this work in a transaction
162+
}
163+
164+
// Poor man's "upsert".
165+
// Since SQLite doesn't support "upsert" we need to fall back on an attempt to UPDATE (in case the
166+
// user already exists, followed by an INSERT (in case the user does not already exist).
167+
// Unfortunately, there is a bug with the insertOnConflict method
168+
// (https://code.google.com/p/android/issues/detail?id=13045) so we need to fall back to the more
169+
// verbose option of querying for the user's primary key if we did an update.
170+
private long addorUpdateUser(InstagramUser user) {
171+
if (user == null) {
172+
throw new IllegalArgumentException(String.format("Attemping to add a null user to %s", DATABASE_NAME));
173+
}
174+
175+
SQLiteDatabase db = getWritableDatabase();
176+
177+
ContentValues values = new ContentValues();
178+
values.put(KEY_USER_NAME, user.userName);
179+
values.put(KEY_USER_PROFILE_PICTURE_URL, user.profilePictureUrl);
180+
long userId = -1;
181+
182+
// First try to update the user in case the user already exists in DB
183+
int rows = db.update(TABLE_USERS, values, KEY_USER_NAME + "= ?", new String[] {user.userName});
184+
185+
// Check if update succeeded
186+
if (rows == 1) {
187+
// Get the primary key of the user we just updated
188+
String usersSelectQuery = String.format("SELECT %s FROM %s WHERE %s = ?",
189+
KEY_USER_ID, TABLE_USERS, KEY_USER_NAME);
190+
191+
Cursor usersCursor = db.rawQuery(usersSelectQuery, new String[]{String.valueOf(user.userName)});
192+
try {
193+
if (usersCursor.moveToFirst()) {
194+
userId = usersCursor.getInt(0);
195+
}
196+
// There should only be one user
197+
if (usersCursor.moveToNext()) {
198+
Log.wtf(TAG, "Too many primary keys returned");
199+
}
200+
} catch (Exception e) {
201+
Log.wtf(TAG, "Error while trying to add or update user");
202+
e.printStackTrace();
203+
} finally {
204+
usersCursor.close();
205+
}
206+
} else {
207+
// user with this userName did not already exist, so insert new user
208+
userId = db.insert(TABLE_USERS, null ,values);
209+
}
210+
211+
return userId;
212+
}
213+
214+
private long addImage(InstagramImage image) {
215+
if (image == null) {
216+
throw new IllegalArgumentException(String.format("Attemping to add a null image to %s", DATABASE_NAME));
217+
}
218+
219+
SQLiteDatabase db = getWritableDatabase();
220+
221+
ContentValues values = new ContentValues();
222+
values.put(KEY_IMAGE_URL, image.imageUrl);
223+
values.put(KEY_IMAGE_HEIGHT, image.imageHeight);
224+
values.put(KEY_IMAGE_WIDTH, image.imageHeight);
225+
226+
return db.insert(TABLE_IMAGES, null, values);
227+
}
228+
229+
private long addComment(InstagramComment comment, long postId) {
230+
if (comment == null) {
231+
throw new IllegalArgumentException(String.format("Attemping to add a null comment to %s", DATABASE_NAME));
232+
}
233+
SQLiteDatabase db = getWritableDatabase();
234+
235+
long commentUserId = addorUpdateUser(comment.user);
236+
237+
ContentValues values = new ContentValues();
238+
values.put(KEY_COMMENT_TEXT, comment.text);
239+
values.put(KEY_COMMENT_USER_ID_FK, commentUserId);
240+
values.put(KEY_COMMENT_CREATED_TIME, comment.createdTime);
241+
242+
long commentId = db.insert(TABLE_COMMENTS, null, values);
243+
addPostCommentMapping(postId, commentId);
244+
return commentId;
245+
}
246+
247+
private long addPostCommentMapping(long postId, long commentId) {
248+
249+
SQLiteDatabase db = getWritableDatabase();
250+
251+
ContentValues values = new ContentValues();
252+
values.put(KEY_POST_COMMENT_COMMENT_ID_FK, commentId);
253+
values.put(KEY_POST_COMMENT_POST_ID_FK, postId);
254+
255+
return db.insert(TABLE_POST_COMMENTS, null, values);
256+
}
257+
258+
public List<InstagramPost> getAllInstagramPosts() {
259+
List<InstagramPost> posts = new ArrayList<>();
260+
261+
String LEFT_OUTER_JOIN_FORMAT_STRING = "LEFT OUTER JOIN %s ON %s.%s = %s.%s";
262+
263+
String userJoin = String.format(LEFT_OUTER_JOIN_FORMAT_STRING,
264+
TABLE_USERS, TABLE_POSTS, KEY_POST_USER_ID_FK, TABLE_USERS, KEY_USER_ID);
265+
266+
String imageJoin = String.format(LEFT_OUTER_JOIN_FORMAT_STRING,
267+
TABLE_IMAGES, TABLE_POSTS, KEY_POST_IMAGE_ID_FK, TABLE_IMAGES, KEY_IMAGE_ID);
268+
269+
String postsSelectQuery = "SELECT * FROM " + TABLE_POSTS + " " + userJoin + " " + imageJoin;
270+
271+
String commentJoin = String.format(LEFT_OUTER_JOIN_FORMAT_STRING,
272+
TABLE_COMMENTS, TABLE_POST_COMMENTS, KEY_POST_COMMENT_COMMENT_ID_FK, TABLE_COMMENTS, KEY_COMMENT_ID);
273+
String commentUserJoin = String.format(LEFT_OUTER_JOIN_FORMAT_STRING,
274+
TABLE_USERS, TABLE_USERS, KEY_USER_ID, TABLE_COMMENTS, KEY_COMMENT_USER_ID_FK);
275+
276+
String commentsSelectQuery = String.format("SELECT * FROM %s %s %s WHERE %s = ?",
277+
TABLE_POST_COMMENTS, commentJoin, commentUserJoin, KEY_POST_COMMENT_POST_ID_FK);
278+
279+
SQLiteDatabase db = this.getReadableDatabase();
280+
Cursor postsCursor = db.rawQuery(postsSelectQuery, null);
281+
282+
try {
283+
if (postsCursor.moveToFirst()) {
284+
do {
285+
InstagramPost post = new InstagramPost();
286+
post.mediaId = postsCursor.getString(postsCursor.getColumnIndexOrThrow(KEY_POST_MEDIA_ID));
287+
post.caption = postsCursor.getString(postsCursor.getColumnIndexOrThrow(KEY_POST_CAPTION));
288+
post.likesCount = postsCursor.getInt(postsCursor.getColumnIndexOrThrow(KEY_POST_LIKES_COUNT));
289+
post.commentsCount = postsCursor.getInt(postsCursor.getColumnIndexOrThrow(KEY_POST_COMMENTS_COUNT));
290+
post.createdTime = postsCursor.getLong(postsCursor.getColumnIndexOrThrow(KEY_POST_CREATED_TIME));
291+
292+
InstagramUser user = new InstagramUser();
293+
user.userName = postsCursor.getString(postsCursor.getColumnIndexOrThrow(KEY_USER_NAME));
294+
user.profilePictureUrl = postsCursor.getString(postsCursor.getColumnIndexOrThrow(KEY_USER_PROFILE_PICTURE_URL));
295+
post.user = user;
296+
297+
InstagramImage image = new InstagramImage();
298+
image.imageUrl = postsCursor.getString(postsCursor.getColumnIndexOrThrow(KEY_IMAGE_URL));
299+
image.imageHeight = postsCursor.getInt(postsCursor.getColumnIndexOrThrow(KEY_IMAGE_HEIGHT));
300+
image.imageWidth = postsCursor.getInt(postsCursor.getColumnIndexOrThrow(KEY_IMAGE_WIDTH));
301+
post.image = image;
302+
303+
int key = postsCursor.getInt(0);
304+
305+
// Get all comments for this post
306+
Cursor commentsCursor = db.rawQuery(commentsSelectQuery, new String[]{String.valueOf(key)});
307+
try {
308+
if (commentsCursor.moveToFirst()) {
309+
do {
310+
InstagramComment comment = new InstagramComment();
311+
comment.text = commentsCursor.getString(commentsCursor.getColumnIndexOrThrow(KEY_COMMENT_TEXT));
312+
comment.createdTime = commentsCursor.getLong(commentsCursor.getColumnIndexOrThrow(KEY_COMMENT_CREATED_TIME));
313+
314+
InstagramUser commentUser = new InstagramUser();
315+
commentUser.userName = commentsCursor.getString(commentsCursor.getColumnIndexOrThrow(KEY_USER_NAME));
316+
commentUser.profilePictureUrl = commentsCursor.getString(commentsCursor.getColumnIndexOrThrow(KEY_USER_PROFILE_PICTURE_URL));
317+
comment.user = commentUser;
318+
319+
post.appendComment(comment);
320+
} while (commentsCursor.moveToNext());
321+
}
322+
} catch (Exception e) {
323+
Log.wtf(TAG, "Error while trying to get comments from database");
324+
e.printStackTrace();
325+
} finally {
326+
commentsCursor.close();
327+
}
328+
posts.add(post);
329+
} while (postsCursor.moveToNext());
330+
}
331+
} catch (Exception e) {
332+
Log.wtf(TAG, "Error while trying to get posts from database");
333+
e.printStackTrace();
334+
} finally {
335+
closeCursor(postsCursor);
336+
}
337+
338+
return posts;
339+
}
46340

47-
// ... add code ...
341+
private void closeCursor(Cursor cursor) {
342+
if (cursor != null && !cursor.isClosed()) {
343+
cursor.close();
344+
}
345+
}
48346
}

0 commit comments

Comments
 (0)