1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/db/BrowserDatabaseHelper.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1839 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.db; 1.10 + 1.11 +import java.io.ByteArrayOutputStream; 1.12 +import java.io.File; 1.13 +import java.lang.reflect.Field; 1.14 +import java.util.ArrayList; 1.15 +import java.util.List; 1.16 +import java.util.Locale; 1.17 +import java.util.regex.Matcher; 1.18 +import java.util.regex.Pattern; 1.19 + 1.20 +import org.json.JSONArray; 1.21 +import org.json.JSONException; 1.22 +import org.json.JSONObject; 1.23 +import org.mozilla.gecko.AppConstants; 1.24 +import org.mozilla.gecko.Distribution; 1.25 +import org.mozilla.gecko.R; 1.26 +import org.mozilla.gecko.db.BrowserContract.Bookmarks; 1.27 +import org.mozilla.gecko.db.BrowserContract.Combined; 1.28 +import org.mozilla.gecko.db.BrowserContract.FaviconColumns; 1.29 +import org.mozilla.gecko.db.BrowserContract.Favicons; 1.30 +import org.mozilla.gecko.db.BrowserContract.History; 1.31 +import org.mozilla.gecko.db.BrowserContract.Obsolete; 1.32 +import org.mozilla.gecko.db.BrowserContract.ReadingListItems; 1.33 +import org.mozilla.gecko.db.BrowserContract.Thumbnails; 1.34 +import org.mozilla.gecko.gfx.BitmapUtils; 1.35 +import org.mozilla.gecko.sync.Utils; 1.36 +import org.mozilla.gecko.util.GeckoJarReader; 1.37 +import org.mozilla.gecko.util.ThreadUtils; 1.38 + 1.39 +import android.content.ContentValues; 1.40 +import android.content.Context; 1.41 +import android.database.Cursor; 1.42 +import android.database.DatabaseUtils; 1.43 +import android.database.SQLException; 1.44 +import android.database.sqlite.SQLiteDatabase; 1.45 +import android.database.sqlite.SQLiteOpenHelper; 1.46 +import android.graphics.Bitmap; 1.47 +import android.net.Uri; 1.48 +import android.os.Build; 1.49 +import android.text.TextUtils; 1.50 +import android.util.Log; 1.51 + 1.52 + 1.53 +final class BrowserDatabaseHelper extends SQLiteOpenHelper { 1.54 + 1.55 + private static final String LOGTAG = "GeckoBrowserDBHelper"; 1.56 + public static final int DATABASE_VERSION = 18; 1.57 + public static final String DATABASE_NAME = "browser.db"; 1.58 + 1.59 + final protected Context mContext; 1.60 + 1.61 + static final String TABLE_BOOKMARKS = Bookmarks.TABLE_NAME; 1.62 + static final String TABLE_HISTORY = History.TABLE_NAME; 1.63 + static final String TABLE_FAVICONS = Favicons.TABLE_NAME; 1.64 + static final String TABLE_THUMBNAILS = Thumbnails.TABLE_NAME; 1.65 + static final String TABLE_READING_LIST = ReadingListItems.TABLE_NAME; 1.66 + 1.67 + static final String VIEW_COMBINED = Combined.VIEW_NAME; 1.68 + static final String VIEW_BOOKMARKS_WITH_FAVICONS = Bookmarks.VIEW_WITH_FAVICONS; 1.69 + static final String VIEW_HISTORY_WITH_FAVICONS = History.VIEW_WITH_FAVICONS; 1.70 + static final String VIEW_COMBINED_WITH_FAVICONS = Combined.VIEW_WITH_FAVICONS; 1.71 + 1.72 + static final String TABLE_BOOKMARKS_JOIN_FAVICONS = TABLE_BOOKMARKS + " LEFT OUTER JOIN " + 1.73 + TABLE_FAVICONS + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.FAVICON_ID) + " = " + 1.74 + qualifyColumn(TABLE_FAVICONS, Favicons._ID); 1.75 + 1.76 + static final String TABLE_HISTORY_JOIN_FAVICONS = TABLE_HISTORY + " LEFT OUTER JOIN " + 1.77 + TABLE_FAVICONS + " ON " + qualifyColumn(TABLE_HISTORY, History.FAVICON_ID) + " = " + 1.78 + qualifyColumn(TABLE_FAVICONS, Favicons._ID); 1.79 + 1.80 + static final String TABLE_BOOKMARKS_TMP = TABLE_BOOKMARKS + "_tmp"; 1.81 + static final String TABLE_HISTORY_TMP = TABLE_HISTORY + "_tmp"; 1.82 + static final String TABLE_IMAGES_TMP = Obsolete.TABLE_IMAGES + "_tmp"; 1.83 + 1.84 + private static final String[] mobileIdColumns = new String[] { Bookmarks._ID }; 1.85 + private static final String[] mobileIdSelectionArgs = new String[] { Bookmarks.MOBILE_FOLDER_GUID }; 1.86 + 1.87 + public BrowserDatabaseHelper(Context context, String databasePath) { 1.88 + super(context, databasePath, null, DATABASE_VERSION); 1.89 + mContext = context; 1.90 + } 1.91 + 1.92 + private void createBookmarksTable(SQLiteDatabase db) { 1.93 + debug("Creating " + TABLE_BOOKMARKS + " table"); 1.94 + 1.95 + // Android versions older than Froyo ship with an sqlite 1.96 + // that doesn't support foreign keys. 1.97 + String foreignKeyOnParent = null; 1.98 + if (Build.VERSION.SDK_INT >= 8) { 1.99 + foreignKeyOnParent = ", FOREIGN KEY (" + Bookmarks.PARENT + 1.100 + ") REFERENCES " + TABLE_BOOKMARKS + "(" + Bookmarks._ID + ")"; 1.101 + } 1.102 + 1.103 + db.execSQL("CREATE TABLE " + TABLE_BOOKMARKS + "(" + 1.104 + Bookmarks._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 1.105 + Bookmarks.TITLE + " TEXT," + 1.106 + Bookmarks.URL + " TEXT," + 1.107 + Bookmarks.TYPE + " INTEGER NOT NULL DEFAULT " + Bookmarks.TYPE_BOOKMARK + "," + 1.108 + Bookmarks.PARENT + " INTEGER," + 1.109 + Bookmarks.POSITION + " INTEGER NOT NULL," + 1.110 + Bookmarks.KEYWORD + " TEXT," + 1.111 + Bookmarks.DESCRIPTION + " TEXT," + 1.112 + Bookmarks.TAGS + " TEXT," + 1.113 + Bookmarks.DATE_CREATED + " INTEGER," + 1.114 + Bookmarks.DATE_MODIFIED + " INTEGER," + 1.115 + Bookmarks.GUID + " TEXT NOT NULL," + 1.116 + Bookmarks.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + 1.117 + (foreignKeyOnParent != null ? foreignKeyOnParent : "") + 1.118 + ");"); 1.119 + 1.120 + db.execSQL("CREATE INDEX bookmarks_url_index ON " + TABLE_BOOKMARKS + "(" 1.121 + + Bookmarks.URL + ")"); 1.122 + db.execSQL("CREATE INDEX bookmarks_type_deleted_index ON " + TABLE_BOOKMARKS + "(" 1.123 + + Bookmarks.TYPE + ", " + Bookmarks.IS_DELETED + ")"); 1.124 + db.execSQL("CREATE UNIQUE INDEX bookmarks_guid_index ON " + TABLE_BOOKMARKS + "(" 1.125 + + Bookmarks.GUID + ")"); 1.126 + db.execSQL("CREATE INDEX bookmarks_modified_index ON " + TABLE_BOOKMARKS + "(" 1.127 + + Bookmarks.DATE_MODIFIED + ")"); 1.128 + } 1.129 + 1.130 + private void createBookmarksTableOn13(SQLiteDatabase db) { 1.131 + debug("Creating " + TABLE_BOOKMARKS + " table"); 1.132 + 1.133 + // Android versions older than Froyo ship with an sqlite 1.134 + // that doesn't support foreign keys. 1.135 + String foreignKeyOnParent = null; 1.136 + if (Build.VERSION.SDK_INT >= 8) { 1.137 + foreignKeyOnParent = ", FOREIGN KEY (" + Bookmarks.PARENT + 1.138 + ") REFERENCES " + TABLE_BOOKMARKS + "(" + Bookmarks._ID + ")"; 1.139 + } 1.140 + 1.141 + db.execSQL("CREATE TABLE " + TABLE_BOOKMARKS + "(" + 1.142 + Bookmarks._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 1.143 + Bookmarks.TITLE + " TEXT," + 1.144 + Bookmarks.URL + " TEXT," + 1.145 + Bookmarks.TYPE + " INTEGER NOT NULL DEFAULT " + Bookmarks.TYPE_BOOKMARK + "," + 1.146 + Bookmarks.PARENT + " INTEGER," + 1.147 + Bookmarks.POSITION + " INTEGER NOT NULL," + 1.148 + Bookmarks.KEYWORD + " TEXT," + 1.149 + Bookmarks.DESCRIPTION + " TEXT," + 1.150 + Bookmarks.TAGS + " TEXT," + 1.151 + Bookmarks.FAVICON_ID + " INTEGER," + 1.152 + Bookmarks.DATE_CREATED + " INTEGER," + 1.153 + Bookmarks.DATE_MODIFIED + " INTEGER," + 1.154 + Bookmarks.GUID + " TEXT NOT NULL," + 1.155 + Bookmarks.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + 1.156 + (foreignKeyOnParent != null ? foreignKeyOnParent : "") + 1.157 + ");"); 1.158 + 1.159 + db.execSQL("CREATE INDEX bookmarks_url_index ON " + TABLE_BOOKMARKS + "(" 1.160 + + Bookmarks.URL + ")"); 1.161 + db.execSQL("CREATE INDEX bookmarks_type_deleted_index ON " + TABLE_BOOKMARKS + "(" 1.162 + + Bookmarks.TYPE + ", " + Bookmarks.IS_DELETED + ")"); 1.163 + db.execSQL("CREATE UNIQUE INDEX bookmarks_guid_index ON " + TABLE_BOOKMARKS + "(" 1.164 + + Bookmarks.GUID + ")"); 1.165 + db.execSQL("CREATE INDEX bookmarks_modified_index ON " + TABLE_BOOKMARKS + "(" 1.166 + + Bookmarks.DATE_MODIFIED + ")"); 1.167 + } 1.168 + 1.169 + private void createHistoryTable(SQLiteDatabase db) { 1.170 + debug("Creating " + TABLE_HISTORY + " table"); 1.171 + db.execSQL("CREATE TABLE " + TABLE_HISTORY + "(" + 1.172 + History._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 1.173 + History.TITLE + " TEXT," + 1.174 + History.URL + " TEXT NOT NULL," + 1.175 + History.VISITS + " INTEGER NOT NULL DEFAULT 0," + 1.176 + History.DATE_LAST_VISITED + " INTEGER," + 1.177 + History.DATE_CREATED + " INTEGER," + 1.178 + History.DATE_MODIFIED + " INTEGER," + 1.179 + History.GUID + " TEXT NOT NULL," + 1.180 + History.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + 1.181 + ");"); 1.182 + 1.183 + db.execSQL("CREATE INDEX history_url_index ON " + TABLE_HISTORY + "(" 1.184 + + History.URL + ")"); 1.185 + db.execSQL("CREATE UNIQUE INDEX history_guid_index ON " + TABLE_HISTORY + "(" 1.186 + + History.GUID + ")"); 1.187 + db.execSQL("CREATE INDEX history_modified_index ON " + TABLE_HISTORY + "(" 1.188 + + History.DATE_MODIFIED + ")"); 1.189 + db.execSQL("CREATE INDEX history_visited_index ON " + TABLE_HISTORY + "(" 1.190 + + History.DATE_LAST_VISITED + ")"); 1.191 + } 1.192 + 1.193 + private void createHistoryTableOn13(SQLiteDatabase db) { 1.194 + debug("Creating " + TABLE_HISTORY + " table"); 1.195 + db.execSQL("CREATE TABLE " + TABLE_HISTORY + "(" + 1.196 + History._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 1.197 + History.TITLE + " TEXT," + 1.198 + History.URL + " TEXT NOT NULL," + 1.199 + History.VISITS + " INTEGER NOT NULL DEFAULT 0," + 1.200 + History.FAVICON_ID + " INTEGER," + 1.201 + History.DATE_LAST_VISITED + " INTEGER," + 1.202 + History.DATE_CREATED + " INTEGER," + 1.203 + History.DATE_MODIFIED + " INTEGER," + 1.204 + History.GUID + " TEXT NOT NULL," + 1.205 + History.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + 1.206 + ");"); 1.207 + 1.208 + db.execSQL("CREATE INDEX history_url_index ON " + TABLE_HISTORY + "(" 1.209 + + History.URL + ")"); 1.210 + db.execSQL("CREATE UNIQUE INDEX history_guid_index ON " + TABLE_HISTORY + "(" 1.211 + + History.GUID + ")"); 1.212 + db.execSQL("CREATE INDEX history_modified_index ON " + TABLE_HISTORY + "(" 1.213 + + History.DATE_MODIFIED + ")"); 1.214 + db.execSQL("CREATE INDEX history_visited_index ON " + TABLE_HISTORY + "(" 1.215 + + History.DATE_LAST_VISITED + ")"); 1.216 + } 1.217 + 1.218 + private void createImagesTable(SQLiteDatabase db) { 1.219 + debug("Creating " + Obsolete.TABLE_IMAGES + " table"); 1.220 + db.execSQL("CREATE TABLE " + Obsolete.TABLE_IMAGES + " (" + 1.221 + Obsolete.Images._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 1.222 + Obsolete.Images.URL + " TEXT UNIQUE NOT NULL," + 1.223 + Obsolete.Images.FAVICON + " BLOB," + 1.224 + Obsolete.Images.FAVICON_URL + " TEXT," + 1.225 + Obsolete.Images.THUMBNAIL + " BLOB," + 1.226 + Obsolete.Images.DATE_CREATED + " INTEGER," + 1.227 + Obsolete.Images.DATE_MODIFIED + " INTEGER," + 1.228 + Obsolete.Images.GUID + " TEXT NOT NULL," + 1.229 + Obsolete.Images.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + 1.230 + ");"); 1.231 + 1.232 + db.execSQL("CREATE INDEX images_url_index ON " + Obsolete.TABLE_IMAGES + "(" 1.233 + + Obsolete.Images.URL + ")"); 1.234 + db.execSQL("CREATE UNIQUE INDEX images_guid_index ON " + Obsolete.TABLE_IMAGES + "(" 1.235 + + Obsolete.Images.GUID + ")"); 1.236 + db.execSQL("CREATE INDEX images_modified_index ON " + Obsolete.TABLE_IMAGES + "(" 1.237 + + Obsolete.Images.DATE_MODIFIED + ")"); 1.238 + } 1.239 + 1.240 + private void createFaviconsTable(SQLiteDatabase db) { 1.241 + debug("Creating " + TABLE_FAVICONS + " table"); 1.242 + db.execSQL("CREATE TABLE " + TABLE_FAVICONS + " (" + 1.243 + Favicons._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 1.244 + Favicons.URL + " TEXT UNIQUE," + 1.245 + Favicons.DATA + " BLOB," + 1.246 + Favicons.DATE_CREATED + " INTEGER," + 1.247 + Favicons.DATE_MODIFIED + " INTEGER" + 1.248 + ");"); 1.249 + 1.250 + db.execSQL("CREATE INDEX favicons_url_index ON " + TABLE_FAVICONS + "(" 1.251 + + Favicons.URL + ")"); 1.252 + db.execSQL("CREATE INDEX favicons_modified_index ON " + TABLE_FAVICONS + "(" 1.253 + + Favicons.DATE_MODIFIED + ")"); 1.254 + } 1.255 + 1.256 + private void createThumbnailsTable(SQLiteDatabase db) { 1.257 + debug("Creating " + TABLE_THUMBNAILS + " table"); 1.258 + db.execSQL("CREATE TABLE " + TABLE_THUMBNAILS + " (" + 1.259 + Thumbnails._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 1.260 + Thumbnails.URL + " TEXT UNIQUE," + 1.261 + Thumbnails.DATA + " BLOB" + 1.262 + ");"); 1.263 + 1.264 + db.execSQL("CREATE INDEX thumbnails_url_index ON " + TABLE_THUMBNAILS + "(" 1.265 + + Thumbnails.URL + ")"); 1.266 + } 1.267 + 1.268 + private void createBookmarksWithImagesView(SQLiteDatabase db) { 1.269 + debug("Creating " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES + " view"); 1.270 + 1.271 + db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES + " AS " + 1.272 + "SELECT " + qualifyColumn(TABLE_BOOKMARKS, "*") + 1.273 + ", " + Obsolete.Images.FAVICON + ", " + Obsolete.Images.THUMBNAIL + " FROM " + 1.274 + Obsolete.TABLE_BOOKMARKS_JOIN_IMAGES); 1.275 + } 1.276 + 1.277 + private void createBookmarksWithFaviconsView(SQLiteDatabase db) { 1.278 + debug("Creating " + VIEW_BOOKMARKS_WITH_FAVICONS + " view"); 1.279 + 1.280 + db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_BOOKMARKS_WITH_FAVICONS + " AS " + 1.281 + "SELECT " + qualifyColumn(TABLE_BOOKMARKS, "*") + 1.282 + ", " + qualifyColumn(TABLE_FAVICONS, Favicons.DATA) + " AS " + Bookmarks.FAVICON + 1.283 + ", " + qualifyColumn(TABLE_FAVICONS, Favicons.URL) + " AS " + Bookmarks.FAVICON_URL + 1.284 + " FROM " + TABLE_BOOKMARKS_JOIN_FAVICONS); 1.285 + } 1.286 + 1.287 + private void createHistoryWithImagesView(SQLiteDatabase db) { 1.288 + debug("Creating " + Obsolete.VIEW_HISTORY_WITH_IMAGES + " view"); 1.289 + 1.290 + db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES + " AS " + 1.291 + "SELECT " + qualifyColumn(TABLE_HISTORY, "*") + 1.292 + ", " + Obsolete.Images.FAVICON + ", " + Obsolete.Images.THUMBNAIL + " FROM " + 1.293 + Obsolete.TABLE_HISTORY_JOIN_IMAGES); 1.294 + } 1.295 + 1.296 + private void createHistoryWithFaviconsView(SQLiteDatabase db) { 1.297 + debug("Creating " + VIEW_HISTORY_WITH_FAVICONS + " view"); 1.298 + 1.299 + db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_HISTORY_WITH_FAVICONS + " AS " + 1.300 + "SELECT " + qualifyColumn(TABLE_HISTORY, "*") + 1.301 + ", " + qualifyColumn(TABLE_FAVICONS, Favicons.DATA) + " AS " + History.FAVICON + 1.302 + ", " + qualifyColumn(TABLE_FAVICONS, Favicons.URL) + " AS " + History.FAVICON_URL + 1.303 + " FROM " + TABLE_HISTORY_JOIN_FAVICONS); 1.304 + } 1.305 + 1.306 + private void createCombinedWithImagesView(SQLiteDatabase db) { 1.307 + debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); 1.308 + 1.309 + db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + 1.310 + " SELECT " + Combined.BOOKMARK_ID + ", " + 1.311 + Combined.HISTORY_ID + ", " + 1.312 + // We need to return an _id column because CursorAdapter requires it for its 1.313 + // default implementation for the getItemId() method. However, since 1.314 + // we're not using this feature in the parts of the UI using this view, 1.315 + // we can just use 0 for all rows. 1.316 + "0 AS " + Combined._ID + ", " + 1.317 + Combined.URL + ", " + 1.318 + Combined.TITLE + ", " + 1.319 + Combined.VISITS + ", " + 1.320 + Combined.DATE_LAST_VISITED + ", " + 1.321 + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + 1.322 + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + 1.323 + " FROM (" + 1.324 + // Bookmarks without history. 1.325 + " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + 1.326 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + 1.327 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + 1.328 + "-1 AS " + Combined.HISTORY_ID + ", " + 1.329 + "-1 AS " + Combined.VISITS + ", " + 1.330 + "-1 AS " + Combined.DATE_LAST_VISITED + 1.331 + " FROM " + TABLE_BOOKMARKS + 1.332 + " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + 1.333 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + 1.334 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + 1.335 + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + 1.336 + " UNION ALL" + 1.337 + // History with and without bookmark. 1.338 + " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + 1.339 + qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + 1.340 + // Prioritze bookmark titles over history titles, since the user may have 1.341 + // customized the title for a bookmark. 1.342 + "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + 1.343 + qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + 1.344 + qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + 1.345 + qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + 1.346 + qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + 1.347 + " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + 1.348 + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + 1.349 + " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + 1.350 + qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + 1.351 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + 1.352 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ")" + 1.353 + ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + 1.354 + " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); 1.355 + } 1.356 + 1.357 + private void createCombinedWithImagesViewOn9(SQLiteDatabase db) { 1.358 + debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); 1.359 + 1.360 + db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + 1.361 + " SELECT " + Combined.BOOKMARK_ID + ", " + 1.362 + Combined.HISTORY_ID + ", " + 1.363 + // We need to return an _id column because CursorAdapter requires it for its 1.364 + // default implementation for the getItemId() method. However, since 1.365 + // we're not using this feature in the parts of the UI using this view, 1.366 + // we can just use 0 for all rows. 1.367 + "0 AS " + Combined._ID + ", " + 1.368 + Combined.URL + ", " + 1.369 + Combined.TITLE + ", " + 1.370 + Combined.VISITS + ", " + 1.371 + Combined.DISPLAY + ", " + 1.372 + Combined.DATE_LAST_VISITED + ", " + 1.373 + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + 1.374 + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + 1.375 + " FROM (" + 1.376 + // Bookmarks without history. 1.377 + " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + 1.378 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + 1.379 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + 1.380 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + 1.381 + Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + 1.382 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.383 + "-1 AS " + Combined.HISTORY_ID + ", " + 1.384 + "-1 AS " + Combined.VISITS + ", " + 1.385 + "-1 AS " + Combined.DATE_LAST_VISITED + 1.386 + " FROM " + TABLE_BOOKMARKS + 1.387 + " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + 1.388 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + 1.389 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + 1.390 + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + 1.391 + " UNION ALL" + 1.392 + // History with and without bookmark. 1.393 + " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + 1.394 + qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + 1.395 + // Prioritze bookmark titles over history titles, since the user may have 1.396 + // customized the title for a bookmark. 1.397 + "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + 1.398 + qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + 1.399 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + 1.400 + Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + 1.401 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.402 + qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + 1.403 + qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + 1.404 + qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + 1.405 + " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + 1.406 + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + 1.407 + " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + 1.408 + qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + 1.409 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + 1.410 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ")" + 1.411 + ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + 1.412 + " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); 1.413 + } 1.414 + 1.415 + private void createCombinedWithImagesViewOn10(SQLiteDatabase db) { 1.416 + debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); 1.417 + 1.418 + db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + 1.419 + " SELECT " + Combined.BOOKMARK_ID + ", " + 1.420 + Combined.HISTORY_ID + ", " + 1.421 + // We need to return an _id column because CursorAdapter requires it for its 1.422 + // default implementation for the getItemId() method. However, since 1.423 + // we're not using this feature in the parts of the UI using this view, 1.424 + // we can just use 0 for all rows. 1.425 + "0 AS " + Combined._ID + ", " + 1.426 + Combined.URL + ", " + 1.427 + Combined.TITLE + ", " + 1.428 + Combined.VISITS + ", " + 1.429 + Combined.DISPLAY + ", " + 1.430 + Combined.DATE_LAST_VISITED + ", " + 1.431 + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + 1.432 + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + 1.433 + " FROM (" + 1.434 + // Bookmarks without history. 1.435 + " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + 1.436 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + 1.437 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + 1.438 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + 1.439 + Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + 1.440 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.441 + "-1 AS " + Combined.HISTORY_ID + ", " + 1.442 + "-1 AS " + Combined.VISITS + ", " + 1.443 + "-1 AS " + Combined.DATE_LAST_VISITED + 1.444 + " FROM " + TABLE_BOOKMARKS + 1.445 + " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + 1.446 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + 1.447 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + 1.448 + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + 1.449 + " UNION ALL" + 1.450 + // History with and without bookmark. 1.451 + " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + 1.452 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + 1.453 + qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + 1.454 + // Prioritze bookmark titles over history titles, since the user may have 1.455 + // customized the title for a bookmark. 1.456 + "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + 1.457 + qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + 1.458 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + 1.459 + Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + 1.460 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.461 + qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + 1.462 + qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + 1.463 + qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + 1.464 + " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + 1.465 + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + 1.466 + " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + 1.467 + qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + 1.468 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + 1.469 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ")" + 1.470 + ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + 1.471 + " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); 1.472 + } 1.473 + 1.474 + private void createCombinedWithImagesViewOn11(SQLiteDatabase db) { 1.475 + debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); 1.476 + 1.477 + db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + 1.478 + " SELECT " + Combined.BOOKMARK_ID + ", " + 1.479 + Combined.HISTORY_ID + ", " + 1.480 + // We need to return an _id column because CursorAdapter requires it for its 1.481 + // default implementation for the getItemId() method. However, since 1.482 + // we're not using this feature in the parts of the UI using this view, 1.483 + // we can just use 0 for all rows. 1.484 + "0 AS " + Combined._ID + ", " + 1.485 + Combined.URL + ", " + 1.486 + Combined.TITLE + ", " + 1.487 + Combined.VISITS + ", " + 1.488 + Combined.DISPLAY + ", " + 1.489 + Combined.DATE_LAST_VISITED + ", " + 1.490 + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + 1.491 + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + 1.492 + " FROM (" + 1.493 + // Bookmarks without history. 1.494 + " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + 1.495 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + 1.496 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + 1.497 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + 1.498 + Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + 1.499 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.500 + "-1 AS " + Combined.HISTORY_ID + ", " + 1.501 + "-1 AS " + Combined.VISITS + ", " + 1.502 + "-1 AS " + Combined.DATE_LAST_VISITED + 1.503 + " FROM " + TABLE_BOOKMARKS + 1.504 + " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + 1.505 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + 1.506 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + 1.507 + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + 1.508 + " UNION ALL" + 1.509 + // History with and without bookmark. 1.510 + " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + 1.511 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + 1.512 + qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + 1.513 + // Prioritze bookmark titles over history titles, since the user may have 1.514 + // customized the title for a bookmark. 1.515 + "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + 1.516 + qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + 1.517 + // Only use DISPLAY_READER if the matching bookmark entry inside reading 1.518 + // list folder is not marked as deleted. 1.519 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " + 1.520 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID + 1.521 + " THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " + 1.522 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.523 + qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + 1.524 + qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + 1.525 + qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + 1.526 + " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + 1.527 + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + 1.528 + " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + 1.529 + qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + 1.530 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + 1.531 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + 1.532 + ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + 1.533 + " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); 1.534 + } 1.535 + 1.536 + private void createCombinedViewOn12(SQLiteDatabase db) { 1.537 + debug("Creating " + VIEW_COMBINED + " view"); 1.538 + 1.539 + db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED + " AS" + 1.540 + " SELECT " + Combined.BOOKMARK_ID + ", " + 1.541 + Combined.HISTORY_ID + ", " + 1.542 + // We need to return an _id column because CursorAdapter requires it for its 1.543 + // default implementation for the getItemId() method. However, since 1.544 + // we're not using this feature in the parts of the UI using this view, 1.545 + // we can just use 0 for all rows. 1.546 + "0 AS " + Combined._ID + ", " + 1.547 + Combined.URL + ", " + 1.548 + Combined.TITLE + ", " + 1.549 + Combined.VISITS + ", " + 1.550 + Combined.DISPLAY + ", " + 1.551 + Combined.DATE_LAST_VISITED + 1.552 + " FROM (" + 1.553 + // Bookmarks without history. 1.554 + " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + 1.555 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + 1.556 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + 1.557 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + 1.558 + Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + 1.559 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.560 + "-1 AS " + Combined.HISTORY_ID + ", " + 1.561 + "-1 AS " + Combined.VISITS + ", " + 1.562 + "-1 AS " + Combined.DATE_LAST_VISITED + 1.563 + " FROM " + TABLE_BOOKMARKS + 1.564 + " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + 1.565 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + 1.566 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + 1.567 + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + 1.568 + " UNION ALL" + 1.569 + // History with and without bookmark. 1.570 + " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + 1.571 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + 1.572 + qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + 1.573 + // Prioritze bookmark titles over history titles, since the user may have 1.574 + // customized the title for a bookmark. 1.575 + "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + 1.576 + qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + 1.577 + // Only use DISPLAY_READER if the matching bookmark entry inside reading 1.578 + // list folder is not marked as deleted. 1.579 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " + 1.580 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID + 1.581 + " THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " + 1.582 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.583 + qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + 1.584 + qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + 1.585 + qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + 1.586 + " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + 1.587 + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + 1.588 + " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + 1.589 + qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + 1.590 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + 1.591 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + 1.592 + ")"); 1.593 + 1.594 + debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); 1.595 + 1.596 + db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + 1.597 + " SELECT *, " + 1.598 + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + 1.599 + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + 1.600 + " FROM " + VIEW_COMBINED + " LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + 1.601 + " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); 1.602 + } 1.603 + 1.604 + private void createCombinedViewOn13(SQLiteDatabase db) { 1.605 + debug("Creating " + VIEW_COMBINED + " view"); 1.606 + 1.607 + db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED + " AS" + 1.608 + " SELECT " + Combined.BOOKMARK_ID + ", " + 1.609 + Combined.HISTORY_ID + ", " + 1.610 + // We need to return an _id column because CursorAdapter requires it for its 1.611 + // default implementation for the getItemId() method. However, since 1.612 + // we're not using this feature in the parts of the UI using this view, 1.613 + // we can just use 0 for all rows. 1.614 + "0 AS " + Combined._ID + ", " + 1.615 + Combined.URL + ", " + 1.616 + Combined.TITLE + ", " + 1.617 + Combined.VISITS + ", " + 1.618 + Combined.DISPLAY + ", " + 1.619 + Combined.DATE_LAST_VISITED + ", " + 1.620 + Combined.FAVICON_ID + 1.621 + " FROM (" + 1.622 + // Bookmarks without history. 1.623 + " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + 1.624 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + 1.625 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + 1.626 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + 1.627 + Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + 1.628 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.629 + "-1 AS " + Combined.HISTORY_ID + ", " + 1.630 + "-1 AS " + Combined.VISITS + ", " + 1.631 + "-1 AS " + Combined.DATE_LAST_VISITED + ", " + 1.632 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.FAVICON_ID) + " AS " + Combined.FAVICON_ID + 1.633 + " FROM " + TABLE_BOOKMARKS + 1.634 + " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + 1.635 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + 1.636 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + 1.637 + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + 1.638 + " UNION ALL" + 1.639 + // History with and without bookmark. 1.640 + " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + 1.641 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + 1.642 + qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + 1.643 + // Prioritize bookmark titles over history titles, since the user may have 1.644 + // customized the title for a bookmark. 1.645 + "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + 1.646 + qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + 1.647 + // Only use DISPLAY_READER if the matching bookmark entry inside reading 1.648 + // list folder is not marked as deleted. 1.649 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " + 1.650 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID + 1.651 + " THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " + 1.652 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.653 + qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + 1.654 + qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + 1.655 + qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + ", " + 1.656 + qualifyColumn(TABLE_HISTORY, History.FAVICON_ID) + " AS " + Combined.FAVICON_ID + 1.657 + " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + 1.658 + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + 1.659 + " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + 1.660 + qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + 1.661 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + 1.662 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + 1.663 + ")"); 1.664 + 1.665 + debug("Creating " + VIEW_COMBINED_WITH_FAVICONS + " view"); 1.666 + 1.667 + db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED_WITH_FAVICONS + " AS" + 1.668 + " SELECT " + qualifyColumn(VIEW_COMBINED, "*") + ", " + 1.669 + qualifyColumn(TABLE_FAVICONS, Favicons.URL) + " AS " + Combined.FAVICON_URL + ", " + 1.670 + qualifyColumn(TABLE_FAVICONS, Favicons.DATA) + " AS " + Combined.FAVICON + 1.671 + " FROM " + VIEW_COMBINED + " LEFT OUTER JOIN " + TABLE_FAVICONS + 1.672 + " ON " + Combined.FAVICON_ID + " = " + qualifyColumn(TABLE_FAVICONS, Favicons._ID)); 1.673 + } 1.674 + 1.675 + private void createCombinedViewOn16(SQLiteDatabase db) { 1.676 + debug("Creating " + VIEW_COMBINED + " view"); 1.677 + 1.678 + db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED + " AS" + 1.679 + " SELECT " + Combined.BOOKMARK_ID + ", " + 1.680 + Combined.HISTORY_ID + ", " + 1.681 + // We need to return an _id column because CursorAdapter requires it for its 1.682 + // default implementation for the getItemId() method. However, since 1.683 + // we're not using this feature in the parts of the UI using this view, 1.684 + // we can just use 0 for all rows. 1.685 + "0 AS " + Combined._ID + ", " + 1.686 + Combined.URL + ", " + 1.687 + Combined.TITLE + ", " + 1.688 + Combined.VISITS + ", " + 1.689 + Combined.DISPLAY + ", " + 1.690 + Combined.DATE_LAST_VISITED + ", " + 1.691 + Combined.FAVICON_ID + 1.692 + " FROM (" + 1.693 + // Bookmarks without history. 1.694 + " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + 1.695 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + 1.696 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + 1.697 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + 1.698 + Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + 1.699 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.700 + "-1 AS " + Combined.HISTORY_ID + ", " + 1.701 + "-1 AS " + Combined.VISITS + ", " + 1.702 + "-1 AS " + Combined.DATE_LAST_VISITED + ", " + 1.703 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.FAVICON_ID) + " AS " + Combined.FAVICON_ID + 1.704 + " FROM " + TABLE_BOOKMARKS + 1.705 + " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + 1.706 + // Ignore pinned bookmarks. 1.707 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " <> " + Bookmarks.FIXED_PINNED_LIST_ID + " AND " + 1.708 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + 1.709 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + 1.710 + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + 1.711 + " UNION ALL" + 1.712 + // History with and without bookmark. 1.713 + " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + 1.714 + // Give pinned bookmarks a NULL ID so that they're not treated as bookmarks. We can't 1.715 + // completely ignore them here because they're joined with history entries we care about. 1.716 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + 1.717 + Bookmarks.FIXED_PINNED_LIST_ID + " THEN NULL ELSE " + 1.718 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " END " + 1.719 + "ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + 1.720 + qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + 1.721 + // Prioritize bookmark titles over history titles, since the user may have 1.722 + // customized the title for a bookmark. 1.723 + "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + 1.724 + qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + 1.725 + // Only use DISPLAY_READER if the matching bookmark entry inside reading 1.726 + // list folder is not marked as deleted. 1.727 + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " + 1.728 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID + 1.729 + " THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " + 1.730 + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + 1.731 + qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + 1.732 + qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + 1.733 + qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + ", " + 1.734 + qualifyColumn(TABLE_HISTORY, History.FAVICON_ID) + " AS " + Combined.FAVICON_ID + 1.735 + " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + 1.736 + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + 1.737 + " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + 1.738 + qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + 1.739 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + 1.740 + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + 1.741 + ")"); 1.742 + 1.743 + debug("Creating " + VIEW_COMBINED_WITH_FAVICONS + " view"); 1.744 + 1.745 + db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED_WITH_FAVICONS + " AS" + 1.746 + " SELECT " + qualifyColumn(VIEW_COMBINED, "*") + ", " + 1.747 + qualifyColumn(TABLE_FAVICONS, Favicons.URL) + " AS " + Combined.FAVICON_URL + ", " + 1.748 + qualifyColumn(TABLE_FAVICONS, Favicons.DATA) + " AS " + Combined.FAVICON + 1.749 + " FROM " + VIEW_COMBINED + " LEFT OUTER JOIN " + TABLE_FAVICONS + 1.750 + " ON " + Combined.FAVICON_ID + " = " + qualifyColumn(TABLE_FAVICONS, Favicons._ID)); 1.751 + } 1.752 + 1.753 + @Override 1.754 + public void onCreate(SQLiteDatabase db) { 1.755 + debug("Creating browser.db: " + db.getPath()); 1.756 + 1.757 + createBookmarksTableOn13(db); 1.758 + createHistoryTableOn13(db); 1.759 + createFaviconsTable(db); 1.760 + createThumbnailsTable(db); 1.761 + 1.762 + createBookmarksWithFaviconsView(db); 1.763 + createHistoryWithFaviconsView(db); 1.764 + createCombinedViewOn16(db); 1.765 + 1.766 + createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID, 1.767 + R.string.bookmarks_folder_places, 0); 1.768 + 1.769 + createOrUpdateAllSpecialFolders(db); 1.770 + 1.771 + // Create distribution bookmarks before our own default bookmarks 1.772 + int pos = createDistributionBookmarks(db); 1.773 + createDefaultBookmarks(db, pos); 1.774 + 1.775 + createReadingListTable(db); 1.776 + } 1.777 + 1.778 + private String getLocalizedProperty(JSONObject bookmark, String property, Locale locale) throws JSONException { 1.779 + // Try the full locale 1.780 + String fullLocale = property + "." + locale.toString(); 1.781 + if (bookmark.has(fullLocale)) { 1.782 + return bookmark.getString(fullLocale); 1.783 + } 1.784 + // Try without a variant 1.785 + if (!TextUtils.isEmpty(locale.getVariant())) { 1.786 + String noVariant = fullLocale.substring(0, fullLocale.lastIndexOf("_")); 1.787 + if (bookmark.has(noVariant)) { 1.788 + return bookmark.getString(noVariant); 1.789 + } 1.790 + } 1.791 + // Try just the language 1.792 + String lang = property + "." + locale.getLanguage(); 1.793 + if (bookmark.has(lang)) { 1.794 + return bookmark.getString(lang); 1.795 + } 1.796 + // Default to the non-localized property name 1.797 + return bookmark.getString(property); 1.798 + } 1.799 + 1.800 + // Returns the number of bookmarks inserted in the db 1.801 + private int createDistributionBookmarks(SQLiteDatabase db) { 1.802 + JSONArray bookmarks = Distribution.getBookmarks(mContext); 1.803 + if (bookmarks == null) { 1.804 + return 0; 1.805 + } 1.806 + 1.807 + Locale locale = Locale.getDefault(); 1.808 + int pos = 0; 1.809 + Integer mobileFolderId = getMobileFolderId(db); 1.810 + if (mobileFolderId == null) { 1.811 + Log.e(LOGTAG, "Error creating distribution bookmarks: mobileFolderId is null"); 1.812 + return 0; 1.813 + } 1.814 + 1.815 + for (int i = 0; i < bookmarks.length(); i++) { 1.816 + try { 1.817 + final JSONObject bookmark = bookmarks.getJSONObject(i); 1.818 + 1.819 + String title = getLocalizedProperty(bookmark, "title", locale); 1.820 + final String url = getLocalizedProperty(bookmark, "url", locale); 1.821 + createBookmark(db, title, url, pos, mobileFolderId); 1.822 + 1.823 + if (bookmark.has("pinned")) { 1.824 + try { 1.825 + // Create a fake bookmark in the hidden pinned folder to pin bookmark 1.826 + // to about:home top sites. Pass pos as the pinned position to pin 1.827 + // sites in the order that bookmarks are specified in bookmarks.json. 1.828 + if (bookmark.getBoolean("pinned")) { 1.829 + createBookmark(db, title, url, pos, Bookmarks.FIXED_PINNED_LIST_ID); 1.830 + } 1.831 + } catch (JSONException e) { 1.832 + Log.e(LOGTAG, "Error pinning bookmark to top sites", e); 1.833 + } 1.834 + } 1.835 + 1.836 + pos++; 1.837 + 1.838 + // return early if there is no icon for this bookmark 1.839 + if (!bookmark.has("icon")) { 1.840 + continue; 1.841 + } 1.842 + 1.843 + // create icons in a separate thread to avoid blocking about:home on startup 1.844 + ThreadUtils.postToBackgroundThread(new Runnable() { 1.845 + @Override 1.846 + public void run() { 1.847 + SQLiteDatabase db = getWritableDatabase(); 1.848 + try { 1.849 + String iconData = bookmark.getString("icon"); 1.850 + Bitmap icon = BitmapUtils.getBitmapFromDataURI(iconData); 1.851 + if (icon != null) { 1.852 + createFavicon(db, url, icon); 1.853 + } 1.854 + } catch (JSONException e) { 1.855 + Log.e(LOGTAG, "Error creating distribution bookmark icon", e); 1.856 + } 1.857 + } 1.858 + }); 1.859 + } catch (JSONException e) { 1.860 + Log.e(LOGTAG, "Error creating distribution bookmark", e); 1.861 + } 1.862 + } 1.863 + return pos; 1.864 + } 1.865 + 1.866 + private void createReadingListTable(SQLiteDatabase db) { 1.867 + debug("Creating " + TABLE_READING_LIST + " table"); 1.868 + 1.869 + db.execSQL("CREATE TABLE " + TABLE_READING_LIST + "(" + 1.870 + ReadingListItems._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + 1.871 + ReadingListItems.URL + " TEXT NOT NULL, " + 1.872 + ReadingListItems.TITLE + " TEXT, " + 1.873 + ReadingListItems.EXCERPT + " TEXT, " + 1.874 + ReadingListItems.READ + " TINYINT DEFAULT 0, " + 1.875 + ReadingListItems.IS_DELETED + " TINYINT DEFAULT 0, " + 1.876 + ReadingListItems.GUID + " TEXT UNIQUE NOT NULL, " + 1.877 + ReadingListItems.DATE_MODIFIED + " INTEGER NOT NULL, " + 1.878 + ReadingListItems.DATE_CREATED + " INTEGER NOT NULL, " + 1.879 + ReadingListItems.LENGTH + " INTEGER DEFAULT 0 ); "); 1.880 + 1.881 + db.execSQL("CREATE INDEX reading_list_url ON " + TABLE_READING_LIST + "(" 1.882 + + ReadingListItems.URL + ")"); 1.883 + db.execSQL("CREATE UNIQUE INDEX reading_list_guid ON " + TABLE_READING_LIST + "(" 1.884 + + ReadingListItems.GUID + ")"); 1.885 + } 1.886 + 1.887 + // Inserts default bookmarks, starting at a specified position 1.888 + private void createDefaultBookmarks(SQLiteDatabase db, int pos) { 1.889 + Class<?> stringsClass = R.string.class; 1.890 + Field[] fields = stringsClass.getFields(); 1.891 + Pattern p = Pattern.compile("^bookmarkdefaults_title_"); 1.892 + 1.893 + Integer mobileFolderId = getMobileFolderId(db); 1.894 + if (mobileFolderId == null) { 1.895 + Log.e(LOGTAG, "Error creating default bookmarks: mobileFolderId is null"); 1.896 + return; 1.897 + } 1.898 + 1.899 + for (int i = 0; i < fields.length; i++) { 1.900 + final String name = fields[i].getName(); 1.901 + Matcher m = p.matcher(name); 1.902 + if (!m.find()) { 1.903 + continue; 1.904 + } 1.905 + try { 1.906 + int titleid = fields[i].getInt(null); 1.907 + String title = mContext.getString(titleid); 1.908 + 1.909 + Field urlField = stringsClass.getField(name.replace("_title_", "_url_")); 1.910 + int urlId = urlField.getInt(null); 1.911 + final String url = mContext.getString(urlId); 1.912 + createBookmark(db, title, url, pos, mobileFolderId); 1.913 + 1.914 + // create icons in a separate thread to avoid blocking about:home on startup 1.915 + ThreadUtils.postToBackgroundThread(new Runnable() { 1.916 + @Override 1.917 + public void run() { 1.918 + SQLiteDatabase db = getWritableDatabase(); 1.919 + Bitmap icon = getDefaultFaviconFromPath(name); 1.920 + if (icon == null) { 1.921 + icon = getDefaultFaviconFromDrawable(name); 1.922 + } 1.923 + if (icon != null) { 1.924 + createFavicon(db, url, icon); 1.925 + } 1.926 + } 1.927 + }); 1.928 + pos++; 1.929 + } catch (java.lang.IllegalAccessException ex) { 1.930 + Log.e(LOGTAG, "Can't create bookmark " + name, ex); 1.931 + } catch (java.lang.NoSuchFieldException ex) { 1.932 + Log.e(LOGTAG, "Can't create bookmark " + name, ex); 1.933 + } 1.934 + } 1.935 + } 1.936 + 1.937 + private void createBookmark(SQLiteDatabase db, String title, String url, int pos, int parent) { 1.938 + ContentValues bookmarkValues = new ContentValues(); 1.939 + bookmarkValues.put(Bookmarks.PARENT, parent); 1.940 + 1.941 + long now = System.currentTimeMillis(); 1.942 + bookmarkValues.put(Bookmarks.DATE_CREATED, now); 1.943 + bookmarkValues.put(Bookmarks.DATE_MODIFIED, now); 1.944 + 1.945 + bookmarkValues.put(Bookmarks.TITLE, title); 1.946 + bookmarkValues.put(Bookmarks.URL, url); 1.947 + bookmarkValues.put(Bookmarks.GUID, Utils.generateGuid()); 1.948 + bookmarkValues.put(Bookmarks.POSITION, pos); 1.949 + db.insertOrThrow(TABLE_BOOKMARKS, Bookmarks.TITLE, bookmarkValues); 1.950 + } 1.951 + 1.952 + private void createFavicon(SQLiteDatabase db, String url, Bitmap icon) { 1.953 + ByteArrayOutputStream stream = new ByteArrayOutputStream(); 1.954 + 1.955 + ContentValues iconValues = new ContentValues(); 1.956 + iconValues.put(Favicons.PAGE_URL, url); 1.957 + 1.958 + byte[] data = null; 1.959 + if (icon.compress(Bitmap.CompressFormat.PNG, 100, stream)) { 1.960 + data = stream.toByteArray(); 1.961 + } else { 1.962 + Log.w(LOGTAG, "Favicon compression failed."); 1.963 + } 1.964 + iconValues.put(Favicons.DATA, data); 1.965 + 1.966 + insertFavicon(db, iconValues); 1.967 + } 1.968 + 1.969 + private Bitmap getDefaultFaviconFromPath(String name) { 1.970 + Class<?> stringClass = R.string.class; 1.971 + try { 1.972 + // Look for a drawable with the id R.drawable.bookmarkdefaults_favicon_* 1.973 + Field faviconField = stringClass.getField(name.replace("_title_", "_favicon_")); 1.974 + if (faviconField == null) { 1.975 + return null; 1.976 + } 1.977 + int faviconId = faviconField.getInt(null); 1.978 + String path = mContext.getString(faviconId); 1.979 + 1.980 + String apkPath = mContext.getPackageResourcePath(); 1.981 + File apkFile = new File(apkPath); 1.982 + String bitmapPath = "jar:jar:" + apkFile.toURI() + "!/" + AppConstants.OMNIJAR_NAME + "!/" + path; 1.983 + return GeckoJarReader.getBitmap(mContext.getResources(), bitmapPath); 1.984 + } catch (java.lang.IllegalAccessException ex) { 1.985 + Log.e(LOGTAG, "[Path] Can't create favicon " + name, ex); 1.986 + } catch (java.lang.NoSuchFieldException ex) { 1.987 + // If the field does not exist, that means we intend to load via a drawable 1.988 + } 1.989 + return null; 1.990 + } 1.991 + 1.992 + private Bitmap getDefaultFaviconFromDrawable(String name) { 1.993 + Class<?> drawablesClass = R.drawable.class; 1.994 + try { 1.995 + // Look for a drawable with the id R.drawable.bookmarkdefaults_favicon_* 1.996 + Field faviconField = drawablesClass.getField(name.replace("_title_", "_favicon_")); 1.997 + if (faviconField == null) { 1.998 + return null; 1.999 + } 1.1000 + int faviconId = faviconField.getInt(null); 1.1001 + return BitmapUtils.decodeResource(mContext, faviconId); 1.1002 + } catch (java.lang.IllegalAccessException ex) { 1.1003 + Log.e(LOGTAG, "[Drawable] Can't create favicon " + name, ex); 1.1004 + } catch (java.lang.NoSuchFieldException ex) { 1.1005 + // If the field does not exist, that means we intend to load via a file path 1.1006 + } 1.1007 + return null; 1.1008 + } 1.1009 + 1.1010 + private void createOrUpdateAllSpecialFolders(SQLiteDatabase db) { 1.1011 + createOrUpdateSpecialFolder(db, Bookmarks.MOBILE_FOLDER_GUID, 1.1012 + R.string.bookmarks_folder_mobile, 0); 1.1013 + createOrUpdateSpecialFolder(db, Bookmarks.TOOLBAR_FOLDER_GUID, 1.1014 + R.string.bookmarks_folder_toolbar, 1); 1.1015 + createOrUpdateSpecialFolder(db, Bookmarks.MENU_FOLDER_GUID, 1.1016 + R.string.bookmarks_folder_menu, 2); 1.1017 + createOrUpdateSpecialFolder(db, Bookmarks.TAGS_FOLDER_GUID, 1.1018 + R.string.bookmarks_folder_tags, 3); 1.1019 + createOrUpdateSpecialFolder(db, Bookmarks.UNFILED_FOLDER_GUID, 1.1020 + R.string.bookmarks_folder_unfiled, 4); 1.1021 + createOrUpdateSpecialFolder(db, Bookmarks.READING_LIST_FOLDER_GUID, 1.1022 + R.string.bookmarks_folder_reading_list, 5); 1.1023 + createOrUpdateSpecialFolder(db, Bookmarks.PINNED_FOLDER_GUID, 1.1024 + R.string.bookmarks_folder_pinned, 6); 1.1025 + } 1.1026 + 1.1027 + private void createOrUpdateSpecialFolder(SQLiteDatabase db, 1.1028 + String guid, int titleId, int position) { 1.1029 + ContentValues values = new ContentValues(); 1.1030 + values.put(Bookmarks.GUID, guid); 1.1031 + values.put(Bookmarks.TYPE, Bookmarks.TYPE_FOLDER); 1.1032 + values.put(Bookmarks.POSITION, position); 1.1033 + 1.1034 + if (guid.equals(Bookmarks.PLACES_FOLDER_GUID)) 1.1035 + values.put(Bookmarks._ID, Bookmarks.FIXED_ROOT_ID); 1.1036 + else if (guid.equals(Bookmarks.READING_LIST_FOLDER_GUID)) 1.1037 + values.put(Bookmarks._ID, Bookmarks.FIXED_READING_LIST_ID); 1.1038 + else if (guid.equals(Bookmarks.PINNED_FOLDER_GUID)) 1.1039 + values.put(Bookmarks._ID, Bookmarks.FIXED_PINNED_LIST_ID); 1.1040 + 1.1041 + // Set the parent to 0, which sync assumes is the root 1.1042 + values.put(Bookmarks.PARENT, Bookmarks.FIXED_ROOT_ID); 1.1043 + 1.1044 + String title = mContext.getResources().getString(titleId); 1.1045 + values.put(Bookmarks.TITLE, title); 1.1046 + 1.1047 + long now = System.currentTimeMillis(); 1.1048 + values.put(Bookmarks.DATE_CREATED, now); 1.1049 + values.put(Bookmarks.DATE_MODIFIED, now); 1.1050 + 1.1051 + int updated = db.update(TABLE_BOOKMARKS, values, 1.1052 + Bookmarks.GUID + " = ?", 1.1053 + new String[] { guid }); 1.1054 + 1.1055 + if (updated == 0) { 1.1056 + db.insert(TABLE_BOOKMARKS, Bookmarks.GUID, values); 1.1057 + debug("Inserted special folder: " + guid); 1.1058 + } else { 1.1059 + debug("Updated special folder: " + guid); 1.1060 + } 1.1061 + } 1.1062 + 1.1063 + private boolean isSpecialFolder(ContentValues values) { 1.1064 + String guid = values.getAsString(Bookmarks.GUID); 1.1065 + if (guid == null) 1.1066 + return false; 1.1067 + 1.1068 + return guid.equals(Bookmarks.MOBILE_FOLDER_GUID) || 1.1069 + guid.equals(Bookmarks.MENU_FOLDER_GUID) || 1.1070 + guid.equals(Bookmarks.TOOLBAR_FOLDER_GUID) || 1.1071 + guid.equals(Bookmarks.UNFILED_FOLDER_GUID) || 1.1072 + guid.equals(Bookmarks.TAGS_FOLDER_GUID); 1.1073 + } 1.1074 + 1.1075 + private void migrateBookmarkFolder(SQLiteDatabase db, int folderId, 1.1076 + BookmarkMigrator migrator) { 1.1077 + Cursor c = null; 1.1078 + 1.1079 + debug("Migrating bookmark folder with id = " + folderId); 1.1080 + 1.1081 + String selection = Bookmarks.PARENT + " = " + folderId; 1.1082 + String[] selectionArgs = null; 1.1083 + 1.1084 + boolean isRootFolder = (folderId == Bookmarks.FIXED_ROOT_ID); 1.1085 + 1.1086 + // If we're loading the root folder, we have to account for 1.1087 + // any previously created special folder that was created without 1.1088 + // setting a parent id (e.g. mobile folder) and making sure we're 1.1089 + // not adding any infinite recursion as root's parent is root itself. 1.1090 + if (isRootFolder) { 1.1091 + selection = Bookmarks.GUID + " != ?" + " AND (" + 1.1092 + selection + " OR " + Bookmarks.PARENT + " = NULL)"; 1.1093 + selectionArgs = new String[] { Bookmarks.PLACES_FOLDER_GUID }; 1.1094 + } 1.1095 + 1.1096 + List<Integer> subFolders = new ArrayList<Integer>(); 1.1097 + List<ContentValues> invalidSpecialEntries = new ArrayList<ContentValues>(); 1.1098 + 1.1099 + try { 1.1100 + c = db.query(TABLE_BOOKMARKS_TMP, 1.1101 + null, 1.1102 + selection, 1.1103 + selectionArgs, 1.1104 + null, null, null); 1.1105 + 1.1106 + // The key point here is that bookmarks should be added in 1.1107 + // parent order to avoid any problems with the foreign key 1.1108 + // in Bookmarks.PARENT. 1.1109 + while (c.moveToNext()) { 1.1110 + ContentValues values = new ContentValues(); 1.1111 + 1.1112 + // We're using a null projection in the query which 1.1113 + // means we're getting all columns from the table. 1.1114 + // It's safe to simply transform the row into the 1.1115 + // values to be inserted on the new table. 1.1116 + DatabaseUtils.cursorRowToContentValues(c, values); 1.1117 + 1.1118 + boolean isSpecialFolder = isSpecialFolder(values); 1.1119 + 1.1120 + // The mobile folder used to be created with PARENT = NULL. 1.1121 + // We want fix that here. 1.1122 + if (values.getAsLong(Bookmarks.PARENT) == null && isSpecialFolder) 1.1123 + values.put(Bookmarks.PARENT, Bookmarks.FIXED_ROOT_ID); 1.1124 + 1.1125 + if (isRootFolder && !isSpecialFolder) { 1.1126 + invalidSpecialEntries.add(values); 1.1127 + continue; 1.1128 + } 1.1129 + 1.1130 + if (migrator != null) 1.1131 + migrator.updateForNewTable(values); 1.1132 + 1.1133 + debug("Migrating bookmark: " + values.getAsString(Bookmarks.TITLE)); 1.1134 + db.insert(TABLE_BOOKMARKS, Bookmarks.URL, values); 1.1135 + 1.1136 + Integer type = values.getAsInteger(Bookmarks.TYPE); 1.1137 + if (type != null && type == Bookmarks.TYPE_FOLDER) 1.1138 + subFolders.add(values.getAsInteger(Bookmarks._ID)); 1.1139 + } 1.1140 + } finally { 1.1141 + if (c != null) 1.1142 + c.close(); 1.1143 + } 1.1144 + 1.1145 + // At this point is safe to assume that the mobile folder is 1.1146 + // in the new table given that we've always created it on 1.1147 + // database creation time. 1.1148 + final int nInvalidSpecialEntries = invalidSpecialEntries.size(); 1.1149 + if (nInvalidSpecialEntries > 0) { 1.1150 + Integer mobileFolderId = getMobileFolderId(db); 1.1151 + if (mobileFolderId == null) { 1.1152 + Log.e(LOGTAG, "Error migrating invalid special folder entries: mobile folder id is null"); 1.1153 + return; 1.1154 + } 1.1155 + 1.1156 + debug("Found " + nInvalidSpecialEntries + " invalid special folder entries"); 1.1157 + for (int i = 0; i < nInvalidSpecialEntries; i++) { 1.1158 + ContentValues values = invalidSpecialEntries.get(i); 1.1159 + values.put(Bookmarks.PARENT, mobileFolderId); 1.1160 + 1.1161 + db.insert(TABLE_BOOKMARKS, Bookmarks.URL, values); 1.1162 + } 1.1163 + } 1.1164 + 1.1165 + final int nSubFolders = subFolders.size(); 1.1166 + for (int i = 0; i < nSubFolders; i++) { 1.1167 + int subFolderId = subFolders.get(i); 1.1168 + migrateBookmarkFolder(db, subFolderId, migrator); 1.1169 + } 1.1170 + } 1.1171 + 1.1172 + private void migrateBookmarksTable(SQLiteDatabase db) { 1.1173 + migrateBookmarksTable(db, null); 1.1174 + } 1.1175 + 1.1176 + private void migrateBookmarksTable(SQLiteDatabase db, BookmarkMigrator migrator) { 1.1177 + debug("Renaming bookmarks table to " + TABLE_BOOKMARKS_TMP); 1.1178 + db.execSQL("ALTER TABLE " + TABLE_BOOKMARKS + 1.1179 + " RENAME TO " + TABLE_BOOKMARKS_TMP); 1.1180 + 1.1181 + debug("Dropping views and indexes related to " + TABLE_BOOKMARKS); 1.1182 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); 1.1183 + 1.1184 + db.execSQL("DROP INDEX IF EXISTS bookmarks_url_index"); 1.1185 + db.execSQL("DROP INDEX IF EXISTS bookmarks_type_deleted_index"); 1.1186 + db.execSQL("DROP INDEX IF EXISTS bookmarks_guid_index"); 1.1187 + db.execSQL("DROP INDEX IF EXISTS bookmarks_modified_index"); 1.1188 + 1.1189 + createBookmarksTable(db); 1.1190 + createBookmarksWithImagesView(db); 1.1191 + 1.1192 + createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID, 1.1193 + R.string.bookmarks_folder_places, 0); 1.1194 + 1.1195 + migrateBookmarkFolder(db, Bookmarks.FIXED_ROOT_ID, migrator); 1.1196 + 1.1197 + // Ensure all special folders exist and have the 1.1198 + // right folder hierarchy. 1.1199 + createOrUpdateAllSpecialFolders(db); 1.1200 + 1.1201 + debug("Dropping bookmarks temporary table"); 1.1202 + db.execSQL("DROP TABLE IF EXISTS " + TABLE_BOOKMARKS_TMP); 1.1203 + } 1.1204 + 1.1205 + 1.1206 + private void migrateHistoryTable(SQLiteDatabase db) { 1.1207 + debug("Renaming history table to " + TABLE_HISTORY_TMP); 1.1208 + db.execSQL("ALTER TABLE " + TABLE_HISTORY + 1.1209 + " RENAME TO " + TABLE_HISTORY_TMP); 1.1210 + 1.1211 + debug("Dropping views and indexes related to " + TABLE_HISTORY); 1.1212 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); 1.1213 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1214 + 1.1215 + db.execSQL("DROP INDEX IF EXISTS history_url_index"); 1.1216 + db.execSQL("DROP INDEX IF EXISTS history_guid_index"); 1.1217 + db.execSQL("DROP INDEX IF EXISTS history_modified_index"); 1.1218 + db.execSQL("DROP INDEX IF EXISTS history_visited_index"); 1.1219 + 1.1220 + createHistoryTable(db); 1.1221 + createHistoryWithImagesView(db); 1.1222 + createCombinedWithImagesView(db); 1.1223 + 1.1224 + db.execSQL("INSERT INTO " + TABLE_HISTORY + " SELECT * FROM " + TABLE_HISTORY_TMP); 1.1225 + 1.1226 + debug("Dropping history temporary table"); 1.1227 + db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY_TMP); 1.1228 + } 1.1229 + 1.1230 + private void migrateImagesTable(SQLiteDatabase db) { 1.1231 + debug("Renaming images table to " + TABLE_IMAGES_TMP); 1.1232 + db.execSQL("ALTER TABLE " + Obsolete.TABLE_IMAGES + 1.1233 + " RENAME TO " + TABLE_IMAGES_TMP); 1.1234 + 1.1235 + debug("Dropping views and indexes related to " + Obsolete.TABLE_IMAGES); 1.1236 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); 1.1237 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1238 + 1.1239 + db.execSQL("DROP INDEX IF EXISTS images_url_index"); 1.1240 + db.execSQL("DROP INDEX IF EXISTS images_guid_index"); 1.1241 + db.execSQL("DROP INDEX IF EXISTS images_modified_index"); 1.1242 + 1.1243 + createImagesTable(db); 1.1244 + createHistoryWithImagesView(db); 1.1245 + createCombinedWithImagesView(db); 1.1246 + 1.1247 + db.execSQL("INSERT INTO " + Obsolete.TABLE_IMAGES + " SELECT * FROM " + TABLE_IMAGES_TMP); 1.1248 + 1.1249 + debug("Dropping images temporary table"); 1.1250 + db.execSQL("DROP TABLE IF EXISTS " + TABLE_IMAGES_TMP); 1.1251 + } 1.1252 + 1.1253 + private void upgradeDatabaseFrom1to2(SQLiteDatabase db) { 1.1254 + migrateBookmarksTable(db); 1.1255 + } 1.1256 + 1.1257 + private void upgradeDatabaseFrom2to3(SQLiteDatabase db) { 1.1258 + debug("Dropping view: " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); 1.1259 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); 1.1260 + 1.1261 + createBookmarksWithImagesView(db); 1.1262 + 1.1263 + debug("Dropping view: " + Obsolete.VIEW_HISTORY_WITH_IMAGES); 1.1264 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); 1.1265 + 1.1266 + createHistoryWithImagesView(db); 1.1267 + } 1.1268 + 1.1269 + private void upgradeDatabaseFrom3to4(SQLiteDatabase db) { 1.1270 + migrateBookmarksTable(db, new BookmarkMigrator3to4()); 1.1271 + } 1.1272 + 1.1273 + private void upgradeDatabaseFrom4to5(SQLiteDatabase db) { 1.1274 + createCombinedWithImagesView(db); 1.1275 + } 1.1276 + 1.1277 + private void upgradeDatabaseFrom5to6(SQLiteDatabase db) { 1.1278 + debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1279 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1280 + 1.1281 + createCombinedWithImagesView(db); 1.1282 + } 1.1283 + 1.1284 + private void upgradeDatabaseFrom6to7(SQLiteDatabase db) { 1.1285 + debug("Removing history visits with NULL GUIDs"); 1.1286 + db.execSQL("DELETE FROM " + TABLE_HISTORY + " WHERE " + History.GUID + " IS NULL"); 1.1287 + 1.1288 + debug("Update images with NULL GUIDs"); 1.1289 + String[] columns = new String[] { Obsolete.Images._ID }; 1.1290 + Cursor cursor = null; 1.1291 + try { 1.1292 + cursor = db.query(Obsolete.TABLE_IMAGES, columns, Obsolete.Images.GUID + " IS NULL", null, null ,null, null, null); 1.1293 + ContentValues values = new ContentValues(); 1.1294 + if (cursor.moveToFirst()) { 1.1295 + do { 1.1296 + values.put(Obsolete.Images.GUID, Utils.generateGuid()); 1.1297 + db.update(Obsolete.TABLE_IMAGES, values, Obsolete.Images._ID + " = ?", new String[] { 1.1298 + cursor.getString(cursor.getColumnIndexOrThrow(Obsolete.Images._ID)) 1.1299 + }); 1.1300 + } while (cursor.moveToNext()); 1.1301 + } 1.1302 + } finally { 1.1303 + if (cursor != null) 1.1304 + cursor.close(); 1.1305 + } 1.1306 + 1.1307 + migrateBookmarksTable(db); 1.1308 + migrateHistoryTable(db); 1.1309 + migrateImagesTable(db); 1.1310 + } 1.1311 + 1.1312 + private void upgradeDatabaseFrom7to8(SQLiteDatabase db) { 1.1313 + debug("Combining history entries with the same URL"); 1.1314 + 1.1315 + final String TABLE_DUPES = "duped_urls"; 1.1316 + final String TOTAL = "total"; 1.1317 + final String LATEST = "latest"; 1.1318 + final String WINNER = "winner"; 1.1319 + 1.1320 + db.execSQL("CREATE TEMP TABLE " + TABLE_DUPES + " AS" + 1.1321 + " SELECT " + History.URL + ", " + 1.1322 + "SUM(" + History.VISITS + ") AS " + TOTAL + ", " + 1.1323 + "MAX(" + History.DATE_MODIFIED + ") AS " + LATEST + ", " + 1.1324 + "MAX(" + History._ID + ") AS " + WINNER + 1.1325 + " FROM " + TABLE_HISTORY + 1.1326 + " GROUP BY " + History.URL + 1.1327 + " HAVING count(" + History.URL + ") > 1"); 1.1328 + 1.1329 + db.execSQL("CREATE UNIQUE INDEX " + TABLE_DUPES + "_url_index ON " + 1.1330 + TABLE_DUPES + " (" + History.URL + ")"); 1.1331 + 1.1332 + final String fromClause = " FROM " + TABLE_DUPES + " WHERE " + 1.1333 + qualifyColumn(TABLE_DUPES, History.URL) + " = " + 1.1334 + qualifyColumn(TABLE_HISTORY, History.URL); 1.1335 + 1.1336 + db.execSQL("UPDATE " + TABLE_HISTORY + 1.1337 + " SET " + History.VISITS + " = (SELECT " + TOTAL + fromClause + "), " + 1.1338 + History.DATE_MODIFIED + " = (SELECT " + LATEST + fromClause + "), " + 1.1339 + History.IS_DELETED + " = " + 1.1340 + "(" + History._ID + " <> (SELECT " + WINNER + fromClause + "))" + 1.1341 + " WHERE " + History.URL + " IN (SELECT " + History.URL + " FROM " + TABLE_DUPES + ")"); 1.1342 + 1.1343 + db.execSQL("DROP TABLE " + TABLE_DUPES); 1.1344 + } 1.1345 + 1.1346 + private void upgradeDatabaseFrom8to9(SQLiteDatabase db) { 1.1347 + createOrUpdateSpecialFolder(db, Bookmarks.READING_LIST_FOLDER_GUID, 1.1348 + R.string.bookmarks_folder_reading_list, 5); 1.1349 + 1.1350 + debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1351 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1352 + 1.1353 + createCombinedWithImagesViewOn9(db); 1.1354 + } 1.1355 + 1.1356 + private void upgradeDatabaseFrom9to10(SQLiteDatabase db) { 1.1357 + debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1358 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1359 + 1.1360 + createCombinedWithImagesViewOn10(db); 1.1361 + } 1.1362 + 1.1363 + private void upgradeDatabaseFrom10to11(SQLiteDatabase db) { 1.1364 + debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1365 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1366 + 1.1367 + db.execSQL("CREATE INDEX bookmarks_type_deleted_index ON " + TABLE_BOOKMARKS + "(" 1.1368 + + Bookmarks.TYPE + ", " + Bookmarks.IS_DELETED + ")"); 1.1369 + 1.1370 + createCombinedWithImagesViewOn11(db); 1.1371 + } 1.1372 + 1.1373 + private void upgradeDatabaseFrom11to12(SQLiteDatabase db) { 1.1374 + debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1375 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1376 + 1.1377 + createCombinedViewOn12(db); 1.1378 + } 1.1379 + 1.1380 + private void upgradeDatabaseFrom12to13(SQLiteDatabase db) { 1.1381 + // Update images table with favicon URLs 1.1382 + SQLiteDatabase faviconsDb = null; 1.1383 + Cursor c = null; 1.1384 + try { 1.1385 + final String FAVICON_TABLE = "favicon_urls"; 1.1386 + final String FAVICON_URL = "favicon_url"; 1.1387 + final String FAVICON_PAGE = "page_url"; 1.1388 + 1.1389 + String dbPath = mContext.getDatabasePath(Obsolete.FAVICON_DB).getPath(); 1.1390 + faviconsDb = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY); 1.1391 + String[] columns = new String[] { FAVICON_URL, FAVICON_PAGE }; 1.1392 + c = faviconsDb.query(FAVICON_TABLE, columns, null, null, null, null, null, null); 1.1393 + int faviconIndex = c.getColumnIndexOrThrow(FAVICON_URL); 1.1394 + int pageIndex = c.getColumnIndexOrThrow(FAVICON_PAGE); 1.1395 + while (c.moveToNext()) { 1.1396 + ContentValues values = new ContentValues(1); 1.1397 + String faviconUrl = c.getString(faviconIndex); 1.1398 + String pageUrl = c.getString(pageIndex); 1.1399 + values.put(FAVICON_URL, faviconUrl); 1.1400 + db.update(Obsolete.TABLE_IMAGES, values, Obsolete.Images.URL + " = ?", new String[] { pageUrl }); 1.1401 + } 1.1402 + } catch (SQLException e) { 1.1403 + // If we can't read from the database for some reason, we won't 1.1404 + // be able to import the favicon URLs. This isn't a fatal 1.1405 + // error, so continue the upgrade. 1.1406 + Log.e(LOGTAG, "Exception importing from " + Obsolete.FAVICON_DB, e); 1.1407 + } finally { 1.1408 + if (c != null) 1.1409 + c.close(); 1.1410 + if (faviconsDb != null) 1.1411 + faviconsDb.close(); 1.1412 + } 1.1413 + 1.1414 + createFaviconsTable(db); 1.1415 + 1.1416 + // Import favicons into the favicons table 1.1417 + db.execSQL("ALTER TABLE " + TABLE_HISTORY 1.1418 + + " ADD COLUMN " + History.FAVICON_ID + " INTEGER"); 1.1419 + db.execSQL("ALTER TABLE " + TABLE_BOOKMARKS 1.1420 + + " ADD COLUMN " + Bookmarks.FAVICON_ID + " INTEGER"); 1.1421 + 1.1422 + try { 1.1423 + c = db.query(Obsolete.TABLE_IMAGES, 1.1424 + new String[] { 1.1425 + Obsolete.Images.URL, 1.1426 + Obsolete.Images.FAVICON_URL, 1.1427 + Obsolete.Images.FAVICON, 1.1428 + Obsolete.Images.DATE_MODIFIED, 1.1429 + Obsolete.Images.DATE_CREATED 1.1430 + }, 1.1431 + Obsolete.Images.FAVICON + " IS NOT NULL", 1.1432 + null, null, null, null); 1.1433 + 1.1434 + while (c.moveToNext()) { 1.1435 + long faviconId = -1; 1.1436 + int faviconUrlIndex = c.getColumnIndexOrThrow(Obsolete.Images.FAVICON_URL); 1.1437 + String faviconUrl = null; 1.1438 + if (!c.isNull(faviconUrlIndex)) { 1.1439 + faviconUrl = c.getString(faviconUrlIndex); 1.1440 + Cursor c2 = null; 1.1441 + try { 1.1442 + c2 = db.query(TABLE_FAVICONS, 1.1443 + new String[] { Favicons._ID }, 1.1444 + Favicons.URL + " = ?", 1.1445 + new String[] { faviconUrl }, 1.1446 + null, null, null); 1.1447 + if (c2.moveToFirst()) { 1.1448 + faviconId = c2.getLong(c2.getColumnIndexOrThrow(Favicons._ID)); 1.1449 + } 1.1450 + } finally { 1.1451 + if (c2 != null) 1.1452 + c2.close(); 1.1453 + } 1.1454 + } 1.1455 + 1.1456 + if (faviconId == -1) { 1.1457 + ContentValues values = new ContentValues(4); 1.1458 + values.put(Favicons.URL, faviconUrl); 1.1459 + values.put(Favicons.DATA, c.getBlob(c.getColumnIndexOrThrow(Obsolete.Images.FAVICON))); 1.1460 + values.put(Favicons.DATE_MODIFIED, c.getLong(c.getColumnIndexOrThrow(Obsolete.Images.DATE_MODIFIED))); 1.1461 + values.put(Favicons.DATE_CREATED, c.getLong(c.getColumnIndexOrThrow(Obsolete.Images.DATE_CREATED))); 1.1462 + faviconId = db.insert(TABLE_FAVICONS, null, values); 1.1463 + } 1.1464 + 1.1465 + ContentValues values = new ContentValues(1); 1.1466 + values.put(FaviconColumns.FAVICON_ID, faviconId); 1.1467 + db.update(TABLE_HISTORY, values, History.URL + " = ?", 1.1468 + new String[] { c.getString(c.getColumnIndexOrThrow(Obsolete.Images.URL)) }); 1.1469 + db.update(TABLE_BOOKMARKS, values, Bookmarks.URL + " = ?", 1.1470 + new String[] { c.getString(c.getColumnIndexOrThrow(Obsolete.Images.URL)) }); 1.1471 + } 1.1472 + } finally { 1.1473 + if (c != null) 1.1474 + c.close(); 1.1475 + } 1.1476 + 1.1477 + createThumbnailsTable(db); 1.1478 + 1.1479 + // Import thumbnails into the thumbnails table 1.1480 + db.execSQL("INSERT INTO " + TABLE_THUMBNAILS + " (" 1.1481 + + Thumbnails.URL + ", " 1.1482 + + Thumbnails.DATA + ") " 1.1483 + + "SELECT " + Obsolete.Images.URL + ", " + Obsolete.Images.THUMBNAIL 1.1484 + + " FROM " + Obsolete.TABLE_IMAGES 1.1485 + + " WHERE " + Obsolete.Images.THUMBNAIL + " IS NOT NULL"); 1.1486 + 1.1487 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); 1.1488 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); 1.1489 + db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); 1.1490 + db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED); 1.1491 + 1.1492 + createBookmarksWithFaviconsView(db); 1.1493 + createHistoryWithFaviconsView(db); 1.1494 + createCombinedViewOn13(db); 1.1495 + 1.1496 + db.execSQL("DROP TABLE IF EXISTS " + Obsolete.TABLE_IMAGES); 1.1497 + } 1.1498 + 1.1499 + private void upgradeDatabaseFrom13to14(SQLiteDatabase db) { 1.1500 + createOrUpdateSpecialFolder(db, Bookmarks.PINNED_FOLDER_GUID, 1.1501 + R.string.bookmarks_folder_pinned, 6); 1.1502 + } 1.1503 + 1.1504 + private void upgradeDatabaseFrom14to15(SQLiteDatabase db) { 1.1505 + Cursor c = null; 1.1506 + try { 1.1507 + // Get all the pinned bookmarks 1.1508 + c = db.query(TABLE_BOOKMARKS, 1.1509 + new String[] { Bookmarks._ID, Bookmarks.URL }, 1.1510 + Bookmarks.PARENT + " = ?", 1.1511 + new String[] { Integer.toString(Bookmarks.FIXED_PINNED_LIST_ID) }, 1.1512 + null, null, null); 1.1513 + 1.1514 + while (c.moveToNext()) { 1.1515 + // Check if this URL can be parsed as a URI with a valid scheme. 1.1516 + String url = c.getString(c.getColumnIndexOrThrow(Bookmarks.URL)); 1.1517 + if (Uri.parse(url).getScheme() != null) { 1.1518 + continue; 1.1519 + } 1.1520 + 1.1521 + // If it can't, update the URL to be an encoded "user-entered" value. 1.1522 + ContentValues values = new ContentValues(1); 1.1523 + String newUrl = Uri.fromParts("user-entered", url, null).toString(); 1.1524 + values.put(Bookmarks.URL, newUrl); 1.1525 + db.update(TABLE_BOOKMARKS, values, Bookmarks._ID + " = ?", 1.1526 + new String[] { Integer.toString(c.getInt(c.getColumnIndexOrThrow(Bookmarks._ID))) }); 1.1527 + } 1.1528 + } finally { 1.1529 + if (c != null) { 1.1530 + c.close(); 1.1531 + } 1.1532 + } 1.1533 + } 1.1534 + 1.1535 + private void upgradeDatabaseFrom15to16(SQLiteDatabase db) { 1.1536 + db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED); 1.1537 + db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED_WITH_FAVICONS); 1.1538 + 1.1539 + createCombinedViewOn16(db); 1.1540 + } 1.1541 + 1.1542 + private void upgradeDatabaseFrom16to17(SQLiteDatabase db) { 1.1543 + // Purge any 0-byte favicons/thumbnails 1.1544 + try { 1.1545 + db.execSQL("DELETE FROM " + TABLE_FAVICONS + 1.1546 + " WHERE length(" + Favicons.DATA + ") = 0"); 1.1547 + db.execSQL("DELETE FROM " + TABLE_THUMBNAILS + 1.1548 + " WHERE length(" + Thumbnails.DATA + ") = 0"); 1.1549 + } catch (SQLException e) { 1.1550 + Log.e(LOGTAG, "Error purging invalid favicons or thumbnails", e); 1.1551 + } 1.1552 + } 1.1553 + 1.1554 + /* 1.1555 + * Moves reading list items from 'bookmarks' table to 'reading_list' table. Uses the 1.1556 + * same item GUID. 1.1557 + */ 1.1558 + private void upgradeDatabaseFrom17to18(SQLiteDatabase db) { 1.1559 + debug("Moving reading list items from 'bookmarks' table to 'reading_list' table"); 1.1560 + 1.1561 + final String selection = Bookmarks.PARENT + " = ? AND " + Bookmarks.IS_DELETED + " = ? "; 1.1562 + final String[] selectionArgs = { String.valueOf(Bookmarks.FIXED_READING_LIST_ID), "0" }; 1.1563 + final String[] projection = { Bookmarks._ID, 1.1564 + Bookmarks.GUID, 1.1565 + Bookmarks.URL, 1.1566 + Bookmarks.DATE_MODIFIED, 1.1567 + Bookmarks.DATE_CREATED, 1.1568 + Bookmarks.TITLE }; 1.1569 + Cursor cursor = null; 1.1570 + try { 1.1571 + // Start transaction 1.1572 + db.beginTransaction(); 1.1573 + 1.1574 + // Create 'reading_list' table 1.1575 + createReadingListTable(db); 1.1576 + 1.1577 + // Get all the reading list items from bookmarks table 1.1578 + cursor = db.query(TABLE_BOOKMARKS, projection, selection, selectionArgs, 1.1579 + null, null, null); 1.1580 + 1.1581 + // Insert reading list items into reading_list table 1.1582 + while (cursor.moveToNext()) { 1.1583 + debug(DatabaseUtils.dumpCurrentRowToString(cursor)); 1.1584 + ContentValues values = new ContentValues(); 1.1585 + DatabaseUtils.cursorStringToContentValues(cursor, Bookmarks.URL, values, ReadingListItems.URL); 1.1586 + DatabaseUtils.cursorStringToContentValues(cursor, Bookmarks.GUID, values, ReadingListItems.GUID); 1.1587 + DatabaseUtils.cursorStringToContentValues(cursor, Bookmarks.TITLE, values, ReadingListItems.TITLE); 1.1588 + DatabaseUtils.cursorLongToContentValues(cursor, Bookmarks.DATE_CREATED, values, ReadingListItems.DATE_CREATED); 1.1589 + DatabaseUtils.cursorLongToContentValues(cursor, Bookmarks.DATE_MODIFIED, values, ReadingListItems.DATE_MODIFIED); 1.1590 + 1.1591 + db.insertOrThrow(TABLE_READING_LIST, null, values); 1.1592 + } 1.1593 + 1.1594 + // Delete reading list items from bookmarks table 1.1595 + db.delete(TABLE_BOOKMARKS, 1.1596 + Bookmarks.PARENT + " = ? ", 1.1597 + new String[] { String.valueOf(Bookmarks.FIXED_READING_LIST_ID) }); 1.1598 + 1.1599 + // Delete reading list special folder 1.1600 + db.delete(TABLE_BOOKMARKS, 1.1601 + Bookmarks._ID + " = ? ", 1.1602 + new String[] { String.valueOf(Bookmarks.FIXED_READING_LIST_ID) }); 1.1603 + // Done 1.1604 + db.setTransactionSuccessful(); 1.1605 + 1.1606 + } catch (SQLException e) { 1.1607 + Log.e(LOGTAG, "Error migrating reading list items", e); 1.1608 + } finally { 1.1609 + if (cursor != null) { 1.1610 + cursor.close(); 1.1611 + } 1.1612 + db.endTransaction(); 1.1613 + } 1.1614 + } 1.1615 + 1.1616 + @Override 1.1617 + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 1.1618 + debug("Upgrading browser.db: " + db.getPath() + " from " + 1.1619 + oldVersion + " to " + newVersion); 1.1620 + 1.1621 + // We have to do incremental upgrades until we reach the current 1.1622 + // database schema version. 1.1623 + for (int v = oldVersion + 1; v <= newVersion; v++) { 1.1624 + switch(v) { 1.1625 + case 2: 1.1626 + upgradeDatabaseFrom1to2(db); 1.1627 + break; 1.1628 + 1.1629 + case 3: 1.1630 + upgradeDatabaseFrom2to3(db); 1.1631 + break; 1.1632 + 1.1633 + case 4: 1.1634 + upgradeDatabaseFrom3to4(db); 1.1635 + break; 1.1636 + 1.1637 + case 5: 1.1638 + upgradeDatabaseFrom4to5(db); 1.1639 + break; 1.1640 + 1.1641 + case 6: 1.1642 + upgradeDatabaseFrom5to6(db); 1.1643 + break; 1.1644 + 1.1645 + case 7: 1.1646 + upgradeDatabaseFrom6to7(db); 1.1647 + break; 1.1648 + 1.1649 + case 8: 1.1650 + upgradeDatabaseFrom7to8(db); 1.1651 + break; 1.1652 + 1.1653 + case 9: 1.1654 + upgradeDatabaseFrom8to9(db); 1.1655 + break; 1.1656 + 1.1657 + case 10: 1.1658 + upgradeDatabaseFrom9to10(db); 1.1659 + break; 1.1660 + 1.1661 + case 11: 1.1662 + upgradeDatabaseFrom10to11(db); 1.1663 + break; 1.1664 + 1.1665 + case 12: 1.1666 + upgradeDatabaseFrom11to12(db); 1.1667 + break; 1.1668 + 1.1669 + case 13: 1.1670 + upgradeDatabaseFrom12to13(db); 1.1671 + break; 1.1672 + 1.1673 + case 14: 1.1674 + upgradeDatabaseFrom13to14(db); 1.1675 + break; 1.1676 + 1.1677 + case 15: 1.1678 + upgradeDatabaseFrom14to15(db); 1.1679 + break; 1.1680 + 1.1681 + case 16: 1.1682 + upgradeDatabaseFrom15to16(db); 1.1683 + break; 1.1684 + 1.1685 + case 17: 1.1686 + upgradeDatabaseFrom16to17(db); 1.1687 + break; 1.1688 + 1.1689 + case 18: 1.1690 + upgradeDatabaseFrom17to18(db); 1.1691 + break; 1.1692 + } 1.1693 + } 1.1694 + 1.1695 + // If an upgrade after 12->13 fails, the entire upgrade is rolled 1.1696 + // back, but we can't undo the deletion of favicon_urls.db if we 1.1697 + // delete this in step 13; therefore, we wait until all steps are 1.1698 + // complete before removing it. 1.1699 + if (oldVersion < 13 && newVersion >= 13 1.1700 + && mContext.getDatabasePath(Obsolete.FAVICON_DB).exists() 1.1701 + && !mContext.deleteDatabase(Obsolete.FAVICON_DB)) { 1.1702 + throw new SQLException("Could not delete " + Obsolete.FAVICON_DB); 1.1703 + } 1.1704 + } 1.1705 + 1.1706 + @Override 1.1707 + public void onOpen(SQLiteDatabase db) { 1.1708 + debug("Opening browser.db: " + db.getPath()); 1.1709 + 1.1710 + Cursor cursor = null; 1.1711 + try { 1.1712 + cursor = db.rawQuery("PRAGMA foreign_keys=ON", null); 1.1713 + } finally { 1.1714 + if (cursor != null) 1.1715 + cursor.close(); 1.1716 + } 1.1717 + cursor = null; 1.1718 + try { 1.1719 + cursor = db.rawQuery("PRAGMA synchronous=NORMAL", null); 1.1720 + } finally { 1.1721 + if (cursor != null) 1.1722 + cursor.close(); 1.1723 + } 1.1724 + 1.1725 + // From Honeycomb on, it's possible to run several db 1.1726 + // commands in parallel using multiple connections. 1.1727 + if (Build.VERSION.SDK_INT >= 11) { 1.1728 + db.enableWriteAheadLogging(); 1.1729 + db.setLockingEnabled(false); 1.1730 + } else { 1.1731 + // Pre-Honeycomb, we can do some lesser optimizations. 1.1732 + cursor = null; 1.1733 + try { 1.1734 + cursor = db.rawQuery("PRAGMA journal_mode=PERSIST", null); 1.1735 + } finally { 1.1736 + if (cursor != null) 1.1737 + cursor.close(); 1.1738 + } 1.1739 + } 1.1740 + } 1.1741 + 1.1742 + static final String qualifyColumn(String table, String column) { 1.1743 + return DBUtils.qualifyColumn(table, column); 1.1744 + } 1.1745 + 1.1746 + // Calculate these once, at initialization. isLoggable is too expensive to 1.1747 + // have in-line in each log call. 1.1748 + private static boolean logDebug = Log.isLoggable(LOGTAG, Log.DEBUG); 1.1749 + private static boolean logVerbose = Log.isLoggable(LOGTAG, Log.VERBOSE); 1.1750 + protected static void trace(String message) { 1.1751 + if (logVerbose) { 1.1752 + Log.v(LOGTAG, message); 1.1753 + } 1.1754 + } 1.1755 + 1.1756 + protected static void debug(String message) { 1.1757 + if (logDebug) { 1.1758 + Log.d(LOGTAG, message); 1.1759 + } 1.1760 + } 1.1761 + 1.1762 + private Integer getMobileFolderId(SQLiteDatabase db) { 1.1763 + Cursor c = null; 1.1764 + 1.1765 + try { 1.1766 + c = db.query(TABLE_BOOKMARKS, 1.1767 + mobileIdColumns, 1.1768 + Bookmarks.GUID + " = ?", 1.1769 + mobileIdSelectionArgs, 1.1770 + null, null, null); 1.1771 + 1.1772 + if (c == null || !c.moveToFirst()) 1.1773 + return null; 1.1774 + 1.1775 + return c.getInt(c.getColumnIndex(Bookmarks._ID)); 1.1776 + } finally { 1.1777 + if (c != null) 1.1778 + c.close(); 1.1779 + } 1.1780 + } 1.1781 + 1.1782 + private long insertFavicon(SQLiteDatabase db, ContentValues values) { 1.1783 + // This method is a dupicate of BrowserProvider.insertFavicon. 1.1784 + // If changes are needed, please update both 1.1785 + String faviconUrl = values.getAsString(Favicons.URL); 1.1786 + String pageUrl = null; 1.1787 + long faviconId; 1.1788 + 1.1789 + trace("Inserting favicon for URL: " + faviconUrl); 1.1790 + 1.1791 + DBUtils.stripEmptyByteArray(values, Favicons.DATA); 1.1792 + 1.1793 + // Extract the page URL from the ContentValues 1.1794 + if (values.containsKey(Favicons.PAGE_URL)) { 1.1795 + pageUrl = values.getAsString(Favicons.PAGE_URL); 1.1796 + values.remove(Favicons.PAGE_URL); 1.1797 + } 1.1798 + 1.1799 + // If no URL is provided, insert using the default one. 1.1800 + if (TextUtils.isEmpty(faviconUrl) && !TextUtils.isEmpty(pageUrl)) { 1.1801 + values.put(Favicons.URL, org.mozilla.gecko.favicons.Favicons.guessDefaultFaviconURL(pageUrl)); 1.1802 + } 1.1803 + 1.1804 + long now = System.currentTimeMillis(); 1.1805 + values.put(Favicons.DATE_CREATED, now); 1.1806 + values.put(Favicons.DATE_MODIFIED, now); 1.1807 + faviconId = db.insertOrThrow(TABLE_FAVICONS, null, values); 1.1808 + 1.1809 + if (pageUrl != null) { 1.1810 + ContentValues updateValues = new ContentValues(1); 1.1811 + updateValues.put(FaviconColumns.FAVICON_ID, faviconId); 1.1812 + db.update(TABLE_HISTORY, 1.1813 + updateValues, 1.1814 + History.URL + " = ?", 1.1815 + new String[] { pageUrl }); 1.1816 + db.update(TABLE_BOOKMARKS, 1.1817 + updateValues, 1.1818 + Bookmarks.URL + " = ?", 1.1819 + new String[] { pageUrl }); 1.1820 + } 1.1821 + 1.1822 + return faviconId; 1.1823 + } 1.1824 + 1.1825 + private interface BookmarkMigrator { 1.1826 + public void updateForNewTable(ContentValues bookmark); 1.1827 + } 1.1828 + 1.1829 + private class BookmarkMigrator3to4 implements BookmarkMigrator { 1.1830 + @Override 1.1831 + public void updateForNewTable(ContentValues bookmark) { 1.1832 + Integer isFolder = bookmark.getAsInteger("folder"); 1.1833 + if (isFolder == null || isFolder != 1) { 1.1834 + bookmark.put(Bookmarks.TYPE, Bookmarks.TYPE_BOOKMARK); 1.1835 + } else { 1.1836 + bookmark.put(Bookmarks.TYPE, Bookmarks.TYPE_FOLDER); 1.1837 + } 1.1838 + 1.1839 + bookmark.remove("folder"); 1.1840 + } 1.1841 + } 1.1842 +} 1.1843 \ No newline at end of file