1

I am currently working on an app in which I display all sorts of different stats about the device it is installed on. One of these stats are used/total space of SDcards. I got it working with the answers found Here and Here.

The thing is, I believe anyhow, that without root permission, it will not return a "true" value of the Internal (emulated) SDcard. Here is some code:

How I retrieve Sd location(s) and size(s):

new StorageUtils();
    ArrayList<StorageInfo> storageInfoList = StorageUtils.getStorageList();

    if (storageInfoList.size() > 0) {
        tvStorageAName = (TextView) findViewById(R.id.tvStorageAName);
        tvStorageAName.setText(storageInfoList.get(0).path);

        devStorageA = StorageUtils.getReadableFileSize(
                (StorageUtils.getUsedSpace(storageInfoList.get(0).path)),
                true)
                + "/"
                + StorageUtils.getReadableFileSize((StorageUtils
                        .getTotalSpace(storageInfoList.get(0).path)), true);

        if (storageInfoList.size() > 1) {

            tvStorageBName = (TextView) findViewById(R.id.tvStorageBName);
            tvStorageBName.setText(storageInfoList.get(1).path);

            devStorageB = StorageUtils.getReadableFileSize(
                    StorageUtils.getUsedSpace(storageInfoList.get(1).path)
                            + (StorageUtils.getUsedSpace("system/")), true)
                    + "/"
                    + StorageUtils.getReadableFileSize((StorageUtils
                            .getTotalSpace(storageInfoList.get(1).path)),
                            true);
        } else {
            devStorageB = "N/A";
        }
    } else {
        devStorageA = "N/A";
        devStorageB = "N/A";
    }

My StorageUtils class:

public class StorageUtils {

private static final String TAG = "StorageUtils";

public static class StorageInfo {

    public final String path;
    public final boolean internal;
    public final boolean readonly;
    public final int display_number;

    StorageInfo(String path, boolean internal, boolean readonly,
            int display_number) {
        this.path = path;
        this.internal = internal;
        this.readonly = readonly;
        this.display_number = display_number;
    }
}

public static ArrayList<StorageInfo> getStorageList() {

    ArrayList<StorageInfo> list = new ArrayList<StorageInfo>();
    String def_path = Environment.getExternalStorageDirectory().getPath();
    boolean def_path_internal = !Environment.isExternalStorageRemovable();
    String def_path_state = Environment.getExternalStorageState();
    boolean def_path_available = def_path_state
            .equals(Environment.MEDIA_MOUNTED)
            || def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
    boolean def_path_readonly = Environment.getExternalStorageState()
            .equals(Environment.MEDIA_MOUNTED_READ_ONLY);
    BufferedReader buf_reader = null;
    try {
        HashSet<String> paths = new HashSet<String>();
        buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
        String line;
        int cur_display_number = 1;
        Log.d(TAG, "/proc/mounts");
        while ((line = buf_reader.readLine()) != null) {
            Log.d(TAG, line);
            if (line.contains("vfat") || line.contains("/mnt")) {
                StringTokenizer tokens = new StringTokenizer(line, " ");
                String unused = tokens.nextToken(); // device
                String mount_point = tokens.nextToken(); // mount point
                if (paths.contains(mount_point)) {
                    continue;
                }
                unused = tokens.nextToken(); // file system
                List<String> flags = Arrays.asList(tokens.nextToken()
                        .split(",")); // flags
                boolean readonly = flags.contains("ro");

                if (mount_point.equals(def_path)) {
                    paths.add(def_path);
                    list.add(new StorageInfo(def_path, def_path_internal,
                            readonly, -1));
                } else if (line.contains("/dev/block/vold")) {
                    if (!line.contains("/mnt/secure")
                            && !line.contains("/mnt/asec")
                            && !line.contains("/mnt/obb")
                            && !line.contains("/dev/mapper")
                            && !line.contains("tmpfs")) {
                        paths.add(mount_point);
                        list.add(new StorageInfo(mount_point, false,
                                readonly, cur_display_number++));
                    }
                }
            }
        }

        if (!paths.contains(def_path) && def_path_available) {
            list.add(new StorageInfo(def_path, def_path_internal,
                    def_path_readonly, -1));
        }

    } catch (FileNotFoundException ex) {
        ex.printStackTrace();
    } catch (IOException ex) {
        ex.printStackTrace();
    } finally {
        if (buf_reader != null) {
            try {
                buf_reader.close();
            } catch (IOException ex) {
            }
        }
    }
    return list;
}

public static String getReadableFileSize(long bytes, boolean si) {
    int unit = si ? 1000 : 1024;
    if (bytes < unit)
        return bytes + " B";
    int exp = (int) (Math.log(bytes) / Math.log(unit));
    String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1)
            + (si ? "" : "i");
    return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
}

@SuppressLint("NewApi")
public static long getFreeSpace(String path) {
    StatFs statFs = new StatFs(path);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        long sdAvailSize = statFs.getFreeBlocksLong()
                * statFs.getBlockSizeLong();
        return sdAvailSize;
    } else {
        @SuppressWarnings("deprecation")
        double sdAvailSize = (double) statFs.getFreeBlocks()
                * (double) statFs.getBlockSize();

        return (long) sdAvailSize;
    }
}

public static long getUsedSpace(String path) {
    return getTotalSpace(path) - getFreeSpace(path);
}

@SuppressLint("NewApi")
public static long getTotalSpace(String path) {
    StatFs statFs = new StatFs(path);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        long sdTotalSize = statFs.getBlockCountLong()
                * statFs.getBlockSizeLong();
        return sdTotalSize;
    } else {
        @SuppressWarnings("deprecation")
        double sdTotalSize = (double) statFs.getBlockCount()
                * statFs.getBlockSize();

        return (long) sdTotalSize;
    }
}

}

Question is I guess, Is this the most accurate way of getting these sizes (without root permissions), or is there a better way of doing this? Any and all help is greatly appreciated, thank you for taking the time to read this!

EDIT: external SDcards are not giving me a problem at all. Internal SDcards (also seems to be the data/ directory) is not showing the grand total. It lacks ~ 5Gb on my test device. Any ideas?

EDIT2: I had a friend test on their phone, and internal showed 36.0 GB (used)/ 8.6 GB (total), why would this return such sporadic results? How would an app, say a file-manager return results?

Community
  • 1
  • 1
MattMatt
  • 905
  • 6
  • 19
  • An emulated volume does not **have** a *true* "size" since there's a tradeoff between usage for that and usage of the same pool of blocks for other purposes. An artificial limit could be imposed by the emulation layer, but that wouldn't really be a true size either. – Chris Stratton Jan 23 '14 at 18:37
  • Thank you for your response. I had a friend test on their phone, and internal showed 36.0 GB (used)/ 8.6 GB (total). It would seem there would be a way to get a better result, any ideas? Thank you for your time. – MattMatt Jan 24 '14 at 00:29

1 Answers1

0

After a while of tinkering with it, I found my satisfaction.

Within my Fragment:

ArrayList<StorageInfo> storageInfoList = StorageUtils.getStorageList();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        kitKatWorkaround();
    } else if (storageInfoList.size() > 0) {

        tvStorageAName.setText(storageInfoList.get(0).path);

        long usedA = StorageUtils.getUsedSpace(storageInfoList.get(0).path);
        long totalA = StorageUtils
                .getTotalSpace(storageInfoList.get(0).path);

        devStorageA = StorageUtils.getReadableFileSize(usedA, true) + "/"
                + StorageUtils.getReadableFileSize(totalA, true);

        progA.setProgress(0);
        progA.setProgress(Integer.parseInt(StorageUtils.getReadableFileSize(usedA, true).replace(".", "").replace("k", "").replace("M", "").replace("G", "").replace("T", "").replace("P", "").replace("B", "")));
        progA.setMax(Integer.parseInt(StorageUtils.getReadableFileSize(totalA, true).replace(".", "").replace("k", "").replace("M", "").replace("G", "").replace("T", "").replace("P", "").replace("B", "")));

        Log.d("Storage", usedA + "/" + totalA);

        if (storageInfoList.size() > 1) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
                    && !storageInfoList.get(0).internal) {
                kitKatWorkaround();
            }
            tvStorageBName.setText(storageInfoList.get(1).path);

            long usedB = StorageUtils
                    .getUsedSpace(storageInfoList.get(1).path)
                    + (StorageUtils.getUsedSpace("system/"));
            long totalB = StorageUtils
                    .getTotalSpace(storageInfoList.get(1).path);

            devStorageB = StorageUtils.getReadableFileSize(usedB, true)
                    + "/" + StorageUtils.getReadableFileSize(totalB, true);

            progB.setProgress(0);
            progB.setProgress(Integer.parseInt(StorageUtils.getReadableFileSize(usedB, true).replace(".", "").replace("k", "").replace("M", "").replace("G", "").replace("T", "").replace("P", "").replace("B", "").trim()));
            progB.setMax(Integer.parseInt(StorageUtils.getReadableFileSize(totalB, true).replace(".", "").replace("k", "").replace("M", "").replace("G", "").replace("T", "").replace("P", "").replace("B", "").trim()));

            Log.d("Storage", usedB + "/" + totalB);
        } else {

            tvStorageBName.setVisibility(View.GONE);
            tvStorageB.setVisibility(View.GONE);
            progB.setVisibility(View.GONE);
            devStorageB = "N/A";
        }
    } else {
        devStorageA = "N/A";

        tvStorageBName.setVisibility(View.GONE);
        tvStorageB.setVisibility(View.GONE);
        progB.setVisibility(View.GONE);
        devStorageB = "N/A";
    }
    //
    // #################################

    // STORAGE
    tvStorageA.setText(devStorageA);

    tvStorageB.setText(devStorageB);
    //
    // #################################
}

and revised StorageUtils methods:

public static String getReadableFileSize(long bytes, boolean si) {
    int unit = si ? 1000 : 1024;
    if (bytes < unit)
        return bytes + " B";
    int exp = (int) (Math.log(bytes) / Math.log(unit));
    String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1)
            + (si ? "" : "i");
    return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
}

@SuppressLint("NewApi")
public static long getFreeSpace(String path) {
    StatFs statFs = new StatFs(path);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        long sdAvailSize = statFs.getFreeBlocksLong()
                * statFs.getBlockSizeLong();
        return sdAvailSize;
    } else {
        @SuppressWarnings("deprecation")
        double sdAvailSize = (double) statFs.getFreeBlocks()
                * (double) statFs.getBlockSize();

        return (long) sdAvailSize;
    }
}

public static long getUsedSpace(String path) {
    return getTotalSpace(path) - getFreeSpace(path);
}

@SuppressLint("NewApi")
public static long getTotalSpace(String path) {
    StatFs statFs = new StatFs(path);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        long sdTotalSize = statFs.getBlockCountLong()
                * statFs.getBlockSizeLong();
        return sdTotalSize;
    } else {
        @SuppressWarnings("deprecation")
        double sdTotalSize = (double) statFs.getBlockCount()
                * statFs.getBlockSize();

        return (long) sdTotalSize;
    }
}

EDIT Also the KitKatWorkaround method for those who are looking:

@SuppressLint("NewApi")
public void kitKatWorkaround() {

    File[] sdCards = getActivity().getApplicationContext()
            .getExternalCacheDirs();

    if (sdCards.length > 0
            && sdCards[0] != null
            && Environment.getStorageState(sdCards[0]).equals(
            Environment.MEDIA_MOUNTED)) {

        File sdCard1 = sdCards[0];

        tvStorageAName.setText(sdCard1.getAbsolutePath()
                .replace(
                        "Android/data/" + getActivity().getPackageName()
                                + "/cache", ""));

        long usedA = StorageUtils.getUsedSpace(sdCard1.getAbsolutePath());
        long totalA = StorageUtils.getTotalSpace(sdCard1.getAbsolutePath());

        devStorageA = StorageUtils.getReadableFileSize(usedA, true) + "/"
                + StorageUtils.getReadableFileSize(totalA, true);

        progA.setProgress(0);
        progA.setProgress(Integer.parseInt(StorageUtils.getReadableFileSize(usedA, true).replace(".", "").replace("k", "").replace("M", "").replace("G", "").replace("T", "").replace("P", "").replace("B", "").trim()));
        progA.setMax(Integer.parseInt(StorageUtils.getReadableFileSize(totalA, true).replace(".", "").replace("k", "").replace("M", "").replace("G", "").replace("T", "").replace("P", "").replace("B", "").trim()));

        Log.d("Storage", usedA + "/" + totalA);
    } else {
        devStorageA = "N/A";
    }

    if (sdCards.length > 1
            && sdCards[1] != null
            && Environment.getStorageState(sdCards[1]).equals(
            Environment.MEDIA_MOUNTED)) {
        File sdCard2 = sdCards[1];

        tvStorageBName.setText(sdCard2.getAbsolutePath()
                .replace(
                        "Android/data/" + getActivity().getPackageName()
                                + "/cache", ""));

        long usedB = StorageUtils.getUsedSpace(sdCard2.getAbsolutePath());
        long totalB = StorageUtils.getTotalSpace(sdCard2.getAbsolutePath());

        devStorageB = StorageUtils.getReadableFileSize(usedB, true) + "/"
                + StorageUtils.getReadableFileSize(totalB, true);
        progB.setProgress(0);
        progB.setProgress(Integer.parseInt(StorageUtils.getReadableFileSize(totalB, true).replace(".", "").replace("k", "").replace("M", "").replace("G", "").replace("T", "").replace("P", "").replace("B", "").trim()));
        progB.setMax(Integer.parseInt(StorageUtils.getReadableFileSize(totalB, true).replace(".", "").replace("k", "").replace("M", "").replace("G", "").replace("T", "").replace("P", "").replace("B", "").trim()));

        Log.d("Storage", usedB + "/" + totalB);

    } else {
        tvStorageBName.setVisibility(View.GONE);
        tvStorageB.setVisibility(View.GONE);
        progB.setVisibility(View.GONE);
        devStorageB = "N/A";
    }

    tvStorageA.setText(devStorageA);
    tvStorageB.setText(devStorageB);
}
MattMatt
  • 905
  • 6
  • 19