blob: d26f0afb15e0a4e302cd7fba0f04c47f5d3c3bf8 [file] [log] [blame]
Leon Scroggins1f005d32009-08-10 17:36:42 -04001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.browser;
18
19import android.content.Context;
20import android.database.DataSetObserver;
21import android.graphics.drawable.Drawable;
22import android.util.AttributeSet;
23import android.util.Log;
24import android.view.LayoutInflater;
25import android.view.MotionEvent;
26import android.view.View;
27import android.view.ViewGroup;
28import android.webkit.WebView;
29import android.widget.AdapterView;
30import android.widget.Gallery;
31import android.widget.SpinnerAdapter;
32
33import java.util.Vector;
34
35/**
36 * The TitleBarSet holds a TitleBar for each open "tab" in the browser.
37 */
38public class TitleBarSet extends Gallery
39 implements AdapterView.OnItemSelectedListener {
40 private Vector<TitleBar> mTitleBars;
41 private BrowserActivity mBrowserActivity;
42 private View mNewButton;
43 private int mCount;
44 private TitleAdapter mTitleAdapter;
45 private boolean mIgnoreSelectedListener;
46 private MotionEvent mLastTouchUp;
47
48 public TitleBarSet(Context context) {
49 this(context, null);
50 }
51
52 public TitleBarSet(Context context, AttributeSet attrs) {
53 super(context, attrs);
54 mTitleBars = new Vector<TitleBar>(TabControl.MAX_TABS);
55 mCount = 0;
56 // Now create the Plus button that goes on the right.
57 LayoutInflater factory = LayoutInflater.from(context);
58 mNewButton = factory.inflate(R.layout.new_window_button, null);
59 mTitleAdapter = new TitleAdapter();
60 setAdapter(mTitleAdapter);
61 setCallbackDuringFling(false);
62 setCallbackOnUnselectedItemClick(true);
63 setSpacing(0);
64 setOnItemSelectedListener(this);
65 }
66
67 /**
68 * Add a tab/titlebar to our set. Called when BrowserActivity adds a new
69 * Tab to its TabControl.
70 * @param view WebView associated with this tab. Used to determine whether
71 * updates are going to the correct place.
72 * @param selected Whether to set the new tab to be selected.
73 */
74 /* package */ void addTab(WebView view, boolean selected) {
75 if (TabControl.MAX_TABS == mCount) {
76 return;
77 }
78 int newSelection = mCount;
79 TitleBar titleBar = new TitleBar(getContext(), view);
80 mTitleBars.add(titleBar);
81 mCount++;
82 if (TabControl.MAX_TABS == mCount) {
83 mNewButton.setEnabled(false);
84 }
85 // Need to refresh our list
86 setAdapter(mTitleAdapter);
87 mIgnoreSelectedListener = true;
88 // No need to call onItemSelected, since the Tab in BrowserActivity has
89 // already been changed.
90 if (selected) {
91 setSelection(newSelection);
92 }
93 mIgnoreSelectedListener = false;
94 }
95
96 /**
97 * Convenience method to get a particular title bar.
98 */
99 private TitleBar getTitleBarAt(int position) {
100 if (position < 0 || position >= mCount) {
101 return null;
102 }
103 return (TitleBar) mTitleBars.elementAt(position);
104 }
105
106 /**
107 * Implementation for OnItemSelectedListener
108 */
109 public void onItemSelected(AdapterView<?> parent, View view, int position,
110 long id) {
111 if (mIgnoreSelectedListener || !(view instanceof TitleBar)) {
112 return;
113 }
114 mBrowserActivity.switchToTab(position);
115 // In case the WebView finished loading while this TitleBar was out of
116 // focus, make sure all its data is up to date
117 TitleBar titleBar = getTitleBarAt(position);
118 WebView webview = titleBar.getWebView();
119 if (webview == null) {
120 // FIXME: Possible that the tab needs to be restored.
121 return;
122 }
123 if (webview.getProgress() == 100) {
124 titleBar.setProgress(100);
125 titleBar.setTitleAndUrl(webview.getTitle(), webview.getUrl());
126 // FIXME: Pass in a bitmap, so we can always update the bitmap
127 // properly
128 //titleBar.setFavicon(webview.getFavicon());
129 }
130 }
131
132 /**
133 * Implementation for OnItemSelectedListener
134 */
135 public void onNothingSelected(AdapterView<?> parent) {
136 // do nothing
137 }
138
139 /**
140 * Override from GestureDetector.OnGestureListener. Store the MotionEvent
141 * so performItemClick can know how to handle the click.
142 */
143 public boolean onSingleTapUp(MotionEvent e) {
144 mLastTouchUp = e;
145 // super.onSingleTapUp will call performItemClick
146 boolean result = super.onSingleTapUp(e);
147 mLastTouchUp = null;
148 return result;
149 }
150
151 /**
152 * Override from View to ensure that the TitleBars get resized to match
153 * the new screen width
154 */
155 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
156 super.onSizeChanged(w, h, oldw, oldh);
Leon Scroggins5a7e6c62009-08-14 16:32:43 -0400157 int selection = getSelectedItemPosition();
Leon Scroggins1f005d32009-08-10 17:36:42 -0400158 // Need to make sure getView gets called again
Leon Scroggins1f005d32009-08-10 17:36:42 -0400159 setAdapter(mTitleAdapter);
Leon Scroggins5a7e6c62009-08-14 16:32:43 -0400160 // Stay on the same tab
161 setCurrentTab(selection);
Leon Scroggins1f005d32009-08-10 17:36:42 -0400162 }
163
164 /**
165 * Override from AdapterView. Using simple OnClickListeners overrides
166 * the GestureDetector.OnGestureListener, so we handle it here.
167 */
168 public boolean performItemClick(View view, int position, long id) {
169 if (!(view instanceof TitleBar)) {
170 // For new window button
171 return super.performItemClick(view, position, id);
172 }
173 // If we have no mLastTouchUp, this was not called from onSingleTapUp,
174 // so ignore it.
175 if (null == mLastTouchUp) {
176 return false;
177 }
178 TitleBar titleBar = (TitleBar) view;
179 // If the user clicks on a view which is not selected, the Gallery will
180 // take care of making it selected.
181 if (titleBar != getTitleBarAt(position)) {
182 return false;
183 }
184 if (titleBar.hitRightButton((int) mLastTouchUp.getX() - mScrollX,
185 (int) mLastTouchUp.getY() - mScrollY)) {
186 if (titleBar.isInLoad()) {
187 WebView webView = titleBar.getWebView();
188 if (null == webView) {
189 // FIXME: How did we get into this situation?
190 return false;
191 }
192 webView.stopLoading();
193 } else {
194 mBrowserActivity.closeCurrentWindow();
195 }
196 } else {
Leon Scroggins160a7e72009-08-14 18:28:01 -0400197 mBrowserActivity.bookmarksOrHistoryPicker(false, false);
Leon Scroggins1f005d32009-08-10 17:36:42 -0400198 }
199 return true;
200 }
201
202 /**
203 * Remove the tab at the given position.
204 */
205 /* package */ void removeTab(int position) {
206 if (TabControl.MAX_TABS == mCount) {
207 mNewButton.setEnabled(true);
208 }
209 mTitleBars.remove(position);
210 mCount--;
211 // Need to refresh our list
212 setAdapter(mTitleAdapter);
213 }
214
215 /**
216 * Convenience method to get the currently selected title bar.
217 */
218 private TitleBar selectedTitleBar() {
219 return getTitleBarAt(getSelectedItemPosition());
220 }
221
222 /**
223 * Set the owning BrowserActivity. Necessary so that we can call methods
224 * on it.
225 */
226 /* package */ void setBrowserActivity(final BrowserActivity ba) {
227 mBrowserActivity = ba;
228 View.OnClickListener listener = new View.OnClickListener() {
229 public void onClick(View v) {
230 ba.openTabAndShow(BrowserActivity.EMPTY_URL_DATA, false, null);
Leon Scroggins160a7e72009-08-14 18:28:01 -0400231 ba.bookmarksOrHistoryPicker(false, true);
Leon Scroggins1f005d32009-08-10 17:36:42 -0400232 }
233 };
234 mNewButton.findViewById(R.id.button).setOnClickListener(listener);
235 }
236
237 /**
238 * Change to the tab at the new position.
239 */
240 /* package */ void setCurrentTab(int position) {
241 mIgnoreSelectedListener = true;
242 setSelection(position);
243 mIgnoreSelectedListener = false;
244 }
245
246 /**
247 * Update the Favicon of the currently selected tab.
248 * @param d The new Drawable for the Favicon
249 * @param topWindow The WebView which posted the update. If it does not
250 * match the WebView of the currently selected tab, do
251 * nothing, since that tab is not being displayed.
252 */
253 /* package */ void setFavicon(Drawable d, WebView topWindow) {
254 TitleBar current = selectedTitleBar();
255 if (current != null && current.getWebView() == topWindow) {
256 current.setFavicon(d);
257 }
258 }
259
260 /**
261 * Update the lock icon of the currently selected tab.
262 * @param d The new Drawable for the lock icon
263 * @param topWindow The WebView which posted the update. If it does not
264 * match the WebView of the currently selected tab, do
265 * nothing, since that tab is not being displayed.
266 */
267 /* package */ void setLock(Drawable d, WebView topWindow) {
268 TitleBar current = selectedTitleBar();
269 if (current != null && current.getWebView() == topWindow) {
270 current.setLock(d);
271 }
272 }
273 /**
274 * Update the progress of the currently selected tab.
275 * @param newProgress The progress, between 0 and 100, of the current tab.
276 * @param topWindow The WebView which posted the update. If it does not
277 * match the WebView of the currently selected tab, do
278 * nothing, since that tab is not being displayed.
279 */
280 /* package */ void setProgress(int newProgress, WebView topWindow) {
281 TitleBar current = selectedTitleBar();
282 if (current != null && current.getWebView() == topWindow) {
283 current.setProgress(newProgress);
284 }
285 }
286 /**
287 * Update the title and URL of the currently selected tab.
288 * @param title The title of the webpage
289 * @param url The URL of the webpage
290 * @param topWindow The WebView which posted the update. If it does not
291 * match the WebView of the currently selected tab, do
292 * nothing, since that tab is not being displayed.
293 */
294 /* package */ void setTitleAndUrl(CharSequence title, CharSequence url,
295 WebView topWindow) {
296 TitleBar current = selectedTitleBar();
297 if (current != null && current.getWebView() == topWindow) {
298 current.setTitleAndUrl(title, url);
299 }
300 }
301
302 // FIXME: Remove
303 /* package */ void setToTabPicker() {
304 TitleBar current = selectedTitleBar();
305 if (current != null) {
306 current.setToTabPicker();
307 }
308 }
309
310 /**
311 * Custom adapter which provides the TitleBars and the NewButton to the
312 * Gallery.
313 */
314 private class TitleAdapter implements SpinnerAdapter {
315 public View getDropDownView(int position, View convertView,
316 ViewGroup parent) {
317 return null;
318 }
319 public void registerDataSetObserver(DataSetObserver observer) {}
320 public void unregisterDataSetObserver(DataSetObserver observer) {}
321 public int getCount() {
322 // To account for new window
323 return mCount + 1;
324 }
325 public Object getItem(int position) {
326 return null;
327 }
328 public long getItemId(int position) {
329 return position;
330 }
331 public boolean hasStableIds() {
332 return true;
333 }
334 public View getView(int position, View convertView, ViewGroup parent) {
335 if (mCount == position) {
336 return mNewButton;
337 }
338 TitleBar titleBar = getTitleBarAt(position);
339 Gallery.LayoutParams lp;
340 int desiredWidth = TitleBarSet.this.getWidth()
341 - (2 * mNewButton.getWidth());
342 ViewGroup.LayoutParams old = titleBar.getLayoutParams();
343 if (old == null || !(old instanceof Gallery.LayoutParams)) {
344 lp = new Gallery.LayoutParams(desiredWidth,
345 ViewGroup.LayoutParams.WRAP_CONTENT);
346 titleBar.setLayoutParams(lp);
347 } else {
348 lp = (Gallery.LayoutParams) old;
349 if (lp.width != desiredWidth) {
350 lp.width = desiredWidth;
351 titleBar.setLayoutParams(lp);
352 requestLayout();
353 }
354 }
355 return titleBar;
356 }
357 public int getItemViewType(int position) {
358 // We are managing our own views.
359 return AdapterView.ITEM_VIEW_TYPE_IGNORE;
360 }
361 public int getViewTypeCount() {
362 return 1;
363 }
364 public boolean isEmpty() {
365 // Will never be empty, because the NewButton is always there
366 // (though sometimes disabled).
367 return false;
368 }
369 }
370}