|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.home; |
|
7 |
|
8 import java.lang.ref.WeakReference; |
|
9 |
|
10 import org.mozilla.gecko.R; |
|
11 import org.mozilla.gecko.Tab; |
|
12 import org.mozilla.gecko.Tabs; |
|
13 import org.mozilla.gecko.db.BrowserContract.Combined; |
|
14 import org.mozilla.gecko.db.BrowserDB.URLColumns; |
|
15 import org.mozilla.gecko.favicons.Favicons; |
|
16 import org.mozilla.gecko.favicons.OnFaviconLoadedListener; |
|
17 import org.mozilla.gecko.util.ThreadUtils; |
|
18 import org.mozilla.gecko.widget.FaviconView; |
|
19 |
|
20 import android.content.Context; |
|
21 import android.database.Cursor; |
|
22 import android.graphics.Bitmap; |
|
23 import android.text.TextUtils; |
|
24 import android.util.AttributeSet; |
|
25 import android.view.Gravity; |
|
26 import android.view.LayoutInflater; |
|
27 import android.widget.LinearLayout; |
|
28 import android.widget.TextView; |
|
29 |
|
30 public class TwoLinePageRow extends LinearLayout |
|
31 implements Tabs.OnTabsChangedListener { |
|
32 |
|
33 protected static final int NO_ICON = 0; |
|
34 |
|
35 private final TextView mTitle; |
|
36 private final TextView mUrl; |
|
37 |
|
38 private int mSwitchToTabIconId; |
|
39 private int mPageTypeIconId; |
|
40 |
|
41 private final FaviconView mFavicon; |
|
42 |
|
43 private boolean mShowIcons; |
|
44 private int mLoadFaviconJobId = Favicons.NOT_LOADING; |
|
45 |
|
46 // Only holds a reference to the FaviconView itself, so if the row gets |
|
47 // discarded while a task is outstanding, we'll leak less memory. |
|
48 private static class UpdateViewFaviconLoadedListener implements OnFaviconLoadedListener { |
|
49 private final WeakReference<FaviconView> view; |
|
50 public UpdateViewFaviconLoadedListener(FaviconView view) { |
|
51 this.view = new WeakReference<FaviconView>(view); |
|
52 } |
|
53 |
|
54 @Override |
|
55 public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) { |
|
56 FaviconView v = view.get(); |
|
57 if (v == null) { |
|
58 // Guess we stuck around after the TwoLinePageRow went away. |
|
59 return; |
|
60 } |
|
61 |
|
62 if (favicon == null) { |
|
63 v.showDefaultFavicon(); |
|
64 return; |
|
65 } |
|
66 |
|
67 v.updateImage(favicon, faviconURL); |
|
68 } |
|
69 } |
|
70 |
|
71 // Listener for handling Favicon loads. |
|
72 private final OnFaviconLoadedListener mFaviconListener; |
|
73 |
|
74 // The URL for the page corresponding to this view. |
|
75 private String mPageUrl; |
|
76 |
|
77 public TwoLinePageRow(Context context) { |
|
78 this(context, null); |
|
79 } |
|
80 |
|
81 public TwoLinePageRow(Context context, AttributeSet attrs) { |
|
82 super(context, attrs); |
|
83 |
|
84 setGravity(Gravity.CENTER_VERTICAL); |
|
85 |
|
86 LayoutInflater.from(context).inflate(R.layout.two_line_page_row, this); |
|
87 mTitle = (TextView) findViewById(R.id.title); |
|
88 mUrl = (TextView) findViewById(R.id.url); |
|
89 |
|
90 mSwitchToTabIconId = NO_ICON; |
|
91 mPageTypeIconId = NO_ICON; |
|
92 mShowIcons = true; |
|
93 |
|
94 mFavicon = (FaviconView) findViewById(R.id.icon); |
|
95 mFaviconListener = new UpdateViewFaviconLoadedListener(mFavicon); |
|
96 } |
|
97 |
|
98 @Override |
|
99 protected void onAttachedToWindow() { |
|
100 Tabs.registerOnTabsChangedListener(this); |
|
101 } |
|
102 |
|
103 @Override |
|
104 protected void onDetachedFromWindow() { |
|
105 // Tabs' listener array is safe to modify during use: its |
|
106 // iteration pattern is based on snapshots. |
|
107 Tabs.unregisterOnTabsChangedListener(this); |
|
108 } |
|
109 |
|
110 @Override |
|
111 public void onTabChanged(final Tab tab, final Tabs.TabEvents msg, final Object data) { |
|
112 switch(msg) { |
|
113 case ADDED: |
|
114 case CLOSED: |
|
115 case LOCATION_CHANGE: |
|
116 updateDisplayedUrl(); |
|
117 break; |
|
118 } |
|
119 } |
|
120 |
|
121 private void setTitle(String text) { |
|
122 mTitle.setText(text); |
|
123 } |
|
124 |
|
125 protected void setUrl(String text) { |
|
126 mUrl.setText(text); |
|
127 } |
|
128 |
|
129 protected void setUrl(int stringId) { |
|
130 mUrl.setText(stringId); |
|
131 } |
|
132 |
|
133 protected String getUrl() { |
|
134 return mPageUrl; |
|
135 } |
|
136 |
|
137 protected void setSwitchToTabIcon(int iconId) { |
|
138 if (mSwitchToTabIconId == iconId) { |
|
139 return; |
|
140 } |
|
141 |
|
142 mSwitchToTabIconId = iconId; |
|
143 mUrl.setCompoundDrawablesWithIntrinsicBounds(mSwitchToTabIconId, 0, mPageTypeIconId, 0); |
|
144 } |
|
145 |
|
146 private void setPageTypeIcon(int iconId) { |
|
147 if (mPageTypeIconId == iconId) { |
|
148 return; |
|
149 } |
|
150 |
|
151 mPageTypeIconId = iconId; |
|
152 mUrl.setCompoundDrawablesWithIntrinsicBounds(mSwitchToTabIconId, 0, mPageTypeIconId, 0); |
|
153 } |
|
154 |
|
155 /** |
|
156 * Stores the page URL, so that we can use it to replace "Switch to tab" if the open |
|
157 * tab changes or is closed. |
|
158 */ |
|
159 private void updateDisplayedUrl(String url) { |
|
160 mPageUrl = url; |
|
161 updateDisplayedUrl(); |
|
162 } |
|
163 |
|
164 /** |
|
165 * Replaces the page URL with "Switch to tab" if there is already a tab open with that URL. |
|
166 * Only looks for tabs that are either private or non-private, depending on the current |
|
167 * selected tab. |
|
168 */ |
|
169 protected void updateDisplayedUrl() { |
|
170 boolean isPrivate = Tabs.getInstance().getSelectedTab().isPrivate(); |
|
171 Tab tab = Tabs.getInstance().getFirstTabForUrl(mPageUrl, isPrivate); |
|
172 if (!mShowIcons || tab == null) { |
|
173 setUrl(mPageUrl); |
|
174 setSwitchToTabIcon(NO_ICON); |
|
175 } else { |
|
176 setUrl(R.string.switch_to_tab); |
|
177 setSwitchToTabIcon(R.drawable.ic_url_bar_tab); |
|
178 } |
|
179 } |
|
180 |
|
181 public void setShowIcons(boolean showIcons) { |
|
182 mShowIcons = showIcons; |
|
183 } |
|
184 |
|
185 public void updateFromCursor(Cursor cursor) { |
|
186 if (cursor == null) { |
|
187 return; |
|
188 } |
|
189 |
|
190 int titleIndex = cursor.getColumnIndexOrThrow(URLColumns.TITLE); |
|
191 final String title = cursor.getString(titleIndex); |
|
192 |
|
193 int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL); |
|
194 final String url = cursor.getString(urlIndex); |
|
195 |
|
196 if (mShowIcons) { |
|
197 final int bookmarkIdIndex = cursor.getColumnIndex(Combined.BOOKMARK_ID); |
|
198 if (bookmarkIdIndex != -1) { |
|
199 final long bookmarkId = cursor.getLong(bookmarkIdIndex); |
|
200 final int displayIndex = cursor.getColumnIndex(Combined.DISPLAY); |
|
201 |
|
202 final int display; |
|
203 if (displayIndex != -1) { |
|
204 display = cursor.getInt(displayIndex); |
|
205 } else { |
|
206 display = Combined.DISPLAY_NORMAL; |
|
207 } |
|
208 |
|
209 // The bookmark id will be 0 (null in database) when the url |
|
210 // is not a bookmark. |
|
211 if (bookmarkId == 0) { |
|
212 setPageTypeIcon(NO_ICON); |
|
213 } else if (display == Combined.DISPLAY_READER) { |
|
214 setPageTypeIcon(R.drawable.ic_url_bar_reader); |
|
215 } else { |
|
216 setPageTypeIcon(R.drawable.ic_url_bar_star); |
|
217 } |
|
218 } else { |
|
219 setPageTypeIcon(NO_ICON); |
|
220 } |
|
221 } |
|
222 |
|
223 // Use the URL instead of an empty title for consistency with the normal URL |
|
224 // bar view - this is the equivalent of getDisplayTitle() in Tab.java |
|
225 setTitle(TextUtils.isEmpty(title) ? url : title); |
|
226 |
|
227 // No point updating the below things if URL has not changed. Prevents evil Favicon flicker. |
|
228 if (url.equals(mPageUrl)) { |
|
229 return; |
|
230 } |
|
231 |
|
232 // Blank the Favicon, so we don't show the wrong Favicon if we scroll and miss DB. |
|
233 mFavicon.clearImage(); |
|
234 mLoadFaviconJobId = Favicons.getSizedFaviconForPageFromLocal(url, mFaviconListener); |
|
235 |
|
236 updateDisplayedUrl(url); |
|
237 } |
|
238 } |