SnapshotProvider.java revision 5eac0b68bf5f16db1bcc2980f1ccb148afde7d71
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.android.browser.provider; 17 18import android.content.ContentProvider; 19import android.content.ContentUris; 20import android.content.ContentValues; 21import android.content.Context; 22import android.content.UriMatcher; 23import android.database.Cursor; 24import android.database.DatabaseUtils; 25import android.database.sqlite.SQLiteDatabase; 26import android.database.sqlite.SQLiteOpenHelper; 27import android.database.sqlite.SQLiteQueryBuilder; 28import android.net.Uri; 29import android.os.FileUtils; 30import android.provider.BrowserContract; 31 32import java.io.File; 33 34public class SnapshotProvider extends ContentProvider { 35 36 public static interface Snapshots { 37 38 public static final Uri CONTENT_URI = Uri.withAppendedPath( 39 SnapshotProvider.AUTHORITY_URI, "snapshots"); 40 public static final String _ID = "_id"; 41 @Deprecated 42 public static final String VIEWSTATE = "view_state"; 43 public static final String BACKGROUND = "background"; 44 public static final String TITLE = "title"; 45 public static final String URL = "url"; 46 public static final String FAVICON = "favicon"; 47 public static final String THUMBNAIL = "thumbnail"; 48 public static final String DATE_CREATED = "date_created"; 49 public static final String VIEWSTATE_PATH = "viewstate_path"; 50 public static final String VIEWSTATE_SIZE = "viewstate_size"; 51 } 52 53 public static final String AUTHORITY = "com.android.browser.snapshots"; 54 public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); 55 56 static final String TABLE_SNAPSHOTS = "snapshots"; 57 static final int SNAPSHOTS = 10; 58 static final int SNAPSHOTS_ID = 11; 59 static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); 60 // Workaround that we can't remove the "NOT NULL" constraint on VIEWSTATE 61 static final byte[] NULL_BLOB_HACK = new byte[0]; 62 63 SnapshotDatabaseHelper mOpenHelper; 64 65 static { 66 URI_MATCHER.addURI(AUTHORITY, "snapshots", SNAPSHOTS); 67 URI_MATCHER.addURI(AUTHORITY, "snapshots/#", SNAPSHOTS_ID); 68 } 69 70 final static class SnapshotDatabaseHelper extends SQLiteOpenHelper { 71 72 static final String DATABASE_NAME = "snapshots.db"; 73 static final int DATABASE_VERSION = 3; 74 75 public SnapshotDatabaseHelper(Context context) { 76 super(context, DATABASE_NAME, null, DATABASE_VERSION); 77 } 78 79 @Override 80 public void onCreate(SQLiteDatabase db) { 81 db.execSQL("CREATE TABLE " + TABLE_SNAPSHOTS + "(" + 82 Snapshots._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 83 Snapshots.TITLE + " TEXT," + 84 Snapshots.URL + " TEXT NOT NULL," + 85 Snapshots.DATE_CREATED + " INTEGER," + 86 Snapshots.FAVICON + " BLOB," + 87 Snapshots.THUMBNAIL + " BLOB," + 88 Snapshots.BACKGROUND + " INTEGER," + 89 Snapshots.VIEWSTATE + " BLOB NOT NULL," + 90 Snapshots.VIEWSTATE_PATH + " TEXT," + 91 Snapshots.VIEWSTATE_SIZE + " INTEGER" + 92 ");"); 93 } 94 95 @Override 96 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 97 if (oldVersion < 2) { 98 db.execSQL("DROP TABLE " + TABLE_SNAPSHOTS); 99 onCreate(db); 100 } 101 if (oldVersion < 3) { 102 db.execSQL("ALTER TABLE " + TABLE_SNAPSHOTS + " ADD COLUMN " 103 + Snapshots.VIEWSTATE_PATH + " TEXT"); 104 db.execSQL("ALTER TABLE " + TABLE_SNAPSHOTS + " ADD COLUMN " 105 + Snapshots.VIEWSTATE_SIZE + " INTEGER"); 106 db.execSQL("UPDATE " + TABLE_SNAPSHOTS + " SET " 107 + Snapshots.VIEWSTATE_SIZE + " = length(" 108 + Snapshots.VIEWSTATE + ")"); 109 } 110 } 111 112 } 113 114 static File getOldDatabasePath(Context context) { 115 File dir = context.getExternalFilesDir(null); 116 return new File(dir, SnapshotDatabaseHelper.DATABASE_NAME); 117 } 118 119 private void migrateToDataFolder() { 120 File dbPath = getContext().getDatabasePath(SnapshotDatabaseHelper.DATABASE_NAME); 121 if (dbPath.exists()) return; 122 File oldPath = getOldDatabasePath(getContext()); 123 if (oldPath.exists()) { 124 // Try to move 125 if (!oldPath.renameTo(dbPath)) { 126 // Failed, do a copy 127 FileUtils.copyFile(oldPath, dbPath); 128 } 129 // Cleanup 130 oldPath.delete(); 131 } 132 } 133 134 @Override 135 public boolean onCreate() { 136 migrateToDataFolder(); 137 mOpenHelper = new SnapshotDatabaseHelper(getContext()); 138 return true; 139 } 140 141 SQLiteDatabase getWritableDatabase() { 142 return mOpenHelper.getWritableDatabase(); 143 } 144 145 SQLiteDatabase getReadableDatabase() { 146 return mOpenHelper.getReadableDatabase(); 147 } 148 149 @Override 150 public Cursor query(Uri uri, String[] projection, String selection, 151 String[] selectionArgs, String sortOrder) { 152 SQLiteDatabase db = getReadableDatabase(); 153 if (db == null) { 154 return null; 155 } 156 final int match = URI_MATCHER.match(uri); 157 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 158 String limit = uri.getQueryParameter(BrowserContract.PARAM_LIMIT); 159 switch (match) { 160 case SNAPSHOTS_ID: 161 selection = DatabaseUtils.concatenateWhere(selection, "_id=?"); 162 selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, 163 new String[] { Long.toString(ContentUris.parseId(uri)) }); 164 // fall through 165 case SNAPSHOTS: 166 qb.setTables(TABLE_SNAPSHOTS); 167 break; 168 169 default: 170 throw new UnsupportedOperationException("Unknown URL " + uri.toString()); 171 } 172 Cursor cursor = qb.query(db, projection, selection, selectionArgs, 173 null, null, sortOrder, limit); 174 cursor.setNotificationUri(getContext().getContentResolver(), 175 AUTHORITY_URI); 176 return cursor; 177 } 178 179 @Override 180 public String getType(Uri uri) { 181 return null; 182 } 183 184 @Override 185 public Uri insert(Uri uri, ContentValues values) { 186 SQLiteDatabase db = getWritableDatabase(); 187 if (db == null) { 188 return null; 189 } 190 int match = URI_MATCHER.match(uri); 191 long id = -1; 192 switch (match) { 193 case SNAPSHOTS: 194 if (!values.containsKey(Snapshots.VIEWSTATE)) { 195 values.put(Snapshots.VIEWSTATE, NULL_BLOB_HACK); 196 } 197 id = db.insert(TABLE_SNAPSHOTS, Snapshots.TITLE, values); 198 break; 199 default: 200 throw new UnsupportedOperationException("Unknown insert URI " + uri); 201 } 202 if (id < 0) { 203 return null; 204 } 205 Uri inserted = ContentUris.withAppendedId(uri, id); 206 getContext().getContentResolver().notifyChange(inserted, null, false); 207 return inserted; 208 } 209 210 static final String[] DELETE_PROJECTION = new String[] { 211 Snapshots.VIEWSTATE_PATH, 212 }; 213 private void deleteDataFiles(SQLiteDatabase db, String selection, 214 String[] selectionArgs) { 215 Cursor c = db.query(TABLE_SNAPSHOTS, DELETE_PROJECTION, selection, 216 selectionArgs, null, null, null); 217 final Context context = getContext(); 218 while (c.moveToNext()) { 219 File f = context.getFileStreamPath(c.getString(0)); 220 if (f.exists()) { 221 if (!f.delete()) { 222 f.deleteOnExit(); 223 } 224 } 225 } 226 c.close(); 227 } 228 229 @Override 230 public int delete(Uri uri, String selection, String[] selectionArgs) { 231 SQLiteDatabase db = getWritableDatabase(); 232 if (db == null) { 233 return 0; 234 } 235 int match = URI_MATCHER.match(uri); 236 int deleted = 0; 237 switch (match) { 238 case SNAPSHOTS_ID: { 239 selection = DatabaseUtils.concatenateWhere(selection, TABLE_SNAPSHOTS + "._id=?"); 240 selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, 241 new String[] { Long.toString(ContentUris.parseId(uri)) }); 242 // fall through 243 } 244 case SNAPSHOTS: 245 deleteDataFiles(db, selection, selectionArgs); 246 deleted = db.delete(TABLE_SNAPSHOTS, selection, selectionArgs); 247 break; 248 default: 249 throw new UnsupportedOperationException("Unknown delete URI " + uri); 250 } 251 if (deleted > 0) { 252 getContext().getContentResolver().notifyChange(uri, null, false); 253 } 254 return deleted; 255 } 256 257 @Override 258 public int update(Uri uri, ContentValues values, String selection, 259 String[] selectionArgs) { 260 throw new UnsupportedOperationException("not implemented"); 261 } 262 263} 264