Add MapRenderer to enable Xamarin.Forms.Maps on Tizen
authorchungryeol lim <cdark.lim@samsung.com>
Tue, 20 Dec 2016 11:16:18 +0000 (20:16 +0900)
committerKangho Hur <kangho.hur@samsung.com>
Fri, 24 Mar 2017 04:18:59 +0000 (13:18 +0900)
    - Implementation of MapRenderer under Xamarin.Forms.Maps.Tizen
    - Implementation of FormsMaps to initialize Tizen Map
    - Implementation of GeocoderBackend

TASK=TCAPI2044

Change-Id: I4b6f15b5190767ca0c92bb27c383af5f66ae5328

Xamarin.Forms.Maps.Tizen/FormsMaps.cs
Xamarin.Forms.Maps.Tizen/GeocoderBackend.cs [changed mode: 0644->0755]
Xamarin.Forms.Maps.Tizen/MapControl.cs [deleted file]
Xamarin.Forms.Maps.Tizen/MapRenderer.cs
Xamarin.Forms.Maps.Tizen/Xamarin.Forms.Maps.Tizen.csproj [changed mode: 0644->0755]
Xamarin.Forms.Maps.Tizen/Xamarin.Forms.Maps.Tizen.project.json
Xamarin.Forms.Maps/Properties/AssemblyInfo.cs [changed mode: 0644->0755]

index b41b50d..0fd7029 100755 (executable)
@@ -1,19 +1,49 @@
-using Xamarin.Forms.Maps.Tizen;
+using System.Diagnostics;
+using Tizen.Maps;
+using Xamarin.Forms.Maps.Tizen;
 
 namespace Xamarin
 {
-    public static class FormsMaps
-    {
-        public static bool IsInitialized { get; private set; }
+       public static class FormsMaps
+       {
+               static MapService _mapService = null;
 
-        public static void Init()
-        {
-            if (IsInitialized)
-                return;
+               static string ProviderName { get; set; }
 
-            IsInitialized = true;
+               static string AuthenticationToken { get; set; }
 
-            GeocoderBackend.Register();
-        }
-    }
+               internal static bool IsInitialized { get; private set; }
+
+               internal static MapService MapService
+               {
+                       get
+                       {
+                               Debug.Assert(_mapService != null, "FormsMaps is not initialized");
+                               return _mapService;
+                       }
+               }
+
+               public static void Init(string provider, string authenticationToken)
+               {
+                       ProviderName = provider;
+                       AuthenticationToken = authenticationToken;
+                       Init();
+               }
+
+               internal static async void Init()
+               {
+                       if (IsInitialized)
+                               return;
+                       var requestResult = await MapService.RequestUserConsent(ProviderName);
+                       if (requestResult)
+                       {
+                               _mapService = new MapService(ProviderName, AuthenticationToken);
+                               if (_mapService != null)
+                               {
+                                       GeocoderBackend.Register();
+                                       IsInitialized = true;
+                               }
+                       }
+               }
+       }
 }
\ No newline at end of file
old mode 100644 (file)
new mode 100755 (executable)
index a18990d..dbf9397
@@ -1,25 +1,32 @@
 using System.Collections.Generic;
 using System.Threading.Tasks;
-using System;
 
 namespace Xamarin.Forms.Maps.Tizen
 {
-    public class Position {};
+       internal class GeocoderBackend
+       {
+               public static void Register()
+               {
+                       Geocoder.GetPositionsForAddressAsyncFunc = GetPositionsForAddressAsync;
+                       Geocoder.GetAddressesForPositionFuncAsync = GetAddressesForPositionAsync;
+               }
 
-    internal class GeocoderBackend
-    {
-        public static void Register()
-        {
-        }
+               public static async Task<IEnumerable<Position>> GetPositionsForAddressAsync(string address)
+               {
+                       var request = FormsMaps.MapService.CreateGeocodeRequest(address);
+                       var positions = new List<Position>();
+                       foreach (var result in await request.GetResponseAsync())
+                               positions.Add(new Position(result.Latitude, result.Longitude));
+                       return positions;
+               }
 
-        public static async Task<IEnumerable<Position>> GetPositionsForAddressAsync(string address)
-        {
-            return new Position[]{};
-        }
-
-        public static async Task<IEnumerable<string>> GetAddressesForPositionAsync(Position position)
-        {
-            return new String[]{"Not supported"};
-        }
-    }
+               public static async Task<IEnumerable<string>> GetAddressesForPositionAsync(Position position)
+               {
+                       var request = FormsMaps.MapService.CreateReverseGeocodeRequest(position.Latitude, position.Longitude);
+                       var addresses = new List<string>();
+                       foreach (var result in await request.GetResponseAsync())
+                               addresses.Add(result.Freetext);
+                       return addresses;
+               }
+       }
 }
\ No newline at end of file
diff --git a/Xamarin.Forms.Maps.Tizen/MapControl.cs b/Xamarin.Forms.Maps.Tizen/MapControl.cs
deleted file mode 100755 (executable)
index a10bb80..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-using TLabel = Xamarin.Forms.Platform.Tizen.Native.Label;
-
-namespace Xamarin.Forms.Maps.Tizen
-{
-    public class MapControl : TLabel
-    {
-        public MapControl(ElmSharp.EvasObject parent) : base(parent)
-        {
-            Text = "Can not supported Maps";
-            TextColor = ElmSharp.Color.Red;
-        }
-    }
-}
-
index b7278dc..d61f6a2 100755 (executable)
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using ElmSharp;
+using Tizen.Location;
+using Tizen.Maps;
 using Xamarin.Forms.Platform.Tizen;
-using TForms = Xamarin.Forms.Platform.Tizen.Forms;
 
 namespace Xamarin.Forms.Maps.Tizen
 {
-    public class MapRenderer : ViewRenderer<Map, MapControl>
-    {
-        public MapRenderer()
-        {
-            RegisterPropertyHandler(Map.MapTypeProperty, UpdateMapType);
-            RegisterPropertyHandler(Map.IsShowingUserProperty, UpdateIsShowingUser);
-            RegisterPropertyHandler(Map.HasScrollEnabledProperty, UpdateHasScrollEnabled);
-            RegisterPropertyHandler(Map.HasZoomEnabledProperty, UpdateHasZoomEnabled);
-        }
-
-        protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
-        {
-            base.OnElementChanged(e);
-
-            if (Control == null)
-            {
-                var mapControl = new MapControl(TForms.Context.MainWindow);
-                SetNativeControl(mapControl);
-            }
-        }
-
-        void UpdateMapType()
-        {
-            // TODO
-        }
-
-        void UpdateIsShowingUser()
-        {
-            // TODO
-        }
-
-        void UpdateHasScrollEnabled()
-        {
-            // TODO
-        }
-
-        void UpdateHasZoomEnabled()
-        {
-            // TODO
-        }
-    }
+       public class MapRenderer : ViewRenderer<Map, MapView>
+       {
+               const string MoveMessageName = "MapMoveToRegion";
+
+               bool _disposed;
+               Overlay _marker;
+               bool _isLocatorStarted = false;
+               Lazy<Locator> _locator = new Lazy<Locator>(InitializeLocator);
+               Dictionary<Pin, MapObject> _pins = new Dictionary<Pin, MapObject>();
+
+               static Locator InitializeLocator()
+               {
+                       var locator = new Locator(LocationType.Hybrid)
+                       {
+                               // Set the default interval to 15s same as UWP
+                               Interval = 15
+                       };
+                       return locator;
+               }
+
+               public MapRenderer()
+               {
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
+               {
+                       if (Control == null)
+                       {
+                               var mapControl = new MapView(Platform.Tizen.Forms.Context.MainWindow, FormsMaps.MapService);
+                               SetNativeControl(mapControl);
+                       }
+
+                       if (e.OldElement != null)
+                       {
+                               ((ObservableCollection<Pin>)e.OldElement.Pins).CollectionChanged -= OnCollectionChanged;
+
+                               MessagingCenter.Unsubscribe<Map, MapSpan>(this, MoveMessageName);
+                       }
+                       if (e.NewElement != null)
+                       {
+                               ((ObservableCollection<Pin>)e.NewElement.Pins).CollectionChanged += OnCollectionChanged;
+                               if (e.NewElement.Pins.Count > 0)
+                               {
+                                       AddPins(e.NewElement.Pins);
+                               }
+
+                               MessagingCenter.Subscribe<Map, MapSpan>(this, MoveMessageName, OnMoveToRegion, e.NewElement);
+
+                               UpdateMapType();
+                               UpdateHasScrollEnabled();
+                               UpdateHasZoomEnabled();
+                               UpdateIsShowingUser();
+                       }
+                       base.OnElementChanged(e);
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (_disposed)
+                       {
+                               return;
+                       }
+
+                       _disposed = true;
+
+                       if (disposing)
+                       {
+                               MessagingCenter.Unsubscribe<Map, MapSpan>(this, "MapMoveToRegion");
+                               if (Element != null)
+                               {
+                                       ((ObservableCollection<Pin>)Element.Pins).CollectionChanged -= OnCollectionChanged;
+                               }
+                               Control.Unrealize();
+                       }
+                       base.Dispose(disposing);
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == Map.MapTypeProperty.PropertyName)
+                               UpdateMapType();
+                       else if (e.PropertyName == Map.IsShowingUserProperty.PropertyName)
+                               UpdateIsShowingUser();
+                       else if (e.PropertyName == Map.HasScrollEnabledProperty.PropertyName)
+                               UpdateHasScrollEnabled();
+                       else if (e.PropertyName == Map.HasZoomEnabledProperty.PropertyName)
+                               UpdateHasZoomEnabled();
+               }
+
+               void OnMoveToRegion(Map map, MapSpan span)
+               {
+                       var newCenter = new Geocoordinates(span.Center.Latitude, span.Center.Longitude);
+                       Control.Center = newCenter;
+               }
+
+
+               void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       if (e.NewItems != null)
+                       {
+                               AddPins(e.NewItems);
+                       }
+                       if (e.OldItems != null)
+                       {
+                               RemovePins(e.OldItems);
+                       }
+                       if (e.Action == NotifyCollectionChangedAction.Reset)
+                       {
+                               ClearPins();
+                       }
+               }
+
+               void AddPins(IEnumerable pins)
+               {
+                       foreach (Pin pin in pins)
+                       {
+                               var coordinates = new Geocoordinates(pin.Position.Latitude, pin.Position.Longitude);
+                               var rectangle = new Background(Platform.Tizen.Forms.Context.MainWindow);
+                               // TODO: Need to change BubbleOverlay to default Marker
+                               // TODO: Need to handle Pin.Clicked event
+                               var nativePin = new BubbleOverlay(coordinates, rectangle);
+                               Control.Add(nativePin);
+                               _pins.Add(pin, nativePin);
+                       }
+               }
+
+               void RemovePins(IEnumerable pins)
+               {
+                       foreach (Pin pin in pins)
+                       {
+                               if (_pins.ContainsKey(pin))
+                               {
+                                       Control.Remove(_pins[pin]);
+                                       _pins.Remove(pin);
+                               }
+                       }
+               }
+
+               void ClearPins()
+               {
+                       foreach (var pin in _pins)
+                       {
+                               Control.Remove(pin.Value);
+                       }
+                       _pins.Clear();
+               }
+
+               void UpdateHasZoomEnabled()
+               {
+                       if (Element.HasZoomEnabled == true)
+                               Control.ZoomChanged += Dummy;
+                       else
+                               Control.ZoomChanged -= Dummy;
+               }
+
+               void UpdateHasScrollEnabled()
+               {
+                       if (Element.HasScrollEnabled == true)
+                               Control.Scrolled += Dummy;
+                       else
+                               Control.Scrolled -= Dummy;
+               }
+
+               void Dummy(object sender, MapGestureEventArgs e)
+               {
+                       //TODO: The implementation of Tizen.Maps needs to be changed to remove this method
+               }
+
+               void ApplyIsShowingUser(Geocoordinates coordinates)
+               {
+                       if (_marker == null)
+                       {
+                               var rectangle = new Background(Platform.Tizen.Forms.Context.MainWindow);
+                               // TODO: Need to change BubbleOverlay to Default Overlay
+                               _marker = new BubbleOverlay(coordinates, rectangle);
+                               _marker.IsVisible = false;
+                               Control.Add(_marker);
+                       }
+                       _marker.Coordinates = coordinates;
+
+                       if (!_marker.IsVisible)
+                       {
+                               _marker.IsVisible = true;
+                               Control.Center = coordinates;
+                               Control.ZoomLevel = 13;
+                       }
+               }
+               void UpdateIsShowingUser()
+               {
+                       if (Element.IsShowingUser)
+                       {
+                               _locator.Value.LocationChanged += OnLocationChanged;
+                               if (!_isLocatorStarted)
+                               {
+                                       _locator.Value.Start();
+                                       _isLocatorStarted = true;
+                               }
+                       }
+                       else
+                       {
+                               if (_locator.IsValueCreated)
+                               {
+                                       _locator.Value.LocationChanged -= OnLocationChanged;
+                                       _locator.Value.Stop();
+                                       _isLocatorStarted = false;
+                               }
+                               if (_marker != null)
+                                       _marker.IsVisible = false;
+                       }
+               }
+
+               void OnLocationChanged(object sender, LocationChangedEventArgs e)
+               {
+                       ApplyIsShowingUser(new Geocoordinates(e.Location.Latitude, e.Location.Longitude));
+               }
+
+               void UpdateMapType()
+               {
+                       switch (Element.MapType)
+                       {
+                               case MapType.Street:
+                                       Control.MapType = MapTypes.Normal;
+                                       break;
+                               case MapType.Satellite:
+                                       Control.MapType = MapTypes.Satellite;
+                                       break;
+                               case MapType.Hybrid:
+                                       Control.MapType = MapTypes.Hybrid;
+                                       break;
+                               default:
+                                       throw new ArgumentOutOfRangeException();
+                       }
+               }
+       }
 }
old mode 100644 (file)
new mode 100755 (executable)
index 9b16376..98733ee
@@ -40,7 +40,6 @@
     <Compile Include="FormsMaps.cs" />
     <Compile Include="GeocoderBackend.cs" />
     <Compile Include="MapRenderer.cs" />
-    <Compile Include="MapControl.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="Xamarin.Forms.Maps.Tizen.project.json" />
index f4511fe..c381ec6 100755 (executable)
@@ -2,11 +2,13 @@
   "dependencies": {
     "ElmSharp": "1.1.0-*",
     "NETStandard.Library": "1.6.0",
-    "Tizen.Applications": "1.0.2"
+    "Tizen.Applications": "1.0.2",
+    "Tizen.Location": "1.0.3",
+    "Tizen.Maps": "1.0.1"
   },
   "frameworks": {
     "netstandard1.6": {
       "imports": "portable-net45+win8+wpa81+wp8"
     }
   }
-}
+}
\ No newline at end of file
old mode 100644 (file)
new mode 100755 (executable)
index 1e3c2f3..48d3c84
@@ -17,6 +17,7 @@ using Xamarin.Forms.Internals;
 [assembly: InternalsVisibleTo("Xamarin.Forms.Maps.iOS")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Maps.iOS.Classic")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Maps.Android")]
+[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.Tizen")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Maps.WP8")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Maps.UWP")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Maps.WinRT.Phone")]