Android Open Source - AudioBook Browser Activity






From Project

Back to project page AudioBook.

License

The source code is released under:

Creative Commons Legal Code Attribution-NonCommercial 3.0 Unported CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT C...

If you think the Android project AudioBook listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 *  This work is licensed under the Creative Commons Attribution-NonCommercial-
 *  NoDerivs 3.0 Unported License. To view a copy of this license, visit
 *  http://creativecommons.org/licenses/by-nc-nd/3.0/ or send a letter to 
 *  Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 
 *  94041, USA.//w w  w .  j av  a  2 s .c o  m
 * 
 *  Use of this work is permitted only in accordance with license rights granted.
 *  Materials provided "AS IS"; no representations or warranties provided.
 * 
 *  Copyright ? 2012 Marcus Parkkinen, Aki K?kel?, Fredrik ?hs.
 **/

package edu.chalmers.dat255.audiobookplayer.view;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import edu.chalmers.dat255.audiobookplayer.R;
import edu.chalmers.dat255.audiobookplayer.constants.Constants;
import edu.chalmers.dat255.audiobookplayer.util.BookCreator;

/**
 * This class is used to display and add new books to the bookshelf. It lists
 * all audio files and their file trees up to the ExternalStorageDirectory.
 * 
 * @author Fredrik ?hs
 * 
 */
public class BrowserActivity extends Activity {
  protected static final String TAG = "BrowserActivity";

  /**
   * The different possible file types when browsing.
   * 
   * @author Fredrik ?hs
   * 
   */
  public enum FILETYPE {
    FILE {
      public String toString() {
        return "File";
      }
    },
    FOLDER {
      public String toString() {
        return "Folder";
      }
    },
    PARENT {
      public String toString() {
        return "Parent Folder";
      }
    };
  }

  private BrowserArrayAdapter adapter;
  private ListView listView;

  // checkedItems is used to save the checkedState of files while navigating
  // through the file tree.
  private Set<File> checkedItems;
  private File currentDirectory;
  private Map<TypedFile, List<TypedFile>> childMap;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_browser);

    // generate the childMap
    childMap = populateChildMap();
    // set up the components
    setUpComponents();

    // get the file to the root of the file system
    File f = new File(Environment.getExternalStorageDirectory()
        .getAbsolutePath());

    // fill the list view from the root
    fill(f);
  }

  @Override
  public void onBackPressed() {
    Log.i(TAG, "Back pressed in Browser Activity");
    finish();
  }

  /**
   * Sets up the components of this activity.
   */
  private void setUpComponents() {
    checkedItems = new TreeSet<File>();
    listView = (ListView) findViewById(R.id.browserList);
    listView.setOnItemClickListener(new OnItemClickListener() {

      public void onItemClick(AdapterView<?> arg0, View arg1,
          int position, long arg3) {
        TypedFile file = adapter.getItem(position);
        // if a folder or the 'parent directory' is clicked, open it
        if (file.getType().equals(FILETYPE.FOLDER)
            || file.getType().equals(FILETYPE.PARENT)) {
          fill(file);
        }
        // otherwise, call onclick method
        else {
          onFileClick(file);
        }
      }
    });

    // set listener for button "Create Book"
    Button createBookButton = (Button) findViewById(R.id.createBook);
    createBookButton.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        createBook();
      }
    });

    // create and listen to the back button
    ImageButton backButton = (ImageButton) findViewById(R.id.backButton);
    backButton.setOnClickListener(new OnClickListener() {

      public void onClick(View v) {
        // finish this activity
        finish();
        Log.d(TAG, "Backed from browser.");
      }

    });

  }

  /**
   * Private class used to create a book from the currently selected items.
   */
  private void createBook() {
    // check that some items are checked
    if (!checkedItems.isEmpty()) {
      // create a list of tracks to add
      List<String> tracks = new ArrayList<String>();
      String name = null;
      for (File f : checkedItems) {
        // only add tracks, not folders
        if (f.isFile()) {
          if (tracks.size() == 0) {
            // this name is but a backup in the case that
            // there is no album id3
            name = f.getParentFile().getName();
          }
          tracks.add(f.getAbsolutePath());
        }
      }
      // if we manage to create a book (the book is valid) then continue
      // as normal
      if (BookCreator.getInstance().createBookToBookshelf(tracks, name,
          null)) {
        // create a toast to notify the user that a book has been
        // added.
        Toast.makeText(BrowserActivity.this, "Book added: " + name,
            Toast.LENGTH_SHORT).show();
        // empty checkedItems and fill the list with unchecked items
        checkedItems = new TreeSet<File>();
        // refill the ListView from currentDirectory
        fill(currentDirectory);
      }
    }
  }

  /**
   * Fills the ListView with the children to root.
   * 
   * @param root
   *            The folder which contents is to be listed.
   */
  private void fill(File root) {

    // in case root is not a directory, no items will be listed (should
    // never happen)
    if (root.isFile()) {
      return;
    }
    currentDirectory = root;
    List<TypedFile> directories = new ArrayList<TypedFile>();
    // store files separately for correct sorting
    List<TypedFile> files = new ArrayList<TypedFile>();
    // check that the file exists in the previously populated childMap
    if (childMap.get(root) != null) {
      // add all files under root in childMap
      for (TypedFile f : childMap.get(root)) {
        if (f.isDirectory()) {
          directories.add(f);
        } else {
          files.add(f);
        }
      }

      // sort found directories and files
      Collections.sort(directories);
      Collections.sort(files);
      // add all files under the directories
      directories.addAll(files);

      // adds an item listed as ".." of type parent topmost in the list
      if (!root.getAbsolutePath()
          .equals(Environment.getExternalStorageDirectory()
              .getAbsolutePath())) {
        directories.add(0,
            new TypedFile(FILETYPE.PARENT, root.getParent()));
      }

      // create a new adapter with the found files/directories and add it
      // to the listview
      adapter = new BrowserArrayAdapter(this, R.layout.file_view,
          directories, checkedItems, childMap);
      listView.setAdapter(adapter);
    } else {
      // notify the user that no files were found on the system
      Toast t = Toast.makeText(getApplicationContext(),
          Constants.Message.NO_AUDIO_FILES_FOUND, Toast.LENGTH_SHORT);
      t.show();
    }

  }

  /**
   * Finds all the tracks on the device and adds them and their parents to a
   * map
   * 
   * @return The map containing the files and their parents.
   */
  private Map<TypedFile, List<TypedFile>> populateChildMap() {
    /*
     * this will prevent files such as notifications and ringtones to appear
     * in the list
     */
    String filtering = MediaStore.Audio.Media.IS_MUSIC + " != 0";

    /*
     * path to the audiofile
     */
    String[] projection = { MediaStore.Audio.Media.DATA };

    // this cursor will point at the paths of all music
    Cursor cursor = this.managedQuery(
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection,
        filtering, null, null);

    Map<File, File> parentMap = new TreeMap<File, File>();
    // cursor.moveToNext will iterate through all music on the device
    while (cursor.moveToNext()) {
      File child = new File(cursor.getString(cursor
          .getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)));
      // loop through all tracks and put them with their parents as value
      while (!child.getAbsolutePath()
          .equals(Environment.getExternalStorageDirectory()
              .getAbsolutePath())) {
        if (parentMap.containsKey(child)) {
          /*
           * if the file is already in the map, so is all its parents
           * and therefore further looping is redundant
           */
          break;
        }
        parentMap.put(child, child.getParentFile());
        child = child.getParentFile();
      }
    }

    // reverse the list for easier iterating
    Map<TypedFile, List<TypedFile>> fileMap = new TreeMap<TypedFile, List<TypedFile>>();

    for (Entry<File, File> entry : parentMap.entrySet()) {
      // if the map does not contain the parent, add the parent with a new
      // list
      TypedFile parent = new TypedFile(FILETYPE.FOLDER, entry.getValue()
          .getAbsolutePath());
      if (!fileMap.containsKey(parent)) {
        fileMap.put(parent, new ArrayList<TypedFile>());
      }
      // add a child to the parents list of children
      File child = entry.getKey();
      if (child.isDirectory()) {
        fileMap.get(parent)
            .add(new TypedFile(FILETYPE.FOLDER, child
                .getAbsolutePath()));
      } else {
        fileMap.get(parent).add(
            new TypedFile(FILETYPE.FILE, child.getAbsolutePath()));
      }
    }
    return fileMap;
  }

  /**
   * Method to be called when a file of type file is clicked.
   * 
   * @param file
   *            The file that was clicked.
   */
  private void onFileClick(TypedFile file) {
    Toast.makeText(this, "File Clicked: " + file.getName(),
        Toast.LENGTH_SHORT).show();
  }

  /**
   * Adapter to the browser.
   * 
   * @author Fredrik ?hs
   * 
   */
  private class BrowserArrayAdapter extends ArrayAdapter<TypedFile> {
    private Context c;
    private int id;
    private List<TypedFile> files;
    private Set<File> checkedItems;
    private Map<TypedFile, List<TypedFile>> childMap;

    /**
     * Constructor.
     * 
     * @param context
     *            The current context.
     * @param textViewResourceId
     *            The resource ID for the layout file used to display the
     *            information.
     * @param filesToDisplay
     *            The list of the files to be displayed.
     * @param checkedItems
     *            Storage for the items that are checked at the moment.
     * @param childMap
     *            A map containing information regarding all the children
     *            which will be checked if a folder is checked.
     */
    public BrowserArrayAdapter(Context context, int textViewResourceId,
        List<TypedFile> filesToDisplay, Set<File> checkedItems,
        Map<TypedFile, List<TypedFile>> childMap) {
      super(context, textViewResourceId, filesToDisplay);
      this.c = context;
      this.id = textViewResourceId;
      this.files = filesToDisplay;
      this.checkedItems = checkedItems;
      this.childMap = childMap;
    }

    /**
     * Creates the view of each listview item
     */
    public View getView(int position, View convertView, ViewGroup parent) {
      View view = convertView;
      // if the view is null,
      if (view == null) {
        LayoutInflater vi = (LayoutInflater) c
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        // inflate the view into itself
        view = vi.inflate(id, null);
      }
      // get file as final since it wont change and it's needed in an
      // anonymous class
      final TypedFile file = files.get(position);
      if (file != null) {
        // get the TextView in the ListView item
        TextView tv1 = (TextView) view.findViewById(R.id.TextView01);
        TextView tv2 = (TextView) view.findViewById(R.id.TextView02);

        if (tv1 != null) {
          tv1.setText(file.getName());
        }
        if (tv2 != null) {
          tv2.setText(file.getType().toString());
        }

      }

      // set the state of the checkbox according to the checkedItems list
      CheckBox cb = (CheckBox) view.findViewById(R.id.checkBox);
      boolean checkState = checkedItems.contains(file);
      cb.setChecked(checkState);

      // set the on click listener of the checkbox
      cb.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
          // checks all children if a folder, or the file otherwise
          checkAllChildren(file, ((CheckBox) v).isChecked());
        }
      });

      return view;
    }

    /**
     * (Un)check all the files within file recursively (in case it's a
     * folder).
     * 
     * @param file
     *            The file (folder) to check.
     * @param checkState
     *            Whether to check or to uncheck the files.
     */
    private void checkAllChildren(File file, boolean checkState) {
      // if file is a regular file
      if (file.isFile()) {
        // check it and return
        checkItem(file, checkState);
        return;
      }
      // the get returns null if file is not contained within the map
      List<TypedFile> list = childMap.get(file);
      // if childMap contains the file, instantiate the list
      if (list != null) {
        for (File f : list) {
          // check the item
          checkItem(f, checkState);
          // recurse
          checkAllChildren(f, checkState);
        }
      }
    }

    /**
     * Adds or removes the file from the checkedItems list
     * 
     * @param file
     *            The file to add or remove.
     * @param checkState
     *            Whether to add or remove the file.
     */
    private void checkItem(File file, boolean checkState) {
      if (checkState) {
        checkedItems.add(file);
      } else {
        checkedItems.remove(file);
      }
    }
  }

  /**
   * Simple class which extends File by also having a FILETYPE which
   * identifies it in the list.
   * 
   * @author Fredrik ?hs
   * 
   */
  private class TypedFile extends File {

    private static final long serialVersionUID = 1L;
    private FILETYPE type;

    /**
     * Constructor
     * 
     * @param type
     *            The filetype of the file.
     * @param path
     *            The path to the file.
     */
    public TypedFile(FILETYPE type, String path) {
      super(path);
      this.type = type;
    }

    /**
     * 
     * @return the type of the file.
     */
    public FILETYPE getType() {
      return type;
    }

    @Override
    public String getName() {
      // the name of a parent folder should be ..
      if (type.equals(FILETYPE.PARENT)) {
        return "..";
      }
      return super.getName();
    }
  }

}




Java Source Code List

edu.chalmers.dat255.audiobookplayer.constants.Constants.java
edu.chalmers.dat255.audiobookplayer.constants.PlaybackStatus.java
edu.chalmers.dat255.audiobookplayer.ctrl.BookshelfControllerTest.java
edu.chalmers.dat255.audiobookplayer.ctrl.BookshelfController.java
edu.chalmers.dat255.audiobookplayer.ctrl.PlayerControllerTest.java
edu.chalmers.dat255.audiobookplayer.ctrl.PlayerController.java
edu.chalmers.dat255.audiobookplayer.instrumentation.AllTests.java
edu.chalmers.dat255.audiobookplayer.interfaces.IBookUpdates.java
edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents.java
edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfGUIEvents.java
edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents.java
edu.chalmers.dat255.audiobookplayer.interfaces.ITrackUpdates.java
edu.chalmers.dat255.audiobookplayer.model.BookTest.java
edu.chalmers.dat255.audiobookplayer.model.Book.java
edu.chalmers.dat255.audiobookplayer.model.BookshelfTest.java
edu.chalmers.dat255.audiobookplayer.model.Bookshelf.java
edu.chalmers.dat255.audiobookplayer.model.TagTest.java
edu.chalmers.dat255.audiobookplayer.model.Tag.java
edu.chalmers.dat255.audiobookplayer.model.TrackTest.java
edu.chalmers.dat255.audiobookplayer.model.Track.java
edu.chalmers.dat255.audiobookplayer.util.BookCreatorTest.java
edu.chalmers.dat255.audiobookplayer.util.BookCreator.java
edu.chalmers.dat255.audiobookplayer.util.BookshelfHandlerTest.java
edu.chalmers.dat255.audiobookplayer.util.BookshelfHandler.java
edu.chalmers.dat255.audiobookplayer.util.FileParserTest.java
edu.chalmers.dat255.audiobookplayer.util.FileParser.java
edu.chalmers.dat255.audiobookplayer.util.JsonParserTest.java
edu.chalmers.dat255.audiobookplayer.util.JsonParser.java
edu.chalmers.dat255.audiobookplayer.util.TextFormatterTest.java
edu.chalmers.dat255.audiobookplayer.util.TextFormatter.java
edu.chalmers.dat255.audiobookplayer.util.TrackCreatorTest.java
edu.chalmers.dat255.audiobookplayer.util.TrackCreator.java
edu.chalmers.dat255.audiobookplayer.view.BookshelfFragment.java
edu.chalmers.dat255.audiobookplayer.view.BrowserActivityTest.java
edu.chalmers.dat255.audiobookplayer.view.BrowserActivity.java
edu.chalmers.dat255.audiobookplayer.view.MainActivity.java
edu.chalmers.dat255.audiobookplayer.view.PlayerFragment.java
edu.chalmers.dat255.audiobookplayer.view.ViewPagerAdapter.java