18 Commits

Author SHA1 Message Date
46173f4f99 Refactoring controllers to use publicly accessible CursorLists 2021-04-12 21:54:21 -04:00
4acf091511 Adding a shared library for common classes 2021-04-12 20:58:44 -04:00
6b14d1264a WIP splitting up the viewModels for the party 2021-04-11 20:54:59 -04:00
bf702e403e Entirely too many commits for this 2021-04-11 14:49:44 -04:00
4b22d5d9be Using single issue template 2021-04-11 14:48:36 -04:00
03a091d853 mv issue template 2021-04-11 14:46:08 -04:00
b2fc0121b0 mv issue folder 2021-04-11 14:31:32 -04:00
589773b29c Merge pull request 'feature/ubuntu' (#4) from feature/ubuntu into master
Reviewed-on: http://gitea.watsonlabs.net/watsonb8/aurora/pulls/4
2021-04-11 14:23:03 -04:00
9252f96bdb Merge branch 'master' into feature/ubuntu 2021-04-11 14:22:56 -04:00
733649c62b Adding story templates 2021-04-11 14:21:48 -04:00
dd4632cdb6 Changing folder names 2021-04-10 10:20:50 -04:00
9859358d7b Merge pull request 'feature/ubuntu' (#3) from feature/ubuntu into master
Reviewed-on: http://gitea.watsonlabs.net/watsonb8/aurora/pulls/3
2021-04-10 09:34:18 -04:00
f1a3771912 Merge branch 'master' of ssh://watsonlabs.net:3122/watsonb8/aurora 2021-04-10 09:29:46 -04:00
4d3413b2a2 Disabling test app so that this will run on ubuntu 2021-04-10 09:29:29 -04:00
e3eedc5ffa Updating xamarin and ripping out datagrid 2021-04-03 16:38:44 -04:00
4ee5656358 Moving protos to neutral place to be shared by projects 2021-03-05 23:51:24 -05:00
d307a049f1 Adding workspace 2021-03-05 23:30:12 -05:00
36ceb6c61a Adding readme 2021-03-05 23:17:32 -05:00
116 changed files with 1079 additions and 2326 deletions

11
.gitea/issue_template.md Normal file
View File

@ -0,0 +1,11 @@
## Story
**As a**:
**I want**:
**So that**:
## Description
## Resources
## Notes

23
README.md Normal file
View File

@ -0,0 +1,23 @@
# Aurora
A collaborative media player
![Aurora](./aurora-sharp-desktop/Docs/Aurora.png)
## The Problem Statement
Music is a medium that should be consumed together. Aurora aims to creat a music listening experience that can be shared with any number of people no matter where the listeners are. With music perfectly in sync between all listeners, remote listening is as easy as loading the app and connecting to your friends.
## The Tech
- GTK: Aurora takes advantage of Xamarin and GTK under the hood in order to render a sleek, modern, cross platform desktop application.
- gRPC: gRPC is a modern open source high performance RPC framework that can run in any environment. This app is inherently a peer to peer application and as such, gRPC is utilized to efficiently and effectively communicate between aurora nodes by implementing simple service definitions.
## The Future
We are always looking for ways to improve the exerience of the application.
- Aurora Turn Server: The next big step for the application is integrating a custom Aurora turn server so that listeners can connect to one another from different networks while maintaining perfectly synchronized output
- 3rd Party Source Integration: Not everybody has a vast local collection of media that they have been collecting for years. We plan to integrate with 3rd Party music sources such as _Spotify_ and _Apple Music_

View File

@ -1,7 +0,0 @@
namespace Aurora.Services.Signal
{
public partial class SignalService : Signal.SignalBase
{
}
}

View File

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<Protobuf Include="Src\Protos\signal.proto" GrpcServices="Server" />
<Protobuf Include="..\..\aurora-proto\Proto\signal.proto" GrpcServices="Server" />
</ItemGroup>
<ItemGroup>

View File

@ -2,7 +2,7 @@ syntax = "proto3";
package Aurora.Proto.Party;
import "Proto/general.proto";
import "general.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/field_mask.proto";
@ -105,13 +105,14 @@ message LeavePartyResponse {
message Member {
//Resource name of the party member to be returned (Added by server)
string name = 1;
string userName = 2;
string id = 2;
string userName = 3;
//Added by server
string ipAddress = 3;
string ipAddress = 4;
//Added by server
google.protobuf.Timestamp addedOn = 4;
google.protobuf.Timestamp addedOn = 5;
}
message ListMembersRequest {

351
aurora-shared/.gitignore vendored Normal file
View File

@ -0,0 +1,351 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
start*.sh

View File

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
#nullable enable
namespace Aurora.Cursor
{
public enum SortDirection
@ -15,8 +14,8 @@ namespace Aurora.Cursor
{
private CursorList<T> _list;
private string? _previousPageToken;
private string? _nextPageToken;
private string _previousPageToken;
private string _nextPageToken;
private int _pageSize;
private Func<KeyValuePair<string, T>, string> _sortDelgate;
private SortDirection _direction;

View File

@ -4,7 +4,6 @@ using System;
using System.Linq;
using Aurora.Utils;
#nullable enable
namespace Aurora.Cursor
{
@ -23,12 +22,8 @@ namespace Aurora.Cursor
id = HashUtil.GetHash(new string[] { item.GetHashCode().ToString() }).ToString();
item.Id = id;
}
bool res = this.TryAdd(id, item);
this.Add(id, item);
if (res == false)
{
throw new System.Exception("Failed to add item to cursor list");
}
return this;
}

View File

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>aurora_shared</RootNamespace>
</PropertyGroup>
</Project>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Xamarin.Forms.4.3.0.991211\build\Xamarin.Forms.props" Condition="Exists('..\packages\Xamarin.Forms.4.3.0.991211\build\Xamarin.Forms.props')" />
<Import Project="..\packages\Xamarin.Forms.5.0.0.2012\build\Xamarin.Forms.props" Condition="Exists('..\packages\Xamarin.Forms.5.0.0.2012\build\Xamarin.Forms.props')" />
<Import Project="..\packages\Grpc.Tools.2.25.0\build\Grpc.Tools.props" Condition="Exists('..\packages\Grpc.Tools.2.25.0\build\Grpc.Tools.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -28,42 +28,24 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="gtk-sharp, Version=2.4.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="gdk-sharp, Version=2.4.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="glib-sharp, Version=2.4.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="glade-sharp, Version=2.4.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="pango-sharp, Version=2.4.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="atk-sharp, Version=2.4.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Mono.Posix" />
<Reference Include="Xamarin.Forms.Core">
<HintPath>..\packages\Xamarin.Forms.4.3.0.991211\lib\netstandard2.0\Xamarin.Forms.Core.dll</HintPath>
<HintPath>..\packages\Xamarin.Forms.5.0.0.2012\lib\netstandard2.0\Xamarin.Forms.Core.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Forms.Platform">
<HintPath>..\packages\Xamarin.Forms.4.3.0.991211\lib\netstandard2.0\Xamarin.Forms.Platform.dll</HintPath>
<HintPath>..\packages\Xamarin.Forms.5.0.0.2012\lib\netstandard2.0\Xamarin.Forms.Platform.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Forms.Xaml">
<HintPath>..\packages\Xamarin.Forms.4.3.0.991211\lib\netstandard2.0\Xamarin.Forms.Xaml.dll</HintPath>
<HintPath>..\packages\Xamarin.Forms.5.0.0.2012\lib\netstandard2.0\Xamarin.Forms.Xaml.dll</HintPath>
</Reference>
<Reference Include="OpenTK">
<HintPath>..\packages\OpenTK.3.1.0\lib\net20\OpenTK.dll</HintPath>
</Reference>
<Reference Include="webkit-sharp">
<HintPath>..\packages\Xamarin.Forms.Platform.GTK.4.3.0.991211\lib\net45\webkit-sharp.dll</HintPath>
<HintPath>..\packages\Xamarin.Forms.Platform.GTK.5.0.0.2012\lib\net45\webkit-sharp.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Forms.Platform.GTK">
<HintPath>..\packages\Xamarin.Forms.Platform.GTK.4.3.0.991211\lib\net45\Xamarin.Forms.Platform.GTK.dll</HintPath>
<HintPath>..\packages\Xamarin.Forms.Platform.GTK.5.0.0.2012\lib\net45\Xamarin.Forms.Platform.GTK.dll</HintPath>
</Reference>
<Reference Include="System.Reflection">
<HintPath>..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll</HintPath>
@ -168,6 +150,15 @@
<HintPath>..\packages\Autofac.5.0.0\lib\net461\Autofac.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="pango-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="Xamarin.Forms.DataGrid">
<HintPath>..\packages\Xamarin.Forms.DataGrid.4.8.0\lib\netstandard2.0\Xamarin.Forms.DataGrid.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="gtk-gui\gui.stetic">
@ -514,6 +505,7 @@
<Project>{17F51282-39BE-4FE3-8EC7-6D108F5DD0FD}</Project>
<Name>Aurora</Name>
</ProjectReference>
<ProjectReference Include="..\..\aurora-shared\aurora-shared.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="Resources\play.png">
@ -545,5 +537,5 @@
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<Import Project="..\packages\Grpc.Core.2.25.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.2.25.0\build\net45\Grpc.Core.targets')" />
<Import Project="..\packages\VideoLAN.LibVLC.Mac.3.1.3.1\build\VideoLAN.LibVLC.Mac.targets" Condition="Exists('..\packages\VideoLAN.LibVLC.Mac.3.1.3.1\build\VideoLAN.LibVLC.Mac.targets')" />
<Import Project="..\packages\Xamarin.Forms.4.3.0.991211\build\Xamarin.Forms.targets" Condition="Exists('..\packages\Xamarin.Forms.4.3.0.991211\build\Xamarin.Forms.targets')" />
<Import Project="..\packages\Xamarin.Forms.5.0.0.2012\build\Xamarin.Forms.targets" Condition="Exists('..\packages\Xamarin.Forms.5.0.0.2012\build\Xamarin.Forms.targets')" />
</Project>

View File

@ -2,6 +2,7 @@
<stetic-interface>
<configuration>
<images-root-path>..</images-root-path>
<target-gtk-version>2.12</target-gtk-version>
</configuration>
<import>
<widget-library name="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />

View File

@ -8,7 +8,7 @@
<package id="Grpc.Core" version="2.25.0" targetFramework="net47" />
<package id="Grpc.Core.Api" version="2.25.0" targetFramework="net47" />
<package id="Grpc.Tools" version="2.25.0" targetFramework="net47" developmentDependency="true" />
<package id="LibVLCSharp" version="3.3.1" targetFramework="net47" />
<package id="LibVLCSharp" version="3.3.1" targetFramework="net47" requireReinstallation="true" />
<package id="LibVLCSharp.Forms" version="3.3.1" targetFramework="net47" />
<package id="LibVLCSharp.Forms.GTK" version="3.3.1" targetFramework="net47" />
<package id="LibVLCSharp.GTK" version="3.3.1" targetFramework="net47" />
@ -25,7 +25,7 @@
<package id="System.Memory" version="4.5.3" targetFramework="net47" />
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net47" />
<package id="System.Reflection" version="4.3.0" targetFramework="net47" />
<package id="System.Reflection.TypeExtensions" version="4.6.0" targetFramework="net47" />
<package id="System.Reflection.TypeExtensions" version="4.6.0" targetFramework="net47" requireReinstallation="true" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net47" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net47" />
<package id="System.Runtime.Serialization.Primitives" version="4.3.0" targetFramework="net47" />
@ -34,6 +34,7 @@
<package id="taglib-sharp-netstandard2.0" version="2.1.0" targetFramework="net47" />
<package id="VideoLAN.LibVLC.Mac" version="3.1.3.1" targetFramework="net47" />
<package id="Xam.Plugins.Settings" version="3.1.1" targetFramework="net47" />
<package id="Xamarin.Forms" version="4.3.0.991211" targetFramework="net47" />
<package id="Xamarin.Forms.Platform.GTK" version="4.3.0.991211" targetFramework="net47" />
<package id="Xamarin.Forms" version="5.0.0.2012" targetFramework="net47" />
<package id="Xamarin.Forms.DataGrid" version="4.8.0" targetFramework="net47" />
<package id="Xamarin.Forms.Platform.GTK" version="5.0.0.2012" targetFramework="net47" />
</packages>

View File

@ -5,8 +5,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aurora", "Aurora\Aurora.csp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aurora.gtk", "Aurora.gtk\Aurora.gtk.csproj", "{E8C8C24A-5C51-47CB-B241-F5A9F0E808B1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aurora.test", "Aurora.test\Aurora.test.csproj", "{45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}"
EndProject
#Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aurora.test", "Aurora.test\Aurora.test.csproj", "{45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}"
#EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|iPhoneSimulator = Debug|iPhoneSimulator

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
@ -21,11 +21,9 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Aurora\Aurora.csproj" />
<ProjectReference Include="..\..\aurora-shared\aurora-shared.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Resources\Jidenna\The Chief\01 A Bull%27s Tale.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\04 Bambi.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@ -56,62 +54,11 @@
<None Update="Resources\Jidenna\The Chief\06 Long Live The Chief.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\02 Chief Don%27t Run.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\11 Little Bit More.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\13 White Niggas.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\16 BDE Bonus %28Prod. By_ ID Labs%29.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\11 Play Ya Cards %28Prod By_ Chuck Inglish%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\08 All Around The World %28Prod. By_ Just Blaze%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\10 In The Air %28Prod By_ Ritz Reynolds%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\05 I%27ll Be There %28feat. Phonte%29 %28Prod. By_ Beanz %27n%27 Kornbread%29.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\03 Donald Trump %28Prod. By_ Sap%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\13 Life Ain%27t Easy %28Prod. By_ ID Labs%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\09 Down The Rabbit Hole %28Prod. By_ Blue of The Sore Losers%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\07 Wake Up %28Prod By_ Sap &amp; ID Labs%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\04 Oy Vey %28Prod By_ ID Labs%29.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\01 Best Day Ever %28Prod. By_ ID Labs%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\02 Get Up %28Prod. By_ Teddy Roxpin%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\14 Snooze %28Prod By_ ID Labs%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\12 She Said %28Prod By_ Khrysis%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\15 Keep Floatin%27 %28feat. Wiz Khalifa%29 %28Prod. By_ ID Labs%29.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\06 Wear My Hat %28Prod. By_ Chuck Inglish%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -7,8 +7,7 @@ using Aurora.Design.Views.Profile;
using Aurora.Design.Views.Songs;
using Aurora.Design.Views.Stations;
using Aurora.Services.EventManager;
using Aurora.Services.Server;
using Aurora.Services.Client;
using Aurora.Services;
using Autofac;
using LibVLCSharp.Shared;
using Xamarin.Forms;
@ -28,19 +27,19 @@ namespace Aurora
//Register DI
ContainerBuilder _builder = new ContainerBuilder();
// _builder.RegisterInstance<IPlayer>(new PlayerService()).SingleInstance();
_builder.RegisterInstance<IPlayer>(new PlayerService()).SingleInstance();
_builder.RegisterType<Global>().As<Global>().SingleInstance();
_builder.RegisterType<PlayerService>().As<IPlayer>().SingleInstance();
_builder.RegisterType<SettingsService>().As<ISettingsService>().SingleInstance();
_builder.RegisterType<LibraryService>().As<ILibraryService>().SingleInstance();
_builder.RegisterType<EventManager>().As<IEventManager>().SingleInstance();
_builder.RegisterType<ServerService>().As<IServerService>().SingleInstance();
_builder.RegisterType<ClientService>().As<IClientService>().SingleInstance();
_builder.RegisterType<MainView>().SingleInstance();
_builder.RegisterType<AlbumsViewModel>();
_builder.RegisterType<ArtistsViewModel>();
_builder.RegisterType<PartyViewModel>();
_builder.RegisterType<ProfileViewModel>();
_builder.RegisterType<SongsViewModel>();
_builder.RegisterType<HostPartyViewModel>();
_builder.RegisterType<StationsViewModel>();
// _builder.RegisterInstance<ISettingsService>(new SettingsService()).SingleInstance();

View File

@ -5,11 +5,12 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>pdbonly</DebugType>
<OutputPath></OutputPath>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Forms" Version="4.3.0.991211" />
<PackageReference Include="Xamarin.Essentials" Version="1.3.1" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2012" />
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
<PackageReference Include="taglib-sharp-netstandard2.0" Version="2.1.0" />
<PackageReference Include="LibVLCSharp.Forms" Version="3.3.1" />
<PackageReference Include="VideoLAN.LibVLC.Mac" Version="3.1.3.1" />
@ -24,6 +25,7 @@
<PackageReference Include="DLToolkit.Forms.Controls.FlowListView" Version="2.0.11" />
<PackageReference Include="CarouselView.FormsPlugin" Version="5.2.0" />
<PackageReference Include="Autofac" Version="5.0.0" />
<PackageReference Include="Xamarin.Forms.DataGrid" Version="4.8.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Design\" />
@ -43,7 +45,6 @@
<Folder Include="Design\Components\MemberList\" />
<Folder Include="Design\Components\Library\" />
<Folder Include="Design\Views\Profile\" />
<Folder Include="Design\Components\DataGrid\" />
<Folder Include="Resources\" />
<Folder Include="Design\Extensions\" />
<Folder Include="Design\Components\ImageButton\" />
@ -60,8 +61,8 @@
</Compile>
</ItemGroup>
<ItemGroup>
<Protobuf Include="Proto\general.proto" />
<Protobuf Include="Proto\party.proto" />
<Protobuf Include="..\..\aurora-proto\Proto\general.proto" />
<Protobuf Include="..\..\aurora-proto\Proto\party.proto" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\backward.png" />
@ -82,4 +83,7 @@
<None Remove="Resources\like.png" />
<None Remove="Resources\play.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\aurora-shared\aurora-shared.csproj" />
</ItemGroup>
</Project>

View File

@ -1,8 +0,0 @@
using System.Collections.Generic;
namespace Aurora.Design.Components.DataGrid
{
public sealed class ColumnCollection : List<DataGridColumn>
{
}
}

View File

@ -1,95 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Grid
x:Name="self"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Aurora.Design.Components.DataGrid.DataGrid"
Padding="0"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition
Height="Auto"/>
<RowDefinition
Height="*"/>
</Grid.RowDefinitions>
<Grid
x:Name="_headerView"
RowSpacing="0">
<Grid.Resources>
<ResourceDictionary>
<!--Default Header Style-->
<Style
x:Key="HeaderDefaultStyle"
TargetType="Label">
<Setter
Property="FontSize"
Value="{Binding HeaderFontSize, Source={x:Reference self}}"/>
<Setter
Property="FontAttributes"
Value="Bold"/>
<Setter
Property="HorizontalOptions"
Value="Center"/>
<Setter
Property="VerticalOptions"
Value="Center"/>
<Setter
Property="TextColor"
Value="{Binding HeaderTextColor,Source={x:Reference self}}"/>
<Setter
Property="LineBreakMode"
Value="WordWrap"/>
</Style>
<Style
TargetType="Grid">
<Setter
Property="BackgroundColor"
Value="{Binding HeaderBackground,Source={x:Reference self}}"/>
</Style>
<Style
x:Key="ImageStyleBase"
TargetType="Image">
<Setter
Property="Aspect"
Value="AspectFill"/>
<Setter
Property="VerticalOptions"
Value="Center"/>
<Setter
Property="HorizontalOptions"
Value="Center"/>
<Setter
Property="HeightRequest"
Value="5"/>
<Setter
Property="WidthRequest"
Value="9"/>
<Setter
Property="Margin"
Value="0,0,4,0"/>
</Style>
<Style
x:Key="AscendingIconStyle"
TargetType="Image"
BasedOn="{StaticResource ImageStyleBase}">
<Setter
Property="Source"
Value="{Binding AscendingIcon, Source={x:Reference self}}"/>
</Style>
<Style
x:Key="DescendingIconStyle"
TargetType="Image"
BasedOn="{StaticResource ImageStyleBase}">
<Setter
Property="Source"
Value="{Binding DescendingIcon, Source={x:Reference self}}"/>
</Style>
</ResourceDictionary>
</Grid.Resources>
</Grid>
<ListView x:Name="DataList" Grid.Row="1" BackgroundColor="#222222" />
<ContentView
x:Name="_noDataView"
Grid.RowSpan="2"
IsVisible="False"/>
</Grid>

View File

@ -1,756 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Windows.Input;
using Xamarin.Forms;
using Aurora.Utils;
namespace Aurora.Design.Components.DataGrid
{
public partial class DataGrid : Grid
{
#region Private Fields
private ObservableCollection<object> _internalItems;
private Dictionary<int, SortingOrder> _sortingOrders;
#endregion Fields
#region Constructor
public DataGrid() : this(ListViewCachingStrategy.RetainElement)
{
}
public DataGrid(ListViewCachingStrategy cachingStrategy)
{
InitializeComponent();
BackgroundColor = Color.Transparent;
_sortingOrders = new Dictionary<int, SortingOrder>();
DataList.ItemTemplate = new DataGridRowTemplateSelector();
DataList.ItemSelected += (s, e) =>
{
if (SelectionEnabled)
{
SelectedItem = DataList.SelectedItem;
}
else
{
DataList.SelectedItem = null;
}
ItemSelected?.Invoke(this, e);
};
DataList.Refreshing += (s, e) =>
{
Refreshing?.Invoke(this, e);
};
DataList.SetBinding(ListView.RowHeightProperty, new Binding("RowHeight", source: this));
}
#endregion Constructor
#region Public Fields
public event EventHandler Refreshing;
public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
#endregion Public Fields
#region Bindable properties
public static readonly BindableProperty ActiveRowColorProperty =
BindableProperty.Create(
nameof(ActiveRowColor),
typeof(Color),
typeof(DataGrid),
Color.FromRgb(128, 144, 160),
coerceValue: (bindable, value) =>
{
if (!(bindable as DataGrid).SelectionEnabled)
throw new InvalidOperationException("Datagrid must be SelectionEnabled=true to set ActiveRowColor");
return value;
});
public static readonly BindableProperty HeaderBackgroundProperty =
BindableProperty.Create(
nameof(HeaderBackground),
typeof(Color),
typeof(DataGrid),
Color.White,
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
if (self._headerView != null && !self.HeaderBordersVisible)
self._headerView.BackgroundColor = (Color)newValue;
});
public static readonly BindableProperty BorderColorProperty =
BindableProperty.Create(
nameof(BorderColor),
typeof(Color),
typeof(DataGrid),
Color.Black,
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
if (self.HeaderBordersVisible)
self._headerView.BackgroundColor = (Color)newValue;
if (self.Columns != null && self.ItemsSource != null)
self.Reload();
});
public static readonly BindableProperty RowsBackgroundColorPaletteProperty =
BindableProperty.Create(nameof(RowsBackgroundColorPalette),
typeof(IColorProvider),
typeof(DataGrid),
new PaletteCollection { default(Color) },
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
if (self.Columns != null && self.ItemsSource != null)
self.Reload();
});
public static readonly BindableProperty RowsTextColorPaletteProperty =
BindableProperty.Create(
nameof(RowsTextColorPalette),
typeof(IColorProvider),
typeof(DataGrid),
new PaletteCollection { Color.Black },
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
if (self.Columns != null && self.ItemsSource != null)
self.Reload();
});
public static readonly BindableProperty ColumnsProperty =
BindableProperty.Create(
nameof(Columns),
typeof(ColumnCollection),
typeof(DataGrid),
propertyChanged: (bindable, oldValue, newValue) =>
{
(bindable as DataGrid).InitHeaderView();
},
defaultValueCreator: bindable => { return new ColumnCollection(); }
);
public static BindableProperty ItemsSourceProperty =
BindableProperty.Create(
propertyName: nameof(ItemsSource),
returnType: typeof(IEnumerable),
declaringType: typeof(DataGrid),
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
DataGrid self = bindable as DataGrid;
//ObservableCollection Tracking
if (oldValue != null && oldValue is INotifyCollectionChanged)
{
(oldValue as INotifyCollectionChanged).CollectionChanged -= self.HandleItemsSourceCollectionChanged;
}
if (newValue != null && newValue is INotifyCollectionChanged)
{
(newValue as INotifyCollectionChanged).CollectionChanged += self.HandleItemsSourceCollectionChanged;
self.InternalItems = new ObservableCollection<object>(((IEnumerable<object>)newValue));
//Assign listview item source
self.DataList.ItemsSource = self.InternalItems;
self.DataList.SetBinding(ListView.ItemsSourceProperty, new Binding("ItemsSource", source: self));
}
if (self.SelectedItem != null && !self.InternalItems.Contains(self.SelectedItem))
{
self.SelectedItem = null;
}
if (self.NoDataView != null)
{
if (self.ItemsSource == null || self.InternalItems.Count() == 0)
{
self._noDataView.IsVisible = true;
}
else if (self._noDataView.IsVisible)
{
self._noDataView.IsVisible = false;
}
}
});
private void HandleItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (object item in e.NewItems)
{
InternalItems.Add(item);
}
}
if (e.OldItems != null)
{
foreach (object item in e.OldItems)
{
InternalItems.Remove(item);
}
}
if (SelectedItem != null && !InternalItems.Contains(SelectedItem))
{
SelectedItem = null;
}
}
public static readonly BindableProperty RowHeightProperty =
BindableProperty.Create(nameof(RowHeight), typeof(int), typeof(DataGrid), 40,
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
self.DataList.RowHeight = (int)newValue;
});
public static readonly BindableProperty HeaderHeightProperty =
BindableProperty.Create(nameof(HeaderHeight), typeof(int), typeof(DataGrid), 40,
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
self._headerView.HeightRequest = (int)newValue;
});
public static readonly BindableProperty IsSortableProperty =
BindableProperty.Create(nameof(IsSortable), typeof(bool), typeof(DataGrid), true);
public static readonly BindableProperty FontSizeProperty =
BindableProperty.Create(nameof(FontSize), typeof(double), typeof(DataGrid), 13.0);
public static readonly BindableProperty FontFamilyProperty =
BindableProperty.Create(
nameof(FontFamily),
typeof(string),
typeof(DataGrid),
Font.Default.FontFamily);
public static readonly BindableProperty SelectedItemProperty =
BindableProperty.Create(
nameof(SelectedItem),
typeof(object),
typeof(DataGrid),
null,
BindingMode.TwoWay,
coerceValue: (bindable, value) =>
{
var self = bindable as DataGrid;
if (!self.SelectionEnabled && value != null)
{
throw new InvalidOperationException("Datagrid must be SelectionEnabled=true to set SelectedItem");
}
if (self.InternalItems != null && self.InternalItems.Contains(value))
{
return value;
}
else
{
return null;
}
},
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
if (self.DataList.SelectedItem != newValue)
{
self.DataList.SelectedItem = newValue;
}
}
);
public static readonly BindableProperty SelectionEnabledProperty =
BindableProperty.Create(nameof(SelectionEnabled), typeof(bool), typeof(DataGrid), true,
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
if (!self.SelectionEnabled && self.SelectedItem != null)
{
self.SelectedItem = null;
}
});
public static readonly BindableProperty PullToRefreshCommandProperty =
BindableProperty.Create(nameof(PullToRefreshCommand), typeof(ICommand), typeof(DataGrid), null,
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
if (newValue == null)
{
self.DataList.IsPullToRefreshEnabled = false;
self.DataList.RefreshCommand = null;
}
else
{
self.DataList.IsPullToRefreshEnabled = true;
self.DataList.RefreshCommand = newValue as ICommand;
}
});
public static readonly BindableProperty IsRefreshingProperty =
BindableProperty.Create(
nameof(IsRefreshing),
typeof(bool),
typeof(DataGrid),
false,
BindingMode.TwoWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
(bindable as DataGrid).DataList.IsRefreshing = (bool)newValue;
});
public static readonly BindableProperty BorderThicknessProperty =
BindableProperty.Create(
nameof(BorderThickness),
typeof(Thickness),
typeof(DataGrid),
new Thickness(1),
propertyChanged: (bindable, oldValue, newValue) =>
{
(bindable as DataGrid)._headerView.ColumnSpacing = ((Thickness)newValue).HorizontalThickness / 2;
(bindable as DataGrid)._headerView.Padding = ((Thickness)newValue).HorizontalThickness / 2;
});
public static readonly BindableProperty HeaderBordersVisibleProperty =
BindableProperty.Create(
nameof(HeaderBordersVisible),
typeof(bool),
typeof(DataGrid),
true,
propertyChanged: (bindable, oldValue, newValue) => (bindable as DataGrid)._headerView.BackgroundColor = (bool)newValue ? (bindable as DataGrid).BorderColor : (bindable as DataGrid).HeaderBackground);
public static readonly BindableProperty SortedColumnIndexProperty =
BindableProperty.Create(
nameof(SortedColumnIndex),
typeof(SortData),
typeof(DataGrid),
null,
BindingMode.TwoWay,
validateValue: (bindable, v) =>
{
var self = bindable as DataGrid;
var sData = (SortData)v;
return
sData == null || //setted to null
self.Columns == null || // Columns binded but not setted
self.Columns.Count == 0 || //columns not setted yet
(sData.Index < self.Columns.Count && self.Columns.ElementAt(sData.Index).SortingEnabled);
},
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
if (oldValue != newValue)
self.SortItems((SortData)newValue);
});
public static readonly BindableProperty HeaderLabelStyleProperty =
BindableProperty.Create(nameof(HeaderLabelStyle), typeof(Style), typeof(DataGrid));
public static readonly BindableProperty AscendingIconProperty =
BindableProperty.Create(
nameof(AscendingIcon),
typeof(ImageSource),
typeof(DataGrid),
ImageSource.FromResource("Xamarin.Forms.DataGrid.up.png",
typeof(DataGrid).GetTypeInfo().Assembly));
public static readonly BindableProperty DescendingIconProperty =
BindableProperty.Create(
nameof(DescendingIcon),
typeof(ImageSource),
typeof(DataGrid),
ImageSource.FromResource("Xamarin.Forms.DataGrid.down.png",
typeof(DataGrid).GetTypeInfo().Assembly));
public static readonly BindableProperty DescendingIconStyleProperty =
BindableProperty.Create(nameof(DescendingIconStyle), typeof(Style), typeof(DataGrid), null,
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
var style = (newValue as Style).Setters.FirstOrDefault(x => x.Property == Image.SourceProperty);
if (style != null)
{
if (style.Value is string vs)
self.DescendingIcon = ImageSource.FromFile(vs);
else
self.DescendingIcon = (ImageSource)style.Value;
}
});
public static readonly BindableProperty AscendingIconStyleProperty =
BindableProperty.Create(nameof(AscendingIconStyle), typeof(Style), typeof(DataGrid), null,
coerceValue: (bindable, v) =>
{
var self = bindable as DataGrid;
return v;
},
propertyChanged: (bindable, oldValue, newValue) =>
{
var self = bindable as DataGrid;
if ((newValue as Style).Setters.Any(x => x.Property == Image.SourceProperty))
{
var style = (newValue as Style).Setters.FirstOrDefault(x => x.Property == Image.SourceProperty);
if (style != null)
{
if (style.Value is string vs)
self.AscendingIcon = ImageSource.FromFile(vs);
else
self.AscendingIcon = (ImageSource)style.Value;
}
}
});
public static readonly BindableProperty NoDataViewProperty =
BindableProperty.Create(nameof(NoDataView), typeof(View), typeof(DataGrid),
propertyChanged: (bindable, oldValue, newValue) =>
{
if (oldValue != newValue)
(bindable as DataGrid)._noDataView.Content = newValue as View;
});
#endregion
#region Properties
public Color ActiveRowColor
{
get { return (Color)GetValue(ActiveRowColorProperty); }
set { SetValue(ActiveRowColorProperty, value); }
}
public Color HeaderBackground
{
get { return (Color)GetValue(HeaderBackgroundProperty); }
set { SetValue(HeaderBackgroundProperty, value); }
}
[Obsolete("Please use HeaderLabelStyle", true)]
public Color HeaderTextColor
{
get; set;
}
public Color BorderColor
{
get { return (Color)GetValue(BorderColorProperty); }
set { SetValue(BorderColorProperty, value); }
}
public IColorProvider RowsBackgroundColorPalette
{
get { return (IColorProvider)GetValue(RowsBackgroundColorPaletteProperty); }
set { SetValue(RowsBackgroundColorPaletteProperty, value); }
}
public IColorProvider RowsTextColorPalette
{
get { return (IColorProvider)GetValue(RowsTextColorPaletteProperty); }
set { SetValue(RowsTextColorPaletteProperty, value); }
}
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
internal ObservableCollection<object> InternalItems
{
get { return _internalItems; }
set
{
if (value != _internalItems)
{
_internalItems = value;
if (IsSortable && SortedColumnIndex != null)
{
SortItems(SortedColumnIndex);
}
}
DataList.ItemsSource = _internalItems;
}
}
public ColumnCollection Columns
{
get { return (ColumnCollection)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
public double FontSize
{
get { return (double)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
[Obsolete("Please use HeaderLabelStyle", true)]
public double HeaderFontSize
{
get; set;
}
public string FontFamily
{
get { return (string)GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
public int RowHeight
{
get { return (int)GetValue(RowHeightProperty); }
set { SetValue(RowHeightProperty, value); }
}
public int HeaderHeight
{
get { return (int)GetValue(HeaderHeightProperty); }
set { SetValue(HeaderHeightProperty, value); }
}
public bool IsSortable
{
get { return (bool)GetValue(IsSortableProperty); }
set { SetValue(IsSortableProperty, value); }
}
public bool SelectionEnabled
{
get { return (bool)GetValue(SelectionEnabledProperty); }
set { SetValue(SelectionEnabledProperty, value); }
}
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public ICommand PullToRefreshCommand
{
get { return (ICommand)GetValue(PullToRefreshCommandProperty); }
set { SetValue(PullToRefreshCommandProperty, value); }
}
public bool IsRefreshing
{
get { return (bool)GetValue(IsRefreshingProperty); }
set { SetValue(IsRefreshingProperty, value); }
}
public Thickness BorderThickness
{
get { return (Thickness)GetValue(BorderThicknessProperty); }
set { SetValue(BorderThicknessProperty, value); }
}
public bool HeaderBordersVisible
{
get { return (bool)GetValue(HeaderBordersVisibleProperty); }
set { SetValue(HeaderBordersVisibleProperty, value); }
}
public SortData SortedColumnIndex
{
get { return (SortData)GetValue(SortedColumnIndexProperty); }
set { SetValue(SortedColumnIndexProperty, value); }
}
public Style HeaderLabelStyle
{
get { return (Style)GetValue(HeaderLabelStyleProperty); }
set { SetValue(HeaderLabelStyleProperty, value); }
}
public ImageSource AscendingIcon
{
get { return (ImageSource)GetValue(AscendingIconProperty); }
set { SetValue(AscendingIconProperty, value); }
}
public ImageSource DescendingIcon
{
get { return (ImageSource)GetValue(DescendingIconProperty); }
set { SetValue(DescendingIconProperty, value); }
}
public Style AscendingIconStyle
{
get { return (Style)GetValue(AscendingIconStyleProperty); }
set { SetValue(AscendingIconStyleProperty, value); }
}
public Style DescendingIconStyle
{
get { return (Style)GetValue(DescendingIconStyleProperty); }
set { SetValue(DescendingIconStyleProperty, value); }
}
public View NoDataView
{
get { return (View)GetValue(NoDataViewProperty); }
set { SetValue(NoDataViewProperty, value); }
}
#endregion
#region UI Methods
protected override void OnParentSet()
{
base.OnParentSet();
InitHeaderView();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
SetColumnsBindingContext();
}
#endregion
#region Private Methods
private void Reload()
{
InternalItems = new ObservableCollection<object>(_internalItems);
}
private void SortItems(SortData sData)
{
if (InternalItems == null || sData.Index >= Columns.Count || !Columns[sData.Index].SortingEnabled)
return;
var items = InternalItems;
var column = Columns[sData.Index];
SortingOrder order = sData.Order;
if (!IsSortable)
throw new InvalidOperationException("This DataGrid is not sortable");
else if (column.PropertyName == null)
throw new InvalidOperationException("Please set the PropertyName property of Column");
//Sort
// if (order == SortingOrder.Descendant)
// items = items.OrderByDescending(x => ReflectionUtils.GetValueByPath(x, column.PropertyName)).ToList();
// else
// items = items.OrderBy(x => ReflectionUtils.GetValueByPath(x, column.PropertyName)).ToList();
column.SortingIcon.Style = (order == SortingOrder.Descendant) ?
AscendingIconStyle ?? (Style)_headerView.Resources["DescendingIconStyle"] :
DescendingIconStyle ?? (Style)_headerView.Resources["AscendingIconStyle"];
//Support DescendingIcon property (if setted)
if (!column.SortingIcon.Style.Setters.Any(x => x.Property == Image.SourceProperty))
{
if (order == SortingOrder.Descendant && DescendingIconProperty.DefaultValue != DescendingIcon)
column.SortingIcon.Source = DescendingIcon;
if (order == SortingOrder.Ascendant && AscendingIconProperty.DefaultValue != AscendingIcon)
column.SortingIcon.Source = AscendingIcon;
}
for (int i = 0; i < Columns.Count; i++)
{
if (i != sData.Index)
{
if (Columns[i].SortingIcon.Style != null)
Columns[i].SortingIcon.Style = null;
if (Columns[i].SortingIcon.Source != null)
Columns[i].SortingIcon.Source = null;
_sortingOrders[i] = SortingOrder.None;
}
}
_internalItems = items;
_sortingOrders[sData.Index] = order;
SortedColumnIndex = sData;
}
private View GetHeaderViewForColumn(DataGridColumn column)
{
column.HeaderLabel.Style = column.HeaderLabelStyle ?? this.HeaderLabelStyle ?? (Style)_headerView.Resources["HeaderDefaultStyle"];
Grid grid = new Grid
{
ColumnSpacing = 0,
};
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) });
if (IsSortable)
{
column.SortingIcon.Style = (Style)_headerView.Resources["ImageStyleBase"];
grid.Children.Add(column.SortingIcon);
Grid.SetColumn(column.SortingIcon, 1);
TapGestureRecognizer tgr = new TapGestureRecognizer();
tgr.Tapped += (s, e) =>
{
int index = Columns.IndexOf(column);
SortingOrder order = _sortingOrders[index] == SortingOrder.Ascendant ? SortingOrder.Descendant : SortingOrder.Ascendant;
if (Columns.ElementAt(index).SortingEnabled)
SortedColumnIndex = new SortData(index, order);
};
grid.GestureRecognizers.Add(tgr);
}
grid.Children.Add(column.HeaderLabel);
return grid;
}
private void InitHeaderView()
{
SetColumnsBindingContext();
_headerView.Children.Clear();
_headerView.ColumnDefinitions.Clear();
_sortingOrders.Clear();
_headerView.Padding = new Thickness(BorderThickness.Left, BorderThickness.Top, BorderThickness.Right, 0);
_headerView.ColumnSpacing = BorderThickness.HorizontalThickness / 2;
if (Columns != null)
{
foreach (var col in Columns)
{
_headerView.ColumnDefinitions.Add(new ColumnDefinition { Width = col.Width });
var cell = GetHeaderViewForColumn(col);
_headerView.Children.Add(cell);
Grid.SetColumn(cell, Columns.IndexOf(col));
_sortingOrders.Add(Columns.IndexOf(col), SortingOrder.None);
}
}
}
private void SetColumnsBindingContext()
{
if (Columns != null)
foreach (var c in Columns)
c.BindingContext = BindingContext;
}
#endregion Private Methods
}
}

View File

@ -1,135 +0,0 @@
using System;
using Xamarin.Forms;
namespace Aurora.Design.Components.DataGrid
{
public class DataGridColumn : BindableObject, IDefinition
{
#region bindable properties
public static readonly BindableProperty WidthProperty =
BindableProperty.Create(nameof(Width), typeof(GridLength), typeof(DataGridColumn), new GridLength(1, GridUnitType.Star),
propertyChanged: (bindable, oldValue, newValue) =>
{
if (oldValue != newValue)
{
(bindable as DataGridColumn).OnSizeChanged();
};
});
public static readonly BindableProperty TitleProperty =
BindableProperty.Create(nameof(Title), typeof(string), typeof(DataGridColumn), string.Empty,
propertyChanged: (b, o, n) => (b as DataGridColumn).HeaderLabel.Text = (string)n);
public static readonly BindableProperty FormattedTitleProperty =
BindableProperty.Create(nameof(FormattedTitle), typeof(FormattedString), typeof(DataGridColumn),
propertyChanged: (b, o, n) => (b as DataGridColumn).HeaderLabel.FormattedText = (FormattedString)n);
public static readonly BindableProperty PropertyNameProperty =
BindableProperty.Create(nameof(PropertyName), typeof(string), typeof(DataGridColumn), null);
public static readonly BindableProperty StringFormatProperty =
BindableProperty.Create(nameof(StringFormat), typeof(string), typeof(DataGridColumn), null);
public static readonly BindableProperty CellTemplateProperty =
BindableProperty.Create(nameof(CellTemplate), typeof(DataTemplate), typeof(DataGridColumn), null);
public static readonly BindableProperty HorizontalContentAlignmentProperty =
BindableProperty.Create(nameof(HorizontalContentAlignment), typeof(LayoutOptions), typeof(DataGridColumn), LayoutOptions.Center);
public static readonly BindableProperty VerticalContentAlignmentProperty =
BindableProperty.Create(nameof(VerticalContentAlignment), typeof(LayoutOptions), typeof(DataGridColumn), LayoutOptions.Center);
public static readonly BindableProperty SortingEnabledProperty =
BindableProperty.Create(nameof(SortingEnabled), typeof(bool), typeof(DataGridColumn), true);
public static readonly BindableProperty HeaderLabelStyleProperty =
BindableProperty.Create(nameof(HeaderLabelStyle), typeof(Style), typeof(DataGridColumn),
propertyChanged: (b, o, n) =>
{
if ((b as DataGridColumn).HeaderLabel != null && (o != n))
(b as DataGridColumn).HeaderLabel.Style = n as Style;
});
#endregion
#region properties
public GridLength Width
{
get { return (GridLength)GetValue(WidthProperty); }
set { SetValue(WidthProperty, value); }
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public FormattedString FormattedTitle
{
get { return (string)GetValue(FormattedTitleProperty); }
set { SetValue(FormattedTitleProperty, value); }
}
public string PropertyName
{
get { return (string)GetValue(PropertyNameProperty); }
set { SetValue(PropertyNameProperty, value); }
}
public string StringFormat
{
get { return (string)GetValue(StringFormatProperty); }
set { SetValue(StringFormatProperty, value); }
}
public DataTemplate CellTemplate
{
get { return (DataTemplate)GetValue(CellTemplateProperty); }
set { SetValue(CellTemplateProperty, value); }
}
internal Image SortingIcon { get; set; }
internal Label HeaderLabel { get; set; }
public LayoutOptions HorizontalContentAlignment
{
get { return (LayoutOptions)GetValue(HorizontalContentAlignmentProperty); }
set { SetValue(HorizontalContentAlignmentProperty, value); }
}
public LayoutOptions VerticalContentAlignment
{
get { return (LayoutOptions)GetValue(VerticalContentAlignmentProperty); }
set { SetValue(VerticalContentAlignmentProperty, value); }
}
public bool SortingEnabled
{
get { return (bool)GetValue(SortingEnabledProperty); }
set { SetValue(SortingEnabledProperty, value); }
}
public Style HeaderLabelStyle
{
get { return (Style)GetValue(HeaderLabelStyleProperty); }
set { SetValue(HeaderLabelStyleProperty, value); }
}
#endregion
public event EventHandler SizeChanged;
public DataGridColumn()
{
HeaderLabel = new Label();
SortingIcon = new Image();
}
void OnSizeChanged()
{
SizeChanged?.Invoke(this, EventArgs.Empty);
}
}
}

View File

@ -1,30 +0,0 @@
using Xamarin.Forms;
namespace Aurora.Design.Components.DataGrid
{
internal class DataGridRowTemplateSelector : DataTemplateSelector
{
private static DataTemplate _dataGridRowTemplate;
public DataGridRowTemplateSelector()
{
_dataGridRowTemplate = new DataTemplate(typeof(DataGridViewCell));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
ListView listView = container as ListView;
DataGrid dataGrid = listView.Parent as DataGrid;
var items = dataGrid.InternalItems;
_dataGridRowTemplate.SetValue(DataGridViewCell.DataGridProperty, dataGrid);
_dataGridRowTemplate.SetValue(DataGridViewCell.RowContextProperty, item);
if (items != null)
{
_dataGridRowTemplate.SetValue(DataGridViewCell.IndexProperty, items.IndexOf(item));
}
return _dataGridRowTemplate;
}
}
}

View File

@ -1,163 +0,0 @@
using Xamarin.Forms;
namespace Aurora.Design.Components.DataGrid
{
internal sealed class DataGridViewCell : ViewCell
{
#region Fields
Grid _mainLayout;
Color _bgColor;
Color _textColor;
bool _hasSelected;
#endregion
#region properties
public DataGrid DataGrid
{
get { return (DataGrid)GetValue(DataGridProperty); }
set { SetValue(DataGridProperty, value); }
}
public int Index
{
get { return (int)GetValue(IndexProperty); }
set { SetValue(IndexProperty, value); }
}
public object RowContext
{
get { return GetValue(RowContextProperty); }
set { SetValue(RowContextProperty, value); }
}
#endregion
#region Bindable Properties
public static readonly BindableProperty DataGridProperty =
BindableProperty.Create(
nameof(DataGrid),
typeof(DataGrid),
typeof(DataGridViewCell),
null,
propertyChanged: (b, o, n) => (b as DataGridViewCell).CreateView());
public static readonly BindableProperty IndexProperty =
BindableProperty.Create(
nameof(Index),
typeof(int),
typeof(DataGridViewCell),
0,
propertyChanged: (b, o, n) => (b as DataGridViewCell).UpdateBackgroundColor());
public static readonly BindableProperty RowContextProperty =
BindableProperty.Create(
nameof(RowContext),
typeof(object),
typeof(DataGridViewCell),
propertyChanged: (b, o, n) => (b as DataGridViewCell).UpdateBackgroundColor());
#endregion
#region Methods
private void CreateView()
{
UpdateBackgroundColor();
_mainLayout = new Grid()
{
BackgroundColor = DataGrid.BorderColor,
RowSpacing = 0,
ColumnSpacing = DataGrid.BorderThickness.HorizontalThickness / 2,
Padding = new Thickness(DataGrid.BorderThickness.HorizontalThickness / 2,
DataGrid.BorderThickness.VerticalThickness / 2),
};
foreach (var col in DataGrid.Columns)
{
_mainLayout.ColumnDefinitions.Add(new ColumnDefinition() { Width = col.Width });
View cell;
if (col.CellTemplate != null)
{
cell = new ContentView() { Content = col.CellTemplate.CreateContent() as View };
if (col.PropertyName != null)
{
cell.SetBinding(BindingContextProperty,
new Binding(col.PropertyName, source: RowContext));
}
}
else
{
var text = new Label
{
TextColor = _textColor,
HorizontalOptions = col.HorizontalContentAlignment,
VerticalOptions = col.VerticalContentAlignment,
LineBreakMode = LineBreakMode.WordWrap,
};
text.SetBinding(Label.TextProperty, new Binding(col.PropertyName, BindingMode.Default, stringFormat: col.StringFormat));
text.SetBinding(Label.FontSizeProperty, new Binding(DataGrid.FontSizeProperty.PropertyName, BindingMode.Default, source: DataGrid));
text.SetBinding(Label.FontFamilyProperty, new Binding(DataGrid.FontFamilyProperty.PropertyName, BindingMode.Default, source: DataGrid));
cell = new ContentView
{
Padding = 0,
BackgroundColor = _bgColor,
Content = text,
};
}
_mainLayout.Children.Add(cell);
Grid.SetColumn(cell, DataGrid.Columns.IndexOf(col));
}
View = _mainLayout;
}
private void UpdateBackgroundColor()
{
_hasSelected = DataGrid.SelectedItem == RowContext;
int actualIndex = DataGrid?.InternalItems?.IndexOf(BindingContext) ?? -1;
if (actualIndex > -1)
{
_bgColor = (DataGrid.SelectionEnabled && DataGrid.SelectedItem != null && DataGrid.SelectedItem == RowContext) ?
DataGrid.ActiveRowColor : DataGrid.RowsBackgroundColorPalette.GetColor(Index, BindingContext);
_textColor = DataGrid.RowsTextColorPalette.GetColor(actualIndex, BindingContext);
ChangeColor(_bgColor);
}
}
private void ChangeColor(Color color)
{
foreach (var v in _mainLayout.Children)
{
v.BackgroundColor = color;
var contentView = v as ContentView;
if (contentView?.Content is Label)
((Label)contentView.Content).TextColor = _textColor;
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
UpdateBackgroundColor();
}
protected override void OnParentSet()
{
base.OnParentSet();
if (Parent != null)
DataGrid.ItemSelected += DataGrid_ItemSelected;
else
DataGrid.ItemSelected -= DataGrid_ItemSelected;
}
private void DataGrid_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (DataGrid.SelectionEnabled && (e.SelectedItem == RowContext || _hasSelected))
{
UpdateBackgroundColor();
}
}
#endregion
}
}

View File

@ -1,8 +0,0 @@
using Xamarin.Forms;
namespace Aurora.Design.Components.DataGrid
{
public interface IColorProvider
{
Color GetColor(int rowIndex, object item);
}
}

View File

@ -1,17 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;
namespace Aurora.Design.Components.DataGrid
{
public sealed class PaletteCollection : List<Color>, IColorProvider
{
public Color GetColor(int rowIndex, object item)
{
if (Count > 0)
return this.ElementAt(rowIndex % Count);
else
return default(Color);
}
}
}

View File

@ -1,51 +0,0 @@
using System;
using Xamarin.Forms;
namespace Aurora.Design.Components.DataGrid
{
[TypeConverter(typeof(SortDataTypeConverter))]
public class SortData
{
#region ctor
public SortData()
{
}
public SortData(int index, SortingOrder order)
{
Index = index;
Order = order;
}
#endregion
#region Properties
public SortingOrder Order { get; set; }
public int Index { get; set; }
#endregion
public static implicit operator SortData(int index)
{
return new SortData
{
Index = Math.Abs(index),
Order = index < 0 ? SortingOrder.Descendant : SortingOrder.Ascendant
};
}
public override bool Equals(object obj)
{
if (obj is SortData)
{
SortData other = obj as SortData;
return other.Index == Index && other.Order == Order;
}
else
return false;
}
}
}

View File

@ -1,24 +0,0 @@
using System;
using Xamarin.Forms;
namespace Aurora.Design.Components.DataGrid
{
public class SortDataTypeConverter : TypeConverter
{
public override bool CanConvertFrom(Type sourceType)
{
return base.CanConvertFrom(sourceType);
}
public override object ConvertFromInvariantString(string value)
{
int index = 0;
if (int.TryParse(value, out index))
return (SortData)index;
else
return null;
}
}
}

View File

@ -1,9 +0,0 @@
namespace Aurora.Design.Components.DataGrid
{
public enum SortingOrder
{
None = 0,
Ascendant = 1,
Descendant = 2,
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -1,577 +0,0 @@
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
using System.Linq;
using Xamarin.Forms;
using Aurora.Proto.Party;
using Aurora.Models.Media;
using Aurora.Services.Client;
using Aurora.Design.Views.Party.NewPartyDialog;
using Aurora.Services.Settings;
using Aurora.Services.Server;
using Aurora.Services.EventManager;
using Grpc.Core;
namespace Aurora.Design.Views.Party
{
//TODO refactor
enum PartyState
{
SelectingHost,
InParty,
Hosting,
Connecting,
}
delegate void EventHandler(BaseEvent e);
public class PartyViewModel : BaseViewModel
{
private PartyState _state;
private string _hostname = "";
private ObservableCollection<Member> _members;
private ObservableCollection<BaseMedia> _queue;
private BaseMedia _selectedMedia;
private ISettingsService _settingsService;
private IClientService _clientService;
private IServerService _serverService;
private IEventManager _eventManager;
private CancellationTokenSource _eventCancellationTokenSource;
private Dictionary<BaseEvent.DerivedEventOneofCase, EventHandler> _eventHandlers;
private int _selectedTabIndex;
public PartyViewModel(
ISettingsService settingsService,
IServerService serverService,
IEventManager eventManager,
IClientService clientService)
{
_members = new ObservableCollection<Member>();
_queue = new ObservableCollection<BaseMedia>();
this._settingsService = settingsService;
this._serverService = serverService;
this._eventManager = eventManager;
this._clientService = clientService;
SetState(PartyState.SelectingHost);
PlayCommand = new Command(OnDoubleClickCommandExecute, CanDoubleClickCommandExecute);
LeavePartyCommand = new Command(OnLeavePartyCommandExecute, CanLeavePartyCommandExecute);
//Setup event handlers
_eventHandlers = new Dictionary<BaseEvent.DerivedEventOneofCase, EventHandler>()
{
{BaseEvent.DerivedEventOneofCase.MediaPausedEvent, this.OnRemoteMediaPaused},
{BaseEvent.DerivedEventOneofCase.MediaResumedEvent, this.OnRemoteMediaResumed},
{BaseEvent.DerivedEventOneofCase.NewMediaPlayingEvent, this.OnNewRemoteMediaPlaying},
{BaseEvent.DerivedEventOneofCase.MemberCreatedEvent, this.OnPartyMemberJoined},
{BaseEvent.DerivedEventOneofCase.MemberDeletedEvent, this.OnPartyMemberLeft}
};
}
#region Properties
public int SelectedTabIndex
{
get { return _selectedTabIndex; }
set { SetProperty(ref _selectedTabIndex, value); }
}
/// <summary>
/// Publc property for the members list
/// </summary>
/// <value></value>
public ObservableCollection<Member> Members
{
get
{
return _members;
}
set
{
SetProperty(ref _members, value);
}
}
/// <summary>
/// Public property for queue item source
/// </summary>
/// <value></value>
public ObservableCollection<BaseMedia> Queue
{
get
{
return _queue;
}
set
{
if (value != _queue)
{
SetProperty(ref _queue, value);
}
}
}
/// <summary>
/// Public property for the currently selected song.
/// </summary>
/// <value></value>
public BaseMedia SelectedSong
{
get { return _selectedMedia; }
set { SetProperty(ref _selectedMedia, value); }
}
/// <summary>
/// Public property for playing media
/// </summary>
/// <value></value>
public Command PlayCommand { get; private set; }
public Command LeavePartyCommand { get; private set; }
#endregion Properties
#region Events
/// <summary>
/// Called by framework when view becomes active
/// </summary>
/// <returns></returns>
public override async Task OnActive()
{
OnPropertyChanged("SelectedTabIndex");
if (this._state == PartyState.Hosting ||
this._state == PartyState.InParty)
{
await this.GetEvents().ConfigureAwait(false);
}
else
{
//Open host selection modal
NewPartyDialogViewModel vm = new NewPartyDialogViewModel();
ConnectionDetails details = new ConnectionDetails();
vm.Finish = () =>
{
this.HideModal();
details = vm.ReturnObject as ConnectionDetails;
_hostname = details.HostName;
switch (details.ConnectionType)
{
case ConnectionType.Host:
{
OnHostCommandExecute();
break;
}
case ConnectionType.Join:
{
OnJoinCommandExecute();
break;
}
}
};
this.ShowModal(typeof(NewPartyDialog.NewPartyDialog), vm);
}
}
/// <summary>
/// Called by framework when view becomes inactive
/// </summary>
/// <returns></returns>
public override Task OnInactive()
{
if(this._eventCancellationTokenSource != null){
this._eventCancellationTokenSource.Cancel();
}
return Task.FromResult<object>(null);
}
/// <summary>
/// Remote media paused event
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void OnRemoteMediaPaused(BaseEvent e)
{
StopPlaying();
}
/// <summary>
/// Remote playing new media event
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void OnNewRemoteMediaPlaying(BaseEvent e)
{
PlayFromBeginning(GetMediaFromQueue(e.NewMediaPlayingEvent.Media.Name));
}
/// <summary>
/// Remote resumed playing event
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void OnRemoteMediaResumed(BaseEvent e)
{
PlayResume();
}
/// <summary>
/// Member joined party event
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void OnPartyMemberJoined(BaseEvent e)
{
Members.Add(e.MemberCreatedEvent.Member);
}
/// <summary>
/// Member left party event
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void OnPartyMemberLeft(BaseEvent e)
{
var found = Members.Where(x => x.Name == e.MemberDeletedEvent.MemberName);
foreach (Member member in found)
{
_members.Remove(member);
}
}
#endregion Events
#region Commands
private async void OnJoinCommandExecute()
{
SetState(PartyState.Connecting);
_clientService.Start(_hostname, this._settingsService.DefaultPort.ToString());
await JoinParty(false);
//TODO add cancellation token
try
{
SetState(PartyState.InParty);
await GetEvents().ConfigureAwait(true);
}
catch (Exception ex)
{
Console.WriteLine("Exception occurred while receiviing events: ", ex.Message);
}
}
private bool CanJoinCommandExecute()
{
return true;
}
private async void OnHostCommandExecute()
{
//Change state
SetState(PartyState.Connecting);
_serverService.Start("test", "asdf");
string localHost = ServerService.GetLocalIPAddress();
_clientService.Start(localHost, this._settingsService.DefaultPort.ToString());
await JoinParty(true);
//TODO add cancellation token
try
{
SetState(PartyState.Hosting);
await GetEvents().ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine("Exception occurred while receiviing events: ", ex.Message);
}
}
private bool CanHostCommandExecute()
{
return true;
}
private async void OnLeavePartyCommandExecute()
{
await _clientService.RemotePartyServiceClient.DeleteMemberAsync(new DeleteMemberRequest()
{
Name = _settingsService.ClientName
});
}
private bool CanLeavePartyCommandExecute()
{
return (this._state == PartyState.InParty || this._state == PartyState.Hosting) ? true : false;
}
public override void OnPlayButtonCommandExecute()
{
if (base.IsPlaying())
{
//Fire play stopped event
AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata;
MediaPausedEvent mediaPaused = new MediaPausedEvent();
_eventManager.FireEvent(new BaseEvent()
{
MediaPausedEvent = mediaPaused
});
}
else
{
//Fire play resume event
AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata;
MediaResumedEvent mediaResumed = new MediaResumedEvent();
_eventManager.FireEvent(new BaseEvent()
{
MediaResumedEvent = mediaResumed
});
}
}
public override bool CanPlayButtonCommandExecute()
{
return this._state == PartyState.Hosting;
}
public override bool CanNextButtonCommandExecute()
{
return this._state == PartyState.Hosting;
}
public override bool CanPreviousButtonCommandExecute()
{
return this._state == PartyState.Hosting;
}
/// <summary>
/// On double click execute, fire media playing event
/// </summary>
public void OnDoubleClickCommandExecute()
{
//Fire Playing event
AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata;
NewMediaPlayingEvent mediaPlaying = new NewMediaPlayingEvent()
{
Media = new Media()
{
//TODO need full resource name
Name = _selectedMedia.Id,
Title = meta.Title,
Artist = meta.Artist,
Album = meta.Album,
}
};
_eventManager.FireEvent(new BaseEvent()
{
NewMediaPlayingEvent = mediaPlaying
});
}
public bool CanDoubleClickCommandExecute()
{
return this._state == PartyState.Hosting;
}
#endregion Commands
#region Private Methods
/// <summary>
/// Join the remote party.
/// </summary>
/// <returns></returns>
private async Task JoinParty(bool asHost)
{
try
{
Member resp = await _clientService.RemotePartyServiceClient.CreateMemberAsync(new CreateMemberRequest
{
Member = new Member()
{
UserName = this._settingsService.Username,
}
});
this._settingsService.ClientName = resp.Name;
await RefreshMembers();
//Subscribe to events
await SubscribeToEvents();
Queue.Clear();
ListMediaResponse mediaResponse = await _clientService.RemotePartyServiceClient.ListMediaAsync(new ListMediaRequest()
{
PageSize = 50,
Parent = "TODO"
});
//Convert received data to remote audio models
foreach (Media data in mediaResponse.Media)
{
//Assign received metadata (since this can't be aquired from a file)
AudioMetadata meta = new AudioMetadata();
meta.Title = data.Title;
meta.Album = data.Album;
meta.Artist = data.Artist;
meta.Duration = data.Duration;
RemoteAudio remote = new RemoteAudio(data.Name,
asHost,
meta,
_clientService.RemotePartyServiceClient);
Queue.Add(remote);
OnPropertyChanged("Queue");
}
}
catch (Exception ex)
{
Console.WriteLine("Error subscribing to events: " + ex.Message);
}
}
private async Task LeaveParty()
{
//Stop receiving events
// _client.StopEvents();
//Unsubscribe
await UnsubscribeFromEvents();
//Leave party
DeleteMemberRequest req = new DeleteMemberRequest()
{
Name = _settingsService.ClientName
};
await _clientService.RemotePartyServiceClient.DeleteMemberAsync(req);
}
private async Task SubscribeToEvents()
{
CreateEventSubscriptionListRequest req = new CreateEventSubscriptionListRequest();
req.Parent = this._settingsService.ClientName;
req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MemberCreated });
req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MemberDeleted });
req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MediaPlaying });
req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MediaStopped });
Console.WriteLine(string.Format("CLIENT {0} - SubscribeToEvents called from client with id", this._settingsService.ClientName));
await _clientService.RemotePartyServiceClient.CreateEventSubscriptionListAsync(req);
}
private async Task UnsubscribeFromEvents()
{
DeleteAllEventSubscriptionsRequest unsubscribeReq = new DeleteAllEventSubscriptionsRequest();
await _clientService.RemotePartyServiceClient.DeleteAllEventSubscriptionsAsync(unsubscribeReq);
}
/// <summary>
/// Refresh members list.
/// </summary>
private async Task RefreshMembers()
{
Members.Clear();
ListMembersResponse response = await _clientService.RemotePartyServiceClient.ListMembersAsync(
new ListMembersRequest()
{
Parent = "TODO",
PageSize = 50,
});
//Add members
foreach (Member member in response.Members)
{
Members.Add(member);
}
}
private void SetState(PartyState state)
{
_state = state;
OnPropertyChanged("IsSelectingHost");
OnPropertyChanged("IsNotSelectingHost");
}
private BaseMedia GetMediaFromQueue(string Id)
{
if (_queue.Any((BaseMedia media) => media.Id == Id))
{
BaseMedia media = _queue.First((BaseMedia med) => med.Id == Id);
return media;
}
else
{
return null;
}
}
private void PlayFromBeginning(BaseMedia args)
{
base.ChangePlayerState(args, Main.PlayAction.Play);
}
private void PlayResume()
{
base.ChangePlayerState(null, Main.PlayAction.Resume);
}
private void StopPlaying()
{
base.ChangePlayerState(null, Main.PlayAction.Pause);
}
/// <summary>
/// Asynchronous function for processing events off of the event stream.
/// </summary>
/// <returns></returns>
public async Task GetEvents()
{
_eventCancellationTokenSource = new CancellationTokenSource();
string clientName = this._settingsService.ClientName;
Console.WriteLine(string.Format("CLIENT {0} - GetEvents called from client with id", clientName));
using (AsyncServerStreamingCall<BaseEvent> eventStream = _clientService.RemotePartyServiceClient
.GetEvents(new GetEventsRequest { Parent = this._settingsService.ClientName }))
{
try
{
while (!_eventCancellationTokenSource.Token.IsCancellationRequested &&
await eventStream.ResponseStream.MoveNext(_eventCancellationTokenSource.Token))
{
try
{
BaseEvent e = new BaseEvent(eventStream.ResponseStream.Current);
_eventHandlers.TryGetValue(e.DerivedEventCase, out EventHandler handler);
if (handler != null && handler != null)
{
handler.Invoke(e);
}
}
catch (Exception ex)
{
Console.WriteLine("Exception while parsing event ---" + ex.Message);
}
}
}
catch (Exception ex)
{
Console.WriteLine(string.Format("EXCEPTION while parsing events --- " + ex.Message));
}
}
}
#endregion Private Methods
}
}

View File

@ -1,11 +1,12 @@
using System;
using Aurora.Cursor;
namespace Aurora.Proto.Party
{
/// <summary>
/// Partial PartyMember class with a constructor that generates a new id
/// </summary>
public partial class Member
public partial class Member : ICursorObject
{
public Member(string id)
{

View File

@ -1,46 +0,0 @@
using Grpc.Core;
using Aurora.Proto.Party;
using Aurora.Services.Settings;
namespace Aurora.Services.Client
{
public class ClientService : IClientService
{
private RemotePartyService.RemotePartyServiceClient _remotePartyClient;
private Channel _channel;
private ISettingsService _settingsService;
public ClientService(ISettingsService settingsService)
{
this._settingsService = settingsService;
}
public bool IsStarted
{
get
{
return _remotePartyClient != null;
}
}
public RemotePartyService.RemotePartyServiceClient RemotePartyServiceClient
{
get { return this._remotePartyClient; }
}
public void Start(string hostname, string port)
{
_channel = new Channel(string.Format("{0}:{1}", hostname, port), ChannelCredentials.Insecure);
_remotePartyClient = new RemotePartyService.RemotePartyServiceClient(_channel);
}
public async void Close()
{
await _channel.ShutdownAsync();
_remotePartyClient = null;
}
}
}

View File

@ -1,15 +0,0 @@
using Aurora.Proto.Party;
namespace Aurora.Services.Client
{
public interface IClientService
{
bool IsStarted { get; }
RemotePartyService.RemotePartyServiceClient RemotePartyServiceClient { get; }
void Start(string hostname, string port);
void Close();
}
}

View File

@ -6,9 +6,10 @@ using Aurora.Services.Library;
using Aurora.Services.Settings;
using Aurora.Models.Media;
using Aurora.Services.EventManager;
using Autofac;
using Aurora.Utils;
using Aurora.Cursor;
namespace Aurora.Services.Server.Controllers
namespace Aurora.Services.Controllers
{
public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase
{
@ -24,7 +25,7 @@ namespace Aurora.Services.Server.Controllers
this._startDateTime = DateTime.UtcNow;
this._displayName = partyName;
this._description = description;
this._memberList = new SortedList<string, Member>();
this._memberList = new CursorList<Member>();
this._mediaList = new SortedList<string, Media>();
_libraryService = libraryService;
@ -36,9 +37,9 @@ namespace Aurora.Services.Server.Controllers
this._hostMember = new Member()
{
Name = GetNewMemberResourceName(_partyResourceName, ServerService.GetLocalIPAddress(), userName),
Name = GetNewMemberResourceName(_partyResourceName, IpUtil.GetLocalIPAddress(), userName),
UserName = userName,
IpAddress = ServerService.GetLocalIPAddress(),
IpAddress = IpUtil.GetLocalIPAddress(),
};
//Add media from library

View File

@ -4,7 +4,7 @@ using System.Threading;
using Aurora.Proto.Party;
using Aurora.Utils;
namespace Aurora.Services.Server.Controllers
namespace Aurora.Services.Controllers
{
public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase
{

View File

@ -5,7 +5,7 @@ using Aurora.Proto.Party;
using Aurora.Proto.General;
using Aurora.Utils;
namespace Aurora.Services.Server.Controllers
namespace Aurora.Services.Controllers
{
public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase
{

View File

@ -8,7 +8,7 @@ using Aurora.Services.Library;
using Aurora.Services.Player;
using Autofac;
namespace Aurora.Services.Server.Controllers
namespace Aurora.Services.Controllers
{
public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase
{

View File

@ -6,40 +6,50 @@ using Aurora.Proto.General;
using Aurora.Utils;
using Grpc.Core;
using Google.Protobuf.WellKnownTypes;
using Aurora.Cursor;
namespace Aurora.Services.Server.Controllers
namespace Aurora.Services.Controllers
{
public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase
{
private SortedList<string, Member> _memberList;
private CursorList<Member> _memberList;
public CursorList<Member> MemberList
{
get
{
return this._memberList;
}
set
{
if(this._memberList != value)
{
_memberList = value;
}
}
}
public override Task<ListMembersResponse> ListMembers(ListMembersRequest request, Grpc.Core.ServerCallContext context)
{
Cursor<Member> cursor = new Cursor<Member>(ref this._memberList);
Aurora.Cursor.SortDirection direction = Aurora.Cursor.SortDirection.Asc;
CursorResult<Member> res = cursor
.WithNextPageToken(request.PageToken)
.WithSize(request.PageSize)
.GetNextPage();
//Ignoring parent field because there is only one instance of the party
ListMembersResponse resp = new ListMembersResponse();
//Determine start idx
int startIdx = 0;
if (!string.IsNullOrEmpty(request.PageToken))
resp.Members.AddRange(res.Result.ConvertAll(member => new Member()
{
startIdx = _memberList.IndexOfKey(request.PageToken) + 1;
}
int pageSize = request.PageSize;
//Assign pageSize
if (pageSize > _memberList.Count)
{
pageSize = _memberList.Count;
}
//Gather page
List<Member> members = new List<Member>(_memberList.Values);
resp.Members.AddRange(members.GetRange(startIdx, pageSize));
//Set next page token
resp.NextPageToken = resp.Members[resp.Members.Count - 1].Name;
Name = member.Name,
UserName = member.UserName,
IpAddress = member.IpAddress,
AddedOn = member.AddedOn
}));
return Task.FromResult(resp);
}
@ -76,7 +86,7 @@ namespace Aurora.Services.Server.Controllers
request.Member.AddedOn = Timestamp.FromDateTime(DateTime.UtcNow);
request.Member.IpAddress = context.Host;
_memberList.Add(resourceName, request.Member);
_memberList.Add(request.Member);
BaseEvent @event = new BaseEvent
{

View File

@ -1,11 +1,11 @@
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Aurora.Utils;
using Aurora.Proto.Party;
using Google.Protobuf.WellKnownTypes;
namespace Aurora.Services.Server.Controllers
namespace Aurora.Services.Controllers
{
public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase
{
@ -22,7 +22,7 @@ namespace Aurora.Services.Server.Controllers
Name = _partyResourceName,
DisplayName = this._displayName,
Description = this._description,
HostIp = ServerService.GetLocalIPAddress(),
HostIp = IpUtil.GetLocalIPAddress(),
HostMember = this._hostMember,
CreatedOn = Timestamp.FromDateTime(_startDateTime)
};

View File

@ -0,0 +1,19 @@
namespace Aurora.Services
{
public enum PartyState
{
Idle,
SelectingHost,
InParty,
Hosting,
Connecting,
}
public class Global
{
public Global()
{
this.State = PartyState.Idle;
}
public PartyState State {get; set;}
}
}

View File

@ -1,27 +0,0 @@
using System.Threading.Tasks;
namespace Aurora.Services.Server
{
public interface IServerService
{
int Port { get; }
string Hostname { get; }
bool Initialized { get; }
/// <summary>
/// Start Server
/// </summary>
void Start(string partyName, string description);
/// <summary>
/// Shutdown server async.
/// </summary>
/// <returns>Task</returns>
Task Stop();
Task Reset();
}
}

View File

@ -1,144 +0,0 @@
using System;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using Grpc.Core;
using Aurora.Services.Server.Controllers;
using Aurora.Services.Settings;
using Aurora.Services.Library;
using Aurora.Services.EventManager;
using Aurora.Proto.Party;
namespace Aurora.Services.Server
{
public class ServerService : IServerService
{
private int _port = 8080;
private string _hostname;
private Grpc.Core.Server _server;
private ILibraryService _libraryService;
private ISettingsService _settingsService;
private IEventManager _eventManager;
//Implementation class declarations
private RemotePartyController _remotePartyController;
/// <summary>
/// Constructor. Registers GRPC service implementations.
/// </summary>
public ServerService(ILibraryService libraryService, ISettingsService settingsService, IEventManager eventManager)
{
string host = GetLocalIPAddress();
this._libraryService = libraryService;
this._settingsService = settingsService;
this._eventManager = eventManager;
if (string.IsNullOrWhiteSpace(host))
{
throw new Exception("This device must have a valid IP address");
}
_hostname = host;
}
public int Port
{
get { return _port; }
}
public string Hostname
{
get { return _hostname; }
}
public bool Initialized
{
get
{
return (_remotePartyController != null &&
_server != null);
}
}
/// <summary>
/// Start Server
/// </summary>
public void Start(string partyName, string description)
{
try
{
Console.WriteLine(string.Format("Starting gRPC server at hostname: {0}, port: {1}", _hostname, _port));
_server = new Grpc.Core.Server
{
Ports = { new ServerPort(_hostname, _port, ServerCredentials.Insecure) }
};
//Construct implementations
_remotePartyController = new RemotePartyController(
partyName,
description,
_libraryService,
_settingsService,
_eventManager);
// Register grpc RemoteService with singleton server service
RegisterService(RemotePartyService.BindService(_remotePartyController));
_server.Start();
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Error starting gRPC server: {0}", ex.Message));
}
}
/// <summary>
/// Shutdown server async.
/// </summary>
/// <returns>Task</returns>
public async Task Stop()
{
try
{
await _server.ShutdownAsync();
await _server.ShutdownTask;
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Error stopping gRPC server: {0}", ex.Message));
}
}
public async Task Reset()
{
await Stop();
_server = new Grpc.Core.Server
{
Ports = { new ServerPort("localhost", _port, ServerCredentials.Insecure) }
};
}
private void RegisterService(ServerServiceDefinition definition)
{
_server.Services.Add(definition);
}
public static string GetLocalIPAddress()
{
string returnIp = "";
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
returnIp = ip.ToString();
}
}
return returnIp;
}
}
}

View File

@ -2,7 +2,7 @@
<ContentView
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:dg="clr-namespace:Aurora.Design.Components.DataGrid"
xmlns:dg="clr-namespace:Xamarin.Forms.DataGrid;assembly=Xamarin.Forms.DataGrid"
x:Class="Aurora.Design.Components.Library.Library">
<ContentView.Resources>
<StyleSheet

View File

@ -12,7 +12,6 @@ namespace Aurora.Design.Components.NavigationMenu
public NavigationItem()
{
}
public int Id { get; set; }
public string Title { get; set; }
public string Group { get; set; }
public Type TargetType { get; set; }

View File

@ -0,0 +1,50 @@
using Aurora.Models.Media;
using Aurora.Design.Views.Main;
namespace Aurora.Design.Views
{
public class BasePlayerViewModel : BaseViewModel
{
public BasePlayerViewModel(): base()
{
}
/// <summary>
/// Delegate for interacting with main screen player control
/// </summary>
/// <value></value>
public SetPlayerDelegate ChangePlayerState { get; set; }
/// <summary>
/// Delegate for checking if main screen player control is currently playing
/// </summary>
/// <value></value>
public GetIsPlayingDelegate IsPlaying { get; set; }
/// <summary>
/// Command event handler for player play button
/// </summary>
public virtual void OnPlayButtonCommandExecute() { }
public virtual bool CanPlayButtonCommandExecute()
{
return true;
}
/// <summary>
/// Command event handler for player next button
/// </summary>
public virtual void OnNextButtonExecute() { }
public virtual bool CanNextButtonCommandExecute()
{
return true;
}
/// <summary>
/// Command event handler for player previous button
/// </summary>
public virtual void OnPreviousButtonExecute() { }
public virtual bool CanPreviousButtonCommandExecute()
{
return true;
}
}
}

View File

@ -15,55 +15,14 @@ namespace Aurora.Design.Views
{
}
#region Player
/// <summary>
/// Command event handler for player play button
/// </summary>
public virtual void OnPlayButtonCommandExecute() { }
public virtual bool CanPlayButtonCommandExecute()
{
return true;
}
/// <summary>
/// Command event handler for player next button
/// </summary>
public virtual void OnNextButtonExecute() { }
public virtual bool CanNextButtonCommandExecute()
{
return true;
}
/// <summary>
/// Command event handler for player previous button
/// </summary>
public virtual void OnPreviousButtonExecute() { }
public virtual bool CanPreviousButtonCommandExecute()
{
return true;
}
/// <summary>
/// Delegate for interacting with main screen player control
/// </summary>
/// <value></value>
public SetPlayerDelegate ChangePlayerState { get; set; }
/// <summary>
/// Delegate for checking if main screen player control is currently playing
/// </summary>
/// <value></value>
public GetIsPlayingDelegate IsPlaying { get; set; }
public ShowModalDelegate ShowModal { get; set; }
public HideModalDelegate HideModal { get; set; }
public SetViewDelegate SetView { get; set; }
#endregion Player
#region Lifecycle
#region Lifecycle Callbacks
/// <summary>
/// Called by main screen on view appearing
/// </summary>
@ -78,7 +37,7 @@ namespace Aurora.Design.Views
/// <returns></returns>
public virtual Task OnInactive() { return Task.FromResult<object>(null); }
#endregion
#endregion Lifecycle Callbacks
#region INotifyPropertyChanged Implementation
public bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)

View File

@ -31,11 +31,12 @@ namespace Aurora.Design.Views.Main
public delegate void ShowModalDelegate(Type view, BaseDialogViewModel viewModel);
public delegate void HideModalDelegate();
public delegate void FinishDialogDelegate();
public delegate void SetViewDelegate(Type viewType, Type viewModelType);
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainView : ContentPage//, IDisposable
{
private Dictionary<int, BaseViewModel> _viewModels;
private Dictionary<string, BaseViewModel> _viewModels;
private BaseViewModel _lastViewModel;
private Player _playerComponent;
private IPlayer _playerService;
@ -45,7 +46,7 @@ namespace Aurora.Design.Views.Main
{
InitializeComponent();
BindingContext = new MainViewModel();
_viewModels = new Dictionary<int, BaseViewModel>();
_viewModels = new Dictionary<string, BaseViewModel>();
_playerComponent = Player;
@ -74,29 +75,35 @@ namespace Aurora.Design.Views.Main
return;
var view = (View)Activator.CreateInstance(item.TargetType);
string viewModelName = item.TargetViewModelType.Name;
//Check if we have an instantiated viewModel
BaseViewModel vm = new BaseViewModel();
if (_viewModels.ContainsKey(item.Id))
if (_viewModels.ContainsKey(viewModelName))
{
_viewModels.TryGetValue(item.Id, out vm);
_viewModels.TryGetValue(viewModelName, out vm);
}
else
{
if (item.TargetViewModelType.BaseType != typeof(BaseViewModel))
if (item.TargetViewModelType.IsAssignableFrom(typeof(BaseViewModel)))
{
throw new InvalidOperationException("TargetViewModel field must be of type BaseViewModel");
}
//Instantiate new view model
vm = (BaseViewModel)App.Container.Resolve(item.TargetViewModelType); //Activator.CreateInstance(item.TargetViewModelType);
_viewModels.Add(item.Id, vm);
_viewModels.Add(viewModelName, vm);
}
//Assign player controls to viewmodel
AssignPlayerControls(vm);
if(vm is BasePlayerViewModel)
{
AssignPlayerControls(vm as BasePlayerViewModel);
}
ChangeModalVisiblity(false);
vm.SetView = this.SetView;
//Activate viewmodel
vm.OnActive();
@ -104,7 +111,10 @@ namespace Aurora.Design.Views.Main
//Deactivate last viewModel
_lastViewModel.OnInactive();
//Unasign deactivating vm
UnassignPlayerControls(_lastViewModel);
if(_lastViewModel is BasePlayerViewModel)
{
UnassignPlayerControls(_lastViewModel as BasePlayerViewModel);
}
//Assign viewModel
_lastViewModel = vm;
@ -128,20 +138,26 @@ namespace Aurora.Design.Views.Main
var view = (View)Activator.CreateInstance(firstNavItem.TargetType);
BaseViewModel vm = new BaseViewModel();
if (_viewModels.ContainsKey(firstNavItem.Id))
if (_viewModels.ContainsKey(nameof(firstNavItem.TargetViewModelType)))
{
_viewModels.TryGetValue(firstNavItem.Id, out vm);
_viewModels.TryGetValue(nameof(firstNavItem.TargetViewModelType), out vm);
}
else
{
//Instantiate new view model
vm = (BaseViewModel)App.Container.Resolve(firstNavItem.TargetViewModelType); //(BaseViewModel)Activator.CreateInstance(firstNavItem.TargetViewModelType);
_viewModels.Add(firstNavItem.Id, vm);
_viewModels.Add(nameof(firstNavItem.TargetViewModelType), vm);
}
view.BindingContext = vm;
_lastViewModel = vm;
AssignPlayerControls(vm);
if(vm is BasePlayerViewModel)
{
AssignPlayerControls(vm as BasePlayerViewModel);
}
vm.SetView = this.SetView;
ChangeModalVisiblity(false);
vm.OnActive();
@ -153,7 +169,7 @@ namespace Aurora.Design.Views.Main
/// Unassign setplayer delegate to prevent vms from changing player info when inactive
/// </summary>
/// <param name="vm"></param>
private void UnassignPlayerControls(BaseViewModel vm)
private void UnassignPlayerControls(BasePlayerViewModel vm)
{
vm.ChangePlayerState = null;
vm.IsPlaying = null;
@ -164,7 +180,7 @@ namespace Aurora.Design.Views.Main
/// Assign main views music player controls to a view model
/// </summary>
/// <param name="vm">BaseViewModel to assign controls to</param>
private void AssignPlayerControls(BaseViewModel vm)
private void AssignPlayerControls(BasePlayerViewModel vm)
{
_playerComponent.PlayButtonCommand = new Command(vm.OnPlayButtonCommandExecute, vm.CanPlayButtonCommandExecute);
_playerComponent.NextButtonCommand = new Command(vm.OnNextButtonExecute, vm.CanNextButtonCommandExecute);
@ -259,5 +275,53 @@ namespace Aurora.Design.Views.Main
Modal.IsVisible = isVisible;
Content.IsVisible = !isVisible;
}
private void SetView(Type viewType, Type viewModelType)
{
BaseViewModel vm = new BaseViewModel();
if(this._viewModels.ContainsKey(viewModelType.Name))
{
this._viewModels.TryGetValue(viewModelType.Name, out vm);
}
else
{
if (viewModelType.IsAssignableFrom(typeof(BaseViewModel)))
{
throw new InvalidOperationException("TargetViewModel field must be of type BaseViewModel");
}
//Instantiate new view model
vm = (BaseViewModel)App.Container.Resolve(viewModelType); //Activator.CreateInstance(item.TargetViewModelType);
_viewModels.Add(viewModelType.Name, vm);
}
//Assign player controls to viewmodel
if(vm is BasePlayerViewModel)
{
AssignPlayerControls(vm as BasePlayerViewModel);
}
ChangeModalVisiblity(false);
//Activate viewmodel
vm.OnActive();
//Deactivate last viewModel
_lastViewModel.OnInactive();
//Unasign deactivating vm
if(_lastViewModel is BasePlayerViewModel)
{
UnassignPlayerControls(_lastViewModel as BasePlayerViewModel);
}
var view = (View)Activator.CreateInstance(viewType);
vm.SetView = this.SetView;
//Assign viewModel
_lastViewModel = vm;
view.BindingContext = vm;
_viewContent.Content = view;
}
}
}

View File

@ -56,13 +56,13 @@ namespace Aurora.Design.Views.MainView
{
_pages = new ObservableCollection<NavigationItem>(new[]
{
new NavigationItem { Id = 0, Title = "Songs", Group="Your Music", TargetType = typeof(SongsView), TargetViewModelType = typeof(SongsViewModel) },
new NavigationItem { Id = 1, Title = "Artists", Group="Your Music", TargetType = typeof(ArtistsView), TargetViewModelType = typeof(ArtistsViewModel)},
new NavigationItem { Id = 2, Title = "Albums", Group="Your Music", TargetType = typeof(AlbumsView), TargetViewModelType = typeof(AlbumsViewModel)},
new NavigationItem { Id = 3, Title = "Stations", Group="Your Music", TargetType = typeof(StationsView), TargetViewModelType = typeof(StationsViewModel)},
new NavigationItem { Id = 4, Title = "Party", Group="Social", TargetType = typeof(PartyView), TargetViewModelType = typeof(PartyViewModel)},
new NavigationItem { Id = 5, Title = "Profile", Group="Social", TargetType = typeof(ProfileView), TargetViewModelType = typeof(ProfileViewModel)},
new NavigationItem { Id = 6, Title = "A + B", Group="Playlists", TargetType = typeof(StationsView), TargetViewModelType = typeof(StationsViewModel)}
new NavigationItem { Title = "Songs", Group="Your Music", TargetType = typeof(SongsView), TargetViewModelType = typeof(SongsViewModel) },
new NavigationItem { Title = "Artists", Group="Your Music", TargetType = typeof(ArtistsView), TargetViewModelType = typeof(ArtistsViewModel)},
new NavigationItem { Title = "Albums", Group="Your Music", TargetType = typeof(AlbumsView), TargetViewModelType = typeof(AlbumsViewModel)},
new NavigationItem { Title = "Stations", Group="Your Music", TargetType = typeof(StationsView), TargetViewModelType = typeof(StationsViewModel)},
new NavigationItem { Title = "Party", Group="Social", TargetType = typeof(PartyView), TargetViewModelType = typeof(PartyViewModel)},
new NavigationItem { Title = "Profile", Group="Social", TargetType = typeof(ProfileView), TargetViewModelType = typeof(ProfileViewModel)},
new NavigationItem { Title = "A + B", Group="Playlists", TargetType = typeof(StationsView), TargetViewModelType = typeof(StationsViewModel)}
});
}

View File

@ -0,0 +1,65 @@
using System.Collections.ObjectModel;
using Aurora.Proto.Party;
using Aurora.Models.Media;
using Aurora.Services.Settings;
using Aurora.Services;
namespace Aurora.Design.Views.Party
{
public class BasePartyViewModel : BasePlayerViewModel
{
protected string _hostname;
protected Global _global;
protected ISettingsService _settingsService;
private ObservableCollection<Member> _members;
private ObservableCollection<BaseMedia> _queue;
private BaseMedia _selectedMedia;
private int _selectedTabIndex;
public BasePartyViewModel(
ISettingsService settingsService,
Global global
)
{
_members = new ObservableCollection<Member>();
_queue = new ObservableCollection<BaseMedia>();
this._settingsService = settingsService;
this._global = global;
}
public int SelectedTabIndex
{
get { return _selectedTabIndex; }
set { SetProperty(ref _selectedTabIndex, value); }
}
/// <summary>
/// Publc property for the members list
/// </summary>
/// <value></value>
public ObservableCollection<Member> Members
{
get
{
return _members;
}
set
{
SetProperty(ref _members, value);
}
}
/// <summary>
/// Public property for the currently selected song.
/// </summary>
/// <value></value>
public BaseMedia SelectedMedia
{
get { return _selectedMedia; }
set { SetProperty(ref _selectedMedia, value); }
}
}
}

View File

@ -0,0 +1,225 @@
using System;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using Grpc.Core;
using Xamarin.Forms;
using Aurora.Services.EventManager;
using Aurora.Services.Library;
using Aurora.Services.Settings;
using Aurora.Services;
using Aurora.Proto.Party;
using Aurora.Models.Media;
using Aurora.Services.Controllers;
using Aurora.Utils;
namespace Aurora.Design.Views.Party
{
public class HostPartyViewModel : BasePartyViewModel
{
private IEventManager _eventManager;
private ILibraryService _libraryService;
private Grpc.Core.Server _server;
private RemotePartyController _remotePartyController;
private Channel _channel;
private int _port = 8080;
private bool _isServerStarted = false;
public HostPartyViewModel(
ISettingsService settingsService,
IEventManager eventManager,
ILibraryService libraryService,
Global global
): base(settingsService, global)
{
this._eventManager = eventManager;
this._global.State = PartyState.Hosting;
this._libraryService = libraryService;
// Create and start grpc server
string host = IpUtil.GetLocalIPAddress();
if(string.IsNullOrWhiteSpace(host))
{
// TODO display error to screen
throw new System.Exception("This device does not have a valid IP address");
}
this._hostname = IpUtil.GetLocalIPAddress();
this.StartServer("test", "test description");
// TODO assign members
// TODO assign songList
// Register commands
PlayCommand = new Command(OnDoubleClickCommandExecute, CanDoubleClickCommandExecute);
LeavePartyCommand = new Command(OnLeavePartyCommandExecute, CanLeavePartyCommandExecute);
}
#region Properties
/// <summary>
/// Public property for playing media command
/// </summary>
/// <value></value>
public Command PlayCommand { get; private set; }
/// <summary>
/// Public property for leave party command
/// </summary>
/// <value></value>
public Command LeavePartyCommand { get; private set; }
#endregion Properties
/// <summary>
/// Called by framework when view becomes active
/// </summary>
/// <returns></returns>
public override Task OnActive()
{
// Start server if not already started
if(!this._isServerStarted)
{
this._hostname = IpUtil.GetLocalIPAddress();
this.StartServer("test", "test description");
}
return Task.FromResult<object>(null);
}
/// <summary>
/// Called by framework when view becomes inactive
/// </summary>
/// <returns></returns>
public override Task OnInactive()
{
return Task.FromResult<object>(null);
}
private async void OnLeavePartyCommandExecute()
{
await this._channel.ShutdownAsync();
await this._server.ShutdownAsync();
this._global.State = PartyState.Idle;
this.SetView(typeof(PartyView), typeof(PartyViewModel));
}
private bool CanLeavePartyCommandExecute()
{
return true;
}
public override void OnPlayButtonCommandExecute()
{
if (base.IsPlaying())
{
//Fire play stopped event
AudioMetadata meta = SelectedMedia.Metadata as AudioMetadata;
MediaPausedEvent mediaPaused = new MediaPausedEvent();
_eventManager.FireEvent(new BaseEvent()
{
MediaPausedEvent = mediaPaused
});
}
else
{
//Fire play resume event
AudioMetadata meta = SelectedMedia.Metadata as AudioMetadata;
MediaResumedEvent mediaResumed = new MediaResumedEvent();
_eventManager.FireEvent(new BaseEvent()
{
MediaResumedEvent = mediaResumed
});
}
}
public override bool CanPlayButtonCommandExecute()
{
return true;
}
public override bool CanNextButtonCommandExecute()
{
return true;
}
public override bool CanPreviousButtonCommandExecute()
{
return true;
}
/// <summary>
/// On double click execute, fire media playing event
/// </summary>
public void OnDoubleClickCommandExecute()
{
//Fire Playing event
AudioMetadata meta = SelectedMedia.Metadata as AudioMetadata;
NewMediaPlayingEvent mediaPlaying = new NewMediaPlayingEvent()
{
Media = new Media()
{
//TODO need full resource name
Name = SelectedMedia.Id,
Title = meta.Title,
Artist = meta.Artist,
Album = meta.Album,
}
};
_eventManager.FireEvent(new BaseEvent()
{
NewMediaPlayingEvent = mediaPlaying
});
}
public bool CanDoubleClickCommandExecute()
{
return true;
}
#region Private Methods
private void StartServer(string partyName, string description)
{
try
{
// TODO bmw replace with display in UI
Console.WriteLine(string.Format("Starting gRPC server at hostname: {0}, port: {1}", _hostname, _port));
_server = new Grpc.Core.Server
{
Ports = { new ServerPort(_hostname, _port, ServerCredentials.Insecure) }
};
//Construct implementations
this._remotePartyController = new RemotePartyController(
partyName,
description,
this._libraryService,
base._settingsService,
_eventManager);
// Register grpc RemoteService with singleton server service
RemotePartyService.BindService(this._remotePartyController);
_server.Start();
this._isServerStarted = true;
}
catch (Exception ex)
{
// TODO bmw replace with display in UI
Console.WriteLine(string.Format("Error starting gRPC server: {0}", ex.Message));
}
}
#endregion Private Methods
}
}

Some files were not shown because too many files have changed in this diff Show More