diff --git a/MonkeyFinder.sln b/MonkeyFinder.sln
new file mode 100644
index 0000000..b99e83b
--- /dev/null
+++ b/MonkeyFinder.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31611.283
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonkeyFinder", "MonkeyFinder\MonkeyFinder.csproj", "{C60F2B1C-790E-4432-A719-52182A333ADE}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{393B8F30-F402-46A9-B209-8C71897A3416}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Directory.build.props = ..\Directory.build.props
+ ..\Directory.build.targets = ..\Directory.build.targets
+ README.md = README.md
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C60F2B1C-790E-4432-A719-52182A333ADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C60F2B1C-790E-4432-A719-52182A333ADE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C60F2B1C-790E-4432-A719-52182A333ADE}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {C60F2B1C-790E-4432-A719-52182A333ADE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C60F2B1C-790E-4432-A719-52182A333ADE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C60F2B1C-790E-4432-A719-52182A333ADE}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {61F7FB11-1E47-470C-91E2-47F8143E1572}
+ EndGlobalSection
+EndGlobal
diff --git a/MonkeyFinder/App.xaml b/MonkeyFinder/App.xaml
new file mode 100644
index 0000000..09c403e
--- /dev/null
+++ b/MonkeyFinder/App.xaml
@@ -0,0 +1,69 @@
+
+
+
+
+ #FFC107
+ #FFA000
+ #00BCD4
+
+ #FAF9F8
+ Black
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MonkeyFinder/App.xaml.cs b/MonkeyFinder/App.xaml.cs
new file mode 100644
index 0000000..09d633f
--- /dev/null
+++ b/MonkeyFinder/App.xaml.cs
@@ -0,0 +1,11 @@
+namespace MonkeyFinder;
+
+public partial class App : Application
+{
+ public App()
+ {
+ InitializeComponent();
+
+ MainPage = new AppShell();
+ }
+}
diff --git a/MonkeyFinder/AppShell.xaml b/MonkeyFinder/AppShell.xaml
new file mode 100644
index 0000000..1a5b5ec
--- /dev/null
+++ b/MonkeyFinder/AppShell.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MonkeyFinder/AppShell.xaml.cs b/MonkeyFinder/AppShell.xaml.cs
new file mode 100644
index 0000000..74df355
--- /dev/null
+++ b/MonkeyFinder/AppShell.xaml.cs
@@ -0,0 +1,12 @@
+namespace MonkeyFinder;
+
+public partial class AppShell : Shell
+{
+ public AppShell()
+ {
+ InitializeComponent();
+
+ // nameof(DetailsPage) == "DetailsPage"
+ Routing.RegisterRoute(nameof(DetailsPage), typeof(DetailsPage));
+ }
+}
\ No newline at end of file
diff --git a/MonkeyFinder/GlobalUsings.cs b/MonkeyFinder/GlobalUsings.cs
new file mode 100644
index 0000000..d68e6fb
--- /dev/null
+++ b/MonkeyFinder/GlobalUsings.cs
@@ -0,0 +1,9 @@
+global using CommunityToolkit.Mvvm.ComponentModel;
+global using CommunityToolkit.Mvvm.Input;
+global using MonkeyFinder.Model;
+global using MonkeyFinder.ViewModel;
+global using System.Collections.ObjectModel;
+global using System.ComponentModel;
+global using System.Diagnostics;
+global using System.Runtime.CompilerServices;
+global using System.Text.Json;
\ No newline at end of file
diff --git a/MonkeyFinder/MauiProgram.cs b/MonkeyFinder/MauiProgram.cs
new file mode 100644
index 0000000..f9baa69
--- /dev/null
+++ b/MonkeyFinder/MauiProgram.cs
@@ -0,0 +1,40 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using MonkeyFinder.Services;
+using MonkeyFinder.View;
+
+namespace MonkeyFinder;
+
+public static class MauiProgram
+{
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ });
+
+#if DEBUG
+ builder.Logging.AddDebug();
+#endif
+
+ builder.Services.AddSingleton(Connectivity.Current);
+ builder.Services.AddSingleton(Geolocation.Default);
+ builder.Services.AddSingleton(Map.Default);
+
+ builder.Services.AddSingleton();
+ builder.Services.AddTransient();
+
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddTransient();
+
+
+
+
+ return builder.Build();
+ }
+}
diff --git a/MonkeyFinder/Model/Monkey.cs b/MonkeyFinder/Model/Monkey.cs
new file mode 100644
index 0000000..9d0b15d
--- /dev/null
+++ b/MonkeyFinder/Model/Monkey.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+
+namespace MonkeyFinder.Model;
+
+public class Monkey
+{
+ public string Name { get; set; }
+ public string Location { get; set; }
+ public string Details { get; set; }
+ public string Image { get; set; }
+ public int Population { get; set; }
+ public double Latitude { get; set; }
+ public double Longitude { get; set; }
+}
diff --git a/MonkeyFinder/MonkeyContext.cs b/MonkeyFinder/MonkeyContext.cs
new file mode 100644
index 0000000..2438c6f
--- /dev/null
+++ b/MonkeyFinder/MonkeyContext.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+
+namespace MonkeyFinder
+{
+ [JsonSerializable(typeof(List))]
+ internal sealed partial class MonkeyContext : JsonSerializerContext
+ {
+
+ }
+}
diff --git a/MonkeyFinder/MonkeyFinder.csproj b/MonkeyFinder/MonkeyFinder.csproj
new file mode 100644
index 0000000..36c4717
--- /dev/null
+++ b/MonkeyFinder/MonkeyFinder.csproj
@@ -0,0 +1,49 @@
+
+
+
+ net8.0-android
+ $(TargetFrameworks);net8.0-ios;net8.0-maccatalyst
+ $(TargetFrameworks);net8.0-windows10.0.19041
+ Exe
+ MonkeyFinder
+ true
+ true
+ enable
+
+
+ MonkeyFinder
+
+
+ com.companyname.monkeyfinder
+ ECD44DAE-B03E-4D8B-B427-71865961E696
+
+
+ 1.0
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MonkeyFinder/Platforms/Android/AndroidManifest.xml b/MonkeyFinder/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..0f4506e
--- /dev/null
+++ b/MonkeyFinder/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MonkeyFinder/Platforms/Android/AssemblyInfo.cs b/MonkeyFinder/Platforms/Android/AssemblyInfo.cs
new file mode 100644
index 0000000..802d33f
--- /dev/null
+++ b/MonkeyFinder/Platforms/Android/AssemblyInfo.cs
@@ -0,0 +1,9 @@
+using Android.App;
+
+
+[assembly: UsesPermission(Android.Manifest.Permission.AccessCoarseLocation)]
+[assembly: UsesPermission(Android.Manifest.Permission.AccessFineLocation)]
+[assembly: UsesFeature("android.hardware.location", Required = false)]
+[assembly: UsesFeature("android.hardware.location.gps", Required = false)]
+[assembly: UsesFeature("android.hardware.location.network", Required = false)]
+[assembly: UsesPermission(Android.Manifest.Permission.Internet)]
\ No newline at end of file
diff --git a/MonkeyFinder/Platforms/Android/MainActivity.cs b/MonkeyFinder/Platforms/Android/MainActivity.cs
new file mode 100644
index 0000000..6175b2c
--- /dev/null
+++ b/MonkeyFinder/Platforms/Android/MainActivity.cs
@@ -0,0 +1,10 @@
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+
+namespace MonkeyFinder;
+
+[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+public class MainActivity : MauiAppCompatActivity
+{
+}
diff --git a/MonkeyFinder/Platforms/Android/MainApplication.cs b/MonkeyFinder/Platforms/Android/MainApplication.cs
new file mode 100644
index 0000000..f075071
--- /dev/null
+++ b/MonkeyFinder/Platforms/Android/MainApplication.cs
@@ -0,0 +1,15 @@
+using Android.App;
+using Android.Runtime;
+
+namespace MonkeyFinder;
+
+[Application]
+public class MainApplication : MauiApplication
+{
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/MonkeyFinder/Platforms/Android/Resources/values/colors.xml b/MonkeyFinder/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000..c04d749
--- /dev/null
+++ b/MonkeyFinder/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/MonkeyFinder/Platforms/MacCatalyst/AppDelegate.cs b/MonkeyFinder/Platforms/MacCatalyst/AppDelegate.cs
new file mode 100644
index 0000000..31595cf
--- /dev/null
+++ b/MonkeyFinder/Platforms/MacCatalyst/AppDelegate.cs
@@ -0,0 +1,9 @@
+using Foundation;
+
+namespace MonkeyFinder;
+
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/MonkeyFinder/Platforms/MacCatalyst/Entitlements.Debug.plist b/MonkeyFinder/Platforms/MacCatalyst/Entitlements.Debug.plist
new file mode 100644
index 0000000..1659c84
--- /dev/null
+++ b/MonkeyFinder/Platforms/MacCatalyst/Entitlements.Debug.plist
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+ com.apple.security.get-task-allow
+
+
+
+
diff --git a/MonkeyFinder/Platforms/MacCatalyst/Entitlements.Release.plist b/MonkeyFinder/Platforms/MacCatalyst/Entitlements.Release.plist
new file mode 100644
index 0000000..1db1d93
--- /dev/null
+++ b/MonkeyFinder/Platforms/MacCatalyst/Entitlements.Release.plist
@@ -0,0 +1,12 @@
+
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.network.client
+
+
+
+
diff --git a/MonkeyFinder/Platforms/MacCatalyst/Info.plist b/MonkeyFinder/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 0000000..10b0a53
--- /dev/null
+++ b/MonkeyFinder/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+ NSLocationWhenInUseUsageDescription
+ Need to find monkeys!
+
+
diff --git a/MonkeyFinder/Platforms/MacCatalyst/Program.cs b/MonkeyFinder/Platforms/MacCatalyst/Program.cs
new file mode 100644
index 0000000..87bfb87
--- /dev/null
+++ b/MonkeyFinder/Platforms/MacCatalyst/Program.cs
@@ -0,0 +1,15 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace MonkeyFinder;
+
+public class Program
+{
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
diff --git a/MonkeyFinder/Platforms/Windows/App.xaml b/MonkeyFinder/Platforms/Windows/App.xaml
new file mode 100644
index 0000000..6c25faa
--- /dev/null
+++ b/MonkeyFinder/Platforms/Windows/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/MonkeyFinder/Platforms/Windows/App.xaml.cs b/MonkeyFinder/Platforms/Windows/App.xaml.cs
new file mode 100644
index 0000000..ab7f6a8
--- /dev/null
+++ b/MonkeyFinder/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,24 @@
+using Microsoft.UI.Xaml;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace MonkeyFinder.WinUI;
+
+///
+/// Provides application-specific behavior to supplement the default Application class.
+///
+public partial class App : MauiWinUIApplication
+{
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
+
diff --git a/MonkeyFinder/Platforms/Windows/Package.appxmanifest b/MonkeyFinder/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 0000000..7bdba98
--- /dev/null
+++ b/MonkeyFinder/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MonkeyFinder/Platforms/Windows/app.manifest b/MonkeyFinder/Platforms/Windows/app.manifest
new file mode 100644
index 0000000..97e6629
--- /dev/null
+++ b/MonkeyFinder/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/MonkeyFinder/Platforms/iOS/AppDelegate.cs b/MonkeyFinder/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 0000000..31595cf
--- /dev/null
+++ b/MonkeyFinder/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,9 @@
+using Foundation;
+
+namespace MonkeyFinder;
+
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/MonkeyFinder/Platforms/iOS/Info.plist b/MonkeyFinder/Platforms/iOS/Info.plist
new file mode 100644
index 0000000..54d309d
--- /dev/null
+++ b/MonkeyFinder/Platforms/iOS/Info.plist
@@ -0,0 +1,33 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+ NSLocationWhenInUseUsageDescription
+ Need to find monkeys!
+
+
diff --git a/MonkeyFinder/Platforms/iOS/Program.cs b/MonkeyFinder/Platforms/iOS/Program.cs
new file mode 100644
index 0000000..87bfb87
--- /dev/null
+++ b/MonkeyFinder/Platforms/iOS/Program.cs
@@ -0,0 +1,15 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace MonkeyFinder;
+
+public class Program
+{
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
diff --git a/MonkeyFinder/Properties/launchSettings.json b/MonkeyFinder/Properties/launchSettings.json
new file mode 100644
index 0000000..edf8aad
--- /dev/null
+++ b/MonkeyFinder/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/MonkeyFinder/Resources/AppIcon/appicon.svg b/MonkeyFinder/Resources/AppIcon/appicon.svg
new file mode 100644
index 0000000..9d63b65
--- /dev/null
+++ b/MonkeyFinder/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/MonkeyFinder/Resources/AppIcon/appiconfg.svg b/MonkeyFinder/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000..21dfb25
--- /dev/null
+++ b/MonkeyFinder/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/MonkeyFinder/Resources/Fonts/OpenSans-Regular.ttf b/MonkeyFinder/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..5a58cb4
Binary files /dev/null and b/MonkeyFinder/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/MonkeyFinder/Resources/Images/dotnet_bot.svg b/MonkeyFinder/Resources/Images/dotnet_bot.svg
new file mode 100644
index 0000000..abfaff2
--- /dev/null
+++ b/MonkeyFinder/Resources/Images/dotnet_bot.svg
@@ -0,0 +1,93 @@
+
diff --git a/MonkeyFinder/Resources/Images/nodata.png b/MonkeyFinder/Resources/Images/nodata.png
new file mode 100644
index 0000000..1769e6b
Binary files /dev/null and b/MonkeyFinder/Resources/Images/nodata.png differ
diff --git a/MonkeyFinder/Resources/Raw/AboutAssets.txt b/MonkeyFinder/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000..ed2e712
--- /dev/null
+++ b/MonkeyFinder/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,14 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories) and given a Build Action of "MauiAsset":
+
+
+
+These files will be deployed with you package and will be accessible using .NET MAUI:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/MonkeyFinder/Resources/Raw/monkeydata.json b/MonkeyFinder/Resources/Raw/monkeydata.json
new file mode 100644
index 0000000..25a86ae
--- /dev/null
+++ b/MonkeyFinder/Resources/Raw/monkeydata.json
@@ -0,0 +1,119 @@
+[
+ {
+ "Name": "Baboon",
+ "Location": "Africa & Asia",
+ "Details": "Baboons are African and Arabian Old World monkeys belonging to the genus Papio, part of the subfamily Cercopithecinae.",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/baboon.jpg",
+ "Population": 10000,
+ "Latitude": -8.783195,
+ "Longitude": 34.508523
+ },
+ {
+ "Name": "Capuchin Monkey",
+ "Location": "Central & South America",
+ "Details": "The capuchin monkeys are New World monkeys of the subfamily Cebinae. Prior to 2011, the subfamily contained only a single genus, Cebus.",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/capuchin.jpg",
+ "Population": 23000,
+ "Latitude": 12.769013,
+ "Longitude": -85.602364
+ },
+ {
+ "Name": "Blue Monkey",
+ "Location": "Central and East Africa",
+ "Details": "The blue monkey or diademed monkey is a species of Old World monkey native to Central and East Africa, ranging from the upper Congo River basin east to the East African Rift and south to northern Angola and Zambia",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/bluemonkey.jpg",
+ "Population": 12000,
+ "Latitude": 1.957709,
+ "Longitude": 37.297204
+ },
+ {
+ "Name": "Squirrel Monkey",
+ "Location": "Central & South America",
+ "Details": "The squirrel monkeys are the New World monkeys of the genus Saimiri. They are the only genus in the subfamily Saimirinae. The name of the genus Saimiri is of Tupi origin, and was also used as an English name by early researchers.",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/saimiri.jpg",
+ "Population": 11000,
+ "Latitude": -8.783195,
+ "Longitude": -55.491477
+ },
+ {
+ "Name": "Golden Lion Tamarin",
+ "Location": "Brazil",
+ "Details": "The golden lion tamarin also known as the golden marmoset, is a small New World monkey of the family Callitrichidae.",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/tamarin.jpg",
+ "Population": 19000,
+ "Latitude": -14.235004,
+ "Longitude": -51.92528
+ },
+ {
+ "Name": "Howler Monkey",
+ "Location": "South America",
+ "Details": "Howler monkeys are among the largest of the New World monkeys. Fifteen species are currently recognised. Previously classified in the family Cebidae, they are now placed in the family Atelidae.",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/alouatta.jpg",
+ "Population": 8000,
+ "Latitude": -8.783195,
+ "Longitude": -55.491477
+ },
+ {
+ "Name": "Japanese Macaque",
+ "Location": "Japan",
+ "Details": "The Japanese macaque, is a terrestrial Old World monkey species native to Japan. They are also sometimes known as the snow monkey because they live in areas where snow covers the ground for months each",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/macasa.jpg",
+ "Population": 1000,
+ "Latitude": 36.204824,
+ "Longitude": 138.252924
+ },
+ {
+ "Name": "Mandrill",
+ "Location": "Southern Cameroon, Gabon, and Congo",
+ "Details": "The mandrill is a primate of the Old World monkey family, closely related to the baboons and even more closely to the drill. It is found in southern Cameroon, Gabon, Equatorial Guinea, and Congo.",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/mandrill.jpg",
+ "Population": 17000,
+ "Latitude": 7.369722,
+ "Longitude": 12.354722
+ },
+ {
+ "Name": "Proboscis Monkey",
+ "Location": "Borneo",
+ "Details": "The proboscis monkey or long-nosed monkey, known as the bekantan in Malay, is a reddish-brown arboreal Old World monkey that is endemic to the south-east Asian island of Borneo.",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/borneo.jpg",
+ "Population": 15000,
+ "Latitude": 0.961883,
+ "Longitude": 114.55485
+ },
+ {
+ "Name": "Sebastian",
+ "Location": "Seattle",
+ "Details": "This little trouble maker lives in Seattle with James and loves traveling on adventures with James and tweeting @MotzMonkeys. He by far is an Android fanboy and is getting ready for the new Nexus 6P!",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/sebastian.jpg",
+ "Population": 1,
+ "Latitude": 47.606209,
+ "Longitude": -122.332071
+ },
+ {
+ "Name": "Henry",
+ "Location": "Phoenix",
+ "Details": "An adorable Monkey who is traveling the world with Heather and live tweets his adventures @MotzMonkeys. His favorite platform is iOS by far and is excited for the new iPhone Xs!",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/henry.jpg",
+ "Population": 1,
+ "Latitude": 33.448377,
+ "Longitude": -112.074037
+ },
+ {
+ "Name": "Red-shanked douc",
+ "Location": "Vietnam",
+ "Details": "The red-shanked douc is a species of Old World monkey, among the most colourful of all primates. The douc is an arboreal and diurnal monkey that eats and sleeps in the trees of the forest.",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/douc.jpg",
+ "Population": 1300,
+ "Latitude": 16.111648,
+ "Longitude": 108.262122
+ },
+ {
+ "Name": "Mooch",
+ "Location": "Seattle",
+ "Details": "An adorable Monkey who is traveling the world with Heather and live tweets his adventures @MotzMonkeys. His favorite platform is iOS by far and is excited for the new iPhone 6s!",
+ "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/Mooch.PNG",
+ "Population": 1,
+ "Latitude": 47.608013,
+ "Longitude": -122.335167
+ }
+]
diff --git a/MonkeyFinder/Resources/Splash/splash.svg b/MonkeyFinder/Resources/Splash/splash.svg
new file mode 100644
index 0000000..21dfb25
--- /dev/null
+++ b/MonkeyFinder/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/MonkeyFinder/Services/MonkeyService.cs b/MonkeyFinder/Services/MonkeyService.cs
new file mode 100644
index 0000000..8a9f9d4
--- /dev/null
+++ b/MonkeyFinder/Services/MonkeyService.cs
@@ -0,0 +1,32 @@
+using System.Net.Http.Json;
+
+namespace MonkeyFinder.Services;
+
+public class MonkeyService
+{
+ HttpClient httpClient;
+ public MonkeyService()
+ {
+ httpClient = new HttpClient();
+ }
+
+ List monkeyList = new();
+
+ public async Task> GetMonkeys()
+ {
+ if (monkeyList?.Count > 0)
+ {
+ return monkeyList;
+ }
+
+ var url = "https://montemagno.com/monkeys.json";
+ var response = await httpClient.GetAsync(url);
+
+ if(response.IsSuccessStatusCode)
+ {
+ monkeyList = await response.Content.ReadFromJsonAsync>();
+ }
+
+ return monkeyList;
+ }
+}
diff --git a/MonkeyFinder/View/DetailsPage.xaml b/MonkeyFinder/View/DetailsPage.xaml
new file mode 100644
index 0000000..3adcd9a
--- /dev/null
+++ b/MonkeyFinder/View/DetailsPage.xaml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MonkeyFinder/View/DetailsPage.xaml.cs b/MonkeyFinder/View/DetailsPage.xaml.cs
new file mode 100644
index 0000000..1b7bcbc
--- /dev/null
+++ b/MonkeyFinder/View/DetailsPage.xaml.cs
@@ -0,0 +1,15 @@
+namespace MonkeyFinder;
+
+public partial class DetailsPage : ContentPage
+{
+ public DetailsPage(MonkeyDetailsViewModel viewModel)
+ {
+ InitializeComponent();
+ BindingContext = viewModel;
+ }
+
+ protected override void OnNavigatedTo(NavigatedToEventArgs args)
+ {
+ base.OnNavigatedTo(args);
+ }
+}
\ No newline at end of file
diff --git a/MonkeyFinder/View/MainPage.xaml b/MonkeyFinder/View/MainPage.xaml
new file mode 100644
index 0000000..9660f97
--- /dev/null
+++ b/MonkeyFinder/View/MainPage.xaml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MonkeyFinder/View/MainPage.xaml.cs b/MonkeyFinder/View/MainPage.xaml.cs
new file mode 100644
index 0000000..6ea637d
--- /dev/null
+++ b/MonkeyFinder/View/MainPage.xaml.cs
@@ -0,0 +1,11 @@
+namespace MonkeyFinder.View;
+
+public partial class MainPage : ContentPage
+{
+ public MainPage(MonkeysViewModel viewModel)
+ {
+ InitializeComponent();
+ BindingContext = viewModel;
+ }
+}
+
diff --git a/MonkeyFinder/ViewModel/BaseViewModel.cs b/MonkeyFinder/ViewModel/BaseViewModel.cs
new file mode 100644
index 0000000..7cbf6eb
--- /dev/null
+++ b/MonkeyFinder/ViewModel/BaseViewModel.cs
@@ -0,0 +1,21 @@
+
+
+namespace MonkeyFinder.ViewModel;
+
+public partial class BaseViewModel : ObservableObject
+{
+ public BaseViewModel()
+ {
+ }
+
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(IsNotBusy))]
+ bool isBusy;
+
+ [ObservableProperty]
+ string title;
+
+
+ public bool IsNotBusy => !IsBusy;
+
+}
diff --git a/MonkeyFinder/ViewModel/MonkeyDetailsViewModel.cs b/MonkeyFinder/ViewModel/MonkeyDetailsViewModel.cs
new file mode 100644
index 0000000..dd3a44a
--- /dev/null
+++ b/MonkeyFinder/ViewModel/MonkeyDetailsViewModel.cs
@@ -0,0 +1,43 @@
+namespace MonkeyFinder.ViewModel;
+
+[QueryProperty("Monkey", "Monkey")]
+
+public partial class MonkeyDetailsViewModel : BaseViewModel
+{
+ IMap map;
+ public MonkeyDetailsViewModel(IMap map)
+ {
+ this.map = map;
+ }
+
+ [ObservableProperty]
+ Monkey monkey;
+
+ //[RelayCommand]
+ //async Task GoBackAsync()
+ //{
+ // await Shell.Current.GoToAsync("..");
+ //}
+
+ [RelayCommand]
+
+ async Task OpenMapAsync()
+ {
+ try
+ {
+ await map.OpenAsync(Monkey.Latitude, Monkey.Longitude,
+ new MapLaunchOptions
+ {
+ Name = Monkey.Name,
+ NavigationMode = NavigationMode.None
+ });
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex);
+ await Shell.Current.DisplayAlert("Error!",
+ $"Unable to open map: {ex.Message}", "OK");
+ }
+ }
+
+}
diff --git a/MonkeyFinder/ViewModel/MonkeysViewModel.cs b/MonkeyFinder/ViewModel/MonkeysViewModel.cs
new file mode 100644
index 0000000..a39ba8e
--- /dev/null
+++ b/MonkeyFinder/ViewModel/MonkeysViewModel.cs
@@ -0,0 +1,108 @@
+using MonkeyFinder.Services;
+
+namespace MonkeyFinder.ViewModel;
+
+public partial class MonkeysViewModel : BaseViewModel
+{
+ MonkeyService monkeyService;
+ public ObservableCollection Monkeys { get; } = new();
+
+ IConnectivity connectivity;
+ IGeolocation geolocation;
+ public MonkeysViewModel(MonkeyService monkeyService, IConnectivity connectivity, IGeolocation geolocation)
+ {
+ Title = "Monkey Finder";
+ this.monkeyService = monkeyService;
+ this.connectivity = connectivity;
+ this.geolocation = geolocation;
+ }
+
+ [RelayCommand]
+ async Task GetClosestMonkeyAsync()
+ {
+ if(IsBusy || Monkeys.Count == 0)
+ return;
+ try
+ {
+ var location = await geolocation.GetLastKnownLocationAsync();
+
+ if (location is null)
+ {
+ location = await geolocation.GetLocationAsync(
+ new GeolocationRequest
+ {
+ DesiredAccuracy = GeolocationAccuracy.Medium,
+ Timeout = TimeSpan.FromSeconds(30),
+ });
+ }
+
+ if (location is null)
+ return;
+
+ var first = Monkeys.OrderBy(m =>
+ location.CalculateDistance(m.Latitude, m.Longitude, DistanceUnits.Kilometers)
+ ).FirstOrDefault();
+
+ await Shell.Current.DisplayAlert("Closest Monkey",
+ $"{first.Name} in {first.Location}", "OK");
+
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex);
+ await Shell.Current.DisplayAlert("Error!",
+ $"Unable to get closest monkey: {ex.Message}", "OK");
+ }
+
+ }
+
+
+ [RelayCommand]
+ async Task GoToDetailsAsync(Monkey monkey)
+ {
+ if (monkey is null)
+ return;
+ await Shell.Current.GoToAsync($"{nameof(DetailsPage)}", true,
+ new Dictionary
+ {
+ {"Monkey", monkey }
+ });
+ }
+
+
+
+ [RelayCommand]
+ async Task GetMonkeysAsync()
+ {
+ if (IsBusy) return;
+
+ try
+ {
+ if (connectivity.NetworkAccess != NetworkAccess.Internet)
+ {
+ await Shell.Current.DisplayAlert("Internet issue",
+ $"Check your internet and try again!", "OK");
+ return;
+ }
+
+ IsBusy = true;
+ var monkeys = await monkeyService.GetMonkeys();
+ if (Monkeys.Count != 0)
+ Monkeys.Clear();
+ foreach (var monkey in monkeys)
+ Monkeys.Add(monkey);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex);
+ await Shell.Current.DisplayAlert("Error!",
+ $"Unable to get monkeys: {ex.Message}", "OK");
+ }
+ finally
+ {
+ IsBusy = false;
+ }
+ }
+
+
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fb3fbad
--- /dev/null
+++ b/README.md
@@ -0,0 +1,157 @@
+
+## Displaying Data
+
+In Part 0 you got a basic understanding of what makes up a .NET MAUI project, now let's start coding and see how to display a list of data in a list.
+
+This module is also available in [Chinese (Simplified)](README.zh-cn.md) & [Chinese (Traditional)](README.zh-tw.md).
+
+### Open Solution in Visual Studio
+
+1. Open **Part 1 - Displaying Data/MonkeyFinder.sln**
+
+This MonkeyFinder contains 1 project:
+
+* MonkeyFinder - The main .NET MAUI project that targets Android, iOS, macOS, and Windows. It includes all scaffolding for the app including Models, Views, ViewModels, and Services.
+
+
+
+The **MonkeyFinder** project also has blank code files and XAML pages that we will use during the workshop. All of the code that we modify will be in this project for the workshop.
+
+### NuGet Restore
+
+All projects have the required NuGet packages already installed, so there will be no need to install additional packages during the Hands on Lab. The first thing that we must do is restore all of the NuGet packages from the internet.
+
+1. **Right-click** on the **Solution** and select **Restore NuGet packages...**
+
+
+
+
+### Model
+
+We will be downloading details about the monkey and will need a class to represent it.
+
+
+
+We can easily convert our json file located at [montemagno.com/monkeys.json](https://montemagno.com/monkeys.json) by using [json2csharp.com](https://json2csharp.com) and pasting the raw json into quicktype to generate our C# classes. Ensure that you set the Name to `Monkey` and the generated namespace to `MonkeyFinder.Model` and select C#.
+
+1. Open `Model/Monkey.cs`
+2. In `Monkey.cs`, copy/paste the properties:
+
+```csharp
+public class Monkey
+{
+ public string Name { get; set; }
+ public string Location { get; set; }
+ public string Details { get; set; }
+ public string Image { get; set; }
+ public int Population { get; set; }
+ public double Latitude { get; set; }
+ public double Longitude { get; set; }
+}
+```
+
+Additionally, because we will be using `System.Text.Json` to deserialize the data, we will want to add a `MonkeyContext` that will dynamically generate code for better performance. The following code will enable this and we will use it in the future.
+
+```csharp
+[JsonSerializable(typeof(List))]
+internal sealed partial class MonkeyContext : JsonSerializerContext
+{
+
+}
+```
+
+### Displaying Data
+
+We can display hard coded data of any data type in a `CollectionView` in our `MainPage.xaml`. This will allow us to build out our user interface by setting the `ItemTemplate` with some simple images and labels.
+
+We first need to add a new namespace at the top of the `MainPage.xaml`:
+
+```xml
+xmlns:model="clr-namespace:MonkeyFinder.Model"
+```
+
+This will allow us to reference the Monkey class above for data binding purposes.
+
+Add the following into the MainPage.xaml's `ContentPage`:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+If we wanted to display the two strings vertically on top of each other, we could wrap two `Label` controls inside of a `VerticalStackLayout` and assign font sizes to stand out:
+
+
+```xml
+
+
+
+
+
+
+
+```
+
+
+
+### Run the App
+
+Ensure that you have your machine setup to deploy and debug to the different platforms:
+
+* [Android Emulator Setup](https://docs.microsoft.com/dotnet/maui/android/emulator/device-manager)
+* [Windows setup for development](https://docs.microsoft.com/dotnet/maui/windows/setup)
+
+1. In Visual Studio, set the Android or Windows app as the startup project by selecting the drop down in the debug menu and changing the `Framework`
+
+
+
+
+2. In Visual Studio, click the "Debug" button or Tools -> Start Debugging
+ - If you are having any trouble, see the Setup guides for your runtime platform
+
+Running the app will result in a list of three monkeys:
+
+
+
+Let's continue and learn about using the MVVM pattern with data binding in [Part 2](../Part%202%20-%20MVVM/README.md)