diff --git a/.bashrc b/.bashrc
deleted file mode 100644
index 272a82c..0000000
--- a/.bashrc
+++ /dev/null
@@ -1,3 +0,0 @@
-if [ -t 1 ]; then
-exec zsh
-fi
diff --git a/GeoClient.sln b/GeoClient.sln
index 0ccd45f..4d87a9e 100644
--- a/GeoClient.sln
+++ b/GeoClient.sln
@@ -9,7 +9,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeoClient.iOS", "GeoClient\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeoClient.UWP", "GeoClient\GeoClient.UWP\GeoClient.UWP.csproj", "{FC79F765-A59D-4681-8326-82124E1BA340}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeoClient", "GeoClient\GeoClient\GeoClient.csproj", "{FA8B858C-9662-4B4F-86B6-0F6515B5480D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GeoClient", "GeoClient\GeoClient\GeoClient.csproj", "{FA8B858C-9662-4B4F-86B6-0F6515B5480D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeoClientTests", "GeoClientTests\GeoClientTests.csproj", "{B19C892E-2628-4CA7-AD27-08D406A3B14B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -171,6 +173,30 @@ Global
{FA8B858C-9662-4B4F-86B6-0F6515B5480D}.Release|x86.ActiveCfg = Release|Any CPU
{FA8B858C-9662-4B4F-86B6-0F6515B5480D}.Release|x86.Build.0 = Release|Any CPU
{FA8B858C-9662-4B4F-86B6-0F6515B5480D}.Release|x86.Deploy.0 = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|ARM.Build.0 = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|x64.Build.0 = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Debug|x86.Build.0 = Debug|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|ARM.ActiveCfg = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|ARM.Build.0 = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|iPhone.Build.0 = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|x64.ActiveCfg = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|x64.Build.0 = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|x86.ActiveCfg = Release|Any CPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/GeoClient/GeoClient.Android/GeoClient.Android.csproj b/GeoClient/GeoClient.Android/GeoClient.Android.csproj
index b7748bd..19c7786 100644
--- a/GeoClient/GeoClient.Android/GeoClient.Android.csproj
+++ b/GeoClient/GeoClient.Android/GeoClient.Android.csproj
@@ -31,7 +31,7 @@
None
- true
+ false
pdbonly
true
bin\Release
diff --git a/GeoClient/GeoClient.Android/Properties/AndroidManifest.xml b/GeoClient/GeoClient.Android/Properties/AndroidManifest.xml
index 786291d..79bdeda 100644
--- a/GeoClient/GeoClient.Android/Properties/AndroidManifest.xml
+++ b/GeoClient/GeoClient.Android/Properties/AndroidManifest.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/GeoClient/GeoClient.iOS/Info.plist b/GeoClient/GeoClient.iOS/Info.plist
index 6e25cc2..50a8863 100644
--- a/GeoClient/GeoClient.iOS/Info.plist
+++ b/GeoClient/GeoClient.iOS/Info.plist
@@ -28,7 +28,7 @@
CFBundleIdentifier
com.companyname.GeoClient
CFBundleVersion
- 1.1.0
+ 1.1.1
UILaunchStoryboardName
LaunchScreen
CFBundleName
diff --git a/GeoClient/GeoClient/GeoClient.csproj b/GeoClient/GeoClient/GeoClient.csproj
index 32bd58b..a9ffb08 100644
--- a/GeoClient/GeoClient/GeoClient.csproj
+++ b/GeoClient/GeoClient/GeoClient.csproj
@@ -6,8 +6,8 @@
Daniel Steiner, Robert Wittek
MIT License
App to share the position data
- 1.1.0
- 1.1.0.0
+ 1.1.1
+ 1.1.1.0
diff --git a/GeoClient/GeoClient/Models/GeoPoint.cs b/GeoClient/GeoClient/Models/GeoPoint.cs
index d010292..98f385b 100644
--- a/GeoClient/GeoClient/Models/GeoPoint.cs
+++ b/GeoClient/GeoClient/Models/GeoPoint.cs
@@ -1,18 +1,40 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace GeoClient.Models
+namespace GeoClient.Models
{
public class GeoPoint
{
- public string Latitude { get; }
- public string Longitude { get; }
+ public double Latitude { get; }
+ public double Longitude { get; }
- public GeoPoint(string latitude, string longitude)
+ public GeoPoint(double latitude, double longitude)
{
Latitude = latitude;
Longitude = longitude;
}
+
+ protected bool Equals(GeoPoint other)
+ {
+ return Latitude.Equals(other.Latitude) && Longitude.Equals(other.Longitude);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((GeoPoint) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return (Latitude.GetHashCode() * 397) ^ Longitude.GetHashCode();
+ }
+ }
+
+ public override string ToString()
+ {
+ return $"{nameof(Latitude)}: {Latitude}, {nameof(Longitude)}: {Longitude}";
+ }
}
}
diff --git a/GeoClient/GeoClient/Models/IncidentItem.cs b/GeoClient/GeoClient/Models/IncidentItem.cs
index 0327f2b..44f5b0b 100644
--- a/GeoClient/GeoClient/Models/IncidentItem.cs
+++ b/GeoClient/GeoClient/Models/IncidentItem.cs
@@ -2,32 +2,98 @@
using GeoClient.Services.Utils;
using System;
using System.Collections.Generic;
+using System.Linq;
+using GeoClient.Views.Utils;
using Xamarin.Forms;
namespace GeoClient.Models
{
- public class IncidentItem
+ public class IncidentItem : IComparable
{
public string Id { get; }
- public GeoIncidentType? Type { get; set; }
- public string Info { get; set; }
- public bool Priority { get; set; }
- public bool Blue { get; set; }
- public GeoPoint Location { get; set; }
+ public GeoIncidentType? Type { get; }
+ public string Info { get; }
+ public bool Priority { get; }
+ public bool Blue { get; }
+ public GeoPoint Location { get; }
- public SortedSet Units { get; set; }
+ public List Units { get; }
public string DescriptiveType => GetDescriptiveType();
public Color BackgroundColor => GetBackgroundColor();
public Color ForegroundColor => GetForegroundColor();
public IncidentTaskState? OwnTaskState => GetOwnTaskState();
public string OwnTaskStateIcon => GetOwnTaskStateIcon();
+ public string OwnTaskStateDescription => OwnTaskState?.GetDescription();
+ public bool IsUnitAssignedToTask => GetIsUnitAssignedToTask();
RegistrationService _registrationService = RegistrationService.Instance;
- public IncidentItem(string id)
+ public IncidentItem(
+ string id,
+ GeoIncidentType type = GeoIncidentType.Unknown,
+ string info = "",
+ bool priority = false,
+ bool blue = false,
+ GeoPoint location = null,
+ List units = null)
{
Id = id;
+ Type = type;
+ Info = info;
+ Priority = priority;
+ Blue = blue;
+ Location = location;
+ Units = units ?? new List();
+ }
+
+ protected bool Equals(IncidentItem other)
+ {
+ return string.Equals(Id, other.Id)
+ && Type == other.Type
+ && string.Equals(Info, other.Info)
+ && Priority == other.Priority
+ && Blue == other.Blue
+ && Equals(Location, other.Location)
+ && ListEquals(Units, other.Units);
+ }
+
+ private bool ListEquals(List units, List otherUnits)
+ {
+ if (units == null)
+ return otherUnits == null;
+ if (otherUnits == null)
+ return false;
+ return units.SequenceEqual(otherUnits);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((IncidentItem) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = (Id != null ? Id.GetHashCode() : 0);
+ hashCode = (hashCode * 397) ^ Type.GetHashCode();
+ hashCode = (hashCode * 397) ^ (Info != null ? Info.GetHashCode() : 0);
+ hashCode = (hashCode * 397) ^ Priority.GetHashCode();
+ hashCode = (hashCode * 397) ^ Blue.GetHashCode();
+ hashCode = (hashCode * 397) ^ (Location != null ? Location.GetHashCode() : 0);
+ hashCode = (hashCode * 397) ^ (Units != null ? Units.GetHashCode() : 0);
+ return hashCode;
+ }
+ }
+
+ public override string ToString()
+ {
+ return
+ $"{nameof(Id)}: {Id}, {nameof(Type)}: {Type}, {nameof(Info)}: {Info}, {nameof(Priority)}: {Priority}, {nameof(Blue)}: {Blue}, {nameof(Location)}: {Location}, Units.Count: {Units.Count}";
}
private string GetDescriptiveType()
@@ -82,5 +148,33 @@ private string GetOwnTaskStateIcon()
{
return StatusIconResolver.GetIconForTaskState(OwnTaskState);
}
+
+ private bool GetIsUnitAssignedToTask()
+ {
+ return OwnTaskState != IncidentTaskState.Unknown;
+ }
+
+ public int CompareTo(IncidentItem other)
+ {
+ if (ReferenceEquals(this, other)) return 0;
+ if (ReferenceEquals(null, other)) return 1;
+
+ var assignmentComparison = IsUnitAssignedToTask.CompareTo(other.IsUnitAssignedToTask);
+ if (assignmentComparison != 0)
+ return -assignmentComparison;
+
+ var blueComparison = Blue.CompareTo(other.Blue);
+ if (blueComparison != 0)
+ return -blueComparison;
+
+ var priorityComparison = Priority.CompareTo(other.Priority);
+ if (priorityComparison != 0)
+ return -priorityComparison;
+
+ var typeComparison = Nullable.Compare(Type, other.Type);
+ if (typeComparison != 0) return typeComparison;
+
+ return string.Compare(Info, other.Info, StringComparison.OrdinalIgnoreCase);
+ }
}
}
\ No newline at end of file
diff --git a/GeoClient/GeoClient/Models/IncidentTaskState.cs b/GeoClient/GeoClient/Models/IncidentTaskState.cs
index 5f471e7..c4bb5c3 100644
--- a/GeoClient/GeoClient/Models/IncidentTaskState.cs
+++ b/GeoClient/GeoClient/Models/IncidentTaskState.cs
@@ -1,12 +1,25 @@
-namespace GeoClient.Models
+using System.ComponentModel;
+
+namespace GeoClient.Models
{
public enum IncidentTaskState
{
+ [Description("Alarmiert")]
Assigned,
+
+ [Description("Zum Berufungsort")]
Zbo,
+
+ [Description("Am Berufungsort")]
Abo,
+
+ [Description("Zum Abgabeort")]
Zao,
+
+ [Description("Am Abgabeort")]
Aao,
+
+ [Description("Unbekannt")]
Unknown
}
}
diff --git a/GeoClient/GeoClient/Models/Unit.cs b/GeoClient/GeoClient/Models/Unit.cs
new file mode 100644
index 0000000..93231e8
--- /dev/null
+++ b/GeoClient/GeoClient/Models/Unit.cs
@@ -0,0 +1,75 @@
+using GeoClient.Services.Utils;
+using System;
+
+namespace GeoClient.Models
+{
+ public class Unit : IComparable
+ {
+ public string Id { get; }
+ public string Name { get; }
+
+ // TODO: This is actually a position, not only a point. There is more detailed information available.
+ public GeoPoint LastPoint { get; }
+ public IncidentTaskState State { get; }
+
+ public string TaskStateIcon => GetTaskStateIcon();
+
+ public Unit(
+ string id,
+ string name,
+ GeoPoint lastPoint = null,
+ IncidentTaskState state = IncidentTaskState.Unknown)
+ {
+ Id = id;
+ Name = name;
+ LastPoint = lastPoint;
+ State = state;
+ }
+
+ public int CompareTo(Unit other)
+ {
+ if (ReferenceEquals(this, other)) return 0;
+ if (ReferenceEquals(null, other)) return 1;
+ return string.Compare(Name, other.Name, StringComparison.OrdinalIgnoreCase);
+ }
+
+ protected bool Equals(Unit other)
+ {
+ return string.Equals(Id, other.Id)
+ && string.Equals(Name, other.Name)
+ && Equals(LastPoint, other.LastPoint)
+ && State == other.State;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((Unit) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = (Id != null ? Id.GetHashCode() : 0);
+ hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0);
+ hashCode = (hashCode * 397) ^ (LastPoint != null ? LastPoint.GetHashCode() : 0);
+ hashCode = (hashCode * 397) ^ (int) State;
+ return hashCode;
+ }
+ }
+
+ public override string ToString()
+ {
+ return
+ $"{nameof(Id)}: {Id}, {nameof(Name)}: {Name}, {nameof(LastPoint)}: {LastPoint}, {nameof(State)}: {State}";
+ }
+
+ private string GetTaskStateIcon()
+ {
+ return StatusIconResolver.GetIconForTaskState(State);
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeoClient/GeoClient/Models/Units.cs b/GeoClient/GeoClient/Models/Units.cs
deleted file mode 100644
index e015d5d..0000000
--- a/GeoClient/GeoClient/Models/Units.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System;
-using GeoClient.Services.Utils;
-
-namespace GeoClient.Models
-{
- public class Unit : IComparable
- {
- public string Id { get; }
- public string Name { get; set; }
-
- // TODO: This is actually a position, not only a point. There is more detailed information available.
- public GeoPoint LastPoint { get; set; }
- public IncidentTaskState State { get; set; }
-
- public string TaskStateIcon => GetTaskStateIcon();
-
- public Unit(string id)
- {
- Id = id;
- }
-
- public int CompareTo(Unit other)
- {
- if (ReferenceEquals(this, other)) return 0;
- if (ReferenceEquals(null, other)) return 1;
- return string.Compare(Name, other.Name, StringComparison.OrdinalIgnoreCase);
- }
-
- private string GetTaskStateIcon()
- {
- return StatusIconResolver.GetIconForTaskState(State);
- }
- }
-}
\ No newline at end of file
diff --git a/GeoClient/GeoClient/Services/IncidentItemFactory.cs b/GeoClient/GeoClient/Services/IncidentItemFactory.cs
index e7525da..3940c57 100644
--- a/GeoClient/GeoClient/Services/IncidentItemFactory.cs
+++ b/GeoClient/GeoClient/Services/IncidentItemFactory.cs
@@ -1,12 +1,11 @@
using GeoClient.Models;
-using GeoClient.Services.Boundary;
using GeoClient.Services.Utils;
+using GeoClient.Views.Utils;
using Newtonsoft.Json.Linq;
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Threading.Tasks;
-using GeoClient.Views.Utils;
+using System.Globalization;
+using static System.Double;
namespace GeoClient.Services
{
@@ -25,61 +24,109 @@ public static List CreateIncidentItemList(List incidents,
incidentList.Add(incidentItem);
}
}
+ else
+ {
+ Console.WriteLine("Provided incidents are null. Cannot parse incident items.");
+ }
return incidentList;
}
private static IncidentItem CreateIncidentItem(JObject incident, List unitJsonObjects)
{
- IncidentItem incidentItem = new IncidentItem((string) incident["id"]);
- incidentItem.Info = (string) incident["info"];
- incidentItem.Type = GeoIncidentTypeFactory.GetTypeFromString((string) incident["type"]);
- incidentItem.Priority = bool.Parse((string) incident["priority"]);
- incidentItem.Blue = bool.Parse((string) incident["blue"]);
- incidentItem.Location = CreateGeoPoint(incident["location"]);
Dictionary rawAssignedUnits =
incident["assignedUnits"].ToObject>();
-
- incidentItem.Units = CreateUnitList(rawAssignedUnits, unitJsonObjects);
-
- return incidentItem;
+ var unitList = CreateUnitList(rawAssignedUnits, unitJsonObjects);
+
+ return new IncidentItem(
+ incident.Value("id"),
+ GeoIncidentTypeFactory.GetTypeFromString((string)incident["type"]),
+ incident.Value("info"),
+ incident.Value("priority"),
+ incident.Value("blue"),
+ CreateGeoPoint(incident["location"]),
+ unitList);
}
- private static GeoPoint CreateGeoPoint(JToken incidentPoint)
+ private static GeoPoint CreateGeoPoint(JToken pointJToken)
{
GeoPoint geoPoint = null;
- string latitude = (string)incidentPoint?["latitude"];
- string longitude = (string)incidentPoint?["longitude"];
+ string latitudeString = (string) pointJToken?["latitude"];
+ string longitudeString = (string) pointJToken?["longitude"];
+
+ if (GeoPointUtil.NotBlank(latitudeString) && GeoPointUtil.NotBlank(longitudeString))
+ {
+ geoPoint = CreateGeoPoint(latitudeString, longitudeString);
+ }
+
+ return geoPoint;
+ }
- if (GeoPointUtil.NotBlank(latitude) && GeoPointUtil.NotBlank(longitude))
+ private static GeoPoint CreateGeoPoint(string latitudeString, string longitudeString)
+ {
+ GeoPoint geoPoint = null;
+ try
{
+ double latitude = Parse(latitudeString, CultureInfo.InvariantCulture);
+ double longitude = Parse(longitudeString, CultureInfo.InvariantCulture);
geoPoint = new GeoPoint(latitude, longitude);
}
+ catch (ArgumentNullException)
+ {
+ Console.WriteLine("Cannot parse double of coordinate value 'null'.");
+ }
+ catch (FormatException)
+ {
+ Console.WriteLine(
+ "Provided coordinates does not contain valid numbers. lat=" + latitudeString +
+ ", lon=" + longitudeString);
+ }
+ catch (OverflowException)
+ {
+ Console.WriteLine("Coordinate values are out of range.");
+ }
return geoPoint;
}
- private static SortedSet CreateUnitList(IReadOnlyDictionary rawAssignedUnits, List unitJsonObjects)
+ private static List CreateUnitList(
+ IReadOnlyDictionary rawAssignedUnits,
+ List unitJsonObjects)
{
- var units = new SortedSet();
+ var units = new List();
if (unitJsonObjects != null)
{
- foreach (var unitJsonObject in unitJsonObjects)
+ foreach (var rawAssignedUnit in rawAssignedUnits)
{
- Unit unit = new Unit((string) unitJsonObject["id"]);
- unit.Name = (string) unitJsonObject["name"];
- unit.LastPoint = CreateGeoPoint(unitJsonObject["lastPoint"]);
-
- rawAssignedUnits.TryGetValue(unit.Id, out var rawUnitState);
- unit.State = IncidentTaskStateFactory.GetTaskStateFromString(rawUnitState);
-
+ var taskState = IncidentTaskStateFactory.GetTaskStateFromString(rawAssignedUnit.Value);
+ var unit = GetUnitFromUnitList(rawAssignedUnit.Key, unitJsonObjects, taskState);
units.Add(unit);
}
}
return units;
}
+
+ private static Unit GetUnitFromUnitList(string unitId, List unitJsonObjects, IncidentTaskState taskStateOfUnit)
+ {
+ foreach (var unitJsonObject in unitJsonObjects)
+ {
+ if ((string) unitJsonObject["id"] == unitId)
+ {
+ Unit unit = new Unit(unitId,
+ unitJsonObject.Value("name"),
+ CreateGeoPoint(unitJsonObject["lastPoint"]),
+ taskStateOfUnit
+ );
+
+ return unit;
+ }
+ }
+
+ Console.WriteLine($"Unit with ID {unitId} is not present in list of units!");
+ return null;
+ }
}
}
\ No newline at end of file
diff --git a/GeoClient/GeoClient/ViewModels/BaseViewModel.cs b/GeoClient/GeoClient/ViewModels/BaseViewModel.cs
index 356423e..bbd99e3 100644
--- a/GeoClient/GeoClient/ViewModels/BaseViewModel.cs
+++ b/GeoClient/GeoClient/ViewModels/BaseViewModel.cs
@@ -7,22 +7,18 @@ namespace GeoClient.ViewModels
{
public class BaseViewModel : INotifyPropertyChanged
{
- bool isBusy = false;
- public bool IsBusy
- {
- get => isBusy;
- set => SetProperty(ref isBusy, value);
- }
-
string _title = string.Empty;
+
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
- protected bool SetProperty(ref T backingStore, T value,
- [CallerMemberName]string propertyName = "",
+ protected bool SetProperty(
+ ref T backingStore,
+ T value,
+ [CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer.Default.Equals(backingStore, value))
@@ -35,13 +31,16 @@ protected bool SetProperty(ref T backingStore, T value,
}
#region INotifyPropertyChanged
+
public event PropertyChangedEventHandler PropertyChanged;
+
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
changed?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
+
#endregion
}
-}
+}
\ No newline at end of file
diff --git a/GeoClient/GeoClient/ViewModels/IncidentsViewModel.cs b/GeoClient/GeoClient/ViewModels/IncidentsViewModel.cs
index c6bd2cf..96d059d 100644
--- a/GeoClient/GeoClient/ViewModels/IncidentsViewModel.cs
+++ b/GeoClient/GeoClient/ViewModels/IncidentsViewModel.cs
@@ -8,11 +8,18 @@ namespace GeoClient.ViewModels
{
public class IncidentsViewModel : BaseViewModel
{
- public ObservableCollection Incidents { get; set; }
- public Command LoadItemsCommand { get; set; }
+ public ObservableCollection Incidents { get; }
+ public Command LoadItemsCommand { get; }
+ private bool _isBusy;
private string _emptyListMessage;
+ public bool IsBusy
+ {
+ get => _isBusy;
+ set => SetProperty(ref _isBusy, value);
+ }
+
public string EmptyListMessage
{
get => _emptyListMessage;
@@ -55,6 +62,8 @@ private void ExecuteLoadItemsCommand()
return;
}
+ IsBusy = true;
+
_restService.GetScope();
}
}
diff --git a/GeoClient/GeoClient/Views/AboutPage.xaml b/GeoClient/GeoClient/Views/AboutPage.xaml
index 1a7dc57..c3380bb 100644
--- a/GeoClient/GeoClient/Views/AboutPage.xaml
+++ b/GeoClient/GeoClient/Views/AboutPage.xaml
@@ -26,7 +26,7 @@
-
+
@@ -44,13 +44,13 @@
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
diff --git a/GeoClient/GeoClient/Views/AboutPage.xaml.cs b/GeoClient/GeoClient/Views/AboutPage.xaml.cs
index 1f36e3b..c6e6c80 100644
--- a/GeoClient/GeoClient/Views/AboutPage.xaml.cs
+++ b/GeoClient/GeoClient/Views/AboutPage.xaml.cs
@@ -89,35 +89,35 @@ private async Task UpdateRegistrationInformation(bool unregisteredOnPurpose)
private void ResetRegistrationInfo()
{
- registrationinfo.Text = "Dieser Client ist derzeit keiner Einheit zugeordnet. Bitte registrieren Sie das Gerät mit dem ausgehändigten Informatonsblatt.";
- registrationButton.Text = "Jetzt registrieren";
+ RegistrationInfo.Text = "Dieser Client ist derzeit keiner Einheit zugeordnet. Bitte registrieren Sie das Gerät mit dem ausgehändigten Informatonsblatt.";
+ RegistrationButton.Text = "Jetzt registrieren";
}
private void DisplayRegistrationInfo()
{
var registrationInfo = _registrationService.GetRegistrationInfo();
- registrationinfo.Text = "Dieses Gerät hat die ID " + registrationInfo.Id + " mit dem Token " + registrationInfo.Token;
- registrationButton.Text = "Erneut registrieren / Zu anderer Einheit zuordnen";
+ RegistrationInfo.Text = "Dieses Gerät hat die ID " + registrationInfo.Id + " mit dem Token " + registrationInfo.Token;
+ RegistrationButton.Text = "Erneut registrieren / Zu anderer Einheit zuordnen";
}
public void LocationUpdated(Location updatedLocation)
{
if (updatedLocation != null)
{
- lblLatitude.Text = "Latitude: " + updatedLocation.Latitude;
- lblLongitude.Text = "Longitude: " + updatedLocation.Longitude;
- lblSpeed.Text = "Speed: " + updatedLocation.Speed;
- lblAccuracy.Text = "Accuracy: " + updatedLocation.Accuracy;
- lblAltitude.Text = "Altitude: " + updatedLocation.Altitude;
+ LabelLatitude.Text = "Latitude: " + updatedLocation.Latitude;
+ LabelLongitude.Text = "Longitude: " + updatedLocation.Longitude;
+ LabelSpeed.Text = "Speed: " + updatedLocation.Speed;
+ LabelAccuracy.Text = "Accuracy: " + updatedLocation.Accuracy;
+ LabelAltitude.Text = "Altitude: " + updatedLocation.Altitude;
}
else
{
Console.WriteLine("Updated location is null.");
- lblLatitude.Text = "Latitude: N/A";
- lblLongitude.Text = "Longitude: N/A";
- lblSpeed.Text = "Speed: N/A";
- lblAccuracy.Text = "Accuracy: N/A";
- lblAltitude.Text = "Altitude: N/A";
+ LabelLatitude.Text = "Latitude: N/A";
+ LabelLongitude.Text = "Longitude: N/A";
+ LabelSpeed.Text = "Speed: N/A";
+ LabelAccuracy.Text = "Accuracy: N/A";
+ LabelAltitude.Text = "Altitude: N/A";
}
}
}
diff --git a/GeoClient/GeoClient/Views/ItemDetailPage.xaml b/GeoClient/GeoClient/Views/ItemDetailPage.xaml
index de1449e..52bccae 100644
--- a/GeoClient/GeoClient/Views/ItemDetailPage.xaml
+++ b/GeoClient/GeoClient/Views/ItemDetailPage.xaml
@@ -13,9 +13,19 @@
-
-
-
+
+
+
@@ -34,11 +44,12 @@
-
+
@@ -47,7 +58,10 @@
-
+
\ No newline at end of file
diff --git a/GeoClient/GeoClient/Views/ItemsPage.xaml b/GeoClient/GeoClient/Views/ItemsPage.xaml
index 8e604c8..51a3a98 100644
--- a/GeoClient/GeoClient/Views/ItemsPage.xaml
+++ b/GeoClient/GeoClient/Views/ItemsPage.xaml
@@ -42,7 +42,7 @@
FontSize="14"
TextColor="{Binding ForegroundColor} " />
-
+
diff --git a/GeoClient/GeoClient/Views/ItemsPage.xaml.cs b/GeoClient/GeoClient/Views/ItemsPage.xaml.cs
index e72397f..c66abd2 100644
--- a/GeoClient/GeoClient/Views/ItemsPage.xaml.cs
+++ b/GeoClient/GeoClient/Views/ItemsPage.xaml.cs
@@ -5,6 +5,7 @@
using GeoClient.ViewModels;
using System;
using System.Collections.Generic;
+using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@@ -82,19 +83,26 @@ public void IncidentsUpdated(List updatedIncidents)
{
IncidentsInvalidated();
- foreach (var incident in updatedIncidents)
+ var sortedIncidents = updatedIncidents.OrderBy(x => x);
+
+ foreach (var incident in sortedIncidents)
{
_viewModel.Incidents.Add(incident);
}
- _viewModel.IsBusy = false;
+ SetBusyIndicationToFalse();
}
public void IncidentsInvalidated()
{
_viewModel.EmptyListMessage = "Keine Aufträge / Einsätze.";
_viewModel.Incidents.Clear();
- _viewModel.IsBusy = false;
+ SetBusyIndicationToFalse();
+ }
+
+ private void SetBusyIndicationToFalse()
+ {
+ Device.BeginInvokeOnMainThread(() => _viewModel.IsBusy = false);
}
}
}
\ No newline at end of file
diff --git a/GeoClient/GeoClient/Views/Utils/EnumExtension.cs b/GeoClient/GeoClient/Views/Utils/EnumExtension.cs
new file mode 100644
index 0000000..48f5a04
--- /dev/null
+++ b/GeoClient/GeoClient/Views/Utils/EnumExtension.cs
@@ -0,0 +1,35 @@
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
+
+namespace GeoClient.Views.Utils
+{
+ public static class EnumExtension
+ {
+ public static string GetDescription(this T enumValue) where T : IConvertible
+ {
+ if (!(enumValue is Enum))
+ return "";
+
+ Type enumType = enumValue.GetType();
+ Array values = Enum.GetValues(enumType);
+
+ foreach (int val in values)
+ {
+ if (val != enumValue.ToInt32(CultureInfo.InvariantCulture))
+ continue;
+
+ var memInfo = enumType.GetMember(enumType.GetEnumName(val));
+ if (memInfo[0]
+ .GetCustomAttributes(typeof(DescriptionAttribute), false)
+ .FirstOrDefault() is DescriptionAttribute descriptionAttribute)
+ {
+ return descriptionAttribute.Description;
+ }
+ }
+
+ return "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeoClient/GeoClient/Views/Utils/GeoPointUtil.cs b/GeoClient/GeoClient/Views/Utils/GeoPointUtil.cs
index bd4c873..46412b3 100644
--- a/GeoClient/GeoClient/Views/Utils/GeoPointUtil.cs
+++ b/GeoClient/GeoClient/Views/Utils/GeoPointUtil.cs
@@ -25,7 +25,7 @@ public static Uri CreateGeoUri(GeoPoint geoPoint, string geoPointerTag)
private static bool IsGeoPointValid(GeoPoint geoPoint)
{
- return geoPoint != null && NotBlank(geoPoint.Latitude) && NotBlank(geoPoint.Longitude);
+ return geoPoint != null;
}
public static bool NotBlank(string inputString)
diff --git a/GeoClientTests/AppInitializer.cs b/GeoClientTests/AppInitializer.cs
new file mode 100644
index 0000000..a9fae9f
--- /dev/null
+++ b/GeoClientTests/AppInitializer.cs
@@ -0,0 +1,17 @@
+using Xamarin.UITest;
+
+namespace GeoClientTests
+{
+ public class AppInitializer
+ {
+ public static IApp StartApp(Platform platform)
+ {
+ if (platform == Platform.Android)
+ {
+ return ConfigureApp.Android.StartApp();
+ }
+
+ return ConfigureApp.iOS.StartApp();
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeoClientTests/GeoClientTests.csproj b/GeoClientTests/GeoClientTests.csproj
new file mode 100644
index 0000000..673726d
--- /dev/null
+++ b/GeoClientTests/GeoClientTests.csproj
@@ -0,0 +1,62 @@
+
+
+
+ Debug
+ AnyCPU
+ {B19C892E-2628-4CA7-AD27-08D406A3B14B}
+ Library
+ GeoClientTests
+ GeoClientTests
+ v4.6.1
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+
+
+ true
+ bin\Release
+ prompt
+ 4
+
+
+
+ False
+ C:\Users\Robert\.nuget\packages\newtonsoft.json\12.0.1\lib\netstandard2.0\Newtonsoft.Json.dll
+
+
+
+
+
+ 12.0.1
+
+
+
+
+
+
+
+ {FA8B858C-9662-4B4F-86B6-0F6515B5480D}
+ GeoClient
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+
+
\ No newline at end of file
diff --git a/GeoClientTests/ServiceTests/IncidentItemFactoryTest.cs b/GeoClientTests/ServiceTests/IncidentItemFactoryTest.cs
new file mode 100644
index 0000000..d81bb29
--- /dev/null
+++ b/GeoClientTests/ServiceTests/IncidentItemFactoryTest.cs
@@ -0,0 +1,91 @@
+using GeoClient.Models;
+using GeoClient.Services;
+using Newtonsoft.Json.Linq;
+using NUnit.Framework;
+using System.Collections.Generic;
+using System.IO;
+
+namespace GeoClientTests.ServiceTests
+{
+ [TestFixture]
+ public class IncidentItemFactoryTest
+ {
+ [Test]
+ public void CreateIncidentItemList_MultipleIncidents_UnitOnlyShownForAssignedIncidents()
+ {
+ var units1String =
+ File.ReadAllText(TestContext.CurrentContext.TestDirectory + "..\\TestResources\\units1.json");
+ var incidents1String =
+ File.ReadAllText(TestContext.CurrentContext.TestDirectory + "..\\TestResources\\incidents1.json");
+
+ var units = ReadJObjectList(units1String);
+ var incidents = ReadJObjectList(incidents1String);
+
+ var incidentItemList = IncidentItemFactory.CreateIncidentItemList(incidents, units);
+
+ var expectedItem1 = CreateExpectedItem1();
+ var expectedItem2 = CreateExpectedItem2();
+ Assert.Contains(expectedItem1, incidentItemList, "Expected first incident with both units in result.");
+ Assert.Contains(expectedItem2, incidentItemList, "Expected second incident only with own unit in results.");
+ }
+
+ private static IncidentItem CreateExpectedItem1()
+ {
+ var expectedItem1 = new IncidentItem(
+ "incident-id-1",
+ GeoIncidentType.Task,
+ "Info 1",
+ location: new GeoPoint(48, 16),
+ units: new List
+ {
+ CreateUnit1(IncidentTaskState.Abo),
+ CreateUnit2()
+ });
+ return expectedItem1;
+ }
+
+ private static IncidentItem CreateExpectedItem2()
+ {
+ var expectedItem1 = new IncidentItem(
+ "incident-id-2",
+ info: "Info 2",
+ location: new GeoPoint(49, 17),
+ type: GeoIncidentType.Task,
+ units: new List
+ {
+ CreateUnit1(IncidentTaskState.Zbo)
+ });
+ return expectedItem1;
+ }
+
+ private static Unit CreateUnit1(IncidentTaskState expectedState)
+ {
+ return new Unit("unit-id-1",
+ "Own Unit",
+ new GeoPoint(48.5, 16.5),
+ expectedState
+ );
+ }
+
+ private static Unit CreateUnit2()
+ {
+ return new Unit("unit-id-2",
+ "No position unit",
+ state: IncidentTaskState.Assigned
+ );
+ }
+
+ private static List ReadJObjectList(string inputString)
+ {
+ var jArray = JArray.Parse(inputString);
+
+ var list = new List();
+ foreach (var jToken in jArray)
+ {
+ list.Add((JObject) jToken);
+ }
+
+ return list;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeoClientTests/TestResources/incidents1.json b/GeoClientTests/TestResources/incidents1.json
new file mode 100644
index 0000000..be92d24
--- /dev/null
+++ b/GeoClientTests/TestResources/incidents1.json
@@ -0,0 +1,29 @@
+[
+ {
+ "id": "incident-id-1",
+ "type": "Task",
+ "priority": false,
+ "blue": false,
+ "info": "Info 1",
+ "location": {
+ "latitude": 48,
+ "longitude": 16
+ },
+ "assignedUnits": {
+ "unit-id-1": "ABO",
+ "unit-id-2": "Assigned"
+ }
+ },
+ {
+ "id": "incident-id-2",
+ "type": "Task",
+ "priority": false,
+ "blue": false,
+ "info": "Info 2",
+ "location": {
+ "latitude": 49,
+ "longitude": 17
+ },
+ "assignedUnits": { "unit-id-1": "ZBO" }
+ }
+]
\ No newline at end of file
diff --git a/GeoClientTests/TestResources/units1.json b/GeoClientTests/TestResources/units1.json
new file mode 100644
index 0000000..e2a261b
--- /dev/null
+++ b/GeoClientTests/TestResources/units1.json
@@ -0,0 +1,20 @@
+[
+ {
+ "id": "unit-id-1",
+ "name": "Own Unit",
+ "lastPoint": {
+ "latitude": 48.50,
+ "longitude": 16.50
+ },
+ "currentPosition": {
+ "timestamp": "2019-02-20T18:20:54Z",
+ "accuracy": 37.0,
+ "latitude": 48.60,
+ "longitude": 16.60
+ }
+ },
+ {
+ "id": "unit-id-2",
+ "name": "No position unit"
+ }
+]
\ No newline at end of file
diff --git a/GeoClientTests/Tests.cs b/GeoClientTests/Tests.cs
new file mode 100644
index 0000000..3d07d1e
--- /dev/null
+++ b/GeoClientTests/Tests.cs
@@ -0,0 +1,36 @@
+using System;
+using System.IO;
+using System.Linq;
+using NUnit.Framework;
+using Xamarin.UITest;
+using Xamarin.UITest.Queries;
+
+namespace GeoClientTests
+{
+ [TestFixture(Platform.Android)]
+ public class Tests
+ {
+ IApp app;
+ Platform platform;
+
+ public Tests(Platform platform)
+ {
+ this.platform = platform;
+ }
+
+ [SetUp]
+ public void BeforeEachTest()
+ {
+ app = AppInitializer.StartApp(platform);
+ }
+
+ [Test]
+ public void WelcomeTextIsDisplayed()
+ {
+ AppResult[] results = app.WaitForElement(c => c.Marked("Welcome to Xamarin.Forms!"));
+ app.Screenshot("Welcome screen.");
+
+ Assert.IsTrue(results.Any());
+ }
+ }
+}