diff --git a/Library/src/com/matsuhiro/android/download/DownloadTask.java b/Library/src/com/matsuhiro/android/download/DownloadTask.java index 52633b5..98aa6c5 100644 --- a/Library/src/com/matsuhiro/android/download/DownloadTask.java +++ b/Library/src/com/matsuhiro/android/download/DownloadTask.java @@ -310,13 +310,15 @@ private long download() throws NetworkErrorException, IOException, /* * check memory */ - long storage = StorageUtils.getAvailableStorage(); + String mFileStorage = StorageUtils.findStoragePathForGivenFile(mFile); + long storageSpace = StorageUtils.getAvailableStorage(mFileStorage); + if (DEBUG) { - Log.i(null, "storage:" + storage + " totalSize:" + length); + Log.v(TAG, "storageSpace: " + storageSpace + "; total file size: " + length); } - if (length - mPreviousFileSize > storage) { - throw new NoMemoryException("SD card no memory."); + if (length - mPreviousFileSize > storageSpace) { + throw new NoMemoryException("Not enough free space on destination storage."); } RandomAccessFile outputStream = new ProgressReportingRandomAccessFile(mTempFile, "rw"); diff --git a/Library/src/com/matsuhiro/android/storage/StorageUtils.java b/Library/src/com/matsuhiro/android/storage/StorageUtils.java index 33034fe..dace761 100644 --- a/Library/src/com/matsuhiro/android/storage/StorageUtils.java +++ b/Library/src/com/matsuhiro/android/storage/StorageUtils.java @@ -1,15 +1,27 @@ package com.matsuhiro.android.storage; +import java.io.File; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.matsuhiro.android.download.DownloadTask; + +import android.os.Build; import android.os.Environment; import android.os.StatFs; +import android.text.TextUtils; +import android.util.Log; public class StorageUtils { + + private static final String TAG = DownloadTask.class.getSimpleName(); - public static long getAvailableStorage() { - - String storageDirectory = null; - storageDirectory = Environment.getExternalStorageDirectory().toString(); + @SuppressWarnings("deprecation") + public static long getAvailableStorage(String storageDirectory) { try { StatFs stat = new StatFs(storageDirectory); @@ -19,5 +31,126 @@ public static long getAvailableStorage() { return 0; } } + + /* method getStorageDirectories() from Stack Overflow: + * + * http://stackoverflow.com/a/18871043/1865860 + * + * Q: http://stackoverflow.com/users/290163/romulus-urakagi-tsai + * A: http://stackoverflow.com/users/2791190/dmitriy-lozenko + */ + private static final Pattern DIR_SEP = Pattern.compile("/"); + /** + * Raturns all available SD-Cards in the system (include emulated) + * + * Warning: Hack! Based on Android source code of version 4.3 (API 18) + * Because there is no standart way to get it. + * TODO: Test on future Android versions 4.4+ + * + * @return paths to all available SD-Cards in the system (include emulated) + */ + public static String[] getStorageDirectories() { + // Final set of paths + final Set rv = new HashSet(); + // Primary physical SD-CARD (not emulated) + final String rawExternalStorage = System.getenv("EXTERNAL_STORAGE"); + // All Secondary SD-CARDs (all exclude primary) separated by ":" + final String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE"); + // Primary emulated SD-CARD + final String rawEmulatedStorageTarget = System.getenv("EMULATED_STORAGE_TARGET"); + if(TextUtils.isEmpty(rawEmulatedStorageTarget)) { + // Device has physical external storage; use plain paths. + if(TextUtils.isEmpty(rawExternalStorage)) { + // EXTERNAL_STORAGE undefined; falling back to default. + rv.add("/storage/sdcard0"); + } else { + rv.add(rawExternalStorage); + } + } else { + // Device has emulated storage; external storage paths should have + // userId burned into them. + final String rawUserId; + if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + rawUserId = ""; + } else { + final String path = Environment.getExternalStorageDirectory().getAbsolutePath(); + final String[] folders = DIR_SEP.split(path); + final String lastFolder = folders[folders.length - 1]; + boolean isDigit = false; + try { + Integer.valueOf(lastFolder); + isDigit = true; + } catch(NumberFormatException ignored) { + // + } + rawUserId = isDigit ? lastFolder : ""; + } + // /storage/emulated/0[1,2,...] + if(TextUtils.isEmpty(rawUserId)) { + rv.add(rawEmulatedStorageTarget); + } else { + rv.add(rawEmulatedStorageTarget + File.separator + rawUserId); + } + } + // Add all secondary storages + if(!TextUtils.isEmpty(rawSecondaryStoragesStr)) { + // All Secondary SD-CARDs splited into array + final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator); + Collections.addAll(rv, rawSecondaryStorages); + } + return rv.toArray(new String[rv.size()]); + } + + private static boolean matchFound(String text, String regEx) { + Pattern pattern = Pattern.compile(regEx); + Matcher matcher = pattern.matcher(text); + if (matcher.find()) return true; + return false; + } + + public static String[] getAlternateStorageDirectories() { + return new String[] { + "/mnt/sdcard", + "/emmc", + "/mnt/sdcard/external_sd", + "/mnt/external_sd", + "/sdcard/sd", + "/mnt/sdcard/bpemmctest", + "/mnt/sdcard/_ExternalSD", + "/mnt/sdcard-ext", + "/mnt/Removable/MicroSD", + "/Removable/MicroSD", + "/mnt/external1", + "/mnt/extSdCard", + "/mnt/extsd", + "/mnt/usb_storage", + "/extSdCard", + "/mnt/extSdCard", + "/mnt/UsbDriveA", + "/mnt/UsbDriveB", }; + } + + public static String findStoragePathForGivenFile(File file) { + + String path = file.getAbsolutePath(); + + String[] storages = getStorageDirectories(); + for (int i = 0; i < storages.length; i++) { + if (matchFound(path, storages[i])) { + Log.d(TAG, "storageInUse: " + storages[i]); + return storages[i]; + } + } + + String[] alt_storages = getAlternateStorageDirectories(); + for (int i = 0; i < alt_storages.length; i++) { + if (matchFound(path, alt_storages[i])) { + Log.d(TAG, "storageInUse: " + alt_storages[i]); + return alt_storages[i]; + } + } + + return Environment.getExternalStorageDirectory().getPath(); + } }