1

I'm trying to make a Fragment that contains a folder browser which shows only folders where there is at least one song. I've tried to follow several tutorials and to use a Filefilter, but I still see folders that don't contain anything that's useful (eg. Facebook folder), how can I do it? In other words, I'm trying to make a folder browser like this; could anyone help me?

Code: FolderFragment.java

public class FolderFragment extends Fragment {
    private File file;
    private List<String> myList;
    private FolderAdapter mAdapter;
    private Context mContext;
    private LayoutInflater mInflater;
    private ViewGroup mContainer;
    private LinearLayoutManager mLayoutManager;
    View mRootView;
    private RecyclerView mRecyclerView;
    String root_files;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        mContext = container.getContext();
        mInflater = inflater;
        mContainer = container;

        mRootView = inflater.inflate(R.layout.fragment_folders, mContainer, false);
        mRecyclerView = (RecyclerView) mRootView.findViewById(R.id.recycler_view_folders);
        mLayoutManager = new LinearLayoutManager(mContext);
        mRecyclerView.setLayoutManager(mLayoutManager);
        if(getActivity() != null)
            new loadFolders().execute("");
        return mRootView;
    }

    private class loadFolders extends AsyncTask<String, Void, String>{

        @Override
        protected String doInBackground(String... params) {
            Activity activity = getActivity();
            if (activity != null) {

                mAdapter = new FolderAdapter(activity, new File("/storage"));
            }
            return "Executed";
        }

        @Override
        protected void onPostExecute(String result){
            mRecyclerView.setAdapter(mAdapter);
            mAdapter.notifyDataSetChanged();
        }
    }
}

FolderAdapter.java

public class FolderAdapter extends RecyclerView.Adapter<FolderAdapter.ItemHolder> implements BubbleTextGetter {
    private List<File> mFileSet;
    private List<Song> mSongs;
    private File mRoot;
    private Activity mContext;
    private boolean mBusy = false;

    public FolderAdapter(Activity context, File root){
        mContext = context;
        mSongs = new ArrayList<>();
        updateDataSet(root);
    }

    @Override
    public FolderAdapter.ItemHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_folder_list, viewGroup, false);
        return new ItemHolder(v);
    }

    @Override
    public void onBindViewHolder(final FolderAdapter.ItemHolder itemHolder, int i) {
        File localItem = mFileSet.get(i);
        Song song = mSongs.get(i);
        itemHolder.title.setText(localItem.getName());
        if (localItem.isDirectory()) {
            itemHolder.albumArt.setImageResource("..".equals(localItem.getName()) ? R.drawable.icon_4 : R.drawable.icon_5);
        } else {
            itemHolder.albumArt.setImageResource(R.drawable.icon_folder);
        }
    }

    @Override
    public int getItemCount(){
        Log.d("size fileset: ", ""+mFileSet.size());
        return mFileSet.size();
    }

    @Deprecated
    public void updateDataSet(File newRoot){
        if(mBusy) return;
        if("..".equals(newRoot.getName())){
            goUp();
            return;
        }
        mRoot = newRoot;
        mFileSet = FolderLoader.getMediaFiles(newRoot, true);
        getSongsForFiles(mFileSet);
    }

    @Deprecated
    public boolean goUp(){
        if(mRoot == null || mBusy){
            return false;
        }
        File parent = mRoot.getParentFile();
        if(parent != null && parent.canRead()){
            updateDataSet(parent);
            return true;
        } else {
            return false;
        }
    }

    public boolean goUpAsync(){
        if(mRoot == null || mBusy){
            return false;
        }
        File parent = mRoot.getParentFile();
        if(parent != null && parent.canRead()){
            return updateDataSetAsync(parent);
        } else {
            return false;
        }
    }

    public boolean updateDataSetAsync(File newRoot){
        if(mBusy){
            return false;
        }
        if("..".equals(newRoot.getName())){
            goUpAsync();
            return false;
        }
        mRoot = newRoot;
        new NavigateTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mRoot);
        return true;
    }

    @Override
    public String getTextToShowInBubble(int pos){
        if(mBusy || mFileSet.size() == 0) return "";
        try{
            File f = mFileSet.get(pos);
            if(f.isDirectory()){
                return String.valueOf(f.getName().charAt(0));
            } else {
                return Character.toString(f.getName().charAt(0));
            }
        } catch(Exception e){
            return "";
        }
    }

    private void getSongsForFiles(List<File> files){
        mSongs.clear();
        for(File file : files){
            mSongs.add(SongLoader.getSongFromPath(file.getAbsolutePath(), mContext));
        }
    }

    private class NavigateTask extends AsyncTask<File, Void, List<File>>{

        @Override
        protected void onPreExecute(){
            super.onPreExecute();
            mBusy = true;
        }

        @Override
        protected List<File> doInBackground(File... params){
            List<File> files = FolderLoader.getMediaFiles(params[0], true);
            getSongsForFiles(files);
            return files;
        }

        @Override
        protected void onPostExecute(List<File> files){
            super.onPostExecute(files);
            mFileSet = files;
            notifyDataSetChanged();
            mBusy = false;
            //PreferencesUtility.getInstance(mContext).storeLastFolder(mRoot.getPath());

        }
    }

    public class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        protected TextView title;
        protected ImageView albumArt;

        public ItemHolder(View view) {
            super(view);
            this.title = (TextView) view.findViewById(R.id.folder_title);
            this.albumArt = (ImageView) view.findViewById(R.id.folder_album_art);
            view.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            if (mBusy) {
                return;
            }
            final File f = mFileSet.get(getAdapterPosition());

            if (f.isDirectory() && updateDataSetAsync(f)) {
                albumArt.setImageResource(R.drawable.ic_menu_send);
            } else if (f.isFile()) {

                final Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(mContext, "", Toast.LENGTH_LONG).show();
                    }
                }, 100);
            }
        }
    }
}

FolderLoader.java

public class FolderLoader {

    private static final String[] SUPPORTED_EXT = new String[] {
            "mp3",
            "m4a",
            "aac",
            "flac",
            "wav"
    };

    public static List<File> getMediaFiles(File dir, final boolean acceptDirs) {
        ArrayList<File> list = new ArrayList<>();
        list.add(new File(dir, "/storage"));
        if (dir.isDirectory()) {
            List<File> files = Arrays.asList(dir.listFiles(new FileFilter() {

                @Override
                public boolean accept(File file) {
                    if (file.isFile()) {
                        String name = file.getName();
                        return !".nomedia".equals(name) && checkFileExt(name);
                    } else if (file.isDirectory()) {
                        return acceptDirs && checkDir(file);
                    } else
                        return false;
                }
            }));
            Collections.sort(files, new FilenameComparator());
            Collections.sort(files, new DirFirstComparator());
            list.addAll(files);
        }

        return list;
    }

    public static boolean isMediaFile(File file) {
        return file.exists() && file.canRead() && checkFileExt(file.getName());
    }

    private static boolean checkDir(File dir) {
        return dir.exists() && dir.canRead() && !".".equals(dir.getName()) && dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                String name = pathname.getName();
                return !".".equals(name) && !"..".equals(name) && pathname.canRead() && (pathname.isDirectory()  || (pathname.isFile() && checkFileExt(name)));
            }

        }).length != 0;
    }

    private static boolean checkFileExt(String name) {
        if (TextUtils.isEmpty(name)) {
            return false;
        }
        int p = name.lastIndexOf(".") + 1;
        if (p < 1) {
            return false;
        }
        String ext = name.substring(p).toLowerCase();
        for (String o : SUPPORTED_EXT) {
            if (o.equals(ext)) {
                return true;
            }
        }
        return false;
    }

    private static class FilenameComparator implements Comparator<File> {
        @Override
        public int compare(File f1, File f2) {
            return f1.getName().compareTo(f2.getName());
        }
    }

    private static class DirFirstComparator implements Comparator<File> {
        @Override
        public int compare(File f1, File f2) {
            if (f1.isDirectory() == f2.isDirectory())
                return 0;
            else if (f1.isDirectory() && !f2.isDirectory())
                return -1;
            else
                return 1;
        }
    }
}
Sabatino
  • 123
  • 3
  • 16

2 Answers2

2

A much simpler approach would be if you use ContentProviders

You can list down all the music files or any file as such(You can even provide sorting or more filters to narrow down your list). It is just like using a database.

I can not post the full code right now. But I can give you the idea of how to work out this problem and this approach would be more readable and concise.

Here what you need to do.

1) Create an instance of Cursor.
2) Iterate Cursor to get your media files

Creating an instance

Cursor cursor = getContentResolver().query(URI uri ,String[] projection, null,null,null);

Let's focus on the first two parameters

a) URI - It could be either Internal or External Storage.
For External Use - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
For External Use - MediaStore.Audio.Media.INTERAL_CONTENT_URI;

b) Projection is an array which you use to get metadata of your file such as PATH, ARTIST NAME, NO OF SONGS and many more

String[] proj = new String[]{MediaStore.Audio.Media.DISPLAY_NAME,MediaStore.Audio.Media.DATA};

Now you have created Cursor. So the next part is to iterate through it.

while (cursor.moveToNext())
  {
     name = cursor.getString(cursor.getColumnIndex(modelConst.get_Proj()[0]));
     path =  cursor.getString(cursor.getColumnIndex(modelConst.get_Proj()[1]));
  }
Rohit Singh
  • 16,950
  • 7
  • 90
  • 88
  • I tried to do this as you said but I get this error: java.lang.NullPointerException: `Attempt to write to field 'int android.support.v7.widget.RecyclerView$ViewHolder.mItemViewType' on a null object reference` (without specifing where is this error but if I put my cursor between comments the code works fine). Here's my new code: https://pastebin.com/6trkmaQu – Sabatino Aug 16 '17 at 15:40
  • @Sabatino I checked your code (the pastebin) in my local machine without RecyclerView Adapter. I t works fine . You seem to have problem with RecyclerView Adapter. – Rohit Singh Aug 16 '17 at 16:32
  • okay I found where my app crashes and works fine (thank you so much), even though I wanted to group all songs by folder (instead of showing them all and only add their path), how can I manage this? I was thinking about a control inside my Adapter that groups all songs with the same path and if I click on an Item it shows me all the songs inside that folder, what do you think? – Sabatino Aug 16 '17 at 17:04
  • @Sabatino im not sure if there is any provider in android sdk which will provide you the folders. But if you want to get parent folder of a file this link might help you https://stackoverflow.com/questions/8197049/how-to-get-just-the-parent-directory-name-of-a-specific-file . I'm not sure though . Let me know if if you get any solution . – Rohit Singh Aug 16 '17 at 18:17
0

Following code may help you. Be more specific, show some codes you have used if following doesn't work or it is not what you want.

Intent intent;
            intent = new Intent();
            intent.setAction(Intent.ACTION_GET_CONTENT);
            intent.setType("audio/*");
            startActivityForResult(Intent.createChooser(intent, "Title"), 89);

on activty result method use following

 if (requestCode == 89 && resultCode == Activity.RESULT_OK){
        if ((data != null) && (data.getData() != null)){
            Uri audioFileUri = data.getData();
           // use uri to get path

            String path= audioFileUri.getPath();
        }
    }

Make sure given external storage read permission in manifest and run time permission check for latest sdk.

SARATH V
  • 500
  • 1
  • 7
  • 33