mobile/android/base/db/BrowserDatabaseHelper.java

changeset 0
6474c204b198
     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

mercurial