blob: 88e667989ff73db6ab547098844ecc44bd95bd78 [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
Bijan Amirzada41242f22014-03-21 12:12:18 -070017package com.android.browser;
Michael Kolb8233fac2010-10-26 16:08:53 -070018
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.app.DownloadManager;
luxiaol62677b02013-07-22 07:54:49 +080022import android.app.DownloadManager.Request;
Michael Kolb8233fac2010-10-26 16:08:53 -070023import android.content.ActivityNotFoundException;
24import android.content.ComponentName;
Michael Kolb8233fac2010-10-26 16:08:53 -070025import android.content.Context;
qqzhoua95a2e22013-04-18 17:28:31 +080026import android.content.DialogInterface;
Michael Kolb8233fac2010-10-26 16:08:53 -070027import android.content.Intent;
28import android.content.pm.PackageManager;
29import android.content.pm.ResolveInfo;
30import android.net.Uri;
luxiaol62677b02013-07-22 07:54:49 +080031import android.os.Bundle;
Michael Kolb8233fac2010-10-26 16:08:53 -070032import android.os.Environment;
luxiaol62677b02013-07-22 07:54:49 +080033import android.os.StatFs;
34import android.os.storage.StorageManager;
Michael Kolb8233fac2010-10-26 16:08:53 -070035import android.util.Log;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080036import org.codeaurora.swe.CookieManager;
Michael Kolb8233fac2010-10-26 16:08:53 -070037import android.webkit.URLUtil;
38import android.widget.Toast;
39
Bijan Amirzada41242f22014-03-21 12:12:18 -070040import com.android.browser.R;
41import com.android.browser.platformsupport.WebAddress;
42import com.android.browser.reflect.ReflectHelper;
luxiaol62677b02013-07-22 07:54:49 +080043
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080044import java.io.File;
Michael Kolb8233fac2010-10-26 16:08:53 -070045/**
46 * Handle download requests
47 */
48public class DownloadHandler {
49
50 private static final boolean LOGD_ENABLED =
Bijan Amirzada41242f22014-03-21 12:12:18 -070051 com.android.browser.Browser.LOGD_ENABLED;
Michael Kolb8233fac2010-10-26 16:08:53 -070052
53 private static final String LOGTAG = "DLHandler";
luxiaol62677b02013-07-22 07:54:49 +080054 private static String mInternalStorage;
55 private static String mExternalStorage;
56 private final static String INVALID_PATH = "/storage";
Michael Kolb8233fac2010-10-26 16:08:53 -070057
luxiaol62677b02013-07-22 07:54:49 +080058 public static void startingDownload(Activity activity,
59 String url, String userAgent, String contentDisposition,
60 String mimetype, String referer, boolean privateBrowsing, long contentLength,
61 String filename, String downloadPath) {
62 // java.net.URI is a lot stricter than KURL so we have to encode some
63 // extra characters. Fix for b 2538060 and b 1634719
64 WebAddress webAddress;
65 try {
66 webAddress = new WebAddress(url);
67 webAddress.setPath(encodePath(webAddress.getPath()));
68 } catch (Exception e) {
69 // This only happens for very bad urls, we want to chatch the
70 // exception here
71 Log.e(LOGTAG, "Exception trying to parse url:" + url);
72 return;
73 }
74
75 String addressString = webAddress.toString();
76 Uri uri = Uri.parse(addressString);
77 final DownloadManager.Request request;
78 try {
79 request = new DownloadManager.Request(uri);
80 } catch (IllegalArgumentException e) {
81 Toast.makeText(activity, R.string.cannot_download, Toast.LENGTH_SHORT).show();
82 return;
83 }
84 request.setMimeType(mimetype);
85 // set downloaded file destination to /sdcard/Download.
86 // or, should it be set to one of several Environment.DIRECTORY* dirs
87 // depending on mimetype?
88 try {
89 setDestinationDir(downloadPath, filename, request);
90 } catch (Exception e) {
91 showNoEnoughMemoryDialog(activity);
92 return;
93 }
94 // let this downloaded file be scanned by MediaScanner - so that it can
95 // show up in Gallery app, for example.
96 request.allowScanningByMediaScanner();
97 request.setDescription(webAddress.getHost());
98 // XXX: Have to use the old url since the cookies were stored using the
99 // old percent-encoded url.
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800100
luxiaol62677b02013-07-22 07:54:49 +0800101 String cookies = CookieManager.getInstance().getCookie(url, privateBrowsing);
102 request.addRequestHeader("cookie", cookies);
103 request.addRequestHeader("User-Agent", userAgent);
104 request.addRequestHeader("Referer", referer);
Panos Thomase57e3a02014-04-30 20:25:16 -0700105 request.setVisibleInDownloadsUi(!privateBrowsing);
luxiaol62677b02013-07-22 07:54:49 +0800106 request.setNotificationVisibility(
107 DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
108 final DownloadManager manager = (DownloadManager) activity
109 .getSystemService(Context.DOWNLOAD_SERVICE);
110 new Thread("Browser download") {
111 public void run() {
112 manager.enqueue(request);
113 }
114 }.start();
Panos Thomase57e3a02014-04-30 20:25:16 -0700115 showStartDownloadToast(activity, privateBrowsing);
luxiaol62677b02013-07-22 07:54:49 +0800116 }
117
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800118 private static boolean isAudioFileType(int fileType){
Bijan Amirzadaac832f72014-03-17 15:29:16 -0700119 Object[] params = {Integer.valueOf(fileType)};
120 Class[] type = new Class[] {int.class};
Bijan Amirzada58383e72014-04-01 14:45:22 -0700121 Boolean result = (Boolean) ReflectHelper.invokeMethod("android.media.MediaFile",
Bijan Amirzadaac832f72014-03-17 15:29:16 -0700122 "isAudioFileType", type, params);
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800123 return result;
124 }
125
126 private static boolean isVideoFileType(int fileType){
Bijan Amirzadaac832f72014-03-17 15:29:16 -0700127 Object[] params = {Integer.valueOf(fileType)};
128 Class[] type = new Class[] {int.class};
Bijan Amirzada58383e72014-04-01 14:45:22 -0700129 Boolean result = (Boolean) ReflectHelper.invokeMethod("android.media.MediaFile",
Bijan Amirzadaac832f72014-03-17 15:29:16 -0700130 "isVideoFileType", type, params);
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800131 return result;
132 }
133
kaiyize6a27d02013-08-22 15:08:19 +0800134 /**
135 * Notify the host application a download should be done, or that
136 * the data should be streamed if a streaming viewer is available.
137 * @param activity Activity requesting the download.
138 * @param url The full url to the content that should be downloaded
139 * @param userAgent User agent of the downloading application.
140 * @param contentDisposition Content-disposition http header, if present.
141 * @param mimetype The mimetype of the content reported by the server
142 * @param referer The referer associated with the downloaded url
143 * @param privateBrowsing If the request is coming from a private browsing tab.
144 */
qqzhoua95a2e22013-04-18 17:28:31 +0800145 public static boolean onDownloadStart(final Activity activity, final String url,
146 final String userAgent, final String contentDisposition, final String mimetype,
luxiaol62677b02013-07-22 07:54:49 +0800147 final String referer, final boolean privateBrowsing, final long contentLength) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700148 // if we're dealing wih A/V content that's not explicitly marked
149 // for download, check if it's streamable.
150 if (contentDisposition == null
151 || !contentDisposition.regionMatches(
152 true, 0, "attachment", 0, 10)) {
qqzhoua95a2e22013-04-18 17:28:31 +0800153 // Add for Carrier Feature - When open an audio/video link, prompt a dialog
154 // to let the user choose play or download operation.
155 Uri uri = Uri.parse(url);
156 String scheme = uri.getScheme();
157 Log.v(LOGTAG, "scheme:" + scheme + ", mimetype:" + mimetype);
158 // Some mimetype for audio/video files is not started with "audio" or "video",
159 // such as ogg audio file with mimetype "application/ogg". So we also check
160 // file type by MediaFile.isAudioFileType() and MediaFile.isVideoFileType().
161 // For those file types other than audio or video, download it immediately.
Bijan Amirzadaac832f72014-03-17 15:29:16 -0700162 Object[] params = {mimetype};
163 Class[] type = new Class[] {String.class};
Bijan Amirzada58383e72014-04-01 14:45:22 -0700164 Integer result = (Integer) ReflectHelper.invokeMethod("android.media.MediaFile",
Bijan Amirzadaac832f72014-03-17 15:29:16 -0700165 "getFileTypeForMimeType", type, params);
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800166 int fileType = result.intValue();
qqzhoua95a2e22013-04-18 17:28:31 +0800167 if ("http".equalsIgnoreCase(scheme) &&
168 (mimetype.startsWith("audio/") ||
169 mimetype.startsWith("video/") ||
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800170 isAudioFileType(fileType) ||
171 isVideoFileType(fileType))) {
qqzhoua95a2e22013-04-18 17:28:31 +0800172 new AlertDialog.Builder(activity)
173 .setTitle(R.string.application_name)
174 .setIcon(R.drawable.default_video_poster)
175 .setMessage(R.string.http_video_msg)
176 .setPositiveButton(R.string.video_save, new DialogInterface.OnClickListener() {
177 public void onClick(DialogInterface dialog, int which) {
178 onDownloadStartNoStream(activity, url, userAgent, contentDisposition,
luxiaol62677b02013-07-22 07:54:49 +0800179 mimetype, referer, privateBrowsing, contentLength);
qqzhoua95a2e22013-04-18 17:28:31 +0800180 }
181 })
182 .setNegativeButton(R.string.video_play, new DialogInterface.OnClickListener() {
183 public void onClick(DialogInterface dialog, int which) {
184 Intent intent = new Intent(Intent.ACTION_VIEW);
185 intent.setDataAndType(Uri.parse(url), mimetype);
186 try {
187 String title = URLUtil.guessFileName(url, contentDisposition, mimetype);
188 intent.putExtra(Intent.EXTRA_TITLE, title);
189 activity.startActivity(intent);
190 } catch (ActivityNotFoundException ex) {
191 Log.w(LOGTAG, "When http stream play, activity not found for "
Bijan Amirzadaac832f72014-03-17 15:29:16 -0700192 + mimetype + " over " + Uri.parse(url).getScheme(), ex);
qqzhoua95a2e22013-04-18 17:28:31 +0800193 }
194 }
195 }).show();
196
197 return true;
198 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700199 // query the package manager to see if there's a registered handler
200 // that matches.
201 Intent intent = new Intent(Intent.ACTION_VIEW);
202 intent.setDataAndType(Uri.parse(url), mimetype);
Leon Scroggins63c02662010-11-18 15:16:27 -0500203 ResolveInfo info = activity.getPackageManager().resolveActivity(intent,
Michael Kolb8233fac2010-10-26 16:08:53 -0700204 PackageManager.MATCH_DEFAULT_ONLY);
205 if (info != null) {
Leon Scroggins63c02662010-11-18 15:16:27 -0500206 ComponentName myName = activity.getComponentName();
Michael Kolb8233fac2010-10-26 16:08:53 -0700207 // If we resolved to ourselves, we don't want to attempt to
208 // load the url only to try and download it again.
209 if (!myName.getPackageName().equals(
210 info.activityInfo.packageName)
211 || !myName.getClassName().equals(
212 info.activityInfo.name)) {
213 // someone (other than us) knows how to handle this mime
214 // type with this scheme, don't download.
215 try {
Leon Scroggins63c02662010-11-18 15:16:27 -0500216 activity.startActivity(intent);
qqzhoua95a2e22013-04-18 17:28:31 +0800217 return false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700218 } catch (ActivityNotFoundException ex) {
219 if (LOGD_ENABLED) {
220 Log.d(LOGTAG, "activity not found for " + mimetype
221 + " over " + Uri.parse(url).getScheme(),
222 ex);
223 }
224 // Best behavior is to fall back to a download in this
225 // case
226 }
227 }
228 }
229 }
Leon Scroggins63c02662010-11-18 15:16:27 -0500230 onDownloadStartNoStream(activity, url, userAgent, contentDisposition,
luxiaol62677b02013-07-22 07:54:49 +0800231 mimetype, referer, privateBrowsing, contentLength);
qqzhoua95a2e22013-04-18 17:28:31 +0800232 return false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700233 }
234
235 // This is to work around the fact that java.net.URI throws Exceptions
236 // instead of just encoding URL's properly
237 // Helper method for onDownloadStartNoStream
238 private static String encodePath(String path) {
239 char[] chars = path.toCharArray();
240
241 boolean needed = false;
242 for (char c : chars) {
Selim Guruna770f8d2012-06-13 14:51:23 -0700243 if (c == '[' || c == ']' || c == '|') {
Michael Kolb8233fac2010-10-26 16:08:53 -0700244 needed = true;
245 break;
246 }
247 }
248 if (needed == false) {
249 return path;
250 }
251
252 StringBuilder sb = new StringBuilder("");
253 for (char c : chars) {
Selim Guruna770f8d2012-06-13 14:51:23 -0700254 if (c == '[' || c == ']' || c == '|') {
Michael Kolb8233fac2010-10-26 16:08:53 -0700255 sb.append('%');
256 sb.append(Integer.toHexString(c));
257 } else {
258 sb.append(c);
259 }
260 }
261
262 return sb.toString();
263 }
264
265 /**
266 * Notify the host application a download should be done, even if there
267 * is a streaming viewer available for thise type.
Leon Scroggins63c02662010-11-18 15:16:27 -0500268 * @param activity Activity requesting the download.
Michael Kolb8233fac2010-10-26 16:08:53 -0700269 * @param url The full url to the content that should be downloaded
Leon Scroggins63c02662010-11-18 15:16:27 -0500270 * @param userAgent User agent of the downloading application.
271 * @param contentDisposition Content-disposition http header, if present.
Michael Kolb8233fac2010-10-26 16:08:53 -0700272 * @param mimetype The mimetype of the content reported by the server
Selim Gurun0b3d66f2012-08-29 13:08:13 -0700273 * @param referer The referer associated with the downloaded url
Kristian Monsenbc5cc752011-03-02 13:14:03 +0000274 * @param privateBrowsing If the request is coming from a private browsing tab.
Michael Kolb8233fac2010-10-26 16:08:53 -0700275 */
luxiaol62677b02013-07-22 07:54:49 +0800276 /* package */static void onDownloadStartNoStream(Activity activity,
Leon Scroggins63c02662010-11-18 15:16:27 -0500277 String url, String userAgent, String contentDisposition,
luxiaol62677b02013-07-22 07:54:49 +0800278 String mimetype, String referer, boolean privateBrowsing, long contentLength) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700279
luxiaol62677b02013-07-22 07:54:49 +0800280 initStorageDefaultPath(activity);
Michael Kolb8233fac2010-10-26 16:08:53 -0700281 String filename = URLUtil.guessFileName(url,
282 contentDisposition, mimetype);
283
284 // Check to see if we have an SDCard
285 String status = Environment.getExternalStorageState();
286 if (!status.equals(Environment.MEDIA_MOUNTED)) {
287 int title;
288 String msg;
289
290 // Check to see if the SDCard is busy, same as the music app
291 if (status.equals(Environment.MEDIA_SHARED)) {
Leon Scroggins63c02662010-11-18 15:16:27 -0500292 msg = activity.getString(R.string.download_sdcard_busy_dlg_msg);
Michael Kolb8233fac2010-10-26 16:08:53 -0700293 title = R.string.download_sdcard_busy_dlg_title;
294 } else {
Leon Scroggins63c02662010-11-18 15:16:27 -0500295 msg = activity.getString(R.string.download_no_sdcard_dlg_msg, filename);
Michael Kolb8233fac2010-10-26 16:08:53 -0700296 title = R.string.download_no_sdcard_dlg_title;
297 }
298
Leon Scroggins63c02662010-11-18 15:16:27 -0500299 new AlertDialog.Builder(activity)
Michael Kolb8233fac2010-10-26 16:08:53 -0700300 .setTitle(title)
Björn Lundén2aa8ba22012-05-31 23:05:56 +0200301 .setIconAttribute(android.R.attr.alertDialogIcon)
Michael Kolb8233fac2010-10-26 16:08:53 -0700302 .setMessage(msg)
303 .setPositiveButton(R.string.ok, null)
304 .show();
305 return;
306 }
307
Michael Kolb8233fac2010-10-26 16:08:53 -0700308 if (mimetype == null) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700309 // We must have long pressed on a link or image to download it. We
310 // are not sure of the mimetype in this case, so do a head request
luxiaol62677b02013-07-22 07:54:49 +0800311 new FetchUrlMimeType(activity, url, userAgent, referer,
312 privateBrowsing, filename).start();
Michael Kolb8233fac2010-10-26 16:08:53 -0700313 } else {
luxiaol62677b02013-07-22 07:54:49 +0800314 startDownloadSettings(activity, url, userAgent, contentDisposition, mimetype, referer,
315 privateBrowsing, contentLength, filename);
Michael Kolb8233fac2010-10-26 16:08:53 -0700316 }
luxiaol62677b02013-07-22 07:54:49 +0800317
318 }
319
320 public static void initStorageDefaultPath(Context context) {
321 mExternalStorage = getExternalStorageDirectory(context);
322 if (isPhoneStorageSupported()) {
323 mInternalStorage = Environment.getExternalStorageDirectory().getPath();
324 } else {
325 mInternalStorage = null;
326 }
327 }
328
329 public static void startDownloadSettings(Activity activity,
330 String url, String userAgent, String contentDisposition,
331 String mimetype, String referer, boolean privateBrowsing, long contentLength,
332 String filename) {
333 Bundle fileInfo = new Bundle();
334 fileInfo.putString("url", url);
335 fileInfo.putString("userAgent", userAgent);
336 fileInfo.putString("contentDisposition", contentDisposition);
337 fileInfo.putString("mimetype", mimetype);
338 fileInfo.putString("referer", referer);
339 fileInfo.putLong("contentLength", contentLength);
340 fileInfo.putBoolean("privateBrowsing", privateBrowsing);
341 fileInfo.putString("filename", filename);
342 Intent intent = new Intent("android.intent.action.BROWSERDOWNLOAD");
343 intent.putExtras(fileInfo);
344 activity.startActivity(intent);
345 }
346
347 public static void setAppointedFolder(String downloadPath) {
348 File file = new File(downloadPath);
349 if (file.exists()) {
350 if (!file.isDirectory()) {
351 throw new IllegalStateException(file.getAbsolutePath() +
352 " already exists and is not a directory");
353 }
354 } else {
355 if (!file.mkdir()) {
356 throw new IllegalStateException("Unable to create directory: " +
357 file.getAbsolutePath());
358 }
359 }
360 }
361
362 private static void setDestinationDir(String downloadPath, String filename, Request request) {
363 File file = new File(downloadPath);
364 if (file.exists()) {
365 if (!file.isDirectory()) {
366 throw new IllegalStateException(file.getAbsolutePath() +
367 " already exists and is not a directory");
368 }
369 } else {
370 if (!file.mkdir()) {
371 throw new IllegalStateException("Unable to create directory: " +
372 file.getAbsolutePath());
373 }
374 }
375 setDestinationFromBase(file, filename, request);
376 }
377
378 private static void setDestinationFromBase(File file, String filename, Request request) {
379 if (filename == null) {
380 throw new NullPointerException("filename cannot be null");
381 }
382 request.setDestinationUri(Uri.withAppendedPath(Uri.fromFile(file), filename));
383 }
384
385 public static void fileExistQueryDialog(Activity activity) {
386 new AlertDialog.Builder(activity)
387 .setTitle(R.string.download_file_exist)
388 .setIcon(android.R.drawable.ic_dialog_info)
389 .setMessage(R.string.download_file_exist_msg)
390 // if yes, delete existed file and start new download thread
391 .setPositiveButton(R.string.ok, null)
392 // if no, do nothing at all
393 .show();
394 }
395
396 public static long getAvailableMemory(String root) {
397 StatFs stat = new StatFs(root);
398 final long LEFT10MByte = 2560;
399 long blockSize = stat.getBlockSize();
400 long availableBlocks = stat.getAvailableBlocks() - LEFT10MByte;
401 return availableBlocks * blockSize;
402 }
403
404 public static void showNoEnoughMemoryDialog(Activity mContext) {
405 new AlertDialog.Builder(mContext)
406 .setTitle(R.string.download_no_enough_memory)
407 .setIconAttribute(android.R.attr.alertDialogIcon)
408 .setMessage(R.string.download_no_enough_memory)
409 .setPositiveButton(R.string.ok, null)
410 .show();
411 }
412
kaiyize6a27d02013-08-22 15:08:19 +0800413 public static boolean manageNoEnoughMemory(long contentLength, String root) {
luxiaol62677b02013-07-22 07:54:49 +0800414 long mAvailableBytes = getAvailableMemory(root);
415 if (mAvailableBytes > 0) {
416 if (contentLength > mAvailableBytes) {
luxiaol62677b02013-07-22 07:54:49 +0800417 return true;
418 }
419 } else {
luxiaol62677b02013-07-22 07:54:49 +0800420 return true;
421 }
422 return false;
423 }
424
Panos Thomase57e3a02014-04-30 20:25:16 -0700425 public static void showStartDownloadToast(Activity activity,
426 boolean privateBrowsing) {
427 if (!privateBrowsing) {
428 Intent intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
429 activity.startActivity(intent);
430 } else {
431 activity.finish();
432 }
Leon Scroggins63c02662010-11-18 15:16:27 -0500433 Toast.makeText(activity, R.string.download_pending, Toast.LENGTH_SHORT)
Michael Kolb8233fac2010-10-26 16:08:53 -0700434 .show();
435 }
436
luxiaol62677b02013-07-22 07:54:49 +0800437 /**
438 * wheather the storage status OK for download file
439 *
440 * @param activity
441 * @param filename the download file's name
442 * @param downloadPath the download file's path will be in
443 * @return boolean true is ok,and false is not
444 */
445 public static boolean isStorageStatusOK(Activity activity, String filename, String downloadPath) {
446 if (downloadPath.equals(INVALID_PATH)) {
447 new AlertDialog.Builder(activity)
448 .setTitle(R.string.path_wrong)
449 .setIcon(android.R.drawable.ic_dialog_alert)
450 .setMessage(R.string.invalid_path)
451 .setPositiveButton(R.string.ok, null)
452 .show();
453 return false;
454 }
455
456 if (!(isPhoneStorageSupported() && downloadPath.contains(mInternalStorage))) {
457 String status = getExternalStorageState(activity);
458 if (!status.equals(Environment.MEDIA_MOUNTED)) {
459 int title;
460 String msg;
461
462 // Check to see if the SDCard is busy, same as the music app
463 if (status.equals(Environment.MEDIA_SHARED)) {
464 msg = activity.getString(R.string.download_sdcard_busy_dlg_msg);
465 title = R.string.download_sdcard_busy_dlg_title;
466 } else {
467 msg = activity.getString(R.string.download_no_sdcard_dlg_msg, filename);
468 title = R.string.download_no_sdcard_dlg_title;
469 }
470
471 new AlertDialog.Builder(activity)
472 .setTitle(title)
473 .setIcon(android.R.drawable.ic_dialog_alert)
474 .setMessage(msg)
475 .setPositiveButton(R.string.ok, null)
476 .show();
477 return false;
478 }
479 } else {
480 String status = Environment.getExternalStorageState();
481 if (!status.equals(Environment.MEDIA_MOUNTED)) {
482 int mTitle = R.string.download_path_unavailable_dlg_title;
483 String mMsg = activity.getString(R.string.download_path_unavailable_dlg_msg);
484 new AlertDialog.Builder(activity)
485 .setTitle(mTitle)
486 .setIcon(android.R.drawable.ic_dialog_alert)
487 .setMessage(mMsg)
488 .setPositiveButton(R.string.ok, null)
489 .show();
490 return false;
491 }
492 }
493 return true;
494 }
495
496 /**
497 * wheather support Phone Storage
498 *
499 * @return boolean true support Phone Storage ,false will be not
500 */
501 public static boolean isPhoneStorageSupported() {
502 return true;
503 }
504
505 /**
506 * show Dialog to warn filename is null
507 *
508 * @param activity
509 */
510 public static void showFilenameEmptyDialog(Activity activity) {
511 new AlertDialog.Builder(activity)
512 .setTitle(R.string.filename_empty_title)
513 .setIcon(android.R.drawable.ic_dialog_alert)
514 .setMessage(R.string.filename_empty_msg)
515 .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
516 public void onClick(DialogInterface dialog, int which) {
517 }
518 })
519 .show();
520 }
521
522 /**
523 * get the filename except the suffix and dot
524 *
525 * @return String the filename except suffix and dot
526 */
527 public static String getFilenameBase(String filename) {
528 int dotindex = filename.lastIndexOf('.');
529 if (dotindex != -1) {
530 return filename.substring(0, dotindex);
531 } else {
532 return "";
533 }
534 }
535
536 /**
537 * get the filename's extension from filename
538 *
539 * @param filename the download filename, may be the user entered
540 * @return String the filename's extension
541 */
542 public static String getFilenameExtension(String filename) {
543 int dotindex = filename.lastIndexOf('.');
544 if (dotindex != -1) {
545 return filename.substring(dotindex + 1);
546 } else {
547 return "";
548 }
549 }
550
551 public static String getDefaultDownloadPath(Context context) {
552 String defaultDownloadPath;
553
554 String defaultStorage;
555 if (isPhoneStorageSupported()) {
556 defaultStorage = Environment.getExternalStorageDirectory().getPath();
557 } else {
558 defaultStorage = getExternalStorageDirectory(context);
559 }
560
kaiyize6a27d02013-08-22 15:08:19 +0800561 defaultDownloadPath = defaultStorage + context.getString(R.string.download_default_path);
luxiaol62677b02013-07-22 07:54:49 +0800562 Log.e(LOGTAG, "defaultStorage directory is : " + defaultDownloadPath);
563 return defaultDownloadPath;
564 }
565
566 /**
567 * translate the directory name into a name which is easy to know for user
568 *
569 * @param activity
570 * @param downloadPath
571 * @return String
572 */
573 public static String getDownloadPathForUser(Activity activity, String downloadPath) {
574 if (downloadPath == null) {
575 return downloadPath;
576 }
577 final String phoneStorageDir;
578 final String sdCardDir = getExternalStorageDirectory(activity);
579 if (isPhoneStorageSupported()) {
580 phoneStorageDir = Environment.getExternalStorageDirectory().getPath();
581 } else {
582 phoneStorageDir = null;
583 }
584
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800585 if (sdCardDir != null && downloadPath.startsWith(sdCardDir)) {
luxiaol62677b02013-07-22 07:54:49 +0800586 String sdCardLabel = activity.getResources().getString(
587 R.string.download_path_sd_card_label);
588 downloadPath = downloadPath.replace(sdCardDir, sdCardLabel);
589 } else if ((phoneStorageDir != null) && downloadPath.startsWith(phoneStorageDir)) {
590 String phoneStorageLabel = activity.getResources().getString(
kaiyizf1a66762013-09-16 16:59:43 +0800591 R.string.download_path_phone_storage_label);
luxiaol62677b02013-07-22 07:54:49 +0800592 downloadPath = downloadPath.replace(phoneStorageDir, phoneStorageLabel);
593 }
594 return downloadPath;
595 }
596
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800597 private static boolean isRemovable(Object obj) {
598 return (Boolean) ReflectHelper.invokeMethod(obj,
599 "isRemovable", null, null);
600 }
601
602 private static boolean allowMassStorage(Object obj) {
603 return (Boolean) ReflectHelper.invokeMethod(obj,
604 "allowMassStorage", null, null);
605 }
606
607 private static String getPath(Object obj) {
608 return (String) ReflectHelper.invokeMethod(obj,
609 "getPath", null, null);
610 }
611
luxiaol62677b02013-07-22 07:54:49 +0800612 private static String getExternalStorageDirectory(Context context) {
613 String sd = null;
614 StorageManager mStorageManager = (StorageManager) context
615 .getSystemService(Context.STORAGE_SERVICE);
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800616 Object[] volumes = (Object[]) ReflectHelper.invokeMethod(
617 mStorageManager, "getVolumeList", null, null);
luxiaol62677b02013-07-22 07:54:49 +0800618 for (int i = 0; i < volumes.length; i++) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800619 if (isRemovable(volumes[i]) && allowMassStorage(volumes[i])) {
620 sd = getPath(volumes[i]);
Vivek Sekhar027ecad2014-04-15 05:42:38 -0700621 break;
luxiaol62677b02013-07-22 07:54:49 +0800622 }
623 }
624 return sd;
625 }
626
627 private static String getExternalStorageState(Context context) {
628 StorageManager mStorageManager = (StorageManager) context
629 .getSystemService(Context.STORAGE_SERVICE);
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800630 String path = getExternalStorageDirectory(context);
631 Object[] params = {path};
632 Class[] type = new Class[] {String.class};
Bijan Amirzada63a855a2014-03-26 13:57:22 -0700633 return (String) ReflectHelper.invokeMethod(mStorageManager,
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800634 "getVolumeState", type, params);
luxiaol62677b02013-07-22 07:54:49 +0800635 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700636}