/WebVox/src/com/marvin/webvox/BrowserHistoryPage.java
http://eyes-free.googlecode.com/ · Java · 525 lines · 382 code · 59 blank · 84 comment · 49 complexity · dc5d0a8d61abdd8b591a0036f07b755b MD5 · raw file
- /*
- * Copyright (C) 2008 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.marvin.webvox;
- import com.marvin.webvox.R;
- import android.app.Activity;
- import android.app.ExpandableListActivity;
- 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;
- import android.util.Log;
- import android.view.ContextMenu;
- import android.view.KeyEvent;
- import android.view.LayoutInflater;
- import android.view.Menu;
- import android.view.MenuInflater;
- import android.view.MenuItem;
- import android.view.View;
- 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;
- import android.widget.ExpandableListView;
- 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;
- private final static String LOGTAG = "browser";
- // Implementation of WebIconDatabase.IconListener
- private class IconReceiver implements IconListener {
- public void onReceivedIcon(String url, Bitmap icon) {
- setListAdapter(mAdapter);
- }
- }
- // Instance of IconReceiver
- private final IconReceiver mIconReceiver = new IconReceiver();
- /**
- * Report back to the calling activity to load a site.
- * @param url Site to load.
- * @param newWindow True if the URL should be loaded in a new window
- */
- private void loadUrl(String url, boolean newWindow) {
- Intent intent = new Intent().setAction(url);
- if (newWindow) {
- Bundle b = new Bundle();
- b.putBoolean("new_window", true);
- intent.putExtras(b);
- }
- setResultToParent(RESULT_OK, intent);
- finish();
- }
-
- private void copy(CharSequence text) {
- /*
- try {
- IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
- if (clip != null) {
- clip.setClipboardText(text);
- }
- } catch (android.os.RemoteException e) {
- Log.e(LOGTAG, "Copy failed", e);
- }
- */
- }
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setTitle(R.string.browser_history);
-
- mDateSorter = new DateSorter(this);
- mAdapter = new HistoryAdapter();
- setListAdapter(mAdapter);
- final ExpandableListView list = getExpandableListView();
- list.setOnCreateContextMenuListener(this);
- View v = new ViewStub(this, R.layout.empty_history);
- addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT,
- LayoutParams.FILL_PARENT));
- list.setEmptyView(v);
- // Do not post the runnable if there is nothing in the list.
- if (list.getExpandableListAdapter().getGroupCount() > 0) {
- list.post(new Runnable() {
- public void run() {
- // In case the history gets cleared before this event
- // happens.
- if (list.getExpandableListAdapter().getGroupCount() > 0) {
- list.expandGroup(0);
- }
- }
- });
- }
- mDisableNewWindow = getIntent().getBooleanExtra("disable_new_window",
- false);
- CombinedBookmarkHistoryActivity.getIconListenerSet()
- .addListener(mIconReceiver);
-
- // initialize the result to canceled, so that if the user just presses
- // back then it will have the correct result
- setResultToParent(RESULT_CANCELED, null);
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- CombinedBookmarkHistoryActivity.getIconListenerSet()
- .removeListener(mIconReceiver);
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.history, menu);
- return true;
- }
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- menu.findItem(R.id.clear_history_menu_id).setVisible(Browser.canClearHistory(this.getContentResolver()));
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.clear_history_menu_id:
- // FIXME: Need to clear the tab control in browserActivity
- // as well
- Browser.clearHistory(getContentResolver());
- mAdapter.refreshData();
- return true;
-
- default:
- break;
- }
- return super.onOptionsItemSelected(item);
- }
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
- ExpandableListContextMenuInfo i =
- (ExpandableListContextMenuInfo) menuInfo;
- // Do not allow a context menu to come up from the group views.
- if (!(i.targetView instanceof HistoryItem)) {
- return;
- }
- // Inflate the menu
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.historycontext, menu);
- HistoryItem historyItem = (HistoryItem) i.targetView;
- // Setup the header
- if (mContextHeader == null) {
- mContextHeader = new HistoryItem(this);
- } else if (mContextHeader.getParent() != null) {
- ((ViewGroup) mContextHeader.getParent()).removeView(mContextHeader);
- }
- historyItem.copyTo(mContextHeader);
- menu.setHeaderView(mContextHeader);
- // Only show open in new tab if it was not explicitly disabled
- if (mDisableNewWindow) {
- menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
- }
- // For a bookmark, provide the option to remove it from bookmarks
- if (historyItem.isBookmark()) {
- MenuItem item = menu.findItem(R.id.save_to_bookmarks_menu_id);
- item.setTitle(R.string.remove_from_bookmarks);
- }
- // decide whether to show the share link option
- PackageManager pm = getPackageManager();
- Intent send = new Intent(Intent.ACTION_SEND);
- send.setType("text/plain");
- ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
- menu.findItem(R.id.share_link_context_menu_id).setVisible(ri != null);
-
- super.onCreateContextMenu(menu, v, menuInfo);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- ExpandableListContextMenuInfo i =
- (ExpandableListContextMenuInfo) item.getMenuInfo();
- HistoryItem historyItem = (HistoryItem) i.targetView;
- String url = historyItem.getUrl();
- String title = historyItem.getName();
- switch (item.getItemId()) {
- case R.id.open_context_menu_id:
- loadUrl(url, false);
- return true;
- case R.id.new_window_context_menu_id:
- loadUrl(url, true);
- return true;
- case R.id.save_to_bookmarks_menu_id:
- if (historyItem.isBookmark()) {
- Bookmarks.removeFromBookmarks(this, getContentResolver(),
- url, title);
- } else {
- Browser.saveBookmark(this, title, url);
- }
- return true;
- case R.id.share_link_context_menu_id:
- // Browser.sendString(this, url,
- // getText(R.string.choosertitle_sharevia).toString());
- return true;
- case R.id.copy_url_context_menu_id:
- copy(url);
- return true;
- case R.id.delete_context_menu_id:
- Browser.deleteFromHistory(getContentResolver(), url);
- mAdapter.refreshData();
- return true;
- case R.id.homepage_context_menu_id:
- BrowserSettings.getInstance().setHomePage(this, url);
- Toast.makeText(this, R.string.homepage_set,
- Toast.LENGTH_LONG).show();
- return true;
- default:
- break;
- }
- return super.onContextItemSelected(item);
- }
-
- @Override
- public boolean onChildClick(ExpandableListView parent, View v,
- int groupPosition, int childPosition, long id) {
- if (v instanceof HistoryItem) {
- loadUrl(((HistoryItem) v).getUrl(), false);
- return true;
- }
- return false;
- }
- // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
- // that situation, we need to pass our result code up to our parent.
- // However, if someone calls this Activity directly, then this has no
- // parent, and it needs to set it on itself.
- private void setResultToParent(int resultCode, Intent data) {
- Activity a = getParent() == null ? this : getParent();
- a.setResult(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);
-
- 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);
- // Add padding on the left so it will be indented from the
- // arrows on the group views.
- item.setPadding(item.getPaddingLeft() + 10,
- item.getPaddingTop(),
- item.getPaddingRight(),
- item.getPaddingBottom());
- } else {
- item = (HistoryItem) convertView;
- }
- int index = childPosition;
- for (int i = 0; i < groupPosition; i++) {
- index += mItemMap[i];
- }
- mCursor.moveToPosition(index);
- item.setName(mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
- String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
- item.setUrl(url);
- byte[] data = mCursor.getBlob(Browser.HISTORY_PROJECTION_FAVICON_INDEX);
- if (data != null) {
- item.setFavicon(BitmapFactory.decodeByteArray(data, 0,
- data.length));
- } else {
- item.setFavicon(CombinedBookmarkHistoryActivity
- .getIconListenerSet().getFavicon(url));
- }
- item.setIsBookmark(1 ==
- mCursor.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 =
- LayoutInflater.from(BrowserHistoryPage.this);
- item = (TextView)
- factory.inflate(R.layout.history_header, null);
- } else {
- item = (TextView) convertView;
- }
- item.setText(mDateSorter.getLabel(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.getCount() == 0;
- }
- }
- }