|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.db; |
|
7 |
|
8 import java.io.ByteArrayOutputStream; |
|
9 import java.io.File; |
|
10 import java.lang.reflect.Field; |
|
11 import java.util.ArrayList; |
|
12 import java.util.List; |
|
13 import java.util.Locale; |
|
14 import java.util.regex.Matcher; |
|
15 import java.util.regex.Pattern; |
|
16 |
|
17 import org.json.JSONArray; |
|
18 import org.json.JSONException; |
|
19 import org.json.JSONObject; |
|
20 import org.mozilla.gecko.AppConstants; |
|
21 import org.mozilla.gecko.Distribution; |
|
22 import org.mozilla.gecko.R; |
|
23 import org.mozilla.gecko.db.BrowserContract.Bookmarks; |
|
24 import org.mozilla.gecko.db.BrowserContract.Combined; |
|
25 import org.mozilla.gecko.db.BrowserContract.FaviconColumns; |
|
26 import org.mozilla.gecko.db.BrowserContract.Favicons; |
|
27 import org.mozilla.gecko.db.BrowserContract.History; |
|
28 import org.mozilla.gecko.db.BrowserContract.Obsolete; |
|
29 import org.mozilla.gecko.db.BrowserContract.ReadingListItems; |
|
30 import org.mozilla.gecko.db.BrowserContract.Thumbnails; |
|
31 import org.mozilla.gecko.gfx.BitmapUtils; |
|
32 import org.mozilla.gecko.sync.Utils; |
|
33 import org.mozilla.gecko.util.GeckoJarReader; |
|
34 import org.mozilla.gecko.util.ThreadUtils; |
|
35 |
|
36 import android.content.ContentValues; |
|
37 import android.content.Context; |
|
38 import android.database.Cursor; |
|
39 import android.database.DatabaseUtils; |
|
40 import android.database.SQLException; |
|
41 import android.database.sqlite.SQLiteDatabase; |
|
42 import android.database.sqlite.SQLiteOpenHelper; |
|
43 import android.graphics.Bitmap; |
|
44 import android.net.Uri; |
|
45 import android.os.Build; |
|
46 import android.text.TextUtils; |
|
47 import android.util.Log; |
|
48 |
|
49 |
|
50 final class BrowserDatabaseHelper extends SQLiteOpenHelper { |
|
51 |
|
52 private static final String LOGTAG = "GeckoBrowserDBHelper"; |
|
53 public static final int DATABASE_VERSION = 18; |
|
54 public static final String DATABASE_NAME = "browser.db"; |
|
55 |
|
56 final protected Context mContext; |
|
57 |
|
58 static final String TABLE_BOOKMARKS = Bookmarks.TABLE_NAME; |
|
59 static final String TABLE_HISTORY = History.TABLE_NAME; |
|
60 static final String TABLE_FAVICONS = Favicons.TABLE_NAME; |
|
61 static final String TABLE_THUMBNAILS = Thumbnails.TABLE_NAME; |
|
62 static final String TABLE_READING_LIST = ReadingListItems.TABLE_NAME; |
|
63 |
|
64 static final String VIEW_COMBINED = Combined.VIEW_NAME; |
|
65 static final String VIEW_BOOKMARKS_WITH_FAVICONS = Bookmarks.VIEW_WITH_FAVICONS; |
|
66 static final String VIEW_HISTORY_WITH_FAVICONS = History.VIEW_WITH_FAVICONS; |
|
67 static final String VIEW_COMBINED_WITH_FAVICONS = Combined.VIEW_WITH_FAVICONS; |
|
68 |
|
69 static final String TABLE_BOOKMARKS_JOIN_FAVICONS = TABLE_BOOKMARKS + " LEFT OUTER JOIN " + |
|
70 TABLE_FAVICONS + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.FAVICON_ID) + " = " + |
|
71 qualifyColumn(TABLE_FAVICONS, Favicons._ID); |
|
72 |
|
73 static final String TABLE_HISTORY_JOIN_FAVICONS = TABLE_HISTORY + " LEFT OUTER JOIN " + |
|
74 TABLE_FAVICONS + " ON " + qualifyColumn(TABLE_HISTORY, History.FAVICON_ID) + " = " + |
|
75 qualifyColumn(TABLE_FAVICONS, Favicons._ID); |
|
76 |
|
77 static final String TABLE_BOOKMARKS_TMP = TABLE_BOOKMARKS + "_tmp"; |
|
78 static final String TABLE_HISTORY_TMP = TABLE_HISTORY + "_tmp"; |
|
79 static final String TABLE_IMAGES_TMP = Obsolete.TABLE_IMAGES + "_tmp"; |
|
80 |
|
81 private static final String[] mobileIdColumns = new String[] { Bookmarks._ID }; |
|
82 private static final String[] mobileIdSelectionArgs = new String[] { Bookmarks.MOBILE_FOLDER_GUID }; |
|
83 |
|
84 public BrowserDatabaseHelper(Context context, String databasePath) { |
|
85 super(context, databasePath, null, DATABASE_VERSION); |
|
86 mContext = context; |
|
87 } |
|
88 |
|
89 private void createBookmarksTable(SQLiteDatabase db) { |
|
90 debug("Creating " + TABLE_BOOKMARKS + " table"); |
|
91 |
|
92 // Android versions older than Froyo ship with an sqlite |
|
93 // that doesn't support foreign keys. |
|
94 String foreignKeyOnParent = null; |
|
95 if (Build.VERSION.SDK_INT >= 8) { |
|
96 foreignKeyOnParent = ", FOREIGN KEY (" + Bookmarks.PARENT + |
|
97 ") REFERENCES " + TABLE_BOOKMARKS + "(" + Bookmarks._ID + ")"; |
|
98 } |
|
99 |
|
100 db.execSQL("CREATE TABLE " + TABLE_BOOKMARKS + "(" + |
|
101 Bookmarks._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + |
|
102 Bookmarks.TITLE + " TEXT," + |
|
103 Bookmarks.URL + " TEXT," + |
|
104 Bookmarks.TYPE + " INTEGER NOT NULL DEFAULT " + Bookmarks.TYPE_BOOKMARK + "," + |
|
105 Bookmarks.PARENT + " INTEGER," + |
|
106 Bookmarks.POSITION + " INTEGER NOT NULL," + |
|
107 Bookmarks.KEYWORD + " TEXT," + |
|
108 Bookmarks.DESCRIPTION + " TEXT," + |
|
109 Bookmarks.TAGS + " TEXT," + |
|
110 Bookmarks.DATE_CREATED + " INTEGER," + |
|
111 Bookmarks.DATE_MODIFIED + " INTEGER," + |
|
112 Bookmarks.GUID + " TEXT NOT NULL," + |
|
113 Bookmarks.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + |
|
114 (foreignKeyOnParent != null ? foreignKeyOnParent : "") + |
|
115 ");"); |
|
116 |
|
117 db.execSQL("CREATE INDEX bookmarks_url_index ON " + TABLE_BOOKMARKS + "(" |
|
118 + Bookmarks.URL + ")"); |
|
119 db.execSQL("CREATE INDEX bookmarks_type_deleted_index ON " + TABLE_BOOKMARKS + "(" |
|
120 + Bookmarks.TYPE + ", " + Bookmarks.IS_DELETED + ")"); |
|
121 db.execSQL("CREATE UNIQUE INDEX bookmarks_guid_index ON " + TABLE_BOOKMARKS + "(" |
|
122 + Bookmarks.GUID + ")"); |
|
123 db.execSQL("CREATE INDEX bookmarks_modified_index ON " + TABLE_BOOKMARKS + "(" |
|
124 + Bookmarks.DATE_MODIFIED + ")"); |
|
125 } |
|
126 |
|
127 private void createBookmarksTableOn13(SQLiteDatabase db) { |
|
128 debug("Creating " + TABLE_BOOKMARKS + " table"); |
|
129 |
|
130 // Android versions older than Froyo ship with an sqlite |
|
131 // that doesn't support foreign keys. |
|
132 String foreignKeyOnParent = null; |
|
133 if (Build.VERSION.SDK_INT >= 8) { |
|
134 foreignKeyOnParent = ", FOREIGN KEY (" + Bookmarks.PARENT + |
|
135 ") REFERENCES " + TABLE_BOOKMARKS + "(" + Bookmarks._ID + ")"; |
|
136 } |
|
137 |
|
138 db.execSQL("CREATE TABLE " + TABLE_BOOKMARKS + "(" + |
|
139 Bookmarks._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + |
|
140 Bookmarks.TITLE + " TEXT," + |
|
141 Bookmarks.URL + " TEXT," + |
|
142 Bookmarks.TYPE + " INTEGER NOT NULL DEFAULT " + Bookmarks.TYPE_BOOKMARK + "," + |
|
143 Bookmarks.PARENT + " INTEGER," + |
|
144 Bookmarks.POSITION + " INTEGER NOT NULL," + |
|
145 Bookmarks.KEYWORD + " TEXT," + |
|
146 Bookmarks.DESCRIPTION + " TEXT," + |
|
147 Bookmarks.TAGS + " TEXT," + |
|
148 Bookmarks.FAVICON_ID + " INTEGER," + |
|
149 Bookmarks.DATE_CREATED + " INTEGER," + |
|
150 Bookmarks.DATE_MODIFIED + " INTEGER," + |
|
151 Bookmarks.GUID + " TEXT NOT NULL," + |
|
152 Bookmarks.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + |
|
153 (foreignKeyOnParent != null ? foreignKeyOnParent : "") + |
|
154 ");"); |
|
155 |
|
156 db.execSQL("CREATE INDEX bookmarks_url_index ON " + TABLE_BOOKMARKS + "(" |
|
157 + Bookmarks.URL + ")"); |
|
158 db.execSQL("CREATE INDEX bookmarks_type_deleted_index ON " + TABLE_BOOKMARKS + "(" |
|
159 + Bookmarks.TYPE + ", " + Bookmarks.IS_DELETED + ")"); |
|
160 db.execSQL("CREATE UNIQUE INDEX bookmarks_guid_index ON " + TABLE_BOOKMARKS + "(" |
|
161 + Bookmarks.GUID + ")"); |
|
162 db.execSQL("CREATE INDEX bookmarks_modified_index ON " + TABLE_BOOKMARKS + "(" |
|
163 + Bookmarks.DATE_MODIFIED + ")"); |
|
164 } |
|
165 |
|
166 private void createHistoryTable(SQLiteDatabase db) { |
|
167 debug("Creating " + TABLE_HISTORY + " table"); |
|
168 db.execSQL("CREATE TABLE " + TABLE_HISTORY + "(" + |
|
169 History._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + |
|
170 History.TITLE + " TEXT," + |
|
171 History.URL + " TEXT NOT NULL," + |
|
172 History.VISITS + " INTEGER NOT NULL DEFAULT 0," + |
|
173 History.DATE_LAST_VISITED + " INTEGER," + |
|
174 History.DATE_CREATED + " INTEGER," + |
|
175 History.DATE_MODIFIED + " INTEGER," + |
|
176 History.GUID + " TEXT NOT NULL," + |
|
177 History.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + |
|
178 ");"); |
|
179 |
|
180 db.execSQL("CREATE INDEX history_url_index ON " + TABLE_HISTORY + "(" |
|
181 + History.URL + ")"); |
|
182 db.execSQL("CREATE UNIQUE INDEX history_guid_index ON " + TABLE_HISTORY + "(" |
|
183 + History.GUID + ")"); |
|
184 db.execSQL("CREATE INDEX history_modified_index ON " + TABLE_HISTORY + "(" |
|
185 + History.DATE_MODIFIED + ")"); |
|
186 db.execSQL("CREATE INDEX history_visited_index ON " + TABLE_HISTORY + "(" |
|
187 + History.DATE_LAST_VISITED + ")"); |
|
188 } |
|
189 |
|
190 private void createHistoryTableOn13(SQLiteDatabase db) { |
|
191 debug("Creating " + TABLE_HISTORY + " table"); |
|
192 db.execSQL("CREATE TABLE " + TABLE_HISTORY + "(" + |
|
193 History._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + |
|
194 History.TITLE + " TEXT," + |
|
195 History.URL + " TEXT NOT NULL," + |
|
196 History.VISITS + " INTEGER NOT NULL DEFAULT 0," + |
|
197 History.FAVICON_ID + " INTEGER," + |
|
198 History.DATE_LAST_VISITED + " INTEGER," + |
|
199 History.DATE_CREATED + " INTEGER," + |
|
200 History.DATE_MODIFIED + " INTEGER," + |
|
201 History.GUID + " TEXT NOT NULL," + |
|
202 History.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + |
|
203 ");"); |
|
204 |
|
205 db.execSQL("CREATE INDEX history_url_index ON " + TABLE_HISTORY + "(" |
|
206 + History.URL + ")"); |
|
207 db.execSQL("CREATE UNIQUE INDEX history_guid_index ON " + TABLE_HISTORY + "(" |
|
208 + History.GUID + ")"); |
|
209 db.execSQL("CREATE INDEX history_modified_index ON " + TABLE_HISTORY + "(" |
|
210 + History.DATE_MODIFIED + ")"); |
|
211 db.execSQL("CREATE INDEX history_visited_index ON " + TABLE_HISTORY + "(" |
|
212 + History.DATE_LAST_VISITED + ")"); |
|
213 } |
|
214 |
|
215 private void createImagesTable(SQLiteDatabase db) { |
|
216 debug("Creating " + Obsolete.TABLE_IMAGES + " table"); |
|
217 db.execSQL("CREATE TABLE " + Obsolete.TABLE_IMAGES + " (" + |
|
218 Obsolete.Images._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + |
|
219 Obsolete.Images.URL + " TEXT UNIQUE NOT NULL," + |
|
220 Obsolete.Images.FAVICON + " BLOB," + |
|
221 Obsolete.Images.FAVICON_URL + " TEXT," + |
|
222 Obsolete.Images.THUMBNAIL + " BLOB," + |
|
223 Obsolete.Images.DATE_CREATED + " INTEGER," + |
|
224 Obsolete.Images.DATE_MODIFIED + " INTEGER," + |
|
225 Obsolete.Images.GUID + " TEXT NOT NULL," + |
|
226 Obsolete.Images.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + |
|
227 ");"); |
|
228 |
|
229 db.execSQL("CREATE INDEX images_url_index ON " + Obsolete.TABLE_IMAGES + "(" |
|
230 + Obsolete.Images.URL + ")"); |
|
231 db.execSQL("CREATE UNIQUE INDEX images_guid_index ON " + Obsolete.TABLE_IMAGES + "(" |
|
232 + Obsolete.Images.GUID + ")"); |
|
233 db.execSQL("CREATE INDEX images_modified_index ON " + Obsolete.TABLE_IMAGES + "(" |
|
234 + Obsolete.Images.DATE_MODIFIED + ")"); |
|
235 } |
|
236 |
|
237 private void createFaviconsTable(SQLiteDatabase db) { |
|
238 debug("Creating " + TABLE_FAVICONS + " table"); |
|
239 db.execSQL("CREATE TABLE " + TABLE_FAVICONS + " (" + |
|
240 Favicons._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + |
|
241 Favicons.URL + " TEXT UNIQUE," + |
|
242 Favicons.DATA + " BLOB," + |
|
243 Favicons.DATE_CREATED + " INTEGER," + |
|
244 Favicons.DATE_MODIFIED + " INTEGER" + |
|
245 ");"); |
|
246 |
|
247 db.execSQL("CREATE INDEX favicons_url_index ON " + TABLE_FAVICONS + "(" |
|
248 + Favicons.URL + ")"); |
|
249 db.execSQL("CREATE INDEX favicons_modified_index ON " + TABLE_FAVICONS + "(" |
|
250 + Favicons.DATE_MODIFIED + ")"); |
|
251 } |
|
252 |
|
253 private void createThumbnailsTable(SQLiteDatabase db) { |
|
254 debug("Creating " + TABLE_THUMBNAILS + " table"); |
|
255 db.execSQL("CREATE TABLE " + TABLE_THUMBNAILS + " (" + |
|
256 Thumbnails._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + |
|
257 Thumbnails.URL + " TEXT UNIQUE," + |
|
258 Thumbnails.DATA + " BLOB" + |
|
259 ");"); |
|
260 |
|
261 db.execSQL("CREATE INDEX thumbnails_url_index ON " + TABLE_THUMBNAILS + "(" |
|
262 + Thumbnails.URL + ")"); |
|
263 } |
|
264 |
|
265 private void createBookmarksWithImagesView(SQLiteDatabase db) { |
|
266 debug("Creating " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES + " view"); |
|
267 |
|
268 db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES + " AS " + |
|
269 "SELECT " + qualifyColumn(TABLE_BOOKMARKS, "*") + |
|
270 ", " + Obsolete.Images.FAVICON + ", " + Obsolete.Images.THUMBNAIL + " FROM " + |
|
271 Obsolete.TABLE_BOOKMARKS_JOIN_IMAGES); |
|
272 } |
|
273 |
|
274 private void createBookmarksWithFaviconsView(SQLiteDatabase db) { |
|
275 debug("Creating " + VIEW_BOOKMARKS_WITH_FAVICONS + " view"); |
|
276 |
|
277 db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_BOOKMARKS_WITH_FAVICONS + " AS " + |
|
278 "SELECT " + qualifyColumn(TABLE_BOOKMARKS, "*") + |
|
279 ", " + qualifyColumn(TABLE_FAVICONS, Favicons.DATA) + " AS " + Bookmarks.FAVICON + |
|
280 ", " + qualifyColumn(TABLE_FAVICONS, Favicons.URL) + " AS " + Bookmarks.FAVICON_URL + |
|
281 " FROM " + TABLE_BOOKMARKS_JOIN_FAVICONS); |
|
282 } |
|
283 |
|
284 private void createHistoryWithImagesView(SQLiteDatabase db) { |
|
285 debug("Creating " + Obsolete.VIEW_HISTORY_WITH_IMAGES + " view"); |
|
286 |
|
287 db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES + " AS " + |
|
288 "SELECT " + qualifyColumn(TABLE_HISTORY, "*") + |
|
289 ", " + Obsolete.Images.FAVICON + ", " + Obsolete.Images.THUMBNAIL + " FROM " + |
|
290 Obsolete.TABLE_HISTORY_JOIN_IMAGES); |
|
291 } |
|
292 |
|
293 private void createHistoryWithFaviconsView(SQLiteDatabase db) { |
|
294 debug("Creating " + VIEW_HISTORY_WITH_FAVICONS + " view"); |
|
295 |
|
296 db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_HISTORY_WITH_FAVICONS + " AS " + |
|
297 "SELECT " + qualifyColumn(TABLE_HISTORY, "*") + |
|
298 ", " + qualifyColumn(TABLE_FAVICONS, Favicons.DATA) + " AS " + History.FAVICON + |
|
299 ", " + qualifyColumn(TABLE_FAVICONS, Favicons.URL) + " AS " + History.FAVICON_URL + |
|
300 " FROM " + TABLE_HISTORY_JOIN_FAVICONS); |
|
301 } |
|
302 |
|
303 private void createCombinedWithImagesView(SQLiteDatabase db) { |
|
304 debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); |
|
305 |
|
306 db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + |
|
307 " SELECT " + Combined.BOOKMARK_ID + ", " + |
|
308 Combined.HISTORY_ID + ", " + |
|
309 // We need to return an _id column because CursorAdapter requires it for its |
|
310 // default implementation for the getItemId() method. However, since |
|
311 // we're not using this feature in the parts of the UI using this view, |
|
312 // we can just use 0 for all rows. |
|
313 "0 AS " + Combined._ID + ", " + |
|
314 Combined.URL + ", " + |
|
315 Combined.TITLE + ", " + |
|
316 Combined.VISITS + ", " + |
|
317 Combined.DATE_LAST_VISITED + ", " + |
|
318 qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + |
|
319 qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + |
|
320 " FROM (" + |
|
321 // Bookmarks without history. |
|
322 " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + |
|
323 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + |
|
324 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + |
|
325 "-1 AS " + Combined.HISTORY_ID + ", " + |
|
326 "-1 AS " + Combined.VISITS + ", " + |
|
327 "-1 AS " + Combined.DATE_LAST_VISITED + |
|
328 " FROM " + TABLE_BOOKMARKS + |
|
329 " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + |
|
330 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + |
|
331 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + |
|
332 " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + |
|
333 " UNION ALL" + |
|
334 // History with and without bookmark. |
|
335 " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + |
|
336 qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + |
|
337 // Prioritze bookmark titles over history titles, since the user may have |
|
338 // customized the title for a bookmark. |
|
339 "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + |
|
340 qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + |
|
341 qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + |
|
342 qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + |
|
343 qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + |
|
344 " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + |
|
345 " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + |
|
346 " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + |
|
347 qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + |
|
348 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + |
|
349 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ")" + |
|
350 ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + |
|
351 " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); |
|
352 } |
|
353 |
|
354 private void createCombinedWithImagesViewOn9(SQLiteDatabase db) { |
|
355 debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); |
|
356 |
|
357 db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + |
|
358 " SELECT " + Combined.BOOKMARK_ID + ", " + |
|
359 Combined.HISTORY_ID + ", " + |
|
360 // We need to return an _id column because CursorAdapter requires it for its |
|
361 // default implementation for the getItemId() method. However, since |
|
362 // we're not using this feature in the parts of the UI using this view, |
|
363 // we can just use 0 for all rows. |
|
364 "0 AS " + Combined._ID + ", " + |
|
365 Combined.URL + ", " + |
|
366 Combined.TITLE + ", " + |
|
367 Combined.VISITS + ", " + |
|
368 Combined.DISPLAY + ", " + |
|
369 Combined.DATE_LAST_VISITED + ", " + |
|
370 qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + |
|
371 qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + |
|
372 " FROM (" + |
|
373 // Bookmarks without history. |
|
374 " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + |
|
375 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + |
|
376 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + |
|
377 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + |
|
378 Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + |
|
379 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
380 "-1 AS " + Combined.HISTORY_ID + ", " + |
|
381 "-1 AS " + Combined.VISITS + ", " + |
|
382 "-1 AS " + Combined.DATE_LAST_VISITED + |
|
383 " FROM " + TABLE_BOOKMARKS + |
|
384 " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + |
|
385 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + |
|
386 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + |
|
387 " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + |
|
388 " UNION ALL" + |
|
389 // History with and without bookmark. |
|
390 " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + |
|
391 qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + |
|
392 // Prioritze bookmark titles over history titles, since the user may have |
|
393 // customized the title for a bookmark. |
|
394 "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + |
|
395 qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + |
|
396 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + |
|
397 Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + |
|
398 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
399 qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + |
|
400 qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + |
|
401 qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + |
|
402 " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + |
|
403 " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + |
|
404 " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + |
|
405 qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + |
|
406 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + |
|
407 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ")" + |
|
408 ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + |
|
409 " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); |
|
410 } |
|
411 |
|
412 private void createCombinedWithImagesViewOn10(SQLiteDatabase db) { |
|
413 debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); |
|
414 |
|
415 db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + |
|
416 " SELECT " + Combined.BOOKMARK_ID + ", " + |
|
417 Combined.HISTORY_ID + ", " + |
|
418 // We need to return an _id column because CursorAdapter requires it for its |
|
419 // default implementation for the getItemId() method. However, since |
|
420 // we're not using this feature in the parts of the UI using this view, |
|
421 // we can just use 0 for all rows. |
|
422 "0 AS " + Combined._ID + ", " + |
|
423 Combined.URL + ", " + |
|
424 Combined.TITLE + ", " + |
|
425 Combined.VISITS + ", " + |
|
426 Combined.DISPLAY + ", " + |
|
427 Combined.DATE_LAST_VISITED + ", " + |
|
428 qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + |
|
429 qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + |
|
430 " FROM (" + |
|
431 // Bookmarks without history. |
|
432 " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + |
|
433 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + |
|
434 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + |
|
435 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + |
|
436 Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + |
|
437 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
438 "-1 AS " + Combined.HISTORY_ID + ", " + |
|
439 "-1 AS " + Combined.VISITS + ", " + |
|
440 "-1 AS " + Combined.DATE_LAST_VISITED + |
|
441 " FROM " + TABLE_BOOKMARKS + |
|
442 " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + |
|
443 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + |
|
444 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + |
|
445 " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + |
|
446 " UNION ALL" + |
|
447 // History with and without bookmark. |
|
448 " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + |
|
449 qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + |
|
450 qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + |
|
451 // Prioritze bookmark titles over history titles, since the user may have |
|
452 // customized the title for a bookmark. |
|
453 "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + |
|
454 qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + |
|
455 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + |
|
456 Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + |
|
457 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
458 qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + |
|
459 qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + |
|
460 qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + |
|
461 " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + |
|
462 " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + |
|
463 " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + |
|
464 qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + |
|
465 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + |
|
466 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ")" + |
|
467 ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + |
|
468 " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); |
|
469 } |
|
470 |
|
471 private void createCombinedWithImagesViewOn11(SQLiteDatabase db) { |
|
472 debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); |
|
473 |
|
474 db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + |
|
475 " SELECT " + Combined.BOOKMARK_ID + ", " + |
|
476 Combined.HISTORY_ID + ", " + |
|
477 // We need to return an _id column because CursorAdapter requires it for its |
|
478 // default implementation for the getItemId() method. However, since |
|
479 // we're not using this feature in the parts of the UI using this view, |
|
480 // we can just use 0 for all rows. |
|
481 "0 AS " + Combined._ID + ", " + |
|
482 Combined.URL + ", " + |
|
483 Combined.TITLE + ", " + |
|
484 Combined.VISITS + ", " + |
|
485 Combined.DISPLAY + ", " + |
|
486 Combined.DATE_LAST_VISITED + ", " + |
|
487 qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + |
|
488 qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + |
|
489 " FROM (" + |
|
490 // Bookmarks without history. |
|
491 " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + |
|
492 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + |
|
493 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + |
|
494 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + |
|
495 Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + |
|
496 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
497 "-1 AS " + Combined.HISTORY_ID + ", " + |
|
498 "-1 AS " + Combined.VISITS + ", " + |
|
499 "-1 AS " + Combined.DATE_LAST_VISITED + |
|
500 " FROM " + TABLE_BOOKMARKS + |
|
501 " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + |
|
502 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + |
|
503 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + |
|
504 " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + |
|
505 " UNION ALL" + |
|
506 // History with and without bookmark. |
|
507 " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + |
|
508 qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + |
|
509 qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + |
|
510 // Prioritze bookmark titles over history titles, since the user may have |
|
511 // customized the title for a bookmark. |
|
512 "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + |
|
513 qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + |
|
514 // Only use DISPLAY_READER if the matching bookmark entry inside reading |
|
515 // list folder is not marked as deleted. |
|
516 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " + |
|
517 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID + |
|
518 " THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " + |
|
519 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
520 qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + |
|
521 qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + |
|
522 qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + |
|
523 " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + |
|
524 " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + |
|
525 " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + |
|
526 qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + |
|
527 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + |
|
528 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + |
|
529 ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + |
|
530 " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); |
|
531 } |
|
532 |
|
533 private void createCombinedViewOn12(SQLiteDatabase db) { |
|
534 debug("Creating " + VIEW_COMBINED + " view"); |
|
535 |
|
536 db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED + " AS" + |
|
537 " SELECT " + Combined.BOOKMARK_ID + ", " + |
|
538 Combined.HISTORY_ID + ", " + |
|
539 // We need to return an _id column because CursorAdapter requires it for its |
|
540 // default implementation for the getItemId() method. However, since |
|
541 // we're not using this feature in the parts of the UI using this view, |
|
542 // we can just use 0 for all rows. |
|
543 "0 AS " + Combined._ID + ", " + |
|
544 Combined.URL + ", " + |
|
545 Combined.TITLE + ", " + |
|
546 Combined.VISITS + ", " + |
|
547 Combined.DISPLAY + ", " + |
|
548 Combined.DATE_LAST_VISITED + |
|
549 " FROM (" + |
|
550 // Bookmarks without history. |
|
551 " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + |
|
552 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + |
|
553 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + |
|
554 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + |
|
555 Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + |
|
556 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
557 "-1 AS " + Combined.HISTORY_ID + ", " + |
|
558 "-1 AS " + Combined.VISITS + ", " + |
|
559 "-1 AS " + Combined.DATE_LAST_VISITED + |
|
560 " FROM " + TABLE_BOOKMARKS + |
|
561 " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + |
|
562 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + |
|
563 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + |
|
564 " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + |
|
565 " UNION ALL" + |
|
566 // History with and without bookmark. |
|
567 " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + |
|
568 qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + |
|
569 qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + |
|
570 // Prioritze bookmark titles over history titles, since the user may have |
|
571 // customized the title for a bookmark. |
|
572 "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + |
|
573 qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + |
|
574 // Only use DISPLAY_READER if the matching bookmark entry inside reading |
|
575 // list folder is not marked as deleted. |
|
576 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " + |
|
577 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID + |
|
578 " THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " + |
|
579 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
580 qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + |
|
581 qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + |
|
582 qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + |
|
583 " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + |
|
584 " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + |
|
585 " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + |
|
586 qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + |
|
587 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + |
|
588 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + |
|
589 ")"); |
|
590 |
|
591 debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); |
|
592 |
|
593 db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + |
|
594 " SELECT *, " + |
|
595 qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + |
|
596 qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + |
|
597 " FROM " + VIEW_COMBINED + " LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + |
|
598 " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); |
|
599 } |
|
600 |
|
601 private void createCombinedViewOn13(SQLiteDatabase db) { |
|
602 debug("Creating " + VIEW_COMBINED + " view"); |
|
603 |
|
604 db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED + " AS" + |
|
605 " SELECT " + Combined.BOOKMARK_ID + ", " + |
|
606 Combined.HISTORY_ID + ", " + |
|
607 // We need to return an _id column because CursorAdapter requires it for its |
|
608 // default implementation for the getItemId() method. However, since |
|
609 // we're not using this feature in the parts of the UI using this view, |
|
610 // we can just use 0 for all rows. |
|
611 "0 AS " + Combined._ID + ", " + |
|
612 Combined.URL + ", " + |
|
613 Combined.TITLE + ", " + |
|
614 Combined.VISITS + ", " + |
|
615 Combined.DISPLAY + ", " + |
|
616 Combined.DATE_LAST_VISITED + ", " + |
|
617 Combined.FAVICON_ID + |
|
618 " FROM (" + |
|
619 // Bookmarks without history. |
|
620 " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + |
|
621 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + |
|
622 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + |
|
623 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + |
|
624 Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + |
|
625 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
626 "-1 AS " + Combined.HISTORY_ID + ", " + |
|
627 "-1 AS " + Combined.VISITS + ", " + |
|
628 "-1 AS " + Combined.DATE_LAST_VISITED + ", " + |
|
629 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.FAVICON_ID) + " AS " + Combined.FAVICON_ID + |
|
630 " FROM " + TABLE_BOOKMARKS + |
|
631 " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + |
|
632 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + |
|
633 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + |
|
634 " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + |
|
635 " UNION ALL" + |
|
636 // History with and without bookmark. |
|
637 " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + |
|
638 qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + |
|
639 qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + |
|
640 // Prioritize bookmark titles over history titles, since the user may have |
|
641 // customized the title for a bookmark. |
|
642 "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + |
|
643 qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + |
|
644 // Only use DISPLAY_READER if the matching bookmark entry inside reading |
|
645 // list folder is not marked as deleted. |
|
646 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " + |
|
647 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID + |
|
648 " THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " + |
|
649 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
650 qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + |
|
651 qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + |
|
652 qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + ", " + |
|
653 qualifyColumn(TABLE_HISTORY, History.FAVICON_ID) + " AS " + Combined.FAVICON_ID + |
|
654 " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + |
|
655 " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + |
|
656 " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + |
|
657 qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + |
|
658 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + |
|
659 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + |
|
660 ")"); |
|
661 |
|
662 debug("Creating " + VIEW_COMBINED_WITH_FAVICONS + " view"); |
|
663 |
|
664 db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED_WITH_FAVICONS + " AS" + |
|
665 " SELECT " + qualifyColumn(VIEW_COMBINED, "*") + ", " + |
|
666 qualifyColumn(TABLE_FAVICONS, Favicons.URL) + " AS " + Combined.FAVICON_URL + ", " + |
|
667 qualifyColumn(TABLE_FAVICONS, Favicons.DATA) + " AS " + Combined.FAVICON + |
|
668 " FROM " + VIEW_COMBINED + " LEFT OUTER JOIN " + TABLE_FAVICONS + |
|
669 " ON " + Combined.FAVICON_ID + " = " + qualifyColumn(TABLE_FAVICONS, Favicons._ID)); |
|
670 } |
|
671 |
|
672 private void createCombinedViewOn16(SQLiteDatabase db) { |
|
673 debug("Creating " + VIEW_COMBINED + " view"); |
|
674 |
|
675 db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED + " AS" + |
|
676 " SELECT " + Combined.BOOKMARK_ID + ", " + |
|
677 Combined.HISTORY_ID + ", " + |
|
678 // We need to return an _id column because CursorAdapter requires it for its |
|
679 // default implementation for the getItemId() method. However, since |
|
680 // we're not using this feature in the parts of the UI using this view, |
|
681 // we can just use 0 for all rows. |
|
682 "0 AS " + Combined._ID + ", " + |
|
683 Combined.URL + ", " + |
|
684 Combined.TITLE + ", " + |
|
685 Combined.VISITS + ", " + |
|
686 Combined.DISPLAY + ", " + |
|
687 Combined.DATE_LAST_VISITED + ", " + |
|
688 Combined.FAVICON_ID + |
|
689 " FROM (" + |
|
690 // Bookmarks without history. |
|
691 " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + |
|
692 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + |
|
693 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + |
|
694 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + |
|
695 Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + |
|
696 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
697 "-1 AS " + Combined.HISTORY_ID + ", " + |
|
698 "-1 AS " + Combined.VISITS + ", " + |
|
699 "-1 AS " + Combined.DATE_LAST_VISITED + ", " + |
|
700 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.FAVICON_ID) + " AS " + Combined.FAVICON_ID + |
|
701 " FROM " + TABLE_BOOKMARKS + |
|
702 " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + |
|
703 // Ignore pinned bookmarks. |
|
704 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " <> " + Bookmarks.FIXED_PINNED_LIST_ID + " AND " + |
|
705 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + |
|
706 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + |
|
707 " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + |
|
708 " UNION ALL" + |
|
709 // History with and without bookmark. |
|
710 " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + |
|
711 // Give pinned bookmarks a NULL ID so that they're not treated as bookmarks. We can't |
|
712 // completely ignore them here because they're joined with history entries we care about. |
|
713 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + |
|
714 Bookmarks.FIXED_PINNED_LIST_ID + " THEN NULL ELSE " + |
|
715 qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " END " + |
|
716 "ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + |
|
717 qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + |
|
718 // Prioritize bookmark titles over history titles, since the user may have |
|
719 // customized the title for a bookmark. |
|
720 "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + |
|
721 qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + |
|
722 // Only use DISPLAY_READER if the matching bookmark entry inside reading |
|
723 // list folder is not marked as deleted. |
|
724 "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " + |
|
725 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID + |
|
726 " THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " + |
|
727 Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + |
|
728 qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + |
|
729 qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + |
|
730 qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + ", " + |
|
731 qualifyColumn(TABLE_HISTORY, History.FAVICON_ID) + " AS " + Combined.FAVICON_ID + |
|
732 " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + |
|
733 " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + |
|
734 " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + |
|
735 qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + |
|
736 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + |
|
737 qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + |
|
738 ")"); |
|
739 |
|
740 debug("Creating " + VIEW_COMBINED_WITH_FAVICONS + " view"); |
|
741 |
|
742 db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED_WITH_FAVICONS + " AS" + |
|
743 " SELECT " + qualifyColumn(VIEW_COMBINED, "*") + ", " + |
|
744 qualifyColumn(TABLE_FAVICONS, Favicons.URL) + " AS " + Combined.FAVICON_URL + ", " + |
|
745 qualifyColumn(TABLE_FAVICONS, Favicons.DATA) + " AS " + Combined.FAVICON + |
|
746 " FROM " + VIEW_COMBINED + " LEFT OUTER JOIN " + TABLE_FAVICONS + |
|
747 " ON " + Combined.FAVICON_ID + " = " + qualifyColumn(TABLE_FAVICONS, Favicons._ID)); |
|
748 } |
|
749 |
|
750 @Override |
|
751 public void onCreate(SQLiteDatabase db) { |
|
752 debug("Creating browser.db: " + db.getPath()); |
|
753 |
|
754 createBookmarksTableOn13(db); |
|
755 createHistoryTableOn13(db); |
|
756 createFaviconsTable(db); |
|
757 createThumbnailsTable(db); |
|
758 |
|
759 createBookmarksWithFaviconsView(db); |
|
760 createHistoryWithFaviconsView(db); |
|
761 createCombinedViewOn16(db); |
|
762 |
|
763 createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID, |
|
764 R.string.bookmarks_folder_places, 0); |
|
765 |
|
766 createOrUpdateAllSpecialFolders(db); |
|
767 |
|
768 // Create distribution bookmarks before our own default bookmarks |
|
769 int pos = createDistributionBookmarks(db); |
|
770 createDefaultBookmarks(db, pos); |
|
771 |
|
772 createReadingListTable(db); |
|
773 } |
|
774 |
|
775 private String getLocalizedProperty(JSONObject bookmark, String property, Locale locale) throws JSONException { |
|
776 // Try the full locale |
|
777 String fullLocale = property + "." + locale.toString(); |
|
778 if (bookmark.has(fullLocale)) { |
|
779 return bookmark.getString(fullLocale); |
|
780 } |
|
781 // Try without a variant |
|
782 if (!TextUtils.isEmpty(locale.getVariant())) { |
|
783 String noVariant = fullLocale.substring(0, fullLocale.lastIndexOf("_")); |
|
784 if (bookmark.has(noVariant)) { |
|
785 return bookmark.getString(noVariant); |
|
786 } |
|
787 } |
|
788 // Try just the language |
|
789 String lang = property + "." + locale.getLanguage(); |
|
790 if (bookmark.has(lang)) { |
|
791 return bookmark.getString(lang); |
|
792 } |
|
793 // Default to the non-localized property name |
|
794 return bookmark.getString(property); |
|
795 } |
|
796 |
|
797 // Returns the number of bookmarks inserted in the db |
|
798 private int createDistributionBookmarks(SQLiteDatabase db) { |
|
799 JSONArray bookmarks = Distribution.getBookmarks(mContext); |
|
800 if (bookmarks == null) { |
|
801 return 0; |
|
802 } |
|
803 |
|
804 Locale locale = Locale.getDefault(); |
|
805 int pos = 0; |
|
806 Integer mobileFolderId = getMobileFolderId(db); |
|
807 if (mobileFolderId == null) { |
|
808 Log.e(LOGTAG, "Error creating distribution bookmarks: mobileFolderId is null"); |
|
809 return 0; |
|
810 } |
|
811 |
|
812 for (int i = 0; i < bookmarks.length(); i++) { |
|
813 try { |
|
814 final JSONObject bookmark = bookmarks.getJSONObject(i); |
|
815 |
|
816 String title = getLocalizedProperty(bookmark, "title", locale); |
|
817 final String url = getLocalizedProperty(bookmark, "url", locale); |
|
818 createBookmark(db, title, url, pos, mobileFolderId); |
|
819 |
|
820 if (bookmark.has("pinned")) { |
|
821 try { |
|
822 // Create a fake bookmark in the hidden pinned folder to pin bookmark |
|
823 // to about:home top sites. Pass pos as the pinned position to pin |
|
824 // sites in the order that bookmarks are specified in bookmarks.json. |
|
825 if (bookmark.getBoolean("pinned")) { |
|
826 createBookmark(db, title, url, pos, Bookmarks.FIXED_PINNED_LIST_ID); |
|
827 } |
|
828 } catch (JSONException e) { |
|
829 Log.e(LOGTAG, "Error pinning bookmark to top sites", e); |
|
830 } |
|
831 } |
|
832 |
|
833 pos++; |
|
834 |
|
835 // return early if there is no icon for this bookmark |
|
836 if (!bookmark.has("icon")) { |
|
837 continue; |
|
838 } |
|
839 |
|
840 // create icons in a separate thread to avoid blocking about:home on startup |
|
841 ThreadUtils.postToBackgroundThread(new Runnable() { |
|
842 @Override |
|
843 public void run() { |
|
844 SQLiteDatabase db = getWritableDatabase(); |
|
845 try { |
|
846 String iconData = bookmark.getString("icon"); |
|
847 Bitmap icon = BitmapUtils.getBitmapFromDataURI(iconData); |
|
848 if (icon != null) { |
|
849 createFavicon(db, url, icon); |
|
850 } |
|
851 } catch (JSONException e) { |
|
852 Log.e(LOGTAG, "Error creating distribution bookmark icon", e); |
|
853 } |
|
854 } |
|
855 }); |
|
856 } catch (JSONException e) { |
|
857 Log.e(LOGTAG, "Error creating distribution bookmark", e); |
|
858 } |
|
859 } |
|
860 return pos; |
|
861 } |
|
862 |
|
863 private void createReadingListTable(SQLiteDatabase db) { |
|
864 debug("Creating " + TABLE_READING_LIST + " table"); |
|
865 |
|
866 db.execSQL("CREATE TABLE " + TABLE_READING_LIST + "(" + |
|
867 ReadingListItems._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + |
|
868 ReadingListItems.URL + " TEXT NOT NULL, " + |
|
869 ReadingListItems.TITLE + " TEXT, " + |
|
870 ReadingListItems.EXCERPT + " TEXT, " + |
|
871 ReadingListItems.READ + " TINYINT DEFAULT 0, " + |
|
872 ReadingListItems.IS_DELETED + " TINYINT DEFAULT 0, " + |
|
873 ReadingListItems.GUID + " TEXT UNIQUE NOT NULL, " + |
|
874 ReadingListItems.DATE_MODIFIED + " INTEGER NOT NULL, " + |
|
875 ReadingListItems.DATE_CREATED + " INTEGER NOT NULL, " + |
|
876 ReadingListItems.LENGTH + " INTEGER DEFAULT 0 ); "); |
|
877 |
|
878 db.execSQL("CREATE INDEX reading_list_url ON " + TABLE_READING_LIST + "(" |
|
879 + ReadingListItems.URL + ")"); |
|
880 db.execSQL("CREATE UNIQUE INDEX reading_list_guid ON " + TABLE_READING_LIST + "(" |
|
881 + ReadingListItems.GUID + ")"); |
|
882 } |
|
883 |
|
884 // Inserts default bookmarks, starting at a specified position |
|
885 private void createDefaultBookmarks(SQLiteDatabase db, int pos) { |
|
886 Class<?> stringsClass = R.string.class; |
|
887 Field[] fields = stringsClass.getFields(); |
|
888 Pattern p = Pattern.compile("^bookmarkdefaults_title_"); |
|
889 |
|
890 Integer mobileFolderId = getMobileFolderId(db); |
|
891 if (mobileFolderId == null) { |
|
892 Log.e(LOGTAG, "Error creating default bookmarks: mobileFolderId is null"); |
|
893 return; |
|
894 } |
|
895 |
|
896 for (int i = 0; i < fields.length; i++) { |
|
897 final String name = fields[i].getName(); |
|
898 Matcher m = p.matcher(name); |
|
899 if (!m.find()) { |
|
900 continue; |
|
901 } |
|
902 try { |
|
903 int titleid = fields[i].getInt(null); |
|
904 String title = mContext.getString(titleid); |
|
905 |
|
906 Field urlField = stringsClass.getField(name.replace("_title_", "_url_")); |
|
907 int urlId = urlField.getInt(null); |
|
908 final String url = mContext.getString(urlId); |
|
909 createBookmark(db, title, url, pos, mobileFolderId); |
|
910 |
|
911 // create icons in a separate thread to avoid blocking about:home on startup |
|
912 ThreadUtils.postToBackgroundThread(new Runnable() { |
|
913 @Override |
|
914 public void run() { |
|
915 SQLiteDatabase db = getWritableDatabase(); |
|
916 Bitmap icon = getDefaultFaviconFromPath(name); |
|
917 if (icon == null) { |
|
918 icon = getDefaultFaviconFromDrawable(name); |
|
919 } |
|
920 if (icon != null) { |
|
921 createFavicon(db, url, icon); |
|
922 } |
|
923 } |
|
924 }); |
|
925 pos++; |
|
926 } catch (java.lang.IllegalAccessException ex) { |
|
927 Log.e(LOGTAG, "Can't create bookmark " + name, ex); |
|
928 } catch (java.lang.NoSuchFieldException ex) { |
|
929 Log.e(LOGTAG, "Can't create bookmark " + name, ex); |
|
930 } |
|
931 } |
|
932 } |
|
933 |
|
934 private void createBookmark(SQLiteDatabase db, String title, String url, int pos, int parent) { |
|
935 ContentValues bookmarkValues = new ContentValues(); |
|
936 bookmarkValues.put(Bookmarks.PARENT, parent); |
|
937 |
|
938 long now = System.currentTimeMillis(); |
|
939 bookmarkValues.put(Bookmarks.DATE_CREATED, now); |
|
940 bookmarkValues.put(Bookmarks.DATE_MODIFIED, now); |
|
941 |
|
942 bookmarkValues.put(Bookmarks.TITLE, title); |
|
943 bookmarkValues.put(Bookmarks.URL, url); |
|
944 bookmarkValues.put(Bookmarks.GUID, Utils.generateGuid()); |
|
945 bookmarkValues.put(Bookmarks.POSITION, pos); |
|
946 db.insertOrThrow(TABLE_BOOKMARKS, Bookmarks.TITLE, bookmarkValues); |
|
947 } |
|
948 |
|
949 private void createFavicon(SQLiteDatabase db, String url, Bitmap icon) { |
|
950 ByteArrayOutputStream stream = new ByteArrayOutputStream(); |
|
951 |
|
952 ContentValues iconValues = new ContentValues(); |
|
953 iconValues.put(Favicons.PAGE_URL, url); |
|
954 |
|
955 byte[] data = null; |
|
956 if (icon.compress(Bitmap.CompressFormat.PNG, 100, stream)) { |
|
957 data = stream.toByteArray(); |
|
958 } else { |
|
959 Log.w(LOGTAG, "Favicon compression failed."); |
|
960 } |
|
961 iconValues.put(Favicons.DATA, data); |
|
962 |
|
963 insertFavicon(db, iconValues); |
|
964 } |
|
965 |
|
966 private Bitmap getDefaultFaviconFromPath(String name) { |
|
967 Class<?> stringClass = R.string.class; |
|
968 try { |
|
969 // Look for a drawable with the id R.drawable.bookmarkdefaults_favicon_* |
|
970 Field faviconField = stringClass.getField(name.replace("_title_", "_favicon_")); |
|
971 if (faviconField == null) { |
|
972 return null; |
|
973 } |
|
974 int faviconId = faviconField.getInt(null); |
|
975 String path = mContext.getString(faviconId); |
|
976 |
|
977 String apkPath = mContext.getPackageResourcePath(); |
|
978 File apkFile = new File(apkPath); |
|
979 String bitmapPath = "jar:jar:" + apkFile.toURI() + "!/" + AppConstants.OMNIJAR_NAME + "!/" + path; |
|
980 return GeckoJarReader.getBitmap(mContext.getResources(), bitmapPath); |
|
981 } catch (java.lang.IllegalAccessException ex) { |
|
982 Log.e(LOGTAG, "[Path] Can't create favicon " + name, ex); |
|
983 } catch (java.lang.NoSuchFieldException ex) { |
|
984 // If the field does not exist, that means we intend to load via a drawable |
|
985 } |
|
986 return null; |
|
987 } |
|
988 |
|
989 private Bitmap getDefaultFaviconFromDrawable(String name) { |
|
990 Class<?> drawablesClass = R.drawable.class; |
|
991 try { |
|
992 // Look for a drawable with the id R.drawable.bookmarkdefaults_favicon_* |
|
993 Field faviconField = drawablesClass.getField(name.replace("_title_", "_favicon_")); |
|
994 if (faviconField == null) { |
|
995 return null; |
|
996 } |
|
997 int faviconId = faviconField.getInt(null); |
|
998 return BitmapUtils.decodeResource(mContext, faviconId); |
|
999 } catch (java.lang.IllegalAccessException ex) { |
|
1000 Log.e(LOGTAG, "[Drawable] Can't create favicon " + name, ex); |
|
1001 } catch (java.lang.NoSuchFieldException ex) { |
|
1002 // If the field does not exist, that means we intend to load via a file path |
|
1003 } |
|
1004 return null; |
|
1005 } |
|
1006 |
|
1007 private void createOrUpdateAllSpecialFolders(SQLiteDatabase db) { |
|
1008 createOrUpdateSpecialFolder(db, Bookmarks.MOBILE_FOLDER_GUID, |
|
1009 R.string.bookmarks_folder_mobile, 0); |
|
1010 createOrUpdateSpecialFolder(db, Bookmarks.TOOLBAR_FOLDER_GUID, |
|
1011 R.string.bookmarks_folder_toolbar, 1); |
|
1012 createOrUpdateSpecialFolder(db, Bookmarks.MENU_FOLDER_GUID, |
|
1013 R.string.bookmarks_folder_menu, 2); |
|
1014 createOrUpdateSpecialFolder(db, Bookmarks.TAGS_FOLDER_GUID, |
|
1015 R.string.bookmarks_folder_tags, 3); |
|
1016 createOrUpdateSpecialFolder(db, Bookmarks.UNFILED_FOLDER_GUID, |
|
1017 R.string.bookmarks_folder_unfiled, 4); |
|
1018 createOrUpdateSpecialFolder(db, Bookmarks.READING_LIST_FOLDER_GUID, |
|
1019 R.string.bookmarks_folder_reading_list, 5); |
|
1020 createOrUpdateSpecialFolder(db, Bookmarks.PINNED_FOLDER_GUID, |
|
1021 R.string.bookmarks_folder_pinned, 6); |
|
1022 } |
|
1023 |
|
1024 private void createOrUpdateSpecialFolder(SQLiteDatabase db, |
|
1025 String guid, int titleId, int position) { |
|
1026 ContentValues values = new ContentValues(); |
|
1027 values.put(Bookmarks.GUID, guid); |
|
1028 values.put(Bookmarks.TYPE, Bookmarks.TYPE_FOLDER); |
|
1029 values.put(Bookmarks.POSITION, position); |
|
1030 |
|
1031 if (guid.equals(Bookmarks.PLACES_FOLDER_GUID)) |
|
1032 values.put(Bookmarks._ID, Bookmarks.FIXED_ROOT_ID); |
|
1033 else if (guid.equals(Bookmarks.READING_LIST_FOLDER_GUID)) |
|
1034 values.put(Bookmarks._ID, Bookmarks.FIXED_READING_LIST_ID); |
|
1035 else if (guid.equals(Bookmarks.PINNED_FOLDER_GUID)) |
|
1036 values.put(Bookmarks._ID, Bookmarks.FIXED_PINNED_LIST_ID); |
|
1037 |
|
1038 // Set the parent to 0, which sync assumes is the root |
|
1039 values.put(Bookmarks.PARENT, Bookmarks.FIXED_ROOT_ID); |
|
1040 |
|
1041 String title = mContext.getResources().getString(titleId); |
|
1042 values.put(Bookmarks.TITLE, title); |
|
1043 |
|
1044 long now = System.currentTimeMillis(); |
|
1045 values.put(Bookmarks.DATE_CREATED, now); |
|
1046 values.put(Bookmarks.DATE_MODIFIED, now); |
|
1047 |
|
1048 int updated = db.update(TABLE_BOOKMARKS, values, |
|
1049 Bookmarks.GUID + " = ?", |
|
1050 new String[] { guid }); |
|
1051 |
|
1052 if (updated == 0) { |
|
1053 db.insert(TABLE_BOOKMARKS, Bookmarks.GUID, values); |
|
1054 debug("Inserted special folder: " + guid); |
|
1055 } else { |
|
1056 debug("Updated special folder: " + guid); |
|
1057 } |
|
1058 } |
|
1059 |
|
1060 private boolean isSpecialFolder(ContentValues values) { |
|
1061 String guid = values.getAsString(Bookmarks.GUID); |
|
1062 if (guid == null) |
|
1063 return false; |
|
1064 |
|
1065 return guid.equals(Bookmarks.MOBILE_FOLDER_GUID) || |
|
1066 guid.equals(Bookmarks.MENU_FOLDER_GUID) || |
|
1067 guid.equals(Bookmarks.TOOLBAR_FOLDER_GUID) || |
|
1068 guid.equals(Bookmarks.UNFILED_FOLDER_GUID) || |
|
1069 guid.equals(Bookmarks.TAGS_FOLDER_GUID); |
|
1070 } |
|
1071 |
|
1072 private void migrateBookmarkFolder(SQLiteDatabase db, int folderId, |
|
1073 BookmarkMigrator migrator) { |
|
1074 Cursor c = null; |
|
1075 |
|
1076 debug("Migrating bookmark folder with id = " + folderId); |
|
1077 |
|
1078 String selection = Bookmarks.PARENT + " = " + folderId; |
|
1079 String[] selectionArgs = null; |
|
1080 |
|
1081 boolean isRootFolder = (folderId == Bookmarks.FIXED_ROOT_ID); |
|
1082 |
|
1083 // If we're loading the root folder, we have to account for |
|
1084 // any previously created special folder that was created without |
|
1085 // setting a parent id (e.g. mobile folder) and making sure we're |
|
1086 // not adding any infinite recursion as root's parent is root itself. |
|
1087 if (isRootFolder) { |
|
1088 selection = Bookmarks.GUID + " != ?" + " AND (" + |
|
1089 selection + " OR " + Bookmarks.PARENT + " = NULL)"; |
|
1090 selectionArgs = new String[] { Bookmarks.PLACES_FOLDER_GUID }; |
|
1091 } |
|
1092 |
|
1093 List<Integer> subFolders = new ArrayList<Integer>(); |
|
1094 List<ContentValues> invalidSpecialEntries = new ArrayList<ContentValues>(); |
|
1095 |
|
1096 try { |
|
1097 c = db.query(TABLE_BOOKMARKS_TMP, |
|
1098 null, |
|
1099 selection, |
|
1100 selectionArgs, |
|
1101 null, null, null); |
|
1102 |
|
1103 // The key point here is that bookmarks should be added in |
|
1104 // parent order to avoid any problems with the foreign key |
|
1105 // in Bookmarks.PARENT. |
|
1106 while (c.moveToNext()) { |
|
1107 ContentValues values = new ContentValues(); |
|
1108 |
|
1109 // We're using a null projection in the query which |
|
1110 // means we're getting all columns from the table. |
|
1111 // It's safe to simply transform the row into the |
|
1112 // values to be inserted on the new table. |
|
1113 DatabaseUtils.cursorRowToContentValues(c, values); |
|
1114 |
|
1115 boolean isSpecialFolder = isSpecialFolder(values); |
|
1116 |
|
1117 // The mobile folder used to be created with PARENT = NULL. |
|
1118 // We want fix that here. |
|
1119 if (values.getAsLong(Bookmarks.PARENT) == null && isSpecialFolder) |
|
1120 values.put(Bookmarks.PARENT, Bookmarks.FIXED_ROOT_ID); |
|
1121 |
|
1122 if (isRootFolder && !isSpecialFolder) { |
|
1123 invalidSpecialEntries.add(values); |
|
1124 continue; |
|
1125 } |
|
1126 |
|
1127 if (migrator != null) |
|
1128 migrator.updateForNewTable(values); |
|
1129 |
|
1130 debug("Migrating bookmark: " + values.getAsString(Bookmarks.TITLE)); |
|
1131 db.insert(TABLE_BOOKMARKS, Bookmarks.URL, values); |
|
1132 |
|
1133 Integer type = values.getAsInteger(Bookmarks.TYPE); |
|
1134 if (type != null && type == Bookmarks.TYPE_FOLDER) |
|
1135 subFolders.add(values.getAsInteger(Bookmarks._ID)); |
|
1136 } |
|
1137 } finally { |
|
1138 if (c != null) |
|
1139 c.close(); |
|
1140 } |
|
1141 |
|
1142 // At this point is safe to assume that the mobile folder is |
|
1143 // in the new table given that we've always created it on |
|
1144 // database creation time. |
|
1145 final int nInvalidSpecialEntries = invalidSpecialEntries.size(); |
|
1146 if (nInvalidSpecialEntries > 0) { |
|
1147 Integer mobileFolderId = getMobileFolderId(db); |
|
1148 if (mobileFolderId == null) { |
|
1149 Log.e(LOGTAG, "Error migrating invalid special folder entries: mobile folder id is null"); |
|
1150 return; |
|
1151 } |
|
1152 |
|
1153 debug("Found " + nInvalidSpecialEntries + " invalid special folder entries"); |
|
1154 for (int i = 0; i < nInvalidSpecialEntries; i++) { |
|
1155 ContentValues values = invalidSpecialEntries.get(i); |
|
1156 values.put(Bookmarks.PARENT, mobileFolderId); |
|
1157 |
|
1158 db.insert(TABLE_BOOKMARKS, Bookmarks.URL, values); |
|
1159 } |
|
1160 } |
|
1161 |
|
1162 final int nSubFolders = subFolders.size(); |
|
1163 for (int i = 0; i < nSubFolders; i++) { |
|
1164 int subFolderId = subFolders.get(i); |
|
1165 migrateBookmarkFolder(db, subFolderId, migrator); |
|
1166 } |
|
1167 } |
|
1168 |
|
1169 private void migrateBookmarksTable(SQLiteDatabase db) { |
|
1170 migrateBookmarksTable(db, null); |
|
1171 } |
|
1172 |
|
1173 private void migrateBookmarksTable(SQLiteDatabase db, BookmarkMigrator migrator) { |
|
1174 debug("Renaming bookmarks table to " + TABLE_BOOKMARKS_TMP); |
|
1175 db.execSQL("ALTER TABLE " + TABLE_BOOKMARKS + |
|
1176 " RENAME TO " + TABLE_BOOKMARKS_TMP); |
|
1177 |
|
1178 debug("Dropping views and indexes related to " + TABLE_BOOKMARKS); |
|
1179 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); |
|
1180 |
|
1181 db.execSQL("DROP INDEX IF EXISTS bookmarks_url_index"); |
|
1182 db.execSQL("DROP INDEX IF EXISTS bookmarks_type_deleted_index"); |
|
1183 db.execSQL("DROP INDEX IF EXISTS bookmarks_guid_index"); |
|
1184 db.execSQL("DROP INDEX IF EXISTS bookmarks_modified_index"); |
|
1185 |
|
1186 createBookmarksTable(db); |
|
1187 createBookmarksWithImagesView(db); |
|
1188 |
|
1189 createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID, |
|
1190 R.string.bookmarks_folder_places, 0); |
|
1191 |
|
1192 migrateBookmarkFolder(db, Bookmarks.FIXED_ROOT_ID, migrator); |
|
1193 |
|
1194 // Ensure all special folders exist and have the |
|
1195 // right folder hierarchy. |
|
1196 createOrUpdateAllSpecialFolders(db); |
|
1197 |
|
1198 debug("Dropping bookmarks temporary table"); |
|
1199 db.execSQL("DROP TABLE IF EXISTS " + TABLE_BOOKMARKS_TMP); |
|
1200 } |
|
1201 |
|
1202 |
|
1203 private void migrateHistoryTable(SQLiteDatabase db) { |
|
1204 debug("Renaming history table to " + TABLE_HISTORY_TMP); |
|
1205 db.execSQL("ALTER TABLE " + TABLE_HISTORY + |
|
1206 " RENAME TO " + TABLE_HISTORY_TMP); |
|
1207 |
|
1208 debug("Dropping views and indexes related to " + TABLE_HISTORY); |
|
1209 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); |
|
1210 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1211 |
|
1212 db.execSQL("DROP INDEX IF EXISTS history_url_index"); |
|
1213 db.execSQL("DROP INDEX IF EXISTS history_guid_index"); |
|
1214 db.execSQL("DROP INDEX IF EXISTS history_modified_index"); |
|
1215 db.execSQL("DROP INDEX IF EXISTS history_visited_index"); |
|
1216 |
|
1217 createHistoryTable(db); |
|
1218 createHistoryWithImagesView(db); |
|
1219 createCombinedWithImagesView(db); |
|
1220 |
|
1221 db.execSQL("INSERT INTO " + TABLE_HISTORY + " SELECT * FROM " + TABLE_HISTORY_TMP); |
|
1222 |
|
1223 debug("Dropping history temporary table"); |
|
1224 db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY_TMP); |
|
1225 } |
|
1226 |
|
1227 private void migrateImagesTable(SQLiteDatabase db) { |
|
1228 debug("Renaming images table to " + TABLE_IMAGES_TMP); |
|
1229 db.execSQL("ALTER TABLE " + Obsolete.TABLE_IMAGES + |
|
1230 " RENAME TO " + TABLE_IMAGES_TMP); |
|
1231 |
|
1232 debug("Dropping views and indexes related to " + Obsolete.TABLE_IMAGES); |
|
1233 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); |
|
1234 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1235 |
|
1236 db.execSQL("DROP INDEX IF EXISTS images_url_index"); |
|
1237 db.execSQL("DROP INDEX IF EXISTS images_guid_index"); |
|
1238 db.execSQL("DROP INDEX IF EXISTS images_modified_index"); |
|
1239 |
|
1240 createImagesTable(db); |
|
1241 createHistoryWithImagesView(db); |
|
1242 createCombinedWithImagesView(db); |
|
1243 |
|
1244 db.execSQL("INSERT INTO " + Obsolete.TABLE_IMAGES + " SELECT * FROM " + TABLE_IMAGES_TMP); |
|
1245 |
|
1246 debug("Dropping images temporary table"); |
|
1247 db.execSQL("DROP TABLE IF EXISTS " + TABLE_IMAGES_TMP); |
|
1248 } |
|
1249 |
|
1250 private void upgradeDatabaseFrom1to2(SQLiteDatabase db) { |
|
1251 migrateBookmarksTable(db); |
|
1252 } |
|
1253 |
|
1254 private void upgradeDatabaseFrom2to3(SQLiteDatabase db) { |
|
1255 debug("Dropping view: " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); |
|
1256 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); |
|
1257 |
|
1258 createBookmarksWithImagesView(db); |
|
1259 |
|
1260 debug("Dropping view: " + Obsolete.VIEW_HISTORY_WITH_IMAGES); |
|
1261 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); |
|
1262 |
|
1263 createHistoryWithImagesView(db); |
|
1264 } |
|
1265 |
|
1266 private void upgradeDatabaseFrom3to4(SQLiteDatabase db) { |
|
1267 migrateBookmarksTable(db, new BookmarkMigrator3to4()); |
|
1268 } |
|
1269 |
|
1270 private void upgradeDatabaseFrom4to5(SQLiteDatabase db) { |
|
1271 createCombinedWithImagesView(db); |
|
1272 } |
|
1273 |
|
1274 private void upgradeDatabaseFrom5to6(SQLiteDatabase db) { |
|
1275 debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1276 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1277 |
|
1278 createCombinedWithImagesView(db); |
|
1279 } |
|
1280 |
|
1281 private void upgradeDatabaseFrom6to7(SQLiteDatabase db) { |
|
1282 debug("Removing history visits with NULL GUIDs"); |
|
1283 db.execSQL("DELETE FROM " + TABLE_HISTORY + " WHERE " + History.GUID + " IS NULL"); |
|
1284 |
|
1285 debug("Update images with NULL GUIDs"); |
|
1286 String[] columns = new String[] { Obsolete.Images._ID }; |
|
1287 Cursor cursor = null; |
|
1288 try { |
|
1289 cursor = db.query(Obsolete.TABLE_IMAGES, columns, Obsolete.Images.GUID + " IS NULL", null, null ,null, null, null); |
|
1290 ContentValues values = new ContentValues(); |
|
1291 if (cursor.moveToFirst()) { |
|
1292 do { |
|
1293 values.put(Obsolete.Images.GUID, Utils.generateGuid()); |
|
1294 db.update(Obsolete.TABLE_IMAGES, values, Obsolete.Images._ID + " = ?", new String[] { |
|
1295 cursor.getString(cursor.getColumnIndexOrThrow(Obsolete.Images._ID)) |
|
1296 }); |
|
1297 } while (cursor.moveToNext()); |
|
1298 } |
|
1299 } finally { |
|
1300 if (cursor != null) |
|
1301 cursor.close(); |
|
1302 } |
|
1303 |
|
1304 migrateBookmarksTable(db); |
|
1305 migrateHistoryTable(db); |
|
1306 migrateImagesTable(db); |
|
1307 } |
|
1308 |
|
1309 private void upgradeDatabaseFrom7to8(SQLiteDatabase db) { |
|
1310 debug("Combining history entries with the same URL"); |
|
1311 |
|
1312 final String TABLE_DUPES = "duped_urls"; |
|
1313 final String TOTAL = "total"; |
|
1314 final String LATEST = "latest"; |
|
1315 final String WINNER = "winner"; |
|
1316 |
|
1317 db.execSQL("CREATE TEMP TABLE " + TABLE_DUPES + " AS" + |
|
1318 " SELECT " + History.URL + ", " + |
|
1319 "SUM(" + History.VISITS + ") AS " + TOTAL + ", " + |
|
1320 "MAX(" + History.DATE_MODIFIED + ") AS " + LATEST + ", " + |
|
1321 "MAX(" + History._ID + ") AS " + WINNER + |
|
1322 " FROM " + TABLE_HISTORY + |
|
1323 " GROUP BY " + History.URL + |
|
1324 " HAVING count(" + History.URL + ") > 1"); |
|
1325 |
|
1326 db.execSQL("CREATE UNIQUE INDEX " + TABLE_DUPES + "_url_index ON " + |
|
1327 TABLE_DUPES + " (" + History.URL + ")"); |
|
1328 |
|
1329 final String fromClause = " FROM " + TABLE_DUPES + " WHERE " + |
|
1330 qualifyColumn(TABLE_DUPES, History.URL) + " = " + |
|
1331 qualifyColumn(TABLE_HISTORY, History.URL); |
|
1332 |
|
1333 db.execSQL("UPDATE " + TABLE_HISTORY + |
|
1334 " SET " + History.VISITS + " = (SELECT " + TOTAL + fromClause + "), " + |
|
1335 History.DATE_MODIFIED + " = (SELECT " + LATEST + fromClause + "), " + |
|
1336 History.IS_DELETED + " = " + |
|
1337 "(" + History._ID + " <> (SELECT " + WINNER + fromClause + "))" + |
|
1338 " WHERE " + History.URL + " IN (SELECT " + History.URL + " FROM " + TABLE_DUPES + ")"); |
|
1339 |
|
1340 db.execSQL("DROP TABLE " + TABLE_DUPES); |
|
1341 } |
|
1342 |
|
1343 private void upgradeDatabaseFrom8to9(SQLiteDatabase db) { |
|
1344 createOrUpdateSpecialFolder(db, Bookmarks.READING_LIST_FOLDER_GUID, |
|
1345 R.string.bookmarks_folder_reading_list, 5); |
|
1346 |
|
1347 debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1348 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1349 |
|
1350 createCombinedWithImagesViewOn9(db); |
|
1351 } |
|
1352 |
|
1353 private void upgradeDatabaseFrom9to10(SQLiteDatabase db) { |
|
1354 debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1355 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1356 |
|
1357 createCombinedWithImagesViewOn10(db); |
|
1358 } |
|
1359 |
|
1360 private void upgradeDatabaseFrom10to11(SQLiteDatabase db) { |
|
1361 debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1362 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1363 |
|
1364 db.execSQL("CREATE INDEX bookmarks_type_deleted_index ON " + TABLE_BOOKMARKS + "(" |
|
1365 + Bookmarks.TYPE + ", " + Bookmarks.IS_DELETED + ")"); |
|
1366 |
|
1367 createCombinedWithImagesViewOn11(db); |
|
1368 } |
|
1369 |
|
1370 private void upgradeDatabaseFrom11to12(SQLiteDatabase db) { |
|
1371 debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1372 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1373 |
|
1374 createCombinedViewOn12(db); |
|
1375 } |
|
1376 |
|
1377 private void upgradeDatabaseFrom12to13(SQLiteDatabase db) { |
|
1378 // Update images table with favicon URLs |
|
1379 SQLiteDatabase faviconsDb = null; |
|
1380 Cursor c = null; |
|
1381 try { |
|
1382 final String FAVICON_TABLE = "favicon_urls"; |
|
1383 final String FAVICON_URL = "favicon_url"; |
|
1384 final String FAVICON_PAGE = "page_url"; |
|
1385 |
|
1386 String dbPath = mContext.getDatabasePath(Obsolete.FAVICON_DB).getPath(); |
|
1387 faviconsDb = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY); |
|
1388 String[] columns = new String[] { FAVICON_URL, FAVICON_PAGE }; |
|
1389 c = faviconsDb.query(FAVICON_TABLE, columns, null, null, null, null, null, null); |
|
1390 int faviconIndex = c.getColumnIndexOrThrow(FAVICON_URL); |
|
1391 int pageIndex = c.getColumnIndexOrThrow(FAVICON_PAGE); |
|
1392 while (c.moveToNext()) { |
|
1393 ContentValues values = new ContentValues(1); |
|
1394 String faviconUrl = c.getString(faviconIndex); |
|
1395 String pageUrl = c.getString(pageIndex); |
|
1396 values.put(FAVICON_URL, faviconUrl); |
|
1397 db.update(Obsolete.TABLE_IMAGES, values, Obsolete.Images.URL + " = ?", new String[] { pageUrl }); |
|
1398 } |
|
1399 } catch (SQLException e) { |
|
1400 // If we can't read from the database for some reason, we won't |
|
1401 // be able to import the favicon URLs. This isn't a fatal |
|
1402 // error, so continue the upgrade. |
|
1403 Log.e(LOGTAG, "Exception importing from " + Obsolete.FAVICON_DB, e); |
|
1404 } finally { |
|
1405 if (c != null) |
|
1406 c.close(); |
|
1407 if (faviconsDb != null) |
|
1408 faviconsDb.close(); |
|
1409 } |
|
1410 |
|
1411 createFaviconsTable(db); |
|
1412 |
|
1413 // Import favicons into the favicons table |
|
1414 db.execSQL("ALTER TABLE " + TABLE_HISTORY |
|
1415 + " ADD COLUMN " + History.FAVICON_ID + " INTEGER"); |
|
1416 db.execSQL("ALTER TABLE " + TABLE_BOOKMARKS |
|
1417 + " ADD COLUMN " + Bookmarks.FAVICON_ID + " INTEGER"); |
|
1418 |
|
1419 try { |
|
1420 c = db.query(Obsolete.TABLE_IMAGES, |
|
1421 new String[] { |
|
1422 Obsolete.Images.URL, |
|
1423 Obsolete.Images.FAVICON_URL, |
|
1424 Obsolete.Images.FAVICON, |
|
1425 Obsolete.Images.DATE_MODIFIED, |
|
1426 Obsolete.Images.DATE_CREATED |
|
1427 }, |
|
1428 Obsolete.Images.FAVICON + " IS NOT NULL", |
|
1429 null, null, null, null); |
|
1430 |
|
1431 while (c.moveToNext()) { |
|
1432 long faviconId = -1; |
|
1433 int faviconUrlIndex = c.getColumnIndexOrThrow(Obsolete.Images.FAVICON_URL); |
|
1434 String faviconUrl = null; |
|
1435 if (!c.isNull(faviconUrlIndex)) { |
|
1436 faviconUrl = c.getString(faviconUrlIndex); |
|
1437 Cursor c2 = null; |
|
1438 try { |
|
1439 c2 = db.query(TABLE_FAVICONS, |
|
1440 new String[] { Favicons._ID }, |
|
1441 Favicons.URL + " = ?", |
|
1442 new String[] { faviconUrl }, |
|
1443 null, null, null); |
|
1444 if (c2.moveToFirst()) { |
|
1445 faviconId = c2.getLong(c2.getColumnIndexOrThrow(Favicons._ID)); |
|
1446 } |
|
1447 } finally { |
|
1448 if (c2 != null) |
|
1449 c2.close(); |
|
1450 } |
|
1451 } |
|
1452 |
|
1453 if (faviconId == -1) { |
|
1454 ContentValues values = new ContentValues(4); |
|
1455 values.put(Favicons.URL, faviconUrl); |
|
1456 values.put(Favicons.DATA, c.getBlob(c.getColumnIndexOrThrow(Obsolete.Images.FAVICON))); |
|
1457 values.put(Favicons.DATE_MODIFIED, c.getLong(c.getColumnIndexOrThrow(Obsolete.Images.DATE_MODIFIED))); |
|
1458 values.put(Favicons.DATE_CREATED, c.getLong(c.getColumnIndexOrThrow(Obsolete.Images.DATE_CREATED))); |
|
1459 faviconId = db.insert(TABLE_FAVICONS, null, values); |
|
1460 } |
|
1461 |
|
1462 ContentValues values = new ContentValues(1); |
|
1463 values.put(FaviconColumns.FAVICON_ID, faviconId); |
|
1464 db.update(TABLE_HISTORY, values, History.URL + " = ?", |
|
1465 new String[] { c.getString(c.getColumnIndexOrThrow(Obsolete.Images.URL)) }); |
|
1466 db.update(TABLE_BOOKMARKS, values, Bookmarks.URL + " = ?", |
|
1467 new String[] { c.getString(c.getColumnIndexOrThrow(Obsolete.Images.URL)) }); |
|
1468 } |
|
1469 } finally { |
|
1470 if (c != null) |
|
1471 c.close(); |
|
1472 } |
|
1473 |
|
1474 createThumbnailsTable(db); |
|
1475 |
|
1476 // Import thumbnails into the thumbnails table |
|
1477 db.execSQL("INSERT INTO " + TABLE_THUMBNAILS + " (" |
|
1478 + Thumbnails.URL + ", " |
|
1479 + Thumbnails.DATA + ") " |
|
1480 + "SELECT " + Obsolete.Images.URL + ", " + Obsolete.Images.THUMBNAIL |
|
1481 + " FROM " + Obsolete.TABLE_IMAGES |
|
1482 + " WHERE " + Obsolete.Images.THUMBNAIL + " IS NOT NULL"); |
|
1483 |
|
1484 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); |
|
1485 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); |
|
1486 db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); |
|
1487 db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED); |
|
1488 |
|
1489 createBookmarksWithFaviconsView(db); |
|
1490 createHistoryWithFaviconsView(db); |
|
1491 createCombinedViewOn13(db); |
|
1492 |
|
1493 db.execSQL("DROP TABLE IF EXISTS " + Obsolete.TABLE_IMAGES); |
|
1494 } |
|
1495 |
|
1496 private void upgradeDatabaseFrom13to14(SQLiteDatabase db) { |
|
1497 createOrUpdateSpecialFolder(db, Bookmarks.PINNED_FOLDER_GUID, |
|
1498 R.string.bookmarks_folder_pinned, 6); |
|
1499 } |
|
1500 |
|
1501 private void upgradeDatabaseFrom14to15(SQLiteDatabase db) { |
|
1502 Cursor c = null; |
|
1503 try { |
|
1504 // Get all the pinned bookmarks |
|
1505 c = db.query(TABLE_BOOKMARKS, |
|
1506 new String[] { Bookmarks._ID, Bookmarks.URL }, |
|
1507 Bookmarks.PARENT + " = ?", |
|
1508 new String[] { Integer.toString(Bookmarks.FIXED_PINNED_LIST_ID) }, |
|
1509 null, null, null); |
|
1510 |
|
1511 while (c.moveToNext()) { |
|
1512 // Check if this URL can be parsed as a URI with a valid scheme. |
|
1513 String url = c.getString(c.getColumnIndexOrThrow(Bookmarks.URL)); |
|
1514 if (Uri.parse(url).getScheme() != null) { |
|
1515 continue; |
|
1516 } |
|
1517 |
|
1518 // If it can't, update the URL to be an encoded "user-entered" value. |
|
1519 ContentValues values = new ContentValues(1); |
|
1520 String newUrl = Uri.fromParts("user-entered", url, null).toString(); |
|
1521 values.put(Bookmarks.URL, newUrl); |
|
1522 db.update(TABLE_BOOKMARKS, values, Bookmarks._ID + " = ?", |
|
1523 new String[] { Integer.toString(c.getInt(c.getColumnIndexOrThrow(Bookmarks._ID))) }); |
|
1524 } |
|
1525 } finally { |
|
1526 if (c != null) { |
|
1527 c.close(); |
|
1528 } |
|
1529 } |
|
1530 } |
|
1531 |
|
1532 private void upgradeDatabaseFrom15to16(SQLiteDatabase db) { |
|
1533 db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED); |
|
1534 db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED_WITH_FAVICONS); |
|
1535 |
|
1536 createCombinedViewOn16(db); |
|
1537 } |
|
1538 |
|
1539 private void upgradeDatabaseFrom16to17(SQLiteDatabase db) { |
|
1540 // Purge any 0-byte favicons/thumbnails |
|
1541 try { |
|
1542 db.execSQL("DELETE FROM " + TABLE_FAVICONS + |
|
1543 " WHERE length(" + Favicons.DATA + ") = 0"); |
|
1544 db.execSQL("DELETE FROM " + TABLE_THUMBNAILS + |
|
1545 " WHERE length(" + Thumbnails.DATA + ") = 0"); |
|
1546 } catch (SQLException e) { |
|
1547 Log.e(LOGTAG, "Error purging invalid favicons or thumbnails", e); |
|
1548 } |
|
1549 } |
|
1550 |
|
1551 /* |
|
1552 * Moves reading list items from 'bookmarks' table to 'reading_list' table. Uses the |
|
1553 * same item GUID. |
|
1554 */ |
|
1555 private void upgradeDatabaseFrom17to18(SQLiteDatabase db) { |
|
1556 debug("Moving reading list items from 'bookmarks' table to 'reading_list' table"); |
|
1557 |
|
1558 final String selection = Bookmarks.PARENT + " = ? AND " + Bookmarks.IS_DELETED + " = ? "; |
|
1559 final String[] selectionArgs = { String.valueOf(Bookmarks.FIXED_READING_LIST_ID), "0" }; |
|
1560 final String[] projection = { Bookmarks._ID, |
|
1561 Bookmarks.GUID, |
|
1562 Bookmarks.URL, |
|
1563 Bookmarks.DATE_MODIFIED, |
|
1564 Bookmarks.DATE_CREATED, |
|
1565 Bookmarks.TITLE }; |
|
1566 Cursor cursor = null; |
|
1567 try { |
|
1568 // Start transaction |
|
1569 db.beginTransaction(); |
|
1570 |
|
1571 // Create 'reading_list' table |
|
1572 createReadingListTable(db); |
|
1573 |
|
1574 // Get all the reading list items from bookmarks table |
|
1575 cursor = db.query(TABLE_BOOKMARKS, projection, selection, selectionArgs, |
|
1576 null, null, null); |
|
1577 |
|
1578 // Insert reading list items into reading_list table |
|
1579 while (cursor.moveToNext()) { |
|
1580 debug(DatabaseUtils.dumpCurrentRowToString(cursor)); |
|
1581 ContentValues values = new ContentValues(); |
|
1582 DatabaseUtils.cursorStringToContentValues(cursor, Bookmarks.URL, values, ReadingListItems.URL); |
|
1583 DatabaseUtils.cursorStringToContentValues(cursor, Bookmarks.GUID, values, ReadingListItems.GUID); |
|
1584 DatabaseUtils.cursorStringToContentValues(cursor, Bookmarks.TITLE, values, ReadingListItems.TITLE); |
|
1585 DatabaseUtils.cursorLongToContentValues(cursor, Bookmarks.DATE_CREATED, values, ReadingListItems.DATE_CREATED); |
|
1586 DatabaseUtils.cursorLongToContentValues(cursor, Bookmarks.DATE_MODIFIED, values, ReadingListItems.DATE_MODIFIED); |
|
1587 |
|
1588 db.insertOrThrow(TABLE_READING_LIST, null, values); |
|
1589 } |
|
1590 |
|
1591 // Delete reading list items from bookmarks table |
|
1592 db.delete(TABLE_BOOKMARKS, |
|
1593 Bookmarks.PARENT + " = ? ", |
|
1594 new String[] { String.valueOf(Bookmarks.FIXED_READING_LIST_ID) }); |
|
1595 |
|
1596 // Delete reading list special folder |
|
1597 db.delete(TABLE_BOOKMARKS, |
|
1598 Bookmarks._ID + " = ? ", |
|
1599 new String[] { String.valueOf(Bookmarks.FIXED_READING_LIST_ID) }); |
|
1600 // Done |
|
1601 db.setTransactionSuccessful(); |
|
1602 |
|
1603 } catch (SQLException e) { |
|
1604 Log.e(LOGTAG, "Error migrating reading list items", e); |
|
1605 } finally { |
|
1606 if (cursor != null) { |
|
1607 cursor.close(); |
|
1608 } |
|
1609 db.endTransaction(); |
|
1610 } |
|
1611 } |
|
1612 |
|
1613 @Override |
|
1614 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { |
|
1615 debug("Upgrading browser.db: " + db.getPath() + " from " + |
|
1616 oldVersion + " to " + newVersion); |
|
1617 |
|
1618 // We have to do incremental upgrades until we reach the current |
|
1619 // database schema version. |
|
1620 for (int v = oldVersion + 1; v <= newVersion; v++) { |
|
1621 switch(v) { |
|
1622 case 2: |
|
1623 upgradeDatabaseFrom1to2(db); |
|
1624 break; |
|
1625 |
|
1626 case 3: |
|
1627 upgradeDatabaseFrom2to3(db); |
|
1628 break; |
|
1629 |
|
1630 case 4: |
|
1631 upgradeDatabaseFrom3to4(db); |
|
1632 break; |
|
1633 |
|
1634 case 5: |
|
1635 upgradeDatabaseFrom4to5(db); |
|
1636 break; |
|
1637 |
|
1638 case 6: |
|
1639 upgradeDatabaseFrom5to6(db); |
|
1640 break; |
|
1641 |
|
1642 case 7: |
|
1643 upgradeDatabaseFrom6to7(db); |
|
1644 break; |
|
1645 |
|
1646 case 8: |
|
1647 upgradeDatabaseFrom7to8(db); |
|
1648 break; |
|
1649 |
|
1650 case 9: |
|
1651 upgradeDatabaseFrom8to9(db); |
|
1652 break; |
|
1653 |
|
1654 case 10: |
|
1655 upgradeDatabaseFrom9to10(db); |
|
1656 break; |
|
1657 |
|
1658 case 11: |
|
1659 upgradeDatabaseFrom10to11(db); |
|
1660 break; |
|
1661 |
|
1662 case 12: |
|
1663 upgradeDatabaseFrom11to12(db); |
|
1664 break; |
|
1665 |
|
1666 case 13: |
|
1667 upgradeDatabaseFrom12to13(db); |
|
1668 break; |
|
1669 |
|
1670 case 14: |
|
1671 upgradeDatabaseFrom13to14(db); |
|
1672 break; |
|
1673 |
|
1674 case 15: |
|
1675 upgradeDatabaseFrom14to15(db); |
|
1676 break; |
|
1677 |
|
1678 case 16: |
|
1679 upgradeDatabaseFrom15to16(db); |
|
1680 break; |
|
1681 |
|
1682 case 17: |
|
1683 upgradeDatabaseFrom16to17(db); |
|
1684 break; |
|
1685 |
|
1686 case 18: |
|
1687 upgradeDatabaseFrom17to18(db); |
|
1688 break; |
|
1689 } |
|
1690 } |
|
1691 |
|
1692 // If an upgrade after 12->13 fails, the entire upgrade is rolled |
|
1693 // back, but we can't undo the deletion of favicon_urls.db if we |
|
1694 // delete this in step 13; therefore, we wait until all steps are |
|
1695 // complete before removing it. |
|
1696 if (oldVersion < 13 && newVersion >= 13 |
|
1697 && mContext.getDatabasePath(Obsolete.FAVICON_DB).exists() |
|
1698 && !mContext.deleteDatabase(Obsolete.FAVICON_DB)) { |
|
1699 throw new SQLException("Could not delete " + Obsolete.FAVICON_DB); |
|
1700 } |
|
1701 } |
|
1702 |
|
1703 @Override |
|
1704 public void onOpen(SQLiteDatabase db) { |
|
1705 debug("Opening browser.db: " + db.getPath()); |
|
1706 |
|
1707 Cursor cursor = null; |
|
1708 try { |
|
1709 cursor = db.rawQuery("PRAGMA foreign_keys=ON", null); |
|
1710 } finally { |
|
1711 if (cursor != null) |
|
1712 cursor.close(); |
|
1713 } |
|
1714 cursor = null; |
|
1715 try { |
|
1716 cursor = db.rawQuery("PRAGMA synchronous=NORMAL", null); |
|
1717 } finally { |
|
1718 if (cursor != null) |
|
1719 cursor.close(); |
|
1720 } |
|
1721 |
|
1722 // From Honeycomb on, it's possible to run several db |
|
1723 // commands in parallel using multiple connections. |
|
1724 if (Build.VERSION.SDK_INT >= 11) { |
|
1725 db.enableWriteAheadLogging(); |
|
1726 db.setLockingEnabled(false); |
|
1727 } else { |
|
1728 // Pre-Honeycomb, we can do some lesser optimizations. |
|
1729 cursor = null; |
|
1730 try { |
|
1731 cursor = db.rawQuery("PRAGMA journal_mode=PERSIST", null); |
|
1732 } finally { |
|
1733 if (cursor != null) |
|
1734 cursor.close(); |
|
1735 } |
|
1736 } |
|
1737 } |
|
1738 |
|
1739 static final String qualifyColumn(String table, String column) { |
|
1740 return DBUtils.qualifyColumn(table, column); |
|
1741 } |
|
1742 |
|
1743 // Calculate these once, at initialization. isLoggable is too expensive to |
|
1744 // have in-line in each log call. |
|
1745 private static boolean logDebug = Log.isLoggable(LOGTAG, Log.DEBUG); |
|
1746 private static boolean logVerbose = Log.isLoggable(LOGTAG, Log.VERBOSE); |
|
1747 protected static void trace(String message) { |
|
1748 if (logVerbose) { |
|
1749 Log.v(LOGTAG, message); |
|
1750 } |
|
1751 } |
|
1752 |
|
1753 protected static void debug(String message) { |
|
1754 if (logDebug) { |
|
1755 Log.d(LOGTAG, message); |
|
1756 } |
|
1757 } |
|
1758 |
|
1759 private Integer getMobileFolderId(SQLiteDatabase db) { |
|
1760 Cursor c = null; |
|
1761 |
|
1762 try { |
|
1763 c = db.query(TABLE_BOOKMARKS, |
|
1764 mobileIdColumns, |
|
1765 Bookmarks.GUID + " = ?", |
|
1766 mobileIdSelectionArgs, |
|
1767 null, null, null); |
|
1768 |
|
1769 if (c == null || !c.moveToFirst()) |
|
1770 return null; |
|
1771 |
|
1772 return c.getInt(c.getColumnIndex(Bookmarks._ID)); |
|
1773 } finally { |
|
1774 if (c != null) |
|
1775 c.close(); |
|
1776 } |
|
1777 } |
|
1778 |
|
1779 private long insertFavicon(SQLiteDatabase db, ContentValues values) { |
|
1780 // This method is a dupicate of BrowserProvider.insertFavicon. |
|
1781 // If changes are needed, please update both |
|
1782 String faviconUrl = values.getAsString(Favicons.URL); |
|
1783 String pageUrl = null; |
|
1784 long faviconId; |
|
1785 |
|
1786 trace("Inserting favicon for URL: " + faviconUrl); |
|
1787 |
|
1788 DBUtils.stripEmptyByteArray(values, Favicons.DATA); |
|
1789 |
|
1790 // Extract the page URL from the ContentValues |
|
1791 if (values.containsKey(Favicons.PAGE_URL)) { |
|
1792 pageUrl = values.getAsString(Favicons.PAGE_URL); |
|
1793 values.remove(Favicons.PAGE_URL); |
|
1794 } |
|
1795 |
|
1796 // If no URL is provided, insert using the default one. |
|
1797 if (TextUtils.isEmpty(faviconUrl) && !TextUtils.isEmpty(pageUrl)) { |
|
1798 values.put(Favicons.URL, org.mozilla.gecko.favicons.Favicons.guessDefaultFaviconURL(pageUrl)); |
|
1799 } |
|
1800 |
|
1801 long now = System.currentTimeMillis(); |
|
1802 values.put(Favicons.DATE_CREATED, now); |
|
1803 values.put(Favicons.DATE_MODIFIED, now); |
|
1804 faviconId = db.insertOrThrow(TABLE_FAVICONS, null, values); |
|
1805 |
|
1806 if (pageUrl != null) { |
|
1807 ContentValues updateValues = new ContentValues(1); |
|
1808 updateValues.put(FaviconColumns.FAVICON_ID, faviconId); |
|
1809 db.update(TABLE_HISTORY, |
|
1810 updateValues, |
|
1811 History.URL + " = ?", |
|
1812 new String[] { pageUrl }); |
|
1813 db.update(TABLE_BOOKMARKS, |
|
1814 updateValues, |
|
1815 Bookmarks.URL + " = ?", |
|
1816 new String[] { pageUrl }); |
|
1817 } |
|
1818 |
|
1819 return faviconId; |
|
1820 } |
|
1821 |
|
1822 private interface BookmarkMigrator { |
|
1823 public void updateForNewTable(ContentValues bookmark); |
|
1824 } |
|
1825 |
|
1826 private class BookmarkMigrator3to4 implements BookmarkMigrator { |
|
1827 @Override |
|
1828 public void updateForNewTable(ContentValues bookmark) { |
|
1829 Integer isFolder = bookmark.getAsInteger("folder"); |
|
1830 if (isFolder == null || isFolder != 1) { |
|
1831 bookmark.put(Bookmarks.TYPE, Bookmarks.TYPE_BOOKMARK); |
|
1832 } else { |
|
1833 bookmark.put(Bookmarks.TYPE, Bookmarks.TYPE_FOLDER); |
|
1834 } |
|
1835 |
|
1836 bookmark.remove("folder"); |
|
1837 } |
|
1838 } |
|
1839 } |