Click here to Skip to main content
Click here to Skip to main content

Android: Ready to use simple directory chooser dialog with new folders creation

, 20 Feb 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
Android: Ready to use simple directory chooser dialog with new folders creation ability - single Java class implementation.

Introduction  

Every time you need to choose a certain SD card directory in your Android  application you need to load a kind of directory chooser dialog that will offer a graphical interface to select a required directory.   

Unfortunately Android does not provide any built in directory chooser dialog as is expected by Android developers. Thereby developers have to write their own directory chooser dialog. In this article I present an implementation of simple directory chooser dialog for Android SD card enhanced with ability to create directories. The implementation is contained in a single file and does not use any extra resources except for the pre-defined Android  resources.  This assumes very easy integration of the dialog in Android applications.     

The implementation code

The directory chooser dialog is based on the AlertDialog supplied with the ListView of sub-directories. The current directory path is displayed in the AlertDialog title. The navigation forth to a directory is accomplished by tapping a sub-directory item in the ListView and back  by pressing the back button. The sub-directories in the list are sorted by name. When required directory is chosen by pressing OK  button a registered callback is invoked supplied with full path of the chosen directory. Note that long current directory paths and sub-directories names are correctly displayed in multiline view.  

The code is contained in a single file DirectoryChooserDialog.java. It loads AlertDialog with ListView of sub-directories of a current directory and keeps track of directories navigation.

The implementation DirectoryChooserDialog class defines the following callback interface.

// Callback interface for selected directory
public interface ChosenDirectoryListener 
{    public void onChosenDir(String chosenDir);
} 

A callback can be registered in the DirectoryChooserDialog class constructor.

public DirectoryChooserDialog(Context context, ChosenDirectoryListener chosenDirectoryListener); 

By default the ability to create  new directories is enabled and can be applied by clicking on the New folder button. It can be switched off by means of setNewFolderEnabled method. When disabled the New folder button  gets hidden. 

///////////////////////////////////////////////////////////////////////
// setNewFolderEnabled() - enable/disable new folder button
///////////////////////////////////////////////////////////////////////

public void setNewFolderEnabled(boolean isNewFolderEnabled)
{
    m_isNewFolderEnabled = isNewFolderEnabled;
}

public boolean getNewFolderEnabled()
{
    return m_isNewFolderEnabled;
} 

The DirectoryChooserDialog class specifies two public chooseDirectory methods for loading directory chooser dialog, one without and another with an initial directory parameter. The default initial directory is SD card root directory.

//////////////////////////////////////////////////////////////////////
// chooseDirectory() - load directory chooser dialog for initial
// default sdcard root directory
//////////////////////////////////////////////////////////////////////

public void chooseDirectory();

////////////////////////////////////////////////////////////////////////////////
// chooseDirectory(String dir) - load directory chooser dialog for initial
// input 'dir' directory
////////////////////////////////////////////////////////////////////////////////

public void chooseDirectory(String dir); 

The DirectoryChooserDialog class full implementation is shown below.

// DirectoryChooserDialog.java

package com.example.directorychooser;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnKeyListener;
import android.os.Environment;
import android.text.Editable;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class DirectoryChooserDialog 
{
    private boolean m_isNewFolderEnabled = true;
    private String m_sdcardDirectory = "";
    private Context m_context;
    private TextView m_titleView;
  
    private String m_dir = "";
    private List<String> m_subdirs = null;
    private ChosenDirectoryListener m_chosenDirectoryListener = null;
    private ArrayAdapter<String> m_listAdapter = null;

    //////////////////////////////////////////////////////
    // Callback interface for selected directory
    //////////////////////////////////////////////////////
    public interface ChosenDirectoryListener 
    {
        public void onChosenDir(String chosenDir);
    }

    public DirectoryChooserDialog(Context context, ChosenDirectoryListener chosenDirectoryListener)
    {
        m_context = context;
        m_sdcardDirectory = Environment.getExternalStorageDirectory().getAbsolutePath();
        m_chosenDirectoryListener = chosenDirectoryListener;

        try
        {
            m_sdcardDirectory = new File(m_sdcardDirectory).getCanonicalPath();
        }
        catch (IOException ioe)
        {
        }
    }

    ///////////////////////////////////////////////////////////////////////
    // setNewFolderEnabled() - enable/disable new folder button
    ///////////////////////////////////////////////////////////////////////

    public void setNewFolderEnabled(boolean isNewFolderEnabled)
    {
        m_isNewFolderEnabled = isNewFolderEnabled;
    }

    public boolean getNewFolderEnabled()
    {
        return m_isNewFolderEnabled;
    }

    ///////////////////////////////////////////////////////////////////////
    // chooseDirectory() - load directory chooser dialog for initial
    // default sdcard directory
    ///////////////////////////////////////////////////////////////////////

    public void chooseDirectory()
    {
        // Initial directory is sdcard directory
        chooseDirectory(m_sdcardDirectory);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // chooseDirectory(String dir) - load directory chooser dialog for initial 
    // input 'dir' directory
    ////////////////////////////////////////////////////////////////////////////////

    public void chooseDirectory(String dir)
    {
        File dirFile = new File(dir);
        if (! dirFile.exists() || ! dirFile.isDirectory())
        {
            dir = m_sdcardDirectory;
        }

        try
        {
            dir = new File(dir).getCanonicalPath();
        }
        catch (IOException ioe)
        {
            return;
        }

        m_dir = dir;
        m_subdirs = getDirectories(dir);

        class DirectoryOnClickListener implements DialogInterface.OnClickListener
        {
            public void onClick(DialogInterface dialog, int item) 
            {
                // Navigate into the sub-directory
                m_dir += "/" + ((AlertDialog) dialog).getListView().getAdapter().getItem(item);
                updateDirectory();
            }
        }

    AlertDialog.Builder dialogBuilder = 
    createDirectoryChooserDialog(dir, m_subdirs, new DirectoryOnClickListener());

    dialogBuilder.setPositiveButton("OK", new OnClickListener() 
    {
        @Override
        public void onClick(DialogInterface dialog, int which) 
        {
            // Current directory chosen
            if (m_chosenDirectoryListener != null)
            {
                // Call registered listener supplied with the chosen directory
                m_chosenDirectoryListener.onChosenDir(m_dir);
            }
        }
    }).setNegativeButton("Cancel", null);

    final AlertDialog dirsDialog = dialogBuilder.create();

    dirsDialog.setOnKeyListener(new OnKeyListener() 
    {
        @Override
        public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) 
        {
            if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN)
            {
                // Back button pressed
                if ( m_dir.equals(m_sdcardDirectory) )
                {
                    // The very top level directory, do nothing
                    return false;
                }
                else
                {
                    // Navigate back to an upper directory
                    m_dir = new File(m_dir).getParent();
                    updateDirectory();
                }
    
                return true;
            }
            else
            {
                return false;
            }
        }
    });

    // Show directory chooser dialog
    dirsDialog.show();
}

private boolean createSubDir(String newDir)
{
    File newDirFile = new File(newDir);
    if (! newDirFile.exists() )
    {
        return newDirFile.mkdir();
    }

    return false;
}

private List<String> getDirectories(String dir)
{
    List<String> dirs = new ArrayList<String>();

    try
    {
        File dirFile = new File(dir);
        if (! dirFile.exists() || ! dirFile.isDirectory())
        {
            return dirs;
        }
 
        for (File file : dirFile.listFiles()) 
        {
            if ( file.isDirectory() )
            {
                dirs.add( file.getName() );
            }
        }
    }
    catch (Exception e)
    {
    }

    Collections.sort(dirs, new Comparator<String>()
    {
        public int compare(String o1, String o2) 
        {
            return o1.compareTo(o2);
        }
    });

    return dirs;
}

private AlertDialog.Builder createDirectoryChooserDialog(String title, List<String> listItems,
        DialogInterface.OnClickListener onClickListener)
{
    AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(m_context);

    // Create custom view for AlertDialog title containing 
    // current directory TextView and possible 'New folder' button.
    // Current directory TextView allows long directory path to be wrapped to multiple lines.
    LinearLayout titleLayout = new LinearLayout(m_context);
    titleLayout.setOrientation(LinearLayout.VERTICAL);

    m_titleView = new TextView(m_context);
    m_titleView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
    m_titleView.setTextAppearance(m_context, android.R.style.TextAppearance_Large);
    m_titleView.setTextColor( m_context.getResources().getColor(android.R.color.white) );
    m_titleView.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
    m_titleView.setText(title);

    Button newDirButton = new Button(m_context);
    newDirButton.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
    newDirButton.setText("New folder");
    newDirButton.setOnClickListener(new View.OnClickListener() 
    {
        @Override
        public void onClick(View v) 
        {
            final EditText input = new EditText(m_context);

            // Show new folder name input dialog
            new AlertDialog.Builder(m_context).
            setTitle("New folder name").
            setView(input).setPositiveButton("OK", new DialogInterface.OnClickListener() 
            {
                public void onClick(DialogInterface dialog, int whichButton) 
                {
                    Editable newDir = input.getText();
                    String newDirName = newDir.toString();
                    // Create new directory
                    if ( createSubDir(m_dir + "/" + newDirName) )
                    {
                        // Navigate into the new directory
                        m_dir += "/" + newDirName;
                        updateDirectory();
                    }
                    else
                    {
                        Toast.makeText(
                        m_context, "Failed to create '" + newDirName + 
                          "' folder", Toast.LENGTH_SHORT).show();
                    }
                }
            }).setNegativeButton("Cancel", null).show(); 
        }
    });

    if (! m_isNewFolderEnabled)
    {
        newDirButton.setVisibility(View.GONE);
    }

    titleLayout.addView(m_titleView);
    titleLayout.addView(newDirButton);

    dialogBuilder.setCustomTitle(titleLayout);

    m_listAdapter = createListAdapter(listItems);

    dialogBuilder.setSingleChoiceItems(m_listAdapter, -1, onClickListener);
    dialogBuilder.setCancelable(false);

    return dialogBuilder;
}

private void updateDirectory()
{
    m_subdirs.clear();
    m_subdirs.addAll( getDirectories(m_dir) );
    m_titleView.setText(m_dir);

    m_listAdapter.notifyDataSetChanged();
}

private ArrayAdapter<String> createListAdapter(List<String> items)
{
    return new ArrayAdapter<String>(m_context, 
      android.R.layout.select_dialog_item, android.R.id.text1, items)
    {
        @Override
        public View getView(int position, View convertView,
        ViewGroup parent) 
        {
            View v = super.getView(position, convertView, parent);

            if (v instanceof TextView)
            {
                // Enable list item (directory) text wrapping
                TextView tv = (TextView) v;
                tv.getLayoutParams().height = LayoutParams.WRAP_CONTENT;
                tv.setEllipsize(null);
            }
            return v;
        }
    };
}
} 

Usage example

The following example shows how to load directory chooser dialog on button click. The ability to create new directories is toggled between dialog appearances. The previous chosen directory turns into initial directory for the next dialog invocation.

// DirectoryChooserActivity.java

package com.example.directorychooser;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class DirectoryChooserActivity extends Activity 
{
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_directory_chooser);

        Button dirChooserButton = (Button) findViewById(R.id.chooseDirButton);
        dirChooserButton.setOnClickListener(new OnClickListener() 
        {
            private String m_chosenDir = "";
            private boolean m_newFolderEnabled = true;

            @Override
            public void onClick(View v) 
            {
                // Create DirectoryChooserDialog and register a callback 
                DirectoryChooserDialog directoryChooserDialog = 
                new DirectoryChooserDialog(DirectoryChooserActivity.this, 
                    new DirectoryChooserDialog.ChosenDirectoryListener() 
                {
                    @Override
                    public void onChosenDir(String chosenDir) 
                    {
                        m_chosenDir = chosenDir;
                        Toast.makeText(
                        DirectoryChooserActivity.this, "Chosen directory: " + 
                          chosenDir, Toast.LENGTH_LONG).show();
                    }
                }); 
                // Toggle new folder button enabling
                directoryChooserDialog.setNewFolderEnabled(m_newFolderEnabled);
                // Load directory chooser dialog for initial 'm_chosenDir' directory.
                // The registered callback will be called upon final directory selection.
                directoryChooserDialog.chooseDirectory(m_chosenDir);
                m_newFolderEnabled = ! m_newFolderEnabled;
            }
        });
    }
}

Conclusion   

This article presented an implementation of a directory chooser dialog enhanced with new directories creation Java class that can be used as is in Android applications. Enjoy!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Gregory Shpitalnik
Software Developer (Senior) Marvell
Israel Israel
17 years experience software engineer at Marvell company in Israel.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberFabio Durigon7-Oct-14 5:15 
QuestionMissing up one level Pinmemberburningthumb27-Sep-13 9:07 
AnswerRe: Missing up one level PinmemberGregory Shpitalnik28-Sep-13 0:37 
GeneralRe: Missing up one level PinmemberFabio Durigon10-Oct-14 5:54 
GeneralRe: Missing up one level PinmemberGophern Kwok14-Oct-14 17:17 
QuestionMy vote is 5, If OptionToChoseAFile = true then outstanding Pinmemberhackerspk11-Aug-13 21:51 
GeneralVery Nice Tutorial........................ Pinmemberjayashree shinde17-Jun-13 23:51 
Questiongreat work. Pinmembercucko30-May-13 23:03 
AnswerRe: great work. PinmemberGregory Shpitalnik28-Sep-13 0:42 
GeneralMy vote of 5 Pinmembercucko30-May-13 22:58 
Questionhow to change /mnt/sdcard text with "personal text" Pinmemberfarhankhan098723-Apr-13 3:28 
GeneralMy vote of 5 PinmemberMihai MOGA11-Mar-13 22:51 
GeneralRe: My vote of 5 PinmemberGregory Shpitalnik12-Mar-13 23:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.1411028.1 | Last Updated 20 Feb 2013
Article Copyright 2013 by Gregory Shpitalnik
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid