Back to project page AudioBook.
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.
/** * 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(); } } }