Browser: add to support new folder/bookmark function

Let browser's bookmark management support new folder/bookmark
through press menu.

Change-Id: Ic676bd76d27e9350d56d65a79f9a8a7d549f9069
diff --git a/src/com/android/browser/AddBookmarkFolder.java b/src/com/android/browser/AddBookmarkFolder.java
new file mode 100644
index 0000000..7fae373
--- /dev/null
+++ b/src/com/android/browser/AddBookmarkFolder.java
@@ -0,0 +1,951 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+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.DialogInterface;
+import android.content.Loader;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.BrowserContract;
+import android.provider.BrowserContract.Accounts;
+import android.text.InputFilter;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+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.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.browser.addbookmark.FolderSpinner;
+import com.android.browser.addbookmark.FolderSpinnerAdapter;
+import com.android.browser.provider.BrowserProvider2;
+
+public class AddBookmarkFolder extends Activity implements View.OnClickListener,
+        TextView.OnEditorActionListener, AdapterView.OnItemClickListener,
+        LoaderManager.LoaderCallbacks<Cursor>, BreadCrumbView.Controller,
+        FolderSpinner.OnSetSelectionListener, OnItemSelectedListener {
+
+    public static final long DEFAULT_FOLDER_ID = -1;
+
+    // Place on an edited bookmark to remove the saved thumbnail
+    public static final String CHECK_FOR_DUPE = "check_for_dupe";
+
+    public static final String BOOKMARK_CURRENT_ID = "bookmark_current_id";
+
+    /* package */static final String EXTRA_EDIT_BOOKMARK = "bookmark";
+
+    /* package */static final String EXTRA_IS_FOLDER = "is_folder";
+
+    private static final int MAX_CRUMBS_SHOWN = 2;
+
+    private long mOriginalFolder = -1;
+
+    private boolean mIsFolderChanged = false;
+
+    private boolean mIsOtherFolderSelected = false;
+
+    private boolean mIsRecentFolder = false;
+
+    // IDs for the CursorLoaders that are used.
+    private static final int LOADER_ID_ACCOUNTS = 0;
+
+    private static final int LOADER_ID_FOLDER_CONTENTS = 1;
+
+    private static final int LOADER_ID_EDIT_INFO = 2;
+
+    private EditText mTitle;
+
+    private EditText mAddress;
+
+    private TextView mButton;
+
+    private View mCancelButton;
+
+    private Bundle mMap;
+
+    private FolderSpinner mFolder;
+
+    private View mDefaultView;
+
+    private View mFolderSelector;
+
+    private EditText mFolderNamer;
+
+    private View mFolderCancel;
+
+    private boolean mIsFolderNamerShowing;
+
+    private View mFolderNamerHolder;
+
+    private View mAddNewFolder;
+
+    private View mAddSeparator;
+
+    private long mCurrentFolder;
+
+    private FolderAdapter mAdapter;
+
+    private BreadCrumbView mCrumbs;
+
+    private TextView mFakeTitle;
+
+    private View mCrumbHolder;
+
+    private AddBookmarkPage.CustomListView mListView;
+
+    private long mRootFolder;
+
+    private TextView mTopLevelLabel;
+
+    private Drawable mHeaderIcon;
+
+    private View mRemoveLink;
+
+    private View mFakeTitleHolder;
+
+    private FolderSpinnerAdapter mFolderAdapter;
+
+    private Spinner mAccountSpinner;
+
+    private ArrayAdapter<BookmarkAccount> mAccountAdapter;
+
+
+    private static class Folder {
+        String mName;
+
+        long mId;
+
+        Folder(String name, long id) {
+            mName = name;
+            mId = id;
+        }
+    }
+
+    private InputMethodManager getInputMethodManager() {
+        return (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
+    }
+
+    private Uri getUriForFolder(long folder) {
+        BookmarkAccount account = (BookmarkAccount) mAccountSpinner.getSelectedItem();
+        if (folder == mRootFolder && account != null) {
+            return BookmarksLoader.addAccount(BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER,
+                    account.mAccountType, account.mAccountName);
+        }
+        return BrowserContract.Bookmarks.buildFolderUri(folder);
+    }
+
+    public static long getIdFromData(Object data) {
+        if (data == null) {
+            return BrowserProvider2.FIXED_ID_ROOT;
+        } else {
+            Folder folder = (Folder) data;
+            return folder.mId;
+        }
+    }
+
+    @Override
+    public void onTop(BreadCrumbView view, int level, Object data) {
+        if (null == data) {
+            return;
+        }
+        Folder folderData = (Folder) data;
+        long folder = folderData.mId;
+        LoaderManager manager = getLoaderManager();
+        CursorLoader loader = (CursorLoader) ((Loader<?>) manager
+                .getLoader(LOADER_ID_FOLDER_CONTENTS));
+        loader.setUri(getUriForFolder(folder));
+        loader.forceLoad();
+        if (mIsFolderNamerShowing) {
+            completeOrCancelFolderNaming(true);
+        }
+        setShowBookmarkIcon(level == 1);
+    }
+
+    /**
+     * Show or hide the icon for bookmarks next to "Bookmarks" in the crumb
+     * view.
+     *
+     * @param show True if the icon should visible, false otherwise.
+     */
+    private void setShowBookmarkIcon(boolean show) {
+        Drawable drawable = show ? mHeaderIcon : null;
+        mTopLevelLabel.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
+    }
+
+    @Override
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        if (v == mFolderNamer) {
+            if (v.getText().length() > 0) {
+                if (actionId == EditorInfo.IME_NULL) {
+                    // Only want to do this once.
+                    if (event.getAction() == KeyEvent.ACTION_UP) {
+                        completeOrCancelFolderNaming(false);
+                    }
+                }
+            }
+            // Steal the key press; otherwise a newline will be added
+            return true;
+        }
+        return false;
+    }
+
+    private void switchToDefaultView(boolean changedFolder) {
+        mFolderSelector.setVisibility(View.GONE);
+        mDefaultView.setVisibility(View.VISIBLE);
+        mCrumbHolder.setVisibility(View.GONE);
+        mFakeTitleHolder.setVisibility(View.VISIBLE);
+        if (changedFolder) {
+            Object data = mCrumbs.getTopData();
+            if (data != null) {
+                Folder folder = (Folder) data;
+                mCurrentFolder = folder.mId;
+                if (mCurrentFolder == mRootFolder) {
+                    // The Spinner changed to show "Other folder ..." Change
+                    // it back to "Bookmarks", which is position 0 if we are
+                    // editing a folder, 1 otherwise.
+                    mFolder.setSelectionIgnoringSelectionChange(0);
+                } else {
+                    mFolderAdapter.setOtherFolderDisplayText(folder.mName);
+                }
+            }
+        } else {
+            if (mCurrentFolder == mRootFolder) {
+                mFolder.setSelectionIgnoringSelectionChange(0);
+            } else {
+                Object data = mCrumbs.getTopData();
+                if (data != null && ((Folder) data).mId == mCurrentFolder) {
+                    // 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.
+                    mFolderAdapter.setOtherFolderDisplayText(((Folder) data).mName);
+                } else {
+                    // We are not showing the correct folder hierarchy.
+                    // Clear the Crumbs and find the proper folder
+                    setupTopCrumb();
+                    LoaderManager manager = getLoaderManager();
+                    manager.restartLoader(LOADER_ID_FOLDER_CONTENTS, null, this);
+
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mButton) {
+            if (mFolderSelector.getVisibility() == View.VISIBLE) {
+                // We are showing the folder selector.
+                if (mIsFolderNamerShowing) {
+                    completeOrCancelFolderNaming(false);
+                } else {
+                    switchToDefaultView(true);
+                }
+            } else {
+                if (save()) {
+                    finish();
+                }
+            }
+        } else if (v == mCancelButton) {
+            if (mIsFolderNamerShowing) {
+                completeOrCancelFolderNaming(true);
+            } else if (mFolderSelector.getVisibility() == View.VISIBLE) {
+                switchToDefaultView(false);
+            } else {
+                finish();
+            }
+        } else if (v == mFolderCancel) {
+            completeOrCancelFolderNaming(true);
+        }
+    }
+
+    private void displayToastForExistingFolder() {
+        Toast.makeText(getApplicationContext(), R.string.duplicated_folder_warning,
+                Toast.LENGTH_LONG).show();
+    }
+
+    @Override
+    public void onSetSelection(long id) {
+        int intId = (int) id;
+        mIsFolderChanged = true;
+        mIsOtherFolderSelected = false;
+        mIsRecentFolder = false;
+        switch (intId) {
+            case FolderSpinnerAdapter.ROOT_FOLDER:
+                mCurrentFolder = mRootFolder;
+                mOriginalFolder = mCurrentFolder;
+                break;
+            case FolderSpinnerAdapter.HOME_SCREEN:
+
+                break;
+            case FolderSpinnerAdapter.OTHER_FOLDER:
+                mIsOtherFolderSelected = true;
+                switchToFolderSelector();
+                break;
+            case FolderSpinnerAdapter.RECENT_FOLDER:
+                mCurrentFolder = mFolderAdapter.recentFolderId();
+                mOriginalFolder = mCurrentFolder;
+                mIsRecentFolder = true;
+                // In case the user decides to select OTHER_FOLDER
+                // and choose a different one, so that we will start from
+                // the correct place.
+                LoaderManager manager = getLoaderManager();
+                manager.restartLoader(LOADER_ID_FOLDER_CONTENTS, null, this);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Finish naming a folder, and close the IME
+     *
+     * @param cancel If true, the new folder is not created. If false, the new
+     *            folder is created and the user is taken inside it.
+     */
+    private void completeOrCancelFolderNaming(boolean cancel) {
+        if (!cancel && !TextUtils.isEmpty(mFolderNamer.getText())) {
+            String name = mFolderNamer.getText().toString();
+            long id = addFolderToCurrent(mFolderNamer.getText().toString());
+            descendInto(name, id);
+        }
+        setShowFolderNamer(false);
+        getInputMethodManager().hideSoftInputFromWindow(mListView.getWindowToken(), 0);
+    }
+
+    private long addFolderToCurrent(String name) {
+        // Add the folder to the database
+        ContentValues values = new ContentValues();
+        values.put(BrowserContract.Bookmarks.TITLE, name);
+        values.put(BrowserContract.Bookmarks.IS_FOLDER, 1);
+        long currentFolder;
+        Object data = null;
+        if (null != mCrumbs) {
+            data = mCrumbs.getTopData();
+        }
+        if (data != null) {
+            currentFolder = ((Folder) data).mId;
+        } else {
+            currentFolder = mRootFolder;
+        }
+        currentFolder = mCurrentFolder;
+        if (mIsRecentFolder) {
+            values.put(BrowserContract.Bookmarks.PARENT, mCurrentFolder);
+        } else if (!(mIsFolderChanged && mIsOtherFolderSelected) && mOriginalFolder != -1) {
+            values.put(BrowserContract.Bookmarks.PARENT, mOriginalFolder);
+        } else {
+            values.put(BrowserContract.Bookmarks.PARENT, currentFolder);
+        }
+        Uri uri = getContentResolver().insert(BrowserContract.Bookmarks.CONTENT_URI, values);
+        if (uri != null) {
+            return ContentUris.parseId(uri);
+        } else {
+            return -1;
+        }
+    }
+
+    private void switchToFolderSelector() {
+        // Set the list to the top in case it is scrolled.
+        mListView.setSelection(0);
+        mFakeTitleHolder.setVisibility(View.GONE);
+        // mFakeTitle.setVisibility(View.GONE);
+        mDefaultView.setVisibility(View.GONE);
+        mFolderSelector.setVisibility(View.VISIBLE);
+        mCrumbHolder.setVisibility(View.VISIBLE);
+        getInputMethodManager().hideSoftInputFromWindow(mListView.getWindowToken(), 0);
+    }
+
+    private void descendInto(String foldername, long id) {
+        if (id != DEFAULT_FOLDER_ID) {
+            mCrumbs.pushView(foldername, new Folder(foldername, id));
+            mCrumbs.notifyController();
+        } else {
+            Toast.makeText(getApplicationContext(), R.string.duplicated_folder_warning,
+                    Toast.LENGTH_LONG).show();
+        }
+    }
+
+    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;
+            // 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.mLastUsedId != -1 && info.mLastUsedId != info.mId) {
+                if (setAccount && info.mLastUsedId != mRootFolder
+                        && TextUtils.equals(info.mLastUsedAccountName, info.mAccountName)
+                        && TextUtils.equals(info.mLastUsedAccountType, info.mAccountType)) {
+                    mFolderAdapter.addRecentFolder(info.mLastUsedId, info.mLastUsedTitle);
+                } else if (!setAccount) {
+                    setAccount = true;
+                    setAccount(info.mLastUsedAccountName, info.mLastUsedAccountType);
+                    if (info.mLastUsedId != mRootFolder) {
+                        mFolderAdapter.addRecentFolder(info.mLastUsedId, info.mLastUsedTitle);
+                    }
+                }
+            }
+            if (!setAccount) {
+                mAccountSpinner.setSelection(0);
+            }
+        }
+
+        @Override
+        public Loader<EditBookmarkInfo> onCreateLoader(int id, Bundle args) {
+            return new EditBookmarkInfoLoader(AddBookmarkFolder.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.mAccountName, accountName)
+                    && TextUtils.equals(account.mAccountType, accountType)) {
+                mAccountSpinner.setSelection(i);
+                onRootFolderFound(account.rootFolderId);
+                return;
+            }
+        }
+    }
+
+    @Override
+    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+        String[] projection;
+        switch (id) {
+            case LOADER_ID_ACCOUNTS:
+                return new AccountsLoader(this);
+            case LOADER_ID_FOLDER_CONTENTS:
+                projection = new String[] {
+                        BrowserContract.Bookmarks._ID, BrowserContract.Bookmarks.TITLE,
+                        BrowserContract.Bookmarks.IS_FOLDER
+                };
+                String where = BrowserContract.Bookmarks.IS_FOLDER + " != 0" + " AND "
+                        + BrowserContract.Bookmarks._ID + " != ?";
+                String whereArgs[] = new String[] {
+                        Long.toString(mMap.getLong(BrowserContract.Bookmarks._ID))
+                };
+                long currentFolder;
+                Object data = mCrumbs.getTopData();
+                if (data != null) {
+                    currentFolder = ((Folder) data).mId;
+                } else {
+                    currentFolder = mRootFolder;
+                }
+                return new CursorLoader(this, getUriForFolder(currentFolder), projection, where,
+                        whereArgs, BrowserContract.Bookmarks._ID + " ASC");
+            default:
+                throw new AssertionError("Asking for nonexistant loader!");
+        }
+    }
+
+    @Override
+    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
+        switch (loader.getId()) {
+            case LOADER_ID_ACCOUNTS:
+                mAccountAdapter.clear();
+                while (cursor.moveToNext()) {
+                    mAccountAdapter.add(new BookmarkAccount(this, cursor));
+                }
+                getLoaderManager().destroyLoader(LOADER_ID_ACCOUNTS);
+                getLoaderManager().restartLoader(LOADER_ID_EDIT_INFO, null,
+                        mEditInfoLoaderCallbacks);
+                break;
+            case LOADER_ID_FOLDER_CONTENTS:
+                mAdapter.changeCursor(cursor);
+                break;
+            default:
+                break;
+        }
+    }
+
+    public void onLoaderReset(Loader<Cursor> loader) {
+        switch (loader.getId()) {
+            case LOADER_ID_FOLDER_CONTENTS:
+                mAdapter.changeCursor(null);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Move cursor to the position that has folderToFind as its "_id".
+     *
+     * @param cursor Cursor containing folders in the bookmarks database
+     * @param folderToFind "_id" of the folder to move to.
+     * @param idIndex Index in cursor of "_id"
+     * @throws AssertionError if cursor is empty or there is no row with
+     *             folderToFind as its "_id".
+     */
+    void moveCursorToFolder(Cursor cursor, long folderToFind, int idIndex) throws AssertionError {
+        if (!cursor.moveToFirst()) {
+            throw new AssertionError("No folders in the database!");
+        }
+        long folder;
+        do {
+            folder = cursor.getLong(idIndex);
+        } while (folder != folderToFind && cursor.moveToNext());
+        if (cursor.isAfterLast()) {
+            throw new AssertionError("Folder(id=" + folderToFind
+                    + ") holding this bookmark does not exist!");
+        }
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        TextView tv = (TextView) view.findViewById(android.R.id.text1);
+        // Switch to the folder that was clicked on.
+        descendInto(tv.getText().toString(), id);
+    }
+
+    private void setShowFolderNamer(boolean show) {
+        if (show != mIsFolderNamerShowing) {
+            mIsFolderNamerShowing = show;
+            if (show) {
+                // Set the selection to the folder namer so it will be in
+                // view.
+                mListView.addFooterView(mFolderNamerHolder);
+            } else {
+                mListView.removeFooterView(mFolderNamerHolder);
+            }
+            // Refresh the list.
+            mListView.setAdapter(mAdapter);
+            if (show) {
+                mListView.setSelection(mListView.getCount() - 1);
+            }
+        }
+    }
+
+    /**
+     * Shows a list of names of folders.
+     */
+    private class FolderAdapter extends CursorAdapter {
+        public FolderAdapter(Context context) {
+            super(context, null);
+        }
+
+        @Override
+        public void bindView(View view, Context context, Cursor cursor) {
+            ((TextView) view.findViewById(android.R.id.text1)).setText(cursor.getString(cursor
+                    .getColumnIndexOrThrow(BrowserContract.Bookmarks.TITLE)));
+        }
+
+        @Override
+        public View newView(Context context, Cursor cursor, ViewGroup parent) {
+            View view = LayoutInflater.from(context).inflate(R.layout.folder_list_item, null);
+            view.setBackgroundDrawable(context.getResources().getDrawable(
+                    android.R.drawable.list_selector_background));
+            return view;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            // Do not show the empty view if the user is creating a new folder.
+            return super.isEmpty() && !mIsFolderNamerShowing;
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+        mMap = getIntent().getExtras();
+
+        setContentView(R.layout.browser_add_bookmark);
+
+        Window window = getWindow();
+
+        String title = this.getString(R.string.new_folder);
+        mFakeTitle = (TextView) findViewById(R.id.fake_title);
+        mFakeTitleHolder = findViewById(R.id.title_holder);
+        mFakeTitle.setText(this.getString(R.string.new_folder));
+
+        mTitle = (EditText) findViewById(R.id.title);
+        // add for cmcc test about waring limit of edit text
+        BrowserUtils.maxLengthFilter(AddBookmarkFolder.this, mTitle, BrowserUtils.FILENAME_MAX_LENGTH);
+
+        mTitle.setText(title);
+        mAddress = (EditText) findViewById(R.id.address);
+        mAddress.setVisibility(View.GONE);
+        findViewById(R.id.row_address).setVisibility(View.GONE);
+
+        mButton = (TextView) findViewById(R.id.OK);
+        mButton.setOnClickListener(this);
+
+        mCancelButton = findViewById(R.id.cancel);
+        mCancelButton.setOnClickListener(this);
+
+        mFolder = (FolderSpinner) findViewById(R.id.folder);
+        mFolderAdapter = new FolderSpinnerAdapter(this, false);
+        mFolder.setAdapter(mFolderAdapter);
+        mFolder.setOnSetSelectionListener(this);
+
+        mDefaultView = findViewById(R.id.default_view);
+        mFolderSelector = findViewById(R.id.folder_selector);
+
+        mFolderNamerHolder = getLayoutInflater().inflate(R.layout.new_folder_layout, null);
+        mFolderNamer = (EditText) mFolderNamerHolder.findViewById(R.id.folder_namer);
+        mFolderNamer.setOnEditorActionListener(this);
+        mFolderCancel = mFolderNamerHolder.findViewById(R.id.close);
+        mFolderCancel.setOnClickListener(this);
+
+        mAddNewFolder = findViewById(R.id.add_new_folder);
+        mAddNewFolder.setVisibility(View.GONE);
+        mAddSeparator = findViewById(R.id.add_divider);
+        mAddSeparator.setVisibility(View.GONE);
+
+        mCrumbs = (BreadCrumbView) findViewById(R.id.crumbs);
+        mCrumbs.setUseBackButton(true);
+        mCrumbs.setController(this);
+        mHeaderIcon = getResources().getDrawable(R.drawable.ic_folder_holo_dark);
+        mCrumbHolder = findViewById(R.id.crumb_holder);
+        mCrumbs.setMaxVisible(MAX_CRUMBS_SHOWN);
+
+        mAdapter = new FolderAdapter(this);
+        mListView = (AddBookmarkPage.CustomListView) findViewById(R.id.list);
+        View empty = findViewById(R.id.empty);
+        mListView.setEmptyView(empty);
+        mListView.setAdapter(mAdapter);
+        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);
+
+        if (!window.getDecorView().isInTouchMode()) {
+            mButton.requestFocus();
+        }
+        // getLoaderManager().restartLoader(LOADER_ID_ACCOUNTS, null, this);
+
+        setShowFolderNamer(false);
+        mFolderNamer.setText(R.string.new_folder);
+        mFolderNamer.requestFocus();
+        InputMethodManager imm = getInputMethodManager();
+        imm.focusIn(mListView);
+        imm.showSoftInput(mFolderNamer, InputMethodManager.SHOW_IMPLICIT);
+
+        mCurrentFolder = getIntent().getLongExtra(
+                BrowserContract.Bookmarks.PARENT, DEFAULT_FOLDER_ID);
+        mOriginalFolder = mCurrentFolder;
+        if (!(mCurrentFolder == -1 || mCurrentFolder == 1)) {
+            mFolder.setSelectionIgnoringSelectionChange(1);
+            mFolderAdapter.setOtherFolderDisplayText(getNameFromId(mOriginalFolder));
+        }
+
+        getLoaderManager().restartLoader(LOADER_ID_ACCOUNTS, null, this);
+    }
+
+    // get folder title from folder id
+    private String getNameFromId(long mCurrentFolder2) {
+        String title = "";
+        Cursor cursor = null;
+        try {
+            cursor = getApplicationContext().getContentResolver().query(
+                    BrowserContract.Bookmarks.CONTENT_URI,
+                    new String[] {
+                        BrowserContract.Bookmarks.TITLE
+                    },
+                    BrowserContract.Bookmarks._ID + " = ? AND "
+                            + BrowserContract.Bookmarks.IS_DELETED + " = ? AND "
+                            + BrowserContract.Bookmarks.IS_FOLDER + " = ? ", new String[] {
+                            String.valueOf(mCurrentFolder2), 0 + "", 1 + ""
+                    }, null);
+            if (cursor != null && cursor.getCount() != 0) {
+                while (cursor.moveToNext()) {
+                    title = cursor.getString(0);
+                }
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return title;
+    }
+
+    private void showRemoveButton() {
+        findViewById(R.id.remove_divider).setVisibility(View.VISIBLE);
+        mRemoveLink = findViewById(R.id.remove);
+        mRemoveLink.setVisibility(View.VISIBLE);
+        mRemoveLink.setOnClickListener(this);
+    }
+
+    // Called once we have determined which folder is the root folder
+    private void onRootFolderFound(long root) {
+        mRootFolder = root;
+        mCurrentFolder = mRootFolder;
+        setupTopCrumb();
+        onCurrentFolderFound();
+    }
+
+    private void setupTopCrumb() {
+        mCrumbs.clear();
+        String name = getString(R.string.bookmarks);
+        mTopLevelLabel = (TextView) mCrumbs.pushView(name, false, new Folder(name, mRootFolder));
+        // To better match the other folders.
+        mTopLevelLabel.setCompoundDrawablePadding(6);
+    }
+
+    private void onCurrentFolderFound() {
+        LoaderManager manager = getLoaderManager();
+        if (mCurrentFolder != mRootFolder) {
+            // 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(1);
+        } else {
+            setShowBookmarkIcon(true);
+        }
+        // Find the contents of the current folder
+        manager.restartLoader(LOADER_ID_FOLDER_CONTENTS, null, this);
+    }
+
+    /**
+     * Parse the data entered in the dialog and post a message to update the
+     * bookmarks database.
+     */
+    private boolean save() {
+        String title = mTitle.getText().toString().trim();
+
+        boolean emptyTitle = title.length() == 0;
+        Resources r = getResources();
+        if (emptyTitle) {
+            mTitle.setError(r.getText(R.string.bookmark_needs_title));
+            return false;
+        }
+
+        long id = addFolderToCurrent(title);
+        if (id == -1) {
+            displayToastForExistingFolder();
+            return false;
+        }
+
+        setResult(RESULT_OK);
+        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);
+                mFolderAdapter.clearRecentFolder();
+            }
+        }
+    }
+
+    @Override
+    public void onNothingSelected(AdapterView<?> parent) {
+        // Don't care
+    }
+
+    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, null);
+        }
+
+    }
+
+    public static class BookmarkAccount {
+
+        private String mLabel;
+
+        String mAccountName;
+        String mAccountType;
+
+        public long rootFolderId;
+
+        public BookmarkAccount(Context context, Cursor cursor) {
+            mAccountName = cursor.getString(AccountsLoader.COLUMN_INDEX_ACCOUNT_NAME);
+            mAccountType = cursor.getString(AccountsLoader.COLUMN_INDEX_ACCOUNT_TYPE);
+            rootFolderId = cursor.getLong(AccountsLoader.COLUMN_INDEX_ROOT_ID);
+            mLabel = mAccountName;
+            if (TextUtils.isEmpty(mLabel)) {
+                mLabel = context.getString(R.string.local_bookmarks);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return mLabel;
+        }
+    }
+
+    static class EditBookmarkInfo {
+        long mId = -1;
+
+        long mParentId = -1;
+
+        String mParentTitle;
+
+        String mEBITitle;
+
+        String mAccountName;
+
+        String mAccountType;
+
+        long mLastUsedId = -1;
+
+        String mLastUsedTitle;
+
+        String mLastUsedAccountName;
+
+        String mLastUsedAccountType;
+    }
+
+    static class EditBookmarkInfoLoader extends AsyncTaskLoader<EditBookmarkInfo> {
+
+        private Context mContext;
+
+        private Bundle mMap;
+
+        public EditBookmarkInfoLoader(Context context, Bundle bundle) {
+            super(context);
+            mContext = context.getApplicationContext();
+            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.mId = mMap.getLong(BrowserContract.Bookmarks._ID, -1);
+                boolean checkForDupe = mMap.getBoolean(CHECK_FOR_DUPE);
+                if (checkForDupe && info.mId == -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.mId = c.getLong(0);
+                    }
+                    c.close();
+                }
+                if (info.mId != -1) {
+                    c = cr.query(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI,
+                            info.mId), new String[] {
+                            BrowserContract.Bookmarks.PARENT,
+                            BrowserContract.Bookmarks.ACCOUNT_NAME,
+                            BrowserContract.Bookmarks.ACCOUNT_TYPE, BrowserContract.Bookmarks.TITLE
+                    }, null, null, null);
+                    if (c.moveToFirst()) {
+                        info.mParentId = c.getLong(0);
+                        info.mAccountName = c.getString(1);
+                        info.mAccountType = c.getString(2);
+                        info.mEBITitle = c.getString(3);
+                    }
+                    c.close();
+                    c = cr.query(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI,
+                            info.mParentId), new String[] {
+                            BrowserContract.Bookmarks.TITLE,
+                    }, null, null, null);
+                    if (c.moveToFirst()) {
+                        info.mParentTitle = 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.mLastUsedId = parent;
+                        info.mLastUsedTitle = c.getString(0);
+                        info.mLastUsedAccountName = c.getString(1);
+                        info.mLastUsedAccountType = 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/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java
index fdb34c4..580723c 100644
--- a/src/com/android/browser/AddBookmarkPage.java
+++ b/src/com/android/browser/AddBookmarkPage.java
@@ -16,10 +16,8 @@
 
 package com.android.browser;
 
-import com.android.browser.addbookmark.FolderSpinner;
-import com.android.browser.addbookmark.FolderSpinnerAdapter;
-
 import android.app.Activity;
+import android.app.AlertDialog;
 import android.app.LoaderManager;
 import android.app.LoaderManager.LoaderCallbacks;
 import android.content.AsyncTaskLoader;
@@ -28,6 +26,7 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.CursorLoader;
+import android.content.DialogInterface;
 import android.content.Loader;
 import android.content.res.Resources;
 import android.database.Cursor;
@@ -40,6 +39,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.provider.Browser;
 import android.provider.BrowserContract;
 import android.provider.BrowserContract.Accounts;
 import android.text.TextUtils;
@@ -62,6 +62,10 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.android.browser.BrowserUtils;
+import com.android.browser.addbookmark.FolderSpinner;
+import com.android.browser.addbookmark.FolderSpinnerAdapter;
+
 import java.net.URI;
 import java.net.URISyntaxException;
 
@@ -123,6 +127,9 @@
     private FolderSpinnerAdapter mFolderAdapter;
     private Spinner mAccountSpinner;
     private ArrayAdapter<BookmarkAccount> mAccountAdapter;
+    // add for carrier which requires same title or address can not exist.
+    private long mDuplicateId;
+    private Context mDuplicateContext;
 
     private static class Folder {
         String Name;
@@ -256,8 +263,16 @@
                     mSaveToHomeScreen = false;
                     switchToDefaultView(true);
                 }
-            } else if (save()) {
-                finish();
+            } else {
+                // add for carrier which requires same title or address can not
+                // exist.
+                if (mSaveToHomeScreen) {
+                    if (save()) {
+                        return;
+                    }
+                } else {
+                    onSaveWithConfirm();
+                }
             }
         } else if (v == mCancelButton) {
             if (mIsFolderNamerShowing) {
@@ -638,9 +653,11 @@
 
         mTitle = (EditText) findViewById(R.id.title);
         mTitle.setText(title);
+        BrowserUtils.maxLengthFilter(AddBookmarkPage.this, mTitle, BrowserUtils.FILENAME_MAX_LENGTH);
 
         mAddress = (EditText) findViewById(R.id.address);
         mAddress.setText(url);
+        BrowserUtils.maxLengthFilter(AddBookmarkPage.this, mAddress, BrowserUtils.ADDRESS_MAX_LENGTH);
 
         mButton = (TextView) findViewById(R.id.OK);
         mButton.setOnClickListener(this);
@@ -659,6 +676,11 @@
         mFolderNamerHolder = getLayoutInflater().inflate(R.layout.new_folder_layout, null);
         mFolderNamer = (EditText) mFolderNamerHolder.findViewById(R.id.folder_namer);
         mFolderNamer.setOnEditorActionListener(this);
+
+        // add for carrier test about warning limit of edit text
+        BrowserUtils.maxLengthFilter(AddBookmarkPage.this, mFolderNamer,
+                BrowserUtils.FILENAME_MAX_LENGTH);
+
         mFolderCancel = mFolderNamerHolder.findViewById(R.id.close);
         mFolderCancel.setOnClickListener(this);
 
@@ -835,6 +857,72 @@
         }
     }
 
+    static void deleteDuplicateBookmark(final Context context, final long id) {
+        Uri uri = ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id);
+        context.getContentResolver().delete(uri, null, null);
+    }
+
+    private void onSaveWithConfirm() {
+        String title = mTitle.getText().toString().trim();
+        String unfilteredUrl = UrlUtils.fixUrl(mAddress.getText().toString());
+        String url = unfilteredUrl.trim();
+        Long id = mMap.getLong(BrowserContract.Bookmarks._ID);
+        int duplicateCount;
+        final ContentResolver cr = getContentResolver();
+
+        Cursor cursor = cr.query(BrowserContract.Bookmarks.CONTENT_URI,
+                BookmarksLoader.PROJECTION,
+                "( title = ? OR url = ? ) AND parent = ?",
+                new String[] {
+                        title, url, Long.toString(mCurrentFolder)
+                },
+                null);
+
+        if (cursor == null) {
+            save();
+            return;
+        }
+
+        duplicateCount = cursor.getCount();
+        if (duplicateCount <= 0) {
+            cursor.close();
+            save();
+            return;
+        } else {
+            try {
+                while (cursor.moveToNext()) {
+                    mDuplicateId = cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID);
+                    mDuplicateContext = AddBookmarkPage.this;
+                }
+            } catch (IllegalStateException e) {
+                e.printStackTrace();
+            } finally {
+                if (cursor != null)
+                    cursor.close();
+            }
+        }
+
+        if (mEditingExisting && duplicateCount == 1 && mDuplicateId == id) {
+            save();
+            return;
+        }
+
+        new AlertDialog.Builder(this)
+                .setTitle(getString(R.string.save_to_bookmarks_title))
+                .setMessage(getString(R.string.overwrite_bookmark_msg))
+                .setNegativeButton(android.R.string.cancel, null)
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int which) {
+                        if (mDuplicateContext == null) {
+                            return;
+                        }
+                        deleteDuplicateBookmark(mDuplicateContext, mDuplicateId);
+                        save();
+                    }
+                })
+                .show();
+    }
+
     /**
      * Parse the data entered in the dialog and post a message to update the bookmarks database.
      */
@@ -842,8 +930,7 @@
         createHandler();
 
         String title = mTitle.getText().toString().trim();
-        String unfilteredUrl;
-        unfilteredUrl = UrlUtils.fixUrl(mAddress.getText().toString());
+        String unfilteredUrl = UrlUtils.fixUrl(mAddress.getText().toString());
 
         boolean emptyTitle = title.length() == 0;
         boolean emptyUrl = unfilteredUrl.trim().length() == 0;
@@ -856,7 +943,6 @@
                 mAddress.setError(r.getText(R.string.bookmark_needs_url));
             }
             return false;
-
         }
         String url = unfilteredUrl.trim();
         if (!mEditingFolder) {
@@ -958,6 +1044,7 @@
             setResult(RESULT_OK);
             LogTag.logBookmarkAdded(url, "bookmarkview");
         }
+        finish();
         return true;
     }
 
diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java
index b11162c..0d31017 100644
--- a/src/com/android/browser/BrowserBookmarksPage.java
+++ b/src/com/android/browser/BrowserBookmarksPage.java
@@ -41,6 +41,7 @@
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
+import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
@@ -88,6 +89,8 @@
     static final String ACCOUNT_TYPE = "account_type";
     static final String ACCOUNT_NAME = "account_name";
 
+    static final long DEFAULT_FOLDER_ID = -1;
+
     BookmarksPageCallbacks mCallbacks;
     View mRoot;
     BookmarkExpandableView mGrid;
@@ -97,6 +100,7 @@
     View mHeader;
     HashMap<Integer, BrowserBookmarksAdapter> mBookmarkAdapters = new HashMap<Integer, BrowserBookmarksAdapter>();
     JSONObject mState;
+    long mCurrentFolderId = BrowserProvider2.FIXED_ID_ROOT;
 
     @Override
     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
@@ -147,6 +151,9 @@
         } else if (loader.getId() >= LOADER_BOOKMARKS) {
             BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId());
             adapter.changeCursor(cursor);
+            if (adapter.getCount() != 0) {
+                mCurrentFolderId = adapter.getItem(0).getLong(BookmarksLoader.COLUMN_INDEX_PARENT);
+            }
         }
     }
 
@@ -158,6 +165,31 @@
         }
     }
 
+    //add for carrier feature which adds new bookmark/folder function.
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+        inflater.inflate(R.menu.bookmark, menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        final Activity activity = getActivity();
+        if (item.getItemId() == R.id.add_bookmark_menu_id) {
+            Intent intent = new Intent(activity, AddBookmarkPage.class);
+            intent.putExtra(BrowserContract.Bookmarks.URL, "http://");
+            intent.putExtra(BrowserContract.Bookmarks.TITLE, "");
+            intent.putExtra(BrowserContract.Bookmarks.PARENT, mCurrentFolderId);
+            activity.startActivity(intent);
+        }
+        if (item.getItemId() == R.id.new_bmfolder_menu_id) {
+            Intent intent = new Intent(activity, AddBookmarkFolder.class);
+            intent.putExtra(BrowserContract.Bookmarks.PARENT, mCurrentFolderId);
+            activity.startActivity(intent);
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
     @Override
     public boolean onContextItemSelected(MenuItem item) {
         if (!(item.getMenuInfo() instanceof BookmarkContextMenuInfo)) {
@@ -444,6 +476,9 @@
                 // update crumbs
                 crumbs.pushView(title, uri);
                 crumbs.setVisibility(View.VISIBLE);
+                Object data = crumbs.getTopData();
+                mCurrentFolderId = (data != null ? ContentUris.parseId((Uri) data)
+                        : DEFAULT_FOLDER_ID);
             }
             loadFolder(groupPosition, uri);
         }
diff --git a/src/com/android/browser/BrowserUtils.java b/src/com/android/browser/BrowserUtils.java
new file mode 100644
index 0000000..e1a3600
--- /dev/null
+++ b/src/com/android/browser/BrowserUtils.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *         copyright notice, this list of conditions and the following
+ *         disclaimer in the documentation and/or other materials provided
+ *         with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *         contributors may be used to endorse or promote products derived
+ *         from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.browser;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.text.InputFilter;
+import android.text.Spanned;
+import android.util.Log;
+import android.widget.EditText;
+
+public class BrowserUtils {
+
+    private static final String LOGTAG = "BrowserUtils";
+    public static final int FILENAME_MAX_LENGTH = 32;
+    public static final int ADDRESS_MAX_LENGTH = 2048;
+    private static AlertDialog.Builder mAlertDialog = null;
+
+    public static void maxLengthFilter(final Context context, final EditText editText,
+            final int max_length) {
+        InputFilter[] contentFilters = new InputFilter[1];
+        contentFilters[0] = new InputFilter.LengthFilter(max_length) {
+            public CharSequence filter(CharSequence source, int start, int end,
+                    Spanned dest, int dstart, int dend) {
+                int keep = max_length - (dest.length() - (dend - dstart));
+                if (keep <= 0) {
+                    showWarningDialog(context, max_length);
+                    return "";
+                } else if (keep >= end - start) {
+                    return null;
+                } else {
+                    if (keep < source.length()) {
+                        showWarningDialog(context, max_length);
+                    }
+                    return source.subSequence(start, start + keep);
+                }
+            }
+        };
+        editText.setFilters(contentFilters);
+    }
+
+    private static void showWarningDialog(final Context context, int max_length) {
+        if (mAlertDialog != null)
+            return;
+
+        mAlertDialog = new AlertDialog.Builder(context);
+        mAlertDialog.setTitle(R.string.browser_max_input_title)
+                .setIcon(android.R.drawable.ic_dialog_info)
+                .setMessage(context.getString(R.string.browser_max_input, max_length))
+                .setPositiveButton(R.string.ok,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int which) {
+                                return;
+                            }
+                        })
+                .show()
+                .setOnDismissListener(new DialogInterface.OnDismissListener() {
+                    public void onDismiss(DialogInterface dialog) {
+                        Log.w("BrowserUtils", "onDismiss");
+                        mAlertDialog = null;
+                        return;
+                    }
+                });
+    }
+}