andbook!.pdf - Learning Android Get an anddev.org - Android-Shirt Back to index
anddev.org Header Logo
FAQ Search Top rated articles Browse Feeds anddev.org - Authors Contact Details Register Log in

Android FileBrowser v.2.0

Goto page 1, 2  Next
 
       anddev.org - Android Development Community | Android Tutorials | Index -> Advanced Tutorials
Author Message
plusminus
Site Admin


Joined: 14 Nov 2007
Posts: 2025
Location: Germany

PostPosted: Sat Nov 24, 2007 7:37 pm    Post subject: Android FileBrowser v.2.0 Reply with quote

Android FileBrowser v.2.0 (Iconified-List-Based)


What is this: We want to pimp the FileBrowser (created in a tutorial before), so it displays fancy little Icons, indicating the type of the ListEntry. i.e.: directories ( ), audio-files ( ), html/php-Files ( ), zip/gz/jar-packages ( ), ....

What this tutorial includes:
  • Browsing the FileSystem showing fancy icons for the Files/Directories
  • Defining Array in XML
  • Sending Intents to the System (Open clicked files)

See also: ListActivity - Functionality

Idea Designed/Tested with sdk-version: m5-rc14

Idea Read before: As we are mostly combining them Exclamation
  1. Building an Android FileBrowser (list-based) Exclamation (as we are modifying/improving that tutorial!)
  2. Iconified TextList - The making of Exclamation


Question Problems/Questions: post right below...

Difficulty: 2 of 5 (+ 2 other projects done before Exclamation )

What it will look like:
(Mode: RELATIVE)
Images taken with SDK-Version m3 !



0. Copy these three files (IconifiedText.java, IconifiedTextView.java and IconifiedTextListAdapter.java) from the Iconified TextList, to the (basic) FileBrowser-Project you've had created somewhen before.

1.So not that much will be changed within this tutorial, the most important change is that icon-thing.
As we now want to display Iconified Texts our class-field this.directoryEntries has to be change:
Java:
// from:
private List<String> directoryEntries = new ArrayList<String>();
// to:
private List<IconifiedText> directoryEntries = new ArrayList<IconifiedText>();


2.As we want to produce highly generic code, we do not 'hardcode' the fileendings to our application, but we define each fileType-Category(Autio, Image, Text, ...) as an array in an XML-File, like the following:
XML:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="fileEndingImage">
        <item>.png</item>
        <item>.gif</item>
        <item>.jpg</item>
        <item>.jpeg</item>
        <item>.bmp</item>
    </array>
<array name="fileEndingAudio">
        <item>.mp3</item>
        <item>.wav</item>
        <item>.ogg</item>
        <item>.midi</item>
    </array>
<array name="fileEndingPackage">
        <item>.jar</item>
        <item>.zip</item>
        <item>.rar</item>
        <item>.gz</item>
    </array>
<array name="fileEndingWebText">
        <item>.htm</item>
        <item>.html</item>
        <item>.php</item>
    </array>
</resources>


Isn't that generic Laughing

3.The main change is within the 'private void fill(File[] files) {...'-method. For each File in the File[]-Array passed, we need to determine its Type(a Directory or a real File) and if it is a real File, we need to find the accurate icon for it.

The first thign of course is to clear the list of the directoryEntries (as we have browsed to another directory). We also add the Directory-Navigation-Icons, as already in Version 1.0:
Java:
          this.directoryEntries.clear();
          
          // Add the "." == "current directory"
          this.directoryEntries.add(new IconifiedText(
                    getString(R.string.current_dir),
                    getResources().getDrawable(R.drawable.folder)));       
          // and the ".." == 'Up one level'
          if(this.currentDirectory.getParent() != null)
               this.directoryEntries.add(new IconifiedText(
                         getString(R.string.up_one_level),
                         getResources().getDrawable(R.drawable.uponelevel)));
        


Now we create an Variable of the type Drawable (~Icon) that will hold the current Icon to be used as we are looping through the Array of Files that was passed as the parameter. If the currentFile is a folder we load the icon from the resources:
Java:
          Drawable currentIcon = null;
          for (File currentFile : files){
               if (currentFile.isDirectory()) {
                    currentIcon = getResources().getDrawable(R.drawable.folder);
               }else{


If currentFile was no Folder then it was a File Arrow we get a Drawable from the Resources, depending on the ending of the fileName of currentFile and what we entered on our fileendings.xml.
Java:
               }else{
                    String fileName = currentFile.getName();
                    /* Determine the Icon to be used,
                     * depending on the FileEndings defined in:
                     * res/values/fileendings.xml. */

                    if(checkEndsWithInStringArray(fileName, getResources().
                                        getStringArray(R.array.fileEndingImage))){
                         currentIcon = getResources().getDrawable(R.drawable.image);
                    }else if(checkEndsWithInStringArray(fileName, getResources().
                                        getStringArray(R.array.fileEndingWebText))){
                         currentIcon = getResources().getDrawable(R.drawable.webtext);
                    }else if(checkEndsWithInStringArray(fileName, getResources().
                                        getStringArray(R.array.fileEndingPackage))){
                         currentIcon = getResources().getDrawable(R.drawable.packed);
                    }else if(checkEndsWithInStringArray(fileName, getResources().
                                        getStringArray(R.array.fileEndingAudio))){
                         currentIcon = getResources().getDrawable(R.drawable.audio);
                    }else{
                         currentIcon = getResources().getDrawable(R.drawable.text);
                    }                   
               }

Lets take a short look at one of those if's:
Java:
                    /* If fileName endsWith on of the FileEndings
                     * defined in 'fileendings.xml' we load the
                     * appropriate Drawable from the Resources */

                    if(checkEndsWithInStringArray(fileName, getResources().
                                        getStringArray(R.array.fileEndingImage))){
                         currentIcon = getResources().getDrawable(R.drawable.image);

     //...
     /** Checks whether checkItsEnd ends with
      * one of the Strings from fileEndings */

     private boolean checkEndsWithInStringArray(String checkItsEnd,
                         String[] fileEndings){
          for(String aEnd : fileEndings){
               if(checkItsEnd.endsWith(aEnd))
                    return true;
          }
          return false;
     }

Having determined the correct Drawable to be displayed, we switch on our displayMode (as already in v1.0) and add a new IconifiedText to this.directoryEntries with the
Java:
               switch (this.displayMode) {
                    case ABSOLUTE:
                         /* On absolute Mode, we show the full path */
                         this.directoryEntries.add(new IconifiedText(currentFile
                                   .getPath(), currentIcon));
                         break;
                    case RELATIVE:
                         /* On relative Mode, we have to cut the
                          * current-path at the beginning */

                         int currentPathStringLenght = this.currentDirectory.
                                                                 getAbsolutePath().length();
                         this.directoryEntries.add(new IconifiedText(
                                   currentFile.getAbsolutePath().
                                   substring(currentPathStringLenght),
                                   currentIcon));

                         break;
               }

Having looped through all files and determined all the Drawables for them, we finally sort them (was not in v1.0) [Note: I simply made: 'public class IconifiedText implements Comparable<IconifiedText>' based on the Text (FileName here). Having sorted them, we create an IconifiedTextListAdapter to handle the IconifiedTexts being put to a ListView (better said: 'itla' is set to be the ListAdapter of our ListActivity):
Java:
          Collections.sort(this.directoryEntries);
          
          IconifiedTextListAdapter itla = new IconifiedTextListAdapter(this);
          itla.setListItems(this.directoryEntries);         
          this.setListAdapter(itla);


There was another little change to browseTo, which alters the Title of our Application, if we are in Relative Mode:
Java:
     private void browseTo(final File aDirectory){
          // On relative we display the full path in the title.
          if(this.displayMode == DISPLAYMODE.RELATIVE)
               this.setTitle(aDirectory.getAbsolutePath() + " :: " +
                         getString(R.string.app_name));


The full source:

Get IconifiedList-Files here (click)
Download all Resource-Files for this tutorial here: here (click)

"/src/your_package_structure/AndroidFileBrowser.java"
Java:
package org.anddev.android.filebrowser;

import java.io.File;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.anddev.android.filebrowser.iconifiedlist.IconifiedText;
import org.anddev.android.filebrowser.iconifiedlist.IconifiedTextListAdapter;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.graphics.drawable.Drawable;
import android.net.ContentURI;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;

public class AndroidFileBrowser extends ListActivity {
     
     private enum DISPLAYMODE{ ABSOLUTE, RELATIVE; }

     private final DISPLAYMODE displayMode = DISPLAYMODE.RELATIVE;
     private List<IconifiedText> directoryEntries = new ArrayList<IconifiedText>();
     private File currentDirectory = new File("/");

     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle icicle) {
          super.onCreate(icicle);
          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){
          // On relative we display the full path in the title.
          if(this.displayMode == DISPLAYMODE.RELATIVE)
               this.setTitle(aDirectory.getAbsolutePath() + " :: " +
                         getString(R.string.app_name));
          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...
                              AndroidFileBrowser.this.openFile(aDirectory);
                    }
               };
               OnClickListener cancelButtonListener = new OnClickListener(){
                    // @Override
                    public void onClick(DialogInterface arg0, int arg1) {
                         // Do nothing ^^
                    }
               };
               AlertDialog.show(this,"Question", R.drawable.folder, "Do you want to open that file?\n"
                                        + aDirectory.getName(),
                                        "OK", okButtonListener,
                                        "Cancel", cancelButtonListener, false, null);
          }
     }
     
     private void openFile(File aFile) {
          Intent myIntent = new Intent(android.content.Intent.VIEW_ACTION,
                    Uri.parse("file://" + aFile.getAbsolutePath()));
          startActivity(myIntent);
     }

     private void fill(File[] files) {
          this.directoryEntries.clear();
          
          // Add the "." == "current directory"
          this.directoryEntries.add(new IconifiedText(
                    getString(R.string.current_dir),
                    getResources().getDrawable(R.drawable.folder)));       
          // and the ".." == 'Up one level'
          if(this.currentDirectory.getParent() != null)
               this.directoryEntries.add(new IconifiedText(
                         getString(R.string.up_one_level),
                         getResources().getDrawable(R.drawable.uponelevel)));
          
          Drawable currentIcon = null;
          for (File currentFile : files){
               if (currentFile.isDirectory()) {
                    currentIcon = getResources().getDrawable(R.drawable.folder);
               }else{
                    String fileName = currentFile.getName();
                    /* Determine the Icon to be used,
                     * depending on the FileEndings defined in:
                     * res/values/fileendings.xml. */

                    if(checkEndsWithInStringArray(fileName, getResources().
                                        getStringArray(R.array.fileEndingImage))){
                         currentIcon = getResources().getDrawable(R.drawable.image);
                    }else if(checkEndsWithInStringArray(fileName, getResources().
                                        getStringArray(R.array.fileEndingWebText))){
                         currentIcon = getResources().getDrawable(R.drawable.webtext);
                    }else if(checkEndsWithInStringArray(fileName, getResources().
                                        getStringArray(R.array.fileEndingPackage))){
                         currentIcon = getResources().getDrawable(R.drawable.packed);
                    }else if(checkEndsWithInStringArray(fileName, getResources().
                                        getStringArray(R.array.fileEndingAudio))){
                         currentIcon = getResources().getDrawable(R.drawable.audio);
                    }else{
                         currentIcon = getResources().getDrawable(R.drawable.text);
                    }                   
               }
               switch (this.displayMode) {
                    case ABSOLUTE:
                         /* On absolute Mode, we show the full path */
                         this.directoryEntries.add(new IconifiedText(currentFile
                                   .getPath(), currentIcon));
                         break;
                    case RELATIVE:
                         /* On relative Mode, we have to cut the
                          * current-path at the beginning */

                         int currentPathStringLenght = this.currentDirectory.
                                                                 getAbsolutePath().length();
                         this.directoryEntries.add(new IconifiedText(
                                   currentFile.getAbsolutePath().
                                   substring(currentPathStringLenght),
                                   currentIcon));

                         break;
               }
          }
          Collections.sort(this.directoryEntries);
          
          IconifiedTextListAdapter itla = new IconifiedTextListAdapter(this);
          itla.setListItems(this.directoryEntries);         
          this.setListAdapter(itla);
     }

     @Override
     protected void onListItemClick(ListView l, View v, int position, long id) {
          super.onListItemClick(l, v, position, id);

          String selectedFileString = this.directoryEntries.get(position)
                    .getText();
          if (selectedFileString.equals(getString(R.string.current_dir))) {
               // Refresh
               this.browseTo(this.currentDirectory);
          } else if (selectedFileString.equals(getString(R.string.up_one_level))) {
               this.upOneLevel();
          } else {
               File clickedFile = null;
               switch (this.displayMode) {
                    case RELATIVE:
                         clickedFile = new File(this.currentDirectory
                                   .getAbsolutePath()
                                   + this.directoryEntries.get(position)
                                             .getText());
                         break;
                    case ABSOLUTE:
                         clickedFile = new File(this.directoryEntries.get(
                                   position).getText());
                         break;
               }
               if (clickedFile != null)
                    this.browseTo(clickedFile);
          }
     }

     /** Checks whether checkItsEnd ends with
      * one of the Strings from fileEndings */

     private boolean checkEndsWithInStringArray(String checkItsEnd,
                         String[] fileEndings){
          for(String aEnd : fileEndings){
               if(checkItsEnd.endsWith(aEnd))
                    return true;
          }
          return false;
     }
}



Regards,
plusminus



Android_File _Browser_2_0.zip
 Description:
Android File Browser 2.0 Full Source
SDK-Version: m3 !

Download
 Filename:  Android_File _Browser_2_0.zip
 Filesize:  107.52 KB
 Downloaded:  568 Time(s)


_________________

| Android Development Community / Tutorials


Last edited by plusminus on Thu Feb 21, 2008 5:18 pm; edited 3 times in total
Back to top
View user's profile Send private message Send e-mail Visit poster's website
ice2age
Freshman


Joined: 27 Dec 2007
Posts: 2

PostPosted: Thu Dec 27, 2007 8:03 pm    Post subject: Hmm. Am i doing something wrong. Reply with quote

Im getting 2 errors.
Bound mismatch: The generic method sort(List<T>) of type Collections is not applicable for the arguments (List<IconifiedText>). The inferred type IconifiedText is not a valid substitute for the bounded parameter <T extends Comparable<? super T>>

The method compareTo(IconifiedText) of type IconifiedText must override a superclass method
Back to top
View user's profile Send private message
plusminus
Site Admin


Joined: 14 Nov 2007
Posts: 2025
Location: Germany

PostPosted: Fri Dec 28, 2007 2:44 pm    Post subject: Reply with quote

Hello ice2age,

dunno why there was the following part missing:
Java:
// Replace:
public class IconifiedText{
// With:
public class IconifiedText implements Comparable<IconifiedText>{

Sorry for the inconveniences...

Regards,
plusminus

_________________

| Android Development Community / Tutorials
Back to top
View user's profile Send private message Send e-mail Visit poster's website
ice2age
Freshman


Joined: 27 Dec 2007
Posts: 2

PostPosted: Fri Dec 28, 2007 5:01 pm    Post subject: That worked Reply with quote

Thanks plusminus. Could you tell me why i cant browse the /data folder?
Im new to the platform and i wonder why there is no /home folder like on other *nix systems.
I tought there would be one something like /home/android/ {and here Pictures, Music .... }.
Is there any universal folder for Pictures, Music?. example If there is some program that can take pictures from the camera and it stores it somwhere in its own data folder and someone else has written a progrem for picture editing how can he know where the pictures are? Thanks
Back to top
View user's profile Send private message
plusminus
Site Admin


Joined: 14 Nov 2007
Posts: 2025
Location: Germany

PostPosted: Fri Dec 28, 2007 5:55 pm    Post subject: Reply with quote

Hello ice2age,

yes I'm also a bit surprised about the differences between the output of the program and what the DDMS-View shows...

You probably cannot browse the "/data/"-Folder, because the rights of the Application are not high enough to do so.

I'm not aware of any default-folders for images/music yet. (also not that familiar with Linux)

Regards,
plusminus

_________________

| Android Development Community / Tutorials
Back to top
View user's profile Send private message Send e-mail Visit poster's website
raquibulbari
Developer


Joined: 16 Dec 2007
Posts: 25
Location: dhaka,bangladesh

PostPosted: Sun Jan 27, 2008 9:43 am    Post subject: Reply with quote

Hello +-,
How are you? These tutorials are simply great
Can i use codes in this forum to my android submission?? will there be any licensing issues???

_________________
Shimugool
Back to top
View user's profile Send private message Visit poster's website
plusminus
Site Admin


Joined: 14 Nov 2007
Posts: 2025
Location: Germany

PostPosted: Sun Jan 27, 2008 1:06 pm    Post subject: Reply with quote

raquibulbari wrote:
Hello +-,
How are you? These tutorials are simply great
Can i use codes in this forum to my android submission?? will there be any licensing issues???


We want 50% of all outcomes Laughing Of course NOT Exclamation
The only thing you "have to do" is, when someone asks you if you know a helpful Android-Forum, you will know what to answer Wink

Regards,
plusminus

_________________

| Android Development Community / Tutorials
Back to top
View user's profile Send private message Send e-mail Visit poster's website
raquibulbari
Developer


Joined: 16 Dec 2007
Posts: 25
Location: dhaka,bangladesh

PostPosted: Sun Jan 27, 2008 3:04 pm    Post subject: Reply with quote

Very Happy
already started to advertise your site, told 2 of my android developer friend Wink

_________________
Shimugool
Back to top
View user's profile Send private message Visit poster's website
gvkreddyvamsi
Developer


Joined: 21 Jan 2008
Posts: 43
Location: INDIA

PostPosted: Wed Feb 13, 2008 8:18 am    Post subject: cann't resolve R.java Reply with quote

HI, application is fine. Done well.

Plz attach zip file for this application.

by
vamsi
Back to top
View user's profile Send private message Send e-mail Visit poster's website
plusminus
Site Admin


Joined: 14 Nov 2007
Posts: 2025
Location: Germany

PostPosted: Wed Feb 13, 2008 9:29 am    Post subject: Re: cann't resolve R.java