How to add a Directory Chooser to your Android application

Hi, today I am going to teach you how to add a Directory Chooser to your Android application. There will be one listing on this page. The listing implements a Directory Chooser within a class inherited from ListActivity.

N.B. To use this code within your application you will need to make sure the following permission is set in your Android Manifest:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Listing 1, Implementation of a File Chooser as a class inherited from ListActivity.

package com.paad.FileZip;

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

import android.app.ListActivity;
import android.content.Intent;
import android.database.sqlite.SQLiteException;
import android.os.Bundle;
import android.os.Environment;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

import com.paad.FileZip.R;

public class CompressionDirectoryChooser extends ListActivity {

 private final static int ACTIVITY_RESULTCODE_INPUTNOTRECEIVED = 0;
 private final static int ACTIVITY_RESULTCODE_INPUTRECEIVED = 1;

 String action;
 boolean export;

 ArrayList<String> filesanddirectories = new ArrayList<String>();
 ArrayList<String> directorypath = new ArrayList<String>();

 //DBAdapter db;

 private static final int ACTIVITY_CREATENEWEXPORTFILE = 0;
 private static final int ACTIVITY_RESULTCODE_CREATENEWEXPORTFILE = 0;


    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      setTitle("Compression Directory File Chooser");
     
      // ------------------------------------------------
     
      filesanddirectories.clear();
      directorypath.clear();
     
      filesanddirectories = getFilesAndDirectories();
     
      setListAdapter((ListAdapter) new ArrayAdapter<String>(this, R.layout.filelist_item, filesanddirectories));

      ListView lv = getListView();
      lv.setTextFilterEnabled(true);
      lv.setOnItemClickListener(lvOnItemClickListener);
    }
      
    @Override
    public synchronized void onResume() {
        super.onResume();
   
        System.gc();
    }
   
    @Override
    public synchronized void onDestroy() {
        super.onDestroy();
   
        //RubberTreeImageView.getDrawingCache().recycle();
      
        unbindDrawables(this.getListView());
       
        System.runFinalization();
        Runtime.getRuntime().gc();
       
        System.gc();
       
        //db.close();
    }
   
    private void unbindDrawables(View view) {
        if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
        }
        if ((view instanceof ViewGroup)&& !(view instanceof AdapterView)) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
            }
        ((ViewGroup) view).removeAllViews();
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event)  {
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR
                && keyCode == KeyEvent.KEYCODE_BACK
                && event.getRepeatCount() == 0) {
            // Take care of calling this method on earlier versions of
            // the platform where it doesn't exist.
            onBackPressed();
        }

        return super.onKeyDown(keyCode, event);
    }

    @Override
    public void onBackPressed() {
        // This will be called either automatically for you on 2.0
        // or later, or by the code above on earlier versions of the
        // platform.
   
     // BACK KEY pressed code goes here.
   
     cancelled();
   
     // --------------------------------
   
        return;
    }

    private void cancelled()
    {
     setResult(ACTIVITY_RESULTCODE_INPUTNOTRECEIVED);
     finish();
    }
   
    OnItemClickListener lvOnItemClickListener = new OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View view,
            int position, long id) {
          // When clicked, show a toast with the TextView text
       
          int pos;
       
          TextView tv = (TextView) view;
          String item = tv.getText().toString();
         
          if((item.indexOf("..") >= 0) && (item.length() == 2))
          {
           // item is a custom option that when selected the screen is updated with
           // a list of all the files and directories in the directory above the
           // current one
          
           directorypath.remove(directorypath.size() - 1);
           filesanddirectories = getFilesAndDirectories();
           CompressionDirectoryChooser.this.setListAdapter((ListAdapter) new ArrayAdapter<String>(CompressionDirectoryChooser.this, R.layout.filelist_item, filesanddirectories));
          
           ListView lv = CompressionDirectoryChooser.this.getListView();
              lv.setTextFilterEnabled(true);
              lv.setOnItemClickListener(lvOnItemClickListener);
          }
          else if((item.indexOf("[CHOOSE THIS DIRECTORY]") >= 0) && (item.length() == "[CHOOSE THIS DIRECTORY]".length()))
          {
           // item is a custom option that when selected creates a new export file
         
           //File root = Environment.getExternalStorageDirectory();
          
           String compressiondirectory = "/mnt" + getPath();
          
           Intent intent = new Intent();
           intent.putExtra("compressiondirectory", compressiondirectory);
           setResult(ACTIVITY_RESULTCODE_INPUTRECEIVED, intent);
           finish();
          }
          else if((pos = item.lastIndexOf("[DIR]")) >= 0)
          {
         // item is a directory
                     
           String directory = item.substring(0, pos - 1);
          
           Toast.makeText(CompressionDirectoryFileChooser.this, directory, Toast.LENGTH_SHORT).show();
          
           directorypath.add(directory);
          
           filesanddirectories = getFilesAndDirectories();
           CompressionDirectoryChooser.this.setListAdapter((ListAdapter) new ArrayAdapter<String>(CompressionDirectoryChooser.this, R.layout.filelist_item, filesanddirectories));
          
           ListView lv = CompressionDirectoryChooser.this.getListView();
              lv.setTextFilterEnabled(true);
              lv.setOnItemClickListener(lvOnItemClickListener);
          }
        }
    };
   
    private File getRoot()
    {
     File storageDir = new File("/mnt/");
   
     return storageDir;
    }
   
 private ArrayList<String> makeCopyOfStrings(ArrayList<String> listToCopy)
 {
  ArrayList<String> listOfStringsCopy = new ArrayList<String>();
  String str;

  for(int i = 0; i < listToCopy.size(); i++)
  {
   str = listToCopy.get(i);
   listOfStringsCopy.add(str);
  }

  return listOfStringsCopy;
 }
   
    private ArrayList<String> appendStrings(ArrayList<String> appendTo, ArrayList<String> appendFrom)
 {
  ArrayList<String> _appendTo = makeCopyOfStrings(appendTo);

  for(int i = 0; i < appendFrom.size(); i++)
  {
   _appendTo.add(appendFrom.get(i));
  }

  return _appendTo;
 }
   
    private ArrayList<String> getFilesAndDirectories()
    {
     ArrayList<String> filesAndDirectories = new ArrayList<String>();
     ArrayList<String> Files = new ArrayList<String>();
     ArrayList<String> Directories = new ArrayList<String>();
   
     //File root = Environment.getExternalStorageDirectory();
    
     File root = getRoot();
   
     if(directorypath.size() > 0) filesAndDirectories.add("..");
     filesAndDirectories.add("[CHOOSE THIS DIRECTORY]");
    
     String path = root.getPath() + getPath();

     File current_path = new File(path);
   
     String [] files = current_path.list();
   
     if(files != null)
     {
      for(int j = 0; j < files.length; j++)
      {
       String item;
       if((new File(current_path, files[j])).isDirectory())
       {
        item = files[j] + " [DIR]";
        Directories.add(item);
       }
       else
       {
        item = files[j];
        Files.add(item);
       }
       //filesAndDirectories.add(item);
      }
     }
   
     Collections.sort(Files, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.compareToIgnoreCase(s2);
            }
        });
   
     Collections.sort(Directories, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.compareToIgnoreCase(s2);
            }
        });
   
     filesAndDirectories = appendStrings(filesAndDirectories, Directories);
     filesAndDirectories = appendStrings(filesAndDirectories, Files);
   
     return filesAndDirectories;
    }
   
    private String getPath()
    {
     String path = "";
   
     for(int i = 0; i < directorypath.size(); i++)
     {
      path += "/" + directorypath.get(i);
     }
     //if(directorypath.size() > 0) path += "/";
   
     return path;
    }
}


End of Listing.

    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      setTitle("Compression Directory File Chooser");
     
      // ------------------------------------------------
     
      filesanddirectories.clear();
      directorypath.clear();
     
      filesanddirectories = getFilesAndDirectories();
     
      setListAdapter((ListAdapter) new ArrayAdapter<String>(this, R.layout.filelist_item, filesanddirectories));

      ListView lv = getListView();
      lv.setTextFilterEnabled(true);
      lv.setOnItemClickListener(lvOnItemClickListener);
    }


onCreate initializes the ListActivity to display the topmost directory listing of the Android Smartphone it is running on. Some options are listed first, then Directories and Files in alphabetical order. Only the options and Directories in the listing are meant to be selectable.

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event)  {
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR
                && keyCode == KeyEvent.KEYCODE_BACK
                && event.getRepeatCount() == 0) {
            // Take care of calling this method on earlier versions of
            // the platform where it doesn't exist.
            onBackPressed();
        }

        return super.onKeyDown(keyCode, event);
    }

    @Override
    public void onBackPressed() {
        // This will be called either automatically for you on 2.0
        // or later, or by the code above on earlier versions of the
        // platform.
   
     // BACK KEY pressed code goes here.
   
     cancelled();
   
     // --------------------------------
   
        return;
    }

    private void cancelled()
    {
     setResult(ACTIVITY_RESULTCODE_INPUTNOTRECEIVED);
     finish();
    }


onKeyDown, onBackPressed and cancelled are there to return control back to the previous Activity, properly, indicating that no Directory has been selected.

    OnItemClickListener lvOnItemClickListener = new OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View view,
            int position, long id) {
          // When clicked, show a toast with the TextView text
       
          int pos;
       
          TextView tv = (TextView) view;
          String item = tv.getText().toString();
         
          if((item.indexOf("..") >= 0) && (item.length() == 2))
          {
           // item is a custom option that when selected the screen is updated with
           // a list of all the files and directories in the directory above the
           // current one
          
           directorypath.remove(directorypath.size() - 1);
           filesanddirectories = getFilesAndDirectories();
           CompressionDirectoryChooser.this.setListAdapter((ListAdapter) new ArrayAdapter<String>(CompressionDirectoryChooser.this, R.layout.filelist_item, filesanddirectories));
          
           ListView lv = CompressionDirectoryChooser.this.getListView();
              lv.setTextFilterEnabled(true);
              lv.setOnItemClickListener(lvOnItemClickListener);
          }
          else if((item.indexOf("[CHOOSE THIS DIRECTORY]") >= 0) && (item.length() == "[CHOOSE THIS DIRECTORY]".length()))
          {
           // item is a custom option that when selected creates a new export file
         
           //File root = Environment.getExternalStorageDirectory();
          
           String compressiondirectory = "/mnt" + getPath();
          
           Intent intent = new Intent();
           intent.putExtra("compressiondirectory", compressiondirectory);
           setResult(ACTIVITY_RESULTCODE_INPUTRECEIVED, intent);
           finish();
          }
          else if((pos = item.lastIndexOf("[DIR]")) >= 0)
          {
         // item is a directory
                     
           String directory = item.substring(0, pos - 1);
          
           Toast.makeText(CompressionDirectoryFileChooser.this, directory, Toast.LENGTH_SHORT).show();
          
           directorypath.add(directory);
          
           filesanddirectories = getFilesAndDirectories();
           CompressionDirectoryChooser.this.setListAdapter((ListAdapter) new ArrayAdapter<String>(CompressionDirectoryChooser.this, R.layout.filelist_item, filesanddirectories));
          
           ListView lv = CompressionDirectoryChooser.this.getListView();
              lv.setTextFilterEnabled(true);
              lv.setOnItemClickListener(lvOnItemClickListener);
          }
        }
    };


lvOnItemClickListener is passed to the ListActivity when it is initialized in onCreate. This Listener implements what happens when an item is selected from the list. The first possibility is that you want to move up a Directory indicated by a choice '..' at the top of the listing. The second possibility is that you want to choose the current Directory indicated by a choice '[CHOOSE THIS DIRECTORY]' somewhere near the top of the listing. The final possibility is that you want to move down a directory indicated by a choice containing '[DIR]' at the end of a Directory name in the listing.

    private File getRoot()
    {
     File storageDir = new File("/mnt/");
   
     return storageDir;
    }

    

getRoot returns a File object as the root of the file system on your Android Smartphone.

 private ArrayList<String> makeCopyOfStrings(ArrayList<String> listToCopy)
 {
  ArrayList<String> listOfStringsCopy = new ArrayList<String>();
  String str;

  for(int i = 0; i < listToCopy.size(); i++)
  {
   str = listToCopy.get(i);
   listOfStringsCopy.add(str);
  }

  return listOfStringsCopy;
 }
   
    private ArrayList<String> appendStrings(ArrayList<String> appendTo, ArrayList<String> appendFrom)
 {
  ArrayList<String> _appendTo = makeCopyOfStrings(appendTo);

  for(int i = 0; i < appendFrom.size(); i++)
  {
   _appendTo.add(appendFrom.get(i));
  }

  return _appendTo;
 }

    

makeCopyOfStrings and appendStrings are utility functions that work with ArrayLists of Strings. I am assuming you will be able to understand what these do, so that is all I will say about them.

    private ArrayList<String> getFilesAndDirectories()
    {
     ArrayList<String> filesAndDirectories = new ArrayList<String>();
     ArrayList<String> Files = new ArrayList<String>();
     ArrayList<String> Directories = new ArrayList<String>();
   
     //File root = Environment.getExternalStorageDirectory();
    
     File root = getRoot();
   
     if(directorypath.size() > 0) filesAndDirectories.add("..");
     filesAndDirectories.add("[CHOOSE THIS DIRECTORY]");
    
     String path = root.getPath() + getPath();

     File current_path = new File(path);
   
     String [] files = current_path.list();
   
     if(files != null)
     {
      for(int j = 0; j < files.length; j++)
      {
       String item;
       if((new File(current_path, files[j])).isDirectory())
       {
        item = files[j] + " [DIR]";
        Directories.add(item);
       }
       else
       {
        item = files[j];
        Files.add(item);
       }
       //filesAndDirectories.add(item);
      }
     }
   
     Collections.sort(Files, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.compareToIgnoreCase(s2);
            }
        });
   
     Collections.sort(Directories, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.compareToIgnoreCase(s2);
            }
        });
   
     filesAndDirectories = appendStrings(filesAndDirectories, Directories);
     filesAndDirectories = appendStrings(filesAndDirectories, Files);
   
     return filesAndDirectories;
    }


getFilesAndDirectories constructs an ArrayList of Strings that will contain the listing of selectable items in the ListActivity.
For '..' to appear, at the top of the listing of selectable items, you will need to move down one level within the file system.
The '[CHOOSE THIS DIRECTORY]' item will always appear somewhere near the top of the list of selectable items of the ListActivity.
Finally a list of Directories, and then Files, in alphabetical order, will then be appended to the ArrayList of Strings, of which only Directories are meant to be selectable from the ListActivity.



    private String getPath()
    {
     String path = "";
   
     for(int i = 0; i < directorypath.size(); i++)
     {
      path += "/" + directorypath.get(i);
     }
     //if(directorypath.size() > 0) path += "/";
   
     return path;
    }


getPath takes an ArrayList of String containing the currently directory hierarchy, in terms of Directory names under consideration, and constructs a directory path as a single string, with all directory names separated by a '/' character.

No comments:

Post a Comment