diff options
Diffstat (limited to 'src/com/android/browser')
-rw-r--r-- | src/com/android/browser/BrowserHistoryPage.java | 230 | ||||
-rw-r--r-- | src/com/android/browser/DateSortedExpandableListAdapter.java | 284 |
2 files changed, 312 insertions, 202 deletions
diff --git a/src/com/android/browser/BrowserHistoryPage.java b/src/com/android/browser/BrowserHistoryPage.java index 831d63f..5818e1d 100644 --- a/src/com/android/browser/BrowserHistoryPage.java +++ b/src/com/android/browser/BrowserHistoryPage.java @@ -18,16 +18,14 @@ package com.android.browser; import android.app.Activity; import android.app.ExpandableListActivity; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.database.ContentObserver; import android.database.Cursor; -import android.database.DataSetObserver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; -import android.os.Handler; import android.os.ServiceManager; import android.provider.Browser; import android.text.IClipboard; @@ -43,7 +41,6 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ContextMenu.ContextMenuInfo; import android.view.ViewStub; -import android.webkit.DateSorter; import android.webkit.WebIconDatabase.IconListener; import android.widget.AdapterView; import android.widget.ExpandableListAdapter; @@ -52,16 +49,12 @@ import android.widget.ExpandableListView.ExpandableListContextMenuInfo; import android.widget.TextView; import android.widget.Toast; -import java.util.List; -import java.util.Vector; - /** * Activity for displaying the browser's history, divided into * days of viewing. */ public class BrowserHistoryPage extends ExpandableListActivity { private HistoryAdapter mAdapter; - private DateSorter mDateSorter; private boolean mDisableNewWindow; private HistoryItem mContextHeader; @@ -107,10 +100,24 @@ public class BrowserHistoryPage extends ExpandableListActivity { protected void onCreate(Bundle icicle) { super.onCreate(icicle); setTitle(R.string.browser_history); - - mDateSorter = new DateSorter(this); - mAdapter = new HistoryAdapter(); + final String whereClause = Browser.BookmarkColumns.VISITS + " > 0" + // In AddBookmarkPage, where we save new bookmarks, we add + // three visits to newly created bookmarks, so that + // bookmarks that have not been visited will show up in the + // most visited, and higher in the goto search box. + // However, this puts the site in the history, unless we + // ignore sites with a DATE of 0, which the next line does. + + " AND " + Browser.BookmarkColumns.DATE + " > 0"; + final String orderBy = Browser.BookmarkColumns.DATE + " DESC"; + + Cursor cursor = managedQuery( + Browser.BOOKMARKS_URI, + Browser.HISTORY_PROJECTION, + whereClause, null, orderBy); + + mAdapter = new HistoryAdapter(this, cursor, + Browser.HISTORY_PROJECTION_DATE_INDEX); setListAdapter(mAdapter); final ExpandableListView list = getExpandableListView(); list.setOnCreateContextMenuListener(this); @@ -290,128 +297,14 @@ public class BrowserHistoryPage extends ExpandableListActivity { resultCode, data); } - private class ChangeObserver extends ContentObserver { - public ChangeObserver() { - super(new Handler()); - } - - @Override - public boolean deliverSelfNotifications() { - return true; - } - - @Override - public void onChange(boolean selfChange) { - mAdapter.refreshData(); - } - } - - private class HistoryAdapter implements ExpandableListAdapter { - - // Array for each of our bins. Each entry represents how many items are - // in that bin. - private int mItemMap[]; - // This is our GroupCount. We will have at most DateSorter.DAY_COUNT - // bins, less if the user has no items in one or more bins. - private int mNumberOfBins; - private Vector<DataSetObserver> mObservers; - private Cursor mCursor; - - HistoryAdapter() { - mObservers = new Vector<DataSetObserver>(); - - final String whereClause = Browser.BookmarkColumns.VISITS + " > 0" - // In AddBookmarkPage, where we save new bookmarks, we add - // three visits to newly created bookmarks, so that - // bookmarks that have not been visited will show up in the - // most visited, and higher in the goto search box. - // However, this puts the site in the history, unless we - // ignore sites with a DATE of 0, which the next line does. - + " AND " + Browser.BookmarkColumns.DATE + " > 0"; - final String orderBy = Browser.BookmarkColumns.DATE + " DESC"; - - mCursor = managedQuery( - Browser.BOOKMARKS_URI, - Browser.HISTORY_PROJECTION, - whereClause, null, orderBy); + private class HistoryAdapter extends DateSortedExpandableListAdapter { + HistoryAdapter(Context context, Cursor cursor, int index) { + super(context, cursor, index); - buildMap(); - mCursor.registerContentObserver(new ChangeObserver()); - } - - void refreshData() { - if (mCursor.isClosed()) { - return; - } - mCursor.requery(); - buildMap(); - for (DataSetObserver o : mObservers) { - o.onChanged(); - } - } - - private void buildMap() { - // The cursor is sorted by date - // The ItemMap will store the number of items in each bin. - int array[] = new int[DateSorter.DAY_COUNT]; - // Zero out the array. - for (int j = 0; j < DateSorter.DAY_COUNT; j++) { - array[j] = 0; - } - mNumberOfBins = 0; - int dateIndex = -1; - if (mCursor.moveToFirst() && mCursor.getCount() > 0) { - while (!mCursor.isAfterLast()) { - long date = mCursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX); - int index = mDateSorter.getIndex(date); - if (index > dateIndex) { - mNumberOfBins++; - if (index == DateSorter.DAY_COUNT - 1) { - // We are already in the last bin, so it will - // include all the remaining items - array[index] = mCursor.getCount() - - mCursor.getPosition(); - break; - } - dateIndex = index; - } - array[dateIndex]++; - mCursor.moveToNext(); - } - } - mItemMap = array; - } - - // This translates from a group position in the Adapter to a position in - // our array. This is necessary because some positions in the array - // have no history items, so we simply do not present those positions - // to the Adapter. - private int groupPositionToArrayPosition(int groupPosition) { - if (groupPosition < 0 || groupPosition >= DateSorter.DAY_COUNT) { - throw new AssertionError("group position out of range"); - } - if (DateSorter.DAY_COUNT == mNumberOfBins || 0 == mNumberOfBins) { - // In the first case, we have exactly the same number of bins - // as our maximum possible, so there is no need to do a - // conversion - // The second statement is in case this method gets called when - // the array is empty, in which case the provided groupPosition - // will do fine. - return groupPosition; - } - int arrayPosition = -1; - while (groupPosition > -1) { - arrayPosition++; - if (mItemMap[arrayPosition] != 0) { - groupPosition--; - } - } - return arrayPosition; } public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { - groupPosition = groupPositionToArrayPosition(groupPosition); HistoryItem item; if (null == convertView || !(convertView instanceof HistoryItem)) { item = new HistoryItem(BrowserHistoryPage.this); @@ -425,16 +318,13 @@ public class BrowserHistoryPage extends ExpandableListActivity { item = (HistoryItem) convertView; } // Bail early if the Cursor is closed. - if (mCursor.isClosed()) return item; - int index = childPosition; - for (int i = 0; i < groupPosition; i++) { - index += mItemMap[i]; + if (!moveCursorToChildPosition(groupPosition, childPosition)) { + return item; } - mCursor.moveToPosition(index); - item.setName(mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX)); - String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX); + item.setName(getString(Browser.HISTORY_PROJECTION_TITLE_INDEX)); + String url = getString(Browser.HISTORY_PROJECTION_URL_INDEX); item.setUrl(url); - byte[] data = mCursor.getBlob(Browser.HISTORY_PROJECTION_FAVICON_INDEX); + byte[] data = getBlob(Browser.HISTORY_PROJECTION_FAVICON_INDEX); if (data != null) { item.setFavicon(BitmapFactory.decodeByteArray(data, 0, data.length)); @@ -443,12 +333,11 @@ public class BrowserHistoryPage extends ExpandableListActivity { .getIconListenerSet().getFavicon(url)); } item.setIsBookmark(1 == - mCursor.getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX)); + getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX)); return item; } public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { - groupPosition = groupPositionToArrayPosition(groupPosition); TextView item; if (null == convertView || !(convertView instanceof TextView)) { LayoutInflater factory = @@ -458,72 +347,9 @@ public class BrowserHistoryPage extends ExpandableListActivity { } else { item = (TextView) convertView; } - item.setText(mDateSorter.getLabel(groupPosition)); + item.setText(getGroupLabel(groupPosition)); return item; } - public boolean areAllItemsEnabled() { - return true; - } - - public boolean isChildSelectable(int groupPosition, int childPosition) { - return true; - } - - public int getGroupCount() { - return mNumberOfBins; - } - - public int getChildrenCount(int groupPosition) { - return mItemMap[groupPositionToArrayPosition(groupPosition)]; - } - - public Object getGroup(int groupPosition) { - return null; - } - - public Object getChild(int groupPosition, int childPosition) { - return null; - } - - public long getGroupId(int groupPosition) { - return groupPosition; - } - - public long getChildId(int groupPosition, int childPosition) { - return (childPosition << 3) + groupPosition; - } - - public boolean hasStableIds() { - return true; - } - - public void registerDataSetObserver(DataSetObserver observer) { - mObservers.add(observer); - } - - public void unregisterDataSetObserver(DataSetObserver observer) { - mObservers.remove(observer); - } - - public void onGroupExpanded(int groupPosition) { - - } - - public void onGroupCollapsed(int groupPosition) { - - } - - public long getCombinedChildId(long groupId, long childId) { - return childId; - } - - public long getCombinedGroupId(long groupId) { - return groupId; - } - - public boolean isEmpty() { - return mCursor.isClosed() || mCursor.getCount() == 0; - } } } diff --git a/src/com/android/browser/DateSortedExpandableListAdapter.java b/src/com/android/browser/DateSortedExpandableListAdapter.java new file mode 100644 index 0000000..05d16bd --- /dev/null +++ b/src/com/android/browser/DateSortedExpandableListAdapter.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser; + +import android.content.Context; +import android.database.ContentObserver; +import android.database.Cursor; +import android.database.DataSetObserver; +import android.os.Handler; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.DateSorter; +import android.widget.ExpandableListAdapter; + +import java.util.Vector; + +/** + * ExpandableListAdapter which separates data into categories based on date. + * Used for History and Downloads. + */ +public class DateSortedExpandableListAdapter implements ExpandableListAdapter { + // Array for each of our bins. Each entry represents how many items are + // in that bin. + private int mItemMap[]; + // This is our GroupCount. We will have at most DateSorter.DAY_COUNT + // bins, less if the user has no items in one or more bins. + private int mNumberOfBins; + private Vector<DataSetObserver> mObservers; + private Cursor mCursor; + private DateSorter mDateSorter; + private int mDateIndex; + + private class ChangeObserver extends ContentObserver { + public ChangeObserver() { + super(new Handler()); + } + + @Override + public boolean deliverSelfNotifications() { + return true; + } + + @Override + public void onChange(boolean selfChange) { + refreshData(); + } + } + + public DateSortedExpandableListAdapter(Context context, Cursor cursor, + int dateIndex) { + mDateSorter = new DateSorter(context); + mObservers = new Vector<DataSetObserver>(); + mCursor = cursor; + cursor.registerContentObserver(new ChangeObserver()); + mDateIndex = dateIndex; + buildMap(); + } + + /** + * Set up the bins for determining which items belong to which groups. + */ + private void buildMap() { + // The cursor is sorted by date + // The ItemMap will store the number of items in each bin. + int array[] = new int[DateSorter.DAY_COUNT]; + // Zero out the array. + for (int j = 0; j < DateSorter.DAY_COUNT; j++) { + array[j] = 0; + } + mNumberOfBins = 0; + int dateIndex = -1; + if (mCursor.moveToFirst() && mCursor.getCount() > 0) { + while (!mCursor.isAfterLast()) { + long date = mCursor.getLong(mDateIndex); + int index = mDateSorter.getIndex(date); + if (index > dateIndex) { + mNumberOfBins++; + if (index == DateSorter.DAY_COUNT - 1) { + // We are already in the last bin, so it will + // include all the remaining items + array[index] = mCursor.getCount() + - mCursor.getPosition(); + break; + } + dateIndex = index; + } + array[dateIndex]++; + mCursor.moveToNext(); + } + } + mItemMap = array; + } + + /** + * Get the byte array at cursorIndex from the Cursor. Assumes the Cursor + * has already been moved to the correct position. Along with + * {@link #getInt} and {@link #getString}, these are provided so the client + * does not need to access the Cursor directly + * @param cursorIndex Index to query the Cursor. + * @return corresponding byte array from the Cursor. + */ + /* package */ byte[] getBlob(int cursorIndex) { + return mCursor.getBlob(cursorIndex); + } + + /** + * Get the integer at cursorIndex from the Cursor. Assumes the Cursor has + * already been moved to the correct position. Along with + * {@link #getBlob} and {@link #getString}, these are provided so the client + * does not need to access the Cursor directly + * @param cursorIndex Index to query the Cursor. + * @return corresponding integer from the Cursor. + */ + /* package */ int getInt(int cursorIndex) { + return mCursor.getInt(cursorIndex); + } + + /** + * Get the label for a group, as specified by the ExpandableList + * @param groupPosition Position in the ExpandableList's set of groups + * @return String label for the corresponding bin. + */ + /* package */ String getGroupLabel(int groupPosition) { + return mDateSorter.getLabel(groupPositionToBin(groupPosition)); + } + + /** + * Get the String at cursorIndex from the Cursor. Assumes the Cursor has + * already been moved to the correct position. Along with + * {@link #getInt} and {@link #getInt}, these are provided so the client + * does not need to access the Cursor directly + * @param cursorIndex Index to query the Cursor. + * @return corresponding String from the Cursor. + */ + /* package */ String getString(int cursorIndex) { + return mCursor.getString(cursorIndex); + } + + /** + * Translates from a group position in the ExpandableList to a bin. This is + * necessary because some groups have no history items, so we do not include + * those in the ExpandableList. + * @param groupPosition Position in the ExpandableList's set of groups + * @return The corresponding bin that holds that group. + */ + private int groupPositionToBin(int groupPosition) { + if (groupPosition < 0 || groupPosition >= DateSorter.DAY_COUNT) { + throw new AssertionError("group position out of range"); + } + if (DateSorter.DAY_COUNT == mNumberOfBins || 0 == mNumberOfBins) { + // In the first case, we have exactly the same number of bins + // as our maximum possible, so there is no need to do a + // conversion + // The second statement is in case this method gets called when + // the array is empty, in which case the provided groupPosition + // will do fine. + return groupPosition; + } + int arrayPosition = -1; + while (groupPosition > -1) { + arrayPosition++; + if (mItemMap[arrayPosition] != 0) { + groupPosition--; + } + } + return arrayPosition; + } + + /** + * Move the cursor the the position indicated. + * @param groupPosition Index of the group containing the desired item. + * @param childPosition Index of the item within the specified group. + * @return boolean False if the cursor is closed, so the Cursor was not + * moved. True on success. + */ + /* package */ boolean moveCursorToChildPosition(int groupPosition, + int childPosition) { + if (mCursor.isClosed()) return false; + groupPosition = groupPositionToBin(groupPosition); + int index = childPosition; + for (int i = 0; i < groupPosition; i++) { + index += mItemMap[i]; + } + mCursor.moveToPosition(index); + return true; + } + + /* package */ void refreshData() { + if (mCursor.isClosed()) { + return; + } + mCursor.requery(); + buildMap(); + for (DataSetObserver o : mObservers) { + o.onChanged(); + } + } + + public View getGroupView(int groupPosition, boolean isExpanded, + View convertView, ViewGroup parent) { + return null; + } + + public View getChildView(int groupPosition, int childPosition, + boolean isLastChild, View convertView, ViewGroup parent) { + return null; + } + + public boolean areAllItemsEnabled() { + return true; + } + + public boolean isChildSelectable(int groupPosition, int childPosition) { + return true; + } + + public int getGroupCount() { + return mNumberOfBins; + } + + public int getChildrenCount(int groupPosition) { + return mItemMap[groupPositionToBin(groupPosition)]; + } + + public Object getGroup(int groupPosition) { + return null; + } + + public Object getChild(int groupPosition, int childPosition) { + return null; + } + + public long getGroupId(int groupPosition) { + return groupPosition; + } + + public long getChildId(int groupPosition, int childPosition) { + return (childPosition << 3) + groupPosition; + } + + public boolean hasStableIds() { + return true; + } + + public void registerDataSetObserver(DataSetObserver observer) { + mObservers.add(observer); + } + + public void unregisterDataSetObserver(DataSetObserver observer) { + mObservers.remove(observer); + } + + public void onGroupExpanded(int groupPosition) { + } + + public void onGroupCollapsed(int groupPosition) { + } + + public long getCombinedChildId(long groupId, long childId) { + return childId; + } + + public long getCombinedGroupId(long groupId) { + return groupId; + } + + public boolean isEmpty() { + return mCursor.isClosed() || mCursor.getCount() == 0; + } +} |