• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.browser;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.DownloadManager;
22 import android.content.ActivityNotFoundException;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.net.Uri;
29 import android.net.WebAddress;
30 import android.os.Environment;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.webkit.CookieManager;
34 import android.webkit.URLUtil;
35 import android.widget.Toast;
36 
37 /**
38  * Handle download requests
39  */
40 public class DownloadHandler {
41 
42     private static final boolean LOGD_ENABLED =
43             com.android.browser.Browser.LOGD_ENABLED;
44 
45     private static final String LOGTAG = "DLHandler";
46 
47     /**
48      * Notify the host application a download should be done, or that
49      * the data should be streamed if a streaming viewer is available.
50      * @param activity Activity requesting the download.
51      * @param url The full url to the content that should be downloaded
52      * @param userAgent User agent of the downloading application.
53      * @param contentDisposition Content-disposition http header, if present.
54      * @param mimetype The mimetype of the content reported by the server
55      * @param referer The referer associated with the downloaded url
56      * @param privateBrowsing If the request is coming from a private browsing tab.
57      */
onDownloadStart(Activity activity, String url, String userAgent, String contentDisposition, String mimetype, String referer, boolean privateBrowsing)58     public static void onDownloadStart(Activity activity, String url,
59             String userAgent, String contentDisposition, String mimetype,
60             String referer, boolean privateBrowsing) {
61         // if we're dealing wih A/V content that's not explicitly marked
62         //     for download, check if it's streamable.
63         if (contentDisposition == null
64                 || !contentDisposition.regionMatches(
65                         true, 0, "attachment", 0, 10)) {
66             // query the package manager to see if there's a registered handler
67             //     that matches.
68             Intent intent = new Intent(Intent.ACTION_VIEW);
69             intent.setDataAndType(Uri.parse(url), mimetype);
70             ResolveInfo info = activity.getPackageManager().resolveActivity(intent,
71                     PackageManager.MATCH_DEFAULT_ONLY);
72             if (info != null) {
73                 ComponentName myName = activity.getComponentName();
74                 // If we resolved to ourselves, we don't want to attempt to
75                 // load the url only to try and download it again.
76                 if (!myName.getPackageName().equals(
77                         info.activityInfo.packageName)
78                         || !myName.getClassName().equals(
79                                 info.activityInfo.name)) {
80                     // someone (other than us) knows how to handle this mime
81                     // type with this scheme, don't download.
82                     try {
83                         activity.startActivity(intent);
84                         return;
85                     } catch (ActivityNotFoundException ex) {
86                         if (LOGD_ENABLED) {
87                             Log.d(LOGTAG, "activity not found for " + mimetype
88                                     + " over " + Uri.parse(url).getScheme(),
89                                     ex);
90                         }
91                         // Best behavior is to fall back to a download in this
92                         // case
93                     }
94                 }
95             }
96         }
97         onDownloadStartNoStream(activity, url, userAgent, contentDisposition,
98                 mimetype, referer, privateBrowsing);
99     }
100 
101     // This is to work around the fact that java.net.URI throws Exceptions
102     // instead of just encoding URL's properly
103     // Helper method for onDownloadStartNoStream
encodePath(String path)104     private static String encodePath(String path) {
105         char[] chars = path.toCharArray();
106 
107         boolean needed = false;
108         for (char c : chars) {
109             if (c == '[' || c == ']' || c == '|') {
110                 needed = true;
111                 break;
112             }
113         }
114         if (needed == false) {
115             return path;
116         }
117 
118         StringBuilder sb = new StringBuilder("");
119         for (char c : chars) {
120             if (c == '[' || c == ']' || c == '|') {
121                 sb.append('%');
122                 sb.append(Integer.toHexString(c));
123             } else {
124                 sb.append(c);
125             }
126         }
127 
128         return sb.toString();
129     }
130 
131     /**
132      * Notify the host application a download should be done, even if there
133      * is a streaming viewer available for thise type.
134      * @param activity Activity requesting the download.
135      * @param url The full url to the content that should be downloaded
136      * @param userAgent User agent of the downloading application.
137      * @param contentDisposition Content-disposition http header, if present.
138      * @param mimetype The mimetype of the content reported by the server
139      * @param referer The referer associated with the downloaded url
140      * @param privateBrowsing If the request is coming from a private browsing tab.
141      */
onDownloadStartNoStream(Activity activity, String url, String userAgent, String contentDisposition, String mimetype, String referer, boolean privateBrowsing)142     /*package */ static void onDownloadStartNoStream(Activity activity,
143             String url, String userAgent, String contentDisposition,
144             String mimetype, String referer, boolean privateBrowsing) {
145 
146         String filename = URLUtil.guessFileName(url,
147                 contentDisposition, mimetype);
148 
149         // Check to see if we have an SDCard
150         String status = Environment.getExternalStorageState();
151         if (!status.equals(Environment.MEDIA_MOUNTED)) {
152             int title;
153             String msg;
154 
155             // Check to see if the SDCard is busy, same as the music app
156             if (status.equals(Environment.MEDIA_SHARED)) {
157                 msg = activity.getString(R.string.download_sdcard_busy_dlg_msg);
158                 title = R.string.download_sdcard_busy_dlg_title;
159             } else {
160                 msg = activity.getString(R.string.download_no_sdcard_dlg_msg, filename);
161                 title = R.string.download_no_sdcard_dlg_title;
162             }
163 
164             new AlertDialog.Builder(activity)
165                 .setTitle(title)
166                 .setIconAttribute(android.R.attr.alertDialogIcon)
167                 .setMessage(msg)
168                 .setPositiveButton(R.string.ok, null)
169                 .show();
170             return;
171         }
172 
173         // java.net.URI is a lot stricter than KURL so we have to encode some
174         // extra characters. Fix for b 2538060 and b 1634719
175         WebAddress webAddress;
176         try {
177             webAddress = new WebAddress(url);
178             webAddress.setPath(encodePath(webAddress.getPath()));
179         } catch (Exception e) {
180             // This only happens for very bad urls, we want to chatch the
181             // exception here
182             Log.e(LOGTAG, "Exception trying to parse url:" + url);
183             return;
184         }
185 
186         String addressString = webAddress.toString();
187         Uri uri = Uri.parse(addressString);
188         final DownloadManager.Request request;
189         try {
190             request = new DownloadManager.Request(uri);
191         } catch (IllegalArgumentException e) {
192             Toast.makeText(activity, R.string.cannot_download, Toast.LENGTH_SHORT).show();
193             return;
194         }
195         request.setMimeType(mimetype);
196         // set downloaded file destination to /sdcard/Download.
197         // or, should it be set to one of several Environment.DIRECTORY* dirs depending on mimetype?
198         request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
199         // let this downloaded file be scanned by MediaScanner - so that it can
200         // show up in Gallery app, for example.
201         request.allowScanningByMediaScanner();
202         request.setDescription(webAddress.getHost());
203         // XXX: Have to use the old url since the cookies were stored using the
204         // old percent-encoded url.
205         String cookies = CookieManager.getInstance().getCookie(url, privateBrowsing);
206         request.addRequestHeader("cookie", cookies);
207         request.addRequestHeader("User-Agent", userAgent);
208         request.addRequestHeader("Referer", referer);
209         request.setNotificationVisibility(
210                 DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
211         if (mimetype == null) {
212             if (TextUtils.isEmpty(addressString)) {
213                 return;
214             }
215             // We must have long pressed on a link or image to download it. We
216             // are not sure of the mimetype in this case, so do a head request
217             new FetchUrlMimeType(activity, request, addressString, cookies,
218                     userAgent).start();
219         } else {
220             final DownloadManager manager
221                     = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
222             new Thread("Browser download") {
223                 public void run() {
224                     manager.enqueue(request);
225                 }
226             }.start();
227         }
228         Toast.makeText(activity, R.string.download_pending, Toast.LENGTH_SHORT)
229                 .show();
230     }
231 
232 }
233