Click here to Skip to main content
15,885,216 members
Articles / Mobile Apps / Windows Mobile

Audio Book Player

Rate me:
Please Sign up or sign in to vote.
4.86/5 (36 votes)
11 Jun 2009CPOL6 min read 197.5K   3.5K   84  
Audio player designed specifically for listening to audio books
//
// I have used all persistancy classes for some time.
// all provide consistant error-free behaviour and sufficient performance
//
// (dis)advantages of each method:
//
//  REGISTRY / DATABASE                     FILE SYSTEM
//  --------------------------------------  ------------------------------------------
//  will not survive hard reset             will survive hard reset
//  not easy to corrupt                     visible via file explorer, hence prone to
//                                          deletion/tampering/corruption
//  independant of media storage            requires storage card. Is created on largest
//                                          capacity storage card (if more than 1 present)
//  can have only 1 book collection         resides on media storage, hence one can have
//                                          multiple book collections each residing on a
//                                          different card with its own library                                      
//  adequate mechanism                      very rudamentary directory/file mechanism
//
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using System.Reflection;

namespace abPlayer
{
    public partial class frmLibrary : Form
    {
        // constants: the listview is used to display alternatively books and files
        // with different font sizes (so a book can be selected without the stylus)
        private const float BOOKS_FONT_SIZE = 14.0F;
        private const float FILES_FONT_SIZE = 10.0F;
        private const String FONT_NAME = "Tahoma";
        // icon constants
        private const int BOOK_ICON = 0;
        private const int BACK_ICON = 1;
        private const int BOOKSHELF_ICON = 2;
        // menu constants
        private const String MENU_BOOK = "Delete Book";
        private const String MENU_BOOKSHELF = "Delete Bookshelf";
        private const String MENU_ROOT = "... (Root)";
        // form used to create a new book and add files to an existing book
        public frmFileSelector FileSelector = null;
        // class used for persistant storage 
        public cBasePersistancy BookStore = null;
        // when the listview displays books, the selected index is used.
        // when switching to file view - SelectedBook is used to maintain
        // the relevant book
        public String SelectedBook = "";
        private String ActiveBookShelf = "";
        private String InfoText = "";

        public frmLibrary()
        {
            InitializeComponent();
// ============= choose one persistancy system =======================================
            //BookStore = new cXMLPersistancy(Assembly.GetExecutingAssembly().GetName().Name);
            BookStore = new cFileSystemPersistantcy(Assembly.GetExecutingAssembly().GetName().Name);
            //BookStore = new cRegistryPersistancy(Assembly.GetExecutingAssembly().GetName().Name);
            //BookStore = new cDatabasePersistantcy(Assembly.GetExecutingAssembly().GetName().Name);
// ===================================================================================
            // form used to create a new book and add files to an existing book
            FileSelector = new frmFileSelector();
        }
        // override the ShowDialog function so we can assure we always
        // start on books view
        public new DialogResult ShowDialog()
        {
            ActiveBookShelf = BookStore.GetBooksBookshelf(BookStore.ActiveBookName);
            ShowBooks();
            lsv.Focus();
            return base.ShowDialog();
        }
        // we switch between books/files display
        private void picSwitch_Click(object sender, EventArgs e)
        {
            FileSelector.FlashControl((Control)sender);
            // we can switch to files only if there is a selected book
            if (lsv.Font.Size == BOOKS_FONT_SIZE)
            {
                ListViewItem lsvi = lsvSelectedItem;
                if (lsvi != null)
                    if (lsvi.ImageIndex == BOOK_ICON)
                    {
                        // save the book that we are going to show its files
                        SelectedBook = lsvSelectedItem.Text;
                        // switch display to files
                        ShowFiles(SelectedBook);
                    }
            }
            else
                ShowBooks();
        }

        void ShowBooks()
        {
            // set font size
            lsv.Items.Clear();
            lsv.Font = new Font(FONT_NAME, BOOKS_FONT_SIZE, FontStyle.Regular);
            lsv.SmallImageList = imlBooks;
            LoadLsv((SelectedBook == "") ? BookStore.ActiveBookName :
                SelectedBook);
            SelectedBook = "";
        }
        // load listview with relevant items (books & bookshelves)
        // and select a book
        void LoadLsv(String selectedBook)
        {
            lsv.Items.Clear();
            if (ActiveBookShelf.Length == 0)
            {
                foreach(String bookshelf in BookStore.Bookshelves)
                {
                    lsv.Items.Add(new ListViewItem(bookshelf)).ImageIndex = BOOKSHELF_ICON;
                }
                foreach (String book in BookStore.GetBookshelfBooks(""))
                {
                    lsv.Items.Add(new ListViewItem(book)).ImageIndex = BOOK_ICON;
                }
            }
            else
            {
                lsv.Items.Add(new ListViewItem("... [" +
                    ActiveBookShelf + "]")).ImageIndex = BACK_ICON;
                foreach (String book in BookStore.GetBookshelfBooks(ActiveBookShelf))
                {
                    lsv.Items.Add(new ListViewItem(book)).ImageIndex = BOOK_ICON;
                }
            }
            lsvSelectItem(selectedBook);
        }
        // select a listviewitem
        void lsvSelectItem(String name)
        {
            foreach (ListViewItem item in lsv.Items)
                item.Selected = (item.Text == name);
        }

        void ShowFiles(String book)
        {
            lsv.Items.Clear();
            // set relevant font size 
            lsv.Font = new Font(FONT_NAME, FILES_FONT_SIZE, FontStyle.Regular);
            lsv.SmallImageList = imlFiles;
            // on books with a large number of files this may take some time -
            // we show the wait cursor to indicate it
            Cursor.Current = Cursors.WaitCursor;
            // extract the file name from the file path and load into the listview
            foreach (String file in BookStore.GetBookFiles(book))
            {
                lsv.Items.Add(new ListViewItem(new FileInfo(file).Name)).ImageIndex = 0;
            }
            int n = BookStore.GetBookProperty(eBookProperty.BOOKMARK_ENTRY, book);
            lsv.Items[n].Selected = true;
            // wait is over
            Cursor.Current = Cursors.Default;
            // edt is used to rename a book so is irelevant in file display
            edt.Enabled = false;
            DrawStatusBar(book, BOOK_ICON);
        }

        private void picMenu_Click(object sender, EventArgs e)
        {
            FileSelector.FlashControl((Control)sender);
            // if we are in books diaplay - show books context menu
            // otherwise show files context menu
            if (lsv.Font.Size == BOOKS_FONT_SIZE)
                pumBooks.Show((Control)sender, new Point(picMenu.Left, 
                    pnlTop.Height));
            else
                pumEntries.Show((Control)sender, new Point(picMenu.Left, 
                    pnlTop.Height));
        }

        private void pumBooks_Popup(object sender, EventArgs e)
        {
            // new bookshelf only on top level
            mnuNewBookshelf.Enabled = (ActiveBookShelf.Length == 0);
            // delete entry only available if an item is selected
            ListViewItem lsvi = lsvSelectedItem;
            mnuDel.Enabled = mnuMove.Enabled = false;
            // set enabled, text and submenues depending
            // on the type of the selected item
            if(lsvi != null)
                switch (lsvi.ImageIndex)
                {
                    case BOOKSHELF_ICON:
                        mnuDel.Enabled = true;
                        mnuDel.Text = MENU_BOOKSHELF;
                        mnuMove.Enabled = false;
                        break;
                    case BOOK_ICON:
                        mnuDel.Enabled = true;
                        mnuDel.Text = MENU_BOOK;
                        mnuMove.Enabled = true;
                        // populate mnuMove submenu items (bbokshelves)
                        // make all except the current enabled
                        String oldBookshelf = BookStore.GetBooksBookshelf(lsvi.Text);
                        mnuMove.MenuItems.Clear();
                        MenuItem mnu = new MenuItem();
                        mnuMove.MenuItems.Add(mnu);
                        mnu.Text = MENU_ROOT;
                        mnu.Enabled = (oldBookshelf.Length > 0);
                        mnu.Click += new EventHandler(mnu_Click);
                        foreach (String bookshelf in BookStore.Bookshelves)
                        {
                            mnu = new MenuItem();
                            mnuMove.MenuItems.Add(mnu);
                            mnu.Text = bookshelf;
                            mnu.Enabled = (oldBookshelf != bookshelf);
                            mnu.Click += new EventHandler(mnu_Click);
                        }
                        break;
                    default: // must be a "go one level up" item
                        mnuDel.Enabled = false;
                        mnuMove.Enabled = false;
                        break;
                }
        }

        void mnu_Click(object sender, EventArgs e)
        {
            // move book
            ListViewItem lsvi = lsvSelectedItem;
            if (lsvi == null) return;
            if (lsvi.ImageIndex != BOOK_ICON) return;
            BookStore.SetBooksBookshelf(lsvi.Text,
                (((MenuItem)sender).Text == MENU_ROOT)? "": ((MenuItem)sender).Text);
            LoadLsv(lsvi.Text);
        }

        private void pumEntries_Popup(object sender, EventArgs e)
        {
            // delete entery relevant only if a file is selected
            mnuDelE.Enabled = ((lsv.Items.Count > 0) &&
                                (lsvSelectedItem != null));
            // delete all relevant only if there are files
            mnuDelAll.Enabled = (lsv.Items.Count > 0);
       }

       private void lsv_SelectedIndexChanged(object sender, EventArgs e)
       {
           ListViewItem lsvi = lsvSelectedItem;
           if (lsvi != null)
               lsv.EnsureVisible(lsvi.Index);

           // if we are not in books display - nothing to do
           if (lsv.Font.Size != BOOKS_FONT_SIZE)
           {
               picPlay.Enabled = (lsvSelectedItem != null);
               return;
           }
           // edt and status bar erased
           InfoText = "";
           // play button enabled only if there is a selected book
           // rename enabled only if there is a selected item
           if (lsvi == null)
               picPlay.Enabled = edt.Enabled = false;
           else
           {
              edt.Enabled = (lsvi.ImageIndex != BACK_ICON);
              picPlay.Enabled = (lsvi.ImageIndex == BOOK_ICON);
           }
           // if we have a renameable item ...
           if (edt.Enabled)
           {
               edt.Text = lsvi.Text;
               DrawStatusBar(lsvi.Text, lsvi.ImageIndex);
           }
           else
           {
               edt.Text = "";
               pnl.Refresh();
           }
       }
       // get listview selected item
       ListViewItem lsvSelectedItem
       {
           get
           {
               try
               {
                   return lsv.Items[lsv.SelectedIndices[0]];
               }
               catch{return null;}
           }
       }
        // new bookshelf
       private void mnuNewBookshelf_Click(object sender, EventArgs e)
       {
           // set default new book name (directory name)
           String bookshelf = BookStore.MakeUnique("BOOKSHELF");
           // commit to persistant storage
           BookStore.NewBookshelf(bookshelf);
           LoadLsv(bookshelf);
           lsv.Focus();
       }
       // new book function
        private void mnuNewBook_Click(object sender, EventArgs e)
       {
            // show file selector and do it if user presed the OK button
            if (FileSelector.ShowDialog() == DialogResult.OK)
            {
                // get default new book name (directory name)
                String book = BookStore.MakeUnique(FileSelector.DefaultBookName);
                // commit to persistant storage
                BookStore.NewBook(book, ActiveBookShelf);
                ArrayList files = new ArrayList();
                // get the files of the new book
                files.AddRange(FileSelector.BookFiles);
                // commit to persistent storage
                BookStore.SetBookFiles((String[])files.ToArray(typeof(string)), book);
                // load available books into the listview
                LoadLsv(book);
                lsv.Focus();
            }
        }
        // delete entry (book/bookshelf)
        private void mnuDel_Click(object sender, EventArgs e)
        {
            // only if a deleteable item is selected
            ListViewItem lsvi = lsvSelectedItem;
            if (lsvi != null)
            {
                // remove from persistant storage and listview
                if (lsvi.ImageIndex == BOOK_ICON)
                {
                    BookStore.DeleteBook(lsvi.Text);
                    lsv.Items.RemoveAt(lsvi.Index);
                }
                if (lsvi.ImageIndex == BOOKSHELF_ICON)
                {
                    BookStore.DeleteBookshelf(lsvi.Text);
                    LoadLsv(BookStore.ActiveBookName);
                }
            }
        }
        // handles entry in the rename book edit box
        private void edt_KeyPress(object sender, KeyPressEventArgs e)
        {
            // if we pressed while no book is selected
            if (lsvSelectedItem == null)
            {
                // erase it and make the edit box loose focus
                edt.Text = "";
                e.Handled = true;
                lsv.Focus();
            }
            // if Enter/Return was presed - signifies end of entry
            if ((e.KeyChar == (char)Keys.Return) || (e.KeyChar == (char)Keys.Enter))
            {
                e.Handled = true;
                // do the book rename
                DoRename();
            }
        }

        private void DoRename()
        {
            // if no book selected - can't rename
            ListViewItem lsvi = lsvSelectedItem;
            if(lsvi == null) return;
            if (lsvi.ImageIndex == BACK_ICON) return;
            // trim leading and trailing blanks
            String item = edt.Text.Trim();
            // can't tell when, but on occasions the last character of 
            // the book name was a null character - we remove it
            int p = item.IndexOf('\0');
            if (p != -1)
                item = item.Substring(0, p);
            // if new name same as old - nothing to do
            // in file system and registry persistancies
            // "ab" = "aB" = "Ab" = "AB"
            if (item.ToUpper() == lsvi.Text.ToUpper())
            {
                lsv.Focus();
                return;
            }
            // if we are left with an actual string (not empty)- go do it
            if (item.Length > 0) 
            {
                // ensure uniqueness
                item = BookStore.MakeUnique(item);
                // rename book in persistant storage
                if (lsvi.ImageIndex == BOOK_ICON)
                    BookStore.RenameBook(item, lsvi.Text);
                else
                    BookStore.RenameBookShelf(item, lsvi.Text);
                // reload listview to maintain sort order
                LoadLsv(item);
            }
            // put back in the edit box (in case we had to make some changes)
            lsvi = lsvSelectedItem;
            if (lsvi != null)
            {
                edt.Text = lsvi.Text;
                // reflect in status bar
                DrawStatusBar(lsvi.Text, lsvi.ImageIndex);
            }
            lsv.Focus();
        }
        // Add files to a book
        private void mnuAddFiles_Click(object sender, EventArgs e)
        {
            // show file selection screen
            if (FileSelector.ShowDialog() == DialogResult.OK)
            {
                // add the new files to the existing ones
                BookStore.SetBookFiles((String[])FileSelector.BookFiles.ToArray(typeof(string)), SelectedBook);
                // extract the file name from the file path and add to listview
                foreach (String file in FileSelector.BookFiles)
                {
                    lsv.Items.Add(new ListViewItem(new FileInfo(file).Name)).ImageIndex = 0;
                }
                // reflect in status bar
                DrawStatusBar(SelectedBook, BOOK_ICON);
            }
        }
        // delete entry
        private void mnuDelE_Click(object sender, EventArgs e)
        {
            BookStore.DeleteBookFile(lsvSelectedItem.Index, SelectedBook);
            // delete from listview
            lsv.Items.RemoveAt(lsvSelectedItem.Index);
            // reflect in status bar
            DrawStatusBar(SelectedBook, BOOK_ICON);
        }
        // delete all files
        private void mnuDelAll_Click(object sender, EventArgs e)
        {
            // clear listview
            lsv.Items.Clear();
            // commit
            BookStore.DeleteBookFiles(SelectedBook);
            DrawStatusBar(SelectedBook, BOOK_ICON);
        }
        // show info on status bar:
        // book name, file count and current position - index and timeline
        private void DrawStatusBar(String item, int itemType)
        {
            InfoText = (itemType == BOOK_ICON) ?
                BookStore.GetBookFileCount(item) + "  Files; Location: " +
                    (BookStore.GetBookProperty(eBookProperty.BOOKMARK_ENTRY, item) + 1).ToString() +
                    " / " +
                    LocationToString(BookStore.GetBookProperty(eBookProperty.BOOKMARK_LOCATION, item)) :
                    BookStore.GetBookshelfBooks(item).GetLength(0).ToString() + " Books;";
            pnl.Refresh();
        }
        // convert timeline location to string HH:MM:SS
        public String LocationToString(int loc)
        {
            int s = loc % 60;
            int m = ((loc - s) / 60) % 60;
            int h = (((loc - s) / 60) - m) / 60;
            if (h > 0)
                return h.ToString() + ":" +
                      ((m < 10) ? "0" + m.ToString() + ":" : m.ToString() + ":") +
                      ((s < 10) ? "0" + s.ToString() : s.ToString());
            else
                return m.ToString() + ":" +
                      ((s < 10) ? "0" + s.ToString() : s.ToString());
        }
        // play selected book
        private void picPlay_Click(object sender, EventArgs e)
        {
            FileSelector.FlashControl((Control)sender);
            ActivateBook();
        }
        // activate a book
        void ActivateBook()
        {
            // if we are on book display take selected index otherwise
            // take book stored in SelectgedBook
            // indicate to frmPlayer by OK result
            if (lsv.Font.Size == BOOKS_FONT_SIZE)
            {
                ListViewItem lsvi = lsvSelectedItem;
                if (lsvi != null)
                    if(lsvi.ImageIndex == BOOK_ICON)
                    {
                        BookStore.ActiveBookName = lsvi.Text;
                        DialogResult = DialogResult.OK;
                    }
            }
            else
            {
                if (BookStore.GetBookFileCount(SelectedBook) > 0)
                {
                    ListViewItem lsvi = lsvSelectedItem;
                    if (lsvi != null)
                    {
                        BookStore.ActiveBookName = SelectedBook;
                        if (BookStore.GetBookProperty(eBookProperty.BOOKMARK_ENTRY, "") != lsvi.Index)
                        {
                            BookStore.SetBookProperty(eBookProperty.BOOKMARK_ENTRY, lsvi.Index, "");
                            BookStore.SetBookProperty(eBookProperty.BOOKMARK_LOCATION, 0, "");
                        }
                    }
                    DialogResult = DialogResult.OK;
                }
            }
        }
        // we show the input panel when the edit box (rename area) gets focus
        private void edt_GotFocus(object sender, EventArgs e)
        {
            inp.Enabled = true;
        }
        // hide inp when we exit the edit box
        private void NotEdt_GotFocus(object sender, EventArgs e)
        {
            inp.Enabled = false;
        }
        // cancel button pressed
        // indicate by Cancel dialog result
        private void picBack_Click(object sender, EventArgs e)
        {
            FileSelector.FlashControl((Control)sender);
            SelectedBook = "";
            DialogResult = DialogResult.Cancel;
        }
        // find uses a recursive function (findbooks) to scan the whole
        // file system, it then looks for media files in each directory
        // found and if exists - creates a new book for this directory
        private void mnuFind_Click(object sender, EventArgs e)
        {
            Cursor.Current = Cursors.WaitCursor;
            FindBooks("\\");
            ShowBooks();
            Cursor.Current = Cursors.Default;
        }

        private void FindBooks(String path)
        {
            String[] dirs = Directory.GetDirectories(path);
            // sort them
            Array.Sort(dirs);
            foreach (String dir in dirs)
            {
                String[] files = FileSelector.GetMediaFiles(dir);
                if (files.GetLength(0) > 0)
                {
                    // add new book
                    String book = BookStore.MakeUnique("(Auto) " + new FileInfo(dir).Name);
                    BookStore.NewBook(book, ActiveBookShelf);
                    // sort
                    Array.Sort(files);
                    // add files
                    BookStore.SetBookFiles(files, book);
                }
                FindBooks(dir);
            }
        }

        private void pnl_Paint(object sender, PaintEventArgs e)
        {
            // erase the whole control face with its backcolor
            e.Graphics.FillRectangle(new SolidBrush(pnl.BackColor), 0, 0,
                pnl.Width, pnl.Height);
            // if no files are displayed - we are done
            if (InfoText.Length == 0) return;
            // get string sizes
            SizeF strWH = e.Graphics.MeasureString(InfoText, this.Font);
            // display string in mid horizontal and vertical point
            e.Graphics.DrawString(InfoText, this.Font, new SolidBrush(Color.White),
                4, 4);
        }

        private void lsv_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Up:
                    NavigateLsv(-1);
                    break;
                case Keys.Down:
                    NavigateLsv(1);
                    break;
                case Keys.Enter:
                    ActivateEntry();
                    break;
            }
            e.Handled = true;
        }

        void ActivateEntry()
        {
            // only relevant in book display
            if (lsv.Font.Size != BOOKS_FONT_SIZE) return;
            ListViewItem lsvi = lsvSelectedItem;
            if (lsvi == null) return;
            switch (lsvi.ImageIndex)
            {
                case BOOKSHELF_ICON:
                    ActiveBookShelf = lsvi.Text;
                    LoadLsv(BookStore.ActiveBookName);
                    break;
                case BACK_ICON:
                    ActiveBookShelf = "";
                    LoadLsv(BookStore.ActiveBookName);
                    break;
                case BOOK_ICON:
                    ActivateBook();
                    break;
            }
        }

        private void lsv_ItemActivate(object sender, EventArgs e)
        {
            ActivateEntry();
        }

        void NavigateLsv(int d)
        {
            if(lsv.Items.Count < 1) return;
            if (lsvSelectedItem == null)
                lsv.Items[0].Selected = true;
            else
            {
                int i = lsvSelectedItem.Index + d;
                if ((i >= 0) &&
                    (i < lsv.Items.Count))
                    lsv.Items[i].Selected = true;
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions