blob: 17ad3206c7679e44b4038f591a6f80a4f84d454f [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;
24import android.content.ContentValues;
25import android.content.Context;
26import android.content.Intent;
27import android.content.pm.PackageManager;
28import android.content.pm.ResolveInfo;
29import android.net.Uri;
30import android.net.WebAddress;
31import android.os.Environment;
Leon Scroggins63c02662010-11-18 15:16:27 -050032import android.text.TextUtils;
Michael Kolb8233fac2010-10-26 16:08:53 -070033import android.util.Log;
34import android.webkit.CookieManager;
35import android.webkit.URLUtil;
36import android.widget.Toast;
37
38/**
39 * Handle download requests
40 */
41public class DownloadHandler {
42
43 private static final boolean LOGD_ENABLED =
44 com.android.browser.Browser.LOGD_ENABLED;
45
46 private static final String LOGTAG = "DLHandler";
47
Michael Kolb8233fac2010-10-26 16:08:53 -070048 /**
49 * Notify the host application a download should be done, or that
50 * the data should be streamed if a streaming viewer is available.
Leon Scroggins63c02662010-11-18 15:16:27 -050051 * @param activity Activity requesting the download.
Michael Kolb8233fac2010-10-26 16:08:53 -070052 * @param url The full url to the content that should be downloaded
Leon Scroggins63c02662010-11-18 15:16:27 -050053 * @param userAgent User agent of the downloading application.
54 * @param contentDisposition Content-disposition http header, if present.
Michael Kolb8233fac2010-10-26 16:08:53 -070055 * @param mimetype The mimetype of the content reported by the server
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,
60 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,
Kristian Monsenbc5cc752011-03-02 13:14:03 +000098 mimetype, 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) {
109 if (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 == ']') {
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.
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
Kristian Monsenbc5cc752011-03-02 13:14:03 +0000139 * @param privateBrowsing If the request is coming from a private browsing tab.
Michael Kolb8233fac2010-10-26 16:08:53 -0700140 */
Leon Scroggins63c02662010-11-18 15:16:27 -0500141 /*package */ static void onDownloadStartNoStream(Activity activity,
142 String url, String userAgent, String contentDisposition,
Kristian Monsenbc5cc752011-03-02 13:14:03 +0000143 String mimetype, boolean privateBrowsing) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700144
145 String filename = URLUtil.guessFileName(url,
146 contentDisposition, mimetype);
147
148 // Check to see if we have an SDCard
149 String status = Environment.getExternalStorageState();
150 if (!status.equals(Environment.MEDIA_MOUNTED)) {
151 int title;
152 String msg;
153
154 // Check to see if the SDCard is busy, same as the music app
155 if (status.equals(Environment.MEDIA_SHARED)) {
Leon Scroggins63c02662010-11-18 15:16:27 -0500156 msg = activity.getString(R.string.download_sdcard_busy_dlg_msg);
Michael Kolb8233fac2010-10-26 16:08:53 -0700157 title = R.string.download_sdcard_busy_dlg_title;
158 } else {
Leon Scroggins63c02662010-11-18 15:16:27 -0500159 msg = activity.getString(R.string.download_no_sdcard_dlg_msg, filename);
Michael Kolb8233fac2010-10-26 16:08:53 -0700160 title = R.string.download_no_sdcard_dlg_title;
161 }
162
Leon Scroggins63c02662010-11-18 15:16:27 -0500163 new AlertDialog.Builder(activity)
Michael Kolb8233fac2010-10-26 16:08:53 -0700164 .setTitle(title)
165 .setIcon(android.R.drawable.ic_dialog_alert)
166 .setMessage(msg)
167 .setPositiveButton(R.string.ok, null)
168 .show();
169 return;
170 }
171
172 // java.net.URI is a lot stricter than KURL so we have to encode some
173 // extra characters. Fix for b 2538060 and b 1634719
174 WebAddress webAddress;
175 try {
176 webAddress = new WebAddress(url);
177 webAddress.setPath(encodePath(webAddress.getPath()));
178 } catch (Exception e) {
179 // This only happens for very bad urls, we want to chatch the
180 // exception here
181 Log.e(LOGTAG, "Exception trying to parse url:" + url);
182 return;
183 }
184
185 String addressString = webAddress.toString();
186 Uri uri = Uri.parse(addressString);
Leon Scroggins11e309c2011-02-01 13:37:14 -0500187 final DownloadManager.Request request;
188 try {
189 request = new DownloadManager.Request(uri);
190 } catch (IllegalArgumentException e) {
191 Toast.makeText(activity, R.string.cannot_download, Toast.LENGTH_SHORT).show();
192 return;
193 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700194 request.setMimeType(mimetype);
Vasu Noric2df8342010-12-18 20:15:46 -0800195 // set downloaded file destination to /sdcard/Download.
196 // or, should it be set to one of several Environment.DIRECTORY* dirs depending on mimetype?
197 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
Michael Kolb8233fac2010-10-26 16:08:53 -0700198 // let this downloaded file be scanned by MediaScanner - so that it can
199 // show up in Gallery app, for example.
200 request.allowScanningByMediaScanner();
201 request.setDescription(webAddress.getHost());
Leon Scroggins63c02662010-11-18 15:16:27 -0500202 // XXX: Have to use the old url since the cookies were stored using the
203 // old percent-encoded url.
Kristian Monsenbc5cc752011-03-02 13:14:03 +0000204 String cookies = CookieManager.getInstance().getCookie(url, privateBrowsing);
Michael Kolb8233fac2010-10-26 16:08:53 -0700205 request.addRequestHeader("cookie", cookies);
206 request.setNotificationVisibility(
207 DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
208 if (mimetype == null) {
Leon Scroggins63c02662010-11-18 15:16:27 -0500209 if (TextUtils.isEmpty(addressString)) {
210 return;
211 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700212 // We must have long pressed on a link or image to download it. We
213 // are not sure of the mimetype in this case, so do a head request
Leon Scroggins63c02662010-11-18 15:16:27 -0500214 new FetchUrlMimeType(activity, request, addressString, cookies,
215 userAgent).start();
Michael Kolb8233fac2010-10-26 16:08:53 -0700216 } else {
Leon Scroggins63c02662010-11-18 15:16:27 -0500217 final DownloadManager manager
218 = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
219 new Thread("Browser download") {
220 public void run() {
221 manager.enqueue(request);
222 }
223 }.start();
Michael Kolb8233fac2010-10-26 16:08:53 -0700224 }
Leon Scroggins63c02662010-11-18 15:16:27 -0500225 Toast.makeText(activity, R.string.download_pending, Toast.LENGTH_SHORT)
Michael Kolb8233fac2010-10-26 16:08:53 -0700226 .show();
227 }
228
229}