diff options
Diffstat (limited to 'src')
20 files changed, 1177 insertions, 213 deletions
diff --git a/src/com/android/browser/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java index cf3fe70..74efdb8 100644 --- a/src/com/android/browser/AddBookmarkPage.java +++ b/src/com/android/browser/AddBookmarkPage.java @@ -48,9 +48,11 @@ public class AddBookmarkPage extends Activity { private Bundle mMap; private static final String[] mProjection = - { "_id", "url", "bookmark", "created", "title", "visits" }; - private static final String WHERE_CLAUSE = "url = ?"; - private final String[] SELECTION_ARGS = new String[1]; + { "_id", "url", "bookmark", "created", "title", "visits" }; + private static final String WHERE_CLAUSE + = "url = ? OR url = ? OR url = ? OR url = ?"; + private static final String WHERE_CLAUSE_SECURE = "url = ? OR url = ?"; + private String[] SELECTION_ARGS; private View.OnClickListener mSaveBookmark = new View.OnClickListener() { public void onClick(View v) { @@ -153,11 +155,36 @@ public class AddBookmarkPage extends Activity { } else { // Want to append to the beginning of the list long creationTime = new Date().getTime(); - SELECTION_ARGS[0] = url; + // First we check to see if the user has already visited this + // site. They may have bookmarked it in a different way from + // how it's stored in the database, so allow different combos + // to map to the same url. + boolean secure = false; + String compareString = url; + if (compareString.startsWith("http://")) { + compareString = compareString.substring(7); + } else if (compareString.startsWith("https://")) { + compareString = compareString.substring(8); + secure = true; + } + if (compareString.startsWith("www.")) { + compareString = compareString.substring(4); + } + if (secure) { + SELECTION_ARGS = new String[2]; + SELECTION_ARGS[0] = "https://" + compareString; + SELECTION_ARGS[1] = "https://www." + compareString; + } else { + SELECTION_ARGS = new String[4]; + SELECTION_ARGS[0] = compareString; + SELECTION_ARGS[1] = "www." + compareString; + SELECTION_ARGS[2] = "http://" + compareString; + SELECTION_ARGS[3] = "http://" + SELECTION_ARGS[1]; + } ContentResolver cr = getContentResolver(); Cursor c = cr.query(Browser.BOOKMARKS_URI, mProjection, - WHERE_CLAUSE, + secure ? WHERE_CLAUSE_SECURE : WHERE_CLAUSE, SELECTION_ARGS, null); ContentValues map = new ContentValues(); @@ -186,6 +213,7 @@ public class AddBookmarkPage extends Activity { cr.update(Browser.BOOKMARKS_URI, map, "_id = " + c.getInt(0), null); matchedTitle = true; + break; } } if (!matchedTitle) { diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java index 3252da3..1966b8b 100644 --- a/src/com/android/browser/BrowserActivity.java +++ b/src/com/android/browser/BrowserActivity.java @@ -36,6 +36,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.DialogInterface.OnCancelListener; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.AssetManager; @@ -57,6 +58,8 @@ import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.PaintDrawable; import android.hardware.SensorListener; import android.hardware.SensorManager; +import android.location.Location; +import android.location.LocationManager; import android.net.ConnectivityManager; import android.net.Uri; import android.net.WebAddress; @@ -81,6 +84,7 @@ import android.provider.Browser; import android.provider.Contacts; import android.provider.Downloads; import android.provider.MediaStore; +import android.provider.Settings; import android.provider.Contacts.Intents.Insert; import android.text.IClipboard; import android.text.TextUtils; @@ -110,11 +114,13 @@ import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.DownloadListener; import android.webkit.HttpAuthHandler; +import android.webkit.PluginManager; import android.webkit.SslErrorHandler; import android.webkit.URLUtil; import android.webkit.WebChromeClient; import android.webkit.WebHistoryItem; import android.webkit.WebIconDatabase; +import android.webkit.WebStorage; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.EditText; @@ -162,6 +168,8 @@ public class BrowserActivity extends Activity private SensorManager mSensorManager = null; + private WebStorage.QuotaUpdater mWebStorageQuotaUpdater = null; + // These are single-character shortcuts for searching popular sources. private static final int SHORTCUT_INVALID = 0; private static final int SHORTCUT_GOOGLE_SEARCH = 1; @@ -586,11 +594,6 @@ public class BrowserActivity extends Activity } copyBuildInfos(); - - // Refresh the plugin list. - if (mTabControl.getCurrentWebView() != null) { - mTabControl.getCurrentWebView().refreshPlugins(false); - } } catch (IOException e) { Log.e(TAG, "IO Exception: " + e); } @@ -637,16 +640,22 @@ public class BrowserActivity extends Activity } } + // Flag to enable the touchable browser bar with buttons + private final boolean CUSTOM_BROWSER_BAR = true; + @Override public void onCreate(Bundle icicle) { if (LOGV_ENABLED) { Log.v(LOGTAG, this + " onStart"); } super.onCreate(icicle); - this.requestWindowFeature(Window.FEATURE_LEFT_ICON); - this.requestWindowFeature(Window.FEATURE_RIGHT_ICON); - this.requestWindowFeature(Window.FEATURE_PROGRESS); - this.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - + if (CUSTOM_BROWSER_BAR) { + this.requestWindowFeature(Window.FEATURE_NO_TITLE); + } else { + this.requestWindowFeature(Window.FEATURE_LEFT_ICON); + this.requestWindowFeature(Window.FEATURE_RIGHT_ICON); + this.requestWindowFeature(Window.FEATURE_PROGRESS); + this.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + } // test the browser in OpenGL // requestWindowFeature(Window.FEATURE_OPENGL); @@ -674,8 +683,21 @@ public class BrowserActivity extends Activity mGenericFavicon = getResources().getDrawable( R.drawable.app_web_browser_sm); - mContentView = (FrameLayout) getWindow().getDecorView().findViewById( - com.android.internal.R.id.content); + FrameLayout frameLayout = (FrameLayout) getWindow().getDecorView() + .findViewById(com.android.internal.R.id.content); + if (CUSTOM_BROWSER_BAR) { + // This LinearLayout will hold the title bar and a FrameLayout, which + // holds everything else. + LinearLayout linearLayout = (LinearLayout) LayoutInflater.from(this) + .inflate(R.layout.custom_screen, null); + mTitleBar = (TitleBar) linearLayout.findViewById(R.id.title_bar); + mTitleBar.setBrowserActivity(this); + mContentView = (FrameLayout) linearLayout.findViewById( + R.id.main_content); + frameLayout.addView(linearLayout, COVER_SCREEN_PARAMS); + } else { + mContentView = frameLayout; + } // Create the tab control and our initial tab mTabControl = new TabControl(this); @@ -741,7 +763,12 @@ public class BrowserActivity extends Activity waitForCredentials(); } } else { - webView.loadUrl(url); + byte[] postData = getLocationData(intent); + if (postData != null) { + webView.postUrl(url, postData); + } else { + webView.loadUrl(url); + } } } else { // TabControl.restoreState() will create a new tab even if @@ -766,6 +793,52 @@ public class BrowserActivity extends Activity } } }; + + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + mPackageInstallationReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + final String packageName = intent.getData() + .getSchemeSpecificPart(); + final boolean replacing = intent.getBooleanExtra( + Intent.EXTRA_REPLACING, false); + if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) { + // if it is replacing, refreshPlugins() when adding + return; + } + PackageManager pm = BrowserActivity.this.getPackageManager(); + PackageInfo pkgInfo = null; + try { + pkgInfo = pm.getPackageInfo(packageName, + PackageManager.GET_PERMISSIONS); + } catch (PackageManager.NameNotFoundException e) { + return; + } + if (pkgInfo != null) { + String permissions[] = pkgInfo.requestedPermissions; + if (permissions == null) { + return; + } + boolean permissionOk = false; + for (String permit : permissions) { + if (PluginManager.PLUGIN_PERMISSION.equals(permit)) { + permissionOk = true; + break; + } + } + if (permissionOk) { + PluginManager.getInstance(BrowserActivity.this) + .refreshPlugins( + Intent.ACTION_PACKAGE_ADDED + .equals(action)); + } + } + } + }; + registerReceiver(mPackageInstallationReceiver, filter); } @Override @@ -827,8 +900,8 @@ public class BrowserActivity extends Activity } else { if (mTabOverview != null && mAnimationCount == 0) { sendAnimateFromOverview(appTab, false, - needsLoad ? url : null, TAB_OVERVIEW_DELAY, - null); + needsLoad ? url : null, null, + TAB_OVERVIEW_DELAY, null); } else { // If the tab was the current tab, we have to attach // it to the view system again. @@ -850,15 +923,20 @@ public class BrowserActivity extends Activity mSettings.toggleDebugSettings(); return; } + byte[] postData = getLocationData(intent); // If the Window overview is up and we are not in the midst of // an animation, animate away from the Window overview. if (mTabOverview != null && mAnimationCount == 0) { sendAnimateFromOverview(current, false, url, - TAB_OVERVIEW_DELAY, null); + postData, TAB_OVERVIEW_DELAY, null); } else { // Get rid of the subwindow if it exists dismissSubWindow(current); - current.getWebView().loadUrl(url); + if (postData != null) { + current.getWebView().postUrl(url, postData); + } else { + current.getWebView().loadUrl(url); + } } } } @@ -976,6 +1054,31 @@ public class BrowserActivity extends Activity return url; } + byte[] getLocationData(Intent intent) { + byte[] postData = null; + if (intent != null) { + final String action = intent.getAction(); + if ((Intent.ACTION_SEARCH.equals(action) + || Intent.ACTION_WEB_SEARCH.equals(action)) + && Settings.Secure.isLocationProviderEnabled( + getContentResolver(), + LocationManager.NETWORK_PROVIDER)) { + // Attempt to get location info + LocationManager locationManager = (LocationManager) + getSystemService(Context.LOCATION_SERVICE); + Location location = locationManager + .getLastKnownLocation(LocationManager.NETWORK_PROVIDER); + if (location != null) { + StringBuilder str = new StringBuilder("sll="); + str.append(location.getLatitude()).append(",").append( + location.getLongitude()); + postData = str.toString().getBytes(); + } + } + } + return postData; + } + /* package */ static String fixUrl(String inUrl) { if (inUrl.startsWith("http://") || inUrl.startsWith("https://")) return inUrl; @@ -1092,8 +1195,9 @@ public class BrowserActivity extends Activity return; } + mTabControl.resumeCurrentTab(); mActivityInPause = false; - resumeWebView(); + resumeWebViewTimers(); if (mWakeLock.isHeld()) { mHandler.removeMessages(RELEASE_WAKELOCK); @@ -1151,8 +1255,9 @@ public class BrowserActivity extends Activity return; } + mTabControl.pauseCurrentTab(); mActivityInPause = true; - if (mTabControl.getCurrentIndex() >= 0 && !pauseWebView()) { + if (mTabControl.getCurrentIndex() >= 0 && !pauseWebViewTimers()) { mWakeLock.acquire(); mHandler.sendMessageDelayed(mHandler .obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT); @@ -1182,8 +1287,10 @@ public class BrowserActivity extends Activity super.onDestroy(); // Remove the current tab and sub window TabControl.Tab t = mTabControl.getCurrentTab(); - dismissSubWindow(t); - removeTabFromContentView(t); + if (t != null) { + dismissSubWindow(t); + removeTabFromContentView(t); + } // Destroy all the tabs mTabControl.destroy(); WebIconDatabase.getInstance().close(); @@ -1201,6 +1308,8 @@ public class BrowserActivity extends Activity // "com.android.masfproxyservice", // "com.android.masfproxyservice.MasfProxyService")); //stopService(proxyServiceIntent); + + unregisterReceiver(mPackageInstallationReceiver); } @Override @@ -1249,7 +1358,7 @@ public class BrowserActivity extends Activity mTabControl.freeMemory(); } - private boolean resumeWebView() { + private boolean resumeWebViewTimers() { if ((!mActivityInPause && !mPageStarted) || (mActivityInPause && mPageStarted)) { CookieSyncManager.getInstance().startSync(); @@ -1263,7 +1372,7 @@ public class BrowserActivity extends Activity } } - private boolean pauseWebView() { + private boolean pauseWebViewTimers() { if (mActivityInPause && !mPageStarted) { CookieSyncManager.getInstance().stopSync(); WebView w = mTabControl.getCurrentWebView(); @@ -1895,8 +2004,8 @@ public class BrowserActivity extends Activity // Send the ANIMTE_FROM_OVERVIEW message after changing the current tab. private void sendAnimateFromOverview(final TabControl.Tab tab, - final boolean newTab, final String url, final int delay, - final Message msg) { + final boolean newTab, final String url, final byte[] postData, + final int delay, final Message msg) { // Set the current tab. mTabControl.setCurrentTab(tab); // Attach the WebView so it will layout. @@ -1921,7 +2030,11 @@ public class BrowserActivity extends Activity // animation. if (url != null) { dismissSubWindow(tab); - tab.getWebView().loadUrl(url); + if (postData != null) { + tab.getWebView().postUrl(url, postData); + } else { + tab.getWebView().loadUrl(url); + } } map.put("msg", msg); mHandler.sendMessageDelayed(mHandler.obtainMessage( @@ -1957,7 +2070,7 @@ public class BrowserActivity extends Activity delay = TAB_ANIMATION_DURATION + TAB_OVERVIEW_DELAY; tabPicker(false, mTabControl.getTabIndex(t), false); } - sendAnimateFromOverview(t, false, url, delay, null); + sendAnimateFromOverview(t, false, url, null, delay, null); } // This method does a ton of stuff. It will attempt to create a new tab @@ -1968,7 +2081,7 @@ public class BrowserActivity extends Activity // the given Message. If the tab overview is already showing (i.e. this // method is called from TabListener.onClick(), the method will animate // away from the tab overview. - private void openTabAndShow(String url, final Message msg, + private TabControl.Tab openTabAndShow(String url, final Message msg, boolean closeOnExit, String appId) { final boolean newTab = mTabControl.getTabCount() != TabControl.MAX_TABS; final TabControl.Tab currentTab = mTabControl.getCurrentTab(); @@ -1997,15 +2110,16 @@ public class BrowserActivity extends Activity } // Animate from the Tab overview after any animations have // finished. - sendAnimateFromOverview( - mTabControl.createNewTab(closeOnExit, appId, url), true, - url, delay, msg); + final TabControl.Tab tab = mTabControl.createNewTab( + closeOnExit, appId, url); + sendAnimateFromOverview(tab, true, url, null, delay, msg); + return tab; } } else if (url != null) { // We should not have a msg here. assert msg == null; if (mTabOverview != null && mAnimationCount == 0) { - sendAnimateFromOverview(currentTab, false, url, + sendAnimateFromOverview(currentTab, false, url, null, TAB_OVERVIEW_DELAY, null); } else { // Get rid of the subwindow if it exists @@ -2014,6 +2128,7 @@ public class BrowserActivity extends Activity currentTab.getWebView().loadUrl(url); } } + return currentTab; } private Animation createTabAnimation(final AnimatingView view, @@ -2233,14 +2348,15 @@ public class BrowserActivity extends Activity mTabListener = null; } - private void openTab(String url) { + private TabControl.Tab openTab(String url) { if (mSettings.openInBackground()) { TabControl.Tab t = mTabControl.createNewTab(); if (t != null) { t.getWebView().loadUrl(url); } + return t; } else { - openTabAndShow(url, null, false, null); + return openTabAndShow(url, null, false, null); } } @@ -2340,7 +2456,11 @@ public class BrowserActivity extends Activity // While the tab overview is animating or being shown, block changes // to the title. if (mAnimationCount == 0 && mTabOverview == null) { - setTitle(buildUrlTitle(url, title)); + if (CUSTOM_BROWSER_BAR) { + mTitleBar.setTitleAndUrl(title, url); + } else { + setTitle(buildUrlTitle(url, title)); + } } } @@ -2417,18 +2537,34 @@ public class BrowserActivity extends Activity if (mAnimationCount > 0 || mTabOverview != null) { return; } - Drawable[] array = new Drawable[2]; - PaintDrawable p = new PaintDrawable(Color.WHITE); - p.setCornerRadius(3f); - array[0] = p; - if (icon == null) { - array[1] = mGenericFavicon; + if (CUSTOM_BROWSER_BAR) { + Drawable[] array = new Drawable[3]; + array[0] = new PaintDrawable(Color.BLACK); + PaintDrawable p = new PaintDrawable(Color.WHITE); + array[1] = p; + if (icon == null) { + array[2] = mGenericFavicon; + } else { + array[2] = new BitmapDrawable(icon); + } + LayerDrawable d = new LayerDrawable(array); + d.setLayerInset(1, 1, 1, 1, 1); + d.setLayerInset(2, 2, 2, 2, 2); + mTitleBar.setFavicon(d); } else { - array[1] = new BitmapDrawable(icon); + Drawable[] array = new Drawable[2]; + PaintDrawable p = new PaintDrawable(Color.WHITE); + p.setCornerRadius(3f); + array[0] = p; + if (icon == null) { + array[1] = mGenericFavicon; + } else { + array[1] = new BitmapDrawable(icon); + } + LayerDrawable d = new LayerDrawable(array); + d.setLayerInset(1, 2, 2, 2, 2); + getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, d); } - LayerDrawable d = new LayerDrawable(array); - d.setLayerInset(1, 2, 2, 2, 2); - getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, d); } /** @@ -2464,7 +2600,7 @@ public class BrowserActivity extends Activity // Change to the parent tab final TabControl.Tab tab = mTabControl.getTab(indexToShow); if (tab != null) { - sendAnimateFromOverview(tab, false, null, delay, null); + sendAnimateFromOverview(tab, false, null, null, delay, null); } else { // Increment this here so that no other animations can happen in // between the end of the tab picker transition and the beginning @@ -2506,9 +2642,9 @@ public class BrowserActivity extends Activity finish(); return; } - // call pauseWebView() now, we won't be able to call it in - // onPause() as the WebView won't be valid. - pauseWebView(); + // call pauseWebViewTimers() now, we won't be able to call + // it in onPause() as the WebView won't be valid. + pauseWebViewTimers(); removeTabFromContentView(current); mTabControl.removeTab(current); } @@ -2674,7 +2810,12 @@ public class BrowserActivity extends Activity loadURL(getTopWindow(), url); break; case R.id.open_newtab_context_menu_id: - openTab(url); + final TabControl.Tab parent = mTabControl + .getCurrentTab(); + final TabControl.Tab newTab = openTab(url); + if (newTab != parent) { + parent.addChildTab(newTab); + } break; case R.id.bookmark_context_menu_id: Intent intent = new Intent(BrowserActivity.this, @@ -2799,8 +2940,9 @@ public class BrowserActivity extends Activity if (!mPageStarted) { mPageStarted = true; - // if onResume() has been called, resumeWebView() does nothing. - resumeWebView(); + // if onResume() has been called, resumeWebViewTimers() does + // nothing. + resumeWebViewTimers(); } // reset sync timer to avoid sync starts during loading a page @@ -2921,9 +3063,9 @@ public class BrowserActivity extends Activity if (mPageStarted) { mPageStarted = false; - // pauseWebView() will do nothing and return false if onPause() - // is not called yet. - if (pauseWebView()) { + // pauseWebViewTimers() will do nothing and return false if + // onPause() is not called yet. + if (pauseWebViewTimers()) { if (mWakeLock.isHeld()) { mHandler.removeMessages(RELEASE_WAKELOCK); mWakeLock.release(); @@ -3334,8 +3476,11 @@ public class BrowserActivity extends Activity // openTabAndShow will dispatch the message after creating the // new WebView. This will prevent another request from coming // in during the animation. - openTabAndShow(null, msg, false, null); - parent.addChildTab(mTabControl.getCurrentTab()); + final TabControl.Tab newTab = openTabAndShow(null, msg, false, + null); + if (newTab != parent) { + parent.addChildTab(newTab); + } WebView.WebViewTransport transport = (WebView.WebViewTransport) msg.obj; transport.setWebView(mTabControl.getCurrentWebView()); @@ -3441,8 +3586,13 @@ public class BrowserActivity extends Activity // Block progress updates to the title bar while the tab overview // is animating or being displayed. if (mAnimationCount == 0 && mTabOverview == null) { - getWindow().setFeatureInt(Window.FEATURE_PROGRESS, - newProgress * 100); + if (CUSTOM_BROWSER_BAR) { + mTitleBar.setProgress(newProgress); + } else { + getWindow().setFeatureInt(Window.FEATURE_PROGRESS, + newProgress * 100); + + } } if (newProgress == 100) { @@ -3476,6 +3626,8 @@ public class BrowserActivity extends Activity url.length() >= SQLiteDatabase.SQLITE_MAX_LIKE_PATTERN_LENGTH) { return; } + // See if we can find the current url in our history database and + // add the new title to it. if (url.startsWith("http://www.")) { url = url.substring(11); } else if (url.startsWith("http://")) { @@ -3490,15 +3642,12 @@ public class BrowserActivity extends Activity Cursor c = mResolver.query(Browser.BOOKMARKS_URI, Browser.HISTORY_PROJECTION, where, selArgs, null); if (c.moveToFirst()) { - if (LOGV_ENABLED) { - Log.v(LOGTAG, "updating cursor"); - } // Current implementation of database only has one entry per // url. - int titleIndex = - c.getColumnIndex(Browser.BookmarkColumns.TITLE); - c.updateString(titleIndex, title); - c.commitUpdates(); + ContentValues map = new ContentValues(); + map.put(Browser.BookmarkColumns.TITLE, title); + mResolver.update(Browser.BOOKMARKS_URI, map, + "_id = " + c.getInt(0), null); } c.close(); } catch (IllegalStateException e) { @@ -3512,6 +3661,50 @@ public class BrowserActivity extends Activity public void onReceivedIcon(WebView view, Bitmap icon) { updateIcon(view.getUrl(), icon); } + + /** + * The origin has exceeded it's database quota. + * @param url the URL that exceeded the quota + * @param databaseIdentifier the identifier of the database on + * which the transaction that caused the quota overflow was run + * @param currentQuota the current quota for the origin. + * @param quotaUpdater The callback to run when a decision to allow or + * deny quota has been made. Don't forget to call this! + */ + @Override + public void onExceededDatabaseQuota(String url, + String databaseIdentifier, long currentQuota, + WebStorage.QuotaUpdater quotaUpdater) { + if(LOGV_ENABLED) { + Log.v(LOGTAG, + "BrowserActivity received onExceededDatabaseQuota for " + + url + + ":" + + databaseIdentifier + + "(current quota: " + + currentQuota + + ")"); + } + mWebStorageQuotaUpdater = quotaUpdater; + String DIALOG_PACKAGE = "com.android.browser"; + String DIALOG_CLASS = DIALOG_PACKAGE + ".PermissionDialog"; + Intent intent = new Intent(); + intent.setClassName(DIALOG_PACKAGE, DIALOG_CLASS); + intent.putExtra(PermissionDialog.PARAM_ORIGIN, url); + intent.putExtra(PermissionDialog.PARAM_QUOTA, currentQuota); + startActivityForResult(intent, WEBSTORAGE_QUOTA_DIALOG); + } + + /* Adds a JavaScript error message to the system log. + * @param message The error message to report. + * @param lineNumber The line number of the error. + * @param sourceID The name of the source file that caused the error. + */ + @Override + public void addMessageToConsole(String message, int lineNumber, String sourceID) { + Log.w(LOGTAG, "Console: " + message + " (" + sourceID + ":" + lineNumber + ")"); + } + }; /** @@ -3627,19 +3820,19 @@ public class BrowserActivity extends Activity String cookies = CookieManager.getInstance().getCookie(url); ContentValues values = new ContentValues(); - values.put(Downloads.URI, uri.toString()); - values.put(Downloads.COOKIE_DATA, cookies); - values.put(Downloads.USER_AGENT, userAgent); - values.put(Downloads.NOTIFICATION_PACKAGE, + values.put(Downloads.COLUMN_URI, uri.toString()); + values.put(Downloads.COLUMN_COOKIE_DATA, cookies); + values.put(Downloads.COLUMN_USER_AGENT, userAgent); + values.put(Downloads.COLUMN_NOTIFICATION_PACKAGE, getPackageName()); - values.put(Downloads.NOTIFICATION_CLASS, + values.put(Downloads.COLUMN_NOTIFICATION_CLASS, BrowserDownloadPage.class.getCanonicalName()); - values.put(Downloads.VISIBILITY, Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - values.put(Downloads.MIMETYPE, mimetype); - values.put(Downloads.FILENAME_HINT, filename); - values.put(Downloads.DESCRIPTION, uri.getHost()); + values.put(Downloads.COLUMN_VISIBILITY, Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + values.put(Downloads.COLUMN_MIME_TYPE, mimetype); + values.put(Downloads.COLUMN_FILE_NAME_HINT, filename); + values.put(Downloads.COLUMN_DESCRIPTION, uri.getHost()); if (contentLength > 0) { - values.put(Downloads.TOTAL_BYTES, contentLength); + values.put(Downloads.COLUMN_TOTAL_BYTES, contentLength); } if (mimetype == null) { // We must have long pressed on a link or image to download it. We @@ -3705,7 +3898,11 @@ public class BrowserActivity extends Activity // If the tab overview is animating or being shown, do not update the // lock icon. if (mAnimationCount == 0 && mTabOverview == null) { - getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, d); + if (CUSTOM_BROWSER_BAR) { + mTitleBar.setLock(d); + } else { + getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, d); + } } } @@ -4207,7 +4404,7 @@ public class BrowserActivity extends Activity // current tab. if (mTabOverview != null && mAnimationCount == 0) { sendAnimateFromOverview(currentTab, false, data, - TAB_OVERVIEW_DELAY, null); + null, TAB_OVERVIEW_DELAY, null); } else { dismissSubWindow(currentTab); if (data != null && data.length() != 0) { @@ -4217,6 +4414,14 @@ public class BrowserActivity extends Activity } } break; + case WEBSTORAGE_QUOTA_DIALOG: + long currentQuota = 0; + if (resultCode == RESULT_OK && intent != null) { + currentQuota = intent.getLongExtra( + PermissionDialog.PARAM_QUOTA, currentQuota); + } + mWebStorageQuotaUpdater.updateQuota(currentQuota); + break; default: break; } @@ -4258,7 +4463,8 @@ public class BrowserActivity extends Activity if (mTabControl.getTabCount() == 0) { current = mTabControl.createNewTab(); sendAnimateFromOverview(current, true, - mSettings.getHomePage(), TAB_OVERVIEW_DELAY, null); + mSettings.getHomePage(), null, TAB_OVERVIEW_DELAY, + null); } else { final int index = position > 0 ? (position - 1) : 0; current = mTabControl.getTab(index); @@ -4295,7 +4501,7 @@ public class BrowserActivity extends Activity openTabAndShow(mSettings.getHomePage(), null, false, null); } else { sendAnimateFromOverview(mTabControl.getTab(index), - false, null, 0, null); + false, null, null, 0, null); } } } @@ -4317,13 +4523,19 @@ public class BrowserActivity extends Activity AnimatingView(Context ctxt, TabControl.Tab t) { super(ctxt); mTab = t; - // Use the top window in the animation since the tab overview will - // display the top window in each cell. - final WebView w = t.getTopWindow(); - mPicture = w.capturePicture(); - mScale = w.getScale() / w.getWidth(); - mScrollX = w.getScrollX(); - mScrollY = w.getScrollY(); + if (t != null && t.getTopWindow() != null) { + // Use the top window in the animation since the tab overview + // will display the top window in each cell. + final WebView w = t.getTopWindow(); + mPicture = w.capturePicture(); + mScale = w.getScale() / w.getWidth(); + mScrollX = w.getScrollX(); + mScrollY = w.getScrollY(); + } else { + mPicture = null; + mScale = 1.0f; + mScrollX = mScrollY = 0; + } } @Override @@ -4397,16 +4609,20 @@ public class BrowserActivity extends Activity mAnimationCount++; // Always change the title bar to the window overview title while // animating. - getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, null); - getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, null); - getWindow().setFeatureInt(Window.FEATURE_PROGRESS, - Window.PROGRESS_VISIBILITY_OFF); - setTitle(R.string.tab_picker_title); + if (CUSTOM_BROWSER_BAR) { + mTitleBar.setToTabPicker(); + } else { + getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, null); + getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, null); + getWindow().setFeatureInt(Window.FEATURE_PROGRESS, + Window.PROGRESS_VISIBILITY_OFF); + setTitle(R.string.tab_picker_title); + } // Make the menu empty until the animation completes. mMenuState = EMPTY_MENU; } - private void bookmarksOrHistoryPicker(boolean startWithHistory) { + /* package */ void bookmarksOrHistoryPicker(boolean startWithHistory) { WebView current = mTabControl.getCurrentWebView(); if (current == null) { return; @@ -4482,7 +4698,7 @@ public class BrowserActivity extends Activity return 0; } - static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile( + protected static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile( "(?i)" + // switch on case insensitive matching "(" + // begin group for schema "(?:http|https|file):\\/\\/" + @@ -4590,6 +4806,8 @@ public class BrowserActivity extends Activity country) + "client=ms-" + Partner.getString(this.getContentResolver(), Partner.CLIENT_ID) + // FIXME, remove this when GEOLOCATION team make their move + + "&action=devloc" + "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&q=%s"; } else { QuickSearch_G = url; @@ -4737,6 +4955,8 @@ public class BrowserActivity extends Activity private Toast mStopToast; + private TitleBar mTitleBar; + // Used during animations to prevent other animations from being triggered. // A count is used since the animation to and from the Window overview can // overlap. A count of 0 means no animation where a count of > 0 means @@ -4754,10 +4974,13 @@ public class BrowserActivity extends Activity private IntentFilter mNetworkStateChangedFilter; private BroadcastReceiver mNetworkStateIntentReceiver; + private BroadcastReceiver mPackageInstallationReceiver; + // activity requestCode - final static int COMBO_PAGE = 1; - final static int DOWNLOAD_PAGE = 2; - final static int PREFERENCES_PAGE = 3; + final static int COMBO_PAGE = 1; + final static int DOWNLOAD_PAGE = 2; + final static int PREFERENCES_PAGE = 3; + final static int WEBSTORAGE_QUOTA_DIALOG = 4; // the frenquency of checking whether system memory is low final static int CHECK_MEMORY_INTERVAL = 30000; // 30 seconds diff --git a/src/com/android/browser/BrowserBookmarksAdapter.java b/src/com/android/browser/BrowserBookmarksAdapter.java index 27782e0..09741ae 100644 --- a/src/com/android/browser/BrowserBookmarksAdapter.java +++ b/src/com/android/browser/BrowserBookmarksAdapter.java @@ -40,8 +40,6 @@ import java.io.ByteArrayOutputStream; class BrowserBookmarksAdapter extends BaseAdapter { - private final String LOGTAG = "Bookmarks"; - private String mCurrentPage; private Cursor mCursor; private int mCount; diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java index dd34c14..45fca87 100644 --- a/src/com/android/browser/BrowserBookmarksPage.java +++ b/src/com/android/browser/BrowserBookmarksPage.java @@ -44,6 +44,7 @@ import android.view.ViewGroup; import android.view.ContextMenu.ContextMenuInfo; import android.widget.AdapterView; import android.widget.ListView; +import android.widget.Toast; /** * View showing the user's bookmarks in the browser. @@ -107,7 +108,13 @@ public class BrowserBookmarksPage extends Activity implements break; case R.id.copy_url_context_menu_id: copy(getUrl(i.position)); - + break; + case R.id.homepage_context_menu_id: + BrowserSettings.getInstance().setHomePage(this, + getUrl(i.position)); + Toast.makeText(this, R.string.homepage_set, + Toast.LENGTH_LONG).show(); + break; default: return super.onContextItemSelected(item); } diff --git a/src/com/android/browser/BrowserDownloadAdapter.java b/src/com/android/browser/BrowserDownloadAdapter.java index 38b83fe..16cb982 100644 --- a/src/com/android/browser/BrowserDownloadAdapter.java +++ b/src/com/android/browser/BrowserDownloadAdapter.java @@ -60,14 +60,14 @@ public class BrowserDownloadAdapter extends ResourceCursorAdapter { public BrowserDownloadAdapter(Context context, int layout, Cursor c) { super(context, layout, c); mFilenameColumnId = c.getColumnIndexOrThrow(Downloads._DATA); - mTitleColumnId = c.getColumnIndexOrThrow(Downloads.TITLE); - mDescColumnId = c.getColumnIndexOrThrow(Downloads.DESCRIPTION); - mStatusColumnId = c.getColumnIndexOrThrow(Downloads.STATUS); - mTotalBytesColumnId = c.getColumnIndexOrThrow(Downloads.TOTAL_BYTES); + mTitleColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_TITLE); + mDescColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_DESCRIPTION); + mStatusColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_STATUS); + mTotalBytesColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_TOTAL_BYTES); mCurrentBytesColumnId = - c.getColumnIndexOrThrow(Downloads.CURRENT_BYTES); - mMimetypeColumnId = c.getColumnIndexOrThrow(Downloads.MIMETYPE); - mDateColumnId = c.getColumnIndexOrThrow(Downloads.LAST_MODIFICATION); + c.getColumnIndexOrThrow(Downloads.COLUMN_CURRENT_BYTES); + mMimetypeColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_MIME_TYPE); + mDateColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_LAST_MODIFICATION); } @Override @@ -106,7 +106,7 @@ public class BrowserDownloadAdapter extends ResourceCursorAdapter { // We have a filename, so we can build a title from that title = new File(fullFilename).getName(); ContentValues values = new ContentValues(); - values.put(Downloads.TITLE, title); + values.put(Downloads.COLUMN_TITLE, title); // assume "_id" is the first column for the cursor context.getContentResolver().update( ContentUris.withAppendedId(Downloads.CONTENT_URI, diff --git a/src/com/android/browser/BrowserDownloadPage.java b/src/com/android/browser/BrowserDownloadPage.java index 4397337..22e0e65 100644 --- a/src/com/android/browser/BrowserDownloadPage.java +++ b/src/com/android/browser/BrowserDownloadPage.java @@ -66,34 +66,30 @@ public class BrowserDownloadPage extends Activity setTitle(getText(R.string.download_title)); mListView = (ListView) findViewById(R.id.list); - LayoutInflater factory = LayoutInflater.from(this); - View v = factory.inflate(R.layout.no_downloads, null); - addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT, - LayoutParams.FILL_PARENT)); - mListView.setEmptyView(v); + mListView.setEmptyView(findViewById(R.id.empty)); mDownloadCursor = managedQuery(Downloads.CONTENT_URI, - new String [] {"_id", Downloads.TITLE, Downloads.STATUS, - Downloads.TOTAL_BYTES, Downloads.CURRENT_BYTES, - Downloads._DATA, Downloads.DESCRIPTION, - Downloads.MIMETYPE, Downloads.LAST_MODIFICATION, - Downloads.VISIBILITY}, + new String [] {"_id", Downloads.COLUMN_TITLE, Downloads.COLUMN_STATUS, + Downloads.COLUMN_TOTAL_BYTES, Downloads.COLUMN_CURRENT_BYTES, + Downloads._DATA, Downloads.COLUMN_DESCRIPTION, + Downloads.COLUMN_MIME_TYPE, Downloads.COLUMN_LAST_MODIFICATION, + Downloads.COLUMN_VISIBILITY}, null, null); // only attach everything to the listbox if we can access // the download database. Otherwise, just show it empty if (mDownloadCursor != null) { mStatusColumnId = - mDownloadCursor.getColumnIndexOrThrow(Downloads.STATUS); + mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_STATUS); mIdColumnId = mDownloadCursor.getColumnIndexOrThrow(Downloads._ID); mTitleColumnId = - mDownloadCursor.getColumnIndexOrThrow(Downloads.TITLE); + mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_TITLE); // Create a list "controller" for the data mDownloadAdapter = new BrowserDownloadAdapter(this, R.layout.browser_download_item, mDownloadCursor); - + mListView.setAdapter(mDownloadAdapter); mListView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET); mListView.setOnCreateContextMenuListener(this); @@ -403,7 +399,7 @@ public class BrowserDownloadPage extends Activity mDownloadCursor.getColumnIndexOrThrow(Downloads._DATA); String filename = mDownloadCursor.getString(filenameColumnId); int mimetypeColumnId = - mDownloadCursor.getColumnIndexOrThrow(Downloads.MIMETYPE); + mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_MIME_TYPE); String mimetype = mDownloadCursor.getString(mimetypeColumnId); Uri path = Uri.parse(filename); // If there is no scheme, then it must be a file @@ -453,13 +449,13 @@ public class BrowserDownloadPage extends Activity private void hideCompletedDownload() { int status = mDownloadCursor.getInt(mStatusColumnId); - int visibilityColumn = mDownloadCursor.getColumnIndexOrThrow(Downloads.VISIBILITY); + int visibilityColumn = mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_VISIBILITY); int visibility = mDownloadCursor.getInt(visibilityColumn); if (Downloads.isStatusCompleted(status) && visibility == Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) { ContentValues values = new ContentValues(); - values.put(Downloads.VISIBILITY, Downloads.VISIBILITY_VISIBLE); + values.put(Downloads.COLUMN_VISIBILITY, Downloads.VISIBILITY_VISIBLE); getContentResolver().update( ContentUris.withAppendedId(Downloads.CONTENT_URI, mDownloadCursor.getLong(mIdColumnId)), values, null, null); diff --git a/src/com/android/browser/BrowserHistoryPage.java b/src/com/android/browser/BrowserHistoryPage.java index 42ca848..e333416 100644 --- a/src/com/android/browser/BrowserHistoryPage.java +++ b/src/com/android/browser/BrowserHistoryPage.java @@ -41,6 +41,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ContextMenu.ContextMenuInfo; +import android.view.ViewStub; import android.webkit.DateSorter; import android.webkit.WebIconDatabase.IconListener; import android.widget.AdapterView; @@ -48,6 +49,7 @@ import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.ExpandableListView.ExpandableListContextMenuInfo; import android.widget.TextView; +import android.widget.Toast; import java.util.List; import java.util.Vector; @@ -110,8 +112,7 @@ public class BrowserHistoryPage extends ExpandableListActivity { setListAdapter(mAdapter); final ExpandableListView list = getExpandableListView(); list.setOnCreateContextMenuListener(this); - LayoutInflater factory = LayoutInflater.from(this); - View v = factory.inflate(R.layout.empty_history, null); + View v = new ViewStub(this, R.layout.empty_history); addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); list.setEmptyView(v); @@ -222,6 +223,11 @@ public class BrowserHistoryPage extends ExpandableListActivity { Browser.deleteFromHistory(getContentResolver(), url); mAdapter.refreshData(); return true; + case R.id.homepage_context_menu_id: + BrowserSettings.getInstance().setHomePage(this, url); + Toast.makeText(this, R.string.homepage_set, + Toast.LENGTH_LONG).show(); + return true; default: break; } @@ -267,18 +273,25 @@ public class BrowserHistoryPage extends ExpandableListActivity { // Array for each of our bins. Each entry represents how many items are // in that bin. - int mItemMap[]; + private int mItemMap[]; // This is our GroupCount. We will have at most DateSorter.DAY_COUNT // bins, less if the user has no items in one or more bins. - int mNumberOfBins; - Vector<DataSetObserver> mObservers; - Cursor mCursor; + private int mNumberOfBins; + private Vector<DataSetObserver> mObservers; + private Cursor mCursor; HistoryAdapter() { mObservers = new Vector<DataSetObserver>(); - String whereClause = Browser.BookmarkColumns.VISITS + " > 0 "; - String orderBy = Browser.BookmarkColumns.DATE + " DESC"; + final String whereClause = Browser.BookmarkColumns.VISITS + " > 0" + // In AddBookmarkPage, where we save new bookmarks, we add + // three visits to newly created bookmarks, so that + // bookmarks that have not been visited will show up in the + // most visited, and higher in the goto search box. + // However, this puts the site in the history, unless we + // ignore sites with a DATE of 0, which the next line does. + + " AND " + Browser.BookmarkColumns.DATE + " > 0"; + final String orderBy = Browser.BookmarkColumns.DATE + " DESC"; mCursor = managedQuery( Browser.BOOKMARKS_URI, diff --git a/src/com/android/browser/BrowserHomepagePreference.java b/src/com/android/browser/BrowserHomepagePreference.java index d4708c3..7324f24 100644 --- a/src/com/android/browser/BrowserHomepagePreference.java +++ b/src/com/android/browser/BrowserHomepagePreference.java @@ -50,8 +50,8 @@ public class BrowserHomepagePreference extends EditTextPreference implements if (dialog != null) { String url = s.toString(); dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled( - url.length() == 0 || url.equals("about:blank") || - Regex.WEB_URL_PATTERN.matcher(url).matches()); + url.length() == 0 || + BrowserActivity.ACCEPTED_URI_SCHEMA.matcher(url).matches()); } } diff --git a/src/com/android/browser/BrowserPreferencesPage.java b/src/com/android/browser/BrowserPreferencesPage.java index 5d6795b..c6ce8a6 100644 --- a/src/com/android/browser/BrowserPreferencesPage.java +++ b/src/com/android/browser/BrowserPreferencesPage.java @@ -17,12 +17,19 @@ package com.android.browser; import java.util.List; +import java.util.Vector; +import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.preference.EditTextPreference; +import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceActivity; +import android.preference.PreferenceScreen; +import android.util.Log; +import android.webkit.Plugin; +import android.webkit.WebStorage; import android.webkit.WebView; import android.webkit.Plugin; @@ -30,6 +37,8 @@ public class BrowserPreferencesPage extends PreferenceActivity implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { + String TAG = "BrowserPreferencesPage"; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -60,6 +69,32 @@ public class BrowserPreferencesPage extends PreferenceActivity e = findPreference(BrowserSettings.PREF_GEARS_SETTINGS); e.setOnPreferenceClickListener(this); + + PreferenceScreen manageDatabases = (PreferenceScreen) + findPreference(BrowserSettings.PREF_WEBSTORAGE_SETTINGS); + + Preference clearDatabases = + findPreference(BrowserSettings.PREF_WEBSTORAGE_CLEAR_ALL); + + Vector origins = WebStorage.getInstance().getOrigins(); + manageDatabases.setEnabled(false); + clearDatabases.setEnabled(false); + if (origins != null) { + if (origins.size() > 0) { + manageDatabases.setEnabled(true); + clearDatabases.setEnabled(true); + } + for (int i = 0; i < origins.size(); i++) { + OriginSettings origin = + new OriginSettings(this, (String) origins.get(i)); + PreferenceScreen screen = + getPreferenceManager().createPreferenceScreen(this); + origin.setScreen(screen); + origin.setRootScreen(manageDatabases); + origin.setup(); + manageDatabases.addPreference(screen); + } + } } @Override diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/BrowserProvider.java index d2f4a0a..8ed889e 100644 --- a/src/com/android/browser/BrowserProvider.java +++ b/src/com/android/browser/BrowserProvider.java @@ -65,8 +65,9 @@ public class BrowserProvider extends ContentProvider { "_id", "url", "title", "bookmark" }; private static final String SUGGEST_SELECTION = - "url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?"; - private String[] SUGGEST_ARGS = new String[4]; + "url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?" + + " OR title LIKE ?"; + private String[] SUGGEST_ARGS = new String[5]; // shared suggestion array index, make sure to match COLUMNS private static final int SUGGEST_COLUMN_INTENT_ACTION_ID = 1; @@ -92,6 +93,8 @@ public class BrowserProvider extends ContentProvider { private static final int MAX_SUGGESTION_SHORT_ENTRIES = 3; private static final int MAX_SUGGESTION_LONG_ENTRIES = 6; + private static final String MAX_SUGGESTION_LONG_ENTRIES_STRING = + Integer.valueOf(MAX_SUGGESTION_LONG_ENTRIES).toString(); // make sure that these match the index of TABLE_NAMES private static final int URI_MATCH_BOOKMARKS = 0; @@ -407,22 +410,22 @@ public class BrowserProvider extends ContentProvider { case SUGGEST_COLUMN_ICON_1_ID: if (mHistoryCount > mPos) { if (mHistoryCursor.getInt(3) == 1) { - return new Integer( + return Integer.valueOf( R.drawable.ic_search_category_bookmark) .toString(); } else { - return new Integer( + return Integer.valueOf( R.drawable.ic_search_category_history) .toString(); } } else { - return new Integer( + return Integer.valueOf( R.drawable.ic_search_category_suggest) .toString(); } case SUGGEST_COLUMN_ICON_2_ID: - return new String("0"); + return "0"; case SUGGEST_COLUMN_QUERY_ID: if (mHistoryCount > mPos) { @@ -573,7 +576,8 @@ public class BrowserProvider extends ContentProvider { myArgs = null; } else { String like = selectionArgs[0] + "%"; - if (selectionArgs[0].startsWith("http")) { + if (selectionArgs[0].startsWith("http") + || selectionArgs[0].startsWith("file")) { myArgs = new String[1]; myArgs[0] = like; suggestSelection = selection; @@ -582,6 +586,8 @@ public class BrowserProvider extends ContentProvider { SUGGEST_ARGS[1] = "http://www." + like; SUGGEST_ARGS[2] = "https://" + like; SUGGEST_ARGS[3] = "https://www." + like; + // To match against titles. + SUGGEST_ARGS[4] = like; myArgs = SUGGEST_ARGS; suggestSelection = SUGGEST_SELECTION; } @@ -589,8 +595,7 @@ public class BrowserProvider extends ContentProvider { Cursor c = db.query(TABLE_NAMES[URI_MATCH_BOOKMARKS], SUGGEST_PROJECTION, suggestSelection, myArgs, null, null, - ORDER_BY, - (new Integer(MAX_SUGGESTION_LONG_ENTRIES)).toString()); + ORDER_BY, MAX_SUGGESTION_LONG_ENTRIES_STRING); if (match == URI_MATCH_BOOKMARKS_SUGGEST || Regex.WEB_URL_PATTERN.matcher(selectionArgs[0]).matches()) { diff --git a/src/com/android/browser/BrowserQuotaPreference.java b/src/com/android/browser/BrowserQuotaPreference.java new file mode 100644 index 0000000..e635bb2 --- /dev/null +++ b/src/com/android/browser/BrowserQuotaPreference.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser; + +import android.preference.ListPreference; +import android.content.Context; +import android.preference.PreferenceScreen; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.webkit.WebStorage; + +import java.util.Vector; + +/** + * Utility class to display and manage the choosen quota + * for an origin (HTML5 WebStorage feature) + */ +class BrowserQuotaPreference extends ListPreference { + + private String TAG = "BrowserQuotaPreference"; + private OriginSettings mOrigin = null; + private static long sOneMB = 1024 * 1024; + + // This is the constructor called by the inflater + public BrowserQuotaPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public BrowserQuotaPreference(Context context, OriginSettings origin) { + super(context); + mOrigin = origin; + } + + /** + * Find the minimum quota fitting the current usage + * and only show larger quotas in the list + */ + public void setQuotaList () { + CharSequence[] entries = getEntries(); + CharSequence[] values = getEntryValues(); + Vector<CharSequence> listEntries = new Vector<CharSequence>(); + Vector<CharSequence> listValues = new Vector<CharSequence>(); + long usage = 0; + if (mOrigin != null) { + usage = mOrigin.getUsage(); + } + for (int i = 0; i < values.length; i++) { + long value = Long.parseLong(values[i].toString()); + value *= sOneMB; // the string array is expressed in MB + if (value >= usage) { + listEntries.add(entries[i]); + listValues.add(values[i]); + } + } + CharSequence[] newEntries = new CharSequence[listEntries.size()]; + CharSequence[] newValues = new CharSequence[listValues.size()]; + for (int i = 0; i < listEntries.size(); i++) { + newEntries[i] = listEntries.get(i); + newValues[i] = listValues.get(i); + } + setEntries(newEntries); + setEntryValues(newValues); + setValueIndex(0); + } + + @Override + protected View onCreateDialogView() { + setQuotaList(); + return super.onCreateDialogView(); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + super.onDialogClosed(positiveResult); + if (mOrigin == null) { + return; + } + if (positiveResult) { + long quota = Long.parseLong(getValue()); + quota *= sOneMB; // getValue() is in MB + mOrigin.setQuota(quota); + } + } +} diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java index 68438b3..34cab94 100644 --- a/src/com/android/browser/BrowserSettings.java +++ b/src/com/android/browser/BrowserSettings.java @@ -25,6 +25,9 @@ import android.content.pm.ActivityInfo; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.os.SystemProperties; +import android.preference.PreferenceActivity; +import android.preference.PreferenceScreen; +import android.util.Log; import android.view.WindowManager; import android.webkit.CacheManager; import android.webkit.CookieManager; @@ -32,6 +35,7 @@ import android.webkit.WebView; import android.webkit.WebViewDatabase; import android.webkit.WebIconDatabase; import android.webkit.WebSettings; +import android.webkit.WebStorage; import android.preference.PreferenceManager; import android.provider.Browser; @@ -53,7 +57,7 @@ import java.util.Observable; */ class BrowserSettings extends Observable { - // Public variables for settings + // Private variables for settings // NOTE: these defaults need to be kept in sync with the XML // until the performance of PreferenceManager.setDefaultValues() // is improved. @@ -70,7 +74,16 @@ class BrowserSettings extends Observable { private String homeUrl = ""; private boolean loginInitialized = false; private boolean autoFitPage = true; + private boolean landscapeOnly = false; private boolean showDebugSettings = false; + private String databasePath; // default value set in loadFromDb() + private boolean databaseEnabled = true; + private long webStorageDefaultQuota = 5 * 1024 * 1024; + // The Browser always enables Application Caches. + private boolean appCacheEnabled = true; + private String appCachePath; // default value set in loadFromDb(). + + private final static String TAG = "BrowserSettings"; // Development settings public WebSettings.LayoutAlgorithm layoutAlgorithm = @@ -100,10 +113,20 @@ class BrowserSettings extends Observable { "privacy_clear_form_data"; public final static String PREF_CLEAR_PASSWORDS = "privacy_clear_passwords"; + public final static String PREF_CLEAR_DATABASES = + "webstorage_clear_databases"; + public final static String PREF_CLEAR_ALL_DATA = + "webstorage_clear_all_data"; + public final static String PREF_MANAGE_QUOTA = + "webstorage_manage_quota"; + public final static String PREF_DEFAULT_QUOTA = + "webstorage_default_quota"; public final static String PREF_EXTRAS_RESET_DEFAULTS = "reset_default_preferences"; public final static String PREF_DEBUG_SETTINGS = "debug_menu"; public final static String PREF_GEARS_SETTINGS = "gears_settings"; + public final static String PREF_WEBSTORAGE_SETTINGS = "webstorage_manage_databases"; + public final static String PREF_WEBSTORAGE_CLEAR_ALL = "webstorage_clear_databases"; public final static String PREF_TEXT_SIZE = "text_size"; public final static String PREF_DEFAULT_TEXT_ENCODING = "default_text_encoding"; @@ -159,7 +182,6 @@ class BrowserSettings extends Observable { s.setLoadsImagesAutomatically(b.loadsImagesAutomatically); s.setJavaScriptEnabled(b.javaScriptEnabled); s.setPluginsEnabled(b.pluginsEnabled); - s.setPluginsPath(b.pluginsPath); s.setJavaScriptCanOpenWindowsAutomatically( b.javaScriptCanOpenWindowsAutomatically); s.setDefaultTextEncodingName(b.defaultTextEncodingName); @@ -179,6 +201,14 @@ class BrowserSettings extends Observable { s.setSupportMultipleWindows(true); // Turn off file access s.setAllowFileAccess(false); + + s.setDatabasePath(b.databasePath); + s.setDatabaseEnabled(b.databaseEnabled); + s.setWebStorageDefaultQuota(b.webStorageDefaultQuota); + + // Turn on Application Caches. + s.setAppCachePath(b.appCachePath); + s.setAppCacheEnabled(b.appCacheEnabled); } } @@ -198,6 +228,10 @@ class BrowserSettings extends Observable { // Set the default value for the plugins path to the application's // local directory. pluginsPath = ctx.getDir("plugins", 0).getPath(); + // Set the default value for the Application Caches path. + appCachePath = ctx.getDir("appcache", 0).getPath(); + // Set the default value for the Database path. + databasePath = ctx.getDir("databases", 0).getPath(); homeUrl = getFactoryResetHomeUrl(ctx); @@ -220,6 +254,13 @@ class BrowserSettings extends Observable { pluginsEnabled = p.getBoolean("enable_plugins", pluginsEnabled); pluginsPath = p.getString("plugins_path", pluginsPath); + databasePath = p.getString("database_path", databasePath); + databaseEnabled = p.getBoolean("enable_database", databaseEnabled); + webStorageDefaultQuota = Long.parseLong(p.getString(PREF_DEFAULT_QUOTA, + String.valueOf(webStorageDefaultQuota))); + appCacheEnabled = p.getBoolean("enable_appcache", + appCacheEnabled); + appCachePath = p.getString("appcache_path", appCachePath); javaScriptCanOpenWindowsAutomatically = !p.getBoolean( "block_popup_windows", !javaScriptCanOpenWindowsAutomatically); @@ -237,6 +278,14 @@ class BrowserSettings extends Observable { textSize = WebSettings.TextSize.valueOf( p.getString(PREF_TEXT_SIZE, textSize.name())); autoFitPage = p.getBoolean("autofit_pages", autoFitPage); + boolean landscapeOnlyTemp = + p.getBoolean("landscape_only", landscapeOnly); + if (landscapeOnlyTemp != landscapeOnly) { + landscapeOnly = landscapeOnlyTemp; + mTabControl.getBrowserActivity().setRequestedOrientation( + landscapeOnly ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + } useWideViewPort = true; // use wide view port for either setting if (autoFitPage) { layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS; @@ -433,14 +482,24 @@ class BrowserSettings extends Observable { db.clearHttpAuthUsernamePassword(); } - /*package*/ void resetDefaultPreferences(Context context) { + /*package*/ void clearDatabases(Context context) { + WebStorage.getInstance().deleteAllDatabases(); + // Remove all listed databases from the preferences + PreferenceActivity activity = (PreferenceActivity) context; + PreferenceScreen screen = (PreferenceScreen) + activity.findPreference(BrowserSettings.PREF_WEBSTORAGE_SETTINGS); + screen.removeAll(); + screen.setEnabled(false); + } + + /*package*/ void resetDefaultPreferences(Context ctx) { SharedPreferences p = - PreferenceManager.getDefaultSharedPreferences(context); + PreferenceManager.getDefaultSharedPreferences(ctx); p.edit().clear().commit(); - PreferenceManager.setDefaultValues(context, R.xml.browser_preferences, + PreferenceManager.setDefaultValues(ctx, R.xml.browser_preferences, true); // reset homeUrl - setHomePage(context, getFactoryResetHomeUrl(context)); + setHomePage(ctx, getFactoryResetHomeUrl(ctx)); } private String getFactoryResetHomeUrl(Context context) { diff --git a/src/com/android/browser/BrowserYesNoPreference.java b/src/com/android/browser/BrowserYesNoPreference.java index 65cde71..11a577b 100644 --- a/src/com/android/browser/BrowserYesNoPreference.java +++ b/src/com/android/browser/BrowserYesNoPreference.java @@ -23,11 +23,21 @@ import android.util.AttributeSet; class BrowserYesNoPreference extends YesNoPreference { + // This is used for the HTML5 pref UI, where we construct + // BrowserYesNoPreference objects on the fly and where we need + // to save the corresponding origin. + OriginSettings mOrigin = null; + // This is the constructor called by the inflater public BrowserYesNoPreference(Context context, AttributeSet attrs) { super(context, attrs); } + public BrowserYesNoPreference(Context context, OriginSettings origin) { + super(context); + mOrigin = origin; + } + @Override protected void onDialogClosed(boolean positiveResult) { super.onDialogClosed(positiveResult); @@ -46,6 +56,12 @@ class BrowserYesNoPreference extends YesNoPreference { BrowserSettings.getInstance().clearFormData(context); } else if (BrowserSettings.PREF_CLEAR_PASSWORDS.equals(getKey())) { BrowserSettings.getInstance().clearPasswords(context); + } else if (BrowserSettings.PREF_CLEAR_DATABASES.equals(getKey())) { + BrowserSettings.getInstance().clearDatabases(context); + } else if (BrowserSettings.PREF_CLEAR_ALL_DATA.equals(getKey())) { + if (mOrigin != null) { + mOrigin.delete(); + } } else if (BrowserSettings.PREF_EXTRAS_RESET_DEFAULTS.equals( getKey())) { BrowserSettings.getInstance().resetDefaultPreferences(context); diff --git a/src/com/android/browser/FetchUrlMimeType.java b/src/com/android/browser/FetchUrlMimeType.java index 8578643..c585dbb 100644 --- a/src/com/android/browser/FetchUrlMimeType.java +++ b/src/com/android/browser/FetchUrlMimeType.java @@ -58,7 +58,7 @@ class FetchUrlMimeType extends AsyncTask<ContentValues, String, String> { mValues = values[0]; // Check to make sure we have a URI to download - String uri = mValues.getAsString(Downloads.URI); + String uri = mValues.getAsString(Downloads.COLUMN_URI); if (uri == null || uri.length() == 0) { return null; } @@ -66,15 +66,15 @@ class FetchUrlMimeType extends AsyncTask<ContentValues, String, String> { // User agent is likely to be null, though the AndroidHttpClient // seems ok with that. AndroidHttpClient client = AndroidHttpClient.newInstance( - mValues.getAsString(Downloads.USER_AGENT)); + mValues.getAsString(Downloads.COLUMN_USER_AGENT)); HttpHead request = new HttpHead(uri); - String cookie = mValues.getAsString(Downloads.COOKIE_DATA); + String cookie = mValues.getAsString(Downloads.COLUMN_COOKIE_DATA); if (cookie != null && cookie.length() > 0) { request.addHeader("Cookie", cookie); } - String referer = mValues.getAsString(Downloads.REFERER); + String referer = mValues.getAsString(Downloads.COLUMN_REFERER); if (referer != null && referer.length() > 0) { request.addHeader("Referer", referer); } @@ -111,19 +111,19 @@ class FetchUrlMimeType extends AsyncTask<ContentValues, String, String> { @Override public void onPostExecute(String mimeType) { if (mimeType != null) { - String url = mValues.getAsString(Downloads.URI); + String url = mValues.getAsString(Downloads.COLUMN_URI); if (mimeType.equalsIgnoreCase("text/plain") || mimeType.equalsIgnoreCase("application/octet-stream")) { String newMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( MimeTypeMap.getFileExtensionFromUrl(url)); if (newMimeType != null) { - mValues.put(Downloads.MIMETYPE, newMimeType); + mValues.put(Downloads.COLUMN_MIME_TYPE, newMimeType); } } String filename = URLUtil.guessFileName(url, null, mimeType); - mValues.put(Downloads.FILENAME_HINT, filename); + mValues.put(Downloads.COLUMN_FILE_NAME_HINT, filename); } // Start the download diff --git a/src/com/android/browser/FindDialog.java b/src/com/android/browser/FindDialog.java index 6e9574c..2049bd0 100644 --- a/src/com/android/browser/FindDialog.java +++ b/src/com/android/browser/FindDialog.java @@ -42,7 +42,6 @@ import android.widget.TextView; private BrowserActivity mBrowserActivity; // Views with which the user can interact. - private View mOk; private EditText mEditText; private View mNextButton; private View mPrevButton; @@ -129,7 +128,6 @@ import android.widget.TextView; button = findViewById(R.id.done); button.setOnClickListener(mFindCancelListener); - mOk = button; mMatches = (TextView) findViewById(R.id.matches); mMatchesView = findViewById(R.id.matches_view); @@ -143,23 +141,14 @@ import android.widget.TextView; mBrowserActivity.closeFind(); mWebView.clearMatches(); } - + @Override public boolean dispatchKeyEvent(KeyEvent event) { - int code = event.getKeyCode(); - boolean up = event.getAction() == KeyEvent.ACTION_UP; - switch (code) { - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_ENTER: - if (!mEditText.hasFocus()) { - break; - } - if (up) { - findNext(); - } - return true; - default: - break; + if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER + && event.getAction() == KeyEvent.ACTION_UP + && mEditText.hasFocus()) { + findNext(); + return true; } return super.dispatchKeyEvent(event); } diff --git a/src/com/android/browser/MostVisitedActivity.java b/src/com/android/browser/MostVisitedActivity.java index 704ee27..90052d3 100644 --- a/src/com/android/browser/MostVisitedActivity.java +++ b/src/com/android/browser/MostVisitedActivity.java @@ -35,6 +35,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; +import android.view.ViewStub; import java.util.Vector; @@ -50,8 +51,7 @@ public class MostVisitedActivity extends ListActivity { .addListener(new IconReceiver()); setListAdapter(mAdapter); ListView list = getListView(); - LayoutInflater factory = LayoutInflater.from(this); - View v = factory.inflate(R.layout.empty_history, null); + View v = new ViewStub(this, R.layout.empty_history); addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); list.setEmptyView(v); @@ -84,9 +84,9 @@ public class MostVisitedActivity extends ListActivity { private Vector<DataSetObserver> mObservers; private Cursor mCursor; // These correspond with projection below. - private final int mUrlIndex = 0; - private final int mTitleIndex = 1; - private final int mBookmarkIndex = 2; + private static final int mUrlIndex = 0; + private static final int mTitleIndex = 1; + private static final int mBookmarkIndex = 2; MyAdapter() { mObservers = new Vector<DataSetObserver>(); diff --git a/src/com/android/browser/OriginSettings.java b/src/com/android/browser/OriginSettings.java new file mode 100644 index 0000000..e80888a --- /dev/null +++ b/src/com/android/browser/OriginSettings.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser; + +import android.content.Context; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceScreen; +import android.util.Log; +import android.webkit.WebStorage; + +/** + * Manage the settings for an origin. + * We use it to keep track of the HTML5 settings, i.e. database (webstorage). + */ +class OriginSettings { + + private String TAG = "OriginSettings"; + private String mOrigin = null; + private long mQuota = 0; + private long mUsage = 0; + private PreferenceScreen mInfoScreen; + private PreferenceScreen mRootScreen; + private PreferenceActivity mActivity; + + private static String sMBUsed = null; + private static String sNoQuotaLeft = null; + private static String sMBLeft = null; + + public OriginSettings(PreferenceActivity activity, String origin) { + mOrigin = origin; + mUsage = WebStorage.getInstance().getUsageForOrigin(mOrigin); + mQuota = WebStorage.getInstance().getQuotaForOrigin(mOrigin); + mActivity = activity; + if (sMBUsed == null) { + sMBUsed = mActivity.getString( + R.string.webstorage_origin_summary_mb_used); + sNoQuotaLeft = mActivity.getString( + R.string.webstorage_origin_summary_no_quota_left); + sMBLeft = mActivity.getString( + R.string.webstorage_origin_summary_mb_left); + } + } + + public String getOrigin() { + return mOrigin; + } + + public long getQuota() { + return mQuota; + } + + public long getUsage() { + return mUsage; + } + + public void setScreen(PreferenceScreen screen) { + mInfoScreen = screen; + } + + public void setRootScreen(PreferenceScreen screen) { + mRootScreen = screen; + } + + private String sizeValueToString(long value) { + float mb = (float) value / (1024.0F * 1024.0F); + int val = (int) (mb * 10); + float ret = (float) (val / 10.0F); + if (ret <= 0) { + return "0"; + } + return String.valueOf(ret); + } + + public void updateSummary() { + String summary = sizeValueToString(mUsage) + " " + sMBUsed; + if ((mQuota <= 0) || ((mQuota - mUsage) <= 0)) { + summary += ", " + sNoQuotaLeft; + } else { + summary += " (" + sizeValueToString(mQuota - mUsage); + summary += " " + sMBLeft + ")"; + } + mInfoScreen.setSummary(summary); + mActivity.onContentChanged(); + } + + public void setup() { + mInfoScreen.setTitle(mOrigin); + mInfoScreen.setKey(mOrigin); + updateSummary(); + + BrowserQuotaPreference manageSite = new BrowserQuotaPreference(mActivity, this); + BrowserYesNoPreference clearAllData = new BrowserYesNoPreference(mActivity, this); + + manageSite.setTitle(R.string.webstorage_manage_quota_title); + manageSite.setSummary(R.string.webstorage_manage_quota_summary); + manageSite.setKey(BrowserSettings.PREF_MANAGE_QUOTA); + manageSite.setEntries(R.array.webstorage_quota_entries); + manageSite.setEntryValues(R.array.webstorage_quota_entries_values); + + clearAllData.setTitle(R.string.webstorage_clear_data_title); + clearAllData.setSummary(R.string.webstorage_clear_data_summary); + clearAllData.setKey(BrowserSettings.PREF_CLEAR_ALL_DATA); + clearAllData.setDialogTitle(R.string.webstorage_clear_data_dialog_title); + clearAllData.setDialogMessage(R.string.webstorage_clear_data_dialog_message); + clearAllData.setDialogIcon(android.R.drawable.ic_dialog_alert); + + mInfoScreen.addPreference(manageSite); + mInfoScreen.addPreference(clearAllData); + } + + public void setQuota(long quota) { + mQuota = quota; + WebStorage.getInstance().setQuotaForOrigin(mOrigin, mQuota); + mInfoScreen.getDialog().dismiss(); + updateSummary(); + } + + public void delete() { + WebStorage.getInstance().deleteOrigin(mOrigin); + mInfoScreen.removeAll(); + mRootScreen.removePreference(mInfoScreen); + mInfoScreen.getDialog().dismiss(); + if (mRootScreen.getPreferenceCount() == 0) { + mRootScreen.getDialog().dismiss(); + mRootScreen.setEnabled(false); + Preference clearDatabases = mActivity.findPreference( + BrowserSettings.PREF_WEBSTORAGE_CLEAR_ALL); + clearDatabases.setEnabled(false); + } + } +} diff --git a/src/com/android/browser/PermissionDialog.java b/src/com/android/browser/PermissionDialog.java new file mode 100644 index 0000000..b71261a --- /dev/null +++ b/src/com/android/browser/PermissionDialog.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.Window; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +/** + * Permission dialog for HTML5 + * @hide + */ +public class PermissionDialog extends Activity { + + private static final String TAG = "PermissionDialog"; + public static final String PARAM_ORIGIN = "origin"; + public static final String PARAM_QUOTA = "quota"; + + private String mWebStorageOrigin; + private long mWebStorageQuota = 0; + private int mNotification = 0; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + getParameters(); + setupDialog(); + } + + private void getParameters() { + Intent intent = getIntent(); + mWebStorageOrigin = intent.getStringExtra(PARAM_ORIGIN); + mWebStorageQuota = intent.getLongExtra(PARAM_QUOTA, 0); + } + + private void setupDialog() { + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.permission_dialog); + + setIcon(R.id.icon, android.R.drawable.ic_popup_disk_full); + setText(R.id.dialog_title, R.string.query_storage_quota_prompt); + setText(R.id.dialog_message, R.string.query_storage_quota_message); + setCharSequence(R.id.origin, mWebStorageOrigin); + + setupButton(R.id.button_allow, R.string.permission_button_allow, + new View.OnClickListener() { + public void onClick(View v) { allow(); } + }); + setupButton(R.id.button_alwaysdeny, R.string.permission_button_alwaysdeny, + new View.OnClickListener() { + public void onClick(View v) { alwaysdeny(); } + }); + setupButton(R.id.button_deny, R.string.permission_button_deny, + new View.OnClickListener() { + public void onClick(View v) { deny(); } + }); + } + + private void setText(int viewID, int stringID) { + setCharSequence(viewID, getString(stringID)); + } + + private void setCharSequence(int viewID, CharSequence string) { + View view = findViewById(viewID); + if (view == null) { + return; + } + view.setVisibility(View.VISIBLE); + TextView textView = (TextView) view; + textView.setText(string); + } + + private void setIcon(int viewID, int imageID) { + View view = findViewById(viewID); + if (view == null) { + return; + } + view.setVisibility(View.VISIBLE); + ImageView icon = (ImageView) view; + icon.setImageResource(imageID); + } + + private void setupButton(int viewID, int stringID, + View.OnClickListener listener) { + View view = findViewById(viewID); + if (view == null) { + return; + } + setText(viewID, stringID); + view.setOnClickListener(listener); + } + + private void useNextQuota() { + CharSequence[] values = getResources().getTextArray( + R.array.webstorage_quota_entries_values); + for (int i=0; i<values.length; i++) { + long value = Long.parseLong(values[i].toString()); + value *= (1024 * 1024); // the string array is expressed in MB + if (value > mWebStorageQuota) { + mWebStorageQuota = value; + break; + } + } + } + + private void allow() { + // If somehow there is no "next quota" in the ladder, + // we'll add 1MB anyway. + mWebStorageQuota += 1024*1024; + useNextQuota(); + mNotification = R.string.webstorage_notification; + closeDialog(); + } + + private void alwaysdeny() { + // Setting the quota to 0 will prevent any new data to be + // added, but the existing data will not be deleted. + mWebStorageQuota = 0; + mNotification = R.string.webstorage_notification; + closeDialog(); + } + + private void deny() { + closeDialog(); + } + + private void closeDialog() { + Intent intent = new Intent(); + intent.putExtra(PARAM_QUOTA, mWebStorageQuota); + setResult(RESULT_OK, intent); + showToast(); + finish(); + } + + private void showToast() { + if (mNotification != 0) { + Toast toast = Toast.makeText(this, mNotification, Toast.LENGTH_LONG); + toast.setGravity(Gravity.BOTTOM, 0, 0); + toast.show(); + } + } + + public boolean dispatchKeyEvent(KeyEvent event) { + if ((event.getKeyCode() == KeyEvent.KEYCODE_BACK) + && (event.getAction() == KeyEvent.ACTION_DOWN)) { + closeDialog(); + return true; // event consumed + } + return super.dispatchKeyEvent(event); + } + +} diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java index 581d144..f66df69 100644 --- a/src/com/android/browser/TabControl.java +++ b/src/com/android/browser/TabControl.java @@ -446,6 +446,9 @@ class TabControl { * @return index of Tab or -1 if not found */ int getTabIndex(Tab tab) { + if (tab == null) { + return -1; + } return mTabs.indexOf(tab); } @@ -681,11 +684,11 @@ class TabControl { return; } - // free the WebView cache - Log.w(LOGTAG, "Free WebView cache"); + // free the WebView's unused memory (this includes the cache) + Log.w(LOGTAG, "Free WebView's unused memory and cache"); WebView view = getCurrentWebView(); if (view != null) { - view.clearCache(false); + view.freeMemory(); } // force a gc System.gc(); @@ -864,6 +867,48 @@ class TabControl { return setCurrentTab(newTab, false); } + /*package*/ void pauseCurrentTab() { + Tab t = getCurrentTab(); + if (t != null) { + t.mMainView.onPause(); + if (t.mSubView != null) { + t.mSubView.onPause(); + } + } + } + + /*package*/ void resumeCurrentTab() { + Tab t = getCurrentTab(); + if (t != null) { + t.mMainView.onResume(); + if (t.mSubView != null) { + t.mSubView.onResume(); + } + } + } + + private void putViewInForeground(WebView v, WebViewClient vc, + WebChromeClient cc) { + v.setWebViewClient(vc); + v.setWebChromeClient(cc); + v.setOnCreateContextMenuListener(mActivity); + v.setDownloadListener(mActivity); + v.onResume(); + } + + private void putViewInBackground(WebView v) { + // Set an empty callback so that default actions are not triggered. + v.setWebViewClient(mEmptyClient); + v.setWebChromeClient(mBackgroundChromeClient); + v.setOnCreateContextMenuListener(null); + // Leave the DownloadManager attached so that downloads can start in + // a non-active window. This can happen when going to a site that does + // a redirect after a period of time. The user could have switched to + // another tab while waiting for the download to start. + v.setDownloadListener(mActivity); + v.onPause(); + } + /** * If force is true, this method skips the check for newTab == current. */ @@ -889,7 +934,6 @@ class TabControl { mTabQueue.add(newTab); WebView mainView; - WebView subView; // Display the new current tab mCurrentTab = mTabs.indexOf(newTab); @@ -899,17 +943,12 @@ class TabControl { // Same work as in createNewTab() except don't do new Tab() newTab.mMainView = mainView = createNewWebView(); } - mainView.setWebViewClient(mActivity.getWebViewClient()); - mainView.setWebChromeClient(mActivity.getWebChromeClient()); - mainView.setOnCreateContextMenuListener(mActivity); - mainView.setDownloadListener(mActivity); + putViewInForeground(mainView, mActivity.getWebViewClient(), + mActivity.getWebChromeClient()); // Add the subwindow if it exists if (newTab.mSubViewContainer != null) { - subView = newTab.mSubView; - subView.setWebViewClient(newTab.mSubViewClient); - subView.setWebChromeClient(newTab.mSubViewChromeClient); - subView.setOnCreateContextMenuListener(mActivity); - subView.setDownloadListener(mActivity); + putViewInForeground(newTab.mSubView, newTab.mSubViewClient, + newTab.mSubViewChromeClient); } if (needRestore) { // Have to finish setCurrentTab work before calling restoreState @@ -924,23 +963,9 @@ class TabControl { * Put the tab in the background using all the empty/background clients. */ private void putTabInBackground(Tab t) { - WebView mainView = t.mMainView; - // Set an empty callback so that default actions are not triggered. - mainView.setWebViewClient(mEmptyClient); - mainView.setWebChromeClient(mBackgroundChromeClient); - mainView.setOnCreateContextMenuListener(null); - // Leave the DownloadManager attached so that downloads can start in - // a non-active window. This can happen when going to a site that does - // a redirect after a period of time. The user could have switched to - // another tab while waiting for the download to start. - mainView.setDownloadListener(mActivity); - WebView subView = t.mSubView; - if (subView != null) { - // Set an empty callback so that default actions are not triggered. - subView.setWebViewClient(mEmptyClient); - subView.setWebChromeClient(mBackgroundChromeClient); - subView.setOnCreateContextMenuListener(null); - subView.setDownloadListener(mActivity); + putViewInBackground(t.mMainView); + if (t.mSubView != null) { + putViewInBackground(t.mSubView); } } diff --git a/src/com/android/browser/TitleBar.java b/src/com/android/browser/TitleBar.java new file mode 100644 index 0000000..f43058b --- /dev/null +++ b/src/com/android/browser/TitleBar.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.webkit.WebView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +public class TitleBar extends LinearLayout { + private TextView mTitle; + private TextView mUrl; + private ImageView mLftButton; + private Drawable mBookmarkDrawable; + private View mRtButton; + private View mDivider; + private ProgressBar mCircularProgress; + private ProgressBar mHorizontalProgress; + private ImageView mFavicon; + private ImageView mLockIcon; + private boolean mInLoad; + + public TitleBar(Context context) { + this(context, null); + } + + public TitleBar(Context context, AttributeSet attrs) { + super(context, attrs); + LayoutInflater factory = LayoutInflater.from(context); + factory.inflate(R.layout.title_bar, this); + + mTitle = (TextView) findViewById(R.id.title); + mUrl = (TextView) findViewById(R.id.url); + + mLftButton = (ImageView) findViewById(R.id.lft_button); + mRtButton = findViewById(R.id.rt_button); + + mCircularProgress = (ProgressBar) findViewById(R.id.progress_circular); + mHorizontalProgress = (ProgressBar) findViewById( + R.id.progress_horizontal); + mFavicon = (ImageView) findViewById(R.id.favicon); + mLockIcon = (ImageView) findViewById(R.id.lock_icon); + mDivider = findViewById(R.id.divider); + } + + /* package */ void setBrowserActivity(final BrowserActivity activity) { + mLftButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + if (mInLoad) { + WebView webView = activity.getTopWindow(); + if (webView != null) { + webView.stopLoading(); + } + } else { + activity.bookmarksOrHistoryPicker(false); + } + } + }); + mRtButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + WebView webView = activity.getTopWindow(); + if (webView != null) { + webView.zoomScrollOut(); + } + } + }); + setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + activity.onSearchRequested(); + } + }); + } + + /* package */ void setFavicon(Drawable d) { + mFavicon.setImageDrawable(d); + } + + /* package */ void setLock(Drawable d) { + if (d == null) { + mLockIcon.setVisibility(View.GONE); + } else { + mLockIcon.setImageDrawable(d); + mLockIcon.setVisibility(View.VISIBLE); + } + } + + /* package */ void setProgress(int newProgress) { + if (newProgress == mCircularProgress.getMax()) { + mCircularProgress.setVisibility(View.GONE); + mHorizontalProgress.setVisibility(View.GONE); + mDivider.setVisibility(View.VISIBLE); + mRtButton.setVisibility(View.VISIBLE); + mLftButton.setImageDrawable(mBookmarkDrawable); + mInLoad = false; + } else { + mCircularProgress.setProgress(newProgress); + mHorizontalProgress.setProgress(newProgress); + mCircularProgress.setVisibility(View.VISIBLE); + mHorizontalProgress.setVisibility(View.VISIBLE); + mDivider.setVisibility(View.GONE); + mRtButton.setVisibility(View.GONE); + if (mBookmarkDrawable == null) { + // The drawable was assigned in the xml file, so it already + // exists. Keep a pointer to it when we switch to the resource + // so we can easily switch back. + mBookmarkDrawable = mLftButton.getDrawable(); + } + mLftButton.setImageResource( + com.android.internal.R.drawable.ic_menu_stop); + mInLoad = true; + } + } + + /* package */ void setTitleAndUrl(CharSequence title, CharSequence url) { + if (null == title) { + mTitle.setText(R.string.title_bar_loading); + } else { + mTitle.setText(title); + } + mUrl.setText(url); + } + + /* package */ void setToTabPicker() { + mTitle.setText(R.string.tab_picker_title); + setFavicon(null); + setLock(null); + mCircularProgress.setVisibility(View.GONE); + mHorizontalProgress.setVisibility(View.GONE); + } +} |