Merge commit 'remotes/korg/cupcake' into merge
Conflicts:
src/com/android/browser/BrowserActivity.java
diff --git a/src/com/android/browser/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java
index ea65a46..cf3fe70 100644
--- a/src/com/android/browser/AddBookmarkPage.java
+++ b/src/com/android/browser/AddBookmarkPage.java
@@ -48,8 +48,8 @@
private Bundle mMap;
private static final String[] mProjection =
- { "_id", "url", "bookmark", "created", "title" };
- private static final String WHERE_CLAUSE = "url = ? AND bookmark = 0";
+ { "_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() {
@@ -119,16 +119,13 @@
boolean emptyTitle = title.length() == 0;
boolean emptyUrl = unfilteredUrl.trim().length() == 0;
Resources r = getResources();
- if (emptyTitle) {
+ if (emptyTitle || emptyUrl) {
+ if (emptyTitle) {
+ mTitle.setError(r.getText(R.string.bookmark_needs_title));
+ }
if (emptyUrl) {
- setTitle(r.getText(R.string.empty_bookmark));
- return false;
- }
- setTitle(r.getText(R.string.bookmark_needs_title));
- return false;
- }
- if (emptyUrl) {
- setTitle(r.getText(R.string.bookmark_needs_url));
+ mAddress.setError(r.getText(R.string.bookmark_needs_url));
+ }
return false;
}
String url = unfilteredUrl;
@@ -138,11 +135,11 @@
try {
address = new WebAddress(unfilteredUrl);
} catch (ParseException e) {
- setTitle(r.getText(R.string.bookmark_url_not_valid));
+ mAddress.setError(r.getText(R.string.bookmark_url_not_valid));
return false;
}
if (address.mHost.length() == 0) {
- setTitle(r.getText(R.string.bookmark_url_not_valid));
+ mAddress.setError(r.getText(R.string.bookmark_url_not_valid));
return false;
}
url = address.toString();
@@ -163,25 +160,58 @@
WHERE_CLAUSE,
SELECTION_ARGS,
null);
- if (c.moveToFirst()) {
- // This means we have been to this site, so convert the
- // history item to a bookmark.
- ContentValues map = new ContentValues();
+ 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 {
- // Adding a bookmark for a site the user has not been to.
- ContentValues map = new ContentValues();
- 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);
- map.put(Browser.BookmarkColumns.VISITS, 0);
- cr.insert(Browser.BOOKMARKS_URI, map);
+ 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();
diff --git a/src/com/android/browser/AddNewBookmark.java b/src/com/android/browser/AddNewBookmark.java
index 748c9c2..a75d002 100644
--- a/src/com/android/browser/AddNewBookmark.java
+++ b/src/com/android/browser/AddNewBookmark.java
@@ -63,10 +63,6 @@
* @param url The new url for the bookmark item.
*/
/* package */ void setUrl(String url) {
- if (url.length() > BrowserSettings.MAX_TEXTVIEW_LEN) {
- mUrlText.setText(url.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN));
- } else {
- mUrlText.setText(url);
- }
+ mUrlText.setText(url);
}
}
diff --git a/src/com/android/browser/BookmarkItem.java b/src/com/android/browser/BookmarkItem.java
index 0015eaf..a70dd4f 100644
--- a/src/com/android/browser/BookmarkItem.java
+++ b/src/com/android/browser/BookmarkItem.java
@@ -19,19 +19,20 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
+import android.view.View;
import android.widget.ImageView;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
import android.widget.TextView;
/**
* Custom layout for an item representing a bookmark in the browser.
*/
-class BookmarkItem extends RelativeLayout {
+class BookmarkItem extends LinearLayout {
- private TextView mTextView;
- private TextView mUrlText;
- private ImageView mImageView;
- private LayoutInflater mFactory;
+ protected TextView mTextView;
+ protected TextView mUrlText;
+ protected ImageView mImageView;
+ protected String mUrl;
/**
* Instantiate a bookmark item, including a default favicon.
@@ -41,11 +42,13 @@
BookmarkItem(Context context) {
super(context);
- mFactory = LayoutInflater.from(context);
- mFactory.inflate(R.layout.bookmark_item, this);
+ LayoutInflater factory = LayoutInflater.from(context);
+ factory.inflate(R.layout.history_item, this);
mTextView = (TextView) findViewById(R.id.title);
mUrlText = (TextView) findViewById(R.id.url);
mImageView = (ImageView) findViewById(R.id.favicon);
+ View star = findViewById(R.id.star);
+ star.setVisibility(View.GONE);
}
/**
@@ -61,8 +64,8 @@
/**
* Return the name assigned to this bookmark item.
*/
- /* package */ CharSequence getName() {
- return mTextView.getText();
+ /* package */ String getName() {
+ return mTextView.getText().toString();
}
/**
@@ -72,6 +75,10 @@
return mTextView;
}
+ /* package */ String getUrl() {
+ return mUrl;
+ }
+
/**
* Set the favicon for this item.
*
@@ -101,5 +108,6 @@
*/
/* package */ void setUrl(String url) {
mUrlText.setText(url);
+ mUrl = url;
}
}
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index 789babe..e018eb4 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -16,26 +16,28 @@
package com.android.browser;
+import com.google.android.googleapps.IGoogleLoginService;
+import com.google.android.googlelogin.GoogleLoginServiceConstants;
+
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
-import android.app.SearchManager;
import android.app.ProgressDialog;
+import android.app.SearchManager;
import android.content.ActivityNotFoundException;
-import android.content.res.AssetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
-import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
+import android.content.DialogInterface.OnCancelListener;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
@@ -54,10 +56,10 @@
import android.graphics.drawable.PaintDrawable;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
+import android.net.ConnectivityManager;
import android.net.Uri;
import android.net.WebAddress;
import android.net.http.EventHandler;
-import android.net.http.RequestQueue;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.os.AsyncTask;
@@ -75,10 +77,10 @@
import android.os.SystemProperties;
import android.preference.PreferenceManager;
import android.provider.Browser;
-import android.provider.Contacts.Intents.Insert;
import android.provider.Contacts;
import android.provider.Downloads;
import android.provider.MediaStore;
+import android.provider.Contacts.Intents.Insert;
import android.text.IClipboard;
import android.text.TextUtils;
import android.text.format.DateFormat;
@@ -86,8 +88,6 @@
import android.util.Config;
import android.util.Log;
import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.MenuItem.OnMenuItemClickListener;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -97,6 +97,9 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.WindowManager;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MenuItem.OnMenuItemClickListener;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
@@ -120,16 +123,14 @@
import android.widget.TextView;
import android.widget.Toast;
-import com.google.android.googleapps.IGoogleLoginService;
-import com.google.android.googlelogin.GoogleLoginServiceConstants;
-
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.net.MalformedURLException;
+import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.text.ParseException;
@@ -138,9 +139,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -202,7 +203,7 @@
if (googleUser == null || !hostedUser.equals(googleUser)) {
String domain = hostedUser.substring(hostedUser.lastIndexOf('@')+1);
homepage = "http://www.google.com/m/a/" + domain + "?client=ms-" +
- SystemProperties.get("ro.com.google.clientid", "unknown");
+ SystemProperties.get("persist.sys.com.google.clientid", "unknown");
}
} catch (RemoteException ignore) {
// Login service died; carry on
@@ -685,9 +686,11 @@
// If the intent is ACTION_VIEW and data is not null, the Browser is
// invoked to view the content by another application. In this case,
// the tab will be close when exit.
+ String url = getUrlFromIntent(intent);
final TabControl.Tab t = mTabControl.createNewTab(
Intent.ACTION_VIEW.equals(intent.getAction()) &&
- intent.getData() != null);
+ intent.getData() != null,
+ intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), url);
mTabControl.setCurrentTab(t);
// This is one of the only places we call attachTabToContentView
// without animating from the tab picker.
@@ -709,7 +712,6 @@
}
copyPlugins(true);
- String url = getUrlFromIntent(intent);
if (url == null || url.length() == 0) {
if (mSettings.isLoginInitialized()) {
webView.loadUrl(mSettings.getHomePage());
@@ -730,19 +732,18 @@
http stack */
mNetworkStateChangedFilter = new IntentFilter();
mNetworkStateChangedFilter.addAction(
- RequestQueue.HTTP_NETWORK_STATE_CHANGED_INTENT);
+ ConnectivityManager.CONNECTIVITY_ACTION);
mNetworkStateIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
- RequestQueue.HTTP_NETWORK_STATE_CHANGED_INTENT)) {
- Boolean up = (Boolean)intent.getExtra(
- RequestQueue.HTTP_NETWORK_STATE_UP);
- onNetworkToggle(up);
+ ConnectivityManager.CONNECTIVITY_ACTION)) {
+ boolean down = intent.getBooleanExtra(
+ ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+ onNetworkToggle(!down);
}
}
};
- setRequestedOrientation(mSettings.getOrientation());
}
@Override
@@ -778,11 +779,44 @@
}
if (Intent.ACTION_VIEW.equals(action) &&
(flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
+ final String appId =
+ intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
+ final TabControl.Tab appTab = mTabControl.getTabFromId(appId);
+ if (appTab != null) {
+ Log.i(LOGTAG, "Reusing tab for " + appId);
+ // Dismiss the subwindow if applicable.
+ dismissSubWindow(appTab);
+ // Since we might kill the WebView, remove it from the
+ // content view first.
+ removeTabFromContentView(appTab);
+ // Recreate the main WebView after destroying the old one.
+ // If the WebView has the same original url and is on that
+ // page, it can be reused.
+ boolean needsLoad =
+ mTabControl.recreateWebView(appTab, url);
+ if (current != appTab) {
+ showTab(appTab, needsLoad ? url : null);
+ } else {
+ if (mTabOverview != null && mAnimationCount == 0) {
+ sendAnimateFromOverview(appTab, false,
+ needsLoad ? url : null, TAB_OVERVIEW_DELAY,
+ null);
+ } else {
+ // If the tab was the current tab, we have to attach
+ // it to the view system again.
+ attachTabToContentView(appTab);
+ if (needsLoad) {
+ appTab.getWebView().loadUrl(url);
+ }
+ }
+ }
+ return;
+ }
// if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url will be
// opened in a new tab unless we have reached MAX_TABS. Then the
// url will be opened in the current tab. If a new tab is
// created, it will have "true" for exit on close.
- openTabAndShow(url, null, true);
+ openTabAndShow(url, null, true, appId);
} else {
if ("about:debug".equals(url)) {
mSettings.toggleDebugSettings();
@@ -1289,13 +1323,12 @@
}
/**
- * Overriding this forces the search key to launch global search. The difference
- * is the final "true" which requests global search.
+ * Overriding this to insert a local information bundle
*/
@Override
public boolean onSearchRequested() {
startSearch(null, false,
- createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHKEY), true);
+ createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHKEY), false);
return true;
}
@@ -1324,19 +1357,13 @@
}
break;
- case R.id.search_menu_id:
- // launch using "global" search, which will bring up the Google search box
- startSearch(null, false,
- createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHMENU), true);
- break;
-
case R.id.bookmarks_menu_id:
- bookmarksPicker();
+ bookmarksOrHistoryPicker(false);
break;
case R.id.windows_menu_id:
if (mTabControl.getTabCount() == 1) {
- openTabAndShow(mSettings.getHomePage(), null, false);
+ openTabAndShow(mSettings.getHomePage(), null, false, null);
} else {
tabPicker(true, mTabControl.getCurrentIndex(), false);
}
@@ -1412,12 +1439,7 @@
break;
case R.id.classic_history_menu_id:
- loadHistory();
- break;
-
- case R.id.bookmark_page_menu_id:
- Browser.saveBookmark(this, getTopWindow().getTitle(),
- getTopWindow().getUrl());
+ bookmarksOrHistoryPicker(true);
break;
case R.id.share_page_menu_id:
@@ -1428,12 +1450,6 @@
getTopWindow().debugDump();
break;
- case R.id.zoom_menu_id:
- // FIXME: Can we move this out of WebView? How does this work
- // for a subwindow?
- getTopWindow().invokeZoomPicker();
- break;
-
case R.id.zoom_in_menu_id:
getTopWindow().zoomIn();
break;
@@ -1446,18 +1462,6 @@
viewDownloads(null);
break;
- case R.id.flip_orientation_menu_id:
- if (mSettings.getOrientation() !=
- ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
- mSettings.setOrientation(this,
- ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
- } else {
- mSettings.setOrientation(this,
- ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- }
- setRequestedOrientation(mSettings.getOrientation());
- break;
-
// -- Tab menu
case R.id.view_tab_menu_id:
if (mTabListener != null && mTabOverview != null) {
@@ -1499,17 +1503,12 @@
}
break;
- case R.id.history_tab_menu_id: {
- Intent i = new Intent(this, BrowserHistoryPage.class);
- i.putExtra("maxTabsOpen",
- mTabControl.getTabCount() >=
- TabControl.MAX_TABS);
- startActivityForResult(i, CLASSIC_HISTORY_PAGE);
- }
+ case R.id.history_tab_menu_id:
+ bookmarksOrHistoryPicker(true);
break;
case R.id.bookmarks_tab_menu_id:
- bookmarksPicker();
+ bookmarksOrHistoryPicker(false);
break;
case R.id.properties_tab_menu_id:
@@ -1570,6 +1569,7 @@
if (mCurrentMenuState != mMenuState) {
menu.setGroupVisible(R.id.MAIN_MENU, false);
menu.setGroupEnabled(R.id.MAIN_MENU, false);
+ menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, false);
menu.setGroupVisible(R.id.TAB_MENU, true);
menu.setGroupEnabled(R.id.TAB_MENU, true);
}
@@ -1582,6 +1582,7 @@
if (mCurrentMenuState != mMenuState) {
menu.setGroupVisible(R.id.MAIN_MENU, false);
menu.setGroupEnabled(R.id.MAIN_MENU, false);
+ menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, false);
menu.setGroupVisible(R.id.TAB_MENU, false);
menu.setGroupEnabled(R.id.TAB_MENU, false);
}
@@ -1590,47 +1591,34 @@
if (mCurrentMenuState != mMenuState) {
menu.setGroupVisible(R.id.MAIN_MENU, true);
menu.setGroupEnabled(R.id.MAIN_MENU, true);
+ menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, true);
menu.setGroupVisible(R.id.TAB_MENU, false);
menu.setGroupEnabled(R.id.TAB_MENU, false);
}
final WebView w = getTopWindow();
- boolean canGoBack = w.canGoBack();
+ boolean canGoBack = false;
+ boolean canGoForward = false;
+ boolean isHome = false;
+ if (w != null) {
+ canGoBack = w.canGoBack();
+ canGoForward = w.canGoForward();
+ isHome = mSettings.getHomePage().equals(w.getUrl());
+ }
final MenuItem back = menu.findItem(R.id.back_menu_id);
- back.setVisible(canGoBack);
back.setEnabled(canGoBack);
- final MenuItem flip =
- menu.findItem(R.id.flip_orientation_menu_id);
- boolean keyboardClosed =
- getResources().getConfiguration().hardKeyboardHidden ==
- Configuration.HARDKEYBOARDHIDDEN_YES;
- flip.setEnabled(keyboardClosed);
- boolean isHome = mSettings.getHomePage().equals(w.getUrl());
final MenuItem home = menu.findItem(R.id.homepage_menu_id);
- home.setVisible(!isHome);
home.setEnabled(!isHome);
menu.findItem(R.id.forward_menu_id)
- .setEnabled(w.canGoForward());
-
- menu.findItem(R.id.zoom_in_menu_id).setVisible(false);
- menu.findItem(R.id.zoom_out_menu_id).setVisible(false);
+ .setEnabled(canGoForward);
// decide whether to show the share link option
PackageManager pm = getPackageManager();
Intent send = new Intent(Intent.ACTION_SEND);
send.setType("text/plain");
- List<ResolveInfo> list = pm.queryIntentActivities(send,
- PackageManager.MATCH_DEFAULT_ONLY);
- menu.findItem(R.id.share_page_menu_id).setVisible(
- list.size() > 0);
-
- // Hide the menu+<window number> items
- // Can't set visibility in menu xml file b/c when a
- // group is set visible, all items are set visible.
- for (int i = 0; i < WINDOW_SHORTCUT_ID_ARRAY.length; i++) {
- menu.findItem(WINDOW_SHORTCUT_ID_ARRAY[i]).setVisible(false);
- }
+ ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
+ menu.findItem(R.id.share_page_menu_id).setVisible(ri != null);
// If there is only 1 window, the text will be "New window"
final MenuItem windows = menu.findItem(R.id.windows_menu_id);
@@ -1737,14 +1725,16 @@
PackageManager pm = getPackageManager();
Intent send = new Intent(Intent.ACTION_SEND);
send.setType("text/plain");
- List<ResolveInfo> list = pm.queryIntentActivities(send,
- PackageManager.MATCH_DEFAULT_ONLY);
- menu.findItem(R.id.share_link_context_menu_id).setVisible(
- list.size() > 0);
- break;
-
+ ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
+ menu.findItem(R.id.share_link_context_menu_id).setVisible(ri != null);
+ if (type == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
+ break;
+ }
+ // otherwise fall through to handle image part
case WebView.HitTestResult.IMAGE_TYPE:
- menu.setHeaderTitle(extra);
+ if (type == WebView.HitTestResult.IMAGE_TYPE) {
+ menu.setHeaderTitle(extra);
+ }
menu.findItem(R.id.view_image_context_menu_id).setIntent(
new Intent(Intent.ACTION_VIEW, Uri.parse(extra)));
menu.findItem(R.id.download_context_menu_id).
@@ -1757,22 +1747,11 @@
}
}
- // Used by attachTabToContentView for the WebView's ZoomControl widget.
- private static final FrameLayout.LayoutParams ZOOM_PARAMS =
- new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- Gravity.BOTTOM);
-
// Attach the given tab to the content view.
private void attachTabToContentView(TabControl.Tab t) {
final WebView main = t.getWebView();
// Attach the main WebView.
mContentView.addView(main, COVER_SCREEN_PARAMS);
- // Attach the Zoom control widget and hide it.
- final View zoom = main.getZoomControls();
- mContentView.addView(zoom, ZOOM_PARAMS);
- zoom.setVisibility(View.GONE);
// Attach the sub window if necessary
attachSubWindow(t);
// Request focus on the top window.
@@ -1792,8 +1771,7 @@
// Remove the given tab from the content view.
private void removeTabFromContentView(TabControl.Tab t) {
- // Remove the Zoom widget and the main WebView.
- mContentView.removeView(t.getWebView().getZoomControls());
+ // Remove the main WebView.
mContentView.removeView(t.getWebView());
// Remove the sub window if it exists.
if (t.getSubWebView() != null) {
@@ -1838,7 +1816,13 @@
// Send the animate message.
final HashMap map = new HashMap();
map.put("view", view);
- map.put("url", url);
+ // Load the url after the AnimatingView has captured the picture. This
+ // prevents any bad layout or bad scale from being used during
+ // animation.
+ if (url != null) {
+ dismissSubWindow(tab);
+ tab.getWebView().loadUrl(url);
+ }
map.put("msg", msg);
mHandler.sendMessageDelayed(mHandler.obtainMessage(
ANIMATE_FROM_OVERVIEW, newTab ? 1 : 0, 0, map), delay);
@@ -1858,6 +1842,10 @@
// Called by TabControl when a tab is requesting focus
/* package */ void showTab(TabControl.Tab t) {
+ showTab(t, null);
+ }
+
+ private void showTab(TabControl.Tab t, String url) {
// Disallow focus change during a tab animation.
if (mAnimationCount > 0) {
return;
@@ -1869,7 +1857,7 @@
delay = TAB_ANIMATION_DURATION + TAB_OVERVIEW_DELAY;
tabPicker(false, mTabControl.getTabIndex(t), false);
}
- sendAnimateFromOverview(t, false, null, delay, null);
+ sendAnimateFromOverview(t, false, url, delay, null);
}
// This method does a ton of stuff. It will attempt to create a new tab
@@ -1881,7 +1869,7 @@
// method is called from TabListener.onClick(), the method will animate
// away from the tab overview.
private void openTabAndShow(String url, final Message msg,
- boolean closeOnExit) {
+ boolean closeOnExit, String appId) {
final boolean newTab = mTabControl.getTabCount() != TabControl.MAX_TABS;
final TabControl.Tab currentTab = mTabControl.getCurrentTab();
if (newTab) {
@@ -1909,8 +1897,9 @@
}
// Animate from the Tab overview after any animations have
// finished.
- sendAnimateFromOverview(mTabControl.createNewTab(closeOnExit),
- true, url, delay, msg);
+ sendAnimateFromOverview(
+ mTabControl.createNewTab(closeOnExit, appId, url), true,
+ url, delay, msg);
}
} else if (url != null) {
// We should not have a msg here.
@@ -1987,15 +1976,17 @@
public void run() {
// Remove the AnimatingView.
mContentView.removeView(view);
- // Make newIndex visible.
- mTabOverview.setCurrentIndex(newIndex);
- // Restore the listener.
- mTabOverview.setListener(mTabListener);
- // Change the menu to TAB_MENU if the
- // ImageGrid is interactive.
- if (mTabOverview.isLive()) {
- mMenuState = R.id.TAB_MENU;
- mTabOverview.requestFocus();
+ if (mTabOverview != null) {
+ // Make newIndex visible.
+ mTabOverview.setCurrentIndex(newIndex);
+ // Restore the listener.
+ mTabOverview.setListener(mTabListener);
+ // Change the menu to TAB_MENU if the
+ // ImageGrid is interactive.
+ if (mTabOverview.isLive()) {
+ mMenuState = R.id.TAB_MENU;
+ mTabOverview.requestFocus();
+ }
}
// If a remove was requested, remove the tab.
if (remove) {
@@ -2013,10 +2004,12 @@
if (currentTab != tab) {
mTabControl.setCurrentTab(currentTab);
}
- mTabOverview.remove(newIndex);
- // Make the current tab visible.
- mTabOverview.setCurrentIndex(
- mTabControl.getCurrentIndex());
+ if (mTabOverview != null) {
+ mTabOverview.remove(newIndex);
+ // Make the current tab visible.
+ mTabOverview.setCurrentIndex(
+ mTabControl.getCurrentIndex());
+ }
}
}
});
@@ -2041,7 +2034,7 @@
// Animate from the tab picker. The index supplied is the index to animate
// from.
private void animateFromTabOverview(final AnimatingView view,
- final boolean newTab, final String url, final Message msg) {
+ final boolean newTab, final Message msg) {
// firstVisible is the first visible tab on the screen. This helps
// to know which corner of the screen the selected tab is.
int firstVisible = mTabOverview.getFirstVisiblePosition();
@@ -2063,7 +2056,7 @@
// Find the view at this location.
final View v = mTabOverview.getChildAt(location);
- // Wait until the animation completes to load the url.
+ // Wait until the animation completes to replace the AnimatingView.
final Animation.AnimationListener l =
new Animation.AnimationListener() {
public void onAnimationStart(Animation a) {}
@@ -2078,15 +2071,9 @@
dismissTabOverview(v == null);
TabControl.Tab t =
mTabControl.getCurrentTab();
- WebView w = t.getWebView();
- if (url != null) {
- // Dismiss the subwindow if one exists.
- dismissSubWindow(t);
- w.loadUrl(url);
- }
mMenuState = R.id.MAIN_MENU;
// Resume regular updates.
- w.resumeTimers();
+ t.getWebView().resumeTimers();
// Dispatch the message after the animation
// completes.
if (msg != null) {
@@ -2111,7 +2098,7 @@
// Make the view VISIBLE during the animation.
view.setVisibility(View.VISIBLE);
} else {
- // Go ahead and load the url.
+ // Go ahead and do all the cleanup.
l.onAnimationEnd(null);
}
}
@@ -2126,7 +2113,12 @@
}
// Just in case there was a problem with animating away from the tab
// overview
- mTabControl.getCurrentWebView().setVisibility(View.VISIBLE);
+ WebView current = mTabControl.getCurrentWebView();
+ if (current != null) {
+ current.setVisibility(View.VISIBLE);
+ } else {
+ Log.e(LOGTAG, "No current WebView in dismissTabOverview");
+ }
// Make the sub window container visible.
if (mTabControl.getCurrentSubWindow() != null) {
mTabControl.getCurrentTab().getSubWebViewContainer()
@@ -2140,12 +2132,12 @@
private void openTab(String url) {
if (mSettings.openInBackground()) {
- TabControl.Tab t = mTabControl.createNewTab(false);
+ TabControl.Tab t = mTabControl.createNewTab();
if (t != null) {
t.getWebView().loadUrl(url);
}
} else {
- openTabAndShow(url, null, false);
+ openTabAndShow(url, null, false, null);
}
}
@@ -2218,8 +2210,6 @@
}
resetTitleAndIcon(current);
int progress = current.getProgress();
- mInLoad = (progress != 100);
- updateInLoadMenuItems();
mWebChromeClient.onProgressChanged(current, progress);
}
@@ -2450,7 +2440,7 @@
return KeyTracker.State.DONE_TRACKING;
}
if (stage == KeyTracker.Stage.LONG_REPEAT) {
- loadHistory();
+ bookmarksOrHistoryPicker(true);
return KeyTracker.State.DONE_TRACKING;
} else if (stage == KeyTracker.Stage.UP) {
// FIXME: Currently, we do not have a notion of the
@@ -2522,13 +2512,6 @@
}
}
- private void loadHistory() {
- Intent intent = new Intent(this, BrowserHistoryPage.class);
- intent.putExtra("maxTabsOpen",
- mTabControl.getTabCount() >= TabControl.MAX_TABS);
- startActivityForResult(intent, CLASSIC_HISTORY_PAGE);
- }
-
// called by a non-UI thread to post the message
public void postMessage(int what, int arg1, int arg2, Object obj) {
mHandler.sendMessage(mHandler.obtainMessage(what, arg1, arg2, obj));
@@ -2555,8 +2538,7 @@
case ANIMATE_FROM_OVERVIEW:
final HashMap map = (HashMap) msg.obj;
animateFromTabOverview((AnimatingView) map.get("view"),
- msg.arg1 == 1, (String) map.get("url"),
- (Message) map.get("msg"));
+ msg.arg1 == 1, (Message) map.get("msg"));
break;
case ANIMATE_TO_OVERVIEW:
@@ -2569,7 +2551,7 @@
// the method relies on the value being 0 to start the next
// animation.
mAnimationCount--;
- openTabAndShow((String) msg.obj, null, false);
+ openTabAndShow((String) msg.obj, null, false, null);
break;
case FOCUS_NODE_HREF:
@@ -2723,6 +2705,18 @@
mInLoad = true;
updateInLoadMenuItems();
+ if (!mIsNetworkUp) {
+ if ( mAlertDialog == null) {
+ mAlertDialog = new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle(R.string.loadSuspendedTitle)
+ .setMessage(R.string.loadSuspended)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ }
+ if (view != null) {
+ view.setNetworkAvailable(false);
+ }
+ }
// schedule to check memory condition
mHandler.sendMessageDelayed(mHandler.obtainMessage(CHECK_MEMORY),
@@ -2739,7 +2733,7 @@
updateLockIconImage(mLockIconType);
// Performance probe
- if (false) {
+ if (false) {
long[] sysCpu = new long[7];
if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
sysCpu, null)) {
@@ -2834,11 +2828,6 @@
}
}
- if (mInLoad) {
- mInLoad = false;
- updateInLoadMenuItems();
- }
-
mHandler.removeMessages(CHECK_MEMORY);
checkMemory();
}
@@ -3180,7 +3169,7 @@
// openTabAndShow will dispatch the message after creating the
// new WebView. This will prevent another request from coming
// in during the animation.
- openTabAndShow(null, msg, false);
+ openTabAndShow(null, msg, false, null);
parent.addChildTab(mTabControl.getCurrentTab());
WebView.WebViewTransport transport =
(WebView.WebViewTransport) msg.obj;
@@ -3296,6 +3285,18 @@
// onPageFinished() is only called for the main frame. sync
// cookie and cache promptly here.
CookieSyncManager.getInstance().sync();
+ if (mInLoad) {
+ mInLoad = false;
+ updateInLoadMenuItems();
+ }
+ } else {
+ // onPageFinished may have already been called but a subframe
+ // is still loading and updating the progress. Reset mInLoad
+ // and update the menu items.
+ if (!mInLoad) {
+ mInLoad = true;
+ updateInLoadMenuItems();
+ }
}
}
@@ -3367,8 +3368,8 @@
// that matches.
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(url), mimetype);
- if (getPackageManager().queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY).size() != 0) {
+ if (getPackageManager().resolveActivity(intent,
+ PackageManager.MATCH_DEFAULT_ONLY) != null) {
// someone knows how to handle this mime type with this scheme, don't download.
try {
startActivity(intent);
@@ -3424,10 +3425,44 @@
return;
}
+ // java.net.URI is a lot stricter than KURL so we have to undo
+ // KURL's percent-encoding and redo the encoding using java.net.URI.
+ URI uri = null;
+ try {
+ // Undo the percent-encoding that KURL may have done.
+ String newUrl = new String(URLUtil.decode(url.getBytes()));
+ // Parse the url into pieces
+ WebAddress w = new WebAddress(newUrl);
+ String frag = null;
+ String query = null;
+ String path = w.mPath;
+ // Break the path into path, query, and fragment
+ if (path.length() > 0) {
+ // Strip the fragment
+ int idx = path.lastIndexOf('#');
+ if (idx != -1) {
+ frag = path.substring(idx + 1);
+ path = path.substring(0, idx);
+ }
+ idx = path.lastIndexOf('?');
+ if (idx != -1) {
+ query = path.substring(idx + 1);
+ path = path.substring(0, idx);
+ }
+ }
+ uri = new URI(w.mScheme, w.mAuthInfo, w.mHost, w.mPort, path,
+ query, frag);
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Could not parse url for download: " + url, e);
+ return;
+ }
+
+ // XXX: Have to use the old url since the cookies were stored using the
+ // old percent-encoded url.
String cookies = CookieManager.getInstance().getCookie(url);
ContentValues values = new ContentValues();
- values.put(Downloads.COLUMN_URI, url);
+ 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,
@@ -3437,7 +3472,7 @@
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.parse(url).getHost());
+ values.put(Downloads.COLUMN_DESCRIPTION, uri.getHost());
if (contentLength > 0) {
values.put(Downloads.COLUMN_TOTAL_BYTES, contentLength);
}
@@ -3884,7 +3919,7 @@
}
mHttpAuthHandler = handler;
- mHttpAuthenticationDialog = new AlertDialog.Builder(this)
+ AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(titleText)
.setIcon(android.R.drawable.ic_dialog_alert)
.setView(v)
@@ -3920,12 +3955,17 @@
mHttpAuthenticationDialog = null;
mHttpAuthHandler = null;
}})
- .show();
+ .create();
+ // Make the IME appear when the dialog is displayed if applicable.
+ dialog.getWindow().setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ dialog.show();
if (focusId != 0) {
- mHttpAuthenticationDialog.findViewById(focusId).requestFocus();
+ dialog.findViewById(focusId).requestFocus();
} else {
v.findViewById(R.id.username_edit).requestFocus();
}
+ mHttpAuthenticationDialog = dialog;
}
public int getProgress() {
@@ -3956,16 +3996,20 @@
}
/**
- * http stack says net has come or gone... inform the user
+ * connectivity manager says net has come or gone... inform the user
* @param up true if net has come up, false if net has gone down
*/
public void onNetworkToggle(boolean up) {
- if (up) {
+ if (up == mIsNetworkUp) {
+ return;
+ } else if (up) {
+ mIsNetworkUp = true;
if (mAlertDialog != null) {
mAlertDialog.cancel();
mAlertDialog = null;
}
} else {
+ mIsNetworkUp = false;
if (mInLoad && mAlertDialog == null) {
mAlertDialog = new AlertDialog.Builder(this)
.setTitle(R.string.loadSuspendedTitle)
@@ -3984,8 +4028,7 @@
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
switch (requestCode) {
- case BOOKMARKS_PAGE:
- case CLASSIC_HISTORY_PAGE:
+ case COMBO_PAGE:
if (resultCode == RESULT_OK && intent != null) {
String data = intent.getAction();
Bundle extras = intent.getExtras();
@@ -4048,7 +4091,7 @@
// If the user removes the last tab, act like the New Tab item
// was clicked on.
if (mTabControl.getTabCount() == 0) {
- current = mTabControl.createNewTab(false);
+ current = mTabControl.createNewTab();
sendAnimateFromOverview(current, true,
mSettings.getHomePage(), TAB_OVERVIEW_DELAY, null);
} else {
@@ -4088,7 +4131,7 @@
// NEW_TAB means that the "New Tab" cell was clicked on.
if (index == ImageGrid.NEW_TAB) {
- openTabAndShow(mSettings.getHomePage(), null, false);
+ openTabAndShow(mSettings.getHomePage(), null, false, null);
} else {
sendAnimateFromOverview(mTabControl.getTab(index),
false, null, 0, null);
@@ -4202,13 +4245,13 @@
mMenuState = EMPTY_MENU;
}
- private void bookmarksPicker() {
+ private void bookmarksOrHistoryPicker(boolean startWithHistory) {
WebView current = mTabControl.getCurrentWebView();
if (current == null) {
return;
}
Intent intent = new Intent(this,
- BrowserBookmarksPage.class);
+ CombinedBookmarkHistoryActivity.class);
String title = current.getTitle();
String url = current.getUrl();
// Just in case the user opens bookmarks before a page finishes loading
@@ -4228,7 +4271,11 @@
intent.putExtra("url", url);
intent.putExtra("maxTabsOpen",
mTabControl.getTabCount() >= TabControl.MAX_TABS);
- startActivityForResult(intent, BOOKMARKS_PAGE);
+ if (startWithHistory) {
+ intent.putExtra(CombinedBookmarkHistoryActivity.STARTING_TAB,
+ CombinedBookmarkHistoryActivity.HISTORY_TAB);
+ }
+ startActivityForResult(intent, COMBO_PAGE);
}
// Called when loading from context menu or LOAD_URL message
@@ -4351,7 +4398,7 @@
return composeSearchUrl(inUrl);
}
- /* package */static String composeSearchUrl(String search) {
+ /* package */ String composeSearchUrl(String search) {
return URLUtil.composeSearchUrl(search, QuickSearch_G,
QUERY_PLACE_HOLDER);
}
@@ -4376,7 +4423,7 @@
R.string.google_search_base, l.getLanguage(),
l.getCountry().toLowerCase())
+ "client=ms-"
- + SystemProperties.get("ro.com.google.clientid", "unknown")
+ + SystemProperties.get("persist.sys.com.google.clientid", "unknown")
+ "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&q=%s";
} else {
QuickSearch_G = url;
@@ -4409,6 +4456,7 @@
boolean mCanChord;
private boolean mInLoad;
+ private boolean mIsNetworkUp;
private boolean mPageStarted;
private boolean mActivityInPause = true;
@@ -4503,8 +4551,6 @@
// "source" parameter for Google search through search key
final static String GOOGLE_SEARCH_SOURCE_SEARCHKEY = "browser-key";
- // "source" parameter for Google search through search menu
- final static String GOOGLE_SEARCH_SOURCE_SEARCHMENU = "browser-menu";
// "source" parameter for Google search through goto menu
final static String GOOGLE_SEARCH_SOURCE_GOTO = "browser-goto";
// "source" parameter for Google search through simplily type
@@ -4543,10 +4589,9 @@
private BroadcastReceiver mNetworkStateIntentReceiver;
// activity requestCode
- final static int BOOKMARKS_PAGE = 1;
- final static int CLASSIC_HISTORY_PAGE = 2;
- final static int DOWNLOAD_PAGE = 3;
- final static int PREFERENCES_PAGE = 4;
+ final static int COMBO_PAGE = 1;
+ final static int DOWNLOAD_PAGE = 2;
+ final static int PREFERENCES_PAGE = 3;
// 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/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java
index 5c509a8..a7d45f9 100644
--- a/src/com/android/browser/BrowserBookmarksPage.java
+++ b/src/com/android/browser/BrowserBookmarksPage.java
@@ -162,7 +162,7 @@
}
mBookmarksAdapter = new BrowserBookmarksAdapter(this,
- getIntent().getStringExtra("title"), mCreateShortcut);
+ getIntent().getStringExtra("url"), mCreateShortcut);
mMaxTabsOpen = getIntent().getBooleanExtra("maxTabsOpen", false);
ListView listView = (ListView) findViewById(R.id.list);
@@ -204,7 +204,7 @@
} else {
final Intent intent = createShortcutIntent(getUrl(position),
getBookmarkTitle(position));
- setResult(RESULT_OK, intent);
+ setResultToParent(RESULT_OK, intent);
finish();
}
}
@@ -212,8 +212,13 @@
private Intent createShortcutIntent(String url, String title) {
final Intent i = new Intent();
- i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(Intent.ACTION_VIEW,
- Uri.parse(url)));
+ final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse(url));
+ long urlHash = url.hashCode();
+ long uniqueId = (urlHash << 32) | shortcutIntent.hashCode();
+ shortcutIntent.putExtra(Browser.EXTRA_APPLICATION_ID,
+ Long.toString(uniqueId));
+ i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(BrowserBookmarksPage.this,
@@ -231,7 +236,8 @@
}
private void loadUrl(int position) {
- setResult(RESULT_OK, (new Intent()).setAction(getUrl(position)));
+ Intent intent = (new Intent()).setAction(getUrl(position));
+ setResultToParent(RESULT_OK, intent);
finish();
}
@@ -262,7 +268,7 @@
private void openInNewWindow(int position) {
Bundle b = new Bundle();
b.putBoolean("new_window", true);
- setResult(RESULT_OK,
+ setResultToParent(RESULT_OK,
(new Intent()).setAction(getUrl(position)).putExtras(b));
finish();
@@ -367,9 +373,18 @@
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isDown()) {
- setResult(RESULT_CANCELED);
+ setResultToParent(RESULT_CANCELED, null);
mCanceled = true;
}
return super.dispatchKeyEvent(event);
}
+
+ // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
+ // that situation, we need to pass our result code up to our parent.
+ // However, if someone calls this Activity directly, then this has no
+ // parent, and it needs to set it on itself.
+ private void setResultToParent(int resultCode, Intent data) {
+ Activity a = getParent() == null ? this : getParent();
+ a.setResult(resultCode, data);
+ }
}
diff --git a/src/com/android/browser/BrowserHistoryPage.java b/src/com/android/browser/BrowserHistoryPage.java
index 3059126..42ca848 100644
--- a/src/com/android/browser/BrowserHistoryPage.java
+++ b/src/com/android/browser/BrowserHistoryPage.java
@@ -16,14 +16,17 @@
package com.android.browser;
-import android.app.ListActivity;
+import android.app.Activity;
+import android.app.ExpandableListActivity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.os.Bundle;
+import android.os.Handler;
import android.os.ServiceManager;
import android.provider.Browser;
import android.text.IClipboard;
@@ -41,11 +44,11 @@
import android.webkit.DateSorter;
import android.webkit.WebIconDatabase.IconListener;
import android.widget.AdapterView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
+import android.widget.ExpandableListAdapter;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.TextView;
-import java.util.HashMap;
import java.util.List;
import java.util.Vector;
@@ -53,22 +56,16 @@
* Activity for displaying the browser's history, divided into
* days of viewing.
*/
-public class BrowserHistoryPage extends ListActivity {
+public class BrowserHistoryPage extends ExpandableListActivity {
private HistoryAdapter mAdapter;
private DateSorter mDateSorter;
private boolean mMaxTabsOpen;
private final static String LOGTAG = "browser";
- // FIXME: Make this a part of Browser so we do not have more than one
- // copy (other copy is in BrowserBookmarksAdapter).
- // Used to store favicons as we get them from the database
- private final HashMap<String, Bitmap> mUrlsToIcons =
- new HashMap<String, Bitmap>();
// Implementation of WebIconDatabase.IconListener
private class IconReceiver implements IconListener {
public void onReceivedIcon(String url, Bitmap icon) {
- mUrlsToIcons.put(url, icon);
setListAdapter(mAdapter);
}
}
@@ -87,7 +84,7 @@
b.putBoolean("new_window", true);
intent.putExtras(b);
}
- setResult(RESULT_OK, intent);
+ setResultToParent(RESULT_OK, intent);
finish();
}
@@ -111,20 +108,32 @@
mAdapter = new HistoryAdapter();
setListAdapter(mAdapter);
- ListView list = getListView();
+ final ExpandableListView list = getExpandableListView();
list.setOnCreateContextMenuListener(this);
LayoutInflater factory = LayoutInflater.from(this);
View v = factory.inflate(R.layout.empty_history, null);
addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
list.setEmptyView(v);
-
+ // Do not post the runnable if there is nothing in the list.
+ if (list.getExpandableListAdapter().getGroupCount() > 0) {
+ list.post(new Runnable() {
+ public void run() {
+ // In case the history gets cleared before this event
+ // happens.
+ if (list.getExpandableListAdapter().getGroupCount() > 0) {
+ list.expandGroup(0);
+ }
+ }
+ });
+ }
mMaxTabsOpen = getIntent().getBooleanExtra("maxTabsOpen", false);
- Browser.requestAllIcons(getContentResolver(), null, mIconReceiver);
+ CombinedBookmarkHistoryActivity.getIconListenerSet(getContentResolver())
+ .addListener(mIconReceiver);
// initialize the result to canceled, so that if the user just presses
// back then it will have the correct result
- setResult(RESULT_CANCELED);
+ setResultToParent(RESULT_CANCELED, null);
}
@Override
@@ -160,9 +169,12 @@
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
- AdapterView.AdapterContextMenuInfo i =
- (AdapterView.AdapterContextMenuInfo)
- menuInfo;
+ ExpandableListContextMenuInfo i =
+ (ExpandableListContextMenuInfo) menuInfo;
+ // Do not allow a context menu to come up from the group views.
+ if (!(i.targetView instanceof HistoryItem)) {
+ return;
+ }
// Inflate the menu
MenuInflater inflater = getMenuInflater();
@@ -174,22 +186,20 @@
// Only show open in new tab if we have not maxed out available tabs
menu.findItem(R.id.new_window_context_menu_id).setVisible(!mMaxTabsOpen);
- // decide whether to show the share link option
+ // decide whether to show the share link option
PackageManager pm = getPackageManager();
Intent send = new Intent(Intent.ACTION_SEND);
send.setType("text/plain");
- List<ResolveInfo> list = pm.queryIntentActivities(send,
- PackageManager.MATCH_DEFAULT_ONLY);
- menu.findItem(R.id.share_link_context_menu_id).setVisible(
- list.size() > 0);
+ ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
+ menu.findItem(R.id.share_link_context_menu_id).setVisible(ri != null);
super.onCreateContextMenu(menu, v, menuInfo);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
- AdapterView.AdapterContextMenuInfo i =
- (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
+ ExpandableListContextMenuInfo i =
+ (ExpandableListContextMenuInfo) item.getMenuInfo();
String url = ((HistoryItem)i.targetView).getUrl();
String title = ((HistoryItem)i.targetView).getName();
switch (item.getItemId()) {
@@ -219,17 +229,48 @@
}
@Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
+ public boolean onChildClick(ExpandableListView parent, View v,
+ int groupPosition, int childPosition, long id) {
if (v instanceof HistoryItem) {
loadUrl(((HistoryItem) v).getUrl(), false);
+ return true;
}
+ return false;
}
- private class HistoryAdapter implements ListAdapter {
+ // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
+ // that situation, we need to pass our result code up to our parent.
+ // However, if someone calls this Activity directly, then this has no
+ // parent, and it needs to set it on itself.
+ private void setResultToParent(int resultCode, Intent data) {
+ Activity a = getParent() == null ? this : getParent();
+ a.setResult(resultCode, data);
+ }
+
+ private class ChangeObserver extends ContentObserver {
+ public ChangeObserver() {
+ super(new Handler());
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mAdapter.refreshData();
+ }
+ }
+
+ private class HistoryAdapter implements ExpandableListAdapter {
- // Map of items. Negative values are labels, positive values
- // and zero are cursor offsets.
+ // Array for each of our bins. Each entry represents how many items are
+ // in that bin.
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;
@@ -245,6 +286,7 @@
whereClause, null, orderBy);
buildMap();
+ mCursor.registerContentObserver(new ChangeObserver());
}
void refreshData() {
@@ -255,107 +297,140 @@
}
}
- public void buildMap() {
+ private void buildMap() {
// The cursor is sorted by date
- // Make one pass to build up the ItemMap with the inserted
- // section separators.
- int array[] = new int[mCursor.getCount() + DateSorter.DAY_COUNT];
+ // The ItemMap will store the number of items in each bin.
+ int array[] = new int[DateSorter.DAY_COUNT];
+ // Zero out the array.
+ for (int j = 0; j < DateSorter.DAY_COUNT; j++) {
+ array[j] = 0;
+ }
+ mNumberOfBins = 0;
int dateIndex = -1;
if (mCursor.moveToFirst() && mCursor.getCount() > 0) {
- int itemIndex = 0;
while (!mCursor.isAfterLast()) {
long date = mCursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX);
int index = mDateSorter.getIndex(date);
if (index > dateIndex) {
+ mNumberOfBins++;
+ if (index == DateSorter.DAY_COUNT - 1) {
+ // We are already in the last bin, so it will
+ // include all the remaining items
+ array[index] = mCursor.getCount()
+ - mCursor.getPosition();
+ break;
+ }
dateIndex = index;
- array[itemIndex] = dateIndex - DateSorter.DAY_COUNT;
- itemIndex++;
}
- array[itemIndex] = mCursor.getPosition();
- itemIndex++;
+ array[dateIndex]++;
mCursor.moveToNext();
}
- } else {
- // The db is empty, just add the heading for the first item
- dateIndex = 0;
- array[0] = dateIndex - DateSorter.DAY_COUNT;
}
- // Compress the array as the trailing date sections may be
- // empty
- int extraEntries = DateSorter.DAY_COUNT - dateIndex - 1;
- if (extraEntries > 0) {
- int newArraySize = array.length - extraEntries;
- mItemMap = new int[newArraySize];
- System.arraycopy(array, 0, mItemMap, 0, newArraySize);
- } else {
- mItemMap = array;
- }
+ mItemMap = array;
}
- public View getView(int position, View convertView, ViewGroup parent) {
- if (mItemMap[position] < 0) {
- return getHeaderView(position, convertView);
+ // This translates from a group position in the Adapter to a position in
+ // our array. This is necessary because some positions in the array
+ // have no history items, so we simply do not present those positions
+ // to the Adapter.
+ private int groupPositionToArrayPosition(int groupPosition) {
+ if (groupPosition < 0 || groupPosition >= DateSorter.DAY_COUNT) {
+ throw new AssertionError("group position out of range");
}
- return getHistoryItem(position, convertView);
+ if (DateSorter.DAY_COUNT == mNumberOfBins || 0 == mNumberOfBins) {
+ // In the first case, we have exactly the same number of bins
+ // as our maximum possible, so there is no need to do a
+ // conversion
+ // The second statement is in case this method gets called when
+ // the array is empty, in which case the provided groupPosition
+ // will do fine.
+ return groupPosition;
+ }
+ int arrayPosition = -1;
+ while (groupPosition > -1) {
+ arrayPosition++;
+ if (mItemMap[arrayPosition] != 0) {
+ groupPosition--;
+ }
+ }
+ return arrayPosition;
}
-
- View getHistoryItem(int position, View convertView) {
+
+ public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+ View convertView, ViewGroup parent) {
+ groupPosition = groupPositionToArrayPosition(groupPosition);
HistoryItem item;
if (null == convertView || !(convertView instanceof HistoryItem)) {
item = new HistoryItem(BrowserHistoryPage.this);
+ // Add padding on the left so it will be indented from the
+ // arrows on the group views.
+ item.setPadding(item.getPaddingLeft() + 10,
+ item.getPaddingTop(),
+ item.getPaddingRight(),
+ item.getPaddingBottom());
} else {
item = (HistoryItem) convertView;
}
- mCursor.moveToPosition(mItemMap[position]);
+ int index = childPosition;
+ for (int i = 0; i < groupPosition; i++) {
+ index += mItemMap[i];
+ }
+ mCursor.moveToPosition(index);
item.setName(mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
item.setUrl(url);
- item.setFavicon((Bitmap) mUrlsToIcons.get(url));
+ item.setFavicon(CombinedBookmarkHistoryActivity.getIconListenerSet(
+ getContentResolver()).getFavicon(url));
+ item.setIsBookmark(1 ==
+ mCursor.getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX));
return item;
}
- View getHeaderView(int position, View convertView) {
+ public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
+ groupPosition = groupPositionToArrayPosition(groupPosition);
TextView item;
if (null == convertView || !(convertView instanceof TextView)) {
LayoutInflater factory =
LayoutInflater.from(BrowserHistoryPage.this);
item = (TextView)
- factory.inflate(android.R.layout.preference_category, null);
+ factory.inflate(R.layout.history_header, null);
} else {
item = (TextView) convertView;
}
- item.setText(mDateSorter.getLabel(
- mItemMap[position] + DateSorter.DAY_COUNT));
+ item.setText(mDateSorter.getLabel(groupPosition));
return item;
}
public boolean areAllItemsEnabled() {
- return false;
+ return true;
}
- public boolean isEnabled(int position) {
- return mItemMap[position] >= 0;
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return true;
}
- public int getCount() {
- return mItemMap.length;
+ public int getGroupCount() {
+ return mNumberOfBins;
}
- public Object getItem(int position) {
+ public int getChildrenCount(int groupPosition) {
+ return mItemMap[groupPositionToArrayPosition(groupPosition)];
+ }
+
+ public Object getGroup(int groupPosition) {
return null;
}
- public long getItemId(int position) {
- return mItemMap[position];
+ public Object getChild(int groupPosition, int childPosition) {
+ return null;
}
- // 0 for TextView, 1 for HistoryItem
- public int getItemViewType(int position) {
- return mItemMap[position] < 0 ? 0 : 1;
+ public long getGroupId(int groupPosition) {
+ return groupPosition;
}
- public int getViewTypeCount() {
- return 2;
+ public long getChildId(int groupPosition, int childPosition) {
+ return (childPosition << 3) + groupPosition;
}
public boolean hasStableIds() {
@@ -370,8 +445,24 @@
mObservers.remove(observer);
}
+ public void onGroupExpanded(int groupPosition) {
+
+ }
+
+ public void onGroupCollapsed(int groupPosition) {
+
+ }
+
+ public long getCombinedChildId(long groupId, long childId) {
+ return childId;
+ }
+
+ public long getCombinedGroupId(long groupId) {
+ return groupId;
+ }
+
public boolean isEmpty() {
- return getCount() == 1;
+ return mCursor.getCount() == 0;
}
}
}
diff --git a/src/com/android/browser/BrowserHomepagePreference.java b/src/com/android/browser/BrowserHomepagePreference.java
index bc21143..d4708c3 100644
--- a/src/com/android/browser/BrowserHomepagePreference.java
+++ b/src/com/android/browser/BrowserHomepagePreference.java
@@ -48,8 +48,10 @@
AlertDialog dialog = (AlertDialog) getDialog();
// This callback is called before the dialog has been fully constructed
if (dialog != null) {
+ String url = s.toString();
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(
- Regex.WEB_URL_PATTERN.matcher(s.toString()).matches());
+ url.length() == 0 || url.equals("about:blank") ||
+ Regex.WEB_URL_PATTERN.matcher(url).matches());
}
}
diff --git a/src/com/android/browser/BrowserPreferencesPage.java b/src/com/android/browser/BrowserPreferencesPage.java
index b23f750..5d6795b 100644
--- a/src/com/android/browser/BrowserPreferencesPage.java
+++ b/src/com/android/browser/BrowserPreferencesPage.java
@@ -83,8 +83,7 @@
if (needUpdate) {
value = value.trim().replace(" ", "%20");
}
- Uri path = Uri.parse(value);
- if (path.getScheme() == null) {
+ if (value.length() != 0 && Uri.parse(value).getScheme() == null) {
value = "http://" + value;
needUpdate = true;
}
diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/BrowserProvider.java
index 7aa5bb2..0c930c6 100644
--- a/src/com/android/browser/BrowserProvider.java
+++ b/src/com/android/browser/BrowserProvider.java
@@ -16,23 +16,26 @@
package com.android.browser;
+import android.app.ISearchManager;
import android.app.SearchManager;
+import android.content.ComponentName;
import android.content.ContentProvider;
-import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import android.content.UriMatcher;
import android.database.AbstractCursor;
-import android.database.ContentObserver;
import android.database.Cursor;
-import android.database.DataSetObserver;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemProperties;
import android.provider.Browser;
import android.util.Log;
+import android.server.search.SearchableInfo;
import android.text.util.Regex;
public class BrowserProvider extends ContentProvider {
@@ -40,22 +43,41 @@
private SQLiteOpenHelper mOpenHelper;
private static final String sDatabaseName = "browser.db";
private static final String TAG = "BrowserProvider";
- private static final String ORDER_BY = "date DESC";
+ private static final String ORDER_BY = "visits DESC, date DESC";
private static final String[] TABLE_NAMES = new String[] {
"bookmarks", "searches"
};
- private static final String[] SUGGEST_PROJECTION = new String [] {
- "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT,
- "url AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA,
- "url AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
- "title AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
- "_id"
+ private static final String[] SUGGEST_PROJECTION = new String[] {
+ "_id", "url", "title", "bookmark"
};
private static final String SUGGEST_SELECTION =
"url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?";
private String[] SUGGEST_ARGS = new String[4];
+ // shared suggestion array index, make sure to match COLUMNS
+ private static final int SUGGEST_COLUMN_INTENT_ACTION_ID = 1;
+ private static final int SUGGEST_COLUMN_INTENT_DATA_ID = 2;
+ private static final int SUGGEST_COLUMN_TEXT_1_ID = 3;
+ private static final int SUGGEST_COLUMN_TEXT_2_ID = 4;
+ private static final int SUGGEST_COLUMN_ICON_1_ID = 5;
+ private static final int SUGGEST_COLUMN_ICON_2_ID = 6;
+ private static final int SUGGEST_COLUMN_QUERY_ID = 7;
+
+ // shared suggestion columns
+ private static final String[] COLUMNS = new String[] {
+ "_id",
+ SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
+ SearchManager.SUGGEST_COLUMN_INTENT_DATA,
+ SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_TEXT_2,
+ SearchManager.SUGGEST_COLUMN_ICON_1,
+ SearchManager.SUGGEST_COLUMN_ICON_2,
+ SearchManager.SUGGEST_COLUMN_QUERY};
+
+ private static final int MAX_SUGGESTION_SHORT_ENTRIES = 3;
+ private static final int MAX_SUGGESTION_LONG_ENTRIES = 6;
+
// make sure that these match the index of TABLE_NAMES
private static final int URI_MATCH_BOOKMARKS = 0;
private static final int URI_MATCH_SEARCHES = 1;
@@ -206,221 +228,195 @@
}
/*
- * Subclass AbstractCursor so we can add "Google Search"
+ * Subclass AbstractCursor so we can combine multiple Cursors and add
+ * "Google Search".
+ * Here are the rules.
+ * 1. We only have MAX_SUGGESTION_LONG_ENTRIES in the list plus
+ * "Google Search";
+ * 2. If bookmark/history entries are less than
+ * (MAX_SUGGESTION_SHORT_ENTRIES -1), we include Google suggest.
*/
private class MySuggestionCursor extends AbstractCursor {
- private Cursor mCursor;
+ private Cursor mHistoryCursor;
+ private Cursor mSuggestCursor;
+ private int mHistoryCount;
+ private int mSuggestionCount;
private boolean mBeyondCursor;
private String mString;
- private Uri mNotifyUri;
- private ContentResolver mContentResolver;
- private AbstractCursor.SelfContentObserver mObserver;
- private final Object mObserverLock = new Object();
- public MySuggestionCursor(Cursor c, String string) {
- mCursor = c;
- if (Regex.WEB_URL_PATTERN.matcher(string).matches()) {
- mString = "";
- } else {
- mString = string;
+ public MySuggestionCursor(Cursor hc, Cursor sc, String string) {
+ mHistoryCursor = hc;
+ mSuggestCursor = sc;
+ mHistoryCount = hc.getCount();
+ mSuggestionCount = sc != null ? sc.getCount() : 0;
+ if (mSuggestionCount > (MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount)) {
+ mSuggestionCount = MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount;
}
+ mString = string;
mBeyondCursor = false;
}
+ @Override
public boolean onMove(int oldPosition, int newPosition) {
- if (mCursor.getCount() == newPosition) {
- mBeyondCursor = true;
- } else {
- mCursor.moveToPosition(newPosition);
+ if (mHistoryCursor == null) {
+ return false;
+ }
+ if (mHistoryCount > newPosition) {
+ mHistoryCursor.moveToPosition(newPosition);
mBeyondCursor = false;
+ } else if (mHistoryCount + mSuggestionCount > newPosition) {
+ mSuggestCursor.moveToPosition(newPosition - mHistoryCount);
+ mBeyondCursor = false;
+ } else {
+ mBeyondCursor = true;
}
return true;
}
+ @Override
public int getCount() {
if (mString.length() > 0) {
- return mCursor.getCount() + 1;
+ return mHistoryCount + mSuggestionCount + 1;
} else {
- return mCursor.getCount();
+ return mHistoryCount + mSuggestionCount;
}
}
- public boolean deleteRow() {
- return !mBeyondCursor && mCursor.deleteRow();
- }
-
+ @Override
public String[] getColumnNames() {
- return mCursor.getColumnNames();
+ return COLUMNS;
}
- public int getColumnCount() {
- return mCursor.getColumnCount();
- }
-
+ @Override
public String getString(int columnIndex) {
- if (!mBeyondCursor) {
- return mCursor.getString(columnIndex);
+ if ((mPos != -1 && mHistoryCursor != null)) {
+ switch(columnIndex) {
+ case SUGGEST_COLUMN_INTENT_ACTION_ID:
+ if (mHistoryCount > mPos) {
+ return Intent.ACTION_VIEW;
+ } else {
+ return Intent.ACTION_SEARCH;
+ }
+
+ case SUGGEST_COLUMN_INTENT_DATA_ID:
+ if (mHistoryCount > mPos) {
+ return mHistoryCursor.getString(1);
+ } else {
+ return null;
+ }
+
+ case SUGGEST_COLUMN_TEXT_1_ID:
+ if (mHistoryCount > mPos) {
+ return mHistoryCursor.getString(1);
+ } else if (!mBeyondCursor) {
+ return mSuggestCursor.getString(1);
+ } else {
+ return mString;
+ }
+
+ case SUGGEST_COLUMN_TEXT_2_ID:
+ if (mHistoryCount > mPos) {
+ return mHistoryCursor.getString(2);
+ } else if (!mBeyondCursor) {
+ return mSuggestCursor.getString(2);
+ } else {
+ return getContext().getString(R.string.search_google);
+ }
+
+ case SUGGEST_COLUMN_ICON_1_ID:
+ if (mHistoryCount > mPos) {
+ if (mHistoryCursor.getInt(3) == 1) {
+ return new Integer(
+ R.drawable.ic_search_category_bookmark)
+ .toString();
+ } else {
+ return new Integer(
+ R.drawable.ic_search_category_history)
+ .toString();
+ }
+ } else {
+ return new Integer(
+ R.drawable.ic_search_category_suggest)
+ .toString();
+ }
+
+ case SUGGEST_COLUMN_ICON_2_ID:
+ return new String("0");
+
+ case SUGGEST_COLUMN_QUERY_ID:
+ if (mHistoryCount > mPos) {
+ return null;
+ } else if (!mBeyondCursor) {
+ return mSuggestCursor.getString(3);
+ } else {
+ return mString;
+ }
+ }
}
- switch (columnIndex) {
- case 2: // SearchManager.SUGGEST_COLUMN_TEXT_1
- return "Google Search for \"" + mString + "\"";
- case 1: // SearchManager.SUGGEST_COLUMN_INTENT_DATA
- return BrowserActivity.composeSearchUrl(mString);
- case 3: // SearchManager.SUGGEST_COLUMN_TEXT_2
- default:
- return "";
+ return null;
+ }
+
+ @Override
+ public double getDouble(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public float getFloat(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getInt(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getLong(int column) {
+ if ((mPos != -1) && column == 0) {
+ return mPos; // use row# as the _Id
}
+ throw new UnsupportedOperationException();
}
- public short getShort(int columnIndex) {
- if (!mBeyondCursor) {
- return mCursor.getShort(columnIndex);
- }
- if (0 == columnIndex) {
- return 0;
- }
- return -1;
+ @Override
+ public short getShort(int column) {
+ throw new UnsupportedOperationException();
}
- public int getInt(int columnIndex) {
- if (!mBeyondCursor) {
- return mCursor.getInt(columnIndex);
- }
- if (0 == columnIndex) {
- return 0;
- }
- return -1;
- }
-
- public long getLong(int columnIndex) {
- if (!mBeyondCursor) {
- return mCursor.getLong(columnIndex);
- }
- if (0 == columnIndex) {
- return 0;
- }
- return -1;
- }
-
- public float getFloat(int columnIndex) {
- if (!mBeyondCursor) {
- return mCursor.getFloat(columnIndex);
- }
- if (0 == columnIndex) {
- return 0f;
- }
- return -1f;
- }
-
- public double getDouble(int columnIndex) {
- if (!mBeyondCursor) {
- return mCursor.getDouble(columnIndex);
- }
- if (0 == columnIndex) {
- return 0.0;
- }
- return -1.0;
- }
-
- public boolean isNull(int columnIndex) {
- return mCursor.isNull(columnIndex);
- }
-
- public boolean supportsUpdates() {
- return false;
- }
-
- public boolean hasUpdates() {
- return false;
- }
-
- public boolean updateString(int columnIndex, String value) {
- return false;
- }
-
- public boolean updateShort(int columnIndex, short value) {
- return false;
- }
-
- public boolean updateInt(int columnIndex, int value) {
- return false;
- }
-
- public boolean updateLong(int columnIndex, long value) {
- return false;
- }
-
- public boolean updateFloat(int columnIndex, float value) {
- return false;
- }
-
- public boolean updateDouble(int columnIndex, double value) {
- return false;
+ @Override
+ public boolean isNull(int column) {
+ throw new UnsupportedOperationException();
}
// TODO Temporary change, finalize after jq's changes go in
public void deactivate() {
- if (mCursor != null) {
- mCursor.deactivate();
+ if (mHistoryCursor != null) {
+ mHistoryCursor.deactivate();
+ }
+ if (mSuggestCursor != null) {
+ mSuggestCursor.deactivate();
}
super.deactivate();
}
public boolean requery() {
- return mCursor.requery();
+ return (mHistoryCursor != null ? mHistoryCursor.requery() : false) |
+ (mSuggestCursor != null ? mSuggestCursor.requery() : false);
}
// TODO Temporary change, finalize after jq's changes go in
public void close() {
super.close();
- if (mCursor != null) {
- mCursor.close();
- mCursor = null;
+ if (mHistoryCursor != null) {
+ mHistoryCursor.close();
+ mHistoryCursor = null;
}
- }
-
- public void registerContentObserver(ContentObserver observer) {
- super.registerContentObserver(observer);
- }
-
- public void unregisterContentObserver(ContentObserver observer) {
- super.unregisterContentObserver(observer);
- }
-
- public void registerDataSetObserver(DataSetObserver observer) {
- super.registerDataSetObserver(observer);
- }
-
- public void unregisterDataSetObserver(DataSetObserver observer) {
- super.unregisterDataSetObserver(observer);
- }
-
- protected void onChange(boolean selfChange) {
- synchronized (mObserverLock) {
- super.onChange(selfChange);
- if (mNotifyUri != null && selfChange) {
- mContentResolver.notifyChange(mNotifyUri, mObserver);
- }
+ if (mSuggestCursor != null) {
+ mSuggestCursor.close();
+ mSuggestCursor = null;
}
}
-
- public void setNotificationUri(ContentResolver cr, Uri uri) {
- synchronized (mObserverLock) {
- if (mObserver != null) {
- cr.unregisterContentObserver(mObserver);
- }
- mObserver = new AbstractCursor.SelfContentObserver(this);
- cr.registerContentObserver(uri, true, mObserver);
- mCursor.setNotificationUri(cr, uri);
- super.setNotificationUri(cr, uri);
- mContentResolver = cr;
- mNotifyUri = uri;
- }
- }
-
- public boolean getWantsAllOnMoveCalls() {
- return mCursor.getWantsAllOnMoveCalls();
- }
}
@Override
@@ -455,13 +451,58 @@
suggestSelection = SUGGEST_SELECTION;
}
}
- // Suggestions are always performed with the default sort order:
- // date ASC.
+
Cursor c = db.query(TABLE_NAMES[URI_MATCH_BOOKMARKS],
SUGGEST_PROJECTION, suggestSelection, myArgs, null, null,
- ORDER_BY, null);
- c.setNotificationUri(getContext().getContentResolver(), url);
- return new MySuggestionCursor(c, selectionArgs[0]);
+ ORDER_BY,
+ (new Integer(MAX_SUGGESTION_LONG_ENTRIES)).toString());
+
+ if (Regex.WEB_URL_PATTERN.matcher(selectionArgs[0]).matches()) {
+ return new MySuggestionCursor(c, null, "");
+ } else {
+ // get Google suggest if there is still space in the list
+ if (myArgs != null && myArgs.length > 1
+ && c.getCount() < (MAX_SUGGESTION_SHORT_ENTRIES - 1)) {
+ ISearchManager sm = ISearchManager.Stub
+ .asInterface(ServiceManager
+ .getService(Context.SEARCH_SERVICE));
+ SearchableInfo si = null;
+ try {
+ // use the global search to get Google suggest provider
+ si = sm.getSearchableInfo(new ComponentName(
+ getContext(), "com.android.browser"), true);
+
+ // similar to the getSuggestions() in SearchDialog.java
+ StringBuilder uriStr = new StringBuilder("content://");
+ uriStr.append(si.getSuggestAuthority());
+ // if content path provided, insert it now
+ final String contentPath = si.getSuggestPath();
+ if (contentPath != null) {
+ uriStr.append('/');
+ uriStr.append(contentPath);
+ }
+ // append standard suggestion query path
+ uriStr.append('/' + SearchManager.SUGGEST_URI_PATH_QUERY);
+ // inject query, either as selection args or inline
+ String[] selArgs = null;
+ if (si.getSuggestSelection() != null) {
+ selArgs = new String[] {selectionArgs[0]};
+ } else {
+ uriStr.append('/');
+ uriStr.append(Uri.encode(selectionArgs[0]));
+ }
+
+ // finally, make the query
+ Cursor sc = getContext().getContentResolver().query(
+ Uri.parse(uriStr.toString()), null,
+ si.getSuggestSelection(), selArgs, null);
+
+ return new MySuggestionCursor(c, sc, selectionArgs[0]);
+ } catch (RemoteException e) {
+ }
+ }
+ return new MySuggestionCursor(c, null, selectionArgs[0]);
+ }
}
String[] projection = null;
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 6164e38..d8c5186 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -26,6 +26,7 @@
import android.view.WindowManager;
import android.webkit.CacheManager;
import android.webkit.CookieManager;
+import android.webkit.WebView;
import android.webkit.WebViewDatabase;
import android.webkit.WebIconDatabase;
import android.webkit.WebSettings;
@@ -53,7 +54,7 @@
// Public variables for settings
// NOTE: these defaults need to be kept in sync with the XML
// until the performance of PreferenceManager.setDefaultValues()
- // is improved.
+ // is improved.
private boolean loadsImagesAutomatically = true;
private boolean javaScriptEnabled = true;
private boolean pluginsEnabled = true;
@@ -64,13 +65,12 @@
private boolean saveFormData = true;
private boolean openInBackground = false;
private String defaultTextEncodingName;
- private String homeUrl = "http://www.google.com/m?client=ms-" +
- SystemProperties.get("ro.com.google.clientid", "unknown");
+ private String homeUrl = "http://www.google.com/m?client=ms-" +
+ SystemProperties.get("persist.sys.com.google.clientid", "unknown");
private boolean loginInitialized = false;
- private int orientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
private boolean autoFitPage = true;
private boolean showDebugSettings = false;
-
+
// Development settings
public WebSettings.LayoutAlgorithm layoutAlgorithm =
WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
@@ -79,7 +79,7 @@
private boolean tracing = false;
private boolean lightTouch = false;
private boolean navDump = false;
- // Browser only settings
+ // Browser only settings
private boolean doFlick = false;
// Private preconfigured values
@@ -89,17 +89,17 @@
private static int defaultFixedFontSize = 13;
private static WebSettings.TextSize textSize =
WebSettings.TextSize.NORMAL;
-
+
// Preference keys that are used outside this class
public final static String PREF_CLEAR_CACHE = "privacy_clear_cache";
public final static String PREF_CLEAR_COOKIES = "privacy_clear_cookies";
public final static String PREF_CLEAR_HISTORY = "privacy_clear_history";
public final static String PREF_HOMEPAGE = "homepage";
- public final static String PREF_CLEAR_FORM_DATA =
+ public final static String PREF_CLEAR_FORM_DATA =
"privacy_clear_form_data";
- public final static String PREF_CLEAR_PASSWORDS =
+ public final static String PREF_CLEAR_PASSWORDS =
"privacy_clear_passwords";
- public final static String PREF_EXTRAS_RESET_DEFAULTS =
+ 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";
@@ -120,7 +120,7 @@
public final static int MAX_TEXTVIEW_LEN = 80;
private TabControl mTabControl;
-
+
// Single instance of the BrowserSettings for use in the Browser app.
private static BrowserSettings sSingleton;
@@ -128,7 +128,7 @@
// observer.
private HashMap<WebSettings,Observer> mWebSettingsToObservers =
new HashMap<WebSettings,Observer>();
-
+
/*
* An observer wrapper for updating a WebSettings object with the new
* settings after a call to BrowserSettings.update().
@@ -180,7 +180,7 @@
s.setAllowFileAccess(false);
}
}
-
+
/**
* Load settings from the browser app's database.
* NOTE: Strings used for the preferences must match those specified
@@ -191,7 +191,7 @@
* observers of this object.
*/
public void loadFromDb(Context ctx) {
- SharedPreferences p =
+ SharedPreferences p =
PreferenceManager.getDefaultSharedPreferences(ctx);
// Set the default value for the plugins path to the application's
@@ -204,34 +204,33 @@
//PreferenceManager.setDefaultValues(ctx, R.xml.browser_preferences);
syncSharedPreferences(p);
}
-
+
/* package */ void syncSharedPreferences(SharedPreferences p) {
- homeUrl =
+ homeUrl =
p.getString(PREF_HOMEPAGE, homeUrl);
- loadsImagesAutomatically = p.getBoolean("load_images",
+ loadsImagesAutomatically = p.getBoolean("load_images",
loadsImagesAutomatically);
- javaScriptEnabled = p.getBoolean("enable_javascript",
+ javaScriptEnabled = p.getBoolean("enable_javascript",
javaScriptEnabled);
- pluginsEnabled = p.getBoolean("enable_plugins",
+ pluginsEnabled = p.getBoolean("enable_plugins",
pluginsEnabled);
pluginsPath = p.getString("plugins_path", pluginsPath);
javaScriptCanOpenWindowsAutomatically = !p.getBoolean(
- "block_popup_windows",
+ "block_popup_windows",
!javaScriptCanOpenWindowsAutomatically);
- showSecurityWarnings = p.getBoolean("show_security_warnings",
+ showSecurityWarnings = p.getBoolean("show_security_warnings",
showSecurityWarnings);
- rememberPasswords = p.getBoolean("remember_passwords",
+ rememberPasswords = p.getBoolean("remember_passwords",
rememberPasswords);
- saveFormData = p.getBoolean("save_formdata",
+ saveFormData = p.getBoolean("save_formdata",
saveFormData);
- boolean accept_cookies = p.getBoolean("accept_cookies",
+ boolean accept_cookies = p.getBoolean("accept_cookies",
CookieManager.getInstance().acceptCookie());
CookieManager.getInstance().setAcceptCookie(accept_cookies);
openInBackground = p.getBoolean("open_in_background", openInBackground);
loginInitialized = p.getBoolean("login_initialized", loginInitialized);
textSize = WebSettings.TextSize.valueOf(
p.getString(PREF_TEXT_SIZE, textSize.name()));
- orientation = p.getInt("orientation", orientation);
autoFitPage = p.getBoolean("autofit_pages", autoFitPage);
useWideViewPort = true; // use wide view port for either setting
if (autoFitPage) {
@@ -242,23 +241,23 @@
defaultTextEncodingName =
p.getString(PREF_DEFAULT_TEXT_ENCODING,
defaultTextEncodingName);
-
- showDebugSettings =
+
+ showDebugSettings =
p.getBoolean(PREF_DEBUG_SETTINGS, showDebugSettings);
// Debug menu items have precidence if the menu is visible
if (showDebugSettings) {
- boolean small_screen = p.getBoolean("small_screen",
- layoutAlgorithm ==
+ boolean small_screen = p.getBoolean("small_screen",
+ layoutAlgorithm ==
WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
if (small_screen) {
layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN;
} else {
- boolean normal_layout = p.getBoolean("normal_layout",
+ boolean normal_layout = p.getBoolean("normal_layout",
layoutAlgorithm == WebSettings.LayoutAlgorithm.NORMAL);
if (normal_layout) {
layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL;
} else {
- layoutAlgorithm =
+ layoutAlgorithm =
WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
}
}
@@ -273,7 +272,7 @@
}
update();
}
-
+
public String getPluginsPath() {
return pluginsPath;
}
@@ -281,42 +280,27 @@
public String getHomePage() {
return homeUrl;
}
-
+
public void setHomePage(Context context, String url) {
Editor ed = PreferenceManager.
- getDefaultSharedPreferences(context).edit();
+ getDefaultSharedPreferences(context).edit();
ed.putString(PREF_HOMEPAGE, url);
ed.commit();
homeUrl = url;
}
-
+
public boolean isLoginInitialized() {
return loginInitialized;
}
-
+
public void setLoginInitialized(Context context) {
loginInitialized = true;
Editor ed = PreferenceManager.
- getDefaultSharedPreferences(context).edit();
+ getDefaultSharedPreferences(context).edit();
ed.putBoolean("login_initialized", loginInitialized);
ed.commit();
}
-
- public int getOrientation() {
- return orientation;
- }
-
- public void setOrientation(Context context, int o) {
- if (orientation == o) {
- return;
- }
- orientation = o;
- Editor ed = PreferenceManager.
- getDefaultSharedPreferences(context).edit();
- ed.putInt("orientation", orientation);
- ed.commit();
- }
-
+
public WebSettings.TextSize getTextSize() {
return textSize;
}
@@ -344,19 +328,21 @@
public boolean doFlick() {
return doFlick;
}
-
+
public boolean showDebugSettings() {
return showDebugSettings;
}
-
+
public void toggleDebugSettings() {
showDebugSettings = !showDebugSettings;
+ navDump = showDebugSettings;
+ update();
}
/**
* Add a WebSettings object to the list of observers that will be updated
* when update() is called.
- *
+ *
* @param s A WebSettings object that is strictly tied to the life of a
* WebView.
*/
@@ -412,7 +398,10 @@
/*package*/ void clearCache(Context context) {
WebIconDatabase.getInstance().removeAllIcons();
if (mTabControl != null) {
- mTabControl.getCurrentWebView().clearCache(true);
+ WebView current = mTabControl.getCurrentWebView();
+ if (current != null) {
+ current.clearCache(true);
+ }
}
}
@@ -438,12 +427,12 @@
db.clearUsernamePassword();
db.clearHttpAuthUsernamePassword();
}
-
+
/*package*/ void resetDefaultPreferences(Context context) {
- SharedPreferences p =
+ SharedPreferences p =
PreferenceManager.getDefaultSharedPreferences(context);
p.edit().clear().commit();
- PreferenceManager.setDefaultValues(context, R.xml.browser_preferences,
+ PreferenceManager.setDefaultValues(context, R.xml.browser_preferences,
true);
}
diff --git a/src/com/android/browser/CombinedBookmarkHistoryActivity.java b/src/com/android/browser/CombinedBookmarkHistoryActivity.java
new file mode 100644
index 0000000..963f179
--- /dev/null
+++ b/src/com/android/browser/CombinedBookmarkHistoryActivity.java
@@ -0,0 +1,124 @@
+/*
+ * 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.TabActivity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.provider.Browser;
+import android.webkit.WebIconDatabase.IconListener;
+import android.widget.TabHost;
+import android.widget.TabHost.TabSpec;
+import android.view.Window;
+
+import java.util.HashMap;
+import java.util.Vector;
+
+public class CombinedBookmarkHistoryActivity extends TabActivity
+ implements TabHost.OnTabChangeListener {
+ /* package */ static String BOOKMARKS_TAB = "bookmark";
+ /* package */ static String VISITED_TAB = "visited";
+ /* package */ static String HISTORY_TAB = "history";
+ /* package */ static String STARTING_TAB = "tab";
+
+ static class IconListenerSet implements IconListener {
+ // Used to store favicons as we get them from the database
+ // FIXME: We use a different method to get the Favicons in
+ // BrowserBookmarksAdapter. They should probably be unified.
+ private HashMap<String, Bitmap> mUrlsToIcons;
+ private Vector<IconListener> mListeners;
+
+ public IconListenerSet() {
+ mUrlsToIcons = new HashMap<String, Bitmap>();
+ mListeners = new Vector<IconListener>();
+ }
+ public void onReceivedIcon(String url, Bitmap icon) {
+ mUrlsToIcons.put(url, icon);
+ for (IconListener listener : mListeners) {
+ listener.onReceivedIcon(url, icon);
+ }
+ }
+ public void addListener(IconListener listener) {
+ mListeners.add(listener);
+ }
+ public Bitmap getFavicon(String url) {
+ return (Bitmap) mUrlsToIcons.get(url);
+ }
+ }
+ private static IconListenerSet sIconListenerSet;
+ static IconListenerSet getIconListenerSet(ContentResolver cr) {
+ if (null == sIconListenerSet) {
+ sIconListenerSet = new IconListenerSet();
+ Browser.requestAllIcons(cr, null, sIconListenerSet);
+ }
+ return sIconListenerSet;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.tabs);
+ TabHost tabHost = getTabHost();
+ tabHost.setOnTabChangedListener(this);
+
+ Bundle extras = getIntent().getExtras();
+ Resources resources = getResources();
+
+ getIconListenerSet(getContentResolver());
+ Intent bookmarksIntent = new Intent(this, BrowserBookmarksPage.class);
+ bookmarksIntent.putExtras(extras);
+ tabHost.addTab(tabHost.newTabSpec(BOOKMARKS_TAB)
+ .setIndicator(resources.getString(R.string.tab_bookmarks),
+ resources.getDrawable(R.drawable.browser_bookmark_tab))
+ .setContent(bookmarksIntent));
+
+ Intent visitedIntent = new Intent(this, MostVisitedActivity.class);
+ visitedIntent.putExtras(extras);
+ tabHost.addTab(tabHost.newTabSpec(VISITED_TAB)
+ .setIndicator(resources.getString(R.string.tab_most_visited),
+ resources.getDrawable(R.drawable.browser_visited_tab))
+ .setContent(visitedIntent));
+
+ Intent historyIntent = new Intent(this, BrowserHistoryPage.class);
+ historyIntent.putExtras(extras);
+ tabHost.addTab(tabHost.newTabSpec(HISTORY_TAB)
+ .setIndicator(resources.getString(R.string.tab_history),
+ resources.getDrawable(R.drawable.
+ browser_history_tab)).setContent(historyIntent));
+
+ String defaultTab = extras.getString(STARTING_TAB);
+ if (defaultTab != null) {
+ tabHost.setCurrentTab(2);
+ }
+ }
+
+ // Copied from DialTacts Activity
+ /** {@inheritDoc} */
+ public void onTabChanged(String tabId) {
+ Activity activity = getLocalActivityManager().getActivity(tabId);
+ if (activity != null) {
+ activity.onWindowFocusChanged(true);
+ }
+ }
+
+
+}
diff --git a/src/com/android/browser/FakeWebView.java b/src/com/android/browser/FakeWebView.java
index 7997672..633b799 100644
--- a/src/com/android/browser/FakeWebView.java
+++ b/src/com/android/browser/FakeWebView.java
@@ -34,10 +34,12 @@
*/
public class FakeWebView extends ImageView {
private TabControl.Tab mTab;
+ private Picture mPicture;
private boolean mUsesResource;
private class Listener implements WebView.PictureListener {
public void onNewPicture(WebView view, Picture p) {
+ FakeWebView.this.mPicture = p;
FakeWebView.this.invalidate();
}
};
@@ -69,13 +71,12 @@
if (mTab != null) {
final WebView w = mTab.getTopWindow();
if (w != null) {
- Picture p = w.capturePicture();
- if (p != null) {
+ if (mPicture != null) {
canvas.save();
float scale = getWidth() * w.getScale() / w.getWidth();
canvas.scale(scale, scale);
canvas.translate(-w.getScrollX(), -w.getScrollY());
- canvas.drawPicture(p);
+ canvas.drawPicture(mPicture);
canvas.restore();
}
}
@@ -99,10 +100,12 @@
mTab = t;
if (t != null && t.getWebView() != null) {
Listener l = new Listener();
- t.getWebView().setPictureListener(l);
if (t.getSubWebView() != null) {
t.getSubWebView().setPictureListener(l);
+ } else {
+ t.getWebView().setPictureListener(l);
}
+ mPicture = mTab.getTopWindow().capturePicture();
}
}
}
diff --git a/src/com/android/browser/FindDialog.java b/src/com/android/browser/FindDialog.java
index 2b26784..43cd1c4 100644
--- a/src/com/android/browser/FindDialog.java
+++ b/src/com/android/browser/FindDialog.java
@@ -17,6 +17,7 @@
package com.android.browser;
import android.app.Dialog;
+import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
@@ -29,6 +30,8 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView;
import android.widget.EditText;
import android.widget.TextView;
@@ -65,9 +68,19 @@
throw new AssertionError("No WebView for FindDialog::onClick");
}
mWebView.findNext(false);
+ hideSoftInput();
}
};
-
+
+ /*
+ * Remove the soft keyboard from the screen.
+ */
+ private void hideSoftInput() {
+ InputMethodManager imm = (InputMethodManager)
+ mBrowserActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
+ }
+
private void disableButtons() {
mPrevButton.setEnabled(false);
mNextButton.setEnabled(false);
@@ -121,6 +134,8 @@
mMatches = (TextView) findViewById(R.id.matches);
mMatchesView = findViewById(R.id.matches_view);
disableButtons();
+ theWindow.setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
}
public void dismiss() {
@@ -154,8 +169,9 @@
throw new AssertionError("No WebView for FindDialog::findNext");
}
mWebView.findNext(true);
+ hideSoftInput();
}
-
+
public void show() {
super.show();
mEditText.requestFocus();
diff --git a/src/com/android/browser/GearsBaseDialog.java b/src/com/android/browser/GearsBaseDialog.java
index afd0dd2..638ba27 100644
--- a/src/com/android/browser/GearsBaseDialog.java
+++ b/src/com/android/browser/GearsBaseDialog.java
@@ -17,6 +17,7 @@
package com.android.browser;
import android.app.Activity;
+import android.app.Dialog;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -67,6 +68,7 @@
public static final int UPDATE_ICON = 5;
public static final int REQUEST_ICON = 6;
public static final int PAUSE_REQUEST_ICON = 7;
+ public static final int CLEAR_REQUEST_ICON = 8;
protected final String LOCAL_DATA_STRING = "localData";
protected final String LOCAL_STORAGE_STRING = "localStorage";
@@ -191,14 +193,14 @@
public void onClick(View v) {
mHandler.sendEmptyMessage(ALWAYS_DENY);
}
- }, true, false);
+ });
setupButton(R.id.button_allow, allowRsc,
new Button.OnClickListener() {
public void onClick(View v) {
mHandler.sendEmptyMessage(ALLOW);
}
- }, false, true);
+ });
setupButton(R.id.button_deny, denyRsc,
new Button.OnClickListener() {
@@ -249,6 +251,77 @@
}
/**
+ * Utility method to hide a view.
+ */
+ void hideView(View v, int rsc) {
+ if (rsc == 0) {
+ return;
+ }
+ View view;
+ if (v == null) {
+ view = findViewById(rsc);
+ } else {
+ view = v.findViewById(rsc);
+ }
+ if (view != null) {
+ view.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Utility method to show a view.
+ */
+ void showView(View v, int rsc) {
+ if (rsc == 0) {
+ return;
+ }
+ View view;
+ if (v == null) {
+ view = findViewById(rsc);
+ } else {
+ view = v.findViewById(rsc);
+ }
+ if (view != null) {
+ view.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Utility method to set a text.
+ */
+ void setText(View v, int rsc, CharSequence text) {
+ if (rsc == 0) {
+ return;
+ }
+ View view = v.findViewById(rsc);
+ if (view != null) {
+ TextView textView = (TextView) view;
+ textView.setText(text);
+ textView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Utility method to set a text.
+ */
+ void setText(View v, int rsc, int txtRsc) {
+ if (rsc == 0) {
+ return;
+ }
+ View view = v.findViewById(rsc);
+ if (view != null) {
+ TextView textView = (TextView) view;
+ if (txtRsc == 0) {
+ textView.setVisibility(View.GONE);
+ } else {
+ CharSequence text = getString(txtRsc);
+ textView.setText(text);
+ textView.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ /**
* Utility class to download an icon in the background.
* Once done ask the UI thread to update the icon.
*/
@@ -360,7 +433,7 @@
*/
public void setupDialog(TextView message, ImageView icon) {
message.setText(R.string.unrecognized_dialog_message);
- icon.setImageResource(R.drawable.gears_icon_48x48);
+ icon.setImageResource(R.drawable.ic_dialog_menu_generic);
message.setVisibility(View.VISIBLE);
}
@@ -373,4 +446,29 @@
setupDialog();
}
+ /**
+ * Method called when the back button is pressed,
+ * allowing the dialog to intercept the default behaviour.
+ */
+ public boolean handleBackButton() {
+ return false;
+ }
+
+ /**
+ * Returns the resource string of the notification displayed
+ * after the dialog. By default, does not return one.
+ */
+ public int notification() {
+ return 0;
+ }
+
+ /**
+ * If a secondary dialog (e.g. a confirmation dialog) is created,
+ * GearsNativeDialog will call this method.
+ */
+ public Dialog onCreateDialog(int id) {
+ // This should be redefined by subclasses as needed.
+ return null;
+ }
+
}
diff --git a/src/com/android/browser/GearsDialog.java b/src/com/android/browser/GearsDialog.java
deleted file mode 100644
index 62a8aaa..0000000
--- a/src/com/android/browser/GearsDialog.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2008 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.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.Button;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.webkit.WebSettings;
-
-import android.view.KeyEvent;
-import android.view.ViewGroup.LayoutParams;
-
-import android.widget.LinearLayout;
-
-public class GearsDialog extends Activity {
-
- private static final String TAG = "GearsDialog";
-
- private WebView webview;
-
- private String htmlContent;
- private String dialogArguments;
-
- private boolean dismissed = false;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- webview = new WebView(this);
- webview.getSettings().setJavaScriptEnabled(true);
- webview.addJavascriptInterface(this, "bridge");
-
- setContentView(webview);
- setTitle("Gears");
- }
-
- @Override
- public void onStart() {
- super.onStart();
- loadHTML();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- // In case we reach this point without
- // notifying GearsDialogService, we do it now.
- if (!dismissed) {
- notifyEndOfDialog();
- }
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- Intent i = getIntent();
- boolean inSettings = i.getBooleanExtra("inSettings", false);
- // If we are called from the settings, we
- // dismiss ourselve upon rotation
- if (inSettings) {
- notifyEndOfDialog();
- finish();
- }
- }
-
- /**
- * Load the HTML content in the WebView
- */
- private void loadHTML() {
- Intent i = getIntent();
- htmlContent = i.getStringExtra("htmlContent");
- dialogArguments = i.getStringExtra("dialogArguments");
- webview.loadDataWithBaseURL("", htmlContent, "text/html", "", "");
- }
-
- /**
- * Signal to GearsDialogService that we are done.
- */
- private void notifyEndOfDialog() {
- GearsDialogService.signalFinishedDialog();
- dismissed = true;
- }
-
- /**
- * Intercepts the back key to immediately notify
- * GearsDialogService that we are done.
- */
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isDown()) {
- notifyEndOfDialog();
- }
- return super.dispatchKeyEvent(event);
- }
-
- /**
- * Returns a json-formatted string containing the information
- * about the site.
- * This method is accessible through the javascript bridge.
- */
- public String getDialogArguments() {
- return dialogArguments;
- }
-
- /**
- * Set the results string and closes the dialog.
- * This method is accessible through the javascript bridge.
- */
- public void closeDialog(String results) {
- GearsDialogService.closeDialog(results);
- notifyEndOfDialog();
- finish();
- }
-
- /**
- * Debug method outputting a message.
- * This method is accessible through the javascript bridge.
- */
- public void log(String msg) {
- Log.v(TAG, msg);
- }
-}
diff --git a/src/com/android/browser/GearsDialogService.java b/src/com/android/browser/GearsDialogService.java
deleted file mode 100644
index b92ff47..0000000
--- a/src/com/android/browser/GearsDialogService.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2008 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.Service;
-import android.util.Log;
-import android.content.Intent;
-import android.os.IBinder;
-
-import android.content.Intent;
-import android.content.ContentValues;
-import android.content.ActivityNotFoundException;
-
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-import java.lang.InterruptedException;
-
-public class GearsDialogService extends Service {
- private static final String TAG = "GearsDialogService";
- private final String DIALOG_PACKAGE = "com.android.browser";
- private final String DIALOG_CLASS = DIALOG_PACKAGE + ".GearsDialog";
-
- public static Lock lock = new ReentrantLock();
- public static Condition dialogFinished = lock.newCondition();
- public static String results = null;
-
- @Override
- public IBinder onBind(Intent intent) {
- if (IGearsDialogService.class.getName().equals(intent.getAction())) {
- return mBinder;
- }
- return null;
- }
-
- private final IGearsDialogService.Stub mBinder = new IGearsDialogService.Stub() {
- public String showDialog(String htmlContent, String dialogArguments,
- boolean inSettings) {
- return GearsDialogService.this.showDialog(htmlContent, dialogArguments,
- inSettings);
- }
- };
-
- public static void closeDialog(String res) {
- results = res;
- }
-
- public static void signalFinishedDialog() {
- lock.lock();
- dialogFinished.signal();
- lock.unlock();
- }
-
- /**
- * Show a 'true' modal dialog displaying html content, necessary
- * for Gears. The method starts the GearsDialog activity, passing
- * the necessary parameters to it, and then wait until the activity
- * is finished. When the dialog closes, it sets the variable results.
- */
- public String showDialog(String htmlContent, String dialogArguments,
- boolean inSettings) {
- try {
- Intent intent = new Intent();
- intent.setClassName(DIALOG_PACKAGE, DIALOG_CLASS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra("htmlContent", htmlContent);
- intent.putExtra("dialogArguments", dialogArguments);
- intent.putExtra("inSettings", inSettings);
- lock.lock();
- startActivity(intent);
- dialogFinished.await();
- } catch (InterruptedException e) {
- Log.e(TAG, "exception e: " + e);
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "exception e: " + e);
- } finally {
- lock.unlock();
- }
- return results;
- }
-}
diff --git a/src/com/android/browser/GearsFilePickerDialog.java b/src/com/android/browser/GearsFilePickerDialog.java
deleted file mode 100644
index d84a970..0000000
--- a/src/com/android/browser/GearsFilePickerDialog.java
+++ /dev/null
@@ -1,725 +0,0 @@
-/*
- * Copyright (C) 2008 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.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.net.Uri;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.MediaStore;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.GridView;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Vector;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-/**
- * Gears FilePicker dialog
- */
-class GearsFilePickerDialog extends GearsBaseDialog
- implements View.OnTouchListener {
-
- private static final String TAG = "Gears FilePicker";
- private static Bitmap mDirectoryIcon;
- private static Bitmap mDefaultIcon;
- private static Bitmap mImageIcon;
- private static Bitmap mBackIcon;
-
- private static String MULTIPLE_FILES = "MULTIPLE_FILES";
- private static String SINGLE_FILE = "SINGLE_FILE";
-
- private static ImagesLoad mImagesLoader;
- private FilePickerAdapter mAdapter;
- private String mSelectionMode;
- private boolean mMultipleSelection;
- private String mCurrentPath;
-
- // Disable saving thumbnails until this is refactored to fit into
- // existing schemes.
- private static final boolean enableSavedThumbnails = false;
-
- public GearsFilePickerDialog(Activity activity,
- Handler handler,
- String arguments) {
- super (activity, handler, arguments);
- mAdapter = new FilePickerAdapter(activity);
- parseArguments();
- }
-
- public void parseArguments() {
- mSelectionMode = MULTIPLE_FILES;
- try {
- JSONObject json = new JSONObject(mDialogArguments);
-
- if (json.has("mode")) {
- mSelectionMode = json.getString("mode");
- }
- } catch (JSONException e) {
- Log.e(TAG, "exc: " + e);
- }
- if (mSelectionMode.equalsIgnoreCase(SINGLE_FILE)) {
- mMultipleSelection = false;
- } else {
- mMultipleSelection = true;
- }
- }
-
- public void setup() {
- inflate(R.layout.gears_dialog_filepicker, R.id.panel_content);
- setupButtons(0,
- R.string.filepicker_button_allow,
- R.string.filepicker_button_deny);
- setupDialog();
-
- TextView textViewPath = (TextView) findViewById(R.id.path_name);
- if (textViewPath != null) {
- textViewPath.setText(R.string.filepicker_path);
- }
-
- GridView view = (GridView) findViewById(R.id.files_list);
- view.setAdapter(mAdapter);
- view.setOnTouchListener(this);
-
- setSelectionText();
-
- mImagesLoader = new ImagesLoad(mAdapter);
- mImagesLoader.setAdapterView(view);
- Thread thread = new Thread(mImagesLoader);
- thread.start();
- }
-
- public void setSelectionText() {
- Vector elements = mAdapter.selectedElements();
- if (elements == null)
- return;
- TextView info = (TextView) findViewById(R.id.selection);
- int nbElements = elements.size();
- if (nbElements == 0) {
- info.setText(R.string.filepicker_no_files_selected);
- } else if (nbElements == 1) {
- info.setText(R.string.filepicker_one_file_selected);
- } else {
- info.setText(nbElements + " " +
- mActivity.getString(
- R.string.filepicker_some_files_selected));
- }
- }
-
- public void setCurrentPath(String path) {
- if (path != null) {
- mCurrentPath = path;
- TextView textViewPath = (TextView) findViewById(R.id.current_path);
- if (textViewPath != null) {
- textViewPath.setText(path);
- }
- }
- }
-
- public void setupDialog(TextView message, ImageView icon) {
- message.setText(R.string.filepicker_message);
- message.setTextSize(24);
- icon.setImageResource(R.drawable.gears_icon_32x32);
- }
-
- public boolean onTouch(View v, MotionEvent event) {
- mImagesLoader.pauseIconRequest();
- return false;
- }
-
- /**
- * Utility class to load and generate thumbnails
- * for image files
- */
- class ImagesLoad implements Runnable {
- private Map mImagesMap;
- private Vector mImagesPath;
- private BaseAdapter mAdapter;
- private AdapterView mAdapterView;
- private Vector<FilePickerElement> mElements;
- private Handler mLoaderHandler;
-
- ImagesLoad(BaseAdapter adapter) {
- mAdapter = adapter;
- }
-
- public void signalChanges() {
- Message message = mHandler.obtainMessage(GearsBaseDialog.NEW_ICON,
- mAdapter);
- mHandler.sendMessage(message);
- }
-
- /**
- * TODO: use the same thumbnails as the photo app
- * (bug: http://b/issue?id=1497927)
- */
- public String getThumbnailPath(String path) {
- File f = new File(path);
- String myPath = f.getParent() + "/.thumbnails";
- File d = new File(myPath);
- if (!d.exists()) {
- d.mkdirs();
- }
- return myPath + "/" + f.getName();
- }
-
- public boolean saveImage(String path, Bitmap image) {
- boolean ret = false;
- try {
- FileOutputStream outStream = new FileOutputStream(path);
- ret = image.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
- } catch (IOException e) {
- Log.e(TAG, "IOException ", e);
- }
- return ret;
- }
-
- public Bitmap generateImage(FilePickerElement elem) {
- String path = elem.getPath();
- Bitmap finalImage = null;
- try {
- String thumbnailPath = getThumbnailPath(path);
- if (enableSavedThumbnails) {
- File thumbnail = new File(thumbnailPath);
- if (thumbnail.exists()) {
- finalImage = BitmapFactory.decodeFile(thumbnailPath);
- if (finalImage != null) {
- return finalImage;
- }
- }
- }
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(path, options);
-
- int width = options.outWidth;
- int height = options.outHeight;
- int size = 128;
- int sampleSize = 1;
- if (width > size || height > size) {
- sampleSize = 2;
- while ((width / sampleSize > size)
- || (height / sampleSize > size)) {
- sampleSize += 2;
- }
- }
- options.inJustDecodeBounds = false;
- options.inSampleSize = sampleSize;
- Bitmap originalImage = BitmapFactory.decodeFile(path, options);
- if (originalImage == null) {
- return null;
- }
- finalImage = Bitmap.createScaledBitmap(originalImage, size, size, true);
- if (enableSavedThumbnails) {
- if (saveImage(thumbnailPath, finalImage)) {
- if (mDebug) {
- Log.v(TAG, "Saved thumbnail for file " + path);
- }
- } else {
- Log.e(TAG, "Could NOT Save thumbnail for file " + path);
- }
- }
- originalImage.recycle();
- } catch (java.lang.OutOfMemoryError e) {
- Log.e(TAG, "Intercepted OOM ", e);
- }
- return finalImage;
- }
-
- public void pauseIconRequest() {
- Message message = Message.obtain(mLoaderHandler,
- GearsBaseDialog.PAUSE_REQUEST_ICON);
- mLoaderHandler.sendMessageAtFrontOfQueue(message);
- }
- public void postIconRequest(FilePickerElement item, int position) {
- if (item == null) {
- return;
- }
- Message message = mLoaderHandler.obtainMessage(
- GearsBaseDialog.REQUEST_ICON, position, 0, item);
- mLoaderHandler.sendMessage(message);
- }
-
- public void generateIcon(FilePickerElement elem) {
- if (elem.isImage()) {
- if (elem.getThumbnail() == null) {
- Bitmap image = generateImage(elem);
- if (image != null) {
- elem.setThumbnail(image);
- }
- }
- }
- }
-
- public void setAdapterView(AdapterView view) {
- mAdapterView = view;
- }
-
- public void run() {
- Looper.prepare();
- mLoaderHandler = new Handler() {
- public void handleMessage(Message msg) {
- int visibleElements = 10;
- if (msg.what == GearsBaseDialog.PAUSE_REQUEST_ICON) {
- try {
- // We are busy (likely) scrolling the view,
- // so we just pause the loading.
- Thread.sleep(1000);
- mLoaderHandler.removeMessages(
- GearsBaseDialog.PAUSE_REQUEST_ICON);
- } catch (InterruptedException e) {
- Log.e(TAG, "InterruptedException ", e);
- }
- } else if (msg.what == GearsBaseDialog.REQUEST_ICON) {
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- Log.e(TAG, "InterruptedException ", e);
- }
- FilePickerElement elem = (FilePickerElement) msg.obj;
- int firstVisiblePosition = mAdapterView.getFirstVisiblePosition();
- // If the elements are not visible, we slow down the update
- // TODO: replace this by a low-priority thread
- if ((msg.arg1 < firstVisiblePosition - visibleElements)
- && msg.arg1 > firstVisiblePosition + visibleElements) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- }
- }
- generateIcon(elem);
- signalChanges();
- }
- }
- };
- Looper.loop();
- }
- }
-
- /**
- * Utility class representing an element displayed in the
- * file picker, associated with an icon and/or thumbnail
- */
- class FilePickerElement {
- private File mPath;
- private String mName;
- private Bitmap mIcon;
- private boolean mIsSelected;
- private Vector mChildren;
- private FilePickerElement mParent;
- private boolean mIsParent;
- private BaseAdapter mAdapter;
- private String mExtension;
- private Bitmap mThumbnail;
- private boolean mIsImage;
-
- public FilePickerElement(String name, BaseAdapter adapter) {
- this(name, adapter, null);
- }
-
- public FilePickerElement(String path, String name, BaseAdapter adapter) {
- this(path, name, adapter, null);
- }
-
- public FilePickerElement(String name,
- BaseAdapter adapter,
- FilePickerElement parent) {
- mName = name;
- mAdapter = adapter;
- mParent = parent;
- mIsSelected = false;
- mChildren = null;
- }
-
- public FilePickerElement(String path,
- String name,
- BaseAdapter adapter,
- FilePickerElement parent) {
- mPath = new File(path);
- mName = name;
- mIsSelected = false;
- mChildren = null;
- mParent = parent;
- mAdapter = adapter;
- mExtension = null;
-
- setIcons();
- }
-
- public void setIcons() {
- if (mPath.isDirectory()) {
- if (mDirectoryIcon == null) {
- mDirectoryIcon = BitmapFactory.decodeResource(
- getResources(), R.drawable.gears_folder);
- }
- mIcon = mDirectoryIcon;
-
- } else {
- if (isImage()) {
- if (mImageIcon == null) {
- mImageIcon = BitmapFactory.decodeResource(
- getResources(), R.drawable.gears_file_image);
- }
- mIcon = mImageIcon;
- } else if (isAudio()) {
- mIcon = BitmapFactory.decodeResource(
- getResources(), R.drawable.gears_file_audio);
- } else if (isVideo()) {
- mIcon = BitmapFactory.decodeResource(
- getResources(), R.drawable.gears_file_video);
- } else {
- if (mDefaultIcon == null) {
- mDefaultIcon = BitmapFactory.decodeResource(
- getResources(), R.drawable.gears_file_unknown);
- }
- mIcon = mDefaultIcon;
- }
- }
- if (mBackIcon == null) {
- mBackIcon = BitmapFactory.decodeResource(getResources(),
- com.android.internal.R.drawable.ic_menu_back);
- }
- }
-
- public boolean isImage() {
- if (mIsImage) return mIsImage;
- String extension = getExtension();
- if (extension != null) {
- if (extension.equalsIgnoreCase("jpg") ||
- extension.equalsIgnoreCase("jpeg") ||
- extension.equalsIgnoreCase("png") ||
- extension.equalsIgnoreCase("gif")) {
- mIsImage = true;
- return true;
- }
- }
- return false;
- }
-
- public boolean isAudio() {
- String extension = getExtension();
- if (extension != null) {
- if (extension.equalsIgnoreCase("mp3") ||
- extension.equalsIgnoreCase("wav") ||
- extension.equalsIgnoreCase("aac")) {
- return true;
- }
- }
- return false;
- }
-
- public boolean isVideo() {
- String extension = getExtension();
- if (extension != null) {
- if (extension.equalsIgnoreCase("mpg") ||
- extension.equalsIgnoreCase("mpeg") ||
- extension.equalsIgnoreCase("mpe") ||
- extension.equalsIgnoreCase("divx") ||
- extension.equalsIgnoreCase("3gpp") ||
- extension.equalsIgnoreCase("avi")) {
- return true;
- }
- }
- return false;
- }
-
- public void setParent(boolean isParent) {
- mIsParent = isParent;
- }
-
- public boolean isDirectory() {
- return mPath.isDirectory();
- }
-
- public String getExtension() {
- if (isDirectory()) {
- return null;
- }
- if (mExtension == null) {
- String path = getPath();
- int index = path.lastIndexOf(".");
- if ((index != -1) && (index != path.length() - 1)){
- // if we find a dot that is not the last character
- mExtension = path.substring(index+1);
- return mExtension;
- }
- }
- return mExtension;
- }
-
- public void refresh() {
- mChildren = null;
- Vector children = getChildren();
- for (int i = 0; i < children.size(); i++) {
- FilePickerElement elem = (FilePickerElement) children.get(i);
- mImagesLoader.postIconRequest(elem, i);
- }
- }
-
- public Vector getChildren() {
- if (isDirectory()) {
- if (mChildren == null) {
- mChildren = new Vector();
- File[] files = mPath.listFiles();
- if (mParent != null) {
- mChildren.add(mParent);
- mParent.setParent(true);
- }
- for (int i = 0; i < files.length; i++) {
- String name = files[i].getName();
- String fpath = files[i].getPath();
- if (!name.startsWith(".")) { // hide dotfiles
- FilePickerElement elem = new FilePickerElement(fpath, name,
- mAdapter, this);
- elem.setParent(false);
- mChildren.add(elem);
- }
- }
- }
- }
- return mChildren;
- }
-
- public FilePickerElement getChild(int position) {
- Vector children = getChildren();
- if (children != null) {
- return (FilePickerElement) children.get(position);
- }
- return null;
- }
-
- public Bitmap getIcon(int position) {
- if (mIsParent) {
- return mBackIcon;
- }
- if (isImage()) {
- if (mThumbnail != null) {
- return mThumbnail;
- } else {
- mImagesLoader.postIconRequest(this, position);
- }
- }
- return mIcon;
- }
-
- public Bitmap getThumbnail() {
- return mThumbnail;
- }
-
- public void setThumbnail(Bitmap icon) {
- mThumbnail = icon;
- }
-
- public String getName() {
- return mName;
- }
-
- public String getPath() {
- return mPath.getPath();
- }
-
- public void toggleSelection() {
- mIsSelected = !mIsSelected;
- }
-
- public boolean isSelected() {
- return mIsSelected;
- }
-
- }
-
- /**
- * Adapter for the GridView
- */
- class FilePickerAdapter extends BaseAdapter {
- private Context mContext;
- private Map mImagesMap;
- private Map mImagesSelected;
-
- private Vector mImages;
- private Vector<FilePickerElement> mFiles;
-
- private FilePickerElement mRootElement;
- private FilePickerElement mCurrentElement;
-
- public FilePickerAdapter(Context context) {
- mContext = context;
- mImages = new Vector();
- mFiles = new Vector();
-
- mImagesMap = Collections.synchronizedMap(new HashMap());
- mImagesSelected = new HashMap();
-
- Uri requests[] = { MediaStore.Images.Media.INTERNAL_CONTENT_URI,
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI };
-
- String startingPath = Environment.getExternalStorageDirectory().getPath();
- mRootElement = new FilePickerElement(startingPath, "SD Card", this);
- mCurrentElement = mRootElement;
- }
-
- public void addImage(String path) {
- mImages.add(path);
- Bitmap image = BitmapFactory.decodeResource(
- getResources(), R.drawable.gears_file_unknown);
- mImagesMap.put(path, image);
- mImagesSelected.put(path, Boolean.FALSE);
- }
-
- public int getCount() {
- Vector elems = mCurrentElement.getChildren();
- setCurrentPath(mCurrentElement.getPath());
- return elems.size();
- }
-
- public Object getItem(int position) {
- return position;
- }
-
- public long getItemId(int position) {
- return position;
- }
-
- public Vector selectedElements() {
- if (mCurrentElement == null) {
- return null;
- }
- Vector children = mCurrentElement.getChildren();
- Vector ret = new Vector();
- for (int i = 0; i < children.size(); i++) {
- FilePickerElement elem = (FilePickerElement) children.get(i);
- if (elem.isSelected()) {
- ret.add(elem);
- }
- }
- return ret;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- View cell = convertView;
- if (cell == null) {
- LayoutInflater inflater = (LayoutInflater) getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- cell = inflater.inflate(R.layout.gears_dialog_filepicker_cell, null);
- }
- ImageView imageView = (ImageView) cell.findViewById(R.id.icon);
- TextView textView = (TextView) cell.findViewById(R.id.name);
- FilePickerElement elem = mCurrentElement.getChild(position);
- if (elem == null) {
- String message = "Could not get elem " + position;
- message += " for " + mCurrentElement.getPath();
- Log.e(TAG, message);
- return null;
- }
- String path = elem.getPath();
- textView.setText(elem.getName());
-
- View.OnClickListener listener = new View.OnClickListener() {
- public void onClick(View view) {
- int pos = (Integer) view.getTag();
- FilePickerElement elem = mCurrentElement.getChild(pos);
- if (elem.isDirectory()) {
- mCurrentElement = elem;
- mCurrentElement.refresh();
- } else {
- if (mMultipleSelection) {
- elem.toggleSelection();
- } else {
- Vector elems = selectedElements();
- if (elems != null) {
- if (elems.size() == 0) {
- elem.toggleSelection();
- } else if ((elems.size() == 1)
- && elem.isSelected()) {
- elem.toggleSelection();
- }
- }
- }
- }
- setSelectionText();
- notifyDataSetChanged();
- }
- };
- cell.setLayoutParams(new GridView.LayoutParams(96, 96));
- cell.setOnClickListener(listener);
- cell.setOnTouchListener(new View.OnTouchListener() {
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- int color = getResources().getColor(R.color.icon_selection);
- v.setBackgroundColor(color);
- } else {
- v.setBackgroundColor(Color.WHITE);
- }
- return false;
- }
- });
-
- cell.setTag(position);
-
- if (elem.isSelected()) {
- int color = getResources().getColor(R.color.icon_selection);
- cell.setBackgroundColor(color);
- } else {
- cell.setBackgroundColor(Color.WHITE);
- }
- Bitmap bmp = elem.getIcon(position);
- if (bmp != null) {
- imageView.setImageBitmap(bmp);
- }
-
- return cell;
- }
- }
-
- private String selectedFiles() {
- Vector selection = mAdapter.selectedElements();
- JSONArray jsonSelection = new JSONArray();
- if (selection != null) {
- for (int i = 0; i < selection.size(); i++) {
- FilePickerElement elem = (FilePickerElement) selection.get(i);
- jsonSelection.put(elem.getPath());
- }
- }
- return jsonSelection.toString();
- }
-
- public String closeDialog(int closingType) {
- return selectedFiles();
- }
-}
diff --git a/src/com/android/browser/GearsNativeDialog.java b/src/com/android/browser/GearsNativeDialog.java
index c8ae741..ecf166d 100644
--- a/src/com/android/browser/GearsNativeDialog.java
+++ b/src/com/android/browser/GearsNativeDialog.java
@@ -17,6 +17,7 @@
package com.android.browser;
import android.app.Activity;
+import android.app.Dialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -24,17 +25,17 @@
import android.os.Message;
import android.util.Config;
import android.util.Log;
+import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Window;
import android.widget.BaseAdapter;
+import android.widget.Toast;
import android.webkit.gears.NativeDialog;
import com.android.browser.GearsBaseDialog;
import com.android.browser.GearsPermissionsDialog;
import com.android.browser.GearsSettingsDialog;
-import com.android.browser.GearsShortcutDialog;
-import com.android.browser.GearsFilePickerDialog;
/**
* Native dialog Activity used by gears
@@ -54,16 +55,12 @@
private int mDialogType;
private final int SETTINGS_DIALOG = 1;
private final int PERMISSION_DIALOG = 2;
- private final int SHORTCUT_DIALOG = 3;
- private final int LOCATION_DIALOG = 4;
- private final int FILEPICKER_DIALOG = 5;
+ private final int LOCATION_DIALOG = 3;
private final String VERSION_STRING = "version";
private final String SETTINGS_DIALOG_STRING = "settings_dialog";
private final String PERMISSION_DIALOG_STRING = "permissions_dialog";
- private final String SHORTCUT_DIALOG_STRING = "shortcuts_dialog";
private final String LOCATION_DIALOG_STRING = "locations_dialog";
- private final String FILEPICKER_DIALOG_STRING = "filepicker_dialog";
private boolean mDialogDismissed = false;
@@ -90,10 +87,15 @@
@Override
public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- setContentView(R.layout.gears_dialog);
getArguments();
+ if (mDialogType == SETTINGS_DIALOG) {
+ setTheme(android.R.style.Theme);
+ }
+ super.onCreate(icicle);
+ if (mDialogType != SETTINGS_DIALOG) {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.gears_dialog);
+ }
switch (mDialogType) {
case SETTINGS_DIALOG:
@@ -103,15 +105,9 @@
case PERMISSION_DIALOG:
dialog = new GearsPermissionsDialog(this, mHandler, mDialogArguments);
break;
- case SHORTCUT_DIALOG:
- dialog = new GearsShortcutDialog(this, mHandler, mDialogArguments);
- break;
case LOCATION_DIALOG:
dialog = new GearsPermissionsDialog(this, mHandler, mDialogArguments);
break;
- case FILEPICKER_DIALOG:
- dialog = new GearsFilePickerDialog(this, mHandler, mDialogArguments);
- break;
default:
dialog = new GearsBaseDialog(this, mHandler, mDialogArguments);
}
@@ -128,7 +124,7 @@
*/
private void getArguments() {
if (mDebug) {
- mDialogType = FILEPICKER_DIALOG +1;
+ mDialogType = LOCATION_DIALOG +1;
mockArguments();
return;
@@ -150,12 +146,8 @@
mGearsVersion = intent.getStringExtra(VERSION_STRING);
} else if (dialogTypeString.equalsIgnoreCase(PERMISSION_DIALOG_STRING)) {
mDialogType = PERMISSION_DIALOG;
- } else if (dialogTypeString.equalsIgnoreCase(SHORTCUT_DIALOG_STRING)) {
- mDialogType = SHORTCUT_DIALOG;
} else if (dialogTypeString.equalsIgnoreCase(LOCATION_DIALOG_STRING)) {
mDialogType = LOCATION_DIALOG;
- } else if (dialogTypeString.equalsIgnoreCase(FILEPICKER_DIALOG_STRING)) {
- mDialogType = FILEPICKER_DIALOG;
}
}
@@ -165,17 +157,6 @@
* Set mock arguments.
*/
private void mockArguments() {
- String argumentsShortcuts = "{ locale: \"en-US\","
- + "name: \"My Application\", link: \"http://www.google.com/\","
- + "description: \"This application does things does things!\","
- + "icon16x16: \"http://google-gears.googlecode.com/"
- + "svn/trunk/gears/test/manual/shortcuts/16.png\","
- + "icon32x32: \"http://google-gears.googlecode.com/"
- + "svn/trunk/gears/test/manual/shortcuts/32.png\","
- + "icon48x48: \"http://google-gears.googlecode.com/"
- + "svn/trunk/gears/test/manual/shortcuts/48.png\","
- + "icon128x128: \"http://google-gears.googlecode.com/"
- + "svn/trunk/gears/test/manual/shortcuts/128.png\"}";
String argumentsPermissions = "{ locale: \"en-US\", "
+ "origin: \"http://www.google.com\", dialogType: \"localData\","
@@ -185,6 +166,9 @@
+ "customMessage: \"Press the button to enable my "
+ "application to run offline!\" };";
+ String argumentsPermissions2 = "{ locale: \"en-US\", "
+ + "origin: \"http://www.google.com\", dialogType: \"localData\" };";
+
String argumentsLocation = "{ locale: \"en-US\", "
+ "origin: \"http://www.google.com\", dialogType: \"locationData\","
+ "customIcon: \"http://google-gears.googlecode.com/"
@@ -195,8 +179,8 @@
String argumentsSettings = "{ locale: \"en-US\", permissions: [ { "
+ "name: \"http://www.google.com\", "
- + "localStorage: { permissionState: 1 }, "
- + "locationData: { permissionState: 0 } }, "
+ + "localStorage: { permissionState: 0 }, "
+ + "locationData: { permissionState: 1 } }, "
+ "{ name: \"http://www.aaronboodman.com\", "
+ "localStorage: { permissionState: 1 }, "
+ "locationData: { permissionState: 2 } }, "
@@ -205,9 +189,6 @@
+ "locationData: { permissionState: 2 } } ] }";
switch (mDialogType) {
- case SHORTCUT_DIALOG:
- mDialogArguments = argumentsShortcuts;
- break;
case PERMISSION_DIALOG:
mDialogArguments = argumentsPermissions;
break;
@@ -216,6 +197,7 @@
break;
case SETTINGS_DIALOG:
mDialogArguments = argumentsSettings;
+ break;
}
}
@@ -232,6 +214,14 @@
NativeDialog.closeDialog(ret);
notifyEndOfDialog();
finish();
+
+ // If the dialog sets a notification, we display it.
+ int notification = dialog.notification();
+ if (notification != 0) {
+ Toast toast = Toast.makeText(this, notification, Toast.LENGTH_LONG);
+ toast.setGravity(Gravity.BOTTOM, 0, 0);
+ toast.show();
+ }
}
@Override
@@ -265,10 +255,26 @@
* NativeDialog that we are done.
*/
public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isDown()) {
- closeDialog(GearsBaseDialog.CANCEL);
+ if ((event.getKeyCode() == KeyEvent.KEYCODE_BACK)
+ && (event.getAction() == KeyEvent.ACTION_DOWN)) {
+ if (!dialog.handleBackButton()) {
+ // if the dialog doesn't do anything with the back button
+ closeDialog(GearsBaseDialog.CANCEL);
+ }
+ return true; // event consumed
}
return super.dispatchKeyEvent(event);
}
+ /**
+ * If the dialog call showDialog() on ourself, we let
+ * it handle the creation of this secondary dialog.
+ * It is used in GearsSettingsDialog, to create the confirmation
+ * dialog when the user click on "Remove this site from Gears"
+ */
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ return dialog.onCreateDialog(id);
+ }
+
}
diff --git a/src/com/android/browser/GearsPermissions.java b/src/com/android/browser/GearsPermissions.java
index cd46324..e48e045 100644
--- a/src/com/android/browser/GearsPermissions.java
+++ b/src/com/android/browser/GearsPermissions.java
@@ -49,37 +49,37 @@
public static final int PERMISSION_DENIED = 2;
String mName;
- int mRowRsc;
- int mAllowedButtonRsc;
- int mDeniedButtonRsc;
+ int mTitleRsc;
+ int mSubtitleOnRsc;
+ int mSubtitleOffRsc;
PermissionType(String name) {
mName = name;
}
- public void setResources(int rowRsc, int allowedButtonRsc,
- int deniedButtonRsc) {
- mRowRsc = rowRsc;
- mAllowedButtonRsc = allowedButtonRsc;
- mDeniedButtonRsc = deniedButtonRsc;
- }
-
- public int getRowRsc() {
- return mRowRsc;
- }
-
- public int getAllowedButtonRsc() {
- return mAllowedButtonRsc;
- }
-
- public int getDeniedButtonRsc() {
- return mDeniedButtonRsc;
+ public void setResources(int titleRsc,
+ int subtitleOnRsc, int subtitleOffRsc) {
+ mTitleRsc = titleRsc;
+ mSubtitleOnRsc = subtitleOnRsc;
+ mSubtitleOffRsc = subtitleOffRsc;
}
public String getName() {
return mName;
}
+ public int getTitleRsc() {
+ return mTitleRsc;
+ }
+
+ public int getSubtitleOnRsc() {
+ return mSubtitleOnRsc;
+ }
+
+ public int getSubtitleOffRsc() {
+ return mSubtitleOffRsc;
+ }
+
}
/**
diff --git a/src/com/android/browser/GearsPermissionsDialog.java b/src/com/android/browser/GearsPermissionsDialog.java
index b57ab0b..dbec363 100644
--- a/src/com/android/browser/GearsPermissionsDialog.java
+++ b/src/com/android/browser/GearsPermissionsDialog.java
@@ -35,6 +35,7 @@
private static final String TAG = "GearsPermissionsDialog";
private String mDialogType;
+ private int mNotification = 0;
public GearsPermissionsDialog(Activity activity,
Handler handler,
@@ -48,15 +49,6 @@
R.string.permission_button_allow,
R.string.permission_button_deny);
- View contentBorder = findViewById(R.id.content_border);
- if (contentBorder != null) {
- contentBorder.setBackgroundResource(R.color.permission_border);
- }
- View contentBackground = findViewById(R.id.content_background);
- if (contentBackground != null) {
- contentBackground.setBackgroundResource(R.color.permission_background);
- }
-
try {
JSONObject json = new JSONObject(mDialogArguments);
@@ -84,6 +76,16 @@
downloadIcon(iconUrl);
}
+ View msg = findViewById(R.id.permission_dialog_message);
+ if (msg != null) {
+ TextView dialogMessage = (TextView) msg;
+ if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
+ dialogMessage.setText(R.string.query_data_message);
+ } else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
+ dialogMessage.setText(R.string.location_message);
+ }
+ }
+
} catch (JSONException e) {
Log.e(TAG, "JSON exception ", e);
}
@@ -91,15 +93,11 @@
public void setupDialog(TextView message, ImageView icon) {
if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
- message.setText(R.string.query_data_message);
- icon.setImageResource(R.drawable.gears_local_data);
+ message.setText(R.string.query_data_prompt);
+ icon.setImageResource(android.R.drawable.ic_popup_disk_full);
} else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
- message.setText(R.string.location_message);
- icon.setImageResource(R.drawable.gears_location_data);
- View privacyPolicyLabel = findViewById(R.id.privacy_policy_label);
- if (privacyPolicyLabel != null) {
- privacyPolicyLabel.setVisibility(View.VISIBLE);
- }
+ message.setText(R.string.location_prompt);
+ icon.setImageResource(R.drawable.ic_dialog_menu_generic);
}
}
@@ -108,9 +106,19 @@
switch (closingType) {
case ALWAYS_DENY:
ret = "{\"allow\": false, \"permanently\": true }";
+ if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
+ mNotification = R.string.storage_notification_alwaysdeny;
+ } else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
+ mNotification = R.string.location_notification_alwaysdeny;
+ }
break;
case ALLOW:
ret = "{\"allow\": true, \"permanently\": true }";
+ if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
+ mNotification = R.string.storage_notification;
+ } else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
+ mNotification = R.string.location_notification;
+ }
break;
case DENY:
ret = "{\"allow\": false, \"permanently\": false }";
@@ -119,4 +127,7 @@
return ret;
}
+ public int notification() {
+ return mNotification;
+ }
}
diff --git a/src/com/android/browser/GearsSettingsDialog.java b/src/com/android/browser/GearsSettingsDialog.java
index 56a1d8d..5ea2342 100644
--- a/src/com/android/browser/GearsSettingsDialog.java
+++ b/src/com/android/browser/GearsSettingsDialog.java
@@ -17,14 +17,21 @@
package com.android.browser;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
import android.widget.Button;
+import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.ListAdapter;
@@ -33,6 +40,7 @@
import android.widget.TextView;
import com.android.browser.GearsPermissions.OriginPermissions;
+import com.android.browser.GearsPermissions.Permission;
import com.android.browser.GearsPermissions.PermissionsChangesListener;
import com.android.browser.GearsPermissions.PermissionType;
@@ -55,6 +63,7 @@
private Vector<OriginPermissions> mCurrentPermissions = null;
private Vector<PermissionType> mPermissions;
+ private static final int CONFIRMATION_REMOVE_DIALOG = 1;
// We declare the permissions globally to simplify the code
private final PermissionType LOCAL_STORAGE =
@@ -64,23 +73,23 @@
private boolean mChanges = false;
+ SettingsAdapter mListAdapter;
public GearsSettingsDialog(Activity activity,
Handler handler,
String arguments) {
super (activity, handler, arguments);
+ activity.setContentView(R.layout.gears_settings);
}
public void setup() {
// First let's add the permissions' resources
- LOCAL_STORAGE.setResources(R.id.local_storage_choice,
- R.id.local_storage_allowed,
- R.id.local_storage_denied);
-
- LOCATION_DATA.setResources(R.id.location_data_choice,
- R.id.location_data_allowed,
- R.id.location_data_denied);
-
+ LOCAL_STORAGE.setResources(R.string.settings_storage_title,
+ R.string.settings_storage_subtitle_on,
+ R.string.settings_storage_subtitle_off);
+ LOCATION_DATA.setResources(R.string.settings_location_title,
+ R.string.settings_location_subtitle_on,
+ R.string.settings_location_subtitle_off);
// add the permissions to the list of permissions.
mPermissions = new Vector<PermissionType>();
mPermissions.add(LOCAL_STORAGE);
@@ -88,25 +97,7 @@
OriginPermissions.setListener(this);
- inflate(R.layout.gears_dialog_settings, R.id.panel_content);
setupDialog();
- setupButtons(0,
- R.string.settings_button_allow,
- R.string.settings_button_deny);
-
- // by default disable the allow button (it will get enabled if
- // something is changed by the user)
- View buttonView = findViewById(R.id.button_allow);
- if (buttonView != null) {
- Button button = (Button) buttonView;
- button.setEnabled(false);
- }
-
- View gearsVersionView = findViewById(R.id.gears_version);
- if (gearsVersionView != null) {
- TextView gearsVersion = (TextView) gearsVersionView;
- gearsVersion.setText(mGearsVersion);
- }
// We manage the permissions using three vectors, mSitesPermissions,
// mOriginalPermissions and mCurrentPermissions.
@@ -165,32 +156,23 @@
View listView = findViewById(R.id.sites_list);
if (listView != null) {
ListView list = (ListView) listView;
- list.setAdapter(new SettingsAdapter(mActivity, mSitesPermissions));
+ mListAdapter = new SettingsAdapter(mActivity, mSitesPermissions);
+ list.setAdapter(mListAdapter);
list.setScrollBarStyle(android.view.View.SCROLLBARS_OUTSIDE_INSET);
+ list.setOnItemClickListener(mListAdapter);
}
if (mDebug) {
printPermissions();
}
}
+ private void setMainTitle() {
+ String windowTitle = mActivity.getString(R.string.pref_extras_gears_settings);
+ mActivity.setTitle(windowTitle);
+ }
+
public void setupDialog() {
- View dialogTitleView = findViewById(R.id.dialog_title);
- if (dialogTitleView != null) {
- TextView dialogTitle = (TextView) dialogTitleView;
- dialogTitle.setText(R.string.settings_title);
- dialogTitle.setVisibility(View.VISIBLE);
- }
- View dialogSubtitleView = findViewById(R.id.dialog_subtitle);
- if (dialogSubtitleView != null) {
- TextView dialogSubtitle = (TextView) dialogSubtitleView;
- dialogSubtitle.setText(R.string.settings_message);
- dialogSubtitle.setVisibility(View.VISIBLE);
- }
- View iconView = findViewById(R.id.icon);
- if (iconView != null) {
- ImageView icon = (ImageView) iconView;
- icon.setImageResource(R.drawable.gears_icon_32x32);
- }
+ setMainTitle();
}
/**
@@ -198,164 +180,95 @@
*/
public boolean setPermission(PermissionType type, int perm) {
if (mChanges == false) {
- signalChanges();
+ mChanges = true;
}
return mChanges;
}
- /**
- * Controller class for binding the model (OriginPermissions) with
- * the UI.
- */
- class PermissionController {
- final static int ALLOWED_BUTTON = 1;
- final static int DENIED_BUTTON = 2;
- private int mButtonType;
- private PermissionType mPermissionType;
- private OriginPermissions mPermissions;
-
- PermissionController(PermissionType permissionType, int buttonType,
- OriginPermissions permissions) {
- mPermissionType = permissionType;
- mButtonType = buttonType;
- mPermissions = permissions;
- }
-
- public boolean isChecked() {
- boolean checked = false;
-
- switch (mButtonType) {
- case ALLOWED_BUTTON:
- if (mPermissions.getPermission(mPermissionType) ==
- PermissionType.PERMISSION_ALLOWED) {
- checked = true;
- } break;
- case DENIED_BUTTON:
- if (mPermissions.getPermission(mPermissionType) ==
- PermissionType.PERMISSION_DENIED) {
- checked = true;
- }
- }
- return checked;
- }
-
- public String print() {
- return printType() + " for " + mPermissions.getOrigin();
- }
-
- private String printType() {
- switch (mButtonType) {
- case ALLOWED_BUTTON:
- return "ALLOWED_BUTTON";
- case DENIED_BUTTON:
- return "DENIED_BUTTON";
- }
- return "UNKNOWN BUTTON";
- }
-
- public void changed(boolean isChecked) {
- if (isChecked == isChecked()) {
- return; // already set
- }
-
- switch (mButtonType) {
- case ALLOWED_BUTTON:
- mPermissions.setPermission(mPermissionType,
- PermissionType.PERMISSION_ALLOWED);
- break;
- case DENIED_BUTTON:
- mPermissions.setPermission(mPermissionType,
- PermissionType.PERMISSION_DENIED);
- break;
- }
- }
+ public boolean handleBackButton() {
+ return mListAdapter.backButtonPressed();
}
-
+ /**
+ * We use this to create a confirmation dialog when the user
+ * clicks on "remove this site from gears"
+ */
+ public Dialog onCreateDialog(int id) {
+ return new AlertDialog.Builder(mActivity)
+ .setTitle(R.string.settings_confirmation_remove_title)
+ .setMessage(R.string.settings_confirmation_remove)
+ .setPositiveButton(android.R.string.ok,
+ new AlertDialog.OnClickListener() {
+ public void onClick(DialogInterface dlg, int which) {
+ mListAdapter.removeCurrentSite();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .create();
+ }
/**
* Adapter class for the list view in the settings dialog
*
- * Every row in the settings dialog display the permissions
- * for a given origin. For every type of permission
- * (location, local data...) there is two radio buttons to
- * authorize or deny the permission.
- * A remove button is also present to let the user remove
- * all the authorization of an origin in one step.
+ * We first display a list of all the origins (sites), or
+ * a message saying that no permission is set if the list is empty.
+ * When the user click on one of the origin, we then display
+ * the list of the permissions existing for that origin.
+ * Each permission can be either allowed or denied by clicking
+ * on the checkbox.
+ * The last row is a special case, allowing to remove the entire origin.
*/
- class SettingsAdapter extends ArrayAdapter {
+ class SettingsAdapter extends BaseAdapter
+ implements AdapterView.OnItemClickListener {
private Activity mContext;
private List mItems;
+ private OriginPermissions mCurrentSite;
+ private Vector mCurrentPermissions;
+ private int MAX_ROW_HEIGHT = 64;
SettingsAdapter(Activity context, List items) {
- super(context, R.layout.gears_dialog_settings_row, items);
mContext = context;
mItems = items;
+ mCurrentSite = null;
}
- /*
- * setup the necessary listeners for the radiobuttons
- * When the buttons are clicked the permissions change.
- */
- private void createAndSetButtonListener(View buttonView,
- OriginPermissions perms, PermissionType permissionType,
- int buttonType) {
- if (buttonView == null) {
- return;
- }
- RadioButton button = (RadioButton) buttonView;
-
- button.setOnCheckedChangeListener(null);
- PermissionController p = new PermissionController(permissionType,
- buttonType, perms);
- button.setTag(p);
-
- CompoundButton.OnCheckedChangeListener listener =
- new CompoundButton.OnCheckedChangeListener() {
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- PermissionController perm = (PermissionController)buttonView.getTag();
- perm.changed(isChecked);
+ public int getCount() {
+ if (mCurrentSite == null) {
+ int size = mItems.size();
+ if (size == 0) {
+ return 1;
+ } else {
+ return size;
}
- };
-
- button.setOnCheckedChangeListener(listener);
-
- if (p.isChecked() != button.isChecked()) {
- button.setChecked(p.isChecked());
}
+ return mCurrentPermissions.size() + 1;
}
- /*
- * setup the remove button for an origin: each row has a global
- * remove button in addition to the radio buttons controlling the
- * permissions.
- */
- private void setRemoveButton(Button button, OriginPermissions perms) {
- Button.OnClickListener listener = new Button.OnClickListener() {
- public void onClick(View buttonView) {
- if (mChanges == false) {
- signalChanges();
- }
- OriginPermissions perm = (OriginPermissions) buttonView.getTag();
- perm.setPermission(LOCAL_STORAGE, PermissionType.PERMISSION_NOT_SET);
- perm.setPermission(LOCATION_DATA, PermissionType.PERMISSION_NOT_SET);
- mSitesPermissions.remove(perm);
+ public long getItemId(int position) {
+ return position;
+ }
- View view = findViewById(R.id.sites_list);
- if (view != null) {
- ListView listView = (ListView) view;
- ListAdapter listAdapter = listView.getAdapter();
- if (listAdapter != null) {
- SettingsAdapter settingsAdapter = (SettingsAdapter) listAdapter;
- settingsAdapter.notifyDataSetChanged();
- }
- }
+ private String shortName(String url) {
+ // We remove the http and https prefix
+ if (url.startsWith("http://")) {
+ return url.substring(7);
}
- };
- button.setTag(perms);
- button.setOnClickListener(listener);
- displayAsLink(button);
+ if (url.startsWith("https://")) {
+ return url.substring(8);
+ }
+ return url;
+ }
+
+ public Object getItem(int position) {
+ if (mCurrentSite == null) {
+ if (mItems.size() == 0) {
+ return null;
+ } else {
+ return mItems.get(position);
+ }
+ }
+ return mCurrentPermissions.get(position);
}
public View getView(int position, View convertView, ViewGroup parent) {
@@ -363,46 +276,117 @@
if (row == null) { // no cached view, we create one
LayoutInflater inflater = (LayoutInflater) getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
- row = inflater.inflate(R.layout.gears_dialog_settings_row, null);
+ row = inflater.inflate(R.layout.gears_settings_row, null);
}
+ row.setMinimumHeight(MAX_ROW_HEIGHT);
- OriginPermissions perms = (OriginPermissions) mItems.get(position);
+ if (mCurrentSite == null) {
+ if (mItems.size() == 0) {
+ hideView(row, R.id.title);
+ hideView(row, R.id.subtitle);
+ hideView(row, R.id.checkbox);
+ hideView(row, R.id.icon);
+ setText(row, R.id.info, R.string.settings_empty);
+ } else {
+ hideView(row, R.id.subtitle);
+ hideView(row, R.id.info);
+ hideView(row, R.id.checkbox);
+ OriginPermissions perms = (OriginPermissions) mItems.get(position);
+ setText(row, R.id.title, shortName(perms.getOrigin()));
+ showView(row, R.id.icon);
+ }
+ } else {
+ if (position == getCount() - 1) {
+ // last position: "remove this site from gears"
+ hideView(row, R.id.subtitle);
+ hideView(row, R.id.info);
+ hideView(row, R.id.checkbox);
+ hideView(row, R.id.icon);
+ setText(row, R.id.title, R.string.settings_remove_site);
+ } else {
+ hideView(row, R.id.info);
+ hideView(row, R.id.icon);
+ showView(row, R.id.checkbox);
- View nameView = row.findViewById(R.id.origin_name);
- if (nameView != null) {
- TextView originName = (TextView) nameView;
- originName.setText(perms.getOrigin());
- }
+ PermissionType type =
+ (PermissionType) mCurrentPermissions.get(position);
+ setText(row, R.id.title, type.getTitleRsc());
- View removeButtonView = row.findViewById(R.id.origin_remove);
- if (removeButtonView != null) {
- Button removeButton = (Button) removeButtonView;
- setRemoveButton(removeButton, perms);
- }
-
- for (int i = 0; i < mPermissions.size(); i++) {
- PermissionType type = mPermissions.get(i);
- int rowRsc = type.getRowRsc();
- int allowedButtonRsc = type.getAllowedButtonRsc();
- int deniedButtonRsc = type.getDeniedButtonRsc();
-
- View rowView = row.findViewById(rowRsc);
- if (rowView != null) {
- int perm = perms.getPermission(type);
- if (perm != PermissionType.PERMISSION_NOT_SET) {
- createAndSetButtonListener(row.findViewById(allowedButtonRsc),
- perms, type, PermissionController.ALLOWED_BUTTON);
- createAndSetButtonListener(row.findViewById(deniedButtonRsc),
- perms, type, PermissionController.DENIED_BUTTON);
- rowView.setVisibility(View.VISIBLE);
- } else {
- rowView.setVisibility(View.GONE);
+ View checkboxView = row.findViewById(R.id.checkbox);
+ if (checkboxView != null) {
+ CheckBox checkbox = (CheckBox) checkboxView;
+ int perm = mCurrentSite.getPermission(type);
+ if (perm == PermissionType.PERMISSION_DENIED) {
+ setText(row, R.id.subtitle, type.getSubtitleOffRsc());
+ checkbox.setChecked(false);
+ } else {
+ setText(row, R.id.subtitle, type.getSubtitleOnRsc());
+ checkbox.setChecked(true);
+ }
}
}
}
-
return row;
}
+
+ public void removeCurrentSite() {
+ mCurrentSite.setPermission(LOCAL_STORAGE,
+ PermissionType.PERMISSION_NOT_SET);
+ mCurrentSite.setPermission(LOCATION_DATA,
+ PermissionType.PERMISSION_NOT_SET);
+ mSitesPermissions.remove(mCurrentSite);
+ mCurrentSite = null;
+ setMainTitle();
+ notifyDataSetChanged();
+ }
+
+ public void onItemClick(AdapterView<?> parent,
+ View view,
+ int position,
+ long id) {
+ if (mItems.size() == 0) {
+ return;
+ }
+ if (mCurrentSite == null) {
+ mCurrentSite = (OriginPermissions) mItems.get(position);
+ mCurrentPermissions = new Vector();
+ for (int i = 0; i < mPermissions.size(); i++) {
+ PermissionType type = mPermissions.get(i);
+ int perm = mCurrentSite.getPermission(type);
+ if (perm != PermissionType.PERMISSION_NOT_SET) {
+ mCurrentPermissions.add(type);
+ }
+ }
+ mContext.setTitle(shortName(mCurrentSite.getOrigin()));
+ } else {
+ if (position == getCount() - 1) { // last item (remove site)
+ // Ask the user to confirm
+ // If yes, removeCurrentSite() will be called via the dialog callback.
+ mActivity.showDialog(CONFIRMATION_REMOVE_DIALOG);
+ } else {
+ PermissionType type =
+ (PermissionType) mCurrentPermissions.get(position);
+ if (mCurrentSite.getPermission(type) ==
+ PermissionType.PERMISSION_ALLOWED) {
+ mCurrentSite.setPermission(type, PermissionType.PERMISSION_DENIED);
+ } else {
+ mCurrentSite.setPermission(type, PermissionType.PERMISSION_ALLOWED);
+ }
+ }
+ }
+ notifyDataSetChanged();
+ }
+
+ public boolean backButtonPressed() {
+ if (mCurrentSite != null) { // we intercept the back button
+ mCurrentSite = null;
+ setMainTitle();
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
}
/**
@@ -423,21 +407,6 @@
}
/**
- * Utility method used by the settings dialog, signaling
- * the user the settings have been modified.
- * We reflect this by enabling the Allow button (disabled
- * by default).
- */
- public void signalChanges() {
- View view = findViewById(R.id.button_allow);
- if (view != null) {
- Button button = (Button) view;
- button.setEnabled(true);
- }
- mChanges = true;
- }
-
- /**
* Computes the difference between the original permissions and the
* current ones. Returns a json-formatted string.
* It is used by the Settings dialog.
@@ -479,18 +448,7 @@
}
public String closeDialog(int closingType) {
- String ret = null;
- switch (closingType) {
- case ALWAYS_DENY:
- ret = "{\"allow\": false }";
- break;
- case ALLOW:
- ret = computeDiff(true);
- break;
- case DENY:
- ret = computeDiff(false);
- break;
- }
+ String ret = computeDiff(mChanges);
if (mDebug) {
printPermissions();
diff --git a/src/com/android/browser/GearsShortcutDialog.java b/src/com/android/browser/GearsShortcutDialog.java
deleted file mode 100644
index deede12..0000000
--- a/src/com/android/browser/GearsShortcutDialog.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2008 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.os.Handler;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-/**
- * Gears Shortcut dialog
- */
-class GearsShortcutDialog extends GearsBaseDialog {
-
- private static final String TAG = "GearsPermissionsDialog";
-
- private final String ICON_16 = "icon16x16";
- private final String ICON_32 = "icon32x32";
- private final String ICON_48 = "icon48x48";
- private final String ICON_128 = "icon128x128";
-
- public GearsShortcutDialog(Activity activity,
- Handler handler,
- String arguments) {
- super (activity, handler, arguments);
- }
-
- public void setup() {
- inflate(R.layout.gears_dialog_permission, R.id.panel_content);
- setupButtons(R.string.shortcut_button_alwaysdeny,
- R.string.shortcut_button_allow,
- R.string.shortcut_button_deny);
-
- View contentBorder = findViewById(R.id.content_border);
- if (contentBorder != null) {
- contentBorder.setBackgroundResource(R.color.shortcut_border);
- }
- View contentBackground = findViewById(R.id.content_background);
- if (contentBackground != null) {
- contentBackground.setBackgroundResource(R.color.shortcut_background);
- }
-
- try {
- JSONObject json = new JSONObject(mDialogArguments);
-
- String iconUrl = pickIconToRender(json);
- if (iconUrl != null) {
- downloadIcon(iconUrl);
- }
-
- setupDialog();
-
- setLabel(json, "name", R.id.origin_title);
- setLabel(json, "link", R.id.origin_subtitle);
- setLabel(json, "description", R.id.origin_message);
- } catch (JSONException e) {
- Log.e(TAG, "JSON exception", e);
- }
- }
-
- public void setupDialog(TextView message, ImageView icon) {
- message.setText(R.string.shortcut_message);
- icon.setImageResource(R.drawable.gears_icon_48x48);
- }
-
- /**
- * Utility method to validate an icon url. Used in the
- * shortcut dialog.
- */
- boolean validIcon(JSONObject json, String name) {
- try {
- if (json.has(name)) {
- String str = json.getString(name);
- if (str.length() > 0) {
- return true;
- }
- }
- } catch (JSONException e) {
- Log.e(TAG, "JSON exception", e);
- }
- return false;
- }
-
-
- /**
- * Utility method to pick the best indicated icon
- * from the dialogs' arguments. Used in the
- * shortcut dialog.
- */
- String pickIconToRender(JSONObject json) {
- try {
- if (validIcon(json, ICON_48)) { // ideal size
- mChoosenIconSize = 48;
- return json.getString(ICON_48);
- } else if (validIcon(json, ICON_32)) {
- mChoosenIconSize = 32;
- return json.getString(ICON_32);
- } else if (validIcon(json, ICON_128)) {
- mChoosenIconSize = 128;
- return json.getString(ICON_128);
- } else if (validIcon(json, ICON_16)) {
- mChoosenIconSize = 16;
- return json.getString(ICON_16);
- }
- } catch (JSONException e) {
- Log.e(TAG, "JSON exception", e);
- }
- mChoosenIconSize = 0;
- return null;
- }
-
- public String closeDialog(int closingType) {
- String ret = null;
- switch (closingType) {
- case ALWAYS_DENY:
- ret = "{\"allow\": false, \"permanently\": true }";
- break;
- case ALLOW:
- ret = "{\"allow\": true, \"locations\": 0 }";
- break;
- case DENY:
- ret = null;
- break;
- }
- return ret;
- }
-
-}
diff --git a/src/com/android/browser/HistoryItem.java b/src/com/android/browser/HistoryItem.java
index c83ced1..55e43f0 100644
--- a/src/com/android/browser/HistoryItem.java
+++ b/src/com/android/browser/HistoryItem.java
@@ -17,26 +17,31 @@
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.graphics.Color;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.PaintDrawable;
-import android.view.LayoutInflater;
-import android.widget.LinearLayout;
+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.
*/
-/* package */ class HistoryItem extends LinearLayout {
+/* package */ class HistoryItem extends BookmarkItem {
- private TextView mTitleView; // Truncated Title
- private String mUrl; // Full Url
- private TextView mUrlText; // Truncated Url
-
+ private CompoundButton mStar; // Star for bookmarking
+ private CompoundButton.OnCheckedChangeListener mListener;
/**
* Create a new HistoryItem.
* @param context Context for this HistoryItem.
@@ -44,83 +49,79 @@
/* package */ HistoryItem(Context context) {
super(context);
- setWillNotDraw(false);
- LayoutInflater factory = LayoutInflater.from(context);
- factory.inflate(R.layout.history_item, this);
- mTitleView = (TextView) findViewById(R.id.title);
- mUrlText = (TextView) findViewById(R.id.url);
+ mStar = (CompoundButton) findViewById(R.id.star);
+ mStar.setVisibility(View.VISIBLE);
+ 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();
+ } 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();
+ }
+ cursor.deactivate();
+ }
+ };
}
void copyTo(HistoryItem item) {
- item.mTitleView.setText(mTitleView.getText());
+ item.mTextView.setText(mTextView.getText());
item.mUrlText.setText(mUrlText.getText());
- }
-
- /**
- * Return the name of this HistoryItem.
- * @return String name of this HistoryItem.
- /
- /* package */ String getName() {
- return mTitleView.getText().toString();
+ item.setIsBookmark(mStar.isChecked());
+ item.mImageView.setImageDrawable(mImageView.getDrawable());
}
/**
- * Return the url of this HistoryItem.
- * @return String url of this HistoryItem.
- /
- /* package */ String getUrl() {
- return mUrl;
- }
-
- /**
- * Set the favicon for this item.
- *
- * @param b The new bitmap for this item.
- * If it is null, will use the default.
+ * Set whether or not this represents a bookmark, and make sure the star
+ * behaves appropriately.
*/
- /* package */ void setFavicon(Bitmap b) {
- Drawable[] array = new Drawable[2];
- PaintDrawable p = new PaintDrawable(Color.WHITE);
- p.setCornerRadius(3f);
- array[0] = p;
- if (b != null) {
- array[1] = new BitmapDrawable(b);
- } else {
- array[1] = new BitmapDrawable(mContext.getResources().
- openRawResource(R.drawable.app_web_browser_sm));
- }
- LayerDrawable d = new LayerDrawable(array);
- d.setLayerInset(1, 2, 2, 2, 2);
- d.setBounds(0, 0, 20, 20);
- mTitleView.setCompoundDrawables(d, null, null, null);
- }
-
- /**
- * Set the name for this HistoryItem.
- * If the name is longer that BrowserSettings.MAX_TEXTVIEW_LEN characters,
- * the name is truncated to BrowserSettings.MAX_TEXTVIEW_LEN characters.
- * The History activity does not expose a UI element that can show the
- * full title.
- * @param name String representing new name for this HistoryItem.
- */
- /* package */ void setName(String name) {
- if (name != null && name.length() > BrowserSettings.MAX_TEXTVIEW_LEN) {
- name = name.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN);
- }
- mTitleView.setText(name);
- }
-
- /**
- * Set the url for this HistoryItem.
- * @param url String representing new url for this HistoryItem.
- */
- /* package */ void setUrl(String url) {
- mUrl = url;
- // Truncate the url for the screen
- if (url.length() > BrowserSettings.MAX_TEXTVIEW_LEN) {
- mUrlText.setText(url.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN));
- } else {
- mUrlText.setText(url);
- }
+ void setIsBookmark(boolean isBookmark) {
+ mStar.setOnCheckedChangeListener(null);
+ mStar.setChecked(isBookmark);
+ mStar.setOnCheckedChangeListener(mListener);
}
}
diff --git a/src/com/android/browser/IGearsDialogService.aidl b/src/com/android/browser/IGearsDialogService.aidl
deleted file mode 100644
index 02b30a2..0000000
--- a/src/com/android/browser/IGearsDialogService.aidl
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.android.browser;
-
-interface IGearsDialogService {
- String showDialog(String htmlContent, String dialogArguments,
- boolean inSettings);
-}
diff --git a/src/com/android/browser/ImageAdapter.java b/src/com/android/browser/ImageAdapter.java
index b4c1209..e957143 100644
--- a/src/com/android/browser/ImageAdapter.java
+++ b/src/com/android/browser/ImageAdapter.java
@@ -231,20 +231,7 @@
if (l == null) {
return;
}
- DialogInterface.OnClickListener confirm =
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int whichButton) {
- l.remove(position);
- }
- };
- new AlertDialog.Builder(mContext)
- .setTitle(R.string.close)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.close_window)
- .setPositiveButton(R.string.ok, confirm)
- .setNegativeButton(R.string.cancel, null)
- .show();
+ l.remove(position);
}
/* (non-Javadoc)
diff --git a/src/com/android/browser/MostVisitedActivity.java b/src/com/android/browser/MostVisitedActivity.java
new file mode 100644
index 0000000..704ee27
--- /dev/null
+++ b/src/com/android/browser/MostVisitedActivity.java
@@ -0,0 +1,199 @@
+/*
+ * 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.ListActivity;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Browser;
+import android.webkit.WebIconDatabase.IconListener;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+
+import java.util.Vector;
+
+public class MostVisitedActivity extends ListActivity {
+
+ private MyAdapter mAdapter;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mAdapter = new MyAdapter();
+ CombinedBookmarkHistoryActivity.getIconListenerSet(getContentResolver())
+ .addListener(new IconReceiver());
+ setListAdapter(mAdapter);
+ ListView list = getListView();
+ LayoutInflater factory = LayoutInflater.from(this);
+ View v = factory.inflate(R.layout.empty_history, null);
+ addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT,
+ LayoutParams.FILL_PARENT));
+ list.setEmptyView(v);
+ }
+
+ private class IconReceiver implements IconListener {
+ public void onReceivedIcon(String url, Bitmap icon) {
+ setListAdapter(mAdapter);
+ }
+ }
+
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ TextView tv = (TextView) v.findViewById(R.id.url);
+ String url = tv.getText().toString();
+ loadUrl(url, false);
+ }
+
+ private void loadUrl(String url, boolean newWindow) {
+ Intent intent = new Intent().setAction(url);
+ if (newWindow) {
+ Bundle b = new Bundle();
+ b.putBoolean("new_window", true);
+ intent.putExtras(b);
+ }
+ setResultToParent(RESULT_OK, intent);
+ finish();
+ }
+
+ private class MyAdapter implements ListAdapter {
+ 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;
+
+ MyAdapter() {
+ mObservers = new Vector<DataSetObserver>();
+ String[] projection = new String[] {
+ Browser.BookmarkColumns.URL,
+ Browser.BookmarkColumns.TITLE,
+ Browser.BookmarkColumns.BOOKMARK };
+ String whereClause = Browser.BookmarkColumns.VISITS + " != 0";
+ String orderBy = Browser.BookmarkColumns.VISITS + " DESC";
+ mCursor = managedQuery(Browser.BOOKMARKS_URI, projection,
+ whereClause, null, orderBy);
+ mCursor.registerContentObserver(new ChangeObserver());
+ }
+
+ private class ChangeObserver extends ContentObserver {
+ public ChangeObserver() {
+ super(new Handler());
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ MyAdapter.this.refreshData();
+ }
+ }
+
+ void refreshData() {
+ mCursor.requery();
+ for (DataSetObserver o : mObservers) {
+ o.onChanged();
+ }
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ HistoryItem item;
+ if (null == convertView) {
+ item = new HistoryItem(MostVisitedActivity.this);
+ } else {
+ item = (HistoryItem) convertView;
+ }
+ mCursor.moveToPosition(position);
+ item.setName(mCursor.getString(mTitleIndex));
+ String url = mCursor.getString(mUrlIndex);
+ item.setUrl(url);
+ item.setFavicon(CombinedBookmarkHistoryActivity.getIconListenerSet(
+ getContentResolver()).getFavicon(url));
+ item.setIsBookmark(1 == mCursor.getInt(mBookmarkIndex));
+ return item;
+ }
+
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ public boolean isEnabled(int position) {
+ return true;
+ }
+
+ public int getCount() {
+ return mCursor.getCount();
+ }
+
+ public Object getItem(int position) {
+ return null;
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ // Always a HistoryItem
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public void registerDataSetObserver(DataSetObserver observer) {
+ mObservers.add(observer);
+ }
+
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ mObservers.remove(observer);
+ }
+
+ public boolean isEmpty() {
+ return getCount() == 0;
+ }
+ }
+
+ // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
+ // that situation, we need to pass our result code up to our parent.
+ // However, if someone calls this Activity directly, then this has no
+ // parent, and it needs to set it on itself.
+ private void setResultToParent(int resultCode, Intent data) {
+ Activity a = getParent() == null ? this : getParent();
+ a.setResult(resultCode, data);
+ }
+}
+
diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java
index dfac185..d58ee22 100644
--- a/src/com/android/browser/TabControl.java
+++ b/src/com/android/browser/TabControl.java
@@ -17,7 +17,9 @@
package com.android.browser;
import android.content.Context;
+import android.net.http.SslError;
import android.os.Bundle;
+import android.os.Message;
import android.util.Config;
import android.util.Log;
import android.view.Gravity;
@@ -25,8 +27,10 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
+import android.webkit.HttpAuthHandler;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
+import android.webkit.SslErrorHandler;
import android.webkit.WebBackForwardList;
import android.webkit.WebChromeClient;
import android.webkit.WebHistoryItem;
@@ -79,6 +83,26 @@
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return mClient.shouldOverrideUrlLoading(view, url);
}
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler,
+ SslError error) {
+ mClient.onReceivedSslError(view, handler, error);
+ }
+ @Override
+ public void onReceivedHttpAuthRequest(WebView view,
+ HttpAuthHandler handler, String host, String realm) {
+ mClient.onReceivedHttpAuthRequest(view, handler, host, realm);
+ }
+ @Override
+ public void onFormResubmission(WebView view, Message dontResend,
+ Message resend) {
+ mClient.onFormResubmission(view, dontResend, resend);
+ }
+ @Override
+ public void onReceivedError(WebView view, int errorCode,
+ String description, String failingUrl) {
+ mClient.onReceivedError(view, errorCode, description, failingUrl);
+ }
}
// Subclass of WebChromeClient to display javascript dialogs.
private class SubWindowChromeClient extends WebChromeClient {
@@ -150,11 +174,19 @@
private Vector<Tab> mChildTabs;
private Boolean mCloseOnExit;
+ // Application identifier used to find tabs that another application
+ // wants to reuse.
+ private String mAppId;
+ // Keep the original url around to avoid killing the old WebView if the
+ // url has not changed.
+ private String mOriginalUrl;
// Construct a new tab
- private Tab(WebView w, boolean closeOnExit) {
+ private Tab(WebView w, boolean closeOnExit, String appId, String url) {
mMainView = w;
mCloseOnExit = closeOnExit;
+ mAppId = appId;
+ mOriginalUrl = url;
}
/**
@@ -379,30 +411,34 @@
}
/**
- * Create a new tab and display the new tab immediately.
+ * Create a new tab.
* @return The newly createTab or null if we have reached the maximum
* number of open tabs.
*/
- Tab createNewTab(boolean closeOnExit) {
+ Tab createNewTab(boolean closeOnExit, String appId, String url) {
int size = mTabs.size();
// Return false if we have maxed out on tabs
if (MAX_TABS == size) {
return null;
}
- // Create a new WebView
- WebView w = new WebView(mActivity);
- w.setMapTrackballToArrowKeys(false); // use trackball directly
- // Add this WebView to the settings observer list and update the
- // settings
- final BrowserSettings s = BrowserSettings.getInstance();
- s.addObserver(w.getSettings()).update(s, null);
+ final WebView w = createNewWebView();
// Create a new tab and add it to the tab list
- Tab t = new Tab(w, closeOnExit);
+ Tab t = new Tab(w, closeOnExit, appId, url);
mTabs.add(t);
+ // Initially put the tab in the background.
+ putTabInBackground(t);
return t;
}
/**
+ * Create a new tab with default values for closeOnExit(false),
+ * appId(null), and url(null).
+ */
+ Tab createNewTab() {
+ return createNewTab(false, null, null);
+ }
+
+ /**
* Remove the tab from the list. If the tab is the current tab shown, the
* last created tab will be shown.
* @param t The tab to be removed.
@@ -510,6 +546,8 @@
private static final String CURRTITLE = "currentTitle";
private static final String CLOSEONEXIT = "closeonexit";
private static final String PARENTTAB = "parentTab";
+ private static final String APPID = "appid";
+ private static final String ORIGINALURL = "originalUrl";
/**
* Save the state of all the Tabs.
@@ -543,7 +581,7 @@
final int currentTab = inState.getInt(CURRTAB, -1);
for (int i = 0; i < numTabs; i++) {
if (i == currentTab) {
- Tab t = createNewTab(false);
+ Tab t = createNewTab();
// Me must set the current tab before restoring the state
// so that all the client classes are set.
setCurrentTab(t);
@@ -555,11 +593,15 @@
} else {
// Create a new tab and don't restore the state yet, add it
// to the tab list
- Tab t = new Tab(null, false);
+ Tab t = new Tab(null, false, null, null);
t.mSavedState = inState.getBundle(WEBVIEW + i);
if (t.mSavedState != null) {
t.mUrl = t.mSavedState.getString(CURRURL);
t.mTitle = t.mSavedState.getString(CURRTITLE);
+ // Need to maintain the app id and original url so we
+ // can possibly reuse this tab.
+ t.mAppId = t.mSavedState.getString(APPID);
+ t.mOriginalUrl = t.mSavedState.getString(ORIGINALURL);
}
mTabs.add(t);
mTabQueue.add(t);
@@ -602,7 +644,9 @@
// free the WebView cache
Log.w(LOGTAG, "Free WebView cache");
WebView view = getCurrentWebView();
- view.clearCache(false);
+ if (view != null) {
+ view.clearCache(false);
+ }
// force a gc
System.gc();
}
@@ -699,13 +743,92 @@
}
/**
+ * Return the tab with the matching application id.
+ * @param id The application identifier.
+ */
+ Tab getTabFromId(String id) {
+ if (id == null) {
+ return null;
+ }
+ final int size = getTabCount();
+ for (int i = 0; i < size; i++) {
+ final Tab t = getTab(i);
+ if (id.equals(t.mAppId)) {
+ return t;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Recreate the main WebView of the given tab. Returns true if the WebView
+ * was deleted.
+ */
+ boolean recreateWebView(Tab t, String url) {
+ final WebView w = t.mMainView;
+ if (w != null) {
+ if (url != null && url.equals(t.mOriginalUrl)) {
+ // The original url matches the current url. Just go back to the
+ // first history item so we can load it faster than if we
+ // rebuilt the WebView.
+ final WebBackForwardList list = w.copyBackForwardList();
+ if (list != null) {
+ w.goBackOrForward(-list.getCurrentIndex());
+ w.clearHistory(); // maintains the current page.
+ return false;
+ }
+ }
+ // Remove the settings object from the global settings and destroy
+ // the WebView.
+ BrowserSettings.getInstance().deleteObserver(
+ t.mMainView.getSettings());
+ t.mMainView.destroy();
+ }
+ // Create a new WebView. If this tab is the current tab, we need to put
+ // back all the clients so force it to be the current tab.
+ t.mMainView = createNewWebView();
+ if (getCurrentTab() == t) {
+ setCurrentTab(t, true);
+ }
+ // Clear the saved state except for the app id and close-on-exit
+ // values.
+ t.mSavedState = null;
+ t.mUrl = null;
+ t.mTitle = null;
+ // Save the new url in order to avoid deleting the WebView.
+ t.mOriginalUrl = url;
+ return true;
+ }
+
+ /**
+ * Creates a new WebView and registers it with the global settings.
+ */
+ private WebView createNewWebView() {
+ // Create a new WebView
+ WebView w = new WebView(mActivity);
+ w.setMapTrackballToArrowKeys(false); // use trackball directly
+ // Add this WebView to the settings observer list and update the
+ // settings
+ final BrowserSettings s = BrowserSettings.getInstance();
+ s.addObserver(w.getSettings()).update(s, null);
+ return w;
+ }
+
+ /**
* Put the current tab in the background and set newTab as the current tab.
* @param newTab The new tab. If newTab is null, the current tab is not
* set.
*/
boolean setCurrentTab(Tab newTab) {
+ return setCurrentTab(newTab, false);
+ }
+
+ /**
+ * If force is true, this method skips the check for newTab == current.
+ */
+ private boolean setCurrentTab(Tab newTab, boolean force) {
Tab current = getTab(mCurrentTab);
- if (current == newTab) {
+ if (current == newTab && !force) {
return true;
}
if (current != null) {
@@ -733,13 +856,7 @@
boolean needRestore = (mainView == null);
if (needRestore) {
// Same work as in createNewTab() except don't do new Tab()
- newTab.mMainView = mainView = new WebView(mActivity);
- mainView.setMapTrackballToArrowKeys(false); // use t-ball directly
-
- // Add this WebView to the settings observer list and update the
- // settings
- final BrowserSettings s = BrowserSettings.getInstance();
- s.addObserver(mainView.getSettings()).update(s, null);
+ newTab.mMainView = mainView = createNewWebView();
}
mainView.setWebViewClient(mActivity.getWebViewClient());
mainView.setWebChromeClient(mActivity.getWebChromeClient());
@@ -872,6 +989,12 @@
b.putString(CURRTITLE, t.mTitle);
}
b.putBoolean(CLOSEONEXIT, t.mCloseOnExit);
+ if (t.mAppId != null) {
+ b.putString(APPID, t.mAppId);
+ }
+ if (t.mOriginalUrl != null) {
+ b.putString(ORIGINALURL, t.mOriginalUrl);
+ }
// Remember the parent tab so the relationship can be restored.
if (t.mParentTab != null) {
@@ -892,6 +1015,15 @@
if (b == null) {
return false;
}
+ // Restore the internal state even if the WebView fails to restore.
+ // This will maintain the app id, original url and close-on-exit values.
+ t.mSavedState = null;
+ t.mUrl = null;
+ t.mTitle = null;
+ t.mCloseOnExit = b.getBoolean(CLOSEONEXIT);
+ t.mAppId = b.getString(APPID);
+ t.mOriginalUrl = b.getString(ORIGINALURL);
+
final WebView w = t.mMainView;
final WebBackForwardList list = w.restoreState(b);
if (list == null) {
@@ -902,10 +1034,6 @@
w.restorePicture(b, f);
f.delete();
}
- t.mSavedState = null;
- t.mUrl = null;
- t.mTitle = null;
- t.mCloseOnExit = b.getBoolean(CLOSEONEXIT);
return true;
}
}