Index: chrome/android/java/src/org/chromium/chrome/browser/NavigationPopup.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/NavigationPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/NavigationPopup.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ba955616515535e0b97a58f903db19307037da8b |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/NavigationPopup.java |
@@ -0,0 +1,245 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser; |
+ |
+import android.content.Context; |
+import android.graphics.Bitmap; |
+import android.graphics.Color; |
+import android.graphics.drawable.BitmapDrawable; |
+import android.graphics.drawable.ColorDrawable; |
+import android.graphics.drawable.Drawable; |
+import android.text.TextUtils; |
+import android.view.Gravity; |
+import android.view.View; |
+import android.view.ViewGroup; |
+import android.widget.AdapterView; |
+import android.widget.BaseAdapter; |
+import android.widget.HeaderViewListAdapter; |
+import android.widget.ListPopupWindow; |
+import android.widget.ListView; |
+import android.widget.ListView.FixedViewInfo; |
+import android.widget.PopupWindow; |
+import android.widget.TextView; |
+ |
+import org.chromium.base.CalledByNative; |
+import org.chromium.base.ThreadUtils; |
+import org.chromium.chrome.R; |
+import org.chromium.content.browser.ContentViewCore; |
+import org.chromium.content.browser.NavigationClient; |
+import org.chromium.content.browser.NavigationEntry; |
+import org.chromium.content.browser.NavigationHistory; |
+ |
+import java.util.ArrayList; |
+import java.util.HashSet; |
+import java.util.Set; |
+ |
+/** |
+ * A popup that handles displaying the navigation history for a given tab. |
+ */ |
+public class NavigationPopup extends ListPopupWindow implements AdapterView.OnItemClickListener { |
+ |
+ private static final int LIST_ITEM_HEIGHT_DP = 48; |
+ private static final int FAVICON_SIZE_DP = 16; |
+ private static final int PADDING_DP = 8; |
+ private static final int TEXT_SIZE_SP = 18; |
+ |
+ private static final int MAXIMUM_HISTORY_ITEMS = 8; |
+ |
+ private final Context mContext; |
+ private final NavigationPopupDelegate mDelegate; |
+ private final NavigationClient mNavigationClient; |
+ private final NavigationHistory mHistory; |
+ private final NavigationAdapter mAdapter; |
+ private final TextView mShowHistoryView; |
+ |
+ private final int mListItemHeight; |
+ private final int mFaviconSize; |
+ private final int mPadding; |
+ |
+ private int mNativeNavigationPopup; |
+ |
+ /** |
+ * Delegate functionality required to operate on the history. |
+ */ |
+ public interface NavigationPopupDelegate { |
+ /** Open the complete browser history page */ |
+ void openHistory(); |
+ } |
+ |
+ /** |
+ * Constructs a new popup with the given history information. |
+ * |
+ * @param context The context used for building the popup. |
+ * @param delegate The delegate that handles history actions outside of the given |
+ * {@link ContentViewCore}. |
+ * @param navigationClient The owner of the history being displayed. |
+ * @param isForward Whether to request forward navigation entries. |
+ */ |
+ public NavigationPopup( |
+ Context context, NavigationPopupDelegate delegate, |
+ NavigationClient navigationClient, boolean isForward) { |
+ super(context, null, android.R.attr.popupMenuStyle); |
+ mContext = context; |
+ assert delegate != null : "NavigationPopup requires non-null delegate."; |
+ mDelegate = delegate; |
+ mNavigationClient = navigationClient; |
+ mHistory = mNavigationClient.getDirectedNavigationHistory( |
+ isForward, MAXIMUM_HISTORY_ITEMS); |
+ mAdapter = new NavigationAdapter(); |
+ |
+ float density = mContext.getResources().getDisplayMetrics().density; |
+ mListItemHeight = (int) (density * LIST_ITEM_HEIGHT_DP); |
+ mFaviconSize = (int) (density * FAVICON_SIZE_DP); |
+ mPadding = (int) (density * PADDING_DP); |
+ |
+ setModal(true); |
+ setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); |
+ setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); |
+ setOnItemClickListener(this); |
+ |
+ FixedViewInfo footerInfo = new ListView(context).new FixedViewInfo(); |
+ mShowHistoryView = createListItem(); |
+ mShowHistoryView.setText(R.string.show_history_label); |
+ footerInfo.isSelectable = true; |
+ footerInfo.view = mShowHistoryView; |
+ ArrayList<FixedViewInfo> footerInfoList = new ArrayList<FixedViewInfo>(1); |
+ footerInfoList.add(footerInfo); |
+ setAdapter(new HeaderViewListAdapter(null, footerInfoList, mAdapter)); |
+ } |
+ |
+ /** |
+ * @return The URL of the page for managing history. |
+ */ |
+ public static String getHistoryUrl() { |
+ return nativeGetHistoryUrl(); |
+ } |
+ |
+ /** |
+ * @return Whether a navigation popup is valid for the given page. |
+ */ |
+ public boolean shouldBeShown() { |
+ return mHistory.getEntryCount() > 0; |
+ } |
+ |
+ @Override |
+ public void show() { |
+ if (mNativeNavigationPopup == 0) initializeNative(); |
+ super.show(); |
+ } |
+ |
+ @Override |
+ public void dismiss() { |
+ if (mNativeNavigationPopup != 0) { |
+ nativeDestroy(mNativeNavigationPopup); |
+ mNativeNavigationPopup = 0; |
+ } |
+ super.dismiss(); |
+ } |
+ |
+ private void initializeNative() { |
+ ThreadUtils.assertOnUiThread(); |
+ mNativeNavigationPopup = nativeInit(); |
+ |
+ Set<String> requestedUrls = new HashSet<String>(); |
+ for (int i = 0; i < mHistory.getEntryCount(); i++) { |
+ NavigationEntry entry = mHistory.getEntryAtIndex(i); |
+ if (entry.getFavicon() != null) continue; |
+ String url = entry.getUrl(); |
+ if (!requestedUrls.contains(url)) { |
+ nativeFetchFaviconForUrl(mNativeNavigationPopup, url); |
+ requestedUrls.add(url); |
+ } |
+ } |
+ nativeFetchFaviconForUrl(mNativeNavigationPopup, nativeGetHistoryUrl()); |
+ } |
+ |
+ @CalledByNative |
+ private void onFaviconUpdated(String url, Object favicon) { |
+ for (int i = 0; i < mHistory.getEntryCount(); i++) { |
+ NavigationEntry entry = mHistory.getEntryAtIndex(i); |
+ if (TextUtils.equals(url, entry.getUrl())) entry.updateFavicon((Bitmap) favicon); |
+ } |
+ if (TextUtils.equals(url, nativeGetHistoryUrl())) { |
+ updateBitmapForTextView(mShowHistoryView, (Bitmap) favicon); |
+ } |
+ mAdapter.notifyDataSetChanged(); |
+ } |
+ |
+ @Override |
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
+ if (position == mHistory.getEntryCount()) { |
+ mDelegate.openHistory(); |
+ } else { |
+ NavigationEntry entry = (NavigationEntry) parent.getItemAtPosition(position); |
+ mNavigationClient.goToNavigationIndex(entry.getIndex()); |
+ } |
+ dismiss(); |
+ } |
+ |
+ private TextView createListItem() { |
+ TextView view = new TextView(mContext); |
+ view.setSingleLine(); |
+ view.setTextSize(TEXT_SIZE_SP); |
+ view.setMinimumHeight(mListItemHeight); |
+ view.setGravity(Gravity.CENTER_VERTICAL); |
+ view.setCompoundDrawablePadding(mPadding); |
+ view.setPadding(mPadding, 0, mPadding, 0); |
+ return view; |
+ } |
+ |
+ private void updateBitmapForTextView(TextView view, Bitmap bitmap) { |
+ Drawable faviconDrawable = null; |
+ if (bitmap != null) { |
+ faviconDrawable = new BitmapDrawable(mContext.getResources(), bitmap); |
+ ((BitmapDrawable) faviconDrawable).setGravity(Gravity.FILL); |
+ } else { |
+ faviconDrawable = new ColorDrawable(Color.TRANSPARENT); |
+ } |
+ faviconDrawable.setBounds(0, 0, mFaviconSize, mFaviconSize); |
+ view.setCompoundDrawables(faviconDrawable, null, null, null); |
+ } |
+ |
+ private class NavigationAdapter extends BaseAdapter { |
+ @Override |
+ public int getCount() { |
+ return mHistory.getEntryCount(); |
+ } |
+ |
+ @Override |
+ public Object getItem(int position) { |
+ return mHistory.getEntryAtIndex(position); |
+ } |
+ |
+ @Override |
+ public long getItemId(int position) { |
+ return ((NavigationEntry) getItem(position)).getIndex(); |
+ } |
+ |
+ @Override |
+ public View getView(int position, View convertView, ViewGroup parent) { |
+ TextView view; |
+ if (convertView != null && convertView instanceof TextView) { |
+ view = (TextView) convertView; |
+ } else { |
+ view = createListItem(); |
+ } |
+ NavigationEntry entry = (NavigationEntry) getItem(position); |
+ |
+ String entryText = entry.getTitle(); |
+ if (TextUtils.isEmpty(entryText)) entryText = entry.getVirtualUrl(); |
+ if (TextUtils.isEmpty(entryText)) entryText = entry.getUrl(); |
+ view.setText(entryText); |
+ updateBitmapForTextView(view, entry.getFavicon()); |
+ |
+ return view; |
+ } |
+ } |
+ |
+ private static native String nativeGetHistoryUrl(); |
+ |
+ private native int nativeInit(); |
+ private native void nativeDestroy(int nativeNavigationPopup); |
+ private native void nativeFetchFaviconForUrl(int nativeNavigationPopup, String url); |
+} |