aboutsummaryrefslogtreecommitdiff
path: root/WordPress/src/main/java/org/wordpress/android/datasets/ReaderDatabase.java
blob: 9ae4f5521b05caff0b729753a12ed88c39408833 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
package org.wordpress.android.datasets;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import org.wordpress.android.WordPress;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * database for all reader information
 */
public class ReaderDatabase extends SQLiteOpenHelper {
    protected static final String DB_NAME = "wpreader.db";
    private static final int DB_VERSION = 125;

    /*
     * version history
     *   67 - added tbl_blog_info to ReaderBlogTable
     *   68 - added author_blog_id to ReaderCommentTable
     *   69 - renamed tbl_blog_urls to tbl_followed_blogs in ReaderBlogTable
     *   70 - added author_id to ReaderCommentTable and ReaderPostTable
     *   71 - added blog_id to ReaderUserTable
     *   72 - removed tbl_followed_blogs from ReaderBlogTable
     *   73 - added tbl_recommended_blogs to ReaderBlogTable
     *   74 - added primary_tag to ReaderPostTable
     *   75 - added secondary_tag to ReaderPostTable
     *   76 - added feed_id to ReaderBlogTable
     *   77 - restructured tag tables (ReaderTagTable)
     *   78 - added tag_type to ReaderPostTable.tbl_post_tags
     *   79 - added is_likes_enabled and is_sharing_enabled to tbl_posts
     *   80 - added tbl_comment_likes in ReaderLikeTable, added num_likes to tbl_comments
     *   81 - added image_url to tbl_blog_info
     *   82 - added idx_posts_timestamp to tbl_posts
     *   83 - removed tag_list from tbl_posts
     *   84 - added tbl_attachments
     *   85 - removed tbl_attachments, added attachments_json to tbl_posts
     *   90 - added default values for all INTEGER columns that were missing them (hotfix 3.1.1)
     *   92 - added default values for all INTEGER columns that were missing them (3.2)
     *   93 - tbl_posts text is now truncated to a max length (3.3)
     *   94 - added is_jetpack to tbl_posts (3.4)
     *   95 - added page_number to tbl_comments (3.4)
     *   96 - removed tbl_tag_updates, added date_updated to tbl_tags (3.4)
     *   97 - added short_url to tbl_posts
     *   98 - added feed_id to tbl_posts
     *   99 - added feed_url to tbl_blog_info
     *  100 - changed primary key on tbl_blog_info
     *  101 - dropped is_reblogged from ReaderPostTable
     *  102 - changed primary key of tbl_blog_info from blog_id+feed_id to just blog_id
     *  103 - added discover_json to ReaderPostTable
     *  104 - added word_count to ReaderPostTable
     *  105 - added date_updated to ReaderBlogTable
     *  106 - dropped is_likes_enabled and is_sharing_enabled from tbl_posts
     *  107 - "Blogs I Follow" renamed to "Followed Sites"
     *  108 - added "has_gap_marker" to tbl_post_tags
     *  109 - added "feed_item_id" to tbl_posts
     *  110 - added xpost_post_id and xpost_blog_id to tbl_posts
     *  111 - added author_first_name to tbl_posts
     *  112 - no structural change, just reset db
     *  113 - added tag_title to tag tables
     *  114 - renamed tag_name to tag_slug in tag tables
     *  115 - added ReaderSearchTable
     *  116 - added tag_display_name to tag tables
     *  117 - changed tbl_posts.timestamp from INTEGER to REAL
     *  118 - renamed tbl_search_history to tbl_search_suggestions
     *  119 - renamed tbl_posts.timestamp to sort_index
     *  120 - added "format" to tbl_posts
     *  121 - removed word_count from tbl_posts
     *  122 - changed tbl_posts primary key to pseudo_id
     *  123 - changed tbl_posts.published to tbl_posts.date
     *  124 - returned tbl_posts.published
     *  125 - added tbl_posts.railcar_json
     */

    /*
	 *  database singleton
	 */
    private static ReaderDatabase mReaderDb;
    private final static Object mDbLock = new Object();
    public static ReaderDatabase getDatabase() {
        if (mReaderDb == null) {
            synchronized(mDbLock) {
                if (mReaderDb == null) {
                    mReaderDb = new ReaderDatabase(WordPress.getContext());
                    // this ensures that onOpen() is called with a writable database (open will fail if app calls getReadableDb() first)
                    mReaderDb.getWritableDatabase();
                }
            }
        }
        return mReaderDb;
    }

    public static SQLiteDatabase getReadableDb() {
        return getDatabase().getReadableDatabase();
    }
    public static SQLiteDatabase getWritableDb() {
        return getDatabase().getWritableDatabase();
    }

    @Override
    public void onOpen(SQLiteDatabase db) {
        super.onOpen(db);
        //copyDatabase(db);
    }

    /*
     * resets (clears) the reader database
     */
    public static void reset() {
        // note that we must call getWritableDb() before getDatabase() in case the database
        // object hasn't been created yet
        SQLiteDatabase db = getWritableDb();
        getDatabase().reset(db);
    }

    public ReaderDatabase(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        createAllTables(db);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // for now just reset the db when upgrading, future versions may want to avoid this
        // and modify table structures, etc., on upgrade while preserving data
        AppLog.i(T.READER, "Upgrading database from version " + oldVersion + " to version " + newVersion);
        reset(db);
    }

    @Override
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // IMPORTANT: do NOT call super() here - doing so throws a SQLiteException
        AppLog.w(T.READER, "Downgrading database from version " + oldVersion + " to version " + newVersion);
        reset(db);
    }

    private void createAllTables(SQLiteDatabase db) {
        ReaderCommentTable.createTables(db);
        ReaderLikeTable.createTables(db);
        ReaderPostTable.createTables(db);
        ReaderTagTable.createTables(db);
        ReaderUserTable.createTables(db);
        ReaderThumbnailTable.createTables(db);
        ReaderBlogTable.createTables(db);
        ReaderSearchTable.createTables(db);
    }

    private void dropAllTables(SQLiteDatabase db) {
        ReaderCommentTable.dropTables(db);
        ReaderLikeTable.dropTables(db);
        ReaderPostTable.dropTables(db);
        ReaderTagTable.dropTables(db);
        ReaderUserTable.dropTables(db);
        ReaderThumbnailTable.dropTables(db);
        ReaderBlogTable.dropTables(db);
        ReaderSearchTable.dropTables(db);
    }

    /*
     * drop & recreate all tables (essentially clears the db of all data)
     */
    private void reset(SQLiteDatabase db) {
        db.beginTransaction();
        try {
            dropAllTables(db);
            createAllTables(db);
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
    }

    /*
     * purge older/unattached data - use purgeAsync() to do this in the background
     */
    private static void purge() {
        SQLiteDatabase db = getWritableDb();
        db.beginTransaction();
        try {
            int numPostsDeleted = ReaderPostTable.purge(db);

            // don't bother purging other data unless posts were purged
            if (numPostsDeleted > 0) {
                AppLog.i(T.READER, String.format("%d total posts purged", numPostsDeleted));

                // purge unattached comments
                int numCommentsDeleted = ReaderCommentTable.purge(db);
                if (numCommentsDeleted > 0) {
                    AppLog.i(T.READER, String.format("%d comments purged", numCommentsDeleted));
                }

                // purge unattached likes
                int numLikesDeleted = ReaderLikeTable.purge(db);
                if (numLikesDeleted > 0) {
                    AppLog.i(T.READER, String.format("%d likes purged", numLikesDeleted));
                }

                // purge unattached thumbnails
                int numThumbsPurged = ReaderThumbnailTable.purge(db);
                if (numThumbsPurged > 0) {
                    AppLog.i(T.READER, String.format("%d thumbnails purged", numThumbsPurged));
                }
            }
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
    }

    public static void purgeAsync() {
        new Thread() {
            @Override
            public void run() {
                purge();
            }
        }.start();
    }

    /*
     * used during development to copy database to external storage so we can access it via DDMS
     */
    private void copyDatabase(SQLiteDatabase db) {
        String copyFrom = db.getPath();
        String copyTo = WordPress.getContext().getExternalFilesDir(null).getAbsolutePath() + "/" + DB_NAME;

        try {
            InputStream input = new FileInputStream(copyFrom);
            OutputStream output = new FileOutputStream(copyTo);

            byte[] buffer = new byte[1024];
            int length;
            while ((length = input.read(buffer)) > 0) {
                output.write(buffer, 0, length);
            }

            output.flush();
            output.close();
            input.close();
        } catch (IOException e) {
            AppLog.e(T.DB, "failed to copy reader database", e);
        }
    }


}