Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / android / java / src / org / chromium / chrome / browser / NavigationPopup.java
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.
4
5 package org.chromium.chrome.browser;
6
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;
23
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;
30
31 import java.util.HashSet;
32 import java.util.Set;
33
34 /**
35  * A popup that handles displaying the navigation history for a given tab.
36  */
37 public class NavigationPopup extends ListPopupWindow implements AdapterView.OnItemClickListener {
38
39     private static final int FAVICON_SIZE_DP = 16;
40
41     private static final int MAXIMUM_HISTORY_ITEMS = 8;
42
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;
48
49     private final int mFaviconSize;
50
51     private long mNativeNavigationPopup;
52
53     /**
54      * Constructs a new popup with the given history information.
55      *
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.
59      */
60     public NavigationPopup(
61             Context context, NavigationClient navigationClient, boolean isForward) {
62         super(context, null, android.R.attr.popupMenuStyle);
63         mContext = context;
64         mNavigationClient = navigationClient;
65         mHistory = mNavigationClient.getDirectedNavigationHistory(
66                 isForward, MAXIMUM_HISTORY_ITEMS);
67         mAdapter = new NavigationAdapter();
68
69         float density = mContext.getResources().getDisplayMetrics().density;
70         mFaviconSize = (int) (density * FAVICON_SIZE_DP);
71
72         setModal(true);
73         setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
74         setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
75         setOnItemClickListener(this);
76
77         setAdapter(new HeaderViewListAdapter(null, null, mAdapter));
78
79         mListItemFactory = new ListItemFactory(context);
80     }
81
82     /**
83      * @return Whether a navigation popup is valid for the given page.
84      */
85     public boolean shouldBeShown() {
86         return mHistory.getEntryCount() > 0;
87     }
88
89     @Override
90     public void show() {
91         if (mNativeNavigationPopup == 0) initializeNative();
92         super.show();
93     }
94
95     @Override
96     public void dismiss() {
97         if (mNativeNavigationPopup != 0) {
98             nativeDestroy(mNativeNavigationPopup);
99             mNativeNavigationPopup = 0;
100         }
101         super.dismiss();
102     }
103
104     private void initializeNative() {
105         ThreadUtils.assertOnUiThread();
106         mNativeNavigationPopup = nativeInit();
107
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);
116             }
117         }
118         nativeFetchFaviconForUrl(mNativeNavigationPopup, nativeGetHistoryUrl());
119     }
120
121     @CalledByNative
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);
126         }
127         mAdapter.notifyDataSetChanged();
128     }
129
130     @Override
131     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
132         NavigationEntry entry = (NavigationEntry) parent.getItemAtPosition(position);
133         mNavigationClient.goToNavigationIndex(entry.getIndex());
134         dismiss();
135     }
136
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);
142         } else {
143             faviconDrawable = new ColorDrawable(Color.TRANSPARENT);
144         }
145         faviconDrawable.setBounds(0, 0, mFaviconSize, mFaviconSize);
146         view.setCompoundDrawables(faviconDrawable, null, null, null);
147     }
148
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;
155
156         int mFadeEdgeLength;
157         int mFadePadding;
158         int mListItemHeight;
159         int mPadding;
160         boolean mIsLayoutDirectionRTL;
161         Context mContext;
162
163         public ListItemFactory(Context context) {
164             mContext = context;
165             computeFadeDimensions();
166         }
167
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();
178         }
179
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);
191             } else {
192                 view.setPadding(mPadding + mFadePadding, 0, mPadding, 0);
193             }
194             return view;
195         }
196     }
197
198     private class NavigationAdapter extends BaseAdapter {
199         @Override
200         public int getCount() {
201             return mHistory.getEntryCount();
202         }
203
204         @Override
205         public Object getItem(int position) {
206             return mHistory.getEntryAtIndex(position);
207         }
208
209         @Override
210         public long getItemId(int position) {
211             return ((NavigationEntry) getItem(position)).getIndex();
212         }
213
214         @Override
215         public View getView(int position, View convertView, ViewGroup parent) {
216             TextView view;
217             if (convertView != null && convertView instanceof TextView) {
218                 view = (TextView) convertView;
219             } else {
220                 view = mListItemFactory.createListItem();
221             }
222             NavigationEntry entry = (NavigationEntry) getItem(position);
223
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());
229
230             return view;
231         }
232     }
233
234     private static native String nativeGetHistoryUrl();
235
236     private native long nativeInit();
237     private native void nativeDestroy(long nativeNavigationPopup);
238     private native void nativeFetchFaviconForUrl(long nativeNavigationPopup, String url);
239 }