org.zirco.ui.activities.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.zirco.ui.activities.MainActivity.java

Source

/*
 * Zirco Browser for Android
 * 
 * Copyright (C) 2010 - 2012 J. Devauchelle and contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

package org.zirco.ui.activities;

import java.lang.reflect.Field;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.http.HttpHost;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.RedirectLocations;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.greendroid.QuickAction;
import org.greendroid.QuickActionGrid;
import org.greendroid.QuickActionWidget;
import org.greendroid.QuickActionWidget.OnQuickActionClickListener;
import org.json.JSONArray;
import org.json.JSONException;
import com.psiphon3.R;
import org.zirco.controllers.Controller;
import org.zirco.events.EventConstants;
import org.zirco.events.EventController;
import org.zirco.events.IDownloadEventsListener;
import org.zirco.model.adapters.UrlSuggestionCursorAdapter;
import org.zirco.model.items.DownloadItem;
import org.zirco.providers.BookmarksProviderWrapper;
import org.zirco.ui.activities.preferences.PreferencesActivity;
import org.zirco.ui.components.CustomWebView;
import org.zirco.ui.components.CustomWebViewClient;
import org.zirco.ui.runnables.FaviconUpdaterRunnable;
import org.zirco.ui.runnables.HideToolbarsRunnable;
import org.zirco.ui.runnables.HistoryUpdater;
import org.zirco.utils.AnimationManager;
import org.zirco.utils.ApplicationUtils;
import org.zirco.utils.Constants;
import org.zirco.utils.UrlUtils;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.ContextMenu;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.inputmethod.InputMethodManager;
import android.webkit.DownloadListener;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebIconDatabase;
import android.webkit.WebView;
import android.webkit.WebView.HitTestResult;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.FilterQueryProvider;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewFlipper;
import android.widget.SimpleCursorAdapter.CursorToStringConverter;

import net.grandcentrix.tray.AppPreferences;

/**
 * The application main activity.
 */
public class MainActivity extends Activity implements IToolbarsContainer, OnTouchListener, IDownloadEventsListener {

    public static MainActivity INSTANCE = null;

    private static final int FLIP_PIXEL_THRESHOLD = 200;
    private static final int FLIP_TIME_THRESHOLD = 400;

    private static final int MENU_FEEDBACK = Menu.FIRST;
    private static final int MENU_ADD_BOOKMARK = Menu.FIRST + 1;
    private static final int MENU_SHOW_BOOKMARKS = Menu.FIRST + 2;
    private static final int MENU_SHOW_DOWNLOADS = Menu.FIRST + 3;
    private static final int MENU_PREFERENCES = Menu.FIRST + 4;
    private static final int MENU_EXIT = Menu.FIRST + 5;

    private static final int CONTEXT_MENU_OPEN = Menu.FIRST + 10;
    private static final int CONTEXT_MENU_OPEN_IN_NEW_TAB = Menu.FIRST + 11;
    private static final int CONTEXT_MENU_DOWNLOAD = Menu.FIRST + 12;
    private static final int CONTEXT_MENU_COPY = Menu.FIRST + 13;
    private static final int CONTEXT_MENU_SEND_MAIL = Menu.FIRST + 14;
    private static final int CONTEXT_MENU_SHARE = Menu.FIRST + 15;

    private static final int OPEN_BOOKMARKS_HISTORY_ACTIVITY = 0;
    private static final int OPEN_DOWNLOADS_ACTIVITY = 1;
    private static final int OPEN_FILE_CHOOSER_ACTIVITY = 2;

    protected static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

    protected LayoutInflater mInflater = null;

    private LinearLayout mTopBar;
    private LinearLayout mBottomBar;

    private LinearLayout mFindBar;

    private ImageButton mFindPreviousButton;
    private ImageButton mFindNextButton;
    private ImageButton mFindCloseButton;

    private EditText mFindText;

    private ImageView mPreviousTabView;
    private ImageView mNextTabView;

    private ImageButton mToolsButton;
    private AutoCompleteTextView mUrlEditText;
    private ImageButton mGoButton;
    private ProgressBar mProgressBar;

    private ImageView mBubbleRightView;
    private ImageView mBubbleLeftView;

    private CustomWebView mCurrentWebView;
    private List<CustomWebView> mWebViews;

    private ImageButton mPreviousButton;
    private ImageButton mNextButton;

    private ImageButton mNewTabButton;
    private ImageButton mRemoveTabButton;

    private ImageButton mQuickButton;

    private Drawable mCircularProgress;

    private boolean mUrlBarVisible;
    private boolean mToolsActionGridVisible = false;
    private boolean mFindDialogVisible = false;

    private TextWatcher mUrlTextWatcher;

    private HideToolbarsRunnable mHideToolbarsRunnable;

    private ViewFlipper mViewFlipper;

    private GestureDetector mGestureDetector;

    private SwitchTabsMethod mSwitchTabsMethod = SwitchTabsMethod.BOTH;

    private QuickActionGrid mToolsActionGrid;

    private ValueCallback<Uri> mUploadMessage;

    private OnSharedPreferenceChangeListener mPreferenceChangeListener;

    private View mCustomView;
    private Bitmap mDefaultVideoPoster = null;
    private View mVideoProgressView = null;

    private FrameLayout mFullscreenContainer;

    private WebChromeClient.CustomViewCallback mCustomViewCallback;

    private int mOriginalOrientation;

    private enum SwitchTabsMethod {
        BUTTONS, FLING, BOTH
    }

    // PSIPHON
    private String mPsiphonServiceClassName;
    private String mPsiphonStatusActivityClassName;
    private String mPsiphonFeedbackActivityClassName;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = getIntent();

        INSTANCE = this;

        Constants.initializeConstantsFromResources(this);

        Controller.getInstance().setPreferences(PreferenceManager.getDefaultSharedPreferences(this));

        if (Controller.getInstance().getPreferences().getBoolean(Constants.PREFERENCES_SHOW_FULL_SCREEN, false)) {
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }

        // PSIPHON: always show the title bar
        //if (Controller.getInstance().getPreferences().getBoolean(Constants.PREFERENCES_GENERAL_HIDE_TITLE_BARS, true)) {
        //   requestWindowFeature(Window.FEATURE_NO_TITLE); 
        //}

        setProgressBarVisibility(true);

        setContentView(R.layout.zirco_main);

        mCircularProgress = getResources().getDrawable(R.drawable.spinner);

        EventController.getInstance().addDownloadListener(this);

        mHideToolbarsRunnable = null;

        mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        buildComponents();

        mViewFlipper.removeAllViews();

        updateSwitchTabsMethod();

        // PSIPHON: Store the class names of the service and status activity.
        // We'll use them to make sure the service is running.
        mPsiphonServiceClassName = intent.getStringExtra("serviceClassName");
        mPsiphonStatusActivityClassName = intent.getStringExtra("statusActivityClassName");
        mPsiphonFeedbackActivityClassName = intent.getStringExtra("feedbackActivityClassName");

        if (!ensurePsiphonRunning()) {
            // Must return before trying to open tabs and whatnot.
            // Control switching to Psiphon task.
            return;
        }

        // PSIPHON: always use internal bookmarks
        //updateBookmarksDatabaseSource();
        //
        //registerPreferenceChangeListener();

        //
        // Psiphon: Restore browser tabs from last session
        //
        if (Controller.getInstance().getPreferences().getBoolean(Constants.PREFERENCES_BROWSER_RESTORE_LAST_PAGE,
                true)) {
            try {
                JSONArray jsonURLs = new JSONArray(
                        Controller.getInstance().getPreferences().getString("urlsToRestore", "[]"));
                for (int i = 0; i < jsonURLs.length(); i++) {
                    String url = jsonURLs.getString(i);
                    if (url == null || url.length() == 0)
                        continue;
                    addTab(false);
                    navigateToUrl(url);
                }
            } catch (JSONException eJSON) {
                // Do nothing
            }

        }
        // end Psiphon changes

        // PSIPHON: open home pages if they're not already open (or a blank tab if none)
        ArrayList<String> homePages = intent.getStringArrayListExtra("homePages");

        if (intent.getData() != null) {
            homePages.add(intent.getDataString());
        }

        for (String homePage : homePages) {
            boolean urlAlreadyOpen = false;

            // If we already have tabs open, check to make sure the homePages 
            // won't create duplicate tabs.
            if (mWebViews.size() > 0) {
                // Many home pages redirect to another URL, so to determine if
                // an open tab really matches a home page, we'll need to collect
                // that home page's redirect URLs.
                List<String> homePageEquivs = findEquivalentUrls(
                        mWebViews.get(0).getSettings().getUserAgentString(), homePage);

                for (CustomWebView webView : mWebViews) {
                    String webViewUrl = webView.getUrl();
                    if (webViewUrl == null || webViewUrl.length() == 0) {
                        webViewUrl = webView.getLoadedUrl();
                    }

                    if (webViewUrl == null)
                        continue;

                    for (String homePageEquiv : homePageEquivs) {
                        if (homePageEquiv.compareToIgnoreCase(webViewUrl) == 0) {
                            urlAlreadyOpen = true;
                            break;
                        }
                    }

                    if (urlAlreadyOpen)
                        break;
                }
            }

            if (!urlAlreadyOpen) {
                addTab(false);
                navigateToUrl(homePage);
            }
        }
        if (mWebViews.size() == 0) {
            addTab(true);
        }

        // PSIPHON: don't show Zirco changelist or restore last page
        // The current intent data Uri is also deliberately ignored
        //
        /*
        if (intent.getData() != null) {
           // App first launch from another app.
           addTab(false);
           navigateToUrl(intent.getDataString());
        } else {
           // Normal start.
           int currentVersionCode = ApplicationUtils.getApplicationVersionCode(this);
           int savedVersionCode = PreferenceManager.getDefaultSharedPreferences(this).getInt(Constants.PREFERENCES_LAST_VERSION_CODE, -1);
               
           // If currentVersionCode and savedVersionCode are different, the application has been updated.
           if (currentVersionCode != savedVersionCode) {
          // Save current version code.
          Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
           editor.putInt(Constants.PREFERENCES_LAST_VERSION_CODE, currentVersionCode);
           editor.commit();
               
           // Display changelog dialog.
           Intent changelogIntent = new Intent(this, ChangelogActivity.class);
          startActivity(changelogIntent);
           }
               
           boolean lastPageRestored = false;
           if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(Constants.PREFERENCES_BROWSER_RESTORE_LAST_PAGE, true)) {
          if (savedInstanceState != null) {              
             String savedUrl = savedInstanceState.getString(Constants.EXTRA_SAVED_URL);
             if (savedUrl != null) {
                addTab(false);
                navigateToUrl(savedUrl);
                lastPageRestored = true;
             }
          }
           }
               
           if (!lastPageRestored) {
          addTab(true);
           }
        }
        */

        initializeWebIconDatabase();

        startToolbarsHideRunnable();
    }

    // PSIPHON
    // TODO: Update to ensure Psiphon is *connected*, not just that the service
    // is running.
    /**
     * Check that Psiphon is running. If it isn't, launch it and close this task. 
     * @return  true if Psiphon is running, false otherwise. If false, the caller
     *          should avoid doing significant work (e.g., just return).
     */
    private boolean ensurePsiphonRunning() {
        // See com.psiphon3.StatusActivity#isServiceRunning() for details.
        ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (service.uid == android.os.Process.myUid()
                    && this.mPsiphonServiceClassName.equals(service.service.getClassName())) {
                return true;
            }
        }

        Class psiphonStatusActivityClass;
        try {
            psiphonStatusActivityClass = Class.forName(this.mPsiphonStatusActivityClassName);
        } catch (ClassNotFoundException e) {
            // This shouldn't happen. 
            return false;
        }

        // The Psiphon service isn't running. Punt back to the Psiphon status activity.
        Intent intent = new Intent("ACTION_VIEW", null, this, psiphonStatusActivityClass);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        this.startActivity(intent);

        this.finish();

        return false;
    }

    /**
     * Initialize the Web icons database.
     */
    private void initializeWebIconDatabase() {

        final WebIconDatabase db = WebIconDatabase.getInstance();
        db.open(getDir("icons", 0).getPath());
    }

    @Override
    protected void onDestroy() {
        WebIconDatabase.getInstance().close();

        if (PreferenceManager.getDefaultSharedPreferences(this)
                .getBoolean(Constants.PREFERENCES_PRIVACY_CLEAR_CACHE_ON_EXIT, false)) {
            mCurrentWebView.clearCache(true);
        }

        EventController.getInstance().removeDownloadListener(this);

        PreferenceManager.getDefaultSharedPreferences(this)
                .unregisterOnSharedPreferenceChangeListener(mPreferenceChangeListener);

        super.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putString(Constants.EXTRA_SAVED_URL, mCurrentWebView.getUrl());
        super.onSaveInstanceState(outState);
    }

    /**
      * Handle url request from external apps.
      * @param intent The intent.
      */
    @Override
    protected void onNewIntent(Intent intent) {

        // PSIPHON: this will ignore "homePages" extra, by design

        if (intent.getData() != null) {
            addTab(false);
            navigateToUrl(intent.getDataString());
        }

        setIntent(intent);

        super.onNewIntent(intent);
    }

    /**
     * Restart the application.
     */
    public void restartApplication() {
        PendingIntent intent = PendingIntent.getActivity(this.getBaseContext(), 0, new Intent(getIntent()),
                getIntent().getFlags());
        AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 2000, intent);
        System.exit(2);
    }

    /**
     * Create main UI.
     */
    private void buildComponents() {

        mToolsActionGrid = new QuickActionGrid(this);
        mToolsActionGrid.addQuickAction(new QuickAction(this, R.drawable.ic_btn_home, R.string.QuickAction_Home));
        mToolsActionGrid.addQuickAction(new QuickAction(this, R.drawable.ic_btn_share, R.string.QuickAction_Share));
        mToolsActionGrid.addQuickAction(new QuickAction(this, R.drawable.ic_btn_find, R.string.QuickAction_Find));
        mToolsActionGrid
                .addQuickAction(new QuickAction(this, R.drawable.ic_btn_select, R.string.QuickAction_SelectText));
        mToolsActionGrid.addQuickAction(
                new QuickAction(this, R.drawable.ic_btn_mobile_view, R.string.QuickAction_MobileView));

        mToolsActionGrid.setOnQuickActionClickListener(new OnQuickActionClickListener() {
            @Override
            public void onQuickActionClicked(QuickActionWidget widget, int position) {
                switch (position) {
                case 0:
                    navigateToHome();
                    break;
                case 1:
                    ApplicationUtils.sharePage(MainActivity.this, mCurrentWebView.getTitle(),
                            mCurrentWebView.getUrl());
                    break;
                case 2:
                    // Somewhat dirty hack: when the find dialog was shown from a QuickAction,
                    // the soft keyboard did not show... Hack is to wait a little before showing
                    // the file dialog through a thread.
                    startShowFindDialogRunnable();
                    break;
                case 3:
                    swithToSelectAndCopyTextMode();
                    break;
                case 4:
                    String currentUrl = mUrlEditText.getText().toString();

                    // Do not reload mobile view if already on it.
                    if (!currentUrl.startsWith(Constants.URL_GOOGLE_MOBILE_VIEW_NO_FORMAT)) {
                        String url = String.format(Constants.URL_GOOGLE_MOBILE_VIEW,
                                mUrlEditText.getText().toString());
                        navigateToUrl(url);
                    }
                    break;
                }
            }
        });

        mToolsActionGrid.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                mToolsActionGridVisible = false;
                startToolbarsHideRunnable();
            }
        });

        mGestureDetector = new GestureDetector(this, new GestureListener());

        mUrlBarVisible = true;

        mWebViews = new ArrayList<CustomWebView>();
        Controller.getInstance().setWebViewList(mWebViews);

        mBubbleRightView = (ImageView) findViewById(R.id.BubbleRightView);
        mBubbleRightView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setToolbarsVisibility(true);
            }
        });
        mBubbleRightView.setVisibility(View.GONE);

        mBubbleLeftView = (ImageView) findViewById(R.id.BubbleLeftView);
        mBubbleLeftView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setToolbarsVisibility(true);
            }
        });
        mBubbleLeftView.setVisibility(View.GONE);

        mViewFlipper = (ViewFlipper) findViewById(R.id.ViewFlipper);

        mTopBar = (LinearLayout) findViewById(R.id.BarLayout);
        mTopBar.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // Dummy event to steel it from the WebView, in case of clicking between the buttons.            
            }
        });

        mBottomBar = (LinearLayout) findViewById(R.id.BottomBarLayout);
        mBottomBar.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // Dummy event to steel it from the WebView, in case of clicking between the buttons.            
            }
        });

        mFindBar = (LinearLayout) findViewById(R.id.findControls);
        mFindBar.setVisibility(View.GONE);

        mPreviousTabView = (ImageView) findViewById(R.id.PreviousTabView);
        mPreviousTabView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showPreviousTab(true);
            }
        });
        mPreviousTabView.setVisibility(View.GONE);

        mNextTabView = (ImageView) findViewById(R.id.NextTabView);
        mNextTabView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showNextTab(true);
            }
        });
        mNextTabView.setVisibility(View.GONE);

        String[] from = new String[] { UrlSuggestionCursorAdapter.URL_SUGGESTION_TITLE,
                UrlSuggestionCursorAdapter.URL_SUGGESTION_URL };
        int[] to = new int[] { R.id.AutocompleteTitle, R.id.AutocompleteUrl };

        UrlSuggestionCursorAdapter adapter = new UrlSuggestionCursorAdapter(this, R.layout.url_autocomplete_line,
                null, from, to);

        adapter.setCursorToStringConverter(new CursorToStringConverter() {
            @Override
            public CharSequence convertToString(Cursor cursor) {
                String aColumnString = cursor
                        .getString(cursor.getColumnIndex(UrlSuggestionCursorAdapter.URL_SUGGESTION_URL));
                return aColumnString;
            }
        });

        adapter.setFilterQueryProvider(new FilterQueryProvider() {
            @Override
            public Cursor runQuery(CharSequence constraint) {
                if ((constraint != null) && (constraint.length() > 0)) {
                    return BookmarksProviderWrapper.getUrlSuggestions(getContentResolver(), constraint.toString(),
                            PreferenceManager.getDefaultSharedPreferences(MainActivity.this)
                                    .getBoolean(Constants.PREFERENCE_USE_WEAVE, false));
                } else {
                    return BookmarksProviderWrapper.getUrlSuggestions(getContentResolver(), null,
                            PreferenceManager.getDefaultSharedPreferences(MainActivity.this)
                                    .getBoolean(Constants.PREFERENCE_USE_WEAVE, false));
                }
            }
        });

        mUrlEditText = (AutoCompleteTextView) findViewById(R.id.UrlText);
        mUrlEditText.setThreshold(1);
        mUrlEditText.setAdapter(adapter);
        mUrlEditText.setSelectAllOnFocus(true);

        mUrlEditText.setOnKeyListener(new View.OnKeyListener() {

            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_ENTER) {
                    navigateToUrl();
                    return true;
                }

                return false;
            }

        });

        mUrlTextWatcher = new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
            }

            @Override
            public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
            }

            @Override
            public void afterTextChanged(Editable arg0) {
                updateGoButton();
            }
        };

        mUrlEditText.addTextChangedListener(mUrlTextWatcher);

        mUrlEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {

            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                // Select all when focus gained.
                if (hasFocus) {
                    mUrlEditText.setSelection(0, mUrlEditText.getText().length());
                }
            }
        });

        mUrlEditText.setCompoundDrawablePadding(5);

        mGoButton = (ImageButton) findViewById(R.id.GoBtn);
        mGoButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {

                if (mCurrentWebView.isLoading()) {
                    mCurrentWebView.stopLoading();
                } else if (!mCurrentWebView.isSameUrl(mUrlEditText.getText().toString())) {
                    navigateToUrl();
                } else {
                    mCurrentWebView.reload();
                }
            }
        });

        mToolsButton = (ImageButton) findViewById(R.id.ToolsBtn);
        mToolsButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mToolsActionGridVisible = true;
                mToolsActionGrid.show(v);
            }
        });

        mProgressBar = (ProgressBar) findViewById(R.id.WebViewProgress);
        mProgressBar.setMax(100);

        mPreviousButton = (ImageButton) findViewById(R.id.PreviousBtn);
        mNextButton = (ImageButton) findViewById(R.id.NextBtn);

        mPreviousButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                navigatePrevious();
            }
        });

        mNextButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                navigateNext();
            }
        });

        mNewTabButton = (ImageButton) findViewById(R.id.NewTabBtn);
        mNewTabButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                addTab(true);
            }
        });

        mRemoveTabButton = (ImageButton) findViewById(R.id.RemoveTabBtn);
        mRemoveTabButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                if (mViewFlipper.getChildCount() == 1
                        && !mCurrentWebView.getUrl().equals(Constants.URL_ABOUT_START)) {
                    navigateToHome();
                    updateUI();
                    updatePreviousNextTabViewsVisibility();
                } else
                    removeCurrentTab();
            }
        });

        mQuickButton = (ImageButton) findViewById(R.id.QuickBtn);
        mQuickButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                onQuickButton();
            }
        });

        mFindPreviousButton = (ImageButton) findViewById(R.id.find_previous);
        mFindPreviousButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                mCurrentWebView.findNext(false);
                hideKeyboardFromFindDialog();
            }
        });

        mFindNextButton = (ImageButton) findViewById(R.id.find_next);
        mFindNextButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                mCurrentWebView.findNext(true);
                hideKeyboardFromFindDialog();
            }
        });

        mFindCloseButton = (ImageButton) findViewById(R.id.find_close);
        mFindCloseButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                closeFindDialog();
            }
        });

        mFindText = (EditText) findViewById(R.id.find_value);
        mFindText.addTextChangedListener(new TextWatcher() {

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                doFind();
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void afterTextChanged(Editable s) {
            }
        });

    }

    // PSIPHON: always use internal DB for bookmarks
    /*
    private void registerPreferenceChangeListener() {
        mPreferenceChangeListener = new OnSharedPreferenceChangeListener() {         
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (key.equals(Constants.PREFERENCE_BOOKMARKS_DATABASE)) {
           updateBookmarksDatabaseSource();
        }
     }
       };
           
       PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(mPreferenceChangeListener);
     }
     */

    /**
     * Apply preferences to the current UI objects.
     */
    public void applyPreferences() {
        // To update to Bubble position.
        setToolbarsVisibility(false);

        updateSwitchTabsMethod();

        for (CustomWebView view : mWebViews) {
            view.initializeOptions();
        }
    }

    private void updateSwitchTabsMethod() {
        String method = PreferenceManager.getDefaultSharedPreferences(this)
                .getString(Constants.PREFERENCES_GENERAL_SWITCH_TABS_METHOD, "buttons");

        if (method.equals("buttons")) {
            mSwitchTabsMethod = SwitchTabsMethod.BUTTONS;
        } else if (method.equals("fling")) {
            mSwitchTabsMethod = SwitchTabsMethod.FLING;
        } else if (method.equals("both")) {
            mSwitchTabsMethod = SwitchTabsMethod.BOTH;
        } else {
            mSwitchTabsMethod = SwitchTabsMethod.BUTTONS;
        }
    }

    /*
    private void updateBookmarksDatabaseSource() {
       String source = PreferenceManager.getDefaultSharedPreferences(this).getString(Constants.PREFERENCE_BOOKMARKS_DATABASE, "INTERNAL");
           
       if (source.equals("STOCK")) {
      BookmarksProviderWrapper.setBookmarksSource(BookmarksSource.STOCK);
       } else if (source.equals("INTERNAL")) {
      BookmarksProviderWrapper.setBookmarksSource(BookmarksSource.INTERNAL);
       }
    }
    */

    private void setStatusBarVisibility(boolean visible) {
        int flag = visible ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN;
        getWindow().setFlags(flag, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }

    /**
     * Initialize a newly created WebView.
     */
    private void initializeCurrentWebView() {

        mCurrentWebView.setWebViewClient(new CustomWebViewClient(this));
        mCurrentWebView.setOnTouchListener(this);

        mCurrentWebView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {

            @Override
            public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
                HitTestResult result = ((WebView) v).getHitTestResult();

                if (result == null) {
                    return;
                }

                int resultType = result.getType();
                if ((resultType == HitTestResult.ANCHOR_TYPE) || (resultType == HitTestResult.IMAGE_ANCHOR_TYPE)
                        || (resultType == HitTestResult.SRC_ANCHOR_TYPE)
                        || (resultType == HitTestResult.SRC_IMAGE_ANCHOR_TYPE)) {

                    Intent i = new Intent();
                    i.putExtra(Constants.EXTRA_ID_URL, result.getExtra());

                    MenuItem item = menu.add(0, CONTEXT_MENU_OPEN, 0, R.string.Main_MenuOpen);
                    item.setIntent(i);

                    item = menu.add(0, CONTEXT_MENU_OPEN_IN_NEW_TAB, 0, R.string.Main_MenuOpenNewTab);
                    item.setIntent(i);

                    item = menu.add(0, CONTEXT_MENU_COPY, 0, R.string.Main_MenuCopyLinkUrl);
                    item.setIntent(i);

                    item = menu.add(0, CONTEXT_MENU_DOWNLOAD, 0, R.string.Main_MenuDownload);
                    item.setIntent(i);

                    item = menu.add(0, CONTEXT_MENU_SHARE, 0, R.string.Main_MenuShareLinkUrl);
                    item.setIntent(i);

                    menu.setHeaderTitle(result.getExtra());
                } else if (resultType == HitTestResult.IMAGE_TYPE) {
                    Intent i = new Intent();
                    i.putExtra(Constants.EXTRA_ID_URL, result.getExtra());

                    MenuItem item = menu.add(0, CONTEXT_MENU_OPEN, 0, R.string.Main_MenuViewImage);
                    item.setIntent(i);

                    item = menu.add(0, CONTEXT_MENU_COPY, 0, R.string.Main_MenuCopyImageUrl);
                    item.setIntent(i);

                    item = menu.add(0, CONTEXT_MENU_DOWNLOAD, 0, R.string.Main_MenuDownloadImage);
                    item.setIntent(i);

                    item = menu.add(0, CONTEXT_MENU_SHARE, 0, R.string.Main_MenuShareImageUrl);
                    item.setIntent(i);

                    menu.setHeaderTitle(result.getExtra());

                } else if (resultType == HitTestResult.EMAIL_TYPE) {

                    Intent sendMail = new Intent(Intent.ACTION_VIEW,
                            Uri.parse(WebView.SCHEME_MAILTO + result.getExtra()));

                    MenuItem item = menu.add(0, CONTEXT_MENU_SEND_MAIL, 0, R.string.Main_MenuSendEmail);
                    item.setIntent(sendMail);

                    Intent i = new Intent();
                    i.putExtra(Constants.EXTRA_ID_URL, result.getExtra());

                    item = menu.add(0, CONTEXT_MENU_COPY, 0, R.string.Main_MenuCopyEmailUrl);
                    item.setIntent(i);

                    item = menu.add(0, CONTEXT_MENU_SHARE, 0, R.string.Main_MenuShareEmailUrl);
                    item.setIntent(i);

                    menu.setHeaderTitle(result.getExtra());
                }
            }

        });

        mCurrentWebView.setDownloadListener(new DownloadListener() {

            @Override
            public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype,
                    long contentLength) {
                doDownloadStart(url, userAgent, contentDisposition, mimetype, contentLength);
            }

        });

        final Activity activity = this;
        mCurrentWebView.setWebChromeClient(new WebChromeClient() {

            @SuppressWarnings("unused")
            // This is an undocumented method, it _is_ used, whatever Eclipse may think :)
            // Used to show a file chooser dialog.
            public void openFileChooser(ValueCallback<Uri> uploadMsg) {
                mUploadMessage = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*");
                MainActivity.this.startActivityForResult(
                        Intent.createChooser(i, MainActivity.this.getString(R.string.Main_FileChooserPrompt)),
                        OPEN_FILE_CHOOSER_ACTIVITY);
            }

            @Override
            public Bitmap getDefaultVideoPoster() {
                if (mDefaultVideoPoster == null) {
                    mDefaultVideoPoster = BitmapFactory.decodeResource(MainActivity.this.getResources(),
                            R.drawable.default_video_poster);
                }

                return mDefaultVideoPoster;
            }

            @Override
            public View getVideoLoadingProgressView() {
                if (mVideoProgressView == null) {
                    LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
                    mVideoProgressView = inflater.inflate(R.layout.video_loading_progress, null);
                }

                return mVideoProgressView;
            }

            @Override
            public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
                super.onShowCustomView(view, callback);
                showCustomView(view, -1, callback);
            }

            @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
            @Override
            public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                    super.onShowCustomView(view, requestedOrientation, callback);
                }
                showCustomView(view, requestedOrientation, callback);
            }

            @Override
            public void onHideCustomView() {
                hideCustomView();
            }

            //         @Override
            //         public void onShowCustomView(View view, CustomViewCallback callback) {
            //            super.onShowCustomView(view, callback);
            //            
            //            if (view instanceof FrameLayout) {
            //               mCustomViewContainer = (FrameLayout) view;
            //                  mCustomViewCallback = callback;
            //                  
            //                  mContentView = (LinearLayout) findViewById(R.id.MainContainer);
            //                  
            //                  if (mCustomViewContainer.getFocusedChild() instanceof VideoView) {
            //                      mCustomVideoView = (VideoView) mCustomViewContainer.getFocusedChild();
            //                      // frame.removeView(video);
            //                      mContentView.setVisibility(View.GONE);
            //                      mCustomViewContainer.setVisibility(View.VISIBLE);
            //                      
            //                      setContentView(mCustomViewContainer);
            //                      //mCustomViewContainer.bringToFront();
            //
            //                      mCustomVideoView.setOnCompletionListener(new OnCompletionListener() {                     
            //                     @Override
            //                     public void onCompletion(MediaPlayer mp) {
            //                        mp.stop();
            //                        onHideCustomView();
            //                     }
            //                  });
            //                      
            //                      mCustomVideoView.setOnErrorListener(new OnErrorListener() {                  
            //                     @Override
            //                     public boolean onError(MediaPlayer mp, int what, int extra) {
            //                        onHideCustomView();
            //                        return true;
            //                     }
            //                  });
            //                      
            //                      mCustomVideoView.start();
            //                  }
            //
            //            }
            //         }
            //
            //         @Override
            //         public void onHideCustomView() {
            //            super.onHideCustomView();
            //            
            //            if (mCustomVideoView == null) {
            //               return;
            //            }
            //            
            //            mCustomVideoView.setVisibility(View.GONE);
            //            mCustomViewContainer.removeView(mCustomVideoView);
            //            mCustomVideoView = null;
            //            
            //            mCustomViewContainer.setVisibility(View.GONE);
            //              mCustomViewCallback.onCustomViewHidden();
            //              
            //              mContentView.setVisibility(View.VISIBLE);
            //              setContentView(mContentView);              
            //         }

            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                ((CustomWebView) view).setProgress(newProgress);
                mProgressBar.setProgress(mCurrentWebView.getProgress());
            }

            @Override
            public void onReceivedIcon(WebView view, Bitmap icon) {
                new Thread(
                        new FaviconUpdaterRunnable(MainActivity.this, view.getUrl(), view.getOriginalUrl(), icon))
                                .start();
                updateFavIcon();

                super.onReceivedIcon(view, icon);
            }

            @Override
            public boolean onCreateWindow(WebView view, final boolean dialog, final boolean userGesture,
                    final Message resultMsg) {

                WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;

                addTab(false, mViewFlipper.getDisplayedChild());

                transport.setWebView(mCurrentWebView);
                resultMsg.sendToTarget();

                return true;
            }

            @Override
            public void onReceivedTitle(WebView view, String title) {
                setTitle(String.format(getResources().getString(R.string.ApplicationNameUrl), title));

                startHistoryUpdaterRunnable(title, mCurrentWebView.getUrl(), mCurrentWebView.getOriginalUrl());

                super.onReceivedTitle(view, title);
            }

            @Override
            public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
                new AlertDialog.Builder(activity).setTitle(R.string.Commons_JavaScriptDialog).setMessage(message)
                        .setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                result.confirm();
                            }
                        }).setCancelable(false).create().show();

                return true;
            }

            @Override
            public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
                new AlertDialog.Builder(MainActivity.this).setTitle(R.string.Commons_JavaScriptDialog)
                        .setMessage(message)
                        .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                result.confirm();
                            }
                        }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                result.cancel();
                            }
                        }).create().show();

                return true;
            }

            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
                    final JsPromptResult result) {

                final LayoutInflater factory = LayoutInflater.from(MainActivity.this);
                final View v = factory.inflate(R.layout.javascript_prompt_dialog, null);
                ((TextView) v.findViewById(R.id.JavaScriptPromptMessage)).setText(message);
                ((EditText) v.findViewById(R.id.JavaScriptPromptInput)).setText(defaultValue);

                new AlertDialog.Builder(MainActivity.this).setTitle(R.string.Commons_JavaScriptDialog).setView(v)
                        .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int whichButton) {
                                String value = ((EditText) v.findViewById(R.id.JavaScriptPromptInput)).getText()
                                        .toString();
                                result.confirm(value);
                            }
                        }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int whichButton) {
                                result.cancel();
                            }
                        }).setOnCancelListener(new DialogInterface.OnCancelListener() {
                            public void onCancel(DialogInterface dialog) {
                                result.cancel();
                            }
                        }).show();

                return true;

            }

        });
    }

    /**
     * Select Text in the webview and automatically sends the selected text to the clipboard.
     */
    public void swithToSelectAndCopyTextMode() {
        try {
            KeyEvent shiftPressEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SHIFT_LEFT, 0, 0);
            shiftPressEvent.dispatch(mCurrentWebView);
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }

    /**
     * Initiate a download. Check the SD card and start the download runnable.
     * @param url The url to download.
     * @param userAgent The user agent.
     * @param contentDisposition The content disposition.
     * @param mimetype The mime type.
     * @param contentLength The content length.
     */
    private void doDownloadStart(String url, String userAgent, String contentDisposition, String mimetype,
            long contentLength) {

        if (ApplicationUtils.checkCardState(this, true)) {
            DownloadItem item = new DownloadItem(this, url);
            Controller.getInstance().addToDownload(item);
            item.startDownload();

            Toast.makeText(this, getString(R.string.Main_DownloadStartedMsg), Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Add a new tab.
     * @param navigateToHome If True, will load the user home page.
     */
    private void addTab(boolean navigateToHome) {
        addTab(navigateToHome, -1);
    }

    /**
     * Add a new tab.
     * @param navigateToHome If True, will load the user home page.
     * @param parentIndex The index of the new tab.
     */
    private void addTab(boolean navigateToHome, int parentIndex) {
        if (mFindDialogVisible) {
            closeFindDialog();
        }

        RelativeLayout view = (RelativeLayout) mInflater.inflate(R.layout.webview, mViewFlipper, false);

        mCurrentWebView = (CustomWebView) view.findViewById(R.id.webview);

        initializeCurrentWebView();

        synchronized (mViewFlipper) {
            if (parentIndex != -1) {
                mWebViews.add(parentIndex + 1, mCurrentWebView);
                mViewFlipper.addView(view, parentIndex + 1);
            } else {
                mWebViews.add(mCurrentWebView);
                mViewFlipper.addView(view);
            }
            mViewFlipper.setDisplayedChild(mViewFlipper.indexOfChild(view));
        }

        updateUI();
        updatePreviousNextTabViewsVisibility();

        mUrlEditText.clearFocus();

        if (navigateToHome) {
            navigateToHome();
        }
    }

    /**
     * Remove the current tab.
     */
    private void removeCurrentTab() {

        if (mFindDialogVisible) {
            closeFindDialog();
        }

        int removeIndex = mViewFlipper.getDisplayedChild();

        mCurrentWebView.doOnPause();

        synchronized (mViewFlipper) {
            mViewFlipper.removeViewAt(removeIndex);
            mViewFlipper.setDisplayedChild(removeIndex - 1);
            mWebViews.remove(removeIndex);
        }

        mCurrentWebView = mWebViews.get(mViewFlipper.getDisplayedChild());

        updateUI();
        updatePreviousNextTabViewsVisibility();

        mUrlEditText.clearFocus();
    }

    private void doFind() {
        CharSequence find = mFindText.getText();
        if (find.length() == 0) {
            mFindPreviousButton.setEnabled(false);
            mFindNextButton.setEnabled(false);
            mCurrentWebView.clearMatches();
        } else {
            int found = mCurrentWebView.findAll(find.toString());
            if (found < 2) {
                mFindPreviousButton.setEnabled(false);
                mFindNextButton.setEnabled(false);
            } else {
                mFindPreviousButton.setEnabled(true);
                mFindNextButton.setEnabled(true);
            }
        }
    }

    private void showFindDialog() {
        setFindBarVisibility(true);
        mCurrentWebView.doSetFindIsUp(true);
        CharSequence text = mFindText.getText();
        if (text.length() > 0) {
            mFindText.setSelection(0, text.length());
            doFind();
        } else {
            mFindPreviousButton.setEnabled(false);
            mFindNextButton.setEnabled(false);
        }

        mFindText.requestFocus();
        showKeyboardForFindDialog();
    }

    private void closeFindDialog() {
        hideKeyboardFromFindDialog();
        mCurrentWebView.doNotifyFindDialogDismissed();
        setFindBarVisibility(false);
    }

    private void setFindBarVisibility(boolean visible) {
        if (visible) {
            mFindBar.startAnimation(AnimationManager.getInstance().getTopBarShowAnimation());
            mFindBar.setVisibility(View.VISIBLE);
            mFindDialogVisible = true;
        } else {
            mFindBar.startAnimation(AnimationManager.getInstance().getTopBarHideAnimation());
            mFindBar.setVisibility(View.GONE);
            mFindDialogVisible = false;
        }
    }

    /**
     * Change the tool bars visibility.
     * @param visible If True, the tool bars will be shown.
     */
    private void setToolbarsVisibility(boolean visible) {
        boolean switchTabByButtons = isSwitchTabsByButtonsEnabled();
        boolean showPreviousTabView = mViewFlipper.getDisplayedChild() > 0;
        boolean showNextTabView = mViewFlipper.getDisplayedChild() < mViewFlipper.getChildCount() - 1;

        if (visible) {

            if (!mUrlBarVisible) {
                // PSIPHON: disable hiding the address bar
                // mTopBar.startAnimation(AnimationManager.getInstance().getTopBarShowAnimation());
                mBottomBar.startAnimation(AnimationManager.getInstance().getBottomBarShowAnimation());

                if (switchTabByButtons) {
                    if (showPreviousTabView) {
                        mPreviousTabView
                                .startAnimation(AnimationManager.getInstance().getPreviousTabViewShowAnimation());
                    }

                    if (showNextTabView) {
                        mNextTabView.startAnimation(AnimationManager.getInstance().getNextTabViewShowAnimation());
                    }
                }

                mTopBar.setVisibility(View.VISIBLE);
                mBottomBar.setVisibility(View.VISIBLE);

                if (switchTabByButtons) {
                    if (showPreviousTabView) {
                        mPreviousTabView.setVisibility(View.VISIBLE);
                    }

                    if (showNextTabView) {
                        mNextTabView.setVisibility(View.VISIBLE);
                    }
                }

                mBubbleRightView.setVisibility(View.GONE);
                mBubbleLeftView.setVisibility(View.GONE);
            }

            startToolbarsHideRunnable();

            mUrlBarVisible = true;

        } else {

            if (mUrlBarVisible) {
                // PSIPHON: disable hiding the address bar
                // mTopBar.startAnimation(AnimationManager.getInstance().getTopBarHideAnimation());
                mBottomBar.startAnimation(AnimationManager.getInstance().getBottomBarHideAnimation());

                if (switchTabByButtons) {
                    if (showPreviousTabView) {
                        mPreviousTabView
                                .startAnimation(AnimationManager.getInstance().getPreviousTabViewHideAnimation());
                    }

                    if (showNextTabView) {
                        mNextTabView.startAnimation(AnimationManager.getInstance().getNextTabViewHideAnimation());
                    }
                }

                // PSIPHON: disable hiding the address bar
                // mTopBar.setVisibility(View.GONE);
                mBottomBar.setVisibility(View.GONE);

                if (switchTabByButtons) {
                    if (showPreviousTabView) {
                        mPreviousTabView.setVisibility(View.GONE);
                    }

                    if (showNextTabView) {
                        mNextTabView.setVisibility(View.GONE);
                    }
                }

                String bubblePosition = Controller.getInstance().getPreferences()
                        .getString(Constants.PREFERENCES_GENERAL_BUBBLE_POSITION, "right");

                if (bubblePosition.equals("right")) {
                    mBubbleRightView.setVisibility(View.VISIBLE);
                    mBubbleLeftView.setVisibility(View.GONE);
                } else if (bubblePosition.equals("left")) {
                    mBubbleRightView.setVisibility(View.GONE);
                    mBubbleLeftView.setVisibility(View.VISIBLE);
                } else if (bubblePosition.equals("both")) {
                    mBubbleRightView.setVisibility(View.VISIBLE);
                    mBubbleLeftView.setVisibility(View.VISIBLE);
                } else {
                    mBubbleRightView.setVisibility(View.VISIBLE);
                    mBubbleLeftView.setVisibility(View.GONE);
                }
            }

            mUrlBarVisible = false;
        }
    }

    private void showKeyboardForFindDialog() {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.showSoftInput(mFindText, InputMethodManager.SHOW_IMPLICIT);
    }

    private void hideKeyboardFromFindDialog() {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(mFindText.getWindowToken(), 0);
    }

    /**
     * Hide the keyboard.
     * @param delayedHideToolbars If True, will start a runnable to delay tool bars hiding. If False, tool bars are hidden immediatly.
     */
    private void hideKeyboard(boolean delayedHideToolbars) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(mUrlEditText.getWindowToken(), 0);

        if (mUrlBarVisible) {
            if (delayedHideToolbars) {
                startToolbarsHideRunnable();
            } else {
                setToolbarsVisibility(false);
            }
        }
    }

    /**
     * Thread to delay the show of the find dialog. This seems to be necessary when shown from
     * a QuickAction. If not, the keyboard does not show. 50ms seems to be enough on
     * a Nexus One and on the (rather) slow emulator. Dirty hack :(
     */
    private void startShowFindDialogRunnable() {
        new Thread(new Runnable() {

            private Handler mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    showFindDialog();
                }
            };

            @Override
            public void run() {
                try {
                    Thread.sleep(50);
                    mHandler.sendEmptyMessage(0);
                } catch (InterruptedException e) {
                    mHandler.sendEmptyMessage(0);
                }

            }
        }).start();
    }

    /**
     * Start a runnable to hide the tool bars after a user-defined delay.
     */
    private void startToolbarsHideRunnable() {

        if (mHideToolbarsRunnable != null) {
            mHideToolbarsRunnable.setDisabled();
        }

        int delay = Integer.parseInt(Controller.getInstance().getPreferences()
                .getString(Constants.PREFERENCES_GENERAL_BARS_DURATION, "3000"));
        if (delay <= 0) {
            delay = 3000;
        }

        mHideToolbarsRunnable = new HideToolbarsRunnable(this, delay);
        new Thread(mHideToolbarsRunnable).start();
    }

    /**
    * Hide the tool bars.
    */
    public void hideToolbars() {
        if (mUrlBarVisible) {
            if ((!mUrlEditText.hasFocus()) && (!mToolsActionGridVisible)) {

                if (!mCurrentWebView.isLoading()) {
                    setToolbarsVisibility(false);
                }
            }
        }
        mHideToolbarsRunnable = null;
    }

    /**
     * Start a runnable to update history.
     * @param title The page title.
     * @param url The page url.
     */
    private void startHistoryUpdaterRunnable(String title, String url, String originalUrl) {
        if ((url != null) && (url.length() > 0)) {
            new Thread(new HistoryUpdater(this, title, url, originalUrl)).start();
        }
    }

    /**
     * Navigate to the given url.
     * @param url The url.
     */
    private void navigateToUrl(String url) {
        // Needed to hide toolbars properly.
        mUrlEditText.clearFocus();

        if ((url != null) && (url.length() > 0)) {

            if (UrlUtils.isUrl(url)) {
                url = UrlUtils.checkUrl(url);
            } else {
                url = UrlUtils.getSearchUrl(this, url);
            }

            hideKeyboard(true);

            if (url.equals(Constants.URL_ABOUT_START)) {

                mCurrentWebView.loadDataWithBaseURL("file:///android_asset/startpage/",
                        ApplicationUtils.getStartPage(this), "text/html", "UTF-8", Constants.URL_ABOUT_START);

            } else {

                // If the url is not from GWT mobile view, and is in the mobile view url list, then load it with GWT.
                if ((!url.startsWith(Constants.URL_GOOGLE_MOBILE_VIEW_NO_FORMAT))
                        && (UrlUtils.checkInMobileViewUrlList(this, url))) {

                    url = String.format(Constants.URL_GOOGLE_MOBILE_VIEW, url);
                }
                mCurrentWebView.loadUrl(url);
            }
        }
    }

    /**
     * Navigate to the url given in the url edit text.
     */
    private void navigateToUrl() {
        navigateToUrl(mUrlEditText.getText().toString());
    }

    /**
     * Navigate to the user home page.
     */
    private void navigateToHome() {
        navigateToUrl(Controller.getInstance().getPreferences().getString(Constants.PREFERENCES_GENERAL_HOME_PAGE,
                Constants.URL_ABOUT_START));
    }

    /**
     * Navigate to the previous page in history.
     */
    private void navigatePrevious() {
        // Needed to hide toolbars properly.
        mUrlEditText.clearFocus();

        hideKeyboard(true);
        mCurrentWebView.goBack();
    }

    /**
     * Navigate to the next page in history. 
     */
    private void navigateNext() {
        // Needed to hide toolbars properly.
        mUrlEditText.clearFocus();

        hideKeyboard(true);
        mCurrentWebView.goForward();
    }

    @Override
    public boolean onKeyLongPress(int keyCode, KeyEvent event) {

        switch (keyCode) {
        case KeyEvent.KEYCODE_BACK:
            this.moveTaskToBack(true);
            return true;
        default:
            return super.onKeyLongPress(keyCode, event);
        }
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {

        switch (keyCode) {
        case KeyEvent.KEYCODE_BACK:
            if (mCustomView != null) {
                hideCustomView();
            } else if (mFindDialogVisible) {
                closeFindDialog();
            } else {
                if (mCurrentWebView.canGoBack()) {
                    mCurrentWebView.goBack();
                } else {
                    this.moveTaskToBack(true);
                    return true;
                }
            }
            return true;
        case KeyEvent.KEYCODE_SEARCH:
            if (!mFindDialogVisible) {
                showFindDialog();
            }
            return true;
        case KeyEvent.KEYCODE_VOLUME_DOWN:
        case KeyEvent.KEYCODE_VOLUME_UP:
            String volumeKeysBehaviour = PreferenceManager.getDefaultSharedPreferences(this)
                    .getString(Constants.PREFERENCES_UI_VOLUME_KEYS_BEHAVIOUR, "DEFAULT");

            if (volumeKeysBehaviour.equals("DEFAULT")) {
                return super.onKeyUp(keyCode, event);
            } else {
                return true;
            }
        default:
            return super.onKeyUp(keyCode, event);
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        String volumeKeysBehaviour = PreferenceManager.getDefaultSharedPreferences(this)
                .getString(Constants.PREFERENCES_UI_VOLUME_KEYS_BEHAVIOUR, "DEFAULT");

        if (!volumeKeysBehaviour.equals("DEFAULT")) {
            switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_DOWN:

                if (volumeKeysBehaviour.equals("SWITCH_TABS")) {
                    showPreviousTab(false);
                } else if (volumeKeysBehaviour.equals("SCROLL")) {
                    mCurrentWebView.pageDown(false);
                } else if (volumeKeysBehaviour.equals("HISTORY")) {
                    mCurrentWebView.goForward();
                } else {
                    mCurrentWebView.zoomIn();
                }

                return true;
            case KeyEvent.KEYCODE_VOLUME_UP:

                if (volumeKeysBehaviour.equals("SWITCH_TABS")) {
                    showNextTab(false);
                } else if (volumeKeysBehaviour.equals("SCROLL")) {
                    mCurrentWebView.pageUp(false);
                } else if (volumeKeysBehaviour.equals("HISTORY")) {
                    mCurrentWebView.goBack();
                } else {
                    mCurrentWebView.zoomOut();
                }

                return true;
            default:
                return super.onKeyDown(keyCode, event);
            }
        } else {
            return super.onKeyDown(keyCode, event);
        }
    }

    /**
     * Set the application title to default.
     */
    private void clearTitle() {
        this.setTitle(getResources().getString(R.string.ApplicationName));
    }

    /**
     * Update the application title.
     */
    private void updateTitle() {
        String value = mCurrentWebView.getTitle();

        if ((value != null) && (value.length() > 0)) {
            this.setTitle(String.format(getResources().getString(R.string.ApplicationNameUrl), value));
        } else {
            clearTitle();
        }
    }

    /**
     * Get a Drawable of the current favicon, with its size normalized relative to current screen density.
     * @return The normalized favicon.
     */
    private BitmapDrawable getNormalizedFavicon() {

        BitmapDrawable favIcon = new BitmapDrawable(getResources(), mCurrentWebView.getFavicon());

        if (mCurrentWebView.getFavicon() != null) {
            int imageButtonSize = ApplicationUtils.getImageButtonSize(this);
            int favIconSize = ApplicationUtils.getFaviconSize(this);

            Bitmap bm = Bitmap.createBitmap(imageButtonSize, imageButtonSize, Bitmap.Config.ARGB_4444);
            Canvas canvas = new Canvas(bm);

            favIcon.setBounds((imageButtonSize / 2) - (favIconSize / 2), (imageButtonSize / 2) - (favIconSize / 2),
                    (imageButtonSize / 2) + (favIconSize / 2), (imageButtonSize / 2) + (favIconSize / 2));
            favIcon.draw(canvas);

            favIcon = new BitmapDrawable(getResources(), bm);
        }

        return favIcon;
    }

    /**
     * Update the "Go" button image.
     */
    private void updateGoButton() {
        if (mCurrentWebView.isLoading()) {
            mGoButton.setImageResource(R.drawable.ic_btn_stop);
            mUrlEditText.setCompoundDrawablesWithIntrinsicBounds(null, null, mCircularProgress, null);
            ((AnimationDrawable) mCircularProgress).start();
        } else {
            if (!mCurrentWebView.isSameUrl(mUrlEditText.getText().toString())) {
                mGoButton.setImageResource(R.drawable.ic_btn_go);
            } else {
                mGoButton.setImageResource(R.drawable.ic_btn_reload);
            }

            mUrlEditText.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
            ((AnimationDrawable) mCircularProgress).stop();
        }
    }

    /**
     * Update the fav icon display.
     */
    private void updateFavIcon() {
        BitmapDrawable favicon = getNormalizedFavicon();

        if (mCurrentWebView.getFavicon() != null) {
            mToolsButton.setImageDrawable(favicon);
        } else {
            mToolsButton.setImageResource(R.drawable.fav_icn_default_toolbar);
        }
    }

    /**
     * Update the UI: Url edit text, previous/next button state,...
     */
    private void updateUI() {
        mUrlEditText.removeTextChangedListener(mUrlTextWatcher);
        mUrlEditText.setText(mCurrentWebView.getUrl());
        mUrlEditText.addTextChangedListener(mUrlTextWatcher);

        mPreviousButton.setEnabled(mCurrentWebView.canGoBack());
        mNextButton.setEnabled(mCurrentWebView.canGoForward());

        if (mCurrentWebView.getUrl() != null)
            mRemoveTabButton.setEnabled((mViewFlipper.getChildCount() > 1
                    || !mCurrentWebView.getUrl().equals(Constants.URL_ABOUT_START)));
        else
            mRemoveTabButton.setEnabled(mViewFlipper.getChildCount() > 1);

        mProgressBar.setProgress(mCurrentWebView.getProgress());

        updateGoButton();

        updateTitle();

        updateFavIcon();
    }

    private boolean isSwitchTabsByFlingEnabled() {
        return (mSwitchTabsMethod == SwitchTabsMethod.FLING) || (mSwitchTabsMethod == SwitchTabsMethod.BOTH);
    }

    private boolean isSwitchTabsByButtonsEnabled() {
        return (mSwitchTabsMethod == SwitchTabsMethod.BUTTONS) || (mSwitchTabsMethod == SwitchTabsMethod.BOTH);
    }

    /**
     * Open the "Add bookmark" dialog.
     */
    private void openAddBookmarkDialog() {
        Intent i = new Intent(this, EditBookmarkActivity.class);

        i.putExtra(Constants.EXTRA_ID_BOOKMARK_ID, (long) -1);
        i.putExtra(Constants.EXTRA_ID_BOOKMARK_TITLE, mCurrentWebView.getTitle());
        i.putExtra(Constants.EXTRA_ID_BOOKMARK_URL, mCurrentWebView.getUrl());

        startActivity(i);
    }

    /**
     * Open the bookmark list.
     */
    private void openBookmarksHistoryActivity() {
        Intent i = new Intent(this, BookmarksHistoryActivity.class);
        startActivityForResult(i, OPEN_BOOKMARKS_HISTORY_ACTIVITY);
    }

    /**
     * Open the download list.
     */
    private void openDownloadsList() {
        Intent i = new Intent(this, DownloadsListActivity.class);
        startActivityForResult(i, OPEN_DOWNLOADS_ACTIVITY);
    }

    /**
     * Perform the user-defined action when clicking on the quick button.
     */
    private void onQuickButton() {
        openBookmarksHistoryActivity();
    }

    /**
     * Open preferences.
     */
    private void openPreferences() {
        Intent preferencesActivity = new Intent(this, PreferencesActivity.class);
        startActivity(preferencesActivity);
    }

    // PSIPHON
    // feedback menu item
    private void launchFeedback() {
        Class psiphonFeedbackActivityClass;
        try {
            psiphonFeedbackActivityClass = Class.forName(this.mPsiphonFeedbackActivityClassName);
        } catch (ClassNotFoundException e) {
            // This shouldn't happen. 
            return;
        }

        Intent intent = new Intent("ACTION_VIEW", null, this, psiphonFeedbackActivityClass);
        this.startActivity(intent);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);

        MenuItem item;

        // PSIPHON
        // feedback menu item
        item = menu.add(0, MENU_FEEDBACK, 0, R.string.Main_MenuFeedback);
        item.setIcon(R.drawable.ic_menu_feedback);

        item = menu.add(0, MENU_ADD_BOOKMARK, 0, R.string.Main_MenuAddBookmark);
        item.setIcon(R.drawable.ic_menu_add_bookmark);

        item = menu.add(0, MENU_SHOW_BOOKMARKS, 0, R.string.Main_MenuShowBookmarks);
        item.setIcon(R.drawable.ic_menu_bookmarks);

        item = menu.add(0, MENU_SHOW_DOWNLOADS, 0, R.string.Main_MenuShowDownloads);
        item.setIcon(R.drawable.ic_menu_downloads);

        item = menu.add(0, MENU_PREFERENCES, 0, R.string.Main_MenuPreferences);
        item.setIcon(R.drawable.ic_menu_preferences);

        item = menu.add(0, MENU_EXIT, 0, R.string.Main_MenuExit);
        item.setIcon(R.drawable.ic_menu_exit);

        return true;
    }

    @Override
    public boolean onMenuItemSelected(int featureId, MenuItem item) {
        switch (item.getItemId()) {
        // PSIPHON
        // feedback menu item
        case MENU_FEEDBACK:
            launchFeedback();
            return true;
        case MENU_ADD_BOOKMARK:
            openAddBookmarkDialog();
            return true;
        case MENU_SHOW_BOOKMARKS:
            openBookmarksHistoryActivity();
            return true;
        case MENU_SHOW_DOWNLOADS:
            openDownloadsList();
            return true;
        case MENU_PREFERENCES:
            openPreferences();
            return true;
        case MENU_EXIT:
            this.finish();
            return true;
        default:
            return super.onMenuItemSelected(featureId, item);
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);

        if (requestCode == OPEN_BOOKMARKS_HISTORY_ACTIVITY) {
            if (intent != null) {
                Bundle b = intent.getExtras();
                if (b != null) {
                    if (b.getBoolean(Constants.EXTRA_ID_NEW_TAB)) {
                        addTab(false);
                    }
                    navigateToUrl(b.getString(Constants.EXTRA_ID_URL));
                }
            }
        } else if (requestCode == OPEN_FILE_CHOOSER_ACTIVITY) {
            if (mUploadMessage == null) {
                return;
            }

            Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;
        }
    }

    @Override
    protected void onPause() {
        //
        // Psiphon: Store the URLs for the open tabs
        //
        if (Controller.getInstance().getPreferences().getBoolean(Constants.PREFERENCES_BROWSER_RESTORE_LAST_PAGE,
                true)) {
            JSONArray jsonURLs = new JSONArray();
            for (CustomWebView webView : mWebViews) {
                String url = webView.getUrl();
                if (url == null || url.length() == 0)
                    url = webView.getLoadedUrl();
                jsonURLs.put(url);
            }

            SharedPreferences prefs = Controller.getInstance().getPreferences();
            SharedPreferences.Editor prefsEditor = prefs.edit();
            prefsEditor.putString("urlsToRestore", jsonURLs.toString());
            prefsEditor.commit();
        }
        // end Psiphon changes

        mCurrentWebView.doOnPause();
        super.onPause();
    }

    @Override
    protected void onResume() {
        mCurrentWebView.doOnResume();
        super.onResume();

        if (!ensurePsiphonRunning()) {
            // Must return -- control switching to Psiphon task.
            return;
        }
    }

    /**
     * Show a toast alert on tab switch.
     */
    private void showToastOnTabSwitch() {
        if (Controller.getInstance().getPreferences().getBoolean(Constants.PREFERENCES_SHOW_TOAST_ON_TAB_SWITCH,
                true)) {
            String text;
            if (mCurrentWebView.getTitle() != null) {
                text = String.format(getString(R.string.Main_ToastTabSwitchFullMessage),
                        mViewFlipper.getDisplayedChild() + 1, mCurrentWebView.getTitle());
            } else {
                text = String.format(getString(R.string.Main_ToastTabSwitchMessage),
                        mViewFlipper.getDisplayedChild() + 1);
            }
            Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
        }
    }

    private void updatePreviousNextTabViewsVisibility() {
        if ((mUrlBarVisible) && (isSwitchTabsByButtonsEnabled())) {
            if (mViewFlipper.getDisplayedChild() > 0) {
                mPreviousTabView.setVisibility(View.VISIBLE);
            } else {
                mPreviousTabView.setVisibility(View.GONE);
            }

            if (mViewFlipper.getDisplayedChild() < mViewFlipper.getChildCount() - 1) {
                mNextTabView.setVisibility(View.VISIBLE);
            } else {
                mNextTabView.setVisibility(View.GONE);
            }
        } else {
            mPreviousTabView.setVisibility(View.GONE);
            mNextTabView.setVisibility(View.GONE);
        }
    }

    /**
     * Show the previous tab, if any.
     */
    private void showPreviousTab(boolean resetToolbarsRunnable) {

        if (mViewFlipper.getChildCount() > 1) {

            if (mFindDialogVisible) {
                closeFindDialog();
            }

            mCurrentWebView.doOnPause();

            mViewFlipper.setInAnimation(AnimationManager.getInstance().getInFromLeftAnimation());
            mViewFlipper.setOutAnimation(AnimationManager.getInstance().getOutToRightAnimation());

            mViewFlipper.showPrevious();

            mCurrentWebView = mWebViews.get(mViewFlipper.getDisplayedChild());

            mCurrentWebView.doOnResume();

            if (resetToolbarsRunnable) {
                startToolbarsHideRunnable();
            }

            showToastOnTabSwitch();

            updatePreviousNextTabViewsVisibility();

            updateUI();
        }
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void setFullscreen(boolean enabled) {
        Window win = MainActivity.this.getWindow();
        WindowManager.LayoutParams winParams = win.getAttributes();
        final int bits = WindowManager.LayoutParams.FLAG_FULLSCREEN;
        if (enabled) {
            winParams.flags |= bits;
            if (mCustomView != null) {
                mCustomView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
            } else {
                //mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
            }
        } else {
            winParams.flags &= ~bits;
            if (mCustomView != null) {
                mCustomView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            } else {
                //mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            }
        }
        win.setAttributes(winParams);
    }

    private void showCustomView(View view, int requestedOrientation, WebChromeClient.CustomViewCallback callback) {
        // if a view already exists then immediately terminate the new one
        if (mCustomView != null) {
            callback.onCustomViewHidden();
            return;
        }

        if (requestedOrientation == -1) {
            requestedOrientation = MainActivity.this.getRequestedOrientation();
        }

        mOriginalOrientation = MainActivity.this.getRequestedOrientation();
        FrameLayout decor = (FrameLayout) MainActivity.this.getWindow().getDecorView();
        mFullscreenContainer = new FullscreenHolder(MainActivity.this);
        mFullscreenContainer.addView(view, COVER_SCREEN_PARAMS);
        decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS);
        mCustomView = view;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            setFullscreen(true);
        }
        mCustomViewCallback = callback;
        MainActivity.this.setRequestedOrientation(requestedOrientation);
    }

    private void hideCustomView() {
        if (mCustomView == null)
            return;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            setFullscreen(false);
        }
        FrameLayout decor = (FrameLayout) MainActivity.this.getWindow().getDecorView();
        decor.removeView(mFullscreenContainer);
        mFullscreenContainer = null;
        mCustomView = null;
        mCustomViewCallback.onCustomViewHidden();
        // Show the content view.
        MainActivity.this.setRequestedOrientation(mOriginalOrientation);
    }

    /**
     * Show the next tab, if any.
     */
    private void showNextTab(boolean resetToolbarsRunnable) {

        if (mViewFlipper.getChildCount() > 1) {

            if (mFindDialogVisible) {
                closeFindDialog();
            }

            mCurrentWebView.doOnPause();

            mViewFlipper.setInAnimation(AnimationManager.getInstance().getInFromRightAnimation());
            mViewFlipper.setOutAnimation(AnimationManager.getInstance().getOutToLeftAnimation());

            mViewFlipper.showNext();

            mCurrentWebView = mWebViews.get(mViewFlipper.getDisplayedChild());

            mCurrentWebView.doOnResume();

            if (resetToolbarsRunnable) {
                startToolbarsHideRunnable();
            }

            showToastOnTabSwitch();

            updatePreviousNextTabViewsVisibility();

            updateUI();
        }
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        hideKeyboard(false);

        return mGestureDetector.onTouchEvent(event);
    }

    /**
     * Check if the url is in the AdBlock white list.
     * @param url The url to check
     * @return true if the url is in the white list
     */
    private boolean checkInAdBlockWhiteList(String url) {

        if (url != null) {
            boolean inList = false;
            Iterator<String> iter = Controller.getInstance().getAdBlockWhiteList(this).iterator();
            while ((iter.hasNext()) && (!inList)) {
                if (url.contains(iter.next())) {
                    inList = true;
                }
            }
            return inList;
        } else {
            return false;
        }
    }

    public void onPageFinished(String url) {
        updateUI();

        /* PSIPHON: Disable AdBlocker
        if ((Controller.getInstance().getPreferences().getBoolean(Constants.PREFERENCES_ADBLOCKER_ENABLE, false)) &&
        (!checkInAdBlockWhiteList(mCurrentWebView.getUrl()))) {
           mCurrentWebView.loadAdSweep();
        }
        */

        WebIconDatabase.getInstance().retainIconForPageUrl(mCurrentWebView.getUrl());

        if (mUrlBarVisible) {
            startToolbarsHideRunnable();
        }
    }

    public void onPageStarted(String url) {
        if (mFindDialogVisible) {
            closeFindDialog();
        }

        mUrlEditText.removeTextChangedListener(mUrlTextWatcher);
        mUrlEditText.setText(url);
        mUrlEditText.addTextChangedListener(mUrlTextWatcher);

        mPreviousButton.setEnabled(false);
        mNextButton.setEnabled(false);

        updateGoButton();

        setToolbarsVisibility(true);
    }

    public void onUrlLoading(String url) {
        setToolbarsVisibility(true);
    }

    public void onMailTo(String url) {
        Intent sendMail = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        startActivity(sendMail);
    }

    public void onExternalApplicationUrl(String url) {
        try {

            Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            startActivity(i);

        } catch (Exception e) {

            // Notify user that the vnd url cannot be viewed.
            new AlertDialog.Builder(this).setTitle(R.string.Main_VndErrorTitle)
                    .setMessage(String.format(getString(R.string.Main_VndErrorMessage), url))
                    .setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                        }
                    }).setCancelable(true).create().show();
        }
    }

    public void setHttpAuthUsernamePassword(String host, String realm, String username, String password) {
        mCurrentWebView.setHttpAuthUsernamePassword(host, realm, username, password);
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {

        if ((item != null) && (item.getIntent() != null)) {
            Bundle b = item.getIntent().getExtras();

            switch (item.getItemId()) {
            case CONTEXT_MENU_OPEN:
                if (b != null) {
                    navigateToUrl(b.getString(Constants.EXTRA_ID_URL));
                }
                return true;

            case CONTEXT_MENU_OPEN_IN_NEW_TAB:
                if (b != null) {
                    addTab(false, mViewFlipper.getDisplayedChild());
                    navigateToUrl(b.getString(Constants.EXTRA_ID_URL));
                }
                return true;

            case CONTEXT_MENU_DOWNLOAD:
                if (b != null) {
                    doDownloadStart(b.getString(Constants.EXTRA_ID_URL), null, null, null, 0);
                }
                return true;
            case CONTEXT_MENU_COPY:
                if (b != null) {
                    ApplicationUtils.copyTextToClipboard(this, b.getString(Constants.EXTRA_ID_URL),
                            getString(R.string.Commons_UrlCopyToastMessage));
                }
                return true;
            case CONTEXT_MENU_SHARE:
                if (b != null) {
                    ApplicationUtils.sharePage(this, "", b.getString(Constants.EXTRA_ID_URL));
                }
                return true;
            default:
                return super.onContextItemSelected(item);
            }
        }

        return super.onContextItemSelected(item);
    }

    @Override
    public void onDownloadEvent(String event, Object data) {
        if (event.equals(EventConstants.EVT_DOWNLOAD_ON_FINISHED)) {

            DownloadItem item = (DownloadItem) data;

            if (item.getErrorMessage() == null) {
                Toast.makeText(this, getString(R.string.Main_DownloadFinishedMsg), Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, getString(R.string.Main_DownloadErrorMsg, item.getErrorMessage()),
                        Toast.LENGTH_SHORT).show();
            }
        }
    }

    // PSIPHON
    /**
     * Discover other URLs that are equivalent to the given URL, where a URL is
     * "equivalent" if it is in the given URL's redirection chain. 
     * @param url  The URL that we want to find equivalents for.
     * @return An array of equivalent URLs, which will contain the original URL.
     *         (E.g., it's guaranteed to be at least size 1.)
     */
    @SuppressWarnings("unchecked")
    private List<String> findEquivalentUrls(final String userAgentString, final String url) {
        List<String> uriStrings = new ArrayList<String>();
        uriStrings.add(url);

        // Hack around android.os.NetworkOnMainThreadException restriction.
        // Still blocks the main thread!

        class RedirectRequest implements Runnable {
            Set<URI> uris = null;

            public Set<URI> getUris() {
                return this.uris;
            }

            public void run() {
                try {
                    HttpParams params = new BasicHttpParams();
                    HttpConnectionParams.setConnectionTimeout(params, 5000);
                    HttpConnectionParams.setSoTimeout(params, 5000);
                    final AppPreferences multiProcessPreferences = new AppPreferences(MainActivity.this);
                    HttpHost httpproxy = new HttpHost("localhost", multiProcessPreferences
                            .getInt(MainActivity.this.getString(R.string.current_local_http_proxy_port), 0));
                    params.setParameter(ConnRoutePNames.DEFAULT_PROXY, httpproxy);
                    DefaultHttpClient client = new DefaultHttpClient(params);
                    HttpContext context = new BasicHttpContext();
                    HttpRequestBase request = new HttpHead(url);
                    request.setHeader("User-Agent", userAgentString);

                    client.execute(request, context);

                    // Clean up the request.
                    request.abort();

                    // The context holds info about the redirections that occurred.
                    RedirectLocations redirectLocations = (RedirectLocations) context
                            .getAttribute("http.protocol.redirect-locations");

                    if (redirectLocations != null) {
                        // In Android's org.apache.http version, there's no way to 
                        // access `redirectLocations.uris`, so we'll have to use reflection
                        // to get at the private variable.
                        Field field;
                        field = redirectLocations.getClass().getDeclaredField("uris");
                        field.setAccessible(true);
                        this.uris = (Set<URI>) field.get(redirectLocations);
                    }
                } catch (Exception e) {
                    // pass
                }
            }
        }
        RedirectRequest redirectRequest = new RedirectRequest();
        Thread thread = new Thread(redirectRequest);
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            // pass
        }

        Set<URI> uris = redirectRequest.getUris();

        // Add in the redirected URIs we've found.
        if (uris != null && uris.size() > 0) {
            for (URI uri : uris) {
                uriStrings.add(uri.toString());
            }
        }

        // Hack:
        // If a uriString doesn't end with a '/', then we'll also add an
        // equivalent that has one. Our redirection testing doesn't seem
        // to catch cases of http://example.com vs. http://example.com/
        for (int i = uriStrings.size() - 1; i >= 0; i--) {
            String uriString = uriStrings.get(i);

            if (!uriString.endsWith("/")) {
                uriStrings.add(uriString + "/");
            }
        }

        return uriStrings;
    }

    /**
     * Gesture listener implementation.
     */
    private class GestureListener extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            mCurrentWebView.zoomIn();
            return super.onDoubleTap(e);
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            if (isSwitchTabsByFlingEnabled()) {
                if (e2.getEventTime() - e1.getEventTime() <= FLIP_TIME_THRESHOLD) {
                    if (e2.getX() > (e1.getX() + FLIP_PIXEL_THRESHOLD)) {

                        showPreviousTab(false);
                        return false;
                    }

                    // going forwards: pushing stuff to the left
                    if (e2.getX() < (e1.getX() - FLIP_PIXEL_THRESHOLD)) {

                        showNextTab(false);
                        return false;
                    }
                }
            }

            return super.onFling(e1, e2, velocityX, velocityY);
        }

    }

    static class FullscreenHolder extends FrameLayout {

        public FullscreenHolder(Context ctx) {
            super(ctx);
            setBackgroundColor(ctx.getResources().getColor(android.R.color.black));
        }

        @Override
        public boolean onTouchEvent(MotionEvent evt) {
            return true;
        }

    }

}