Bug 785945 - Load favicon images asynchronously in the All Pages tab (r=mfinkle)

This commit is contained in:
Lucas Rocha 2012-10-31 12:34:32 +00:00
parent c0b03818fc
commit 0a22ff83f9
2 changed files with 152 additions and 4 deletions

View File

@ -7,8 +7,10 @@ package org.mozilla.gecko;
import org.mozilla.gecko.AwesomeBar.ContextMenuSubject; import org.mozilla.gecko.AwesomeBar.ContextMenuSubject;
import org.mozilla.gecko.db.BrowserContract.Combined; import org.mozilla.gecko.db.BrowserContract.Combined;
import org.mozilla.gecko.db.BrowserContract.Images;
import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.BrowserDB.URLColumns; import org.mozilla.gecko.db.BrowserDB.URLColumns;
import org.mozilla.gecko.util.GeckoAsyncTask;
import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.GeckoEventListener;
import org.json.JSONArray; import org.json.JSONArray;
@ -18,8 +20,13 @@ import org.json.JSONObject;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock; import android.os.SystemClock;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -49,6 +56,7 @@ import android.widget.TextView;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener { public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
public static final String LOGTAG = "ALL_PAGES"; public static final String LOGTAG = "ALL_PAGES";
@ -68,6 +76,11 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
private LinearLayout mAllPagesView; private LinearLayout mAllPagesView;
private boolean mAnimateSuggestions; private boolean mAnimateSuggestions;
private View mSuggestionsOptInPrompt; private View mSuggestionsOptInPrompt;
private Handler mHandler;
private static final int MESSAGE_LOAD_FAVICONS = 1;
private static final int MESSAGE_UPDATE_FAVICONS = 2;
private static final int DELAY_SHOW_THUMBNAILS = 550;
private class SearchEntryViewHolder { private class SearchEntryViewHolder {
public FlowLayout suggestionView; public FlowLayout suggestionView;
@ -122,9 +135,14 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
((Activity)mContext).registerForContextMenu(mView); ((Activity)mContext).registerForContextMenu(mView);
mView.setTag(TAG); mView.setTag(TAG);
AwesomeBarCursorAdapter adapter = getCursorAdapter(); AwesomeBarCursorAdapter adapter = getCursorAdapter();
((ListView)mView).setAdapter(adapter);
mView.setOnTouchListener(mListListener); ListView listView = (ListView) mView;
listView.setAdapter(adapter);
listView.setOnTouchListener(mListListener);
mHandler = new AllPagesHandler();
} }
return (ListView)mView; return (ListView)mView;
} }
@ -138,6 +156,10 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
Cursor cursor = adapter.getCursor(); Cursor cursor = adapter.getCursor();
if (cursor != null) if (cursor != null)
cursor.close(); cursor.close();
mHandler.removeMessages(MESSAGE_UPDATE_FAVICONS);
mHandler.removeMessages(MESSAGE_LOAD_FAVICONS);
mHandler = null;
} }
public void filter(String searchTerm) { public void filter(String searchTerm) {
@ -196,6 +218,8 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
Cursor c = BrowserDB.filter(getContentResolver(), constraint, MAX_RESULTS); Cursor c = BrowserDB.filter(getContentResolver(), constraint, MAX_RESULTS);
c.getCount(); c.getCount();
postLoadFavicons();
long end = SystemClock.uptimeMillis(); long end = SystemClock.uptimeMillis();
int time = (int)(end - start); int time = (int)(end - start);
Log.i(LOGTAG, "Got cursor in " + time + "ms"); Log.i(LOGTAG, "Got cursor in " + time + "ms");
@ -398,8 +422,8 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
updateTitle(viewHolder.titleView, cursor); updateTitle(viewHolder.titleView, cursor);
updateUrl(viewHolder.urlView, cursor); updateUrl(viewHolder.urlView, cursor);
updateFavicon(viewHolder.faviconView, cursor);
updateBookmarkIcon(viewHolder.bookmarkIconView, cursor); updateBookmarkIcon(viewHolder.bookmarkIconView, cursor);
displayFavicon(viewHolder);
} }
return convertView; return convertView;
@ -706,4 +730,129 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
private void unregisterEventListener(String event) { private void unregisterEventListener(String event) {
GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this); GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this);
} }
private List<String> getUrlsWithoutFavicon() {
List<String> urls = new ArrayList<String>();
Cursor c = mCursorAdapter.getCursor();
if (c == null || !c.moveToFirst())
return urls;
do {
final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
// We only want to load favicons from DB if they are not in the
// memory cache yet.
Favicons favicons = GeckoApp.mAppContext.getFavicons();
if (favicons.getFaviconFromMemCache(url) != null)
continue;
urls.add(url);
} while (c.moveToNext());
return urls;
}
public void storeFaviconsInMemCache(Cursor c) {
try {
if (c == null || !c.moveToFirst())
return;
Favicons favicons = GeckoApp.mAppContext.getFavicons();
do {
final String url = c.getString(c.getColumnIndexOrThrow(Images.URL));
final byte[] b = c.getBlob(c.getColumnIndexOrThrow(Images.FAVICON));
if (b == null)
continue;
Bitmap favicon = BitmapFactory.decodeByteArray(b, 0, b.length);
if (favicon == null)
continue;
Drawable faviconDrawable = new BitmapDrawable(getResources(), favicon);
favicons.putFaviconInMemCache(url, faviconDrawable);
} while (c.moveToNext());
} finally {
if (c != null)
c.close();
}
}
private void loadFaviconsForCurrentResults() {
final List<String> urls = getUrlsWithoutFavicon();
if (urls.size() == 0)
return;
(new GeckoAsyncTask<Void, Void, Cursor>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
@Override
public Cursor doInBackground(Void... params) {
return BrowserDB.getFaviconsForUrls(getContentResolver(), urls);
}
@Override
public void onPostExecute(Cursor c) {
storeFaviconsInMemCache(c);
postUpdateFavicons();
}
}).execute();
}
private void displayFavicon(AwesomeEntryViewHolder viewHolder) {
final String url = viewHolder.urlView.getText().toString();
Favicons favicons = GeckoApp.mAppContext.getFavicons();
viewHolder.faviconView.setImageDrawable(favicons.getFaviconFromMemCache(url));
}
private void updateFavicons() {
ListView listView = (ListView) mView;
for (int i = 0; i < listView.getChildCount(); i++) {
final View view = listView.getChildAt(i);
final Object tag = view.getTag();
if (tag == null || !(tag instanceof AwesomeEntryViewHolder))
continue;
final AwesomeEntryViewHolder viewHolder = (AwesomeEntryViewHolder) tag;
displayFavicon(viewHolder);
}
mView.invalidate();
}
private void postUpdateFavicons() {
if (mHandler == null)
return;
Message msg = mHandler.obtainMessage(MESSAGE_UPDATE_FAVICONS,
AllPagesTab.this);
mHandler.removeMessages(MESSAGE_UPDATE_FAVICONS);
mHandler.sendMessage(msg);
}
private void postLoadFavicons() {
if (mHandler == null)
return;
Message msg = mHandler.obtainMessage(MESSAGE_LOAD_FAVICONS,
AllPagesTab.this);
mHandler.removeMessages(MESSAGE_LOAD_FAVICONS);
mHandler.sendMessageDelayed(msg, 200);
}
private class AllPagesHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_LOAD_FAVICONS:
loadFaviconsForCurrentResults();
break;
case MESSAGE_UPDATE_FAVICONS:
updateFavicons();
break;
}
}
}
} }

View File

@ -182,7 +182,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
new String[] { Combined._ID, new String[] { Combined._ID,
Combined.URL, Combined.URL,
Combined.TITLE, Combined.TITLE,
Combined.FAVICON,
Combined.DISPLAY, Combined.DISPLAY,
Combined.BOOKMARK_ID, Combined.BOOKMARK_ID,
Combined.HISTORY_ID }, Combined.HISTORY_ID },