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..be6c16f6e64f8466f153e4648c7c3c078e54efeb |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/NavigationPopup.java |
@@ -0,0 +1,229 @@ |
+// Copyright (c) 2012 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.drawable.BitmapDrawable; |
+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.chrome.R; |
+import org.chromium.content.browser.ContentViewCore; |
+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 String HISTORY_URL = "chrome://history/"; |
David Trainor- moved to gerrit
2013/01/03 18:57:31
Is this in some native resource?
Ted C
2013/01/03 19:56:41
Changed it so it's pulling it from native.
|
+ |
+ private static final int LIST_ITEM_HEIGHT_DP = 48; |
David Trainor- moved to gerrit
2013/01/03 18:57:31
These should probably be in res/dimens.xml
Ted C
2013/01/03 19:56:41
I'd prefer not until it needs to be configuration
|
+ 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; |
David Trainor- moved to gerrit
2013/01/03 18:57:31
res/values.xml?
|
+ |
+ private final Context mContext; |
+ private final NavigationPopupDelegate mDelegate; |
+ private final ContentViewCore mContentViewCore; |
+ 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(); |
David Trainor- moved to gerrit
2013/01/03 18:57:31
Does the implementer have to know what HISTORY_URL
Ted C
2013/01/03 19:56:41
I exposed a method getHistoryUrl that pulls it fro
|
+ } |
+ |
+ /** |
+ * 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 contentView The owner of the history being displayed. |
+ * @param isForward Whether to request forward navigation entries. |
+ */ |
+ public NavigationPopup( |
+ Context context, NavigationPopupDelegate delegate, |
+ ContentViewCore contentView, boolean isForward) { |
+ super(context, null, android.R.attr.popupMenuStyle); |
+ mContext = context; |
+ mDelegate = delegate; |
David Trainor- moved to gerrit
2013/01/03 18:57:31
assert delegate isn't null since we don't null che
Ted C
2013/01/03 19:56:41
Done.
|
+ mContentViewCore = contentView; |
+ mHistory = mContentViewCore.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(); |
David Trainor- moved to gerrit
2013/01/03 18:57:31
Gah this is how you build the FixedViewInfo? Weir
Ted C
2013/01/03 19:56:41
Lame I know...but it's the only way :-/. We do th
|
+ mShowHistoryView = createListItem(); |
+ mShowHistoryView.setText(R.string.show_history_label); |
David Trainor- moved to gerrit
2013/01/03 18:57:31
Does this string already exist natively?
Ted C
2013/01/03 19:56:41
I think in android we do different casing, so it's
|
+ footerInfo.isSelectable = true; |
+ footerInfo.view = mShowHistoryView; |
+ ArrayList<FixedViewInfo> footerInfoList = new ArrayList<FixedViewInfo>(1); |
+ footerInfoList.add(footerInfo); |
+ setAdapter(new HeaderViewListAdapter(null, footerInfoList, mAdapter)); |
+ } |
+ |
+ /** |
+ * @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() { |
+ 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, HISTORY_URL); |
+ } |
+ |
+ @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())) continue; |
David Trainor- moved to gerrit
2013/01/03 18:57:31
Might be a cleaner flow to get rid of ! and just m
Ted C
2013/01/03 19:56:41
Much nicer...done.
|
+ entry.updateFavicon((Bitmap) favicon); |
+ } |
+ if (TextUtils.equals(url, HISTORY_URL)) { |
+ 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); |
+ mContentViewCore.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) { |
+ BitmapDrawable faviconDrawable = null; |
+ if (bitmap != null) { |
+ faviconDrawable = new BitmapDrawable(mContext.getResources(), bitmap); |
+ faviconDrawable.setGravity(Gravity.FILL); |
+ 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 native int nativeInit(); |
+ private native void nativeDestroy(int nativeNavigationPopup); |
+ private native void nativeFetchFaviconForUrl(int nativeNavigationPopup, String url); |
+} |