18233facddcc51865d612a919d450db6954aa48e3Michael Kolb/* 28233facddcc51865d612a919d450db6954aa48e3Michael Kolb * Copyright (C) 2010 The Android Open Source Project 38233facddcc51865d612a919d450db6954aa48e3Michael Kolb * 48233facddcc51865d612a919d450db6954aa48e3Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License"); 58233facddcc51865d612a919d450db6954aa48e3Michael Kolb * you may not use this file except in compliance with the License. 68233facddcc51865d612a919d450db6954aa48e3Michael Kolb * You may obtain a copy of the License at 78233facddcc51865d612a919d450db6954aa48e3Michael Kolb * 88233facddcc51865d612a919d450db6954aa48e3Michael Kolb * http://www.apache.org/licenses/LICENSE-2.0 98233facddcc51865d612a919d450db6954aa48e3Michael Kolb * 108233facddcc51865d612a919d450db6954aa48e3Michael Kolb * Unless required by applicable law or agreed to in writing, software 118233facddcc51865d612a919d450db6954aa48e3Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS, 128233facddcc51865d612a919d450db6954aa48e3Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138233facddcc51865d612a919d450db6954aa48e3Michael Kolb * See the License for the specific language governing permissions and 148233facddcc51865d612a919d450db6954aa48e3Michael Kolb * limitations under the License. 158233facddcc51865d612a919d450db6954aa48e3Michael Kolb */ 168233facddcc51865d612a919d450db6954aa48e3Michael Kolb 178233facddcc51865d612a919d450db6954aa48e3Michael Kolbpackage com.android.browser; 188233facddcc51865d612a919d450db6954aa48e3Michael Kolb 198233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.app.Activity; 208233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.app.AlertDialog; 218233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.app.DownloadManager; 228233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.content.ActivityNotFoundException; 238233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.content.ComponentName; 248233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.content.Context; 258233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.content.Intent; 268233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.content.pm.PackageManager; 278233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.content.pm.ResolveInfo; 288233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.net.Uri; 298233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.net.WebAddress; 308233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.os.Environment; 3163c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scrogginsimport android.text.TextUtils; 328233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.util.Log; 338233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.webkit.CookieManager; 348233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.webkit.URLUtil; 358233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.widget.Toast; 368233facddcc51865d612a919d450db6954aa48e3Michael Kolb 378233facddcc51865d612a919d450db6954aa48e3Michael Kolb/** 388233facddcc51865d612a919d450db6954aa48e3Michael Kolb * Handle download requests 398233facddcc51865d612a919d450db6954aa48e3Michael Kolb */ 408233facddcc51865d612a919d450db6954aa48e3Michael Kolbpublic class DownloadHandler { 418233facddcc51865d612a919d450db6954aa48e3Michael Kolb 428233facddcc51865d612a919d450db6954aa48e3Michael Kolb private static final boolean LOGD_ENABLED = 438233facddcc51865d612a919d450db6954aa48e3Michael Kolb com.android.browser.Browser.LOGD_ENABLED; 448233facddcc51865d612a919d450db6954aa48e3Michael Kolb 458233facddcc51865d612a919d450db6954aa48e3Michael Kolb private static final String LOGTAG = "DLHandler"; 468233facddcc51865d612a919d450db6954aa48e3Michael Kolb 478233facddcc51865d612a919d450db6954aa48e3Michael Kolb /** 488233facddcc51865d612a919d450db6954aa48e3Michael Kolb * Notify the host application a download should be done, or that 498233facddcc51865d612a919d450db6954aa48e3Michael Kolb * the data should be streamed if a streaming viewer is available. 5063c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins * @param activity Activity requesting the download. 518233facddcc51865d612a919d450db6954aa48e3Michael Kolb * @param url The full url to the content that should be downloaded 5263c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins * @param userAgent User agent of the downloading application. 5363c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins * @param contentDisposition Content-disposition http header, if present. 548233facddcc51865d612a919d450db6954aa48e3Michael Kolb * @param mimetype The mimetype of the content reported by the server 550b3d66fc2275fb5270cb7166cc991be7dc9d8ff7Selim Gurun * @param referer The referer associated with the downloaded url 56bc5cc75c302eb49d15258155fc6f672fcbd62842Kristian Monsen * @param privateBrowsing If the request is coming from a private browsing tab. 578233facddcc51865d612a919d450db6954aa48e3Michael Kolb */ 5863c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins public static void onDownloadStart(Activity activity, String url, 59bc5cc75c302eb49d15258155fc6f672fcbd62842Kristian Monsen String userAgent, String contentDisposition, String mimetype, 600b3d66fc2275fb5270cb7166cc991be7dc9d8ff7Selim Gurun String referer, boolean privateBrowsing) { 618233facddcc51865d612a919d450db6954aa48e3Michael Kolb // if we're dealing wih A/V content that's not explicitly marked 628233facddcc51865d612a919d450db6954aa48e3Michael Kolb // for download, check if it's streamable. 638233facddcc51865d612a919d450db6954aa48e3Michael Kolb if (contentDisposition == null 648233facddcc51865d612a919d450db6954aa48e3Michael Kolb || !contentDisposition.regionMatches( 658233facddcc51865d612a919d450db6954aa48e3Michael Kolb true, 0, "attachment", 0, 10)) { 668233facddcc51865d612a919d450db6954aa48e3Michael Kolb // query the package manager to see if there's a registered handler 678233facddcc51865d612a919d450db6954aa48e3Michael Kolb // that matches. 688233facddcc51865d612a919d450db6954aa48e3Michael Kolb Intent intent = new Intent(Intent.ACTION_VIEW); 698233facddcc51865d612a919d450db6954aa48e3Michael Kolb intent.setDataAndType(Uri.parse(url), mimetype); 7063c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins ResolveInfo info = activity.getPackageManager().resolveActivity(intent, 718233facddcc51865d612a919d450db6954aa48e3Michael Kolb PackageManager.MATCH_DEFAULT_ONLY); 728233facddcc51865d612a919d450db6954aa48e3Michael Kolb if (info != null) { 7363c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins ComponentName myName = activity.getComponentName(); 748233facddcc51865d612a919d450db6954aa48e3Michael Kolb // If we resolved to ourselves, we don't want to attempt to 758233facddcc51865d612a919d450db6954aa48e3Michael Kolb // load the url only to try and download it again. 768233facddcc51865d612a919d450db6954aa48e3Michael Kolb if (!myName.getPackageName().equals( 778233facddcc51865d612a919d450db6954aa48e3Michael Kolb info.activityInfo.packageName) 788233facddcc51865d612a919d450db6954aa48e3Michael Kolb || !myName.getClassName().equals( 798233facddcc51865d612a919d450db6954aa48e3Michael Kolb info.activityInfo.name)) { 808233facddcc51865d612a919d450db6954aa48e3Michael Kolb // someone (other than us) knows how to handle this mime 818233facddcc51865d612a919d450db6954aa48e3Michael Kolb // type with this scheme, don't download. 828233facddcc51865d612a919d450db6954aa48e3Michael Kolb try { 8363c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins activity.startActivity(intent); 848233facddcc51865d612a919d450db6954aa48e3Michael Kolb return; 858233facddcc51865d612a919d450db6954aa48e3Michael Kolb } catch (ActivityNotFoundException ex) { 868233facddcc51865d612a919d450db6954aa48e3Michael Kolb if (LOGD_ENABLED) { 878233facddcc51865d612a919d450db6954aa48e3Michael Kolb Log.d(LOGTAG, "activity not found for " + mimetype 888233facddcc51865d612a919d450db6954aa48e3Michael Kolb + " over " + Uri.parse(url).getScheme(), 898233facddcc51865d612a919d450db6954aa48e3Michael Kolb ex); 908233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 918233facddcc51865d612a919d450db6954aa48e3Michael Kolb // Best behavior is to fall back to a download in this 928233facddcc51865d612a919d450db6954aa48e3Michael Kolb // case 938233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 948233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 958233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 968233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 9763c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins onDownloadStartNoStream(activity, url, userAgent, contentDisposition, 980b3d66fc2275fb5270cb7166cc991be7dc9d8ff7Selim Gurun mimetype, referer, privateBrowsing); 998233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 1008233facddcc51865d612a919d450db6954aa48e3Michael Kolb 1018233facddcc51865d612a919d450db6954aa48e3Michael Kolb // This is to work around the fact that java.net.URI throws Exceptions 1028233facddcc51865d612a919d450db6954aa48e3Michael Kolb // instead of just encoding URL's properly 1038233facddcc51865d612a919d450db6954aa48e3Michael Kolb // Helper method for onDownloadStartNoStream 1048233facddcc51865d612a919d450db6954aa48e3Michael Kolb private static String encodePath(String path) { 1058233facddcc51865d612a919d450db6954aa48e3Michael Kolb char[] chars = path.toCharArray(); 1068233facddcc51865d612a919d450db6954aa48e3Michael Kolb 1078233facddcc51865d612a919d450db6954aa48e3Michael Kolb boolean needed = false; 1088233facddcc51865d612a919d450db6954aa48e3Michael Kolb for (char c : chars) { 109a770f8d4bd583e9a719af6ce314838db12003b33Selim Gurun if (c == '[' || c == ']' || c == '|') { 1108233facddcc51865d612a919d450db6954aa48e3Michael Kolb needed = true; 1118233facddcc51865d612a919d450db6954aa48e3Michael Kolb break; 1128233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 1138233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 1148233facddcc51865d612a919d450db6954aa48e3Michael Kolb if (needed == false) { 1158233facddcc51865d612a919d450db6954aa48e3Michael Kolb return path; 1168233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 1178233facddcc51865d612a919d450db6954aa48e3Michael Kolb 1188233facddcc51865d612a919d450db6954aa48e3Michael Kolb StringBuilder sb = new StringBuilder(""); 1198233facddcc51865d612a919d450db6954aa48e3Michael Kolb for (char c : chars) { 120a770f8d4bd583e9a719af6ce314838db12003b33Selim Gurun if (c == '[' || c == ']' || c == '|') { 1218233facddcc51865d612a919d450db6954aa48e3Michael Kolb sb.append('%'); 1228233facddcc51865d612a919d450db6954aa48e3Michael Kolb sb.append(Integer.toHexString(c)); 1238233facddcc51865d612a919d450db6954aa48e3Michael Kolb } else { 1248233facddcc51865d612a919d450db6954aa48e3Michael Kolb sb.append(c); 1258233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 1268233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 1278233facddcc51865d612a919d450db6954aa48e3Michael Kolb 1288233facddcc51865d612a919d450db6954aa48e3Michael Kolb return sb.toString(); 1298233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 1308233facddcc51865d612a919d450db6954aa48e3Michael Kolb 1318233facddcc51865d612a919d450db6954aa48e3Michael Kolb /** 1328233facddcc51865d612a919d450db6954aa48e3Michael Kolb * Notify the host application a download should be done, even if there 1338233facddcc51865d612a919d450db6954aa48e3Michael Kolb * is a streaming viewer available for thise type. 13463c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins * @param activity Activity requesting the download. 1358233facddcc51865d612a919d450db6954aa48e3Michael Kolb * @param url The full url to the content that should be downloaded 13663c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins * @param userAgent User agent of the downloading application. 13763c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins * @param contentDisposition Content-disposition http header, if present. 1388233facddcc51865d612a919d450db6954aa48e3Michael Kolb * @param mimetype The mimetype of the content reported by the server 1390b3d66fc2275fb5270cb7166cc991be7dc9d8ff7Selim Gurun * @param referer The referer associated with the downloaded url 140bc5cc75c302eb49d15258155fc6f672fcbd62842Kristian Monsen * @param privateBrowsing If the request is coming from a private browsing tab. 1418233facddcc51865d612a919d450db6954aa48e3Michael Kolb */ 14263c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins /*package */ static void onDownloadStartNoStream(Activity activity, 14363c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins String url, String userAgent, String contentDisposition, 1440b3d66fc2275fb5270cb7166cc991be7dc9d8ff7Selim Gurun String mimetype, String referer, boolean privateBrowsing) { 1458233facddcc51865d612a919d450db6954aa48e3Michael Kolb 1468233facddcc51865d612a919d450db6954aa48e3Michael Kolb String filename = URLUtil.guessFileName(url, 1478233facddcc51865d612a919d450db6954aa48e3Michael Kolb contentDisposition, mimetype); 1488233facddcc51865d612a919d450db6954aa48e3Michael Kolb 1498233facddcc51865d612a919d450db6954aa48e3Michael Kolb // Check to see if we have an SDCard 1508233facddcc51865d612a919d450db6954aa48e3Michael Kolb String status = Environment.getExternalStorageState(); 1518233facddcc51865d612a919d450db6954aa48e3Michael Kolb if (!status.equals(Environment.MEDIA_MOUNTED)) { 1528233facddcc51865d612a919d450db6954aa48e3Michael Kolb int title; 1538233facddcc51865d612a919d450db6954aa48e3Michael Kolb String msg; 1548233facddcc51865d612a919d450db6954aa48e3Michael Kolb 1558233facddcc51865d612a919d450db6954aa48e3Michael Kolb // Check to see if the SDCard is busy, same as the music app 1568233facddcc51865d612a919d450db6954aa48e3Michael Kolb if (status.equals(Environment.MEDIA_SHARED)) { 15763c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins msg = activity.getString(R.string.download_sdcard_busy_dlg_msg); 1588233facddcc51865d612a919d450db6954aa48e3Michael Kolb title = R.string.download_sdcard_busy_dlg_title; 1598233facddcc51865d612a919d450db6954aa48e3Michael Kolb } else { 16063c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins msg = activity.getString(R.string.download_no_sdcard_dlg_msg, filename); 1618233facddcc51865d612a919d450db6954aa48e3Michael Kolb title = R.string.download_no_sdcard_dlg_title; 1628233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 1638233facddcc51865d612a919d450db6954aa48e3Michael Kolb 16463c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins new AlertDialog.Builder(activity) 1658233facddcc51865d612a919d450db6954aa48e3Michael Kolb .setTitle(title) 1662aa8ba254d128ca75f97a62b8d48d1305183e93cBjörn Lundén .setIconAttribute(android.R.attr.alertDialogIcon) 1678233facddcc51865d612a919d450db6954aa48e3Michael Kolb .setMessage(msg) 1688233facddcc51865d612a919d450db6954aa48e3Michael Kolb .setPositiveButton(R.string.ok, null) 1698233facddcc51865d612a919d450db6954aa48e3Michael Kolb .show(); 1708233facddcc51865d612a919d450db6954aa48e3Michael Kolb return; 1718233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 1728233facddcc51865d612a919d450db6954aa48e3Michael Kolb 1738233facddcc51865d612a919d450db6954aa48e3Michael Kolb // java.net.URI is a lot stricter than KURL so we have to encode some 1748233facddcc51865d612a919d450db6954aa48e3Michael Kolb // extra characters. Fix for b 2538060 and b 1634719 1758233facddcc51865d612a919d450db6954aa48e3Michael Kolb WebAddress webAddress; 1768233facddcc51865d612a919d450db6954aa48e3Michael Kolb try { 1778233facddcc51865d612a919d450db6954aa48e3Michael Kolb webAddress = new WebAddress(url); 1788233facddcc51865d612a919d450db6954aa48e3Michael Kolb webAddress.setPath(encodePath(webAddress.getPath())); 1798233facddcc51865d612a919d450db6954aa48e3Michael Kolb } catch (Exception e) { 1808233facddcc51865d612a919d450db6954aa48e3Michael Kolb // This only happens for very bad urls, we want to chatch the 1818233facddcc51865d612a919d450db6954aa48e3Michael Kolb // exception here 1828233facddcc51865d612a919d450db6954aa48e3Michael Kolb Log.e(LOGTAG, "Exception trying to parse url:" + url); 1838233facddcc51865d612a919d450db6954aa48e3Michael Kolb return; 1848233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 1858233facddcc51865d612a919d450db6954aa48e3Michael Kolb 1868233facddcc51865d612a919d450db6954aa48e3Michael Kolb String addressString = webAddress.toString(); 1878233facddcc51865d612a919d450db6954aa48e3Michael Kolb Uri uri = Uri.parse(addressString); 18811e309c58cfbbd2d0d02c0262ad6161fc9ba01ebLeon Scroggins final DownloadManager.Request request; 18911e309c58cfbbd2d0d02c0262ad6161fc9ba01ebLeon Scroggins try { 19011e309c58cfbbd2d0d02c0262ad6161fc9ba01ebLeon Scroggins request = new DownloadManager.Request(uri); 19111e309c58cfbbd2d0d02c0262ad6161fc9ba01ebLeon Scroggins } catch (IllegalArgumentException e) { 19211e309c58cfbbd2d0d02c0262ad6161fc9ba01ebLeon Scroggins Toast.makeText(activity, R.string.cannot_download, Toast.LENGTH_SHORT).show(); 19311e309c58cfbbd2d0d02c0262ad6161fc9ba01ebLeon Scroggins return; 19411e309c58cfbbd2d0d02c0262ad6161fc9ba01ebLeon Scroggins } 1958233facddcc51865d612a919d450db6954aa48e3Michael Kolb request.setMimeType(mimetype); 196c2df8342039829c868b30ad3bc3f865e26a04b1cVasu Nori // set downloaded file destination to /sdcard/Download. 197c2df8342039829c868b30ad3bc3f865e26a04b1cVasu Nori // or, should it be set to one of several Environment.DIRECTORY* dirs depending on mimetype? 198c2df8342039829c868b30ad3bc3f865e26a04b1cVasu Nori request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename); 1998233facddcc51865d612a919d450db6954aa48e3Michael Kolb // let this downloaded file be scanned by MediaScanner - so that it can 2008233facddcc51865d612a919d450db6954aa48e3Michael Kolb // show up in Gallery app, for example. 2018233facddcc51865d612a919d450db6954aa48e3Michael Kolb request.allowScanningByMediaScanner(); 2028233facddcc51865d612a919d450db6954aa48e3Michael Kolb request.setDescription(webAddress.getHost()); 20363c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins // XXX: Have to use the old url since the cookies were stored using the 20463c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins // old percent-encoded url. 205bc5cc75c302eb49d15258155fc6f672fcbd62842Kristian Monsen String cookies = CookieManager.getInstance().getCookie(url, privateBrowsing); 2068233facddcc51865d612a919d450db6954aa48e3Michael Kolb request.addRequestHeader("cookie", cookies); 207d0fc5898276ba5a02c7b8761065040d0a545cac4Patrik Stenkilsson request.addRequestHeader("User-Agent", userAgent); 2080b3d66fc2275fb5270cb7166cc991be7dc9d8ff7Selim Gurun request.addRequestHeader("Referer", referer); 2098233facddcc51865d612a919d450db6954aa48e3Michael Kolb request.setNotificationVisibility( 2108233facddcc51865d612a919d450db6954aa48e3Michael Kolb DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); 2118233facddcc51865d612a919d450db6954aa48e3Michael Kolb if (mimetype == null) { 21263c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins if (TextUtils.isEmpty(addressString)) { 21363c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins return; 21463c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins } 2158233facddcc51865d612a919d450db6954aa48e3Michael Kolb // We must have long pressed on a link or image to download it. We 2168233facddcc51865d612a919d450db6954aa48e3Michael Kolb // are not sure of the mimetype in this case, so do a head request 21763c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins new FetchUrlMimeType(activity, request, addressString, cookies, 21863c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins userAgent).start(); 2198233facddcc51865d612a919d450db6954aa48e3Michael Kolb } else { 22063c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins final DownloadManager manager 22163c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE); 22263c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins new Thread("Browser download") { 22363c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins public void run() { 22463c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins manager.enqueue(request); 22563c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins } 22663c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins }.start(); 2278233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 22863c0266b5d1fca4df859fe4fa3a9555d0783a2b6Leon Scroggins Toast.makeText(activity, R.string.download_pending, Toast.LENGTH_SHORT) 2298233facddcc51865d612a919d450db6954aa48e3Michael Kolb .show(); 2308233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 2318233facddcc51865d612a919d450db6954aa48e3Michael Kolb 2328233facddcc51865d612a919d450db6954aa48e3Michael Kolb} 233