From 1cb700c3c24abbf8724f78cbaca0ed9bdcca0128 Mon Sep 17 00:00:00 2001 From: Adrian Ulrich Date: Sun, 28 Mar 2021 16:52:32 +0200 Subject: [PATCH] improve file picker to include media dir paths. Android will not allow us to readdir() on any path, but we can guess the existence of paths based on the return value of `getExternalMediaDirs`. --- .../android/vanilla/FolderPickerAdapter.java | 76 ++++++++++++------- 1 file changed, 49 insertions(+), 27 deletions(-) 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); + } }