import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
+import android.database.DatabaseUtils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Debug;
import android.os.Environment;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Browser;
import android.provider.ContactsContract.Intents.Insert;
import android.provider.Downloads;
import android.provider.MediaStore;
+import android.speech.RecognizerResultsIntent;
import android.text.IClipboard;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Patterns;
import android.view.ContextMenu;
import android.view.Gravity;
import android.view.KeyEvent;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.accounts.AccountManagerCallback;
-import com.android.common.Patterns;
-
-import com.google.android.googleapps.IGoogleLoginService;
-import com.google.android.googlelogin.GoogleLoginServiceConstants;
+import com.android.common.Search;
+import com.android.common.speech.LoggingEvents;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class BrowserActivity extends Activity
- implements View.OnCreateContextMenuListener,
- DownloadListener {
+ implements 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 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;
-
// 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_DICTIONARY_SEARCH = 3;
private static final int SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH = 4;
- private void setupHomePage() {
- final Runnable getAccount = new Runnable() {
- public void run() {
- // Lower priority
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- // get the default home page
- String homepage = mSettings.getHomePage();
-
- 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);
-
- // three cases:
- //
- // hostedUser == googleUser
- // The device has only a google account
- //
- // hostedUser != googleUser
- // The device has a hosted account and a google account
- //
- // hostedUser != null, googleUser == null
- // The device has only a hosted account (so far)
-
- // developers might have no accounts at all
- if (hostedUser == null) return;
-
- if (googleUser == null || !hostedUser.equals(googleUser)) {
- String domain = hostedUser.substring(hostedUser.lastIndexOf('@')+1);
- homepage = homepage.replace("?", "/a/" + domain + "?");
- }
- } catch (RemoteException ignore) {
- // Login service died; carry on
- } catch (RuntimeException ignore) {
- // Login service died; carry on
- } finally {
- finish(homepage);
- }
- }
-
- private void finish(final String homepage) {
- mHandler.post(new Runnable() {
- public void run() {
- mSettings.setHomePage(BrowserActivity.this, homepage);
- resumeAfterCredentials();
-
- // as this is running in a separate thread,
- // BrowserActivity's onDestroy() may have been called,
- // which also calls unbindService().
- if (mGlsConnection != null) {
- // we no longer need to keep GLS open
- unbindService(mGlsConnection);
- mGlsConnection = null;
- }
- } });
- } };
-
- final boolean[] done = { false };
-
- // Open a connection to the Google Login Service. The first
- // time the connection is established, set up the homepage depending on
- // the account in a background thread.
- mGlsConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- mGls = IGoogleLoginService.Stub.asInterface(service);
- if (done[0] == false) {
- done[0] = true;
- Thread account = new Thread(getAccount);
- account.setName("GLSAccount");
- account.start();
- }
- }
- public void onServiceDisconnected(ComponentName className) {
- mGls = null;
- }
- };
-
- bindService(GoogleLoginServiceConstants.SERVICE_INTENT,
- mGlsConnection, Context.BIND_AUTO_CREATE);
- }
-
private static class ClearThumbnails extends AsyncTask<File, Void, Void> {
@Override
public Void doInBackground(File... files) {
// test the browser in OpenGL
// requestWindowFeature(Window.FEATURE_OPENGL);
+ // enable this to test the browser in 32bit
+ if (false) {
+ getWindow().setFormat(PixelFormat.RGBX_8888);
+ BitmapFactory.setDefaultConfig(Bitmap.Config.ARGB_8888);
+ }
+
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
mResolver = getContentResolver();
.findViewById(R.id.fullscreen_custom_content);
frameLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS);
mTitleBar = new TitleBar(this);
+ // mTitleBar will be always shown in the fully loaded mode
+ mTitleBar.setProgress(100);
mFakeTitleBar = new TitleBar(this);
// Create the tab control and our initial tab
// Keep a settings instance handy.
mSettings = BrowserSettings.getInstance();
mSettings.setTabControl(mTabControl);
- mSettings.loadFromDb(this);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser");
+ // Find out if the network is currently up.
+ ConnectivityManager cm = (ConnectivityManager) getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ NetworkInfo info = cm.getActiveNetworkInfo();
+ if (info != null) {
+ mIsNetworkUp = info.isAvailable();
+ }
+
/* enables registration for changes in network status from
http stack */
mNetworkStateChangedFilter = new IntentFilter();
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
ConnectivityManager.CONNECTIVITY_ACTION)) {
- boolean noConnectivity = intent.getBooleanExtra(
- ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
- onNetworkToggle(!noConnectivity);
+
+ NetworkInfo info = intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ String typeName = info.getTypeName();
+ String subtypeName = info.getSubtypeName();
+ sendNetworkType(typeName.toLowerCase(),
+ (subtypeName != null ? subtypeName.toLowerCase() : ""));
+
+ onNetworkToggle(info.isAvailable());
}
}
};
// if it is replacing, refreshPlugins() when adding
return;
}
+
+ if (sGoogleApps.contains(packageName)) {
+ BrowserActivity.this.packageChanged(packageName,
+ Intent.ACTION_PACKAGE_ADDED.equals(action));
+ }
+
PackageManager pm = BrowserActivity.this.getPackageManager();
PackageInfo pkgInfo = null;
try {
// the tab will be close when exit.
UrlData urlData = getUrlDataFromIntent(intent);
+ String action = intent.getAction();
final Tab t = mTabControl.createNewTab(
- Intent.ACTION_VIEW.equals(intent.getAction()) &&
- intent.getData() != null,
+ (Intent.ACTION_VIEW.equals(action) &&
+ intent.getData() != null)
+ || RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS
+ .equals(action),
intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), urlData.mUrl);
mTabControl.setCurrentTab(t);
attachTabToContentView(t);
webView.setInitialScale(scale);
}
}
- // If we are not restoring from an icicle, then there is a high
- // likely hood this is the first run. So, check to see if the
- // homepage needs to be configured and copy any plugins from our
- // asset directory to the data partition.
- if ((extra == null || !extra.getBoolean("testing"))
- && !mSettings.isLoginInitialized()) {
- setupHomePage();
- }
if (urlData.isEmpty()) {
- if (mSettings.isLoginInitialized()) {
- webView.loadUrl(mSettings.getHomePage());
- } else {
- waitForCredentials();
- }
+ loadUrl(webView, mSettings.getHomePage());
} else {
- if (extra != null) {
- urlData.setPostData(extra
- .getByteArray(Browser.EXTRA_POST_DATA));
- }
- urlData.loadIn(webView);
+ loadUrlDataIn(t, urlData);
}
} else {
// TabControl.restoreState() will create a new tab even if
if (jsFlags.trim().length() != 0) {
mTabControl.getCurrentWebView().setJsFlags(jsFlags);
}
+ // Work out which packages are installed on the system.
+ getInstalledPackages();
+
+ // Start watching the default geolocation permissions
+ mSystemAllowGeolocationOrigins
+ = new SystemAllowGeolocationOrigins(getApplicationContext());
+ mSystemAllowGeolocationOrigins.start();
+ }
+
+ /**
+ * Feed the previously stored results strings to the BrowserProvider so that
+ * the SearchDialog will show them instead of the standard searches.
+ * @param result String to show on the editable line of the SearchDialog.
+ */
+ /* package */ void showVoiceSearchResults(String result) {
+ ContentProviderClient client = mResolver.acquireContentProviderClient(
+ Browser.BOOKMARKS_URI);
+ ContentProvider prov = client.getLocalContentProvider();
+ BrowserProvider bp = (BrowserProvider) prov;
+ bp.setQueryResults(mTabControl.getCurrentTab().getVoiceSearchResults());
+ client.release();
+
+ Bundle bundle = createGoogleSearchSourceBundle(
+ GOOGLE_SEARCH_SOURCE_SEARCHKEY);
+ bundle.putBoolean(SearchManager.CONTEXT_IS_VOICE, true);
+ startSearch(result, false, bundle, false);
}
@Override
// just resume the browser
return;
}
+ // In case the SearchDialog is open.
+ ((SearchManager) getSystemService(Context.SEARCH_SERVICE))
+ .stopSearch();
+ boolean activateVoiceSearch = RecognizerResultsIntent
+ .ACTION_VOICE_SEARCH_RESULTS.equals(action);
if (Intent.ACTION_VIEW.equals(action)
|| Intent.ACTION_SEARCH.equals(action)
|| MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
- || Intent.ACTION_WEB_SEARCH.equals(action)) {
+ || Intent.ACTION_WEB_SEARCH.equals(action)
+ || activateVoiceSearch) {
+ if (current.isInVoiceSearchMode()) {
+ String title = current.getVoiceDisplayTitle();
+ if (title != null && title.equals(intent.getStringExtra(
+ SearchManager.QUERY))) {
+ // The user submitted the same search as the last voice
+ // search, so do nothing.
+ return;
+ }
+ if (Intent.ACTION_SEARCH.equals(action)
+ && current.voiceSearchSourceIsGoogle()) {
+ Intent logIntent = new Intent(
+ LoggingEvents.ACTION_LOG_EVENT);
+ logIntent.putExtra(LoggingEvents.EXTRA_EVENT,
+ LoggingEvents.VoiceSearch.QUERY_UPDATED);
+ logIntent.putExtra(
+ LoggingEvents.VoiceSearch.EXTRA_QUERY_UPDATED_VALUE,
+ intent.getDataString());
+ sendBroadcast(logIntent);
+ // Note, onPageStarted will revert the voice title bar
+ // When http://b/issue?id=2379215 is fixed, we should update
+ // the title bar here.
+ }
+ }
// 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)) {
if (urlData.isEmpty()) {
urlData = new UrlData(mSettings.getHomePage());
}
- urlData.setPostData(intent
- .getByteArrayExtra(Browser.EXTRA_POST_DATA));
final String appId = intent
.getStringExtra(Browser.EXTRA_APPLICATION_ID);
- if (Intent.ACTION_VIEW.equals(action)
+ if ((Intent.ACTION_VIEW.equals(action)
+ // If a voice search has no appId, it means that it came
+ // from the browser. In that case, reuse the current tab.
+ || (activateVoiceSearch && appId != null))
&& !getPackageName().equals(appId)
&& (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
Tab appTab = mTabControl.getTabFromId(appId);
// If the WebView has the same original url and is on that
// page, it can be reused.
boolean needsLoad =
- mTabControl.recreateWebView(appTab, urlData.mUrl);
+ mTabControl.recreateWebView(appTab, urlData);
if (current != appTab) {
switchToTab(mTabControl.getTabIndex(appTab));
if (needsLoad) {
- urlData.loadIn(appTab.getWebView());
+ loadUrlDataIn(appTab, urlData);
}
} else {
// If the tab was the current tab, we have to attach
// it to the view system again.
attachTabToContentView(appTab);
if (needsLoad) {
- urlData.loadIn(appTab.getWebView());
+ loadUrlDataIn(appTab, urlData);
}
}
return;
current.getWebView().dumpRenderTree(true);
} else if ("about:debug.display".equals(urlData.mUrl)) {
current.getWebView().dumpDisplayTree();
+ } else if (urlData.mUrl.startsWith("about:debug.drag")) {
+ int index = urlData.mUrl.codePointAt(16) - '0';
+ if (index <= 0 || index > 9) {
+ current.getWebView().setDragTracker(null);
+ } else {
+ current.getWebView().setDragTracker(new MeshTracker(index));
+ }
} else {
mSettings.toggleDebugSettings();
}
}
// Get rid of the subwindow if it exists
dismissSubWindow(current);
- urlData.loadIn(current.getWebView());
+ loadUrlDataIn(current, urlData);
}
}
}
String url = null;
final String action = intent.getAction();
+ if (RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS.equals(
+ action)) {
+ return false;
+ }
if (Intent.ACTION_VIEW.equals(action)) {
Uri data = intent.getData();
if (data != null) url = data.toString();
return false;
}
- Browser.updateVisitedHistory(mResolver, url, false);
- Browser.addSearchUrl(mResolver, url);
+ final ContentResolver cr = mResolver;
+ final String newUrl = url;
+ new AsyncTask<Void, Void, Void>() {
+ protected Void doInBackground(Void... unused) {
+ Browser.updateVisitedHistory(cr, newUrl, false);
+ Browser.addSearchUrl(cr, newUrl);
+ return null;
+ }
+ }.execute();
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
intent.addCategory(Intent.CATEGORY_DEFAULT);
}
private UrlData getUrlDataFromIntent(Intent intent) {
- String url = null;
+ String url = "";
+ Map<String, String> headers = null;
if (intent != null) {
final String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action)) {
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));
+ if (url != null && url.startsWith("http")) {
+ final Bundle pairs = intent
+ .getBundleExtra(Browser.EXTRA_HEADERS);
+ if (pairs != null && !pairs.isEmpty()) {
+ Iterator<String> iter = pairs.keySet().iterator();
+ headers = new HashMap<String, String>();
+ while (iter.hasNext()) {
+ String key = iter.next();
+ headers.put(key, pairs.getString(key));
+ }
+ }
}
} else if (Intent.ACTION_SEARCH.equals(action)
|| MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
url = intent.getStringExtra(SearchManager.QUERY);
if (url != null) {
mLastEnteredUrl = url;
- Browser.updateVisitedHistory(mResolver, url, false);
// In general, we shouldn't modify URL from Intent.
// But currently, we get the user-typed URL from search box as well.
url = fixUrl(url);
url = smartUrlFilter(url);
+ final ContentResolver cr = mResolver;
+ final String newUrl = url;
+ new AsyncTask<Void, Void, Void>() {
+ protected Void doInBackground(Void... unused) {
+ Browser.updateVisitedHistory(cr, newUrl, false);
+ return null;
+ }
+ }.execute();
String searchSource = "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&";
if (url.contains(searchSource)) {
String source = null;
final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
if (appData != null) {
- source = appData.getString(SearchManager.SOURCE);
+ source = appData.getString(Search.SOURCE);
}
if (TextUtils.isEmpty(source)) {
source = GOOGLE_SEARCH_SOURCE_UNKNOWN;
}
}
}
- return new UrlData(url);
+ return new UrlData(url, headers, intent);
+ }
+ /* package */ void showVoiceTitleBar(String title) {
+ mTitleBar.setInVoiceMode(true);
+ mFakeTitleBar.setInVoiceMode(true);
+
+ mTitleBar.setDisplayTitle(title);
+ mFakeTitleBar.setDisplayTitle(title);
}
+ /* package */ void revertVoiceTitleBar() {
+ mTitleBar.setInVoiceMode(false);
+ mFakeTitleBar.setInVoiceMode(false);
+ mTitleBar.setDisplayTitle(mUrl);
+ mFakeTitleBar.setDisplayTitle(mUrl);
+ }
/* package */ static String fixUrl(String inUrl) {
// FIXME: Converting the url to lower case
// duplicates functionality in smartUrlFilter().
mWakeLock.release();
}
- if (mCredsDlg != null) {
- if (!mHandler.hasMessages(CANCEL_CREDS_REQUEST)) {
- // In case credential request never comes back
- mHandler.sendEmptyMessageDelayed(CANCEL_CREDS_REQUEST, 6000);
- }
- }
-
registerReceiver(mNetworkStateIntentReceiver,
mNetworkStateChangedFilter);
WebView.enablePlatformNotifications();
private TitleBar mFakeTitleBar;
/**
- * Holder for the fake title bar. It will have a foreground shadow, as well
- * as a white background, so the fake title bar looks like the real one.
- */
- private ViewGroup mFakeTitleBarHolder;
-
- /**
- * Layout parameters for the fake title bar within mFakeTitleBarHolder
- */
- private FrameLayout.LayoutParams mFakeTitleBarParams
- = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- /**
* Keeps track of whether the options menu is open. This is important in
* determining whether to show or hide the title bar overlay.
*/
return true;
}
- /**
- * Special class used exclusively for the shadow drawn underneath the fake
- * title bar. The shadow does not need to be drawn if the WebView
- * underneath is scrolled to the top, because it will draw directly on top
- * of the embedded shadow.
- */
- private static class Shadow extends View {
- private WebView mWebView;
-
- public Shadow(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void setWebView(WebView view) {
- mWebView = view;
- }
-
- @Override
- public void draw(Canvas canvas) {
- // In general onDraw is the method to override, but we care about
- // whether or not the background gets drawn, which happens in draw()
- if (mWebView == null || mWebView.getScrollY() > getHeight()) {
- super.draw(canvas);
- }
- // Need to invalidate so that if the scroll position changes, we
- // still draw as appropriate.
- invalidate();
- }
- }
-
private void showFakeTitleBar() {
- final View decor = getWindow().peekDecorView();
if (mFakeTitleBar.getParent() == null && mActiveTabsPage == null
- && !mActivityInPause && decor != null
- && decor.getWindowToken() != null) {
- Rect visRect = new Rect();
- if (!mBrowserFrameLayout.getGlobalVisibleRect(visRect)) {
- if (LOGD_ENABLED) {
- Log.d(LOGTAG, "showFakeTitleBar visRect failed");
- }
+ && !mActivityInPause) {
+ WebView mainView = mTabControl.getCurrentWebView();
+ // if there is no current WebView, don't show the faked title bar;
+ if (mainView == null) {
return;
}
// while the menu is up
WindowManager.LayoutParams params
= new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
+ WindowManager.LayoutParams.TYPE_APPLICATION,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP;
- WebView mainView = mTabControl.getCurrentWebView();
- boolean atTop = mainView != null && mainView.getScrollY() == 0;
+ boolean atTop = mainView.getScrollY() == 0;
params.windowAnimations = atTop ? 0 : R.style.TitleBar;
- // XXX : Without providing an offset, the fake title bar will be
- // placed underneath the status bar. Use the global visible rect
- // of mBrowserFrameLayout to determine the bottom of the status bar
- params.y = visRect.top;
- // Add a holder for the title bar. It also holds a shadow to show
- // below the title bar.
- if (mFakeTitleBarHolder == null) {
- mFakeTitleBarHolder = (ViewGroup) LayoutInflater.from(this)
- .inflate(R.layout.title_bar_bg, null);
- }
- Shadow shadow = (Shadow) mFakeTitleBarHolder.findViewById(
- R.id.shadow);
- shadow.setWebView(mainView);
- mFakeTitleBarHolder.addView(mFakeTitleBar, 0, mFakeTitleBarParams);
- manager.addView(mFakeTitleBarHolder, params);
+ manager.addView(mFakeTitleBar, params);
}
}
private void hideFakeTitleBar() {
if (mFakeTitleBar.getParent() == null) return;
WindowManager.LayoutParams params = (WindowManager.LayoutParams)
- mFakeTitleBarHolder.getLayoutParams();
+ mFakeTitleBar.getLayoutParams();
WebView mainView = mTabControl.getCurrentWebView();
// Although we decided whether or not to animate based on the current
// scroll position, the scroll position may have changed since the
? 0 : R.style.TitleBar;
WindowManager manager
= (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- manager.updateViewLayout(mFakeTitleBarHolder, params);
- mFakeTitleBarHolder.removeView(mFakeTitleBar);
- manager.removeView(mFakeTitleBarHolder);
+ manager.updateViewLayout(mFakeTitleBar, params);
+ manager.removeView(mFakeTitleBar);
}
/**
.obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT);
}
- // Clear the credentials toast if it is up
- if (mCredsDlg != null && mCredsDlg.isShowing()) {
- mCredsDlg.dismiss();
- }
- mCredsDlg = null;
-
// FIXME: This removes the active tabs page and resets the menu to
// MAIN_MENU. A better solution might be to do this work in onNewIntent
// but then we would need to save it in onSaveInstanceState and restore
// Destroy all the tabs
mTabControl.destroy();
WebIconDatabase.getInstance().close();
- if (mGlsConnection != null) {
- unbindService(mGlsConnection);
- mGlsConnection = null;
- }
unregisterReceiver(mPackageInstallationReceiver);
+
+ // Stop watching the default geolocation permissions
+ mSystemAllowGeolocationOrigins.stop();
+ mSystemAllowGeolocationOrigins = null;
}
@Override
mTabControl.freeMemory();
}
- private boolean resumeWebViewTimers() {
+ private void resumeWebViewTimers() {
Tab tab = mTabControl.getCurrentTab();
+ if (tab == null) return; // monkey can trigger this
boolean inLoad = tab.inLoad();
if ((!mActivityInPause && !inLoad) || (mActivityInPause && inLoad)) {
CookieSyncManager.getInstance().startSync();
if (w != null) {
w.resumeTimers();
}
- return true;
- } else {
- return false;
}
}
}
}
- // FIXME: Do we want to call this when loading google for the first time?
- /*
- * This function is called when we are launching for the first time. We
- * are waiting for the login credentials before loading Google home
- * pages. This way the user will be logged in straight away.
- */
- private void waitForCredentials() {
- // Show a toast
- mCredsDlg = new ProgressDialog(this);
- mCredsDlg.setIndeterminate(true);
- mCredsDlg.setMessage(getText(R.string.retrieving_creds_dlg_msg));
- // If the user cancels the operation, then cancel the Google
- // Credentials request.
- mCredsDlg.setCancelMessage(mHandler.obtainMessage(CANCEL_CREDS_REQUEST));
- mCredsDlg.show();
-
- // We set a timeout for the retrieval of credentials in onResume()
- // as that is when we have freed up some CPU time to get
- // the login credentials.
- }
-
- /*
- * If we have received the credentials or we have timed out and we are
- * showing the credentials dialog, then it is time to move on.
- */
- private void resumeAfterCredentials() {
- if (mCredsDlg == null) {
- return;
- }
-
- // Clear the toast
- if (mCredsDlg.isShowing()) {
- mCredsDlg.dismiss();
- }
- mCredsDlg = null;
-
- // Clear any pending timeout
- mHandler.removeMessages(CANCEL_CREDS_REQUEST);
-
- // Load the page
- WebView w = mTabControl.getCurrentWebView();
- if (w != null) {
- w.loadUrl(mSettings.getHomePage());
- }
-
- // Update the settings, need to do this last as it can take a moment
- // to persist the settings. In the mean time we could be loading
- // content.
- mSettings.setLoginInitialized(this);
- }
-
// Open the icon database and retain all the icons for visited sites.
private void retainIconsOnStartup() {
final WebIconDatabase db = WebIconDatabase.getInstance();
db.open(getDir("icons", 0).getPath());
+ Cursor c = null;
try {
- Cursor c = Browser.getAllBookmarks(mResolver);
- if (!c.moveToFirst()) {
- c.deactivate();
- return;
+ c = Browser.getAllBookmarks(mResolver);
+ if (c.moveToFirst()) {
+ int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
+ do {
+ String url = c.getString(urlIndex);
+ db.retainIconForPageUrl(url);
+ } while (c.moveToNext());
}
- int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
- do {
- String url = c.getString(urlIndex);
- db.retainIconForPageUrl(url);
- } while (c.moveToNext());
- c.deactivate();
} catch (IllegalStateException e) {
Log.e(LOGTAG, "retainIconsOnStartup", e);
+ } finally {
+ if (c!= null) c.close();
}
}
// options selector, so set mCanChord to true so we can access them.
mCanChord = true;
int id = item.getItemId();
+ boolean result = true;
switch (id) {
// For the context menu from the title bar
- case R.id.title_bar_share_page_url:
case R.id.title_bar_copy_page_url:
- WebView mainView = mTabControl.getCurrentWebView();
- if (null == mainView) {
- return false;
+ Tab currentTab = mTabControl.getCurrentTab();
+ if (null == currentTab) {
+ result = false;
+ break;
}
- if (id == R.id.title_bar_share_page_url) {
- Browser.sendString(this, mainView.getUrl());
- } else {
- copy(mainView.getUrl());
+ WebView mainView = currentTab.getWebView();
+ if (null == mainView) {
+ result = false;
+ break;
}
+ copy(mainView.getUrl());
break;
// -- Browser context menu
case R.id.open_context_menu_id:
case R.id.copy_link_context_menu_id:
final WebView webView = getTopWindow();
if (null == webView) {
- return false;
+ result = false;
+ break;
}
final HashMap hrefMap = new HashMap();
hrefMap.put("webview", webView);
default:
// For other context menus
- return onOptionsItemSelected(item);
+ result = onOptionsItemSelected(item);
}
mCanChord = false;
- return true;
+ return result;
}
private Bundle createGoogleSearchSourceBundle(String source) {
Bundle bundle = new Bundle();
- bundle.putString(SearchManager.SOURCE, source);
+ bundle.putString(Search.SOURCE, source);
return bundle;
}
- /**
- * Overriding this to insert a local information bundle
- */
- @Override
- public boolean onSearchRequested() {
+ /* package */ void editUrl() {
if (mOptionsMenuOpen) closeOptionsMenu();
String url = (getTopWindow() == null) ? null : getTopWindow().getUrl();
startSearch(mSettings.getHomePage().equals(url) ? null : url, true,
- createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHKEY), false);
- return true;
+ null, false);
}
+ /**
+ * Overriding this to insert a local information bundle
+ */
@Override
public void startSearch(String initialQuery, boolean selectInitialQuery,
Bundle appSearchData, boolean globalSearch) {
break;
case R.id.goto_menu_id:
- onSearchRequested();
+ editUrl();
break;
case R.id.bookmarks_menu_id:
Tab current = mTabControl.getCurrentTab();
if (current != null) {
dismissSubWindow(current);
- current.getWebView().loadUrl(mSettings.getHomePage());
+ loadUrl(current.getWebView(), mSettings.getHomePage());
}
break;
}
mFindDialog.setWebView(getTopWindow());
mFindDialog.show();
+ getTopWindow().setFindIsUp(true);
mMenuState = EMPTY_MENU;
break;
bookmarksOrHistoryPicker(true);
break;
+ case R.id.title_bar_share_page_url:
case R.id.share_page_menu_id:
- Browser.sendString(this, getTopWindow().getUrl(),
- getText(R.string.choosertitle_sharevia).toString());
+ Tab currentTab = mTabControl.getCurrentTab();
+ if (null == currentTab) {
+ mCanChord = false;
+ return false;
+ }
+ currentTab.populatePickerData();
+ sharePage(this, currentTab.getTitle(),
+ currentTab.getUrl(), currentTab.getFavicon(),
+ createScreenshot(currentTab.getWebView()));
break;
case R.id.dump_nav_menu_id:
getTopWindow().debugDump();
break;
+ case R.id.dump_counters_menu_id:
+ getTopWindow().dumpV8Counters();
+ break;
+
case R.id.zoom_in_menu_id:
getTopWindow().zoomIn();
break;
final MenuItem nav = menu.findItem(R.id.dump_nav_menu_id);
nav.setVisible(isNavDump);
nav.setEnabled(isNavDump);
+
+ boolean showDebugSettings = mSettings.showDebugSettings();
+ final MenuItem counter = menu.findItem(R.id.dump_counters_menu_id);
+ counter.setVisible(showDebugSettings);
+ counter.setEnabled(showDebugSettings);
+
break;
}
mCurrentMenuState = mMenuState;
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
+ if (v instanceof TitleBar) {
+ return;
+ }
WebView webview = (WebView) v;
WebView.HitTestResult result = webview.getHitTestResult();
if (result == null) {
new Intent(Intent.ACTION_VIEW, Uri.parse(extra)));
menu.findItem(R.id.download_context_menu_id).
setOnMenuItemClickListener(new Download(extra));
+ menu.findItem(R.id.set_wallpaper_context_menu_id).
+ setOnMenuItemClickListener(new SetAsWallpaper(extra));
break;
default:
}
mErrorConsoleContainer.addView(errorConsole,
- new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
+ new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
}
WebView view = t.getWebView();
view.setEmbeddedTitleBar(mTitleBar);
+ if (t.isInVoiceSearchMode()) {
+ showVoiceTitleBar(t.getVoiceDisplayTitle());
+ } else {
+ revertVoiceTitleBar();
+ }
// Request focus on the top window.
t.getTopWindow().requestFocus();
}
mTabControl.setCurrentTab(tab);
attachTabToContentView(tab);
if (!urlData.isEmpty()) {
- urlData.loadIn(webview);
+ loadUrlDataIn(tab, urlData);
}
return tab;
} else {
dismissSubWindow(currentTab);
if (!urlData.isEmpty()) {
// Load the given url.
- urlData.loadIn(currentTab.getWebView());
+ loadUrlDataIn(currentTab, urlData);
}
+ return currentTab;
}
- return currentTab;
}
private Tab openTab(String url) {
Tab t = mTabControl.createNewTab();
if (t != null) {
WebView view = t.getWebView();
- view.loadUrl(url);
+ loadUrl(view, url);
}
return t;
} else {
}
}
+ private class SetAsWallpaper extends Thread implements
+ OnMenuItemClickListener, DialogInterface.OnCancelListener {
+ private URL mUrl;
+ private ProgressDialog mWallpaperProgress;
+ private boolean mCanceled = false;
+
+ public SetAsWallpaper(String url) {
+ try {
+ mUrl = new URL(url);
+ } catch (MalformedURLException e) {
+ mUrl = null;
+ }
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ mCanceled = true;
+ }
+
+ public boolean onMenuItemClick(MenuItem item) {
+ if (mUrl != null) {
+ // The user may have tried to set a image with a large file size as their
+ // background so it may take a few moments to perform the operation. Display
+ // a progress spinner while it is working.
+ mWallpaperProgress = new ProgressDialog(BrowserActivity.this);
+ mWallpaperProgress.setIndeterminate(true);
+ mWallpaperProgress.setMessage(getText(R.string.progress_dialog_setting_wallpaper));
+ mWallpaperProgress.setCancelable(true);
+ mWallpaperProgress.setOnCancelListener(this);
+ mWallpaperProgress.show();
+ start();
+ }
+ return true;
+ }
+
+ public void run() {
+ Drawable oldWallpaper = BrowserActivity.this.getWallpaper();
+ try {
+ // TODO: This will cause the resource to be downloaded again, when we
+ // should in most cases be able to grab it from the cache. To fix this
+ // we should query WebCore to see if we can access a cached version and
+ // instead open an input stream on that. This pattern could also be used
+ // in the download manager where the same problem exists.
+ InputStream inputstream = mUrl.openStream();
+ if (inputstream != null) {
+ setWallpaper(inputstream);
+ }
+ } catch (IOException e) {
+ Log.e(LOGTAG, "Unable to set new wallpaper");
+ // Act as though the user canceled the operation so we try to
+ // restore the old wallpaper.
+ mCanceled = true;
+ }
+
+ if (mCanceled) {
+ // Restore the old wallpaper if the user cancelled whilst we were setting
+ // the new wallpaper.
+ int width = oldWallpaper.getIntrinsicWidth();
+ int height = oldWallpaper.getIntrinsicHeight();
+ Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ Canvas canvas = new Canvas(bm);
+ oldWallpaper.setBounds(0, 0, width, height);
+ oldWallpaper.draw(canvas);
+ try {
+ setWallpaper(bm);
+ } catch (IOException e) {
+ Log.e(LOGTAG, "Unable to restore old wallpaper.");
+ }
+ mCanceled = false;
+ }
+
+ if (mWallpaperProgress.isShowing()) {
+ mWallpaperProgress.dismiss();
+ }
+ }
+ }
+
private void copy(CharSequence text) {
try {
IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
mUrl = url;
mTitle = title;
- mTitleBar.setTitleAndUrl(title, url);
- mFakeTitleBar.setTitleAndUrl(title, url);
+ // If we are in voice search mode, the title has already been set.
+ if (mTabControl.getCurrentTab().isInVoiceSearchMode()) return;
+ mTitleBar.setDisplayTitle(url);
+ mFakeTitleBar.setDisplayTitle(url);
}
/**
resetTitleIconAndProgress();
}
- private void goBackOnePageOrQuit() {
+ /* package */ void goBackOnePageOrQuit() {
Tab current = mTabControl.getCurrentTab();
if (current == null) {
/*
// Message Ids
private static final int FOCUS_NODE_HREF = 102;
- private static final int CANCEL_CREDS_REQUEST = 103;
private static final int RELEASE_WAKELOCK = 107;
static final int UPDATE_BOOKMARK_THUMBNAIL = 108;
switch (msg.arg1) {
case R.id.open_context_menu_id:
case R.id.view_image_context_menu_id:
- loadURL(getTopWindow(), url);
+ loadUrlFromContext(getTopWindow(), url);
break;
case R.id.open_newtab_context_menu_id:
final Tab parent = mTabControl.getCurrentTab();
startActivity(intent);
break;
case R.id.share_link_context_menu_id:
- Browser.sendString(BrowserActivity.this, url,
- getText(R.string.choosertitle_sharevia).toString());
+ // See if this site has been visited before
+ StringBuilder sb = new StringBuilder(
+ Browser.BookmarkColumns.URL + " = ");
+ DatabaseUtils.appendEscapedSQLString(sb, url);
+ Cursor c = mResolver.query(Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION,
+ sb.toString(),
+ null,
+ null);
+ if (c.moveToFirst()) {
+ // The site has been visited before, so grab the
+ // info from the database.
+ Bitmap favicon = null;
+ Bitmap thumbnail = null;
+ String linkTitle = c.getString(Browser.
+ HISTORY_PROJECTION_TITLE_INDEX);
+ byte[] data = c.getBlob(Browser.
+ HISTORY_PROJECTION_FAVICON_INDEX);
+ if (data != null) {
+ favicon = BitmapFactory.decodeByteArray(
+ data, 0, data.length);
+ }
+ data = c.getBlob(Browser.
+ HISTORY_PROJECTION_THUMBNAIL_INDEX);
+ if (data != null) {
+ thumbnail = BitmapFactory.decodeByteArray(
+ data, 0, data.length);
+ }
+ sharePage(BrowserActivity.this,
+ linkTitle, url, favicon, thumbnail);
+ } else {
+ Browser.sendString(BrowserActivity.this, url,
+ getString(
+ R.string.choosertitle_sharevia));
+ }
break;
case R.id.copy_link_context_menu_id:
copy(url);
}
case LOAD_URL:
- loadURL(getTopWindow(), (String) msg.obj);
+ loadUrlFromContext(getTopWindow(), (String) msg.obj);
break;
case STOP_LOAD:
stopLoading();
break;
- case CANCEL_CREDS_REQUEST:
- resumeAfterCredentials();
- break;
-
case RELEASE_WAKELOCK:
if (mWakeLock.isHeld()) {
mWakeLock.release();
}
};
+ /**
+ * Share a page, providing the title, url, favicon, and a screenshot. Uses
+ * an {@link Intent} to launch the Activity chooser.
+ * @param c Context used to launch a new Activity.
+ * @param title Title of the page. Stored in the Intent with
+ * {@link Intent#EXTRA_SUBJECT}
+ * @param url URL of the page. Stored in the Intent with
+ * {@link Intent#EXTRA_TEXT}
+ * @param favicon Bitmap of the favicon for the page. Stored in the Intent
+ * with {@link Browser#EXTRA_SHARE_FAVICON}
+ * @param screenshot Bitmap of a screenshot of the page. Stored in the
+ * Intent with {@link Browser#EXTRA_SHARE_SCREENSHOT}
+ */
+ public static final void sharePage(Context c, String title, String url,
+ Bitmap favicon, Bitmap screenshot) {
+ Intent send = new Intent(Intent.ACTION_SEND);
+ send.setType("text/plain");
+ send.putExtra(Intent.EXTRA_TEXT, url);
+ send.putExtra(Intent.EXTRA_SUBJECT, title);
+ send.putExtra(Browser.EXTRA_SHARE_FAVICON, favicon);
+ send.putExtra(Browser.EXTRA_SHARE_SCREENSHOT, screenshot);
+ try {
+ c.startActivity(Intent.createChooser(send, c.getString(
+ R.string.choosertitle_sharevia)));
+ } catch(android.content.ActivityNotFoundException ex) {
+ // if no app handles it, do nothing
+ }
+ }
+
private void updateScreenshot(WebView view) {
// If this is a bookmarked site, add a screenshot to the database.
// FIXME: When should we update? Every time?
return null;
}
Bitmap bm = Bitmap.createBitmap(getDesiredThumbnailWidth(this),
- getDesiredThumbnailHeight(this), Bitmap.Config.ARGB_4444);
+ getDesiredThumbnailHeight(this), Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bm);
// May need to tweak these values to determine what is the
// best scale factor
/* package */ final static String SCHEME_WTAI_SD = "wtai://wp/sd;";
/* package */ final static String SCHEME_WTAI_AP = "wtai://wp/ap;";
+ // Keep this initial progress in sync with initialProgressValue (* 100)
+ // in ProgressTracker.cpp
+ private final static int INITIAL_PROGRESS = 10;
+
void onPageStarted(WebView view, String url, Bitmap favicon) {
// when BrowserActivity just starts, onPageStarted may be called before
// onResume as it is triggered from onCreate. Call resumeWebViewTimers
resetLockIcon(url);
setUrlTitle(url, null);
setFavicon(favicon);
- // Keep this initial progress in sync with initialProgressValue (* 100)
- // in ProgressTracker.cpp
// Show some progress so that the user knows the page is beginning to
// load
- onProgressChanged(view, 10);
+ onProgressChanged(view, INITIAL_PROGRESS);
mDidStopLoad = false;
if (!mIsNetworkUp) createAndShowNetworkDialog();
// -------------------------------------------------------------------------
void onProgressChanged(WebView view, int newProgress) {
- mTitleBar.setProgress(newProgress);
mFakeTitleBar.setProgress(newProgress);
if (newProgress == 100) {
hideFakeTitleBar();
}
}
- } else if (!mInLoad) {
- // onPageFinished may have already been called but a subframe is
- // still loading and updating the progress. Reset mInLoad and update
- // the menu items.
- mInLoad = true;
- updateInLoadMenuItems();
+ } else {
+ if (!mInLoad) {
+ // onPageFinished may have already been called but a subframe is
+ // still loading and updating the progress. Reset mInLoad and
+ // update the menu items.
+ mInLoad = true;
+ updateInLoadMenuItems();
+ }
+ // When the page first begins to load, the Activity may still be
+ // paused, in which case showFakeTitleBar will do nothing. Call
+ // again as the page continues to load so that it will be shown.
+ // (Calling it will the fake title bar is already showing will also
+ // do nothing.
if (!mOptionsMenuOpen || mIconView) {
// This page has begun to load, so show the title bar
showFakeTitleBar();
onDownloadStartNoStream(url, userAgent, contentDisposition, mimetype, contentLength);
}
+ // This is to work around the fact that java.net.URI throws Exceptions
+ // instead of just encoding URL's properly
+ // Helper method for onDownloadStartNoStream
+ private static String encodePath(String path) {
+ char[] chars = path.toCharArray();
+
+ boolean needed = false;
+ for (char c : chars) {
+ if (c == '[' || c == ']') {
+ needed = true;
+ break;
+ }
+ }
+ if (needed == false) {
+ return path;
+ }
+
+ StringBuilder sb = new StringBuilder("");
+ for (char c : chars) {
+ if (c == '[' || c == ']') {
+ sb.append('%');
+ sb.append(Integer.toHexString(c));
+ } else {
+ sb.append(c);
+ }
+ }
+
+ return sb.toString();
+ }
+
/**
* Notify the host application a download should be done, even if there
* is a streaming viewer available for thise type.
return;
}
- // java.net.URI is a lot stricter than KURL so we have to undo
- // KURL's percent-encoding and redo the encoding using java.net.URI.
- URI uri = null;
+ // java.net.URI is a lot stricter than KURL so we have to encode some
+ // extra characters. Fix for b 2538060 and b 1634719
+ WebAddress webAddress;
try {
- // Undo the percent-encoding that KURL may have done.
- String newUrl = new String(URLUtil.decode(url.getBytes()));
- // Parse the url into pieces
- WebAddress w = new WebAddress(newUrl);
- String frag = null;
- String query = null;
- String path = w.mPath;
- // Break the path into path, query, and fragment
- if (path.length() > 0) {
- // Strip the fragment
- int idx = path.lastIndexOf('#');
- if (idx != -1) {
- frag = path.substring(idx + 1);
- path = path.substring(0, idx);
- }
- idx = path.lastIndexOf('?');
- if (idx != -1) {
- query = path.substring(idx + 1);
- path = path.substring(0, idx);
- }
- }
- uri = new URI(w.mScheme, w.mAuthInfo, w.mHost, w.mPort, path,
- query, frag);
+ webAddress = new WebAddress(url);
+ webAddress.mPath = encodePath(webAddress.mPath);
} catch (Exception e) {
- Log.e(LOGTAG, "Could not parse url for download: " + url, e);
+ // This only happens for very bad urls, we want to chatch the
+ // exception here
+ Log.e(LOGTAG, "Exception trying to parse url:" + url);
return;
}
String cookies = CookieManager.getInstance().getCookie(url);
ContentValues values = new ContentValues();
- 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,
+ values.put(Downloads.Impl.COLUMN_URI, webAddress.toString());
+ values.put(Downloads.Impl.COLUMN_COOKIE_DATA, cookies);
+ values.put(Downloads.Impl.COLUMN_USER_AGENT, userAgent);
+ values.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
getPackageName());
- values.put(Downloads.COLUMN_NOTIFICATION_CLASS,
+ values.put(Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
BrowserDownloadPage.class.getCanonicalName());
- 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());
+ values.put(Downloads.Impl.COLUMN_VISIBILITY,
+ Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+ values.put(Downloads.Impl.COLUMN_MIME_TYPE, mimetype);
+ values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT, filename);
+ values.put(Downloads.Impl.COLUMN_DESCRIPTION, webAddress.mHost);
if (contentLength > 0) {
- values.put(Downloads.COLUMN_TOTAL_BYTES, contentLength);
+ values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, contentLength);
}
if (mimetype == null) {
// We must have long pressed on a link or image to download it. We
new FetchUrlMimeType(this).execute(values);
} else {
final Uri contentUri =
- getContentResolver().insert(Downloads.CONTENT_URI, values);
- viewDownloads(contentUri);
+ getContentResolver().insert(Downloads.Impl.CONTENT_URI, values);
}
-
+ Toast.makeText(this, R.string.download_pending, Toast.LENGTH_SHORT)
+ .show();
}
// -------------------------------------------------------------------------
}
// issued on:
- String issuedOn = reformatCertificateDate(
- certificate.getValidNotBefore());
+ String issuedOn = formatCertificateDate(
+ certificate.getValidNotBeforeDate());
((TextView) certificateView.findViewById(R.id.issued_on))
.setText(issuedOn);
// expires on:
- String expiresOn = reformatCertificateDate(
- certificate.getValidNotAfter());
+ String expiresOn = formatCertificateDate(
+ certificate.getValidNotAfterDate());
((TextView) certificateView.findViewById(R.id.expires_on))
.setText(expiresOn);
}
/**
- * Re-formats the certificate date (Date.toString()) string to
- * a properly localized date string.
+ * Formats the certificate date to a properly localized date string.
* @return Properly localized version of the certificate date string and
- * the original certificate date string if fails to localize.
- * If the original string is null, returns an empty string "".
+ * the "" if it fails to localize.
*/
- private String reformatCertificateDate(String certificateDate) {
- String reformattedDate = null;
-
- if (certificateDate != null) {
- Date date = null;
- try {
- date = java.text.DateFormat.getInstance().parse(certificateDate);
- } catch (ParseException e) {
- date = null;
- }
-
- if (date != null) {
- reformattedDate =
- DateFormat.getDateFormat(this).format(date);
- }
+ private String formatCertificateDate(Date certificateDate) {
+ if (certificateDate == null) {
+ return "";
}
-
- return reformattedDate != null ? reformattedDate :
- (certificateDate != null ? certificateDate : "");
+ String formattedDate = DateFormat.getDateFormat(this).format(certificateDate);
+ if (formattedDate == null) {
+ return "";
+ }
+ return formattedDate;
}
/**
public void setHttpAuthUsernamePassword(String host, String realm,
String username,
String password) {
- WebView w = mTabControl.getCurrentWebView();
+ WebView w = getTopWindow();
if (w != null) {
w.setHttpAuthUsernamePassword(host, realm, username, password);
}
mTabControl.getCurrentTab();
dismissSubWindow(currentTab);
if (data != null && data.length() != 0) {
- getTopWindow().loadUrl(data);
+ loadUrl(getTopWindow(), data);
}
}
}
/*
* This method is called as a result of the user selecting the options
- * menu to see the download window, or when a download changes state. It
- * shows the download window ontop of the current window.
+ * menu to see the download window. It shows the download window on top of
+ * the current window.
*/
- /* package */ void viewDownloads(Uri downloadRecord) {
+ private void viewDownloads(Uri downloadRecord) {
Intent intent = new Intent(this,
BrowserDownloadPage.class);
intent.setData(downloadRecord);
}
// Called when loading from context menu or LOAD_URL message
- private void loadURL(WebView view, String url) {
+ private void loadUrlFromContext(WebView view, String url) {
// In case the user enters nothing.
if (url != null && url.length() != 0 && view != null) {
url = smartUrlFilter(url);
if (!view.getWebViewClient().shouldOverrideUrlLoading(view, url)) {
- view.loadUrl(url);
+ loadUrl(view, url);
}
}
}
+ /**
+ * Load the URL into the given WebView and update the title bar
+ * to reflect the new load. Call this instead of WebView.loadUrl
+ * directly.
+ * @param view The WebView used to load url.
+ * @param url The URL to load.
+ */
+ private void loadUrl(WebView view, String url) {
+ updateTitleBarForNewLoad(view, url);
+ view.loadUrl(url);
+ }
+
+ /**
+ * Load UrlData into a Tab and update the title bar to reflect the new
+ * load. Call this instead of UrlData.loadIn directly.
+ * @param t The Tab used to load.
+ * @param data The UrlData being loaded.
+ */
+ private void loadUrlDataIn(Tab t, UrlData data) {
+ updateTitleBarForNewLoad(t.getWebView(), data.mUrl);
+ data.loadIn(t);
+ }
+
+ /**
+ * If the WebView is the top window, update the title bar to reflect
+ * loading the new URL. i.e. set its text, clear the favicon (which
+ * will be set once the page begins loading), and set the progress to
+ * INITIAL_PROGRESS to show that the page has begun to load. Called
+ * by loadUrl and loadUrlDataIn.
+ * @param view The WebView that is starting a load.
+ * @param url The URL that is being loaded.
+ */
+ private void updateTitleBarForNewLoad(WebView view, String url) {
+ if (view == getTopWindow()) {
+ setUrlTitle(url, null);
+ setFavicon(null);
+ onProgressChanged(view, INITIAL_PROGRESS);
+ }
+ }
+
private String smartUrlFilter(Uri inUri) {
if (inUri != null) {
return smartUrlFilter(inUri.toString());
// Now we can add it to the main view.
mErrorConsoleContainer.addView(errorConsole,
- new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
+ new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
} else {
mErrorConsoleContainer.removeView(errorConsole);
getWindow().setFlags(flag, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
+
+ private void sendNetworkType(String type, String subtype) {
+ WebView w = mTabControl.getCurrentWebView();
+ if (w != null) {
+ w.setNetworkType(type, subtype);
+ }
+ }
+
+ private void packageChanged(String packageName, boolean wasAdded) {
+ WebView w = mTabControl.getCurrentWebView();
+ if (w == null) {
+ return;
+ }
+
+ if (wasAdded) {
+ w.addPackageName(packageName);
+ } else {
+ w.removePackageName(packageName);
+ }
+ }
+
+ private void addPackageNames(Set<String> packageNames) {
+ WebView w = mTabControl.getCurrentWebView();
+ if (w == null) {
+ return;
+ }
+
+ w.addPackageNames(packageNames);
+ }
+
+ private void getInstalledPackages() {
+ AsyncTask<Void, Void, Set<String> > task =
+ new AsyncTask<Void, Void, Set<String> >() {
+ protected Set<String> doInBackground(Void... unused) {
+ Set<String> installedPackages = new HashSet<String>();
+ PackageManager pm = BrowserActivity.this.getPackageManager();
+ if (pm != null) {
+ List<PackageInfo> packages = pm.getInstalledPackages(0);
+ for (PackageInfo p : packages) {
+ if (BrowserActivity.this.sGoogleApps.contains(p.packageName)) {
+ installedPackages.add(p.packageName);
+ }
+ }
+ }
+
+ return installedPackages;
+ }
+
+ // Executes on the UI thread
+ protected void onPostExecute(Set<String> installedPackages) {
+ addPackageNames(installedPackages);
+ }
+ };
+ task.execute();
+ }
+
final static int LOCK_ICON_UNSECURE = 0;
final static int LOCK_ICON_SECURE = 1;
final static int LOCK_ICON_MIXED = 2;
private boolean mIsNetworkUp;
private boolean mDidStopLoad;
- private boolean mActivityInPause = true;
+ /* package */ boolean mActivityInPause = true;
private boolean mMenuIsDown;
/* hold a ref so we can auto-cancel if necessary */
private AlertDialog mAlertDialog;
- // Wait for credentials before loading google.com
- private ProgressDialog mCredsDlg;
-
// The up-to-date URL and title (these can be different from those stored
// in WebView, since it takes some time for the information in WebView to
// get updated)
/*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS =
new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.FILL_PARENT);
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
/*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_GRAVITY_CENTER =
new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
Gravity.CENTER);
// Google search
final static String QuickSearch_G = "http://www.google.com/m?q=%s";
private BroadcastReceiver mPackageInstallationReceiver;
+ private SystemAllowGeolocationOrigins mSystemAllowGeolocationOrigins;
+
// activity requestCode
final static int COMBO_PAGE = 1;
final static int DOWNLOAD_PAGE = 2;
// the video progress view
private View mVideoProgressView;
+ // The Google packages we monitor for the navigator.isApplicationInstalled()
+ // API. Add as needed.
+ private static Set<String> sGoogleApps;
+ static {
+ sGoogleApps = new HashSet<String>();
+ sGoogleApps.add("com.google.android.youtube");
+ }
+
/**
* 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;
- byte[] mPostData;
+ /* package */ static class UrlData {
+ final String mUrl;
+ final Map<String, String> mHeaders;
+ final Intent mVoiceIntent;
UrlData(String url) {
this.mUrl = url;
+ this.mHeaders = null;
+ this.mVoiceIntent = null;
}
- void setPostData(byte[] postData) {
- mPostData = postData;
+ UrlData(String url, Map<String, String> headers, Intent intent) {
+ this.mUrl = url;
+ this.mHeaders = headers;
+ if (RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS
+ .equals(intent.getAction())) {
+ this.mVoiceIntent = intent;
+ } else {
+ this.mVoiceIntent = null;
+ }
}
boolean isEmpty() {
- return mUrl == null || mUrl.length() == 0;
+ return mVoiceIntent == null && (mUrl == null || mUrl.length() == 0);
}
- public void loadIn(WebView webView) {
- if (mPostData != null) {
- webView.postUrl(mUrl, mPostData);
+ /**
+ * Load this UrlData into the given Tab. Use loadUrlDataIn to update
+ * the title bar as well.
+ */
+ public void loadIn(Tab t) {
+ if (mVoiceIntent != null) {
+ t.activateVoiceSearchMode(mVoiceIntent);
} else {
- webView.loadUrl(mUrl);
+ t.getWebView().loadUrl(mUrl, mHeaders);
}
}
};
- /**
- * 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;
- @Override
- boolean isEmpty() {
- return mInlined == null || mInlined.length() == 0 || super.isEmpty();
- }
-
- @Override
- public void loadIn(WebView webView) {
- webView.loadDataWithBaseURL(null, mInlined, mMimeType, mEncoding, mUrl);
- }
- }
-
/* package */ static final UrlData EMPTY_URL_DATA = new UrlData(null);
}