blob: 7377a1e73f63384b51fd86f2d18617998edfb74b [file] [log] [blame]
The Android Open Source Project0c908882009-03-03 19:32:16 -08001/*
2 * Copyright (C) 2007 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
Mike Reede5073c22010-01-29 11:31:39 -050019import android.graphics.Bitmap;
20import android.graphics.BitmapFactory;
21import android.graphics.BitmapShader;
22import android.graphics.Paint;
23import android.graphics.Shader;
The Android Open Source Project0c908882009-03-03 19:32:16 -080024import android.os.Bundle;
The Android Open Source Project0c908882009-03-03 19:32:16 -080025import android.util.Log;
The Android Open Source Project0c908882009-03-03 19:32:16 -080026import android.view.View;
The Android Open Source Project0c908882009-03-03 19:32:16 -080027import android.webkit.WebBackForwardList;
The Android Open Source Project0c908882009-03-03 19:32:16 -080028import android.webkit.WebView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080029
30import java.io.File;
31import java.util.ArrayList;
Elliott Slaughter3d6df162010-08-25 13:17:44 -070032import java.util.HashMap;
The Android Open Source Project0c908882009-03-03 19:32:16 -080033import java.util.Vector;
34
35class TabControl {
36 // Log Tag
37 private static final String LOGTAG = "TabControl";
38 // Maximum number of tabs.
Grace Kloba22ac16e2009-10-07 18:00:23 -070039 private static final int MAX_TABS = 8;
The Android Open Source Project0c908882009-03-03 19:32:16 -080040 // Private array of WebViews that are used as tabs.
41 private ArrayList<Tab> mTabs = new ArrayList<Tab>(MAX_TABS);
42 // Queue of most recently viewed tabs.
43 private ArrayList<Tab> mTabQueue = new ArrayList<Tab>(MAX_TABS);
44 // Current position in mTabs.
45 private int mCurrentTab = -1;
46 // A private instance of BrowserActivity to interface with when adding and
47 // switching between tabs.
48 private final BrowserActivity mActivity;
The Android Open Source Project0c908882009-03-03 19:32:16 -080049 // Directory to store thumbnails for each WebView.
50 private final File mThumbnailDir;
51
52 /**
53 * Construct a new TabControl object that interfaces with the given
54 * BrowserActivity instance.
55 * @param activity A BrowserActivity instance that TabControl will interface
56 * with.
57 */
58 TabControl(BrowserActivity activity) {
59 mActivity = activity;
The Android Open Source Project0c908882009-03-03 19:32:16 -080060 mThumbnailDir = activity.getDir("thumbnails", 0);
61 }
62
63 File getThumbnailDir() {
64 return mThumbnailDir;
65 }
66
67 BrowserActivity getBrowserActivity() {
68 return mActivity;
69 }
70
71 /**
72 * Return the current tab's main WebView. This will always return the main
73 * WebView for a given tab and not a subwindow.
74 * @return The current tab's WebView.
75 */
76 WebView getCurrentWebView() {
77 Tab t = getTab(mCurrentTab);
78 if (t == null) {
79 return null;
80 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070081 return t.getWebView();
Ben Murdochbff2d602009-07-01 20:19:05 +010082 }
83
84 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080085 * Return the current tab's top-level WebView. This can return a subwindow
86 * if one exists.
87 * @return The top-level WebView of the current tab.
88 */
89 WebView getCurrentTopWebView() {
90 Tab t = getTab(mCurrentTab);
91 if (t == null) {
92 return null;
93 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070094 return t.getTopWindow();
The Android Open Source Project0c908882009-03-03 19:32:16 -080095 }
96
97 /**
98 * Return the current tab's subwindow if it exists.
99 * @return The subwindow of the current tab or null if it doesn't exist.
100 */
101 WebView getCurrentSubWindow() {
102 Tab t = getTab(mCurrentTab);
103 if (t == null) {
104 return null;
105 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700106 return t.getSubWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800107 }
108
109 /**
110 * Return the tab at the specified index.
111 * @return The Tab for the specified index or null if the tab does not
112 * exist.
113 */
114 Tab getTab(int index) {
115 if (index >= 0 && index < mTabs.size()) {
116 return mTabs.get(index);
117 }
118 return null;
119 }
120
121 /**
122 * Return the current tab.
123 * @return The current tab.
124 */
125 Tab getCurrentTab() {
126 return getTab(mCurrentTab);
127 }
128
129 /**
130 * Return the current tab index.
131 * @return The current tab index
132 */
133 int getCurrentIndex() {
134 return mCurrentTab;
135 }
Michael Kolbfe251992010-07-08 15:41:55 -0700136
The Android Open Source Project0c908882009-03-03 19:32:16 -0800137 /**
138 * Given a Tab, find it's index
139 * @param Tab to find
140 * @return index of Tab or -1 if not found
141 */
142 int getTabIndex(Tab tab) {
Patrick Scottae641ac2009-04-20 13:51:49 -0400143 if (tab == null) {
144 return -1;
145 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800146 return mTabs.indexOf(tab);
147 }
148
Grace Kloba22ac16e2009-10-07 18:00:23 -0700149 boolean canCreateNewTab() {
150 return MAX_TABS != mTabs.size();
151 }
152
The Android Open Source Project0c908882009-03-03 19:32:16 -0800153 /**
Elliott Slaughtere440a882010-08-20 13:54:45 -0700154 * Returns true if there are any incognito tabs open.
155 * @return True when any incognito tabs are open, false otherwise.
156 */
157 boolean hasAnyOpenIncognitoTabs() {
158 for (Tab tab : mTabs) {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700159 if (tab.getWebView() != null && tab.getWebView().isPrivateBrowsingEnabled()) {
Elliott Slaughtere440a882010-08-20 13:54:45 -0700160 return true;
161 }
162 }
163 return false;
164 }
165
166 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700167 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800168 * @return The newly createTab or null if we have reached the maximum
169 * number of open tabs.
170 */
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700171 Tab createNewTab(boolean closeOnExit, String appId, String url,
172 boolean privateBrowsing) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800173 int size = mTabs.size();
174 // Return false if we have maxed out on tabs
175 if (MAX_TABS == size) {
176 return null;
177 }
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700178 final WebView w = createNewWebView(privateBrowsing);
Steve Block2bc69912009-07-30 14:45:13 +0100179
The Android Open Source Project0c908882009-03-03 19:32:16 -0800180 // Create a new tab and add it to the tab list
Grace Kloba22ac16e2009-10-07 18:00:23 -0700181 Tab t = new Tab(mActivity, w, closeOnExit, appId, url);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800182 mTabs.add(t);
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700183 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700184 t.putInBackground();
Michael Kolbfe251992010-07-08 15:41:55 -0700185 if (mTabChangeListener != null) {
186 mTabChangeListener.onNewTab(t);
187 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800188 return t;
189 }
190
191 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700192 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700193 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700194 */
195 Tab createNewTab() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700196 return createNewTab(false, null, null, false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700197 }
198
199 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500200 * Remove the parent child relationships from all tabs.
201 */
202 void removeParentChildRelationShips() {
203 for (Tab tab : mTabs) {
204 tab.removeFromTree();
205 }
206 }
207
208 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800209 * Remove the tab from the list. If the tab is the current tab shown, the
210 * last created tab will be shown.
211 * @param t The tab to be removed.
212 */
213 boolean removeTab(Tab t) {
214 if (t == null) {
215 return false;
216 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700217
Patrick Scottd944d4d2010-01-27 16:39:11 -0500218 // Grab the current tab before modifying the list.
219 Tab current = getCurrentTab();
220
221 // Remove t from our list of tabs.
222 mTabs.remove(t);
223
224 // Put the tab in the background only if it is the current one.
225 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700226 t.putInBackground();
227 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500228 } else {
229 // If a tab that is earlier in the list gets removed, the current
230 // index no longer points to the correct tab.
231 mCurrentTab = getTabIndex(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800232 }
233
Grace Kloba22ac16e2009-10-07 18:00:23 -0700234 // destroy the tab
235 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800236 // clear it's references to parent and children
237 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800238
239 // The tab indices have shifted, update all the saved state so we point
240 // to the correct index.
241 for (Tab tab : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700242 Vector<Tab> children = tab.getChildTabs();
243 if (children != null) {
244 for (Tab child : children) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800245 child.setParentTab(tab);
246 }
247 }
248 }
249
The Android Open Source Project0c908882009-03-03 19:32:16 -0800250 // Remove it from the queue of viewed tabs.
251 mTabQueue.remove(t);
Michael Kolbfe251992010-07-08 15:41:55 -0700252 if (mTabChangeListener != null) {
253 mTabChangeListener.onRemoveTab(t);
254 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800255 return true;
256 }
257
258 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800259 * Destroy all the tabs and subwindows
260 */
261 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800262 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700263 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800264 }
265 mTabs.clear();
266 mTabQueue.clear();
267 }
268
269 /**
270 * Returns the number of tabs created.
271 * @return The number of tabs created.
272 */
273 int getTabCount() {
274 return mTabs.size();
275 }
276
The Android Open Source Project0c908882009-03-03 19:32:16 -0800277
278 /**
279 * Save the state of all the Tabs.
280 * @param outState The Bundle to save the state to.
281 */
282 void saveState(Bundle outState) {
283 final int numTabs = getTabCount();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700284 outState.putInt(Tab.NUMTABS, numTabs);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800285 final int index = getCurrentIndex();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700286 outState.putInt(Tab.CURRTAB, (index >= 0 && index < numTabs) ? index : 0);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800287 for (int i = 0; i < numTabs; i++) {
288 final Tab t = getTab(i);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700289 if (t.saveState()) {
290 outState.putBundle(Tab.WEBVIEW + i, t.getSavedState());
The Android Open Source Project0c908882009-03-03 19:32:16 -0800291 }
292 }
293 }
294
295 /**
296 * Restore the state of all the tabs.
297 * @param inState The saved state of all the tabs.
298 * @return True if there were previous tabs that were restored. False if
299 * there was no saved state or restoring the state failed.
300 */
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700301 boolean restoreState(Bundle inState, boolean dontRestoreIncognitoTabs) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800302 final int numTabs = (inState == null)
Grace Kloba22ac16e2009-10-07 18:00:23 -0700303 ? -1 : inState.getInt(Tab.NUMTABS, -1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800304 if (numTabs == -1) {
305 return false;
306 } else {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700307 final int oldCurrentTab = inState.getInt(Tab.CURRTAB, -1);
308
309 // Determine whether the saved current tab can be restored, and
310 // if not, which tab will take its place.
311 int currentTab = -1;
312 if (!dontRestoreIncognitoTabs
313 || !inState.getBundle(Tab.WEBVIEW + oldCurrentTab).getBoolean(Tab.INCOGNITO)) {
314 currentTab = oldCurrentTab;
315 } else {
316 for (int i = 0; i < numTabs; i++) {
317 if (!inState.getBundle(Tab.WEBVIEW + i).getBoolean(Tab.INCOGNITO)) {
318 currentTab = i;
319 break;
320 }
321 }
322 }
323 if (currentTab < 0) {
324 return false;
325 }
326
327 // Map saved tab indices to new indices, in case any incognito tabs
328 // need to not be restored.
329 HashMap<Integer, Integer> originalTabIndices = new HashMap<Integer, Integer>();
330 originalTabIndices.put(-1, -1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800331 for (int i = 0; i < numTabs; i++) {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700332 Bundle state = inState.getBundle(Tab.WEBVIEW + i);
333
334 if (dontRestoreIncognitoTabs && state != null && state.getBoolean(Tab.INCOGNITO)) {
335 originalTabIndices.put(i, -1);
336 } else if (i == currentTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700337 Tab t = createNewTab();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800338 // Me must set the current tab before restoring the state
339 // so that all the client classes are set.
340 setCurrentTab(t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700341 if (!t.restoreState(state)) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800342 Log.w(LOGTAG, "Fail in restoreState, load home page.");
Grace Kloba22ac16e2009-10-07 18:00:23 -0700343 t.getWebView().loadUrl(BrowserSettings.getInstance()
The Android Open Source Project0c908882009-03-03 19:32:16 -0800344 .getHomePage());
345 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700346 originalTabIndices.put(i, getTabCount() - 1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800347 } else {
348 // Create a new tab and don't restore the state yet, add it
349 // to the tab list
Grace Kloba22ac16e2009-10-07 18:00:23 -0700350 Tab t = new Tab(mActivity, null, false, null, null);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700351 if (state != null) {
352 t.setSavedState(state);
353 t.populatePickerDataFromSavedState();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700354 // Need to maintain the app id and original url so we
355 // can possibly reuse this tab.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700356 t.setAppId(state.getString(Tab.APPID));
357 t.setOriginalUrl(state.getString(Tab.ORIGINALURL));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800358 }
359 mTabs.add(t);
Grace Klobaf56f68d2010-04-11 22:53:42 -0700360 // added the tab to the front as they are not current
361 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700362 originalTabIndices.put(i, getTabCount() - 1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800363 }
364 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700365
The Android Open Source Project0c908882009-03-03 19:32:16 -0800366 // Rebuild the tree of tabs. Do this after all tabs have been
367 // created/restored so that the parent tab exists.
368 for (int i = 0; i < numTabs; i++) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700369 final Bundle b = inState.getBundle(Tab.WEBVIEW + i);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800370 final Tab t = getTab(i);
371 if (b != null && t != null) {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700372 final Integer parentIndex = originalTabIndices.get(b.getInt(Tab.PARENTTAB, -1));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800373 if (parentIndex != -1) {
374 final Tab parent = getTab(parentIndex);
375 if (parent != null) {
376 parent.addChildTab(t);
377 }
378 }
379 }
380 }
381 }
382 return true;
383 }
384
385 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700386 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800387 * WebView cache;
388 */
389 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700390 if (getTabCount() == 0) return;
391
Grace Klobaf56f68d2010-04-11 22:53:42 -0700392 // free the least frequently used background tabs
393 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
394 if (tabs.size() > 0) {
395 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
396 for (Tab t : tabs) {
397 // store the WebView's state.
398 t.saveState();
399 // destroy the tab
400 t.destroy();
401 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800402 return;
403 }
404
Derek Sollenberger4433d032009-06-10 15:37:21 -0400405 // free the WebView's unused memory (this includes the cache)
406 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800407 WebView view = getCurrentWebView();
408 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400409 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800410 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800411 }
412
Grace Klobaf56f68d2010-04-11 22:53:42 -0700413 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
414 Vector<Tab> tabsToGo = new Vector<Tab>();
415
Patrick Scott2a67de42009-08-31 09:48:37 -0400416 // Don't do anything if we only have 1 tab or if the current tab is
417 // null.
418 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700419 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800420 }
421
Grace Klobaf56f68d2010-04-11 22:53:42 -0700422 if (mTabQueue.size() == 0) {
423 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800424 }
425
Grace Klobaf56f68d2010-04-11 22:53:42 -0700426 // Rip through the queue starting at the beginning and tear down half of
427 // available tabs which are not the current tab or the parent of the
428 // current tab.
429 int openTabCount = 0;
430 for (Tab t : mTabQueue) {
431 if (t != null && t.getWebView() != null) {
432 openTabCount++;
433 if (t != current && t != current.getParentTab()) {
434 tabsToGo.add(t);
435 }
436 }
437 }
438
439 openTabCount /= 2;
440 if (tabsToGo.size() > openTabCount) {
441 tabsToGo.setSize(openTabCount);
442 }
443
444 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800445 }
446
The Android Open Source Project0c908882009-03-03 19:32:16 -0800447 /**
448 * Show the tab that contains the given WebView.
449 * @param view The WebView used to find the tab.
450 */
451 Tab getTabFromView(WebView view) {
452 final int size = getTabCount();
453 for (int i = 0; i < size; i++) {
454 final Tab t = getTab(i);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700455 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800456 return t;
457 }
458 }
459 return null;
460 }
461
462 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700463 * Return the tab with the matching application id.
464 * @param id The application identifier.
465 */
466 Tab getTabFromId(String id) {
467 if (id == null) {
468 return null;
469 }
470 final int size = getTabCount();
471 for (int i = 0; i < size; i++) {
472 final Tab t = getTab(i);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700473 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700474 return t;
475 }
476 }
477 return null;
478 }
479
Grace Kloba22ac16e2009-10-07 18:00:23 -0700480 /**
481 * Stop loading in all opened WebView including subWindows.
482 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700483 void stopAllLoading() {
484 final int size = getTabCount();
485 for (int i = 0; i < size; i++) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700486 final Tab t = getTab(i);
487 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700488 if (webview != null) {
489 webview.stopLoading();
490 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700491 final WebView subview = t.getSubWebView();
492 if (subview != null) {
493 webview.stopLoading();
494 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700495 }
496 }
497
Patrick Scottcd115892009-07-16 09:42:58 -0400498 // This method checks if a non-app tab (one created within the browser)
499 // matches the given url.
500 private boolean tabMatchesUrl(Tab t, String url) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700501 if (t.getAppId() != null) {
Patrick Scottcd115892009-07-16 09:42:58 -0400502 return false;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700503 }
504 WebView webview = t.getWebView();
505 if (webview == null) {
Patrick Scottcd115892009-07-16 09:42:58 -0400506 return false;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700507 } else if (url.equals(webview.getUrl())
508 || url.equals(webview.getOriginalUrl())) {
Patrick Scottcd115892009-07-16 09:42:58 -0400509 return true;
510 }
511 return false;
512 }
513
514 /**
515 * Return the tab that has no app id associated with it and the url of the
516 * tab matches the given url.
517 * @param url The url to search for.
518 */
519 Tab findUnusedTabWithUrl(String url) {
520 if (url == null) {
521 return null;
522 }
523 // Check the current tab first.
524 Tab t = getCurrentTab();
525 if (t != null && tabMatchesUrl(t, url)) {
526 return t;
527 }
528 // Now check all the rest.
529 final int size = getTabCount();
530 for (int i = 0; i < size; i++) {
531 t = getTab(i);
532 if (tabMatchesUrl(t, url)) {
533 return t;
534 }
535 }
536 return null;
537 }
538
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700539 /**
540 * Recreate the main WebView of the given tab. Returns true if the WebView
Leon Scroggins6eac63e2010-03-15 18:19:14 -0400541 * requires a load, whether it was due to the fact that it was deleted, or
542 * it is because it was a voice search.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700543 */
Leon Scroggins6eac63e2010-03-15 18:19:14 -0400544 boolean recreateWebView(Tab t, BrowserActivity.UrlData urlData) {
545 final String url = urlData.mUrl;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700546 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700547 if (w != null) {
Leon Scroggins47208682010-04-07 17:59:48 -0400548 if (url != null && url.equals(t.getOriginalUrl())
549 // Treat a voice intent as though it is a different URL,
550 // since it most likely is.
551 && urlData.mVoiceIntent == null) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700552 // The original url matches the current url. Just go back to the
553 // first history item so we can load it faster than if we
554 // rebuilt the WebView.
555 final WebBackForwardList list = w.copyBackForwardList();
556 if (list != null) {
557 w.goBackOrForward(-list.getCurrentIndex());
558 w.clearHistory(); // maintains the current page.
559 return false;
560 }
561 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700562 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700563 }
564 // Create a new WebView. If this tab is the current tab, we need to put
565 // back all the clients so force it to be the current tab.
Steve Block2bc69912009-07-30 14:45:13 +0100566 t.setWebView(createNewWebView());
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700567 if (getCurrentTab() == t) {
568 setCurrentTab(t, true);
569 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700570 // Clear the saved state and picker data
571 t.setSavedState(null);
572 t.clearPickerData();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700573 // Save the new url in order to avoid deleting the WebView.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700574 t.setOriginalUrl(url);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700575 return true;
576 }
577
578 /**
579 * Creates a new WebView and registers it with the global settings.
580 */
581 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700582 return createNewWebView(false);
583 }
584
585 /**
586 * Creates a new WebView and registers it with the global settings.
587 * @param privateBrowsing When true, enables private browsing in the new
588 * WebView.
589 */
590 private WebView createNewWebView(boolean privateBrowsing) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700591 // Create a new WebView
Michael Kolba2b2ba82010-08-04 17:54:03 -0700592 ScrollWebView w = new ScrollWebView(mActivity, null,
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700593 com.android.internal.R.attr.webViewStyle, privateBrowsing);
Michael Kolba2b2ba82010-08-04 17:54:03 -0700594 w.setScrollListener(mActivity.getScrollListener());
Grace Kloba67f0a562009-09-28 21:24:51 -0700595 w.setScrollbarFadingEnabled(true);
596 w.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700597 w.setMapTrackballToArrowKeys(false); // use trackball directly
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700598 // Enable the built-in zoom
599 w.getSettings().setBuiltInZoomControls(true);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700600 // Add this WebView to the settings observer list and update the
601 // settings
602 final BrowserSettings s = BrowserSettings.getInstance();
603 s.addObserver(w.getSettings()).update(s, null);
Mike Reedd5a80a52009-11-12 12:49:34 -0500604
Mike Reeda947d2d2010-01-14 14:57:45 -0800605 // pick a default
Mike Reedd5c3e0f2010-02-24 11:12:54 -0500606 if (false) {
Mike Reede5073c22010-01-29 11:31:39 -0500607 MeshTracker mt = new MeshTracker(2);
608 Paint paint = new Paint();
609 Bitmap bm = BitmapFactory.decodeResource(mActivity.getResources(),
610 R.drawable.pattern_carbon_fiber_dark);
611 paint.setShader(new BitmapShader(bm, Shader.TileMode.REPEAT,
612 Shader.TileMode.REPEAT));
613 mt.setBGPaint(paint);
614 w.setDragTracker(mt);
615 }
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700616 return w;
617 }
618
619 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800620 * Put the current tab in the background and set newTab as the current tab.
621 * @param newTab The new tab. If newTab is null, the current tab is not
622 * set.
623 */
624 boolean setCurrentTab(Tab newTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700625 return setCurrentTab(newTab, false);
626 }
627
Grace Kloba22ac16e2009-10-07 18:00:23 -0700628 void pauseCurrentTab() {
Mike Reed7bfa63b2009-05-28 11:08:32 -0400629 Tab t = getCurrentTab();
630 if (t != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700631 t.pause();
Mike Reed7bfa63b2009-05-28 11:08:32 -0400632 }
633 }
634
Grace Kloba22ac16e2009-10-07 18:00:23 -0700635 void resumeCurrentTab() {
Mike Reed7bfa63b2009-05-28 11:08:32 -0400636 Tab t = getCurrentTab();
637 if (t != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700638 t.resume();
Mike Reed7bfa63b2009-05-28 11:08:32 -0400639 }
640 }
641
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700642 /**
643 * If force is true, this method skips the check for newTab == current.
644 */
645 private boolean setCurrentTab(Tab newTab, boolean force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800646 Tab current = getTab(mCurrentTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700647 if (current == newTab && !force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800648 return true;
649 }
650 if (current != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700651 current.putInBackground();
652 mCurrentTab = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800653 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800654 if (newTab == null) {
655 return false;
656 }
657
658 // Move the newTab to the end of the queue
659 int index = mTabQueue.indexOf(newTab);
660 if (index != -1) {
661 mTabQueue.remove(index);
662 }
663 mTabQueue.add(newTab);
664
The Android Open Source Project0c908882009-03-03 19:32:16 -0800665 // Display the new current tab
666 mCurrentTab = mTabs.indexOf(newTab);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700667 WebView mainView = newTab.getWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800668 boolean needRestore = (mainView == null);
669 if (needRestore) {
670 // Same work as in createNewTab() except don't do new Tab()
Steve Block2bc69912009-07-30 14:45:13 +0100671 mainView = createNewWebView();
672 newTab.setWebView(mainView);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800673 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700674 newTab.putInForeground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800675 if (needRestore) {
676 // Have to finish setCurrentTab work before calling restoreState
Grace Kloba22ac16e2009-10-07 18:00:23 -0700677 if (!newTab.restoreState(newTab.getSavedState())) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800678 mainView.loadUrl(BrowserSettings.getInstance().getHomePage());
679 }
680 }
681 return true;
682 }
Michael Kolbfe251992010-07-08 15:41:55 -0700683
684 interface TabChangeListener {
685
686 public void onNewTab(Tab tab);
687
688 public void onRemoveTab(Tab tab);
689
690 public void onCurrentTab(Tab tab);
691
692 public void onProgress(Tab tab, int progress);
693
694 public void onUrlAndTitle(Tab tab, String url, String title);
695
696 public void onFavicon(Tab tab, Bitmap favicon);
697
698 public void onPageStarted(Tab tab);
699
700 public void onPageFinished(Tab tab);
701
702 }
703
704 private TabChangeListener mTabChangeListener;
705
706 /**
707 * register the TabChangeListener with the tab control
708 * @param listener
709 */
710 void setOnTabChangeListener(TabChangeListener listener) {
711 mTabChangeListener = listener;
712 }
713
714 /**
715 * get the current TabChangeListener (used by the tabs)
716 */
717 TabChangeListener getTabChangeListener() {
718 return mTabChangeListener;
719 }
720
The Android Open Source Project0c908882009-03-03 19:32:16 -0800721}