* IconConstants.cs: Relocate

* QWorkspaceInfo.cs:
* QParser.cs:
* WorkspacePrompt.cs:
* QEventArgs.cs: Remove redundant QWorkspaceInfo class

* QRecentWorkspaceInfo.cs: Add bundle info class for recent workspace
  info

* ControlPage.xaml.cs:
* QBrowserPage.xaml.cs:
* QButton.cs:
* qConnectionPage.xaml.cs:
* QLevelsCell.cs:
* QCueListCell.cs:
* QInstanceCell.cs:
* QControlsBlock.cs:
* ShadowButton.cs:
* QLevelsButton.cs:
* QSelectedCueCell.cs: Move Cells classess under UI

* AppDelegate.cs:
* packages.config:
* MainActivity.cs:
* packages.config:
* qController.iOS.csproj:
* qController.Droid.csproj:
* qController.csproj: Update dependencies

* App.xaml.cs: Add AppAction code for recent workspace

* QStorage.cs: Add recent workspace to persistent storage

* QCueGrid.cs: Start on interactivity with cue list grid

* QCueViewModel.cs: debug for children updated start on reactive
  QCueGrid

* WorkspacePage.xaml.cs: Start handling a "no selected cue" state and
  recent workspace

* QInstance.cs:
* IPHelper.cs: move reachable check to IPHelper - TODO: implement
This commit is contained in:
2020-11-29 14:22:01 -06:00
parent cb19056088
commit 31a10d9b16
26 changed files with 5670 additions and 120 deletions
+49
View File
@@ -4,6 +4,8 @@ using Xamarin.Essentials;
using qController.Communication;
using qController.Pages;
using System;
using QControlKit;
using Serilog;
namespace qController
{
@@ -34,6 +36,8 @@ namespace qController
public App()
{
InitializeComponent();
AppActions.OnAppAction += AppActions_OnAppAction;
/*MainPage = new NavigationPage(new QConnectionPage());
iNav = MainPage.Navigation;*/
@@ -65,9 +69,32 @@ namespace qController
rootPage.Master = menuPage;
rootPage.Detail = NavigationPage;
MainPage = rootPage;
Log.Debug("Finished rootPage Setup in App Constructor");
rootPage.Init();
}
private void AppActions_OnAppAction(object sender, AppActionEventArgs e)
{
// Don't handle events fired for old application instances
// and cleanup the old instance's event handler
if (Current != this && Current is App app)
{
AppActions.OnAppAction -= app.AppActions_OnAppAction;
return;
}
Device.BeginInvokeOnMainThread(async () =>
{
QRecentWorkspaceInfo packagedInfo = QStorage.recentWorkspaceInfo;
if (packagedInfo != null && e.AppAction.Id.Equals("recent_workspace"))
{
await NavigationPage.PopToRootAsync();
QWorkspace recentWorkspace = new QWorkspace(packagedInfo.workspaceInfo, new QServer(packagedInfo.serverInfo.host, packagedInfo.serverInfo.port));
await NavigationPage.PushAsync(new WorkspacePage(recentWorkspace));
Log.Debug("Pushed WorkspacePage from App Action");
}
});
}
private void Current_RequestedThemeChanged(object sender, AppThemeChangedEventArgs e)
{
SetAppResources();
@@ -139,6 +166,28 @@ namespace qController
}
public static void SetRecentWorkspace(QWorkspace workspace)
{
try
{
QRecentWorkspaceInfo recentWorkspaceInfo = new QRecentWorkspaceInfo {
workspaceInfo = workspace.getWorkspaceInfo(),
serverInfo = workspace.GetServerInfo()
};
QStorage.UpdateRecentWorkspace(recentWorkspaceInfo);
Device.BeginInvokeOnMainThread(async () =>
{
await AppActions.SetAsync(new AppAction("recent_workspace", workspace.name, workspace.version));
});
}
catch (Exception ex)
{
Log.Debug(ex.ToString());
}
}
public static void showToast(string message)
{
var toastConfig = new ToastConfig(message);
@@ -49,7 +49,7 @@ namespace qController.Communication
public class WorkspaceInfoArgs : EventArgs
{
public List<QWorkspaceInfo> WorkspaceInfo
public List<QControlKit.QWorkspaceInfo> WorkspaceInfo
{
get;
set;
@@ -15,5 +15,21 @@ namespace qController.Communication
IPAddress address;
return IPAddress.TryParse(ipString, out address);
}
public static bool IsReachable(string ipString)
{
/*try
{
IPAddress instanceIP = IPAddress.Parse(address);
Ping p = new System.Net.NetworkInformation.Ping();
PingReply reply = p.Send(instanceIP);
return reply.Status == IPStatus.Success ? true : false;
}
catch
{
return false;
}*/
return true;
}
}
}
@@ -94,7 +94,7 @@ namespace qController.Communication
public void ParseQInfo(OscMessage msg)
{
JToken qInfo = OSC2JSON(msg);
List<QWorkspaceInfo> qWorkspaceInfo = JsonConvert.DeserializeObject<List<QWorkspaceInfo>>(qInfo.ToString());
List<QControlKit.QWorkspaceInfo> qWorkspaceInfo = JsonConvert.DeserializeObject<List<QControlKit.QWorkspaceInfo>>(qInfo.ToString());
OnWorkspaceInfoReceived(qWorkspaceInfo);
}
@@ -171,7 +171,7 @@ namespace qController.Communication
{
WorkspaceDisconnect?.Invoke(this, new EventArgs());
}
protected virtual void OnWorkspaceInfoReceived(List<QWorkspaceInfo> workspaces)
protected virtual void OnWorkspaceInfoReceived(List<QControlKit.QWorkspaceInfo> workspaces)
{
WorkspaceInfoReceived?.Invoke(this, new WorkspaceInfoArgs() { WorkspaceInfo = workspaces });
}
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using Acr.UserDialogs;
using qController.QItems;
using Serilog;
using QControlKit;
namespace qController.Dialogs
{
File diff suppressed because it is too large Load Diff
@@ -17,18 +17,7 @@ namespace qController.QItems
}
public bool IsReachable()
{
/*try
{
IPAddress instanceIP = IPAddress.Parse(address);
Ping p = new System.Net.NetworkInformation.Ping();
PingReply reply = p.Send(instanceIP);
return reply.Status == IPStatus.Success ? true : false;
}
catch
{
return false;
}*/
return true;
return qController.Communication.IPHelper.IsReachable(address);
}
}
}
@@ -1,11 +0,0 @@
//General information about a QLab Workspace
namespace qController.QItems
{
public class QWorkspaceInfo
{
public string version { get; set; }
public string displayName { get; set; }
public string uniqueID { get; set; }
public bool hasPasscode { get; set; }
}
}
@@ -0,0 +1,10 @@
using QControlKit;
namespace qController
{
public class QRecentWorkspaceInfo
{
public QWorkspaceInfo workspaceInfo { get; set; }
public QServerInfo serverInfo { get; set; }
}
}
@@ -2,24 +2,33 @@
using System.Collections.ObjectModel;
using Acr.Settings;
using qController.QItems;
using QControlKit;
namespace qController
{
public class QStorage
{
public static ObservableCollection<QInstance> qInstances;
public static QRecentWorkspaceInfo recentWorkspaceInfo;
static QStorage(){
qInstances = (ObservableCollection<QInstance>)CrossSettings.Current.GetValue(new ObservableCollection<QInstance>().GetType(), "qInstances", null);
recentWorkspaceInfo = (QRecentWorkspaceInfo)CrossSettings.Current.GetValue(typeof (QRecentWorkspaceInfo),"recentWorkspaceInfo",null);
if (qInstances == null)
{
Log.Debug("QSTORAGE - No qInstance exists creating one");
qInstances = new ObservableCollection<QInstance>();
updateStorage();
UpdateStorage();
}
}
public static void UpdateRecentWorkspace(QRecentWorkspaceInfo packagedInfo)
{
recentWorkspaceInfo = packagedInfo;
CrossSettings.Current.SetValue("recentWorkspaceInfo", recentWorkspaceInfo);
}
public static bool AddInstance(string name, string address){
return AddInstance(new QInstance(name, address));
}
@@ -27,7 +36,7 @@ namespace qController
public static bool AddInstance(QInstance q){
if(!Contains(q.name, q.address)){
qInstances.Add(q);
updateStorage();
UpdateStorage();
return true;
}else{
return false;
@@ -45,10 +54,10 @@ namespace qController
}
public static void RemoveInstance(QInstance q){
qInstances.Remove(q);
updateStorage();
UpdateStorage();
}
private static void updateStorage(){
private static void UpdateStorage(){
CrossSettings.Current.SetValue("qInstances", qInstances);
}
@@ -0,0 +1,45 @@
using Xamarin.Forms;
namespace qController.UI.Buttons
{
public class QButton : Button
{
public QCommand qCommand
{
get;
set;
}
public QButton(QCommand command)
{
qCommand = command;
Text = qCommand.text;
TextColor = Color.Black;
FontSize = App.HeightUnit * 2.5;
if (qCommand.osc.Contains("go"))
{
BackgroundColor = Color.SeaGreen;
switch (Device.RuntimePlatform)
{
case Device.iOS:
FontSize = App.HeightUnit * 4;
FontAttributes = FontAttributes.Bold;
break;
case Device.Android:
FontSize = App.HeightUnit * 4;
FontAttributes = FontAttributes.Bold;
break;
}
}
else if (qCommand.osc.Contains("panic"))
{
BackgroundColor = Color.IndianRed;
}
else
{
BackgroundColor = Color.FromHex("D8D8D8");
}
}
}
}
@@ -0,0 +1,29 @@
using Xamarin.Forms;
namespace qController.UI.Buttons
{
public class QLevelsButton : ShadowButton
{
public Button button;
public QLevelsButton()
{
button = new Button
{
Text = QIcon.SLIDERS,
TextColor = Color.Black,
FontFamily = App.QFont,
FontSize = App.HeightUnit * 4,
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
HeightRequest = App.HeightUnit * 8,
WidthRequest = App.HeightUnit * 8,
CornerRadius = (int)(App.HeightUnit * 4),
BackgroundColor = Color.LightBlue
};
HeightRequest = button.Height;
WidthRequest = button.Width;
CornerRadius = button.CornerRadius;
Content = button;
}
}
}
@@ -0,0 +1,28 @@
using Xamarin.Forms;
namespace qController.UI.Buttons
{
public class ShadowButton : Frame
{
public ShadowButton(Button b)
{
//Content = b;
HeightRequest = b.Height;
WidthRequest = b.Width;
CornerRadius = b.CornerRadius;
Padding = new Thickness(0);
Content = b;
HasShadow = true;
BorderColor = Color.Black;
IsVisible = false;
}
public ShadowButton()
{
HasShadow = true;
BorderColor = Color.Black;
IsVisible = false;
Padding = new Thickness(0);
}
}
}
+12 -7
View File
@@ -42,7 +42,7 @@ namespace qController.UI
};
cueLabel.SetBinding(Label.TextProperty, "name", BindingMode.OneWay);
cueLabel.SetDynamicResource(Label.TextColorProperty, "PrimaryTextColor");
cueLabel.SetBinding(Label.PaddingProperty, "nestPadding", BindingMode.OneWay);
cueLabel.SetBinding(Label.MarginProperty, "nestPadding", BindingMode.OneWay);
var cueTypeLabel = new Label
{
@@ -70,13 +70,18 @@ namespace qController.UI
cueTypeLabel.SetBinding(Label.TextProperty, "type", BindingMode.OneWay);
cueTypeLabel.SetDynamicResource(Label.TextColorProperty, "IconTextColor");
//Section for selecting a cue by tapping the name Label
var selectCueGesture = new TapGestureRecognizer();
selectCueGesture.Tapped += (sender, e) =>
if (!cue.IsCueList)
{
cue.workspace.firstCueList.playbackPositionID = cue.uid;
};
cueLabel.GestureRecognizers.Add(selectCueGesture);
//Section for selecting a cue by tapping the name Label
var selectCueGesture = new TapGestureRecognizer();
selectCueGesture.Tapped += (sender, e) =>
{
//TODO: need to make this work regardless of the cue list a cue is in
cue.workspace.firstCueList.playbackPositionID = cue.uid;
};
cueLabel.GestureRecognizers.Add(selectCueGesture);
}
//Add collapse gesture to the cue type icon if it is a group cue
if (cue.IsGroup)
@@ -44,6 +44,10 @@ namespace qController.ViewModels
{
OnPropertyChanged("color");
}
else if (property.Equals(QOSCKey.Children))
{
Log.Debug("children property updated");
}
}
}
@@ -6,7 +6,8 @@ using Acr.UserDialogs;
using qController.Dialogs;
using qController.QItems;
using qController.Cell;
using qController.UI.Cells;
using qController.UI.Buttons;
using qController.Communication;
using qController.Events;
@@ -109,7 +110,7 @@ namespace qController
}
}
private void PromptForWorkspace(List<QWorkspaceInfo> workspaces)
private void PromptForWorkspace(List<QControlKit.QWorkspaceInfo> workspaces)
{
UserDialogs.Instance.ActionSheet(workspacePrompt.getActionSheetConfigForWorkspaces(workspaces));
@@ -1,7 +1,7 @@
using QControlKit;
using Xamarin.Forms;
using Serilog;
using qController.Cell;
using qController.UI.Cells;
using Acr.UserDialogs;
using qController.Dialogs;
using Xamarin.Essentials;
@@ -11,12 +11,14 @@ using qController.UI;
using QControlKit;
using QControlKit.Events;
using QControlKit.Constants;
using System;
namespace qController
{
public partial class WorkspacePage : ContentPage
{
private QWorkspace connectedWorkspace;
private string passcode;
private Dictionary<string, Grid> cueGridDict = new Dictionary<string, Grid>();
public WorkspacePage(QWorkspace workspace, string passcode = null)
{
@@ -24,7 +26,7 @@ namespace qController
connectedWorkspace = workspace;
this.passcode = passcode;
SetupTopBar();
connectedWorkspace.WorkspaceUpdated += Workspace_WorkspaceUpdated;
@@ -32,10 +34,17 @@ namespace qController
connectedWorkspace.defaultSendUpdatesOSC = true;
connectedWorkspace.CueListChangedPlaybackPosition += ConnectedWorkspace_CueListChangedPlaybackPosition;
connectedWorkspace.connect(passcode);
}
protected override void OnAppearing()
{
base.OnAppearing();
connectedWorkspace.connect(passcode);
}
private void SetupTopBar()
{
NavigationPage.SetHasNavigationBar(this, false);
@@ -79,12 +88,15 @@ namespace qController
Device.BeginInvokeOnMainThread(() =>
{
QCue selectedCue = connectedWorkspace.cueWithID(args.cueID);
connectedWorkspace.fetchDefaultPropertiesForCue(selectedCue);
selectedCueFrame.BindingContext = new QCueViewModel(selectedCue, false);
if (cueGridDict.ContainsKey(args.cueID))
if(selectedCue != null)
{
var cueGrid = cueGridDict[args.cueID]; //element to scroll to
cueListScrollView.ScrollToAsync(cueGrid, ScrollToPosition.Center, true);
connectedWorkspace.fetchDefaultPropertiesForCue(selectedCue);
selectedCueFrame.BindingContext = new QCueViewModel(selectedCue, false);
if (cueGridDict.ContainsKey(args.cueID))
{
var cueGrid = cueGridDict[args.cueID]; //element to scroll to
cueListScrollView.ScrollToAsync(cueGrid, ScrollToPosition.Center, true);
}
}
});
}
@@ -92,31 +104,34 @@ namespace qController
void Workspace_WorkspaceUpdated(object source, QWorkspaceUpdatedArgs args)
{
Log.Debug("[workspacepage] Workspace Updated");
foreach (var cue in connectedWorkspace.cueLists)
App.SetRecentWorkspace(connectedWorkspace);
if (connectedWorkspace.cueLists.Count > 0)
{
if (cue.cues.Count > 0)
List<Task> cueAddTasks = new List<Task>();
foreach (var aCue in connectedWorkspace.cueLists)
{
List<Task> cueAddTasks = new List<Task>();
foreach (var aCue in cue.cues)
if(aCue.cues.Count > 0)
{
QCueGrid cueGrid = cueToGrid(aCue);
cueGridDict.Add(aCue.uid, cueGrid);
MainThread.InvokeOnMainThreadAsync(() =>
{
cueListsGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
cueListsGrid.Children.Add(cueGrid, 0, aCue.sortIndex);
}).Wait();
}
connectedWorkspace.valueForKey(cue, QOSCKey.PlaybackPositionId); //fetch playback position for cueList once all cue loading is done.
Device.BeginInvokeOnMainThread(() =>
{
selectedCueFrame.IsVisible = true;
cueListScrollView.IsVisible = true;
});
break; //only load first cue list with cues.
}
connectedWorkspace.valueForKey(connectedWorkspace.firstCueList, QOSCKey.PlaybackPositionId); //fetch playback position for cueList once all cue loading is done.
Device.BeginInvokeOnMainThread(() =>
{
selectedCueFrame.IsVisible = true;
cueListScrollView.IsVisible = true;
});
//break; //only load first cue list with cues.
}
}
@@ -172,5 +187,12 @@ namespace qController
Back();
}
}
protected async Task WaitAndExecute(int milisec, Action actionToExecute)
{
await Task.Delay(milisec);
actionToExecute();
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
using Serilog;
using qController.QItems;
using qController.Dialogs;
using qController.Cell;
using qController.UI.Cells;
using Xamarin.Essentials;
namespace qController
+7 -6
View File
@@ -57,14 +57,14 @@
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Acr.UserDialogs" Version="7.1.0.462" />
<PackageReference Include="Acr.UserDialogs" Version="7.1.0.468" />
<PackageReference Include="Zeroconf" Version="3.4.2" />
<PackageReference Include="Xamarin.Forms" Version="4.8.0.1560" />
<PackageReference Include="Xamarin.Forms" Version="4.8.0.1687" />
<PackageReference Include="Acr.Settings" Version="9.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Xamarin.Essentials" Version="1.5.3.2" />
<PackageReference Include="Xamarin.Essentials" Version="1.6.0-pre4" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="QControlKit" Version="0.0.5" />
<PackageReference Include="QControlKit" Version="0.0.10" />
</ItemGroup>
<ItemGroup>
<None Remove="App.xaml" />
@@ -87,12 +87,13 @@
</ItemGroup>-->
<ItemGroup>
<Folder Include="Classes\QItems\" />
<Folder Include="Classes\Cell\" />
<Folder Include="Classes\Settings\" />
<Folder Include="Classes\Dialogs\" />
<Folder Include="Classes\Buttons\" />
<Folder Include="Classes\UI\" />
<Folder Include="Classes\Events\" />
<Folder Include="Classes\UI\Buttons\" />
<Folder Include="Classes\UI\Cells\" />
<Folder Include="Classes\Helpers\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Pages\RootPageMaster.xaml" />