Merge "Browser: add save path setting feature and full indicator"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 14ec59e..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,6 +199,17 @@
             </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>
 
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/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 84f117c..e3df6d7 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -432,4 +432,31 @@
     <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/strings.xml b/res/values/strings.xml
index cf8d0ab..2aa0ddb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1046,4 +1046,33 @@
     <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>
 </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/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 0b415be..b6ccfe5 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/Controller.java b/src/com/android/browser/Controller.java
index 4ec5042..40df461 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -518,7 +518,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;
@@ -1051,7 +1051,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 +2238,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
+}