diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/FolderPickerAdapter.java b/app/src/main/java/ch/blinkenlights/android/vanilla/FolderPickerAdapter.java index 2992e8ce..2b946149 100644 --- a/app/src/main/java/ch/blinkenlights/android/vanilla/FolderPickerAdapter.java +++ b/app/src/main/java/ch/blinkenlights/android/vanilla/FolderPickerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2017 Adrian Ulrich + * Copyright (C) 2013-2021 Adrian Ulrich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,6 +32,11 @@ import android.graphics.drawable.Drawable; import java.io.File; import java.util.Arrays; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +import java.nio.file.Path; public class FolderPickerAdapter extends ArrayAdapter @@ -48,14 +53,14 @@ public class FolderPickerAdapter } } + /** + * The context we run in. + */ + private Context mContext; /** * Our layout inflater instance */ private final LayoutInflater mInflater; - /** - * The external storage directory as reported by the OS - */ - final private File mStorageDir; /** * The filesystem root */ @@ -76,9 +81,9 @@ public class FolderPickerAdapter public FolderPickerAdapter(Context context, int resource) { super(context, resource); + mContext = context; mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mStorageDir = Environment.getExternalStorageDirectory(); - mCurrentDir = mStorageDir; + mCurrentDir = Environment.getExternalStorageDirectory(); } @Override @@ -176,35 +181,52 @@ public class FolderPickerAdapter */ private void refresh() { File path = mCurrentDir; - File[]dirs = path.listFiles(); clear(); if (!mFsRoot.equals(path)) add(new FolderPickerAdapter.Item("..", null, 0)); - // Hack alert: Android >= 6.0's default storage root directory - // is usually not readable. That's not a big issue but - // can be very annoying for users who browse around. - // We are therefore detecting this and will 'simulate' - // the existence of the default storage root. - if (dirs == null && mStorageDir.getParentFile().equals(path)) { - dirs = new File[] { mStorageDir }; + List dirs; + File []l = path.listFiles(); + if (l != null) { + dirs = Arrays.asList(l); + } else { + dirs = getFallbackDirectories(path); } - if (dirs != null) { - Arrays.sort(dirs); - for(File fentry: dirs) { - if(fentry.isDirectory()) { - int color = 0; - if (mIncludedDirs.contains(fentry.getAbsolutePath())) - color = 0xff00c853; - if (mExcludedDirs.contains(fentry.getAbsolutePath())) - color = 0xffd50000; - Item item = new Item(fentry.getName(), fentry, color); - add(item); - } + Collections.sort(dirs); + for(File fentry: dirs) { + if(fentry.isDirectory()) { + int color = 0; + if (mIncludedDirs.contains(fentry.getAbsolutePath())) + color = 0xff00c853; + if (mExcludedDirs.contains(fentry.getAbsolutePath())) + color = 0xffd50000; + Item item = new Item(fentry.getName(), fentry, color); + add(item); } } } + + /** + * Returns a list of directores contained in 'dir' which are very likely to exist, based + * on what Android told us about the existence of external media dirs. + * This is required as users otherwise may end up in folders they can not navigate out of. + */ + private List getFallbackDirectories(File dir) { + HashSet result = new HashSet<>(); + + Path prefix = dir.toPath(); + for (File f : mContext.getExternalMediaDirs()) { + Path p = f.toPath(); + if (p.getNameCount() <= prefix.getNameCount()) + continue; + if (!p.startsWith(prefix)) + continue; + Path sp = p.subpath(prefix.getNameCount(), prefix.getNameCount()+1); + result.add(new File(dir, sp.toString())); + } + return new ArrayList(result); + } }