Building an Android FileBrowser (list-based) !

Basic Tutorials concerning: GUI, Views, Activites, XML, Layouts, Intents, ...

Building an Android FileBrowser (list-based) !

Postby plusminus » Mon Nov 19, 2007 7:21 pm

John Lombard granted permission to write a tutorial based on his FileBrowser-Tutorial here. Thank you :)
@His Source
Building an Android FileBrowser (list-based)


What is this: We create an really simple file browser using a ListView.

What this tutorial includes:
  • Browse the FileSystem
  • React on Clicks
  • Sending Intents to the System (Open clicked files)
See also: ListActivity - Functionality

:?: Problems/Questions: post right below...

Difficulty: 1.75 of 5 :) ( rumors say its just a: 1.74 of 5 )

What it will look like:
Mode: ABSOLUTE
Image
Mode: RELATIVE
Image



Description:
0. Create a new Project, you could use package names like the following...
Image

1. Create a new layout called file_row.xml. It should look the same as below.
Syntax: [ Download ] [ Hide ]
Using xml Syntax Highlighting
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <TextView
  3.         xmlns:android="http://schemas.android.com/apk/res/android"
  4.         android:layout_width="fill_parent"
  5.         android:layout_height="wrap_content"
  6. />
Parsed in 0.001 seconds, using GeSHi 1.0.8.4

2. Modify the onCreate() function in AndroidFileBrowser.java so that it reads as the following:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         /** Called when the activity is first created. */
  2.         @Override
  3.         public void onCreate(Bundle icicle) {
  4.                 super.onCreate(icicle);
  5.                 // setContentView() gets called within the next line,
  6.                 // so we do not need it here.
  7.                 browseToRoot();
  8.         }
Parsed in 0.030 seconds, using GeSHi 1.0.8.4

We added a call to the browseToRoot() method. We haven't written browseToRoot() yet, but we'll get there in a second.

3. Change the class that AndroidFileBrowser extends from "Activity" to "ListActivity", because our files/directories will be listed in a list. It should look like this:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. public class FileList extends ListActivity {
Parsed in 0.031 seconds, using GeSHi 1.0.8.4


4. Create a private field, items, that holds the current directory and a list of the sub-Entries(Files/Dirs). This should go just under the "public class FileList" line. As we are planning to make Relative and Absolute Views available, we create a little enum and create a field of it. That part will look like the following:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. //...
  2. public class AndroidFileBrowser extends ListActivity {
  3.        
  4.         private enum DISPLAYMODE{ ABSOLUTE, RELATIVE; }
  5.  
  6.         private final DISPLAYMODE displayMode = DISPLAYMODE.ABSOLUTE;
  7.         private List<String> directoryEntries = new ArrayList<String>();
  8.         private File currentDirectory = new File("/");
  9.         //...
Parsed in 0.035 seconds, using GeSHi 1.0.8.4


5. Lets take a look at the browseToRoot()-Function we called in inCreate()...
As Android is Linux based, the filesystem-root-directory is called "/".
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         /**
  2.          * This function browses to the
  3.          * root-directory of the file-system.
  4.          */
  5.         private void browseToRoot() {
  6.                 browseTo(new File("/"));
  7.     }
Parsed in 0.036 seconds, using GeSHi 1.0.8.4


6. Next is the browseTo-Function which determines, whether the passed parameter is a directory (calls "fill(aDirectory.listFiles());") or a File (calls "openFile(aDirectory);" if the user permitted.)
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         private void browseTo(final File aDirectory){
  2.                 if (aDirectory.isDirectory()){
  3.                         this.currentDirectory = aDirectory;
  4.                         fill(aDirectory.listFiles());
  5.                 }else{
  6.                         OnClickListener okButtonListener = new OnClickListener(){
  7.                                 // @Override
  8.                                 public void onClick(DialogInterface arg0, int arg1) {
  9.                                                 // Lets start an intent to View the file, that was clicked...
  10.                                                 openFile(aDirectory);
  11.                                 }
  12.                         };
  13.                         OnClickListener cancelButtonListener = new OnClickListener(){
  14.                                 // @Override
  15.                                 public void onClick(DialogInterface arg0, int arg1) {
  16.                                         // Do nothing
  17.                                 }
  18.                         };
  19.                         AlertDialog.show(this,"Question", "Do you want to open that file?n"
  20.                                                                 + aDirectory.getName(),
  21.                                                                 "OK", okButtonListener,
  22.                                                                 "Cancel", cancelButtonListener, false, null);
  23.                 }
  24.         }
Parsed in 0.039 seconds, using GeSHi 1.0.8.4



7. The private fill() method takes a File[] array, and displays each file name on the screen in the list, including a switch on out Mode (Rel. or Abs.). It also displays two String for backward navigation (we define in strings.xml: 'up_one_level', ...)
Syntax: [ Download ] [ Hide ]
Using xml Syntax Highlighting
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3.     <string name="app_name">Android File Browser - by anddev.org</string>
  4.     <string name="up_one_level">..</string>
  5.     <string name="current_dir">.</string>
  6. </resources>
Parsed in 0.002 seconds, using GeSHi 1.0.8.4

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         private void fill(File[] files) {
  2.                 this.directoryEntries.clear();
  3.                
  4.                 // Add the "." == "current directory"
  5.                 // and the ".." == 'Up one level'
  6.                 this.directoryEntries.add(getString(R.string.current_dir));            
  7.                 if(this.currentDirectory.getParent() != null)
  8.                         this.directoryEntries.add(getString(R.string.up_one_level));
  9.                
  10.                 switch(this.displayMode){
  11.                         case ABSOLUTE:
  12.                                 for (File file : files){
  13.                                         this.directoryEntries.add(file.getPath());
  14.                                 }
  15.                                 break;
  16.                         case RELATIVE: // On relative Mode, we have to add the current-path to the beginning
  17.                                 int currentPathStringLenght = this.currentDirectory.getAbsolutePath().length();
  18.                                 for (File file : files){
  19.                                         this.directoryEntries.add(file.getAbsolutePath().substring(currentPathStringLenght));
  20.                                 }
  21.                                 break;
  22.                 }
Parsed in 0.039 seconds, using GeSHi 1.0.8.4


8. The lines in each switch-case take the files[] array and fill the class-field "this.directoryEntries" of the type ArrayList<String> with all the file's name. "this.directoryEntries" is a class field because it will be used later when the user clicks on one of the files or directories.

9. The last two lines (see below) create an ArrayAdapter<String> out of the items using the file_row layout. This is what marries the list of List of files to the list on the screen.
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.                 ArrayAdapter<String> directoryList = new ArrayAdapter<String>(this,
  2.                                 R.layout.file_row, this.directoryEntries);
  3.                
  4.                 this.setListAdapter(directoryList);
Parsed in 0.036 seconds, using GeSHi 1.0.8.4


10. Hit Shift-Control-O to Organize Imports, from time to time...

11. Save all your files and run. You should have a list of directories on your emulated Android phone.

12. Click on them and nothing happens -- next step fixes that.

Navigating the Directory Tree:

Now that we can see the root directory structure, we somehow need to get 'into' the FileSystem...

1. When the user clicks on a list item, we have the opportunity to intercept that click and do something interesting with it. To that end we override the onListItemClick(...) method of ListActivity
1.1. The only thing we need to do now is switching, which item we have clicked ('current_dir', 'up_one_level', or another File/Directory)

Note: that using the "getString(R.string.<name>)" is preferable to including the string as a final field within your class (or even worse, just putting the string in-line). Moving all of your strings to the resource directory goes a long way toward internationalizing your code if you ever need to do so.

The Full Source:
res/layout/file_row.xml
Syntax: [ Download ] [ Hide ]
Using xml Syntax Highlighting
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <TextView
  3.         xmlns:android="http://schemas.android.com/apk/res/android"
  4.         android:layout_width="fill_parent"
  5.         android:layout_height="wrap_content"
  6. />
Parsed in 0.001 seconds, using GeSHi 1.0.8.4


res/values/strings.xml
Syntax: [ Download ] [ Hide ]
Using xml Syntax Highlighting
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3.     <string name="app_name">Android File Browser - by anddev.org</string>
  4.     <string name="up_one_level">..</string>
  5.     <string name="current_dir">.</string>
  6. </resources>
Parsed in 0.002 seconds, using GeSHi 1.0.8.4


sry/your_package_strcuture/AndroidFileBrowser.java
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package org.anddev.android.filebrowser;
  2.  
  3. import java.io.File;
  4. import java.net.URISyntaxException;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7.  
  8. import android.app.AlertDialog;
  9. import android.app.ListActivity;
  10. import android.content.DialogInterface;
  11. import android.content.Intent;
  12. import android.content.DialogInterface.OnClickListener;
  13. import android.net.ContentURI;
  14. import android.os.Bundle;
  15. import android.view.View;
  16. import android.widget.ArrayAdapter;
  17. import android.widget.ListView;
  18.  
  19. public class AndroidFileBrowser extends ListActivity {
  20.        
  21.         private enum DISPLAYMODE{ ABSOLUTE, RELATIVE; }
  22.  
  23.         private final DISPLAYMODE displayMode = DISPLAYMODE.ABSOLUTE;
  24.         private List<String> directoryEntries = new ArrayList<String>();
  25.         private File currentDirectory = new File("/");
  26.  
  27.         /** Called when the activity is first created. */
  28.         @Override
  29.         public void onCreate(Bundle icicle) {
  30.                 super.onCreate(icicle);
  31.                 // setContentView() gets called within the next line,
  32.                 // so we do not need it here.
  33.                 browseToRoot();
  34.         }
  35.        
  36.         /**
  37.          * This function browses to the
  38.          * root-directory of the file-system.
  39.          */
  40.         private void browseToRoot() {
  41.                 browseTo(new File("/"));
  42.     }
  43.        
  44.         /**
  45.          * This function browses up one level
  46.          * according to the field: currentDirectory
  47.          */
  48.         private void upOneLevel(){
  49.                 if(this.currentDirectory.getParent() != null)
  50.                         this.browseTo(this.currentDirectory.getParentFile());
  51.         }
  52.        
  53.         private void browseTo(final File aDirectory){
  54.                 if (aDirectory.isDirectory()){
  55.                         this.currentDirectory = aDirectory;
  56.                         fill(aDirectory.listFiles());
  57.                 }else{
  58.                         OnClickListener okButtonListener = new OnClickListener(){
  59.                                 // @Override
  60.                                 public void onClick(DialogInterface arg0, int arg1) {
  61.                                         try {
  62.                                                 // Lets start an intent to View the file, that was clicked...
  63.                                                 Intent myIntent = new Intent(android.content.Intent.VIEW_ACTION,
  64.                                                                 new ContentURI("file://"
  65.                                                                                 + aDirectory.getAbsolutePath()));
  66.                                                 startActivity(myIntent);
  67.                                         } catch (URISyntaxException e) {
  68.                                                 e.printStackTrace();
  69.                                         }
  70.                                 }
  71.                         };
  72.                         OnClickListener cancelButtonListener = new OnClickListener(){
  73.                                 // @Override
  74.                                 public void onClick(DialogInterface arg0, int arg1) {
  75.                                         // Do nothing
  76.                                 }
  77.                         };
  78.                         AlertDialog.show(this,"Question", "Do you want to open that file?n"
  79.                                                                 + aDirectory.getName(),
  80.                                                                 "OK", okButtonListener,
  81.                                                                 "Cancel", cancelButtonListener, false, null);
  82.                 }
  83.         }
  84.  
  85.         private void fill(File[] files) {
  86.                 this.directoryEntries.clear();
  87.                
  88.                 // Add the "." and the ".." == 'Up one level'
  89.                 try {
  90.                         Thread.sleep(10);
  91.                 } catch (InterruptedException e1) {
  92.                         // TODO Auto-generated catch block
  93.                         e1.printStackTrace();
  94.                 }
  95.                 this.directoryEntries.add(".");
  96.                
  97.                 if(this.currentDirectory.getParent() != null)
  98.                         this.directoryEntries.add("..");
  99.                
  100.                 switch(this.displayMode){
  101.                         case ABSOLUTE:
  102.                                 for (File file : files){
  103.                                         this.directoryEntries.add(file.getPath());
  104.                                 }
  105.                                 break;
  106.                         case RELATIVE: // On relative Mode, we have to add the current-path to the beginning
  107.                                 int currentPathStringLenght = this.currentDirectory.getAbsolutePath().length();
  108.                                 for (File file : files){
  109.                                         this.directoryEntries.add(file.getAbsolutePath().substring(currentPathStringLenght));
  110.                                 }
  111.                                 break;
  112.                 }
  113.                
  114.                 ArrayAdapter<String> directoryList = new ArrayAdapter<String>(this,
  115.                                 R.layout.file_row, this.directoryEntries);
  116.                
  117.                 this.setListAdapter(directoryList);
  118.         }
  119.  
  120.         @Override
  121.         protected void onListItemClick(ListView l, View v, int position, long id) {
  122.                 int selectionRowID = (int) this.getSelectionRowID();
  123.                 String selectedFileString = this.directoryEntries.get(selectionRowID);
  124.                 if (selectedFileString.equals(".")) {
  125.                         // Refresh
  126.                         this.browseTo(this.currentDirectory);
  127.                 } else if(selectedFileString.equals("..")){
  128.                         this.upOneLevel();
  129.                 } else {
  130.                         File clickedFile = null;
  131.                         switch(this.displayMode){
  132.                                 case RELATIVE:
  133.                                         clickedFile = new File(this.currentDirectory.getAbsolutePath()
  134.                                                                                                 + this.directoryEntries.get(selectionRowID));
  135.                                         break;
  136.                                 case ABSOLUTE:
  137.                                         clickedFile = new File(this.directoryEntries.get(selectionRowID));
  138.                                         break;
  139.                         }
  140.                         if(clickedFile != null)
  141.                                 this.browseTo(clickedFile);
  142.                 }
  143.         }
  144. }
Parsed in 0.056 seconds, using GeSHi 1.0.8.4


Regards,
plusminus
Last edited by plusminus on Fri Jan 04, 2008 10:27 am, edited 3 times in total.
Image
Image | Android Development Community / Tutorials
User avatar
plusminus
Site Admin
Site Admin
 
Posts: 2688
Joined: Wed Nov 14, 2007 8:37 pm
Location: Schriesheim, Germany

Top

FileList class?

Postby atr » Tue Nov 20, 2007 2:01 pm

Hi there,

Step 3: about FileList class, there is no such class right? Did you misstype or something? The only class is AndroidFileBrowser.

Thank you!
atr
Junior Developer
Junior Developer
 
Posts: 14
Joined: Mon Nov 19, 2007 11:36 am
Location: Singapore

DirectoryList

Postby atr » Tue Nov 20, 2007 2:17 pm

Hi,

and the other failure is within onCreate():
Code: Select all
setContentView(R.layout.directory_list);


that line has to be removed! Thats all... really nice tutorial, anyway! Thanks :P
atr
Junior Developer
Junior Developer
 
Posts: 14
Joined: Mon Nov 19, 2007 11:36 am
Location: Singapore

Postby plusminus » Tue Nov 20, 2007 3:12 pm

Huh yes,
that tut was a lot of copy-pasting from eclipse... :P

Thx for the info :!: , fixed it.

Regards,
plusminus
Image
Image | Android Development Community / Tutorials
User avatar
plusminus
Site Admin
Site Admin
 
Posts: 2688
Joined: Wed Nov 14, 2007 8:37 pm
Location: Schriesheim, Germany

Postby plusminus » Thu Jan 03, 2008 2:51 pm

Hey guys,

Note: The application runs with application privileges. I.e. the data folder requires system privileges. When you start adb you run as root. But this application runs only with application-privileges. I imagine that Android will implement something, at some point, that allows the user to override security if necessary.

Regards,
plusminus
Image
Image | Android Development Community / Tutorials
User avatar
plusminus
Site Admin
Site Admin
 
Posts: 2688
Joined: Wed Nov 14, 2007 8:37 pm
Location: Schriesheim, Germany

Postby hiro » Thu Jan 03, 2008 8:53 pm

Thank you for this tutorial! It was exactly what I was looking for. (working on a silly little mp3 player)
hiro
Freshman
Freshman
 
Posts: 4
Joined: Wed Jan 02, 2008 8:07 pm
Location: Tucson

Top

Postby blackice » Thu Jan 03, 2008 9:49 pm

I am new to the platform and may be mistaken :oops: but I think the following method is missing.

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. private void openFile(File aFile){
  2.  
  3.         try {
  4.  
  5.              Intent myIntent = new Intent(android.content.Intent.VIEW_ACTION,
  6.  
  7.                   new ContentURI("file://" + aFile.getAbsolutePath()));
  8.  
  9.              startActivity(myIntent);
  10.  
  11.         } catch (URISyntaxException e) {
  12.  
  13.              e.printStackTrace();
  14.  
  15.         }
  16.  
  17.    }
  18.  
  19.  
Parsed in 0.038 seconds, using GeSHi 1.0.8.4

Thank-you very much for the tutorials.
blackice
Freshman
Freshman
 
Posts: 2
Joined: Thu Jan 03, 2008 7:49 pm

Postby plusminus » Fri Jan 04, 2008 10:55 am

Hello blackice,

yes, that is missing. But as far as I remember it didn't work as good as expected :(

Regards,
plusminus
Image
Image | Android Development Community / Tutorials
User avatar
plusminus
Site Admin
Site Admin
 
Posts: 2688
Joined: Wed Nov 14, 2007 8:37 pm
Location: Schriesheim, Germany

Postby rmeph » Fri Jan 04, 2008 3:11 pm

there

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.                               Intent myIntent = new Intent(android.content.Intent.VIEW_ACTION,
  2.  
  3.                                         new ContentURI("file://"
  4.  
  5.                                                   + aDirectory.getAbsolutePath()));
  6.  
  7.                               startActivity(myIntent);
Parsed in 0.036 seconds, using GeSHi 1.0.8.4

u get file in ContentURL................can we it get in string(string[] or inputstream) ??????if that is possible that how do it???
rmeph
Senior Developer
Senior Developer
 
Posts: 121
Joined: Mon Dec 10, 2007 1:54 pm
Location: India

Postby inter » Thu Feb 28, 2008 2:58 pm

Hii

I use new platform .

Intent i=new Intent(android.content.Intent.VIEW_ACTION,Uri.parse("file://"+aDirectory.getAbsolutePath()));
this.startActivity(i);


it's so OK
inter
Junior Developer
Junior Developer
 
Posts: 23
Joined: Thu Feb 21, 2008 10:13 am

Postby manojo » Tue Mar 25, 2008 3:02 pm

I don't have the android.net.ContentURI class. Has there been a modification in the specs?

Manojo
manojo
Freshman
Freshman
 
Posts: 4
Joined: Fri Mar 21, 2008 12:33 pm

Postby bhushank_vit » Mon Mar 31, 2008 7:22 am

[quote="manojo"]I don't have the android.net.ContentURI class. Has there been a modification in the specs?

Manojo[/quote]

hi Manojo,
Please refer this link..
http://code.google.com/android/migratin ... s.html#sql

u can use Uri.parse("//....") Instead of new Contenturi()

...Bhushan
bhushank_vit
Freshman
Freshman
 
Posts: 2
Joined: Tue Feb 19, 2008 6:03 am
Location: india

Postby bhushank_vit » Mon Mar 31, 2008 7:52 am

hi,
m getting this error....

Application Error:
com.google.android.fileBrowser
An Error has occured in com.google.android.fileBrowser.
No activity found to handle Intent{action = android.intent.action.VIEW data=/ }.

please suggest something..
thanks'
bhushank_vit
Freshman
Freshman
 
Posts: 2
Joined: Tue Feb 19, 2008 6:03 am
Location: india

Re: Building an Android FileBrowser (list-based) !

Postby bjreddi » Wed Apr 09, 2008 1:02 pm

Hi plusminus,

In your filebrowser application.

In browseTo() function call , you have used this AlertDialog.show () function. I get the error on the line, When i checked it.. there were less number of arguments than expected.

i think correct one is:
AlertDialog.show(this,"Question","Do you want to open that file?\n"
+ aDirectory.getName(),
"OK", okButtonListener,
"Cancel", cancelButtonListener, false, null);

Thank you for the application.

Regards,
Jo
bjreddi
Junior Developer
Junior Developer
 
Posts: 17
Joined: Thu Apr 03, 2008 1:24 pm

Postby bjreddi » Wed Apr 09, 2008 2:00 pm

Hi,

Yes.. That works in previous versions of Android.
Now in m5 android release.. Network API has been changed.

Instead of contentURI you can use..

Uri.parse("");

and import android.net.Uri;

That works fine.
bjreddi
Junior Developer
Junior Developer
 
Posts: 17
Joined: Thu Apr 03, 2008 1:24 pm

Top
Next

Return to Novice Tutorials

Who is online

Users browsing this forum: No registered users and 5 guests