blob: 219114e1186dc00c135b440dee597cfd98684e65 [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);
John Reck6cfe9772011-11-23 10:17:22 -080070 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Leon Scroggins63c02662010-11-18 15:16:27 -050071 ResolveInfo info = activity.getPackageManager().resolveActivity(intent,
Michael Kolb8233fac2010-10-26 16:08:53 -070072 PackageManager.MATCH_DEFAULT_ONLY);
73 if (info != null) {
Leon Scroggins63c02662010-11-18 15:16:27 -050074 ComponentName myName = activity.getComponentName();
Michael Kolb8233fac2010-10-26 16:08:53 -070075 // If we resolved to ourselves, we don't want to attempt to
76 // load the url only to try and download it again.
77 if (!myName.getPackageName().equals(
78 info.activityInfo.packageName)
79 || !myName.getClassName().equals(
80 info.activityInfo.name)) {
81 // someone (other than us) knows how to handle this mime
82 // type with this scheme, don't download.
83 try {
Leon Scroggins63c02662010-11-18 15:16:27 -050084 activity.startActivity(intent);
Michael Kolb8233fac2010-10-26 16:08:53 -070085 return;
86 } catch (ActivityNotFoundException ex) {
87 if (LOGD_ENABLED) {
88 Log.d(LOGTAG, "activity not found for " + mimetype
89 + " over " + Uri.parse(url).getScheme(),
90 ex);
91 }
92 // Best behavior is to fall back to a download in this
93 // case
94 }
95 }
96 }
97 }
Leon Scroggins63c02662010-11-18 15:16:27 -050098 onDownloadStartNoStream(activity, url, userAgent, contentDisposition,
Kristian Monsenbc5cc752011-03-02 13:14:03 +000099 mimetype, privateBrowsing);
Michael Kolb8233fac2010-10-26 16:08:53 -0700100 }
101
102 // This is to work around the fact that java.net.URI throws Exceptions
103 // instead of just encoding URL's properly
104 // Helper method for onDownloadStartNoStream
105 private static String encodePath(String path) {
106 char[] chars = path.toCharArray();
107
108 boolean needed = false;
109 for (char c : chars) {
Selim Guruna770f8d2012-06-13 14:51:23 -0700110 if (c == '[' || c == ']' || c == '|') {
Michael Kolb8233fac2010-10-26 16:08:53 -0700111 needed = true;
112 break;
113 }
114 }
115 if (needed == false) {
116 return path;
117 }
118
119 StringBuilder sb = new StringBuilder("");
120 for (char c : chars) {
Selim Guruna770f8d2012-06-13 14:51:23 -0700121 if (c == '[' || c == ']' || c == '|') {
Michael Kolb8233fac2010-10-26 16:08:53 -0700122 sb.append('%');
123 sb.append(Integer.toHexString(c));
124 } else {
125 sb.append(c);
126 }
127 }
128
129 return sb.toString();
130 }
131
132 /**
133 * Notify the host application a download should be done, even if there
134 * is a streaming viewer available for thise type.
Leon Scroggins63c02662010-11-18 15:16:27 -0500135 * @param activity Activity requesting the download.
Michael Kolb8233fac2010-10-26 16:08:53 -0700136 * @param url The full url to the content that should be downloaded
Leon Scroggins63c02662010-11-18 15:16:27 -0500137 * @param userAgent User agent of the downloading application.
138 * @param contentDisposition Content-disposition http header, if present.
Michael Kolb8233fac2010-10-26 16:08:53 -0700139 * @param mimetype The mimetype of the content reported by the server
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,
Kristian Monsenbc5cc752011-03-02 13:14:03 +0000144 String mimetype, 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)
166 .setIcon(android.R.drawable.ic_dialog_alert)
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);
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);
207 request.setNotificationVisibility(
208 DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
209 if (mimetype == null) {
Leon Scroggins63c02662010-11-18 15:16:27 -0500210 if (TextUtils.isEmpty(addressString)) {
211 return;
212 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700213 // We must have long pressed on a link or image to download it. We
214 // are not sure of the mimetype in this case, so do a head request
Leon Scroggins63c02662010-11-18 15:16:27 -0500215 new FetchUrlMimeType(activity, request, addressString, cookies,
216 userAgent).start();
Michael Kolb8233fac2010-10-26 16:08:53 -0700217 } else {
Leon Scroggins63c02662010-11-18 15:16:27 -0500218 final DownloadManager manager
219 = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
220 new Thread("Browser download") {
221 public void run() {
222 manager.enqueue(request);
223 }
224 }.start();
Michael Kolb8233fac2010-10-26 16:08:53 -0700225 }
Leon Scroggins63c02662010-11-18 15:16:27 -0500226 Toast.makeText(activity, R.string.download_pending, Toast.LENGTH_SHORT)
Michael Kolb8233fac2010-10-26 16:08:53 -0700227 .show();
228 }
229
230}