blob: fcccad1ab8a71fb10e0053b4042309f64239eeea [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;
The Android Open Source Project0c908882009-03-03 19:32:16 -080020import android.os.Bundle;
The Android Open Source Project0c908882009-03-03 19:32:16 -080021import android.util.Log;
The Android Open Source Project0c908882009-03-03 19:32:16 -080022import android.view.View;
The Android Open Source Project0c908882009-03-03 19:32:16 -080023import android.webkit.WebBackForwardList;
The Android Open Source Project0c908882009-03-03 19:32:16 -080024import android.webkit.WebView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080025
26import java.io.File;
27import java.util.ArrayList;
Elliott Slaughter3d6df162010-08-25 13:17:44 -070028import java.util.HashMap;
The Android Open Source Project0c908882009-03-03 19:32:16 -080029import java.util.Vector;
30
31class TabControl {
32 // Log Tag
33 private static final String LOGTAG = "TabControl";
34 // Maximum number of tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070035 private int mMaxTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080036 // Private array of WebViews that are used as tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070037 private ArrayList<Tab> mTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080038 // Queue of most recently viewed tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070039 private ArrayList<Tab> mTabQueue;
The Android Open Source Project0c908882009-03-03 19:32:16 -080040 // Current position in mTabs.
41 private int mCurrentTab = -1;
42 // A private instance of BrowserActivity to interface with when adding and
43 // switching between tabs.
44 private final BrowserActivity mActivity;
The Android Open Source Project0c908882009-03-03 19:32:16 -080045 // Directory to store thumbnails for each WebView.
46 private final File mThumbnailDir;
Michael Kolb68775752010-08-19 12:42:01 -070047 // Use on screen zoom buttons
48 private boolean mDisplayZoomControls;
The Android Open Source Project0c908882009-03-03 19:32:16 -080049
50 /**
51 * Construct a new TabControl object that interfaces with the given
52 * BrowserActivity instance.
53 * @param activity A BrowserActivity instance that TabControl will interface
54 * with.
55 */
56 TabControl(BrowserActivity activity) {
57 mActivity = activity;
The Android Open Source Project0c908882009-03-03 19:32:16 -080058 mThumbnailDir = activity.getDir("thumbnails", 0);
Michael Kolb68775752010-08-19 12:42:01 -070059 mDisplayZoomControls = true;
Michael Kolb6e4653e2010-09-27 16:22:38 -070060 mMaxTabs = activity.getResources().getInteger(R.integer.max_tabs);
61 mTabs = new ArrayList<Tab>(mMaxTabs);
62 mTabQueue = new ArrayList<Tab>(mMaxTabs);
The Android Open Source Project0c908882009-03-03 19:32:16 -080063 }
64
65 File getThumbnailDir() {
66 return mThumbnailDir;
67 }
68
69 BrowserActivity getBrowserActivity() {
70 return mActivity;
71 }
72
73 /**
Michael Kolb68775752010-08-19 12:42:01 -070074 * Set if the webview should use the on screen zoom controls
75 * @param enabled
76 */
77 void setDisplayZoomControls(boolean enabled) {
78 mDisplayZoomControls = enabled;
79 }
80
81 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080082 * Return the current tab's main WebView. This will always return the main
83 * WebView for a given tab and not a subwindow.
84 * @return The current tab's WebView.
85 */
86 WebView getCurrentWebView() {
87 Tab t = getTab(mCurrentTab);
88 if (t == null) {
89 return null;
90 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070091 return t.getWebView();
Ben Murdochbff2d602009-07-01 20:19:05 +010092 }
93
94 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080095 * Return the current tab's top-level WebView. This can return a subwindow
96 * if one exists.
97 * @return The top-level WebView of the current tab.
98 */
99 WebView getCurrentTopWebView() {
100 Tab t = getTab(mCurrentTab);
101 if (t == null) {
102 return null;
103 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700104 return t.getTopWindow();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800105 }
106
107 /**
108 * Return the current tab's subwindow if it exists.
109 * @return The subwindow of the current tab or null if it doesn't exist.
110 */
111 WebView getCurrentSubWindow() {
112 Tab t = getTab(mCurrentTab);
113 if (t == null) {
114 return null;
115 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700116 return t.getSubWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800117 }
118
119 /**
120 * Return the tab at the specified index.
121 * @return The Tab for the specified index or null if the tab does not
122 * exist.
123 */
124 Tab getTab(int index) {
125 if (index >= 0 && index < mTabs.size()) {
126 return mTabs.get(index);
127 }
128 return null;
129 }
130
131 /**
132 * Return the current tab.
133 * @return The current tab.
134 */
135 Tab getCurrentTab() {
136 return getTab(mCurrentTab);
137 }
138
139 /**
140 * Return the current tab index.
141 * @return The current tab index
142 */
143 int getCurrentIndex() {
144 return mCurrentTab;
145 }
Michael Kolbfe251992010-07-08 15:41:55 -0700146
The Android Open Source Project0c908882009-03-03 19:32:16 -0800147 /**
148 * Given a Tab, find it's index
149 * @param Tab to find
150 * @return index of Tab or -1 if not found
151 */
152 int getTabIndex(Tab tab) {
Patrick Scottae641ac2009-04-20 13:51:49 -0400153 if (tab == null) {
154 return -1;
155 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800156 return mTabs.indexOf(tab);
157 }
158
Grace Kloba22ac16e2009-10-07 18:00:23 -0700159 boolean canCreateNewTab() {
Michael Kolb6e4653e2010-09-27 16:22:38 -0700160 return mMaxTabs != mTabs.size();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700161 }
162
The Android Open Source Project0c908882009-03-03 19:32:16 -0800163 /**
Elliott Slaughtere440a882010-08-20 13:54:45 -0700164 * Returns true if there are any incognito tabs open.
165 * @return True when any incognito tabs are open, false otherwise.
166 */
167 boolean hasAnyOpenIncognitoTabs() {
168 for (Tab tab : mTabs) {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700169 if (tab.getWebView() != null && tab.getWebView().isPrivateBrowsingEnabled()) {
Elliott Slaughtere440a882010-08-20 13:54:45 -0700170 return true;
171 }
172 }
173 return false;
174 }
175
176 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700177 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800178 * @return The newly createTab or null if we have reached the maximum
179 * number of open tabs.
180 */
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700181 Tab createNewTab(boolean closeOnExit, String appId, String url,
182 boolean privateBrowsing) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800183 int size = mTabs.size();
184 // Return false if we have maxed out on tabs
Michael Kolb6e4653e2010-09-27 16:22:38 -0700185 if (mMaxTabs == size) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800186 return null;
187 }
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700188 final WebView w = createNewWebView(privateBrowsing);
Steve Block2bc69912009-07-30 14:45:13 +0100189
The Android Open Source Project0c908882009-03-03 19:32:16 -0800190 // Create a new tab and add it to the tab list
Grace Kloba22ac16e2009-10-07 18:00:23 -0700191 Tab t = new Tab(mActivity, w, closeOnExit, appId, url);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800192 mTabs.add(t);
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700193 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700194 t.putInBackground();
Michael Kolbfe251992010-07-08 15:41:55 -0700195 if (mTabChangeListener != null) {
196 mTabChangeListener.onNewTab(t);
197 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800198 return t;
199 }
200
201 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700202 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700203 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700204 */
205 Tab createNewTab() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700206 return createNewTab(false, null, null, false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700207 }
208
209 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500210 * Remove the parent child relationships from all tabs.
211 */
212 void removeParentChildRelationShips() {
213 for (Tab tab : mTabs) {
214 tab.removeFromTree();
215 }
216 }
217
218 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800219 * Remove the tab from the list. If the tab is the current tab shown, the
220 * last created tab will be shown.
221 * @param t The tab to be removed.
222 */
223 boolean removeTab(Tab t) {
224 if (t == null) {
225 return false;
226 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700227
Patrick Scottd944d4d2010-01-27 16:39:11 -0500228 // Grab the current tab before modifying the list.
229 Tab current = getCurrentTab();
230
231 // Remove t from our list of tabs.
232 mTabs.remove(t);
233
234 // Put the tab in the background only if it is the current one.
235 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700236 t.putInBackground();
237 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500238 } else {
239 // If a tab that is earlier in the list gets removed, the current
240 // index no longer points to the correct tab.
241 mCurrentTab = getTabIndex(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800242 }
243
Grace Kloba22ac16e2009-10-07 18:00:23 -0700244 // destroy the tab
245 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800246 // clear it's references to parent and children
247 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800248
249 // The tab indices have shifted, update all the saved state so we point
250 // to the correct index.
251 for (Tab tab : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700252 Vector<Tab> children = tab.getChildTabs();
253 if (children != null) {
254 for (Tab child : children) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800255 child.setParentTab(tab);
256 }
257 }
258 }
259
The Android Open Source Project0c908882009-03-03 19:32:16 -0800260 // Remove it from the queue of viewed tabs.
261 mTabQueue.remove(t);
Michael Kolbfe251992010-07-08 15:41:55 -0700262 if (mTabChangeListener != null) {
263 mTabChangeListener.onRemoveTab(t);
264 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800265 return true;
266 }
267
268 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800269 * Destroy all the tabs and subwindows
270 */
271 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800272 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700273 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800274 }
275 mTabs.clear();
276 mTabQueue.clear();
277 }
278
279 /**
280 * Returns the number of tabs created.
281 * @return The number of tabs created.
282 */
283 int getTabCount() {
284 return mTabs.size();
285 }
286
The Android Open Source Project0c908882009-03-03 19:32:16 -0800287
288 /**
289 * Save the state of all the Tabs.
290 * @param outState The Bundle to save the state to.
291 */
292 void saveState(Bundle outState) {
293 final int numTabs = getTabCount();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700294 outState.putInt(Tab.NUMTABS, numTabs);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800295 final int index = getCurrentIndex();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700296 outState.putInt(Tab.CURRTAB, (index >= 0 && index < numTabs) ? index : 0);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800297 for (int i = 0; i < numTabs; i++) {
298 final Tab t = getTab(i);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700299 if (t.saveState()) {
300 outState.putBundle(Tab.WEBVIEW + i, t.getSavedState());
The Android Open Source Project0c908882009-03-03 19:32:16 -0800301 }
302 }
303 }
304
305 /**
306 * Restore the state of all the tabs.
307 * @param inState The saved state of all the tabs.
308 * @return True if there were previous tabs that were restored. False if
309 * there was no saved state or restoring the state failed.
310 */
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700311 boolean restoreState(Bundle inState, boolean dontRestoreIncognitoTabs) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800312 final int numTabs = (inState == null)
Grace Kloba22ac16e2009-10-07 18:00:23 -0700313 ? -1 : inState.getInt(Tab.NUMTABS, -1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800314 if (numTabs == -1) {
315 return false;
316 } else {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700317 final int oldCurrentTab = inState.getInt(Tab.CURRTAB, -1);
318
319 // Determine whether the saved current tab can be restored, and
320 // if not, which tab will take its place.
321 int currentTab = -1;
322 if (!dontRestoreIncognitoTabs
323 || !inState.getBundle(Tab.WEBVIEW + oldCurrentTab).getBoolean(Tab.INCOGNITO)) {
324 currentTab = oldCurrentTab;
325 } else {
326 for (int i = 0; i < numTabs; i++) {
327 if (!inState.getBundle(Tab.WEBVIEW + i).getBoolean(Tab.INCOGNITO)) {
328 currentTab = i;
329 break;
330 }
331 }
332 }
333 if (currentTab < 0) {
334 return false;
335 }
336
337 // Map saved tab indices to new indices, in case any incognito tabs
338 // need to not be restored.
339 HashMap<Integer, Integer> originalTabIndices = new HashMap<Integer, Integer>();
340 originalTabIndices.put(-1, -1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800341 for (int i = 0; i < numTabs; i++) {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700342 Bundle state = inState.getBundle(Tab.WEBVIEW + i);
343
344 if (dontRestoreIncognitoTabs && state != null && state.getBoolean(Tab.INCOGNITO)) {
345 originalTabIndices.put(i, -1);
346 } else if (i == currentTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700347 Tab t = createNewTab();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800348 // Me must set the current tab before restoring the state
349 // so that all the client classes are set.
350 setCurrentTab(t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700351 if (!t.restoreState(state)) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800352 Log.w(LOGTAG, "Fail in restoreState, load home page.");
Grace Kloba22ac16e2009-10-07 18:00:23 -0700353 t.getWebView().loadUrl(BrowserSettings.getInstance()
The Android Open Source Project0c908882009-03-03 19:32:16 -0800354 .getHomePage());
355 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700356 originalTabIndices.put(i, getTabCount() - 1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800357 } else {
358 // Create a new tab and don't restore the state yet, add it
359 // to the tab list
Grace Kloba22ac16e2009-10-07 18:00:23 -0700360 Tab t = new Tab(mActivity, null, false, null, null);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700361 if (state != null) {
362 t.setSavedState(state);
363 t.populatePickerDataFromSavedState();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700364 // Need to maintain the app id and original url so we
365 // can possibly reuse this tab.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700366 t.setAppId(state.getString(Tab.APPID));
367 t.setOriginalUrl(state.getString(Tab.ORIGINALURL));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800368 }
369 mTabs.add(t);
Grace Klobaf56f68d2010-04-11 22:53:42 -0700370 // added the tab to the front as they are not current
371 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700372 originalTabIndices.put(i, getTabCount() - 1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800373 }
374 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700375
The Android Open Source Project0c908882009-03-03 19:32:16 -0800376 // Rebuild the tree of tabs. Do this after all tabs have been
377 // created/restored so that the parent tab exists.
378 for (int i = 0; i < numTabs; i++) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700379 final Bundle b = inState.getBundle(Tab.WEBVIEW + i);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800380 final Tab t = getTab(i);
381 if (b != null && t != null) {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700382 final Integer parentIndex = originalTabIndices.get(b.getInt(Tab.PARENTTAB, -1));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800383 if (parentIndex != -1) {
384 final Tab parent = getTab(parentIndex);
385 if (parent != null) {
386 parent.addChildTab(t);
387 }
388 }
389 }
390 }
391 }
392 return true;
393 }
394
395 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700396 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800397 * WebView cache;
398 */
399 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700400 if (getTabCount() == 0) return;
401
Grace Klobaf56f68d2010-04-11 22:53:42 -0700402 // free the least frequently used background tabs
403 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
404 if (tabs.size() > 0) {
405 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
406 for (Tab t : tabs) {
407 // store the WebView's state.
408 t.saveState();
409 // destroy the tab
410 t.destroy();
411 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800412 return;
413 }
414
Derek Sollenberger4433d032009-06-10 15:37:21 -0400415 // free the WebView's unused memory (this includes the cache)
416 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800417 WebView view = getCurrentWebView();
418 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400419 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800420 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800421 }
422
Grace Klobaf56f68d2010-04-11 22:53:42 -0700423 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
424 Vector<Tab> tabsToGo = new Vector<Tab>();
425
Patrick Scott2a67de42009-08-31 09:48:37 -0400426 // Don't do anything if we only have 1 tab or if the current tab is
427 // null.
428 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700429 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800430 }
431
Grace Klobaf56f68d2010-04-11 22:53:42 -0700432 if (mTabQueue.size() == 0) {
433 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800434 }
435
Grace Klobaf56f68d2010-04-11 22:53:42 -0700436 // Rip through the queue starting at the beginning and tear down half of
437 // available tabs which are not the current tab or the parent of the
438 // current tab.
439 int openTabCount = 0;
440 for (Tab t : mTabQueue) {
441 if (t != null && t.getWebView() != null) {
442 openTabCount++;
443 if (t != current && t != current.getParentTab()) {
444 tabsToGo.add(t);
445 }
446 }
447 }
448
449 openTabCount /= 2;
450 if (tabsToGo.size() > openTabCount) {
451 tabsToGo.setSize(openTabCount);
452 }
453
454 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800455 }
456
The Android Open Source Project0c908882009-03-03 19:32:16 -0800457 /**
458 * Show the tab that contains the given WebView.
459 * @param view The WebView used to find the tab.
460 */
461 Tab getTabFromView(WebView view) {
462 final int size = getTabCount();
463 for (int i = 0; i < size; i++) {
464 final Tab t = getTab(i);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700465 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800466 return t;
467 }
468 }
469 return null;
470 }
471
472 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700473 * Return the tab with the matching application id.
474 * @param id The application identifier.
475 */
476 Tab getTabFromId(String id) {
477 if (id == null) {
478 return null;
479 }
480 final int size = getTabCount();
481 for (int i = 0; i < size; i++) {
482 final Tab t = getTab(i);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700483 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700484 return t;
485 }
486 }
487 return null;
488 }
489
Grace Kloba22ac16e2009-10-07 18:00:23 -0700490 /**
491 * Stop loading in all opened WebView including subWindows.
492 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700493 void stopAllLoading() {
494 final int size = getTabCount();
495 for (int i = 0; i < size; i++) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700496 final Tab t = getTab(i);
497 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700498 if (webview != null) {
499 webview.stopLoading();
500 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700501 final WebView subview = t.getSubWebView();
502 if (subview != null) {
503 webview.stopLoading();
504 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700505 }
506 }
507
Patrick Scottcd115892009-07-16 09:42:58 -0400508 // This method checks if a non-app tab (one created within the browser)
509 // matches the given url.
510 private boolean tabMatchesUrl(Tab t, String url) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700511 if (t.getAppId() != null) {
Patrick Scottcd115892009-07-16 09:42:58 -0400512 return false;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700513 }
514 WebView webview = t.getWebView();
515 if (webview == null) {
Patrick Scottcd115892009-07-16 09:42:58 -0400516 return false;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700517 } else if (url.equals(webview.getUrl())
518 || url.equals(webview.getOriginalUrl())) {
Patrick Scottcd115892009-07-16 09:42:58 -0400519 return true;
520 }
521 return false;
522 }
523
524 /**
525 * Return the tab that has no app id associated with it and the url of the
526 * tab matches the given url.
527 * @param url The url to search for.
528 */
529 Tab findUnusedTabWithUrl(String url) {
530 if (url == null) {
531 return null;
532 }
533 // Check the current tab first.
534 Tab t = getCurrentTab();
535 if (t != null && tabMatchesUrl(t, url)) {
536 return t;
537 }
538 // Now check all the rest.
539 final int size = getTabCount();
540 for (int i = 0; i < size; i++) {
541 t = getTab(i);
542 if (tabMatchesUrl(t, url)) {
543 return t;
544 }
545 }
546 return null;
547 }
548
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700549 /**
550 * Recreate the main WebView of the given tab. Returns true if the WebView
Leon Scroggins6eac63e2010-03-15 18:19:14 -0400551 * requires a load, whether it was due to the fact that it was deleted, or
552 * it is because it was a voice search.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700553 */
Leon Scroggins6eac63e2010-03-15 18:19:14 -0400554 boolean recreateWebView(Tab t, BrowserActivity.UrlData urlData) {
555 final String url = urlData.mUrl;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700556 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700557 if (w != null) {
Leon Scroggins47208682010-04-07 17:59:48 -0400558 if (url != null && url.equals(t.getOriginalUrl())
559 // Treat a voice intent as though it is a different URL,
560 // since it most likely is.
561 && urlData.mVoiceIntent == null) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700562 // The original url matches the current url. Just go back to the
563 // first history item so we can load it faster than if we
564 // rebuilt the WebView.
565 final WebBackForwardList list = w.copyBackForwardList();
566 if (list != null) {
567 w.goBackOrForward(-list.getCurrentIndex());
568 w.clearHistory(); // maintains the current page.
569 return false;
570 }
571 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700572 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700573 }
574 // Create a new WebView. If this tab is the current tab, we need to put
575 // back all the clients so force it to be the current tab.
Steve Block2bc69912009-07-30 14:45:13 +0100576 t.setWebView(createNewWebView());
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700577 if (getCurrentTab() == t) {
578 setCurrentTab(t, true);
579 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700580 // Clear the saved state and picker data
581 t.setSavedState(null);
582 t.clearPickerData();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700583 // Save the new url in order to avoid deleting the WebView.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700584 t.setOriginalUrl(url);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700585 return true;
586 }
587
588 /**
589 * Creates a new WebView and registers it with the global settings.
590 */
591 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700592 return createNewWebView(false);
593 }
594
595 /**
596 * Creates a new WebView and registers it with the global settings.
597 * @param privateBrowsing When true, enables private browsing in the new
598 * WebView.
599 */
600 private WebView createNewWebView(boolean privateBrowsing) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700601 // Create a new WebView
Michael Kolba2b2ba82010-08-04 17:54:03 -0700602 ScrollWebView w = new ScrollWebView(mActivity, null,
Bjorn Bringertb1402a52010-10-12 10:53:12 +0100603 android.R.attr.webViewStyle, privateBrowsing);
Michael Kolba2b2ba82010-08-04 17:54:03 -0700604 w.setScrollListener(mActivity.getScrollListener());
Grace Kloba67f0a562009-09-28 21:24:51 -0700605 w.setScrollbarFadingEnabled(true);
606 w.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700607 w.setMapTrackballToArrowKeys(false); // use trackball directly
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700608 // Enable the built-in zoom
609 w.getSettings().setBuiltInZoomControls(true);
Michael Kolb68775752010-08-19 12:42:01 -0700610 w.getSettings().setDisplayZoomControls(mDisplayZoomControls);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700611 // Add this WebView to the settings observer list and update the
612 // settings
613 final BrowserSettings s = BrowserSettings.getInstance();
614 s.addObserver(w.getSettings()).update(s, null);
Mike Reedd5a80a52009-11-12 12:49:34 -0500615
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
John Reckfb3017f2010-10-26 19:01:24 -0700698 public void onPageStarted(Tab tab, String url, Bitmap favicon);
Michael Kolbfe251992010-07-08 15:41:55 -0700699
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}