blob: dee10ae2eecdd59f183cf65ab8d547b26b65c2e3 [file] [log] [blame]
Michael Kolb8233fac2010-10-26 16:08:53 -07001/*
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
17package com.android.browser;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.app.DownloadManager;
22import android.content.ActivityNotFoundException;
23import android.content.ComponentName;
Michael Kolb8233fac2010-10-26 16:08:53 -070024import android.content.Context;
25import android.content.Intent;
26import android.content.pm.PackageManager;
27import android.content.pm.ResolveInfo;
28import android.net.Uri;
29import android.net.WebAddress;
30import android.os.Environment;
Leon Scroggins63c02662010-11-18 15:16:27 -050031import android.text.TextUtils;
Michael Kolb8233fac2010-10-26 16:08:53 -070032import android.util.Log;
33import android.webkit.CookieManager;
34import android.webkit.URLUtil;
35import android.widget.Toast;
36
37/**
38 * Handle download requests
39 */
40public 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
Michael Kolb8233fac2010-10-26 16:08:53 -070047 /**
48 * Notify the host application a download should be done, or that
49 * the data should be streamed if a streaming viewer is available.
Leon Scroggins63c02662010-11-18 15:16:27 -050050 * @param activity Activity requesting the download.
Michael Kolb8233fac2010-10-26 16:08:53 -070051 * @param url The full url to the content that should be downloaded
Leon Scroggins63c02662010-11-18 15:16:27 -050052 * @param userAgent User agent of the downloading application.
53 * @param contentDisposition Content-disposition http header, if present.
Michael Kolb8233fac2010-10-26 16:08:53 -070054 * @param mimetype The mimetype of the content reported by the server
Selim Gurun0b3d66f2012-08-29 13:08:13 -070055 * @param referer The referer associated with the downloaded url
Kristian Monsenbc5cc752011-03-02 13:14:03 +000056 * @param privateBrowsing If the request is coming from a private browsing tab.
Michael Kolb8233fac2010-10-26 16:08:53 -070057 */
Leon Scroggins63c02662010-11-18 15:16:27 -050058 public static void onDownloadStart(Activity activity, String url,
Kristian Monsenbc5cc752011-03-02 13:14:03 +000059 String userAgent, String contentDisposition, String mimetype,
Selim Gurun0b3d66f2012-08-29 13:08:13 -070060 String referer, boolean privateBrowsing) {
Michael Kolb8233fac2010-10-26 16:08:53 -070061 // 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);
Leon Scroggins63c02662010-11-18 15:16:27 -050070 ResolveInfo info = activity.getPackageManager().resolveActivity(intent,
Michael Kolb8233fac2010-10-26 16:08:53 -070071 PackageManager.MATCH_DEFAULT_ONLY);
72 if (info != null) {
Leon Scroggins63c02662010-11-18 15:16:27 -050073 ComponentName myName = activity.getComponentName();
Michael Kolb8233fac2010-10-26 16:08:53 -070074 // 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 {
Leon Scroggins63c02662010-11-18 15:16:27 -050083 activity.startActivity(intent);
Michael Kolb8233fac2010-10-26 16:08:53 -070084 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 }
Leon Scroggins63c02662010-11-18 15:16:27 -050097 onDownloadStartNoStream(activity, url, userAgent, contentDisposition,
Selim Gurun0b3d66f2012-08-29 13:08:13 -070098 mimetype, referer, privateBrowsing);
Michael Kolb8233fac2010-10-26 16:08:53 -070099 }
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
104 private static String encodePath(String path) {
105 char[] chars = path.toCharArray();
106
107 boolean needed = false;
108 for (char c : chars) {
Selim Guruna770f8d2012-06-13 14:51:23 -0700109 if (c == '[' || c == ']' || c == '|') {
Michael Kolb8233fac2010-10-26 16:08:53 -0700110 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) {
Selim Guruna770f8d2012-06-13 14:51:23 -0700120 if (c == '[' || c == ']' || c == '|') {
Michael Kolb8233fac2010-10-26 16:08:53 -0700121 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.
Leon Scroggins63c02662010-11-18 15:16:27 -0500134 * @param activity Activity requesting the download.
Michael Kolb8233fac2010-10-26 16:08:53 -0700135 * @param url The full url to the content that should be downloaded
Leon Scroggins63c02662010-11-18 15:16:27 -0500136 * @param userAgent User agent of the downloading application.
137 * @param contentDisposition Content-disposition http header, if present.
Michael Kolb8233fac2010-10-26 16:08:53 -0700138 * @param mimetype The mimetype of the content reported by the server
Selim Gurun0b3d66f2012-08-29 13:08:13 -0700139 * @param referer The referer associated with the downloaded url
Kristian Monsenbc5cc752011-03-02 13:14:03 +0000140 * @param privateBrowsing If the request is coming from a private browsing tab.
Michael Kolb8233fac2010-10-26 16:08:53 -0700141 */
Leon Scroggins63c02662010-11-18 15:16:27 -0500142 /*package */ static void onDownloadStartNoStream(Activity activity,
143 String url, String userAgent, String contentDisposition,
Selim Gurun0b3d66f2012-08-29 13:08:13 -0700144 String mimetype, String referer, boolean privateBrowsing) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700145
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)) {
Leon Scroggins63c02662010-11-18 15:16:27 -0500157 msg = activity.getString(R.string.download_sdcard_busy_dlg_msg);
Michael Kolb8233fac2010-10-26 16:08:53 -0700158 title = R.string.download_sdcard_busy_dlg_title;
159 } else {
Leon Scroggins63c02662010-11-18 15:16:27 -0500160 msg = activity.getString(R.string.download_no_sdcard_dlg_msg, filename);
Michael Kolb8233fac2010-10-26 16:08:53 -0700161 title = R.string.download_no_sdcard_dlg_title;
162 }
163
Leon Scroggins63c02662010-11-18 15:16:27 -0500164 new AlertDialog.Builder(activity)
Michael Kolb8233fac2010-10-26 16:08:53 -0700165 .setTitle(title)
Björn Lundén2aa8ba22012-05-31 23:05:56 +0200166 .setIconAttribute(android.R.attr.alertDialogIcon)
Michael Kolb8233fac2010-10-26 16:08:53 -0700167 .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);
Leon Scroggins11e309c2011-02-01 13:37:14 -0500188 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 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700195 request.setMimeType(mimetype);
Vasu Noric2df8342010-12-18 20:15:46 -0800196 // 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);
Michael Kolb8233fac2010-10-26 16:08:53 -0700199 // 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());
Leon Scroggins63c02662010-11-18 15:16:27 -0500203 // XXX: Have to use the old url since the cookies were stored using the
204 // old percent-encoded url.
Kristian Monsenbc5cc752011-03-02 13:14:03 +0000205 String cookies = CookieManager.getInstance().getCookie(url, privateBrowsing);
Michael Kolb8233fac2010-10-26 16:08:53 -0700206 request.addRequestHeader("cookie", cookies);
Patrik Stenkilssond0fc5892012-02-14 09:33:24 +0100207 request.addRequestHeader("User-Agent", userAgent);
Selim Gurun0b3d66f2012-08-29 13:08:13 -0700208 request.addRequestHeader("Referer", referer);
Michael Kolb8233fac2010-10-26 16:08:53 -0700209 request.setNotificationVisibility(
210 DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
211 if (mimetype == null) {
Leon Scroggins63c02662010-11-18 15:16:27 -0500212 if (TextUtils.isEmpty(addressString)) {
213 return;
214 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700215 // 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
Leon Scroggins63c02662010-11-18 15:16:27 -0500217 new FetchUrlMimeType(activity, request, addressString, cookies,
218 userAgent).start();
Michael Kolb8233fac2010-10-26 16:08:53 -0700219 } else {
Leon Scroggins63c02662010-11-18 15:16:27 -0500220 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();
Michael Kolb8233fac2010-10-26 16:08:53 -0700227 }
Leon Scroggins63c02662010-11-18 15:16:27 -0500228 Toast.makeText(activity, R.string.download_pending, Toast.LENGTH_SHORT)
Michael Kolb8233fac2010-10-26 16:08:53 -0700229 .show();
230 }
231
232}