summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/browser/AddBookmarkPage.java74
-rw-r--r--src/com/android/browser/BookmarkGridPage.java243
-rw-r--r--src/com/android/browser/Bookmarks.java196
-rw-r--r--src/com/android/browser/BrowserActivity.java467
-rw-r--r--src/com/android/browser/BrowserBookmarksAdapter.java15
-rw-r--r--src/com/android/browser/BrowserBookmarksPage.java9
-rw-r--r--src/com/android/browser/BrowserDownloadAdapter.java16
-rw-r--r--src/com/android/browser/BrowserDownloadPage.java28
-rw-r--r--src/com/android/browser/BrowserHistoryPage.java29
-rw-r--r--src/com/android/browser/BrowserHomepagePreference.java4
-rw-r--r--src/com/android/browser/BrowserPreferencesPage.java31
-rw-r--r--src/com/android/browser/BrowserProvider.java27
-rw-r--r--src/com/android/browser/BrowserSettings.java86
-rw-r--r--src/com/android/browser/BrowserYesNoPreference.java1
-rw-r--r--src/com/android/browser/CombinedBookmarkHistoryActivity.java2
-rw-r--r--src/com/android/browser/FetchUrlMimeType.java14
-rw-r--r--src/com/android/browser/FindDialog.java23
-rw-r--r--src/com/android/browser/HistoryItem.java61
-rw-r--r--src/com/android/browser/MostVisitedActivity.java10
-rw-r--r--src/com/android/browser/PermissionDialog.java175
-rw-r--r--src/com/android/browser/TabControl.java85
-rw-r--r--src/com/android/browser/TitleBar.java153
-rw-r--r--src/com/android/browser/WebsiteSettingsActivity.java291
23 files changed, 1685 insertions, 355 deletions
diff --git a/src/com/android/browser/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java
index cf3fe70..e827a7e 100644
--- a/src/com/android/browser/AddBookmarkPage.java
+++ b/src/com/android/browser/AddBookmarkPage.java
@@ -18,18 +18,14 @@ package com.android.browser;
import android.app.Activity;
import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
import android.content.Intent;
import android.content.res.Resources;
-import android.database.Cursor;
import android.net.ParseException;
import android.net.WebAddress;
import android.os.Bundle;
import android.provider.Browser;
import android.view.View;
import android.view.Window;
-import android.webkit.WebIconDatabase;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
@@ -46,11 +42,6 @@ public class AddBookmarkPage extends Activity {
private View mCancelButton;
private boolean mEditingExisting;
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];
private View.OnClickListener mSaveBookmark = new View.OnClickListener() {
public void onClick(View v) {
@@ -151,70 +142,7 @@ public class AddBookmarkPage extends Activity {
setResult(RESULT_OK, (new Intent()).setAction(
getIntent().toString()).putExtras(mMap));
} else {
- // Want to append to the beginning of the list
- long creationTime = new Date().getTime();
- SELECTION_ARGS[0] = url;
- ContentResolver cr = getContentResolver();
- Cursor c = cr.query(Browser.BOOKMARKS_URI,
- mProjection,
- WHERE_CLAUSE,
- SELECTION_ARGS,
- null);
- ContentValues map = new ContentValues();
- if (c.moveToFirst() && c.getInt(c.getColumnIndexOrThrow(
- Browser.BookmarkColumns.BOOKMARK)) == 0) {
- // This means we have been to this site but not bookmarked
- // it, so convert the history item to a bookmark
- map.put(Browser.BookmarkColumns.CREATED, creationTime);
- map.put(Browser.BookmarkColumns.TITLE, title);
- map.put(Browser.BookmarkColumns.BOOKMARK, 1);
- cr.update(Browser.BOOKMARKS_URI, map,
- "_id = " + c.getInt(0), null);
- } else {
- int count = c.getCount();
- boolean matchedTitle = false;
- for (int i = 0; i < count; i++) {
- // One or more bookmarks already exist for this site.
- // Check the names of each
- c.moveToPosition(i);
- if (c.getString(c.getColumnIndexOrThrow(
- Browser.BookmarkColumns.TITLE)).equals(title)) {
- // The old bookmark has the same name.
- // Update its creation time.
- map.put(Browser.BookmarkColumns.CREATED,
- creationTime);
- cr.update(Browser.BOOKMARKS_URI, map,
- "_id = " + c.getInt(0), null);
- matchedTitle = true;
- }
- }
- if (!matchedTitle) {
- // Adding a bookmark for a site the user has visited,
- // or a new bookmark (with a different name) for a site
- // the user has visited
- map.put(Browser.BookmarkColumns.TITLE, title);
- map.put(Browser.BookmarkColumns.URL, url);
- map.put(Browser.BookmarkColumns.CREATED, creationTime);
- map.put(Browser.BookmarkColumns.BOOKMARK, 1);
- map.put(Browser.BookmarkColumns.DATE, 0);
- int visits = 0;
- if (count > 0) {
- // The user has already bookmarked, and possibly
- // visited this site. However, they are creating
- // a new bookmark with the same url but a different
- // name. The new bookmark should have the same
- // number of visits as the already created bookmark.
- visits = c.getInt(c.getColumnIndexOrThrow(
- Browser.BookmarkColumns.VISITS));
- }
- // Bookmark starts with 3 extra visits so that it will
- // bubble up in the most visited and goto search box
- map.put(Browser.BookmarkColumns.VISITS, visits + 3);
- cr.insert(Browser.BOOKMARKS_URI, map);
- }
- }
- WebIconDatabase.getInstance().retainIconForPageUrl(url);
- c.deactivate();
+ Bookmarks.addBookmark(null, getContentResolver(), url, title);
setResult(RESULT_OK);
}
} catch (IllegalStateException e) {
diff --git a/src/com/android/browser/BookmarkGridPage.java b/src/com/android/browser/BookmarkGridPage.java
new file mode 100644
index 0000000..a9db7ac
--- /dev/null
+++ b/src/com/android/browser/BookmarkGridPage.java
@@ -0,0 +1,243 @@
+/*
+ * 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.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.ContentObserver;
+import android.database.DataSetObserver;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Browser;
+import android.provider.Browser.BookmarkColumns;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+public class BookmarkGridPage extends Activity {
+ private final static int SPACING = 10;
+ private BookmarkGrid mGridView;
+ private BookmarkGridAdapter mAdapter;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mGridView = new BookmarkGrid(this);
+ mGridView.setNumColumns(3);
+ mAdapter = new BookmarkGridAdapter(this);
+ mGridView.setAdapter(mAdapter);
+ mGridView.setFocusable(true);
+ mGridView.setFocusableInTouchMode(true);
+ mGridView.setSelector(android.R.drawable.gallery_thumb);
+ mGridView.setVerticalSpacing(SPACING);
+ mGridView.setHorizontalSpacing(SPACING);
+ setContentView(mGridView);
+ mGridView.requestFocus();
+ }
+
+ private class BookmarkGrid extends GridView {
+ public BookmarkGrid(Context context) {
+ super(context);
+ }
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ int thumbHeight = (h - 2 * (SPACING + getListPaddingTop()
+ + getListPaddingBottom())) / 3;
+ mAdapter.heightChanged(thumbHeight);
+ super.onSizeChanged(w, h, oldw, oldh);
+ }
+ }
+
+ private class BookmarkGridAdapter implements ListAdapter {
+ private ArrayList<DataSetObserver> mDataObservers;
+ private Context mContext; // Context to use to inflate views
+ private Cursor mCursor;
+ private int mThumbHeight;
+
+ public BookmarkGridAdapter(Context context) {
+ mContext = context;
+ mDataObservers = new ArrayList<DataSetObserver>();
+ String whereClause = Browser.BookmarkColumns.BOOKMARK + " != 0";
+ String orderBy = Browser.BookmarkColumns.VISITS + " DESC";
+ mCursor = managedQuery(Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION, whereClause, null, orderBy);
+ mCursor.registerContentObserver(new ChangeObserver());
+ mGridView.setOnItemClickListener(
+ new AdapterView.OnItemClickListener() {
+ public void onItemClick(AdapterView parent, View v,
+ int position, long id) {
+ mCursor.moveToPosition(position);
+ String url = mCursor.getString(
+ Browser.HISTORY_PROJECTION_URL_INDEX);
+ Intent intent = (new Intent()).setAction(url);
+ getParent().setResult(RESULT_OK, intent);
+ finish();
+ }});
+ }
+
+ void heightChanged(int newHeight) {
+ mThumbHeight = newHeight;
+ }
+
+ private class ChangeObserver extends ContentObserver {
+ public ChangeObserver() {
+ super(new Handler());
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ BookmarkGridAdapter.this.refreshData();
+ }
+ }
+
+ void refreshData() {
+ mCursor.requery();
+ for (DataSetObserver o : mDataObservers) {
+ o.onChanged();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.ListAdapter#areAllItemsSelectable()
+ */
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.ListAdapter#isSelectable(int)
+ */
+ public boolean isEnabled(int position) {
+ if (position >= 0 && position < mCursor.getCount()) {
+ return true;
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#getCount()
+ */
+ public int getCount() {
+ return mCursor.getCount();
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#getItem(int)
+ */
+ public Object getItem(int position) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#getItemId(int)
+ */
+ public long getItemId(int position) {
+ return position;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
+ */
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = null;
+ if (convertView != null) {
+ v = convertView;
+ } else {
+ LayoutInflater factory = LayoutInflater.from(mContext);
+ v = factory.inflate(R.layout.bookmark_thumbnail, null);
+ }
+ ImageView thumb = (ImageView) v.findViewById(R.id.thumb);
+ TextView tv = (TextView) v.findViewById(R.id.label);
+
+ mCursor.moveToPosition(position);
+ tv.setText(mCursor.getString(
+ Browser.HISTORY_PROJECTION_TITLE_INDEX));
+ byte[] data = mCursor.getBlob(
+ Browser.HISTORY_PROJECTION_THUMBNAIL_INDEX);
+ if (data == null) {
+ // Backup is to show the favicon
+ data = mCursor.getBlob(
+ Browser.HISTORY_PROJECTION_FAVICON_INDEX);
+ thumb.setScaleType(ImageView.ScaleType.CENTER);
+ } else {
+ thumb.setScaleType(ImageView.ScaleType.FIT_XY);
+ }
+ if (data != null) {
+ thumb.setImageBitmap(
+ BitmapFactory.decodeByteArray(data, 0, data.length));
+ } else {
+ thumb.setImageResource(R.drawable.app_web_browser_sm);
+ thumb.setScaleType(ImageView.ScaleType.CENTER);
+ }
+ ViewGroup.LayoutParams lp = thumb.getLayoutParams();
+ if (lp.height != mThumbHeight) {
+ lp.height = mThumbHeight;
+ thumb.requestLayout();
+ }
+ return v;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#registerDataSetObserver(android.database.DataSetObserver)
+ */
+ public void registerDataSetObserver(DataSetObserver observer) {
+ mDataObservers.add(observer);
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#hasStableIds()
+ */
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#unregisterDataSetObserver(android.database.DataSetObserver)
+ */
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ mDataObservers.remove(observer);
+ }
+
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ public boolean isEmpty() {
+ return getCount() == 0;
+ }
+ }
+}
diff --git a/src/com/android/browser/Bookmarks.java b/src/com/android/browser/Bookmarks.java
new file mode 100644
index 0000000..97e6b20
--- /dev/null
+++ b/src/com/android/browser/Bookmarks.java
@@ -0,0 +1,196 @@
+/*
+ * 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.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Browser;
+import android.util.Log;
+import android.webkit.WebIconDatabase;
+import android.widget.Toast;
+
+import java.util.Date;
+
+/**
+ * This class is purely to have a common place for adding/deleting bookmarks.
+ */
+/* package */ class Bookmarks {
+ private static final String WHERE_CLAUSE
+ = "url = ? OR url = ? OR url = ? OR url = ?";
+ private static final String WHERE_CLAUSE_SECURE = "url = ? OR url = ?";
+
+ private static String[] SELECTION_ARGS;
+
+ /**
+ * Add a bookmark to the database.
+ * @param context Context of the calling Activity. This is used to make
+ * Toast confirming that the bookmark has been added. If the
+ * caller provides null, the Toast will not be shown.
+ * @param cr The ContentResolver being used to add the bookmark to the db.
+ * @param url URL of the website to be bookmarked.
+ * @param name Provided name for the bookmark.
+ */
+ /* package */ static void addBookmark(Context context,
+ ContentResolver cr, String url, String name) {
+ // Want to append to the beginning of the list
+ long creationTime = new Date().getTime();
+ // 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];
+ }
+ Cursor cursor = cr.query(Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION,
+ secure ? WHERE_CLAUSE_SECURE : WHERE_CLAUSE,
+ SELECTION_ARGS,
+ null);
+ ContentValues map = new ContentValues();
+ if (cursor.moveToFirst() && cursor.getInt(
+ Browser.HISTORY_PROJECTION_BOOKMARK_INDEX) == 0) {
+ // This means we have been to this site but not bookmarked
+ // it, so convert the history item to a bookmark
+ map.put(Browser.BookmarkColumns.CREATED, creationTime);
+ map.put(Browser.BookmarkColumns.TITLE, name);
+ map.put(Browser.BookmarkColumns.BOOKMARK, 1);
+ cr.update(Browser.BOOKMARKS_URI, map,
+ "_id = " + cursor.getInt(0), null);
+ } else {
+ int count = cursor.getCount();
+ boolean matchedTitle = false;
+ for (int i = 0; i < count; i++) {
+ // One or more bookmarks already exist for this site.
+ // Check the names of each
+ cursor.moveToPosition(i);
+ if (cursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX)
+ .equals(name)) {
+ // The old bookmark has the same name.
+ // Update its creation time.
+ map.put(Browser.BookmarkColumns.CREATED,
+ creationTime);
+ cr.update(Browser.BOOKMARKS_URI, map,
+ "_id = " + cursor.getInt(0), null);
+ matchedTitle = true;
+ break;
+ }
+ }
+ if (!matchedTitle) {
+ // Adding a bookmark for a site the user has visited,
+ // or a new bookmark (with a different name) for a site
+ // the user has visited
+ map.put(Browser.BookmarkColumns.TITLE, name);
+ map.put(Browser.BookmarkColumns.URL, url);
+ map.put(Browser.BookmarkColumns.CREATED, creationTime);
+ map.put(Browser.BookmarkColumns.BOOKMARK, 1);
+ map.put(Browser.BookmarkColumns.DATE, 0);
+ int visits = 0;
+ if (count > 0) {
+ // The user has already bookmarked, and possibly
+ // visited this site. However, they are creating
+ // a new bookmark with the same url but a different
+ // name. The new bookmark should have the same
+ // number of visits as the already created bookmark.
+ visits = cursor.getInt(
+ Browser.HISTORY_PROJECTION_VISITS_INDEX);
+ }
+ // Bookmark starts with 3 extra visits so that it will
+ // bubble up in the most visited and goto search box
+ map.put(Browser.BookmarkColumns.VISITS, visits + 3);
+ cr.insert(Browser.BOOKMARKS_URI, map);
+ }
+ }
+ WebIconDatabase.getInstance().retainIconForPageUrl(url);
+ cursor.deactivate();
+ if (context != null) {
+ Toast.makeText(context, R.string.added_to_bookmarks,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ /**
+ * Remove a bookmark from the database. If the url is a visited site, it
+ * will remain in the database, but only as a history item, and not as a
+ * bookmarked site.
+ * @param context Context of the calling Activity. This is used to make
+ * Toast confirming that the bookmark has been removed. If the
+ * caller provides null, the Toast will not be shown.
+ * @param cr The ContentResolver being used to remove the bookmark.
+ * @param url URL of the website to be removed.
+ */
+ /* package */ static void removeFromBookmarks(Context context,
+ ContentResolver cr, String url) {
+ Cursor cursor = cr.query(
+ Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION,
+ "url = ?",
+ new String[] { url },
+ null);
+ boolean first = cursor.moveToFirst();
+ // Should be in the database no matter what
+ if (!first) {
+ throw new AssertionError("URL is not in the database!");
+ }
+ // Remove from bookmarks
+ WebIconDatabase.getInstance().releaseIconForPageUrl(url);
+ Uri uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
+ cursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX));
+ int numVisits = cursor.getInt(
+ Browser.HISTORY_PROJECTION_VISITS_INDEX);
+ if (0 == numVisits) {
+ cr.delete(uri, null, null);
+ } else {
+ // It is no longer a bookmark, but it is still a visited
+ // site.
+ ContentValues values = new ContentValues();
+ values.put(Browser.BookmarkColumns.BOOKMARK, 0);
+ try {
+ cr.update(uri, values, null, null);
+ } catch (IllegalStateException e) {
+ Log.e("removeFromBookmarks", "no database!");
+ }
+ }
+ if (context != null) {
+ Toast.makeText(context, R.string.removed_from_bookmarks,
+ Toast.LENGTH_LONG).show();
+ }
+ cursor.deactivate();
+ }
+} \ No newline at end of file
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index d0fa0bd..b28d892 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -28,6 +28,7 @@ import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
@@ -35,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;
@@ -107,11 +109,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;
@@ -121,6 +125,7 @@ import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -159,6 +164,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;
@@ -583,11 +590,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);
}
@@ -634,16 +636,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);
@@ -668,8 +676,28 @@ 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 FrameLayout will hold the custom FrameLayout and a LinearLayout
+ // that contains the title bar and a FrameLayout, which
+ // holds everything else.
+ FrameLayout browserFrameLayout = (FrameLayout) LayoutInflater.from(this)
+ .inflate(R.layout.custom_screen, null);
+ mTitleBar = (TitleBar) browserFrameLayout.findViewById(R.id.title_bar);
+ mTitleBar.setBrowserActivity(this);
+ mContentView = (FrameLayout) browserFrameLayout.findViewById(
+ R.id.main_content);
+ mCustomViewContainer = (FrameLayout) browserFrameLayout
+ .findViewById(R.id.fullscreen_custom_content);
+ frameLayout.addView(browserFrameLayout, COVER_SCREEN_PARAMS);
+ } else {
+ mCustomViewContainer = new FrameLayout(this);
+ mCustomViewContainer.setBackgroundColor(Color.BLACK);
+ mContentView = new FrameLayout(this);
+ frameLayout.addView(mCustomViewContainer, COVER_SCREEN_PARAMS);
+ frameLayout.addView(mContentView, COVER_SCREEN_PARAMS);
+ }
// Create the tab control and our initial tab
mTabControl = new TabControl(this);
@@ -748,6 +776,11 @@ public class BrowserActivity extends Activity
// are not animating from the tab picker.
attachTabToContentView(mTabControl.getCurrentTab());
}
+ // Read JavaScript flags if it exists.
+ String jsFlags = mSettings.getJsFlags();
+ if (jsFlags.trim().length() != 0) {
+ mTabControl.getCurrentWebView().setJsFlags(jsFlags);
+ }
/* enables registration for changes in network status from
http stack */
@@ -765,6 +798,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
@@ -830,8 +909,8 @@ public class BrowserActivity extends Activity
} else {
if (mTabOverview != null && mAnimationCount == 0) {
sendAnimateFromOverview(appTab, false,
- needsLoad ? urlData : EMPTY_URL_DATA, TAB_OVERVIEW_DELAY,
- null);
+ needsLoad ? urlData : EMPTY_URL_DATA,
+ TAB_OVERVIEW_DELAY, null);
} else {
// If the tab was the current tab, we have to attach
// it to the view system again.
@@ -1105,8 +1184,9 @@ public class BrowserActivity extends Activity
return;
}
+ mTabControl.resumeCurrentTab();
mActivityInPause = false;
- resumeWebView();
+ resumeWebViewTimers();
if (mWakeLock.isHeld()) {
mHandler.removeMessages(RELEASE_WAKELOCK);
@@ -1164,8 +1244,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);
@@ -1195,8 +1276,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();
@@ -1214,6 +1297,8 @@ public class BrowserActivity extends Activity
// "com.android.masfproxyservice",
// "com.android.masfproxyservice.MasfProxyService"));
//stopService(proxyServiceIntent);
+
+ unregisterReceiver(mPackageInstallationReceiver);
}
@Override
@@ -1262,7 +1347,7 @@ public class BrowserActivity extends Activity
mTabControl.freeMemory();
}
- private boolean resumeWebView() {
+ private boolean resumeWebViewTimers() {
if ((!mActivityInPause && !mPageStarted) ||
(mActivityInPause && mPageStarted)) {
CookieSyncManager.getInstance().startSync();
@@ -1276,7 +1361,7 @@ public class BrowserActivity extends Activity
}
}
- private boolean pauseWebView() {
+ private boolean pauseWebViewTimers() {
if (mActivityInPause && !mPageStarted) {
CookieSyncManager.getInstance().stopSync();
WebView w = mTabControl.getCurrentWebView();
@@ -1975,9 +2060,9 @@ public class BrowserActivity extends Activity
// A wrapper function of {@link #openTabAndShow(UrlData, Message, boolean, String)}
// that accepts url as string.
- private void openTabAndShow(String url, final Message msg,
+ private TabControl.Tab openTabAndShow(String url, final Message msg,
boolean closeOnExit, String appId) {
- openTabAndShow(new UrlData(url), msg, closeOnExit, appId);
+ return openTabAndShow(new UrlData(url), msg, closeOnExit, appId);
}
// This method does a ton of stuff. It will attempt to create a new tab
@@ -1988,7 +2073,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(UrlData urlData, final Message msg,
+ private TabControl.Tab openTabAndShow(UrlData urlData, final Message msg,
boolean closeOnExit, String appId) {
final boolean newTab = mTabControl.getTabCount() != TabControl.MAX_TABS;
final TabControl.Tab currentTab = mTabControl.getCurrentTab();
@@ -2017,9 +2102,10 @@ public class BrowserActivity extends Activity
}
// Animate from the Tab overview after any animations have
// finished.
- sendAnimateFromOverview(
- mTabControl.createNewTab(closeOnExit, appId, urlData.mUrl), true,
- urlData, delay, msg);
+ final TabControl.Tab tab = mTabControl.createNewTab(
+ closeOnExit, appId, urlData.mUrl);
+ sendAnimateFromOverview(tab, true, urlData, delay, msg);
+ return tab;
}
} else if (!urlData.isEmpty()) {
// We should not have a msg here.
@@ -2034,6 +2120,7 @@ public class BrowserActivity extends Activity
urlData.loadIn(currentTab.getWebView());
}
}
+ return currentTab;
}
private Animation createTabAnimation(final AnimatingView view,
@@ -2255,14 +2342,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);
}
}
@@ -2362,7 +2450,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));
+ }
}
}
@@ -2403,7 +2495,7 @@ public class BrowserActivity extends Activity
* or an empty string if, for example, the URL in question is a
* file:// URL with no hostname.
*/
- private static String buildTitleUrl(String url) {
+ /* package */ static String buildTitleUrl(String url) {
String titleUrl = null;
if (url != null) {
@@ -2439,18 +2531,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);
}
/**
@@ -2528,9 +2636,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);
}
@@ -2554,10 +2662,15 @@ public class BrowserActivity extends Activity
// because of accumulated key events,
// we should ignore it as browser is not active any more.
WebView topWindow = getTopWindow();
- if (topWindow == null)
+ if (topWindow == null && mCustomView == null)
return KeyTracker.State.NOT_TRACKING;
if (keyCode == KeyEvent.KEYCODE_BACK) {
+ // Check if a custom view is currently showing and, if it is, hide it.
+ if (mCustomView != null) {
+ mWebChromeClient.onHideCustomView();
+ return KeyTracker.State.DONE_TRACKING;
+ }
// During animations, block the back key so that other animations
// are not triggered and so that we don't end up destroying all the
// WebViews before finishing the animation.
@@ -2696,7 +2809,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,
@@ -2821,8 +2939,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
@@ -2857,6 +2976,47 @@ public class BrowserActivity extends Activity
// Update the lock icon image only once we are done loading
updateLockIconImage(mLockIconType);
+ // If this is a bookmarked site, add a screenshot to the database.
+ // FIXME: When should we update? Every time?
+ if (url != null) {
+ // copied from BrowserBookmarksAdapter
+ int query = url.indexOf('?');
+ String noQuery = url;
+ if (query != -1) {
+ noQuery = url.substring(0, query);
+ }
+ String URL = noQuery + '?';
+ String[] selArgs = new String[] { noQuery, URL };
+ final String where = "(url == ? OR url GLOB ? || '*') AND bookmark == 1";
+ final String[] projection = new String[] { Browser.BookmarkColumns._ID };
+ ContentResolver cr = getContentResolver();
+ final Cursor c = cr.query(Browser.BOOKMARKS_URI, projection, where, selArgs, null);
+ boolean succeed = c.moveToFirst();
+ ContentValues values = null;
+ while (succeed) {
+ if (values == null) {
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ Picture thumbnail = view.capturePicture();
+ // Height was arbitrarily chosen
+ Bitmap bm = Bitmap.createBitmap(100, 100,
+ Bitmap.Config.ARGB_4444);
+ Canvas canvas = new Canvas(bm);
+ // Scale chosen to be about one third, since we want
+ // roughly three rows/columns for bookmark page
+ canvas.scale(.3f, .3f);
+ thumbnail.draw(canvas);
+ bm.compress(Bitmap.CompressFormat.PNG, 100, os);
+ values = new ContentValues();
+ values.put(Browser.BookmarkColumns.THUMBNAIL,
+ os.toByteArray());
+ }
+ cr.update(ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
+ c.getInt(0)), values, null, null);
+ succeed = c.moveToNext();
+ }
+ c.close();
+ }
+
// Performance probe
if (false) {
long[] sysCpu = new long[7];
@@ -2943,9 +3103,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();
@@ -3381,8 +3541,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(EMPTY_URL_DATA, msg, false, null);
- parent.addChildTab(mTabControl.getCurrentTab());
+ final TabControl.Tab newTab =
+ openTabAndShow(EMPTY_URL_DATA, msg, false, null);
+ if (newTab != parent) {
+ parent.addChildTab(newTab);
+ }
WebView.WebViewTransport transport =
(WebView.WebViewTransport) msg.obj;
transport.setWebView(mTabControl.getCurrentWebView());
@@ -3488,8 +3651,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) {
@@ -3523,6 +3691,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://")) {
@@ -3537,15 +3707,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) {
@@ -3559,6 +3726,81 @@ public class BrowserActivity extends Activity
public void onReceivedIcon(WebView view, Bitmap icon) {
updateIcon(view.getUrl(), icon);
}
+
+ @Override
+ public void onShowCustomView(View view) {
+ if (mCustomView != null)
+ return;
+
+ // Add the custom view to its container.
+ mCustomViewContainer.addView(view, COVER_SCREEN_GRAVITY_CENTER);
+ mCustomView = view;
+ // Save the menu state and set it to empty while the custom
+ // view is showing.
+ mOldMenuState = mMenuState;
+ mMenuState = EMPTY_MENU;
+ // Finally show the custom view container.
+ mCustomViewContainer.setVisibility(View.VISIBLE);
+ mCustomViewContainer.bringToFront();
+ }
+
+ @Override
+ public void onHideCustomView() {
+ if (mCustomView == null)
+ return;
+
+ // Remove the custom view from its container.
+ mCustomViewContainer.removeView(mCustomView);
+ mCustomView = null;
+ // Reset the old menu state.
+ mMenuState = mOldMenuState;
+ mOldMenuState = EMPTY_MENU;
+ mCustomViewContainer.setVisibility(View.GONE);
+ }
+
+ /**
+ * 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 + ")");
+ }
+
};
/**
@@ -3674,19 +3916,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
@@ -3752,7 +3994,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);
+ }
}
}
@@ -4253,8 +4499,8 @@ public class BrowserActivity extends Activity
// middle of an animation, animate away from it to the
// current tab.
if (mTabOverview != null && mAnimationCount == 0) {
- sendAnimateFromOverview(currentTab, false, new UrlData(data),
- TAB_OVERVIEW_DELAY, null);
+ sendAnimateFromOverview(currentTab, false,
+ new UrlData(data), TAB_OVERVIEW_DELAY, null);
} else {
dismissSubWindow(currentTab);
if (data != null && data.length() != 0) {
@@ -4264,6 +4510,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;
}
@@ -4304,8 +4558,8 @@ public class BrowserActivity extends Activity
// was clicked on.
if (mTabControl.getTabCount() == 0) {
current = mTabControl.createNewTab();
- sendAnimateFromOverview(current, true,
- new UrlData(mSettings.getHomePage()), TAB_OVERVIEW_DELAY, null);
+ sendAnimateFromOverview(current, true, new UrlData(
+ mSettings.getHomePage()), TAB_OVERVIEW_DELAY, null);
} else {
final int index = position > 0 ? (position - 1) : 0;
current = mTabControl.getTab(index);
@@ -4341,8 +4595,8 @@ public class BrowserActivity extends Activity
if (index == ImageGrid.NEW_TAB) {
openTabAndShow(mSettings.getHomePage(), null, false, null);
} else {
- sendAnimateFromOverview(mTabControl.getTab(index),
- false, EMPTY_URL_DATA, 0, null);
+ sendAnimateFromOverview(mTabControl.getTab(index), false,
+ EMPTY_URL_DATA, 0, null);
}
}
}
@@ -4364,13 +4618,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
@@ -4444,16 +4704,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;
@@ -4529,7 +4793,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):\\/\\/" +
@@ -4606,11 +4870,14 @@ public class BrowserActivity extends Activity
private ContentResolver mResolver;
private FrameLayout mContentView;
private ImageGrid mTabOverview;
+ private View mCustomView;
+ private FrameLayout mCustomViewContainer;
// FIXME, temp address onPrepareMenu performance problem. When we move everything out of
// view, we should rewrite this.
private int mCurrentMenuState = 0;
private int mMenuState = R.id.MAIN_MENU;
+ private int mOldMenuState = EMPTY_MENU;
private static final int EMPTY_MENU = -1;
private Menu mMenu;
@@ -4701,6 +4968,11 @@ public class BrowserActivity extends Activity
new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT);
+ /*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_GRAVITY_CENTER =
+ new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.FILL_PARENT,
+ Gravity.CENTER);
// Google search
final static String QuickSearch_G = "http://www.google.com/m?q=%s";
// Wikipedia search
@@ -4734,6 +5006,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
@@ -4751,10 +5025,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..c403b44 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;
@@ -181,18 +179,7 @@ class BrowserBookmarksAdapter extends BaseAdapter {
}
mCursor.moveToPosition(position- mExtraOffset);
String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
- WebIconDatabase.getInstance().releaseIconForPageUrl(url);
- Uri uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI, mCursor
- .getInt(Browser.HISTORY_PROJECTION_ID_INDEX));
- int numVisits = mCursor.getInt(Browser.HISTORY_PROJECTION_VISITS_INDEX);
- if (0 == numVisits) {
- mContentResolver.delete(uri, null, null);
- } else {
- // It is no longer a bookmark, but it is still a visited site.
- ContentValues values = new ContentValues();
- values.put(Browser.BookmarkColumns.BOOKMARK, 0);
- mContentResolver.update(uri, values, null, null);
- }
+ Bookmarks.removeFromBookmarks(null, mContentResolver, url);
refreshList();
}
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 3b747d1..2524eb8 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);
@@ -66,6 +75,28 @@ public class BrowserPreferencesPage extends PreferenceActivity
e = findPreference(BrowserSettings.PREF_GEARS_SETTINGS);
e.setOnPreferenceClickListener(this);
+
+ PreferenceScreen manageDatabases = (PreferenceScreen)
+ findPreference(BrowserSettings.PREF_WEBSITE_SETTINGS);
+ Intent intent = new Intent(this, WebsiteSettingsActivity.class);
+ manageDatabases.setIntent(intent);
+ }
+
+ /*
+ * We need to set the manageDatabases PreferenceScreen state
+ * in the onResume(), as the number of origins with databases
+ * could have changed after calling the WebsiteSettingsActivity.
+ */
+ @Override
+ protected void onResume() {
+ super.onResume();
+ PreferenceScreen manageDatabases = (PreferenceScreen)
+ findPreference(BrowserSettings.PREF_WEBSITE_SETTINGS);
+ manageDatabases.setEnabled(false);
+ Vector origins = WebStorage.getInstance().getOrigins();
+ if ((origins != null) && (origins.size() > 0)) {
+ manageDatabases.setEnabled(true);
+ }
}
@Override
diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/BrowserProvider.java
index bc12bde..c1d2ef7 100644
--- a/src/com/android/browser/BrowserProvider.java
+++ b/src/com/android/browser/BrowserProvider.java
@@ -96,6 +96,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;
@@ -143,7 +145,8 @@ public class BrowserProvider extends ContentProvider {
// 15 -> 17 Set it up for the SearchManager
// 17 -> 18 Added favicon in bookmarks table for Home shortcuts
// 18 -> 19 Remove labels table
- private static final int DATABASE_VERSION = 19;
+ // 19 -> 20 Added thumbnail
+ private static final int DATABASE_VERSION = 20;
// Regular expression which matches http://, followed by some stuff, followed by
// optionally a trailing slash, all matched as separate groups.
@@ -215,7 +218,8 @@ public class BrowserProvider extends ContentProvider {
"created LONG," +
"description TEXT," +
"bookmark INTEGER," +
- "favicon BLOB DEFAULT NULL" +
+ "favicon BLOB DEFAULT NULL," +
+ "thumbnail BLOB DEFAULT NULL" +
");");
final CharSequence[] bookmarks = mContext.getResources()
@@ -242,9 +246,12 @@ public class BrowserProvider extends ContentProvider {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
- + newVersion + ", which will destroy all old data");
+ + newVersion);
if (oldVersion == 18) {
db.execSQL("DROP TABLE IF EXISTS labels");
+ }
+ if (oldVersion <= 19) {
+ db.execSQL("ALTER TABLE bookmarks ADD COLUMN thumbnail BLOB DEFAULT NULL;");
} else {
db.execSQL("DROP TABLE IF EXISTS bookmarks");
db.execSQL("DROP TABLE IF EXISTS searches");
@@ -471,22 +478,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) {
@@ -641,7 +648,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;
@@ -659,8 +667,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/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index a5e23c9..aa7e103 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -20,13 +20,17 @@ import com.google.android.providers.GoogleSettings.Partner;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
import android.webkit.CookieManager;
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;
@@ -48,7 +52,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.
@@ -65,7 +69,18 @@ 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 boolean domStorageEnabled = true;
+ private String jsFlags = "";
+
+ private final static String TAG = "BrowserSettings";
// Development settings
public WebSettings.LayoutAlgorithm layoutAlgorithm =
@@ -75,6 +90,7 @@ class BrowserSettings extends Observable {
private boolean tracing = false;
private boolean lightTouch = false;
private boolean navDump = false;
+
// Browser only settings
private boolean doFlick = false;
@@ -97,22 +113,25 @@ class BrowserSettings extends Observable {
"privacy_clear_form_data";
public final static String PREF_CLEAR_PASSWORDS =
"privacy_clear_passwords";
+ 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_WEBSITE_SETTINGS = "website_settings";
public final static String PREF_TEXT_SIZE = "text_size";
public final static String PREF_DEFAULT_ZOOM = "default_zoom";
public final static String PREF_DEFAULT_TEXT_ENCODING =
"default_text_encoding";
private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (Macintosh; " +
- "U; Intel Mac OS X 10_5_5; en-us) AppleWebKit/525.18 (KHTML, " +
- "like Gecko) Version/3.1.2 Safari/525.20.1";
+ "U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.17 (KHTML, " +
+ "like Gecko) Version/4.0 Safari/530.17";
private static final String IPHONE_USERAGENT = "Mozilla/5.0 (iPhone; U; " +
- "CPU iPhone OS 2_2 like Mac OS X; en-us) AppleWebKit/525.18.1 " +
- "(KHTML, like Gecko) Version/3.1.1 Mobile/5G77 Safari/525.20";
+ "CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 " +
+ "(KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16";
// Value to truncate strings when adding them to a TextView within
// a ListView
@@ -157,7 +176,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);
@@ -178,6 +196,15 @@ class BrowserSettings extends Observable {
s.setSupportMultipleWindows(true);
// Turn off file access
s.setAllowFileAccess(false);
+
+ s.setDatabasePath(b.databasePath);
+ s.setDatabaseEnabled(b.databaseEnabled);
+ s.setDomStorageEnabled(b.domStorageEnabled);
+ s.setWebStorageDefaultQuota(b.webStorageDefaultQuota);
+
+ // Turn on Application Caches.
+ s.setAppCachePath(b.appCachePath);
+ s.setAppCacheEnabled(b.appCacheEnabled);
}
}
@@ -197,6 +224,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);
@@ -219,6 +250,15 @@ 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);
+ domStorageEnabled = p.getBoolean("enable_domstorage",
+ domStorageEnabled);
+ appCachePath = p.getString("appcache_path", appCachePath);
javaScriptCanOpenWindowsAutomatically = !p.getBoolean(
"block_popup_windows",
!javaScriptCanOpenWindowsAutomatically);
@@ -238,6 +278,14 @@ class BrowserSettings extends Observable {
zoomDensity = WebSettings.ZoomDensity.valueOf(
p.getString(PREF_DEFAULT_ZOOM, zoomDensity.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;
@@ -274,7 +322,9 @@ class BrowserSettings extends Observable {
doFlick = p.getBoolean("enable_flick", doFlick);
userAgent = Integer.parseInt(p.getString("user_agent", "0"));
}
- update();
+ // JS flags is loaded from DB even if showDebugSettings is false,
+ // so that it can be set once and be effective all the time.
+ jsFlags = p.getString("js_engine_flags", "");
}
public String getPluginsPath() {
@@ -285,6 +335,10 @@ class BrowserSettings extends Observable {
return homeUrl;
}
+ public String getJsFlags() {
+ return jsFlags;
+ }
+
public void setHomePage(Context context, String url) {
Editor ed = PreferenceManager.
getDefaultSharedPreferences(context).edit();
@@ -436,14 +490,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_WEBSITE_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..ae93882 100644
--- a/src/com/android/browser/BrowserYesNoPreference.java
+++ b/src/com/android/browser/BrowserYesNoPreference.java
@@ -38,6 +38,7 @@ class BrowserYesNoPreference extends YesNoPreference {
Context context = getContext();
if (BrowserSettings.PREF_CLEAR_CACHE.equals(getKey())) {
BrowserSettings.getInstance().clearCache(context);
+ BrowserSettings.getInstance().clearDatabases(context);
} else if (BrowserSettings.PREF_CLEAR_COOKIES.equals(getKey())) {
BrowserSettings.getInstance().clearCookies(context);
} else if (BrowserSettings.PREF_CLEAR_HISTORY.equals(getKey())) {
diff --git a/src/com/android/browser/CombinedBookmarkHistoryActivity.java b/src/com/android/browser/CombinedBookmarkHistoryActivity.java
index 963f179..6926c8f 100644
--- a/src/com/android/browser/CombinedBookmarkHistoryActivity.java
+++ b/src/com/android/browser/CombinedBookmarkHistoryActivity.java
@@ -84,7 +84,7 @@ public class CombinedBookmarkHistoryActivity extends TabActivity
Resources resources = getResources();
getIconListenerSet(getContentResolver());
- Intent bookmarksIntent = new Intent(this, BrowserBookmarksPage.class);
+ Intent bookmarksIntent = new Intent(this, BookmarkGridPage.class);
bookmarksIntent.putExtras(extras);
tabHost.addTab(tabHost.newTabSpec(BOOKMARKS_TAB)
.setIndicator(resources.getString(R.string.tab_bookmarks),
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/HistoryItem.java b/src/com/android/browser/HistoryItem.java
index 55e43f0..b37a3bd 100644
--- a/src/com/android/browser/HistoryItem.java
+++ b/src/com/android/browser/HistoryItem.java
@@ -17,23 +17,13 @@
package com.android.browser;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
import android.content.Context;
-import android.database.Cursor;
import android.graphics.Bitmap;
-import android.net.Uri;
import android.provider.Browser;
-import android.util.Log;
import android.view.View;
-import android.webkit.WebIconDatabase;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
-import android.widget.Toast;
-
-import java.util.Date;
/**
* Layout representing a history item in the classic history viewer.
@@ -54,56 +44,13 @@ import java.util.Date;
mListener = new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
- ContentResolver cr = mContext.getContentResolver();
- Cursor cursor = cr.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- "url = ?",
- new String[] { mUrl },
- null);
- boolean first = cursor.moveToFirst();
- // Should be in the database no matter what
- if (!first) {
- throw new AssertionError("URL is not in the database!");
- }
if (isChecked) {
- // Add to bookmarks
- // FIXME: Share code with AddBookmarkPage.java
- ContentValues map = new ContentValues();
- map.put(Browser.BookmarkColumns.CREATED,
- new Date().getTime());
- map.put(Browser.BookmarkColumns.TITLE, getName());
- map.put(Browser.BookmarkColumns.BOOKMARK, 1);
- try {
- cr.update(Browser.BOOKMARKS_URI, map,
- "_id = " + cursor.getInt(0), null);
- } catch (IllegalStateException e) {
- Log.e("HistoryItem", "no database!");
- }
- WebIconDatabase.getInstance().retainIconForPageUrl(mUrl);
- // catch IllegalStateException?
- Toast.makeText(mContext, R.string.added_to_bookmarks,
- Toast.LENGTH_LONG).show();
+ Bookmarks.addBookmark(mContext,
+ mContext.getContentResolver(), mUrl, getName());
} else {
- // Remove from bookmarks
- // FIXME: This code should be shared with
- // BrowserBookmarksAdapter.java
- WebIconDatabase.getInstance().releaseIconForPageUrl(mUrl);
- Uri uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
- cursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX));
- // It is no longer a bookmark, but it is still a visited
- // site.
- ContentValues values = new ContentValues();
- values.put(Browser.BookmarkColumns.BOOKMARK, 0);
- try {
- cr.update(uri, values, null, null);
- } catch (IllegalStateException e) {
- Log.e("HistoryItem", "no database!");
- }
- Toast.makeText(mContext, R.string.removed_from_bookmarks,
- Toast.LENGTH_LONG).show();
+ Bookmarks.removeFromBookmarks(mContext,
+ mContext.getContentResolver(), mUrl);
}
- cursor.deactivate();
}
};
}
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/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 575be8d..bdb57fa 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();
@@ -865,6 +868,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.
*/
@@ -890,7 +935,6 @@ class TabControl {
mTabQueue.add(newTab);
WebView mainView;
- WebView subView;
// Display the new current tab
mCurrentTab = mTabs.indexOf(newTab);
@@ -900,17 +944,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
@@ -925,23 +964,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..b818dd2
--- /dev/null
+++ b/src/com/android/browser/TitleBar.java
@@ -0,0 +1,153 @@
+/*
+ * 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);
+ }
+ if (url != null) {
+ url = BrowserActivity.buildTitleUrl(url.toString());
+ }
+ 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);
+ }
+}
diff --git a/src/com/android/browser/WebsiteSettingsActivity.java b/src/com/android/browser/WebsiteSettingsActivity.java
new file mode 100644
index 0000000..7fea766
--- /dev/null
+++ b/src/com/android/browser/WebsiteSettingsActivity.java
@@ -0,0 +1,291 @@
+/*
+ * 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.AlertDialog;
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Browser;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebIconDatabase;
+import android.webkit.WebStorage;
+import android.widget.ArrayAdapter;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+/**
+ * Manage the settings for an origin.
+ * We use it to keep track of the HTML5 settings, i.e. database (webstorage).
+ */
+public class WebsiteSettingsActivity extends ListActivity {
+
+ private String LOGTAG = "WebsiteSettingsActivity";
+ private static String sMBStored = null;
+ private SiteAdapter mAdapter = null;
+
+ class Site {
+ private String mOrigin;
+ private String mTitle;
+ private Bitmap mIcon;
+
+ public Site(String origin, String title, Bitmap icon) {
+ mOrigin = origin;
+ mTitle = title;
+ mIcon = icon;
+ }
+
+ public String getOrigin() {
+ return mOrigin;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setIcon(Bitmap icon) {
+ mIcon = icon;
+ }
+
+ public Bitmap getIcon() {
+ return mIcon;
+ }
+ }
+
+ class SiteAdapter extends ArrayAdapter<Site>
+ implements AdapterView.OnItemClickListener {
+ private int mResource;
+ private LayoutInflater mInflater;
+ private Bitmap mDefaultIcon;
+ private Site mCurrentSite;
+ private final static int STORED_DATA = 0;
+
+ public SiteAdapter(Context context, int rsc) {
+ super(context, rsc);
+ mResource = rsc;
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mDefaultIcon = BitmapFactory.decodeResource(getResources(),
+ R.drawable.ic_launcher_shortcut_browser_bookmark);
+ populateOrigins();
+ }
+
+ public void populateOrigins() {
+ clear();
+
+ // Get the list of origins we want to display
+ HashMap<String, Site> uris = new HashMap<String, Site>();
+ Vector origins = WebStorage.getInstance().getOrigins();
+ if (origins != null) {
+ for (int i = 0; i < origins.size(); i++) {
+ String origin = (String) origins.get(i);
+ Site site = new Site(origin, origin, null);
+ uris.put(Uri.parse(origin).getHost(), site);
+ }
+ }
+
+ // Check the bookmark db -- if one of our origin matches,
+ // we set its title and favicon
+ Cursor c = getContext().getContentResolver().query(Browser.BOOKMARKS_URI,
+ new String[] { Browser.BookmarkColumns.URL, Browser.BookmarkColumns.TITLE,
+ Browser.BookmarkColumns.FAVICON }, "bookmark = 1", null, null);
+
+ if ((c != null) && c.moveToFirst()) {
+ int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
+ int titleIndex = c.getColumnIndex(Browser.BookmarkColumns.TITLE);
+ int faviconIndex = c.getColumnIndex(Browser.BookmarkColumns.FAVICON);
+ do {
+ String url = c.getString(urlIndex);
+ String host = Uri.parse(url).getHost();
+ if (uris.containsKey(host)) {
+ String title = c.getString(titleIndex);
+ Site site = uris.get(host);
+ site.setTitle(title);
+ byte[] data = c.getBlob(faviconIndex);
+ if (data != null) {
+ Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
+ if (bmp != null) {
+ site.setIcon(bmp);
+ }
+ }
+ }
+ } while (c.moveToNext());
+ }
+
+ // We can now simply populate our array with Site instances
+ Set keys = uris.keySet();
+ Iterator iter = keys.iterator();
+ while (iter.hasNext()) {
+ String origin = (String) iter.next();
+ Site site = uris.get(origin);
+ add(site);
+ }
+
+ if (getCount() == 0) {
+ finish(); // we close the screen
+ }
+ }
+
+ public int getCount() {
+ if (mCurrentSite == null) {
+ return super.getCount();
+ }
+ return 1; // db view
+ }
+
+ public 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);
+ }
+
+ /*
+ * If we receive the back event and are displaying
+ * site's settings, we want to go back to the main
+ * list view. If not, we just do nothing (see
+ * dispatchKeyEvent() below).
+ */
+ public boolean backKeyPressed() {
+ if (mCurrentSite != null) {
+ mCurrentSite = null;
+ populateOrigins();
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view;
+ TextView title;
+ TextView subtitle;
+ ImageView icon;
+
+ if (convertView == null) {
+ view = mInflater.inflate(mResource, parent, false);
+ } else {
+ view = convertView;
+ }
+
+ title = (TextView) view.findViewById(R.id.title);
+ subtitle = (TextView) view.findViewById(R.id.subtitle);
+ icon = (ImageView) view.findViewById(R.id.icon);
+
+ if (mCurrentSite == null) {
+ Site site = getItem(position);
+ title.setText(site.getTitle());
+ subtitle.setText(site.getOrigin());
+ icon.setVisibility(View.VISIBLE);
+ Bitmap bmp = site.getIcon();
+ if (bmp == null) {
+ bmp = mDefaultIcon;
+ }
+ icon.setImageBitmap(bmp);
+ // We set the site as the view's tag,
+ // so that we can get it in onItemClick()
+ view.setTag(site);
+ } else {
+ icon.setVisibility(View.GONE);
+ if (position == STORED_DATA) {
+ String origin = mCurrentSite.getOrigin();
+ long usageValue = WebStorage.getInstance().getUsageForOrigin(origin);
+ String usage = sizeValueToString(usageValue) + " " + sMBStored;
+
+ title.setText(R.string.webstorage_clear_data_title);
+ subtitle.setText(usage);
+ }
+ }
+
+ return view;
+ }
+
+ public void onItemClick(AdapterView<?> parent,
+ View view,
+ int position,
+ long id) {
+ if (mCurrentSite != null) {
+ if (position == STORED_DATA) {
+ new AlertDialog.Builder(getContext())
+ .setTitle(R.string.webstorage_clear_data_dialog_title)
+ .setMessage(R.string.webstorage_clear_data_dialog_message)
+ .setPositiveButton(R.string.webstorage_clear_data_dialog_ok_button,
+ new AlertDialog.OnClickListener() {
+ public void onClick(DialogInterface dlg, int which) {
+ WebStorage.getInstance().deleteOrigin(mCurrentSite.getOrigin());
+ mCurrentSite = null;
+ populateOrigins();
+ notifyDataSetChanged();
+ }})
+ .setNegativeButton(R.string.webstorage_clear_data_dialog_cancel_button, null)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .show();
+ }
+ } else {
+ mCurrentSite = (Site) view.getTag();
+ notifyDataSetChanged();
+ }
+ }
+ }
+
+ /**
+ * Intercepts the back key to immediately notify
+ * NativeDialog that we are done.
+ */
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if ((event.getKeyCode() == KeyEvent.KEYCODE_BACK)
+ && (event.getAction() == KeyEvent.ACTION_DOWN)) {
+ if ((mAdapter != null) && (mAdapter.backKeyPressed())){
+ return true; // event consumed
+ }
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ if (sMBStored == null) {
+ sMBStored = getString(R.string.webstorage_origin_summary_mb_stored);
+ }
+ mAdapter = new SiteAdapter(this, R.layout.application);
+ setListAdapter(mAdapter);
+ getListView().setOnItemClickListener(mAdapter);
+ }
+}