summaryrefslogtreecommitdiffstats
path: root/src/com/android/swe/browser/BrowserHistoryPage.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/swe/browser/BrowserHistoryPage.java')
-rw-r--r--src/com/android/swe/browser/BrowserHistoryPage.java666
1 files changed, 666 insertions, 0 deletions
diff --git a/src/com/android/swe/browser/BrowserHistoryPage.java b/src/com/android/swe/browser/BrowserHistoryPage.java
new file mode 100644
index 00000000..9493b85e
--- /dev/null
+++ b/src/com/android/swe/browser/BrowserHistoryPage.java
@@ -0,0 +1,666 @@
+/*
+ * 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.android.browser;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.app.FragmentBreadCrumbs;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ClipboardManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Browser;
+import android.provider.BrowserContract;
+import android.provider.BrowserContract.Combined;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+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.ViewStub;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.ExpandableListView.OnChildClickListener;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Activity for displaying the browser's history, divided into
+ * days of viewing.
+ */
+public class BrowserHistoryPage extends Fragment
+ implements LoaderCallbacks<Cursor>, OnChildClickListener {
+
+ static final int LOADER_HISTORY = 1;
+ static final int LOADER_MOST_VISITED = 2;
+
+ CombinedBookmarksCallbacks mCallback;
+ HistoryAdapter mAdapter;
+ HistoryChildWrapper mChildWrapper;
+ boolean mDisableNewWindow;
+ HistoryItem mContextHeader;
+ String mMostVisitsLimit;
+ ListView mGroupList, mChildList;
+ private ViewGroup mPrefsContainer;
+ private FragmentBreadCrumbs mFragmentBreadCrumbs;
+ private ExpandableListView mHistoryList;
+
+ private View mRoot;
+
+ static interface HistoryQuery {
+ static final String[] PROJECTION = new String[] {
+ Combined._ID, // 0
+ Combined.DATE_LAST_VISITED, // 1
+ Combined.TITLE, // 2
+ Combined.URL, // 3
+ Combined.FAVICON, // 4
+ Combined.VISITS, // 5
+ Combined.IS_BOOKMARK, // 6
+ };
+
+ static final int INDEX_ID = 0;
+ static final int INDEX_DATE_LAST_VISITED = 1;
+ static final int INDEX_TITE = 2;
+ static final int INDEX_URL = 3;
+ static final int INDEX_FAVICON = 4;
+ static final int INDEX_VISITS = 5;
+ static final int INDEX_IS_BOOKMARK = 6;
+ }
+
+ private void copy(CharSequence text) {
+ ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
+ Context.CLIPBOARD_SERVICE);
+ cm.setText(text);
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ Uri.Builder combinedBuilder = Combined.CONTENT_URI.buildUpon();
+
+ switch (id) {
+ case LOADER_HISTORY: {
+ String sort = Combined.DATE_LAST_VISITED + " DESC";
+ String where = Combined.VISITS + " > 0";
+ CursorLoader loader = new CursorLoader(getActivity(), combinedBuilder.build(),
+ HistoryQuery.PROJECTION, where, null, sort);
+ return loader;
+ }
+
+ case LOADER_MOST_VISITED: {
+ Uri uri = combinedBuilder
+ .appendQueryParameter(BrowserContract.PARAM_LIMIT, mMostVisitsLimit)
+ .build();
+ String where = Combined.VISITS + " > 0";
+ CursorLoader loader = new CursorLoader(getActivity(), uri,
+ HistoryQuery.PROJECTION, where, null, Combined.VISITS + " DESC");
+ return loader;
+ }
+
+ default: {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ void selectGroup(int position) {
+ mGroupItemClickListener.onItemClick(null,
+ mAdapter.getGroupView(position, false, null, null),
+ position, position);
+ }
+
+ void checkIfEmpty() {
+ if (mAdapter.mMostVisited != null && mAdapter.mHistoryCursor != null) {
+ // Both cursors have loaded - check to see if we have data
+ if (mAdapter.isEmpty()) {
+ mRoot.findViewById(R.id.history).setVisibility(View.GONE);
+ mRoot.findViewById(android.R.id.empty).setVisibility(View.VISIBLE);
+ } else {
+ mRoot.findViewById(R.id.history).setVisibility(View.VISIBLE);
+ mRoot.findViewById(android.R.id.empty).setVisibility(View.GONE);
+ }
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ switch (loader.getId()) {
+ case LOADER_HISTORY: {
+ mAdapter.changeCursor(data);
+ if (!mAdapter.isEmpty() && mGroupList != null
+ && mGroupList.getCheckedItemPosition() == ListView.INVALID_POSITION) {
+ selectGroup(0);
+ }
+
+ checkIfEmpty();
+ break;
+ }
+
+ case LOADER_MOST_VISITED: {
+ mAdapter.changeMostVisitedCursor(data);
+
+ checkIfEmpty();
+ break;
+ }
+
+ default: {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setHasOptionsMenu(true);
+
+ Bundle args = getArguments();
+ mDisableNewWindow = args.getBoolean(BrowserBookmarksPage.EXTRA_DISABLE_WINDOW, false);
+ int mvlimit = getResources().getInteger(R.integer.most_visits_limit);
+ mMostVisitsLimit = Integer.toString(mvlimit);
+ mCallback = (CombinedBookmarksCallbacks) getActivity();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ mRoot = inflater.inflate(R.layout.history, container, false);
+ mAdapter = new HistoryAdapter(getActivity());
+ ViewStub stub = (ViewStub) mRoot.findViewById(R.id.pref_stub);
+ if (stub != null) {
+ inflateTwoPane(stub);
+ } else {
+ inflateSinglePane();
+ }
+
+ // Start the loaders
+ getLoaderManager().restartLoader(LOADER_HISTORY, null, this);
+ getLoaderManager().restartLoader(LOADER_MOST_VISITED, null, this);
+
+ return mRoot;
+ }
+
+ private void inflateSinglePane() {
+ mHistoryList = (ExpandableListView) mRoot.findViewById(R.id.history);
+ mHistoryList.setAdapter(mAdapter);
+ mHistoryList.setOnChildClickListener(this);
+ registerForContextMenu(mHistoryList);
+ }
+
+ private void inflateTwoPane(ViewStub stub) {
+ stub.setLayoutResource(R.layout.preference_list_content);
+ stub.inflate();
+ mGroupList = (ListView) mRoot.findViewById(android.R.id.list);
+ mPrefsContainer = (ViewGroup) mRoot.findViewById(R.id.prefs_frame);
+ mFragmentBreadCrumbs = (FragmentBreadCrumbs) mRoot.findViewById(android.R.id.title);
+ mFragmentBreadCrumbs.setMaxVisible(1);
+ mFragmentBreadCrumbs.setActivity(getActivity());
+ mPrefsContainer.setVisibility(View.VISIBLE);
+ mGroupList.setAdapter(new HistoryGroupWrapper(mAdapter));
+ mGroupList.setOnItemClickListener(mGroupItemClickListener);
+ mGroupList.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+ mChildWrapper = new HistoryChildWrapper(mAdapter);
+ mChildList = new ListView(getActivity());
+ mChildList.setAdapter(mChildWrapper);
+ mChildList.setOnItemClickListener(mChildItemClickListener);
+ registerForContextMenu(mChildList);
+ ViewGroup prefs = (ViewGroup) mRoot.findViewById(R.id.prefs);
+ prefs.addView(mChildList);
+ }
+
+ private OnItemClickListener mGroupItemClickListener = new OnItemClickListener() {
+ @Override
+ public void onItemClick(
+ AdapterView<?> parent, View view, int position, long id) {
+ CharSequence title = ((TextView) view).getText();
+ mFragmentBreadCrumbs.setTitle(title, title);
+ mChildWrapper.setSelectedGroup(position);
+ mGroupList.setItemChecked(position, true);
+ }
+ };
+
+ private OnItemClickListener mChildItemClickListener = new OnItemClickListener() {
+ @Override
+ public void onItemClick(
+ AdapterView<?> parent, View view, int position, long id) {
+ mCallback.openUrl(((HistoryItem) view).getUrl());
+ }
+ };
+
+ @Override
+ public boolean onChildClick(ExpandableListView parent, View view,
+ int groupPosition, int childPosition, long id) {
+ mCallback.openUrl(((HistoryItem) view).getUrl());
+ return true;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ getLoaderManager().destroyLoader(LOADER_HISTORY);
+ getLoaderManager().destroyLoader(LOADER_MOST_VISITED);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.history, menu);
+ }
+
+ void promptToClearHistory() {
+ final ContentResolver resolver = getActivity().getContentResolver();
+ final ClearHistoryTask clear = new ClearHistoryTask(resolver);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setMessage(R.string.pref_privacy_clear_history_dlg)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ clear.start();
+ }
+ }
+ });
+ final Dialog dialog = builder.create();
+ dialog.show();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.clear_history_menu_id) {
+ promptToClearHistory();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ static class ClearHistoryTask extends Thread {
+ ContentResolver mResolver;
+
+ public ClearHistoryTask(ContentResolver resolver) {
+ mResolver = resolver;
+ }
+
+ @Override
+ public void run() {
+ Browser.clearHistory(mResolver);
+ }
+ }
+
+ View getTargetView(ContextMenuInfo menuInfo) {
+ if (menuInfo instanceof AdapterContextMenuInfo) {
+ return ((AdapterContextMenuInfo) menuInfo).targetView;
+ }
+ if (menuInfo instanceof ExpandableListContextMenuInfo) {
+ return ((ExpandableListContextMenuInfo) menuInfo).targetView;
+ }
+ return null;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+
+ View targetView = getTargetView(menuInfo);
+ if (!(targetView instanceof HistoryItem)) {
+ return;
+ }
+ HistoryItem historyItem = (HistoryItem) targetView;
+
+ // Inflate the menu
+ Activity parent = getActivity();
+ MenuInflater inflater = parent.getMenuInflater();
+ inflater.inflate(R.menu.historycontext, menu);
+
+ // Setup the header
+ if (mContextHeader == null) {
+ mContextHeader = new HistoryItem(parent, false);
+ mContextHeader.setEnableScrolling(true);
+ } 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 = parent.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) {
+ ContextMenuInfo menuInfo = item.getMenuInfo();
+ if (menuInfo == null) {
+ return false;
+ }
+ View targetView = getTargetView(menuInfo);
+ if (!(targetView instanceof HistoryItem)) {
+ return false;
+ }
+ HistoryItem historyItem = (HistoryItem) targetView;
+ String url = historyItem.getUrl();
+ String title = historyItem.getName();
+ Activity activity = getActivity();
+ switch (item.getItemId()) {
+ case R.id.open_context_menu_id:
+ mCallback.openUrl(url);
+ return true;
+ case R.id.new_window_context_menu_id:
+ mCallback.openInNewTab(url);
+ return true;
+ case R.id.save_to_bookmarks_menu_id:
+ if (historyItem.isBookmark()) {
+ Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(),
+ url, title);
+ } else {
+ Browser.saveBookmark(activity, title, url);
+ }
+ return true;
+ case R.id.share_link_context_menu_id:
+ Browser.sendString(activity, url,
+ activity.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(activity.getContentResolver(), url);
+ return true;
+ case R.id.homepage_context_menu_id:
+ BrowserSettings.getInstance().setHomePage(url);
+ Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show();
+ return true;
+ default:
+ break;
+ }
+ return super.onContextItemSelected(item);
+ }
+
+ private static abstract class HistoryWrapper extends BaseAdapter {
+
+ protected HistoryAdapter mAdapter;
+ private DataSetObserver mObserver = new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ super.onChanged();
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onInvalidated() {
+ super.onInvalidated();
+ notifyDataSetInvalidated();
+ }
+ };
+
+ public HistoryWrapper(HistoryAdapter adapter) {
+ mAdapter = adapter;
+ mAdapter.registerDataSetObserver(mObserver);
+ }
+
+ }
+ private static class HistoryGroupWrapper extends HistoryWrapper {
+
+ public HistoryGroupWrapper(HistoryAdapter adapter) {
+ super(adapter);
+ }
+
+ @Override
+ public int getCount() {
+ return mAdapter.getGroupCount();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return mAdapter.getGroupView(position, false, convertView, parent);
+ }
+
+ }
+
+ private static class HistoryChildWrapper extends HistoryWrapper {
+
+ private int mSelectedGroup;
+
+ public HistoryChildWrapper(HistoryAdapter adapter) {
+ super(adapter);
+ }
+
+ void setSelectedGroup(int groupPosition) {
+ mSelectedGroup = groupPosition;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mAdapter.getChildrenCount(mSelectedGroup);
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return mAdapter.getChildView(mSelectedGroup, position,
+ false, convertView, parent);
+ }
+
+ }
+
+ private class HistoryAdapter extends DateSortedExpandableListAdapter {
+
+ private Cursor mMostVisited, mHistoryCursor;
+ Drawable mFaviconBackground;
+
+ HistoryAdapter(Context context) {
+ super(context, HistoryQuery.INDEX_DATE_LAST_VISITED);
+ mFaviconBackground = BookmarkUtils.createListFaviconBackground(context);
+ }
+
+ @Override
+ public void changeCursor(Cursor cursor) {
+ mHistoryCursor = cursor;
+ super.changeCursor(cursor);
+ }
+
+ void changeMostVisitedCursor(Cursor cursor) {
+ if (mMostVisited == cursor) {
+ return;
+ }
+ if (mMostVisited != null) {
+ mMostVisited.unregisterDataSetObserver(mDataSetObserver);
+ mMostVisited.close();
+ }
+ mMostVisited = cursor;
+ if (mMostVisited != null) {
+ mMostVisited.registerDataSetObserver(mDataSetObserver);
+ }
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public long getChildId(int groupPosition, int childPosition) {
+ if (moveCursorToChildPosition(groupPosition, childPosition)) {
+ Cursor cursor = getCursor(groupPosition);
+ return cursor.getLong(HistoryQuery.INDEX_ID);
+ }
+ return 0;
+ }
+
+ @Override
+ public int getGroupCount() {
+ return super.getGroupCount() + (!isMostVisitedEmpty() ? 1 : 0);
+ }
+
+ @Override
+ public int getChildrenCount(int groupPosition) {
+ if (groupPosition >= super.getGroupCount()) {
+ if (isMostVisitedEmpty()) {
+ return 0;
+ }
+ return mMostVisited.getCount();
+ }
+ return super.getChildrenCount(groupPosition);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ if (!super.isEmpty()) {
+ return false;
+ }
+ return isMostVisitedEmpty();
+ }
+
+ private boolean isMostVisitedEmpty() {
+ return mMostVisited == null
+ || mMostVisited.isClosed()
+ || mMostVisited.getCount() == 0;
+ }
+
+ Cursor getCursor(int groupPosition) {
+ if (groupPosition >= super.getGroupCount()) {
+ return mMostVisited;
+ }
+ return mHistoryCursor;
+ }
+
+ @Override
+ public View getGroupView(int groupPosition, boolean isExpanded,
+ View convertView, ViewGroup parent) {
+ if (groupPosition >= super.getGroupCount()) {
+ if (mMostVisited == null || mMostVisited.isClosed()) {
+ throw new IllegalStateException("Data is not valid");
+ }
+ TextView item;
+ if (null == convertView || !(convertView instanceof TextView)) {
+ LayoutInflater factory = LayoutInflater.from(getContext());
+ item = (TextView) factory.inflate(R.layout.history_header, null);
+ } else {
+ item = (TextView) convertView;
+ }
+ item.setText(R.string.tab_most_visited);
+ return item;
+ }
+ return super.getGroupView(groupPosition, isExpanded, convertView, parent);
+ }
+
+ @Override
+ boolean moveCursorToChildPosition(
+ int groupPosition, int childPosition) {
+ if (groupPosition >= super.getGroupCount()) {
+ if (mMostVisited != null && !mMostVisited.isClosed()) {
+ mMostVisited.moveToPosition(childPosition);
+ return true;
+ }
+ return false;
+ }
+ return super.moveCursorToChildPosition(groupPosition, childPosition);
+ }
+
+ @Override
+ public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+ View convertView, ViewGroup parent) {
+ HistoryItem item;
+ if (null == convertView || !(convertView instanceof HistoryItem)) {
+ item = new HistoryItem(getContext());
+ // 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());
+ item.setFaviconBackground(mFaviconBackground);
+ } else {
+ item = (HistoryItem) convertView;
+ }
+
+ // Bail early if the Cursor is closed.
+ if (!moveCursorToChildPosition(groupPosition, childPosition)) {
+ return item;
+ }
+
+ Cursor cursor = getCursor(groupPosition);
+ item.setName(cursor.getString(HistoryQuery.INDEX_TITE));
+ String url = cursor.getString(HistoryQuery.INDEX_URL);
+ item.setUrl(url);
+ byte[] data = cursor.getBlob(HistoryQuery.INDEX_FAVICON);
+ if (data != null) {
+ item.setFavicon(BitmapFactory.decodeByteArray(data, 0,
+ data.length));
+ }
+ item.setIsBookmark(cursor.getInt(HistoryQuery.INDEX_IS_BOOKMARK) == 1);
+ return item;
+ }
+ }
+}