Merge "Browser: add homepage and bookmark feature"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 13f13b3..8c88f43 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -36,6 +36,7 @@
     <uses-permission android:name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS" />
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
@@ -198,9 +199,30 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="DownloadSettings" android:label="@string/download_settings_title"
+                  android:theme="@style/DialogWhenLarge"
+                  android:launchMode="singleTask"
+                  android:configChanges="orientation|keyboardHidden|screenSize"
+                  android:windowSoftInputMode="adjustResize">
+            <intent-filter>
+                <action android:name="android.intent.action.BROWSERDOWNLOAD" />
+                 <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="ComboViewActivity">
         </activity>
 
+        <!-- used to new folder in bookmark page for carrier -->
+        <activity android:name="AddBookmarkFolder" android:label="@string/save_bookmark_folder"
+                  android:theme="@style/DialogWhenLarge"
+                  android:configChanges="orientation|keyboardHidden|screenSize">
+            <intent-filter>
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.dir/bookmark"/>
+            </intent-filter>
+        </activity>
+
         <!-- Bookmark thumbnail homescreen widget -->
         <receiver
             android:name=".widget.BookmarkThumbnailWidgetProvider"
diff --git a/res/layout/download_settings.xml b/res/layout/download_settings.xml
new file mode 100644
index 0000000..ad47363
--- /dev/null
+++ b/res/layout/download_settings.xml
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:divider="?android:attr/dividerHorizontal"
+    android:orientation="vertical"
+    android:showDividers="middle" >
+    <LinearLayout android:id="@+id/title_holder"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingLeft="5dip"
+        android:paddingRight="5dip"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        >
+        <TextView android:id="@+id/download_settings_title"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:gravity="center_vertical"
+            android:drawableLeft="@drawable/ic_bookmark_on_holo_dark"
+            android:text="@string/download_settings_title"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    </LinearLayout>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1" >
+
+        <ScrollView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" >
+
+            <TableLayout
+                android:id="@+id/download_table_layout"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingLeft="20dip"
+                android:paddingRight="20dip"
+                android:paddingTop="10dip"
+                android:shrinkColumns="1"
+                android:stretchColumns="1" >
+
+                <TableRow android:layout_marginBottom="10dip" >
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center_vertical"
+                        android:layout_weight="1"
+                        android:text="@string/download_edit_filename_label"
+                        android:textAppearance="?android:attr/textAppearanceMedium"
+                        android:textSize="18sp" >
+                    </TextView>
+
+                    <EditText
+                        android:id="@+id/download_filename_edit"
+                        android:layout_width="260dip"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center_vertical"
+                        android:layout_weight="1"
+                        android:inputType="textCapSentences"
+                        android:selectAllOnFocus="true"
+                        android:textAppearance="?android:attr/textAppearanceMedium"
+                        android:textSize="18sp" >
+                    </EditText>
+                </TableRow>
+
+                <TableRow
+                    android:layout_marginBottom="10dip" >
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center_vertical"
+                        android:layout_weight="1"
+                        android:text="@string/download_filepath_label"
+                        android:textAppearance="?android:attr/textAppearanceMedium"
+                        android:textSize="18sp" >
+                    </TextView>
+
+                    <EditText
+                        android:id="@+id/download_filepath_selected"
+                        android:layout_width="260dip"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"
+                        android:editable="false"
+                        android:ellipsize="end"
+                        android:focusableInTouchMode="false"
+                        android:textAppearance="?android:attr/textAppearanceMedium"
+                        android:textColor="@android:color/holo_blue_light"
+                        android:textSize="18sp" >
+                    </EditText>
+                </TableRow>
+
+                <TableRow
+                    android:layout_marginBottom="10dip" >
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center_vertical"
+                        android:text="@string/download_filesize"
+                        android:textAppearance="?android:attr/textAppearanceMedium"
+                        android:textSize="18sp" >
+                    </TextView>
+                    <TextView
+                        android:id="@+id/download_estimate_size_content"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center_vertical"
+                        android:text="@string/download_filesize"
+                        android:paddingLeft="10dip"
+                        android:textAppearance="?android:attr/textAppearanceMedium"
+                        android:textSize="18sp" >
+                    </TextView>
+                </TableRow>
+
+                <TableRow
+                    android:layout_marginBottom="10dip" >
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center_vertical"
+                        android:text="@string/download_timeneeded"
+                        android:textAppearance="?android:attr/textAppearanceMedium"
+                        android:textSize="18sp" >
+                    </TextView>
+                    <TextView
+                        android:id="@+id/download_estimate_time_content"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center_vertical"
+                        android:paddingLeft="10dip"
+                        android:text="@string/download_timeneeded"
+                        android:textAppearance="?android:attr/textAppearanceMedium"
+                        android:textSize="18sp" >
+                    </TextView>
+                </TableRow>
+            </TableLayout>
+        </ScrollView>
+    </FrameLayout>
+
+    <LinearLayout
+        style="?android:attr/buttonBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <Button
+            android:id="@+id/download_cancle"
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_gravity="right"
+            android:layout_weight="1"
+            android:maxLines="1"
+            android:text="@string/download_cancel" />
+
+        <Button
+            android:id="@+id/download_start"
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_gravity="left"
+            android:layout_weight="1"
+            android:maxLines="1"
+            android:text="@string/download_start" />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/menu/bookmark.xml b/res/menu/bookmark.xml
new file mode 100644
index 0000000..b43d7ce
--- /dev/null
+++ b/res/menu/bookmark.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/add_bookmark_menu_id"
+        android:title="@string/add_new_bookmark"
+        android:icon="@drawable/ic_add_string"/>
+    <item android:id="@+id/new_bmfolder_menu_id"
+        android:title="@string/new_folder"
+        android:icon="@drawable/ic_add_string"/>
+</menu>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index d0b8452..e3df6d7 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -424,4 +424,39 @@
     <string name="exit_browser_msg">请选择退出浏览器的方式?</string>
     <string name="exit_minimize">最小化</string>
     <string name="exit_quit">退出</string>
+
+    <!-- Add for new bookmark/folder Feature -->
+    <string name="save_bookmark_folder">新建书签文件夹</string>
+    <string name="browser_max_input_title">已达输入上限</string>
+    <string name="browser_max_input">您输入的字符数已经达到最大长度<xliff:g id="MAXLIMIT">%s</xliff:g>个字符。</string>
+    <string name="save_to_bookmarks_title">保存书签</string>
+    <string name="duplicated_folder_warning">该文件夹已经存在</string>
+    <string name="overwrite_bookmark_msg">该书签的标签或者地址已经存在,是否覆盖?</string>
+
+    <!-- Add for download save path setting Feature -->
+    <string name="invalid_path">"无效路径!"</string>
+    <string name="path_wrong">"路径错误"</string>
+    <string name="pref_download_title">下载路径设置</string>
+    <string name="pref_download_path_setting_screen_title">自定义下载路径</string>
+    <string name="download_start">确定</string>
+    <string name="download_cancel">取消</string>
+    <string name="download_timeneeded">时间</string>
+    <string name="download_filesize">大小</string>
+    <string name="download_filepath_label">路径</string>
+    <string name="download_edit_filename_label">名称</string>
+    <string name="download_default_path">/Download</string>
+    <string name="download_no_enough_memory">存储空间不足</string>
+    <string name="download_settings_title">下载设置</string>
+    <string name="save_page_needs_title">文件名称不能为空!</string>
+    <string name="filename_empty_title">文件名为空</string>
+    <string name="filename_empty_msg">文件名不能为空,请重新输入!</string>
+    <string name="unknow_length">未知</string>
+    <string name="download_file_exist_msg">文件已经存在,请重新输入文件名!</string>
+    <string name="download_file_exist">文件已经存在</string>
+    <string name ="time_min">分钟</string>
+    <string name="download_path_phone_stroage_label">/内置存储器</string>
+    <string name="download_path_sd_card_label">/SD卡</string>
+    <string name="download_path_unavailable_dlg_title">浏览器的下载路径不可达</string>
+    <string name="download_path_unavailable_dlg_msg">请重新设置浏览器的下载路径</string>
+    <string name="activity_not_found">没有找到处理 Intent <xliff:g id="NOACTIVITY">%s</xliff:g> 的Activity.</string>
 </resources>
diff --git a/res/values/integers.xml b/res/values/integers.xml
index 5f75723..9fd97b2 100644
--- a/res/values/integers.xml
+++ b/res/values/integers.xml
@@ -30,4 +30,5 @@
     <integer name="max_bookmark_columns">5</integer>
     <!-- The duration of the titlebar animation in millisecs -->
     <integer name="titlebar_animation_duration">200</integer>
+    <integer name="netswitch_type_remind">1</integer>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5167822..1b7fe73 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1038,4 +1038,42 @@
     <string name="exit_browser_msg">Please Select How to Exit Browser?</string>
     <string name="exit_minimize">Minimize</string>
     <string name="exit_quit">Quit</string>
+
+    <!-- Add for new bookmark/folder Feature -->
+    <string name="save_bookmark_folder">New folder</string>
+    <string name="browser_max_input_title">Reached Input Limit</string>
+    <string name="browser_max_input">Your input has reached the maximum limit of <xliff:g id="MAXLIMIT">%s</xliff:g> characters.</string>
+    <string name="save_to_bookmarks_title">Save to Bookmarks</string>
+    <string name="duplicated_folder_warning">The folder has already exists</string>
+    <string name="overwrite_bookmark_msg">This bookmark\'s name or address already exists, overwrite it?</string>
+
+    <!-- Add for download save path setting Feature -->
+    <string name="invalid_path">"Path is invalid!"</string>
+    <string name="path_wrong">"Wrong Path"</string>
+    <string name="pref_download_title">Download Path Settings</string>
+    <string name="pref_download_path_setting_screen_title">Custom Download Path</string>
+    <string name="download_start">OK</string>
+    <string name="download_cancel">Cancel</string>
+    <string name="download_timeneeded">Time</string>
+    <string name="download_filesize">Size</string>
+    <string name="download_filepath_label">Path</string>
+    <string name="download_edit_filename_label">Name</string>
+    <string name="download_default_path">default</string>
+    <string name="default_savepath_name">/Download</string>
+    <string name="download_no_enough_memory">Download no enough memory</string>
+    <string name="download_settings_title">Download Settings</string>
+    <string name="save_page_needs_title">Download File Name is Empty!</string>
+    <string name="filename_empty_title">Download File Name is Null</string>
+    <string name="filename_empty_msg">Download File Name Can not Be Null!Please Enter a Valid File Name!</string>
+    <string name="download_file_setting">Download File Settings</string>
+    <string name="unknow_length">Unknow</string>
+    <string name="download_file_exist_msg">This File is Already Exist,Please Enter a New File Name!</string>
+    <string name="download_file_exist">File Exist</string>
+    <string name ="time_min">min</string>
+    <string name="download_path_phone_stroage_label">/Phone Stroage</string>
+    <string name="download_path_sd_card_label">/SD card</string>
+    <string name="download_path_unavailable_dlg_title">Download Directory Unavailable</string>
+    <string name="download_path_unavailable_dlg_msg">Please modify the Download Directory of Browser</string>
+    <string name="activity_not_found">Activity Not Found to Handle Intent <xliff:g id="NOACTIVITY">%s</xliff:g>.</string>
+    <string name="network_switch_remind_type">wifi_browser_interaction_remind</string>
 </resources>
diff --git a/res/xml/download_settings_preferences.xml b/res/xml/download_settings_preferences.xml
new file mode 100644
index 0000000..0cd99a9
--- /dev/null
+++ b/res/xml/download_settings_preferences.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+<PreferenceScreen
+        xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <PreferenceCategory
+       android:title="@string/pref_download_title"
+       android:key="download_path_setting_category">
+         <PreferenceScreen
+               android:key="download_path_setting_screen"
+               android:title="@string/pref_download_path_setting_screen_title"/>
+
+    </PreferenceCategory>
+
+</PreferenceScreen>
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/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index b35ca04..a58e2a4 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -40,6 +40,8 @@
 import android.webkit.JavascriptInterface;
 
 import com.android.browser.UI.ComboViews;
+import com.android.browser.search.DefaultSearchEngine;
+import com.android.browser.search.SearchEngine;
 import com.android.browser.stub.NullController;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -88,7 +90,9 @@
 
         // 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())) {
+        SearchEngine searchEngine = BrowserSettings.getInstance().getSearchEngine();
+        boolean result = IntentHandler.handleWebSearchIntent(this, null, getIntent());
+        if (result && (searchEngine instanceof DefaultSearchEngine)) {
             finish();
             return;
         }
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/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 68c575a..8d2d7b0 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -713,6 +713,11 @@
         }
     }
 
+    public String getDownloadPath() {
+        return mPrefs.getString(PREF_DOWNLOAD_PATH,
+                DownloadHandler.getDefaultDownloadPath(mContext));
+    }
+
     // -----------------------------
     // getter/setters for accessibility_preferences.xml
     // -----------------------------
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;
+                    }
+                });
+    }
+}
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index 4ec5042..729e79e 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -39,8 +39,11 @@
 import android.database.sqlite.SQLiteException;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 import android.net.Uri;
 import android.net.http.SslError;
+import android.net.wifi.WifiManager;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Environment;
@@ -55,6 +58,7 @@
 import android.provider.BrowserContract.Images;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Intents.Insert;
+import android.provider.Settings;
 import android.speech.RecognizerIntent;
 import android.text.TextUtils;
 import android.util.Log;
@@ -113,7 +117,9 @@
     private static final String SEND_APP_ID_EXTRA =
         "android.speech.extras.SEND_APPLICATION_ID_EXTRA";
     private static final String INCOGNITO_URI = "browser:incognito";
-
+    private static final String PROP_NETSWITCH = "persist.env.browser.netswitch";
+    private static final String INTENT_WIFI_SELECTION_DATA_CONNECTION =
+            "android.net.wifi.cmcc.WIFI_SELECTION_DATA_CONNECTION";
 
     // public message ids
     public final static int LOAD_URL = 1001;
@@ -175,6 +181,7 @@
     private Message mAutoFillSetupMessage;
 
     private boolean mShouldShowErrorConsole;
+    private boolean mNetworkShouldNotify = true;
 
     private SystemAllowGeolocationOrigins mSystemAllowGeolocationOrigins;
 
@@ -518,7 +525,7 @@
                             case R.id.download_context_menu_id:
                                 DownloadHandler.onDownloadStartNoStream(
                                         mActivity, url, view.getSettings().getUserAgentString(),
-                                        null, null, null, view.isPrivateBrowsingEnabled());
+                                        null, null, null, view.isPrivateBrowsingEnabled(), 0);
                                 break;
                         }
                         break;
@@ -782,7 +789,6 @@
         // Stop watching the default geolocation permissions
         mSystemAllowGeolocationOrigins.stop();
         mSystemAllowGeolocationOrigins = null;
-        mCrashRecoveryHandler.clearState();
     }
 
     protected boolean isActivityPaused() {
@@ -828,6 +834,41 @@
         return mLoadStopped;
     }
 
+    private void handleNetworkNotify(WebView view) {
+        ConnectivityManager conMgr = (ConnectivityManager) this.getContext().getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        WifiManager wifiMgr = (WifiManager) this.getContext()
+                .getSystemService(Context.WIFI_SERVICE);
+        int networkSwitchTypeOK = this.getContext().getResources()
+                .getInteger(R.integer.netswitch_type_remind);
+
+        if (wifiMgr.isWifiEnabled()) {
+            NetworkInfo mNetworkInfo = conMgr.getActiveNetworkInfo();
+            if (mNetworkInfo == null
+                    || (mNetworkInfo != null && (mNetworkInfo.getType() !=
+                    ConnectivityManager.TYPE_WIFI))) {
+                int isReminder = Settings.System.getInt(
+                        mActivity.getContentResolver(),
+                        this.getContext().getResources()
+                                .getString(R.string.network_switch_remind_type),
+                        networkSwitchTypeOK);
+
+                if (isReminder == networkSwitchTypeOK) {
+                    Intent intent = new Intent(
+                            INTENT_WIFI_SELECTION_DATA_CONNECTION);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    this.getContext().startActivity(intent);
+                }
+                mNetworkShouldNotify = false;
+            }
+        } else {
+            if (!mNetworkHandler.isNetworkUp()) {
+                view.setNetworkAvailable(false);
+                Log.v(LOGTAG, "handleNetworkNotify() Wlan is not enabled.");
+            }
+        }
+    }
+
     // WebViewController
 
     @Override
@@ -843,8 +884,26 @@
         // reset sync timer to avoid sync starts during loading a page
         CookieSyncManager.getInstance().resetSync();
 
-        if (!mNetworkHandler.isNetworkUp()) {
-            view.setNetworkAvailable(false);
+        if (SystemProperties.getBoolean(PROP_NETSWITCH, false)) {
+            if (!mNetworkHandler.isNetworkUp()) {
+                Log.d(LOGTAG, "onPageStarted() network unavailable");
+                if (mNetworkShouldNotify) {
+                    handleNetworkNotify(view);
+                } else {
+                    view.setNetworkAvailable(false);
+                }
+                mNetworkShouldNotify = false;
+            } else {
+                Log.d(LOGTAG, "onPageStarted() network available");
+                if (mNetworkShouldNotify) {
+                    handleNetworkNotify(view);
+                }
+                mNetworkShouldNotify = false;
+            }
+        } else {
+            if (!mNetworkHandler.isNetworkUp()) {
+                view.setNetworkAvailable(false);
+            }
         }
 
         // when BrowserActivity just starts, onPageStarted may be called before
@@ -1051,7 +1110,7 @@
             long contentLength) {
         WebView w = tab.getWebView();
         boolean ret = DownloadHandler.onDownloadStart(mActivity, url, userAgent,
-                contentDisposition, mimetype, referer, w.isPrivateBrowsingEnabled());
+                contentDisposition, mimetype, referer, w.isPrivateBrowsingEnabled(), contentLength);
         if (ret == false && w.copyBackForwardList().getSize() == 0) {
             // This Tab was opened for the sole purpose of downloading a
             // file. Remove it.
@@ -2238,7 +2297,7 @@
                 saveDataUri();
             } else {
                 DownloadHandler.onDownloadStartNoStream(mActivity, mText, mUserAgent,
-                        null, null, null, mPrivateBrowsing);
+                        null, null, null, mPrivateBrowsing, 0);
             }
             return true;
         }
diff --git a/src/com/android/browser/DownloadHandler.java b/src/com/android/browser/DownloadHandler.java
index 46e6cbc..e3d31cd 100644
--- a/src/com/android/browser/DownloadHandler.java
+++ b/src/com/android/browser/DownloadHandler.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.DownloadManager;
+import android.app.DownloadManager.Request;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
@@ -29,13 +30,19 @@
 import android.media.MediaFile;
 import android.net.Uri;
 import android.net.WebAddress;
+import android.os.Bundle;
 import android.os.Environment;
+import android.os.StatFs;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
 import android.text.TextUtils;
 import android.util.Log;
 import android.webkit.CookieManager;
 import android.webkit.URLUtil;
 import android.widget.Toast;
 
+import java.io.File;
+
 /**
  * Handle download requests
  */
@@ -45,6 +52,9 @@
             com.android.browser.Browser.LOGD_ENABLED;
 
     private static final String LOGTAG = "DLHandler";
+    private static String mInternalStorage;
+    private static String mExternalStorage;
+    private final static String INVALID_PATH = "/storage";
 
     /**
      * Notify the host application a download should be done, or that
@@ -57,9 +67,68 @@
      * @param referer The referer associated with the downloaded url
      * @param privateBrowsing If the request is coming from a private browsing tab.
      */
+
+    public static void startingDownload(Activity activity,
+            String url, String userAgent, String contentDisposition,
+            String mimetype, String referer, boolean privateBrowsing, long contentLength,
+            String filename, String downloadPath) {
+        // java.net.URI is a lot stricter than KURL so we have to encode some
+        // extra characters. Fix for b 2538060 and b 1634719
+        WebAddress webAddress;
+        try {
+            webAddress = new WebAddress(url);
+            webAddress.setPath(encodePath(webAddress.getPath()));
+        } catch (Exception e) {
+            // This only happens for very bad urls, we want to chatch the
+            // exception here
+            Log.e(LOGTAG, "Exception trying to parse url:" + url);
+            return;
+        }
+
+        String addressString = webAddress.toString();
+        Uri uri = Uri.parse(addressString);
+        final DownloadManager.Request request;
+        try {
+            request = new DownloadManager.Request(uri);
+        } catch (IllegalArgumentException e) {
+            Toast.makeText(activity, R.string.cannot_download, Toast.LENGTH_SHORT).show();
+            return;
+        }
+        request.setMimeType(mimetype);
+        // set downloaded file destination to /sdcard/Download.
+        // or, should it be set to one of several Environment.DIRECTORY* dirs
+        // depending on mimetype?
+        try {
+            setDestinationDir(downloadPath, filename, request);
+        } catch (Exception e) {
+            showNoEnoughMemoryDialog(activity);
+            return;
+        }
+        // let this downloaded file be scanned by MediaScanner - so that it can
+        // show up in Gallery app, for example.
+        request.allowScanningByMediaScanner();
+        request.setDescription(webAddress.getHost());
+        // XXX: Have to use the old url since the cookies were stored using the
+        // old percent-encoded url.
+        String cookies = CookieManager.getInstance().getCookie(url, privateBrowsing);
+        request.addRequestHeader("cookie", cookies);
+        request.addRequestHeader("User-Agent", userAgent);
+        request.addRequestHeader("Referer", referer);
+        request.setNotificationVisibility(
+                DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+        final DownloadManager manager = (DownloadManager) activity
+                .getSystemService(Context.DOWNLOAD_SERVICE);
+        new Thread("Browser download") {
+            public void run() {
+                manager.enqueue(request);
+            }
+        }.start();
+        showStartDownloadToast(activity);
+    }
+
     public static boolean onDownloadStart(final Activity activity, final String url,
             final String userAgent, final String contentDisposition, final String mimetype,
-            final String referer, final boolean privateBrowsing) {
+            final String referer, final boolean privateBrowsing, final long contentLength) {
         // if we're dealing wih A/V content that's not explicitly marked
         //     for download, check if it's streamable.
         if (contentDisposition == null
@@ -88,7 +157,7 @@
                 .setPositiveButton(R.string.video_save, new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int which) {
                         onDownloadStartNoStream(activity, url, userAgent, contentDisposition,
-                                mimetype, referer, privateBrowsing);
+                                mimetype, referer, privateBrowsing, contentLength);
                     }
                  })
                 .setNegativeButton(R.string.video_play, new DialogInterface.OnClickListener() {
@@ -142,7 +211,7 @@
             }
         }
         onDownloadStartNoStream(activity, url, userAgent, contentDisposition,
-                mimetype, referer, privateBrowsing);
+                mimetype, referer, privateBrowsing, contentLength);
         return false;
     }
 
@@ -187,10 +256,11 @@
      * @param referer The referer associated with the downloaded url
      * @param privateBrowsing If the request is coming from a private browsing tab.
      */
-    /*package */ static void onDownloadStartNoStream(Activity activity,
+    /* package */static void onDownloadStartNoStream(Activity activity,
             String url, String userAgent, String contentDisposition,
-            String mimetype, String referer, boolean privateBrowsing) {
+            String mimetype, String referer, boolean privateBrowsing, long contentLength) {
 
+        initStorageDefaultPath(activity);
         String filename = URLUtil.guessFileName(url,
                 contentDisposition, mimetype);
 
@@ -218,63 +288,309 @@
             return;
         }
 
-        // java.net.URI is a lot stricter than KURL so we have to encode some
-        // extra characters. Fix for b 2538060 and b 1634719
-        WebAddress webAddress;
-        try {
-            webAddress = new WebAddress(url);
-            webAddress.setPath(encodePath(webAddress.getPath()));
-        } catch (Exception e) {
-            // This only happens for very bad urls, we want to chatch the
-            // exception here
-            Log.e(LOGTAG, "Exception trying to parse url:" + url);
-            return;
-        }
-
-        String addressString = webAddress.toString();
-        Uri uri = Uri.parse(addressString);
-        final DownloadManager.Request request;
-        try {
-            request = new DownloadManager.Request(uri);
-        } catch (IllegalArgumentException e) {
-            Toast.makeText(activity, R.string.cannot_download, Toast.LENGTH_SHORT).show();
-            return;
-        }
-        request.setMimeType(mimetype);
-        // set downloaded file destination to /sdcard/Download.
-        // or, should it be set to one of several Environment.DIRECTORY* dirs depending on mimetype?
-        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
-        // let this downloaded file be scanned by MediaScanner - so that it can 
-        // show up in Gallery app, for example.
-        request.allowScanningByMediaScanner();
-        request.setDescription(webAddress.getHost());
-        // XXX: Have to use the old url since the cookies were stored using the
-        // old percent-encoded url.
-        String cookies = CookieManager.getInstance().getCookie(url, privateBrowsing);
-        request.addRequestHeader("cookie", cookies);
-        request.addRequestHeader("User-Agent", userAgent);
-        request.addRequestHeader("Referer", referer);
-        request.setNotificationVisibility(
-                DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
         if (mimetype == null) {
-            if (TextUtils.isEmpty(addressString)) {
-                return;
-            }
             // We must have long pressed on a link or image to download it. We
             // are not sure of the mimetype in this case, so do a head request
-            new FetchUrlMimeType(activity, request, addressString, cookies,
-                    userAgent).start();
+            new FetchUrlMimeType(activity, url, userAgent, referer,
+                    privateBrowsing, filename).start();
         } else {
-            final DownloadManager manager
-                    = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
-            new Thread("Browser download") {
-                public void run() {
-                    manager.enqueue(request);
-                }
-            }.start();
+            startDownloadSettings(activity, url, userAgent, contentDisposition, mimetype, referer,
+                    privateBrowsing, contentLength, filename);
         }
+
+    }
+
+    public static void initStorageDefaultPath(Context context) {
+        mExternalStorage = getExternalStorageDirectory(context);
+        if (isPhoneStorageSupported()) {
+            mInternalStorage = Environment.getExternalStorageDirectory().getPath();
+        } else {
+            mInternalStorage = null;
+        }
+    }
+
+    public static void startDownloadSettings(Activity activity,
+            String url, String userAgent, String contentDisposition,
+            String mimetype, String referer, boolean privateBrowsing, long contentLength,
+            String filename) {
+        Bundle fileInfo = new Bundle();
+        fileInfo.putString("url", url);
+        fileInfo.putString("userAgent", userAgent);
+        fileInfo.putString("contentDisposition", contentDisposition);
+        fileInfo.putString("mimetype", mimetype);
+        fileInfo.putString("referer", referer);
+        fileInfo.putLong("contentLength", contentLength);
+        fileInfo.putBoolean("privateBrowsing", privateBrowsing);
+        fileInfo.putString("filename", filename);
+        Intent intent = new Intent("android.intent.action.BROWSERDOWNLOAD");
+        intent.putExtras(fileInfo);
+        activity.startActivity(intent);
+    }
+
+    public static void setAppointedFolder(String downloadPath) {
+        File file = new File(downloadPath);
+        if (file.exists()) {
+            if (!file.isDirectory()) {
+                throw new IllegalStateException(file.getAbsolutePath() +
+                        " already exists and is not a directory");
+            }
+        } else {
+            if (!file.mkdir()) {
+                throw new IllegalStateException("Unable to create directory: " +
+                        file.getAbsolutePath());
+            }
+        }
+    }
+
+    private static void setDestinationDir(String downloadPath, String filename, Request request) {
+        File file = new File(downloadPath);
+        if (file.exists()) {
+            if (!file.isDirectory()) {
+                throw new IllegalStateException(file.getAbsolutePath() +
+                        " already exists and is not a directory");
+            }
+        } else {
+            if (!file.mkdir()) {
+                throw new IllegalStateException("Unable to create directory: " +
+                        file.getAbsolutePath());
+            }
+        }
+        setDestinationFromBase(file, filename, request);
+    }
+
+    private static void setDestinationFromBase(File file, String filename, Request request) {
+        if (filename == null) {
+            throw new NullPointerException("filename cannot be null");
+        }
+        request.setDestinationUri(Uri.withAppendedPath(Uri.fromFile(file), filename));
+    }
+
+    public static void fileExistQueryDialog(Activity activity) {
+        new AlertDialog.Builder(activity)
+                .setTitle(R.string.download_file_exist)
+                .setIcon(android.R.drawable.ic_dialog_info)
+                .setMessage(R.string.download_file_exist_msg)
+                // if yes, delete existed file and start new download thread
+                .setPositiveButton(R.string.ok, null)
+                // if no, do nothing at all
+                .show();
+    }
+
+    public static long getAvailableMemory(String root) {
+        StatFs stat = new StatFs(root);
+        final long LEFT10MByte = 2560;
+        long blockSize = stat.getBlockSize();
+        long availableBlocks = stat.getAvailableBlocks() - LEFT10MByte;
+        return availableBlocks * blockSize;
+    }
+
+    public static void showNoEnoughMemoryDialog(Activity mContext) {
+        new AlertDialog.Builder(mContext)
+                .setTitle(R.string.download_no_enough_memory)
+                .setIconAttribute(android.R.attr.alertDialogIcon)
+                .setMessage(R.string.download_no_enough_memory)
+                .setPositiveButton(R.string.ok, null)
+                .show();
+    }
+
+    public static boolean manageNoEnoughMemory(Activity mContext, long contentLength, String root) {
+        Log.i(LOGTAG, "----------- download file contentLength is ------------>" + contentLength);
+        long mAvailableBytes = getAvailableMemory(root);
+        if (mAvailableBytes > 0) {
+            if (contentLength > mAvailableBytes) {
+                showNoEnoughMemoryDialog(mContext);
+                return true;
+            }
+        } else {
+            showNoEnoughMemoryDialog(mContext);
+            return true;
+        }
+        return false;
+    }
+
+    public static void showStartDownloadToast(Activity activity) {
+        Intent intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+        activity.startActivity(intent);
         Toast.makeText(activity, R.string.download_pending, Toast.LENGTH_SHORT)
                 .show();
     }
 
+    /**
+     * wheather the storage status OK for download file
+     *
+     * @param activity
+     * @param filename the download file's name
+     * @param downloadPath the download file's path will be in
+     * @return boolean true is ok,and false is not
+     */
+    public static boolean isStorageStatusOK(Activity activity, String filename, String downloadPath) {
+        if (downloadPath.equals(INVALID_PATH)) {
+            new AlertDialog.Builder(activity)
+                    .setTitle(R.string.path_wrong)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setMessage(R.string.invalid_path)
+                    .setPositiveButton(R.string.ok, null)
+                    .show();
+            return false;
+        }
+
+        if (!(isPhoneStorageSupported() && downloadPath.contains(mInternalStorage))) {
+            String status = getExternalStorageState(activity);
+            if (!status.equals(Environment.MEDIA_MOUNTED)) {
+                int title;
+                String msg;
+
+                // Check to see if the SDCard is busy, same as the music app
+                if (status.equals(Environment.MEDIA_SHARED)) {
+                    msg = activity.getString(R.string.download_sdcard_busy_dlg_msg);
+                    title = R.string.download_sdcard_busy_dlg_title;
+                } else {
+                    msg = activity.getString(R.string.download_no_sdcard_dlg_msg, filename);
+                    title = R.string.download_no_sdcard_dlg_title;
+                }
+
+                new AlertDialog.Builder(activity)
+                        .setTitle(title)
+                        .setIcon(android.R.drawable.ic_dialog_alert)
+                        .setMessage(msg)
+                        .setPositiveButton(R.string.ok, null)
+                        .show();
+                return false;
+            }
+        } else {
+            String status = Environment.getExternalStorageState();
+            if (!status.equals(Environment.MEDIA_MOUNTED)) {
+                int mTitle = R.string.download_path_unavailable_dlg_title;
+                String mMsg = activity.getString(R.string.download_path_unavailable_dlg_msg);
+                new AlertDialog.Builder(activity)
+                        .setTitle(mTitle)
+                        .setIcon(android.R.drawable.ic_dialog_alert)
+                        .setMessage(mMsg)
+                        .setPositiveButton(R.string.ok, null)
+                        .show();
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * wheather support Phone Storage
+     *
+     * @return boolean true support Phone Storage ,false will be not
+     */
+    public static boolean isPhoneStorageSupported() {
+        return true;
+    }
+
+    /**
+     * show Dialog to warn filename is null
+     *
+     * @param activity
+     */
+    public static void showFilenameEmptyDialog(Activity activity) {
+        new AlertDialog.Builder(activity)
+                .setTitle(R.string.filename_empty_title)
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setMessage(R.string.filename_empty_msg)
+                .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int which) {
+                    }
+                })
+                .show();
+    }
+
+    /**
+     * get the filename except the suffix and dot
+     *
+     * @return String the filename except suffix and dot
+     */
+    public static String getFilenameBase(String filename) {
+        int dotindex = filename.lastIndexOf('.');
+        if (dotindex != -1) {
+            return filename.substring(0, dotindex);
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * get the filename's extension from filename
+     *
+     * @param filename the download filename, may be the user entered
+     * @return String the filename's extension
+     */
+    public static String getFilenameExtension(String filename) {
+        int dotindex = filename.lastIndexOf('.');
+        if (dotindex != -1) {
+            return filename.substring(dotindex + 1);
+        } else {
+            return "";
+        }
+    }
+
+    public static String getDefaultDownloadPath(Context context) {
+        String defaultDownloadPath;
+
+        String defaultStorage;
+        if (isPhoneStorageSupported()) {
+            defaultStorage = Environment.getExternalStorageDirectory().getPath();
+        } else {
+            defaultStorage = getExternalStorageDirectory(context);
+        }
+
+        defaultDownloadPath = defaultStorage + context.getString(R.string.default_savepath_name);
+        Log.e(LOGTAG, "defaultStorage directory is : " + defaultDownloadPath);
+        return defaultDownloadPath;
+    }
+
+    /**
+     * translate the directory name into a name which is easy to know for user
+     *
+     * @param activity
+     * @param downloadPath
+     * @return String
+     */
+    public static String getDownloadPathForUser(Activity activity, String downloadPath) {
+        if (downloadPath == null) {
+            return downloadPath;
+        }
+        final String phoneStorageDir;
+        final String sdCardDir = getExternalStorageDirectory(activity);
+        if (isPhoneStorageSupported()) {
+            phoneStorageDir = Environment.getExternalStorageDirectory().getPath();
+        } else {
+            phoneStorageDir = null;
+        }
+
+        if (downloadPath.startsWith(sdCardDir)) {
+            String sdCardLabel = activity.getResources().getString(
+                    R.string.download_path_sd_card_label);
+            downloadPath = downloadPath.replace(sdCardDir, sdCardLabel);
+        } else if ((phoneStorageDir != null) && downloadPath.startsWith(phoneStorageDir)) {
+            String phoneStorageLabel = activity.getResources().getString(
+                    R.string.download_path_phone_stroage_label);
+            downloadPath = downloadPath.replace(phoneStorageDir, phoneStorageLabel);
+        }
+        return downloadPath;
+    }
+
+    private static String getExternalStorageDirectory(Context context) {
+        String sd = null;
+        StorageManager mStorageManager = (StorageManager) context
+                .getSystemService(Context.STORAGE_SERVICE);
+        StorageVolume[] volumes = mStorageManager.getVolumeList();
+        for (int i = 0; i < volumes.length; i++) {
+            if (volumes[i].isRemovable() && volumes[i].allowMassStorage()) {
+                sd = volumes[i].getPath();
+            }
+        }
+        return sd;
+    }
+
+    private static String getExternalStorageState(Context context) {
+        StorageManager mStorageManager = (StorageManager) context
+                .getSystemService(Context.STORAGE_SERVICE);
+        return mStorageManager.getVolumeState(getExternalStorageDirectory(context));
+    }
 }
diff --git a/src/com/android/browser/DownloadSettings.java b/src/com/android/browser/DownloadSettings.java
new file mode 100644
index 0000000..c1ddcb6
--- /dev/null
+++ b/src/com/android/browser/DownloadSettings.java
@@ -0,0 +1,312 @@
+/*
+ * 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 java.io.File;
+
+import android.app.Activity;
+import android.content.Intent;
+import java.lang.Thread;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.format.*;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.view.Window;
+import android.widget.Toast;
+import android.text.TextUtils;
+
+public class DownloadSettings extends Activity {
+
+    private EditText downloadFilenameET;
+    private EditText downloadPathET;
+    private TextView downloadEstimateSize;
+    private TextView downloadEstimateTime;
+    private Button downloadStart;
+    private Button downloadCancel;
+    private String url;
+    private String userAgent;
+    private String contentDisposition;
+    private String mimetype;
+    private String referer;
+    private String filenameBase;
+    private String filename;
+    private String filenameExtension;
+    private boolean privateBrowsing;
+    private long contentLength;
+    private String downloadPath;
+    private String downloadPathForUser;
+    private static final int downloadRate = (1024 * 100 * 60);// Download Rate
+                                                              // 100KB/s
+    private final static String LOGTAG = "DownloadSettings";
+    private final static int DOWNLOAD_PATH = 0;
+    private boolean isDownloadStarted = false;
+
+    private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET";
+
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // initial the DownloadSettings view
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        setContentView(R.layout.download_settings);
+        downloadFilenameET = (EditText) findViewById(R.id.download_filename_edit);
+        downloadPathET = (EditText) findViewById(R.id.download_filepath_selected);
+        downloadEstimateSize = (TextView) findViewById(R.id.download_estimate_size_content);
+        downloadEstimateTime = (TextView) findViewById(R.id.download_estimate_time_content);
+        downloadStart = (Button) findViewById(R.id.download_start);
+        downloadCancel = (Button) findViewById(R.id.download_cancle);
+        downloadPathET.setOnClickListener(downloadPathListener);
+        downloadStart.setOnClickListener(downloadStartListener);
+        downloadCancel.setOnClickListener(downloadCancelListener);
+
+        // get the bundle from Intent
+        Intent intent = getIntent();
+        Bundle fileInfo = intent.getExtras();
+        url = fileInfo.getString("url");
+        userAgent = fileInfo.getString("userAgent");
+        contentDisposition = fileInfo.getString("contentDisposition");
+        mimetype = fileInfo.getString("mimetype");
+        referer = fileInfo.getString("referer");
+        contentLength = fileInfo.getLong("contentLength");
+        privateBrowsing = fileInfo.getBoolean("privateBrowsing");
+        filename = fileInfo.getString("filename");
+
+        // download filenamebase's length is depended on filenameLength's values
+        // if filenamebase.length >= flienameLength, destroy the last string!
+
+        filenameBase = DownloadHandler.getFilenameBase(filename);
+        if (filenameBase.length() >= (BrowserUtils.FILENAME_MAX_LENGTH)) {
+            filenameBase = filenameBase.substring(0, BrowserUtils.FILENAME_MAX_LENGTH);
+        }
+
+        // warring when user enter more over letters into the EditText
+        BrowserUtils.maxLengthFilter(DownloadSettings.this, downloadFilenameET,
+                BrowserUtils.FILENAME_MAX_LENGTH);
+
+        downloadFilenameET.setText(filenameBase);
+        downloadPath = BrowserSettings.getInstance().getDownloadPath();
+        downloadPathForUser = DownloadHandler.getDownloadPathForUser(DownloadSettings.this,
+                downloadPath);
+        setDownloadPathForUserText(downloadPathForUser);
+        setDownloadFileSizeText();
+        setDownloadFileTimeText();
+
+    }
+
+    private OnClickListener downloadPathListener = new OnClickListener() {
+
+        @Override
+        public void onClick(View v) {
+
+            // start filemanager for getting download path
+            try {
+                Intent downloadPathIntent = new Intent("com.android.fileexplorer.action.DIR_SEL");
+                DownloadSettings.this.startActivityForResult(downloadPathIntent, DOWNLOAD_PATH);
+            } catch (Exception e) {
+                String err_msg = getString(R.string.activity_not_found,
+                        "com.android.fileexplorer.action.DIR_SEL");
+                Toast.makeText(DownloadSettings.this, err_msg, Toast.LENGTH_LONG).show();
+            }
+
+        }
+    };
+
+    private OnClickListener downloadStartListener = new OnClickListener() {
+
+        @Override
+        public void onClick(View v) {
+            filenameBase = getFilenameBaseFromUserEnter();
+            // check the filename user enter is null or not
+            if (filenameBase.length() <= 0) {
+                DownloadHandler.showFilenameEmptyDialog(DownloadSettings.this);
+                return;
+            }
+
+            filenameExtension = DownloadHandler.getFilenameExtension(filename);
+            filename = filenameBase + "." + filenameExtension;
+
+            // check the storage status
+            if (!DownloadHandler.isStorageStatusOK(DownloadSettings.this, filename, downloadPath)) {
+                return;
+            }
+
+            // check the storage memory enough or not
+            try {
+                DownloadHandler.setAppointedFolder(downloadPath);
+            } catch (Exception e) {
+                DownloadHandler.showNoEnoughMemoryDialog(DownloadSettings.this);
+                return;
+            }
+            boolean isNoEnoughMemory = DownloadHandler.manageNoEnoughMemory(DownloadSettings.this,
+                    contentLength, downloadPath);
+            if (isNoEnoughMemory) {
+                return;
+            }
+
+            // check the download file is exist or not
+            String fullFilename = downloadPath + "/" + filename;
+            if (mimetype != null && new File(fullFilename).exists()) {
+                DownloadHandler.fileExistQueryDialog(DownloadSettings.this);
+                return;
+            }
+
+            // staring downloading
+            DownloadHandler.startingDownload(DownloadSettings.this,
+                    url, userAgent, contentDisposition,
+                    mimetype, referer, privateBrowsing, contentLength,
+                    Uri.encode(filename), downloadPath);
+            isDownloadStarted = true;
+        }
+    };
+
+    private OnClickListener downloadCancelListener = new OnClickListener() {
+
+        @Override
+        public void onClick(View v) {
+            finish();
+        }
+    };
+
+    protected void onDestroy() {
+        super.onDestroy();
+    }
+
+    protected void onPause() {
+        super.onPause();
+        if (isDownloadStarted) {
+            finish();
+        }
+    }
+
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+
+        if (DOWNLOAD_PATH == requestCode) {
+            if (resultCode == Activity.RESULT_OK && intent != null) {
+                downloadPath = intent.getStringExtra("result_dir_sel");
+                if (downloadPath != null) {
+                    String rawEmulatedStorageTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
+                    if (!TextUtils.isEmpty(rawEmulatedStorageTarget)) {
+                        if (downloadPath.startsWith("/storage/sdcard0"))
+                            downloadPath = downloadPath.replace("/storage/sdcard0",
+                                    "/storage/emulated/0");
+                        if (downloadPath.startsWith("/storage/emulated/legacy"))
+                            downloadPath = downloadPath.replace("/storage/emulated/legacy",
+                                    "/storage/emulated/0");
+                    }
+                    downloadPathForUser = DownloadHandler.getDownloadPathForUser(
+                            DownloadSettings.this, downloadPath);
+                    setDownloadPathForUserText(downloadPathForUser);
+                }
+            }
+        }
+    }
+
+    /**
+     * show download path for user
+     *
+     * @param downloadPath the download path user can see
+     */
+    private void setDownloadPathForUserText(String downloadPathForUser) {
+        downloadPathET.setText(downloadPathForUser);
+    }
+
+    /**
+     * get the filename from user select the download path
+     *
+     * @return String the filename from user selected
+     */
+    private String getFilenameBaseFromUserEnter() {
+        return downloadFilenameET.getText().toString();
+    }
+
+    /**
+     * set the download file size for user to be known
+     */
+    private void setDownloadFileSizeText() {
+        String sizeText;
+        if (contentLength <= 0) {
+            sizeText = getString(R.string.unknow_length);
+        } else {
+            sizeText = getDownloadFileSize();
+        }
+        downloadEstimateSize.setText(sizeText);
+
+    }
+
+    /**
+     * set the time which downloaded this file will be estimately use;
+     */
+    private void setDownloadFileTimeText() {
+        String neededTimeText;
+        if (contentLength <= 0) {
+            neededTimeText = getString(R.string.unknow_length);
+        } else {
+            neededTimeText = getNeededTime() + getString(R.string.time_min);
+        }
+        downloadEstimateTime.setText(neededTimeText);
+    }
+
+    /**
+     * count the download file's size and format the values
+     *
+     * @return String the format values
+     */
+    private String getDownloadFileSize() {
+        String currentSizeText = "";
+        if (contentLength > 0) {
+            currentSizeText = Formatter.formatFileSize(DownloadSettings.this, contentLength);
+        }
+        return currentSizeText;
+    }
+
+    /**
+     * get the time download this file will be use,and format this time values
+     *
+     * @return long the valses of time which download this file will be use
+     */
+    private long getNeededTime() {
+        long timeNeeded = contentLength / downloadRate;
+        if (timeNeeded < 1) {
+            timeNeeded = 1;
+        }
+        Log.e(LOGTAG, "TimeNeeded:" + timeNeeded + "min");
+        // return the time like 5 min, not 5 s;
+        return timeNeeded;
+    }
+}
diff --git a/src/com/android/browser/FetchUrlMimeType.java b/src/com/android/browser/FetchUrlMimeType.java
index 33b5808..28bfc80 100644
--- a/src/com/android/browser/FetchUrlMimeType.java
+++ b/src/com/android/browser/FetchUrlMimeType.java
@@ -22,14 +22,15 @@
 import org.apache.http.client.methods.HttpHead;
 import org.apache.http.conn.params.ConnRouteParams;
 
-import android.app.DownloadManager;
+import android.app.Activity;
 import android.content.Context;
 import android.net.Proxy;
+import android.net.Uri;
 import android.net.http.AndroidHttpClient;
-import android.os.Environment;
+import android.text.TextUtils;
 import android.util.Log;
+import android.webkit.CookieManager;
 import android.webkit.MimeTypeMap;
-import android.webkit.URLUtil;
 
 import java.io.IOException;
 
@@ -48,18 +49,23 @@
     private final static String LOGTAG = "FetchUrlMimeType";
 
     private Context mContext;
-    private DownloadManager.Request mRequest;
     private String mUri;
-    private String mCookies;
     private String mUserAgent;
+    private String mFilename;
+    private String mReferer;
+    private Activity mActivity;
+    private boolean mPrivateBrowsing;
+    private long mContentLength;
 
-    public FetchUrlMimeType(Context context, DownloadManager.Request request,
-            String uri, String cookies, String userAgent) {
-        mContext = context.getApplicationContext();
-        mRequest = request;
-        mUri = uri;
-        mCookies = cookies;
+    public FetchUrlMimeType(Activity activity, String url, String userAgent,
+            String referer, boolean privateBrowsing, String filename) {
+        mActivity = activity;
+        mContext = activity.getApplicationContext();
+        mUri = url;
         mUserAgent = userAgent;
+        mPrivateBrowsing = privateBrowsing;
+        mFilename = filename;
+        mReferer = referer;
     }
 
     @Override
@@ -80,13 +86,16 @@
         }
         HttpHead request = new HttpHead(mUri);
 
-        if (mCookies != null && mCookies.length() > 0) {
-            request.addHeader("Cookie", mCookies);
+        String cookies = CookieManager.getInstance().getCookie(mUri, mPrivateBrowsing);
+        if (cookies != null && cookies.length() > 0) {
+            request.addHeader("Cookie", cookies);
         }
 
         HttpResponse response;
+        String filename = mFilename;
         String mimeType = null;
         String contentDisposition = null;
+        String contentLength = null;
         try {
             response = client.execute(request);
             // We could get a redirect here, but if we do lets let
@@ -101,6 +110,10 @@
                         mimeType = mimeType.substring(0, semicolonIndex);
                     }
                 }
+                Header contentLengthHeader = response.getFirstHeader("Content-Length");
+                if (contentLengthHeader != null) {
+                    contentLength = contentLengthHeader.getValue();
+                }
                 Header contentDispositionHeader = response.getFirstHeader("Content-Disposition");
                 if (contentDispositionHeader != null) {
                     contentDisposition = contentDispositionHeader.getValue();
@@ -114,26 +127,150 @@
             client.close();
         }
 
-       if (mimeType != null) {
-           if (mimeType.equalsIgnoreCase("text/plain") ||
-                   mimeType.equalsIgnoreCase("application/octet-stream")) {
-               String newMimeType =
-                       MimeTypeMap.getSingleton().getMimeTypeFromExtension(
-                           MimeTypeMap.getFileExtensionFromUrl(mUri));
-               if (newMimeType != null) {
-                   mimeType = newMimeType;
-                   mRequest.setMimeType(newMimeType);
-               }
-           }
-           String filename = URLUtil.guessFileName(mUri, contentDisposition,
-                mimeType);
-           mRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
-       }
+        if (mimeType != null) {
+            Log.e(LOGTAG, "-----------the mimeType from http header is ------------->" + mimeType);
+            if (mimeType.equalsIgnoreCase("text/plain") ||
+                    mimeType.equalsIgnoreCase("application/octet-stream")) {
+                String newMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+                        MimeTypeMap.getFileExtensionFromUrl(mUri));
+                if (newMimeType != null) {
+                    mimeType = newMimeType;
+                }
+            }
 
-       // Start the download
-       DownloadManager manager = (DownloadManager) mContext.getSystemService(
-               Context.DOWNLOAD_SERVICE);
-       manager.enqueue(mRequest);
+            String fileExtension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
+            if (fileExtension == null || (fileExtension != null && fileExtension.equals("bin"))) {
+                fileExtension = MimeTypeMap.getFileExtensionFromUrl(mUri);
+                if (fileExtension == null) {
+                    fileExtension = "bin";
+                }
+            }
+            filename = DownloadHandler.getFilenameBase(filename) + "." + fileExtension;
+
+        } else {
+            String fileExtension = getFileExtensionFromUrlEx(mUri);
+            if (fileExtension == "") {
+                fileExtension = "bin";
+            }
+            String newMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
+            if (newMimeType != null) {
+                mimeType = newMimeType;
+            }
+            filename = guessFileNameEx(mUri, contentDisposition, mimeType);
+
+        }
+
+        if (contentLength != null) {
+            mContentLength = Long.parseLong(contentLength);
+        } else {
+            mContentLength = 0;
+        }
+
+        DownloadHandler.startDownloadSettings(mActivity, mUri, mUserAgent, contentDisposition,
+                mimeType, mReferer, mPrivateBrowsing, mContentLength, filename);
+    }
+
+    /**
+     * when we can not parse MineType and Filename from the header of http body
+     * ,Call the fallowing functions for this matter
+     * getFileExtensionFromUrlEx(String url) : get the file Extension from Url
+     * guessFileNameEx() : get the file name from url Note: this modified for
+     * download http://www.baidu.com girl picture error extension and error
+     * filename
+     */
+    private String getFileExtensionFromUrlEx(String url) {
+        Log.e("FetchUrlMimeType",
+                "--------can not get mimetype from http header, the URL is ---------->" + url);
+        if (!TextUtils.isEmpty(url)) {
+            int fragment = url.lastIndexOf('#');
+            if (fragment > 0) {
+                url = url.substring(0, fragment);
+            }
+
+            int filenamePos = url.lastIndexOf('/');
+            String filename =
+                    0 <= filenamePos ? url.substring(filenamePos + 1) : url;
+            Log.e(LOGTAG,
+                    "--------can not get mimetype from http header, the temp filename is----------"
+                            + filename);
+            // if the filename contains special characters, we don't
+            // consider it valid for our matching purposes:
+            if (!filename.isEmpty()) {
+                int dotPos = filename.lastIndexOf('.');
+                if (0 <= dotPos) {
+                    return filename.substring(dotPos + 1);
+                }
+            }
+        }
+
+        return "";
+    }
+
+    private String guessFileNameEx(String url, String contentDisposition, String mimeType) {
+        String filename = null;
+        String extension = null;
+
+        // If all the other http-related approaches failed, use the plain uri
+        if (filename == null) {
+            String decodedUrl = Uri.decode(url);
+            if (decodedUrl != null) {
+                if (!decodedUrl.endsWith("/")) {
+                    int index = decodedUrl.lastIndexOf('/') + 1;
+                    if (index > 0) {
+                        filename = decodedUrl.substring(index);
+                    }
+                }
+            }
+        }
+
+        // Finally, if couldn't get filename from URI, get a generic filename
+        if (filename == null) {
+            filename = "downloadfile";
+        }
+
+        // Split filename between base and extension
+        // Add an extension if filename does not have one
+        int dotIndex = filename.indexOf('.');
+        if (dotIndex < 0) {
+            if (mimeType != null) {
+                extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
+                if (extension != null) {
+                    extension = "." + extension;
+                }
+            }
+            if (extension == null) {
+                if (mimeType != null && mimeType.toLowerCase().startsWith("text/")) {
+                    if (mimeType.equalsIgnoreCase("text/html")) {
+                        extension = ".html";
+                    } else {
+                        extension = ".txt";
+                    }
+                } else {
+                    extension = ".bin";
+                }
+            }
+        } else {
+            if (mimeType != null) {
+                // Compare the last segment of the extension against the mime
+                // type.
+                // If there's a mismatch, discard the entire extension.
+                int lastDotIndex = filename.lastIndexOf('.');
+                String typeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+                        filename.substring(lastDotIndex + 1));
+                if (typeFromExt != null && !typeFromExt.equalsIgnoreCase(mimeType)) {
+                    extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
+                    if (extension != null) {
+                        extension = "." + extension;
+                    }
+                }
+            }
+            if (extension == null) {
+                extension = filename.substring(dotIndex);
+            }
+            filename = filename.substring(0, dotIndex);
+        }
+
+        return filename + extension;
     }
 
 }
diff --git a/src/com/android/browser/PreferenceKeys.java b/src/com/android/browser/PreferenceKeys.java
index 1828032..ffbb39a 100644
--- a/src/com/android/browser/PreferenceKeys.java
+++ b/src/com/android/browser/PreferenceKeys.java
@@ -47,7 +47,8 @@
     static final String PREF_SEARCH_ENGINE = "search_engine";
     static final String PREF_WEBSITE_SETTINGS = "website_settings";
     static final String PREF_ALLOW_APP_TABS = "allow_apptabs";
-
+    // Keys for download path settings
+    static final String PREF_DOWNLOAD_PATH = "download_path_setting_screen";
     // ----------------------
     // Keys for debug_preferences.xml
     // ----------------------
diff --git a/src/com/android/browser/preferences/AdvancedPreferencesFragment.java b/src/com/android/browser/preferences/AdvancedPreferencesFragment.java
index 0a97ba0..acc26ce 100644
--- a/src/com/android/browser/preferences/AdvancedPreferencesFragment.java
+++ b/src/com/android/browser/preferences/AdvancedPreferencesFragment.java
@@ -17,11 +17,16 @@
 package com.android.browser.preferences;
 
 import com.android.browser.BrowserActivity;
+import com.android.browser.BrowserSettings;
+import com.android.browser.DownloadHandler;
 import com.android.browser.PreferenceKeys;
 import com.android.browser.R;
 
+import android.app.Activity;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.content.SharedPreferences.Editor;
+import android.net.Uri;
 import android.os.Bundle;
 import android.preference.ListPreference;
 import android.preference.Preference;
@@ -31,6 +36,7 @@
 import android.webkit.GeolocationPermissions;
 import android.webkit.ValueCallback;
 import android.webkit.WebStorage;
+import android.widget.Toast;
 
 import java.util.Map;
 import java.util.Set;
@@ -38,6 +44,7 @@
 public class AdvancedPreferencesFragment extends PreferenceFragment
         implements Preference.OnPreferenceChangeListener {
 
+    private static final int DOWNLOAD_PATH_RESULT_CODE = 1;
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -68,6 +75,62 @@
         e = findPreference(PreferenceKeys.PREF_PLUGIN_STATE);
         e.setOnPreferenceChangeListener(this);
         updateListPreferenceSummary((ListPreference) e);
+        onInitdownloadSettingsPreference();
+    }
+
+    private void onInitdownloadSettingsPreference() {
+        addPreferencesFromResource(R.xml.download_settings_preferences);
+        PreferenceScreen downloadPathPreset =
+                (PreferenceScreen) findPreference(PreferenceKeys.PREF_DOWNLOAD_PATH);
+        downloadPathPreset.setOnPreferenceClickListener(onClickDownloadPathSettings());
+
+        String downloadPath = downloadPathPreset.getSharedPreferences().
+                getString(PreferenceKeys.PREF_DOWNLOAD_PATH,
+                        BrowserSettings.getInstance().getDownloadPath());
+        String downloadPathForUser = DownloadHandler.getDownloadPathForUser(this.getActivity(),
+                downloadPath);
+        downloadPathPreset.setSummary(downloadPathForUser);
+    }
+
+    private Preference.OnPreferenceClickListener onClickDownloadPathSettings() {
+        return new Preference.OnPreferenceClickListener() {
+            public boolean onPreferenceClick(Preference preference) {
+                try {
+                    Intent i = new Intent("com.android.fileexplorer.action.DIR_SEL");
+                    AdvancedPreferencesFragment.this.startActivityForResult(i,
+                            DOWNLOAD_PATH_RESULT_CODE);
+                } catch (Exception e) {
+                    String err_msg = getResources().getString(R.string.activity_not_found,
+                            "com.android.fileexplorer.action.DIR_SEL");
+                    Toast.makeText(getActivity(), err_msg, Toast.LENGTH_LONG).show();
+                }
+                return true;
+            }
+        };
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        if (requestCode == DOWNLOAD_PATH_RESULT_CODE) {
+            if (resultCode == Activity.RESULT_OK && data != null) {
+                String downloadPath = data.getStringExtra("result_dir_sel");
+                if (downloadPath != null) {
+                    PreferenceScreen downloadPathPreset =
+                            (PreferenceScreen) findPreference(PreferenceKeys.PREF_DOWNLOAD_PATH);
+                    Editor editor = downloadPathPreset.getEditor();
+                    editor.putString(PreferenceKeys.PREF_DOWNLOAD_PATH, downloadPath);
+                    editor.apply();
+                    String downloadPathForUser = DownloadHandler.getDownloadPathForUser(
+                            this.getActivity(), downloadPath);
+                    downloadPathPreset.setSummary(downloadPathForUser);
+                }
+
+                return;
+            }
+        }
+        return;
     }
 
     void updateListPreferenceSummary(ListPreference e) {
@@ -154,4 +217,4 @@
 
         return "";
     }
-}
\ No newline at end of file
+}