mobile/android/base/db/TabsProvider.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 package org.mozilla.gecko.db;
     7 import java.util.Collections;
     8 import java.util.HashMap;
     9 import java.util.Map;
    11 import org.mozilla.gecko.db.BrowserContract.Clients;
    12 import org.mozilla.gecko.db.BrowserContract.Tabs;
    14 import android.content.ContentUris;
    15 import android.content.ContentValues;
    16 import android.content.Context;
    17 import android.content.UriMatcher;
    18 import android.database.Cursor;
    19 import android.database.sqlite.SQLiteDatabase;
    20 import android.database.sqlite.SQLiteOpenHelper;
    21 import android.database.sqlite.SQLiteQueryBuilder;
    22 import android.net.Uri;
    23 import android.text.TextUtils;
    25 public class TabsProvider extends PerProfileDatabaseProvider<TabsProvider.TabsDatabaseHelper> {
    26     static final String DATABASE_NAME = "tabs.db";
    28     static final int DATABASE_VERSION = 2;
    30     static final String TABLE_TABS = "tabs";
    31     static final String TABLE_CLIENTS = "clients";
    33     static final int TABS = 600;
    34     static final int TABS_ID = 601;
    35     static final int CLIENTS = 602;
    36     static final int CLIENTS_ID = 603;
    38     static final String DEFAULT_TABS_SORT_ORDER = Clients.LAST_MODIFIED + " DESC, " + Tabs.LAST_USED + " DESC";
    39     static final String DEFAULT_CLIENTS_SORT_ORDER = Clients.LAST_MODIFIED + " DESC";
    41     static final String INDEX_TABS_GUID = "tabs_guid_index";
    42     static final String INDEX_TABS_POSITION = "tabs_position_index";
    43     static final String INDEX_CLIENTS_GUID = "clients_guid_index";
    45     static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
    47     static final Map<String, String> TABS_PROJECTION_MAP;
    48     static final Map<String, String> CLIENTS_PROJECTION_MAP;
    50     static {
    51         URI_MATCHER.addURI(BrowserContract.TABS_AUTHORITY, "tabs", TABS);
    52         URI_MATCHER.addURI(BrowserContract.TABS_AUTHORITY, "tabs/#", TABS_ID);
    53         URI_MATCHER.addURI(BrowserContract.TABS_AUTHORITY, "clients", CLIENTS);
    54         URI_MATCHER.addURI(BrowserContract.TABS_AUTHORITY, "clients/#", CLIENTS_ID);
    56         HashMap<String, String> map;
    58         map = new HashMap<String, String>();
    59         map.put(Tabs._ID, Tabs._ID);
    60         map.put(Tabs.TITLE, Tabs.TITLE);
    61         map.put(Tabs.URL, Tabs.URL);
    62         map.put(Tabs.HISTORY, Tabs.HISTORY);
    63         map.put(Tabs.FAVICON, Tabs.FAVICON);
    64         map.put(Tabs.LAST_USED, Tabs.LAST_USED);
    65         map.put(Tabs.POSITION, Tabs.POSITION);
    66         map.put(Clients.GUID, Clients.GUID);
    67         map.put(Clients.NAME, Clients.NAME);
    68         map.put(Clients.LAST_MODIFIED, Clients.LAST_MODIFIED);
    69         TABS_PROJECTION_MAP = Collections.unmodifiableMap(map);
    71         map = new HashMap<String, String>();
    72         map.put(Clients.GUID, Clients.GUID);
    73         map.put(Clients.NAME, Clients.NAME);
    74         map.put(Clients.LAST_MODIFIED, Clients.LAST_MODIFIED);
    75         CLIENTS_PROJECTION_MAP = Collections.unmodifiableMap(map);
    76     }
    78     private static final String selectColumn(String table, String column) {
    79         return table + "." + column + " = ?";
    80     }
    82     final class TabsDatabaseHelper extends SQLiteOpenHelper {
    83         public TabsDatabaseHelper(Context context, String databasePath) {
    84             super(context, databasePath, null, DATABASE_VERSION);
    85         }
    87         @Override
    88         public void onCreate(SQLiteDatabase db) {
    89             debug("Creating tabs.db: " + db.getPath());
    90             debug("Creating " + TABLE_TABS + " table");
    92             // Table for each tab on any client.
    93             db.execSQL("CREATE TABLE " + TABLE_TABS + "(" +
    94                        Tabs._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
    95                        Tabs.CLIENT_GUID + " TEXT," +
    96                        Tabs.TITLE + " TEXT," +
    97                        Tabs.URL + " TEXT," +
    98                        Tabs.HISTORY + " TEXT," +
    99                        Tabs.FAVICON + " TEXT," +
   100                        Tabs.LAST_USED + " INTEGER," +
   101                        Tabs.POSITION + " INTEGER" +
   102                        ");");
   104             // Indices on CLIENT_GUID and POSITION.
   105             db.execSQL("CREATE INDEX " + INDEX_TABS_GUID +
   106                        " ON " + TABLE_TABS + "(" + Tabs.CLIENT_GUID + ")");
   107             db.execSQL("CREATE INDEX " + INDEX_TABS_POSITION +
   108                        " ON " + TABLE_TABS + "(" + Tabs.POSITION + ")");
   110             debug("Creating " + TABLE_CLIENTS + " table");
   112             // Table for client's name-guid mapping.
   113             db.execSQL("CREATE TABLE " + TABLE_CLIENTS + "(" +
   114                        Clients.GUID + " TEXT PRIMARY KEY," +
   115                        Clients.NAME + " TEXT," +
   116                        Clients.LAST_MODIFIED + " INTEGER" +
   117                        ");");
   119             // Index on GUID.
   120             db.execSQL("CREATE INDEX " + INDEX_CLIENTS_GUID +
   121                        " ON " + TABLE_CLIENTS + "(" + Clients.GUID + ")");
   123             createLocalClient(db);
   124         }
   126         // Insert a client row for our local Fennec client.
   127         private void createLocalClient(SQLiteDatabase db) {
   128             debug("Inserting local Fennec client into " + TABLE_CLIENTS + " table");
   130             ContentValues values = new ContentValues();
   131             values.put(BrowserContract.Clients.LAST_MODIFIED, System.currentTimeMillis());
   132             db.insertOrThrow(TABLE_CLIENTS, null, values);
   133         }
   135         @Override
   136         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
   137             debug("Upgrading tabs.db: " + db.getPath() + " from " +
   138                   oldVersion + " to " + newVersion);
   140             // We have to do incremental upgrades until we reach the current
   141             // database schema version.
   142             for (int v = oldVersion + 1; v <= newVersion; v++) {
   143                 switch(v) {
   144                     case 2:
   145                         createLocalClient(db);
   146                         break;
   147                  }
   148              }
   149         }
   151         @Override
   152         public void onOpen(SQLiteDatabase db) {
   153             debug("Opening tabs.db: " + db.getPath());
   154             db.rawQuery("PRAGMA synchronous=OFF", null).close();
   156             if (shouldUseTransactions()) {
   157                 db.enableWriteAheadLogging();
   158                 db.setLockingEnabled(false);
   159                 return;
   160             }
   162             // If we're not using transactions (in particular, prior to
   163             // Honeycomb), then we can do some lesser optimizations.
   164             db.rawQuery("PRAGMA journal_mode=PERSIST", null).close();
   165         }
   166     }
   168     @Override
   169     public String getType(Uri uri) {
   170         final int match = URI_MATCHER.match(uri);
   172         trace("Getting URI type: " + uri);
   174         switch (match) {
   175             case TABS:
   176                 trace("URI is TABS: " + uri);
   177                 return Tabs.CONTENT_TYPE;
   179             case TABS_ID:
   180                 trace("URI is TABS_ID: " + uri);
   181                 return Tabs.CONTENT_ITEM_TYPE;
   183             case CLIENTS:
   184                 trace("URI is CLIENTS: " + uri);
   185                 return Clients.CONTENT_TYPE;
   187             case CLIENTS_ID:
   188                 trace("URI is CLIENTS_ID: " + uri);
   189                 return Clients.CONTENT_ITEM_TYPE;
   190         }
   192         debug("URI has unrecognized type: " + uri);
   194         return null;
   195     }
   197     @SuppressWarnings("fallthrough")
   198     public int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
   199         trace("Calling delete in transaction on URI: " + uri);
   201         final int match = URI_MATCHER.match(uri);
   202         int deleted = 0;
   204         switch (match) {
   205             case CLIENTS_ID:
   206                 trace("Delete on CLIENTS_ID: " + uri);
   207                 selection = DBUtils.concatenateWhere(selection, selectColumn(TABLE_CLIENTS, Clients.ROWID));
   208                 selectionArgs = DBUtils.appendSelectionArgs(selectionArgs,
   209                         new String[] { Long.toString(ContentUris.parseId(uri)) });
   210                 // fall through
   211             case CLIENTS:
   212                 trace("Delete on CLIENTS: " + uri);
   213                 // Delete from both TABLE_TABS and TABLE_CLIENTS.
   214                 deleteValues(uri, selection, selectionArgs, TABLE_TABS);
   215                 deleted = deleteValues(uri, selection, selectionArgs, TABLE_CLIENTS);
   216                 break;
   218             case TABS_ID:
   219                 trace("Delete on TABS_ID: " + uri);
   220                 selection = DBUtils.concatenateWhere(selection, selectColumn(TABLE_TABS, Tabs._ID));
   221                 selectionArgs = DBUtils.appendSelectionArgs(selectionArgs,
   222                         new String[] { Long.toString(ContentUris.parseId(uri)) });
   223                 // fall through
   224             case TABS:
   225                 trace("Deleting on TABS: " + uri);
   226                 deleted = deleteValues(uri, selection, selectionArgs, TABLE_TABS);
   227                 break;
   229             default:
   230                 throw new UnsupportedOperationException("Unknown delete URI " + uri);
   231         }
   233         debug("Deleted " + deleted + " rows for URI: " + uri);
   235         return deleted;
   236     }
   238     public Uri insertInTransaction(Uri uri, ContentValues values) {
   239         trace("Calling insert in transaction on URI: " + uri);
   241         final SQLiteDatabase db = getWritableDatabase(uri);
   242         int match = URI_MATCHER.match(uri);
   243         long id = -1;
   245         switch (match) {
   246             case CLIENTS:
   247                 String guid = values.getAsString(Clients.GUID);
   248                 debug("Inserting client in database with GUID: " + guid);
   249                 id = db.insertOrThrow(TABLE_CLIENTS, Clients.GUID, values);
   250                 break;
   252             case TABS:
   253                 String url = values.getAsString(Tabs.URL);
   254                 debug("Inserting tab in database with URL: " + url);
   255                 id = db.insertOrThrow(TABLE_TABS, Tabs.TITLE, values);
   256                 break;
   258             default:
   259                 throw new UnsupportedOperationException("Unknown insert URI " + uri);
   260         }
   262         debug("Inserted ID in database: " + id);
   264         if (id >= 0)
   265             return ContentUris.withAppendedId(uri, id);
   267         return null;
   268     }
   270     public int updateInTransaction(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
   271         trace("Calling update in transaction on URI: " + uri);
   273         int match = URI_MATCHER.match(uri);
   274         int updated = 0;
   276         switch (match) {
   277             case CLIENTS_ID:
   278                 trace("Update on CLIENTS_ID: " + uri);
   279                 selection = DBUtils.concatenateWhere(selection, selectColumn(TABLE_CLIENTS, Clients.ROWID));
   280                 selectionArgs = DBUtils.appendSelectionArgs(selectionArgs,
   281                         new String[] { Long.toString(ContentUris.parseId(uri)) });
   282                 // fall through
   283             case CLIENTS:
   284                 trace("Update on CLIENTS: " + uri);
   285                 updated = updateValues(uri, values, selection, selectionArgs, TABLE_CLIENTS);
   286                 break;
   288             case TABS_ID:
   289                 trace("Update on TABS_ID: " + uri);
   290                 selection = DBUtils.concatenateWhere(selection, selectColumn(TABLE_TABS, Tabs._ID));
   291                 selectionArgs = DBUtils.appendSelectionArgs(selectionArgs,
   292                         new String[] { Long.toString(ContentUris.parseId(uri)) });
   293                 // fall through
   294             case TABS:
   295                 trace("Update on TABS: " + uri);
   296                 updated = updateValues(uri, values, selection, selectionArgs, TABLE_TABS);
   297                 break;
   299             default:
   300                 throw new UnsupportedOperationException("Unknown update URI " + uri);
   301         }
   303         debug("Updated " + updated + " rows for URI: " + uri);
   305         return updated;
   306     }
   308     @Override
   309     @SuppressWarnings("fallthrough")
   310     public Cursor query(Uri uri, String[] projection, String selection,
   311             String[] selectionArgs, String sortOrder) {
   312         SQLiteDatabase db = getReadableDatabase(uri);
   313         final int match = URI_MATCHER.match(uri);
   315         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
   316         String limit = uri.getQueryParameter(BrowserContract.PARAM_LIMIT);
   318         switch (match) {
   319             case TABS_ID:
   320                 trace("Query is on TABS_ID: " + uri);
   321                 selection = DBUtils.concatenateWhere(selection, selectColumn(TABLE_TABS, Tabs._ID));
   322                 selectionArgs = DBUtils.appendSelectionArgs(selectionArgs,
   323                         new String[] { Long.toString(ContentUris.parseId(uri)) });
   324                 // fall through
   325             case TABS:
   326                 trace("Query is on TABS: " + uri);
   327                 if (TextUtils.isEmpty(sortOrder)) {
   328                     sortOrder = DEFAULT_TABS_SORT_ORDER;
   329                 } else {
   330                     debug("Using sort order " + sortOrder + ".");
   331                 }
   333                 qb.setProjectionMap(TABS_PROJECTION_MAP);
   334                 qb.setTables(TABLE_TABS + " LEFT OUTER JOIN " + TABLE_CLIENTS + " ON (" + TABLE_TABS + "." + Tabs.CLIENT_GUID + " = " + TABLE_CLIENTS + "." + Clients.GUID + ")");
   335                 break;
   337             case CLIENTS_ID:
   338                 trace("Query is on CLIENTS_ID: " + uri);
   339                 selection = DBUtils.concatenateWhere(selection, selectColumn(TABLE_CLIENTS, Clients.ROWID));
   340                 selectionArgs = DBUtils.appendSelectionArgs(selectionArgs,
   341                         new String[] { Long.toString(ContentUris.parseId(uri)) });
   342                 // fall through
   343             case CLIENTS:
   344                 trace("Query is on CLIENTS: " + uri);
   345                 if (TextUtils.isEmpty(sortOrder)) {
   346                     sortOrder = DEFAULT_CLIENTS_SORT_ORDER;
   347                 } else {
   348                     debug("Using sort order " + sortOrder + ".");
   349                 }
   351                 qb.setProjectionMap(CLIENTS_PROJECTION_MAP);
   352                 qb.setTables(TABLE_CLIENTS);
   353                 break;
   355             default:
   356                 throw new UnsupportedOperationException("Unknown query URI " + uri);
   357         }
   359         trace("Running built query.");
   360         final Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder, limit);
   361         cursor.setNotificationUri(getContext().getContentResolver(), BrowserContract.TABS_AUTHORITY_URI);
   363         return cursor;
   364     }
   366     int updateValues(Uri uri, ContentValues values, String selection, String[] selectionArgs, String table) {
   367         trace("Updating tabs on URI: " + uri);
   369         final SQLiteDatabase db = getWritableDatabase(uri);
   370         beginWrite(db);
   371         return db.update(table, values, selection, selectionArgs);
   372     }
   374     int deleteValues(Uri uri, String selection, String[] selectionArgs, String table) {
   375         debug("Deleting tabs for URI: " + uri);
   377         final SQLiteDatabase db = getWritableDatabase(uri);
   378         beginWrite(db);
   379         return db.delete(table, selection, selectionArgs);
   380     }
   382     @Override
   383     protected TabsDatabaseHelper createDatabaseHelper(Context context, String databasePath) {
   384         return new TabsDatabaseHelper(context, databasePath);
   385     }
   387     @Override
   388     protected String getDatabaseName() {
   389         return DATABASE_NAME;
   390     }
   391 }

mercurial