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






4.90/5 (20 votes)
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!