1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.chrome.browser;
7 import android.content.Context;
8 import android.graphics.Bitmap;
9 import android.graphics.Color;
10 import android.graphics.drawable.BitmapDrawable;
11 import android.graphics.drawable.ColorDrawable;
12 import android.graphics.drawable.Drawable;
13 import android.text.TextUtils;
14 import android.view.Gravity;
15 import android.view.View;
16 import android.view.ViewGroup;
17 import android.widget.AdapterView;
18 import android.widget.BaseAdapter;
19 import android.widget.HeaderViewListAdapter;
20 import android.widget.ListPopupWindow;
21 import android.widget.PopupWindow;
22 import android.widget.TextView;
24 import org.chromium.base.CalledByNative;
25 import org.chromium.base.ThreadUtils;
26 import org.chromium.content.browser.NavigationClient;
27 import org.chromium.content_public.browser.NavigationEntry;
28 import org.chromium.content_public.browser.NavigationHistory;
29 import org.chromium.ui.base.LocalizationUtils;
31 import java.util.HashSet;
35 * A popup that handles displaying the navigation history for a given tab.
37 public class NavigationPopup extends ListPopupWindow implements AdapterView.OnItemClickListener {
39 private static final int FAVICON_SIZE_DP = 16;
41 private static final int MAXIMUM_HISTORY_ITEMS = 8;
43 private final Context mContext;
44 private final NavigationClient mNavigationClient;
45 private final NavigationHistory mHistory;
46 private final NavigationAdapter mAdapter;
47 private final ListItemFactory mListItemFactory;
49 private final int mFaviconSize;
51 private long mNativeNavigationPopup;
54 * Constructs a new popup with the given history information.
56 * @param context The context used for building the popup.
57 * @param navigationClient The owner of the history being displayed.
58 * @param isForward Whether to request forward navigation entries.
60 public NavigationPopup(
61 Context context, NavigationClient navigationClient, boolean isForward) {
62 super(context, null, android.R.attr.popupMenuStyle);
64 mNavigationClient = navigationClient;
65 mHistory = mNavigationClient.getDirectedNavigationHistory(
66 isForward, MAXIMUM_HISTORY_ITEMS);
67 mAdapter = new NavigationAdapter();
69 float density = mContext.getResources().getDisplayMetrics().density;
70 mFaviconSize = (int) (density * FAVICON_SIZE_DP);
73 setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
74 setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
75 setOnItemClickListener(this);
77 setAdapter(new HeaderViewListAdapter(null, null, mAdapter));
79 mListItemFactory = new ListItemFactory(context);
83 * @return Whether a navigation popup is valid for the given page.
85 public boolean shouldBeShown() {
86 return mHistory.getEntryCount() > 0;
91 if (mNativeNavigationPopup == 0) initializeNative();
96 public void dismiss() {
97 if (mNativeNavigationPopup != 0) {
98 nativeDestroy(mNativeNavigationPopup);
99 mNativeNavigationPopup = 0;
104 private void initializeNative() {
105 ThreadUtils.assertOnUiThread();
106 mNativeNavigationPopup = nativeInit();
108 Set<String> requestedUrls = new HashSet<String>();
109 for (int i = 0; i < mHistory.getEntryCount(); i++) {
110 NavigationEntry entry = mHistory.getEntryAtIndex(i);
111 if (entry.getFavicon() != null) continue;
112 String url = entry.getUrl();
113 if (!requestedUrls.contains(url)) {
114 nativeFetchFaviconForUrl(mNativeNavigationPopup, url);
115 requestedUrls.add(url);
118 nativeFetchFaviconForUrl(mNativeNavigationPopup, nativeGetHistoryUrl());
122 private void onFaviconUpdated(String url, Object favicon) {
123 for (int i = 0; i < mHistory.getEntryCount(); i++) {
124 NavigationEntry entry = mHistory.getEntryAtIndex(i);
125 if (TextUtils.equals(url, entry.getUrl())) entry.updateFavicon((Bitmap) favicon);
127 mAdapter.notifyDataSetChanged();
131 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
132 NavigationEntry entry = (NavigationEntry) parent.getItemAtPosition(position);
133 mNavigationClient.goToNavigationIndex(entry.getIndex());
137 private void updateBitmapForTextView(TextView view, Bitmap bitmap) {
138 Drawable faviconDrawable = null;
139 if (bitmap != null) {
140 faviconDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
141 ((BitmapDrawable) faviconDrawable).setGravity(Gravity.FILL);
143 faviconDrawable = new ColorDrawable(Color.TRANSPARENT);
145 faviconDrawable.setBounds(0, 0, mFaviconSize, mFaviconSize);
146 view.setCompoundDrawables(faviconDrawable, null, null, null);
149 private static class ListItemFactory {
150 private static final int LIST_ITEM_HEIGHT_DP = 48;
151 private static final int PADDING_DP = 8;
152 private static final int TEXT_SIZE_SP = 18;
153 private static final float FADE_LENGTH_DP = 25.0f;
154 private static final float FADE_STOP = 0.75f;
160 boolean mIsLayoutDirectionRTL;
163 public ListItemFactory(Context context) {
165 computeFadeDimensions();
168 private void computeFadeDimensions() {
169 // Fade with linear gradient starting 25dp from right margin.
170 // Reaches 0% opacity at 75% length. (Simulated with extra padding)
171 float density = mContext.getResources().getDisplayMetrics().density;
172 float fadeLength = (FADE_LENGTH_DP * density);
173 mFadeEdgeLength = (int) (fadeLength * FADE_STOP);
174 mFadePadding = (int) (fadeLength * (1 - FADE_STOP));
175 mListItemHeight = (int) (density * LIST_ITEM_HEIGHT_DP);
176 mPadding = (int) (density * PADDING_DP);
177 mIsLayoutDirectionRTL = LocalizationUtils.isLayoutRtl();
180 public TextView createListItem() {
181 TextView view = new TextView(mContext);
182 view.setFadingEdgeLength(mFadeEdgeLength);
183 view.setHorizontalFadingEdgeEnabled(true);
184 view.setSingleLine();
185 view.setTextSize(TEXT_SIZE_SP);
186 view.setMinimumHeight(mListItemHeight);
187 view.setGravity(Gravity.CENTER_VERTICAL);
188 view.setCompoundDrawablePadding(mPadding);
189 if (!mIsLayoutDirectionRTL) {
190 view.setPadding(mPadding, 0, mPadding + mFadePadding , 0);
192 view.setPadding(mPadding + mFadePadding, 0, mPadding, 0);
198 private class NavigationAdapter extends BaseAdapter {
200 public int getCount() {
201 return mHistory.getEntryCount();
205 public Object getItem(int position) {
206 return mHistory.getEntryAtIndex(position);
210 public long getItemId(int position) {
211 return ((NavigationEntry) getItem(position)).getIndex();
215 public View getView(int position, View convertView, ViewGroup parent) {
217 if (convertView != null && convertView instanceof TextView) {
218 view = (TextView) convertView;
220 view = mListItemFactory.createListItem();
222 NavigationEntry entry = (NavigationEntry) getItem(position);
224 String entryText = entry.getTitle();
225 if (TextUtils.isEmpty(entryText)) entryText = entry.getVirtualUrl();
226 if (TextUtils.isEmpty(entryText)) entryText = entry.getUrl();
227 view.setText(entryText);
228 updateBitmapForTextView(view, entry.getFavicon());
234 private static native String nativeGetHistoryUrl();
236 private native long nativeInit();
237 private native void nativeDestroy(long nativeNavigationPopup);
238 private native void nativeFetchFaviconForUrl(long nativeNavigationPopup, String url);