# Linaris
[](https://codefirst.iut.uca.fr/corentin.lemaire/Linaris_MAUI_SAE_201)
[](https://codefirst.iut.uca.fr/sonar/dashboard?id=Linaris_LEMAIRE_LABORIE)
## Overview
Linaris is a music and playlist management application, as well as a source of information about various popular albums and tracks. Our app is available on Windows and Android platforms through Visual Studio.
## Trailer
[](https://opencast.dsi.uca.fr/paella/ui/watch.html?id=41e87f72-a922-44a3-ad2c-1432540bace0)
## Documentation
You can find all the documentation [here](https://codefirst.iut.uca.fr/git/corentin.lemaire/Linaris_MAUI_SAE_201#documentation-1)
### Prerequisites
- Visual Studio 2022
- Git
- .NET 7.0 SDK Framework
- Android SDK
## Getting Started
1. Clone the repository
2. Open **'Linaris.sln'** in Visual Studio 2022
3. Start the application on the platform you want (for Android, you must use an Emulator)
5. Build and run the Linaris
## Features
- Manage your own titles
- Manage playlists
- Listen to your titles
- Get informed on famous albums and titles
## Features not yet available
- Customize listening of your music (balance, pitch...)
- Login system
- Listening mode systems (nightcore, vynile, cinema...)
## Known issues
### Cross-platform
- Playing music reset when a page change
### Android
- Cut custom titles display in a playlist
- Footer not aestetic
- Footer controls too hard to find
### Windows
- The Layout doesn't disappear as the screen is shrinking
## Complex features
- Music player
- We went through lot's of issues when trying to implement the music player. This was the most complex feature of Linaris. Indeed, we really wanted to offer the possibility to play music on both Windows and Android, and not only one platform.
- Serialization
- The serialization part took lot's of time. We tried our best to make it with LINQ to enjoy its the power. However, many problems delayed us a lot.
- Playlist system
- The playlist system was hard to implement with the options to loop and to shuffle the playlist. We had to refactor our code to get a way better option.
- Styles
- While coding the styles, we encountered an issue with the XAML language where the TargetName didn't work. We had to work around the problem, which ended up costing us a lot of time.
## Built with
- [.NET MAUI 7.0](https://learn.microsoft.com/fr-fr/dotnet/maui/get-started/installation)
* [C#](https://learn.microsoft.com/fr-fr/dotnet/csharp/)
* [XAML](https://learn.microsoft.com/fr-fr/dotnet/desktop/wpf/xaml/?view=netdesktop-7.0)
- [CodeFirst](https://codefirst.iut.uca.fr/)
* [Drone](https://codefirst.iut.uca.fr/drone)
* [SonarQube](https://codefirst.iut.uca.fr/sonar/)
- [Visual Studio 2022](https://visualstudio.microsoft.com/fr/vs/)
- [Doxygen](https://www.doxygen.nl/index.html)
## Contributors
* [LEMAIRE Corentin](https://codefirst.iut.uca.fr/git/corentin.lemaire)
* [LABORIE Louis](https://codefirst.iut.uca.fr/git/louis.laborie)
## Documentation
### Description de l'architecture

Notre programme se compose de deux parties distinctes : les **vues** et le **modèle**.
Pour les **vues**, cela correspond aux visuels de l'application. Pour le modèle, cela correspond à la logique de l'application, son fonctionnement.
Ces deux parties sont liées par le **DataBinding** liant les données et le modèle avec les vues.
Pour permettre cela, nous avons mis en oeuvre le patron de conception de **"façade"** grâce à la classe *Manager*. En effet, cette classe est un *point d'entrée* vers le modèle, où le fonctionnement est caché par des méthodes et propriétés comme c'est le cas pour les listes d'objets. Les vues ne savent pas comment ces listes sont créées, elles ne font que l'utiliser. Cette classe **gère le modèle** mais est aussi l'interlocuteur des vues. C'est un **lien** entre ces deux parties. Cela nous permettait de bien **séparer les vues et le modèle**, mais aussi de **réduire** et **clarifier** le code présent dans le code-behind des vues.
De plus, nous avons intégré le patron de conception de **"stratégie"** grâce à l'interface *IDataManager* et l'*injection de dépendance par le contructeur* du Manager. En effet, cela nous permet de *changer de méthode* de sérialisation très facilement. Nous pouvons donc utiliser les stubs pour tester le programme et repasser sur la sérialisation XML pour utiliser l'application en un simple changement. Cela permet une **adaptabilité** de code non négligeable. Il suffit de changer le paramètre donné au constructeur du Manager.
En outre, pour que cela fonctionne, l'interface *IDataManager* est indispensable, afin de s'assurer que toutes les méthodes de sérialisation aient bien **toutes les méthodes** nécessaires au bon fonctionnement de l'application. Grâce à cela, nous n'avons pas à nous demander quelle est la méthode de sérialisation pusique l'appel de la méthode de l'interface appellera la méthode de la technique de sérialisation demandée. Nous obtenons un résultat différent selon la méthode **sans changer le code**. Par exemple, dans la classe *App*, nous pouvons simplement appeler la méthode de sérialisation du manager, nous n'avons pas à changer ce code à chaque changement de méthode de sérialisation. Cela nous fait **gagner en temps** et en **adaptabilité de code**.
Grâce à ces patrons de conception, nous pouvons passer des stubs à la sérialisation XML bien plus facilement et **sans changer le code** des vues.
Afin de lier nos différents projets, nous avons ajouté des **dépendances**. En effet, pour que l'*application console*, les *vues* et les *tests unitaires* fonctionnent, nous avons ajouté la dépendance vers le *modèle*.
Par ailleurs, nous avons aussi ajouté la dépendance vers **XUnit** pour le projet des *tests unitaires* et la dépendance vers **ToolKit** pour les vues afin de pouvoir utiliser *MediaElement* pour la lecture de médias.
Nous récupérons les musiques des utilisateurs par le biais du chemin absolu du système de fichier.
Nous proposons Linaris sur les plateformes **Windows** et **Android**.
Enfin, nous avons utilisé *Drone* pour notre **CI** et **CD**. En effet, nous avons ajoutés une pipeline avec un job de **build**, un job de **tests** de l'application et un job d'**inspection de code** pour pouvoir y accéder via *SonarCube*. Pour la CD, nous avons aussi un job pour la génération automatique de **Doxygen**.
---
### Diagramme de classe
```plantuml
@startuml
class Artist {
+ string Name
+ Artist(string name)
+ Artist()
+ boolean Equals(object obj)
+ int GetHashCode()
+ string ToString()
}
class Album {
- {static} long nbAlbum = 0
+ long ID
+ string Name
+ string Description
+ string ImageURL
+ string Information
+ ObservableCollection InfoTitles
+ Album(string name, string imageURL, Artist artist, string description, string information)
+ Album()
+ void AddTitle(InfoTitle title)
+ void RemoveTitle(InfoTitle title)
+ boolean Equals(object obj)
+ int GetHashCode()
+ string ToString()
}
class Playlist {
+ event PropertyChangedEventHandler PropertyChanged
+ string Name
+ string Description
+ ObservableCollection Titles
+ string ImageURL
+ int Index
+ boolean Shuffle = false
+ boolean looptitle = false
+ IEnumerable played
+ bool IsSubMenuVisible
+ Playlist(string nom, string description, string imageURL)
+ Playlist()
+ void AddTitle(CustomTitle morceau)
+ void RemoveTitle(CustomTitle morceau)
+ CustomTitle NextTitle()
+ CustomTitle PreviousTitle()
+ CustomTitle GetCurrentTitle()
+ boolean Equals(object? obj)
+ int GetHashCode()
+ string ToString()
- {static} int RandomGenerator(int n)
+ bool HasCustomTitle(CustomTitle customTitle)
# void OnPropertyChanged([CallerMemberName] string propertyName = null)
}
class Title {
+ event PropertyChangedEventHandler PropertyChanged
+ string Name
+ string ImageURL
+ string Information
+ Title(string nom, string file_Name, string informations)
+ boolean Equals(object obj)
+ int GetHashCode()
+ string ToString()
# void OnPropertyChanged([CallerMemberName] string propertyName = null)
}
enum Genre
{
HIP_HOP
POP
ROCK
ELECTRO
CLASSIQUE
JAZZ
VARIETE_FRANCAISE
VARIETE_INTERNATIONALE
REGGAE
RAP
RNB
DISCO
BLUES
COUNTRY
FUNK
GOSPEL
METAL
K_POP
}
class CustomTitle {
+ string Path
+ bool IsSubManuVisible
+ bool IsPlaylistMenuVisible
+ bool IsNewPlaylistMenuVisible
+ CustomTitle(string name, string imageURL, string information, string path)
+ CustomTitle()
+ boolean Equals(object obj)
+ int GetHashCode()
+ string ToString()
}
class InfoTitle {
+ string Description
+ IEnumerable Feat
+ long AlbumID
+ InfoTitle(string name, string imageURL, string information, string description, Genre genre, long albumID)
+ InfoTitle()
+ void AddFeat(Artist artist)
+ void RemoveFeat(Artist artiste)
+ boolean Equals(object obj)
+ int GetHashCode()
+ string ToString()
}
class Manager {
+ event PropertyChangedEventHandler PropertyChanged
+ {static} readonly int MAX_NAME_LENGTH = 75
+ {static} readonly int MAX_DESCRIPTION_LENGTH = 500
+ {static} readonly string DEFAULT_NAME = "Unknown"
+ {static} readonly string DEFAULT_URL = "none.png"
+ {static} readonly string DEFAULT_DESC = ""
+ ObservableCollection Albums
+ ObservableCollection CustomTitles
+ ObservableCollection InfoTitles
+ ObservableCollection Playlists
+ ObservableCollection Artists
+ Album CurrentAlbum
+ Playlist CurrentPlaylist
+ InfoTitle CurrentInfoTitle
+ CustomTitle CurrentPlaying
+ Dictionary> AlbumsFromArtist
+ Dictionary> infoTitlesFromArtist
+ Manager(IDataManager dataManager)
+ void LoadDictionaries
+ void NextTitle()
+ void PreviousTitle()
+ CustomTitle CurrentTitle()
+ void Loop()
+ void Shuffle()
+ void AddAlbum(Album album)
+ void AddCustomTitle(CustomTitle title)
+ void AddInfoTitle(InfoTitle title)
+ void AddPlaylist(Playlist playlist)
+ void AddArtist(Artist artist)
+ void RemoveAlbum(Album album)
+ void RemoveCustomTitle(CustomTitle title)
+ void RemoveInfoTitle(InfoTitle title)
+ void RemovePlaylist(Playlist playlist)
+ ObservableCollection GetPlaylists()
+ ObservableCollection GetAlbums()
+ ObservableCollection GetCustomTitles()
+ ObservableCollection GetInfoTitles()
+ ObservableCollection GetArtists()
+ void LoadSerialization()
+ void SaveSerialization()
+ Playlist GetPlaylistByName(string name)
+ Artist GetArtistByName(string name)
+ CustomTitle GetCustomTitleByPath(string path)
+ InfoTitle GetInfoTitleByName(string name)
+ Album GetAlbumByName(string name)
+ Album GetAlbumById(long id)
# void OnPropertyChanged([CallerMemberName] string propertyName = null)
}
Album o--> "+ Artist" Artist
Album *--> "- infoTitles*" InfoTitle
Album ..> Manager
Artist ..> Manager
Playlist o--> "- titles*" CustomTitle
Playlist ..> Manager
Title ..> Manager
CustomTitle --|> Title
InfoTitle --|> Title
InfoTitle o--> "+ Genre" Genre
InfoTitle o--> "- feat*" Feat
Manager o--> "- albums*" Album
Manager o--> Album
Manager o--> "- artists*" Artist
Manager o--> "- infoTitles*" InfoTitle
Manager o--> InfoTitle
Manager o--> "- playlists*" Playlist
Manager o--> "- customTitles*" CustomTitle
Manager o--> "- currentAlbum" Album
Manager o--> "- currentPlaylist" Playlist
Manager o--> "- currentInfoTitle" InfoTitle
Manager o--> "- currentPlaying" CustomTitle
@enduml
```
#### Explications
###### Note
*Les classes CustomTitle, Playlist et Manager héritent de l'interface INotifyPropertyChanged afin de permettre le DataBinding de ces classes*
##### Album
Cette classe sert à modéliser des **albums** de musique. Ils ne sont pas jouables et sont uniquement implantés à titre **informatif**. Dans ce but, elle comporte plusieurs attributs comme un *nom*, une *description*, des *informations complémentaires* ou encore une *URL* pour son image (la pochette).
##### Artist
Cette classe sert à modéliser les ***artistes*** qui réalise les albums. Il ne possède qu'un *nom* en atrribut.
##### Title
Cette classe sert à modéliser différents **titres**. Il possède plusieurs attributs comme un *nom*, une *URL* pour son image (cover) ainsi que des *informations complémentaires*.
##### InfoTitle
Cette classe hérite de **Title**. Elle hérite donc de tout ses attributs. Comme son nom l'indique, ces titres ont comme spécificité d'être uniquement informatif. Ils sont contenus dans les albums. Elle possède également d'autres attributs comme
une description, un artiste (classe **Artist**) et une liste d'artiste pour les featuring.
##### CustomTitle
Cette classe hérite de **Title**. Elle hérite donc de tout ses attributs. Ces titres sont destinés à pouvoir être ajouter dans des playlists et à être jouer.
Hormis les attributs hérités, cette classe possède un attribut path (chemin) qui lui permet d'indiquer où se situe le fichier audio.
##### Playlist
Cette classe possède une structure similaire à la classe **Album**. Elle contient des titres personnalisés (**CustomTitle**). Les morceaux sont joués dans un ordre précis (du premier jusqu'au dernier). Cette classe hérite des attributs de Title. Cette classe permet la gestion des playlists, soit l'accès à la musique suivante, précédente en fonction de la demande de l'utilisateur (en boule, aléatoire). Cela permet une lecture de musiques d'une playlist en boucle sans problème.
##### Manager
Notre classe **Manager** est une interface entre le code-behind et les vues. Il permet de gérer les données grâce au *DataManager* et de **gérer** les fonctionnalités de l'application comme les playlists. Cette classe fonctionne avec le *parton de conception de façade*, étant une interface, un point d'entrée de l'application vers le modèle. De plus, nous utilisons un deuxième patron de conception, qui est la *stratégie*, avec une *injection de dépendance* par le constructeur de la classe. En effet, afin de créer un Manager, il faut joindre la méthode de sérialisation (stubs ou LINQ dans notre cas).
---
```plantuml
@startuml
interface IDataManager {
+ void AddAlbum(Album album)
+ void AddAlbums(List albumsList)
+ void AddArtist(Artist artist)
+ void AddArtists(List artistsList)
+ void AddPlaylist(Playlist playlist)
+ void AddPlaylists(List playlistsList)
+ void AddCustomTitle(CustomTitle title)
+ void AddCustomTitles(List customTitlesList)
+ void AddInfoTitle(InfoTitle title)
+ void AddInfoTitles(List infoTitlesList)
+ ObservableCollection GetCustomTitles()
+ CustomTitle GetCustomTitleByPath(string custom)
+ ObservableCollection GetInfoTitles()
+ InfoTitle GetInfoTitleByName(string name)
+ ObservableCollection GetAlbums()
+ Album GetAlbumByName(string name)
+ Album GetAlbumById(long id)
+ List GetArtists()
+ Artist GetArtistByName(string name)
+ ObservableCollection GetPlaylists()
+ Playlist GetPlaylistByName(string name)
+ void UpdateCustomTitle(CustomTitle title, string name, string url, string info, string path)
+ void UpdateCustomTitleByPath(string path, string name, string newUrl, string info, string newPath)
+ void UpdateInfoTitle(InfoTitle title, string name, string url, string info, Artist artist, string description, Genre genre)
+ void UpdateInfoTitleByName(string name, string newUrl, string info, Artist artist, string description, Genre genre)
+ void UpdateAlbum(Album album, string name, string url, Artist artist, string description, string info)
+ void UpdateAlbumByName(string name, string newUrl, Artist artist, string description, string info)
+ void UpdateAlbumByArtistName(Album album, string name, string url, string artist, string description, string info)
+ void UpdateAlbumByNameByArtistName(string name, string newUrl, string artist, string description, string info)
+ void UpdatePlaylist(Playlist playlist, string name, string description, string url)
+ void UpdatePlaylistByName(string name, string description, string newUrl)
+ void UpdateArtist(Artist artist, string name)
+ void UpdateArtistByName(string name, string newName)
+ void RemoveAlbum(Album album)
+ void RemoveAlbums(List albumsList)
+ void RemoveArtist(Artist artist)
+ void RemoveArtists(List artistsList)
+ void RemovePlaylist(Playlist playlist)
+ void RemovePlaylists(List playlistsList)
+ void RemoveCustomTitle(CustomTitle title)
+ void RemoveCustomTitles(List customTitlesList)
+ void RemoveInfoTitle(InfoTitle title)
+ void RemoveInfoTitles(List infoTitlesList)
+ void LoadSerialization()
+ void SaveSerialization()
+ bool ExistsPlaylist(Playlist playlist)
+ bool ExistsPlaylistByName(string name)
+ bool ExistsAlbum(Album album)
+ bool ExistsAlbumByName(string name)
+ bool ExistsArtist(Artist artist)
+ bool ExistsArtistByName(string name)
+ bool ExistsCustomTitle(CustomTitle title)
+ bool ExistsCustomTitleByName(string name)
+ bool ExistsInfoTitle(InfoTitle title)
+ bool ExistsInfoTitleByName(string name)
}
class StubManager {
+ StubAlbum StubAlbum
+ StubArtist StubArtist
+ StubCustomTitle StubCustomTitle
+ StubInfoTitle StubInfoTitle
+ StubPlaylist StubPlaylist
+ StubManager()
+ ObservableCollection GetAlbums()
+ List GetArtists()
+ ObservableCollection GetPlaylists()
+ ObservableCollection GetCustomTitles()
+ ObservableCollection GetInfoTitles()
+ void AddAlbum(Album album)
+ void AddCustomTitle(CustomTitle title)
+ void AddInfoTitle(InfoTitle title)
+ {static} void AddFeat(InfoTitle infoTitle, Artist artist)
+ void AddPlaylist(Playlist playlist)
+ void AddArtist(Artist artist)
+ void RemoveAlbum(Album album)
+ void RemoveCustomTitle(CustomTitle title)
+ void RemoveInfoTitle(InfoTitle title)
+ void RemovePlaylist(Playlist playlist)
+ void RemoveArtist(Artist artist)
+ void LoadSerialization()
+ void SaveSerialization()
+ CustomTitle GetCustomTitleByPath(string custom)
+ InfoTitle GetInfoTitleByName(string name)
+ Album GetAlbumByName(string name)
+ Artist GetArtistByName(string name)
+ void AddAlbums(List albumsList)
+ void AddArtists(List artistsList)
+ void AddPlaylists(List playlistsList)
+ void AddCustomTitles(List customTitlesList)
+ void AddInfoTitles(List infoTitlesList)
+ Playlist GetPlaylistByName(string name)
+ void UpdateCustomTitle(CustomTitle title, string name, string url, string info, string path)
+ void UpdateCustomTitleByPath(string path, string name, string newUrl, string info, string newPath)
+ void UpdateInfoTitle(InfoTitle title, string name, string url, string info, Artist artist, string description, Genre genre)
+ void UpdateInfoTitleByName(string name, string newUrl, string info, Artist artist, string description, Genre genre)
+ void UpdateAlbum(Album album, string name, string url, Artist artist, string description, string info)
+ void UpdateAlbumByName(string name, string newUrl, Artist artist, string description, string info)
+ void UpdateAlbumByArtistName(Album album, string name, string url, string artist, string description, string info)
+ void UpdateAlbumByNameByArtistName(string name, string newUrl, string artist, string description, string info)
+ void UpdatePlaylist(Playlist playlist, string name, string description, string url)
+ void UpdatePlaylistByName(string name, string description, string newUrl)
+ void UpdateArtist(Artist artist, string name)
+ void UpdateArtistByName(string name, string newName)
+ void RemoveAlbums(List albumsList)
+ void RemoveArtists(List artistsList)
+ void RemovePlaylists(List playlistsList)
+ void RemoveCustomTitles(List customTitlesList)
+ void RemoveInfoTitles(List infoTitlesList)
+ bool ExistsPlaylist(Playlist playlist)
+ bool ExistsPlaylistByName(string name)
+ bool ExistsAlbum(Album album)
+ bool ExistsAlbumByName(string name)
+ bool ExistsArtist(Artist artist)
+ bool ExistsArtistByName(string name)
+ bool ExistsCustomTitle(CustomTitle title)
+ bool ExistsCustomTitleByName(string name)
+ bool ExistsInfoTitle(InfoTitle title)
+ bool ExistsInfoTitleByName(string name)
+ Album GetAlbumById(long id)
}
class LinqXmlSerialization {
- string XMLPATH
- string XMLFILEPLAYLISTS
- string XMLFILECUSTOMS
+ StubInfoTitle StubInfoTitle
+ List Artists
+ ObservableCollection Albums
+ ObservableCollection Playlists
+ ObservableCollection InfoTitles
+ ObservableCollection CustomTitles
+ LinqXmlSerialization(string pathDirectory)
+ void AddAlbum(Album album)
+ void AddArtist(Artist artist)
+ void AddCustomTitle(CustomTitle title)
+ void AddInfoTitle(InfoTitle title)
+ void AddPlaylist(Playlist playlist)
+ ObservableCollection GetAlbums()
+ List GetArtists()
+ ObservableCollection GetCustomTitles()
+ ObservableCollection GetInfoTitles()
+ ObservableCollection GetPlaylists()
+ void RemoveAlbum(Album album)
+ void RemoveArtist(Artist artist)
+ void RemoveCustomTitle(CustomTitle title)
+ void RemoveInfoTitle(InfoTitle title)
+ void RemovePlaylist(Playlist playlist)
+ void LoadSerialization()
+ void SaveSerialization()
+ void LoadPlaylists()
+ void SavePlaylists()
+ void LoadArtists()
+ void SaveArtists()
+ void LoadCustomTitles()
+ void SaveCustomTitles()
+ void LoadAlbums()
+ void SaveAlbums()
+ void LoadInfoTitles()
+ void SaveInfoTitles()
+ {static} Genre GetGenreByName(string genre)
+ InfoTitle GetInfoTitleByName(string name)
+ Artist GetArtistByName(string name)
+ Album GetAlbumByName(string name)
+ Album GetAlbumById(string id)
+ CustomTitle GetCustomTitleByPath(string custom)
+ void AddAlbums(List albumsList)
+ void AddArtists(List artistsList)
+ void AddPlaylists(List playlistsList)
+ void AddCustomTitles(List customTitlesList)
+ void AddInfoTitles(List infoTitlesList)
+ Playlist GetPlaylistByName(string name)
+ void UpdateCustomTitle(CustomTitle title, string name, string url, string info, string path)
+ void UpdateCustomTitleByPath(string path, string name, string newUrl, string info, string newPath)
+ void UpdateInfoTitle(InfoTitle title, string name, string url, string info, Artist artist, string description, Genre genre)
+ void UpdateInfoTitleByName(string name, string newUrl, string info, Artist artist, string description, Genre genre)
+ void UpdateAlbum(Album album, string name, string url, Artist artist, string description, string info)
+ void UpdateAlbumByName(string name, string newUrl, Artist artist, string description, string info)
+ void UpdateAlbumByArtistName(Album album, string name, string url, string artist, string description, string info)
+ void UpdateAlbumByNameByArtistName(string name, string newUrl, string artist, string description, string info)
+ void UpdatePlaylist(Playlist playlist, string name, string description, string url)
+ void UpdatePlaylistByName(string name, string description, string newUrl)
+ void UpdateArtist(Artist artist, string name)
+ void UpdateArtistByName(string name, string newName)
+ void RemoveAlbums(List albumsList)
+ void RemoveArtists(List artistsList)
+ void RemovePlaylists(List playlistsList)
+ void RemoveCustomTitles(List customTitlesList)
+ void RemoveInfoTitles(List infoTitlesList)
+ bool ExistsPlaylist(Playlist playlist)
+ bool ExistsPlaylistByName(string name)
+ bool ExistsAlbum(Album album)
+ bool ExistsAlbumByName(string name)
+ bool ExistsArtist(Artist artist)
+ bool ExistsArtistByName(string name)
+ bool ExistsCustomTitle(CustomTitle title)
+ bool ExistsCustomTitleByName(string name)
+ bool ExistsInfoTitle(InfoTitle title)
+ bool ExistsInfoTitleByName(string name)
}
class StubAlbum {
+ StubArtist StubArtist
+ ObservableCollection Albums
+ StubAlbum()
+ ObservableCollection GetAlbums()
+ Album GetAlbumByName(string name)
+ void AddAlbum(Album album)
+ void RemoveAlbum(Album album)
}
class StubArtist {
+ List Artists
+ StubArtist()
+ List GetArtists()
+ Artist GetArtistByName(string name)
+ void AddArtist(Artist artist)
+ void RemoveArtist(Artist artist)
}
class StubCustomTitle {
+ ObservableCollection CustomTitles
+ StubCustomTitle()
+ ObservableCollection GetCustomTitles()
+ List GetCustomTitlesByNames(List names)
+ void AddCustomTitle(CustomTitle customTitle)
+ void RemoveCustomTitle(CustomTitle customTitle)
}
class StubInfoTitle {
+ StubAlbum StubAlbum
+ ObservableCollection InfoTitles
+ StubInfoTitle()
+ ObservableCollection GetInfoTitles()
+ List GetInfoTitlesByNames(List names)
+ void AddInfoTitle(InfoTitle title)
+ void RemoveInfoTitle(InfoTitle title)
+ {static} void AddFeat(InfoTitle infoTitle, Artist artist)
+ {static} void RemoveFeat(InfoTitle infoTitle, Artist artist)
}
class StubPlaylist {
+ ObservableCollection Playlists
+ StubPlaylist()
+ ObservableCollection GetPlaylists()
+ Playlist GetPlaylistByName(string name)
+ void AddPlaylist(Playlist playlist)
+ void RemovePlaylist(Playlist playlist)
}
LinqXmlSerialization --|> IDataManager
LinqXmlSerialization o--> "- stubInfoTitle" StubInfoTitle
StubManager --|> IDataManager
StubManager *--> "+ StubArtist" StubArtist
StubManager *--> "+ StubPlaylist" StubPlaylist
StubManager *--> "+ StubAlbum" StubAlbum
StubManager *--> "+ StubInfoTitle" StubInfoTitle
StubManager *--> "+ StubCustomTitle" StubCustomTitle
StubInfoTitle *--> "- stubAlbum" StubAlbum
StubAlbum *--> "- stubArtist" StubArtist
@enduml
```
#### Explications
##### IDataManager
Cette **interface** nous permet de passer d'un sérialisation à une autre. En effet, chacune des méthodes qui implémenteront IDataManager auront les méthodes telles que les *CRUD* et celles de la gestion de la *sérialisation*.
##### LinqXmlSerialization
Notre **sérialisation** fonctionne avec lecture/écriture dans des fichiers **XML**. Pour cela, nous utilisons la bibliothèque *LINQ_XML* qui nous permet de créer et de modifier les différents fichiers. Pour la sérialisation des *CustomTitle* et des *Playlists* nous utilisons le LINQ. Cependant, pour les classes *InfoTitle*, *Artist* et *Album*, ces données sont récupérées des Stubs, étant statiques et à but *informatif*. Cette sérialisation a été testée et approuvée sur **Windows** et **Android**.
##### StubManager
Le StubManager est un **point d'entrée** vers les Stubs, permettant de les **gérer** avec des *requêtes CRUD*. Ces Stubs vont permettre de *tester* l'application, le modèle et l'application en générant des données directement dans le code. Cette classe permet de séparer le code de chaque Stub afin de le gérer avec plus de facilité.
##### StubAlbum
Le StubAlbum permet de **générer des albums** avec des informations écrites dans le code. Il permet également de les **gérer**. Cette classe utilise *StubArtist* afin de lier l'album avec l'artiste. Ce stub est aussi utilisé afin d'afficher ces albums à titre *informatif*.
##### StubArtist
Le StubArtist permet de **créer des informations sur des artistes**S. Il permet de plus de créer les artistes pour les *InfoTitle* à afficher dans les titres informatifs présents dans les albums.
##### StubInfoTitle
Le StubInfoTitle est un stub avec des valeurs pour les **titres informatifs** affichés dans les albums déjà rédigées. Il est donc utilisé dans la *sérialisation LINQ* aussi.
##### StubCustomTitle
Le StubCustomTitle permet de **tester la gestion des titres personnalisés** avec des valeurs prédéfinies. Ce stub n'est utilisé que pour les *tests* et le *debug*.
##### StubPlaylist
Le StubPlaylist **créé des informations** écrites dans le code afin de **tester** les méthodes et la classe des playlists. Elle n'est pas utilisée par la sérialisation LINQ.
---
```plantuml
@startuml
class StubAlbum {}
class StubArtist {}
class StubCustomTitle {}
class StubInfoTitle {}
class StubPlaylist {}
class IDataManager {}
class LinqXmlSerialization {}
class StubManager
{
+ Manager(IDataManager dataManager)
}
class Manager {}
StubAlbum o--> "- albums*" Album
StubArtist *--> "- artists*" Artist
StubCustomTitle *--> "- customTitles*" CustomTitle
StubInfoTitle *--> "- infoTitles*" InfoTitle
StubPlaylist *--> "- playlists*" Playlist
IDataManager ..> Album
IDataManager ..> Artist
IDataManager ..> InfoTitle
IDataManager ..> CustomTitle
IDataManager ..> Playlist
IDataManager ..> Genre
LinqXmlSerialization o--> "- artists*" Artist
LinqXmlSerialization o--> "- albums*" Album
LinqXmlSerialization o--> "- playlists*" Playlist
LinqXmlSerialization o--> "- infotitles*" InfoTitle
LinqXmlSerialization o--> "- customtitles*" CustomTitle
StubManager ..> InfoTitle
StubManager ..> Artist
StubManager ..> Album
StubManager ..> CustomTitle
StubManager ..> Playlist
Manager *--> "+ DataManager" IDataManager
@enduml
```
#### Explications
Ce diagramme représente les **liens** entre les deux diagrammes ci-dessus.
---
### Diagramme de paquetage

#### Explications
Notre projet est un projet MAUI se nommant **Linaris**. Une erreur a été effectuée lors de la conception, ce qui fait que nos vues
portent le même nom. Pour les différencier, le paquet *Linaris* qui concerne les vues sera écrit en italique.
Tous ces projets sont en .NET 7.0.
##### Model
Le paquet **Model** est une bibliothèque de classes C#. Certaines se trouvent à la racine de celle-ci. D'autres se trouvent dans des sous-dossiers
comme la sérialisation (**Serialization**) ou encore les stubs (**Stub**).
La **sérialisation** a besoin du **stub** pour pouvoir stocker et charger les informations présentes dans les différents fichiers de sauvergarde.
Dans ce projet, nous avons les classes Album, Artist, CustomTitle, Genre, InfoTitle, Manager, Playlist et Title, mais aussi l'interface IDataManager.
Stub contient quant à lui StubAlbum, StubArtist, StubCustomTitle, StubInfoTitle, StubManager et StubPlaylist.
Enfin, Serialization contient la classe LinqXmlSerialization
##### Linaris
Ce paquet contient nos différentes vues codées en C#/XAML.
Nos vues (*Linaris*) ont besoin de Model afin d'effectuer le data-binding pour que notre application ne soit pas uniquement graphique.
Linaris contient les classes AlbumPage, App, AppShell, FooterPage, Layout, LocalFilesPage, MainPage, MauiProgram, PlaylistPage et PlaylistsPage.
##### Console
Ce paquet contient une application console C#. Elle contient donc nos tests fonctionnels de notre application.
Pour effectuer différents tests fonctionnels sur nos différentes classes du modèle, l'application console (**Console**) a besoin de celui-ci.
Console ne contient que la classe Program.
##### TestUnitaires
Ce paquet contient les tests unitaires de nos différentes classes. Il utilise *xUnit* pour les réaliser.
Pour effectuer ceux-ci, le paquet correspondant (**TestUnitaires**) dépend du modèle.
TU_Album, TU_Artist, TU_CustomTitle, TU_InfoTitle, TU_Manager, TU_Playlist et TU_Title sont les classes contenues par TestUnitaires.
---
### Diagramme de séquence
#### LoadSerialization()
```plantuml
@startuml
autonumber
actor Utilisateur as user
participant Front as vues
participant Serialization as seria
participant Files as files
collections Collections as lists
group LoadSerialization [On Start]
user -> vues : Démarre l'application
vues -> seria : Appelle la fonction de chargement
seria -> files : Demande les données des fichiers
seria <-- files : Retourne les données des fichiers
seria -> lists : Demande les données dans les collections
seria <-- lists : Retourne les données des stubs
vues <-- seria : Données utilisables par les vues
end
@enduml
```
##### Explications
Notre sérialisation permet de sauvegarder nos données dans des fichiers *XML* ainsi que de charger dans des collections les données contenues dans ceux-ci.
Cette méthode est appelée lorsque l'utilisateur démarre l'application *Linaris* (MAUI). Celle-ci appelle ensuite les différentes fonctions de chargement codées en *C#* présentes dans la classe *LinqXmlSerialization*. Grâce à la bibliothèque **LINQ_XML**, la sérialisation peut récupérer les données présentes dans les différents fichiers pour les classes **Artist**, **CustomTitle** et **Playlist** et les mettre dans les différentes *ObservableCollection*. Pour les classes **InfoTitle** et **Album**, les données sont récupérées dans les collections des stubs correspondant et les mettre dans les différentes *ObservableCollection*. Les données sont ensuite utilisables par les vues via le **manager**.
L'**utilisateur** représente toute personne utilisant l'application et **Front** désigne les vues. Pour ce qui est de **Sérialization**, elle désigne soit les stubs, soit la sérialisation en XML selon le mode de sérialisation utilisée par Linaris. **Files** correspond aux données de l'application, qu'elles soient dans les stubs ou dans des fichiers *.xml. Enfin, **Collections** désigne les structure de données de Linaris.
| Step | Explanation |
| ----------- | ----------- |
| 1 | L'utilisateur lance l'application via sont téléphone Android ou Windows,lançant le programme de Linaris |
| 2 | L'événement de création de App appelle la méthode de sérialisation des données |
| 3 | Le DataManager va charger les données dans des listes, que ce soit des données chargées des stubs ou chargées d'un fichier par le biais du LINQ |
| 4 | Les listes sont retournées au Manager afin de lui donner l'accès à ces données |
| 5 | Afin de charger les albums, les titres informatifs et artistes, le DataManager va aussi charger ces données par les stubs |
| 6 | La liste chargée des informations est retournée au Manager |
| 7 | Après les chargement, le Manager a accès aux données utilisateurs et aux titres informatifs, albums et artistes, permettant l'affichage des albums sur la page d'accueil au lancement de l'application, mais aussi des autres données sur les différentes vues de Linaris |
#### NextTitle()
```plantuml
@startuml
autonumber
actor Utilisateur as user
participant Front as vues
participant Manager as man
participant Playlist as play
group NextTitle
user -> vues : Clique sur le bouton suivant
vues -> man : Appelle la fonction NextTitle
alt currentPlaylist == null
vues <-- man : ne retourne rien
else currentPlaylist != null
man -> play : Demande le titre suivant
alt loop == true
man <-- play : retourne le titre actuel
else loop == false and shuffle == true
man <-- play : retourne un titre aléatoire de la playlist (index entre 0 et la taille de la playlist)
else loop == false and shuffle == false
man <-- play : retourne le titre suivant (index + 1)
end
vues <-- man : retourne le titre
user <-- vues : joue le titre
end
end
@enduml
```
##### Explications
Lorsque l'utilisateur clique sur le bouton **suivant**, la fonction **NextTitle** de la classe C# *Manager* est appelée. Si la variable *currentPlaylist* est initialisée,
la fonction **NextTitle** de la classe *Playlist* est appelée. Si le booléen *loop* est vrai, la fonction retourne le titre actuel. Si le booléen *loop* est faux et le booléen *shuffle* est vrai,
la fonction retourne un titre aléatoire en générant un nombre aléatoire compris entre 0 et la taille de la playlist (nombre de titres) grâce à la fonction **RandomGenerator** de la playlist.
À l'inverse, si le booléen *shuffle* est faux, la fonction retourne le titre suivant en incrémentant de 1 l'index du son actuel.
L'**utilisateur** représente toute personne utilisant l'application et **Front** désigne les vues. **Manager** représente la classe Manager, gérant tout le code-behind de Linaris. Enfin, **Playlist** représente la classe Playlist donnant accès à toute la gestion des playlists de l'application.
Le premier alt est utile afin de s'assurer qu'une playlist est en cours, pour **ne pas causer de problème**. Le deuxième alt vérifie les options possibles de la playlist, en accord avec les **choix** de l'utilisateur.
| Step | Explanation |
| ----------- | ----------- |
| 1 | L'utilisateur intéragit avec le bouton de musique suivante du footer |
| 2 | Cet événement déclenche la méthode NextTitle du code-behind |
| 3 | Si aucune playlist n'est en train d'être jouée, la méthode ne fait rien |
| 4 | Sinon, le Manager va chercher le morceau suivant de la playlist actuelle |
| 5 | Si l'option de lecture du morceau en boucle est activée, le même morceau est renvoyé |
| 6 | Si l'option de lecture en boucle est désactivée mais que la lecture en aléatoire l'est, un morceau aléatoire de la plyalist actuelle est renvoyée en choisissant ce morceau par un index |
| 7 | Sinon, cela renvoie le morceau à la suite du morceau en cours dans la playlist actuelle |
| 8 | Le Manager peut donc renvoyer le morceau à jouer au Footer |
| 9 | Le footer peut donc lancer la lecture du morceau à jouer |