import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.DialogInterface.OnCancelListener;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.AssetManager;
import android.graphics.drawable.PaintDrawable;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
+import android.location.Location;
+import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.net.WebAddress;
import android.provider.Contacts;
import android.provider.Downloads;
import android.provider.MediaStore;
+import android.provider.Settings;
import android.provider.Contacts.Intents.Insert;
import android.text.IClipboard;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.util.Regex;
-import android.util.Config;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Gravity;
import android.webkit.CookieSyncManager;
import android.webkit.DownloadListener;
import android.webkit.HttpAuthHandler;
+import android.webkit.PluginManager;
import android.webkit.SslErrorHandler;
import android.webkit.URLUtil;
import android.webkit.WebChromeClient;
import android.webkit.WebHistoryItem;
import android.webkit.WebIconDatabase;
+import android.webkit.WebStorage;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
View.OnCreateContextMenuListener,
DownloadListener {
+ /* Define some aliases to make these debugging flags easier to refer to.
+ * This file imports android.provider.Browser, so we can't just refer to "Browser.DEBUG".
+ */
+ private final static boolean DEBUG = com.android.browser.Browser.DEBUG;
+ private final static boolean LOGV_ENABLED = com.android.browser.Browser.LOGV_ENABLED;
+ private final static boolean LOGD_ENABLED = com.android.browser.Browser.LOGD_ENABLED;
+
private IGoogleLoginService mGls = null;
private ServiceConnection mGlsConnection = null;
private SensorManager mSensorManager = null;
+ private WebStorage.QuotaUpdater mWebStorageQuotaUpdater = null;
+
+ // These are single-character shortcuts for searching popular sources.
+ private static final int SHORTCUT_INVALID = 0;
+ private static final int SHORTCUT_GOOGLE_SEARCH = 1;
+ private static final int SHORTCUT_WIKIPEDIA_SEARCH = 2;
+ private static final int SHORTCUT_DICTIONARY_SEARCH = 3;
+ private static final int SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH = 4;
+
/* Whitelisted webpages
private static HashSet<String> sWhiteList;
try {
if (mGls == null) return;
+ if (!homepage.startsWith("http://www.google.")) return;
+ if (homepage.indexOf('?') == -1) return;
+
String hostedUser = mGls.getAccount(GoogleLoginServiceConstants.PREFER_HOSTED);
String googleUser = mGls.getAccount(GoogleLoginServiceConstants.REQUIRE_GOOGLE);
if (googleUser == null || !hostedUser.equals(googleUser)) {
String domain = hostedUser.substring(hostedUser.lastIndexOf('@')+1);
- homepage = "http://www.google.com/m/a/" + domain + "?client=ms-" +
- Partner.getString(BrowserActivity.this.getContentResolver(), Partner.CLIENT_ID);
+ homepage = homepage.replace("?", "/a/" + domain + "?");
}
} catch (RemoteException ignore) {
// Login service died; carry on
s.loadFromDb(mContext);
pluginsPath = s.getPluginsPath();
}
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(TAG, "Plugin path: " + pluginsPath);
}
}
* We delete the directory, then recreate it.
*/
public void cleanPluginsDirectory() {
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(TAG, "delete plugins directory: " + pluginsPath);
}
File pluginsDirectory = new File(pluginsPath);
*/
public void copyBuildInfos() {
try {
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(TAG, "Copy build infos to the plugins directory");
}
File buildInfoFile = new File(SYSTEM_BUILD_INFOS_FILE);
File buildInfoFile = new File(SYSTEM_BUILD_INFOS_FILE);
File buildInfoPlugins = new File(pluginsPath, BUILD_INFOS_FILE);
if (!buildInfoPlugins.exists()) {
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(TAG, "build.prop in plugins directory " + pluginsPath
+ " does not exist, therefore it's a new system image");
}
String buildInfoPlugin = contentsOfFile(buildInfoPlugins);
if (buildInfo == null || buildInfoPlugin == null
|| buildInfo.compareTo(buildInfoPlugin) != 0) {
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(TAG, "build.prop are different, "
+ " therefore it's a new system image");
}
String path = entry.getName().substring(zipFilterLength);
File outputFile = new File(pluginsPath, path);
if (!outputFile.exists()) {
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(TAG, "checkIsDifferentVersions(): extracted file "
+ path + " does not exist, we have a different version");
}
outputFile.getParentFile().mkdirs();
if (outputFile.exists() && !mDoOverwrite) {
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(TAG, path + " already extracted.");
}
} else {
path + TEMPORARY_EXTENSION);
}
FileOutputStream fos = new FileOutputStream(outputFile);
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(TAG, "copy " + entry + " to "
+ pluginsPath + "/" + path);
}
File renamedFile = (File) elems.nextElement();
File sourceFile = new File(renamedFile.getPath()
+ TEMPORARY_EXTENSION);
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(TAG, "rename " + sourceFile.getPath()
+ " to " + renamedFile.getPath());
}
}
copyBuildInfos();
-
- // Refresh the plugin list.
- if (mTabControl.getCurrentWebView() != null) {
- mTabControl.getCurrentWebView().refreshPlugins(false);
- }
} catch (IOException e) {
Log.e(TAG, "IO Exception: " + e);
}
}
}
+ // Flag to enable the touchable browser bar with buttons
+ private final boolean CUSTOM_BROWSER_BAR = true;
+
@Override public void onCreate(Bundle icicle) {
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(LOGTAG, this + " onStart");
}
super.onCreate(icicle);
- this.requestWindowFeature(Window.FEATURE_LEFT_ICON);
- this.requestWindowFeature(Window.FEATURE_RIGHT_ICON);
- this.requestWindowFeature(Window.FEATURE_PROGRESS);
- this.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-
+ if (CUSTOM_BROWSER_BAR) {
+ this.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ } else {
+ this.requestWindowFeature(Window.FEATURE_LEFT_ICON);
+ this.requestWindowFeature(Window.FEATURE_RIGHT_ICON);
+ this.requestWindowFeature(Window.FEATURE_PROGRESS);
+ this.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ }
// test the browser in OpenGL
// requestWindowFeature(Window.FEATURE_OPENGL);
mGenericFavicon = getResources().getDrawable(
R.drawable.app_web_browser_sm);
- mContentView = (FrameLayout) getWindow().getDecorView().findViewById(
- com.android.internal.R.id.content);
+ FrameLayout frameLayout = (FrameLayout) getWindow().getDecorView()
+ .findViewById(com.android.internal.R.id.content);
+ if (CUSTOM_BROWSER_BAR) {
+ // This LinearLayout will hold the title bar and a FrameLayout, which
+ // holds everything else.
+ LinearLayout linearLayout = (LinearLayout) LayoutInflater.from(this)
+ .inflate(R.layout.custom_screen, null);
+ mTitleBar = (TitleBar) linearLayout.findViewById(R.id.title_bar);
+ mTitleBar.setBrowserActivity(this);
+ mContentView = (FrameLayout) linearLayout.findViewById(
+ R.id.main_content);
+ frameLayout.addView(linearLayout, COVER_SCREEN_PARAMS);
+ } else {
+ mContentView = frameLayout;
+ }
// Create the tab control and our initial tab
mTabControl = new TabControl(this);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser");
+ // If this was a web search request, pass it on to the default web search provider.
+ if (handleWebSearchIntent(getIntent())) {
+ moveTaskToBack(true);
+ return;
+ }
+
if (!mTabControl.restoreState(icicle)) {
// clear up the thumbnail directory if we can't restore the state as
// none of the files in the directory are referenced any more.
// If the intent is ACTION_VIEW and data is not null, the Browser is
// invoked to view the content by another application. In this case,
// the tab will be close when exit.
- String url = getUrlFromIntent(intent);
+ UrlData urlData = getUrlDataFromIntent(intent);
+
final TabControl.Tab t = mTabControl.createNewTab(
Intent.ACTION_VIEW.equals(intent.getAction()) &&
intent.getData() != null,
- intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), url);
+ intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), urlData.mUrl);
mTabControl.setCurrentTab(t);
// This is one of the only places we call attachTabToContentView
// without animating from the tab picker.
}
copyPlugins(true);
- if (url == null || url.length() == 0) {
+ if (urlData.isEmpty()) {
if (mSettings.isLoginInitialized()) {
webView.loadUrl(mSettings.getHomePage());
} else {
waitForCredentials();
}
} else {
- webView.loadUrl(url);
+ byte[] postData = getLocationData(intent);
+ if (postData != null) {
+ webView.postUrl(urlData.mUrl, postData);
+ } else {
+ urlData.loadIn(webView);
+ }
}
} else {
// TabControl.restoreState() will create a new tab even if
}
}
};
+
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mPackageInstallationReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final String packageName = intent.getData()
+ .getSchemeSpecificPart();
+ final boolean replacing = intent.getBooleanExtra(
+ Intent.EXTRA_REPLACING, false);
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
+ // if it is replacing, refreshPlugins() when adding
+ return;
+ }
+ PackageManager pm = BrowserActivity.this.getPackageManager();
+ PackageInfo pkgInfo = null;
+ try {
+ pkgInfo = pm.getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ return;
+ }
+ if (pkgInfo != null) {
+ String permissions[] = pkgInfo.requestedPermissions;
+ if (permissions == null) {
+ return;
+ }
+ boolean permissionOk = false;
+ for (String permit : permissions) {
+ if (PluginManager.PLUGIN_PERMISSION.equals(permit)) {
+ permissionOk = true;
+ break;
+ }
+ }
+ if (permissionOk) {
+ PluginManager.getInstance(BrowserActivity.this)
+ .refreshPlugins(
+ Intent.ACTION_PACKAGE_ADDED
+ .equals(action));
+ }
+ }
+ }
+ };
+ registerReceiver(mPackageInstallationReceiver, filter);
}
@Override
|| Intent.ACTION_SEARCH.equals(action)
|| MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
|| Intent.ACTION_WEB_SEARCH.equals(action)) {
- String url = getUrlFromIntent(intent);
- if (url == null || url.length() == 0) {
- url = mSettings.getHomePage();
+ // If this was a search request (e.g. search query directly typed into the address bar),
+ // pass it on to the default web search provider.
+ if (handleWebSearchIntent(intent)) {
+ return;
}
+
+ UrlData urlData = getUrlDataFromIntent(intent);
+ if (urlData.isEmpty()) {
+ urlData = new UrlData(mSettings.getHomePage());
+ }
+
if (Intent.ACTION_VIEW.equals(action) &&
(flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
final String appId =
// If the WebView has the same original url and is on that
// page, it can be reused.
boolean needsLoad =
- mTabControl.recreateWebView(appTab, url);
+ mTabControl.recreateWebView(appTab, urlData.mUrl);
+
if (current != appTab) {
- showTab(appTab, needsLoad ? url : null);
+ showTab(appTab, needsLoad ? urlData : EMPTY_URL_DATA);
} else {
if (mTabOverview != null && mAnimationCount == 0) {
sendAnimateFromOverview(appTab, false,
- needsLoad ? url : null, TAB_OVERVIEW_DELAY,
- null);
+ needsLoad ? urlData : EMPTY_URL_DATA, null,
+ TAB_OVERVIEW_DELAY, null);
} else {
// If the tab was the current tab, we have to attach
// it to the view system again.
attachTabToContentView(appTab);
if (needsLoad) {
- appTab.getWebView().loadUrl(url);
+ urlData.loadIn(appTab.getWebView());
}
}
}
// opened in a new tab unless we have reached MAX_TABS. Then the
// url will be opened in the current tab. If a new tab is
// created, it will have "true" for exit on close.
- openTabAndShow(url, null, true, appId);
+ openTabAndShow(urlData, null, true, appId);
} else {
- if ("about:debug".equals(url)) {
+ if ("about:debug".equals(urlData.mUrl)) {
mSettings.toggleDebugSettings();
return;
}
+ byte[] postData = getLocationData(intent);
// If the Window overview is up and we are not in the midst of
// an animation, animate away from the Window overview.
if (mTabOverview != null && mAnimationCount == 0) {
- sendAnimateFromOverview(current, false, url,
- TAB_OVERVIEW_DELAY, null);
+ sendAnimateFromOverview(current, false, urlData,
+ postData, TAB_OVERVIEW_DELAY, null);
} else {
// Get rid of the subwindow if it exists
dismissSubWindow(current);
- current.getWebView().loadUrl(url);
+ if (postData != null) {
+ current.getWebView().postUrl(urlData.mUrl, postData);
+ } else {
+ urlData.loadIn(current.getWebView());
+ }
}
}
}
}
- private String getUrlFromIntent(Intent intent) {
+ private int parseUrlShortcut(String url) {
+ if (url == null) return SHORTCUT_INVALID;
+
+ // FIXME: quick search, need to be customized by setting
+ if (url.length() > 2 && url.charAt(1) == ' ') {
+ switch (url.charAt(0)) {
+ case 'g': return SHORTCUT_GOOGLE_SEARCH;
+ case 'w': return SHORTCUT_WIKIPEDIA_SEARCH;
+ case 'd': return SHORTCUT_DICTIONARY_SEARCH;
+ case 'l': return SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH;
+ }
+ }
+ return SHORTCUT_INVALID;
+ }
+
+ /**
+ * Launches the default web search activity with the query parameters if the given intent's data
+ * are identified as plain search terms and not URLs/shortcuts.
+ * @return true if the intent was handled and web search activity was launched, false if not.
+ */
+ private boolean handleWebSearchIntent(Intent intent) {
+ if (intent == null) return false;
+
+ String url = null;
+ final String action = intent.getAction();
+ if (Intent.ACTION_VIEW.equals(action)) {
+ url = intent.getData().toString();
+ } else if (Intent.ACTION_SEARCH.equals(action)
+ || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
+ || Intent.ACTION_WEB_SEARCH.equals(action)) {
+ url = intent.getStringExtra(SearchManager.QUERY);
+ }
+ return handleWebSearchRequest(url);
+ }
+
+ /**
+ * Launches the default web search activity with the query parameters if the given url string
+ * was identified as plain search terms and not URL/shortcut.
+ * @return true if the request was handled and web search activity was launched, false if not.
+ */
+ private boolean handleWebSearchRequest(String inUrl) {
+ if (inUrl == null) return false;
+
+ // In general, we shouldn't modify URL from Intent.
+ // But currently, we get the user-typed URL from search box as well.
+ String url = fixUrl(inUrl).trim();
+
+ // URLs and site specific search shortcuts are handled by the regular flow of control, so
+ // return early.
+ if (Regex.WEB_URL_PATTERN.matcher(url).matches()
+ || ACCEPTED_URI_SCHEMA.matcher(url).matches()
+ || parseUrlShortcut(url) != SHORTCUT_INVALID) {
+ return false;
+ }
+
+ Browser.updateVisitedHistory(mResolver, url, false);
+ Browser.addSearchUrl(mResolver, url);
+
+ Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.putExtra(SearchManager.QUERY, url);
+ startActivity(intent);
+
+ return true;
+ }
+
+ private UrlData getUrlDataFromIntent(Intent intent) {
String url = null;
if (intent != null) {
final String action = intent.getAction();
url += "?" + mimeType;
}
}
+ if ("inline:".equals(url)) {
+ return new InlinedUrlData(
+ intent.getStringExtra(Browser.EXTRA_INLINE_CONTENT),
+ intent.getType(),
+ intent.getStringExtra(Browser.EXTRA_INLINE_ENCODING),
+ intent.getStringExtra(Browser.EXTRA_INLINE_FAILURL));
+ }
} else if (Intent.ACTION_SEARCH.equals(action)
|| MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
|| Intent.ACTION_WEB_SEARCH.equals(action)) {
}
}
}
- return url;
+ return new UrlData(url);
+ }
+
+ byte[] getLocationData(Intent intent) {
+ byte[] postData = null;
+ if (intent != null) {
+ final String action = intent.getAction();
+ if ((Intent.ACTION_SEARCH.equals(action)
+ || Intent.ACTION_WEB_SEARCH.equals(action))
+ && Settings.Secure.isLocationProviderEnabled(
+ getContentResolver(),
+ LocationManager.NETWORK_PROVIDER)) {
+ // Attempt to get location info
+ LocationManager locationManager = (LocationManager)
+ getSystemService(Context.LOCATION_SERVICE);
+ Location location = locationManager
+ .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+ if (location != null) {
+ StringBuilder str = new StringBuilder("sll=");
+ str.append(location.getLatitude()).append(",").append(
+ location.getLongitude());
+ postData = str.toString().getBytes();
+ }
+ }
+ }
+ return postData;
}
/* package */ static String fixUrl(String inUrl) {
@Override protected void onResume() {
super.onResume();
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(LOGTAG, "BrowserActivity.onResume: this=" + this);
}
return;
}
+ mTabControl.resumeCurrentTab();
mActivityInPause = false;
- resumeWebView();
+ resumeWebViewTimers();
if (mWakeLock.isHeld()) {
mHandler.removeMessages(RELEASE_WAKELOCK);
* the saved state.
*/
@Override protected void onSaveInstanceState(Bundle outState) {
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(LOGTAG, "BrowserActivity.onSaveInstanceState: this=" + this);
}
// the default implementation requires each view to have an id. As the
return;
}
+ mTabControl.pauseCurrentTab();
mActivityInPause = true;
- if (mTabControl.getCurrentIndex() >= 0 && !pauseWebView()) {
+ if (mTabControl.getCurrentIndex() >= 0 && !pauseWebViewTimers()) {
mWakeLock.acquire();
mHandler.sendMessageDelayed(mHandler
.obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT);
}
@Override protected void onDestroy() {
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(LOGTAG, "BrowserActivity.onDestroy: this=" + this);
}
super.onDestroy();
// Remove the current tab and sub window
TabControl.Tab t = mTabControl.getCurrentTab();
- dismissSubWindow(t);
- removeTabFromContentView(t);
+ if (t != null) {
+ dismissSubWindow(t);
+ removeTabFromContentView(t);
+ }
// Destroy all the tabs
mTabControl.destroy();
WebIconDatabase.getInstance().close();
// "com.android.masfproxyservice",
// "com.android.masfproxyservice.MasfProxyService"));
//stopService(proxyServiceIntent);
+
+ unregisterReceiver(mPackageInstallationReceiver);
}
@Override
mTabControl.freeMemory();
}
- private boolean resumeWebView() {
+ private boolean resumeWebViewTimers() {
if ((!mActivityInPause && !mPageStarted) ||
(mActivityInPause && mPageStarted)) {
CookieSyncManager.getInstance().startSync();
}
}
- private boolean pauseWebView() {
+ private boolean pauseWebViewTimers() {
if (mActivityInPause && !mPageStarted) {
CookieSyncManager.getInstance().stopSync();
WebView w = mTabControl.getCurrentWebView();
mCanChord = true;
int id = item.getItemId();
final WebView webView = getTopWindow();
+ if (null == webView) {
+ return false;
+ }
final HashMap hrefMap = new HashMap();
hrefMap.put("webview", webView);
final Message msg = mHandler.obtainMessage(
// menu key.
return false;
}
+ if (null == mTabOverview && null == getTopWindow()) {
+ return false;
+ }
switch (item.getItemId()) {
// -- Main menu
case R.id.goto_menu_id: {
// Send the ANIMTE_FROM_OVERVIEW message after changing the current tab.
private void sendAnimateFromOverview(final TabControl.Tab tab,
- final boolean newTab, final String url, final int delay,
- final Message msg) {
+ final boolean newTab, final UrlData urlData, final byte[] postData,
+ final int delay, final Message msg) {
// Set the current tab.
mTabControl.setCurrentTab(tab);
// Attach the WebView so it will layout.
// Load the url after the AnimatingView has captured the picture. This
// prevents any bad layout or bad scale from being used during
// animation.
- if (url != null) {
+ if (!urlData.isEmpty()) {
dismissSubWindow(tab);
- tab.getWebView().loadUrl(url);
+ if (postData != null) {
+ tab.getWebView().postUrl(urlData.mUrl, postData);
+ } else {
+ urlData.loadIn(tab.getWebView());
+ }
}
map.put("msg", msg);
mHandler.sendMessageDelayed(mHandler.obtainMessage(
}
// 500ms animation with 800ms delay
- private static final int TAB_ANIMATION_DURATION = 500;
- private static final int TAB_OVERVIEW_DELAY = 800;
+ private static final int TAB_ANIMATION_DURATION = 200;
+ private static final int TAB_OVERVIEW_DELAY = 500;
// Called by TabControl when a tab is requesting focus
/* package */ void showTab(TabControl.Tab t) {
- showTab(t, null);
+ showTab(t, EMPTY_URL_DATA);
}
- private void showTab(TabControl.Tab t, String url) {
+ private void showTab(TabControl.Tab t, UrlData urlData) {
// Disallow focus change during a tab animation.
if (mAnimationCount > 0) {
return;
delay = TAB_ANIMATION_DURATION + TAB_OVERVIEW_DELAY;
tabPicker(false, mTabControl.getTabIndex(t), false);
}
- sendAnimateFromOverview(t, false, url, delay, null);
+ sendAnimateFromOverview(t, false, urlData, null, delay, null);
+ }
+
+ // A wrapper function of {@link #openTabAndShow(UrlData, Message, boolean, String)}
+ // that accepts url as string.
+ private TabControl.Tab openTabAndShow(String url, final Message msg,
+ boolean closeOnExit, String appId) {
+ return openTabAndShow(new UrlData(url), msg, closeOnExit, appId);
}
// This method does a ton of stuff. It will attempt to create a new tab
// the given Message. If the tab overview is already showing (i.e. this
// method is called from TabListener.onClick(), the method will animate
// away from the tab overview.
- private void openTabAndShow(String url, final Message msg,
+ private TabControl.Tab openTabAndShow(UrlData urlData, final Message msg,
boolean closeOnExit, String appId) {
final boolean newTab = mTabControl.getTabCount() != TabControl.MAX_TABS;
final TabControl.Tab currentTab = mTabControl.getCurrentTab();
// If the tab overview is up and there are animations, just load
// the url.
if (mTabOverview != null && mAnimationCount > 0) {
- if (url != null) {
+ if (!urlData.isEmpty()) {
// We should not have a msg here since onCreateWindow
// checks the animation count and every other caller passes
// null.
assert msg == null;
// just dismiss the subwindow and load the given url.
dismissSubWindow(currentTab);
- currentTab.getWebView().loadUrl(url);
+ urlData.loadIn(currentTab.getWebView());
}
} else {
// show mTabOverview if it is not there.
}
// Animate from the Tab overview after any animations have
// finished.
- sendAnimateFromOverview(
- mTabControl.createNewTab(closeOnExit, appId, url), true,
- url, delay, msg);
+ final TabControl.Tab tab = mTabControl.createNewTab(
+ closeOnExit, appId, urlData.mUrl);
+ sendAnimateFromOverview(tab, true, urlData, null, delay, msg);
+ return tab;
}
- } else if (url != null) {
+ } else if (!urlData.isEmpty()) {
// We should not have a msg here.
assert msg == null;
if (mTabOverview != null && mAnimationCount == 0) {
- sendAnimateFromOverview(currentTab, false, url,
+ sendAnimateFromOverview(currentTab, false, urlData, null,
TAB_OVERVIEW_DELAY, null);
} else {
// Get rid of the subwindow if it exists
dismissSubWindow(currentTab);
// Load the given url.
- currentTab.getWebView().loadUrl(url);
+ urlData.loadIn(currentTab.getWebView());
}
}
+ return currentTab;
}
private Animation createTabAnimation(final AnimatingView view,
.setVisibility(View.VISIBLE);
}
mContentView.removeView(mTabOverview);
+ // Clear all the data for tab picker so next time it will be
+ // recreated.
+ mTabControl.wipeAllPickerData();
mTabOverview.clear();
mTabOverview = null;
mTabListener = null;
}
- private void openTab(String url) {
+ private TabControl.Tab openTab(String url) {
if (mSettings.openInBackground()) {
TabControl.Tab t = mTabControl.createNewTab();
if (t != null) {
t.getWebView().loadUrl(url);
}
+ return t;
} else {
- openTabAndShow(url, null, false, null);
+ return openTabAndShow(url, null, false, null);
}
}
// While the tab overview is animating or being shown, block changes
// to the title.
if (mAnimationCount == 0 && mTabOverview == null) {
- setTitle(buildUrlTitle(url, title));
+ if (CUSTOM_BROWSER_BAR) {
+ mTitleBar.setTitleAndUrl(title, url);
+ } else {
+ setTitle(buildUrlTitle(url, title));
+ }
}
}
* or an empty string if, for example, the URL in question is a
* file:// URL with no hostname.
*/
- private static String buildTitleUrl(String url) {
+ /* package */ static String buildTitleUrl(String url) {
String titleUrl = null;
if (url != null) {
if (mAnimationCount > 0 || mTabOverview != null) {
return;
}
- Drawable[] array = new Drawable[2];
- PaintDrawable p = new PaintDrawable(Color.WHITE);
- p.setCornerRadius(3f);
- array[0] = p;
- if (icon == null) {
- array[1] = mGenericFavicon;
+ if (CUSTOM_BROWSER_BAR) {
+ Drawable[] array = new Drawable[3];
+ array[0] = new PaintDrawable(Color.BLACK);
+ PaintDrawable p = new PaintDrawable(Color.WHITE);
+ array[1] = p;
+ if (icon == null) {
+ array[2] = mGenericFavicon;
+ } else {
+ array[2] = new BitmapDrawable(icon);
+ }
+ LayerDrawable d = new LayerDrawable(array);
+ d.setLayerInset(1, 1, 1, 1, 1);
+ d.setLayerInset(2, 2, 2, 2, 2);
+ mTitleBar.setFavicon(d);
} else {
- array[1] = new BitmapDrawable(icon);
+ Drawable[] array = new Drawable[2];
+ PaintDrawable p = new PaintDrawable(Color.WHITE);
+ p.setCornerRadius(3f);
+ array[0] = p;
+ if (icon == null) {
+ array[1] = mGenericFavicon;
+ } else {
+ array[1] = new BitmapDrawable(icon);
+ }
+ LayerDrawable d = new LayerDrawable(array);
+ d.setLayerInset(1, 2, 2, 2, 2);
+ getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, d);
}
- LayerDrawable d = new LayerDrawable(array);
- d.setLayerInset(1, 2, 2, 2, 2);
- getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, d);
}
/**
private void revertLockIcon() {
mLockIconType = mPrevLockType;
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(LOGTAG, "BrowserActivity.revertLockIcon:" +
" revert lock icon to " + mLockIconType);
}
// Change to the parent tab
final TabControl.Tab tab = mTabControl.getTab(indexToShow);
if (tab != null) {
- sendAnimateFromOverview(tab, false, null, delay, null);
+ sendAnimateFromOverview(tab, false, EMPTY_URL_DATA, null, delay,
+ null);
} else {
// Increment this here so that no other animations can happen in
// between the end of the tab picker transition and the beginning
finish();
return;
}
- // call pauseWebView() now, we won't be able to call it in
- // onPause() as the WebView won't be valid.
- pauseWebView();
+ // call pauseWebViewTimers() now, we won't be able to call
+ // it in onPause() as the WebView won't be valid.
+ pauseWebViewTimers();
removeTabFromContentView(current);
mTabControl.removeTab(current);
}
loadURL(getTopWindow(), url);
break;
case R.id.open_newtab_context_menu_id:
- openTab(url);
+ final TabControl.Tab parent = mTabControl
+ .getCurrentTab();
+ final TabControl.Tab newTab = openTab(url);
+ if (newTab != parent) {
+ parent.addChildTab(newTab);
+ }
break;
case R.id.bookmark_context_menu_id:
Intent intent = new Intent(BrowserActivity.this,
if (!mPageStarted) {
mPageStarted = true;
- // if onResume() has been called, resumeWebView() does nothing.
- resumeWebView();
+ // if onResume() has been called, resumeWebViewTimers() does
+ // nothing.
+ resumeWebViewTimers();
}
// reset sync timer to avoid sync starts during loading a page
String uiInfo = "UI thread used "
+ (SystemClock.currentThreadTimeMillis() - mUiStart)
+ " ms";
- if (Config.LOGD) {
+ if (LOGD_ENABLED) {
Log.d(LOGTAG, uiInfo);
}
//The string that gets written to the log
+ " ms and irq took "
+ (sysCpu[4] + sysCpu[5] + sysCpu[6] - mIrqStart)
* 10 + " ms, " + uiInfo;
- if (Config.LOGD) {
+ if (LOGD_ENABLED) {
Log.d(LOGTAG, performanceString + "\nWebpage: " + url);
}
if (url != null) {
} else if (newUrl.startsWith("https://")) {
newUrl = newUrl.substring(8);
}
- if (Config.LOGD) {
+ if (LOGD_ENABLED) {
Log.d(LOGTAG, newUrl + " loaded");
}
/*
if (mPageStarted) {
mPageStarted = false;
- // pauseWebView() will do nothing and return false if onPause()
- // is not called yet.
- if (pauseWebView()) {
+ // pauseWebViewTimers() will do nothing and return false if
+ // onPause() is not called yet.
+ if (pauseWebViewTimers()) {
if (mWakeLock.isHeld()) {
mHandler.removeMessages(RELEASE_WAKELOCK);
mWakeLock.release();
// If NOT a 'safe' url, change the lock to mixed content!
if (!(URLUtil.isHttpsUrl(url) || URLUtil.isDataUrl(url) || URLUtil.isAboutUrl(url))) {
mLockIconType = LOCK_ICON_MIXED;
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(LOGTAG, "BrowserActivity.updateLockIcon:" +
" updated lock icon to " + mLockIconType + " due to " + url);
}
// openTabAndShow will dispatch the message after creating the
// new WebView. This will prevent another request from coming
// in during the animation.
- openTabAndShow(null, msg, false, null);
- parent.addChildTab(mTabControl.getCurrentTab());
+ final TabControl.Tab newTab =
+ openTabAndShow(EMPTY_URL_DATA, msg, false, null);
+ if (newTab != parent) {
+ parent.addChildTab(newTab);
+ }
WebView.WebViewTransport transport =
(WebView.WebViewTransport) msg.obj;
transport.setWebView(mTabControl.getCurrentWebView());
// Block progress updates to the title bar while the tab overview
// is animating or being displayed.
if (mAnimationCount == 0 && mTabOverview == null) {
- getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
- newProgress * 100);
+ if (CUSTOM_BROWSER_BAR) {
+ mTitleBar.setProgress(newProgress);
+ } else {
+ getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
+ newProgress * 100);
+
+ }
}
if (newProgress == 100) {
@Override
public void onReceivedTitle(WebView view, String title) {
- String url = view.getOriginalUrl();
+ String url = view.getUrl();
// here, if url is null, we want to reset the title
setUrlTitle(url, title);
url.length() >= SQLiteDatabase.SQLITE_MAX_LIKE_PATTERN_LENGTH) {
return;
}
+ // See if we can find the current url in our history database and
+ // add the new title to it.
if (url.startsWith("http://www.")) {
url = url.substring(11);
} else if (url.startsWith("http://")) {
Cursor c = mResolver.query(Browser.BOOKMARKS_URI,
Browser.HISTORY_PROJECTION, where, selArgs, null);
if (c.moveToFirst()) {
- if (Config.LOGV) {
- Log.v(LOGTAG, "updating cursor");
- }
// Current implementation of database only has one entry per
// url.
- int titleIndex =
- c.getColumnIndex(Browser.BookmarkColumns.TITLE);
- c.updateString(titleIndex, title);
- c.commitUpdates();
+ ContentValues map = new ContentValues();
+ map.put(Browser.BookmarkColumns.TITLE, title);
+ mResolver.update(Browser.BOOKMARKS_URI, map,
+ "_id = " + c.getInt(0), null);
}
c.close();
} catch (IllegalStateException e) {
public void onReceivedIcon(WebView view, Bitmap icon) {
updateIcon(view.getUrl(), icon);
}
+
+ /**
+ * The origin has exceeded it's database quota.
+ * @param url the URL that exceeded the quota
+ * @param databaseIdentifier the identifier of the database on
+ * which the transaction that caused the quota overflow was run
+ * @param currentQuota the current quota for the origin.
+ * @param quotaUpdater The callback to run when a decision to allow or
+ * deny quota has been made. Don't forget to call this!
+ */
+ @Override
+ public void onExceededDatabaseQuota(String url,
+ String databaseIdentifier, long currentQuota,
+ WebStorage.QuotaUpdater quotaUpdater) {
+ if(LOGV_ENABLED) {
+ Log.v(LOGTAG,
+ "BrowserActivity received onExceededDatabaseQuota for "
+ + url +
+ ":"
+ + databaseIdentifier +
+ "(current quota: "
+ + currentQuota +
+ ")");
+ }
+ mWebStorageQuotaUpdater = quotaUpdater;
+ String DIALOG_PACKAGE = "com.android.browser";
+ String DIALOG_CLASS = DIALOG_PACKAGE + ".PermissionDialog";
+ Intent intent = new Intent();
+ intent.setClassName(DIALOG_PACKAGE, DIALOG_CLASS);
+ intent.putExtra(PermissionDialog.PARAM_ORIGIN, url);
+ intent.putExtra(PermissionDialog.PARAM_QUOTA, currentQuota);
+ startActivityForResult(intent, WEBSTORAGE_QUOTA_DIALOG);
+ }
+
+ /* Adds a JavaScript error message to the system log.
+ * @param message The error message to report.
+ * @param lineNumber The line number of the error.
+ * @param sourceID The name of the source file that caused the error.
+ */
+ @Override
+ public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+ Log.w(LOGTAG, "Console: " + message + " (" + sourceID + ":" + lineNumber + ")");
+ }
+
};
/**
startActivity(intent);
return;
} catch (ActivityNotFoundException ex) {
- if (Config.LOGD) {
+ if (LOGD_ENABLED) {
Log.d(LOGTAG, "activity not found for " + mimetype
+ " over " + Uri.parse(url).getScheme(), ex);
}
String cookies = CookieManager.getInstance().getCookie(url);
ContentValues values = new ContentValues();
- values.put(Downloads.URI, uri.toString());
- values.put(Downloads.COOKIE_DATA, cookies);
- values.put(Downloads.USER_AGENT, userAgent);
- values.put(Downloads.NOTIFICATION_PACKAGE,
+ values.put(Downloads.COLUMN_URI, uri.toString());
+ values.put(Downloads.COLUMN_COOKIE_DATA, cookies);
+ values.put(Downloads.COLUMN_USER_AGENT, userAgent);
+ values.put(Downloads.COLUMN_NOTIFICATION_PACKAGE,
getPackageName());
- values.put(Downloads.NOTIFICATION_CLASS,
+ values.put(Downloads.COLUMN_NOTIFICATION_CLASS,
BrowserDownloadPage.class.getCanonicalName());
- values.put(Downloads.VISIBILITY, Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
- values.put(Downloads.MIMETYPE, mimetype);
- values.put(Downloads.FILENAME_HINT, filename);
- values.put(Downloads.DESCRIPTION, uri.getHost());
+ values.put(Downloads.COLUMN_VISIBILITY, Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+ values.put(Downloads.COLUMN_MIME_TYPE, mimetype);
+ values.put(Downloads.COLUMN_FILE_NAME_HINT, filename);
+ values.put(Downloads.COLUMN_DESCRIPTION, uri.getHost());
if (contentLength > 0) {
- values.put(Downloads.TOTAL_BYTES, contentLength);
+ values.put(Downloads.COLUMN_TOTAL_BYTES, contentLength);
}
if (mimetype == null) {
// We must have long pressed on a link or image to download it. We
mLockIconType = LOCK_ICON_UNSECURE;
if (URLUtil.isHttpsUrl(url)) {
mLockIconType = LOCK_ICON_SECURE;
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(LOGTAG, "BrowserActivity.resetLockIcon:" +
" reset lock icon to " + mLockIconType);
}
mLockIconType = LOCK_ICON_UNSECURE;
- if (Config.LOGV) {
+ if (LOGV_ENABLED) {
Log.v(LOGTAG, "BrowserActivity.resetLockIcon:" +
" reset lock icon to " + mLockIconType);
}
// If the tab overview is animating or being shown, do not update the
// lock icon.
if (mAnimationCount == 0 && mTabOverview == null) {
- getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, d);
+ if (CUSTOM_BROWSER_BAR) {
+ mTitleBar.setLock(d);
+ } else {
+ getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, d);
+ }
}
}
// middle of an animation, animate away from it to the
// current tab.
if (mTabOverview != null && mAnimationCount == 0) {
- sendAnimateFromOverview(currentTab, false, data,
- TAB_OVERVIEW_DELAY, null);
+ sendAnimateFromOverview(currentTab, false, new UrlData(data),
+ null, TAB_OVERVIEW_DELAY, null);
} else {
dismissSubWindow(currentTab);
if (data != null && data.length() != 0) {
}
}
break;
+ case WEBSTORAGE_QUOTA_DIALOG:
+ long currentQuota = 0;
+ if (resultCode == RESULT_OK && intent != null) {
+ currentQuota = intent.getLongExtra(
+ PermissionDialog.PARAM_QUOTA, currentQuota);
+ }
+ mWebStorageQuotaUpdater.updateQuota(currentQuota);
+ break;
default:
break;
}
private class TabListener implements ImageGrid.Listener {
public void remove(int position) {
// Note: Remove is not enabled if we have only one tab.
- if (Config.DEBUG && mTabControl.getTabCount() == 1) {
+ if (DEBUG && mTabControl.getTabCount() == 1) {
throw new AssertionError();
}
if (mTabControl.getTabCount() == 0) {
current = mTabControl.createNewTab();
sendAnimateFromOverview(current, true,
- mSettings.getHomePage(), TAB_OVERVIEW_DELAY, null);
+ new UrlData(mSettings.getHomePage()), null, TAB_OVERVIEW_DELAY,
+ null);
} else {
final int index = position > 0 ? (position - 1) : 0;
current = mTabControl.getTab(index);
}
}
- // Clear all the data for tab picker so next time it will be
- // recreated.
- mTabControl.wipeAllPickerData();
-
// NEW_TAB means that the "New Tab" cell was clicked on.
if (index == ImageGrid.NEW_TAB) {
openTabAndShow(mSettings.getHomePage(), null, false, null);
} else {
sendAnimateFromOverview(mTabControl.getTab(index),
- false, null, 0, null);
+ false, EMPTY_URL_DATA, null, 0, null);
}
}
}
AnimatingView(Context ctxt, TabControl.Tab t) {
super(ctxt);
mTab = t;
- // Use the top window in the animation since the tab overview will
- // display the top window in each cell.
- final WebView w = t.getTopWindow();
- mPicture = w.capturePicture();
- mScale = w.getScale() / w.getWidth();
- mScrollX = w.getScrollX();
- mScrollY = w.getScrollY();
+ if (t != null && t.getTopWindow() != null) {
+ // Use the top window in the animation since the tab overview
+ // will display the top window in each cell.
+ final WebView w = t.getTopWindow();
+ mPicture = w.capturePicture();
+ mScale = w.getScale() / w.getWidth();
+ mScrollX = w.getScrollX();
+ mScrollY = w.getScrollY();
+ } else {
+ mPicture = null;
+ mScale = 1.0f;
+ mScrollX = mScrollY = 0;
+ }
}
@Override
mAnimationCount++;
// Always change the title bar to the window overview title while
// animating.
- getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, null);
- getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, null);
- getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
- Window.PROGRESS_VISIBILITY_OFF);
- setTitle(R.string.tab_picker_title);
+ if (CUSTOM_BROWSER_BAR) {
+ mTitleBar.setToTabPicker();
+ } else {
+ getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, null);
+ getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, null);
+ getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
+ Window.PROGRESS_VISIBILITY_OFF);
+ setTitle(R.string.tab_picker_title);
+ }
// Make the menu empty until the animation completes.
mMenuState = EMPTY_MENU;
}
- private void bookmarksOrHistoryPicker(boolean startWithHistory) {
+ /* package */ void bookmarksOrHistoryPicker(boolean startWithHistory) {
WebView current = mTabControl.getCurrentWebView();
if (current == null) {
return;
return 0;
}
- static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
+ protected static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
"(?i)" + // switch on case insensitive matching
"(" + // begin group for schema
"(?:http|https|file):\\/\\/" +
- "|(?:data|about|content|javascript):" +
+ "|(?:inline|data|about|content|javascript):" +
")" +
"(.*)" );
Matcher matcher = ACCEPTED_URI_SCHEMA.matcher(inUrl);
if (matcher.matches()) {
- if (hasSpace) {
- inUrl = inUrl.replace(" ", "%20");
- }
// force scheme to lowercase
String scheme = matcher.group(1);
String lcScheme = scheme.toLowerCase();
if (!lcScheme.equals(scheme)) {
- return lcScheme + matcher.group(2);
+ inUrl = lcScheme + matcher.group(2);
+ }
+ if (hasSpace) {
+ inUrl = inUrl.replace(" ", "%20");
}
return inUrl;
}
if (hasSpace) {
- // FIXME: quick search, need to be customized by setting
- if (inUrl.length() > 2 && inUrl.charAt(1) == ' ') {
- // FIXME: Is this the correct place to add to searches?
- // what if someone else calls this function?
- char char0 = inUrl.charAt(0);
-
- if (char0 == 'g') {
- Browser.addSearchUrl(mResolver, inUrl);
- return composeSearchUrl(inUrl.substring(2));
-
- } else if (char0 == 'w') {
- Browser.addSearchUrl(mResolver, inUrl);
- return URLUtil.composeSearchUrl(inUrl.substring(2),
- QuickSearch_W,
- QUERY_PLACE_HOLDER);
-
- } else if (char0 == 'd') {
- Browser.addSearchUrl(mResolver, inUrl);
- return URLUtil.composeSearchUrl(inUrl.substring(2),
- QuickSearch_D,
- QUERY_PLACE_HOLDER);
-
- } else if (char0 == 'l') {
- Browser.addSearchUrl(mResolver, inUrl);
+ // FIXME: Is this the correct place to add to searches?
+ // what if someone else calls this function?
+ int shortcut = parseUrlShortcut(inUrl);
+ if (shortcut != SHORTCUT_INVALID) {
+ Browser.addSearchUrl(mResolver, inUrl);
+ String query = inUrl.substring(2);
+ switch (shortcut) {
+ case SHORTCUT_GOOGLE_SEARCH:
+ return composeSearchUrl(query);
+ case SHORTCUT_WIKIPEDIA_SEARCH:
+ return URLUtil.composeSearchUrl(query, QuickSearch_W, QUERY_PLACE_HOLDER);
+ case SHORTCUT_DICTIONARY_SEARCH:
+ return URLUtil.composeSearchUrl(query, QuickSearch_D, QUERY_PLACE_HOLDER);
+ case SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH:
// FIXME: we need location in this case
- return URLUtil.composeSearchUrl(inUrl.substring(2),
- QuickSearch_L,
- QUERY_PLACE_HOLDER);
+ return URLUtil.composeSearchUrl(query, QuickSearch_L, QUERY_PLACE_HOLDER);
}
}
} else {
* mcc-specific xml files.)
*/
Locale l = Locale.getDefault();
+ String language = l.getLanguage();
+ String country = l.getCountry().toLowerCase();
+ // Chinese and Portuguese have two langauge variants.
+ if ("zh".equals(language)) {
+ if ("cn".equals(country)) {
+ language = "zh-CN";
+ } else if ("tw".equals(country)) {
+ language = "zh-TW";
+ }
+ } else if ("pt".equals(language)) {
+ if ("br".equals(country)) {
+ language = "pt-BR";
+ } else if ("pt".equals(country)) {
+ language = "pt-PT";
+ }
+ }
QuickSearch_G = getResources().getString(
- R.string.google_search_base, l.getLanguage(),
- l.getCountry().toLowerCase())
+ R.string.google_search_base,
+ language,
+ country)
+ "client=ms-"
+ Partner.getString(this.getContentResolver(), Partner.CLIENT_ID)
+ // FIXME, remove this when GEOLOCATION team make their move
+ + "&action=devloc"
+ "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&q=%s";
} else {
QuickSearch_G = url;
private Toast mStopToast;
+ private TitleBar mTitleBar;
+
// Used during animations to prevent other animations from being triggered.
// A count is used since the animation to and from the Window overview can
// overlap. A count of 0 means no animation where a count of > 0 means
private IntentFilter mNetworkStateChangedFilter;
private BroadcastReceiver mNetworkStateIntentReceiver;
+ private BroadcastReceiver mPackageInstallationReceiver;
+
// activity requestCode
- final static int COMBO_PAGE = 1;
- final static int DOWNLOAD_PAGE = 2;
- final static int PREFERENCES_PAGE = 3;
+ final static int COMBO_PAGE = 1;
+ final static int DOWNLOAD_PAGE = 2;
+ final static int PREFERENCES_PAGE = 3;
+ final static int WEBSTORAGE_QUOTA_DIALOG = 4;
// the frenquency of checking whether system memory is low
final static int CHECK_MEMORY_INTERVAL = 30000; // 30 seconds
+
+ /**
+ * A UrlData class to abstract how the content will be set to WebView.
+ * This base class uses loadUrl to show the content.
+ */
+ private static class UrlData {
+ String mUrl;
+
+ UrlData(String url) {
+ this.mUrl = url;
+ }
+
+ boolean isEmpty() {
+ return mUrl == null || mUrl.length() == 0;
+ }
+
+ private void loadIn(WebView webView) {
+ webView.loadUrl(mUrl);
+ }
+ };
+
+ /**
+ * A subclass of UrlData class that can display inlined content using
+ * {@link WebView#loadDataWithBaseURL(String, String, String, String, String)}.
+ */
+ private static class InlinedUrlData extends UrlData {
+ InlinedUrlData(String inlined, String mimeType, String encoding, String failUrl) {
+ super(failUrl);
+ mInlined = inlined;
+ mMimeType = mimeType;
+ mEncoding = encoding;
+ }
+ String mMimeType;
+ String mInlined;
+ String mEncoding;
+
+ boolean isEmpty() {
+ return mInlined == null || mInlined.length() == 0 || super.isEmpty();
+ }
+
+ void loadIn(WebView webView) {
+ webView.loadDataWithBaseURL(null, mInlined, mMimeType, mEncoding, mUrl);
+ }
+ }
+
+ private static final UrlData EMPTY_URL_DATA = new UrlData(null);
}