Porting 1847 and 1599-qrd browser changes
---
Fix issue with exit dialog showing on downloads
The dialog was only shown when the download was initiated from
outside the browser. Added a property to Tab to identify
it was created from an intent and handle closing it appropriately.
Change-Id: I83d342571f3fbf74223e248dd6bb6a8660bca96b
---
Memory monitor not deleting tabs when switching tabs.
Fixed bug where tab.destroy() was called only when new tab is
created. Fix was to check for stale tab when setActiveTab is
called and destroy webview for it.
Change-Id: Ibcb6431039ac97fcf21aaab4ace0dfb27f4227dc
---
Fix content disposition having multiple attributes
Change-Id: If04e930f16243dd1d4ee0acec3a31dfff971ea78
---
Fix yahoo search url where url 'http://cn.search.yahoo.com' is
redirected to 'http://sg.search.yahoo.com/' and query string is
lost in redirection.
Change-Id: I326d15528ae3b7a1cd7d90ca7a454c3bfbddfdd2
---
Fix issue in Autofill delete button
Change-Id: I9dff2a21d976c3269455b2d112cc964980642f82
---
Smoother tab transition and miscellaneous UI fixes.
- Fixed white screen issue seen when render process
was killed during tab switches.
- Fixed jankiness with animation during tab switching.
- Fixed issue where cancelling a dialog was causing
the menu item to be disabled.
Change-Id: I099b7ed3fdd8e10142f5ce932192f9db19f9650f
---
Fixes crash view to be shown for background tabs
Change-Id: I128e165ecfa7fa44c1d98ba13c5b22719c12d2a7
---
Fix timing issue with tab animation
A max num counter is introduced to put a upper limit on time to wait
for the tab (SurfaceView) to be ready. This fixes issue seens on JB
Change-Id: I5519534bc2ea572ca7eb4bb3b1f27921315e9359
---
Allow copy URI for My Navigation page
Change-Id: Ic98a2ad426462df7adf748b15b7e2e70a00d382e
---
Enable WLAN checks.
- Enable WLAN access point selection if WLAN is enabled
and access point is available.
- Prompt user to enable mobile data when WLAN is enabled
access point is not available.
Change-Id: I97e0a0053b07357ee438c0d48d0adfa8abbd80a2
---
Introspect mimetype for octect stream mimetype based on file extension.
Change-Id: I2b3e039647cf60b595d36261cf2ee391207304bc
---
Fix crash when handler is null
Change-Id: I09afd1e8558a5e6473df5b44b5cf4e2584feefe9
---
Fix for JS interface required for overlay default home page
Modified how the JS interface is initialized for the default
home page and included it during the restore of default page.
Change-Id: I077dc8d56aeb6a1db6159dd8f2a1b812b4cd2c79
---
Multiple fixes to SWE.
- Fix showing added navigation correctly.
- Fix showing white screen when creating new tab.
Change-Id: I209f7f5a903a930f021f0a680547bba3dd74d597
---
Pause newly created tab when it is in background
- When a new tab is opened in background, we need to ensure that the
tab is properly paused.
Change-Id: I45e1286de2a89d74581decf3c0c2a23655ae0c47
---
Fix for disappearing title bar caused by copy/paste control
Change-Id: I19860bd9356b2dc392539aa1e16819545e32de6d
---
Geolocation improvements
- Provide users the option to make geolocation permission choices that are
only good for a day.
- Add a clickable icon to indicate that a site has permission to request
Geolocation information. Selecting the icon allows the user to edit/clear
the geolocation policy for the current origin
- Enhanced preferences under Settings > Advanced > Website settings to allow
users to modify/clear the per origin geolocation policy
- Disable importing approved origins from
/data/data/com.android.providers.settings/databases/settings.db
Change-Id: Iafe0ea63f487e06bffecea4fbfc6db2b7b0b85c6
---
Add DuckDuckGo to list of search engines
- User can now switch to DuckDuckGo from Settings->Advanced->Set search engine.
Change-Id: I2ef5624d1eea7bd257c6619cc65b057ed865b5df
---
Capture thumbnail when tab is paused
- Fixes blank tab thumbnail when a tab is opened via adb comman
Change-Id: I817d65575c88c37e6ac5828ea8e561dae6a14ba6
---
Fix for tab thumbnail preview cropped in landscape orientation
Change-Id: Id7c14ddad2e465a9057d6cc85710b1f0dc660739
---
Change menu id for automation test
Change-Id: Ic089b303d39f8677debeae292b8fd2bad096baef
---
Do Not Track (Browser)
- Browser modifications to set the Do Not Track privacy option.
Change-Id: I262b7a05d43b39837cb84eb79f5844d14277e3fd
---
Change-Id: Icc95bdf4bd8d2bd3e3282f77d7f6a88232b59cda
diff --git a/src/com/android/browser/AutoFillSettingsFragment.java b/src/com/android/browser/AutoFillSettingsFragment.java
index e87cb89..cc77102 100644
--- a/src/com/android/browser/AutoFillSettingsFragment.java
+++ b/src/com/android/browser/AutoFillSettingsFragment.java
@@ -56,6 +56,7 @@
private EditText mPhoneEdit;
private MenuItem mSaveMenuItem;
+ private MenuItem mDeleteMenuItem;
private boolean mInitialised;
@@ -92,6 +93,7 @@
}
updateSaveMenuItemState();
+ updateDeleteMenuItemState();
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -104,6 +106,7 @@
private class FieldChangedListener implements TextWatcher {
public void afterTextChanged(Editable s) {
updateSaveMenuItemState();
+ updateDeleteMenuItemState();
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -152,7 +155,9 @@
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.autofill_profile_editor, menu);
mSaveMenuItem = menu.findItem(R.id.autofill_profile_editor_save_profile_menu_id);
+ mDeleteMenuItem = menu.findItem(R.id.autofill_profile_editor_delete_profile_menu_id);
updateSaveMenuItemState();
+ updateDeleteMenuItemState();
}
@Override
@@ -176,6 +181,7 @@
mSettings.updateAutoFillProfile(null);
updateSaveMenuItemState();
+ updateDeleteMenuItemState();
return true;
case R.id.autofill_profile_editor_save_profile_menu_id:
@@ -248,10 +254,38 @@
mInitialised = true;
updateSaveMenuItemState();
+ updateDeleteMenuItemState();
return v;
}
+ private void updateDeleteMenuItemState() {
+ if (mDeleteMenuItem == null) {
+ return;
+ }
+
+ if (!mInitialised) {
+ mDeleteMenuItem.setEnabled(false);
+ return;
+ }
+
+ boolean currentState = mDeleteMenuItem.isEnabled();
+ boolean newState = (mFullNameEdit.getText().toString().length() > 0 ||
+ mEmailEdit.getText().toString().length() > 0 ||
+ mCompanyEdit.getText().toString().length() > 0 ||
+ mAddressLine1Edit.getText().toString().length() > 0 ||
+ mAddressLine2Edit.getText().toString().length() > 0 ||
+ mCityEdit.getText().toString().length() > 0 ||
+ mStateEdit.getText().toString().length() > 0 ||
+ mZipEdit.getText().toString().length() > 0 ||
+ mCountryEdit.getText().toString().length() > 0) &&
+ mPhoneEdit.getError() == null;
+
+ if (currentState != newState) {
+ mDeleteMenuItem.setEnabled(newState);
+ }
+ }
+
private void updateSaveMenuItemState() {
if (mSaveMenuItem == null) {
return;
diff --git a/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java
index 7c166aa..27b2b82 100644
--- a/src/com/android/browser/BaseUi.java
+++ b/src/com/android/browser/BaseUi.java
@@ -313,11 +313,10 @@
Runnable mRunnable = null;
protected void scheduleRemoveTab(Tab tabToRemove, Tab tabToWaitFor) {
- android.os.Handler handler = mTitleBar.getHandler();
//remove previously scehduled tab
if (mTabToRemove != null) {
if (mRunnable != null)
- handler.removeCallbacks(mRunnable);
+ mTitleBar.removeCallbacks(mRunnable);
removeTabFromContentView(mTabToRemove);
mTabToRemove.performPostponedDestroy();
mRunnable = null;
@@ -334,11 +333,10 @@
protected void tryRemoveTab() {
mNumRemoveTries++;
- android.os.Handler handler = mTitleBar.getHandler();
// Ensure the webview is still valid
if (mNumRemoveTries < 20 && mTabToWaitFor.getWebView() != null) {
if (!mTabToWaitFor.getWebView().isReady()) {
- if (mRunnable != null) {
+ if (mRunnable == null) {
mRunnable = new Runnable() {
public void run() {
tryRemoveTab();
@@ -348,13 +346,13 @@
/*if the new tab is still not ready, wait another 2 frames
before trying again. 1 frame for the tab to render the first
frame, another 1 frame to make sure the swap is done*/
- handler.postDelayed(mRunnable, 33);
+ mTitleBar.postDelayed(mRunnable, 33);
return;
}
}
if (mTabToRemove != null) {
if (mRunnable != null)
- handler.removeCallbacks(mRunnable);
+ mTitleBar.removeCallbacks(mRunnable);
removeTabFromContentView(mTabToRemove);
mTabToRemove.performPostponedDestroy();
mRunnable = null;
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 25dcd02..1e2e84a 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -957,6 +957,10 @@
return mPrefs.getBoolean(PREF_SHOW_SECURITY_WARNINGS, true);
}
+ public boolean doNotTrack() {
+ return mPrefs.getBoolean(PREF_DO_NOT_TRACK, true);
+ }
+
public boolean acceptCookies() {
return mPrefs.getBoolean(PREF_ACCEPT_COOKIES, true);
}
diff --git a/src/com/android/browser/BrowserYesNoPreference.java b/src/com/android/browser/BrowserYesNoPreference.java
index 4bb3c9c..e8d6af9 100644
--- a/src/com/android/browser/BrowserYesNoPreference.java
+++ b/src/com/android/browser/BrowserYesNoPreference.java
@@ -33,10 +33,9 @@
super.onDialogClosed(positiveResult);
if (callChangeListener(positiveResult)) {
- setEnabled(false);
if (!positiveResult)
return;
-
+ setEnabled(false);
BrowserSettings settings = BrowserSettings.getInstance();
if (PreferenceKeys.PREF_PRIVACY_CLEAR_CACHE.equals(getKey())) {
settings.clearCache();
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index 012191e..8bd85f6 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -195,10 +195,6 @@
private boolean mShouldShowErrorConsole;
private boolean mNetworkShouldNotify = true;
- private boolean mJsInterfaceEnabled = false;
-
- private SystemAllowGeolocationOrigins mSystemAllowGeolocationOrigins;
-
// FIXME, temp address onPrepareMenu performance problem.
// When we move everything out of view, we should rewrite this.
private int mCurrentMenuState = 0;
@@ -276,11 +272,6 @@
BrowserContract.Bookmarks.CONTENT_URI, true, mBookmarksObserver);
mNetworkHandler = new NetworkStateHandler(mActivity, this);
- // Start watching the default geolocation permissions
- mSystemAllowGeolocationOrigins =
- new SystemAllowGeolocationOrigins(mActivity.getApplicationContext());
- mSystemAllowGeolocationOrigins.start();
-
openIconDatabase();
}
@@ -343,6 +334,7 @@
}
} else {
t = openTab(urlData);
+ t.setDerivedFromIntent(true);
}
if (t != null) {
t.setAppId(intent.getStringExtra(Browser.EXTRA_APPLICATION_ID));
@@ -363,6 +355,9 @@
ArrayList<Long> restoredTabs = new ArrayList<Long>(tabs.size());
for (Tab t : tabs) {
restoredTabs.add(t.getId());
+ if (t != mTabControl.getCurrentTab()) {
+ t.pause();
+ }
}
BackgroundHandler.execute(new PruneThumbnails(mActivity, restoredTabs));
if (tabs.size() == 0) {
@@ -818,9 +813,6 @@
// Destroy all the tabs
mTabControl.destroy();
WebIconDatabase.getInstance().close();
- // Stop watching the default geolocation permissions
- mSystemAllowGeolocationOrigins.stop();
- mSystemAllowGeolocationOrigins = null;
}
protected boolean isActivityPaused() {
@@ -871,45 +863,42 @@
R.string.def_wifi_browser_interaction_remind_type);
final String selectionConnnection = getContext().getResources().getString(
R.string.def_action_wifi_selection_data_connections);
+ final String wifiSelection = getContext().getResources().getString(
+ R.string.def_intent_pick_network);
- if (reminderType.isEmpty() || selectionConnnection.isEmpty())
+ if (reminderType.isEmpty() || selectionConnnection.isEmpty() ||
+ wifiSelection.isEmpty())
return;
ConnectivityManager conMgr = (ConnectivityManager) this.getContext().getSystemService(
Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = conMgr.getActiveNetworkInfo();
+ WifiManager wifiMgr = (WifiManager) this.getContext()
+ .getSystemService(Context.WIFI_SERVICE);
if (networkInfo == null
|| (networkInfo != null && (networkInfo.getType() !=
ConnectivityManager.TYPE_WIFI))) {
int isReminder = Settings.System.getInt(mActivity.getContentResolver(),
reminderType, NETWORK_SWITCH_TYPE_OK);
- if (isReminder == NETWORK_SWITCH_TYPE_OK) {
- mNetworkShouldNotify = false;
+ List<ScanResult> list = wifiMgr.getScanResults();
+ // Have no AP's for Wifi's fall back to data
+ if (list != null && list.size() == 0 && isReminder == NETWORK_SWITCH_TYPE_OK) {
Intent intent = new Intent(selectionConnnection);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.getContext().startActivity(intent);
} else {
- if (!mNetworkHandler.isNetworkUp())
- view.setNetworkAvailable(false);
+ // Request to select Wifi AP
+ try {
+ Intent intent = new Intent(wifiSelection);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ this.getContext().startActivity(intent);
+ } catch (Exception e) {
+ String err_msg = this.getContext().getString(
+ R.string.acivity_not_found, wifiSelection);
+ Toast.makeText(this.getContext(), err_msg, Toast.LENGTH_LONG).show();
+ }
}
- }
- }
-
- /***
- * Add/remove a Javascript interface for a local default homepage only
- */
- private void handleJsInterface(WebView webview){
- if (webview.getUrl() != null &&
- webview.getUrl().equals(mActivity.getResources().getString(R.string.homepage_base)) &&
- webview.getUrl().startsWith("file:///")) {
- mJsInterfaceEnabled = true;
- webview.getSettings().setJavaScriptEnabled(true);
- webview.addJavascriptInterface(mActivity, "default_homepage");
- } else {
- if (mJsInterfaceEnabled) {
- webview.removeJavascriptInterface("default_homepage");
- mJsInterfaceEnabled = false;
- }
+ mNetworkShouldNotify = false;
}
}
@@ -927,10 +916,10 @@
// reset sync timer to avoid sync starts during loading a page
CookieSyncManager.getInstance().resetSync();
WifiManager wifiMgr = (WifiManager) this.getContext()
- .getSystemService(Context.WIFI_SERVICE);
+ .getSystemService(Context.WIFI_SERVICE);
boolean networkNotifier =
mActivity.getApplicationContext().getResources().getBoolean(R.bool.network_notifier);
- if (networkNotifier && mNetworkShouldNotify && wifiMgr.isWifiEnabled()) {
+ if (networkNotifier && mNetworkShouldNotify && wifiMgr.isWifiEnabled()){
handleNetworkNotify(view);
} else {
if (!mNetworkHandler.isNetworkUp()) {
@@ -981,7 +970,6 @@
int newProgress = tab.getLoadProgress();
if (newProgress == 100) {
- handleJsInterface(tab.getWebView());
CookieSyncManager.getInstance().sync();
// onProgressChanged() may continue to be called after the main
// frame has finished loading, as any remaining sub frames continue
@@ -1188,7 +1176,10 @@
// file. Remove it.
if (tab == mTabControl.getCurrentTab()) {
// In this case, the Tab is still on top.
- goBackOnePageOrQuit();
+ if (tab.getDerivedFromIntent())
+ closeTab(tab);
+ else
+ goBackOnePageOrQuit();
} else {
// In this case, it is not.
closeTab(tab);
@@ -2709,6 +2700,8 @@
// the tab is guaranteed to have a webview after setCurrentTab
mUi.setActiveTab(tab);
tab.setTimeStamp();
+ //Purge active tabs
+ MemoryMonitor.purgeActiveTabs(mActivity.getApplicationContext(), this, mSettings);
}
}
@@ -2854,23 +2847,13 @@
private Tab createNewTab(boolean incognito, boolean setActive,
boolean useCurrent) {
Tab tab = null;
- MemoryMonitor memMonitor = null;
if (mTabControl.canCreateNewTab()) {
- if (mSettings.enableMemoryMonitor()) {
- Log.d(LOGTAG, " Memory Monitor Enabled .");
- memMonitor = MemoryMonitor.getInstance(mActivity.getApplicationContext(),this);
- if (memMonitor != null) {
- //Remove webview associated with the oldest tab
- memMonitor.destroyLeastRecentlyActiveTab();
- }
- } else {
- Log.d(LOGTAG, " Memory Monitor disabled .");
- }
tab = mTabControl.createNewTab(incognito);
addTab(tab);
- tab.setTimeStamp();
if (setActive) {
setActiveTab(tab);
+ } else {
+ tab.pause();
}
} else {
if (useCurrent) {
diff --git a/src/com/android/browser/DownloadHandler.java b/src/com/android/browser/DownloadHandler.java
index 88e6679..f42ee76 100644
--- a/src/com/android/browser/DownloadHandler.java
+++ b/src/com/android/browser/DownloadHandler.java
@@ -41,6 +41,9 @@
import com.android.browser.platformsupport.WebAddress;
import com.android.browser.reflect.ReflectHelper;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
import java.io.File;
/**
* Handle download requests
@@ -184,7 +187,8 @@
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(url), mimetype);
try {
- String title = URLUtil.guessFileName(url, contentDisposition, mimetype);
+ String trimmedcontentDisposition = trimContentDisposition(contentDisposition);
+ String title = URLUtil.guessFileName(url, trimmedcontentDisposition, mimetype);
intent.putExtra(Intent.EXTRA_TITLE, title);
activity.startActivity(intent);
} catch (ActivityNotFoundException ex) {
@@ -278,6 +282,9 @@
String mimetype, String referer, boolean privateBrowsing, long contentLength) {
initStorageDefaultPath(activity);
+
+ contentDisposition = trimContentDisposition(contentDisposition);
+
String filename = URLUtil.guessFileName(url,
contentDisposition, mimetype);
@@ -317,6 +324,27 @@
}
+ static String trimContentDisposition(String contentDisposition) {
+ final Pattern CONTENT_DISPOSITION_PATTERN =
+ Pattern.compile("attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*;",
+ Pattern.CASE_INSENSITIVE);
+
+ if (contentDisposition != null) {
+
+ try {
+ Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
+ if (m.find()) {
+ return m.group();
+ } else {
+ return contentDisposition;
+ }
+ } catch (IllegalStateException ex) {
+ // This function is defined as returning null when it can't parse the header
+ }
+ }
+ return null;
+ }
+
public static void initStorageDefaultPath(Context context) {
mExternalStorage = getExternalStorageDirectory(context);
if (isPhoneStorageSupported()) {
diff --git a/src/com/android/browser/DownloadSettings.java b/src/com/android/browser/DownloadSettings.java
index cca26cc..4c7c829 100644
--- a/src/com/android/browser/DownloadSettings.java
+++ b/src/com/android/browser/DownloadSettings.java
@@ -126,7 +126,9 @@
String filenameExtension = DownloadHandler.getFilenameExtension(filename);
- if (mimetype == null || mimetype.isEmpty()) {
+ // introspect for octet stream mimetype what type of file extension it has
+ // and reassign mimetype
+ if (mimetype == null || mimetype.isEmpty() || mimetype.equals(OCTET_STREAM)) {
String updatedFileName = filenameBase + "." + filenameExtension;
Object[] params = {updatedFileName};
diff --git a/src/com/android/browser/GeolocationPermissionsPrompt.java b/src/com/android/browser/GeolocationPermissionsPrompt.java
index 127107f..d0e94ad 100755
--- a/src/com/android/browser/GeolocationPermissionsPrompt.java
+++ b/src/com/android/browser/GeolocationPermissionsPrompt.java
@@ -18,14 +18,17 @@
import com.android.browser.R;
+import org.codeaurora.swe.GeolocationPermissions;
+import org.json.JSONArray;
+
import android.content.Context;
import android.net.Uri;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
-import android.webkit.GeolocationPermissions;
import android.widget.Button;
import android.widget.CheckBox;
+import android.widget.CompoundButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
@@ -33,11 +36,14 @@
public class GeolocationPermissionsPrompt extends RelativeLayout {
private TextView mMessage;
private Button mShareButton;
+ private Button mShareForLimitedTimeButton;
private Button mDontShareButton;
private CheckBox mRemember;
- private GeolocationPermissions.Callback mCallback;
+ private android.webkit.GeolocationPermissions.Callback mCallback;
private String mOrigin;
+ private static final long MILLIS_PER_DAY = 86400000;
+
public GeolocationPermissionsPrompt(Context context) {
this(context, null);
}
@@ -55,17 +61,30 @@
private void init() {
mMessage = (TextView) findViewById(R.id.message);
mShareButton = (Button) findViewById(R.id.share_button);
+ mShareForLimitedTimeButton = (Button)
+ findViewById(R.id.share_for_limited_time_button);
mDontShareButton = (Button) findViewById(R.id.dont_share_button);
mRemember = (CheckBox) findViewById(R.id.remember);
+ mRemember.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ mShareForLimitedTimeButton.setEnabled(isChecked);
+ }
+ });
+
mShareButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
- handleButtonClick(true);
+ handleButtonClick(true, GeolocationPermissions.DO_NOT_EXPIRE);
+ }
+ });
+ mShareForLimitedTimeButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ handleButtonClick(true, System.currentTimeMillis() + MILLIS_PER_DAY);
}
});
mDontShareButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
- handleButtonClick(false);
+ handleButtonClick(false, GeolocationPermissions.DO_NOT_EXPIRE);
}
});
}
@@ -74,7 +93,8 @@
* Shows the prompt for the given origin. When the user clicks on one of
* the buttons, the supplied callback is be called.
*/
- public void show(String origin, GeolocationPermissions.Callback callback) {
+ public void show(String origin,
+ android.webkit.GeolocationPermissions.Callback callback) {
mOrigin = origin;
mCallback = callback;
Uri uri = Uri.parse(mOrigin);
@@ -94,7 +114,7 @@
/**
* Handles a click on one the buttons by invoking the callback.
*/
- private void handleButtonClick(boolean allow) {
+ private void handleButtonClick(boolean allow, long expirationTime) {
hide();
boolean remember = mRemember.isChecked();
@@ -108,7 +128,11 @@
toast.show();
}
- mCallback.invoke(mOrigin, allow, remember);
+ // Encode the expirationTime and origin as a JSON string.
+ JSONArray jsonArray = new JSONArray();
+ jsonArray.put(expirationTime);
+ jsonArray.put(mOrigin);
+ mCallback.invoke(jsonArray.toString(), allow, remember);
}
/**
diff --git a/src/com/android/browser/IntentHandler.java b/src/com/android/browser/IntentHandler.java
index ec19246..f829ae1 100644
--- a/src/com/android/browser/IntentHandler.java
+++ b/src/com/android/browser/IntentHandler.java
@@ -109,6 +109,7 @@
if (intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false)
|| urlData.isPreloaded()) {
Tab t = mController.openTab(urlData);
+ t.setDerivedFromIntent(true);
return;
}
/*
@@ -123,7 +124,8 @@
if (!TextUtils.isEmpty(urlData.mUrl) &&
urlData.mUrl.startsWith("javascript:")) {
// Always open javascript: URIs in new tabs
- mController.openTab(urlData);
+ Tab jsTab = mController.openTab(urlData);
+ jsTab.setDerivedFromIntent(true);
return;
}
if (Intent.ACTION_VIEW.equals(action)
@@ -165,6 +167,7 @@
Tab tab = mController.openTab(urlData);
if (tab != null) {
tab.setAppId(appId);
+ tab.setDerivedFromIntent(true);
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
tab.setCloseOnBack(true);
}
diff --git a/src/com/android/browser/LocationButton.java b/src/com/android/browser/LocationButton.java
new file mode 100644
index 0000000..e805e43
--- /dev/null
+++ b/src/com/android/browser/LocationButton.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+package com.android.browser;
+
+import org.codeaurora.swe.GeolocationPermissions;
+import org.codeaurora.swe.GeolocationPermissions.OnGeolocationPolicyModifiedListener;
+import org.json.JSONArray;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.net.Uri;
+import android.util.AttributeSet;
+import android.view.View;
+import android.webkit.ValueCallback;
+import android.widget.ImageButton;
+
+public class LocationButton extends ImageButton
+ implements OnGeolocationPolicyModifiedListener {
+ private GeolocationPermissions mGeolocationPermissions;
+ private long mCurrentTabId;
+ private String mCurrentOrigin;
+ private boolean mCurrentIncognito;
+
+ private static final long MILLIS_PER_DAY = 86400000;
+
+ protected long geolocationPolicyExpiration;
+ protected boolean geolocationPolicyOriginAllowed;
+
+ public LocationButton(Context context) {
+ super(context);
+ }
+
+ public LocationButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ }
+
+ public LocationButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ init();
+ }
+
+ private void init() {
+ mGeolocationPermissions = GeolocationPermissions.getInstance();
+ mGeolocationPermissions.registerOnGeolocationPolicyModifiedListener(this);
+ mCurrentTabId = -1;
+ mCurrentOrigin = null;
+ mCurrentIncognito = false;
+
+ setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mCurrentOrigin.isEmpty()) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ final GeolocationPermissions geolocationPermissions =
+ (mCurrentIncognito ?
+ GeolocationPermissions.getIncognitoInstance() :
+ GeolocationPermissions.getInstance());
+
+ DialogInterface.OnClickListener alertDialogListener =
+ new AlertDialog.OnClickListener() {
+ public void onClick(DialogInterface dlg, int which) {
+ String origin = mCurrentOrigin;
+ int selectedPosition = ((AlertDialog)dlg)
+ .getListView().getCheckedItemPosition();
+ switch (selectedPosition) {
+ case 0: // Deny forever
+ geolocationPermissions.deny(origin);
+ break;
+ case 1: // Extend for 24 hours
+ // encode the expiration time and origin as a JSON string
+ JSONArray jsonArray = new JSONArray();
+ jsonArray.put(System.currentTimeMillis() + MILLIS_PER_DAY);
+ jsonArray.put(origin);
+ geolocationPermissions.allow(jsonArray.toString());
+ break;
+ case 2: // Allow forever
+ geolocationPermissions.allow(origin);
+ break;
+ case 3: // Always ask
+ geolocationPermissions.clear(origin);
+ break;
+ default:
+ break;
+ }
+ }};
+
+ builder.setTitle(String.format(getResources()
+ .getString(R.string.geolocation_settings_page_dialog_title),
+ "http".equals(Uri.parse(mCurrentOrigin).getScheme()) ?
+ mCurrentOrigin.substring(7) : mCurrentOrigin))
+ .setPositiveButton(R.string.geolocation_settings_page_dialog_ok_button,
+ alertDialogListener)
+ .setNegativeButton(R.string.geolocation_settings_page_dialog_cancel_button, null);
+
+ final ValueCallback<Long> getExpirationCallback = new ValueCallback<Long>() {
+ public void onReceiveValue(Long expirationTime) {
+ if (expirationTime != null) {
+ geolocationPolicyExpiration = expirationTime.longValue();
+ // Set radio button and location icon
+ if (!geolocationPolicyOriginAllowed) {
+ // 0: Deny forever
+ builder.setSingleChoiceItems(R.array.geolocation_settings_choices, 0, null);
+ } else {
+ if (geolocationPolicyExpiration
+ != GeolocationPermissions.DO_NOT_EXPIRE) {
+ // 1: Allow for 24 hours
+ builder.setSingleChoiceItems(R.array.geolocation_settings_choices, 1, null);
+ } else {
+ // 2: Allow forever
+ builder.setSingleChoiceItems(R.array.geolocation_settings_choices, 2, null);
+ }
+ }
+ }
+ builder.show();
+ }};
+
+ final ValueCallback<Boolean> getAllowedCallback = new ValueCallback<Boolean>() {
+ public void onReceiveValue(Boolean allowed) {
+ if (allowed != null) {
+ geolocationPolicyOriginAllowed = allowed.booleanValue();
+ //Get the policy expiration time
+ geolocationPermissions
+ .getExpirationTime(mCurrentOrigin, getExpirationCallback);
+ }
+ }};
+
+ geolocationPermissions.hasOrigin(mCurrentOrigin,
+ new ValueCallback<Boolean>() {
+ public void onReceiveValue(Boolean hasOrigin) {
+ if (hasOrigin != null && hasOrigin.booleanValue()) {
+ //Get whether origin is allowed or denied
+ geolocationPermissions.getAllowed(mCurrentOrigin,
+ getAllowedCallback);
+ }
+ }
+ });
+ }
+ }
+ });
+ }
+
+ public void onTabDataChanged(Tab tab) {
+ long tabId = tab.getId();
+ String origin = GeolocationPermissions.getOriginFromUrl(tab.getUrl());
+ boolean incognito = tab.isPrivateBrowsingEnabled();
+
+ if (mCurrentTabId != tabId) {
+ mCurrentTabId = tabId;
+ mCurrentOrigin = origin;
+
+ // Switch GeolocationPermissions if we went from a regular to an
+ // incognito tab or vice versa
+ if (mCurrentIncognito != incognito) {
+ mCurrentIncognito = incognito;
+ mGeolocationPermissions = mCurrentIncognito ?
+ GeolocationPermissions.getIncognitoInstance() :
+ GeolocationPermissions.getInstance();
+ mGeolocationPermissions.registerOnGeolocationPolicyModifiedListener(this);
+ }
+ update();
+ }
+ // Update icon if we are in the same tab and origin has changed
+ else if (!((mCurrentOrigin == null && origin == null) ||
+ (mCurrentOrigin != null && origin != null
+ && mCurrentOrigin.equals(origin)))) {
+ mCurrentOrigin = origin;
+ update();
+ }
+ }
+
+ public void update() {
+ if (mCurrentOrigin != null) {
+ mGeolocationPermissions.hasOrigin(mCurrentOrigin,
+ new ValueCallback<Boolean>() {
+ public void onReceiveValue(Boolean hasOrigin) {
+ if (hasOrigin != null && hasOrigin.booleanValue()) {
+ mGeolocationPermissions.getAllowed(mCurrentOrigin,
+ new ValueCallback<Boolean>() {
+ public void onReceiveValue(Boolean allowed) {
+ if (allowed != null) {
+ if (allowed.booleanValue()) {
+ LocationButton.this.setImageResource(R.drawable.ic_gps_on_holo_dark);
+ LocationButton.this.setVisibility(VISIBLE);
+ } else {
+ LocationButton.this.setImageResource(R.drawable.ic_gps_denied_holo_dark);
+ LocationButton.this.setVisibility(VISIBLE);
+ }
+ }
+ }
+ });
+ } else {
+ LocationButton.this.setVisibility(GONE);
+ }
+ }
+ });
+ } else {
+ this.setVisibility(GONE);
+ }
+ }
+
+ @Override
+ public void onGeolocationPolicyAdded(String origin, boolean allow) {
+ if (mCurrentOrigin != null && mCurrentOrigin.equals(origin)) {
+ this.setImageResource(allow ? R.drawable.ic_gps_on_holo_dark :
+ R.drawable.ic_gps_denied_holo_dark);
+ this.setVisibility(VISIBLE);
+ }
+ }
+
+ @Override
+ public void onGeolocationPolicyCleared(String origin) {
+ if (mCurrentOrigin != null && mCurrentOrigin.equals(origin)) {
+ this.setVisibility(GONE);
+ }
+ }
+
+ @Override
+ public void onGeolocationPolicyClearedAll() {
+ this.setVisibility(GONE);
+ }
+
+}
diff --git a/src/com/android/browser/MemoryMonitor.java b/src/com/android/browser/MemoryMonitor.java
index a18f698..36c714f 100644
--- a/src/com/android/browser/MemoryMonitor.java
+++ b/src/com/android/browser/MemoryMonitor.java
@@ -32,85 +32,48 @@
import android.app.ActivityManager;
import android.content.Context;
-import android.util.Log;
import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
public class MemoryMonitor {
- //This number is used with device memory class to calculate max number
- //of active tabs.
- private static int sMaxActiveTabs = 0;
- private static MemoryMonitor sMemoryMonitor;
- private TabControl mTabControl;
- private final static String LOGTAG = "MemoryMonitor";
-
- // Should be called only once
-
- public static MemoryMonitor getInstance(Context context,
- Controller controller) {
- if (sMemoryMonitor == null) {
- sMemoryMonitor = new MemoryMonitor(context,controller);
- }
- return sMemoryMonitor;
- }
-
- MemoryMonitor(Context context,Controller controller) {
- mTabControl = controller.getTabControl();
- sMaxActiveTabs = getMaxActiveTabs(context);
- Log.d(LOGTAG,"Max Active Tabs: "+ sMaxActiveTabs);
- }
-
- private int getActiveTabs() {
- int numNativeActiveTab = 0;
- int size = mTabControl.getTabCount();
-
- for (int i = 0; i < size; i++) {
- Tab tab = mTabControl.getTab(i);
- if (((Tab)tab).isNativeActive()){
- numNativeActiveTab++;
- }
- }
- return numNativeActiveTab;
- }
-
/**
* if number of tabs whose native tab is active, is greater
* than MAX_ACTIVE_TABS destroy the nativetab of oldest used Tab
*/
+ public static void purgeActiveTabs(Context context,
+ Controller controller,
+ BrowserSettings settings) {
+ if(!settings.enableMemoryMonitor())
+ return;
- public void destroyLeastRecentlyActiveTab() {
- int numActiveTabs = getActiveTabs();
- int numActiveTabsToRelease = numActiveTabs - sMaxActiveTabs;
+ int maxActiveTabs = getMaxActiveTabs(context);
+ TabControl tabControl = controller.getTabControl();
- // The most common case will be that we need to delete one
- // NativeTab to make room for a new one. So, find the most-stale.
- if (numActiveTabsToRelease == 1) {
- Tab mostStaleTab = null;
- for (Tab t : mTabControl.getTabs()) {
- if (t.isNativeActive() && !(t.inForeground())) {
- if (mostStaleTab == null){
- mostStaleTab = t;
- }
- else {
- if (t.getTimestamp().compareTo(mostStaleTab.
- getTimestamp()) < 0) {
- mostStaleTab = t;
- }
- }
- }
+ ArrayList<Tab> activeTabList = new ArrayList<Tab>();
+
+ for (int i = 0; i < tabControl.getTabCount(); i++) {
+ Tab tab = tabControl.getTab(i);
+ if(tab.isNativeActive())
+ activeTabList.add(tab);
+ }
+
+ int numActiveTabsToRelease = activeTabList.size() - maxActiveTabs;
+
+ if(numActiveTabsToRelease < 1)
+ return;
+ // sort tabs in order of LRU first
+ Collections.sort(activeTabList, new Comparator<Tab>() {
+ @Override
+ public int compare(Tab tab1, Tab tab2) {
+ return tab1.getTimestamp().compareTo(tab2.getTimestamp());
}
- if (mostStaleTab != null) {
- mostStaleTab.destroy();
- }
- } else if (numActiveTabsToRelease > 1) {
- // Since there is more than 1 "extra" tab, just release all
- // NativeTabs in the background. This would be true when
- // tracking was turned on after multiple tabs already exists
- for (Tab t : mTabControl.getTabs()) {
- if (t.isNativeActive() && !(t.inForeground())) {
- t.destroy();
- }
- }
+ });
+
+ for(int i = 0; i < numActiveTabsToRelease; i++) {
+ activeTabList.get(i).destroy();
}
}
@@ -118,7 +81,7 @@
* Returns the default max number of active tabs based on device's
* memory class.
*/
- static int getMaxActiveTabs(Context context) {
+ private static int getMaxActiveTabs(Context context) {
// We use device memory class to decide number of active tabs
// (minimum memory class is 16).
ActivityManager am =(ActivityManager)context.
diff --git a/src/com/android/browser/NavigationBarBase.java b/src/com/android/browser/NavigationBarBase.java
index da3f3da..100e8d7 100644
--- a/src/com/android/browser/NavigationBarBase.java
+++ b/src/com/android/browser/NavigationBarBase.java
@@ -53,10 +53,12 @@
protected TitleBar mTitleBar;
protected UiController mUiController;
protected UrlInputView mUrlInput;
+ protected LocationButton mLocationButton;
private ImageView mFavicon;
private ImageView mLockIcon;
+
public NavigationBarBase(Context context) {
super(context);
}
@@ -73,6 +75,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mLockIcon = (ImageView) findViewById(R.id.lock);
+ mLocationButton = (LocationButton) findViewById(R.id.location_button);
mFavicon = (ImageView) findViewById(R.id.favicon);
mUrlInput = (UrlInputView) findViewById(R.id.url);
mUrlInput.setUrlInputListener(this);
@@ -394,6 +397,7 @@
}
public void onTabDataChanged(Tab tab) {
+ mLocationButton.onTabDataChanged(tab);
}
public void onVoiceResult(String s) {
diff --git a/src/com/android/browser/NavigationBarPhone.java b/src/com/android/browser/NavigationBarPhone.java
index a5257d1..b6bd52c 100644
--- a/src/com/android/browser/NavigationBarPhone.java
+++ b/src/com/android/browser/NavigationBarPhone.java
@@ -78,7 +78,7 @@
mMagnify = (ImageView) findViewById(R.id.magnify);
mTabSwitcher = findViewById(R.id.tab_switcher);
mTabSwitcher.setOnClickListener(this);
- mMore = findViewById(R.id.more);
+ mMore = findViewById(R.id.more_browser_settings);
mMore.setOnClickListener(this);
mComboIcon = findViewById(R.id.iconcombo);
mComboIcon.setOnClickListener(this);
diff --git a/src/com/android/browser/PhoneUi.java b/src/com/android/browser/PhoneUi.java
index 17de464..d83d81d 100644
--- a/src/com/android/browser/PhoneUi.java
+++ b/src/com/android/browser/PhoneUi.java
@@ -25,6 +25,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.TypedValue;
@@ -57,6 +58,8 @@
boolean mAnimating;
boolean mShowNav = false;
+ static final int POST_DELAY = 300;
+
/**
* @param browser
* @param controller
@@ -243,6 +246,14 @@
@Override
public void onActionModeFinished(boolean inLoad) {
super.onActionModeFinished(inLoad);
+ mTitleBar.animate().translationY(0);
+ stopEditingUrl();
+ Handler handler = new Handler();
+ handler.postDelayed(new Runnable() {
+ public void run() {
+ mNavigationBar.onStateChanged(StateListener.STATE_NORMAL);
+ }}, POST_DELAY);
+
if (inLoad) {
if (mUseQuickControls) {
mTitleBar.setShowProgressOnly(true);
@@ -302,9 +313,6 @@
int toRight = toLeft + width;
int toBottom = toTop + height;
float scaleFactor = width / (float) mContentView.getWidth();
- // SWE: Detaching the active tab results flashing screen with SWE.
- // Not detaching the tab doesn't seem to have any issues.
- //detachTab(mActiveTab);
mContentView.setVisibility(View.GONE);
AnimatorSet set1 = new AnimatorSet();
AnimatorSet inanim = new AnimatorSet();
@@ -378,7 +386,7 @@
if (mAnimScreen == null) {
mAnimScreen = new AnimScreen(mActivity);
}
- mAnimScreen.set(tab.getScreenshot());
+ mAnimScreen.set(tab.getFullScreenshot());
if (mAnimScreen.mMain.getParent() == null) {
mCustomViewContainer.addView(mAnimScreen.mMain, COVER_SCREEN_PARAMS);
}
@@ -395,12 +403,12 @@
toTop = (tab.getWebView() != null) ? tab.getWebView().getVisibleTitleHeight() : 0;
}
int toRight = mContentView.getWidth();
- int width = target.getDrawable().getIntrinsicWidth();
- int height = target.getDrawable().getIntrinsicHeight();
+ int width = mContentView.getWidth();
+ int height = mContentView.getHeight();
int fromLeft = tabview.getLeft() + target.getLeft() - mNavScreen.mScroller.getScrollX();
int fromTop = tabview.getTop() + target.getTop() - mNavScreen.mScroller.getScrollY();
- int fromRight = fromLeft + width;
- int fromBottom = fromTop + height;
+ int fromRight = fromLeft + target.getDrawable().getIntrinsicWidth();
+ int fromBottom = fromTop + target.getDrawable().getIntrinsicHeight();
float scaleFactor = mContentView.getWidth() / (float) width;
int toBottom = toTop + (int) (height * scaleFactor);
mAnimScreen.mContent.setLeft(fromLeft);
@@ -437,37 +445,50 @@
combo.start();
}
+
+ private int mNumTries = 0;
private void checkTabReady() {
boolean isready = true;
Tab tab = mUiController.getTabControl().getCurrentTab();
+ BrowserWebView webview = null;
if (tab == null)
isready = false;
else {
- BrowserWebView webview = (BrowserWebView)tab.getWebView();
- if (webview == null)
+ webview = (BrowserWebView)tab.getWebView();
+ if (webview == null) {
isready = false;
- else
+ }
+ else if (webview.hasCrashed()) {
+ webview.reload();
+ isready = true;
+ } else {
isready = webview.isReady();
+ }
}
- android.os.Handler handler = mCustomViewContainer.getHandler();
- if (!isready) {
- handler.postDelayed(new Runnable() {
+ // Post only when not ready and not crashed
+ if (!isready && mNumTries++ < 150) {
+ mCustomViewContainer.postDelayed(new Runnable() {
public void run() {
checkTabReady();
}
}, 17); //WebView is not ready. check again in for next frame.
return;
}
- handler.postDelayed(new Runnable() {
+ mNumTries = 0;
+ final boolean hasCrashed = (webview == null) ? false : webview.hasCrashed();
+ mCustomViewContainer.postDelayed(new Runnable() {
public void run() {
- fadeOutCustomViewContainer();
+ fadeOutCustomViewContainer(hasCrashed);
}
- }, 33); //WebView is ready, but give it extra 2 frame's time to display and finish the swaps
+ }, 33); //WebView is ready, but give it extra 2 frame's time to display and finish the swaps
}
- private void fadeOutCustomViewContainer() {
+ private void fadeOutCustomViewContainer(boolean hasCrashed) {
ObjectAnimator otheralpha = ObjectAnimator.ofFloat(mCustomViewContainer, "alpha", 1f, 0f);
- otheralpha.setDuration(100);
+ if (hasCrashed)
+ otheralpha.setDuration(300);
+ else
+ otheralpha.setDuration(100);
otheralpha.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator anim) {
diff --git a/src/com/android/browser/PreferenceKeys.java b/src/com/android/browser/PreferenceKeys.java
index 8620053..5860cd4 100644
--- a/src/com/android/browser/PreferenceKeys.java
+++ b/src/com/android/browser/PreferenceKeys.java
@@ -100,6 +100,7 @@
static final String PREF_REMEMBER_PASSWORDS = "remember_passwords";
static final String PREF_SAVE_FORMDATA = "save_formdata";
static final String PREF_SHOW_SECURITY_WARNINGS = "show_security_warnings";
+ static final String PREF_DO_NOT_TRACK = "do_not_track";
// ----------------------
// Keys for bandwidth_preferences.xml
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index 1b0b68c..c9e785f 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -175,6 +175,8 @@
private String mAppId;
// flag to indicate if tab should be closed on back
private boolean mCloseOnBack;
+ // flag to indicate if the tab was opened from an intent
+ private boolean mDerivedFromIntent = false;
// Keep the original url around to avoid killing the old WebView if the url
// has not changed.
// Error console for the tab
@@ -185,6 +187,8 @@
// Listener used to know when we move forward or back in the history list.
private final WebBackForwardListClient mWebBackForwardListClient;
private DataController mDataController;
+ //Indicates if a JS interface was created for a specific url
+ private boolean mJsInterfaceEnabled = false;
// AsyncTask for downloading touch icons
DownloadTouchIcon mTouchIconLoader;
@@ -193,6 +197,7 @@
private int mCaptureWidth;
private int mCaptureHeight;
private Bitmap mCapture;
+ private Bitmap mScreenShot;
private Handler mHandler;
private boolean mUpdateThumbnail;
private Timestamp timestamp;
@@ -643,8 +648,10 @@
@Override
public void onRendererCrash(WebView view, boolean crashedWhileOomProtected) {
Log.e(LOGTAG, "Tab Crashed");
- hasCrashed = true;
- showCrashView();
+ if (mWebViewController.getTabControl().getCurrentTab() == Tab.this) {
+ hasCrashed = true;
+ showCrashView();
+ }
}
/**
@@ -1538,6 +1545,7 @@
}
void pause() {
+ capture();
if (mMainView != null) {
mMainView.onPause();
if (mSubView != null) {
@@ -1568,7 +1576,6 @@
if (!mInForeground) {
return;
}
- capture();
mInForeground = false;
pause();
mMainView.setOnCreateContextMenuListener(null);
@@ -1676,6 +1683,14 @@
mCloseOnBack = close;
}
+ boolean getDerivedFromIntent() {
+ return mDerivedFromIntent;
+ }
+
+ void setDerivedFromIntent(boolean derived) {
+ mDerivedFromIntent = derived;
+ }
+
String getUrl() {
return UrlUtils.filteredUrl(mCurrentState.mUrl);
}
@@ -1863,6 +1878,12 @@
}
}
+ public Bitmap getFullScreenshot() {
+ synchronized (Tab.this) {
+ return mScreenShot;
+ }
+ }
+
public boolean isSnapshot() {
return false;
}
@@ -1950,11 +1971,27 @@
mPageLoadProgress = INITIAL_PROGRESS;
mInPageLoad = true;
mCurrentState = new PageState(mContext, false, url, null);
+ handleJsInterface(mMainView, url);
mWebViewController.onPageStarted(this, mMainView, null);
mMainView.loadUrl(url, headers);
}
}
+ public void handleJsInterface(WebView webview, String url){
+ if (url != null &&
+ url.equals(mContext.getResources().getString(R.string.homepage_base)) &&
+ url.startsWith("file:///")) {
+ mJsInterfaceEnabled = true;
+ webview.getSettings().setJavaScriptEnabled(true);
+ webview.addJavascriptInterface(mContext, "default_homepage");
+ } else {
+ if (mJsInterfaceEnabled) {
+ webview.removeJavascriptInterface("default_homepage");
+ mJsInterfaceEnabled = false;
+ }
+ }
+ }
+
public void disableUrlOverridingForLoad() {
mDisableOverrideUrlLoading = true;
}
@@ -1966,19 +2003,32 @@
}
Canvas c = new Canvas(mCapture);
int state = c.save();
+ float scale = 0;
Bitmap screenShot = mMainView.getViewportBitmap();
+ mScreenShot = screenShot;
if (screenShot != null) {
- mCapture.eraseColor(Color.WHITE);
- float scale = (float) mCaptureWidth / screenShot.getWidth();
- c.scale(scale, scale);
- c.drawBitmap(screenShot, 0, 0, null);
- } else {
- final int left = mMainView.getViewScrollX();
- final int top = mMainView.getViewScrollY() + mMainView.getVisibleTitleHeight();
- c.translate(-left, -top);
- float scale = mCaptureWidth / (float) mMainView.getWidth();
- c.scale(scale, scale, left, top);
- if (mMainView instanceof BrowserWebView) {
+ //scale based on device orientation
+ if (screenShot.getHeight() > screenShot.getWidth()){
+ scale = (float) mCaptureWidth / screenShot.getWidth();
+ } else {
+ scale = (float) mCaptureHeight / screenShot.getHeight();
+ }
+ mCapture.eraseColor(Color.WHITE);
+ c.scale(scale, scale);
+ c.drawBitmap(screenShot, 0, 0, null);
+ } else {
+ final int left = mMainView.getViewScrollX();
+ final int top = mMainView.getViewScrollY() + mMainView.getVisibleTitleHeight();
+
+ if (mMainView.getHeight() > mMainView.getWidth()){
+ scale = mCaptureWidth / (float) mMainView.getWidth();
+ } else {
+ scale = mCaptureHeight / (float) mMainView.getHeight();
+ }
+
+ c.translate(-left, -top);
+ c.scale(scale, scale, left, top);
+ if (mMainView instanceof BrowserWebView) {
((BrowserWebView)mMainView).drawContent(c);
} else {
mMainView.draw(c);
diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java
index a9da8cf..33f60ff 100644
--- a/src/com/android/browser/TabControl.java
+++ b/src/com/android/browser/TabControl.java
@@ -19,6 +19,7 @@
import android.os.Bundle;
import android.util.Log;
+import org.codeaurora.swe.GeolocationPermissions;
import org.codeaurora.swe.WebView;
import java.util.ArrayList;
@@ -50,6 +51,8 @@
private int mCurrentTab = -1;
// the main browser controller
private final Controller mController;
+ // number of incognito tabs
+ private int mNumIncognito = 0;
private OnThumbnailUpdatedListener mOnThumbnailUpdatedListener;
@@ -204,6 +207,9 @@
// Create a new tab and add it to the tab list
Tab t = new Tab(mController, w, state);
mTabs.add(t);
+ if (privateBrowsing) {
+ mNumIncognito += 1;
+ }
// Initially put the tab in the background.
t.putInBackground();
return t;
@@ -248,6 +254,14 @@
// Remove t from our list of tabs.
mTabs.remove(t);
+ //Clear incognito geolocation state if this is the last incognito tab.
+ if (t.isPrivateBrowsingEnabled()) {
+ mNumIncognito -= 1;
+ if (mNumIncognito == 0) {
+ GeolocationPermissions.onIncognitoTabsRemoved();
+ }
+ }
+
// Put the tab in the background only if it is the current one.
if (current == t) {
t.putInBackground();
@@ -410,6 +424,9 @@
// sNextId to be set correctly.
continue;
}
+ //handle restored pages that may require a JS interface
+ t.handleJsInterface(t.getWebView(), t.getUrl());
+
tabMap.put(id, t);
// Me must set the current tab before restoring the state
// so that all the client classes are set.
diff --git a/src/com/android/browser/UrlInputView.java b/src/com/android/browser/UrlInputView.java
index c8f2d40..f0037a1 100644
--- a/src/com/android/browser/UrlInputView.java
+++ b/src/com/android/browser/UrlInputView.java
@@ -71,6 +71,7 @@
static final int POST_DELAY = 100;
static final int URL_MAX_LENGTH = 2048;
+ static final int POST_DELAY_FOCUS = 300;
static interface StateListener {
static final int STATE_NORMAL = 0;
@@ -181,11 +182,11 @@
state = StateListener.STATE_NORMAL;
}
final int s = state;
- post(new Runnable() {
+ postDelayed(new Runnable() {
public void run() {
changeState(s);
}
- });
+ }, POST_DELAY_FOCUS);
}
@Override
diff --git a/src/com/android/browser/mynavigation/AddMyNavigationPage.java b/src/com/android/browser/mynavigation/AddMyNavigationPage.java
index e750aa2..cc42d96 100755
--- a/src/com/android/browser/mynavigation/AddMyNavigationPage.java
+++ b/src/com/android/browser/mynavigation/AddMyNavigationPage.java
@@ -77,11 +77,7 @@
private View.OnClickListener mOKListener = new View.OnClickListener() {
public void onClick(View v) {
- if (save()) {
- AddMyNavigationPage.this.setResult(Activity.RESULT_OK,
- (new Intent()).putExtra("need_refresh", true));
- finish();
- }
+ save();
}
};
@@ -181,6 +177,8 @@
Uri uri = ContentUris.withAppendedId(MyNavigationUtil.MY_NAVIGATION_URI,
cursor.getLong(0));
cr.update(uri, values, null, null);
+ AddMyNavigationPage.this.setResult(Activity.RESULT_OK,
+ (new Intent()).putExtra("need_refresh", true));
} else {
Log.e(LOGTAG, "this item does not exist!");
}
@@ -189,6 +187,7 @@
} finally {
if (null != cursor) {
cursor.close();
+ AddMyNavigationPage.this.finish();
}
}
}
diff --git a/src/com/android/browser/preferences/WebsiteSettingsFragment.java b/src/com/android/browser/preferences/WebsiteSettingsFragment.java
index a621dec..38c1926 100644
--- a/src/com/android/browser/preferences/WebsiteSettingsFragment.java
+++ b/src/com/android/browser/preferences/WebsiteSettingsFragment.java
@@ -23,6 +23,8 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -52,6 +54,7 @@
import org.codeaurora.swe.GeolocationPermissions;
import org.codeaurora.swe.WebStorage;
+import org.json.JSONArray;
/**
* Manage the settings for an origin.
@@ -61,11 +64,15 @@
public class WebsiteSettingsFragment extends ListFragment implements OnClickListener {
private static final String EXTRA_SITE = "site";
+ private static final long MILLIS_PER_DAY = 86400000;
private String LOGTAG = "WebsiteSettingsActivity";
private static String sMBStored = null;
private SiteAdapter mAdapter = null;
private Site mSite = null;
+ protected long geolocationPolicyExpiration;
+ protected boolean geolocationPolicyOriginAllowed;
+
static class Site implements Parcelable {
private String mOrigin;
private String mTitle;
@@ -601,22 +608,101 @@
.show();
break;
case Site.FEATURE_GEOLOCATION:
- new AlertDialog.Builder(getContext())
- .setMessage(R.string.geolocation_settings_page_dialog_message)
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ final String origin = mCurrentSite.getOrigin();
+ final GeolocationPermissions geolocationPermissions
+ = GeolocationPermissions.getInstance();
+
+ DialogInterface.OnClickListener alertDialogListener =
+ new AlertDialog.OnClickListener() {
+ public void onClick(DialogInterface dlg, int which) {
+ GeolocationPermissions geolocationPermissions =
+ GeolocationPermissions.getInstance();
+ String origin = mCurrentSite.getOrigin();
+ int selectedPosition = ((AlertDialog)dlg)
+ .getListView().getCheckedItemPosition();
+ switch (selectedPosition) {
+ case 0: // Deny forever
+ geolocationPermissions.deny(origin);
+ break;
+ case 1: // Allow for 24 hours
+ // encode the expiration time and origin as a JSON string
+ JSONArray jsonArray = new JSONArray();
+ jsonArray.put(System.currentTimeMillis() + MILLIS_PER_DAY);
+ jsonArray.put(origin);
+ geolocationPermissions.allow(jsonArray.toString());
+ break;
+ case 2: // Allow forever
+ geolocationPermissions.allow(origin);
+ break;
+ case 3: // Always ask
+ geolocationPermissions.clear(origin);
+ mCurrentSite.removeFeature(Site.FEATURE_GEOLOCATION);
+ if (mCurrentSite.getFeatureCount() == 0) {
+ finish();
+ }
+ break;
+ default:
+ break;
+ }
+ askForOrigins();
+ notifyDataSetChanged();
+ }};
+
+ builder.setTitle(String.format(getResources()
+ .getString(R.string.geolocation_settings_page_dialog_title),
+ "http".equals(Uri.parse(mCurrentSite.getOrigin()).getScheme()) ?
+ origin.substring(7) : origin ))
.setPositiveButton(R.string.geolocation_settings_page_dialog_ok_button,
- new AlertDialog.OnClickListener() {
- public void onClick(DialogInterface dlg, int which) {
- GeolocationPermissions.getInstance().clear(mCurrentSite.getOrigin());
- mCurrentSite.removeFeature(Site.FEATURE_GEOLOCATION);
- if (mCurrentSite.getFeatureCount() == 0) {
- finish();
+ alertDialogListener)
+ .setNegativeButton(R.string.geolocation_settings_page_dialog_cancel_button, null);
+
+ final ValueCallback<Long> getExpirationCallback =
+ new ValueCallback<Long>() {
+ public void onReceiveValue(Long expirationTime) {
+ if (expirationTime != null) {
+ geolocationPolicyExpiration = expirationTime.longValue();
+ // Set radio button and location icon
+ if (!geolocationPolicyOriginAllowed) {
+ // 0: Deny forever
+ builder.setSingleChoiceItems(R.array.geolocation_settings_choices, 0, null);
+ } else {
+ if (geolocationPolicyExpiration
+ != GeolocationPermissions.DO_NOT_EXPIRE) {
+ // 1: Allow for 24 hours
+ builder.setSingleChoiceItems(R.array.geolocation_settings_choices, 1, null);
+ } else {
+ // 2: Allow forever
+ builder.setSingleChoiceItems(R.array.geolocation_settings_choices, 2, null);
+ }
}
- askForOrigins();
- notifyDataSetChanged();
- }})
- .setNegativeButton(R.string.geolocation_settings_page_dialog_cancel_button, null)
- .setIconAttribute(android.R.attr.alertDialogIcon)
- .show();
+ }
+ builder.show();
+ }
+ };
+
+ final ValueCallback<Boolean> getAllowedCallback =
+ new ValueCallback<Boolean>() {
+ public void onReceiveValue(Boolean allowed) {
+ if (allowed != null) {
+ geolocationPolicyOriginAllowed = allowed.booleanValue();
+ //Get the policy expiration time
+ geolocationPermissions.getExpirationTime(origin,
+ getExpirationCallback);
+ }
+ }
+ };
+
+ geolocationPermissions.hasOrigin(origin,
+ new ValueCallback<Boolean>() {
+ public void onReceiveValue(Boolean hasOrigin) {
+ if (hasOrigin != null && hasOrigin.booleanValue()) {
+ //Get whether origin is allowed or denied
+ geolocationPermissions.getAllowed(origin,
+ getAllowedCallback);
+ }
+ }
+ });
break;
}
} else {
@@ -691,6 +777,9 @@
public void onClick(DialogInterface dlg, int which) {
WebStorage.getInstance().deleteAllData();
GeolocationPermissions.getInstance().clearAll();
+ if (GeolocationPermissions.isIncognitoCreated()) {
+ GeolocationPermissions.getIncognitoInstance().clearAll();
+ }
WebStorageSizeManager.resetLastOutOfSpaceNotificationTime();
mAdapter.askForOrigins();
finish();