| Author |
Message |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2655 Location: College Park, MD
|
Posted: Mon Nov 19, 2007 7:21 pm Post subject: Building an Android FileBrowser (list-based) ! |
|
|
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
Mode: RELATIVE

Description:
0. Create a new Project, you could use package names like the following...
1. Create a new layout called file_row.xml. It should look the same as below.
| XML: | <?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/> |
2. Modify the onCreate() function in AndroidFileBrowser.java so that it reads as the following:
| Java: | /** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// setContentView() gets called within the next line,
// so we do not need it here.
browseToRoot();
} |
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:
| Java: | public class FileList extends ListActivity { |
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:
| Java: | //...
public class AndroidFileBrowser extends ListActivity {
private enum DISPLAYMODE{ ABSOLUTE, RELATIVE; }
private final DISPLAYMODE displayMode = DISPLAYMODE.ABSOLUTE;
private List<String> directoryEntries = new ArrayList<String>();
private File currentDirectory = new File("/");
//... |
5. Lets take a look at the browseToRoot()-Function we called in inCreate()...
As Android is Linux based, the filesystem-root-directory is called "/".
| Java: | /**
* This function browses to the
* root-directory of the file-system.
*/
private void browseToRoot() {
browseTo(new File("/"));
} |
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.)
| Java: | private void browseTo(final File aDirectory){
if (aDirectory.isDirectory()){
this.currentDirectory = aDirectory;
fill(aDirectory.listFiles());
}else{
OnClickListener okButtonListener = new OnClickListener(){
// @Override
public void onClick(DialogInterface arg0, int arg1) {
// Lets start an intent to View the file, that was clicked...
openFile(aDirectory);
}
};
OnClickListener cancelButtonListener = new OnClickListener(){
// @Override
public void onClick(DialogInterface arg0, int arg1) {
// Do nothing
}
};
AlertDialog.show(this,"Question", "Do you want to open that file?\n"
+ aDirectory.getName(),
"OK", okButtonListener,
"Cancel", cancelButtonListener, false, null);
}
} |
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', ...)
| XML: | <?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Android File Browser - by anddev.org</string>
<string name="up_one_level">..</string>
<string name="current_dir">.</string>
</resources> |
| Java: | private void fill(File[] files) {
this.directoryEntries.clear();
// Add the "." == "current directory"
// and the ".." == 'Up one level'
this.directoryEntries.add(getString(R.string.current_dir));
if(this.currentDirectory.getParent() != null)
this.directoryEntries.add(getString(R.string.up_one_level));
switch(this.displayMode){
case ABSOLUTE:
for (File file : files){
this.directoryEntries.add(file.getPath());
}
break;
case RELATIVE: // On relative Mode, we have to add the current-path to the beginning
int currentPathStringLenght = this.currentDirectory.getAbsolutePath().length();
for (File file : files){
this.directoryEntries.add(file.getAbsolutePath().substring(currentPathStringLenght));
}
break;
} |
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.
| Java: | ArrayAdapter<String> directoryList = new ArrayAdapter<String>(this,
R.layout.file_row, this.directoryEntries);
this.setListAdapter(directoryList); |
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
| XML: | <?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/> |
res/values/strings.xml
| XML: | <?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Android File Browser - by anddev.org</string>
<string name="up_one_level">..</string>
<string name="current_dir">.</string>
</resources> |
sry/your_package_strcuture/AndroidFileBrowser.java
| Java: | package org.anddev.android.filebrowser;
import java.io.File;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.net.ContentURI;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class AndroidFileBrowser extends ListActivity {
private enum DISPLAYMODE{ ABSOLUTE, RELATIVE; }
private final DISPLAYMODE displayMode = DISPLAYMODE.ABSOLUTE;
private List<String> directoryEntries = new ArrayList<String>();
private File currentDirectory = new File("/");
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// setContentView() gets called within the next line,
// so we do not need it here.
browseToRoot();
}
/**
* This function browses to the
* root-directory of the file-system.
*/
private void browseToRoot() {
browseTo(new File("/"));
}
/**
* This function browses up one level
* according to the field: currentDirectory
*/
private void upOneLevel(){
if(this.currentDirectory.getParent() != null)
this.browseTo(this.currentDirectory.getParentFile());
}
private void browseTo(final File aDirectory){
if (aDirectory.isDirectory()){
this.currentDirectory = aDirectory;
fill(aDirectory.listFiles());
}else{
OnClickListener okButtonListener = new OnClickListener(){
// @Override
public void onClick(DialogInterface arg0, int arg1) {
try {
// Lets start an intent to View the file, that was clicked...
Intent myIntent = new Intent(android.content.Intent.VIEW_ACTION,
new ContentURI("file://"
+ aDirectory.getAbsolutePath()));
startActivity(myIntent);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
};
OnClickListener cancelButtonListener = new OnClickListener(){
// @Override
public void onClick(DialogInterface arg0, int arg1) {
// Do nothing
}
};
AlertDialog.show(this,"Question", "Do you want to open that file?\n"
+ aDirectory.getName(),
"OK", okButtonListener,
"Cancel", cancelButtonListener, false, null);
}
}
private void fill(File[] files) {
this.directoryEntries.clear();
// Add the "." and the ".." == 'Up one level'
try {
Thread.sleep(10);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
this.directoryEntries.add(".");
if(this.currentDirectory.getParent() != null)
this.directoryEntries.add("..");
switch(this.displayMode){
case ABSOLUTE:
for (File file : files){
this.directoryEntries.add(file.getPath());
}
break;
case RELATIVE: // On relative Mode, we have to add the current-path to the beginning
int currentPathStringLenght = this.currentDirectory.getAbsolutePath().length();
for (File file : files){
this.directoryEntries.add(file.getAbsolutePath().substring(currentPathStringLenght));
}
break;
}
ArrayAdapter<String> directoryList = new ArrayAdapter<String>(this,
R.layout.file_row, this.directoryEntries);
this.setListAdapter(directoryList);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
int selectionRowID = (int) this.getSelectionRowID();
String selectedFileString = this.directoryEntries.get(selectionRowID);
if (selectedFileString.equals(".")) {
// Refresh
this.browseTo(this.currentDirectory);
} else if(selectedFileString.equals("..")){
this.upOneLevel();
} else {
File clickedFile = null;
switch(this.displayMode){
case RELATIVE:
clickedFile = new File(this.currentDirectory.getAbsolutePath()
+ this.directoryEntries.get(selectionRowID));
break;
case ABSOLUTE:
clickedFile = new File(this.directoryEntries.get(selectionRowID));
break;
}
if(clickedFile != null)
this.browseTo(clickedFile);
}
}
} |
Regards,
plusminus _________________
Download my apps  Please remember, that this board is give & take 
| Android Development Community / Tutorials
Last edited by plusminus on Fri Jan 04, 2008 10:27 am; edited 3 times in total |
|
| Back to top |
|
 |
|
|
 |
atr Junior Developer

Joined: 19 Nov 2007 Posts: 14 Location: Singapore
|
Posted: Tue Nov 20, 2007 2:01 pm Post subject: FileList class? |
|
|
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! |
|
| Back to top |
|
 |
atr Junior Developer

Joined: 19 Nov 2007 Posts: 14 Location: Singapore
|
Posted: Tue Nov 20, 2007 2:17 pm Post subject: DirectoryList |
|
|
Hi,
and the other failure is within onCreate():
| Code: | | setContentView(R.layout.directory_list); |
that line has to be removed! Thats all... really nice tutorial, anyway! Thanks  |
|
| Back to top |
|
 |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2655 Location: College Park, MD
|
Posted: Tue Nov 20, 2007 3:12 pm Post subject: |
|
|
Huh yes,
that tut was a lot of copy-pasting from eclipse...
Thx for the info , fixed it.
Regards,
plusminus _________________
Download my apps  Please remember, that this board is give & take 
| Android Development Community / Tutorials |
|
| Back to top |
|
 |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2655 Location: College Park, MD
|
Posted: Thu Jan 03, 2008 2:51 pm Post subject: |
|
|
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 _________________
Download my apps  Please remember, that this board is give & take 
| Android Development Community / Tutorials |
|
| Back to top |
|
 |
hiro Freshman

Joined: 02 Jan 2008 Posts: 4 Location: Tucson
|
Posted: Thu Jan 03, 2008 8:53 pm Post subject: |
|
|
| Thank you for this tutorial! It was exactly what I was looking for. (working on a silly little mp3 player) |
|
| Back to top |
|
 |
|
|
 |
blackice Freshman

Joined: 03 Jan 2008 Posts: 2
|
|
| Back to top |
|
 |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2655 Location: College Park, MD
|
Posted: Fri Jan 04, 2008 10:55 am Post subject: |
|
|
Hello blackice,
yes, that is missing. But as far as I remember it didn't work as good as expected
Regards,
plusminus _________________
Download my apps  Please remember, that this board is give & take 
| Android Development Community / Tutorials |
|
| Back to top |
|
 |
rmeph Senior Developer

Joined: 10 Dec 2007 Posts: 120 Location: India
|
Posted: Fri Jan 04, 2008 3:11 pm Post subject: |
|
|
there
| Java: | Intent myIntent = new Intent(android.content.Intent.VIEW_ACTION,
new ContentURI("file://"
+ aDirectory.getAbsolutePath()));
startActivity(myIntent); |
u get file in ContentURL................can we it get in string(string[] or inputstream) ??????if that is possible that how do it??? |
|
| Back to top |
|
 |
inter Junior Developer

Joined: 21 Feb 2008 Posts: 23
|
Posted: Thu Feb 28, 2008 2:58 pm Post subject: |
|
|
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 |
|
| Back to top |
|
 |
manojo Freshman

Joined: 21 Mar 2008 Posts: 4
|
Posted: Tue Mar 25, 2008 3:02 pm Post subject: |
|
|
I don't have the android.net.ContentURI class. Has there been a modification in the specs?
Manojo |
|
| Back to top |
|
 |
bhushank_vit Freshman

Joined: 19 Feb 2008 Posts: 2 Location: india
|
|
| Back to top |
|
 |
bhushank_vit Freshman

Joined: 19 Feb 2008 Posts: 2 Location: india
|
Posted: Mon Mar 31, 2008 7:52 am Post subject: |
|
|
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' |
|
| Back to top |
|
 |
bjreddi Junior Developer

Joined: 03 Apr 2008 Posts: 17
|
Posted: Wed Apr 09, 2008 1:02 pm Post subject: Re: Building an Android FileBrowser (list-based) ! |
|
|
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 |
|
| Back to top |
|
 |
bjreddi Junior Developer

Joined: 03 Apr 2008 Posts: 17
|
Posted: Wed Apr 09, 2008 2:00 pm Post subject: |
|
|
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. |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
© 2007, Android Development Community
All rights reserved.
Powered by phpBB.
|