FileDocCategorySizeDatePackage
TabControl.javaAPI DocAndroid 1.5 API35937Wed May 06 22:42:42 BST 2009com.android.browser

TabControl

public class TabControl extends Object

Fields Summary
private static final String
LOGTAG
static final int
MAX_TABS
private static final android.webkit.WebViewClient
mEmptyClient
private final BackgroundChromeClient
mBackgroundChromeClient
private ArrayList
mTabs
private ArrayList
mTabQueue
private int
mCurrentTab
private final BrowserActivity
mActivity
private final android.view.LayoutInflater
mInflateService
private final File
mThumbnailDir
private static final String
WEBVIEW
private static final String
NUMTABS
private static final String
CURRTAB
private static final String
CURRURL
private static final String
CURRTITLE
private static final String
CLOSEONEXIT
private static final String
PARENTTAB
private static final String
APPID
private static final String
ORIGINALURL
Constructors Summary
TabControl(BrowserActivity activity)
Construct a new TabControl object that interfaces with the given BrowserActivity instance.

param
activity A BrowserActivity instance that TabControl will interface with.

        mActivity = activity;
        mInflateService =
                ((LayoutInflater) activity.getSystemService(
                        Context.LAYOUT_INFLATER_SERVICE));
        mThumbnailDir = activity.getDir("thumbnails", 0);
    
Methods Summary
voidclearHistory()
Clear the back/forward list for all the current tabs.

        int size = getTabCount();
        for (int i = 0; i < size; i++) {
            Tab t = mTabs.get(i);
            // TODO: if a tab is freed due to low memory, its history is not
            // cleared here.
            if (t.mMainView != null) {
                t.mMainView.clearHistory();
            }
            if (t.mSubView != null) {
                t.mSubView.clearHistory();
            }
        }
    
com.android.browser.TabControl$TabcreateNewTab(boolean closeOnExit, java.lang.String appId, java.lang.String url)
Create a new tab.

return
The newly createTab or null if we have reached the maximum number of open tabs.

        int size = mTabs.size();
        // Return false if we have maxed out on tabs
        if (MAX_TABS == size) {
            return null;
        }
        final WebView w = createNewWebView();
        // Create a new tab and add it to the tab list
        Tab t = new Tab(w, closeOnExit, appId, url);
        mTabs.add(t);
        // Initially put the tab in the background.
        putTabInBackground(t);
        return t;
    
com.android.browser.TabControl$TabcreateNewTab()
Create a new tab with default values for closeOnExit(false), appId(null), and url(null).

        return createNewTab(false, null, null);
    
private android.webkit.WebViewcreateNewWebView()
Creates a new WebView and registers it with the global settings.

        // Create a new WebView
        WebView w = new WebView(mActivity);
        w.setMapTrackballToArrowKeys(false); // use trackball directly
        // Enable the built-in zoom
        w.getSettings().setBuiltInZoomControls(true);
        // Add this WebView to the settings observer list and update the
        // settings
        final BrowserSettings s = BrowserSettings.getInstance();
        s.addObserver(w.getSettings()).update(s, null);
        return w;
    
voidcreateSubWindow()
Create a new subwindow unless a subwindow already exists.

return
True if a new subwindow was created. False if one already exists.

        Tab t = getTab(mCurrentTab);
        if (t != null && t.mSubView == null) {
            final View v = mInflateService.inflate(R.layout.browser_subwindow, null);
            final WebView w = (WebView) v.findViewById(R.id.webview);
            w.setMapTrackballToArrowKeys(false); // use trackball directly
            final SubWindowClient subClient =
                    new SubWindowClient(mActivity.getWebViewClient());
            final SubWindowChromeClient subChromeClient =
                    new SubWindowChromeClient(t,
                            mActivity.getWebChromeClient());
            w.setWebViewClient(subClient);
            w.setWebChromeClient(subChromeClient);
            w.setDownloadListener(mActivity);
            w.setOnCreateContextMenuListener(mActivity);
            final BrowserSettings s = BrowserSettings.getInstance();
            s.addObserver(w.getSettings()).update(s, null);
            t.mSubView = w;
            t.mSubViewClient = subClient;
            t.mSubViewChromeClient = subChromeClient;
            // FIXME: I really hate having to know the name of the view
            // containing the webview.
            t.mSubViewContainer = v.findViewById(R.id.subwindow_container);
            final ImageButton cancel =
                    (ImageButton) v.findViewById(R.id.subwindow_close);
            cancel.setOnClickListener(new OnClickListener() {
                    public void onClick(View v) {
                        subChromeClient.onCloseWindow(w);
                    }
                });
        }
    
voiddestroy()
Destroy all the tabs and subwindows

        BrowserSettings s = BrowserSettings.getInstance();
        for (Tab t : mTabs) {
            if (t.mMainView != null) {
                dismissSubWindow(t);
                s.deleteObserver(t.mMainView.getSettings());
                t.mMainView.destroy();
                t.mMainView = null;
            }
        }
        mTabs.clear();
        mTabQueue.clear();
    
voiddismissSubWindow(com.android.browser.TabControl$Tab t)

        if (t != null && t.mSubView != null) {
            BrowserSettings.getInstance().deleteObserver(
                    t.mSubView.getSettings());
            t.mSubView.destroy();
            t.mSubView = null;
            t.mSubViewContainer = null;
        }
    
voidfreeMemory()
Free the memory in this order, 1) free the background tab; 2) free the WebView cache;

        // free the least frequently used background tab
        Tab t = getLeastUsedTab();
        if (t != null) {
            Log.w(LOGTAG, "Free a tab in the browser");
            freeTab(t);
            // force a gc
            System.gc();
            return;
        }

        // free the WebView cache
        Log.w(LOGTAG, "Free WebView cache");
        WebView view = getCurrentWebView();
        if (view != null) {
            view.clearCache(false);
        }
        // force a gc
        System.gc();
    
private voidfreeTab(com.android.browser.TabControl$Tab t)

        // Store the WebView's state.
        saveState(t);

        // Tear down the tab.
        dismissSubWindow(t);
        // Remove the WebView's settings from the BrowserSettings list of
        // observers.
        BrowserSettings.getInstance().deleteObserver(t.mMainView.getSettings());
        t.mMainView.destroy();
        t.mMainView = null;
    
BrowserActivitygetBrowserActivity()

        return mActivity;
    
intgetCurrentIndex()
Return the current tab index.

return
The current tab index

        return mCurrentTab;
    
android.webkit.WebViewgetCurrentSubWindow()
Return the current tab's subwindow if it exists.

return
The subwindow of the current tab or null if it doesn't exist.

        Tab t = getTab(mCurrentTab);
        if (t == null) {
            return null;
        }
        return t.mSubView;
    
com.android.browser.TabControl$TabgetCurrentTab()
Return the current tab.

return
The current tab.

        return getTab(mCurrentTab);
    
android.webkit.WebViewgetCurrentTopWebView()
Return the current tab's top-level WebView. This can return a subwindow if one exists.

return
The top-level WebView of the current tab.

        Tab t = getTab(mCurrentTab);
        if (t == null) {
            return null;
        }
        return t.mSubView != null ? t.mSubView : t.mMainView;
    
android.webkit.WebViewgetCurrentWebView()
Return the current tab's main WebView. This will always return the main WebView for a given tab and not a subwindow.

return
The current tab's WebView.

        Tab t = getTab(mCurrentTab);
        if (t == null) {
            return null;
        }
        return t.mMainView;
    
private com.android.browser.TabControl$TabgetLeastUsedTab()

        // Don't do anything if we only have 1 tab.
        if (getTabCount() == 1) {
            return null;
        }

        // Rip through the queue starting at the beginning and teardown the
        // next available tab.
        Tab t = null;
        int i = 0;
        final int queueSize = mTabQueue.size();
        if (queueSize == 0) {
            return null;
        }
        do {
            t = mTabQueue.get(i++);
        } while (i < queueSize && t != null && t.mMainView == null);

        // Don't do anything if the last remaining tab is the current one.
        if (t == getCurrentTab()) {
            return null;
        }

        return t;
    
com.android.browser.TabControl$TabgetTab(int index)
Return the tab at the specified index.

return
The Tab for the specified index or null if the tab does not exist.

        if (index >= 0 && index < mTabs.size()) {
            return mTabs.get(index);
        }
        return null;
    
intgetTabCount()
Returns the number of tabs created.

return
The number of tabs created.

        return mTabs.size();
    
com.android.browser.TabControl$TabgetTabFromId(java.lang.String id)
Return the tab with the matching application id.

param
id The application identifier.

        if (id == null) {
            return null;
        }
        final int size = getTabCount();
        for (int i = 0; i < size; i++) {
            final Tab t = getTab(i);
            if (id.equals(t.mAppId)) {
                return t;
            }
        }
        return null;
    
com.android.browser.TabControl$TabgetTabFromView(android.webkit.WebView view)
Show the tab that contains the given WebView.

param
view The WebView used to find the tab.

        final int size = getTabCount();
        for (int i = 0; i < size; i++) {
            final Tab t = getTab(i);
            if (t.mSubView == view || t.mMainView == view) {
                return t;
            }
        }
        return null;
    
intgetTabIndex(com.android.browser.TabControl$Tab tab)
Given a Tab, find it's index

param
Tab to find
return
index of Tab or -1 if not found

        return mTabs.indexOf(tab);
    
java.io.FilegetThumbnailDir()

        return mThumbnailDir;
    
voidpopulatePickerData(com.android.browser.TabControl$Tab t)
Ensure that Tab t has a title, url, and favicon.

param
t Tab to populate.

        if (t == null || t.mMainView == null) {
            return;
        }
        // FIXME: The only place we cared about subwindow was for 
        // bookmarking (i.e. not when saving state). Was this deliberate?
        final WebBackForwardList list = t.mMainView.copyBackForwardList();
        final WebHistoryItem item =
                list != null ? list.getCurrentItem() : null;
        populatePickerData(t, item);
    
private voidpopulatePickerData(com.android.browser.TabControl$Tab t, android.webkit.WebHistoryItem item)

        if (item != null) {
            t.mUrl = item.getUrl();
            t.mTitle = item.getTitle();
            if (t.mTitle == null) {
                t.mTitle = t.mUrl;
            }
        }
    
private voidputTabInBackground(com.android.browser.TabControl$Tab t)

        WebView mainView = t.mMainView;
        // Set an empty callback so that default actions are not triggered.
        mainView.setWebViewClient(mEmptyClient);
        mainView.setWebChromeClient(mBackgroundChromeClient);
        mainView.setOnCreateContextMenuListener(null);
        // Leave the DownloadManager attached so that downloads can start in
        // a non-active window. This can happen when going to a site that does
        // a redirect after a period of time. The user could have switched to
        // another tab while waiting for the download to start.
        mainView.setDownloadListener(mActivity);
        WebView subView = t.mSubView;
        if (subView != null) {
            // Set an empty callback so that default actions are not triggered.
            subView.setWebViewClient(mEmptyClient);
            subView.setWebChromeClient(mBackgroundChromeClient);
            subView.setOnCreateContextMenuListener(null);
            subView.setDownloadListener(mActivity);
        }
    
booleanrecreateWebView(com.android.browser.TabControl$Tab t, java.lang.String url)
Recreate the main WebView of the given tab. Returns true if the WebView was deleted.

        final WebView w = t.mMainView;
        if (w != null) {
            if (url != null && url.equals(t.mOriginalUrl)) {
                // The original url matches the current url. Just go back to the
                // first history item so we can load it faster than if we
                // rebuilt the WebView.
                final WebBackForwardList list = w.copyBackForwardList();
                if (list != null) {
                    w.goBackOrForward(-list.getCurrentIndex());
                    w.clearHistory(); // maintains the current page.
                    return false;
                }
            }
            // Remove the settings object from the global settings and destroy
            // the WebView.
            BrowserSettings.getInstance().deleteObserver(
                    t.mMainView.getSettings());
            t.mMainView.destroy();
        }
        // Create a new WebView. If this tab is the current tab, we need to put
        // back all the clients so force it to be the current tab.
        t.mMainView = createNewWebView();
        if (getCurrentTab() == t) {
            setCurrentTab(t, true);
        }
        // Clear the saved state except for the app id and close-on-exit
        // values.
        t.mSavedState = null;
        t.mUrl = null;
        t.mTitle = null;
        // Save the new url in order to avoid deleting the WebView.
        t.mOriginalUrl = url;
        return true;
    
booleanremoveTab(com.android.browser.TabControl$Tab t)
Remove the tab from the list. If the tab is the current tab shown, the last created tab will be shown.

param
t The tab to be removed.

        if (t == null) {
            return false;
        }
        // Only remove the tab if it is the current one.
        if (getCurrentTab() == t) {
            putTabInBackground(t);
        }

        // Only destroy the WebView if it still exists.
        if (t.mMainView != null) {
            // Take down the sub window.
            dismissSubWindow(t);
            // Remove the WebView's settings from the BrowserSettings list of
            // observers.
            BrowserSettings.getInstance().deleteObserver(
                    t.mMainView.getSettings());
            // Destroy the main view and subview
            t.mMainView.destroy();
            t.mMainView = null;
        }
        // clear it's references to parent and children
        t.removeFromTree();
        
        // Remove it from our list of tabs.
        mTabs.remove(t);

        // The tab indices have shifted, update all the saved state so we point
        // to the correct index.
        for (Tab tab : mTabs) {
            if (tab.mChildTabs != null) {
                for (Tab child : tab.mChildTabs) {
                    child.setParentTab(tab);
                }
            }
        }


        // This tab may have been pushed in to the background and then closed.
        // If the saved state contains a picture file, delete the file.
        if (t.mSavedState != null) {
            if (t.mSavedState.containsKey("picture")) {
                new File(t.mSavedState.getString("picture")).delete();
            }
        }

        // Remove it from the queue of viewed tabs.
        mTabQueue.remove(t);
        mCurrentTab = -1;
        return true;
    
booleanrestoreState(android.os.Bundle inState)
Restore the state of all the tabs.

param
inState The saved state of all the tabs.
return
True if there were previous tabs that were restored. False if there was no saved state or restoring the state failed.

        final int numTabs = (inState == null)
                ? -1 : inState.getInt(NUMTABS, -1);
        if (numTabs == -1) {
            return false;
        } else {
            final int currentTab = inState.getInt(CURRTAB, -1);
            for (int i = 0; i < numTabs; i++) {
                if (i == currentTab) {
                    Tab t = createNewTab();
                    // Me must set the current tab before restoring the state
                    // so that all the client classes are set.
                    setCurrentTab(t);
                    if (!restoreState(inState.getBundle(WEBVIEW + i), t)) {
                        Log.w(LOGTAG, "Fail in restoreState, load home page.");
                        t.mMainView.loadUrl(BrowserSettings.getInstance()
                                .getHomePage());
                    }
                } else {
                    // Create a new tab and don't restore the state yet, add it
                    // to the tab list
                    Tab t = new Tab(null, false, null, null);
                    t.mSavedState = inState.getBundle(WEBVIEW + i);
                    if (t.mSavedState != null) {
                        t.mUrl = t.mSavedState.getString(CURRURL);
                        t.mTitle = t.mSavedState.getString(CURRTITLE);
                        // Need to maintain the app id and original url so we
                        // can possibly reuse this tab.
                        t.mAppId = t.mSavedState.getString(APPID);
                        t.mOriginalUrl = t.mSavedState.getString(ORIGINALURL);
                    }
                    mTabs.add(t);
                    mTabQueue.add(t);
                }
            }
            // Rebuild the tree of tabs. Do this after all tabs have been
            // created/restored so that the parent tab exists.
            for (int i = 0; i < numTabs; i++) {
                final Bundle b = inState.getBundle(WEBVIEW + i);
                final Tab t = getTab(i);
                if (b != null && t != null) {
                    final int parentIndex = b.getInt(PARENTTAB, -1);
                    if (parentIndex != -1) {
                        final Tab parent = getTab(parentIndex);
                        if (parent != null) {
                            parent.addChildTab(t);
                        }
                    }
                }
            }
        }
        return true;
    
private booleanrestoreState(android.os.Bundle b, com.android.browser.TabControl$Tab t)

        if (b == null) {
            return false;
        }
        // Restore the internal state even if the WebView fails to restore.
        // This will maintain the app id, original url and close-on-exit values.
        t.mSavedState = null;
        t.mUrl = null;
        t.mTitle = null;
        t.mCloseOnExit = b.getBoolean(CLOSEONEXIT);
        t.mAppId = b.getString(APPID);
        t.mOriginalUrl = b.getString(ORIGINALURL);

        final WebView w = t.mMainView;
        final WebBackForwardList list = w.restoreState(b);
        if (list == null) {
            return false;
        }
        if (b.containsKey("picture")) {
            final File f = new File(b.getString("picture"));
            w.restorePicture(b, f);
            f.delete();
        }
        return true;
    
voidsaveState(android.os.Bundle outState)
Save the state of all the Tabs.

param
outState The Bundle to save the state to.


                         
       
        final int numTabs = getTabCount();
        outState.putInt(NUMTABS, numTabs);
        final int index = getCurrentIndex();
        outState.putInt(CURRTAB, (index >= 0 && index < numTabs) ? index : 0);
        for (int i = 0; i < numTabs; i++) {
            final Tab t = getTab(i);
            if (saveState(t)) {
                outState.putBundle(WEBVIEW + i, t.mSavedState);
            }
        }
    
private booleansaveState(com.android.browser.TabControl$Tab t)

        if (t != null) {
            final WebView w = t.mMainView;
            // If the WebView is null it means we ran low on memory and we
            // already stored the saved state in mSavedState.
            if (w == null) {
                return true;
            }
            final Bundle b = new Bundle();
            final WebBackForwardList list = w.saveState(b);
            if (list != null) {
                final File f = new File(mThumbnailDir, w.hashCode()
                        + "_pic.save");
                if (w.savePicture(b, f)) {
                    b.putString("picture", f.getPath());
                }
            }

            // Store some extra info for displaying the tab in the picker.
            final WebHistoryItem item =
                    list != null ? list.getCurrentItem() : null;
            populatePickerData(t, item);
            if (t.mUrl != null) {
                b.putString(CURRURL, t.mUrl);
            }
            if (t.mTitle != null) {
                b.putString(CURRTITLE, t.mTitle);
            }
            b.putBoolean(CLOSEONEXIT, t.mCloseOnExit);
            if (t.mAppId != null) {
                b.putString(APPID, t.mAppId);
            }
            if (t.mOriginalUrl != null) {
                b.putString(ORIGINALURL, t.mOriginalUrl);
            }

            // Remember the parent tab so the relationship can be restored.
            if (t.mParentTab != null) {
                b.putInt(PARENTTAB, getTabIndex(t.mParentTab));
            }

            // Remember the saved state.
            t.mSavedState = b;
            return true;
        }
        return false;
    
booleansetCurrentTab(com.android.browser.TabControl$Tab newTab)
Put the current tab in the background and set newTab as the current tab.

param
newTab The new tab. If newTab is null, the current tab is not set.

        return setCurrentTab(newTab, false);
    
private booleansetCurrentTab(com.android.browser.TabControl$Tab newTab, boolean force)
If force is true, this method skips the check for newTab == current.

        Tab current = getTab(mCurrentTab);
        if (current == newTab && !force) {
            return true;
        }
        if (current != null) {
            // Remove the current WebView and the container of the subwindow
            putTabInBackground(current);
        }

        if (newTab == null) {
            return false;
        }

        // Move the newTab to the end of the queue
        int index = mTabQueue.indexOf(newTab);
        if (index != -1) {
            mTabQueue.remove(index);
        }
        mTabQueue.add(newTab);

        WebView mainView;
        WebView subView;

        // Display the new current tab
        mCurrentTab = mTabs.indexOf(newTab);
        mainView = newTab.mMainView;
        boolean needRestore = (mainView == null);
        if (needRestore) {
            // Same work as in createNewTab() except don't do new Tab()
            newTab.mMainView = mainView = createNewWebView();
        }
        mainView.setWebViewClient(mActivity.getWebViewClient());
        mainView.setWebChromeClient(mActivity.getWebChromeClient());
        mainView.setOnCreateContextMenuListener(mActivity);
        mainView.setDownloadListener(mActivity);
        // Add the subwindow if it exists
        if (newTab.mSubViewContainer != null) {
            subView = newTab.mSubView;
            subView.setWebViewClient(newTab.mSubViewClient);
            subView.setWebChromeClient(newTab.mSubViewChromeClient);
            subView.setOnCreateContextMenuListener(mActivity);
            subView.setDownloadListener(mActivity);
        }
        if (needRestore) {
            // Have to finish setCurrentTab work before calling restoreState
            if (!restoreState(newTab.mSavedState, newTab)) {
                mainView.loadUrl(BrowserSettings.getInstance().getHomePage());
            }
        }
        return true;
    
voidwipeAllPickerData()
Clean up the data for all tabs.

        int size = getTabCount();
        for (int i = 0; i < size; i++) {
            final Tab t = getTab(i);
            if (t != null && t.mSavedState == null) {
                t.mUrl = null;
                t.mTitle = null;
            }
        }