blob: 40278f48040d53d9d0a354dad858624d89d205a5 [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
Michael Kolb8233fac2010-10-26 16:08:53 -070056 */
Leon Scroggins63c02662010-11-18 15:16:27 -050057 public static void onDownloadStart(Activity activity, String url,
58 String userAgent, String contentDisposition, String mimetype) {
Michael Kolb8233fac2010-10-26 16:08:53 -070059 // if we're dealing wih A/V content that's not explicitly marked
60 // for download, check if it's streamable.
61 if (contentDisposition == null
62 || !contentDisposition.regionMatches(
63 true, 0, "attachment", 0, 10)) {
64 // query the package manager to see if there's a registered handler
65 // that matches.
66 Intent intent = new Intent(Intent.ACTION_VIEW);
67 intent.setDataAndType(Uri.parse(url), mimetype);
Leon Scroggins63c02662010-11-18 15:16:27 -050068 ResolveInfo info = activity.getPackageManager().resolveActivity(intent,
Michael Kolb8233fac2010-10-26 16:08:53 -070069 PackageManager.MATCH_DEFAULT_ONLY);
70 if (info != null) {
Leon Scroggins63c02662010-11-18 15:16:27 -050071 ComponentName myName = activity.getComponentName();
Michael Kolb8233fac2010-10-26 16:08:53 -070072 // If we resolved to ourselves, we don't want to attempt to
73 // load the url only to try and download it again.
74 if (!myName.getPackageName().equals(
75 info.activityInfo.packageName)
76 || !myName.getClassName().equals(
77 info.activityInfo.name)) {
78 // someone (other than us) knows how to handle this mime
79 // type with this scheme, don't download.
80 try {
Leon Scroggins63c02662010-11-18 15:16:27 -050081 activity.startActivity(intent);
Michael Kolb8233fac2010-10-26 16:08:53 -070082 return;
83 } catch (ActivityNotFoundException ex) {
84 if (LOGD_ENABLED) {
85 Log.d(LOGTAG, "activity not found for " + mimetype
86 + " over " + Uri.parse(url).getScheme(),
87 ex);
88 }
89 // Best behavior is to fall back to a download in this
90 // case
91 }
92 }
93 }
94 }
Leon Scroggins63c02662010-11-18 15:16:27 -050095 onDownloadStartNoStream(activity, url, userAgent, contentDisposition,
96 mimetype);
Michael Kolb8233fac2010-10-26 16:08:53 -070097 }
98
99 // This is to work around the fact that java.net.URI throws Exceptions
100 // instead of just encoding URL's properly
101 // Helper method for onDownloadStartNoStream
102 private static String encodePath(String path) {
103 char[] chars = path.toCharArray();
104
105 boolean needed = false;
106 for (char c : chars) {
107 if (c == '[' || c == ']') {
108 needed = true;
109 break;
110 }
111 }
112 if (needed == false) {
113 return path;
114 }
115
116 StringBuilder sb = new StringBuilder("");
117 for (char c : chars) {
118 if (c == '[' || c == ']') {
119 sb.append('%');
120 sb.append(Integer.toHexString(c));
121 } else {
122 sb.append(c);
123 }
124 }
125
126 return sb.toString();
127 }
128
129 /**
130 * Notify the host application a download should be done, even if there
131 * is a streaming viewer available for thise type.
Leon Scroggins63c02662010-11-18 15:16:27 -0500132 * @param activity Activity requesting the download.
Michael Kolb8233fac2010-10-26 16:08:53 -0700133 * @param url The full url to the content that should be downloaded
Leon Scroggins63c02662010-11-18 15:16:27 -0500134 * @param userAgent User agent of the downloading application.
135 * @param contentDisposition Content-disposition http header, if present.
Michael Kolb8233fac2010-10-26 16:08:53 -0700136 * @param mimetype The mimetype of the content reported by the server
Michael Kolb8233fac2010-10-26 16:08:53 -0700137 */
Leon Scroggins63c02662010-11-18 15:16:27 -0500138 /*package */ static void onDownloadStartNoStream(Activity activity,
139 String url, String userAgent, String contentDisposition,
140 String mimetype) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700141
142 String filename = URLUtil.guessFileName(url,
143 contentDisposition, mimetype);
144
145 // Check to see if we have an SDCard
146 String status = Environment.getExternalStorageState();
147 if (!status.equals(Environment.MEDIA_MOUNTED)) {
148 int title;
149 String msg;
150
151 // Check to see if the SDCard is busy, same as the music app
152 if (status.equals(Environment.MEDIA_SHARED)) {
Leon Scroggins63c02662010-11-18 15:16:27 -0500153 msg = activity.getString(R.string.download_sdcard_busy_dlg_msg);
Michael Kolb8233fac2010-10-26 16:08:53 -0700154 title = R.string.download_sdcard_busy_dlg_title;
155 } else {
Leon Scroggins63c02662010-11-18 15:16:27 -0500156 msg = activity.getString(R.string.download_no_sdcard_dlg_msg, filename);
Michael Kolb8233fac2010-10-26 16:08:53 -0700157 title = R.string.download_no_sdcard_dlg_title;
158 }
159
Leon Scroggins63c02662010-11-18 15:16:27 -0500160 new AlertDialog.Builder(activity)
Michael Kolb8233fac2010-10-26 16:08:53 -0700161 .setTitle(title)
162 .setIcon(android.R.drawable.ic_dialog_alert)
163 .setMessage(msg)
164 .setPositiveButton(R.string.ok, null)
165 .show();
166 return;
167 }
168
169 // java.net.URI is a lot stricter than KURL so we have to encode some
170 // extra characters. Fix for b 2538060 and b 1634719
171 WebAddress webAddress;
172 try {
173 webAddress = new WebAddress(url);
174 webAddress.setPath(encodePath(webAddress.getPath()));
175 } catch (Exception e) {
176 // This only happens for very bad urls, we want to chatch the
177 // exception here
178 Log.e(LOGTAG, "Exception trying to parse url:" + url);
179 return;
180 }
181
182 String addressString = webAddress.toString();
183 Uri uri = Uri.parse(addressString);
Leon Scroggins63c02662010-11-18 15:16:27 -0500184 final DownloadManager.Request request = new DownloadManager.Request(uri);
Michael Kolb8233fac2010-10-26 16:08:53 -0700185 request.setMimeType(mimetype);
Vasu Noric2df8342010-12-18 20:15:46 -0800186 // set downloaded file destination to /sdcard/Download.
187 // or, should it be set to one of several Environment.DIRECTORY* dirs depending on mimetype?
188 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
Michael Kolb8233fac2010-10-26 16:08:53 -0700189 // let this downloaded file be scanned by MediaScanner - so that it can
190 // show up in Gallery app, for example.
191 request.allowScanningByMediaScanner();
192 request.setDescription(webAddress.getHost());
Leon Scroggins63c02662010-11-18 15:16:27 -0500193 // XXX: Have to use the old url since the cookies were stored using the
194 // old percent-encoded url.
Michael Kolb8233fac2010-10-26 16:08:53 -0700195 String cookies = CookieManager.getInstance().getCookie(url);
196 request.addRequestHeader("cookie", cookies);
197 request.setNotificationVisibility(
198 DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
199 if (mimetype == null) {
Leon Scroggins63c02662010-11-18 15:16:27 -0500200 if (TextUtils.isEmpty(addressString)) {
201 return;
202 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700203 // We must have long pressed on a link or image to download it. We
204 // are not sure of the mimetype in this case, so do a head request
Leon Scroggins63c02662010-11-18 15:16:27 -0500205 new FetchUrlMimeType(activity, request, addressString, cookies,
206 userAgent).start();
Michael Kolb8233fac2010-10-26 16:08:53 -0700207 } else {
Leon Scroggins63c02662010-11-18 15:16:27 -0500208 final DownloadManager manager
209 = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
210 new Thread("Browser download") {
211 public void run() {
212 manager.enqueue(request);
213 }
214 }.start();
Michael Kolb8233fac2010-10-26 16:08:53 -0700215 }
Leon Scroggins63c02662010-11-18 15:16:27 -0500216 Toast.makeText(activity, R.string.download_pending, Toast.LENGTH_SHORT)
Michael Kolb8233fac2010-10-26 16:08:53 -0700217 .show();
218 }
219
220}