diff options
Diffstat (limited to 'src')
61 files changed, 5075 insertions, 3059 deletions
diff --git a/src/com/android/browser/ActiveTabsPage.java b/src/com/android/browser/ActiveTabsPage.java index 664fd68..0feba9a 100644 --- a/src/com/android/browser/ActiveTabsPage.java +++ b/src/com/android/browser/ActiveTabsPage.java @@ -18,62 +18,88 @@ package com.android.browser; import android.content.Context; import android.graphics.Bitmap; -import android.os.Handler; +import android.text.TextUtils; import android.util.AttributeSet; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.AbsListView; import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.ImageView; +import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; -import android.widget.ListView; import android.widget.TextView; -public class ActiveTabsPage extends LinearLayout { +interface OnCloseTab { + void onCloseTab(int position); +} - private static final String LOGTAG = "TabPicker"; +public class ActiveTabsPage extends LinearLayout implements OnClickListener, + OnItemClickListener, OnCloseTab { - private final LayoutInflater mFactory; - private final UiController mUiController; - private final TabControl mControl; - private final TabsListAdapter mAdapter; - private final ListView mListView; + private Context mContext; + private UiController mController; + private TabControl mTabControl; + private View mNewTab, mNewIncognitoTab; + private TabAdapter mAdapter; + private AbsListView mTabsList; - public ActiveTabsPage(Context context, UiController control) { + public ActiveTabsPage(Context context, UiController controller) { super(context); - mUiController = control; - mControl = control.getTabControl(); - mFactory = LayoutInflater.from(context); - mFactory.inflate(R.layout.active_tabs, this); - mListView = (ListView) findViewById(R.id.list); - mAdapter = new TabsListAdapter(); - mListView.setAdapter(mAdapter); - mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - public void onItemClick(AdapterView<?> parent, View view, - int position, long id) { - if (mControl.canCreateNewTab()) { - position -= 2; - } - boolean needToAttach = false; - if (position == -2) { - // Create a new tab - mUiController.openTabToHomePage(); - } else if (position == -1) { - // Create a new incognito tab - mUiController.openIncognitoTab(); - } else { - // Open the corresponding tab - // If the tab is the current one, switchToTab will - // do nothing and return, so we need to make sure - // it gets attached back to its mContentView in - // removeActiveTabPage - needToAttach = !mUiController.switchToTab(position); - } - mUiController.removeActiveTabsPage(needToAttach); - } - }); + mContext = context; + mController = controller; + mTabControl = mController.getTabControl(); + setOrientation(VERTICAL); + setBackgroundResource(R.drawable.bg_browser); + LayoutInflater inflate = LayoutInflater.from(mContext); + inflate.inflate(R.layout.active_tabs, this, true); + mNewTab = findViewById(R.id.new_tab); + mNewIncognitoTab = findViewById(R.id.new_incognito_tab); + mNewTab.setOnClickListener(this); + mNewIncognitoTab.setOnClickListener(this); + int visibility = mTabControl.canCreateNewTab() ? View.VISIBLE : View.GONE; + mNewTab.setVisibility(visibility); + mNewIncognitoTab.setVisibility(visibility); + mTabsList = (AbsListView) findViewById(android.R.id.list); + mAdapter = new TabAdapter(mContext, mTabControl); + mAdapter.setOnCloseListener(this); + mTabsList.setAdapter(mAdapter); + mTabsList.setOnItemClickListener(this); + } + + @Override + public void onClick(View v) { + if (v == mNewTab) { + mController.openTabToHomePage(); + } else if (v == mNewIncognitoTab) { + mController.openIncognitoTab(); + } + mController.removeActiveTabsPage(false); + } + + @Override + public void onItemClick( + AdapterView<?> parent, View view, int position, long id) { + final Tab tab = mTabControl.getTab(position); + boolean needToAttach = !mController.switchToTab(tab); + mController.removeActiveTabsPage(needToAttach); + } + + @Override + public void onCloseTab(int position) { + Tab tab = mTabControl.getTab(position); + if (tab != null) { + mController.closeTab(tab); + if (mTabControl.getTabCount() == 0) { + mController.openTabToHomePage(); + mController.removeActiveTabsPage(false); + } else { + mAdapter.notifyDataSetChanged(); + } + } } /** @@ -81,7 +107,7 @@ public class ActiveTabsPage extends LinearLayout { * the parent to be pressed without being pressed itself. This way the line * of a tab can be pressed, but the close button itself is not. */ - private static class CloseHolder extends ImageView { + public static class CloseHolder extends ImageView { public CloseHolder(Context context, AttributeSet attrs) { super(context, attrs); } @@ -96,118 +122,79 @@ public class ActiveTabsPage extends LinearLayout { } } - private class TabsListAdapter extends BaseAdapter { - private boolean mNotified = true; - private int mReturnedCount; - private Handler mHandler = new Handler(); + static class TabAdapter extends BaseAdapter implements OnClickListener { - public int getCount() { - int count = mControl.getTabCount(); - if (mControl.canCreateNewTab()) { - count += 2; - } - // XXX: This is a workaround to be more like a real adapter. Most - // adapters call notifyDataSetChanged() whenever the internal data - // has changed. Since TabControl is our internal data, we don't - // know when that changes. - // - // Keep track of the last count we returned and whether we called - // notifyDataSetChanged(). If we did not initiate a data set - // change, and the count is different, send the notify and return - // the old count. - if (!mNotified && count != mReturnedCount) { - notifyChange(); - return mReturnedCount; - } - mReturnedCount = count; - mNotified = false; - return count; - } - public Object getItem(int position) { - return null; - } - public long getItemId(int position) { - return position; - } - public int getViewTypeCount() { - return 2; + LayoutInflater mInflater; + OnCloseTab mCloseListener; + TabControl mTabControl; + + TabAdapter(Context context, TabControl tabs) { + mInflater = LayoutInflater.from(context); + mTabControl = tabs; } - public int getItemViewType(int position) { - if (mControl.canCreateNewTab()) { - position -= 2; - } - // Do not recycle the "add new tab" item. - return position < 0 ? IGNORE_ITEM_VIEW_TYPE : 1; + + void setOnCloseListener(OnCloseTab listener) { + mCloseListener = listener; } - public View getView(int position, View convertView, ViewGroup parent) { - final int tabCount = mControl.getTabCount(); - if (mControl.canCreateNewTab()) { - position -= 2; + + @Override + public View getView(int position, View view, ViewGroup parent) { + if (view == null) { + view = mInflater.inflate(R.layout.tab_view, parent, false); } + ImageView favicon = (ImageView) view.findViewById(R.id.favicon); + ImageView thumbnail = (ImageView) view.findViewById(R.id.thumb); + TextView title = (TextView) view.findViewById(R.id.label); + Tab tab = getItem(position); - if (convertView == null) { - if (position == -2) { - convertView = mFactory.inflate(R.layout.tab_view_add_tab, null); - } else if (position == -1) { - convertView = mFactory.inflate(R.layout.tab_view_add_incognito_tab, null); + String label = tab.getTitle(); + if (TextUtils.isEmpty(label)) { + label = tab.getUrl(); + } + title.setText(label); + Bitmap thumbnailBitmap = tab.getScreenshot(); + if (thumbnailBitmap == null) { + thumbnail.setImageResource(R.drawable.browser_thumbnail); + } else { + thumbnail.setImageBitmap(thumbnailBitmap); + } + Bitmap faviconBitmap = tab.getFavicon(); + if (tab.isPrivateBrowsingEnabled()) { + favicon.setImageResource(R.drawable.ic_incognito_holo_dark); + } else { + if (faviconBitmap == null) { + favicon.setImageResource(R.drawable.app_web_browser_sm); } else { - convertView = mFactory.inflate(R.layout.tab_view, null); + favicon.setImageBitmap(faviconBitmap); } } + View close = view.findViewById(R.id.close); + close.setTag(position); + close.setOnClickListener(this); + return view; + } - if (position >= 0) { - TextView title = - (TextView) convertView.findViewById(R.id.title); - TextView url = (TextView) convertView.findViewById(R.id.url); - ImageView favicon = - (ImageView) convertView.findViewById(R.id.favicon); - View close = convertView.findViewById(R.id.close); - Tab tab = mControl.getTab(position); - if (tab.getWebView() == null) { - // This means that populatePickerData will have to use the - // saved state. - Log.w(LOGTAG, "Tab " + position + " has a null WebView and " - + (tab.getSavedState() == null ? "null" : "non-null") - + " saved state "); - } - if (tab.getTitle() == null || tab.getTitle().length() == 0) { - Log.w(LOGTAG, "Tab " + position + " has no title. " - + "Check above in the Logs to see whether it has a " - + "null WebView or null WebHistoryItem"); - } - title.setText(tab.getTitle()); - url.setText(tab.getUrl()); - Bitmap icon = tab.getFavicon(); - if (icon != null) { - favicon.setImageBitmap(icon); - } else { - favicon.setImageResource(R.drawable.app_web_browser_sm); - } - final int closePosition = position; - close.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mUiController.closeTab( - mControl.getTab(closePosition)); - if (tabCount == 1) { - mUiController.openTabToHomePage(); - mUiController.removeActiveTabsPage(false); - } else { - mNotified = true; - notifyDataSetChanged(); - } - } - }); + @Override + public void onClick(View v) { + int position = (Integer) v.getTag(); + if (mCloseListener != null) { + mCloseListener.onCloseTab(position); } - return convertView; } - void notifyChange() { - mHandler.post(new Runnable() { - public void run() { - mNotified = true; - notifyDataSetChanged(); - } - }); + @Override + public int getCount() { + return mTabControl.getTabCount(); + } + + @Override + public Tab getItem(int position) { + return mTabControl.getTab(position); + } + + @Override + public long getItemId(int position) { + return position; } } } diff --git a/src/com/android/browser/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java index 08f9d39..a9e5f52 100644 --- a/src/com/android/browser/AddBookmarkPage.java +++ b/src/com/android/browser/AddBookmarkPage.java @@ -16,19 +16,19 @@ package com.android.browser; -import com.android.browser.provider.BrowserProvider2; import com.android.browser.addbookmark.FolderSpinner; import com.android.browser.addbookmark.FolderSpinnerAdapter; import android.app.Activity; import android.app.LoaderManager; +import android.app.LoaderManager.LoaderCallbacks; +import android.content.AsyncTaskLoader; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.CursorLoader; import android.content.Loader; -import android.content.SharedPreferences; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; @@ -40,8 +40,8 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; -import android.preference.PreferenceManager; import android.provider.BrowserContract; +import android.provider.BrowserContract.Accounts; import android.text.TextUtils; import android.util.AttributeSet; import android.view.KeyEvent; @@ -53,20 +53,23 @@ import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; import android.widget.CursorAdapter; import android.widget.EditText; import android.widget.ListView; +import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import java.net.URI; import java.net.URISyntaxException; -import java.util.Stack; public class AddBookmarkPage extends Activity implements View.OnClickListener, TextView.OnEditorActionListener, AdapterView.OnItemClickListener, LoaderManager.LoaderCallbacks<Cursor>, - BreadCrumbView.Controller, FolderSpinner.OnSetSelectionListener { + BreadCrumbView.Controller, FolderSpinner.OnSetSelectionListener, + OnItemSelectedListener { public static final long DEFAULT_FOLDER_ID = -1; public static final String TOUCH_ICON_URL = "touch_icon_url"; @@ -83,12 +86,9 @@ public class AddBookmarkPage extends Activity private final String LOGTAG = "Bookmarks"; // IDs for the CursorLoaders that are used. - private final int LOADER_ID_FOLDER_CONTENTS = 0; - private final int LOADER_ID_ALL_FOLDERS = 1; - private final int LOADER_ID_FIND_ROOT = 2; - private final int LOADER_ID_CHECK_FOR_DUPE = 3; - private final int LOADER_ID_MOST_RECENTLY_SAVED_BOOKMARK = 4; - private final int LOADER_ID_FIND_FOLDER_BY_ID = 5; + private final int LOADER_ID_ACCOUNTS = 0; + private final int LOADER_ID_FOLDER_CONTENTS = 1; + private final int LOADER_ID_EDIT_INFO = 2; private EditText mTitle; private EditText mAddress; @@ -121,6 +121,9 @@ public class AddBookmarkPage extends Activity private View mRemoveLink; private View mFakeTitleHolder; private FolderSpinnerAdapter mFolderAdapter; + private Spinner mAccountSpinner; + private ArrayAdapter<BookmarkAccount> mAccountAdapter; + private static class Folder { String Name; long Id; @@ -142,26 +145,16 @@ public class AddBookmarkPage extends Activity } private Uri getUriForFolder(long folder) { - Uri uri; - if (folder == mRootFolder) { - uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER; - } else { - uri = BrowserContract.Bookmarks.buildFolderUri(folder); - } - String[] accountInfo = getAccountNameAndType(this); - if (accountInfo != null) { - uri = BookmarksLoader.addAccount(uri, accountInfo[1], accountInfo[0]); - } - return uri; + return BrowserContract.Bookmarks.buildFolderUri(folder); } @Override - public void onTop(int level, Object data) { + public void onTop(BreadCrumbView view, int level, Object data) { if (null == data) return; Folder folderData = (Folder) data; long folder = folderData.Id; LoaderManager manager = getLoaderManager(); - CursorLoader loader = (CursorLoader) ((Loader) manager.getLoader( + CursorLoader loader = (CursorLoader) ((Loader<?>) manager.getLoader( LOADER_ID_FOLDER_CONTENTS)); loader.setUri(getUriForFolder(folder)); loader.forceLoad(); @@ -213,7 +206,7 @@ public class AddBookmarkPage extends Activity // editing a folder, 1 otherwise. mFolder.setSelectionIgnoringSelectionChange(mEditingFolder ? 0 : 1); } else { - ((TextView) mFolder.getSelectedView()).setText(folder.Name); + mFolderAdapter.setOtherFolderDisplayText(folder.Name); } } } else { @@ -227,17 +220,15 @@ public class AddBookmarkPage extends Activity } else { Object data = mCrumbs.getTopData(); if (data != null && ((Folder) data).Id == mCurrentFolder) { - // We are showing the correct folder heirarchy. The + // We are showing the correct folder hierarchy. The // folder selector will say "Other folder..." Change it // to say the name of the folder once again. - ((TextView) mFolder.getSelectedView()).setText(((Folder) data).Name); + mFolderAdapter.setOtherFolderDisplayText(((Folder) data).Name); } else { - // We are not be showing the correct folder heirarchy. + // We are not showing the correct folder hierarchy. // Clear the Crumbs and find the proper folder - mCrumbs.clear(); setupTopCrumb(); LoaderManager manager = getLoaderManager(); - manager.restartLoader(LOADER_ID_ALL_FOLDERS, null, this); manager.restartLoader(LOADER_ID_FOLDER_CONTENTS, null, this); } @@ -319,7 +310,6 @@ public class AddBookmarkPage extends Activity // and choose a different one, so that we will start from // the correct place. LoaderManager manager = getLoaderManager(); - manager.initLoader(LOADER_ID_ALL_FOLDERS, null, this); manager.restartLoader(LOADER_ID_FOLDER_CONTENTS, null, this); break; default: @@ -351,11 +341,6 @@ public class AddBookmarkPage extends Activity values.put(BrowserContract.Bookmarks.TITLE, name); values.put(BrowserContract.Bookmarks.IS_FOLDER, 1); - String[] accountInfo = getAccountNameAndType(this); - if (accountInfo != null) { - values.put(BrowserContract.Bookmarks.ACCOUNT_TYPE, accountInfo[1]); - values.put(BrowserContract.Bookmarks.ACCOUNT_NAME, accountInfo[0]); - } long currentFolder; Object data = mCrumbs.getTopData(); if (data != null) { @@ -391,66 +376,78 @@ public class AddBookmarkPage extends Activity } } + private LoaderCallbacks<EditBookmarkInfo> mEditInfoLoaderCallbacks = + new LoaderCallbacks<EditBookmarkInfo>() { + + @Override + public void onLoaderReset(Loader<EditBookmarkInfo> loader) { + // Don't care + } + + @Override + public void onLoadFinished(Loader<EditBookmarkInfo> loader, + EditBookmarkInfo info) { + boolean setAccount = false; + if (info.id != -1) { + mEditingExisting = true; + showRemoveButton(); + mFakeTitle.setText(R.string.edit_bookmark); + mTitle.setText(info.title); + mFolderAdapter.setOtherFolderDisplayText(info.parentTitle); + mMap.putLong(BrowserContract.Bookmarks._ID, info.id); + setAccount = true; + setAccount(info.accountName, info.accountType); + mCurrentFolder = info.parentId; + onCurrentFolderFound(); + } + // TODO: Detect if lastUsedId is a subfolder of info.id in the + // editing folder case. For now, just don't show the last used + // folder at all to prevent any chance of the user adding a parent + // folder to a child folder + if (info.lastUsedId != -1 && info.lastUsedId != info.id + && !mEditingFolder) { + if (setAccount && info.lastUsedId != mRootFolder + && TextUtils.equals(info.lastUsedAccountName, info.accountName) + && TextUtils.equals(info.lastUsedAccountType, info.accountType)) { + mFolderAdapter.addRecentFolder(info.lastUsedId, info.lastUsedTitle); + } else if (!setAccount) { + setAccount = true; + setAccount(info.lastUsedAccountName, info.lastUsedAccountType); + if (info.lastUsedId != mRootFolder) { + mFolderAdapter.addRecentFolder(info.lastUsedId, + info.lastUsedTitle); + } + } + } + if (!setAccount) { + mAccountSpinner.setSelection(0); + } + } + + @Override + public Loader<EditBookmarkInfo> onCreateLoader(int id, Bundle args) { + return new EditBookmarkInfoLoader(AddBookmarkPage.this, mMap); + } + }; + + void setAccount(String accountName, String accountType) { + for (int i = 0; i < mAccountAdapter.getCount(); i++) { + BookmarkAccount account = mAccountAdapter.getItem(i); + if (TextUtils.equals(account.accountName, accountName) + && TextUtils.equals(account.accountType, accountType)) { + onRootFolderFound(account.rootFolderId); + mAccountSpinner.setSelection(i); + return; + } + } + } + @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { String[] projection; switch (id) { - case LOADER_ID_CHECK_FOR_DUPE: - projection = new String[] { - BrowserContract.Bookmarks._ID, - BrowserContract.Bookmarks.PARENT, - BrowserContract.Bookmarks.TITLE - }; - return new CursorLoader(this, - BookmarkUtils.getBookmarksUri(this), - projection, - BrowserContract.Bookmarks.URL + " = ?", - new String[] { mOriginalUrl }, - null); - case LOADER_ID_FIND_ROOT: - String name = args.getString(BrowserBookmarksPage.PREF_ACCOUNT_NAME); - String type = args.getString(BrowserBookmarksPage.PREF_ACCOUNT_TYPE); - - projection = new String[] { BrowserContract.Bookmarks._ID }; - String selection = BrowserContract.ChromeSyncColumns.SERVER_UNIQUE + "=? AND " - + BrowserContract.Bookmarks.ACCOUNT_NAME + "=? AND " - + BrowserContract.Bookmarks.ACCOUNT_TYPE + "=?"; - String[] selArgs = new String[] { - BrowserContract.ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR, - name, - type - }; - return new CursorLoader(this, - BrowserContract.Bookmarks.CONTENT_URI, - projection, - selection, - selArgs, - null); - case LOADER_ID_FIND_FOLDER_BY_ID: - projection = new String[] { - BrowserContract.Bookmarks._ID, - BrowserContract.Bookmarks.TITLE - }; - return new CursorLoader(this, - BookmarkUtils.getBookmarksUri(this), - projection, - BrowserContract.Bookmarks._ID + " = " - + args.getLong(BrowserContract.Bookmarks._ID), - null, - null); - case LOADER_ID_ALL_FOLDERS: - projection = new String[] { - BrowserContract.Bookmarks._ID, - BrowserContract.Bookmarks.PARENT, - BrowserContract.Bookmarks.TITLE, - BrowserContract.Bookmarks.IS_FOLDER - }; - return new CursorLoader(this, - BookmarkUtils.getBookmarksUri(this), - projection, - BrowserContract.Bookmarks.IS_FOLDER + " != 0", - null, - null); + case LOADER_ID_ACCOUNTS: + return new AccountsLoader(this); case LOADER_ID_FOLDER_CONTENTS: projection = new String[] { BrowserContract.Bookmarks._ID, @@ -458,26 +455,25 @@ public class AddBookmarkPage extends Activity BrowserContract.Bookmarks.IS_FOLDER }; String where = BrowserContract.Bookmarks.IS_FOLDER + " != 0"; + String whereArgs[] = null; if (mEditingFolder) { - where += " AND " + BrowserContract.Bookmarks._ID + " != " - + mMap.getLong(BrowserContract.Bookmarks._ID); + where += " AND " + BrowserContract.Bookmarks._ID + " != ?"; + whereArgs = new String[] { Long.toString(mMap.getLong( + BrowserContract.Bookmarks._ID)) }; + } + long currentFolder; + Object data = mCrumbs.getTopData(); + if (data != null) { + currentFolder = ((Folder) data).Id; + } else { + currentFolder = mRootFolder; } return new CursorLoader(this, - getUriForFolder(mCurrentFolder), + getUriForFolder(currentFolder), projection, where, - null, + whereArgs, BrowserContract.Bookmarks._ID + " ASC"); - case LOADER_ID_MOST_RECENTLY_SAVED_BOOKMARK: - projection = new String[] { - BrowserContract.Bookmarks.PARENT - }; - return new CursorLoader(this, - BookmarkUtils.getBookmarksUri(this), - projection, - BrowserContract.Bookmarks.IS_FOLDER + " = 0", - null, - BrowserContract.Bookmarks.DATE_CREATED + " DESC"); default: throw new AssertionError("Asking for nonexistant loader!"); } @@ -486,101 +482,18 @@ public class AddBookmarkPage extends Activity @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { switch (loader.getId()) { - case LOADER_ID_CHECK_FOR_DUPE: - if (cursor != null && cursor.moveToFirst()) { - // Site is bookmarked. - mEditingExisting = true; - showRemoveButton(); - mFakeTitle.setText(R.string.edit_bookmark); - int index = cursor.getColumnIndexOrThrow( - BrowserContract.Bookmarks.PARENT); - mCurrentFolder = cursor.getLong(index); - index = cursor.getColumnIndexOrThrow( - BrowserContract.Bookmarks.TITLE); - String title = cursor.getString(index); - mTitle.setText(title); - index = cursor.getColumnIndexOrThrow( - BrowserContract.Bookmarks._ID); - long id = cursor.getLong(index); - mMap.putLong(BrowserContract.Bookmarks._ID, id); + case LOADER_ID_ACCOUNTS: + mAccountAdapter.clear(); + while (cursor.moveToNext()) { + mAccountAdapter.add(new BookmarkAccount(this, cursor)); } - onCurrentFolderFound(); - getLoaderManager().destroyLoader(LOADER_ID_CHECK_FOR_DUPE); - break; - case LOADER_ID_FIND_ROOT: - long root; - if (cursor != null && cursor.moveToFirst()) { - root = cursor.getLong(0); - } else { - root = BrowserProvider2.FIXED_ID_ROOT; - } - onRootFolderFound(root); - getLoaderManager().destroyLoader(LOADER_ID_FIND_ROOT); + getLoaderManager().destroyLoader(LOADER_ID_ACCOUNTS); + getLoaderManager().restartLoader(LOADER_ID_EDIT_INFO, null, + mEditInfoLoaderCallbacks); break; case LOADER_ID_FOLDER_CONTENTS: mAdapter.changeCursor(cursor); break; - case LOADER_ID_MOST_RECENTLY_SAVED_BOOKMARK: - LoaderManager manager = getLoaderManager(); - if (cursor != null && cursor.moveToFirst()) { - // Find the parent - long lastUsedFolder = cursor.getLong(0); - if (lastUsedFolder != mRootFolder - && lastUsedFolder != mCurrentFolder - && lastUsedFolder != 0) { - // Find out the parent's name - Bundle b = new Bundle(); - b.putLong(BrowserContract.Bookmarks._ID, lastUsedFolder); - manager.initLoader(LOADER_ID_FIND_FOLDER_BY_ID, b, this); - } - } - manager.destroyLoader(LOADER_ID_MOST_RECENTLY_SAVED_BOOKMARK); - break; - case LOADER_ID_FIND_FOLDER_BY_ID: - if (cursor != null && cursor.moveToFirst()) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow( - BrowserContract.Bookmarks._ID)); - String title = cursor.getString(cursor.getColumnIndexOrThrow( - BrowserContract.Bookmarks.TITLE)); - mFolderAdapter.addRecentFolder(id, title); - } - getLoaderManager().destroyLoader(LOADER_ID_FIND_FOLDER_BY_ID); - break; - case LOADER_ID_ALL_FOLDERS: - long parent = mCurrentFolder; - int idIndex = cursor.getColumnIndexOrThrow( - BrowserContract.Bookmarks._ID); - int titleIndex = cursor.getColumnIndexOrThrow( - BrowserContract.Bookmarks.TITLE); - int parentIndex = cursor.getColumnIndexOrThrow( - BrowserContract.Bookmarks.PARENT); - // If the user is editing anything inside the "Other Bookmarks" - // folder, we need to stop searching up when we reach its parent. - // Find the root folder - moveCursorToFolder(cursor, mRootFolder, idIndex); - // omniparent is the folder which contains root, and therefore - // also the parent of the "Other Bookmarks" folder. - long omniparent = cursor.getLong(parentIndex); - Stack<Folder> folderStack = new Stack<Folder>(); - while ((parent != mRootFolder) && (parent != 0) && (parent != omniparent)) { - // First, find the folder corresponding to the current - // folder - moveCursorToFolder(cursor, parent, idIndex); - String name = cursor.getString(titleIndex); - if (parent == mCurrentFolder) { - ((TextView) mFolder.getSelectedView()).setText(name); - } - folderStack.push(new Folder(name, parent)); - parent = cursor.getLong(parentIndex); - } - while (!folderStack.isEmpty()) { - Folder thisFolder = folderStack.pop(); - mCrumbs.pushView(thisFolder.Name, thisFolder); - } - getLoaderManager().destroyLoader(LOADER_ID_ALL_FOLDERS); - break; - default: - break; } } @@ -727,7 +640,7 @@ public class AddBookmarkPage extends Activity mCancelButton.setOnClickListener(this); mFolder = (FolderSpinner) findViewById(R.id.folder); - mFolderAdapter = new FolderSpinnerAdapter(!mEditingFolder); + mFolderAdapter = new FolderSpinnerAdapter(this, !mEditingFolder); mFolder.setAdapter(mFolderAdapter); mFolder.setOnSetSelectionListener(this); @@ -759,22 +672,22 @@ public class AddBookmarkPage extends Activity mListView.setOnItemClickListener(this); mListView.addEditText(mFolderNamer); + mAccountAdapter = new ArrayAdapter<BookmarkAccount>(this, + android.R.layout.simple_spinner_item); + mAccountAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + mAccountSpinner = (Spinner) findViewById(R.id.accounts); + mAccountSpinner.setAdapter(mAccountAdapter); + mAccountSpinner.setOnItemSelectedListener(this); + + mFakeTitleHolder = findViewById(R.id.title_holder); if (!window.getDecorView().isInTouchMode()) { mButton.requestFocus(); } - String[] accountInfo = getAccountNameAndType(this); - if (accountInfo == null) { - onRootFolderFound(BrowserProvider2.FIXED_ID_ROOT); - } else { - Bundle args = new Bundle(); - args.putString(BrowserBookmarksPage.PREF_ACCOUNT_NAME, accountInfo[0]); - args.putString(BrowserBookmarksPage.PREF_ACCOUNT_TYPE, accountInfo[1]); - getLoaderManager().initLoader(LOADER_ID_FIND_ROOT, args, this); - } - + getLoaderManager().restartLoader(LOADER_ID_ACCOUNTS, null, this); } private void showRemoveButton() { @@ -787,22 +700,13 @@ public class AddBookmarkPage extends Activity // Called once we have determined which folder is the root folder private void onRootFolderFound(long root) { mRootFolder = root; - if (mCurrentFolder == DEFAULT_FOLDER_ID) { - mCurrentFolder = mRootFolder; - } + mCurrentFolder = mRootFolder; setupTopCrumb(); - if (mEditingExisting || TextUtils.isEmpty(mOriginalUrl) - || !(mMap != null && mMap.getBoolean(CHECK_FOR_DUPE))) { - onCurrentFolderFound(); - } else { - // User is attempting to bookmark a site, rather than deliberately - // editing a bookmark. Rather than let them create a duplicate - // bookmark, see if the bookmark already exists. - getLoaderManager().initLoader(LOADER_ID_CHECK_FOR_DUPE, null, this); - } + onCurrentFolderFound(); } private void setupTopCrumb() { + mCrumbs.clear(); String name = getString(R.string.bookmarks); mTopLevelLabel = (TextView) mCrumbs.pushView(name, false, new Folder(name, mRootFolder)); @@ -813,19 +717,12 @@ public class AddBookmarkPage extends Activity private void onCurrentFolderFound() { LoaderManager manager = getLoaderManager(); if (mCurrentFolder != mRootFolder) { - // Find all the folders - manager.initLoader(LOADER_ID_ALL_FOLDERS, null, this); // Since we're not in the root folder, change the selection to other // folder now. The text will get changed once we select the correct // folder. mFolder.setSelectionIgnoringSelectionChange(mEditingFolder ? 1 : 2); } else { setShowBookmarkIcon(true); - if (!mEditingExisting) { - // Find the most recently saved bookmark, so that we can include it in - // the list of options to save the current bookmark. - manager.initLoader(LOADER_ID_MOST_RECENTLY_SAVED_BOOKMARK, null, this); - } if (!mEditingFolder) { // Initially the "Bookmarks" folder should be showing, rather than // the home screen. In the editing folder case, home screen is not @@ -834,22 +731,7 @@ public class AddBookmarkPage extends Activity } } // Find the contents of the current folder - manager.initLoader(LOADER_ID_FOLDER_CONTENTS, null, this); -} - /** - * Get the account name and type of the currently synced account. - * @param context Context to access preferences. - * @return null if no account name or type. Otherwise, the result will be - * an array of two Strings, the accountName and accountType, respectively. - */ - private String[] getAccountNameAndType(Context context) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - String accountName = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_NAME, null); - String accountType = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_TYPE, null); - if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { - return null; - } - return new String[] { accountName, accountType }; + manager.restartLoader(LOADER_ID_FOLDER_CONTENTS, null, this); } /** @@ -1070,6 +952,22 @@ public class AddBookmarkPage extends Activity return true; } + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, + long id) { + if (mAccountSpinner == parent) { + long root = mAccountAdapter.getItem(position).rootFolderId; + if (root != mRootFolder) { + onRootFolderFound(root); + } + } + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + // Don't care + } + /* * Class used as a proxy for the InputMethodManager to get to mFolderNamer */ @@ -1097,4 +995,162 @@ public class AddBookmarkPage extends Activity return view == mEditText; } } + + static class AccountsLoader extends CursorLoader { + + static final String[] PROJECTION = new String[] { + Accounts.ACCOUNT_NAME, + Accounts.ACCOUNT_TYPE, + Accounts.ROOT_ID, + }; + + static final int COLUMN_INDEX_ACCOUNT_NAME = 0; + static final int COLUMN_INDEX_ACCOUNT_TYPE = 1; + static final int COLUMN_INDEX_ROOT_ID = 2; + + public AccountsLoader(Context context) { + super(context, Accounts.CONTENT_URI, PROJECTION, null, null, + Accounts.ACCOUNT_NAME + " ASC"); + } + + } + + static class BookmarkAccount { + + private String mLabel; + String accountName, accountType; + long rootFolderId; + + public BookmarkAccount(Context context, Cursor cursor) { + accountName = cursor.getString( + AccountsLoader.COLUMN_INDEX_ACCOUNT_NAME); + accountType = cursor.getString( + AccountsLoader.COLUMN_INDEX_ACCOUNT_TYPE); + rootFolderId = cursor.getLong( + AccountsLoader.COLUMN_INDEX_ROOT_ID); + mLabel = accountName; + if (TextUtils.isEmpty(mLabel)) { + mLabel = context.getString(R.string.local_bookmarks); + } + } + + @Override + public String toString() { + return mLabel; + } + } + + static class EditBookmarkInfo { + long id = -1; + long parentId = -1; + String parentTitle; + String title; + String accountName; + String accountType; + + long lastUsedId = -1; + String lastUsedTitle; + String lastUsedAccountName; + String lastUsedAccountType; + } + + static class EditBookmarkInfoLoader extends AsyncTaskLoader<EditBookmarkInfo> { + + private Context mContext; + private Bundle mMap; + + public EditBookmarkInfoLoader(Context context, Bundle bundle) { + super(context); + mContext = context; + mMap = bundle; + } + + @Override + public EditBookmarkInfo loadInBackground() { + final ContentResolver cr = mContext.getContentResolver(); + EditBookmarkInfo info = new EditBookmarkInfo(); + Cursor c = null; + + try { + // First, let's lookup the bookmark (check for dupes, get needed info) + String url = mMap.getString(BrowserContract.Bookmarks.URL); + info.id = mMap.getLong(BrowserContract.Bookmarks._ID, -1); + boolean checkForDupe = mMap.getBoolean(CHECK_FOR_DUPE); + if (checkForDupe && info.id == -1 && !TextUtils.isEmpty(url)) { + c = cr.query(BrowserContract.Bookmarks.CONTENT_URI, + new String[] { BrowserContract.Bookmarks._ID}, + BrowserContract.Bookmarks.URL + "=?", + new String[] { url }, null); + if (c.getCount() == 1 && c.moveToFirst()) { + info.id = c.getLong(0); + } + c.close(); + } + if (info.id != -1) { + c = cr.query(ContentUris.withAppendedId( + BrowserContract.Bookmarks.CONTENT_URI, info.id), + new String[] { + BrowserContract.Bookmarks.PARENT, + BrowserContract.Bookmarks.ACCOUNT_NAME, + BrowserContract.Bookmarks.ACCOUNT_TYPE, + BrowserContract.Bookmarks.TITLE}, + null, null, null); + if (c.moveToFirst()) { + info.parentId = c.getLong(0); + info.accountName = c.getString(1); + info.accountType = c.getString(2); + info.title = c.getString(3); + } + c.close(); + c = cr.query(ContentUris.withAppendedId( + BrowserContract.Bookmarks.CONTENT_URI, info.parentId), + new String[] { + BrowserContract.Bookmarks.TITLE,}, + null, null, null); + if (c.moveToFirst()) { + info.parentTitle = c.getString(0); + } + c.close(); + } + + // Figure out the last used folder/account + c = cr.query(BrowserContract.Bookmarks.CONTENT_URI, + new String[] { + BrowserContract.Bookmarks.PARENT, + }, null, null, + BrowserContract.Bookmarks.DATE_MODIFIED + " DESC LIMIT 1"); + if (c.moveToFirst()) { + long parent = c.getLong(0); + c.close(); + c = cr.query(BrowserContract.Bookmarks.CONTENT_URI, + new String[] { + BrowserContract.Bookmarks.TITLE, + BrowserContract.Bookmarks.ACCOUNT_NAME, + BrowserContract.Bookmarks.ACCOUNT_TYPE}, + BrowserContract.Bookmarks._ID + "=?", new String[] { + Long.toString(parent)}, null); + if (c.moveToFirst()) { + info.lastUsedId = parent; + info.lastUsedTitle = c.getString(0); + info.lastUsedAccountName = c.getString(1); + info.lastUsedAccountType = c.getString(2); + } + c.close(); + } + } finally { + if (c != null) { + c.close(); + } + } + + return info; + } + + @Override + protected void onStartLoading() { + forceLoad(); + } + + } + } diff --git a/src/com/android/browser/AutoFillSettingsFragment.java b/src/com/android/browser/AutoFillSettingsFragment.java index 3a7ae12..e87645e 100644 --- a/src/com/android/browser/AutoFillSettingsFragment.java +++ b/src/com/android/browser/AutoFillSettingsFragment.java @@ -53,6 +53,7 @@ public class AutoFillSettingsFragment extends Fragment { // Used to display toast after DB interactions complete. private Handler mHandler; + private BrowserSettings mSettings; private final static int PROFILE_SAVED_MSG = 100; private final static int PROFILE_DELETED_MSG = 101; @@ -130,6 +131,7 @@ public class AutoFillSettingsFragment extends Fragment { @Override public void onCreate(Bundle savedState) { super.onCreate(savedState); + mSettings = BrowserSettings.getInstance(); } @Override @@ -177,7 +179,7 @@ public class AutoFillSettingsFragment extends Fragment { mCountryEdit.getText().toString(), mPhoneEdit.getText().toString()); - BrowserSettings.getInstance().setAutoFillProfile(getActivity(), newProfile, + mSettings.setAutoFillProfile(newProfile, mHandler.obtainMessage(PROFILE_SAVED_MSG)); closeEditor(); } @@ -200,7 +202,7 @@ public class AutoFillSettingsFragment extends Fragment { // Update browser settings and native with a null profile. This will // trigger the current profile to get deleted from the DB. - BrowserSettings.getInstance().setAutoFillProfile(getActivity(), null, + mSettings.setAutoFillProfile(null, mHandler.obtainMessage(PROFILE_DELETED_MSG)); updateButtonState(); @@ -215,7 +217,7 @@ public class AutoFillSettingsFragment extends Fragment { }); // Populate the text boxes with any pre existing AutoFill data. - AutoFillProfile activeProfile = BrowserSettings.getInstance().getAutoFillProfile(); + AutoFillProfile activeProfile = mSettings.getAutoFillProfile(); if (activeProfile != null) { mFullNameEdit.setText(activeProfile.getFullName()); mEmailEdit.setText(activeProfile.getEmailAddress()); diff --git a/src/com/android/browser/AutofillHandler.java b/src/com/android/browser/AutofillHandler.java new file mode 100644 index 0000000..b6b237d --- /dev/null +++ b/src/com/android/browser/AutofillHandler.java @@ -0,0 +1,202 @@ + +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.database.Cursor; +import android.os.AsyncTask; +import android.os.Message; +import android.preference.PreferenceManager; +import android.webkit.WebSettings.AutoFillProfile; + +import java.util.concurrent.CountDownLatch; + +public class AutofillHandler { + + private AutoFillProfile mAutoFillProfile; + // Default to zero. In the case no profile is set up, the initial + // value will come from the AutoFillSettingsFragment when the user + // creates a profile. Otherwise, we'll read the ID of the last used + // profile from the prefs db. + private int mAutoFillActiveProfileId; + private static final int NO_AUTOFILL_PROFILE_SET = 0; + + private CountDownLatch mLoaded = new CountDownLatch(1); + private Context mContext; + + public AutofillHandler(Context context) { + mContext = context; + } + + /** + * Load settings from the browser app's database. It is performed in + * an AsyncTask as it involves plenty of slow disk IO. + * NOTE: Strings used for the preferences must match those specified + * in the various preference XML files. + */ + public void asyncLoadFromDb() { + // Run the initial settings load in an AsyncTask as it hits the + // disk multiple times through SharedPreferences and SQLite. We + // need to be certain though that this has completed before we start + // to load pages though, so in the worst case we will block waiting + // for it to finish in BrowserActivity.onCreate(). + new LoadFromDb().start(); + } + + public void waitForLoad() { + try { + mLoaded.await(); + } catch (InterruptedException e) {} + } + + private class LoadFromDb extends Thread { + + @Override + public void run() { + SharedPreferences p = + PreferenceManager.getDefaultSharedPreferences(mContext); + + // Read the last active AutoFill profile id. + mAutoFillActiveProfileId = p.getInt( + PreferenceKeys.PREF_AUTOFILL_ACTIVE_PROFILE_ID, + mAutoFillActiveProfileId); + + // Load the autofill profile data from the database. We use a database separate + // to the browser preference DB to make it easier to support multiple profiles + // and switching between them. + AutoFillProfileDatabase autoFillDb = AutoFillProfileDatabase.getInstance(mContext); + Cursor c = autoFillDb.getProfile(mAutoFillActiveProfileId); + + if (c.getCount() > 0) { + c.moveToFirst(); + + String fullName = c.getString(c.getColumnIndex( + AutoFillProfileDatabase.Profiles.FULL_NAME)); + String email = c.getString(c.getColumnIndex( + AutoFillProfileDatabase.Profiles.EMAIL_ADDRESS)); + String company = c.getString(c.getColumnIndex( + AutoFillProfileDatabase.Profiles.COMPANY_NAME)); + String addressLine1 = c.getString(c.getColumnIndex( + AutoFillProfileDatabase.Profiles.ADDRESS_LINE_1)); + String addressLine2 = c.getString(c.getColumnIndex( + AutoFillProfileDatabase.Profiles.ADDRESS_LINE_2)); + String city = c.getString(c.getColumnIndex( + AutoFillProfileDatabase.Profiles.CITY)); + String state = c.getString(c.getColumnIndex( + AutoFillProfileDatabase.Profiles.STATE)); + String zip = c.getString(c.getColumnIndex( + AutoFillProfileDatabase.Profiles.ZIP_CODE)); + String country = c.getString(c.getColumnIndex( + AutoFillProfileDatabase.Profiles.COUNTRY)); + String phone = c.getString(c.getColumnIndex( + AutoFillProfileDatabase.Profiles.PHONE_NUMBER)); + mAutoFillProfile = new AutoFillProfile(mAutoFillActiveProfileId, + fullName, email, company, addressLine1, addressLine2, city, + state, zip, country, phone); + } + c.close(); + autoFillDb.close(); + + mLoaded.countDown(); + } + } + + public void setAutoFillProfile(AutoFillProfile profile, Message msg) { + int profileId = NO_AUTOFILL_PROFILE_SET; + if (profile != null) { + profileId = profile.getUniqueId(); + // Update the AutoFill DB with the new profile. + new SaveProfileToDbTask(msg).execute(profile); + } else { + // Delete the current profile. + if (mAutoFillProfile != null) { + new DeleteProfileFromDbTask(msg).execute(mAutoFillProfile.getUniqueId()); + } + } + // Make sure we set mAutoFillProfile before calling setActiveAutoFillProfileId + // Calling setActiveAutoFillProfileId will trigger an update of WebViews + // which will expect a new profile to be set + mAutoFillProfile = profile; + setActiveAutoFillProfileId(profileId); + } + + public AutoFillProfile getAutoFillProfile() { + return mAutoFillProfile; + } + + private void setActiveAutoFillProfileId(int activeProfileId) { + mAutoFillActiveProfileId = activeProfileId; + Editor ed = PreferenceManager. + getDefaultSharedPreferences(mContext).edit(); + ed.putInt(PreferenceKeys.PREF_AUTOFILL_ACTIVE_PROFILE_ID, activeProfileId); + ed.apply(); + } + + private abstract class AutoFillProfileDbTask<T> extends AsyncTask<T, Void, Void> { + AutoFillProfileDatabase mAutoFillProfileDb; + Message mCompleteMessage; + + public AutoFillProfileDbTask(Message msg) { + mCompleteMessage = msg; + } + + @Override + protected void onPostExecute(Void result) { + if (mCompleteMessage != null) { + mCompleteMessage.sendToTarget(); + } + mAutoFillProfileDb.close(); + } + + @Override + abstract protected Void doInBackground(T... values); + } + + + private class SaveProfileToDbTask extends AutoFillProfileDbTask<AutoFillProfile> { + public SaveProfileToDbTask(Message msg) { + super(msg); + } + + @Override + protected Void doInBackground(AutoFillProfile... values) { + mAutoFillProfileDb = AutoFillProfileDatabase.getInstance(mContext); + assert mAutoFillActiveProfileId != NO_AUTOFILL_PROFILE_SET; + AutoFillProfile newProfile = values[0]; + mAutoFillProfileDb.addOrUpdateProfile(mAutoFillActiveProfileId, newProfile); + return null; + } + } + + private class DeleteProfileFromDbTask extends AutoFillProfileDbTask<Integer> { + public DeleteProfileFromDbTask(Message msg) { + super(msg); + } + + @Override + protected Void doInBackground(Integer... values) { + mAutoFillProfileDb = AutoFillProfileDatabase.getInstance(mContext); + int id = values[0]; + assert id > 0; + mAutoFillProfileDb.dropProfile(id); + return null; + } + } +} diff --git a/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java index 4f80e9d..7530732 100644 --- a/src/com/android/browser/BaseUi.java +++ b/src/com/android/browser/BaseUi.java @@ -17,14 +17,20 @@ package com.android.browser; import com.android.browser.Tab.LockIcon; +import com.android.internal.view.menu.MenuBuilder; import android.animation.ObjectAnimator; import android.app.Activity; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +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.os.Bundle; import android.text.TextUtils; import android.util.Log; @@ -72,10 +78,11 @@ public abstract class BaseUi implements UI, WebViewFactory { private Drawable mSecLockIcon; private Drawable mMixLockIcon; + protected Drawable mGenericFavicon; private FrameLayout mBrowserFrameLayout; protected FrameLayout mContentView; - private FrameLayout mCustomViewContainer; + protected FrameLayout mCustomViewContainer; private View mCustomView; private WebChromeClient.CustomViewCallback mCustomViewCallback; @@ -86,7 +93,6 @@ public abstract class BaseUi implements UI, WebViewFactory { private Toast mStopToast; - private boolean mTitleShowing; // the default <video> poster private Bitmap mDefaultVideoPoster; @@ -116,7 +122,23 @@ public abstract class BaseUi implements UI, WebViewFactory { mCustomViewContainer = (FrameLayout) mBrowserFrameLayout .findViewById(R.id.fullscreen_custom_content); frameLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS); - mTitleShowing = false; + setFullscreen(BrowserSettings.getInstance().useFullscreen()); + mGenericFavicon = res.getDrawable( + R.drawable.app_web_browser_sm); + } + + @Override + public WebView createWebView(boolean privateBrowsing) { + // Create a new WebView + BrowserWebView w = new BrowserWebView(mActivity, null, + android.R.attr.webViewStyle, privateBrowsing); + initWebViewSettings(w); + return w; + } + + @Override + public WebView createSubWebView(boolean privateBrowsing) { + return createWebView(privateBrowsing); } /** @@ -129,11 +151,15 @@ public abstract class BaseUi implements UI, WebViewFactory { w.setMapTrackballToArrowKeys(false); // use trackball directly // Enable the built-in zoom w.getSettings().setBuiltInZoomControls(true); + boolean supportsMultiTouch = mActivity.getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH); + w.getSettings().setDisplayZoomControls(!supportsMultiTouch); + w.setExpandedTileBounds(true); // smoother scrolling // Add this WebView to the settings observer list and update the // settings final BrowserSettings s = BrowserSettings.getInstance(); - s.addObserver(w.getSettings()).update(s, null); + s.startManagingSettings(w.getSettings()); } private void cancelStopToast() { @@ -164,8 +190,6 @@ public abstract class BaseUi implements UI, WebViewFactory { public void onConfigurationChanged(Configuration config) { } - public abstract void editUrl(boolean clearInput); - // key handling @Override @@ -212,7 +236,7 @@ public abstract class BaseUi implements UI, WebViewFactory { @Override public boolean needsRestoreAllTabs() { - return false; + return true; } @Override @@ -300,8 +324,6 @@ public abstract class BaseUi implements UI, WebViewFactory { Log.w(LOGTAG, "mContainer is already attached to content in" + " attachTabToContentView!"); } - mainView.setNextFocusUpId(R.id.url_focused); - mainView.setNextFocusDownId(R.id.url_focused); mUiController.attachSubWindow(tab); } @@ -395,24 +417,43 @@ public abstract class BaseUi implements UI, WebViewFactory { mContentView.addView(container, COVER_SCREEN_PARAMS); } + protected void refreshWebView() { + WebView web = getWebView(); + if (web != null) { + web.invalidate(); + } + } + + public void editUrl(boolean clearInput) { + if (mUiController.isInCustomActionMode()) { + mUiController.endActionMode(); + } + showTitleBar(); + getTitleBar().startEditingUrl(clearInput); + } + boolean canShowTitleBar() { return !isTitleBarShowing() && !isActivityPaused() && (getActiveTab() != null) - && (getActiveTab().getWebView() != null) + && (getWebView() != null) && !mUiController.isInCustomActionMode(); } void showTitleBar() { - mTitleShowing = true; + if (canShowTitleBar()) { + getTitleBar().show(); + } } protected void hideTitleBar() { - mTitleShowing = false; + if (getTitleBar().isShowing()) { + getTitleBar().hide(); + } } protected boolean isTitleBarShowing() { - return mTitleShowing; + return getTitleBar().isShowing(); } protected abstract TitleBarBase getTitleBar(); @@ -421,24 +462,28 @@ public abstract class BaseUi implements UI, WebViewFactory { WebView web = getWebView(); if (web != null) { web.setTitleBarGravity(gravity); - web.invalidate(); } } @Override - public void showVoiceTitleBar(String title) { - getTitleBar().setInVoiceMode(true); + public void showVoiceTitleBar(String title, List<String> results) { + getTitleBar().setInVoiceMode(true, results); getTitleBar().setDisplayTitle(title); } @Override public void revertVoiceTitleBar(Tab tab) { - getTitleBar().setInVoiceMode(false); + getTitleBar().setInVoiceMode(false, null); String url = tab.getUrl(); getTitleBar().setDisplayTitle(url); } @Override + public void registerDropdownChangeListener(DropdownChangeListener d) { + getTitleBar().registerDropdownChangeListener(d); + } + + @Override public void showComboView(boolean startWithHistory, Bundle extras) { if (mComboView != null) { return; @@ -452,8 +497,9 @@ public abstract class BaseUi implements UI, WebViewFactory { FrameLayout wrapper = (FrameLayout) mContentView.findViewById(R.id.webview_wrapper); wrapper.setVisibility(View.GONE); - hideTitleBar(); + getTitleBar().stopEditingUrl(); dismissIME(); + hideTitleBar(); if (mActiveTab != null) { WebView web = mActiveTab.getWebView(); mActiveTab.putInBackground(); @@ -559,7 +605,9 @@ public abstract class BaseUi implements UI, WebViewFactory { protected void updateNavigationState(Tab tab) { } - protected void updateAutoLogin(Tab tab, boolean animate) {} + protected void updateAutoLogin(Tab tab, boolean animate) { + getTitleBar().updateAutoLogin(tab, animate); + } /** * Update the lock icon to correspond to our latest state. @@ -605,11 +653,6 @@ public abstract class BaseUi implements UI, WebViewFactory { @Override public void onActionModeFinished(boolean inLoad) { - if (inLoad) { - // the titlebar was removed when the CAB was shown - // if the page is loading, show it again - showTitleBar(); - } } // active tabs page @@ -718,16 +761,59 @@ public abstract class BaseUi implements UI, WebViewFactory { warning.show(); } - @Override - public void registerDropdownChangeListener(DropdownChangeListener d) { + protected void captureTab(final Tab tab) { + captureTab(tab, + (int) mActivity.getResources() + .getDimension(R.dimen.qc_thumb_width), + (int) mActivity.getResources() + .getDimension(R.dimen.qc_thumb_height)); + } + + protected void captureTab(final Tab tab, int width, int height) { + if ((tab == null) || (tab.getWebView() == null)) return; + Bitmap sshot = Controller.createScreenshot(tab, width, height); + tab.setScreenshot(sshot); } protected WebView getWebView() { - Tab tab = getActiveTab(); - if (tab != null) { - return tab.getWebView(); + if (mActiveTab != null) { + return mActiveTab.getWebView(); + } else { + return null; + } + } + + protected Menu getMenu() { + MenuBuilder menu = new MenuBuilder(mActivity); + mActivity.getMenuInflater().inflate(R.menu.browser, menu); + return menu; + } + + public void setFullscreen(boolean enabled) { + if (enabled) { + mActivity.getWindow().setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } else { + mActivity.getWindow().clearFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + } + + protected Drawable getFaviconDrawable(Bitmap icon) { + Drawable[] array = new Drawable[3]; + array[0] = new PaintDrawable(Color.BLACK); + PaintDrawable p = new PaintDrawable(Color.WHITE); + array[1] = p; + if (icon == null) { + array[2] = mGenericFavicon; + } else { + array[2] = new BitmapDrawable(icon); } - return null; + LayerDrawable d = new LayerDrawable(array); + d.setLayerInset(1, 1, 1, 1, 1); + d.setLayerInset(2, 2, 2, 2, 2); + return d; } } diff --git a/src/com/android/browser/BookmarkDragHandler.java b/src/com/android/browser/BookmarkDragHandler.java new file mode 100644 index 0000000..0707058 --- /dev/null +++ b/src/com/android/browser/BookmarkDragHandler.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser; + +import android.content.ClipData; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.provider.BrowserContract; +import android.provider.BrowserContract.Bookmarks; +import android.view.DragEvent; +import android.view.View; +import android.view.View.OnDragListener; + +public class BookmarkDragHandler { + + public static interface BookmarkDragController { + boolean startDrag(Cursor item); + } + + public static interface BookmarkDragAdapter { + void setBookmarkDragHandler(BookmarkDragHandler handler); + Cursor getItemForView(View v); + } + + static class BookmarkDragState { + long id; + long parent; + } + + static final String BOOKMARK_DRAG_LABEL = "com.android.browser.BOOKMARK_LABEL"; + + private Context mContext; + private BookmarkDragController mDragController; + private BookmarkDragAdapter mDragAdapter; + + public BookmarkDragHandler(Context context, BookmarkDragController controller, + BookmarkDragAdapter adapter) { + mContext = context; + mDragController = controller; + mDragAdapter = adapter; + mDragAdapter.setBookmarkDragHandler(this); + } + + public boolean startDrag(View view, Cursor item, long id) { + if (!mDragController.startDrag(item)) { + return false; + } + Uri uri = ContentUris.withAppendedId( + BrowserContract.Bookmarks.CONTENT_URI, id); + ClipData data = ClipData.newRawUri(BOOKMARK_DRAG_LABEL, uri); + BookmarkDragState state = new BookmarkDragState(); + state.id = id; + state.parent = item.getLong(BookmarksLoader.COLUMN_INDEX_PARENT); + view.startDrag(data, new View.DragShadowBuilder(view), state, 0); + return true; + } + + public void registerBookmarkDragHandler(View view) { + view.setOnDragListener(mBookmarkDragListener); + } + + private OnDragListener mBookmarkDragListener = new OnDragListener() { + + @Override + public boolean onDrag(View v, DragEvent event) { + Cursor c = mDragAdapter.getItemForView(v); + BookmarkDragState state = (BookmarkDragState) event.getLocalState(); + switch (event.getAction()) { + case DragEvent.ACTION_DRAG_STARTED: + return true; + case DragEvent.ACTION_DROP: + long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID); + if (id == state.id) { + // We dropped onto ourselves, show the context menu + v.showContextMenu(); + return false; + } + long parent = c.getLong(BookmarksLoader.COLUMN_INDEX_PARENT); + if (isFolder(c)) { + parent = c.getLong(BookmarksLoader.COLUMN_INDEX_ID); + } + if (parent != state.parent) { + ContentResolver cr = mContext.getContentResolver(); + ContentValues values = new ContentValues(); + values.put(Bookmarks.PARENT, parent); + Uri uri = event.getClipData().getItemAt(0).getUri(); + cr.update(uri, values, null, null); + } + break; + } + return false; + } + }; + + static boolean isFolder(Cursor c) { + return c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0; + } +} diff --git a/src/com/android/browser/BookmarkItem.java b/src/com/android/browser/BookmarkItem.java index 4e60073..e7f37a5 100644 --- a/src/com/android/browser/BookmarkItem.java +++ b/src/com/android/browser/BookmarkItem.java @@ -30,6 +30,8 @@ import android.widget.TextView; */ class BookmarkItem extends LinearLayout { + final static int MAX_TEXTVIEW_LEN = 80; + protected TextView mTextView; protected TextView mUrlText; protected ImageView mImageView; @@ -63,6 +65,16 @@ class BookmarkItem extends LinearLayout { item.mImageView.setImageDrawable(mImageView.getDrawable()); } + public void startMarquee() { + mTextView.setSelected(true); + mUrlText.setSelected(true); + } + + public void stopMarquee() { + mTextView.setSelected(false); + mUrlText.setSelected(false); + } + /** * Return the name assigned to this bookmark item. */ @@ -111,8 +123,8 @@ class BookmarkItem extends LinearLayout { mTitle = name; - if (name.length() > BrowserSettings.MAX_TEXTVIEW_LEN) { - name = name.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN); + if (name.length() > MAX_TEXTVIEW_LEN) { + name = name.substring(0, MAX_TEXTVIEW_LEN); } mTextView.setText(name); @@ -129,8 +141,8 @@ class BookmarkItem extends LinearLayout { mUrl = url; - if (url.length() > BrowserSettings.MAX_TEXTVIEW_LEN) { - url = url.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN); + if (url.length() > MAX_TEXTVIEW_LEN) { + url = url.substring(0, MAX_TEXTVIEW_LEN); } mUrlText.setText(url); diff --git a/src/com/android/browser/BookmarkUtils.java b/src/com/android/browser/BookmarkUtils.java index 3aaf5d4..383dc7e 100644 --- a/src/com/android/browser/BookmarkUtils.java +++ b/src/com/android/browser/BookmarkUtils.java @@ -25,7 +25,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -42,7 +41,6 @@ import android.os.Message; import android.preference.PreferenceManager; import android.provider.Browser; import android.provider.BrowserContract; -import android.util.DisplayMetrics; public class BookmarkUtils { private final static String LOGTAG = "BookmarkUtils"; @@ -218,10 +216,10 @@ public class BookmarkUtils { } /* package */ static Uri getBookmarksUri(Context context) { - return addAccountInfo(context, - BrowserContract.Bookmarks.CONTENT_URI.buildUpon()).build(); + return BrowserContract.Bookmarks.CONTENT_URI; } + @Deprecated public static Uri.Builder addAccountInfo(Context context, Uri.Builder ub) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); String accountType = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_TYPE, null); diff --git a/src/com/android/browser/BookmarksLoader.java b/src/com/android/browser/BookmarksLoader.java index 7722392..bc06497 100644 --- a/src/com/android/browser/BookmarksLoader.java +++ b/src/com/android/browser/BookmarksLoader.java @@ -49,8 +49,8 @@ public class BookmarksLoader extends CursorLoader { ChromeSyncColumns.SERVER_UNIQUE, // 9 }; - private String mAccountType; - private String mAccountName; + String mAccountType; + String mAccountName; public BookmarksLoader(Context context, String accountType, String accountName) { super(context, addAccount(Bookmarks.CONTENT_URI_DEFAULT_FOLDER, accountType, accountName), diff --git a/src/com/android/browser/BreadCrumbView.java b/src/com/android/browser/BreadCrumbView.java index 9aaecd7..6706deb 100644 --- a/src/com/android/browser/BreadCrumbView.java +++ b/src/com/android/browser/BreadCrumbView.java @@ -41,8 +41,8 @@ import java.util.List; public class BreadCrumbView extends LinearLayout implements OnClickListener { private static final int DIVIDER_PADDING = 12; // dips - interface Controller { - public void onTop(int level, Object data); + public interface Controller { + public void onTop(BreadCrumbView view, int level, Object data); } private ImageButton mBackButton; @@ -52,6 +52,7 @@ public class BreadCrumbView extends LinearLayout implements OnClickListener { private Drawable mSeparatorDrawable; private float mDividerPadding; private int mMaxVisible = -1; + private Context mContext; /** * @param context @@ -81,12 +82,14 @@ public class BreadCrumbView extends LinearLayout implements OnClickListener { } private void init(Context ctx) { + mContext = ctx; + setFocusable(true); mUseBackButton = false; mCrumbs = new ArrayList<Crumb>(); - TypedArray a = ctx.obtainStyledAttributes(com.android.internal.R.styleable.Theme); + TypedArray a = mContext.obtainStyledAttributes(com.android.internal.R.styleable.Theme); mSeparatorDrawable = a.getDrawable(com.android.internal.R.styleable.Theme_dividerVertical); a.recycle(); - mDividerPadding = DIVIDER_PADDING * ctx.getResources().getDisplayMetrics().density; + mDividerPadding = DIVIDER_PADDING * mContext.getResources().getDisplayMetrics().density; addBackButton(); } @@ -134,9 +137,9 @@ public class BreadCrumbView extends LinearLayout implements OnClickListener { public void notifyController() { if (mController != null) { if (mCrumbs.size() > 0) { - mController.onTop(mCrumbs.size(), getTopCrumb().data); + mController.onTop(this, mCrumbs.size(), getTopCrumb().data); } else { - mController.onTop(0, null); + mController.onTop(this, 0, null); } } } diff --git a/src/com/android/browser/Browser.java b/src/com/android/browser/Browser.java index f49da5d..65eb0ce 100644 --- a/src/com/android/browser/Browser.java +++ b/src/com/android/browser/Browser.java @@ -16,12 +16,9 @@ package com.android.browser; -import android.os.FileUtils; -import android.util.Log; - import android.app.Application; import android.content.Intent; -import android.webkit.CookieManager; +import android.util.Log; import android.webkit.CookieSyncManager; import dalvik.system.VMRuntime; @@ -52,9 +49,6 @@ public class Browser extends Application { public void onCreate() { super.onCreate(); - // Set the umask so that native code creates files with the correct - // permissions (0660) - FileUtils.setUMask(FileUtils.S_IRWXO); if (LOGV_ENABLED) Log.v(LOGTAG, "Browser.onCreate: this=" + this); @@ -63,7 +57,7 @@ public class Browser extends Application { TARGET_HEAP_UTILIZATION); // create CookieSyncManager with current Context CookieSyncManager.createInstance(this); - BrowserSettings.getInstance().asyncLoadFromDb(this); + BrowserSettings.initialize(getApplicationContext()); } static Intent createBrowserViewIntent() { diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java index a9b65fd..963f29f 100644 --- a/src/com/android/browser/BrowserActivity.java +++ b/src/com/android/browser/BrowserActivity.java @@ -62,11 +62,6 @@ public class BrowserActivity extends Activity { BrowserSettings settings = BrowserSettings.getInstance(); - // We load the first set of BrowserSettings from the db asynchronously - // but if it has not completed at this point, we have no choice but - // to block waiting for them to finish loading. :( - settings.waitForLoadFromDbToComplete(); - // render the browser in OpenGL if (settings.isHardwareAccelerated()) { // Set the flag in the activity's window @@ -77,12 +72,6 @@ public class BrowserActivity extends Activity { this.getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); } - // enable this to test the browser in 32bit - if (false) { - getWindow().setFormat(PixelFormat.RGBX_8888); - BitmapFactory.setDefaultConfig(Bitmap.Config.ARGB_8888); - } - // If this was a web search request, pass it on to the default web // search provider and finish this activity. if (IntentHandler.handleWebSearchIntent(this, null, getIntent())) { diff --git a/src/com/android/browser/BrowserBookmarksAdapter.java b/src/com/android/browser/BrowserBookmarksAdapter.java index 9e71077..7543528 100644 --- a/src/com/android/browser/BrowserBookmarksAdapter.java +++ b/src/com/android/browser/BrowserBookmarksAdapter.java @@ -20,7 +20,6 @@ import android.content.Context; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -28,10 +27,9 @@ import android.widget.CursorAdapter; import android.widget.ImageView; import android.widget.TextView; -class BrowserBookmarksAdapter extends CursorAdapter { +public class BrowserBookmarksAdapter extends CursorAdapter { LayoutInflater mInflater; int mCurrentView; - Drawable mFaviconBackground; /** * Create a new BrowserBookmarksAdapter. @@ -42,8 +40,6 @@ class BrowserBookmarksAdapter extends CursorAdapter { super(context, null, 0); mInflater = LayoutInflater.from(context); selectView(defaultView); - float density = context.getResources().getDisplayMetrics().density; - mFaviconBackground = BookmarkUtils.createListFaviconBackground(context); } @Override @@ -101,9 +97,7 @@ class BrowserBookmarksAdapter extends CursorAdapter { } else { favicon.setImageBitmap(faviconBitmap); } - //favicon.setBackgroundResource(R.drawable.bookmark_list_favicon_bg); - // TODO: Switch to above instead of below once b/3353813 is fixed - favicon.setBackgroundDrawable(mFaviconBackground); + favicon.setBackgroundResource(R.drawable.bookmark_list_favicon_bg); } } diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java index c6bc2bc..448f881 100644 --- a/src/com/android/browser/BrowserBookmarksPage.java +++ b/src/com/android/browser/BrowserBookmarksPage.java @@ -16,7 +16,9 @@ package com.android.browser; -import com.android.browser.BreadCrumbView.Crumb; +import com.android.browser.BookmarkDragHandler.BookmarkDragController; +import com.android.browser.view.BookmarkExpandableGridView; +import com.android.browser.view.BookmarkExpandableGridView.BookmarkContextMenuInfo; import android.app.Activity; import android.app.Fragment; @@ -25,11 +27,11 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.ContentUris; import android.content.Context; +import android.content.CursorLoader; import android.content.Intent; import android.content.Loader; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; @@ -40,6 +42,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.provider.BrowserContract; +import android.provider.BrowserContract.Accounts; import android.provider.BrowserContract.ChromeSyncColumns; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; @@ -50,116 +53,115 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.webkit.WebIconDatabase.IconListener; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.GridView; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.OnChildClickListener; import android.widget.ListView; import android.widget.PopupMenu.OnMenuItemClickListener; import android.widget.Toast; +import java.util.HashMap; + interface BookmarksPageCallbacks { // Return true if handled boolean onBookmarkSelected(Cursor c, boolean isFolder); // Return true if handled boolean onOpenInNewWindow(Cursor c); - void onFolderChanged(int level, Uri uri); } /** * View showing the user's bookmarks in the browser. */ public class BrowserBookmarksPage extends Fragment implements View.OnCreateContextMenuListener, - LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener, IconListener, - BreadCrumbView.Controller, OnMenuItemClickListener, OnSharedPreferenceChangeListener { + LoaderManager.LoaderCallbacks<Cursor>, IconListener, + BreadCrumbView.Controller, OnMenuItemClickListener, OnChildClickListener { static final String LOGTAG = "browser"; - static final int LOADER_BOOKMARKS = 1; + static final int LOADER_ACCOUNTS = 1; + static final int LOADER_BOOKMARKS = 100; static final String EXTRA_DISABLE_WINDOW = "disable_new_window"; - static final String ACCOUNT_NAME_UNSYNCED = "Unsynced"; - public static final String PREF_ACCOUNT_TYPE = "acct_type"; public static final String PREF_ACCOUNT_NAME = "acct_name"; + static final String ACCOUNT_TYPE = "account_type"; + static final String ACCOUNT_NAME = "account_name"; + static final int VIEW_THUMBNAILS = 1; static final int VIEW_LIST = 2; static final String PREF_SELECTED_VIEW = "bookmarks_view"; BookmarksPageCallbacks mCallbacks; View mRoot; - GridView mGrid; + BookmarkExpandableGridView mGrid; ListView mList; - BrowserBookmarksAdapter mAdapter; boolean mDisableNewWindow; - boolean mCanceled = false; boolean mEnableContextMenu = true; - boolean mShowRootFolder = false; View mEmptyView; int mCurrentView; View mHeader; - ViewGroup mHeaderContainer; - BreadCrumbView mCrumbs; - int mCrumbVisibility = View.VISIBLE; - int mCrumbMaxVisible = -1; - boolean mCrumbBackButton = false; + HashMap<Integer, BrowserBookmarksAdapter> mBookmarkAdapters = new HashMap<Integer, BrowserBookmarksAdapter>(); + BookmarkDragHandler mDragHandler; static BrowserBookmarksPage newInstance(BookmarksPageCallbacks cb, Bundle args, ViewGroup headerContainer) { BrowserBookmarksPage bbp = new BrowserBookmarksPage(); bbp.mCallbacks = cb; - bbp.mHeaderContainer = headerContainer; bbp.setArguments(args); return bbp; } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { - switch (id) { - case LOADER_BOOKMARKS: { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getActivity()); - String accountType = prefs.getString(PREF_ACCOUNT_TYPE, null); - String accountName = prefs.getString(PREF_ACCOUNT_NAME, null); - BookmarksLoader bl = new BookmarksLoader(getActivity(), - accountType, accountName); - if (mCrumbs != null) { - Uri uri = (Uri) mCrumbs.getTopData(); - if (uri != null) { - bl.setUri(uri); - } - } - return bl; - } + if (id == LOADER_ACCOUNTS) { + return new AccountsLoader(getActivity()); + } else if (id >= LOADER_BOOKMARKS) { + String accountType = args.getString(ACCOUNT_TYPE); + String accountName = args.getString(ACCOUNT_NAME); + BookmarksLoader bl = new BookmarksLoader(getActivity(), + accountType, accountName); + return bl; + } else { + throw new UnsupportedOperationException("Unknown loader id " + id); } - throw new UnsupportedOperationException("Unknown loader id " + id); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { - switch (loader.getId()) { - case LOADER_BOOKMARKS: { - // Set the visibility of the empty vs. content views - if (cursor == null || cursor.getCount() == 0) { - mEmptyView.setVisibility(View.VISIBLE); - mGrid.setVisibility(View.GONE); - mList.setVisibility(View.GONE); - } else { - mEmptyView.setVisibility(View.GONE); - setupBookmarkView(); - } - - // Give the new data to the adapter - mAdapter.changeCursor(cursor); - break; + if (loader.getId() == LOADER_ACCOUNTS) { + LoaderManager lm = getLoaderManager(); + int id = LOADER_BOOKMARKS; + while (cursor.moveToNext()) { + String accountName = cursor.getString(0); + String accountType = cursor.getString(1); + Bundle args = new Bundle(); + args.putString(ACCOUNT_NAME, accountName); + args.putString(ACCOUNT_TYPE, accountType); + BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter( + getActivity(), mCurrentView); + mBookmarkAdapters.put(id, adapter); + mGrid.addAccount(accountName, adapter); + lm.restartLoader(id, args, this); + id++; } + // TODO: Figure out what a reload of these means + // Currently, a reload is triggered whenever bookmarks change + // This is less than ideal + // It also causes UI flickering as a new adapter is created + // instead of re-using an existing one when the account_name is the + // same. + // For now, this is a one-shot load + getLoaderManager().destroyLoader(LOADER_ACCOUNTS); + } else if (loader.getId() >= LOADER_BOOKMARKS) { + BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId()); + adapter.changeCursor(cursor); } } @Override public void onLoaderReset(Loader<Cursor> loader) { - onLoadFinished(loader, null); + // TODO: Figure out what to do here (if anything?) } long getFolderId() { @@ -181,37 +183,32 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte @Override public boolean onContextItemSelected(MenuItem item) { final Activity activity = getActivity(); - // It is possible that the view has been canceled when we get to - // this point as back has a higher priority - if (mCanceled) { - return false; - } - AdapterView.AdapterContextMenuInfo i = - (AdapterView.AdapterContextMenuInfo)item.getMenuInfo(); + BookmarkContextMenuInfo i = (BookmarkContextMenuInfo)item.getMenuInfo(); // If we have no menu info, we can't tell which item was selected. if (i == null) { return false; } + BrowserBookmarksAdapter adapter = getChildAdapter(i.groupPosition); switch (item.getItemId()) { case R.id.open_context_menu_id: - loadUrl(i.position); + loadUrl(adapter, i.childPosition); break; case R.id.edit_context_menu_id: - editBookmark(i.position); + editBookmark(adapter, i.childPosition); break; case R.id.shortcut_context_menu_id: - Cursor c = mAdapter.getItem(i.position); + Cursor c = adapter.getItem(i.childPosition); activity.sendBroadcast(createShortcutIntent(getActivity(), c)); break; case R.id.delete_context_menu_id: - displayRemoveBookmarkDialog(i.position); + displayRemoveBookmarkDialog(adapter, i.childPosition); break; case R.id.new_window_context_menu_id: - openInNewWindow(i.position); + openInNewWindow(adapter, i.childPosition); break; case R.id.share_link_context_menu_id: { - Cursor cursor = mAdapter.getItem(i.position); + Cursor cursor = adapter.getItem(i.childPosition); Controller.sharePage(activity, cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE), cursor.getString(BookmarksLoader.COLUMN_INDEX_URL), @@ -220,16 +217,16 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte break; } case R.id.copy_url_context_menu_id: - copy(getUrl(i.position)); + copy(getUrl(adapter, i.childPosition)); break; case R.id.homepage_context_menu_id: { - BrowserSettings.getInstance().setHomePage(activity, getUrl(i.position)); + BrowserSettings.getInstance().setHomePage(getUrl(adapter, i.childPosition)); Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show(); break; } // Only for the Most visited page case R.id.save_to_bookmarks_menu_id: { - Cursor cursor = mAdapter.getItem(i.position); + Cursor cursor = adapter.getItem(i.childPosition); String name = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE); String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL); // If the site is bookmarked, the item becomes remove from @@ -270,8 +267,9 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Cursor cursor = mAdapter.getItem(info.position); + BookmarkContextMenuInfo info = (BookmarkContextMenuInfo) menuInfo; + BrowserBookmarksAdapter adapter = getChildAdapter(info.groupPosition); + Cursor cursor = adapter.getItem(info.childPosition); if (!canEdit(cursor)) { return; } @@ -330,55 +328,37 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - - setHasOptionsMenu(true); + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(getActivity()); + mCurrentView = prefs.getInt(PREF_SELECTED_VIEW, getDefaultView()); + // TODO: Support list view + mCurrentView = VIEW_THUMBNAILS; Bundle args = getArguments(); mDisableNewWindow = args == null ? false : args.getBoolean(EXTRA_DISABLE_WINDOW, false); + + setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - Context context = getActivity(); - mRoot = inflater.inflate(R.layout.bookmarks, container, false); mEmptyView = mRoot.findViewById(android.R.id.empty); - mGrid = (GridView) mRoot.findViewById(R.id.grid); - mGrid.setOnItemClickListener(this); - mGrid.setColumnWidth(Controller.getDesiredThumbnailWidth(getActivity())); + mGrid = (BookmarkExpandableGridView) mRoot.findViewById(R.id.grid); + mGrid.setOnChildClickListener(this); + mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail); + mGrid.setBreadcrumbController(this); mList = (ListView) mRoot.findViewById(R.id.list); - mList.setOnItemClickListener(this); + // TODO: mList.setOnItemClickListener(this); setEnableContextMenu(mEnableContextMenu); + mDragHandler = new BookmarkDragHandler(getActivity(), mDragController, + mGrid.getDragAdapter()); - // Prep the header - ViewGroup hc = mHeaderContainer; - if (hc == null) { - hc = (ViewGroup) mRoot.findViewById(R.id.header_container); - hc.setVisibility(View.VISIBLE); - } - mHeader = inflater.inflate(R.layout.bookmarks_header, hc, false); - hc.addView(mHeader); - mCrumbs = (BreadCrumbView) mHeader.findViewById(R.id.crumbs); - mCrumbs.setController(this); - mCrumbs.setUseBackButton(mCrumbBackButton); - mCrumbs.setMaxVisible(mCrumbMaxVisible); - mCrumbs.setVisibility(mCrumbVisibility); - String name = getString(R.string.bookmarks); - mCrumbs.pushView(name, false, BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER); - if (mCallbacks != null) { - mCallbacks.onFolderChanged(1, BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER); - } // Start the loaders LoaderManager lm = getLoaderManager(); - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getActivity()); - prefs.registerOnSharedPreferenceChangeListener(this); - mCurrentView = - prefs.getInt(PREF_SELECTED_VIEW, getDefaultView()); - mAdapter = new BrowserBookmarksAdapter(getActivity(), mCurrentView); - lm.restartLoader(LOADER_BOOKMARKS, null, this); + lm.restartLoader(LOADER_ACCOUNTS, null, this); // Add our own listener in case there are favicons that have yet to be loaded. CombinedBookmarkHistoryView.getIconListenerSet().addListener(this); @@ -396,16 +376,14 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte @Override public void onDestroyView() { super.onDestroyView(); - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getActivity()); - prefs.unregisterOnSharedPreferenceChangeListener(this); - if (mHeaderContainer != null) { - mHeaderContainer.removeView(mHeader); - } - mCrumbs.setController(null); - mCrumbs = null; - getLoaderManager().destroyLoader(LOADER_BOOKMARKS); - mAdapter = null; + mGrid.setBreadcrumbController(null); + LoaderManager lm = getLoaderManager(); + lm.destroyLoader(LOADER_ACCOUNTS); + for (int id : mBookmarkAdapters.keySet()) { + lm.destroyLoader(id); + } + mBookmarkAdapters.clear(); + CombinedBookmarkHistoryView.getIconListenerSet().removeListener(this); } @@ -413,35 +391,52 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte public void onReceivedIcon(String url, Bitmap icon) { // A new favicon has been loaded, so let anything attached to the adapter know about it // so new icons will be loaded. - mAdapter.notifyDataSetChanged(); + // TODO: Notify all of data set changed + // TODO: Wait, is this even needed? Won't this trigger a DB change anyway? } - @Override - public void onItemClick(AdapterView<?> parent, View v, int position, long id) { - // It is possible that the view has been canceled when we get to - // this point as back has a higher priority - if (mCanceled) { - android.util.Log.e(LOGTAG, "item clicked when dismissing"); - return; + private BrowserBookmarksAdapter getChildAdapter(int groupPosition) { + if (mCurrentView == VIEW_THUMBNAILS) { + return mGrid.getChildAdapter(groupPosition); + } else { + // TODO: Support expandable list + return null; + } + } + + private BreadCrumbView getBreadCrumbs(int groupPosition) { + if (mCurrentView == VIEW_THUMBNAILS) { + return mGrid.getBreadCrumbs(groupPosition); + } else { + // TODO: Support expandable list + return null; } + } - Cursor cursor = mAdapter.getItem(position); + @Override + public boolean onChildClick(ExpandableListView parent, View v, + int groupPosition, int childPosition, long id) { + BrowserBookmarksAdapter adapter = getChildAdapter(groupPosition); + Cursor cursor = adapter.getItem(childPosition); boolean isFolder = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0; if (mCallbacks != null && mCallbacks.onBookmarkSelected(cursor, isFolder)) { - return; + return true; } + // TODO: Folder stuff if (isFolder) { String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE); Uri uri = ContentUris.withAppendedId( BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, id); - if (mCrumbs != null) { + BreadCrumbView crumbs = getBreadCrumbs(groupPosition); + if (crumbs != null) { // update crumbs - mCrumbs.pushView(title, uri); + crumbs.pushView(title, uri); } - loadFolder(uri); + loadFolder(groupPosition, uri); } + return true; } /* package */ static Intent createShortcutIntent(Context context, Cursor cursor) { @@ -452,15 +447,15 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte return BookmarkUtils.createAddToHomeIntent(context, url, title, touchIcon, favicon); } - private void loadUrl(int position) { - if (mCallbacks != null && mAdapter != null) { - mCallbacks.onBookmarkSelected(mAdapter.getItem(position), false); + private void loadUrl(BrowserBookmarksAdapter adapter, int position) { + if (mCallbacks != null && adapter != null) { + mCallbacks.onBookmarkSelected(adapter.getItem(position), false); } } - private void openInNewWindow(int position) { + private void openInNewWindow(BrowserBookmarksAdapter adapter, int position) { if (mCallbacks != null) { - Cursor c = mAdapter.getItem(position); + Cursor c = adapter.getItem(position); boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1; if (isFolder) { long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID); @@ -497,9 +492,9 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte } - private void editBookmark(int position) { + private void editBookmark(BrowserBookmarksAdapter adapter, int position) { Intent intent = new Intent(getActivity(), AddBookmarkPage.class); - Cursor cursor = mAdapter.getItem(position); + Cursor cursor = adapter.getItem(position); Bundle item = new Bundle(); item.putString(BrowserContract.Bookmarks.TITLE, cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE)); @@ -520,18 +515,19 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte startActivity(intent); } - private void displayRemoveBookmarkDialog(final int position) { + private void displayRemoveBookmarkDialog(BrowserBookmarksAdapter adapter, + int position) { // Put up a dialog asking if the user really wants to // delete the bookmark - Cursor cursor = mAdapter.getItem(position); + Cursor cursor = adapter.getItem(position); long id = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID); String title = cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE); Context context = getActivity(); BookmarkUtils.displayRemoveBookmarkDialog(id, title, context, null); } - private String getUrl(int position) { - return getUrl(mAdapter.getItem(position)); + private String getUrl(BrowserBookmarksAdapter adapter, int position) { + return getUrl(adapter.getItem(position)); } /* package */ static String getUrl(Cursor c) { @@ -563,6 +559,7 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte Resources res = getActivity().getResources(); int horizontalSpacing = (int) res.getDimension(R.dimen.combo_horizontalSpacing); mGrid.setHorizontalSpacing(horizontalSpacing); + mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail); int paddingLeftRight = (int) res.getDimension(R.dimen.combo_paddingLeftRight); int paddingTop = (int) res.getDimension(R.dimen.combo_paddingTop); mRoot.setPadding(paddingLeftRight, paddingTop, @@ -578,10 +575,11 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte } void selectView(int view) { + // TODO: Support list view + view = mCurrentView; if (view == mCurrentView) { return; } - mCurrentView = view; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); Editor edit = prefs.edit(); edit.putInt(PREF_SELECTED_VIEW, mCurrentView); @@ -593,52 +591,53 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte } private void setupBookmarkView() { - mAdapter.selectView(mCurrentView); - switch (mCurrentView) { - case VIEW_THUMBNAILS: - mList.setAdapter(null); - if (mGrid.getAdapter() != mAdapter) { - mGrid.setAdapter(mAdapter); - } - mGrid.setVisibility(View.VISIBLE); - mList.setVisibility(View.GONE); - break; - case VIEW_LIST: - mGrid.setAdapter(null); - if (mList.getAdapter() != mAdapter) { - mList.setAdapter(mAdapter); - } - mGrid.setVisibility(View.GONE); - mList.setVisibility(View.VISIBLE); - break; - } + // TODO: Support list view +// mAdapter.selectView(mCurrentView); +// switch (mCurrentView) { +// case VIEW_THUMBNAILS: +// mList.setAdapter(null); +// SharedPreferences prefs = PreferenceManager +// .getDefaultSharedPreferences(getActivity()); +// String accountName = prefs.getString(PREF_ACCOUNT_NAME, null); +// mGrid.addAccount(accountName, mAdapter); +// mGrid.setVisibility(View.VISIBLE); +// mList.setVisibility(View.GONE); +// break; +// case VIEW_LIST: +// mGrid.clearAccounts(); +// if (mList.getAdapter() != mAdapter) { +// mList.setAdapter(mAdapter); +// } +// mGrid.setVisibility(View.GONE); +// mList.setVisibility(View.VISIBLE); +// break; +// } } /** * BreadCrumb controller callback */ @Override - public void onTop(int level, Object data) { + public void onTop(BreadCrumbView view, int level, Object data) { + int groupPosition = (Integer) view.getTag(R.id.group_position); Uri uri = (Uri) data; if (uri == null) { // top level uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER; } - loadFolder(uri); + loadFolder(groupPosition, uri); } /** * @param uri */ - private void loadFolder(Uri uri) { + private void loadFolder(int groupPosition, Uri uri) { LoaderManager manager = getLoaderManager(); - BookmarksLoader loader = - (BookmarksLoader) ((Loader<?>) manager.getLoader(LOADER_BOOKMARKS)); + // This assumes groups are ordered the same as loaders + BookmarksLoader loader = (BookmarksLoader) ((Loader<?>) + manager.getLoader(LOADER_BOOKMARKS + groupPosition)); loader.setUri(uri); loader.forceLoad(); - if (mCallbacks != null) { - mCallbacks.onFolderChanged(mCrumbs.getTopLevel(), uri); - } } @Override @@ -655,18 +654,9 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte } public boolean onBackPressed() { - if (canGoBack()) { - mCrumbs.popView(); - return true; - } return false; } - private boolean canGoBack() { - Crumb c = mCrumbs.getTopCrumb(); - return c != null && c.canGoBack; - } - public void setCallbackListener(BookmarksPageCallbacks callbackListener) { mCallbacks = callbackListener; } @@ -691,6 +681,14 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte } } + private BookmarkDragController mDragController = new BookmarkDragController() { + + @Override + public boolean startDrag(Cursor item) { + return canEdit(item); + } + }; + private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> { Context mContext; BookmarkItem mHeader; @@ -723,41 +721,17 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte } } - public void setBreadCrumbVisibility(int visibility) { - mCrumbVisibility = visibility; - if (mCrumbs != null) { - mCrumbs.setVisibility(mCrumbVisibility); - } - } + static class AccountsLoader extends CursorLoader { - public void setBreadCrumbUseBackButton(boolean use) { - mCrumbBackButton = use; - if (mCrumbs != null) { - mCrumbs.setUseBackButton(mCrumbBackButton); - } - } + static String[] ACCOUNTS_PROJECTION = new String[] { + Accounts.ACCOUNT_NAME, + Accounts.ACCOUNT_TYPE + }; - public void setBreadCrumbMaxVisible(int max) { - mCrumbMaxVisible = max; - if (mCrumbs != null) { - mCrumbs.setMaxVisible(mCrumbMaxVisible); + public AccountsLoader(Context context) { + super(context, Accounts.CONTENT_URI, ACCOUNTS_PROJECTION, null, null, + Accounts.ACCOUNT_NAME + " ASC"); } - } - @Override - public void onSharedPreferenceChanged( - SharedPreferences sharedPreferences, String key) { - if (PREF_ACCOUNT_NAME.equals(key) || PREF_ACCOUNT_TYPE.equals(key)) { - mCrumbs.setController(null); - mCrumbs.clear(); - LoaderManager lm = getLoaderManager(); - lm.restartLoader(LOADER_BOOKMARKS, null, this); - mCrumbs.setController(this); - String name = getString(R.string.bookmarks); - mCrumbs.pushView(name, false, BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER); - if (mCallbacks != null) { - mCallbacks.onFolderChanged(1, BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER); - } - } } } diff --git a/src/com/android/browser/BrowserHistoryPage.java b/src/com/android/browser/BrowserHistoryPage.java index 44f358d..e728254 100644 --- a/src/com/android/browser/BrowserHistoryPage.java +++ b/src/com/android/browser/BrowserHistoryPage.java @@ -59,6 +59,9 @@ import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.ExpandableListContextMenuInfo; +import android.widget.ExpandableListView.OnChildClickListener; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; @@ -68,7 +71,7 @@ import android.widget.Toast; * days of viewing. */ public class BrowserHistoryPage extends Fragment - implements LoaderCallbacks<Cursor> { + implements LoaderCallbacks<Cursor>, OnChildClickListener { static final int LOADER_HISTORY = 1; static final int LOADER_MOST_VISITED = 2; @@ -82,6 +85,7 @@ public class BrowserHistoryPage extends Fragment ListView mGroupList, mChildList; private ViewGroup mPrefsContainer; private FragmentBreadCrumbs mFragmentBreadCrumbs; + private ExpandableListView mHistoryList; // Implementation of WebIconDatabase.IconListener class IconReceiver implements IconListener { @@ -187,7 +191,7 @@ public class BrowserHistoryPage extends Fragment switch (loader.getId()) { case LOADER_HISTORY: { mAdapter.changeCursor(data); - if (!mAdapter.isEmpty() + if (!mAdapter.isEmpty() && mGroupList != null && mGroupList.getCheckedItemPosition() == ListView.INVALID_POSITION) { selectGroup(0); } @@ -229,16 +233,39 @@ public class BrowserHistoryPage extends Fragment public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mRoot = inflater.inflate(R.layout.history, container, false); + mAdapter = new HistoryAdapter(getActivity()); ViewStub stub = (ViewStub) mRoot.findViewById(R.id.pref_stub); - stub.setLayoutResource(com.android.internal.R.layout.preference_list_content); + if (stub != null) { + inflateTwoPane(stub); + } else { + inflateSinglePane(); + } + + // Start the loaders + getLoaderManager().restartLoader(LOADER_HISTORY, null, this); + getLoaderManager().restartLoader(LOADER_MOST_VISITED, null, this); + + // Register to receive icons in case they haven't all been loaded. + CombinedBookmarkHistoryView.getIconListenerSet().addListener(mIconReceiver); + return mRoot; + } + + private void inflateSinglePane() { + mHistoryList = (ExpandableListView) mRoot.findViewById(R.id.history); + mHistoryList.setAdapter(mAdapter); + mHistoryList.setOnChildClickListener(this); + registerForContextMenu(mHistoryList); + } + + private void inflateTwoPane(ViewStub stub) { + stub.setLayoutResource(R.layout.preference_list_content); stub.inflate(); mGroupList = (ListView) mRoot.findViewById(android.R.id.list); - mPrefsContainer = (ViewGroup) mRoot.findViewById(com.android.internal.R.id.prefs_frame); + mPrefsContainer = (ViewGroup) mRoot.findViewById(R.id.prefs_frame); mFragmentBreadCrumbs = (FragmentBreadCrumbs) mRoot.findViewById(android.R.id.title); mFragmentBreadCrumbs.setMaxVisible(1); mFragmentBreadCrumbs.setActivity(getActivity()); mPrefsContainer.setVisibility(View.VISIBLE); - mAdapter = new HistoryAdapter(getActivity()); mGroupList.setAdapter(new HistoryGroupWrapper(mAdapter)); mGroupList.setOnItemClickListener(mGroupItemClickListener); mGroupList.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); @@ -247,16 +274,8 @@ public class BrowserHistoryPage extends Fragment mChildList.setAdapter(mChildWrapper); mChildList.setOnItemClickListener(mChildItemClickListener); registerForContextMenu(mChildList); - ViewGroup prefs = (ViewGroup) mRoot.findViewById(com.android.internal.R.id.prefs); + ViewGroup prefs = (ViewGroup) mRoot.findViewById(R.id.prefs); prefs.addView(mChildList); - - // Start the loaders - getLoaderManager().restartLoader(LOADER_HISTORY, null, this); - getLoaderManager().restartLoader(LOADER_MOST_VISITED, null, this); - - // Register to receive icons in case they haven't all been loaded. - CombinedBookmarkHistoryView.getIconListenerSet().addListener(mIconReceiver); - return mRoot; } private OnItemClickListener mGroupItemClickListener = new OnItemClickListener() { @@ -279,6 +298,13 @@ public class BrowserHistoryPage extends Fragment }; @Override + public boolean onChildClick(ExpandableListView parent, View view, + int groupPosition, int childPosition, long id) { + mCallbacks.onUrlSelected(((HistoryItem) view).getUrl(), false); + return true; + } + + @Override public void onDestroy() { super.onDestroy(); CombinedBookmarkHistoryView.getIconListenerSet().removeListener(mIconReceiver); @@ -341,17 +367,30 @@ public class BrowserHistoryPage extends Fragment } } + View getTargetView(ContextMenuInfo menuInfo) { + if (menuInfo instanceof AdapterContextMenuInfo) { + return ((AdapterContextMenuInfo) menuInfo).targetView; + } + if (menuInfo instanceof ExpandableListContextMenuInfo) { + return ((ExpandableListContextMenuInfo) menuInfo).targetView; + } + return null; + } + @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - AdapterContextMenuInfo i = (AdapterContextMenuInfo) menuInfo; + + View targetView = getTargetView(menuInfo); + if (!(targetView instanceof HistoryItem)) { + return; + } + HistoryItem historyItem = (HistoryItem) targetView; // Inflate the menu Activity parent = getActivity(); MenuInflater inflater = parent.getMenuInflater(); inflater.inflate(R.menu.historycontext, menu); - HistoryItem historyItem = (HistoryItem) i.targetView; - // Setup the header if (mContextHeader == null) { mContextHeader = new HistoryItem(parent, false); @@ -382,12 +421,11 @@ public class BrowserHistoryPage extends Fragment @Override public boolean onContextItemSelected(MenuItem item) { - AdapterContextMenuInfo i = - (AdapterContextMenuInfo) item.getMenuInfo(); - if (i == null) { + ContextMenuInfo menuInfo = item.getMenuInfo(); + if (menuInfo == null) { return false; } - HistoryItem historyItem = (HistoryItem) i.targetView; + HistoryItem historyItem = (HistoryItem) getTargetView(menuInfo); String url = historyItem.getUrl(); String title = historyItem.getName(); Activity activity = getActivity(); @@ -417,7 +455,7 @@ public class BrowserHistoryPage extends Fragment Browser.deleteFromHistory(activity.getContentResolver(), url); return true; case R.id.homepage_context_menu_id: - BrowserSettings.getInstance().setHomePage(activity, url); + BrowserSettings.getInstance().setHomePage(url); Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show(); return true; default: @@ -636,6 +674,7 @@ public class BrowserHistoryPage extends Fragment item.getPaddingRight(), item.getPaddingBottom()); item.setFaviconBackground(mFaviconBackground); + item.startMarquee(); } else { item = (HistoryItem) convertView; } diff --git a/src/com/android/browser/BrowserPreferencesPage.java b/src/com/android/browser/BrowserPreferencesPage.java index dae838f..8302011 100644 --- a/src/com/android/browser/BrowserPreferencesPage.java +++ b/src/com/android/browser/BrowserPreferencesPage.java @@ -21,7 +21,6 @@ import com.android.browser.preferences.DebugPreferencesFragment; import android.app.ActionBar; import android.os.Bundle; import android.preference.PreferenceActivity; -import android.preference.PreferenceManager; import android.view.MenuItem; import java.util.List; @@ -48,7 +47,7 @@ public class BrowserPreferencesPage extends PreferenceActivity { public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.preference_headers, target); - if (BrowserSettings.DEV_BUILD || BrowserSettings.getInstance().showDebugSettings()) { + if (BrowserSettings.getInstance().isDebugEnabled()) { Header debug = new Header(); debug.title = getText(R.string.pref_development_title); debug.fragment = DebugPreferencesFragment.class.getName(); @@ -57,16 +56,6 @@ public class BrowserPreferencesPage extends PreferenceActivity { } @Override - protected void onPause() { - super.onPause(); - - // sync the shared preferences back to BrowserSettings - BrowserSettings.getInstance().syncSharedPreferences( - getApplicationContext(), - PreferenceManager.getDefaultSharedPreferences(this)); - } - - @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java index 9ae2a25..dd46314 100644 --- a/src/com/android/browser/BrowserSettings.java +++ b/src/com/android/browser/BrowserSettings.java @@ -1,6 +1,5 @@ - /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2011 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. @@ -18,409 +17,288 @@ package com.android.browser; import com.android.browser.homepages.HomeProvider; +import com.android.browser.provider.BrowserProvider; import com.android.browser.search.SearchEngine; import com.android.browser.search.SearchEngines; import android.app.ActivityManager; import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.content.pm.PackageManager; -import android.database.Cursor; -import android.net.Uri; -import android.os.AsyncTask; +import android.os.Build; import android.os.Message; import android.preference.PreferenceManager; import android.provider.Browser; -import android.provider.BrowserContract.Bookmarks; -import android.util.Log; import android.webkit.CookieManager; import android.webkit.GeolocationPermissions; import android.webkit.WebIconDatabase; import android.webkit.WebSettings; import android.webkit.WebSettings.AutoFillProfile; +import android.webkit.WebSettings.LayoutAlgorithm; +import android.webkit.WebSettings.PluginState; +import android.webkit.WebSettings.TextSize; +import android.webkit.WebSettings.ZoomDensity; import android.webkit.WebStorage; import android.webkit.WebView; import android.webkit.WebViewDatabase; -import java.util.HashMap; -import java.util.Observable; +import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.WeakHashMap; -/* - * Package level class for storing various WebView and Browser settings. To use - * this class: - * BrowserSettings s = BrowserSettings.getInstance(); - * s.addObserver(webView.getSettings()); - * s.loadFromDb(context); // Only needed on app startup - * s.javaScriptEnabled = true; - * ... // set any other settings - * s.update(); // this will update all the observers - * - * To remove an observer: - * s.deleteObserver(webView.getSettings()); +/** + * Class for managing settings */ -public class BrowserSettings extends Observable implements OnSharedPreferenceChangeListener { - // Private variables for settings - // NOTE: these defaults need to be kept in sync with the XML - // until the performance of PreferenceManager.setDefaultValues() - // is improved. - // Note: boolean variables are set inside reset function. - private boolean loadsImagesAutomatically; - private boolean javaScriptEnabled; - private WebSettings.PluginState pluginState; - private boolean javaScriptCanOpenWindowsAutomatically; - private boolean showSecurityWarnings; - private boolean rememberPasswords; - private boolean saveFormData; - private boolean autoFillEnabled; - private boolean openInBackground; - private String defaultTextEncodingName; - private String homeUrl = ""; - private SearchEngine searchEngine; - private boolean autoFitPage; - private boolean loadsPageInOverviewMode; - private boolean showDebugSettings; - // HTML5 API flags - private boolean appCacheEnabled; - private boolean databaseEnabled; - private boolean domStorageEnabled; - private boolean geolocationEnabled; - private boolean workersEnabled; // only affects V8. JSC does not have a similar setting - // HTML5 API configuration params - private long appCacheMaxSize = Long.MAX_VALUE; - private String appCachePath; // default value set in loadFromDb(). - private String databasePath; // default value set in loadFromDb() - private String geolocationDatabasePath; // default value set in loadFromDb() - private WebStorageSizeManager webStorageSizeManager; - - private String jsFlags = ""; - - private final static String TAG = "BrowserSettings"; - - // Development settings - public WebSettings.LayoutAlgorithm layoutAlgorithm = - WebSettings.LayoutAlgorithm.NARROW_COLUMNS; - private boolean useWideViewPort = true; - private int userAgent = 0; - private boolean tracing = false; - private boolean lightTouch = false; - private boolean navDump = false; - private boolean hardwareAccelerated = true; - private boolean showVisualIndicator = false; - // Lab settings - private boolean quickControls = false; - private boolean useMostVisitedHomepage = false; - private boolean useInstant = false; - - // By default the error console is shown once the user navigates to about:debug. - // The setting can be then toggled from the settings menu. - private boolean showConsole = true; - - // Private preconfigured values - private static int minimumFontSize = 1; - private static int minimumLogicalFontSize = 1; - private static int defaultFontSize = 16; - private static int defaultFixedFontSize = 13; - private static WebSettings.TextSize textSize = - WebSettings.TextSize.NORMAL; - private static WebSettings.ZoomDensity zoomDensity = - WebSettings.ZoomDensity.MEDIUM; - private static int pageCacheCapacity; - - - private AutoFillProfile autoFillProfile; - // Default to zero. In the case no profile is set up, the initial - // value will come from the AutoFillSettingsFragment when the user - // creates a profile. Otherwise, we'll read the ID of the last used - // profile from the prefs db. - private int autoFillActiveProfileId; - private static final int NO_AUTOFILL_PROFILE_SET = 0; - - // 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_SEARCH_ENGINE = "search_engine"; - public final static String PREF_CLEAR_FORM_DATA = - "privacy_clear_form_data"; - public final static String PREF_CLEAR_PASSWORDS = - "privacy_clear_passwords"; - 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_WEBSITE_SETTINGS = "website_settings"; - public final static String PREF_TEXT_SIZE = "text_size"; - public final static String PREF_DEFAULT_ZOOM = "default_zoom"; - public final static String PREF_DEFAULT_TEXT_ENCODING = - "default_text_encoding"; - public final static String PREF_CLEAR_GEOLOCATION_ACCESS = - "privacy_clear_geolocation_access"; - public final static String PREF_AUTOFILL_ENABLED = "autofill_enabled"; - public final static String PREF_AUTOFILL_PROFILE = "autofill_profile"; - public final static String PREF_AUTOFILL_ACTIVE_PROFILE_ID = "autofill_active_profile_id"; - public final static String PREF_HARDWARE_ACCEL = "enable_hardware_accel"; - public final static String PREF_VISUAL_INDICATOR = "enable_visual_indicator"; - public final static String PREF_USER_AGENT = "user_agent"; - - public final static String PREF_QUICK_CONTROLS = "enable_quick_controls"; - public final static String PREF_MOST_VISITED_HOMEPAGE = "use_most_visited_homepage"; - public final static String PREF_PLUGIN_STATE = "plugin_state"; - public final static String PREF_USE_INSTANT = "use_instant_search"; - - private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (Macintosh; " + - "U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/533.16 (KHTML, " + - "like Gecko) Version/5.0 Safari/533.16"; +public class BrowserSettings implements OnSharedPreferenceChangeListener, + PreferenceKeys { + + // TODO: Do something with this UserAgent stuff + private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; " + + "Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) " + + "Chrome/11.0.696.34 Safari/534.24"; private static final String IPHONE_USERAGENT = "Mozilla/5.0 (iPhone; U; " + - "CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 " + - "(KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7"; + "CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 " + + "(KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7"; private static final String IPAD_USERAGENT = "Mozilla/5.0 (iPad; U; " + - "CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 " + - "(KHTML, like Gecko) Version/4.0.4 Mobile/7B367 Safari/531.21.10"; + "CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 " + + "(KHTML, like Gecko) Version/4.0.4 Mobile/7B367 Safari/531.21.10"; private static final String FROYO_USERAGENT = "Mozilla/5.0 (Linux; U; " + - "Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 " + - "(KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"; - - // Value to truncate strings when adding them to a TextView within - // a ListView - public final static int MAX_TEXTVIEW_LEN = 80; - - public static final String RLZ_PROVIDER = "com.google.android.partnersetup.rlzappprovider"; - - public static final Uri RLZ_PROVIDER_URI = Uri.parse("content://" + RLZ_PROVIDER + "/"); - - // Set to true to enable some of the about:debug options - public static final boolean DEV_BUILD = false; - + "Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 " + + "(KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"; + + private static final String HONEYCOMB_USERAGENT = "Mozilla/5.0 (Linux; U; " + + "Android 3.1; en-us; Xoom Build/HMJ25) AppleWebKit/534.13 " + + "(KHTML, like Gecko) Version/4.0 Safari/534.13"; + + private static final String USER_AGENTS[] = { null, + DESKTOP_USERAGENT, + IPHONE_USERAGENT, + IPAD_USERAGENT, + FROYO_USERAGENT, + HONEYCOMB_USERAGENT, + }; + + private static BrowserSettings sInstance; + + private Context mContext; + private SharedPreferences mPrefs; + private LinkedList<WeakReference<WebSettings>> mManagedSettings; private Controller mController; + private WebStorageSizeManager mWebStorageSizeManager; + private AutofillHandler mAutofillHandler; + private WeakHashMap<WebSettings, String> mCustomUserAgents; - // Single instance of the BrowserSettings for use in the Browser app. - private static BrowserSettings sSingleton; - - // Private map of WebSettings to Observer objects used when deleting an - // observer. - private HashMap<WebSettings,Observer> mWebSettingsToObservers = - new HashMap<WebSettings,Observer>(); + // Cached settings + private SearchEngine mSearchEngine; - private String mRlzValue = ""; + public static void initialize(final Context context) { + sInstance = new BrowserSettings(context); + } - private boolean mLoadFromDbComplete; + public static BrowserSettings getInstance() { + return sInstance; + } - public void waitForLoadFromDbToComplete() { - synchronized (sSingleton) { - while (!mLoadFromDbComplete) { - try { - sSingleton.wait(); - } catch (InterruptedException e) { } - } + private BrowserSettings(Context context) { + mContext = context; + mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); + if (Build.VERSION.CODENAME.equals("REL")) { + // This is a release build, always startup with debug disabled + setDebugEnabled(false); } + mAutofillHandler = new AutofillHandler(mContext); + mManagedSettings = new LinkedList<WeakReference<WebSettings>>(); + mCustomUserAgents = new WeakHashMap<WebSettings, String>(); + mWebStorageSizeManager = new WebStorageSizeManager(mContext, + new WebStorageSizeManager.StatFsDiskInfo(getAppCachePath()), + new WebStorageSizeManager.WebKitAppCacheInfo(getAppCachePath())); + mPrefs.registerOnSharedPreferenceChangeListener(this); + mAutofillHandler.asyncLoadFromDb(); } - /* - * An observer wrapper for updating a WebSettings object with the new - * settings after a call to BrowserSettings.update(). - */ - static class Observer implements java.util.Observer { - // Private WebSettings object that will be updated. - private WebSettings mSettings; + public void setController(Controller controller) { + mController = controller; + syncSharedSettings(); - Observer(WebSettings w) { - mSettings = w; + if (mController != null && (mSearchEngine instanceof InstantSearchEngine)) { + ((InstantSearchEngine) mSearchEngine).setController(mController); } + } - public void update(Observable o, Object arg) { - BrowserSettings b = (BrowserSettings)o; - WebSettings s = mSettings; - - s.setLayoutAlgorithm(b.layoutAlgorithm); - if (b.userAgent == 0) { - // use the default ua string - s.setUserAgentString(null); - } else if (b.userAgent == 1) { - s.setUserAgentString(DESKTOP_USERAGENT); - } else if (b.userAgent == 2) { - s.setUserAgentString(IPHONE_USERAGENT); - } else if (b.userAgent == 3) { - s.setUserAgentString(IPAD_USERAGENT); - } else if (b.userAgent == 4) { - s.setUserAgentString(FROYO_USERAGENT); - } - s.setUseWideViewPort(b.useWideViewPort); - s.setLoadsImagesAutomatically(b.loadsImagesAutomatically); - s.setJavaScriptEnabled(b.javaScriptEnabled); - s.setShowVisualIndicator(b.showVisualIndicator); - s.setPluginState(b.pluginState); - s.setJavaScriptCanOpenWindowsAutomatically( - b.javaScriptCanOpenWindowsAutomatically); - s.setDefaultTextEncodingName(b.defaultTextEncodingName); - s.setMinimumFontSize(b.minimumFontSize); - s.setMinimumLogicalFontSize(b.minimumLogicalFontSize); - s.setDefaultFontSize(b.defaultFontSize); - s.setDefaultFixedFontSize(b.defaultFixedFontSize); - s.setNavDump(b.navDump); - s.setTextSize(b.textSize); - s.setDefaultZoom(b.zoomDensity); - s.setLightTouchEnabled(b.lightTouch); - s.setSaveFormData(b.saveFormData); - s.setAutoFillEnabled(b.autoFillEnabled); - s.setSavePassword(b.rememberPasswords); - s.setLoadWithOverviewMode(b.loadsPageInOverviewMode); - s.setPageCacheCapacity(pageCacheCapacity); - - // WebView inside Browser doesn't want initial focus to be set. - s.setNeedInitialFocus(false); - // Browser supports multiple windows - s.setSupportMultipleWindows(true); - // enable smooth transition for better performance during panning or - // zooming - s.setEnableSmoothTransition(true); - // disable content url access - s.setAllowContentAccess(false); - - // HTML5 API flags - s.setAppCacheEnabled(b.appCacheEnabled); - s.setDatabaseEnabled(b.databaseEnabled); - s.setDomStorageEnabled(b.domStorageEnabled); - s.setWorkersEnabled(b.workersEnabled); // This only affects V8. - s.setGeolocationEnabled(b.geolocationEnabled); - - // HTML5 configuration parameters. - s.setAppCacheMaxSize(b.appCacheMaxSize); - s.setAppCachePath(b.appCachePath); - s.setDatabasePath(b.databasePath); - s.setGeolocationDatabasePath(b.geolocationDatabasePath); - - // Active AutoFill profile data. - s.setAutoFillProfile(b.autoFillProfile); - - b.updateTabControlSettings(); + public void startManagingSettings(WebSettings settings) { + synchronized (mManagedSettings) { + syncStaticSettings(settings); + syncSetting(settings); + mManagedSettings.add(new WeakReference<WebSettings>(settings)); } } /** - * Load settings from the browser app's database. It is performed in - * an AsyncTask as it involves plenty of slow disk IO. - * NOTE: Strings used for the preferences must match those specified - * in the various preference XML files. - * @param ctx A Context object used to query the browser's settings - * database. If the database exists, the saved settings will be - * stored in this BrowserSettings object. This will update all - * observers of this object. + * Syncs all the settings that have a Preference UI */ - public void asyncLoadFromDb(final Context ctx) { - mLoadFromDbComplete = false; - // Run the initial settings load in an AsyncTask as it hits the - // disk multiple times through SharedPreferences and SQLite. We - // need to be certain though that this has completed before we start - // to load pages though, so in the worst case we will block waiting - // for it to finish in BrowserActivity.onCreate(). - new LoadFromDbTask(ctx).execute(); + private void syncSetting(WebSettings settings) { + settings.setGeolocationEnabled(enableGeolocation()); + settings.setJavaScriptEnabled(enableJavascript()); + settings.setLightTouchEnabled(enableLightTouch()); + settings.setNavDump(enableNavDump()); + settings.setShowVisualIndicator(enableVisualIndicator()); + settings.setDefaultTextEncodingName(getDefaultTextEncoding()); + settings.setDefaultZoom(getDefaultZoom()); + settings.setMinimumFontSize(getMinimumFontSize()); + settings.setMinimumLogicalFontSize(getMinimumFontSize()); + settings.setForceUserScalable(forceEnableUserScalable()); + settings.setPluginState(getPluginState()); + settings.setTextSize(getTextSize()); + settings.setAutoFillEnabled(isAutofillEnabled()); + settings.setLayoutAlgorithm(getLayoutAlgorithm()); + settings.setJavaScriptCanOpenWindowsAutomatically(blockPopupWindows()); + settings.setLoadsImagesAutomatically(loadImages()); + settings.setLoadWithOverviewMode(loadPageInOverviewMode()); + settings.setSavePassword(rememberPasswords()); + settings.setSaveFormData(saveFormdata()); + settings.setUseWideViewPort(isWideViewport()); + settings.setAutoFillProfile(getAutoFillProfile()); + + String ua = mCustomUserAgents.get(settings); + if (enableUseragentSwitcher() && ua != null) { + settings.setUserAgentString(ua); + } else { + settings.setUserAgentString(USER_AGENTS[getUserAgent()]); + } } - private class LoadFromDbTask extends AsyncTask<Void, Void, Void> { - private Context mContext; + /** + * Syncs all the settings that have no UI + * These cannot change, so we only need to set them once per WebSettings + */ + private void syncStaticSettings(WebSettings settings) { + settings.setDefaultFontSize(16); + settings.setDefaultFixedFontSize(13); + settings.setPageCacheCapacity(getPageCacheCapacity()); + + // WebView inside Browser doesn't want initial focus to be set. + settings.setNeedInitialFocus(false); + // Browser supports multiple windows + settings.setSupportMultipleWindows(true); + // enable smooth transition for better performance during panning or + // zooming + settings.setEnableSmoothTransition(true); + // disable content url access + settings.setAllowContentAccess(false); - public LoadFromDbTask(Context context) { - mContext = context; - } + // HTML5 API flags + settings.setAppCacheEnabled(true); + settings.setDatabaseEnabled(true); + settings.setDomStorageEnabled(true); + settings.setWorkersEnabled(true); // This only affects V8. - protected Void doInBackground(Void... unused) { - SharedPreferences p = - PreferenceManager.getDefaultSharedPreferences(mContext); - // Set the default value for the Application Caches path. - appCachePath = mContext.getDir("appcache", 0).getPath(); - // Determine the maximum size of the application cache. - webStorageSizeManager = new WebStorageSizeManager( - mContext, - new WebStorageSizeManager.StatFsDiskInfo(appCachePath), - new WebStorageSizeManager.WebKitAppCacheInfo(appCachePath)); - appCacheMaxSize = webStorageSizeManager.getAppCacheMaxSize(); - // Set the default value for the Database path. - databasePath = mContext.getDir("databases", 0).getPath(); - // Set the default value for the Geolocation database path. - geolocationDatabasePath = mContext.getDir("geolocation", 0).getPath(); - - if (p.getString(PREF_HOMEPAGE, null) == null) { - // No home page preferences is set, set it to default. - setHomePage(mContext, getFactoryResetHomeUrl(mContext)); - } + // HTML5 configuration parametersettings. + settings.setAppCacheMaxSize(mWebStorageSizeManager.getAppCacheMaxSize()); + settings.setAppCachePath(getAppCachePath()); + settings.setDatabasePath(mContext.getDir("databases", 0).getPath()); + settings.setGeolocationDatabasePath(mContext.getDir("geolocation", 0).getPath()); + } - // the cost of one cached page is ~3M (measured using nytimes.com). For - // low end devices, we only cache one page. For high end devices, we try - // to cache more pages, currently choose 5. - ActivityManager am = (ActivityManager) mContext - .getSystemService(Context.ACTIVITY_SERVICE); - if (am.getMemoryClass() > 16) { - pageCacheCapacity = 5; - } else { - pageCacheCapacity = 1; + private void syncSharedSettings() { + CookieManager.getInstance().setAcceptCookie(acceptCookies()); + if (mController != null) { + mController.setShouldShowErrorConsole(enableJavascriptConsole()); + } + } + + private void syncManagedSettings() { + syncSharedSettings(); + synchronized (mManagedSettings) { + Iterator<WeakReference<WebSettings>> iter = mManagedSettings.iterator(); + while (iter.hasNext()) { + WeakReference<WebSettings> ref = iter.next(); + WebSettings settings = ref.get(); + if (settings == null) { + iter.remove(); + continue; + } + syncSetting(settings); } + } + } - // Read the last active AutoFill profile id. - autoFillActiveProfileId = p.getInt( - PREF_AUTOFILL_ACTIVE_PROFILE_ID, autoFillActiveProfileId); - - // Load the autofill profile data from the database. We use a database separate - // to the browser preference DB to make it easier to support multiple profiles - // and switching between them. - AutoFillProfileDatabase autoFillDb = AutoFillProfileDatabase.getInstance(mContext); - Cursor c = autoFillDb.getProfile(autoFillActiveProfileId); - - if (c.getCount() > 0) { - c.moveToFirst(); - - String fullName = c.getString(c.getColumnIndex( - AutoFillProfileDatabase.Profiles.FULL_NAME)); - String email = c.getString(c.getColumnIndex( - AutoFillProfileDatabase.Profiles.EMAIL_ADDRESS)); - String company = c.getString(c.getColumnIndex( - AutoFillProfileDatabase.Profiles.COMPANY_NAME)); - String addressLine1 = c.getString(c.getColumnIndex( - AutoFillProfileDatabase.Profiles.ADDRESS_LINE_1)); - String addressLine2 = c.getString(c.getColumnIndex( - AutoFillProfileDatabase.Profiles.ADDRESS_LINE_2)); - String city = c.getString(c.getColumnIndex( - AutoFillProfileDatabase.Profiles.CITY)); - String state = c.getString(c.getColumnIndex( - AutoFillProfileDatabase.Profiles.STATE)); - String zip = c.getString(c.getColumnIndex( - AutoFillProfileDatabase.Profiles.ZIP_CODE)); - String country = c.getString(c.getColumnIndex( - AutoFillProfileDatabase.Profiles.COUNTRY)); - String phone = c.getString(c.getColumnIndex( - AutoFillProfileDatabase.Profiles.PHONE_NUMBER)); - autoFillProfile = new AutoFillProfile(autoFillActiveProfileId, - fullName, email, company, addressLine1, addressLine2, city, - state, zip, country, phone); + @Override + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, String key) { + syncManagedSettings(); + if (PREF_SEARCH_ENGINE.equals(key)) { + updateSearchEngine(false); + } + if (PREF_USE_INSTANT_SEARCH.equals(key)) { + updateSearchEngine(true); + } + if (PREF_FULLSCREEN.equals(key)) { + if (mController.getUi() != null) { + mController.getUi().setFullscreen(useFullscreen()); } - c.close(); - autoFillDb.close(); + } + } - // PreferenceManager.setDefaultValues is TOO SLOW, need to manually keep - // the defaults in sync - p.registerOnSharedPreferenceChangeListener(BrowserSettings.this); - syncSharedPreferences(mContext, p); + static String getFactoryResetHomeUrl(Context context) { + String url = context.getResources().getString(R.string.homepage_base); + if (url.indexOf("{CID}") != -1) { + url = url.replace("{CID}", + BrowserProvider.getClientId(context.getContentResolver())); + } + return url; + } - synchronized (sSingleton) { - mLoadFromDbComplete = true; - sSingleton.notify(); + public LayoutAlgorithm getLayoutAlgorithm() { + LayoutAlgorithm layoutAlgorithm = LayoutAlgorithm.NORMAL; + if (autofitPages()) { + layoutAlgorithm = LayoutAlgorithm.NARROW_COLUMNS; + } + if (isDebugEnabled()) { + if (isSmallScreen()) { + layoutAlgorithm = LayoutAlgorithm.SINGLE_COLUMN; + } else { + if (isNormalLayout()) { + layoutAlgorithm = LayoutAlgorithm.NORMAL; + } else { + layoutAlgorithm = LayoutAlgorithm.NARROW_COLUMNS; + } } - return null; } + return layoutAlgorithm; } - private void updateSearchEngine(Context ctx, String searchEngineName, boolean force) { - if (force || searchEngine == null || - !searchEngine.getName().equals(searchEngineName)) { - if (searchEngine != null) { - if (searchEngine.supportsVoiceSearch()) { + // TODO: Cache + public int getPageCacheCapacity() { + // the cost of one cached page is ~3M (measured using nytimes.com). For + // low end devices, we only cache one page. For high end devices, we try + // to cache more pages, currently choose 5. + if (ActivityManager.staticGetMemoryClass() > 16) { + return 5; + } else { + return 1; + } + } + + public WebStorageSizeManager getWebStorageSizeManager() { + return mWebStorageSizeManager; + } + + // TODO: Cache + private String getAppCachePath() { + return mContext.getDir("appcache", 0).getPath(); + } + + private void updateSearchEngine(boolean force) { + String searchEngineName = getSearchEngineName(); + if (force || mSearchEngine == null || + !mSearchEngine.getName().equals(searchEngineName)) { + if (mSearchEngine != null) { + if (mSearchEngine.supportsVoiceSearch()) { // One or more tabs could have been in voice search mode. // Clear it, since the new SearchEngine may not support // it, or may handle it differently. @@ -428,585 +306,329 @@ public class BrowserSettings extends Observable implements OnSharedPreferenceCha mController.getTabControl().getTab(i).revertVoiceSearchMode(); } } - searchEngine.close(); + mSearchEngine.close(); } - searchEngine = SearchEngines.get(ctx, searchEngineName); + mSearchEngine = SearchEngines.get(mContext, searchEngineName); - if (mController != null && (searchEngine instanceof InstantSearchEngine)) { - ((InstantSearchEngine) searchEngine).setController(mController); + if (mController != null && (mSearchEngine instanceof InstantSearchEngine)) { + ((InstantSearchEngine) mSearchEngine).setController(mController); } } } - /* package */ void syncSharedPreferences(Context ctx, SharedPreferences p) { - - homeUrl = - p.getString(PREF_HOMEPAGE, homeUrl); - - useInstant = p.getBoolean(PREF_USE_INSTANT, useInstant); - String searchEngineName = p.getString(PREF_SEARCH_ENGINE, - SearchEngine.GOOGLE); - updateSearchEngine(ctx, searchEngineName, false); - - loadsImagesAutomatically = p.getBoolean("load_images", - loadsImagesAutomatically); - javaScriptEnabled = p.getBoolean("enable_javascript", - javaScriptEnabled); - pluginState = WebSettings.PluginState.valueOf( - p.getString(PREF_PLUGIN_STATE, pluginState.name())); - javaScriptCanOpenWindowsAutomatically = !p.getBoolean( - "block_popup_windows", - !javaScriptCanOpenWindowsAutomatically); - showSecurityWarnings = p.getBoolean("show_security_warnings", - showSecurityWarnings); - rememberPasswords = p.getBoolean("remember_passwords", - rememberPasswords); - saveFormData = p.getBoolean("save_formdata", - saveFormData); - autoFillEnabled = p.getBoolean("autofill_enabled", autoFillEnabled); - boolean accept_cookies = p.getBoolean("accept_cookies", - CookieManager.getInstance().acceptCookie()); - CookieManager.getInstance().setAcceptCookie(accept_cookies); - openInBackground = p.getBoolean("open_in_background", openInBackground); - textSize = WebSettings.TextSize.valueOf( - p.getString(PREF_TEXT_SIZE, textSize.name())); - zoomDensity = WebSettings.ZoomDensity.valueOf( - p.getString(PREF_DEFAULT_ZOOM, zoomDensity.name())); - autoFitPage = p.getBoolean("autofit_pages", autoFitPage); - loadsPageInOverviewMode = p.getBoolean("load_page", - loadsPageInOverviewMode); - useWideViewPort = true; // use wide view port for either setting - if (autoFitPage) { - layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS; - } else { - layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL; - } - defaultTextEncodingName = - p.getString(PREF_DEFAULT_TEXT_ENCODING, - defaultTextEncodingName); - - 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 == - WebSettings.LayoutAlgorithm.SINGLE_COLUMN); - if (small_screen) { - layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN; - } else { - boolean normal_layout = p.getBoolean("normal_layout", - layoutAlgorithm == WebSettings.LayoutAlgorithm.NORMAL); - if (normal_layout) { - layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL; - } else { - layoutAlgorithm = - WebSettings.LayoutAlgorithm.NARROW_COLUMNS; - } - } - useWideViewPort = p.getBoolean("wide_viewport", useWideViewPort); - tracing = p.getBoolean("enable_tracing", tracing); - lightTouch = p.getBoolean("enable_light_touch", lightTouch); - navDump = p.getBoolean("enable_nav_dump", navDump); - showVisualIndicator = p.getBoolean(PREF_VISUAL_INDICATOR, showVisualIndicator); - } - - quickControls = p.getBoolean(PREF_QUICK_CONTROLS, quickControls); - useMostVisitedHomepage = p.getBoolean(PREF_MOST_VISITED_HOMEPAGE, useMostVisitedHomepage); - - // Only set these if this is a dev build or debug is enabled - if (DEV_BUILD || showDebugSettings()) { - userAgent = Integer.parseInt(p.getString(PREF_USER_AGENT, "0")); - hardwareAccelerated = p.getBoolean(PREF_HARDWARE_ACCEL, hardwareAccelerated); + public SearchEngine getSearchEngine() { + if (mSearchEngine == null) { + updateSearchEngine(false); } + return mSearchEngine; + } - // JS flags is loaded from DB even if showDebugSettings is false, - // so that it can be set once and be effective all the time. - jsFlags = p.getString("js_engine_flags", ""); - - // Read the setting for showing/hiding the JS Console always so that should the - // user enable debug settings, we already know if we should show the console. - // The user will never see the console unless they navigate to about:debug, - // regardless of the setting we read here. This setting is only used after debug - // is enabled. - showConsole = p.getBoolean("javascript_console", showConsole); - - // HTML5 API flags - appCacheEnabled = p.getBoolean("enable_appcache", appCacheEnabled); - databaseEnabled = p.getBoolean("enable_database", databaseEnabled); - domStorageEnabled = p.getBoolean("enable_domstorage", domStorageEnabled); - geolocationEnabled = p.getBoolean("enable_geolocation", geolocationEnabled); - workersEnabled = p.getBoolean("enable_workers", workersEnabled); + public boolean isDebugEnabled() { + return mPrefs.getBoolean(PREF_DEBUG_MENU, false); + } - update(); + public void setDebugEnabled(boolean value) { + mPrefs.edit().putBoolean(PREF_DEBUG_MENU, value).apply(); } - public String getHomePage() { - if (useMostVisitedHomepage) { - return HomeProvider.MOST_VISITED; + public void clearCache() { + WebIconDatabase.getInstance().removeAllIcons(); + if (mController != null) { + WebView current = mController.getCurrentWebView(); + if (current != null) { + current.clearCache(true); + } } - return homeUrl; } - public SearchEngine getSearchEngine() { - return searchEngine; + public void clearCookies() { + CookieManager.getInstance().removeAllCookie(); } - public String getJsFlags() { - return jsFlags; + public void clearHistory() { + ContentResolver resolver = mContext.getContentResolver(); + Browser.clearHistory(resolver); + Browser.clearSearches(resolver); } - public WebStorageSizeManager getWebStorageSizeManager() { - return webStorageSizeManager; + public void clearFormData() { + WebViewDatabase.getInstance(mContext).clearFormData(); + if (mController!= null) { + WebView currentTopView = mController.getCurrentTopWebView(); + if (currentTopView != null) { + currentTopView.clearFormData(); + } + } } - public void setHomePage(Context context, String url) { - Editor ed = PreferenceManager. - getDefaultSharedPreferences(context).edit(); - ed.putString(PREF_HOMEPAGE, url); - ed.apply(); - homeUrl = url; + public void clearPasswords() { + WebViewDatabase db = WebViewDatabase.getInstance(mContext); + db.clearUsernamePassword(); + db.clearHttpAuthUsernamePassword(); } - public WebSettings.TextSize getTextSize() { - return textSize; + public void clearDatabases() { + WebStorage.getInstance().deleteAllData(); } - public WebSettings.ZoomDensity getDefaultZoom() { - return zoomDensity; + public void clearLocationAccess() { + GeolocationPermissions.getInstance().clearAll(); } - public boolean openInBackground() { - return openInBackground; + public void resetDefaultPreferences() { + mPrefs.edit().clear().apply(); + syncManagedSettings(); } - public boolean showSecurityWarnings() { - return showSecurityWarnings; + public AutoFillProfile getAutoFillProfile() { + mAutofillHandler.waitForLoad(); + return mAutofillHandler.getAutoFillProfile(); } - public boolean isTracing() { - return tracing; + public void setAutoFillProfile(AutoFillProfile profile, Message msg) { + mAutofillHandler.waitForLoad(); + mAutofillHandler.setAutoFillProfile(profile, msg); } - public boolean isLightTouch() { - return lightTouch; + public void toggleDebugSettings() { + setDebugEnabled(!isDebugEnabled()); } - public boolean isNavDump() { - return navDump; + public boolean hasDesktopUseragent(WebView view) { + return view != null && mCustomUserAgents.get(view.getSettings()) != null; } - public boolean isHardwareAccelerated() { - return hardwareAccelerated; + public void toggleDesktopUseragent(WebView view) { + WebSettings settings = view.getSettings(); + if (mCustomUserAgents.get(settings) != null) { + mCustomUserAgents.remove(settings); + settings.setUserAgentString(USER_AGENTS[getUserAgent()]); + } else { + mCustomUserAgents.put(settings, DESKTOP_USERAGENT); + settings.setUserAgentString(DESKTOP_USERAGENT); + } } - public boolean showVisualIndicator() { - return showVisualIndicator; + // ----------------------------- + // getter/setters for accessibility_preferences.xml + // ----------------------------- + + // TODO: Cache + public TextSize getTextSize() { + String textSize = mPrefs.getString(PREF_TEXT_SIZE, "NORMAL"); + return TextSize.valueOf(textSize); } - public boolean useQuickControls() { - return quickControls; + public int getMinimumFontSize() { + return mPrefs.getInt(PREF_MIN_FONT_SIZE, 1); } - public boolean useMostVisitedHomepage() { - return useMostVisitedHomepage; + public boolean forceEnableUserScalable() { + return mPrefs.getBoolean(PREF_FORCE_USERSCALABLE, false); } - public boolean useInstant() { - return useInstant; + // ----------------------------- + // getter/setters for advanced_preferences.xml + // ----------------------------- + + public String getSearchEngineName() { + return mPrefs.getString(PREF_SEARCH_ENGINE, SearchEngine.GOOGLE); } - public boolean showDebugSettings() { - return showDebugSettings; + public boolean openInBackground() { + return mPrefs.getBoolean(PREF_OPEN_IN_BACKGROUND, false); } - public void toggleDebugSettings(Context context) { - showDebugSettings = !showDebugSettings; - navDump = showDebugSettings; - syncSharedPreferences(context, - PreferenceManager.getDefaultSharedPreferences(context)); - update(); + public boolean enableJavascript() { + return mPrefs.getBoolean(PREF_ENABLE_JAVASCRIPT, true); } - public void setAutoFillProfile(Context ctx, AutoFillProfile profile, Message msg) { - if (profile != null) { - setActiveAutoFillProfileId(ctx, profile.getUniqueId()); - // Update the AutoFill DB with the new profile. - new SaveProfileToDbTask(ctx, msg).execute(profile); - } else { - // Delete the current profile. - if (autoFillProfile != null) { - new DeleteProfileFromDbTask(ctx, msg).execute(autoFillProfile.getUniqueId()); - setActiveAutoFillProfileId(ctx, NO_AUTOFILL_PROFILE_SET); - } - } - autoFillProfile = profile; + // TODO: Cache + public PluginState getPluginState() { + String state = mPrefs.getString(PREF_PLUGIN_STATE, "ON"); + return PluginState.valueOf(state); } - public AutoFillProfile getAutoFillProfile() { - return autoFillProfile; + // TODO: Cache + public ZoomDensity getDefaultZoom() { + String zoom = mPrefs.getString(PREF_DEFAULT_ZOOM, "MEDIUM"); + return ZoomDensity.valueOf(zoom); } - private void setActiveAutoFillProfileId(Context context, int activeProfileId) { - autoFillActiveProfileId = activeProfileId; - Editor ed = PreferenceManager. - getDefaultSharedPreferences(context).edit(); - ed.putInt(PREF_AUTOFILL_ACTIVE_PROFILE_ID, activeProfileId); - ed.apply(); + public boolean loadPageInOverviewMode() { + return mPrefs.getBoolean(PREF_LOAD_PAGE, true); } - /* package */ void disableAutoFill(Context ctx) { - autoFillEnabled = false; - Editor ed = PreferenceManager.getDefaultSharedPreferences(ctx).edit(); - ed.putBoolean(PREF_AUTOFILL_ENABLED, false); - ed.apply(); + public boolean autofitPages() { + return mPrefs.getBoolean(PREF_AUTOFIT_PAGES, true); } - /** - * 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. - */ - public Observer addObserver(WebSettings s) { - Observer old = mWebSettingsToObservers.get(s); - if (old != null) { - super.deleteObserver(old); - } - Observer o = new Observer(s); - mWebSettingsToObservers.put(s, o); - super.addObserver(o); - return o; + public boolean blockPopupWindows() { + return mPrefs.getBoolean(PREF_BLOCK_POPUP_WINDOWS, true); } - /** - * Delete the given WebSettings observer from the list of observers. - * @param s The WebSettings object to be deleted. - */ - public void deleteObserver(WebSettings s) { - Observer o = mWebSettingsToObservers.get(s); - if (o != null) { - mWebSettingsToObservers.remove(s); - super.deleteObserver(o); - } + public boolean loadImages() { + return mPrefs.getBoolean(PREF_LOAD_IMAGES, true); } - /* - * Application level method for obtaining a single app instance of the - * BrowserSettings. - */ - public static BrowserSettings getInstance() { - if (sSingleton == null ) { - sSingleton = new BrowserSettings(); - } - return sSingleton; + public String getDefaultTextEncoding() { + return mPrefs.getString(PREF_DEFAULT_TEXT_ENCODING, null); } - /* - * Package level method for associating the BrowserSettings with TabControl - */ - /* package */void setController(Controller ctrl) { - mController = ctrl; - updateTabControlSettings(); + // ----------------------------- + // getter/setters for general_preferences.xml + // ----------------------------- - if (mController != null && (searchEngine instanceof InstantSearchEngine)) { - ((InstantSearchEngine) searchEngine).setController(mController); + public String getHomePage() { + if (useMostVisitedHomepage()) { + return HomeProvider.MOST_VISITED; } + return mPrefs.getString(PREF_HOMEPAGE, getFactoryResetHomeUrl(mContext)); } - /* - * Update all the observers of the object. - */ - /*package*/ void update() { - setChanged(); - notifyObservers(); + public void setHomePage(String value) { + mPrefs.edit().putString(PREF_HOMEPAGE, value).apply(); } - /*package*/ void clearCache(Context context) { - WebIconDatabase.getInstance().removeAllIcons(); - if (mController != null) { - WebView current = mController.getCurrentWebView(); - if (current != null) { - current.clearCache(true); - } - } + public boolean isAutofillEnabled() { + return mPrefs.getBoolean(PREF_AUTOFILL_ENABLED, true); } - /*package*/ void clearCookies(Context context) { - CookieManager.getInstance().removeAllCookie(); + public void setAutofillEnabled(boolean value) { + mPrefs.edit().putBoolean(PREF_AUTOFILL_ENABLED, value).apply(); } - /* package */void clearHistory(Context context) { - ContentResolver resolver = context.getContentResolver(); - Browser.clearHistory(resolver); - Browser.clearSearches(resolver); - } + // ----------------------------- + // getter/setters for debug_preferences.xml + // ----------------------------- - /* package */ void clearFormData(Context context) { - WebViewDatabase.getInstance(context).clearFormData(); - if (mController!= null) { - WebView currentTopView = mController.getCurrentTopWebView(); - if (currentTopView != null) { - currentTopView.clearFormData(); - } + public boolean isHardwareAccelerated() { + if (!isDebugEnabled()) { + return true; } + return mPrefs.getBoolean(PREF_ENABLE_HARDWARE_ACCEL, true); } - /*package*/ void clearPasswords(Context context) { - WebViewDatabase db = WebViewDatabase.getInstance(context); - db.clearUsernamePassword(); - db.clearHttpAuthUsernamePassword(); + public int getUserAgent() { + if (!isDebugEnabled()) { + return 0; + } + return Integer.parseInt(mPrefs.getString(PREF_USER_AGENT, "0")); } - private void updateTabControlSettings() { - // Enable/disable the error console. - mController.setShouldShowErrorConsole( - showDebugSettings && showConsole); - } + // ----------------------------- + // getter/setters for hidden_debug_preferences.xml + // ----------------------------- - /*package*/ void clearDatabases(Context context) { - WebStorage.getInstance().deleteAllData(); + public boolean enableVisualIndicator() { + if (!isDebugEnabled()) { + return false; + } + return mPrefs.getBoolean(PREF_ENABLE_VISUAL_INDICATOR, false); } - /*package*/ void clearLocationAccess(Context context) { - GeolocationPermissions.getInstance().clearAll(); + public boolean enableJavascriptConsole() { + if (!isDebugEnabled()) { + return false; + } + return mPrefs.getBoolean(PREF_JAVASCRIPT_CONSOLE, true); } - /*package*/ void resetDefaultPreferences(Context ctx) { - reset(); - SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(ctx); - p.edit().clear().apply(); - PreferenceManager.setDefaultValues(ctx, R.xml.general_preferences, true); - PreferenceManager.setDefaultValues(ctx, R.xml.privacy_security_preferences, true); - PreferenceManager.setDefaultValues(ctx, R.xml.advanced_preferences, true); - // reset homeUrl - setHomePage(ctx, getFactoryResetHomeUrl(ctx)); - // reset appcache max size - appCacheMaxSize = webStorageSizeManager.getAppCacheMaxSize(); - setActiveAutoFillProfileId(ctx, NO_AUTOFILL_PROFILE_SET); + public boolean isSmallScreen() { + if (!isDebugEnabled()) { + return false; + } + return mPrefs.getBoolean(PREF_SMALL_SCREEN, false); } - /*package*/ static String getFactoryResetHomeUrl(Context context) { - String url = context.getResources().getString(R.string.homepage_base); - if (url.indexOf("{CID}") != -1) { - url = url.replace("{CID}", - BrowserProvider.getClientId(context.getContentResolver())); + public boolean isWideViewport() { + if (!isDebugEnabled()) { + return true; } - return url; + return mPrefs.getBoolean(PREF_WIDE_VIEWPORT, true); } - // Private constructor that does nothing. - private BrowserSettings() { - reset(); - } - - private void reset() { - // Private variables for settings - // NOTE: these defaults need to be kept in sync with the XML - // until the performance of PreferenceManager.setDefaultValues() - // is improved. - loadsImagesAutomatically = true; - javaScriptEnabled = true; - pluginState = WebSettings.PluginState.ON; - javaScriptCanOpenWindowsAutomatically = false; - showSecurityWarnings = true; - rememberPasswords = true; - saveFormData = true; - autoFillEnabled = true; - openInBackground = false; - autoFitPage = true; - loadsPageInOverviewMode = true; - showDebugSettings = false; - // HTML5 API flags - appCacheEnabled = true; - databaseEnabled = true; - domStorageEnabled = true; - geolocationEnabled = true; - workersEnabled = true; // only affects V8. JSC does not have a similar setting + public boolean isNormalLayout() { + if (!isDebugEnabled()) { + return false; + } + return mPrefs.getBoolean(PREF_NORMAL_LAYOUT, false); } - private abstract class AutoFillProfileDbTask<T> extends AsyncTask<T, Void, Void> { - Context mContext; - AutoFillProfileDatabase mAutoFillProfileDb; - Message mCompleteMessage; - - public AutoFillProfileDbTask(Context ctx, Message msg) { - mContext = ctx; - mCompleteMessage = msg; + public boolean isTracing() { + if (!isDebugEnabled()) { + return false; } + return mPrefs.getBoolean(PREF_ENABLE_TRACING, false); + } - protected void onPostExecute(Void result) { - if (mCompleteMessage != null) { - mCompleteMessage.sendToTarget(); - } - mAutoFillProfileDb.close(); + public boolean enableLightTouch() { + if (!isDebugEnabled()) { + return false; } - - abstract protected Void doInBackground(T... values); + return mPrefs.getBoolean(PREF_ENABLE_LIGHT_TOUCH, false); } - - private class SaveProfileToDbTask extends AutoFillProfileDbTask<AutoFillProfile> { - public SaveProfileToDbTask(Context ctx, Message msg) { - super(ctx, msg); + public boolean enableNavDump() { + if (!isDebugEnabled()) { + return false; } + return mPrefs.getBoolean(PREF_ENABLE_NAV_DUMP, false); + } - protected Void doInBackground(AutoFillProfile... values) { - mAutoFillProfileDb = AutoFillProfileDatabase.getInstance(mContext); - assert autoFillActiveProfileId != NO_AUTOFILL_PROFILE_SET; - AutoFillProfile newProfile = values[0]; - mAutoFillProfileDb.addOrUpdateProfile(autoFillActiveProfileId, newProfile); - return null; + public String getJsEngineFlags() { + if (!isDebugEnabled()) { + return ""; } + return mPrefs.getString(PREF_JS_ENGINE_FLAGS, ""); } - private class DeleteProfileFromDbTask extends AutoFillProfileDbTask<Integer> { - public DeleteProfileFromDbTask(Context ctx, Message msg) { - super(ctx, msg); - } + // ----------------------------- + // getter/setters for lab_preferences.xml + // ----------------------------- - protected Void doInBackground(Integer... values) { - mAutoFillProfileDb = AutoFillProfileDatabase.getInstance(mContext); - int id = values[0]; - assert id > 0; - mAutoFillProfileDb.dropProfile(id); - return null; - } + public boolean useQuickControls() { + return mPrefs.getBoolean(PREF_ENABLE_QUICK_CONTROLS, false); } - @Override - public void onSharedPreferenceChanged( - SharedPreferences p, String key) { - if (PREF_HARDWARE_ACCEL.equals(key)) { - hardwareAccelerated = p.getBoolean(PREF_HARDWARE_ACCEL, hardwareAccelerated); - } else if (PREF_VISUAL_INDICATOR.equals(key)) { - showVisualIndicator = p.getBoolean(PREF_VISUAL_INDICATOR, showVisualIndicator); - } else if (PREF_USER_AGENT.equals(key)) { - userAgent = Integer.parseInt(p.getString(PREF_USER_AGENT, "0")); - update(); - } else if (PREF_QUICK_CONTROLS.equals(key)) { - quickControls = p.getBoolean(PREF_QUICK_CONTROLS, quickControls); - } else if (PREF_MOST_VISITED_HOMEPAGE.equals(key)) { - useMostVisitedHomepage = p.getBoolean(PREF_MOST_VISITED_HOMEPAGE, useMostVisitedHomepage); - } else if (PREF_USE_INSTANT.equals(key)) { - useInstant = p.getBoolean(PREF_USE_INSTANT, useInstant); - updateSearchEngine(mController.getActivity(), SearchEngine.GOOGLE, true); - } else if (PREF_SEARCH_ENGINE.equals(key)) { - final String searchEngineName = p.getString(PREF_SEARCH_ENGINE, - SearchEngine.GOOGLE); - updateSearchEngine(mController.getActivity(), searchEngineName, false); - } + public boolean useMostVisitedHomepage() { + return mPrefs.getBoolean(PREF_USE_MOST_VISITED_HOMEPAGE, false); } - /*package*/ String getRlzValue() { - return mRlzValue; + public boolean useInstantSearch() { + return mPrefs.getBoolean(PREF_USE_INSTANT_SEARCH, false); } - /*package*/ void updateRlzValues(Context context) { - // Use AsyncTask because this queries both RlzProvider and Bookmarks URIs - new RlzUpdateTask(context).execute(); + public boolean useFullscreen() { + return mPrefs.getBoolean(PREF_FULLSCREEN, false); } - private class RlzUpdateTask extends AsyncTask<Void, Void, Void> { - private final Context context; + public boolean enableUseragentSwitcher() { + return mPrefs.getBoolean(PREF_ENABLE_USERAGENT_SWITCHER, false); + } - public RlzUpdateTask(Context context) { - this.context = context; - } + // ----------------------------- + // getter/setters for privacy_security_preferences.xml + // ----------------------------- - @Override - protected Void doInBackground(Void...unused) { - String rlz = retrieveRlzValue(context); - if (!rlz.isEmpty()) { - mRlzValue = rlz; - updateHomePageRlzParameter(context); - updateBookmarksRlzParameter(context); - } - return null; - } + public boolean showSecurityWarnings() { + return mPrefs.getBoolean(PREF_SHOW_SECURITY_WARNINGS, true); } - // Update RLZ value if present in Home page - private void updateHomePageRlzParameter(Context context) { - Uri uri = Uri.parse(homeUrl); - if ((uri.getQueryParameter("rlz") != null) && UrlUtils.isGoogleUri(uri)) { - String newHomeUrl = updateRlzParameter(homeUrl); - if (!homeUrl.equals(newHomeUrl)) { - setHomePage(context, newHomeUrl); - } - } + public boolean acceptCookies() { + return mPrefs.getBoolean(PREF_ACCEPT_COOKIES, true); } - // Update RLZ value if present in bookmarks - private void updateBookmarksRlzParameter(Context context) { - Cursor cur = null; - try { - cur = context.getContentResolver().query(Bookmarks.CONTENT_URI_DEFAULT_FOLDER, - new String[] { Bookmarks._ID, Bookmarks.URL }, "url LIKE '%rlz=%'", null, null); - if ((cur == null) || (cur.getCount() == 0)) { - return; - } - for (cur.moveToFirst(); !cur.isAfterLast(); cur.moveToNext()) { - long id = cur.getLong(0); - String url = cur.getString(1); - if ((url == null) || url.isEmpty()) { - continue; - } - - Uri uri = Uri.parse(url); - if ((uri.getQueryParameter("rlz") != null) && UrlUtils.isGoogleUri(uri)) { - String newUrl = updateRlzParameter(url); - if (!url.equals(newUrl)) { - ContentValues values = new ContentValues(); - values.put(Bookmarks.URL, newUrl); - Uri bookmarkUri = ContentUris.withAppendedId( - BookmarkUtils.getBookmarksUri(context), id); - context.getContentResolver().update(bookmarkUri, values, null, null); - } - } - } - } finally { - if (cur != null) { - cur.close(); - } - } + public boolean saveFormdata() { + return mPrefs.getBoolean(PREF_SAVE_FORMDATA, true); } - private String updateRlzParameter(String url) { - Uri uri = Uri.parse(url); - String oldRlz = uri.getQueryParameter("rlz"); - if (oldRlz != null) { - return url.replace("rlz=" + oldRlz, "rlz=" + mRlzValue); - } - return url; + public boolean enableGeolocation() { + return mPrefs.getBoolean(PREF_ENABLE_GEOLOCATION, true); } - // Retrieve the RLZ value from the Rlz Provider - private static String retrieveRlzValue(Context context) { - String rlz = ""; - PackageManager pm = context.getPackageManager(); - if (pm.resolveContentProvider(RLZ_PROVIDER, 0) == null) { - return rlz; - } - - String ap = context.getResources().getString(R.string.rlz_access_point); - if (ap.isEmpty()) { - return rlz; - } - - Uri rlzUri = Uri.withAppendedPath(RLZ_PROVIDER_URI, ap); - Cursor cur = null; - try { - cur = context.getContentResolver().query(rlzUri, null, null, null, null); - if (cur != null && cur.moveToFirst() && !cur.isNull(0)) { - rlz = cur.getString(0); - } - } finally { - if (cur != null) { - cur.close(); - } - } - return rlz; + public boolean rememberPasswords() { + return mPrefs.getBoolean(PREF_REMEMBER_PASSWORDS, true); } + } diff --git a/src/com/android/browser/ScrollWebView.java b/src/com/android/browser/BrowserWebView.java index 8c89e51..a1d8c2d 100644 --- a/src/com/android/browser/ScrollWebView.java +++ b/src/com/android/browser/BrowserWebView.java @@ -18,6 +18,7 @@ package com.android.browser; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.Canvas; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; @@ -28,14 +29,15 @@ import java.util.Map; /** * Manage WebView scroll events */ -public class ScrollWebView extends WebView implements Runnable { +public class BrowserWebView extends WebView implements Runnable { private ScrollListener mScrollListener; private boolean mIsCancelled; private boolean mBackgroundRemoved = false; private boolean mUserInitiated = false; private TitleBarBase mTitleBar; - private Bitmap mBitmap; + private int mCaptureSize; + private Bitmap mCapture; /** * @param context @@ -43,7 +45,7 @@ public class ScrollWebView extends WebView implements Runnable { * @param defStyle * @param javascriptInterfaces */ - public ScrollWebView(Context context, AttributeSet attrs, int defStyle, + public BrowserWebView(Context context, AttributeSet attrs, int defStyle, Map<String, Object> javascriptInterfaces, boolean privateBrowsing) { super(context, attrs, defStyle, javascriptInterfaces, privateBrowsing); } @@ -53,24 +55,33 @@ public class ScrollWebView extends WebView implements Runnable { * @param attrs * @param defStyle */ - public ScrollWebView( + public BrowserWebView( Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing) { super(context, attrs, defStyle, privateBrowsing); + init(); } /** * @param context * @param attrs */ - public ScrollWebView(Context context, AttributeSet attrs) { + public BrowserWebView(Context context, AttributeSet attrs) { super(context, attrs); + init(); } /** * @param context */ - public ScrollWebView(Context context) { + public BrowserWebView(Context context) { super(context); + init(); + } + + private void init() { + mCaptureSize = mContext.getResources().getDimensionPixelSize(R.dimen.tab_capture_size); + mCapture = Bitmap.createBitmap(mCaptureSize, mCaptureSize, + Bitmap.Config.RGB_565); } @Override @@ -140,6 +151,18 @@ public class ScrollWebView extends WebView implements Runnable { public void onScroll(int visibleTitleHeight, boolean userInitiated); } + protected Bitmap capture() { + if (mCapture == null) return null; + Canvas c = new Canvas(mCapture); + final int left = getScrollX(); + final int top = getScrollY() + getVisibleTitleHeight(); + c.translate(-left, -top); + float scale = mCaptureSize / (float) Math.max(getWidth(), getHeight()); + c.scale(scale, scale, left, top); + onDraw(c); + return mCapture; + } + @Override protected void onDraw(android.graphics.Canvas c) { super.onDraw(c); diff --git a/src/com/android/browser/BrowserYesNoPreference.java b/src/com/android/browser/BrowserYesNoPreference.java index caea092..d5d5205 100644 --- a/src/com/android/browser/BrowserYesNoPreference.java +++ b/src/com/android/browser/BrowserYesNoPreference.java @@ -35,25 +35,25 @@ class BrowserYesNoPreference extends YesNoPreference { if (positiveResult) { setEnabled(false); - Context context = getContext(); - if (BrowserSettings.PREF_CLEAR_CACHE.equals(getKey())) { - BrowserSettings.getInstance().clearCache(context); - BrowserSettings.getInstance().clearDatabases(context); - } else if (BrowserSettings.PREF_CLEAR_COOKIES.equals(getKey())) { - BrowserSettings.getInstance().clearCookies(context); - } else if (BrowserSettings.PREF_CLEAR_HISTORY.equals(getKey())) { - BrowserSettings.getInstance().clearHistory(context); - } else if (BrowserSettings.PREF_CLEAR_FORM_DATA.equals(getKey())) { - BrowserSettings.getInstance().clearFormData(context); - } else if (BrowserSettings.PREF_CLEAR_PASSWORDS.equals(getKey())) { - BrowserSettings.getInstance().clearPasswords(context); - } else if (BrowserSettings.PREF_EXTRAS_RESET_DEFAULTS.equals( + BrowserSettings settings = BrowserSettings.getInstance(); + if (PreferenceKeys.PREF_PRIVACY_CLEAR_CACHE.equals(getKey())) { + settings.clearCache(); + settings.clearDatabases(); + } else if (PreferenceKeys.PREF_PRIVACY_CLEAR_COOKIES.equals(getKey())) { + settings.clearCookies(); + } else if (PreferenceKeys.PREF_PRIVACY_CLEAR_HISTORY.equals(getKey())) { + settings.clearHistory(); + } else if (PreferenceKeys.PREF_PRIVACY_CLEAR_FORM_DATA.equals(getKey())) { + settings.clearFormData(); + } else if (PreferenceKeys.PREF_PRIVACY_CLEAR_PASSWORDS.equals(getKey())) { + settings.clearPasswords(); + } else if (PreferenceKeys.PREF_RESET_DEFAULT_PREFERENCES.equals( getKey())) { - BrowserSettings.getInstance().resetDefaultPreferences(context); + settings.resetDefaultPreferences(); setEnabled(true); - } else if (BrowserSettings.PREF_CLEAR_GEOLOCATION_ACCESS.equals( + } else if (PreferenceKeys.PREF_PRIVACY_CLEAR_GEOLOCATION_ACCESS.equals( getKey())) { - BrowserSettings.getInstance().clearLocationAccess(context); + settings.clearLocationAccess(); } } } diff --git a/src/com/android/browser/CombinedBookmarkHistoryView.java b/src/com/android/browser/CombinedBookmarkHistoryView.java index febf75b..1fdc260 100644 --- a/src/com/android/browser/CombinedBookmarkHistoryView.java +++ b/src/com/android/browser/CombinedBookmarkHistoryView.java @@ -204,27 +204,11 @@ public class CombinedBookmarkHistoryView extends LinearLayout mUiController.onUrlSelected(BrowserBookmarksPage.getUrl(c), false); return true; } - - @Override - public void onFolderChanged(int level, Uri uri) { - final int toggleFlags = ActionBar.DISPLAY_SHOW_CUSTOM - | ActionBar.DISPLAY_HOME_AS_UP; - // 1 is "bookmarks" root folder - if (level <= 1) { - mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - mActionBar.setDisplayOptions(0, toggleFlags); - } else { - mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); - mActionBar.setDisplayOptions(toggleFlags, toggleFlags); - } - } }; private void initFragments(Bundle extras) { mBookmarks = BrowserBookmarksPage.newInstance(mBookmarkCallbackWrapper, extras, mBookmarksHeader); - mBookmarks.setBreadCrumbMaxVisible(2); - mBookmarks.setBreadCrumbUseBackButton(false); mHistory = BrowserHistoryPage.newInstance(mUiController, extras); } diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java index dcb62a6..06f00c7 100644 --- a/src/com/android/browser/Controller.java +++ b/src/com/android/browser/Controller.java @@ -18,6 +18,7 @@ package com.android.browser; import com.android.browser.IntentHandler.UrlData; import com.android.browser.UI.DropdownChangeListener; +import com.android.browser.provider.BrowserProvider; import com.android.browser.search.SearchEngine; import com.android.common.Search; @@ -56,7 +57,6 @@ import android.provider.BrowserContract.Images; import android.provider.ContactsContract; import android.provider.ContactsContract.Intents.Insert; import android.speech.RecognizerIntent; -import android.speech.RecognizerResultsIntent; import android.text.TextUtils; import android.util.Log; import android.util.Patterns; @@ -80,6 +80,7 @@ import android.webkit.WebIconDatabase; import android.webkit.WebSettings; import android.webkit.WebView; import android.widget.Toast; + import java.io.ByteArrayOutputStream; import java.io.File; import java.net.URLEncoder; @@ -96,6 +97,7 @@ public class Controller private static final String LOGTAG = "Controller"; private static final String SEND_APP_ID_EXTRA = "android.speech.extras.SEND_APPLICATION_ID_EXTRA"; + private static final String INCOGNITO_URI = "browser:incognito"; // public message ids @@ -132,6 +134,9 @@ public class Controller // "source" parameter for Google search through simplily type final static String GOOGLE_SEARCH_SOURCE_TYPE = "browser-type"; + // "no-crash-recovery" parameter in intetnt to suppress crash recovery + final static String NO_CRASH_RECOVERY = "no-crash-recovery"; + private Activity mActivity; private UI mUi; private TabControl mTabControl; @@ -197,6 +202,7 @@ public class Controller // Tabs' notion of whether they represent bookmarked sites. private ContentObserver mBookmarksObserver; private DataController mDataController; + private CrashRecoveryHandler mCrashRecoveryHandler; private static class ClearThumbnails extends AsyncTask<File, Void, Void> { @Override @@ -218,7 +224,7 @@ public class Controller mDataController = DataController.getInstance(mActivity); mTabControl = new TabControl(this); mSettings.setController(this); - mSettings.updateRlzValues(mActivity); + mCrashRecoveryHandler = new CrashRecoveryHandler(this); mUrlHandler = new UrlHandler(this); mIntentHandler = new IntentHandler(mActivity, this); @@ -252,6 +258,16 @@ public class Controller } void start(final Bundle icicle, final Intent intent) { + boolean noCrashRecovery = intent.getBooleanExtra(NO_CRASH_RECOVERY, false); + if (icicle != null || noCrashRecovery) { + mCrashRecoveryHandler.clearState(); + doStart(icicle, intent); + } else { + mCrashRecoveryHandler.startRecovery(intent); + } + } + + void doStart(final Bundle icicle, final Intent intent) { // Unless the last browser usage was within 24 hours, destroy any // remaining incognito tabs. @@ -266,10 +282,10 @@ public class Controller || lastActiveDate.after(today)); // Find out if we will restore any state and remember the tab. - final int currentTab = + final long currentTabId = mTabControl.canRestoreState(icicle, restoreIncognitoTabs); - if (currentTab == -1) { + if (currentTabId == -1) { // Not able to restore so we go ahead and clear session cookies. We // must do this before trying to login the user as we don't want to // clear any session cookies set during login. @@ -279,31 +295,29 @@ public class Controller GoogleAccountLogin.startLoginIfNeeded(mActivity, new Runnable() { @Override public void run() { - start(icicle, intent, currentTab, restoreIncognitoTabs); + onPreloginFinished(icicle, intent, currentTabId, restoreIncognitoTabs); } }); } - private void start(Bundle icicle, Intent intent, int currentTab, + private void onPreloginFinished(Bundle icicle, Intent intent, long currentTabId, boolean restoreIncognitoTabs) { - if (currentTab == -1) { + if (currentTabId == -1) { final Bundle extra = intent.getExtras(); // Create an initial tab. // 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. UrlData urlData = mIntentHandler.getUrlDataFromIntent(intent); - - String action = intent.getAction(); - final Tab t = mTabControl.createNewTab( - (Intent.ACTION_VIEW.equals(action) && - intent.getData() != null) - || RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS - .equals(action), - intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), - urlData.mUrl, false); - addTab(t); - setActiveTab(t); + Tab t = null; + if (urlData.isEmpty()) { + t = openTabToHomePage(); + } else { + t = openTab(urlData); + } + if (t != null) { + t.setAppId(intent.getStringExtra(Browser.EXTRA_APPLICATION_ID)); + } WebView webView = t.getWebView(); if (extra != null) { int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0); @@ -311,17 +325,8 @@ public class Controller webView.setInitialScale(scale); } } - - if (urlData.isEmpty()) { - loadUrl(webView, mSettings.getHomePage()); - } else { - // monkey protection against delayed start - if (t != null) { - loadUrlDataIn(t, urlData); - } - } } else { - mTabControl.restoreState(icicle, currentTab, restoreIncognitoTabs, + mTabControl.restoreState(icicle, currentTabId, restoreIncognitoTabs, mUi.needsRestoreAllTabs()); mUi.updateTabs(mTabControl.getTabs()); // TabControl.restoreState() will create a new tab even if @@ -334,7 +339,7 @@ public class Controller new ClearThumbnails().execute(mTabControl.getThumbnailDir() .listFiles()); // Read JavaScript flags if it exists. - String jsFlags = getSettings().getJsFlags(); + String jsFlags = getSettings().getJsEngineFlags(); if (jsFlags.trim().length() != 0) { getCurrentWebView().setJsFlags(jsFlags); } @@ -475,7 +480,12 @@ public class Controller break; case R.id.open_newtab_context_menu_id: final Tab parent = mTabControl.getCurrentTab(); - final Tab newTab = openTab(parent, url, false); + final Tab newTab = + openTab(url, + (parent != null) + && parent.isPrivateBrowsingEnabled(), + !mSettings.openInBackground(), + true); if (newTab != null && newTab != parent) { parent.addChildTab(newTab); } @@ -610,6 +620,7 @@ public class Controller mNetworkHandler.onPause(); WebView.disablePlatformNotifications(); + mCrashRecoveryHandler.clearState(); } void onSaveInstanceState(Bundle outState) { @@ -911,6 +922,11 @@ public class Controller } mDataController.updateVisitedHistory(url); WebIconDatabase.getInstance().retainIconForPageUrl(url); + if (!mActivityPaused) { + // Since we clear the state in onPause, don't backup the current + // state if we are already paused + mCrashRecoveryHandler.backupState(); + } } @Override @@ -1056,14 +1072,20 @@ public class Controller mActivity.startActivity(intent); } - public void activateVoiceSearchMode(String title) { - mUi.showVoiceTitleBar(title); + @Override + public void activateVoiceSearchMode(String title, List<String> results) { + mUi.showVoiceTitleBar(title, results); } public void revertVoiceSearchMode(Tab tab) { mUi.revertVoiceTitleBar(tab); } + public boolean supportsVoiceSearch() { + SearchEngine searchEngine = getSettings().getSearchEngine(); + return (searchEngine != null && searchEngine.supportsVoiceSearch()); + } + public void showCustomView(Tab tab, View view, WebChromeClient.CustomViewCallback callback) { if (tab.inForeground()) { @@ -1098,7 +1120,7 @@ public class Controller case PREFERENCES_PAGE: if (resultCode == Activity.RESULT_OK && intent != null) { String action = intent.getStringExtra(Intent.EXTRA_TEXT); - if (BrowserSettings.PREF_CLEAR_HISTORY.equals(action)) { + if (PreferenceKeys.PREF_PRIVACY_CLEAR_HISTORY.equals(action)) { mTabControl.removeParentChildRelationShips(); } } @@ -1161,7 +1183,11 @@ public class Controller removeComboView(); if (!TextUtils.isEmpty(url)) { if (newTab) { - openTab(mTabControl.getCurrentTab(), url, false); + final Tab parent = mTabControl.getCurrentTab(); + openTab(url, + (parent != null) && parent.isPrivateBrowsingEnabled(), + !mSettings.openInBackground(), + true); } else { final Tab currentTab = mTabControl.getCurrentTab(); dismissSubWindow(currentTab); @@ -1335,8 +1361,7 @@ public class Controller boolean showNewTab = mTabControl.canCreateNewTab(); MenuItem newTabItem = menu.findItem(R.id.open_newtab_context_menu_id); - newTabItem.setTitle( - BrowserSettings.getInstance().openInBackground() + newTabItem.setTitle(getSettings().openInBackground() ? R.string.contextmenu_openlink_newwindow_background : R.string.contextmenu_openlink_newwindow); newTabItem.setVisible(showNewTab); @@ -1363,8 +1388,12 @@ public class Controller @Override public boolean onMenuItemClick(MenuItem item) { final Tab parent = mTabControl.getCurrentTab(); - final Tab newTab = openTab(parent, - extra, false); + final Tab newTab = + openTab(extra, + (parent != null) + && parent.isPrivateBrowsingEnabled(), + !mSettings.openInBackground(), + true); if (newTab != parent) { parent.addChildTab(newTab); } @@ -1465,12 +1494,12 @@ public class Controller PackageManager.MATCH_DEFAULT_ONLY); menu.findItem(R.id.share_page_menu_id).setVisible(ri != null); - boolean isNavDump = mSettings.isNavDump(); + boolean isNavDump = mSettings.enableNavDump(); final MenuItem nav = menu.findItem(R.id.dump_nav_menu_id); nav.setVisible(isNavDump); nav.setEnabled(isNavDump); - boolean showDebugSettings = mSettings.showDebugSettings(); + boolean showDebugSettings = mSettings.isDebugEnabled(); final MenuItem counter = menu.findItem(R.id.dump_counters_menu_id); counter.setVisible(showDebugSettings); counter.setEnabled(showDebugSettings); @@ -1683,7 +1712,7 @@ public class Controller Tab desiredTab = mTabControl.getTab(id); if (desiredTab != null && desiredTab != mTabControl.getCurrentTab()) { - switchToTab(id); + switchToTab(desiredTab); } break; } @@ -1774,7 +1803,6 @@ public class Controller // title bar once again. mExtendedMenuOpen = false; mUi.onExtendedMenuClosed(mInLoad); - mUi.onOptionsMenuOpened(); } } } else { @@ -2103,7 +2131,8 @@ public class Controller mTabControl.removeTab(tab); } - protected void setActiveTab(Tab tab) { + @Override + public void setActiveTab(Tab tab) { // monkey protection against delayed start if (tab != null) { mTabControl.setCurrentTab(tab); @@ -2116,9 +2145,9 @@ public class Controller Tab current = mTabControl.getCurrentTab(); if (current != null && current.getWebView().copyBackForwardList().getSize() == 0) { - Tab parent = current.getParentTab(); + Tab parent = current.getParent(); if (parent != null) { - switchToTab(mTabControl.getTabIndex(parent)); + switchToTab(parent); closeTab(current); } } @@ -2135,7 +2164,7 @@ public class Controller // TODO: analyze why the remove and add are necessary mUi.attachTab(appTab); if (mTabControl.getCurrentTab() != appTab) { - switchToTab(mTabControl.getTabIndex(appTab)); + switchToTab(appTab); loadUrlDataIn(appTab, urlData); } else { // If the tab was the current tab, we have to attach @@ -2169,92 +2198,74 @@ public class Controller } } - @Override - public Tab openTabToHomePage() { - // check for max tabs - if (mTabControl.canCreateNewTab()) { - return openTabAndShow(null, new UrlData(mSettings.getHomePage()), - false, null); - } else { - mUi.showMaxTabsWarning(); - return null; + // open a non inconito tab with the given url data + // and set as active tab + public Tab openTab(UrlData urlData) { + Tab tab = createNewTab(false, true, true); + if ((tab != null) && !urlData.isEmpty()) { + loadUrlDataIn(tab, urlData); } + return tab; } - protected Tab openTab(Tab parent, String url, boolean forceForeground) { - if (mSettings.openInBackground() && !forceForeground) { - Tab tab = mTabControl.createNewTab(false, null, null, - (parent != null) && parent.isPrivateBrowsingEnabled()); - if (tab != null) { - addTab(tab); - WebView view = tab.getWebView(); - loadUrl(view, url); - } - return tab; - } else { - return openTabAndShow(parent, new UrlData(url), false, null); - } + @Override + public Tab openTabToHomePage() { + return openTab(mSettings.getHomePage(), false, true, false); } + @Override + public Tab openIncognitoTab() { + return openTab(INCOGNITO_URI, true, true, false); + } - // This method does a ton of stuff. It will attempt to create a new tab - // if we haven't reached MAX_TABS. Otherwise it uses the current tab. If - // url isn't null, it will load the given url. - public Tab openTabAndShow(Tab parent, final UrlData urlData, - boolean closeOnExit, String appId) { - final Tab currentTab = mTabControl.getCurrentTab(); - if (mTabControl.canCreateNewTab()) { - final Tab tab = mTabControl.createNewTab(closeOnExit, appId, - urlData.mUrl, - (parent != null) && parent.isPrivateBrowsingEnabled()); - WebView webview = tab.getWebView(); - // We must set the new tab as the current tab to reflect the old - // animation behavior. - addTab(tab); - setActiveTab(tab); - if (!urlData.isEmpty()) { - loadUrlDataIn(tab, urlData); - } - return tab; - } else { - // Get rid of the subwindow if it exists - dismissSubWindow(currentTab); - if (!urlData.isEmpty()) { - // Load the given url. - loadUrlDataIn(currentTab, urlData); + @Override + public Tab openTab(String url, boolean incognito, boolean setActive, + boolean useCurrent) { + Tab tab = createNewTab(incognito, setActive, useCurrent); + if (tab != null) { + WebView w = tab.getWebView(); + if (url != null) { + loadUrl(w, url); } - return currentTab; } + return tab; } - @Override - public Tab openIncognitoTab() { + // this method will attempt to create a new tab + // incognito: private browsing tab + // setActive: ste tab as current tab + // useCurrent: if no new tab can be created, return current tab + private Tab createNewTab(boolean incognito, boolean setActive, + boolean useCurrent) { + Tab tab = null; if (mTabControl.canCreateNewTab()) { - Tab currentTab = mTabControl.getCurrentTab(); - Tab tab = mTabControl.createNewTab(false, null, - null, true); + tab = mTabControl.createNewTab(incognito); addTab(tab); - setActiveTab(tab); - loadUrlDataIn(tab, new UrlData("browser:incognito")); - return tab; + if (setActive) { + setActiveTab(tab); + } } else { - mUi.showMaxTabsWarning(); - return null; + if (useCurrent) { + tab = mTabControl.getCurrentTab(); + // Get rid of the subwindow if it exists + dismissSubWindow(tab); + } else { + mUi.showMaxTabsWarning(); + } } + return tab; } /** - * @param index Index of the tab to change to, as defined by - * mTabControl.getTabIndex(Tab t). + * @param tab the tab to switch to * @return boolean True if we successfully switched to a different tab. If * the indexth tab is null, or if that tab is the same as * the current one, return false. */ @Override - public boolean switchToTab(int index) { + public boolean switchToTab(Tab tab) { // hide combo view if open removeComboView(); - Tab tab = mTabControl.getTab(index); Tab currentTab = mTabControl.getCurrentTab(); if (tab == null || tab == currentTab) { return false; @@ -2267,25 +2278,20 @@ public class Controller public void closeCurrentTab() { // hide combo view if open removeComboView(); - final Tab current = mTabControl.getCurrentTab(); if (mTabControl.getTabCount() == 1) { mActivity.finish(); return; } - final Tab parent = current.getParentTab(); - int indexToShow = -1; - if (parent != null) { - indexToShow = mTabControl.getTabIndex(parent); - } else { - final int currentIndex = mTabControl.getCurrentIndex(); - // Try to move to the tab to the right - indexToShow = currentIndex + 1; - if (indexToShow > mTabControl.getTabCount() - 1) { - // Try to move to the tab to the left - indexToShow = currentIndex - 1; + final Tab current = mTabControl.getCurrentTab(); + final int pos = mTabControl.getCurrentPosition(); + Tab newTab = current.getParent(); + if (newTab == null) { + newTab = mTabControl.getTab(pos + 1); + if (newTab == null) { + newTab = mTabControl.getTab(pos - 1); } } - if (switchToTab(indexToShow)) { + if (switchToTab(newTab)) { // Close window closeTab(current); } @@ -2299,10 +2305,6 @@ public class Controller public void closeTab(Tab tab) { // hide combo view if open removeComboView(); - int currentIndex = mTabControl.getCurrentIndex(); - int removeIndex = mTabControl.getTabIndex(tab); - Tab newtab = mTabControl.getTab(currentIndex); - setActiveTab(newtab); removeTab(tab); } @@ -2370,18 +2372,12 @@ public class Controller } else { // Check to see if we are closing a window that was created by // another window. If so, we switch back to that window. - Tab parent = current.getParentTab(); + Tab parent = current.getParent(); if (parent != null) { - switchToTab(mTabControl.getTabIndex(parent)); + switchToTab(parent); // Now we close the other tab closeTab(current); } else { - if (current.closeOnExit()) { - // This will finish the activity if there is only one tab - // open or it will switch to the next available tab if - // available. - closeCurrentTab(); - } /* * Instead of finishing the activity, simply push this to the back * of the stack and let ActivityManager to choose the foreground @@ -2414,12 +2410,6 @@ public class Controller startSearch(result, false, bundle, false); } - @Override - public void startSearch(String url) { - startSearch(mSettings.getHomePage().equals(url) ? null : url, true, - null, false); - } - private void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch) { if (appSearchData == null) { @@ -2441,21 +2431,22 @@ public class Controller return bundle; } - /** + /** * helper method for key handler * returns the current tab if it can't advance */ - private int getNextTabIndex() { - return Math.min(mTabControl.getTabCount() - 1, - mTabControl.getCurrentIndex() + 1); + private Tab getNextTab() { + return mTabControl.getTab(Math.min(mTabControl.getTabCount() - 1, + mTabControl.getCurrentPosition() + 1)); } /** * helper method for key handler * returns the current tab if it can't advance */ - private int getPrevTabIndex() { - return Math.max(0, mTabControl.getCurrentIndex() - 1); + private Tab getPrevTab() { + return mTabControl.getTab(Math.max(0, + mTabControl.getCurrentPosition() - 1)); } /** @@ -2489,10 +2480,10 @@ public class Controller if (event.isCtrlPressed()) { if (event.isShiftPressed()) { // prev tab - switchToTab(getPrevTabIndex()); + switchToTab(getPrevTab()); } else { // next tab - switchToTab(getNextTabIndex()); + switchToTab(getNextTab()); } return true; } diff --git a/src/com/android/browser/CrashRecoveryHandler.java b/src/com/android/browser/CrashRecoveryHandler.java new file mode 100644 index 0000000..9e98e76 --- /dev/null +++ b/src/com/android/browser/CrashRecoveryHandler.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.os.Bundle; +import android.os.Debug; +import android.os.Parcel; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; + +public class CrashRecoveryHandler { + + private static final String LOGTAG = "BrowserCrashRecovery"; + private static final String STATE_FILE = "browser_state.parcel"; + private static final int BUFFER_SIZE = 4096; + + private Controller mController; + + public CrashRecoveryHandler(Controller controller) { + mController = controller; + } + + public void backupState() { + final Bundle state = new Bundle(); + mController.onSaveInstanceState(state); + final Context context = mController.getActivity(); + new Thread() { + @Override + public void run() { + Parcel p = Parcel.obtain(); + try { + state.writeToParcel(p, 0); + FileOutputStream fout = context.openFileOutput(STATE_FILE, + Context.MODE_PRIVATE); + fout.write(p.marshall()); + fout.close(); + } catch (Exception e) { + Log.i(LOGTAG, "Failed to save persistent state", e); + } finally { + p.recycle(); + } + } + }.start(); + } + + public void clearState() { + Context context = mController.getActivity(); + context.deleteFile(STATE_FILE); + } + + public void promptToRecover(final Bundle state, final Intent intent) { + new AlertDialog.Builder(mController.getActivity()) + .setTitle(R.string.recover_title) + .setMessage(R.string.recover_prompt) + .setPositiveButton(R.string.recover_yes, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mController.doStart(state, intent); + } + }) + .setNegativeButton(R.string.recover_no, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + clearState(); + mController.doStart(null, intent); + } + }) + .show(); + } + + public void startRecovery(Intent intent) { + Parcel parcel = Parcel.obtain(); + try { + Context context = mController.getActivity(); + FileInputStream fin = context.openFileInput(STATE_FILE); + ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[BUFFER_SIZE]; + int read; + while ((read = fin.read(buffer)) > 0) { + dataStream.write(buffer, 0, read); + } + byte[] data = dataStream.toByteArray(); + parcel.unmarshall(data, 0, data.length); + parcel.setDataPosition(0); + Bundle state = parcel.readBundle(); + promptToRecover(state, intent); + } catch (FileNotFoundException e) { + // No state to recover + mController.doStart(null, intent); + } catch (Exception e) { + Log.w(LOGTAG, "Failed to recover state!", e); + mController.doStart(null, intent); + } finally { + parcel.recycle(); + } + } +} diff --git a/src/com/android/browser/InstantSearchEngine.java b/src/com/android/browser/InstantSearchEngine.java index 85e494a..e2e9c8a 100644 --- a/src/com/android/browser/InstantSearchEngine.java +++ b/src/com/android/browser/InstantSearchEngine.java @@ -18,6 +18,7 @@ package com.android.browser; import com.android.browser.Controller; import com.android.browser.R; import com.android.browser.UI.DropdownChangeListener; +import com.android.browser.provider.BrowserProvider; import com.android.browser.search.SearchEngine; import android.app.SearchManager; @@ -174,7 +175,12 @@ public class InstantSearchEngine implements SearchEngine, DropdownChangeListener * visible tab. */ private void switchSearchboxIfNeeded() { - final SearchBox searchBox = getCurrentWebview().getSearchBox(); + final WebView current = getCurrentWebview(); + if (current == null) { + return; + } + + final SearchBox searchBox = current.getSearchBox(); if (searchBox != mSearchBox) { if (mSearchBox != null) { mSearchBox.removeSearchBoxListener(mListener); @@ -188,7 +194,12 @@ public class InstantSearchEngine implements SearchEngine, DropdownChangeListener } private boolean isInstantPage() { - String currentUrl = getCurrentWebview().getUrl(); + final WebView current = getCurrentWebview(); + if (current == null) { + return false; + } + + final String currentUrl = current.getUrl(); if (currentUrl != null) { Uri uri = Uri.parse(currentUrl); @@ -210,7 +221,10 @@ public class InstantSearchEngine implements SearchEngine, DropdownChangeListener mController.getActivity().runOnUiThread(new Runnable() { @Override public void run() { - getCurrentWebview().loadUrl(getInstantBaseUrl()); + final WebView current = getCurrentWebview(); + if (current != null) { + current.loadUrl(getInstantBaseUrl()); + } } }); } @@ -292,7 +306,12 @@ public class InstantSearchEngine implements SearchEngine, DropdownChangeListener } private int rescaleHeight(int height) { - final float scale = getCurrentWebview().getScale(); + final WebView current = getCurrentWebview(); + if (current == null) { + return 0; + } + + final float scale = current.getScale(); if (scale != 0) { return (int) (height / scale); } @@ -306,8 +325,10 @@ public class InstantSearchEngine implements SearchEngine, DropdownChangeListener if (rescaledHeight != mHeight) { mHeight = rescaledHeight; - mSearchBox.setDimensions(0, 0, 0, rescaledHeight); - mSearchBox.onresize(); + if (mSearchBox != null) { + mSearchBox.setDimensions(0, 0, 0, rescaledHeight); + mSearchBox.onresize(); + } } } diff --git a/src/com/android/browser/IntentHandler.java b/src/com/android/browser/IntentHandler.java index b556638..8d1b784 100644 --- a/src/com/android/browser/IntentHandler.java +++ b/src/com/android/browser/IntentHandler.java @@ -134,7 +134,7 @@ public class IntentHandler { } if (intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false)) { - mController.openTabAndShow(mTabControl.getCurrentTab(), urlData, false, null); + mController.openTab(urlData); return; } final String appId = intent @@ -146,12 +146,15 @@ public class IntentHandler { && !mActivity.getPackageName().equals(appId) && (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) { if (activateVoiceSearch) { - Tab appTab = mTabControl.getTabFromId(appId); + Tab appTab = mTabControl.getTabFromAppId(appId); if (appTab != null) { mController.reuseTab(appTab, appId, urlData); return; } else { - mController.openTabAndShow(null, urlData, false, appId); + Tab tab = mController.openTab(urlData); + if (tab != null) { + tab.setAppId(appId); + } } } else { // No matching application tab, try to find a regular tab @@ -159,7 +162,7 @@ public class IntentHandler { Tab appTab = mTabControl.findUnusedTabWithUrl(urlData.mUrl); if (appTab != null) { if (current != appTab) { - mController.switchToTab(mTabControl.getTabIndex(appTab)); + mController.switchToTab(appTab); } // Otherwise, we are already viewing the correct tab. } else { @@ -168,7 +171,10 @@ public class IntentHandler { // 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. - mController.openTabAndShow(null, urlData, false, appId); + Tab tab = mController.openTab(urlData); + if (tab != null) { + tab.setAppId(appId); + } } } } else { @@ -187,7 +193,7 @@ public class IntentHandler { } else if ("about:debug.nav".equals(urlData.mUrl)) { current.getWebView().debugDump(); } else { - mSettings.toggleDebugSettings(mActivity); + mSettings.toggleDebugSettings(); } return; } @@ -221,17 +227,6 @@ public class IntentHandler { headers.put(key, pairs.getString(key)); } } - - // AppId will be set to the Browser for Search Bar initiated searches - final String appId = intent.getStringExtra(Browser.EXTRA_APPLICATION_ID); - if (mActivity.getPackageName().equals(appId)) { - String rlz = mSettings.getRlzValue(); - Uri uri = Uri.parse(url); - if (!rlz.isEmpty() && needsRlz(uri)) { - Uri rlzUri = addRlzParameter(uri, rlz); - url = rlzUri.toString(); - } - } } } else if (Intent.ACTION_SEARCH.equals(action) || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action) @@ -375,19 +370,4 @@ public class IntentHandler { } } - private static boolean needsRlz(Uri uri) { - if ((uri.getQueryParameter("rlz") == null) && - (uri.getQueryParameter("q") != null) && - UrlUtils.isGoogleUri(uri)) { - return true; - } - return false; - } - - private static Uri addRlzParameter(Uri uri, String rlz) { - if (rlz.isEmpty()) { - return uri; - } - return uri.buildUpon().appendQueryParameter("rlz", rlz).build(); - } } diff --git a/src/com/android/browser/NavScreen.java b/src/com/android/browser/NavScreen.java new file mode 100644 index 0000000..a5090fa --- /dev/null +++ b/src/com/android/browser/NavScreen.java @@ -0,0 +1,457 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Matrix; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.webkit.WebView; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.FrameLayout.LayoutParams; +import android.widget.Gallery; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.ImageView.ScaleType; +import android.widget.LinearLayout; +import android.widget.ListPopupWindow; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +public class NavScreen extends LinearLayout implements OnClickListener { + + UiController mUiController; + PhoneUi mUi; + Tab mTab; + Activity mActivity; + + View mTopPanel; + ImageButton mBack; + ImageButton mRefresh; + ImageButton mForward; + ImageButton mTabs; + ImageButton mBookmarks; + ImageButton mMore; + ImageButton mNewTab; + ImageButton mNewIncognito; + FrameLayout mHolder; + + Gallery mFlipper; + float mTabAspect = 0.66f; + int mTabWidth; + int mTabHeight; + TabAdapter mAdapter; + ListPopupWindow mPopup; + int mOrientation; + + public NavScreen(Activity activity, UiController ctl, PhoneUi ui) { + super(activity); + mActivity = activity; + mUiController = ctl; + mUi = ui; + mOrientation = activity.getResources().getConfiguration().orientation; + init(); + } + + @Override + public void onMeasure(int wspec, int hspec) { + super.onMeasure(wspec, hspec); + mTabHeight = mFlipper.getMeasuredHeight(); + mTabWidth = (int) (mTabHeight * mTabAspect); + if (mAdapter != null) { + mAdapter.notifyDataSetChanged(); + } + } + + protected Tab getSelectedTab() { + return (Tab) mFlipper.getSelectedItem(); + } + + protected void showMenu() { + Menu menu = mUi.getMenu(); + menu.setGroupVisible(R.id.NAV_MENU, false); + + MenuAdapter menuAdapter = new MenuAdapter(mContext); + menuAdapter.setMenu(menu); + ListPopupWindow popup = new ListPopupWindow(mContext); + popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); + popup.setAdapter(menuAdapter); + popup.setModal(true); + popup.setAnchorView(mMore); + popup.setWidth((int) mContext.getResources().getDimension( + R.dimen.menu_width)); + popup.show(); + mPopup = popup; + } + + protected float getToolbarHeight() { + return mActivity.getResources().getDimension(R.dimen.toolbar_height); + } + + protected void dismissMenu() { + if (mPopup != null) { + mPopup.dismiss(); + } + } + + // for configuration changes + @Override + protected void onConfigurationChanged(Configuration newconfig) { + if (newconfig.orientation != mOrientation) { + int selIx = mFlipper.getSelectedItemPosition(); + removeAllViews(); + init(); + mFlipper.setSelection(selIx); + mOrientation = newconfig.orientation; + } + } + + private void init() { + LayoutInflater.from(mContext).inflate(R.layout.nav_screen, this); + LinearLayout content = (LinearLayout) findViewById(R.id.nav_screen); + mTopPanel = findViewById(R.id.navtop); + mBack = (ImageButton) findViewById(R.id.back); + mForward = (ImageButton) findViewById(R.id.forward); + mRefresh = (ImageButton) findViewById(R.id.refresh); + mTabs = (ImageButton) findViewById(R.id.tabs); + mBookmarks = (ImageButton) findViewById(R.id.bookmarks); + mNewTab = (ImageButton) findViewById(R.id.newtab); + mNewIncognito = (ImageButton) findViewById(R.id.newincognito); + mMore = (ImageButton) findViewById(R.id.more); + mBack.setOnClickListener(this); + mForward.setOnClickListener(this); + mRefresh.setOnClickListener(this); + mTabs.setOnClickListener(this); + mBookmarks.setOnClickListener(this); + mNewTab.setOnClickListener(this); + mNewIncognito.setOnClickListener(this); + mMore.setOnClickListener(this); + mHolder = (FrameLayout) findViewById(R.id.galleryholder); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + mFlipper = new TabGallery(mContext); + mFlipper.setSpacing((int)(mContext.getResources() + .getDimension(R.dimen.nav_tab_spacing))); + mFlipper.setUnselectedAlpha(0.8f); + mFlipper.setLayoutParams(lp); + mHolder.addView(mFlipper, 0); + mAdapter = new TabAdapter(mContext, mUiController.getTabControl()); + mFlipper.setAdapter(mAdapter); + setTab(mUi.getActiveTab(), true); + mFlipper.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, + int position, long id) { + // post as runnable to prevent bug in gesturedetector + // when view is removed in click handler + // sends action_cancel before action_up + mFlipper.post(new Runnable() { + public void run() { + close(); + } + }); + } + }); + mFlipper.setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, + int position, long id) { + final Tab tab = mAdapter.getItem(position); + setTab(tab, false); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + } + }); + } + + private void setTab(Tab tab, boolean updateFlipper) { + mTab = tab; + // refresh state from tab + WebView web = tab.getWebView(); + if (web != null) { + mBack.setImageResource(web.canGoBack() + ? R.drawable.ic_back_holo_dark + : R.drawable.ic_back_disabled_holo_dark); + mForward.setImageResource(web.canGoForward() + ? R.drawable.ic_forward_holo_dark + : R.drawable.ic_forward_disabled_holo_dark); + } + if (updateFlipper) { + mFlipper.setSelection(mUiController.getTabControl().getTabPosition(tab)); + } + } + + @Override + public void onClick(View v) { + WebView web = (mTab != null) ? mTab.getWebView() : null; + if (web != null) { + if (mBack == v) { + mUi.hideNavScreen(true); + switchToSelected(); + web.goBack(); + } else if (mForward == v) { + mUi.hideNavScreen(true); + switchToSelected(); + web.goForward(); + } else if (mRefresh == v) { + mUi.hideNavScreen(true); + switchToSelected(); + web.reload(); + } + } + if (mBookmarks == v) { + mUi.hideNavScreen(false); + switchToSelected(); + mUiController.bookmarksOrHistoryPicker(false); + } else if (mTabs == v) { + } else if (mNewTab == v) { + openNewTab(); + } else if (mMore == v) { + showMenu(); + } else if (mNewIncognito == v) { + mUi.hideNavScreen(true); + mUiController.openIncognitoTab(); + } + } + + private void openNewTab() { + Tab tab = mUiController.openTabToHomePage(); + mAdapter.notifyDataSetChanged(); + + if (tab != null) { + // set tab as the selected in flipper, then hide + final int tix = mUi.mTabControl.getTabPosition(tab); + post(new Runnable() { + public void run() { + if (tix != -1) { + for (int i = mFlipper.getSelectedItemPosition(); + i <= tix; i++) { + mFlipper.setSelection(i, true); + mFlipper.invalidate(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + mUi.hideNavScreen(true); + switchToSelected(); + } + }); + } + } + + private void switchToSelected() { + Tab tab = (Tab) mFlipper.getSelectedItem(); + if (tab != mUi.getActiveTab()) { + mUiController.setActiveTab(tab); + } + } + + protected void close() { + close(true); + } + + protected void close(boolean animate) { + mUi.hideNavScreen(animate); + switchToSelected(); + } + + class TabGallery extends Gallery { + + public TabGallery(Context ctx) { + super(ctx); + setUnselectedAlpha(0.3f); + } + + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + return new Gallery.LayoutParams( + LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { + return generateDefaultLayoutParams(); + } + + } + + class TabAdapter extends BaseAdapter { + + Context context; + TabControl tabControl; + + public TabAdapter(Context ctx, TabControl tc) { + context = ctx; + tabControl = tc; + } + + void onCloseTab(Tab tab) { + if (tab != null) { + mUiController.closeTab(tab); + if (tabControl.getTabCount() == 0) { + mUiController.openTabToHomePage(); + mUi.hideNavScreen(false); + } else { + notifyDataSetChanged(); + } + } + } + + @Override + public int getCount() { + return tabControl.getTabCount(); + } + + @Override + public Tab getItem(int position) { + return tabControl.getTab(position); + } + + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ImageView content = null; + if (convertView == null) { + convertView = LayoutInflater.from(context).inflate(R.layout.nav_tab_view, + null); + content = (ImageView) convertView.findViewById(R.id.content); + content.setLayoutParams(new LayoutParams(mTabWidth, mTabHeight)); + } else { + content = (ImageView) convertView.findViewById(R.id.content); + content.setLayoutParams(new LayoutParams(mTabWidth, mTabHeight)); + } + View tbar = convertView.findViewById(R.id.titlebar); + TextView title = (TextView) convertView.findViewById(R.id.title); + ImageView icon = (ImageView) convertView.findViewById(R.id.favicon); + ImageButton close = (ImageButton) convertView.findViewById(R.id.closetab); + final Tab tab = getItem(position); + icon.setImageDrawable(mUi.getFaviconDrawable(tab.getFavicon())); + title.setText(tab.getUrl()); + content.setScaleType(ScaleType.MATRIX); + Matrix matrix = new Matrix(); + Bitmap screen = tab.getScreenshot(); + if (screen != null) { + float scale = 1.0f; + if (mTabWidth > mTabHeight) { + scale = mTabWidth / (float) screen.getWidth(); + } else { + scale = mTabHeight / (float) screen.getHeight(); + } + matrix.setScale(scale, scale); + content.setImageMatrix(matrix); + content.setImageBitmap(screen); + } + close.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + onCloseTab(tab); + } + }); + tbar.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + close(false); + mUi.getTitleBar().setSkipTitleBarAnimations(true); + mUi.editUrl(false); + mUi.getTitleBar().setSkipTitleBarAnimations(false); + } + }); + return convertView; + } + } + + private class MenuAdapter extends BaseAdapter implements OnClickListener { + + List<MenuItem> mItems; + LayoutInflater mInflater; + + public MenuAdapter(Context ctx) { + mInflater = LayoutInflater.from(ctx); + mItems = new ArrayList<MenuItem>(); + } + + public void setMenu(Menu menu) { + mItems.clear(); + for (int i = 0; i < menu.size(); i++) { + MenuItem item = menu.getItem(i); + if (item.isEnabled() && item.isVisible()) { + mItems.add(item); + } + } + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mItems.size(); + } + + @Override + public MenuItem getItem(int position) { + return mItems.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public void onClick(View v) { + if (v.getTag() != null) { + dismissMenu(); + mActivity.closeOptionsMenu(); + mUi.hideNavScreen(false); + mUiController.onOptionsItemSelected((MenuItem) v.getTag()); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final MenuItem item = mItems.get(position); + View view = mInflater.inflate(R.layout.qc_menu_item, null); + TextView label = (TextView) view.findViewById(R.id.title); + label.setText(item.getTitle()); + label.setTag(item); + label.setOnClickListener(this); + return label; + } + + } + +} diff --git a/src/com/android/browser/PhoneUi.java b/src/com/android/browser/PhoneUi.java index f1939e4..e838cd4 100644 --- a/src/com/android/browser/PhoneUi.java +++ b/src/com/android/browser/PhoneUi.java @@ -16,18 +16,19 @@ package com.android.browser; +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.app.Activity; -import android.content.Context; -import android.graphics.PixelFormat; import android.util.Log; import android.view.ActionMode; import android.view.Gravity; import android.view.KeyEvent; import android.view.Menu; -import android.view.MotionEvent; import android.view.View; -import android.view.WindowManager; import android.webkit.WebView; +import android.widget.FrameLayout; /** * Ui for regular phone screen sizes @@ -35,13 +36,17 @@ import android.webkit.WebView; public class PhoneUi extends BaseUi { private static final String LOGTAG = "PhoneUi"; + private static final float NAV_TAB_SCALE = 0.75f; - private TitleBar mTitleBar; + private TitleBarPhone mTitleBar; private ActiveTabsPage mActiveTabsPage; - private TouchProxy mTitleOverlay; + private boolean mUseQuickControls; + private PieControl mPieControl; + private NavScreen mNavScreen; boolean mExtendedMenuOpen; boolean mOptionsMenuOpen; + boolean mAnimating; /** * @param browser @@ -49,11 +54,13 @@ public class PhoneUi extends BaseUi { */ public PhoneUi(Activity browser, UiController controller) { super(browser, controller); - mTitleBar = new TitleBar(mActivity, mUiController, this); + mTitleBar = new TitleBarPhone(mActivity, mUiController, this, + mContentView); // mTitleBar will be always be shown in the fully loaded mode on // phone mTitleBar.setProgress(100); mActivity.getActionBar().hide(); + setUseQuickControls(BrowserSettings.getInstance().useQuickControls()); } @Override @@ -62,23 +69,6 @@ public class PhoneUi extends BaseUi { mActivity.getActionBar().hide(); } - // webview factory - - @Override - public WebView createWebView(boolean privateBrowsing) { - // Create a new WebView - WebView w = new WebView(mActivity, null, - android.R.attr.webViewStyle, privateBrowsing); - initWebViewSettings(w); - return w; - } - - @Override - public WebView createSubWebView(boolean privateBrowsing) { - WebView web = createWebView(privateBrowsing); - return web; - } - // lifecycle @Override @@ -100,8 +90,10 @@ public class PhoneUi extends BaseUi { @Override public void editUrl(boolean clearInput) { - String url = getActiveTab().getUrl(); - mUiController.startSearch(url); + if (mUseQuickControls) { + getTitleBar().setShowProgressOnly(false); + } + super.editUrl(clearInput); } @Override @@ -110,11 +102,31 @@ public class PhoneUi extends BaseUi { // if tab page is showing, hide it mUiController.removeActiveTabsPage(true); return true; + } else if (mNavScreen != null) { + mNavScreen.close(); + return true; } return super.onBackKey(); } @Override + public boolean dispatchKey(int code, KeyEvent event) { + if (!isComboViewShowing()) { + switch (code) { + case KeyEvent.KEYCODE_MENU: + if (mNavScreen == null) { + showNavScreen(); + return true; + } else { + mNavScreen.close(); + return true; + } + } + } + return false; + } + + @Override public void onProgressChanged(Tab tab) { if (tab.inForeground()) { int progress = tab.getLoadProgress(); @@ -122,9 +134,16 @@ public class PhoneUi extends BaseUi { if (progress == 100) { if (!mOptionsMenuOpen || !mExtendedMenuOpen) { hideTitleBar(); + if (mUseQuickControls) { + mTitleBar.setShowProgressOnly(false); + } } } else { if (!mOptionsMenuOpen || mExtendedMenuOpen) { + if (mUseQuickControls && !mTitleBar.isEditingUrl()) { + mTitleBar.setShowProgressOnly(true); + setTitleGravity(Gravity.TOP); + } showTitleBar(); } } @@ -132,41 +151,41 @@ public class PhoneUi extends BaseUi { } @Override - public void setActiveTab(Tab tab) { - super.setActiveTab(tab); - WebView view = tab.getWebView(); + public void setActiveTab(final Tab tab) { + captureTab(mActiveTab); + super.setActiveTab(tab, true); + setActiveTab(tab, true); + } + + @Override + void setActiveTab(Tab tab, boolean needsAttaching) { + BrowserWebView view = (BrowserWebView) tab.getWebView(); // TabControl.setCurrentTab has been called before this, // so the tab is guaranteed to have a webview if (view == null) { Log.e(LOGTAG, "active tab with no webview detected"); return; } - view.setEmbeddedTitleBar(getTitleBar()); + // Request focus on the top window. + if (mUseQuickControls) { + mPieControl.forceToTop(mContentView); + view.setScrollListener(null); + } else { + // check if title bar is already attached by animation + if (mTitleBar.getParent() == null) { + view.setEmbeddedTitleBar(mTitleBar); + } + } if (tab.isInVoiceSearchMode()) { - showVoiceTitleBar(tab.getVoiceDisplayTitle()); + showVoiceTitleBar(tab.getVoiceDisplayTitle(), tab.getVoiceSearchResults()); } else { revertVoiceTitleBar(tab); } + updateLockIconToLatest(tab); tab.getTopWindow().requestFocus(); } @Override - protected void showTitleBar() { - if (canShowTitleBar()) { - setTitleGravity(Gravity.TOP); - super.showTitleBar(); - } - } - - @Override - protected void hideTitleBar() { - if (isTitleBarShowing()) { - setTitleGravity(Gravity.NO_GRAVITY); - super.hideTitleBar(); - } - } - - @Override protected TitleBarBase getTitleBar() { return mTitleBar; } @@ -175,6 +194,7 @@ public class PhoneUi extends BaseUi { @Override public void showActiveTabsPage() { + captureTab(mActiveTab); mActiveTabsPage = new ActiveTabsPage(mActivity, mUiController); mTitleBar.setVisibility(View.GONE); hideTitleBar(); @@ -200,91 +220,200 @@ public class PhoneUi extends BaseUi { // menu handling callbacks @Override - public void onOptionsMenuOpened() { - mOptionsMenuOpen = true; - // options menu opened, show title bar - showTitleBar(); - if (mTitleOverlay == null) { - // This assumes that getTitleBar always returns the same View - mTitleOverlay = new TouchProxy(mActivity, getTitleBar()); - } - mActivity.getWindowManager().addView(mTitleOverlay, - mTitleOverlay.getWindowLayoutParams()); - } - - @Override - public void onExtendedMenuOpened() { - // Switching the menu to expanded view, so hide the - // title bar. - mExtendedMenuOpen = true; + public void onContextMenuCreated(Menu menu) { hideTitleBar(); } @Override - public void onOptionsMenuClosed(boolean inLoad) { - mOptionsMenuOpen = false; - mActivity.getWindowManager().removeView(mTitleOverlay); - if (!inLoad && !getTitleBar().hasFocus()) { - hideTitleBar(); + public void onContextMenuClosed(Menu menu, boolean inLoad) { + if (inLoad) { + showTitleBar(); } } - @Override - public void onExtendedMenuClosed(boolean inLoad) { - mExtendedMenuOpen = false; - showTitleBar(); - } + // action mode callbacks @Override - public void onContextMenuCreated(Menu menu) { + public void onActionModeStarted(ActionMode mode) { hideTitleBar(); } @Override - public void onContextMenuClosed(Menu menu, boolean inLoad) { + public void onActionModeFinished(boolean inLoad) { if (inLoad) { + if (mUseQuickControls) { + mTitleBar.setShowProgressOnly(true); + } showTitleBar(); } + mActivity.getActionBar().hide(); } - // action mode callbacks - @Override - public void onActionModeStarted(ActionMode mode) { - hideTitleBar(); + protected void setTitleGravity(int gravity) { + if (mUseQuickControls) { + FrameLayout.LayoutParams lp = + (FrameLayout.LayoutParams) getTitleBar().getLayoutParams(); + lp.gravity = gravity; + getTitleBar().setLayoutParams(lp); + } else { + super.setTitleGravity(gravity); + } + } + + private void setUseQuickControls(boolean useQuickControls) { + mUseQuickControls = useQuickControls; + getTitleBar().setUseQuickControls(mUseQuickControls); + if (useQuickControls) { + mPieControl = new PieControl(mActivity, mUiController, this); + mPieControl.attachToContainer(mContentView); + WebView web = getWebView(); + if (web != null) { + web.setEmbeddedTitleBar(null); + } + } else { + if (mPieControl != null) { + mPieControl.removeFromContainer(mContentView); + } + WebView web = getWebView(); + if (web != null) { + web.setEmbeddedTitleBar(mTitleBar); + } + setTitleGravity(Gravity.NO_GRAVITY); + } } @Override - public boolean dispatchKey(int code, KeyEvent event) { - return false; + protected void captureTab(final Tab tab) { + if (tab == null) return; + if (mUseQuickControls) { + super.captureTab(tab); + } else { + BrowserWebView web = (BrowserWebView) tab.getWebView(); + if (web != null) { + tab.setScreenshot(web.capture()); + } + } } - static class TouchProxy extends View { + void showNavScreen() { + if (mAnimating) return; + mAnimating = true; + mNavScreen = new NavScreen(mActivity, mUiController, this); + float yoffset = 0; + WebView web = getWebView(); + if (web != null) { + yoffset = mNavScreen.getToolbarHeight() - + web.getVisibleTitleHeight(); + } + // Add the custom view to its container. + mCustomViewContainer.addView(mNavScreen, COVER_SCREEN_GRAVITY_CENTER); + mContentView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + ObjectAnimator animx = ObjectAnimator.ofFloat(mContentView, + "scaleX", 1.0f, NAV_TAB_SCALE); + ObjectAnimator animy = ObjectAnimator.ofFloat(mContentView, + "scaleY", 1.0f, NAV_TAB_SCALE); + ObjectAnimator translatey = ObjectAnimator.ofFloat(mContentView, + "translationY", 0, yoffset * NAV_TAB_SCALE); + AnimatorSet anims = new AnimatorSet(); + anims.setDuration(200); + anims.addListener(new AnimatorListener() { + + @Override + public void onAnimationCancel(Animator animation) { + } - View mTarget; + @Override + public void onAnimationEnd(Animator animation) { + finishShowNavScreen(); + } - TouchProxy(Context context, View target) { - super(context); - mTarget = target; - } + @Override + public void onAnimationRepeat(Animator animation) { + } - @Override - public boolean dispatchTouchEvent(MotionEvent event) { - return mTarget.dispatchTouchEvent(event); + @Override + public void onAnimationStart(Animator animation) { + } + }); + anims.playTogether(animx, animy, translatey); + anims.start(); + } + + private void finishShowNavScreen() { + // Hide the content view. + mContentView.setLayerType(View.LAYER_TYPE_NONE, null); + mContentView.setVisibility(View.GONE); + mContentView.setScaleX(1.0f); + mContentView.setScaleY(1.0f); + mContentView.setTranslationY(0); + BrowserWebView web = (BrowserWebView) getWebView(); + if (web != null) { + mActiveTab.setScreenshot(web.capture()); } + // Finally show the custom view container. + mCustomViewContainer.setVisibility(View.VISIBLE); + mCustomViewContainer.bringToFront(); + mAnimating = false; + } + + void hideNavScreen(boolean animateToPage) { + if (mAnimating || mNavScreen == null) return; + mAnimating = true; + mNavScreen.setLayerType(View.LAYER_TYPE_HARDWARE, null); + if (animateToPage) { + float yoffset = 0; + WebView web = mNavScreen.getSelectedTab().getWebView(); + if (web != null) { + yoffset = mNavScreen.getToolbarHeight() - + web.getVisibleTitleHeight(); + } + ObjectAnimator animx = ObjectAnimator.ofFloat(mNavScreen, "scaleX", + 1.0f, 1 / NAV_TAB_SCALE); + ObjectAnimator animy = ObjectAnimator.ofFloat(mNavScreen, "scaleY", + 1.0f, 1 / NAV_TAB_SCALE); + ObjectAnimator translatey = ObjectAnimator.ofFloat(mNavScreen, + "translationY", 0, -yoffset); + AnimatorSet anims = new AnimatorSet(); + anims.setDuration(200); + anims.addListener(new AnimatorListener() { + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + finishHideNavScreen(); + } - WindowManager.LayoutParams getWindowLayoutParams() { - WindowManager.LayoutParams params = - new WindowManager.LayoutParams( - mTarget.getWidth(), - mTarget.getHeight(), - WindowManager.LayoutParams.TYPE_APPLICATION, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, - PixelFormat.TRANSPARENT); - params.gravity = Gravity.TOP | Gravity.LEFT; - params.y = mTarget.getTop(); - params.x = mTarget.getLeft(); - return params; + @Override + public void onAnimationRepeat(Animator animation) { + } + + @Override + public void onAnimationStart(Animator animation) { + } + }); + anims.playTogether(animx, animy, translatey); + anims.start(); + } else { + finishHideNavScreen(); } + } + + private void finishHideNavScreen() { + // Hide the custom view. + mNavScreen.setVisibility(View.GONE); + mNavScreen.setLayerType(View.LAYER_TYPE_NONE, null); + // Remove the custom view from its container. + mCustomViewContainer.removeView(mNavScreen); + mNavScreen = null; + mCustomViewContainer.setVisibility(View.GONE); + // Show the content view. + mContentView.setVisibility(View.VISIBLE); + mAnimating = false; + } + } diff --git a/src/com/android/browser/PieControl.java b/src/com/android/browser/PieControl.java index 38ed1bb..8bcd972 100644 --- a/src/com/android/browser/PieControl.java +++ b/src/com/android/browser/PieControl.java @@ -50,7 +50,7 @@ public class PieControl implements OnClickListener, PieMenu.PieController { private Activity mActivity; private UiController mUiController; - private XLargeUi mUi; + private BaseUi mUi; private PieMenu mPie; private PieItem mBack; private PieItem mForward; @@ -66,7 +66,7 @@ public class PieControl implements OnClickListener, PieMenu.PieController { private TextView mTabsCount; private int mItemSize; - public PieControl(Activity activity, UiController controller, XLargeUi ui) { + public PieControl(Activity activity, UiController controller, BaseUi ui) { mActivity = activity; mUiController = controller; mUi = ui; @@ -107,7 +107,7 @@ public class PieControl implements OnClickListener, PieMenu.PieController { menuview.setLayoutListener(new OnLayoutListener() { @Override public void onLayout(int ax, int ay, boolean left) { - mActivity.openOptionsMenu(); + buildMenu(); } }); @@ -128,10 +128,10 @@ public class PieControl implements OnClickListener, PieMenu.PieController { // level 2 mPie.addItem(mForward); mPie.addItem(mRefresh); + mPie.addItem(mOptions); mPie.addItem(mShowTabs); mPie.addItem(mNewTab); mPie.addItem(mClose); - mPie.addItem(mOptions); mPie.setController(this); } container.addView(mPie); @@ -142,11 +142,13 @@ public class PieControl implements OnClickListener, PieMenu.PieController { mUi.captureTab(mUi.getActiveTab()); mTabAdapter.setTabs(tabs); PieStackView sym = (PieStackView) mShowTabs.getPieView(); - sym.setCurrent(mUiController.getTabControl().getCurrentIndex()); + sym.setCurrent(mUiController.getTabControl().getCurrentPosition()); } - protected void onMenuOpened(Menu menu) { + private void buildMenu() { + Menu menu = mUi.getMenu(); + menu.setGroupVisible(R.id.NAV_MENU, false); mMenuAdapter.setMenu(menu); } @@ -205,12 +207,12 @@ public class PieControl implements OnClickListener, PieMenu.PieController { web.reload(); } } else if (mUrl.getView() == v) { - mUi.showTitleBarAndEdit(); + mUi.editUrl(false); } else if (mBookmarks.getView() == v) { mUiController.bookmarksOrHistoryPicker(false); } else if (mNewTab.getView() == v) { mUiController.openTabToHomePage(); - mUi.showTitleBarAndEdit(); + mUi.editUrl(false); } else if (mClose.getView() == v) { mUiController.closeCurrentTab(); } @@ -279,8 +281,7 @@ public class PieControl implements OnClickListener, PieMenu.PieController { view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - mUiController.switchToTab(mUiController.getTabControl() - .getTabIndex(tab)); + mUiController.switchToTab(tab); } }); return view; diff --git a/src/com/android/browser/PreferenceKeys.java b/src/com/android/browser/PreferenceKeys.java new file mode 100644 index 0000000..cff9f70 --- /dev/null +++ b/src/com/android/browser/PreferenceKeys.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2011 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; + +public interface PreferenceKeys { + + static final String PREF_AUTOFILL_ACTIVE_PROFILE_ID = "autofill_active_profile_id"; + static final String PREF_DEBUG_MENU = "debug_menu"; + + // ---------------------- + // Keys for accessibility_preferences.xml + // ---------------------- + static final String PREF_MIN_FONT_SIZE = "min_font_size"; + static final String PREF_TEXT_SIZE = "text_size"; + static final String PREF_FORCE_USERSCALABLE = "force_userscalable"; + + // ---------------------- + // Keys for advanced_preferences.xml + // ---------------------- + static final String PREF_AUTOFIT_PAGES = "autofit_pages"; + static final String PREF_BLOCK_POPUP_WINDOWS = "block_popup_windows"; + static final String PREF_DEFAULT_TEXT_ENCODING = "default_text_encoding"; + static final String PREF_DEFAULT_ZOOM = "default_zoom"; + static final String PREF_ENABLE_JAVASCRIPT = "enable_javascript"; + static final String PREF_LOAD_IMAGES = "load_images"; + static final String PREF_LOAD_PAGE = "load_page"; + static final String PREF_OPEN_IN_BACKGROUND = "open_in_background"; + static final String PREF_PLUGIN_STATE = "plugin_state"; + static final String PREF_RESET_DEFAULT_PREFERENCES = "reset_default_preferences"; + static final String PREF_SEARCH_ENGINE = "search_engine"; + static final String PREF_WEBSITE_SETTINGS = "website_settings"; + + // ---------------------- + // Keys for debug_preferences.xml + // ---------------------- + static final String PREF_ENABLE_HARDWARE_ACCEL = "enable_hardware_accel"; + static final String PREF_USER_AGENT = "user_agent"; + + // ---------------------- + // Keys for general_preferences.xml + // ---------------------- + static final String PREF_AUTOFILL_ENABLED = "autofill_enabled"; + static final String PREF_AUTOFILL_PROFILE = "autofill_profile"; + static final String PREF_HOMEPAGE = "homepage"; + static final String PREF_SYNC_WITH_CHROME = "sync_with_chrome"; + + // ---------------------- + // Keys for hidden_debug_preferences.xml + // ---------------------- + static final String PREF_ENABLE_LIGHT_TOUCH = "enable_light_touch"; + static final String PREF_ENABLE_NAV_DUMP = "enable_nav_dump"; + static final String PREF_ENABLE_TRACING = "enable_tracing"; + static final String PREF_ENABLE_VISUAL_INDICATOR = "enable_visual_indicator"; + static final String PREF_JAVASCRIPT_CONSOLE = "javascript_console"; + static final String PREF_JS_ENGINE_FLAGS = "js_engine_flags"; + static final String PREF_NORMAL_LAYOUT = "normal_layout"; + static final String PREF_SMALL_SCREEN = "small_screen"; + static final String PREF_WIDE_VIEWPORT = "wide_viewport"; + + // ---------------------- + // Keys for lab_preferences.xml + // ---------------------- + static final String PREF_ENABLE_QUICK_CONTROLS = "enable_quick_controls"; + static final String PREF_USE_MOST_VISITED_HOMEPAGE = "use_most_visited_homepage"; + static final String PREF_USE_INSTANT_SEARCH = "use_instant_search"; + static final String PREF_FULLSCREEN = "fullscreen"; + static final String PREF_ENABLE_USERAGENT_SWITCHER = "enable_useragent_switcher"; + + // ---------------------- + // Keys for privacy_security_preferences.xml + // ---------------------- + static final String PREF_ACCEPT_COOKIES = "accept_cookies"; + static final String PREF_ENABLE_GEOLOCATION = "enable_geolocation"; + static final String PREF_PRIVACY_CLEAR_CACHE = "privacy_clear_cache"; + static final String PREF_PRIVACY_CLEAR_COOKIES = "privacy_clear_cookies"; + static final String PREF_PRIVACY_CLEAR_FORM_DATA = "privacy_clear_form_data"; + static final String PREF_PRIVACY_CLEAR_GEOLOCATION_ACCESS = "privacy_clear_geolocation_access"; + static final String PREF_PRIVACY_CLEAR_HISTORY = "privacy_clear_history"; + static final String PREF_PRIVACY_CLEAR_PASSWORDS = "privacy_clear_passwords"; + static final String PREF_REMEMBER_PASSWORDS = "remember_passwords"; + static final String PREF_SAVE_FORMDATA = "save_formdata"; + static final String PREF_SHOW_SECURITY_WARNINGS = "show_security_warnings"; + +} diff --git a/src/com/android/browser/ShortcutActivity.java b/src/com/android/browser/ShortcutActivity.java index 16a4cbe..af1788d 100644 --- a/src/com/android/browser/ShortcutActivity.java +++ b/src/com/android/browser/ShortcutActivity.java @@ -38,8 +38,6 @@ public class ShortcutActivity extends Activity mBookmarks = (BrowserBookmarksPage) getFragmentManager() .findFragmentById(R.id.bookmarks); mBookmarks.setEnableContextMenu(false); - mBookmarks.setBreadCrumbMaxVisible(2); - mBookmarks.setBreadCrumbUseBackButton(true); mBookmarks.setCallbackListener(this); View cancel = findViewById(R.id.cancel); if (cancel != null) { @@ -73,10 +71,6 @@ public class ShortcutActivity extends Activity } @Override - public void onFolderChanged(int level, Uri uri) { - } - - @Override public void onClick(View v) { switch (v.getId()) { case R.id.cancel: diff --git a/src/com/android/browser/SuggestionsAdapter.java b/src/com/android/browser/SuggestionsAdapter.java index ecdaa15..242e170 100644 --- a/src/com/android/browser/SuggestionsAdapter.java +++ b/src/com/android/browser/SuggestionsAdapter.java @@ -71,6 +71,7 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable, final Object mResultsLock = new Object(); List<String> mVoiceResults; boolean mIncognitoMode; + BrowserSettings mSettings; interface CompletionListener { @@ -82,6 +83,7 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable, public SuggestionsAdapter(Context ctx, CompletionListener listener) { mContext = ctx; + mSettings = BrowserSettings.getInstance(); mListener = listener; mLinesPortrait = mContext.getResources(). getInteger(R.integer.max_suggest_lines_portrait); @@ -276,7 +278,7 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable, } private boolean shouldProcessEmptyQuery() { - final SearchEngine searchEngine = BrowserSettings.getInstance().getSearchEngine(); + final SearchEngine searchEngine = mSettings.getSearchEngine(); return searchEngine.wantsEmptyQuery(); } @@ -470,7 +472,6 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable, Uri.Builder ub = BrowserContract.Combined.CONTENT_URI.buildUpon(); ub.appendQueryParameter(BrowserContract.PARAM_LIMIT, Integer.toString(Math.max(mLinesLandscape, mLinesPortrait))); - BookmarkUtils.addAccountInfo(mContext, ub); mCursor = mContext.getContentResolver().query(ub.build(), COMBINED_PROJECTION, selection, @@ -541,7 +542,7 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable, if (mCursor != null) { mCursor.close(); } - SearchEngine searchEngine = BrowserSettings.getInstance().getSearchEngine(); + SearchEngine searchEngine = mSettings.getSearchEngine(); if (!TextUtils.isEmpty(constraint)) { if (searchEngine != null && searchEngine.supportsSuggestions()) { mCursor = searchEngine.getSuggestions(mContext, constraint.toString()); @@ -560,7 +561,7 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable, } private boolean useInstant() { - return BrowserSettings.getInstance().useInstant(); + return mSettings.useInstantSearch(); } public void clearCache() { diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java index 863fc95..e1dd1ca 100644 --- a/src/com/android/browser/Tab.java +++ b/src/com/android/browser/Tab.java @@ -88,6 +88,9 @@ class Tab { Activity mActivity; private WebViewController mWebViewController; + // The tab ID + private long mId; + // The Geolocation permissions prompt private GeolocationPermissionsPrompt mGeolocationPermissionsPrompt; // Main WebView wrapper @@ -104,12 +107,10 @@ class Tab { private Bundle mSavedState; // Parent Tab. This is the Tab that created this Tab, or null if the Tab was // created by the UI - private Tab mParentTab; + private Tab mParent; // Tab that constructed by this Tab. This is used when this Tab is // destroyed, it clears all mParentTab values in the children. - private Vector<Tab> mChildTabs; - // If true, the tab will be removed when back out of the first page. - private boolean mCloseOnExit; + private Vector<Tab> mChildren; // If true, the tab is in the foreground of the current activity. private boolean mInForeground; // If true, the tab is in page loading state (after onPageStarted, @@ -139,6 +140,7 @@ class Tab { DownloadTouchIcon mTouchIconLoader; private Bitmap mScreenshot; + private BrowserSettings mSettings; // All the state needed for a page private static class PageState { @@ -182,18 +184,11 @@ class Tab { private PageState mCurrentState; // Used for saving and restoring each Tab - // TODO: Figure out who uses what and where - // Some of these aren't use in this class, and some are only used in - // restoring state but not saving it - FIX THIS - static final String WEBVIEW = "webview"; - static final String NUMTABS = "numTabs"; - static final String CURRTAB = "currentTab"; + static final String ID = "ID"; static final String CURRURL = "currentUrl"; static final String CURRTITLE = "currentTitle"; - static final String CLOSEONEXIT = "closeonexit"; static final String PARENTTAB = "parentTab"; static final String APPID = "appid"; - static final String ORIGINALURL = "originalUrl"; static final String INCOGNITO = "privateBrowsingEnabled"; static final String SCREENSHOT = "screenshot"; @@ -313,7 +308,9 @@ class Tab { mVoiceSearchData.mLastVoiceSearchTitle = mVoiceSearchData.mVoiceSearchResults.get(index); if (mInForeground) { - mWebViewController.activateVoiceSearchMode(mVoiceSearchData.mLastVoiceSearchTitle); + mWebViewController.activateVoiceSearchMode( + mVoiceSearchData.mLastVoiceSearchTitle, + mVoiceSearchData.mVoiceSearchResults); } if (mVoiceSearchData.mVoiceSearchHtmls != null) { // When index was found it was already ensured that it was valid @@ -709,7 +706,7 @@ class Tab { setLockIconType(LockIcon.LOCK_ICON_UNSECURE); return; } - if (BrowserSettings.getInstance().showSecurityWarnings()) { + if (mSettings.showSecurityWarnings()) { final LayoutInflater factory = LayoutInflater.from(mActivity); final View warningsView = @@ -856,9 +853,9 @@ class Tab { mWebViewController.attachSubWindow(Tab.this); transport.setWebView(mSubView); } else { - final Tab newTab = mWebViewController.openTabAndShow( - Tab.this, - IntentHandler.EMPTY_URL_DATA, false, null); + final Tab newTab = mWebViewController.openTab(null, + Tab.this.isPrivateBrowsingEnabled(), + true, true); if (newTab != Tab.this) { Tab.this.addChildTab(newTab); } @@ -935,18 +932,16 @@ class Tab { @Override public void onRequestFocus(WebView view) { if (!mInForeground) { - mWebViewController.switchToTab(mWebViewController.getTabControl().getTabIndex( - Tab.this)); + mWebViewController.switchToTab(Tab.this); } } @Override public void onCloseWindow(WebView window) { - if (mParentTab != null) { + if (mParent != null) { // JavaScript can only close popup window. if (mInForeground) { - mWebViewController.switchToTab(mWebViewController.getTabControl() - .getTabIndex(mParentTab)); + mWebViewController.switchToTab(mParent); } mWebViewController.closeTab(Tab.this); } @@ -1015,7 +1010,7 @@ class Tab { public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) { - BrowserSettings.getInstance().getWebStorageSizeManager() + mSettings.getWebStorageSizeManager() .onExceededDatabaseQuota(url, databaseIdentifier, currentQuota, estimatedSize, totalUsedQuota, quotaUpdater); @@ -1034,7 +1029,7 @@ class Tab { @Override public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) { - BrowserSettings.getInstance().getWebStorageSizeManager() + mSettings.getWebStorageSizeManager() .onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota, quotaUpdater); } @@ -1172,10 +1167,7 @@ class Tab { if (disableAutoFill.isChecked()) { // Disable autofill and show a toast with how to turn it on again. - BrowserSettings s = BrowserSettings.getInstance(); - s.addObserver(mMainView.getSettings()); - s.disableAutoFill(mActivity); - s.update(); + mSettings.setAutofillEnabled(false); Toast.makeText(mActivity, R.string.autofill_setup_dialog_negative_toast, Toast.LENGTH_LONG).show(); @@ -1290,14 +1282,13 @@ class Tab { // remove later // Construct a new tab - Tab(WebViewController wvcontroller, WebView w, boolean closeOnExit, String appId, - String url) { + Tab(WebViewController wvcontroller, WebView w) { mWebViewController = wvcontroller; mActivity = mWebViewController.getActivity(); - mCloseOnExit = closeOnExit; - mAppId = appId; + mSettings = BrowserSettings.getInstance(); mDataController = DataController.getInstance(mActivity); - mCurrentState = new PageState(mActivity, w.isPrivateBrowsingEnabled()); + mCurrentState = new PageState(mActivity, w != null + ? w.isPrivateBrowsingEnabled() : false); mInPageLoad = false; mInForeground = false; @@ -1328,6 +1319,14 @@ class Tab { setWebView(w); } + public void setId(long id) { + mId = id; + } + + public long getId() { + return mId; + } + /** * Sets the WebView for this tab, correctly removing the old WebView from * the container view. @@ -1366,7 +1365,6 @@ class Tab { void destroy() { if (mMainView != null) { dismissSubWindow(); - BrowserSettings.getInstance().deleteObserver(mMainView.getSettings()); // save the WebView to call destroy() after detach it from the tab WebView webView = mMainView; setWebView(null); @@ -1379,14 +1377,14 @@ class Tab { */ void removeFromTree() { // detach the children - if (mChildTabs != null) { - for(Tab t : mChildTabs) { - t.setParentTab(null); + if (mChildren != null) { + for(Tab t : mChildren) { + t.setParent(null); } } // remove itself from the parent list - if (mParentTab != null) { - mParentTab.mChildTabs.remove(this); + if (mParent != null) { + mParent.mChildren.remove(this); } } @@ -1428,8 +1426,6 @@ class Tab { void dismissSubWindow() { if (mSubView != null) { mWebViewController.endActionMode(); - BrowserSettings.getInstance().deleteObserver( - mSubView.getSettings()); mSubView.destroy(); mSubView = null; mSubViewContainer = null; @@ -1440,37 +1436,45 @@ class Tab { /** * Set the parent tab of this tab. */ - void setParentTab(Tab parent) { - mParentTab = parent; + void setParent(Tab parent) { + mParent = parent; // This tab may have been freed due to low memory. If that is the case, - // the parent tab index is already saved. If we are changing that index + // the parent tab id is already saved. If we are changing that id // (most likely due to removing the parent tab) we must update the - // parent tab index in the saved Bundle. + // parent tab id in the saved Bundle. if (mSavedState != null) { if (parent == null) { mSavedState.remove(PARENTTAB); } else { - mSavedState.putInt(PARENTTAB, mWebViewController.getTabControl() - .getTabIndex(parent)); + mSavedState.putLong(PARENTTAB, parent.getId()); } } } /** + * If this Tab was created through another Tab, then this method returns + * that Tab. + * @return the Tab parent or null + */ + public Tab getParent() { + return mParent; + } + + /** * When a Tab is created through the content of another Tab, then we * associate the Tabs. * @param child the Tab that was created from this Tab */ void addChildTab(Tab child) { - if (mChildTabs == null) { - mChildTabs = new Vector<Tab>(); + if (mChildren == null) { + mChildren = new Vector<Tab>(); } - mChildTabs.add(child); - child.setParentTab(this); + mChildren.add(child); + child.setParent(this); } - Vector<Tab> getChildTabs() { - return mChildTabs; + Vector<Tab> getChildren() { + return mChildren; } void resume() { @@ -1658,24 +1662,6 @@ class Tab { return mErrorConsole; } - /** - * If this Tab was created through another Tab, then this method returns - * that Tab. - * @return the Tab parent or null - */ - public Tab getParentTab() { - return mParentTab; - } - - /** - * Return whether this tab should be closed when it is backing out of the - * first page. - * @return TRUE if this tab should be closed when exit. - */ - boolean closeOnExit() { - return mCloseOnExit; - } - private void setLockIconType(LockIcon icon) { mCurrentState.mLockIcon = icon; mWebViewController.onUpdatedLockIcon(this); @@ -1740,16 +1726,15 @@ class Tab { // Store some extra info for displaying the tab in the picker. final WebHistoryItem item = list != null ? list.getCurrentItem() : null; + mSavedState.putLong(ID, mId); mSavedState.putString(CURRURL, mCurrentState.mUrl); mSavedState.putString(CURRTITLE, mCurrentState.mTitle); - mSavedState.putBoolean(CLOSEONEXIT, mCloseOnExit); if (mAppId != null) { mSavedState.putString(APPID, mAppId); } // Remember the parent tab so the relationship can be restored. - if (mParentTab != null) { - mSavedState.putInt(PARENTTAB, mWebViewController.getTabControl().getTabIndex( - mParentTab)); + if (mParent != null) { + mSavedState.putLong(PARENTTAB, mParent.mId); } if (mScreenshot != null) { mSavedState.putParcelable(SCREENSHOT, mScreenshot); @@ -1767,7 +1752,7 @@ class Tab { // Restore the internal state even if the WebView fails to restore. // This will maintain the app id, original url and close-on-exit values. mSavedState = null; - mCloseOnExit = b.getBoolean(CLOSEONEXIT); + mId = b.getLong(ID); mAppId = b.getString(APPID); mScreenshot = b.getParcelable(SCREENSHOT); diff --git a/src/com/android/browser/TabBar.java b/src/com/android/browser/TabBar.java index 7abb203..6c3949a 100644 --- a/src/com/android/browser/TabBar.java +++ b/src/com/android/browser/TabBar.java @@ -16,7 +16,7 @@ package com.android.browser; -import com.android.browser.ScrollWebView.ScrollListener; +import com.android.browser.BrowserWebView.ScrollListener; import android.animation.Animator; import android.animation.Animator.AnimatorListener; @@ -166,7 +166,7 @@ public class TabBar extends LinearLayout TabView tv = buildTabView(tab); mTabs.addTab(tv); } - mTabs.setSelectedTab(mTabControl.getCurrentIndex()); + mTabs.setSelectedTab(mTabControl.getCurrentPosition()); } @Override @@ -213,7 +213,7 @@ public class TabBar extends LinearLayout mUi.hideTitleBar(); } else { mUi.stopWebViewScrolling(); - mUi.showTitleBarAndEdit(); + mUi.editUrl(false); } } else if (mUi.isTitleBarShowing() && !isLoading()) { mUi.stopEditingUrl(); @@ -221,11 +221,12 @@ public class TabBar extends LinearLayout } else { showUrlBar(); } - } else { + } else if (view instanceof TabView) { + final Tab tab = ((TabView) view).mTab; int ix = mTabs.getChildIndex(view); if (ix >= 0) { mTabs.setSelectedTab(ix); - mUiController.switchToTab(ix); + mUiController.switchToTab(tab); } } } @@ -616,7 +617,7 @@ public class TabBar extends LinearLayout // TabChangeListener implementation public void onSetActiveTab(Tab tab) { - mTabs.setSelectedTab(mTabControl.getTabIndex(tab)); + mTabs.setSelectedTab(mTabControl.getTabPosition(tab)); TabView tv = mTabMap.get(tab); if (tv != null) { tv.setProgress(tv.mTab.getLoadProgress()); diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java index af9928a..1fe9053 100644 --- a/src/com/android/browser/TabControl.java +++ b/src/com/android/browser/TabControl.java @@ -16,11 +16,8 @@ package com.android.browser; -import com.android.browser.IntentHandler.UrlData; - import android.os.Bundle; import android.util.Log; -import android.webkit.WebBackForwardList; import android.webkit.WebView; import java.io.File; @@ -32,6 +29,13 @@ import java.util.Vector; class TabControl { // Log Tag private static final String LOGTAG = "TabControl"; + + // next Tab ID + private static long sNextId = 0; + + private static final String POSITIONS = "positions"; + private static final String CURRENT = "current"; + // Maximum number of tabs. private int mMaxTabs; // Private array of WebViews that are used as tabs. @@ -57,6 +61,10 @@ class TabControl { mTabQueue = new ArrayList<Tab>(mMaxTabs); } + static long getNextId() { + return sNextId++; + } + File getThumbnailDir() { return mThumbnailDir; } @@ -107,13 +115,13 @@ class TabControl { } /** - * Return the tab at the specified index. - * @return The Tab for the specified index or null if the tab does not + * Return the tab at the specified position. + * @return The Tab for the specified position or null if the tab does not * exist. */ - Tab getTab(int index) { - if (index >= 0 && index < mTabs.size()) { - return mTabs.get(index); + Tab getTab(int position) { + if (position >= 0 && position < mTabs.size()) { + return mTabs.get(position); } return null; } @@ -127,19 +135,19 @@ class TabControl { } /** - * Return the current tab index. - * @return The current tab index + * Return the current tab position. + * @return The current tab position */ - int getCurrentIndex() { + int getCurrentPosition() { return mCurrentTab; } /** - * Given a Tab, find it's index + * Given a Tab, find it's position * @param Tab to find - * @return index of Tab or -1 if not found + * @return position of Tab or -1 if not found */ - int getTabIndex(Tab tab) { + int getTabPosition(Tab tab) { if (tab == null) { return -1; } @@ -156,7 +164,8 @@ class TabControl { */ boolean hasAnyOpenIncognitoTabs() { for (Tab tab : mTabs) { - if (tab.getWebView() != null && tab.getWebView().isPrivateBrowsingEnabled()) { + if (tab.getWebView() != null + && tab.getWebView().isPrivateBrowsingEnabled()) { return true; } } @@ -168,8 +177,7 @@ class TabControl { * @return The newly createTab or null if we have reached the maximum * number of open tabs. */ - Tab createNewTab(boolean closeOnExit, String appId, String url, - boolean privateBrowsing) { + Tab createNewTab(boolean privateBrowsing) { int size = mTabs.size(); // Return false if we have maxed out on tabs if (mMaxTabs == size) { @@ -178,7 +186,8 @@ class TabControl { final WebView w = createNewWebView(privateBrowsing); // Create a new tab and add it to the tab list - Tab t = new Tab(mController, w, closeOnExit, appId, url); + Tab t = new Tab(mController, w); + t.setId(getNextId()); mTabs.add(t); // Initially put the tab in the background. t.putInBackground(); @@ -190,7 +199,7 @@ class TabControl { * appId(null), url(null), and privateBrowsing(false). */ Tab createNewTab() { - return createNewTab(false, null, null, false); + return createNewTab(false); } /** @@ -225,7 +234,7 @@ class TabControl { } else { // If a tab that is earlier in the list gets removed, the current // index no longer points to the correct tab. - mCurrentTab = getTabIndex(current); + mCurrentTab = getTabPosition(current); } // destroy the tab @@ -233,17 +242,6 @@ class TabControl { // clear it's references to parent and children t.removeFromTree(); - // The tab indices have shifted, update all the saved state so we point - // to the correct index. - for (Tab tab : mTabs) { - Vector<Tab> children = tab.getChildTabs(); - if (children != null) { - for (Tab child : children) { - child.setParentTab(tab); - } - } - } - // Remove it from the queue of viewed tabs. mTabQueue.remove(t); return true; @@ -268,61 +266,59 @@ class TabControl { return mTabs.size(); } - /** - * Save the state of all the Tabs. - * @param outState The Bundle to save the state to. + * save the tab state: + * current position + * position sorted array of tab ids + * for each tab id, save the tab state + * @param outState */ void saveState(Bundle outState) { final int numTabs = getTabCount(); - outState.putInt(Tab.NUMTABS, numTabs); - final int index = getCurrentIndex(); - outState.putInt(Tab.CURRTAB, (index >= 0 && index < numTabs) ? index : 0); - for (int i = 0; i < numTabs; i++) { - final Tab t = getTab(i); - if (t.saveState()) { - outState.putBundle(Tab.WEBVIEW + i, t.getSavedState()); + long[] ids = new long[numTabs]; + int i = 0; + for (Tab tab : mTabs) { + ids[i++] = tab.getId(); + if (tab.saveState()) { + outState.putBundle(Long.toString(tab.getId()), tab.getSavedState()); } } + outState.putLongArray(POSITIONS, ids); + final long cid = getCurrentTab().getId(); + outState.putLong(CURRENT, cid); } /** * Check if the state can be restored. If the state can be restored, the - * current tab index is returned. This can be passed to restoreState below + * current tab id is returned. This can be passed to restoreState below * in order to restore the correct tab. Otherwise, -1 is returned and the * state cannot be restored. */ - int canRestoreState(Bundle inState, boolean restoreIncognitoTabs) { - final int numTabs = (inState == null) - ? - 1 : inState.getInt(Tab.NUMTABS, -1); - if (numTabs == -1) { + long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) { + final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS); + if (ids == null) { return -1; } - final int oldCurrentTab = inState.getInt(Tab.CURRTAB, -1); - - // Determine whether the saved current tab can be restored, and if not, - // which tab will take its place. - int currentTab = -1; + final long oldcurrent = inState.getLong(CURRENT); + long current = -1; if (restoreIncognitoTabs || - !inState.getBundle(Tab.WEBVIEW + oldCurrentTab) - .getBoolean(Tab.INCOGNITO)) { - currentTab = oldCurrentTab; + !inState.getBundle(Long.toString(oldcurrent)).getBoolean(Tab.INCOGNITO)) { + current = oldcurrent; } else { - for (int i = 0; i < numTabs; i++) { - if (!inState.getBundle(Tab.WEBVIEW + i) - .getBoolean(Tab.INCOGNITO)) { - currentTab = i; + // pick first non incognito tab + for (long id : ids) { + if (!inState.getBundle(Long.toString(id)).getBoolean(Tab.INCOGNITO)) { + current = id; break; } } } - - return currentTab; + return current; } /** * Restore the state of all the tabs. - * @param currentTab The tab index to restore. + * @param currentId The tab id to restore. * @param inState The saved state of all the tabs. * @param restoreIncognitoTabs Restoring private browsing tabs * @param restoreAll All webviews get restored, not just the current tab @@ -330,29 +326,29 @@ class TabControl { * @return True if there were previous tabs that were restored. False if * there was no saved state or restoring the state failed. */ - void restoreState(Bundle inState, int currentTab, + void restoreState(Bundle inState, long currentId, boolean restoreIncognitoTabs, boolean restoreAll) { - if (currentTab == -1) { + if (currentId == -1) { return; } - - // If currentTab is valid, numTabs must be present. - final int numTabs = inState.getInt(Tab.NUMTABS, -1); - - // Map saved tab indices to new indices, in case any incognito tabs - // need to not be restored. - HashMap<Integer, Integer> originalTabIndices = new HashMap<Integer, Integer>(); - originalTabIndices.put(-1, -1); - for (int i = 0; i < numTabs; i++) { - Bundle state = inState.getBundle(Tab.WEBVIEW + i); - - if (!restoreIncognitoTabs && state != null && state.getBoolean(Tab.INCOGNITO)) { - originalTabIndices.put(i, -1); - } else if (i == currentTab || restoreAll) { + long[] ids = inState.getLongArray(POSITIONS); + long maxId = -Long.MAX_VALUE; + HashMap<Long, Tab> tabMap = new HashMap<Long, Tab>(); + for (long id : ids) { + if (id > maxId) { + maxId = id; + } + final String idkey = Long.toString(id); + Bundle state = inState.getBundle(idkey); + if (!restoreIncognitoTabs && state != null + && state.getBoolean(Tab.INCOGNITO)) { + // ignore tab + } else if (id == currentId || restoreAll) { Tab t = createNewTab(); + tabMap.put(id, t); // Me must set the current tab before restoring the state // so that all the client classes are set. - if (i == currentTab) { + if (id == currentId) { setCurrentTab(t); } if (!t.restoreState(state)) { @@ -360,11 +356,12 @@ class TabControl { t.getWebView().loadUrl(BrowserSettings.getInstance() .getHomePage()); } - originalTabIndices.put(i, getTabCount() - 1); } else { // Create a new tab and don't restore the state yet, add it // to the tab list - Tab t = new Tab(mController, null, false, null, null); + Tab t = new Tab(mController, null); + t.setId(id); + tabMap.put(id, t); if (state != null) { t.setSavedState(state); // Need to maintain the app id and original url so we @@ -374,21 +371,22 @@ class TabControl { mTabs.add(t); // added the tab to the front as they are not current mTabQueue.add(0, t); - originalTabIndices.put(i, getTabCount() - 1); } - } - - // Rebuild the tree of tabs. Do this after all tabs have been - // created/restored so that the parent tab exists. - for (int i = 0; i < numTabs; i++) { - final Bundle b = inState.getBundle(Tab.WEBVIEW + i); - final Tab t = getTab(i); - if (b != null && t != null) { - final Integer parentIndex = originalTabIndices.get(b.getInt(Tab.PARENTTAB, -1)); - if (parentIndex != -1) { - final Tab parent = getTab(parentIndex); + // make sure that there is no id overlap between the restored + // and new tabs + sNextId = maxId + 1; + + } + // restore parent/child relationships + for (long id : ids) { + final Tab tab = tabMap.get(id); + final Bundle b = inState.getBundle(Long.toString(id)); + if ((b != null) && (tab != null)) { + final long parentId = b.getLong(Tab.PARENTTAB, -1); + if (parentId != -1) { + final Tab parent = tabMap.get(parentId); if (parent != null) { - parent.addChildTab(t); + parent.addChildTab(tab); } } } @@ -443,7 +441,7 @@ class TabControl { for (Tab t : mTabQueue) { if (t != null && t.getWebView() != null) { openTabCount++; - if (t != current && t != current.getParentTab()) { + if (t != current && t != current.getParent()) { tabsToGo.add(t); } } @@ -462,9 +460,7 @@ class TabControl { * @param view The WebView used to find the tab. */ Tab getTabFromView(WebView view) { - final int size = getTabCount(); - for (int i = 0; i < size; i++) { - final Tab t = getTab(i); + for (Tab t : mTabs) { if (t.getSubWebView() == view || t.getWebView() == view) { return t; } @@ -476,13 +472,11 @@ class TabControl { * Return the tab with the matching application id. * @param id The application identifier. */ - Tab getTabFromId(String id) { + Tab getTabFromAppId(String id) { if (id == null) { return null; } - final int size = getTabCount(); - for (int i = 0; i < size; i++) { - final Tab t = getTab(i); + for (Tab t : mTabs) { if (id.equals(t.getAppId())) { return t; } @@ -494,9 +488,7 @@ class TabControl { * Stop loading in all opened WebView including subWindows. */ void stopAllLoading() { - final int size = getTabCount(); - for (int i = 0; i < size; i++) { - final Tab t = getTab(i); + for (Tab t : mTabs) { final WebView webview = t.getWebView(); if (webview != null) { webview.stopLoading(); @@ -539,10 +531,8 @@ class TabControl { return t; } // Now check all the rest. - final int size = getTabCount(); - for (int i = 0; i < size; i++) { - t = getTab(i); - if (tabMatchesUrl(t, url)) { + for (Tab tab : mTabs) { + if (tabMatchesUrl(tab, url)) { return t; } } diff --git a/src/com/android/browser/TitleBar.java b/src/com/android/browser/TitleBar.java deleted file mode 100644 index 686416c..0000000 --- a/src/com/android/browser/TitleBar.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.browser; - -import android.app.Activity; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; -import android.speech.RecognizerIntent; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.TextUtils; -import android.text.style.ImageSpan; -import android.view.ContextMenu; -import android.view.LayoutInflater; -import android.view.MenuInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnFocusChangeListener; -import android.widget.ImageButton; -import android.widget.ImageView; - -/** - * This class represents a title bar for a particular "tab" or "window" in the - * browser. - */ -public class TitleBar extends TitleBarBase implements OnFocusChangeListener, - OnClickListener { - - private Activity mActivity; - private ImageButton mBookmarkButton; - private PageProgressView mHorizontalProgress; - private ImageButton mStopButton; - private Drawable mBookmarkDrawable; - private Drawable mVoiceDrawable; - private boolean mInLoad; - private Intent mVoiceSearchIntent; - private ImageSpan mArcsSpan; - - public TitleBar(Activity activity, UiController controller, PhoneUi ui) { - super(activity, controller, ui); - LayoutInflater factory = LayoutInflater.from(activity); - factory.inflate(R.layout.title_bar, this); - mActivity = activity; - - mUrlInput = (UrlInputView) findViewById(R.id.url_input); - mUrlInput.setCompoundDrawablePadding(5); - mUrlInput.setContainer(this); - mUrlInput.setSelectAllOnFocus(true); - mUrlInput.setController(mUiController); - mUrlInput.setUrlInputListener(this); - mUrlInput.setOnFocusChangeListener(this); - - mLockIcon = (ImageView) findViewById(R.id.lock); - mFavicon = (ImageView) findViewById(R.id.favicon); - mStopButton = (ImageButton) findViewById(R.id.stop); - mBookmarkButton = (ImageButton) findViewById(R.id.bookmark); - mStopButton.setOnClickListener(this); - mBookmarkButton.setOnClickListener(this); - - mHorizontalProgress = (PageProgressView) findViewById( - R.id.progress_horizontal); - mVoiceSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); - mVoiceSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, - RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); - // This extra tells voice search not to send the application id in its - // results intent - http://b/2546173 - // - // TODO: Make a constant for this extra. - mVoiceSearchIntent.putExtra("android.speech.extras.SEND_APPLICATION_ID_EXTRA", - false); - PackageManager pm = activity.getPackageManager(); - ResolveInfo ri = pm.resolveActivity(mVoiceSearchIntent, - PackageManager.MATCH_DEFAULT_ONLY); - Resources resources = getResources(); - if (ri == null) { - mVoiceSearchIntent = null; - } else { - mVoiceDrawable = resources.getDrawable( - android.R.drawable.ic_btn_speak_now); - } - mBookmarkDrawable = mBookmarkButton.getDrawable(); - mArcsSpan = new ImageSpan(activity, R.drawable.arcs, - ImageSpan.ALIGN_BASELINE); - } - - @Override - public void createContextMenu(ContextMenu menu) { - MenuInflater inflater = mActivity.getMenuInflater(); - inflater.inflate(R.menu.title_context, menu); - mActivity.onCreateContextMenu(menu, this, null); - } - - /** - * Change the TitleBar to or from voice mode. If there is no package to - * handle voice search, the TitleBar cannot be set to voice mode. - */ - @Override - void setInVoiceMode(boolean inVoiceMode) { - if (mInVoiceMode == inVoiceMode) return; - mInVoiceMode = inVoiceMode && mVoiceSearchIntent != null; - Drawable titleDrawable; - if (mInVoiceMode) { - mBookmarkButton.setImageDrawable(mVoiceDrawable); - mUrlInput.setEllipsize(null); - mBookmarkButton.setVisibility(View.VISIBLE); - mStopButton.setVisibility(View.GONE); - } else { - if (mInLoad) { - mBookmarkButton.setVisibility(View.GONE); - mStopButton.setVisibility(View.VISIBLE); - } else { - mBookmarkButton.setVisibility(View.VISIBLE); - mStopButton.setVisibility(View.GONE); - mBookmarkButton.setImageDrawable(mBookmarkDrawable); - } - mUrlInput.setEllipsize(TextUtils.TruncateAt.END); - } - mUrlInput.setSingleLine(!mInVoiceMode); - } - - /** - * Update the progress, from 0 to 100. - */ - @Override - void setProgress(int newProgress) { - if (newProgress >= PROGRESS_MAX) { - mHorizontalProgress.setVisibility(View.GONE); - if (!mInVoiceMode) { - mBookmarkButton.setImageDrawable(mBookmarkDrawable); - mBookmarkButton.setVisibility(View.VISIBLE); - mStopButton.setVisibility(View.GONE); - } - mInLoad = false; - } else { - mHorizontalProgress.setProgress(newProgress * PageProgressView.MAX_PROGRESS - / PROGRESS_MAX); - if (!mInLoad) { - mHorizontalProgress.setVisibility(View.VISIBLE); - if (!mInVoiceMode) { - mBookmarkButton.setVisibility(View.GONE); - mStopButton.setVisibility(View.VISIBLE); - } - mInLoad = true; - } - } - } - - /** - * Update the text displayed in the title bar. - * @param title String to display. If null, the new tab string will be - * shown. - */ - @Override - void setDisplayTitle(String title) { - if (title == null) { - mUrlInput.setText(R.string.new_tab); - } else { - if (mInVoiceMode) { - // Add two spaces. The second one will be replaced with an - // image, and the first one will put space between it and the - // text - SpannableString spannable = new SpannableString(title + " "); - int end = spannable.length(); - spannable.setSpan(mArcsSpan, end - 1, end, - Spanned.SPAN_MARK_POINT); - mUrlInput.setText(spannable); - } else { - mUrlInput.setText(title); - } - } - } - - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (v == mUrlInput && hasFocus) { - mActivity.closeOptionsMenu(); - } - } - - @Override - public void onClick(View v) { - if (v == mStopButton) { - mUiController.stopLoading(); - } else if (v == mBookmarkButton) { - mUiController.bookmarkCurrentPage(AddBookmarkPage.DEFAULT_FOLDER_ID, - true); - } - } - - @Override - public void setCurrentUrlIsBookmark(boolean isBookmark) { - mBookmarkButton.setActivated(isBookmark); - } -} diff --git a/src/com/android/browser/TitleBarBase.java b/src/com/android/browser/TitleBarBase.java index b905d4e..4dc960c 100644 --- a/src/com/android/browser/TitleBarBase.java +++ b/src/com/android/browser/TitleBarBase.java @@ -16,27 +16,51 @@ package com.android.browser; +import com.android.browser.UI.DropdownChangeListener; import com.android.browser.UrlInputView.UrlInputListener; +import com.android.browser.autocomplete.SuggestedTextController.TextChangeWatcher; +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.ObjectAnimator; import android.app.SearchManager; import android.content.Context; import android.content.Intent; 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.os.Bundle; import android.speech.RecognizerResultsIntent; +import android.text.TextUtils; +import android.view.ContextThemeWrapper; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnFocusChangeListener; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; +import android.view.animation.AnimationUtils; +import android.webkit.WebView; +import android.widget.AbsoluteLayout; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.Spinner; +import android.widget.TextView; + +import java.util.List; /** * Base class for a title bar used by the browser. */ -public class TitleBarBase extends LinearLayout implements UrlInputListener { +public class TitleBarBase extends RelativeLayout + implements OnClickListener, OnFocusChangeListener, UrlInputListener, + TextChangeWatcher, DeviceAccountLogin.AutoLoginCallback { protected static final int PROGRESS_MAX = 100; @@ -44,22 +68,206 @@ public class TitleBarBase extends LinearLayout implements UrlInputListener { protected ImageView mFavicon; protected ImageView mLockIcon; - protected Drawable mGenericFavicon; protected UiController mUiController; protected BaseUi mBaseUi; + protected FrameLayout mParent; + protected PageProgressView mProgress; protected UrlInputView mUrlInput; protected boolean mInVoiceMode; + protected View mContainer; + + + // Auto-login UI + protected View mAutoLogin; + protected Spinner mAutoLoginAccount; + protected Button mAutoLoginLogin; + protected ProgressBar mAutoLoginProgress; + protected TextView mAutoLoginError; + protected View mAutoLoginCancel; + protected DeviceAccountLogin mAutoLoginHandler; + protected ArrayAdapter<String> mAccountsAdapter; + protected boolean mUseQuickControls; - public TitleBarBase(Context context, UiController controller, BaseUi ui) { + //state + protected boolean mShowing; + protected boolean mInLoad; + protected boolean mSkipTitleBarAnimations; + private Animator mTitleBarAnimator; + + public TitleBarBase(Context context, UiController controller, BaseUi ui, + FrameLayout parent) { super(context, null); mUiController = controller; mBaseUi = ui; - mGenericFavicon = context.getResources().getDrawable( - R.drawable.app_web_browser_sm); + mParent = parent; + } + + protected void initLayout(Context context, int layoutId) { + LayoutInflater factory = LayoutInflater.from(context); + factory.inflate(layoutId, this); + mContainer = findViewById(R.id.taburlbar); + mProgress = (PageProgressView) findViewById(R.id.progress); + mUrlInput = (UrlInputView) findViewById(R.id.url); + mLockIcon = (ImageView) findViewById(R.id.lock); + mUrlInput.setUrlInputListener(this); + mUrlInput.setController(mUiController); + mUrlInput.setOnFocusChangeListener(this); + mUrlInput.setSelectAllOnFocus(true); + mUrlInput.addQueryTextWatcher(this); + mAutoLogin = findViewById(R.id.autologin); + mAutoLoginAccount = (Spinner) findViewById(R.id.autologin_account); + mAutoLoginLogin = (Button) findViewById(R.id.autologin_login); + mAutoLoginLogin.setOnClickListener(this); + mAutoLoginProgress = (ProgressBar) findViewById(R.id.autologin_progress); + mAutoLoginError = (TextView) findViewById(R.id.autologin_error); + mAutoLoginCancel = mAutoLogin.findViewById(R.id.autologin_close); + mAutoLoginCancel.setOnClickListener(this); + } + + protected void setupUrlInput() { + } + + protected void setUseQuickControls(boolean use) { + mUseQuickControls = use; + setLayoutParams(makeLayoutParams()); + } + + void setShowProgressOnly(boolean progress) { + if (progress && !inAutoLogin()) { + mContainer.setVisibility(View.GONE); + } else { + mContainer.setVisibility(View.VISIBLE); + } + } + + void setSkipTitleBarAnimations(boolean skip) { + mSkipTitleBarAnimations = skip; + } + + void show() { + if (mUseQuickControls) { + mParent.addView(this); + } else { + if (!mSkipTitleBarAnimations) { + cancelTitleBarAnimation(false); + int visibleHeight = getVisibleTitleHeight(); + float startPos = (-getEmbeddedHeight() + visibleHeight); + if (getTranslationY() != 0) { + startPos = Math.max(startPos, getTranslationY()); + } + mTitleBarAnimator = ObjectAnimator.ofFloat(this, + "translationY", + startPos, 0); + mTitleBarAnimator.start(); + } + mBaseUi.setTitleGravity(Gravity.TOP); + } + mShowing = true; + } + + void hide() { + if (mUseQuickControls) { + mParent.removeView(this); + } else { + if (!mSkipTitleBarAnimations) { + cancelTitleBarAnimation(false); + int visibleHeight = getVisibleTitleHeight(); + mTitleBarAnimator = ObjectAnimator.ofFloat(this, + "translationY", getTranslationY(), + (-getEmbeddedHeight() + visibleHeight)); + mTitleBarAnimator.addListener(mHideTileBarAnimatorListener); + mTitleBarAnimator.start(); + } else { + mBaseUi.setTitleGravity(Gravity.NO_GRAVITY); + } + } + mShowing = false; + } + + boolean isShowing() { + return mShowing; + } + + void cancelTitleBarAnimation(boolean reset) { + if (mTitleBarAnimator != null) { + mTitleBarAnimator.cancel(); + mTitleBarAnimator = null; + } + if (reset) { + setTranslationY(0); + } + } + + private AnimatorListener mHideTileBarAnimatorListener = new AnimatorListener() { + + boolean mWasCanceled; + @Override + public void onAnimationStart(Animator animation) { + mWasCanceled = false; + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!mWasCanceled) { + setTranslationY(0); + } + mBaseUi.setTitleGravity(Gravity.NO_GRAVITY); + } + + @Override + public void onAnimationCancel(Animator animation) { + mWasCanceled = true; + } + }; + + private int getVisibleTitleHeight() { + Tab tab = mBaseUi.getActiveTab(); + WebView webview = tab != null ? tab.getWebView() : null; + return webview != null ? webview.getVisibleTitleHeight() : 0; } - /* package */ void setProgress(int newProgress) {} - /* package */ void setDisplayTitle(String title) {} + /** + * Update the progress, from 0 to 100. + */ + void setProgress(int newProgress) { + if (newProgress >= PROGRESS_MAX) { + mProgress.setProgress(PageProgressView.MAX_PROGRESS); + mProgress.setVisibility(View.GONE); + mInLoad = false; + onProgressStopped(); + // check if needs to be hidden + if (!isEditingUrl() && !inAutoLogin()) { + hide(); + if (mUseQuickControls) { + setShowProgressOnly(false); + } + } + } else { + if (!mInLoad) { + mProgress.setVisibility(View.VISIBLE); + mInLoad = true; + onProgressStarted(); + } + mProgress.setProgress(newProgress * PageProgressView.MAX_PROGRESS + / PROGRESS_MAX); + if (!mShowing) { + if (mUseQuickControls && !isEditingUrl()) { + setShowProgressOnly(true); + } + show(); + } + } + } + + protected void onProgressStarted() { + } + + protected void onProgressStopped() { + } /* package */ void setLock(Drawable d) { assert mLockIcon != null; @@ -72,28 +280,206 @@ public class TitleBarBase extends LinearLayout implements UrlInputListener { } /* package */ void setFavicon(Bitmap icon) { - assert mFavicon != null; - Drawable[] array = new Drawable[3]; - array[0] = new PaintDrawable(Color.BLACK); - PaintDrawable p = new PaintDrawable(Color.WHITE); - array[1] = p; - if (icon == null) { - array[2] = mGenericFavicon; + mFavicon.setImageDrawable(mBaseUi.getFaviconDrawable(icon)); + } + + public int getEmbeddedHeight() { + int height = mContainer.getHeight(); + if (mAutoLogin.getVisibility() == View.VISIBLE) { + height += mAutoLogin.getHeight(); + } + return height; + } + + protected void updateAutoLogin(Tab tab, boolean animate) { + DeviceAccountLogin login = tab.getDeviceAccountLogin(); + if (login != null) { + mAutoLoginHandler = login; + ContextThemeWrapper wrapper = new ContextThemeWrapper(mContext, + android.R.style.Theme_Holo_Light); + mAccountsAdapter = new ArrayAdapter<String>(wrapper, + android.R.layout.simple_spinner_item, login.getAccountNames()); + mAccountsAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + mAutoLoginAccount.setAdapter(mAccountsAdapter); + mAutoLoginAccount.setSelection(0); + mAutoLoginAccount.setEnabled(true); + mAutoLoginLogin.setEnabled(true); + mAutoLoginProgress.setVisibility(View.INVISIBLE); + mAutoLoginError.setVisibility(View.GONE); + switch (login.getState()) { + case DeviceAccountLogin.PROCESSING: + mAutoLoginAccount.setEnabled(false); + mAutoLoginLogin.setEnabled(false); + mAutoLoginProgress.setVisibility(View.VISIBLE); + break; + case DeviceAccountLogin.FAILED: + mAutoLoginProgress.setVisibility(View.INVISIBLE); + mAutoLoginError.setVisibility(View.VISIBLE); + break; + case DeviceAccountLogin.INITIAL: + break; + default: + throw new IllegalStateException(); + } + showAutoLogin(animate); + } else { + hideAutoLogin(animate); + } + } + + protected void showAutoLogin(boolean animate) { + if (mUseQuickControls) { + mBaseUi.showTitleBar(); + } + mAutoLogin.setVisibility(View.VISIBLE); + if (animate) { + mAutoLogin.startAnimation(AnimationUtils.loadAnimation( + getContext(), R.anim.autologin_enter)); + } + } + + protected void hideAutoLogin(boolean animate) { + mAutoLoginHandler = null; + if (mUseQuickControls) { + mBaseUi.hideTitleBar(); + mAutoLogin.setVisibility(View.GONE); + mBaseUi.refreshWebView(); } else { - array[2] = new BitmapDrawable(icon); + if (animate) { + Animation anim = AnimationUtils.loadAnimation(getContext(), + R.anim.autologin_exit); + anim.setAnimationListener(new AnimationListener() { + @Override + public void onAnimationEnd(Animation a) { + mAutoLogin.setVisibility(View.GONE); + mBaseUi.refreshWebView(); + } + + @Override + public void onAnimationStart(Animation a) { + } + + @Override + public void onAnimationRepeat(Animation a) { + } + }); + mAutoLogin.startAnimation(anim); + } else if (mAutoLogin.getAnimation() == null) { + mAutoLogin.setVisibility(View.GONE); + mBaseUi.refreshWebView(); + } } - LayerDrawable d = new LayerDrawable(array); - d.setLayerInset(1, 1, 1, 1, 1); - d.setLayerInset(2, 2, 2, 2, 2); - mFavicon.setImageDrawable(d); } - /* package */ void setInVoiceMode(boolean inVoiceMode) {} + @Override + public void loginFailed() { + mAutoLoginAccount.setEnabled(true); + mAutoLoginLogin.setEnabled(true); + mAutoLoginProgress.setVisibility(View.INVISIBLE); + mAutoLoginError.setVisibility(View.VISIBLE); + } + - /* package */ void setIncognitoMode(boolean incognito) {} + protected boolean inAutoLogin() { + return mAutoLoginHandler != null; + } - public int getEmbeddedHeight() { - return getHeight(); + @Override + public void onClick(View v) { + if (mAutoLoginCancel == v) { + if (mAutoLoginHandler != null) { + mAutoLoginHandler.cancel(); + mAutoLoginHandler = null; + } + hideAutoLogin(true); + } else if (mAutoLoginLogin == v) { + if (mAutoLoginHandler != null) { + mAutoLoginAccount.setEnabled(false); + mAutoLoginLogin.setEnabled(false); + mAutoLoginProgress.setVisibility(View.VISIBLE); + mAutoLoginError.setVisibility(View.GONE); + mAutoLoginHandler.login( + mAutoLoginAccount.getSelectedItemPosition(), this); + } + } + } + + @Override + public void onFocusChange(View view, boolean hasFocus) { + // if losing focus and not in touch mode, leave as is + if (hasFocus || view.isInTouchMode() || mUrlInput.needsUpdate()) { + setFocusState(hasFocus); + } + if (hasFocus) { + mUrlInput.forceIme(); + if (mInVoiceMode) { + mUrlInput.forceFilter(); + } + } else if (!mUrlInput.needsUpdate()) { + mUrlInput.dismissDropDown(); + mUrlInput.hideIME(); + if (mUrlInput.getText().length() == 0) { + Tab currentTab = mUiController.getTabControl().getCurrentTab(); + if (currentTab != null) { + mUrlInput.setText(currentTab.getUrl(), false); + } + } + } + mUrlInput.clearNeedsUpdate(); + } + + protected void setFocusState(boolean focus) { + if (focus) { + updateSearchMode(false); + } + } + + protected void updateSearchMode(boolean userEdited) { + setSearchMode(!userEdited || TextUtils.isEmpty(mUrlInput.getUserText())); + } + + protected void setSearchMode(boolean voiceSearchEnabled) {} + + boolean isEditingUrl() { + return mUrlInput.hasFocus(); + } + + void stopEditingUrl() { + mUrlInput.clearFocus(); + } + + void setDisplayTitle(String title) { + if (!isEditingUrl()) { + mUrlInput.setText(title, false); + } + } + + // UrlInput text watcher + + @Override + public void onTextChanged(String newText) { + if (mUrlInput.hasFocus()) { + // check if input field is empty and adjust voice search state + updateSearchMode(true); + // clear voice mode when user types + setInVoiceMode(false, null); + } + } + + // voicesearch + + public void setInVoiceMode(boolean voicemode, List<String> voiceResults) { + mInVoiceMode = voicemode; + mUrlInput.setVoiceResults(voiceResults); + } + + void setIncognitoMode(boolean incognito) { + mUrlInput.setIncognitoMode(incognito); + } + + void clearCompletions() { + mUrlInput.setSuggestedText(null); } // UrlInputListener implementation @@ -157,4 +543,58 @@ public class TitleBarBase extends LinearLayout implements UrlInputListener { public void setCurrentUrlIsBookmark(boolean isBookmark) { } + @Override + public boolean dispatchKeyEventPreIme(KeyEvent evt) { + if (evt.getKeyCode() == KeyEvent.KEYCODE_BACK) { + // catch back key in order to do slightly more cleanup than usual + mUrlInput.clearFocus(); + return true; + } + return super.dispatchKeyEventPreIme(evt); + } + + protected WebView getCurrentWebView() { + Tab t = mBaseUi.getActiveTab(); + if (t != null) { + return t.getWebView(); + } else { + return null; + } + } + + void registerDropdownChangeListener(DropdownChangeListener d) { + mUrlInput.registerDropdownChangeListener(d); + } + + /** + * called from the Ui when the user wants to edit + * @param clearInput clear the input field + */ + void startEditingUrl(boolean clearInput) { + // editing takes preference of progress + mContainer.setVisibility(View.VISIBLE); + if (mUseQuickControls) { + mProgress.setVisibility(View.GONE); + } + if (!mUrlInput.hasFocus()) { + mUrlInput.requestFocus(); + } + if (clearInput) { + mUrlInput.setText(""); + } else if (mInVoiceMode) { + mUrlInput.showDropDown(); + } + } + + private ViewGroup.LayoutParams makeLayoutParams() { + if (mUseQuickControls) { + return new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT); + } else { + return new AbsoluteLayout.LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, + 0, 0); + } + } + } diff --git a/src/com/android/browser/TitleBarPhone.java b/src/com/android/browser/TitleBarPhone.java new file mode 100644 index 0000000..9242f99 --- /dev/null +++ b/src/com/android/browser/TitleBarPhone.java @@ -0,0 +1,151 @@ +/* + * 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 com.android.browser.autocomplete.SuggestedTextController.TextChangeWatcher; + +import android.app.Activity; +import android.content.Context; +import android.view.ContextMenu; +import android.view.MenuInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnFocusChangeListener; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import java.util.List; + +/** + * This class represents a title bar for a particular "tab" or "window" in the + * browser. + */ +public class TitleBarPhone extends TitleBarBase implements OnFocusChangeListener, + OnClickListener, TextChangeWatcher { + + private Activity mActivity; + private ImageView mStopButton; + private ImageView mVoiceButton; + private boolean mHasLockIcon; + + public TitleBarPhone(Activity activity, UiController controller, PhoneUi ui, + FrameLayout parent) { + super(activity, controller, ui, parent); + mActivity = activity; + initLayout(activity, R.layout.title_bar); + } + + @Override + protected void initLayout(Context context, int layoutId) { + super.initLayout(context, layoutId); + mLockIcon = (ImageView) findViewById(R.id.lock); + mFavicon = (ImageView) findViewById(R.id.favicon); + mStopButton = (ImageView) findViewById(R.id.stop); + mStopButton.setOnClickListener(this); + mVoiceButton = (ImageView) findViewById(R.id.voice); + mVoiceButton.setOnClickListener(this); + setFocusState(false); + } + + @Override + public void createContextMenu(ContextMenu menu) { + MenuInflater inflater = mActivity.getMenuInflater(); + inflater.inflate(R.menu.title_context, menu); + mActivity.onCreateContextMenu(menu, this, null); + } + + @Override + public void setInVoiceMode(boolean voicemode, List<String> voiceResults) { + super.setInVoiceMode(voicemode, voiceResults); + } + + @Override + protected void setSearchMode(boolean voiceSearchEnabled) { + boolean showvoicebutton = voiceSearchEnabled && + mUiController.supportsVoiceSearch(); + mVoiceButton.setVisibility(showvoicebutton ? View.VISIBLE : + View.GONE); + } + + @Override + protected void setFocusState(boolean focus) { + super.setFocusState(focus); + if (focus) { + mHasLockIcon = (mLockIcon.getVisibility() == View.VISIBLE); + mLockIcon.setVisibility(View.GONE); + mStopButton.setVisibility(View.GONE); + mVoiceButton.setVisibility(View.VISIBLE); + } else { + mLockIcon.setVisibility(mHasLockIcon ? View.VISIBLE : View.GONE); + if (mInLoad) { + mStopButton.setVisibility(View.VISIBLE); + } else { + mStopButton.setVisibility(View.GONE); + } + mVoiceButton.setVisibility(View.GONE); + } + } + + @Override + protected void onProgressStarted() { + setFocusState(mUrlInput.hasFocus()); + } + + @Override + protected void onProgressStopped() { + setFocusState(mUrlInput.hasFocus()); + } + + /** + * Update the text displayed in the title bar. + * @param title String to display. If null, the new tab string will be + * shown. + */ + @Override + void setDisplayTitle(String title) { + if (title == null) { + mUrlInput.setText(R.string.new_tab); + } else { + mUrlInput.setText(title); + } + } + + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (v == mUrlInput) { + if (hasFocus) { + mActivity.closeOptionsMenu(); + } + } + super.onFocusChange(v, hasFocus); + if (!hasFocus) { + mBaseUi.hideTitleBar(); + } + } + + @Override + public void onClick(View v) { + if (v == mStopButton) { + mUiController.stopLoading(); + } else if (v == mVoiceButton) { + mUiController.startVoiceSearch(); + } else { + super.onClick(v); + } + } + +} diff --git a/src/com/android/browser/TitleBarXLarge.java b/src/com/android/browser/TitleBarXLarge.java index e91597f..8c03e4c 100644 --- a/src/com/android/browser/TitleBarXLarge.java +++ b/src/com/android/browser/TitleBarXLarge.java @@ -16,41 +16,24 @@ package com.android.browser; -import com.android.browser.UI.DropdownChangeListener; import com.android.browser.autocomplete.SuggestedTextController.TextChangeWatcher; -import com.android.browser.search.SearchEngine; -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.app.Activity; import android.content.Context; -import android.content.res.Configuration; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.preference.PreferenceManager; import android.text.TextUtils; -import android.view.ContextThemeWrapper; -import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; -import android.view.ViewGroup; -import android.view.animation.Animation; -import android.view.animation.Animation.AnimationListener; -import android.view.animation.AnimationUtils; import android.webkit.WebView; -import android.widget.AbsoluteLayout; -import android.widget.ArrayAdapter; -import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.Spinner; -import android.widget.TextView; import java.util.List; @@ -66,39 +49,25 @@ public class TitleBarXLarge extends TitleBarBase private Drawable mStopDrawable; private Drawable mReloadDrawable; - private View mContainer; + private View mUrlContainer; private ImageButton mBackButton; private ImageButton mForwardButton; private ImageView mStar; private ImageView mUrlIcon; private ImageView mSearchButton; - private View mUrlContainer; - private View mNavButtons; private View mGoButton; private ImageView mStopButton; private View mAllButton; private View mClearButton; private ImageView mVoiceSearch; - private PageProgressView mProgressView; private Drawable mFocusDrawable; private Drawable mUnfocusDrawable; - // Auto-login UI - private View mAutoLogin; - private Spinner mAutoLoginAccount; - private Button mAutoLoginLogin; - private ProgressBar mAutoLoginProgress; - private TextView mAutoLoginError; - private ImageButton mAutoLoginCancel; - private DeviceAccountLogin mAutoLoginHandler; - private ArrayAdapter<String> mAccountsAdapter; - - private boolean mInLoad; - private boolean mUseQuickControls; - private boolean mHideNavButtons; + private boolean mHasFocus = false; + private BrowserSettings mSettings; public TitleBarXLarge(Activity activity, UiController controller, - XLargeUi ui) { - super(activity, controller, ui); + XLargeUi ui, FrameLayout parent) { + super(activity, controller, ui, parent); mUi = ui; Resources resources = activity.getResources(); mStopDrawable = resources.getDrawable(R.drawable.ic_stop_holo_dark); @@ -108,17 +77,15 @@ public class TitleBarXLarge extends TitleBarBase mUnfocusDrawable = resources.getDrawable( R.drawable.textfield_default_holo_dark); mInVoiceMode = false; - initLayout(activity); + mSettings = BrowserSettings.getInstance(); + initLayout(activity, R.layout.url_bar); + PreferenceManager.getDefaultSharedPreferences(activity) + .registerOnSharedPreferenceChangeListener(mSharedPrefsListener); } - private void initLayout(Context context) { - Resources res = mContext.getResources(); - mHideNavButtons = res.getBoolean(R.bool.hide_nav_buttons); - LayoutInflater factory = LayoutInflater.from(context); - factory.inflate(R.layout.url_bar, this); - - mContainer = findViewById(R.id.taburlbar); - mUrlInput = (UrlInputView) findViewById(R.id.url_focused); + @Override + protected void initLayout(Context context, int layoutId) { + super.initLayout(context, layoutId); mAllButton = findViewById(R.id.all_btn); // TODO: Change enabled states based on whether you can go // back/forward. Probably should be done inside onPageStarted. @@ -132,8 +99,6 @@ public class TitleBarXLarge extends TitleBarBase mGoButton = findViewById(R.id.go); mClearButton = findViewById(R.id.clear); mVoiceSearch = (ImageView) findViewById(R.id.voicesearch); - mProgressView = (PageProgressView) findViewById(R.id.progress); - mNavButtons = findViewById(R.id.navbuttons); mUrlContainer = findViewById(R.id.urlbar_focused); mBackButton.setOnClickListener(this); mForwardButton.setOnClickListener(this); @@ -144,45 +109,11 @@ public class TitleBarXLarge extends TitleBarBase mGoButton.setOnClickListener(this); mClearButton.setOnClickListener(this); mVoiceSearch.setOnClickListener(this); - mUrlInput.setUrlInputListener(this); + mUrlIcon.setOnClickListener(this); mUrlInput.setContainer(mUrlContainer); - mUrlInput.setController(mUiController); - mUrlInput.setOnFocusChangeListener(this); - mUrlInput.setSelectAllOnFocus(true); - mUrlInput.addQueryTextWatcher(this); - mAutoLogin = findViewById(R.id.autologin); - mAutoLoginAccount = (Spinner) findViewById(R.id.autologin_account); - mAutoLoginLogin = (Button) findViewById(R.id.autologin_login); - mAutoLoginLogin.setOnClickListener(this); - mAutoLoginProgress = - (ProgressBar) findViewById(R.id.autologin_progress); - mAutoLoginError = (TextView) findViewById(R.id.autologin_error); - mAutoLoginCancel = - (ImageButton) mAutoLogin.findViewById(R.id.autologin_close); - mAutoLoginCancel.setOnClickListener(this); - setFocusState(false); } - @Override - public void onConfigurationChanged(Configuration config) { - super.onConfigurationChanged(config); - Resources res = mContext.getResources(); - mHideNavButtons = res.getBoolean(R.bool.hide_nav_buttons); - if (mUrlInput.hasFocus()) { - if (mHideNavButtons && (mNavButtons.getVisibility() == View.VISIBLE)) { - int aw = mNavButtons.getMeasuredWidth(); - mNavButtons.setVisibility(View.GONE); - mNavButtons.setAlpha(0f); - mNavButtons.setTranslationX(-aw); - } else if (!mHideNavButtons && (mNavButtons.getVisibility() == View.GONE)) { - mNavButtons.setVisibility(View.VISIBLE); - mNavButtons.setAlpha(1f); - mNavButtons.setTranslationX(0); - } - } - } - void updateNavigationState(Tab tab) { WebView web = tab.getWebView(); if (web != null) { @@ -193,100 +124,7 @@ public class TitleBarXLarge extends TitleBarBase ? R.drawable.ic_forward_holo_dark : R.drawable.ic_forward_disabled_holo_dark); } - } - - void updateAutoLogin(Tab tab, boolean animate) { - DeviceAccountLogin login = tab.getDeviceAccountLogin(); - if (login != null) { - mAutoLoginHandler = login; - mAutoLogin.setVisibility(View.VISIBLE); - ContextThemeWrapper wrapper = new ContextThemeWrapper(mContext, - android.R.style.Theme_Holo_Light); - mAccountsAdapter = new ArrayAdapter<String>(wrapper, - android.R.layout.simple_spinner_item, login.getAccountNames()); - mAccountsAdapter.setDropDownViewResource( - android.R.layout.simple_spinner_dropdown_item); - mAutoLoginAccount.setAdapter(mAccountsAdapter); - mAutoLoginAccount.setSelection(0); - mAutoLoginAccount.setEnabled(true); - mAutoLoginLogin.setEnabled(true); - mAutoLoginProgress.setVisibility(View.GONE); - mAutoLoginError.setVisibility(View.GONE); - switch (login.getState()) { - case DeviceAccountLogin.PROCESSING: - mAutoLoginAccount.setEnabled(false); - mAutoLoginLogin.setEnabled(false); - mAutoLoginProgress.setVisibility(View.VISIBLE); - break; - case DeviceAccountLogin.FAILED: - mAutoLoginProgress.setVisibility(View.GONE); - mAutoLoginError.setVisibility(View.VISIBLE); - break; - case DeviceAccountLogin.INITIAL: - break; - default: - throw new IllegalStateException(); - } - if (mUseQuickControls) { - mUi.showTitleBar(); - } else { - if (animate) { - mAutoLogin.startAnimation(AnimationUtils.loadAnimation( - getContext(), R.anim.autologin_enter)); - } - } - } else { - mAutoLoginHandler = null; - if (mUseQuickControls) { - mUi.hideTitleBar(); - mAutoLogin.setVisibility(View.GONE); - mUi.refreshWebView(); - } else { - if (animate) { - hideAutoLogin(); - } else if (mAutoLogin.getAnimation() == null) { - mAutoLogin.setVisibility(View.GONE); - mUi.refreshWebView(); - } - } - } - } - - boolean inAutoLogin() { - return mAutoLoginHandler != null; - } - - private ViewGroup.LayoutParams makeLayoutParams() { - if (mUseQuickControls) { - return new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.WRAP_CONTENT); - } else { - return new AbsoluteLayout.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, - 0, 0); - } - } - - @Override - public int getEmbeddedHeight() { - int height = mContainer.getHeight(); - if (mAutoLogin.getVisibility() == View.VISIBLE) { - height += mAutoLogin.getHeight(); - } - return height; - } - - void setUseQuickControls(boolean useQuickControls) { - mUseQuickControls = useQuickControls; - setLayoutParams(makeLayoutParams()); - } - - void setShowProgressOnly(boolean progress) { - if (progress && !inAutoLogin()) { - mContainer.setVisibility(View.GONE); - } else { - mContainer.setVisibility(View.VISIBLE); - } + updateUrlIcon(); } @Override @@ -324,47 +162,6 @@ public class TitleBarXLarge extends TitleBarBase mStar.setActivated(isBookmark); } - /** - * called from the Ui when the user wants to edit - * @param clearInput clear the input field - */ - void startEditingUrl(boolean clearInput) { - // editing takes preference of progress - mContainer.setVisibility(View.VISIBLE); - if (mUseQuickControls) { - mProgressView.setVisibility(View.GONE); - } - if (!mUrlInput.hasFocus()) { - mUrlInput.requestFocus(); - } - if (clearInput) { - mUrlInput.setText(""); - } else if (mInVoiceMode) { - mUrlInput.showDropDown(); - } - } - - boolean isEditingUrl() { - return mUrlInput.hasFocus(); - } - - void stopEditingUrl() { - mUrlInput.clearFocus(); - } - - private void hideAutoLogin() { - Animation anim = AnimationUtils.loadAnimation( - getContext(), R.anim.autologin_exit); - anim.setAnimationListener(new AnimationListener() { - @Override public void onAnimationEnd(Animation a) { - mAutoLogin.setVisibility(View.GONE); - mUi.refreshWebView(); - } - @Override public void onAnimationStart(Animation a) {} - @Override public void onAnimationRepeat(Animation a) {} - }); - mAutoLogin.startAnimation(anim); - } @Override public void onClick(View v) { @@ -390,33 +187,19 @@ public class TitleBarXLarge extends TitleBarBase clearOrClose(); } else if (mVoiceSearch == v) { mUiController.startVoiceSearch(); - } else if (mAutoLoginCancel == v) { - if (mAutoLoginHandler != null) { - mAutoLoginHandler.cancel(); - mAutoLoginHandler = null; - } - hideAutoLogin(); - } else if (mAutoLoginLogin == v) { - if (mAutoLoginHandler != null) { - mAutoLoginAccount.setEnabled(false); - mAutoLoginLogin.setEnabled(false); - mAutoLoginProgress.setVisibility(View.VISIBLE); - mAutoLoginError.setVisibility(View.GONE); - mAutoLoginHandler.login( - mAutoLoginAccount.getSelectedItemPosition(), this); + } else if (mUrlIcon == v) { + WebView web = mUiController.getCurrentWebView(); + if (mSettings.enableUseragentSwitcher() && web != null) { + mSettings.toggleDesktopUseragent(web); + web.loadUrl(web.getOriginalUrl()); + updateUrlIcon(); } + } else { + super.onClick(v); } } @Override - public void loginFailed() { - mAutoLoginAccount.setEnabled(true); - mAutoLoginLogin.setEnabled(true); - mAutoLoginProgress.setVisibility(View.GONE); - mAutoLoginError.setVisibility(View.VISIBLE); - } - - @Override void setFavicon(Bitmap icon) { } private void clearOrClose() { @@ -429,22 +212,46 @@ public class TitleBarXLarge extends TitleBarBase } } - private void setFocusState(boolean focus) { - if (focus) { - if (mHideNavButtons) { - hideNavButtons(); + void updateUrlIcon() { + if (mHasFocus) { + return; + } + if (!mInVoiceMode && mSettings.enableUseragentSwitcher()) { + WebView web = mUiController.getCurrentWebView(); + if (mSettings.hasDesktopUseragent(web)) { + mUrlIcon.setImageResource(R.drawable.ic_ua_desktop); + } else { + mUrlIcon.setImageResource(R.drawable.ic_ua_android); } - mUrlInput.setDropDownWidth(mUrlContainer.getWidth()); - mUrlInput.setDropDownHorizontalOffset(-mUrlInput.getLeft()); + } else { + mUrlIcon.setImageResource(mInVoiceMode ? + R.drawable.ic_search_holo_dark + : R.drawable.ic_web_holo_dark); + } + } + + private OnSharedPreferenceChangeListener mSharedPrefsListener = + new OnSharedPreferenceChangeListener() { + + @Override + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, String key) { + updateUrlIcon(); + } + + }; + + @Override + protected void setFocusState(boolean focus) { + super.setFocusState(focus); + mHasFocus = focus; + if (focus) { mSearchButton.setVisibility(View.GONE); mStar.setVisibility(View.GONE); mClearButton.setVisibility(View.VISIBLE); mUrlIcon.setImageResource(R.drawable.ic_search_holo_dark); updateSearchMode(false); } else { - if (mHideNavButtons) { - showNavButtons(); - } mGoButton.setVisibility(View.GONE); mVoiceSearch.setVisibility(View.GONE); mStar.setVisibility(View.VISIBLE); @@ -454,9 +261,7 @@ public class TitleBarXLarge extends TitleBarBase } else { mSearchButton.setVisibility(View.VISIBLE); } - mUrlIcon.setImageResource(mInVoiceMode ? - R.drawable.ic_search_holo_dark - : R.drawable.ic_web_holo_dark); + updateUrlIcon(); } } @@ -468,41 +273,25 @@ public class TitleBarXLarge extends TitleBarBase } } - /** - * Update the progress, from 0 to 100. - */ @Override - void setProgress(int newProgress) { - boolean blockvisuals = mUseQuickControls && isEditingUrl(); - if (newProgress >= PROGRESS_MAX) { - if (!blockvisuals) { - mProgressView.setProgress(PageProgressView.MAX_PROGRESS); - mProgressView.setVisibility(View.GONE); - mStopButton.setImageDrawable(mReloadDrawable); - } - mInLoad = false; - } else { - if (!mInLoad) { - if (!blockvisuals) { - mProgressView.setVisibility(View.VISIBLE); - mStopButton.setImageDrawable(mStopDrawable); - } - mInLoad = true; - } - mProgressView.setProgress(newProgress * PageProgressView.MAX_PROGRESS - / PROGRESS_MAX); - } + protected void onProgressStarted() { + mStopButton.setImageDrawable(mStopDrawable); + } + + @Override + protected void onProgressStopped() { + mStopButton.setImageDrawable(mReloadDrawable); } - private void updateSearchMode(boolean userEdited) { + @Override + protected void updateSearchMode(boolean userEdited) { setSearchMode(!userEdited || TextUtils.isEmpty(mUrlInput.getUserText())); } - private void setSearchMode(boolean voiceSearchEnabled) { - SearchEngine searchEngine = BrowserSettings.getInstance() - .getSearchEngine(); + @Override + protected void setSearchMode(boolean voiceSearchEnabled) { boolean showvoicebutton = voiceSearchEnabled && - (searchEngine != null && searchEngine.supportsVoiceSearch()); + mUiController.supportsVoiceSearch(); mVoiceSearch.setVisibility(showvoicebutton ? View.VISIBLE : View.GONE); mGoButton.setVisibility(voiceSearchEnabled ? View.GONE : @@ -510,45 +299,14 @@ public class TitleBarXLarge extends TitleBarBase } @Override - /* package */ void setDisplayTitle(String title) { - if (!isEditingUrl()) { - mUrlInput.setText(title, false); - } - } - - // UrlInput text watcher - - @Override - public void onTextChanged(String newText) { - if (mUrlInput.hasFocus()) { - // check if input field is empty and adjust voice search state - updateSearchMode(true); - // clear voice mode when user types - setInVoiceMode(false, null); - } - } - - // voicesearch - - @Override - public void setInVoiceMode(boolean voicemode) { - setInVoiceMode(voicemode, null); - } - public void setInVoiceMode(boolean voicemode, List<String> voiceResults) { - mInVoiceMode = voicemode; - mUrlInput.setVoiceResults(voiceResults); + super.setInVoiceMode(voicemode, voiceResults); if (voicemode) { mUrlIcon.setImageDrawable(mSearchButton.getDrawable()); } } @Override - void setIncognitoMode(boolean incognito) { - mUrlInput.setIncognitoMode(incognito); - } - - @Override public View focusSearch(View focused, int dir) { if (FOCUS_DOWN == dir && hasFocus()) { return getCurrentWebView(); @@ -556,74 +314,4 @@ public class TitleBarXLarge extends TitleBarBase return super.focusSearch(focused, dir); } - void clearCompletions() { - mUrlInput.setSuggestedText(null); - } - - @Override - public boolean dispatchKeyEventPreIme(KeyEvent evt) { - if (evt.getKeyCode() == KeyEvent.KEYCODE_BACK) { - // catch back key in order to do slightly more cleanup than usual - mUrlInput.clearFocus(); - return true; - } - return super.dispatchKeyEventPreIme(evt); - } - - private WebView getCurrentWebView() { - Tab t = mUi.getActiveTab(); - if (t != null) { - return t.getWebView(); - } else { - return null; - } - } - - void registerDropdownChangeListener(DropdownChangeListener d) { - mUrlInput.registerDropdownChangeListener(d); - } - - private void hideNavButtons() { - int awidth = mNavButtons.getMeasuredWidth(); - Animator anim1 = ObjectAnimator.ofFloat(mNavButtons, "translationX", 0, - awidth); - Animator anim2 = ObjectAnimator.ofInt(mUrlContainer, "left", mUrlContainer.getLeft(), - mUrlContainer.getPaddingLeft()); - Animator anim3 = ObjectAnimator.ofFloat(mNavButtons, "alpha", 1f, 0f); - AnimatorSet combo = new AnimatorSet(); - combo.playTogether(anim1, anim2, anim3); - combo.addListener(new AnimatorListener() { - - @Override - public void onAnimationCancel(Animator animation) { - } - - @Override - public void onAnimationEnd(Animator animation) { - mNavButtons.setVisibility(View.GONE); - } - - @Override - public void onAnimationRepeat(Animator animation) { - } - - @Override - public void onAnimationStart(Animator animation) { - } - }); - combo.setDuration(150); - combo.start(); - } - - private void showNavButtons() { - int awidth = mNavButtons.getMeasuredWidth(); - Animator anim1 = ObjectAnimator.ofFloat(mNavButtons, "translationX", -awidth, 0); - Animator anim2 = ObjectAnimator.ofInt(mUrlContainer, "left", 0, awidth); - Animator anim3 = ObjectAnimator.ofFloat(mNavButtons, "alpha", 0f, 1f); - AnimatorSet combo = new AnimatorSet(); - combo.playTogether(anim1, anim2, anim3); - mNavButtons.setVisibility(View.VISIBLE); - combo.setDuration(150); - combo.start(); - } - } diff --git a/src/com/android/browser/UI.java b/src/com/android/browser/UI.java index 368c829..bb34ada 100644 --- a/src/com/android/browser/UI.java +++ b/src/com/android/browser/UI.java @@ -85,7 +85,7 @@ public interface UI { public boolean isCustomViewShowing(); - public void showVoiceTitleBar(String title); + public void showVoiceTitleBar(String title, List<String> results); public void revertVoiceTitleBar(Tab tab); @@ -132,4 +132,6 @@ public interface UI { void showAutoLogin(Tab tab); void hideAutoLogin(Tab tab); + + void setFullscreen(boolean enabled); } diff --git a/src/com/android/browser/UiController.java b/src/com/android/browser/UiController.java index 65fa5f8..4fc37af 100644 --- a/src/com/android/browser/UiController.java +++ b/src/com/android/browser/UiController.java @@ -44,7 +44,12 @@ public interface UiController extends BookmarksHistoryCallbacks { Tab openIncognitoTab(); - boolean switchToTab(int tabIndex); + Tab openTab(String url, boolean incognito, boolean setActive, + boolean useCurrent); + + void setActiveTab(Tab tab); + + boolean switchToTab(Tab tab); void closeCurrentTab(); @@ -56,10 +61,10 @@ public interface UiController extends BookmarksHistoryCallbacks { void bookmarksOrHistoryPicker(boolean openHistory); - void startSearch(String url); - void startVoiceSearch(); + boolean supportsVoiceSearch(); + void showVoiceSearchResults(String title); void editUrl(); diff --git a/src/com/android/browser/UrlHandler.java b/src/com/android/browser/UrlHandler.java index b23dc7d..02a080f 100644 --- a/src/com/android/browser/UrlHandler.java +++ b/src/com/android/browser/UrlHandler.java @@ -22,7 +22,9 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.database.Cursor; import android.net.Uri; +import android.os.AsyncTask; import android.util.Log; import android.webkit.WebView; @@ -35,6 +37,9 @@ import java.util.regex.Matcher; */ public class UrlHandler { + static final String RLZ_PROVIDER = "com.google.android.partnersetup.rlzappprovider"; + static final Uri RLZ_PROVIDER_URI = Uri.parse("content://" + RLZ_PROVIDER + "/"); + // Use in overrideUrlLoading /* package */ final static String SCHEME_WTAI = "wtai://wp/"; /* package */ final static String SCHEME_WTAI_MC = "wtai://wp/mc;"; @@ -44,6 +49,9 @@ public class UrlHandler { Controller mController; Activity mActivity; + private Boolean mIsProviderPresent = null; + private Uri mRlzUri = null; + public UrlHandler(Controller controller) { mController = controller; mActivity = mController.getActivity(); @@ -92,6 +100,20 @@ public class UrlHandler { return false; } + // If this is a Google search, attempt to add an RLZ string + // (if one isn't already present). + if (rlzProviderPresent()) { + Uri siteUri = Uri.parse(url); + if (needsRlzString(siteUri)) { + // Need to look up the RLZ info from a database, so do it in an + // AsyncTask. Although we are not overriding the URL load synchronously, + // we guarantee that we will handle this URL load after the task executes, + // so it's safe to just return true to WebCore now to stop its own loading. + new RLZTask(tab, siteUri, view).execute(); + return true; + } + } + if (startActivityForUrl(url)) { return true; } @@ -192,11 +214,120 @@ public class UrlHandler { // depressed by opening in a new tab boolean handleMenuClick(Tab tab, String url) { if (mController.isMenuDown()) { - mController.openTab(tab, url, false); + mController.openTab(url, + (tab != null) && tab.isPrivateBrowsingEnabled(), + !BrowserSettings.getInstance().openInBackground(), true); mActivity.closeOptionsMenu(); return true; } return false; } + + // TODO: Move this class into Tab, where it can be properly stopped upon + // closure of the tab + private class RLZTask extends AsyncTask<Void, Void, String> { + private Tab mTab; + private Uri mSiteUri; + private WebView mWebView; + + public RLZTask(Tab tab, Uri uri, WebView webView) { + mTab = tab; + mSiteUri = uri; + mWebView = webView; + } + + protected String doInBackground(Void... unused) { + String result = mSiteUri.toString(); + Cursor cur = null; + try { + cur = mActivity.getContentResolver() + .query(getRlzUri(), null, null, null, null); + if (cur != null && cur.moveToFirst() && !cur.isNull(0)) { + result = mSiteUri.buildUpon() + .appendQueryParameter("rlz", cur.getString(0)) + .build().toString(); + } + } finally { + if (cur != null) { + cur.close(); + } + } + return result; + } + + protected void onPostExecute(String result) { + // Make sure the Tab was not closed while handling the task + if (mController.getTabControl().getTabPosition(mTab) != -1) { + // If the Activity Manager is not invoked, load the URL directly + if (!startActivityForUrl(result)) { + if (!handleMenuClick(mTab, result)) { + mController.loadUrl(mWebView, result); + } + } + } + } + } + + // Determine whether the RLZ provider is present on the system. + private boolean rlzProviderPresent() { + if (mIsProviderPresent == null) { + PackageManager pm = mActivity.getPackageManager(); + mIsProviderPresent = pm.resolveContentProvider(RLZ_PROVIDER, 0) != null; + } + return mIsProviderPresent; + } + + // Retrieve the RLZ access point string and cache the URI used to + // retrieve RLZ values. + private Uri getRlzUri() { + if (mRlzUri == null) { + String ap = mActivity.getResources() + .getString(R.string.rlz_access_point); + mRlzUri = Uri.withAppendedPath(RLZ_PROVIDER_URI, ap); + } + return mRlzUri; + } + + // Determine if this URI appears to be for a Google search + // and does not have an RLZ parameter. + // Taken largely from Chrome source, src/chrome/browser/google_url_tracker.cc + private static boolean needsRlzString(Uri uri) { + String scheme = uri.getScheme(); + if (("http".equals(scheme) || "https".equals(scheme)) && + (uri.getQueryParameter("q") != null) && + (uri.getQueryParameter("rlz") == null)) { + String host = uri.getHost(); + if (host == null) { + return false; + } + String[] hostComponents = host.split("\\."); + + if (hostComponents.length < 2) { + return false; + } + int googleComponent = hostComponents.length - 2; + String component = hostComponents[googleComponent]; + if (!"google".equals(component)) { + if (hostComponents.length < 3 || + (!"co".equals(component) && !"com".equals(component))) { + return false; + } + googleComponent = hostComponents.length - 3; + if (!"google".equals(hostComponents[googleComponent])) { + return false; + } + } + + // Google corp network handling. + if (googleComponent > 0 && "corp".equals( + hostComponents[googleComponent - 1])) { + return false; + } + + return true; + } + return false; + } + } diff --git a/src/com/android/browser/UrlInputView.java b/src/com/android/browser/UrlInputView.java index 9c5d338..7545e6a 100644 --- a/src/com/android/browser/UrlInputView.java +++ b/src/com/android/browser/UrlInputView.java @@ -175,7 +175,7 @@ public class UrlInputView extends SuggestiveAutoCompleteTextView @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (BrowserSettings.getInstance().useInstant() && + if (BrowserSettings.getInstance().useInstantSearch() && (actionId == EditorInfo.IME_ACTION_NEXT)) { // When instant is turned on AND the user chooses to complete // using the tab key, then use the completion rather than the @@ -298,11 +298,11 @@ public class UrlInputView extends SuggestiveAutoCompleteTextView } /* - * no-op to prevent scrolling of webview when embedded titlebar is edited + * no-op to prevent scrolling of webview when embedded titlebar + * gets edited */ @Override public boolean requestRectangleOnScreen(Rect rect, boolean immediate) { return false; } - } diff --git a/src/com/android/browser/UrlUtils.java b/src/com/android/browser/UrlUtils.java index 8c789db..ccf9710 100644 --- a/src/com/android/browser/UrlUtils.java +++ b/src/com/android/browser/UrlUtils.java @@ -16,8 +16,6 @@ package com.android.browser; -import com.android.browser.homepages.HomeProvider; - import android.net.Uri; import android.util.Patterns; import android.webkit.URLUtil; @@ -59,7 +57,7 @@ public class UrlUtils { * @return a stripped url like "www.google.com", or the original string if it could * not be stripped */ - /* package */ static String stripUrl(String url) { + public static String stripUrl(String url) { if (url == null) return null; Matcher m = STRIP_URL_PATTERN.matcher(url); if (m.matches() && m.groupCount() == 3) { @@ -159,34 +157,4 @@ public class UrlUtils { return inUrl; } - // Determine if this URI appears to be a Google property - /* package */ static boolean isGoogleUri(Uri uri) { - String scheme = uri.getScheme(); - if (!"http".equals(scheme) && !"https".equals(scheme)) { - return false; - } - - String host = uri.getHost(); - if (host == null) { - return false; - } - String[] hostComponents = host.split("\\."); - if (hostComponents.length < 2) { - return false; - } - - int googleComponent = hostComponents.length - 2; - String component = hostComponents[googleComponent]; - if (!"google".equals(component)) { - if (hostComponents.length < 3 || - (!"co".equals(component) && !"com".equals(component))) { - return false; - } - googleComponent = hostComponents.length - 3; - if (!"google".equals(hostComponents[googleComponent])) { - return false; - } - } - return true; - } } diff --git a/src/com/android/browser/WebViewController.java b/src/com/android/browser/WebViewController.java index 6b44207..bf3bdba 100644 --- a/src/com/android/browser/WebViewController.java +++ b/src/com/android/browser/WebViewController.java @@ -16,8 +16,6 @@ package com.android.browser; -import com.android.browser.IntentHandler.UrlData; - import android.app.Activity; import android.graphics.Bitmap; import android.net.Uri; @@ -31,6 +29,8 @@ import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebView; +import java.util.List; + /** * WebView aspect of the controller */ @@ -85,7 +85,7 @@ public interface WebViewController { void onUserCanceledSsl(Tab tab); - void activateVoiceSearchMode(String title); + void activateVoiceSearchMode(String title, List<String> results); void revertVoiceSearchMode(Tab tab); @@ -101,10 +101,10 @@ public interface WebViewController { void dismissSubWindow(Tab tab); - Tab openTabAndShow(Tab parent, UrlData urlData, boolean closeOnExit, - String appId); + Tab openTab(String url, boolean incognito, boolean setActive, + boolean useCurrent); - boolean switchToTab(int tabindex); + boolean switchToTab(Tab tab); void closeTab(Tab tab); diff --git a/src/com/android/browser/XLargeUi.java b/src/com/android/browser/XLargeUi.java index 8c34fc9..4bfd3cf 100644 --- a/src/com/android/browser/XLargeUi.java +++ b/src/com/android/browser/XLargeUi.java @@ -16,26 +16,19 @@ package com.android.browser; -import com.android.browser.ScrollWebView.ScrollListener; +import com.android.browser.BrowserWebView.ScrollListener; -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.animation.ObjectAnimator; import android.app.ActionBar; import android.app.Activity; -import android.content.pm.PackageManager; -import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.ActionMode; import android.view.Gravity; import android.view.KeyEvent; -import android.view.Menu; import android.view.View; import android.webkit.WebChromeClient.CustomViewCallback; import android.webkit.WebView; -import android.widget.FrameLayout; import java.util.List; @@ -50,8 +43,6 @@ public class XLargeUi extends BaseUi implements ScrollListener { private TabBar mTabBar; private TitleBarXLarge mTitleBar; - private Animator mTitleBarAnimator; - private boolean mSkipTitleBarAnimations; private boolean mUseQuickControls; private PieControl mPieControl; @@ -64,7 +55,8 @@ public class XLargeUi extends BaseUi implements ScrollListener { public XLargeUi(Activity browser, UiController controller) { super(browser, controller); mHandler = new Handler(); - mTitleBar = new TitleBarXLarge(mActivity, mUiController, this); + mTitleBar = new TitleBarXLarge(mActivity, mUiController, this, + mContentView); mTitleBar.setProgress(100); mTabBar = new TabBar(mActivity, mUiController, this); mActionBar = mActivity.getActionBar(); @@ -103,16 +95,16 @@ public class XLargeUi extends BaseUi implements ScrollListener { checkTabCount(); mPieControl = new PieControl(mActivity, mUiController, this); mPieControl.attachToContainer(mContentView); - Tab tab = getActiveTab(); - if ((tab != null) && (tab.getWebView() != null)) { - tab.getWebView().setEmbeddedTitleBar(null); + WebView web = getWebView(); + if (web != null) { + web.setEmbeddedTitleBar(null); } } else { mActivity.getActionBar().show(); if (mPieControl != null) { mPieControl.removeFromContainer(mContentView); } - WebView web = mTabControl.getCurrentWebView(); + WebView web = getWebView(); if (web != null) { web.setEmbeddedTitleBar(mTitleBar); } @@ -134,7 +126,7 @@ public class XLargeUi extends BaseUi implements ScrollListener { @Override public void onResume() { super.onResume(); - if (!BrowserSettings.getInstance().useInstant()) { + if (!BrowserSettings.getInstance().useInstantSearch()) { mTitleBar.clearCompletions(); } } @@ -149,23 +141,14 @@ public class XLargeUi extends BaseUi implements ScrollListener { @Override public WebView createWebView(boolean privateBrowsing) { // Create a new WebView - ScrollWebView w = new ScrollWebView(mActivity, null, - android.R.attr.webViewStyle, privateBrowsing); - initWebViewSettings(w); + BrowserWebView w = (BrowserWebView) super.createWebView(privateBrowsing); w.setScrollListener(this); - boolean supportsMultiTouch = mActivity.getPackageManager() - .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH); - w.getSettings().setDisplayZoomControls(!supportsMultiTouch); - w.setExpandedTileBounds(true); // smoother scrolling return w; } @Override public WebView createSubWebView(boolean privateBrowsing) { - ScrollWebView web = (ScrollWebView) createWebView(privateBrowsing); - // no scroll listener for subview - web.setScrollListener(null); - return web; + return super.createWebView(privateBrowsing); } @Override @@ -174,7 +157,7 @@ public class XLargeUi extends BaseUi implements ScrollListener { } void stopWebViewScrolling() { - ScrollWebView web = (ScrollWebView) mUiController.getCurrentWebView(); + BrowserWebView web = (BrowserWebView) mUiController.getCurrentWebView(); if (web != null) { web.stopScroll(); } @@ -188,31 +171,10 @@ public class XLargeUi extends BaseUi implements ScrollListener { mTabBar.onProgress(tab, progress); if (tab.inForeground()) { mTitleBar.setProgress(progress); - if (progress == 100) { - if (!mTitleBar.isEditingUrl() && !mTitleBar.inAutoLogin()) { - hideTitleBar(); - if (mUseQuickControls) { - mTitleBar.setShowProgressOnly(false); - } - } - } else { - if (!isTitleBarShowing()) { - if (mUseQuickControls && !mTitleBar.isEditingUrl()) { - mTitleBar.setShowProgressOnly(true); - setTitleGravity(Gravity.TOP); - } - showTitleBar(); - } - } } } @Override - public boolean needsRestoreAllTabs() { - return true; - } - - @Override public void addTab(Tab tab) { mTabBar.onNewTab(tab); } @@ -223,9 +185,8 @@ public class XLargeUi extends BaseUi implements ScrollListener { @Override public void setActiveTab(final Tab tab) { - cancelTitleBarAnimation(true); - mSkipTitleBarAnimations = true; - stopEditingUrl(); + mTitleBar.cancelTitleBarAnimation(true); + mTitleBar.setSkipTitleBarAnimations(true); if (mUseQuickControls) { if (mActiveTab != null) { captureTab(mActiveTab); @@ -233,12 +194,12 @@ public class XLargeUi extends BaseUi implements ScrollListener { } super.setActiveTab(tab, true); setActiveTab(tab, true); - mSkipTitleBarAnimations = false; + mTitleBar.setSkipTitleBarAnimations(false); } @Override void setActiveTab(Tab tab, boolean needsAttaching) { - ScrollWebView view = (ScrollWebView) tab.getWebView(); + BrowserWebView view = (BrowserWebView) tab.getWebView(); // TabControl.setCurrentTab has been called before this, // so the tab is guaranteed to have a webview if (view == null) { @@ -259,7 +220,7 @@ public class XLargeUi extends BaseUi implements ScrollListener { } mTabBar.onSetActiveTab(tab); if (tab.isInVoiceSearchMode()) { - showVoiceTitleBar(tab.getVoiceDisplayTitle()); + showVoiceTitleBar(tab.getVoiceDisplayTitle(), tab.getVoiceSearchResults()); } else { revertVoiceTitleBar(tab); } @@ -267,15 +228,6 @@ public class XLargeUi extends BaseUi implements ScrollListener { tab.getTopWindow().requestFocus(); } - public void captureTab(final Tab tab) { - Bitmap sshot = Controller.createScreenshot(tab, - (int) mActivity.getResources() - .getDimension(R.dimen.qc_thumb_width), - (int) mActivity.getResources() - .getDimension(R.dimen.qc_thumb_height)); - tab.setScreenshot(sshot); - } - @Override public void updateTabs(List<Tab> tabs) { mTabBar.updateTabs(tabs); @@ -284,11 +236,11 @@ public class XLargeUi extends BaseUi implements ScrollListener { @Override public void removeTab(Tab tab) { - cancelTitleBarAnimation(true); - mSkipTitleBarAnimations = true; + mTitleBar.cancelTitleBarAnimation(true); + mTitleBar.setSkipTitleBarAnimations(true); super.removeTab(tab); mTabBar.onRemoveTab(tab); - mSkipTitleBarAnimations = false; + mTitleBar.setSkipTitleBarAnimations(false); } protected void onRemoveTabCompleted(Tab tab) { @@ -304,17 +256,10 @@ public class XLargeUi extends BaseUi implements ScrollListener { @Override public void editUrl(boolean clearInput) { - if (mUiController.isInCustomActionMode()) { - mUiController.endActionMode(); + if (mUseQuickControls) { + getTitleBar().setShowProgressOnly(false); } - showTitleBar(); - mTitleBar.startEditingUrl(clearInput); - } - - void showTitleBarAndEdit() { - mTitleBar.setShowProgressOnly(false); - showTitleBar(); - mTitleBar.startEditingUrl(false); + super.editUrl(clearInput); } void stopEditingUrl() { @@ -324,24 +269,7 @@ public class XLargeUi extends BaseUi implements ScrollListener { @Override protected void showTitleBar() { if (canShowTitleBar()) { - if (mUseQuickControls) { - mContentView.addView(mTitleBar); - } else { - if (!mSkipTitleBarAnimations) { - cancelTitleBarAnimation(false); - int visibleHeight = getVisibleTitleHeight(); - float startPos = (-mTitleBar.getEmbeddedHeight() + visibleHeight); - if (mTitleBar.getTranslationY() != 0) { - startPos = Math.max(startPos, mTitleBar.getTranslationY()); - } - mTitleBarAnimator = ObjectAnimator.ofFloat(mTitleBar, - "translationY", - startPos, 0); - mTitleBarAnimator.start(); - } - setTitleGravity(Gravity.TOP); - } - super.showTitleBar(); + mTitleBar.show(); mTabBar.onShowTitleBar(); } } @@ -350,66 +278,10 @@ public class XLargeUi extends BaseUi implements ScrollListener { protected void hideTitleBar() { if (isTitleBarShowing()) { mTabBar.onHideTitleBar(); - if (mUseQuickControls) { - mContentView.removeView(mTitleBar); - } else { - if (!mSkipTitleBarAnimations) { - cancelTitleBarAnimation(false); - int visibleHeight = getVisibleTitleHeight(); - mTitleBarAnimator = ObjectAnimator.ofFloat(mTitleBar, - "translationY", mTitleBar.getTranslationY(), - (-mTitleBar.getEmbeddedHeight() + visibleHeight)); - mTitleBarAnimator.addListener(mHideTileBarAnimatorListener); - mTitleBarAnimator.start(); - } else { - setTitleGravity(Gravity.NO_GRAVITY); - } - } - super.hideTitleBar(); + mTitleBar.hide(); } } - private void cancelTitleBarAnimation(boolean reset) { - if (mTitleBarAnimator != null) { - mTitleBarAnimator.cancel(); - mTitleBarAnimator = null; - } - if (reset) { - mTitleBar.setTranslationY(0); - } - } - - private int getVisibleTitleHeight() { - WebView webview = mActiveTab != null ? mActiveTab.getWebView() : null; - return webview != null ? webview.getVisibleTitleHeight() : 0; - } - - private AnimatorListener mHideTileBarAnimatorListener = new AnimatorListener() { - - boolean mWasCanceled; - @Override - public void onAnimationStart(Animator animation) { - mWasCanceled = false; - } - - @Override - public void onAnimationRepeat(Animator animation) { - } - - @Override - public void onAnimationEnd(Animator animation) { - if (!mWasCanceled) { - mTitleBar.setTranslationY(0); - } - setTitleGravity(Gravity.NO_GRAVITY); - } - - @Override - public void onAnimationCancel(Animator animation) { - mWasCanceled = true; - } - }; - public boolean isEditingUrl() { return mTitleBar.isEditingUrl(); } @@ -421,12 +293,7 @@ public class XLargeUi extends BaseUi implements ScrollListener { @Override protected void setTitleGravity(int gravity) { - if (mUseQuickControls) { - FrameLayout.LayoutParams lp = - (FrameLayout.LayoutParams) mTitleBar.getLayoutParams(); - lp.gravity = gravity; - mTitleBar.setLayoutParams(lp); - } else { + if (!mUseQuickControls) { super.setTitleGravity(gravity); } } @@ -460,18 +327,6 @@ public class XLargeUi extends BaseUi implements ScrollListener { } @Override - protected void updateAutoLogin(Tab tab, boolean animate) { - mTitleBar.updateAutoLogin(tab, animate); - } - - protected void refreshWebView() { - Tab tab = getActiveTab(); - if ((tab != null) && (tab.getWebView() != null)) { - tab.getWebView().invalidate(); - } - } - - @Override public void setUrlTitle(Tab tab) { super.setUrlTitle(tab); mTabBar.onUrlAndTitle(tab, tab.getUrl(), tab.getTitle()); @@ -485,11 +340,7 @@ public class XLargeUi extends BaseUi implements ScrollListener { } @Override - public void showVoiceTitleBar(String title) { - List<String> vsresults = null; - if (getActiveTab() != null) { - vsresults = getActiveTab().getVoiceSearchResults(); - } + public void showVoiceTitleBar(String title, List<String> vsresults) { mTitleBar.setInVoiceMode(true, vsresults); mTitleBar.setDisplayTitle(title); } @@ -549,19 +400,4 @@ public class XLargeUi extends BaseUi implements ScrollListener { return mTabBar; } - @Override - public void registerDropdownChangeListener(DropdownChangeListener d) { - mTitleBar.registerDropdownChangeListener(d); - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - if (mUseQuickControls) { - mPieControl.onMenuOpened(menu); - return false; - } else { - return true; - } - } - } diff --git a/src/com/android/browser/addbookmark/FolderSpinnerAdapter.java b/src/com/android/browser/addbookmark/FolderSpinnerAdapter.java index 261aa62..67563c0 100644 --- a/src/com/android/browser/addbookmark/FolderSpinnerAdapter.java +++ b/src/com/android/browser/addbookmark/FolderSpinnerAdapter.java @@ -19,33 +19,37 @@ package com.android.browser.addbookmark; import com.android.browser.R; import android.content.Context; -import android.content.res.Resources; -import android.database.DataSetObserver; import android.graphics.drawable.Drawable; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Adapter; -import android.widget.SpinnerAdapter; +import android.widget.BaseAdapter; import android.widget.TextView; /** * SpinnerAdapter used in the AddBookmarkPage to select where to save a * bookmark/folder. */ -public class FolderSpinnerAdapter implements SpinnerAdapter { - private boolean mIncludeHomeScreen; - private boolean mIncludesRecentFolder; - private long mRecentFolderId; - private String mRecentFolderName; +public class FolderSpinnerAdapter extends BaseAdapter { public static final int HOME_SCREEN = 0; public static final int ROOT_FOLDER = 1; public static final int OTHER_FOLDER = 2; public static final int RECENT_FOLDER = 3; - public FolderSpinnerAdapter(boolean includeHomeScreen) { + private boolean mIncludeHomeScreen; + private boolean mIncludesRecentFolder; + private long mRecentFolderId; + private String mRecentFolderName; + private LayoutInflater mInflater; + private Context mContext; + private String mOtherFolderDisplayText; + + public FolderSpinnerAdapter(Context context, boolean includeHomeScreen) { mIncludeHomeScreen = includeHomeScreen; + mContext = context; + mInflater = LayoutInflater.from(mContext); } public void addRecentFolder(long folderId, String folderName) { @@ -56,8 +60,7 @@ public class FolderSpinnerAdapter implements SpinnerAdapter { public long recentFolderId() { return mRecentFolderId; } - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { + private void bindView(int position, View view, boolean isDropDown) { int labelResource; int drawableResource; if (!mIncludeHomeScreen) { @@ -84,26 +87,39 @@ public class FolderSpinnerAdapter implements SpinnerAdapter { // assert break; } - Context context = parent.getContext(); - LayoutInflater factory = LayoutInflater.from(context); - TextView textView = (TextView) factory.inflate(R.layout.add_to_option, null); + TextView textView = (TextView) view; if (position == RECENT_FOLDER) { textView.setText(mRecentFolderName); + } else if (position == OTHER_FOLDER && !isDropDown + && mOtherFolderDisplayText != null) { + textView.setText(mOtherFolderDisplayText); } else { textView.setText(labelResource); } - Drawable drawable = context.getResources().getDrawable(drawableResource); + textView.setGravity(Gravity.CENTER_VERTICAL); + Drawable drawable = mContext.getResources().getDrawable(drawableResource); textView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); - return textView; } @Override - public void registerDataSetObserver(DataSetObserver observer) { + public View getDropDownView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = mInflater.inflate( + android.R.layout.simple_spinner_dropdown_item, parent, false); + } + bindView(position, convertView, true); + return convertView; } @Override - public void unregisterDataSetObserver(DataSetObserver observer) { + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = mInflater.inflate(android.R.layout.simple_spinner_item, + parent, false); + } + bindView(position, convertView, false); + return convertView; } @Override @@ -133,24 +149,9 @@ public class FolderSpinnerAdapter implements SpinnerAdapter { return true; } - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return getDropDownView(position, convertView, parent); - } - - @Override - public int getItemViewType(int position) { - // Never want to recycle views - return Adapter.IGNORE_ITEM_VIEW_TYPE; - } - - @Override - public int getViewTypeCount() { - return 1; + public void setOtherFolderDisplayText(String parentTitle) { + mOtherFolderDisplayText = parentTitle; + notifyDataSetChanged(); } - @Override - public boolean isEmpty() { - return false; - } } diff --git a/src/com/android/browser/autocomplete/SuggestiveAutoCompleteTextView.java b/src/com/android/browser/autocomplete/SuggestiveAutoCompleteTextView.java index e51a629..50ba758 100644 --- a/src/com/android/browser/autocomplete/SuggestiveAutoCompleteTextView.java +++ b/src/com/android/browser/autocomplete/SuggestiveAutoCompleteTextView.java @@ -752,7 +752,7 @@ public class SuggestiveAutoCompleteTextView extends EditText implements Filter.F } private void updateText(SuggestionsAdapter adapter) { - if (!BrowserSettings.getInstance().useInstant()) { + if (!BrowserSettings.getInstance().useInstantSearch()) { return; } diff --git a/src/com/android/browser/preferences/AccessibilityPreferencesFragment.java b/src/com/android/browser/preferences/AccessibilityPreferencesFragment.java new file mode 100644 index 0000000..99bd687 --- /dev/null +++ b/src/com/android/browser/preferences/AccessibilityPreferencesFragment.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2011 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.preferences; + +import com.android.browser.PreferenceKeys; +import com.android.browser.R; + +import android.content.res.Resources; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.view.View; + +public class AccessibilityPreferencesFragment extends PreferenceFragment + implements Preference.OnPreferenceChangeListener { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.accessibility_preferences); + + Preference e = findPreference(PreferenceKeys.PREF_TEXT_SIZE); + e.setOnPreferenceChangeListener(this); + e.setSummary(getVisualTextSizeName( + getPreferenceScreen().getSharedPreferences() + .getString(PreferenceKeys.PREF_TEXT_SIZE, null)) ); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + getListView().setItemsCanFocus(true); + } + + @Override + public boolean onPreferenceChange(Preference pref, Object objValue) { + if (getActivity() == null) { + // We aren't attached, so don't accept preferences changes from the + // invisible UI. + return false; + } + + if (pref.getKey().equals(PreferenceKeys.PREF_TEXT_SIZE)) { + pref.setSummary(getVisualTextSizeName((String) objValue)); + return true; + } + return false; + } + + private CharSequence getVisualTextSizeName(String enumName) { + Resources res = getActivity().getResources(); + CharSequence[] visualNames = res.getTextArray(R.array.pref_text_size_choices); + CharSequence[] enumNames = res.getTextArray(R.array.pref_text_size_values); + + // Sanity check + if (visualNames.length != enumNames.length) { + return ""; + } + + int length = enumNames.length; + for (int i = 0; i < length; i++) { + if (enumNames[i].equals(enumName)) { + return visualNames[i]; + } + } + + return ""; + } + +}
\ No newline at end of file diff --git a/src/com/android/browser/preferences/AdvancedPreferencesFragment.java b/src/com/android/browser/preferences/AdvancedPreferencesFragment.java index e2e45f5..2cc504e 100644 --- a/src/com/android/browser/preferences/AdvancedPreferencesFragment.java +++ b/src/com/android/browser/preferences/AdvancedPreferencesFragment.java @@ -17,7 +17,7 @@ package com.android.browser.preferences; import com.android.browser.BrowserActivity; -import com.android.browser.BrowserSettings; +import com.android.browser.PreferenceKeys; import com.android.browser.R; import android.content.Intent; @@ -46,28 +46,22 @@ public class AdvancedPreferencesFragment extends PreferenceFragment addPreferencesFromResource(R.xml.advanced_preferences); PreferenceScreen websiteSettings = (PreferenceScreen) findPreference( - BrowserSettings.PREF_WEBSITE_SETTINGS); + PreferenceKeys.PREF_WEBSITE_SETTINGS); websiteSettings.setFragment(WebsiteSettingsFragment.class.getName()); - Preference e = findPreference(BrowserSettings.PREF_TEXT_SIZE); - e.setOnPreferenceChangeListener(this); - e.setSummary(getVisualTextSizeName( - getPreferenceScreen().getSharedPreferences() - .getString(BrowserSettings.PREF_TEXT_SIZE, null)) ); - - e = findPreference(BrowserSettings.PREF_DEFAULT_ZOOM); + Preference e = findPreference(PreferenceKeys.PREF_DEFAULT_ZOOM); e.setOnPreferenceChangeListener(this); e.setSummary(getVisualDefaultZoomName( getPreferenceScreen().getSharedPreferences() - .getString(BrowserSettings.PREF_DEFAULT_ZOOM, null)) ); + .getString(PreferenceKeys.PREF_DEFAULT_ZOOM, null)) ); - e = findPreference(BrowserSettings.PREF_DEFAULT_TEXT_ENCODING); + e = findPreference(PreferenceKeys.PREF_DEFAULT_TEXT_ENCODING); e.setOnPreferenceChangeListener(this); - e = findPreference(BrowserSettings.PREF_EXTRAS_RESET_DEFAULTS); + e = findPreference(PreferenceKeys.PREF_RESET_DEFAULT_PREFERENCES); e.setOnPreferenceChangeListener(this); - e = findPreference(BrowserSettings.PREF_PLUGIN_STATE); + e = findPreference(PreferenceKeys.PREF_PLUGIN_STATE); e.setOnPreferenceChangeListener(this); updatePluginSummary((ListPreference) e); } @@ -85,7 +79,7 @@ public class AdvancedPreferencesFragment extends PreferenceFragment public void onResume() { super.onResume(); final PreferenceScreen websiteSettings = (PreferenceScreen) findPreference( - BrowserSettings.PREF_WEBSITE_SETTINGS); + PreferenceKeys.PREF_WEBSITE_SETTINGS); websiteSettings.setEnabled(false); WebStorage.getInstance().getOrigins(new ValueCallback<Map>() { @Override @@ -114,23 +108,20 @@ public class AdvancedPreferencesFragment extends PreferenceFragment return false; } - if (pref.getKey().equals(BrowserSettings.PREF_TEXT_SIZE)) { - pref.setSummary(getVisualTextSizeName((String) objValue)); - return true; - } else if (pref.getKey().equals(BrowserSettings.PREF_DEFAULT_ZOOM)) { + if (pref.getKey().equals(PreferenceKeys.PREF_DEFAULT_ZOOM)) { pref.setSummary(getVisualDefaultZoomName((String) objValue)); return true; - } else if (pref.getKey().equals(BrowserSettings.PREF_DEFAULT_TEXT_ENCODING)) { + } else if (pref.getKey().equals(PreferenceKeys.PREF_DEFAULT_TEXT_ENCODING)) { pref.setSummary((String) objValue); return true; - } else if (pref.getKey().equals(BrowserSettings.PREF_EXTRAS_RESET_DEFAULTS)) { + } else if (pref.getKey().equals(PreferenceKeys.PREF_RESET_DEFAULT_PREFERENCES)) { Boolean value = (Boolean) objValue; if (value.booleanValue() == true) { startActivity(new Intent(BrowserActivity.ACTION_RESTART, null, getActivity(), BrowserActivity.class)); return true; } - } else if (pref.getKey().equals(BrowserSettings.PREF_PLUGIN_STATE)) { + } else if (pref.getKey().equals(PreferenceKeys.PREF_PLUGIN_STATE)) { ListPreference lp = (ListPreference) pref; lp.setValue((String) objValue); updatePluginSummary(lp); @@ -139,26 +130,6 @@ public class AdvancedPreferencesFragment extends PreferenceFragment return false; } - private CharSequence getVisualTextSizeName(String enumName) { - Resources res = getActivity().getResources(); - CharSequence[] visualNames = res.getTextArray(R.array.pref_text_size_choices); - CharSequence[] enumNames = res.getTextArray(R.array.pref_text_size_values); - - // Sanity check - if (visualNames.length != enumNames.length) { - return ""; - } - - int length = enumNames.length; - for (int i = 0; i < length; i++) { - if (enumNames[i].equals(enumName)) { - return visualNames[i]; - } - } - - return ""; - } - private CharSequence getVisualDefaultZoomName(String enumName) { Resources res = getActivity().getResources(); CharSequence[] visualNames = res.getTextArray(R.array.pref_default_zoom_choices); diff --git a/src/com/android/browser/preferences/DebugPreferencesFragment.java b/src/com/android/browser/preferences/DebugPreferencesFragment.java index 0a82371..984c12a 100644 --- a/src/com/android/browser/preferences/DebugPreferencesFragment.java +++ b/src/com/android/browser/preferences/DebugPreferencesFragment.java @@ -18,21 +18,14 @@ package com.android.browser.preferences; import com.android.browser.BrowserActivity; import com.android.browser.BrowserSettings; -import com.android.browser.Controller; +import com.android.browser.PreferenceKeys; import com.android.browser.R; -import android.content.Context; import android.content.Intent; -import android.os.AsyncTask; import android.os.Bundle; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.PreferenceActivity.Header; import android.preference.PreferenceFragment; -import android.preference.PreferenceManager.OnActivityResultListener; - -import java.io.IOException; -import java.io.Serializable; public class DebugPreferencesFragment extends PreferenceFragment implements OnPreferenceChangeListener { @@ -43,11 +36,7 @@ public class DebugPreferencesFragment extends PreferenceFragment // Load the XML preferences file addPreferencesFromResource(R.xml.debug_preferences); - if (BrowserSettings.getInstance().showDebugSettings()) { - addPreferencesFromResource(R.xml.hidden_debug_preferences); - } - - Preference e = findPreference(BrowserSettings.PREF_HARDWARE_ACCEL); + Preference e = findPreference(PreferenceKeys.PREF_ENABLE_HARDWARE_ACCEL); e.setOnPreferenceChangeListener(this); } diff --git a/src/com/android/browser/preferences/GeneralPreferencesFragment.java b/src/com/android/browser/preferences/GeneralPreferencesFragment.java index 0c63ab5..879b95d 100644 --- a/src/com/android/browser/preferences/GeneralPreferencesFragment.java +++ b/src/com/android/browser/preferences/GeneralPreferencesFragment.java @@ -19,7 +19,7 @@ package com.android.browser.preferences; import com.android.browser.BrowserBookmarksPage; import com.android.browser.BrowserHomepagePreference; import com.android.browser.BrowserPreferencesPage; -import com.android.browser.BrowserSettings; +import com.android.browser.PreferenceKeys; import com.android.browser.R; import com.android.browser.widget.BookmarkThumbnailWidgetProvider; @@ -62,10 +62,10 @@ public class GeneralPreferencesFragment extends PreferenceFragment // Load the XML preferences file addPreferencesFromResource(R.xml.general_preferences); - Preference e = findPreference(BrowserSettings.PREF_HOMEPAGE); + Preference e = findPreference(PreferenceKeys.PREF_HOMEPAGE); e.setOnPreferenceChangeListener(this); e.setSummary(getPreferenceScreen().getSharedPreferences() - .getString(BrowserSettings.PREF_HOMEPAGE, null)); + .getString(PreferenceKeys.PREF_HOMEPAGE, null)); ((BrowserHomepagePreference) e).setCurrentPage( getActivity().getIntent().getStringExtra(BrowserPreferencesPage.CURRENT_PAGE)); mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); @@ -81,7 +81,7 @@ public class GeneralPreferencesFragment extends PreferenceFragment return false; } - if (pref.getKey().equals(BrowserSettings.PREF_HOMEPAGE)) { + if (pref.getKey().equals(PreferenceKeys.PREF_HOMEPAGE)) { pref.setSummary((String) objValue); return true; } @@ -202,8 +202,8 @@ public class GeneralPreferencesFragment extends PreferenceFragment new GetAccountsTask(getActivity()).execute(); PreferenceScreen autoFillSettings = - (PreferenceScreen)findPreference(BrowserSettings.PREF_AUTOFILL_PROFILE); - autoFillSettings.setDependency(BrowserSettings.PREF_AUTOFILL_ENABLED); + (PreferenceScreen)findPreference(PreferenceKeys.PREF_AUTOFILL_PROFILE); + autoFillSettings.setDependency(PreferenceKeys.PREF_AUTOFILL_ENABLED); } @Override diff --git a/src/com/android/browser/preferences/LabPreferencesFragment.java b/src/com/android/browser/preferences/LabPreferencesFragment.java index a06dc3e..f99b96d 100644 --- a/src/com/android/browser/preferences/LabPreferencesFragment.java +++ b/src/com/android/browser/preferences/LabPreferencesFragment.java @@ -18,6 +18,7 @@ package com.android.browser.preferences; import com.android.browser.BrowserActivity; import com.android.browser.BrowserSettings; +import com.android.browser.PreferenceKeys; import com.android.browser.R; import com.android.browser.search.SearchEngine; @@ -41,22 +42,26 @@ public class LabPreferencesFragment extends PreferenceFragment // Load the XML preferences file addPreferencesFromResource(R.xml.lab_preferences); - Preference e = findPreference(BrowserSettings.PREF_QUICK_CONTROLS); - e.setOnPreferenceChangeListener(this); - useInstantPref = findPreference(BrowserSettings.PREF_USE_INSTANT); + Preference e = findPreference(PreferenceKeys.PREF_ENABLE_QUICK_CONTROLS); + if (e != null) { + e.setOnPreferenceChangeListener(this); + } + useInstantPref = findPreference(PreferenceKeys.PREF_USE_INSTANT_SEARCH); } @Override public void onResume() { super.onResume(); - useInstantPref.setEnabled(false); + if (useInstantPref != null) { + useInstantPref.setEnabled(false); - // Enable the "use instant" preference only if the selected - // search engine is google. - if (mBrowserSettings.getSearchEngine() != null) { - final String currentName = mBrowserSettings.getSearchEngine().getName(); - if (SearchEngine.GOOGLE.equals(currentName)) { - useInstantPref.setEnabled(true); + // Enable the "use instant" preference only if the selected + // search engine is google. + if (mBrowserSettings.getSearchEngine() != null) { + final String currentName = mBrowserSettings.getSearchEngine().getName(); + if (SearchEngine.GOOGLE.equals(currentName)) { + useInstantPref.setEnabled(true); + } } } } diff --git a/src/com/android/browser/preferences/MinFontSizePreference.java b/src/com/android/browser/preferences/MinFontSizePreference.java new file mode 100644 index 0000000..22092b0 --- /dev/null +++ b/src/com/android/browser/preferences/MinFontSizePreference.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2011 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.preferences; + +import com.android.browser.R; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; + +public class MinFontSizePreference extends Preference implements OnSeekBarChangeListener { + + // range from 1:6..24 + static final int MIN = 5; + static final int MAX = 23; + private int mProgress; + View mRoot; + + public MinFontSizePreference( + Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public MinFontSizePreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public MinFontSizePreference(Context context) { + super(context); + } + + @Override + public View getView(View convertView, ViewGroup parent) { + if (mRoot == null) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + mRoot = inflater.inflate(R.layout.min_font_size, parent, false); + SeekBar seek = (SeekBar) mRoot.findViewById(R.id.seekbar); + seek.setMax((MAX - MIN)); + seek.setProgress(mProgress); + seek.setOnSeekBarChangeListener(this); + } + return mRoot; + } + + @Override + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { + mProgress = restoreValue ? getPersistedInt(mProgress) + : (Integer) defaultValue; + mProgress -= 1; + } + + @Override + public void onProgressChanged( + SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser) { + if (progress == 0) { + persistInt(1); + } else { + persistInt(progress + MIN + 1); + } + } + mRoot.invalidate(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + + + @Override + protected Parcelable onSaveInstanceState() { + /* + * Suppose a client uses this preference type without persisting. We + * must save the instance state so it is able to, for example, survive + * orientation changes. + */ + + final Parcelable superState = super.onSaveInstanceState(); + if (isPersistent()) { + // No need to save instance state since it's persistent + return superState; + } + + // Save the instance state + final SavedState myState = new SavedState(superState); + myState.progress = mProgress; + return myState; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (!state.getClass().equals(SavedState.class)) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state); + return; + } + + // Restore the instance state + SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + mProgress = myState.progress; + notifyChanged(); + } + + /** + * SavedState, a subclass of {@link BaseSavedState}, will store the state + * of MyPreference, a subclass of Preference. + * <p> + * It is important to always call through to super methods. + */ + private static class SavedState extends BaseSavedState { + int progress; + + public SavedState(Parcel source) { + super(source); + + // Restore the click counter + progress = source.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + + // Save the click counter + dest.writeInt(progress); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + public static final Parcelable.Creator<SavedState> CREATOR = + new Parcelable.Creator<SavedState>() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + +} diff --git a/src/com/android/browser/preferences/PrivacySecurityPreferencesFragment.java b/src/com/android/browser/preferences/PrivacySecurityPreferencesFragment.java index 2266608..35e6e43 100644 --- a/src/com/android/browser/preferences/PrivacySecurityPreferencesFragment.java +++ b/src/com/android/browser/preferences/PrivacySecurityPreferencesFragment.java @@ -16,7 +16,7 @@ package com.android.browser.preferences; -import com.android.browser.BrowserSettings; +import com.android.browser.PreferenceKeys; import com.android.browser.R; import android.app.Activity; @@ -28,18 +28,14 @@ import android.preference.PreferenceFragment; public class PrivacySecurityPreferencesFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener { - private BrowserSettings mSettings; - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mSettings = BrowserSettings.getInstance(); - // Load the preferences from an XML resource addPreferencesFromResource(R.xml.privacy_security_preferences); - Preference e = findPreference(BrowserSettings.PREF_CLEAR_HISTORY); + Preference e = findPreference(PreferenceKeys.PREF_PRIVACY_CLEAR_HISTORY); e.setOnPreferenceChangeListener(this); } @@ -50,7 +46,7 @@ public class PrivacySecurityPreferencesFragment extends PreferenceFragment @Override public boolean onPreferenceChange(Preference pref, Object objValue) { - if (pref.getKey().equals(BrowserSettings.PREF_CLEAR_HISTORY) + if (pref.getKey().equals(PreferenceKeys.PREF_PRIVACY_CLEAR_HISTORY) && ((Boolean) objValue).booleanValue() == true) { // Need to tell the browser to remove the parent/child relationship // between tabs diff --git a/src/com/android/browser/preferences/WebViewPreview.java b/src/com/android/browser/preferences/WebViewPreview.java new file mode 100644 index 0000000..a269dbd --- /dev/null +++ b/src/com/android/browser/preferences/WebViewPreview.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2011 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.preferences; + +import com.android.browser.BrowserSettings; +import com.android.browser.R; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.res.Resources; +import android.preference.Preference; +import android.preference.PreferenceManager; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebSettings; +import android.webkit.WebView; + +public class WebViewPreview extends Preference implements OnSharedPreferenceChangeListener { + + // 80 char line width limit? Rules are made to be broken. + static final String HTML_FORMAT = "<html><head><style type=\"text/css\">p { margin: 2px auto;}</style><body><p style=\"font-size: .4em\">%s</p><p style=\"font-size: .7em\">%s</p><p style=\"font-size: 1em\">%s</p><p style=\"font-size: 1.3em\">%s</p><p style=\"font-size: 1.6em\">%s</p></body></html>"; + + String HTML; + private View mRoot; + private WebView mWebView; + + public WebViewPreview( + Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context); + } + + public WebViewPreview(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public WebViewPreview(Context context) { + super(context); + init(context); + } + + void init(Context context) { + Resources res = context.getResources(); + Object[] visualNames = res.getStringArray(R.array.pref_text_size_choices); + HTML = String.format(HTML_FORMAT, visualNames); + } + + void updatePreview() { + if (mWebView == null) return; + + WebSettings ws = mWebView.getSettings(); + BrowserSettings bs = BrowserSettings.getInstance(); + ws.setMinimumFontSize(bs.getMinimumFontSize()); + ws.setTextSize(bs.getTextSize()); + mWebView.loadData(HTML, "text/html", "utf-8"); + } + + @Override + public View getView(View convertView, ViewGroup parent) { + if (mWebView == null) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + mRoot = inflater.inflate(R.layout.webview_preview, parent, false); + mWebView = (WebView) mRoot.findViewById(R.id.webview); + mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + } + updatePreview(); + return mRoot; + } + + @Override + protected void onAttachedToHierarchy(PreferenceManager preferenceManager) { + super.onAttachedToHierarchy(preferenceManager); + getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + } + + @Override + protected void onPrepareForRemoval() { + getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); + super.onPrepareForRemoval(); + } + + @Override + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, String key) { + updatePreview(); + } + +} diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/provider/BrowserProvider.java index f69665c..b55b84a 100644 --- a/src/com/android/browser/BrowserProvider.java +++ b/src/com/android/browser/provider/BrowserProvider.java @@ -14,8 +14,10 @@ * limitations under the License. */ -package com.android.browser; +package com.android.browser.provider; +import com.android.browser.BrowserSettings; +import com.android.browser.R; import com.android.browser.search.SearchEngine; import android.app.SearchManager; @@ -40,7 +42,6 @@ import android.os.Process; import android.preference.PreferenceManager; import android.provider.Browser; import android.provider.Browser.BookmarkColumns; -import android.provider.Settings; import android.speech.RecognizerResultsIntent; import android.text.TextUtils; import android.util.Log; @@ -58,14 +59,14 @@ public class BrowserProvider extends ContentProvider { private SQLiteOpenHelper mOpenHelper; private BackupManager mBackupManager; - private static final String sDatabaseName = "browser.db"; + static final String sDatabaseName = "browser.db"; private static final String TAG = "BrowserProvider"; private static final String ORDER_BY = "visits DESC, date DESC"; private static final String PICASA_URL = "http://picasaweb.google.com/m/" + "viewer?source=androidclient"; - private static final String[] TABLE_NAMES = new String[] { + static final String[] TABLE_NAMES = new String[] { "bookmarks", "searches" }; private static final String[] SUGGEST_PROJECTION = new String[] { @@ -113,7 +114,7 @@ public class BrowserProvider extends ContentProvider { // make sure that these match the index of TABLE_NAMES - private static final int URI_MATCH_BOOKMARKS = 0; + static final int URI_MATCH_BOOKMARKS = 0; private static final int URI_MATCH_SEARCHES = 1; // (id % 10) should match the table name index private static final int URI_MATCH_BOOKMARKS_ID = 10; @@ -179,7 +180,7 @@ public class BrowserProvider extends ContentProvider { // XXX: This is a major hack to remove our dependency on gsf constants and // its content provider. http://b/issue?id=2425179 - static String getClientId(ContentResolver cr) { + public static String getClientId(ContentResolver cr) { String ret = "android-google"; Cursor legacyClientIdCursor = null; Cursor searchClientIdCursor = null; @@ -245,7 +246,7 @@ public class BrowserProvider extends ContentProvider { return sb; } - private static class DatabaseHelper extends SQLiteOpenHelper { + static class DatabaseHelper extends SQLiteOpenHelper { private Context mContext; public DatabaseHelper(Context context) { @@ -839,7 +840,7 @@ public class BrowserProvider extends ContentProvider { * by the SearchDialog when the BrowserActivity is in voice search mode. * @param results Strings to display in the dropdown from the SearchDialog */ - /* package */ void setQueryResults(ArrayList<String> results) { + public /* package */ void setQueryResults(ArrayList<String> results) { synchronized (mResultsCursorLock) { if (results == null) { mResultsCursor = null; diff --git a/src/com/android/browser/provider/BrowserProvider2.java b/src/com/android/browser/provider/BrowserProvider2.java index d154f20..a8739ca 100644 --- a/src/com/android/browser/provider/BrowserProvider2.java +++ b/src/com/android/browser/provider/BrowserProvider2.java @@ -21,6 +21,7 @@ import com.google.common.annotations.VisibleForTesting; import com.android.browser.BookmarkUtils; import com.android.browser.BrowserBookmarksPage; import com.android.browser.R; +import com.android.browser.UrlUtils; import com.android.browser.widget.BookmarkThumbnailWidgetProvider; import com.android.common.content.SyncStateContentProviderHelper; @@ -63,6 +64,7 @@ import android.provider.SyncStateContract; import android.text.TextUtils; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; @@ -86,6 +88,8 @@ public class BrowserProvider2 extends SQLiteContentProvider { static final String TABLE_HISTORY_JOIN_IMAGES = "history LEFT OUTER JOIN images " + "ON history.url = images." + Images.URL; + static final String VIEW_ACCOUNTS = "v_accounts"; + static final String FORMAT_COMBINED_JOIN_SUBQUERY_JOIN_IMAGES = "history LEFT OUTER JOIN (%s) bookmarks " + "ON history.url = bookmarks.url LEFT OUTER JOIN images " + @@ -203,6 +207,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { map = ACCOUNTS_PROJECTION_MAP; map.put(Accounts.ACCOUNT_TYPE, Accounts.ACCOUNT_TYPE); map.put(Accounts.ACCOUNT_NAME, Accounts.ACCOUNT_NAME); + map.put(Accounts.ROOT_ID, Accounts.ROOT_ID); // Bookmarks map = BOOKMARKS_PROJECTION_MAP; @@ -328,7 +333,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { final class DatabaseHelper extends SQLiteOpenHelper { static final String DATABASE_NAME = "browser2.db"; - static final int DATABASE_VERSION = 26; + static final int DATABASE_VERSION = 27; public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @@ -390,15 +395,109 @@ public class BrowserProvider2 extends SQLiteContentProvider { Settings.VALUE + " TEXT NOT NULL" + ");"); + createAccountsView(db); + mSyncHelper.createDatabase(db); - createDefaultBookmarks(db); + if (!importFromBrowserProvider(db)) { + createDefaultBookmarks(db); + } + } + + boolean importFromBrowserProvider(SQLiteDatabase db) { + Context context = getContext(); + File oldDbFile = context.getDatabasePath(BrowserProvider.sDatabaseName); + if (oldDbFile.exists()) { + BrowserProvider.DatabaseHelper helper = + new BrowserProvider.DatabaseHelper(context); + SQLiteDatabase oldDb = helper.getWritableDatabase(); + Cursor c = null; + try { + String table = BrowserProvider.TABLE_NAMES[BrowserProvider.URI_MATCH_BOOKMARKS]; + // Import bookmarks + c = oldDb.query(table, + new String[] { + BookmarkColumns.URL, // 0 + BookmarkColumns.TITLE, // 1 + BookmarkColumns.FAVICON, // 2 + BookmarkColumns.TOUCH_ICON, // 3 + }, BookmarkColumns.BOOKMARK + "!=0", null, + null, null, null); + if (c != null) { + while (c.moveToNext()) { + ContentValues values = new ContentValues(); + values.put(Bookmarks.URL, c.getString(0)); + values.put(Bookmarks.TITLE, c.getString(1)); + values.put(Bookmarks.POSITION, 0); + values.put(Bookmarks.PARENT, FIXED_ID_ROOT); + ContentValues imageValues = new ContentValues(); + imageValues.put(Images.URL, c.getString(0)); + imageValues.put(Images.FAVICON, c.getBlob(2)); + imageValues.put(Images.TOUCH_ICON, c.getBlob(3)); + db.insertOrThrow(TABLE_IMAGES, Images.THUMBNAIL, imageValues); + db.insertOrThrow(TABLE_BOOKMARKS, Bookmarks.DIRTY, values); + } + c.close(); + } + // Import history + c = oldDb.query(table, + new String[] { + BookmarkColumns.URL, // 0 + BookmarkColumns.TITLE, // 1 + BookmarkColumns.VISITS, // 2 + BookmarkColumns.DATE, // 3 + BookmarkColumns.CREATED, // 4 + }, null, null, null, null, null); + if (c != null) { + while (c.moveToNext()) { + ContentValues values = new ContentValues(); + values.put(History.URL, c.getString(0)); + values.put(History.TITLE, c.getString(1)); + values.put(History.VISITS, c.getInt(2)); + values.put(History.DATE_LAST_VISITED, c.getLong(3)); + values.put(History.DATE_CREATED, c.getLong(4)); + db.insertOrThrow(TABLE_HISTORY, History.FAVICON, values); + } + c.close(); + } + // Wipe the old DB, in case the delete fails. + oldDb.delete(table, null, null); + } finally { + if (c != null) c.close(); + oldDb.close(); + helper.close(); + } + if (!oldDbFile.delete()) { + oldDbFile.deleteOnExit(); + } + return true; + } + return false; + } + + void createAccountsView(SQLiteDatabase db) { + db.execSQL("CREATE VIEW IF NOT EXISTS v_accounts AS " + + "SELECT NULL AS " + Accounts.ACCOUNT_NAME + + ", NULL AS " + Accounts.ACCOUNT_TYPE + + ", " + FIXED_ID_ROOT + " AS " + Accounts.ROOT_ID + + " UNION ALL SELECT " + Accounts.ACCOUNT_NAME + + ", " + Accounts.ACCOUNT_TYPE + ", " + + Bookmarks._ID + " AS " + Accounts.ROOT_ID + + " FROM " + TABLE_BOOKMARKS + " WHERE " + + ChromeSyncColumns.SERVER_UNIQUE + " = \"" + + ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR + "\" AND " + + Bookmarks.IS_DELETED + " = 0"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO write upgrade logic - db.execSQL("DROP VIEW IF EXISTS combined"); + if (oldVersion < 27) { + createAccountsView(db); + } + if (oldVersion < 26) { + db.execSQL("DROP VIEW IF EXISTS combined"); + } if (oldVersion < 25) { db.execSQL("DROP TABLE IF EXISTS " + TABLE_BOOKMARKS); db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY); @@ -650,10 +749,8 @@ public class BrowserProvider2 extends SQLiteContentProvider { String limit = uri.getQueryParameter(BrowserContract.PARAM_LIMIT); switch (match) { case ACCOUNTS: { - qb.setTables(TABLE_BOOKMARKS); + qb.setTables(VIEW_ACCOUNTS); qb.setProjectionMap(ACCOUNTS_PROJECTION_MAP); - qb.setDistinct(true); - qb.appendWhere(Bookmarks.ACCOUNT_NAME + " IS NOT NULL"); break; } @@ -1523,9 +1620,35 @@ public class BrowserProvider2 extends SQLiteContentProvider { String[] selectionArgs, boolean callerIsSyncAdapter) { int count = 0; final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - Cursor cursor = db.query(TABLE_BOOKMARKS, - new String[] { Bookmarks._ID, Bookmarks.VERSION, Bookmarks.URL }, + final String[] bookmarksProjection = new String[] { + Bookmarks._ID, // 0 + Bookmarks.VERSION, // 1 + Bookmarks.URL, // 2 + Bookmarks.TITLE, // 3 + Bookmarks.IS_FOLDER, // 4 + Bookmarks.ACCOUNT_NAME, // 5 + Bookmarks.ACCOUNT_TYPE, // 6 + }; + Cursor cursor = db.query(TABLE_BOOKMARKS, bookmarksProjection, selection, selectionArgs, null, null, null); + boolean updatingParent = values.containsKey(Bookmarks.PARENT); + String parentAccountName = null; + String parentAccountType = null; + if (updatingParent) { + long parent = values.getAsLong(Bookmarks.PARENT); + Cursor c = db.query(TABLE_BOOKMARKS, new String[] { + Bookmarks.ACCOUNT_NAME, Bookmarks.ACCOUNT_TYPE}, + "_id = ?", new String[] { Long.toString(parent) }, + null, null, null); + if (c.moveToFirst()) { + parentAccountName = c.getString(0); + parentAccountType = c.getString(1); + c.close(); + } + } else if (values.containsKey(Bookmarks.ACCOUNT_NAME) + || values.containsKey(Bookmarks.ACCOUNT_TYPE)) { + // TODO: Implement if needed (no one needs this yet) + } try { String[] args = new String[1]; // Mark the bookmark dirty if the caller isn't a sync adapter @@ -1542,12 +1665,47 @@ public class BrowserProvider2 extends SQLiteContentProvider { ContentValues imageValues = extractImageValues(values, url); while (cursor.moveToNext()) { - args[0] = cursor.getString(0); - if (!callerIsSyncAdapter) { - // increase the local version for non-sync changes - values.put(Bookmarks.VERSION, cursor.getLong(1) + 1); + long id = cursor.getLong(0); + args[0] = Long.toString(id); + String accountName = cursor.getString(5); + String accountType = cursor.getString(6); + // If we are updating the parent and either the account name or + // type do not match that of the new parent + if (updatingParent + && (!TextUtils.equals(accountName, parentAccountName) + || !TextUtils.equals(accountType, parentAccountType))) { + // Parent is a different account + // First, insert a new bookmark/folder with the new account + // Then, if this is a folder, reparent all it's children + // Finally, delete the old bookmark/folder + ContentValues newValues = valuesFromCursor(cursor); + newValues.putAll(values); + newValues.remove(Bookmarks._ID); + newValues.remove(Bookmarks.VERSION); + newValues.put(Bookmarks.ACCOUNT_NAME, parentAccountName); + newValues.put(Bookmarks.ACCOUNT_TYPE, parentAccountType); + Uri insertUri = insertInTransaction(Bookmarks.CONTENT_URI, + newValues, callerIsSyncAdapter); + long newId = ContentUris.parseId(insertUri); + if (cursor.getInt(4) != 0) { + // This is a folder, reparent + ContentValues updateChildren = new ContentValues(1); + updateChildren.put(Bookmarks.PARENT, newId); + count += updateBookmarksInTransaction(updateChildren, + Bookmarks.PARENT + "=?", new String[] { + Long.toString(id)}, callerIsSyncAdapter); + } + // Now, delete the old one + Uri uri = ContentUris.withAppendedId(Bookmarks.CONTENT_URI, id); + deleteInTransaction(uri, null, null, callerIsSyncAdapter); + count += 1; + } else { + if (!callerIsSyncAdapter) { + // increase the local version for non-sync changes + values.put(Bookmarks.VERSION, cursor.getLong(1) + 1); + } + count += db.update(TABLE_BOOKMARKS, values, "_id=?", args); } - count += db.update(TABLE_BOOKMARKS, values, "_id=?", args); // Update the images over in their table if (imageValues != null) { @@ -1570,6 +1728,29 @@ public class BrowserProvider2 extends SQLiteContentProvider { return count; } + ContentValues valuesFromCursor(Cursor c) { + int count = c.getColumnCount(); + ContentValues values = new ContentValues(count); + String[] colNames = c.getColumnNames(); + for (int i = 0; i < count; i++) { + switch (c.getType(i)) { + case Cursor.FIELD_TYPE_BLOB: + values.put(colNames[i], c.getBlob(i)); + break; + case Cursor.FIELD_TYPE_FLOAT: + values.put(colNames[i], c.getFloat(i)); + break; + case Cursor.FIELD_TYPE_INTEGER: + values.put(colNames[i], c.getLong(i)); + break; + case Cursor.FIELD_TYPE_STRING: + values.put(colNames[i], c.getString(i)); + break; + } + } + return values; + } + /** * Does a query to find the matching bookmarks and updates each one with the provided values. */ @@ -1724,7 +1905,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { case SUGGEST_COLUMN_INTENT_DATA_ID: case SUGGEST_COLUMN_TEXT_2_TEXT_ID: case SUGGEST_COLUMN_TEXT_2_URL_ID: - return mSource.getString(URL_INDEX); + return UrlUtils.stripUrl(mSource.getString(URL_INDEX)); case SUGGEST_COLUMN_TEXT_1_ID: return mSource.getString(TITLE_INDEX); case SUGGEST_COLUMN_ICON_1_ID: diff --git a/src/com/android/browser/search/SearchEngines.java b/src/com/android/browser/search/SearchEngines.java index a159f17..fd967f9 100644 --- a/src/com/android/browser/search/SearchEngines.java +++ b/src/com/android/browser/search/SearchEngines.java @@ -32,7 +32,7 @@ public class SearchEngines { private static final String TAG = "SearchEngines"; public static SearchEngine getDefaultSearchEngine(Context context) { - if (BrowserSettings.getInstance().useInstant()) { + if (BrowserSettings.getInstance().useInstantSearch()) { return new InstantSearchEngine(context, DefaultSearchEngine.create(context)); } diff --git a/src/com/android/browser/view/BookmarkContainer.java b/src/com/android/browser/view/BookmarkContainer.java new file mode 100644 index 0000000..260b05e --- /dev/null +++ b/src/com/android/browser/view/BookmarkContainer.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2011 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.view; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.StateListDrawable; +import android.graphics.drawable.TransitionDrawable; +import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewConfiguration; +import android.widget.RelativeLayout; + +public class BookmarkContainer extends RelativeLayout implements OnClickListener { + + private OnClickListener mClickListener; + + public BookmarkContainer(Context context) { + super(context); + init(); + } + + public BookmarkContainer(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public BookmarkContainer( + Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + void init() { + setFocusable(true); + super.setOnClickListener(this); + } + + @Override + public void setBackgroundDrawable(Drawable d) { + super.setBackgroundDrawable(d); + } + + @Override + public void setOnClickListener(OnClickListener l) { + mClickListener = l; + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + updateTransitionDrawable(isPressed()); + } + + void updateTransitionDrawable(boolean pressed) { + final int longPressTimeout = ViewConfiguration.getLongPressTimeout(); + Drawable selector = getBackground(); + if (selector != null && selector instanceof StateListDrawable) { + Drawable d = ((StateListDrawable)selector).getCurrent(); + if (d != null && d instanceof TransitionDrawable) { + if (pressed && isLongClickable()) { + ((TransitionDrawable) d).startTransition(longPressTimeout); + } else { + ((TransitionDrawable) d).resetTransition(); + } + } + } + } + + @Override + public void onClick(View view) { + updateTransitionDrawable(false); + if (mClickListener != null) { + mClickListener.onClick(view); + } + } +} diff --git a/src/com/android/browser/view/BookmarkExpandableGridView.java b/src/com/android/browser/view/BookmarkExpandableGridView.java new file mode 100644 index 0000000..2cf4a65 --- /dev/null +++ b/src/com/android/browser/view/BookmarkExpandableGridView.java @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2011 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.view; + +import com.android.browser.BookmarkDragHandler; +import com.android.browser.BreadCrumbView; +import com.android.browser.BrowserBookmarksAdapter; +import com.android.browser.R; +import com.android.browser.BookmarkDragHandler.BookmarkDragAdapter; +import com.android.internal.view.menu.MenuBuilder; + +import android.content.Context; +import android.database.Cursor; +import android.database.DataSetObserver; +import android.provider.BrowserContract; +import android.util.AttributeSet; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseExpandableListAdapter; +import android.widget.ExpandableListAdapter; +import android.widget.ExpandableListView; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.HashMap; + +public class BookmarkExpandableGridView extends ExpandableListView + implements BreadCrumbView.Controller { + + private BookmarkAccountAdapter mAdapter; + private int mColumnWidth; + private Context mContext; + private OnChildClickListener mOnChildClickListener; + private ContextMenuInfo mContextMenuInfo = null; + private OnCreateContextMenuListener mOnCreateContextMenuListener; + private boolean mLongClickable; + private BreadCrumbView.Controller mBreadcrumbController; + private BookmarkDragHandler mDragHandler; + + public BookmarkExpandableGridView(Context context) { + super(context); + init(context); + } + + public BookmarkExpandableGridView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public BookmarkExpandableGridView( + Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context); + } + + void init(Context context) { + mContext = context; + setItemsCanFocus(true); + setLongClickable(false); + mAdapter = new BookmarkAccountAdapter(mContext); + super.setAdapter(mAdapter); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + mAdapter.measureChildren(); + } + + @Override + public void setAdapter(ExpandableListAdapter adapter) { + throw new RuntimeException("Not supported"); + } + + public void setColumnWidthFromLayout(int layout) { + LayoutInflater infalter = LayoutInflater.from(mContext); + View v = infalter.inflate(layout, this, false); + v.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + mColumnWidth = v.getMeasuredWidth(); + } + + public void setHorizontalSpacing(int horizontalSpacing) { + // TODO Auto-generated method stub + } + + public void clearAccounts() { + mAdapter.clear(); + } + + public void addAccount(String accountName, BrowserBookmarksAdapter adapter) { + // First, check if it already exists + int indexOf = mAdapter.mGroups.indexOf(accountName); + if (indexOf >= 0) { + BrowserBookmarksAdapter existing = mAdapter.mChildren.get(indexOf); + if (existing != adapter) { + existing.unregisterDataSetObserver(mAdapter.mObserver); + // Replace the existing one + mAdapter.mChildren.remove(indexOf); + mAdapter.mChildren.add(indexOf, adapter); + adapter.registerDataSetObserver(mAdapter.mObserver); + } + } else { + mAdapter.mGroups.add(accountName); + mAdapter.mChildren.add(adapter); + adapter.registerDataSetObserver(mAdapter.mObserver); + } + mAdapter.notifyDataSetChanged(); + expandGroup(mAdapter.getGroupCount() - 1); + } + + @Override + public void setOnChildClickListener(OnChildClickListener onChildClickListener) { + mOnChildClickListener = onChildClickListener; + } + + @Override + public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) { + mOnCreateContextMenuListener = l; + if (!mLongClickable) { + mLongClickable = true; + if (mAdapter != null) { + mAdapter.notifyDataSetChanged(); + } + } + } + + @Override + public void createContextMenu(ContextMenu menu) { + // The below is copied from View - we want to bypass the override + // in AbsListView + + ContextMenuInfo menuInfo = getContextMenuInfo(); + + // Sets the current menu info so all items added to menu will have + // my extra info set. + ((MenuBuilder)menu).setCurrentMenuInfo(menuInfo); + + onCreateContextMenu(menu); + if (mOnCreateContextMenuListener != null) { + mOnCreateContextMenuListener.onCreateContextMenu(menu, this, menuInfo); + } + + // Clear the extra information so subsequent items that aren't mine don't + // have my extra info. + ((MenuBuilder)menu).setCurrentMenuInfo(null); + + if (mParent != null) { + mParent.createContextMenu(menu); + } + } + + @Override + public boolean showContextMenuForChild(View originalView) { + int groupPosition = (Integer) originalView.getTag(R.id.group_position); + int childPosition = (Integer) originalView.getTag(R.id.child_position); + + mContextMenuInfo = new BookmarkContextMenuInfo(childPosition, + groupPosition); + if (getParent() != null) { + getParent().showContextMenuForChild(this); + } + + return true; + } + + @Override + public void onTop(BreadCrumbView view, int level, Object data) { + if (mBreadcrumbController != null) { + mBreadcrumbController.onTop(view, level, data); + } + } + + public void setBreadcrumbController(BreadCrumbView.Controller controller) { + mBreadcrumbController = controller; + } + + @Override + protected ContextMenuInfo getContextMenuInfo() { + return mContextMenuInfo; + } + + public BrowserBookmarksAdapter getChildAdapter(int groupPosition) { + return mAdapter.mChildren.get(groupPosition); + } + + public BookmarkDragAdapter getDragAdapter() { + return mDragAdapter; + } + + private BookmarkDragAdapter mDragAdapter = new BookmarkDragAdapter() { + + @Override + public void setBookmarkDragHandler(BookmarkDragHandler handler) { + mDragHandler = handler; + } + + @Override + public Cursor getItemForView(View v) { + int groupPosition = (Integer) v.getTag(R.id.group_position); + int childPosition = (Integer) v.getTag(R.id.child_position); + return getChildAdapter(groupPosition).getItem(childPosition); + } + }; + + private OnClickListener mChildClickListener = new OnClickListener() { + + @Override + public void onClick(View v) { + int groupPosition = (Integer) v.getTag(R.id.group_position); + int childPosition = (Integer) v.getTag(R.id.child_position); + long id = (Long) v.getTag(R.id.child_id); + if (mOnChildClickListener != null) { + mOnChildClickListener.onChildClick(BookmarkExpandableGridView.this, + v, groupPosition, childPosition, id); + } + } + }; + + private OnClickListener mGroupOnClickListener = new OnClickListener() { + + @Override + public void onClick(View v) { + int groupPosition = (Integer) v.getTag(R.id.group_position); + if (isGroupExpanded(groupPosition)) { + collapseGroup(groupPosition); + } else { + expandGroup(groupPosition, true); + } + } + }; + + private OnLongClickListener mChildOnLongClickListener = new OnLongClickListener() { + + @Override + public boolean onLongClick(View v) { + int groupPosition = (Integer) v.getTag(R.id.group_position); + int childPosition = (Integer) v.getTag(R.id.child_position); + long id = (Long) v.getTag(R.id.child_id); + Cursor c = getChildAdapter(groupPosition).getItem(childPosition); + return mDragHandler.startDrag(v, c, id); + } + }; + + public BreadCrumbView getBreadCrumbs(int groupPosition) { + return mAdapter.getBreadCrumbView(groupPosition); + } + + class BookmarkAccountAdapter extends BaseExpandableListAdapter { + ArrayList<BrowserBookmarksAdapter> mChildren; + ArrayList<String> mGroups; + HashMap<Integer, BreadCrumbView> mBreadcrumbs = + new HashMap<Integer, BreadCrumbView>(); + LayoutInflater mInflater; + int mRowCount = 1; // assume at least 1 child fits in a row + int mLastViewWidth = -1; + int mRowPadding = -1; + DataSetObserver mObserver = new DataSetObserver() { + @Override + public void onChanged() { + notifyDataSetChanged(); + } + + @Override + public void onInvalidated() { + notifyDataSetChanged(); + } + }; + + public BookmarkAccountAdapter(Context context) { + mContext = context; + mInflater = LayoutInflater.from(mContext); + mChildren = new ArrayList<BrowserBookmarksAdapter>(); + mGroups = new ArrayList<String>(); + } + + public void clear() { + mGroups.clear(); + mChildren.clear(); + notifyDataSetChanged(); + } + + @Override + public Object getChild(int groupPosition, int childPosition) { + return mChildren.get(groupPosition).getItem(childPosition); + } + + @Override + public long getChildId(int groupPosition, int childPosition) { + return childPosition; + } + + @Override + public View getChildView(int groupPosition, int childPosition, + boolean isLastChild, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = mInflater.inflate(R.layout.bookmark_grid_row, parent, false); + } + LinearLayout row = (LinearLayout) convertView; + row.setPadding( + mRowPadding, + row.getPaddingTop(), + mRowPadding, + row.getPaddingBottom()); + if (row.getChildCount() > mRowCount) { + row.removeViews(mRowCount, row.getChildCount() - mRowCount); + } + for (int i = 0; i < mRowCount; i++) { + View cv = null; + if (row.getChildCount() > i) { + cv = row.getChildAt(i); + } + int realChildPosition = (childPosition * mRowCount) + i; + BrowserBookmarksAdapter childAdapter = mChildren.get(groupPosition); + if (realChildPosition < childAdapter.getCount()) { + View v = childAdapter.getView(realChildPosition, cv, row); + v.setTag(R.id.group_position, groupPosition); + v.setTag(R.id.child_position, realChildPosition); + v.setTag(R.id.child_id, childAdapter.getItemId(realChildPosition)); + v.setOnClickListener(mChildClickListener); + v.setLongClickable(mLongClickable); + if (mDragHandler != null) { + v.setOnLongClickListener(mChildOnLongClickListener); + mDragHandler.registerBookmarkDragHandler(v); + } + if (cv == null) { + row.addView(v); + } else if (cv != v) { + row.removeViewAt(i); + row.addView(v, i); + } else { + cv.setVisibility(View.VISIBLE); + } + } else if (cv != null) { + cv.setVisibility(View.GONE); + } + } + return row; + } + + @Override + public int getChildrenCount(int groupPosition) { + return (int) Math.ceil( + mChildren.get(groupPosition).getCount() / (float)mRowCount); + } + + @Override + public Object getGroup(int groupPosition) { + return mChildren.get(groupPosition); + } + + @Override + public int getGroupCount() { + return mGroups.size(); + } + + public void measureChildren() { + int viewWidth = getMeasuredWidth(); + if (mLastViewWidth == viewWidth) return; + + ViewGroup parent = (ViewGroup) mInflater.inflate(R.layout.bookmark_grid_row, null); + viewWidth -= parent.getPaddingLeft() + parent.getPaddingRight(); + int rowCount = viewWidth / mColumnWidth; + int rowPadding = (viewWidth - (rowCount * mColumnWidth)) / 2; + boolean notify = rowCount != mRowCount || rowPadding != mRowPadding; + mRowCount = rowCount; + mRowPadding = rowPadding; + mLastViewWidth = viewWidth; + if (notify) { + notifyDataSetChanged(); + } + } + + @Override + public long getGroupId(int groupPosition) { + return groupPosition; + } + + @Override + public View getGroupView(int groupPosition, boolean isExpanded, + View view, ViewGroup parent) { + if (view == null) { + view = mInflater.inflate(R.layout.bookmark_group_view, parent, false); + view.setOnClickListener(mGroupOnClickListener); + } + view.setTag(R.id.group_position, groupPosition); + FrameLayout crumbHolder = (FrameLayout) view.findViewById(R.id.crumb_holder); + crumbHolder.removeAllViews(); + BreadCrumbView crumbs = getBreadCrumbView(groupPosition); + if (crumbs.getParent() != null) { + ((ViewGroup)crumbs.getParent()).removeView(crumbs); + } + crumbHolder.addView(crumbs); + TextView name = (TextView) view.findViewById(R.id.group_name); + String groupName = mGroups.get(groupPosition); + if (groupName == null) { + groupName = mContext.getString(R.string.local_bookmarks); + } + name.setText(groupName); + return view; + } + + public BreadCrumbView getBreadCrumbView(int groupPosition) { + BreadCrumbView crumbs = mBreadcrumbs.get(groupPosition); + if (crumbs == null) { + crumbs = (BreadCrumbView) + mInflater.inflate(R.layout.bookmarks_header, null); + crumbs.setController(BookmarkExpandableGridView.this); + crumbs.setUseBackButton(true); + crumbs.setMaxVisible(2); + String bookmarks = mContext.getString(R.string.bookmarks); + crumbs.pushView(bookmarks, false, + BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER); + crumbs.setTag(R.id.group_position, groupPosition); + mBreadcrumbs.put(groupPosition, crumbs); + } + return crumbs; + } + + @Override + public boolean hasStableIds() { + return false; + } + + @Override + public boolean isChildSelectable(int groupPosition, int childPosition) { + return true; + } + } + + public static class BookmarkContextMenuInfo implements ContextMenuInfo { + + private BookmarkContextMenuInfo(int childPosition, int groupPosition) { + this.childPosition = childPosition; + this.groupPosition = groupPosition; + } + + public int childPosition; + public int groupPosition; + } + +} diff --git a/src/com/android/browser/view/StopProgressView.java b/src/com/android/browser/view/StopProgressView.java new file mode 100644 index 0000000..64fa5d0 --- /dev/null +++ b/src/com/android/browser/view/StopProgressView.java @@ -0,0 +1,98 @@ + +package com.android.browser.view; + +import com.android.browser.R; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.ProgressBar; + + +public class StopProgressView extends ProgressBar { + + Drawable mOverlayDrawable; + Drawable mProgressDrawable; + int mWidth; + int mHeight; + + /** + * @param context + * @param attrs + * @param defStyle + * @param styleRes + */ + public StopProgressView(Context context, AttributeSet attrs, int defStyle, int styleRes) { + super(context, attrs, defStyle, styleRes); + init(attrs); + } + + /** + * @param context + * @param attrs + * @param defStyle + */ + public StopProgressView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs); + } + + /** + * @param context + * @param attrs + */ + public StopProgressView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + /** + * @param context + */ + public StopProgressView(Context context) { + super(context); + init(null); + } + + private void init(AttributeSet attrs) { + mProgressDrawable = getIndeterminateDrawable(); + setImageDrawable(mContext.getResources() + .getDrawable(R.drawable.ic_stop_holo_dark)); + } + + public void hideProgress() { + setIndeterminateDrawable(null); + } + + public void showProgress() { + setIndeterminateDrawable(mProgressDrawable); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + mWidth = (right - left) * 2 / 3; + mHeight = (bottom - top) * 2 / 3; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (mOverlayDrawable != null) { + int l = (getWidth() - mWidth) / 2; + int t = (getHeight() - mHeight) / 2; + mOverlayDrawable.setBounds(l, t, l + mWidth, t + mHeight); + mOverlayDrawable.draw(canvas); + } + } + + public Drawable getDrawable() { + return mOverlayDrawable; + } + + public void setImageDrawable(Drawable d) { + mOverlayDrawable = d; + } + +} diff --git a/src/com/android/browser/RlzReceiver.java b/src/com/android/browser/view/TabHolderView.java index 1dfb11a..c5a2b32 100644 --- a/src/com/android/browser/RlzReceiver.java +++ b/src/com/android/browser/view/TabHolderView.java @@ -14,25 +14,39 @@ * limitations under the License. */ -package com.android.browser; +package com.android.browser.view; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; +import android.util.AttributeSet; +import android.widget.LinearLayout; -/** - * This {@link BroadcastReceiver} handles RLZ broadcast notifications. - */ -public class RlzReceiver extends BroadcastReceiver { - public static final String RLZ_VALUES_UPDATED_ACTION = - "android.intent.action.RLZ_VALUES_UPDATED"; +public class TabHolderView extends LinearLayout { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (RLZ_VALUES_UPDATED_ACTION.equals(action)) { - BrowserSettings settings = BrowserSettings.getInstance(); - settings.updateRlzValues(context); - } + /** + * @param context + * @param attrs + * @param defStyle + */ + public TabHolderView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); } + + /** + * @param context + * @param attrs + */ + public TabHolderView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * @param context + */ + public TabHolderView(Context context) { + super(context); + } + + @Override + public void setPressed(boolean p) {} + } |