diff options
Diffstat (limited to 'Xamarin.Forms.Maps.Android')
-rw-r--r-- | Xamarin.Forms.Maps.Android/FormsMaps.cs | 43 | ||||
-rw-r--r-- | Xamarin.Forms.Maps.Android/GeocoderBackend.cs | 36 | ||||
-rw-r--r-- | Xamarin.Forms.Maps.Android/MapRenderer.cs | 308 | ||||
-rw-r--r-- | Xamarin.Forms.Maps.Android/Properties/AssemblyInfo.cs | 23 | ||||
-rw-r--r-- | Xamarin.Forms.Maps.Android/Xamarin.Forms.Maps.Android.csproj | 121 | ||||
-rw-r--r-- | Xamarin.Forms.Maps.Android/packages.config | 9 |
6 files changed, 540 insertions, 0 deletions
diff --git a/Xamarin.Forms.Maps.Android/FormsMaps.cs b/Xamarin.Forms.Maps.Android/FormsMaps.cs new file mode 100644 index 00000000..76fc48d4 --- /dev/null +++ b/Xamarin.Forms.Maps.Android/FormsMaps.cs @@ -0,0 +1,43 @@ +using System; +using Android.App; +using Android.Content; +using Android.Gms.Common; +using Android.Gms.Maps; +using Android.OS; +using Xamarin.Forms.Maps.Android; + +namespace Xamarin +{ + public static class FormsMaps + { + public static bool IsInitialized { get; private set; } + + public static Context Context { get; private set; } + + public static void Init(Activity activity, Bundle bundle) + { + if (IsInitialized) + return; + IsInitialized = true; + + Context = activity; + + MapRenderer.Bundle = bundle; + + if (GooglePlayServicesUtil.IsGooglePlayServicesAvailable(Context) == ConnectionResult.Success) + { + try + { + MapsInitializer.Initialize(Context); + } + catch (Exception e) + { + Console.WriteLine("Google Play Services Not Found"); + Console.WriteLine("Exception: {0}", e); + } + } + + GeocoderBackend.Register(Context); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Maps.Android/GeocoderBackend.cs b/Xamarin.Forms.Maps.Android/GeocoderBackend.cs new file mode 100644 index 00000000..34a08b0a --- /dev/null +++ b/Xamarin.Forms.Maps.Android/GeocoderBackend.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Android.Content; +using Android.Locations; +using AGeocoder = Android.Locations.Geocoder; + +namespace Xamarin.Forms.Maps.Android +{ + internal class GeocoderBackend + { + public static void Register(Context context) + { + Geocoder.GetPositionsForAddressAsyncFunc = GetPositionsForAddressAsync; + Geocoder.GetAddressesForPositionFuncAsync = GetAddressesForPositionAsync; + } + + public static async Task<IEnumerable<Position>> GetPositionsForAddressAsync(string address) + { + var geocoder = new AGeocoder(Forms.Context); + IList<Address> addresses = await geocoder.GetFromLocationNameAsync(address, 5); + return addresses.Select(p => new Position(p.Latitude, p.Longitude)); + } + + public static async Task<IEnumerable<string>> GetAddressesForPositionAsync(Position position) + { + var geocoder = new AGeocoder(Forms.Context); + IList<Address> addresses = await geocoder.GetFromLocationAsync(position.Latitude, position.Longitude, 5); + return addresses.Select(p => + { + IEnumerable<string> lines = Enumerable.Range(0, p.MaxAddressLineIndex + 1).Select(p.GetAddressLine); + return string.Join("\n", lines); + }); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Maps.Android/MapRenderer.cs b/Xamarin.Forms.Maps.Android/MapRenderer.cs new file mode 100644 index 00000000..20cb1229 --- /dev/null +++ b/Xamarin.Forms.Maps.Android/MapRenderer.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using Android.Gms.Maps; +using Android.Gms.Maps.Model; +using Android.OS; +using Java.Lang; +using Xamarin.Forms.Platform.Android; +using Math = System.Math; +using Android.Runtime; +using System.Collections; + +namespace Xamarin.Forms.Maps.Android +{ + public class MapRenderer : ViewRenderer, + GoogleMap.IOnCameraChangeListener + { + public MapRenderer () + { + AutoPackage = false; + } + + static Bundle s_bundle; + internal static Bundle Bundle { set { s_bundle = value; } } + + List<Marker> _markers; + + const string MoveMessageName = "MapMoveToRegion"; + + protected GoogleMap NativeMap => ((MapView) Control).Map; + + protected Map Map => (Map) Element; + + public override SizeRequest GetDesiredSize (int widthConstraint, int heightConstraint) + { + return new SizeRequest (new Size (Context.ToPixels (40), Context.ToPixels (40))); + } + + protected override void OnElementChanged (ElementChangedEventArgs<View> e) + { + base.OnElementChanged (e); + + var oldMapView = (MapView)Control; + + var mapView = new MapView (Context); + mapView.OnCreate (s_bundle); + mapView.OnResume (); + SetNativeControl (mapView); + + if (e.OldElement != null) { + var oldMapModel = (Map) e.OldElement; + ((ObservableCollection<Pin>)oldMapModel.Pins).CollectionChanged -= OnCollectionChanged; + + MessagingCenter.Unsubscribe<Map, MapSpan> (this, MoveMessageName); + + if (oldMapView.Map != null) { + oldMapView.Map.SetOnCameraChangeListener (null); + NativeMap.InfoWindowClick -= MapOnMarkerClick; + } + + oldMapView.Dispose(); + } + + var map = NativeMap; + if (map != null) { + map.SetOnCameraChangeListener (this); + NativeMap.InfoWindowClick += MapOnMarkerClick; + + map.UiSettings.ZoomControlsEnabled = Map.HasZoomEnabled; + map.UiSettings.ZoomGesturesEnabled = Map.HasZoomEnabled; + map.UiSettings.ScrollGesturesEnabled = Map.HasScrollEnabled; + map.MyLocationEnabled = map.UiSettings.MyLocationButtonEnabled = Map.IsShowingUser; + SetMapType (); + } + + MessagingCenter.Subscribe<Map, MapSpan> (this, MoveMessageName, OnMoveToRegionMessage, Map); + + var incc = Map.Pins as INotifyCollectionChanged; + if (incc != null) + incc.CollectionChanged += OnCollectionChanged; + } + + void OnCollectionChanged (object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) + { + switch (notifyCollectionChangedEventArgs.Action) { + case NotifyCollectionChangedAction.Add: + AddPins (notifyCollectionChangedEventArgs.NewItems); + break; + case NotifyCollectionChangedAction.Remove: + RemovePins (notifyCollectionChangedEventArgs.OldItems); + break; + case NotifyCollectionChangedAction.Replace: + RemovePins (notifyCollectionChangedEventArgs.OldItems); + AddPins (notifyCollectionChangedEventArgs.NewItems); + break; + case NotifyCollectionChangedAction.Reset: + _markers?.ForEach (m => m.Remove ()); + _markers = null; + AddPins ((IList)(Element as Map).Pins); + break; + case NotifyCollectionChangedAction.Move: + //do nothing + break; + } + } + + void OnMoveToRegionMessage (Map s, MapSpan a) + { + MoveToRegion (a, true); + } + + void MoveToRegion (MapSpan span, bool animate) + { + var map = NativeMap; + if (map == null) + return; + + span = span.ClampLatitude (85, -85); + var ne = new LatLng (span.Center.Latitude + span.LatitudeDegrees / 2, span.Center.Longitude + span.LongitudeDegrees / 2); + var sw = new LatLng (span.Center.Latitude - span.LatitudeDegrees / 2, span.Center.Longitude - span.LongitudeDegrees / 2); + var update = CameraUpdateFactory.NewLatLngBounds (new LatLngBounds (sw, ne), 0); + + try { + if (animate) + map.AnimateCamera (update); + else + map.MoveCamera (update); + } catch (IllegalStateException exc) { + System.Diagnostics.Debug.WriteLine ("MoveToRegion exception: " + exc); + } + } + + bool _init = true; + + protected override void OnLayout (bool changed, int l, int t, int r, int b) + { + base.OnLayout (changed, l, t, r, b); + + if (_init) { + MoveToRegion (((Map)Element).LastMoveToRegion, false); + OnCollectionChanged (((Map)Element).Pins, new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset)); + _init = false; + } else if (changed) { + UpdateVisibleRegion (NativeMap.CameraPosition.Target); + } + } + + protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged (sender, e); + + if (e.PropertyName == Map.MapTypeProperty.PropertyName) { + SetMapType(); + return; + } + + var gmap = NativeMap; + if (gmap == null) + return; + + if (e.PropertyName == Map.IsShowingUserProperty.PropertyName) + gmap.MyLocationEnabled = gmap.UiSettings.MyLocationButtonEnabled = Map.IsShowingUser; + else if (e.PropertyName == Map.HasScrollEnabledProperty.PropertyName) + gmap.UiSettings.ScrollGesturesEnabled = Map.HasScrollEnabled; + else if (e.PropertyName == Map.HasZoomEnabledProperty.PropertyName) { + gmap.UiSettings.ZoomControlsEnabled = Map.HasZoomEnabled; + gmap.UiSettings.ZoomGesturesEnabled = Map.HasZoomEnabled; + } + } + + void SetMapType () + { + var map = NativeMap; + if (map == null) + return; + + switch (Map.MapType) { + case MapType.Street: + map.MapType = GoogleMap.MapTypeNormal; + break; + case MapType.Satellite: + map.MapType = GoogleMap.MapTypeSatellite; + break; + case MapType.Hybrid: + map.MapType = GoogleMap.MapTypeHybrid; + break; + default: + throw new ArgumentOutOfRangeException (); + } + } + + public void OnCameraChange (CameraPosition pos) + { + UpdateVisibleRegion (pos.Target); + } + + void UpdateVisibleRegion (LatLng pos) + { + var map = NativeMap; + if (map == null) + return; + var projection = map.Projection; + var width = Control.Width; + var height = Control.Height; + var ul = projection.FromScreenLocation (new global::Android.Graphics.Point (0, 0)); + var ur = projection.FromScreenLocation (new global::Android.Graphics.Point (width, 0)); + var ll = projection.FromScreenLocation (new global::Android.Graphics.Point (0, height)); + var lr = projection.FromScreenLocation (new global::Android.Graphics.Point (width, height)); + var dlat = Math.Max (Math.Abs (ul.Latitude - lr.Latitude), Math.Abs (ur.Latitude - ll.Latitude)); + var dlong = Math.Max (Math.Abs (ul.Longitude - lr.Longitude), Math.Abs (ur.Longitude - ll.Longitude)); + ((Map)Element).VisibleRegion = new MapSpan ( + new Position ( + pos.Latitude, + pos.Longitude + ), + dlat, + dlong + ); + } + + void AddPins (IList pins) + { + var map = NativeMap; + if (map == null) + return; + + if (_markers == null) + _markers = new List<Marker> (); + + _markers.AddRange( pins.Cast<Pin>().Select (p => { + var pin = (Pin)p; + var opts = new MarkerOptions (); + opts.SetPosition (new LatLng (pin.Position.Latitude, pin.Position.Longitude)); + opts.SetTitle (pin.Label); + opts.SetSnippet (pin.Address); + var marker = map.AddMarker (opts); + + // associate pin with marker for later lookup in event handlers + pin.Id = marker.Id; + return marker; + })); + } + + void RemovePins (IList pins) + { + var map = NativeMap; + if (map == null) + return; + if (_markers == null) + return; + + foreach (Pin p in pins) { + var marker = _markers.FirstOrDefault (m => m.Id == p.Id); + if (marker == null) + continue; + marker.Remove (); + _markers.Remove (marker); + } + } + + void MapOnMarkerClick (object sender, GoogleMap.InfoWindowClickEventArgs eventArgs) + { + // clicked marker + var marker = eventArgs.Marker; + + // lookup pin + Pin targetPin = null; + for (var i = 0; i < Map.Pins.Count; i++) { + var pin = Map.Pins[i]; + if ((string)pin.Id != marker.Id) + continue; + + targetPin = pin; + break; + } + + // only consider event handled if a handler is present. + // Else allow default behavior of displaying an info window. + targetPin?.SendTap (); + } + + bool _disposed; + protected override void Dispose (bool disposing) + { + if (disposing && !_disposed) { + _disposed = true; + + var mapModel = Element as Map; + if (mapModel != null) { + MessagingCenter.Unsubscribe<Map, MapSpan> (this, MoveMessageName); + ((ObservableCollection<Pin>)mapModel.Pins).CollectionChanged -= OnCollectionChanged; + } + + var gmap = NativeMap; + if (gmap == null) + return; + gmap.MyLocationEnabled = false; + gmap.InfoWindowClick -= MapOnMarkerClick; + gmap.Dispose (); + } + + base.Dispose (disposing); + } + } +} diff --git a/Xamarin.Forms.Maps.Android/Properties/AssemblyInfo.cs b/Xamarin.Forms.Maps.Android/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..efdb17b1 --- /dev/null +++ b/Xamarin.Forms.Maps.Android/Properties/AssemblyInfo.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using System.Runtime.InteropServices; +using Android; +using Android.App; +using Xamarin.Forms; +using Xamarin.Forms.Maps; +using Xamarin.Forms.Maps.Android; +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +[assembly: AssemblyTitle("Xamarin.Forms.Maps.Android")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] + +// Add some common permissions, these can be removed if not needed + +[assembly: UsesPermission(Manifest.Permission.Internet)] +[assembly: UsesPermission(Manifest.Permission.WriteExternalStorage)] +[assembly: ExportRenderer(typeof (Map), typeof (MapRenderer))] +[assembly: Preserve]
\ No newline at end of file diff --git a/Xamarin.Forms.Maps.Android/Xamarin.Forms.Maps.Android.csproj b/Xamarin.Forms.Maps.Android/Xamarin.Forms.Maps.Android.csproj new file mode 100644 index 00000000..293d9221 --- /dev/null +++ b/Xamarin.Forms.Maps.Android/Xamarin.Forms.Maps.Android.csproj @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>8.0.30703</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{BD50B39A-EBC5-408F-9C5E-923A8EBAE473}</ProjectGuid> + <ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Xamarin.Forms.Maps.Android</RootNamespace> + <AssemblyName>Xamarin.Forms.Maps.Android</AssemblyName> + <FileAlignment>512</FileAlignment> + <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile> + <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies> + <AndroidUseLatestPlatformSdk>true</AndroidUseLatestPlatformSdk> + <TargetFrameworkVersion>v6.0</TargetFrameworkVersion> + <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> + <RestorePackages>true</RestorePackages> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Turkey|AnyCPU'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\Turkey\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>AnyCPU</PlatformTarget> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <WarningLevel>4</WarningLevel> + <Optimize>false</Optimize> + </PropertyGroup> + <ItemGroup> + <Reference Include="Mono.Android" /> + <Reference Include="mscorlib" /> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Xml" /> + <Reference Include="Xamarin.Android.Support.v4, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Xamarin.Android.Support.v4.23.1.1.1\lib\MonoAndroid403\Xamarin.Android.Support.v4.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Xamarin.Android.Support.v7.AppCompat, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Xamarin.Android.Support.v7.AppCompat.23.1.1.1\lib\MonoAndroid403\Xamarin.Android.Support.v7.AppCompat.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Xamarin.Android.Support.v7.MediaRouter, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Xamarin.Android.Support.v7.MediaRouter.23.1.1.1\lib\MonoAndroid403\Xamarin.Android.Support.v7.MediaRouter.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Xamarin.GooglePlayServices.Base, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Xamarin.GooglePlayServices.Base.29.0.0.1\lib\MonoAndroid41\Xamarin.GooglePlayServices.Base.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Xamarin.GooglePlayServices.Basement, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Xamarin.GooglePlayServices.Basement.29.0.0.1\lib\MonoAndroid41\Xamarin.GooglePlayServices.Basement.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Xamarin.GooglePlayServices.Maps, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Xamarin.GooglePlayServices.Maps.29.0.0.1\lib\MonoAndroid41\Xamarin.GooglePlayServices.Maps.dll</HintPath> + <Private>True</Private> + </Reference> + </ItemGroup> + <ItemGroup> + <Compile Include="..\Xamarin.Forms.Core\Properties\GlobalAssemblyInfo.cs"> + <Link>Properties\GlobalAssemblyInfo.cs</Link> + </Compile> + <Compile Include="MapRenderer.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="FormsMaps.cs" /> + <Compile Include="GeocoderBackend.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj"> + <Project>{57b8b73d-c3b5-4c42-869e-7b2f17d354ac}</Project> + <Name>Xamarin.Forms.Core</Name> + </ProjectReference> + <ProjectReference Include="..\Xamarin.Forms.Maps\Xamarin.Forms.Maps.csproj"> + <Project>{7d13bac2-c6a4-416a-b07e-c169b199e52b}</Project> + <Name>Xamarin.Forms.Maps</Name> + </ProjectReference> + <ProjectReference Include="..\Xamarin.Forms.Platform.Android.FormsViewGroup\Xamarin.Forms.Platform.Android.FormsViewGroup.csproj"> + <Project>{3b72465b-acae-43ae-9327-10f372fe5f80}</Project> + <Name>Xamarin.Forms.Platform.Android.FormsViewGroup</Name> + </ProjectReference> + <ProjectReference Include="..\Xamarin.Forms.Platform.Android\Xamarin.Forms.Platform.Android.csproj"> + <Project>{0e16e70a-d6dd-4323-ad5d-363abff42d6a}</Project> + <Name>Xamarin.Forms.Platform.Android</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> + <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file diff --git a/Xamarin.Forms.Maps.Android/packages.config b/Xamarin.Forms.Maps.Android/packages.config new file mode 100644 index 00000000..3873ad93 --- /dev/null +++ b/Xamarin.Forms.Maps.Android/packages.config @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Xamarin.Android.Support.v4" version="23.1.1.1" targetFramework="monoandroid60" /> + <package id="Xamarin.Android.Support.v7.AppCompat" version="23.1.1.1" targetFramework="monoandroid60" /> + <package id="Xamarin.Android.Support.v7.MediaRouter" version="23.1.1.1" targetFramework="monoandroid60" /> + <package id="Xamarin.GooglePlayServices.Base" version="29.0.0.1" targetFramework="monoandroid60" /> + <package id="Xamarin.GooglePlayServices.Basement" version="29.0.0.1" targetFramework="monoandroid60" /> + <package id="Xamarin.GooglePlayServices.Maps" version="29.0.0.1" targetFramework="monoandroid60" /> +</packages>
\ No newline at end of file |