blob: e417a3949f114c6d03392ade467ade3309ee3fa7 [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.
Michael Kolb6e4653e2010-09-27 16:22:38 -070039 private int mMaxTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080040 // Private array of WebViews that are used as tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070041 private ArrayList<Tab> mTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080042 // Queue of most recently viewed tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070043 private ArrayList<Tab> mTabQueue;
The Android Open Source Project0c908882009-03-03 19:32:16 -080044 // 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;
Michael Kolb68775752010-08-19 12:42:01 -070051 // Use on screen zoom buttons
52 private boolean mDisplayZoomControls;
The Android Open Source Project0c908882009-03-03 19:32:16 -080053
54 /**
55 * Construct a new TabControl object that interfaces with the given
56 * BrowserActivity instance.
57 * @param activity A BrowserActivity instance that TabControl will interface
58 * with.
59 */
60 TabControl(BrowserActivity activity) {
61 mActivity = activity;
The Android Open Source Project0c908882009-03-03 19:32:16 -080062 mThumbnailDir = activity.getDir("thumbnails", 0);
Michael Kolb68775752010-08-19 12:42:01 -070063 mDisplayZoomControls = true;
Michael Kolb6e4653e2010-09-27 16:22:38 -070064 mMaxTabs = activity.getResources().getInteger(R.integer.max_tabs);
65 mTabs = new ArrayList<Tab>(mMaxTabs);
66 mTabQueue = new ArrayList<Tab>(mMaxTabs);
The Android Open Source Project0c908882009-03-03 19:32:16 -080067 }
68
69 File getThumbnailDir() {
70 return mThumbnailDir;
71 }
72
73 BrowserActivity getBrowserActivity() {
74 return mActivity;
75 }
76
77 /**
Michael Kolb68775752010-08-19 12:42:01 -070078 * Set if the webview should use the on screen zoom controls
79 * @param enabled
80 */
81 void setDisplayZoomControls(boolean enabled) {
82 mDisplayZoomControls = enabled;
83 }
84
85 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080086 * Return the current tab's main WebView. This will always return the main
87 * WebView for a given tab and not a subwindow.
88 * @return The current tab's WebView.
89 */
90 WebView getCurrentWebView() {
91 Tab t = getTab(mCurrentTab);
92 if (t == null) {
93 return null;
94 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070095 return t.getWebView();
Ben Murdochbff2d602009-07-01 20:19:05 +010096 }
97
98 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080099 * Return the current tab's top-level WebView. This can return a subwindow
100 * if one exists.
101 * @return The top-level WebView of the current tab.
102 */
103 WebView getCurrentTopWebView() {
104 Tab t = getTab(mCurrentTab);
105 if (t == null) {
106 return null;
107 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700108 return t.getTopWindow();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800109 }
110
111 /**
112 * Return the current tab's subwindow if it exists.
113 * @return The subwindow of the current tab or null if it doesn't exist.
114 */
115 WebView getCurrentSubWindow() {
116 Tab t = getTab(mCurrentTab);
117 if (t == null) {
118 return null;
119 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700120 return t.getSubWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800121 }
122
123 /**
124 * Return the tab at the specified index.
125 * @return The Tab for the specified index or null if the tab does not
126 * exist.
127 */
128 Tab getTab(int index) {
129 if (index >= 0 && index < mTabs.size()) {
130 return mTabs.get(index);
131 }
132 return null;
133 }
134
135 /**
136 * Return the current tab.
137 * @return The current tab.
138 */
139 Tab getCurrentTab() {
140 return getTab(mCurrentTab);
141 }
142
143 /**
144 * Return the current tab index.
145 * @return The current tab index
146 */
147 int getCurrentIndex() {
148 return mCurrentTab;
149 }
Michael Kolbfe251992010-07-08 15:41:55 -0700150
The Android Open Source Project0c908882009-03-03 19:32:16 -0800151 /**
152 * Given a Tab, find it's index
153 * @param Tab to find
154 * @return index of Tab or -1 if not found
155 */
156 int getTabIndex(Tab tab) {
Patrick Scottae641ac2009-04-20 13:51:49 -0400157 if (tab == null) {
158 return -1;
159 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800160 return mTabs.indexOf(tab);
161 }
162
Grace Kloba22ac16e2009-10-07 18:00:23 -0700163 boolean canCreateNewTab() {
Michael Kolb6e4653e2010-09-27 16:22:38 -0700164 return mMaxTabs != mTabs.size();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700165 }
166
The Android Open Source Project0c908882009-03-03 19:32:16 -0800167 /**
Elliott Slaughtere440a882010-08-20 13:54:45 -0700168 * Returns true if there are any incognito tabs open.
169 * @return True when any incognito tabs are open, false otherwise.
170 */
171 boolean hasAnyOpenIncognitoTabs() {
172 for (Tab tab : mTabs) {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700173 if (tab.getWebView() != null && tab.getWebView().isPrivateBrowsingEnabled()) {
Elliott Slaughtere440a882010-08-20 13:54:45 -0700174 return true;
175 }
176 }
177 return false;
178 }
179
180 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700181 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800182 * @return The newly createTab or null if we have reached the maximum
183 * number of open tabs.
184 */
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700185 Tab createNewTab(boolean closeOnExit, String appId, String url,
186 boolean privateBrowsing) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800187 int size = mTabs.size();
188 // Return false if we have maxed out on tabs
Michael Kolb6e4653e2010-09-27 16:22:38 -0700189 if (mMaxTabs == size) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800190 return null;
191 }
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700192 final WebView w = createNewWebView(privateBrowsing);
Steve Block2bc69912009-07-30 14:45:13 +0100193
The Android Open Source Project0c908882009-03-03 19:32:16 -0800194 // Create a new tab and add it to the tab list
Grace Kloba22ac16e2009-10-07 18:00:23 -0700195 Tab t = new Tab(mActivity, w, closeOnExit, appId, url);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800196 mTabs.add(t);
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700197 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700198 t.putInBackground();
Michael Kolbfe251992010-07-08 15:41:55 -0700199 if (mTabChangeListener != null) {
200 mTabChangeListener.onNewTab(t);
201 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800202 return t;
203 }
204
205 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700206 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700207 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700208 */
209 Tab createNewTab() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700210 return createNewTab(false, null, null, false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700211 }
212
213 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500214 * Remove the parent child relationships from all tabs.
215 */
216 void removeParentChildRelationShips() {
217 for (Tab tab : mTabs) {
218 tab.removeFromTree();
219 }
220 }
221
222 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800223 * Remove the tab from the list. If the tab is the current tab shown, the
224 * last created tab will be shown.
225 * @param t The tab to be removed.
226 */
227 boolean removeTab(Tab t) {
228 if (t == null) {
229 return false;
230 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700231
Patrick Scottd944d4d2010-01-27 16:39:11 -0500232 // Grab the current tab before modifying the list.
233 Tab current = getCurrentTab();
234
235 // Remove t from our list of tabs.
236 mTabs.remove(t);
237
238 // Put the tab in the background only if it is the current one.
239 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700240 t.putInBackground();
241 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500242 } else {
243 // If a tab that is earlier in the list gets removed, the current
244 // index no longer points to the correct tab.
245 mCurrentTab = getTabIndex(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800246 }
247
Grace Kloba22ac16e2009-10-07 18:00:23 -0700248 // destroy the tab
249 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800250 // clear it's references to parent and children
251 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800252
253 // The tab indices have shifted, update all the saved state so we point
254 // to the correct index.
255 for (Tab tab : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700256 Vector<Tab> children = tab.getChildTabs();
257 if (children != null) {
258 for (Tab child : children) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800259 child.setParentTab(tab);
260 }
261 }
262 }
263
The Android Open Source Project0c908882009-03-03 19:32:16 -0800264 // Remove it from the queue of viewed tabs.
265 mTabQueue.remove(t);
Michael Kolbfe251992010-07-08 15:41:55 -0700266 if (mTabChangeListener != null) {
267 mTabChangeListener.onRemoveTab(t);
268 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800269 return true;
270 }
271
272 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800273 * Destroy all the tabs and subwindows
274 */
275 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800276 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700277 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800278 }
279 mTabs.clear();
280 mTabQueue.clear();
281 }
282
283 /**
284 * Returns the number of tabs created.
285 * @return The number of tabs created.
286 */
287 int getTabCount() {
288 return mTabs.size();
289 }
290
The Android Open Source Project0c908882009-03-03 19:32:16 -0800291
292 /**
293 * Save the state of all the Tabs.
294 * @param outState The Bundle to save the state to.
295 */
296 void saveState(Bundle outState) {
297 final int numTabs = getTabCount();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700298 outState.putInt(Tab.NUMTABS, numTabs);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800299 final int index = getCurrentIndex();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700300 outState.putInt(Tab.CURRTAB, (index >= 0 && index < numTabs) ? index : 0);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800301 for (int i = 0; i < numTabs; i++) {
302 final Tab t = getTab(i);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700303 if (t.saveState()) {
304 outState.putBundle(Tab.WEBVIEW + i, t.getSavedState());
The Android Open Source Project0c908882009-03-03 19:32:16 -0800305 }
306 }
307 }
308
309 /**
310 * Restore the state of all the tabs.
311 * @param inState The saved state of all the tabs.
312 * @return True if there were previous tabs that were restored. False if
313 * there was no saved state or restoring the state failed.
314 */
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700315 boolean restoreState(Bundle inState, boolean dontRestoreIncognitoTabs) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800316 final int numTabs = (inState == null)
Grace Kloba22ac16e2009-10-07 18:00:23 -0700317 ? -1 : inState.getInt(Tab.NUMTABS, -1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800318 if (numTabs == -1) {
319 return false;
320 } else {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700321 final int oldCurrentTab = inState.getInt(Tab.CURRTAB, -1);
322
323 // Determine whether the saved current tab can be restored, and
324 // if not, which tab will take its place.
325 int currentTab = -1;
326 if (!dontRestoreIncognitoTabs
327 || !inState.getBundle(Tab.WEBVIEW + oldCurrentTab).getBoolean(Tab.INCOGNITO)) {
328 currentTab = oldCurrentTab;
329 } else {
330 for (int i = 0; i < numTabs; i++) {
331 if (!inState.getBundle(Tab.WEBVIEW + i).getBoolean(Tab.INCOGNITO)) {
332 currentTab = i;
333 break;
334 }
335 }
336 }
337 if (currentTab < 0) {
338 return false;
339 }
340
341 // Map saved tab indices to new indices, in case any incognito tabs
342 // need to not be restored.
343 HashMap<Integer, Integer> originalTabIndices = new HashMap<Integer, Integer>();
344 originalTabIndices.put(-1, -1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800345 for (int i = 0; i < numTabs; i++) {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700346 Bundle state = inState.getBundle(Tab.WEBVIEW + i);
347
348 if (dontRestoreIncognitoTabs && state != null && state.getBoolean(Tab.INCOGNITO)) {
349 originalTabIndices.put(i, -1);
350 } else if (i == currentTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700351 Tab t = createNewTab();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800352 // Me must set the current tab before restoring the state
353 // so that all the client classes are set.
354 setCurrentTab(t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700355 if (!t.restoreState(state)) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800356 Log.w(LOGTAG, "Fail in restoreState, load home page.");
Grace Kloba22ac16e2009-10-07 18:00:23 -0700357 t.getWebView().loadUrl(BrowserSettings.getInstance()
The Android Open Source Project0c908882009-03-03 19:32:16 -0800358 .getHomePage());
359 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700360 originalTabIndices.put(i, getTabCount() - 1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800361 } else {
362 // Create a new tab and don't restore the state yet, add it
363 // to the tab list
Grace Kloba22ac16e2009-10-07 18:00:23 -0700364 Tab t = new Tab(mActivity, null, false, null, null);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700365 if (state != null) {
366 t.setSavedState(state);
367 t.populatePickerDataFromSavedState();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700368 // Need to maintain the app id and original url so we
369 // can possibly reuse this tab.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700370 t.setAppId(state.getString(Tab.APPID));
371 t.setOriginalUrl(state.getString(Tab.ORIGINALURL));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800372 }
373 mTabs.add(t);
Grace Klobaf56f68d2010-04-11 22:53:42 -0700374 // added the tab to the front as they are not current
375 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700376 originalTabIndices.put(i, getTabCount() - 1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800377 }
378 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700379
The Android Open Source Project0c908882009-03-03 19:32:16 -0800380 // Rebuild the tree of tabs. Do this after all tabs have been
381 // created/restored so that the parent tab exists.
382 for (int i = 0; i < numTabs; i++) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700383 final Bundle b = inState.getBundle(Tab.WEBVIEW + i);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800384 final Tab t = getTab(i);
385 if (b != null && t != null) {
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700386 final Integer parentIndex = originalTabIndices.get(b.getInt(Tab.PARENTTAB, -1));
The Android Open Source Project0c908882009-03-03 19:32:16 -0800387 if (parentIndex != -1) {
388 final Tab parent = getTab(parentIndex);
389 if (parent != null) {
390 parent.addChildTab(t);
391 }
392 }
393 }
394 }
395 }
396 return true;
397 }
398
399 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700400 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800401 * WebView cache;
402 */
403 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700404 if (getTabCount() == 0) return;
405
Grace Klobaf56f68d2010-04-11 22:53:42 -0700406 // free the least frequently used background tabs
407 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
408 if (tabs.size() > 0) {
409 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
410 for (Tab t : tabs) {
411 // store the WebView's state.
412 t.saveState();
413 // destroy the tab
414 t.destroy();
415 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800416 return;
417 }
418
Derek Sollenberger4433d032009-06-10 15:37:21 -0400419 // free the WebView's unused memory (this includes the cache)
420 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800421 WebView view = getCurrentWebView();
422 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400423 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800424 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800425 }
426
Grace Klobaf56f68d2010-04-11 22:53:42 -0700427 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
428 Vector<Tab> tabsToGo = new Vector<Tab>();
429
Patrick Scott2a67de42009-08-31 09:48:37 -0400430 // Don't do anything if we only have 1 tab or if the current tab is
431 // null.
432 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700433 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800434 }
435
Grace Klobaf56f68d2010-04-11 22:53:42 -0700436 if (mTabQueue.size() == 0) {
437 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800438 }
439
Grace Klobaf56f68d2010-04-11 22:53:42 -0700440 // Rip through the queue starting at the beginning and tear down half of
441 // available tabs which are not the current tab or the parent of the
442 // current tab.
443 int openTabCount = 0;
444 for (Tab t : mTabQueue) {
445 if (t != null && t.getWebView() != null) {
446 openTabCount++;
447 if (t != current && t != current.getParentTab()) {
448 tabsToGo.add(t);
449 }
450 }
451 }
452
453 openTabCount /= 2;
454 if (tabsToGo.size() > openTabCount) {
455 tabsToGo.setSize(openTabCount);
456 }
457
458 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800459 }
460
The Android Open Source Project0c908882009-03-03 19:32:16 -0800461 /**
462 * Show the tab that contains the given WebView.
463 * @param view The WebView used to find the tab.
464 */
465 Tab getTabFromView(WebView view) {
466 final int size = getTabCount();
467 for (int i = 0; i < size; i++) {
468 final Tab t = getTab(i);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700469 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800470 return t;
471 }
472 }
473 return null;
474 }
475
476 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700477 * Return the tab with the matching application id.
478 * @param id The application identifier.
479 */
480 Tab getTabFromId(String id) {
481 if (id == null) {
482 return null;
483 }
484 final int size = getTabCount();
485 for (int i = 0; i < size; i++) {
486 final Tab t = getTab(i);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700487 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700488 return t;
489 }
490 }
491 return null;
492 }
493
Grace Kloba22ac16e2009-10-07 18:00:23 -0700494 /**
495 * Stop loading in all opened WebView including subWindows.
496 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700497 void stopAllLoading() {
498 final int size = getTabCount();
499 for (int i = 0; i < size; i++) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700500 final Tab t = getTab(i);
501 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700502 if (webview != null) {
503 webview.stopLoading();
504 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700505 final WebView subview = t.getSubWebView();
506 if (subview != null) {
507 webview.stopLoading();
508 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700509 }
510 }
511
Patrick Scottcd115892009-07-16 09:42:58 -0400512 // This method checks if a non-app tab (one created within the browser)
513 // matches the given url.
514 private boolean tabMatchesUrl(Tab t, String url) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700515 if (t.getAppId() != null) {
Patrick Scottcd115892009-07-16 09:42:58 -0400516 return false;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700517 }
518 WebView webview = t.getWebView();
519 if (webview == null) {
Patrick Scottcd115892009-07-16 09:42:58 -0400520 return false;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700521 } else if (url.equals(webview.getUrl())
522 || url.equals(webview.getOriginalUrl())) {
Patrick Scottcd115892009-07-16 09:42:58 -0400523 return true;
524 }
525 return false;
526 }
527
528 /**
529 * Return the tab that has no app id associated with it and the url of the
530 * tab matches the given url.
531 * @param url The url to search for.
532 */
533 Tab findUnusedTabWithUrl(String url) {
534 if (url == null) {
535 return null;
536 }
537 // Check the current tab first.
538 Tab t = getCurrentTab();
539 if (t != null && tabMatchesUrl(t, url)) {
540 return t;
541 }
542 // Now check all the rest.
543 final int size = getTabCount();
544 for (int i = 0; i < size; i++) {
545 t = getTab(i);
546 if (tabMatchesUrl(t, url)) {
547 return t;
548 }
549 }
550 return null;
551 }
552
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700553 /**
554 * Recreate the main WebView of the given tab. Returns true if the WebView
Leon Scroggins6eac63e2010-03-15 18:19:14 -0400555 * requires a load, whether it was due to the fact that it was deleted, or
556 * it is because it was a voice search.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700557 */
Leon Scroggins6eac63e2010-03-15 18:19:14 -0400558 boolean recreateWebView(Tab t, BrowserActivity.UrlData urlData) {
559 final String url = urlData.mUrl;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700560 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700561 if (w != null) {
Leon Scroggins47208682010-04-07 17:59:48 -0400562 if (url != null && url.equals(t.getOriginalUrl())
563 // Treat a voice intent as though it is a different URL,
564 // since it most likely is.
565 && urlData.mVoiceIntent == null) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700566 // The original url matches the current url. Just go back to the
567 // first history item so we can load it faster than if we
568 // rebuilt the WebView.
569 final WebBackForwardList list = w.copyBackForwardList();
570 if (list != null) {
571 w.goBackOrForward(-list.getCurrentIndex());
572 w.clearHistory(); // maintains the current page.
573 return false;
574 }
575 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700576 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700577 }
578 // Create a new WebView. If this tab is the current tab, we need to put
579 // back all the clients so force it to be the current tab.
Steve Block2bc69912009-07-30 14:45:13 +0100580 t.setWebView(createNewWebView());
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700581 if (getCurrentTab() == t) {
582 setCurrentTab(t, true);
583 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700584 // Clear the saved state and picker data
585 t.setSavedState(null);
586 t.clearPickerData();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700587 // Save the new url in order to avoid deleting the WebView.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700588 t.setOriginalUrl(url);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700589 return true;
590 }
591
592 /**
593 * Creates a new WebView and registers it with the global settings.
594 */
595 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700596 return createNewWebView(false);
597 }
598
599 /**
600 * Creates a new WebView and registers it with the global settings.
601 * @param privateBrowsing When true, enables private browsing in the new
602 * WebView.
603 */
604 private WebView createNewWebView(boolean privateBrowsing) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700605 // Create a new WebView
Michael Kolba2b2ba82010-08-04 17:54:03 -0700606 ScrollWebView w = new ScrollWebView(mActivity, null,
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700607 com.android.internal.R.attr.webViewStyle, privateBrowsing);
Michael Kolba2b2ba82010-08-04 17:54:03 -0700608 w.setScrollListener(mActivity.getScrollListener());
Grace Kloba67f0a562009-09-28 21:24:51 -0700609 w.setScrollbarFadingEnabled(true);
610 w.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700611 w.setMapTrackballToArrowKeys(false); // use trackball directly
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700612 // Enable the built-in zoom
613 w.getSettings().setBuiltInZoomControls(true);
Michael Kolb68775752010-08-19 12:42:01 -0700614 w.getSettings().setDisplayZoomControls(mDisplayZoomControls);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700615 // Add this WebView to the settings observer list and update the
616 // settings
617 final BrowserSettings s = BrowserSettings.getInstance();
618 s.addObserver(w.getSettings()).update(s, null);
Mike Reedd5a80a52009-11-12 12:49:34 -0500619
Mike Reeda947d2d2010-01-14 14:57:45 -0800620 // pick a default
Mike Reedd5c3e0f2010-02-24 11:12:54 -0500621 if (false) {
Mike Reede5073c22010-01-29 11:31:39 -0500622 MeshTracker mt = new MeshTracker(2);
623 Paint paint = new Paint();
624 Bitmap bm = BitmapFactory.decodeResource(mActivity.getResources(),
625 R.drawable.pattern_carbon_fiber_dark);
626 paint.setShader(new BitmapShader(bm, Shader.TileMode.REPEAT,
627 Shader.TileMode.REPEAT));
628 mt.setBGPaint(paint);
629 w.setDragTracker(mt);
630 }
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700631 return w;
632 }
633
634 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800635 * Put the current tab in the background and set newTab as the current tab.
636 * @param newTab The new tab. If newTab is null, the current tab is not
637 * set.
638 */
639 boolean setCurrentTab(Tab newTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700640 return setCurrentTab(newTab, false);
641 }
642
Grace Kloba22ac16e2009-10-07 18:00:23 -0700643 void pauseCurrentTab() {
Mike Reed7bfa63b2009-05-28 11:08:32 -0400644 Tab t = getCurrentTab();
645 if (t != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700646 t.pause();
Mike Reed7bfa63b2009-05-28 11:08:32 -0400647 }
648 }
649
Grace Kloba22ac16e2009-10-07 18:00:23 -0700650 void resumeCurrentTab() {
Mike Reed7bfa63b2009-05-28 11:08:32 -0400651 Tab t = getCurrentTab();
652 if (t != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700653 t.resume();
Mike Reed7bfa63b2009-05-28 11:08:32 -0400654 }
655 }
656
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700657 /**
658 * If force is true, this method skips the check for newTab == current.
659 */
660 private boolean setCurrentTab(Tab newTab, boolean force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800661 Tab current = getTab(mCurrentTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700662 if (current == newTab && !force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800663 return true;
664 }
665 if (current != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700666 current.putInBackground();
667 mCurrentTab = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800668 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800669 if (newTab == null) {
670 return false;
671 }
672
673 // Move the newTab to the end of the queue
674 int index = mTabQueue.indexOf(newTab);
675 if (index != -1) {
676 mTabQueue.remove(index);
677 }
678 mTabQueue.add(newTab);
679
The Android Open Source Project0c908882009-03-03 19:32:16 -0800680 // Display the new current tab
681 mCurrentTab = mTabs.indexOf(newTab);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700682 WebView mainView = newTab.getWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800683 boolean needRestore = (mainView == null);
684 if (needRestore) {
685 // Same work as in createNewTab() except don't do new Tab()
Steve Block2bc69912009-07-30 14:45:13 +0100686 mainView = createNewWebView();
687 newTab.setWebView(mainView);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800688 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700689 newTab.putInForeground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800690 if (needRestore) {
691 // Have to finish setCurrentTab work before calling restoreState
Grace Kloba22ac16e2009-10-07 18:00:23 -0700692 if (!newTab.restoreState(newTab.getSavedState())) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800693 mainView.loadUrl(BrowserSettings.getInstance().getHomePage());
694 }
695 }
696 return true;
697 }
Michael Kolbfe251992010-07-08 15:41:55 -0700698
699 interface TabChangeListener {
700
701 public void onNewTab(Tab tab);
702
703 public void onRemoveTab(Tab tab);
704
705 public void onCurrentTab(Tab tab);
706
707 public void onProgress(Tab tab, int progress);
708
709 public void onUrlAndTitle(Tab tab, String url, String title);
710
711 public void onFavicon(Tab tab, Bitmap favicon);
712
713 public void onPageStarted(Tab tab);
714
715 public void onPageFinished(Tab tab);
716
717 }
718
719 private TabChangeListener mTabChangeListener;
720
721 /**
722 * register the TabChangeListener with the tab control
723 * @param listener
724 */
725 void setOnTabChangeListener(TabChangeListener listener) {
726 mTabChangeListener = listener;
727 }
728
729 /**
730 * get the current TabChangeListener (used by the tabs)
731 */
732 TabChangeListener getTabChangeListener() {
733 return mTabChangeListener;
734 }
735
The Android Open Source Project0c908882009-03-03 19:32:16 -0800736}