gecko/mobile/android/base/db/AndroidBrowserDB.java

429 lines
17 KiB
Java

/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.db;
import java.io.ByteArrayOutputStream;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.database.sqlite.SQLiteConstraintException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Build;
import android.provider.Browser;
import android.provider.Browser.BookmarkColumns;
import android.util.Log;
public class AndroidBrowserDB implements BrowserDB.BrowserDBIface {
private static final String LOGTAG = "AndroidBrowserDB";
private static final String URL_COLUMN_ID = "_id";
private static final String URL_COLUMN_THUMBNAIL = "thumbnail";
// Only available on Android >= 11
private static final String URL_COLUMN_DELETED = "deleted";
private static final Uri BOOKMARKS_CONTENT_URI_POST_11 = Uri.parse("content://com.android.browser/bookmarks");
public void invalidateCachedState() {
// Do nothing
}
private Cursor filterAllSites(ContentResolver cr, String[] projection, CharSequence constraint, int limit, CharSequence urlFilter) {
Cursor c = cr.query(Browser.BOOKMARKS_URI,
projection,
// The length restriction on URL is for the same reason as in the general bookmark query
// (see comment earlier in this file).
(urlFilter != null ? "(" + Browser.BookmarkColumns.URL + " NOT LIKE ? ) AND " : "" ) +
"(" + Browser.BookmarkColumns.URL + " LIKE ? OR " + Browser.BookmarkColumns.TITLE + " LIKE ?)"
+ " AND LENGTH(" + Browser.BookmarkColumns.URL + ") > 0",
urlFilter == null ? new String[] {"%" + constraint.toString() + "%", "%" + constraint.toString() + "%"} :
new String[] {urlFilter.toString(), "%" + constraint.toString() + "%", "%" + constraint.toString() + "%"},
// ORDER BY is number of visits times a multiplier from 1 - 120 of how recently the site
// was accessed with a site accessed today getting 120 and a site accessed 119 or more
// days ago getting 1
Browser.BookmarkColumns.VISITS + " * MAX(1, (" +
Browser.BookmarkColumns.DATE + " - " + System.currentTimeMillis() + ") / 86400000 + 120) DESC LIMIT " + limit);
return new AndroidDBCursor(c);
}
public Cursor filter(ContentResolver cr, CharSequence constraint, int limit) {
return filterAllSites(cr,
new String[] { URL_COLUMN_ID,
BookmarkColumns.URL,
BookmarkColumns.TITLE,
BookmarkColumns.FAVICON },
constraint,
limit,
null);
}
public Cursor getTopSites(ContentResolver cr, int limit) {
return filterAllSites(cr,
new String[] { URL_COLUMN_ID,
BookmarkColumns.URL,
BookmarkColumns.TITLE,
URL_COLUMN_THUMBNAIL },
"",
limit,
BrowserDB.ABOUT_PAGES_URL_FILTER);
}
public void updateVisitedHistory(ContentResolver cr, String uri) {
Browser.updateVisitedHistory(cr, uri, true);
}
public void updateHistoryTitle(ContentResolver cr, String uri, String title) {
ContentValues values = new ContentValues();
values.put(Browser.BookmarkColumns.TITLE, title);
cr.update(Browser.BOOKMARKS_URI,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri });
}
public void updateHistoryEntry(ContentResolver cr, String uri, String title,
long date, int visits) {
int oldVisits = 0;
Cursor cursor = null;
try {
cursor = cr.query(Browser.BOOKMARKS_URI,
new String[] { Browser.BookmarkColumns.VISITS },
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri },
null);
if (cursor.moveToFirst()) {
oldVisits = cursor.getInt(0);
}
} finally {
if (cursor != null)
cursor.close();
}
ContentValues values = new ContentValues();
values.put(Browser.BookmarkColumns.DATE, date);
values.put(Browser.BookmarkColumns.VISITS, oldVisits + visits);
if (title != null) {
values.put(Browser.BookmarkColumns.TITLE, title);
}
cr.update(Browser.BOOKMARKS_URI,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri });
}
public Cursor getAllVisitedHistory(ContentResolver cr) {
Cursor c = cr.query(Browser.BOOKMARKS_URI,
new String[] { Browser.BookmarkColumns.URL },
Browser.BookmarkColumns.BOOKMARK + " = 0 AND " +
Browser.BookmarkColumns.VISITS + " > 0",
null,
null);
return new AndroidDBCursor(c);
}
public Cursor getRecentHistory(ContentResolver cr, int limit) {
Cursor c = cr.query(Browser.BOOKMARKS_URI,
new String[] { URL_COLUMN_ID,
BookmarkColumns.URL,
BookmarkColumns.TITLE,
BookmarkColumns.FAVICON,
BookmarkColumns.DATE,
BookmarkColumns.VISITS },
// Bookmarks that have not been visited have a date value
// of 0, so don't pick them up in the history view.
Browser.BookmarkColumns.DATE + " > 0",
null,
Browser.BookmarkColumns.DATE + " DESC LIMIT " + limit);
return new AndroidDBCursor(c);
}
public void removeHistoryEntry(ContentResolver cr, int id) {
// Not implemented
}
public void clearHistory(ContentResolver cr) {
Browser.clearHistory(cr);
}
public Cursor getBookmarksInFolder(ContentResolver cr, long folderId) {
Cursor c = cr.query(null, null, null, null, null);
return new AndroidDBCursor(c);
}
public Cursor isBookmarkQueryPre11(ContentResolver cr, String uri) {
return cr.query(Browser.BOOKMARKS_URI,
new String[] { BookmarkColumns.URL },
Browser.BookmarkColumns.URL + " = ? and " + Browser.BookmarkColumns.BOOKMARK + " = ?",
new String[] { uri, "1" },
Browser.BookmarkColumns.URL);
}
public Cursor isBookmarkQueryPost11(ContentResolver cr, String uri) {
return cr.query(BOOKMARKS_CONTENT_URI_POST_11,
new String[] { BookmarkColumns.URL },
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri },
Browser.BookmarkColumns.URL);
}
public boolean isBookmark(ContentResolver cr, String uri) {
Cursor cursor;
if (Build.VERSION.SDK_INT >= 11)
cursor = isBookmarkQueryPost11(cr, uri);
else
cursor = isBookmarkQueryPre11(cr, uri);
int count = cursor.getCount();
cursor.close();
return (count > 0);
}
public boolean isReadingListItem(ContentResolver cr, String uri) {
return false;
}
public String getUrlForKeyword(ContentResolver cr, String keyword) {
return null;
}
public void addBookmarkPre11(ContentResolver cr, String title, String uri) {
ContentValues values = new ContentValues();
values.put(Browser.BookmarkColumns.BOOKMARK, "1");
values.put(Browser.BookmarkColumns.TITLE, title);
values.put(Browser.BookmarkColumns.URL, uri);
int updated = cr.update(Browser.BOOKMARKS_URI,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri });
if (updated == 0)
cr.insert(Browser.BOOKMARKS_URI, values);
}
public void addBookmarkPost11(ContentResolver cr, String title, String uri) {
ContentValues values = new ContentValues();
values.put(Browser.BookmarkColumns.TITLE, title);
values.put(Browser.BookmarkColumns.URL, uri);
values.put(URL_COLUMN_DELETED, "0");
int updated = cr.update(BOOKMARKS_CONTENT_URI_POST_11,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri });
if (updated == 0)
cr.insert(BOOKMARKS_CONTENT_URI_POST_11, values);
}
public void addBookmark(ContentResolver cr, String title, String uri) {
if (Build.VERSION.SDK_INT >= 11)
addBookmarkPost11(cr, title, uri);
else
addBookmarkPre11(cr, title, uri);
}
public void updateBookmark(ContentResolver cr, int id, String uri, String title, String keyword) {
// Not implemented
}
public void removeBookmarkPre11(ContentResolver cr, String uri) {
ContentValues values = new ContentValues();
values.put(Browser.BookmarkColumns.BOOKMARK, "0");
cr.update(Browser.BOOKMARKS_URI,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri });
}
public void removeBookmarkPost11(ContentResolver cr, String uri) {
cr.delete(BOOKMARKS_CONTENT_URI_POST_11,
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri });
}
public void removeBookmark(ContentResolver cr, int id) {
// Not implemented
}
public void removeBookmarksWithURL(ContentResolver cr, String uri) {
if (Build.VERSION.SDK_INT >= 11)
removeBookmarkPost11(cr, uri);
else
removeBookmarkPre11(cr, uri);
}
public void addReadingListItem(ContentResolver cr, String title, String uri) {
// Do nothing
}
public void removeReadingListItemWithURL(ContentResolver cr, String uri) {
// Do nothing
}
public void registerBookmarkObserverPre11(ContentResolver cr, ContentObserver observer) {
cr.registerContentObserver(Browser.BOOKMARKS_URI, false, observer);
}
public void registerBookmarkObserverPost11(ContentResolver cr, ContentObserver observer) {
cr.registerContentObserver(BOOKMARKS_CONTENT_URI_POST_11, false, observer);
}
public void registerBookmarkObserver(ContentResolver cr, ContentObserver observer) {
if (Build.VERSION.SDK_INT >= 11)
registerBookmarkObserverPost11(cr, observer);
else
registerBookmarkObserverPre11(cr, observer);
}
public void registerHistoryObserver(ContentResolver cr, ContentObserver observer) {
// Not implemented
}
public BitmapDrawable getFaviconForUrl(ContentResolver cr, String uri) {
Cursor c = cr.query(Browser.BOOKMARKS_URI,
new String[] { Browser.BookmarkColumns.FAVICON },
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri },
null);
if (!c.moveToFirst()) {
c.close();
return null;
}
int faviconIndex = c.getColumnIndexOrThrow(Browser.BookmarkColumns.FAVICON);
byte[] b = c.getBlob(faviconIndex);
c.close();
if (b == null)
return null;
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
return new BitmapDrawable(bitmap);
}
public void updateFaviconForUrl(ContentResolver cr, String uri,
BitmapDrawable favicon) {
Bitmap bitmap = favicon.getBitmap();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
ContentValues values = new ContentValues();
values.put(Browser.BookmarkColumns.FAVICON, stream.toByteArray());
values.put(Browser.BookmarkColumns.URL, uri);
int updated = cr.update(Browser.BOOKMARKS_URI,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri });
if (updated == 0) {
try {
cr.insert(Browser.BOOKMARKS_URI, values);
} catch (SQLiteConstraintException e) {
// insert() mysteriously and intermittently fails with "error
// code 19: constraint failed" on some Honeycomb and ICS
// devices. Bookmark favicons are not a critical feature, so
// we can ignore this error for now. bug 711977; bug 712791
Log.e(LOGTAG,
String.format("Inserting favicon for \"%s\" failed with SQLiteConstraintException: %s",
uri, e.getMessage()));
}
}
}
public void updateThumbnailForUrl(ContentResolver cr, String uri,
BitmapDrawable thumbnail) {
Bitmap bitmap = thumbnail.getBitmap();
ContentValues values = new ContentValues();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
values.put(URL_COLUMN_THUMBNAIL, bos.toByteArray());
values.put(Browser.BookmarkColumns.URL, uri);
int updated = cr.update(Browser.BOOKMARKS_URI,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri });
if (updated == 0)
cr.insert(Browser.BOOKMARKS_URI, values);
}
public byte[] getThumbnailForUrl(ContentResolver cr, String uri) {
Cursor c = cr.query(Browser.BOOKMARKS_URI,
new String[] { URL_COLUMN_THUMBNAIL },
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri },
null);
if (!c.moveToFirst()) {
c.close();
return null;
}
int thumbnailIndex = c.getColumnIndexOrThrow(URL_COLUMN_THUMBNAIL);
byte[] b = c.getBlob(thumbnailIndex);
c.close();
return b;
}
private static class AndroidDBCursor extends CursorWrapper {
public AndroidDBCursor(Cursor c) {
super(c);
}
private String translateColumnName(String columnName) {
if (columnName.equals(BrowserDB.URLColumns.URL)) {
columnName = Browser.BookmarkColumns.URL;
} else if (columnName.equals(BrowserDB.URLColumns.TITLE)) {
columnName = Browser.BookmarkColumns.TITLE;
} else if (columnName.equals(BrowserDB.URLColumns.FAVICON)) {
columnName = Browser.BookmarkColumns.FAVICON;
} else if (columnName.equals(BrowserDB.URLColumns.THUMBNAIL)) {
columnName = URL_COLUMN_THUMBNAIL;
} else if (columnName.equals(BrowserDB.URLColumns.DATE_LAST_VISITED)) {
columnName = Browser.BookmarkColumns.DATE;
} else if (columnName.equals(BrowserDB.URLColumns.VISITS)) {
columnName = Browser.BookmarkColumns.VISITS;
}
return columnName;
}
@Override
public int getColumnIndex(String columnName) {
return super.getColumnIndex(translateColumnName(columnName));
}
@Override
public int getColumnIndexOrThrow(String columnName) {
return super.getColumnIndexOrThrow(translateColumnName(columnName));
}
}
}