First pass at songs view

Using data grid for songs view. Added FileSystemUtil for async folder traversal
This commit is contained in:
watsonb8
2019-05-19 17:25:42 -04:00
parent ae6bd68707
commit 1fcaffb9b1
10 changed files with 272 additions and 3 deletions

View File

@ -0,0 +1,41 @@
using System;
namespace Aurora.Frontend.Views.Songs
{
public class Song
{
public Song()
{
}
/// <summary>
/// The title of the song.
/// </summary>
/// <value></value>
public string Title { get; set; }
/// <summary>
/// The artist of the song.
/// </summary>
/// <value></value>
public string Artist { get; set; }
/// <summary>
/// The album from which the song belongs.
/// </summary>
/// <value></value>
public string Album { get; set; }
/// <summary>
/// The duration of the song.
/// </summary>
/// <value></value>
public string Duration { get; set; }
/// <summary>
/// Extra data associated with a song.
/// </summary>
/// <value></value>
public object Metadata { get; set; }
}
}

View File

@ -0,0 +1,82 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Aurora.Frontend.Utils
{
public class FileSystemUtils
{
public FileSystemUtils()
{
}
/// <summary>
/// Asynchronousely recursively traverse a directory path.
/// </summary>
/// <param name="path">The path to the directory to traverse</param>
/// <param name="extensions">Comma separated list of file extensions to accept</param>
public static List<FileInfo> TraverseFoldersAsync(string path, string extensions)
{
string[] extensionList = extensions.Split(',');
ConcurrentBag<Task> tasks = new ConcurrentBag<Task>();
List<FileInfo> outFiles = new List<FileInfo>();
DirectoryInfo directoryInfo = new DirectoryInfo(path);
tasks.Add(Task.Run(() => Traverse(directoryInfo, tasks, outFiles, extensionList)));
Task waitingTask;
while (tasks.TryTake(out waitingTask))
{
waitingTask.Wait();
}
return outFiles;
}
/// <summary>
/// Recursive method to capture children of a directory.
/// </summary>
/// <param name="dir">The directory to traverse</param>
/// <param name="tasks">The list of currently running tasks</param>
private static void Traverse(DirectoryInfo dir, ConcurrentBag<Task> tasks, List<FileInfo> outFiles, string[] extensions)
{
try
{
DirectoryInfo[] directoryInfos = dir.GetDirectories();
//Enque children
foreach (DirectoryInfo childInfo in directoryInfos)
{
tasks.Add(Task.Run(() => Traverse(childInfo, tasks, outFiles, extensions)));
}
//Collect files
foreach (FileInfo fileInfo in dir.GetFiles())
{
if (extensions.Any(e => e == fileInfo.Extension))
{
//Don't know if this lock is necessary
lock (outFiles)
{
outFiles.Add(fileInfo);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType()} {ex.Message}\n{ex.StackTrace}");
ex = ex.InnerException;
}
}
}
}

View File

@ -1,6 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Aurora.Frontend.Views.Songs.SongsView">
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:dg="clr-namespace:Xamarin.Forms.DataGrid;assembly=Xamarin.Forms.DataGrid"
x:Class="Aurora.Frontend.Views.Songs.SongsView">
<ContentPage.Content>
<Label Text="Songs" />
<dg:DataGrid ItemsSource="{Binding SongsList}" SelectionEnabled="True" SelectedItem="{Binding SelectedSong}"
RowHeight="30" HeaderHeight="50" BorderColor="#CCCCCC" HeaderBackground="#E0E6F8">
<!-- Header -->
<dg:DataGrid.HeaderFontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Tablet>15</OnIdiom.Tablet>
<OnIdiom.Phone>13</OnIdiom.Phone>
<OnIdiom.Desktop>20</OnIdiom.Desktop>
</OnIdiom>
</dg:DataGrid.HeaderFontSize>
<!-- Columns -->
<dg:DataGrid.Columns>
<dg:DataGridColumn Title="Title" PropertyName="Title" Width="2*"/>
<dg:DataGridColumn Title="Album" PropertyName="Album" Width="0.95*"/>
<dg:DataGridColumn Title="Artist" PropertyName="Artist" Width="1*"/>
<dg:DataGridColumn Title="Duration" PropertyName="Duration"/>
</dg:DataGrid.Columns>
<!-- Row Colors -->
<dg:DataGrid.RowsBackgroundColorPalette>
<dg:PaletteCollection>
<Color>#F2F2F2</Color>
<Color>#FFFFFF</Color>
</dg:PaletteCollection>
</dg:DataGrid.RowsBackgroundColorPalette>
</dg:DataGrid>
</ContentPage.Content>
</ContentPage>

View File

@ -10,6 +10,7 @@ namespace Aurora.Frontend.Views.Songs
public SongsView()
{
InitializeComponent();
BindingContext = new SongsViewModel();
}
}
}

View File

@ -1,10 +1,77 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using Aurora.Frontend.Utils;
namespace Aurora.Frontend.Views.Songs
{
public class SongsViewModel
public class SongsViewModel : BaseViewModel
{
#region Fields
private string _pathName = "/Users/brandonwatson/Music/iTunes/iTunes Media/Music";
private string _extensions = ".wav,.mp3,.aiff,.flac,.m4a,.m4b,.wma";
private ObservableCollection<Song> _songsList;
private Song _selectedSong;
#endregion Fields
#region Constructor
public SongsViewModel()
{
_songsList = new ObservableCollection<Song>();
Initialize();
}
#endregion Constructor
#region Properties
public ObservableCollection<Song> SongsList
{
get { return _songsList; }
set
{
if (value != _songsList)
{
_songsList = value;
OnPropertyChanged("SongList");
}
}
}
public Song SelectedSong
{
get { return _selectedSong; }
set
{
if (value != _selectedSong)
{
_selectedSong = value;
OnPropertyChanged("SelectedSong");
}
}
}
#endregion Properties
#region Methods
public void Initialize()
{
List<FileInfo> musicFiles = FileSystemUtils.TraverseFoldersAsync(_pathName, _extensions);
foreach (FileInfo file in musicFiles)
{
TagLib.File tagFile = TagLib.File.Create(file.FullName);
_songsList.Add(new Song()
{
Title = tagFile.Tag.Title,
Album = tagFile.Tag.Album,
Artist = tagFile.Tag.FirstAlbumArtist
});
}
}
#endregion Methods
}
}