Chromium Code Reviews| 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); |
| +} |