diff --git a/CapacitorCommunityCapacitorGooglemapsNative.podspec b/CapacitorCommunityCapacitorGooglemapsNative.podspec
index bc16cd78..e55853ae 100644
--- a/CapacitorCommunityCapacitorGooglemapsNative.podspec
+++ b/CapacitorCommunityCapacitorGooglemapsNative.podspec
@@ -14,5 +14,6 @@ Pod::Spec.new do |s|
s.ios.deployment_target = '12.0'
s.dependency 'Capacitor'
s.dependency 'GoogleMaps'
+ s.dependency 'Google-Maps-iOS-Utils'
s.static_framework = true
end
diff --git a/README.md b/README.md
index 6ce2bf0c..47504973 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
-
+
@@ -30,20 +30,6 @@ Maps SDK for Android & iOS bring better performance and offline caching compared
| -----------| -------| -------|
| Hemang Kumar | [hemangsk](https://github.com/hemangsk) | hemangsk@gmail.com |
-## Support Development
-If you like this plugin and use it on your projects, please consider donating to support the development. Thank you!
-
-
-
-
-
- |
-
-
- |
-
-
-
## Project Status
| Features | Android | iOS | Current Status | Pending |
@@ -80,7 +66,8 @@ You'll have two API keys by the end of this step. Lets proceed:
### Add API key to your App
-- [Android](https://developers.google.com/maps/documentation/android-sdk/get-api-key) in AndroidManifest.xml:
+#### Android
+[Android](https://developers.google.com/maps/documentation/android-sdk/get-api-key) in AndroidManifest.xml:
```
...
@@ -91,6 +78,23 @@ You'll have two API keys by the end of this step. Lets proceed:
...
```
+As of [Capacitor 3](https://capacitorjs.com/docs/updating/3-0), the plugin needs to be registered in MainActivity.java:
+```java
+import com.hemangkumar.capacitorgooglemaps.CapacitorGoogleMaps;
+//...
+
+public class MainActivity extends BridgeActivity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // ...
+ registerPlugin(CapacitorGoogleMaps.class);
+ }
+}
+
+```
+#### iOS
- On iOS, this step is little different and mentioned below.
### Importing & Initializing the plugin
@@ -112,13 +116,13 @@ await CapacitorGoogleMaps.initialize({
`component.html`
-```
+```html
```
`component.css`
-```
+```css
#map {
margin: 2em 1em;
height: 250px;
@@ -196,6 +200,12 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
![](https://avatars.githubusercontent.com/u/28204537?v=4?s=100) selected-pixel-jameson π |
![](https://avatars.githubusercontent.com/u/12167528?v=4?s=100) chikalio π |
![](https://avatars.githubusercontent.com/u/1047598?v=4?s=100) Javier Gonzalez π» |
+ ![](https://avatars.githubusercontent.com/u/37834723?v=4?s=100) Shane B. π |
+
+
+ ![](https://avatars.githubusercontent.com/u/11479696?v=4?s=100) Manuel RodrΓguez π» |
+ ![](https://avatars.githubusercontent.com/u/20730765?v=4?s=100) Jamilu Salisu π» |
+ ![](https://avatars.githubusercontent.com/u/7632849?v=4?s=100) rastafan π» |
diff --git a/android/build.gradle b/android/build.gradle
index d8457f83..82e3e850 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,8 +1,4 @@
-ext {
- junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.12'
- androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.1'
- androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.2.0'
-}
+
buildscript {
repositories {
@@ -11,11 +7,18 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
+ classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.0"
}
}
apply plugin: 'com.android.library'
+ext {
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.12'
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.1'
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.2.0'
+}
+
android {
compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 29
defaultConfig {
@@ -24,16 +27,27 @@ android {
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+ manifestPlaceholders = [ MAPS_API_KEY:"debugKey" ]
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ manifestPlaceholders = [ MAPS_API_KEY:"prodKey" ]
}
}
lintOptions {
abortOnError false
}
+ sourceSets {
+ main {
+ assets {
+ srcDirs 'src/main/assets', 'src/main/marker-categories-assets', 'src/main/assets/marker-categories'
+ }
+ }
+ }
}
repositories {
@@ -49,5 +63,11 @@ dependencies {
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
- implementation 'com.google.android.libraries.maps:maps:3.1.0-beta'
+
+ implementation 'com.google.maps.android:android-maps-utils:2.3.0'
+ implementation 'com.google.android.gms:play-services-maps:18.0.0'
+ implementation 'com.google.android.gms:play-services-location:16.0.0'
+ implementation("androidx.appcompat:appcompat:1.3.0")
+
}
+
diff --git a/android/gradle.properties b/android/gradle.properties
index 2dbcb071..123de31e 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -18,4 +18,4 @@ org.gradle.jvmargs=-Xmx1536m
# Supports AndroidX
android.useAndroidX=true
-android.enableJetifier=true
\ No newline at end of file
+android.enableJetifier=true
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index a160a15d..928381e5 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -1,5 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/src/main/assets/marker-categories/category1.png b/android/src/main/assets/marker-categories/category1.png
new file mode 100644
index 00000000..7a1f0d8c
Binary files /dev/null and b/android/src/main/assets/marker-categories/category1.png differ
diff --git a/android/src/main/assets/marker-categories/category2.png b/android/src/main/assets/marker-categories/category2.png
new file mode 100644
index 00000000..6766828e
Binary files /dev/null and b/android/src/main/assets/marker-categories/category2.png differ
diff --git a/android/src/main/assets/marker-categories/category3.png b/android/src/main/assets/marker-categories/category3.png
new file mode 100644
index 00000000..6277afb2
Binary files /dev/null and b/android/src/main/assets/marker-categories/category3.png differ
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/CapacitorGoogleMaps.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/CapacitorGoogleMaps.java
index d4b38c69..73097c43 100644
--- a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/CapacitorGoogleMaps.java
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/CapacitorGoogleMaps.java
@@ -4,793 +4,929 @@
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
-import android.content.pm.PackageManager;
-import android.location.Address;
-import android.location.Geocoder;
-import android.location.Location;
-import android.util.Log;
+import android.content.IntentSender;
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.util.Base64;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.FrameLayout;
import androidx.annotation.NonNull;
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
-import com.getcapacitor.JSArray;
import com.getcapacitor.JSObject;
-import com.getcapacitor.NativePlugin;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
import com.getcapacitor.annotation.Permission;
-import com.google.android.libraries.maps.CameraUpdateFactory;
-import com.google.android.libraries.maps.GoogleMap;
-import com.google.android.libraries.maps.GoogleMapOptions;
-import com.google.android.libraries.maps.MapView;
-import com.google.android.libraries.maps.OnMapReadyCallback;
-import com.google.android.libraries.maps.UiSettings;
-import com.google.android.libraries.maps.model.CameraPosition;
-import com.google.android.libraries.maps.model.CircleOptions;
-import com.google.android.libraries.maps.model.LatLng;
-import com.google.android.libraries.maps.model.MapStyleOptions;
-import com.google.android.libraries.maps.model.Marker;
-import com.google.android.libraries.maps.model.MarkerOptions;
-import com.google.android.libraries.maps.model.PointOfInterest;
-import com.google.android.libraries.maps.model.PolygonOptions;
-import com.google.android.libraries.maps.model.PolylineOptions;
+import com.google.android.gms.common.api.ApiException;
+import com.google.android.gms.common.api.ResolvableApiException;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationServices;
+import com.google.android.gms.location.LocationSettingsRequest;
+import com.google.android.gms.location.LocationSettingsResponse;
+import com.google.android.gms.location.LocationSettingsStatusCodes;
+import com.google.android.gms.maps.model.CameraPosition;
+
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.Task;
+import com.hemangkumar.capacitorgooglemaps.marker.CustomMarker;
+import com.hemangkumar.capacitorgooglemaps.marker.MarkerCategory;
+import com.hemangkumar.capacitorgooglemaps.mapsutility.Events;
+import com.hemangkumar.capacitorgooglemaps.utils.BoundingRect;
+
+import org.json.JSONArray;
import org.json.JSONException;
-import org.json.JSONObject;
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
@CapacitorPlugin(
name = "CapacitorGoogleMaps",
permissions = {
@Permission(
- strings = { Manifest.permission.ACCESS_FINE_LOCATION },
+ strings = {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION },
alias = "geolocation"
),
@Permission(strings = { Manifest.permission.INTERNET }, alias = "internet"),
}
)
-public class CapacitorGoogleMaps extends Plugin implements OnMapReadyCallback, GoogleMap.OnMyLocationClickListener, GoogleMap.OnMyLocationButtonClickListener {
+public class CapacitorGoogleMaps extends Plugin implements CustomMapViewEvents {
- private MapView mapView;
- GoogleMap googleMap;
- Integer mapViewParentId;
- Integer DEFAULT_WIDTH = 500;
- Integer DEFAULT_HEIGHT = 500;
- Float DEFAULT_ZOOM = 12.0f;
- private HashMap mHashMap = new HashMap<>();
+ // -- constants --
+ private String MARKER_CATEGORY_DIRECTORY = "marker-categories";
+
+ // -- private fields --
+ private String GOOGLE_MAPS_KEY;
+
+
+ public static final HashMap customMapViews = new HashMap<>();
+ Float devicePixelRatio;
+
+ private String lastEventChainId;
+ public List previousEvents = new ArrayList<>();
+ private String delegateTouchEventsToMapId;
+
+
+ /**
+ * This method should be called after we requested the WebView through notifyListeners("didRequestElementFromPoint").
+ * It should tell us if the exact point that was being touched, is from an element in which a MapView exists.
+ * Otherwise it is a 'normal' HTML element, and we should thus not delegate touch events.
+ */
+ @PluginMethod()
+ public void elementFromPointResult(PluginCall call) {
+ String eventChainId = call.getString("eventChainId");
+ if (eventChainId != null && eventChainId.equals(lastEventChainId)) {
+ Boolean isSameNode = call.getBoolean("isSameNode", false);
+ if (isSameNode != null && isSameNode) {
+ // The WebView apparently has decide the touched point belongs to a certain MapView.
+ // Now we should find out which one exactly.
+ String mapId = call.getString("mapId");
+ if (mapId != null) {
+ delegateTouchEventsToMapId = mapId;
+ }
+ }
+ }
+ call.resolve();
+ }
+
+
+ @SuppressLint("ClickableViewAccessibility")
@Override
- public void onMapReady(GoogleMap googleMap) {
- this.googleMap = googleMap;
- notifyListeners("onMapReady", null);
+ public void load() {
+ super.load();
+
+ initMarkerCategories();
+
+ this.getBridge().getWebView().setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ int touchType = event.getActionMasked();
+
+ if (touchType == MotionEvent.ACTION_DOWN) {
+ // Throw away all previous state when starting a new touch gesture.
+ // The framework may have dropped the up or cancel event for the previous gesture
+ // due to an app switch, ANR, or some other state change.
+ delegateTouchEventsToMapId = null;
+ previousEvents.clear();
+
+ // Initialize JSObjects for resolve().
+ JSObject result = new JSObject();
+ JSObject point = new JSObject();
+
+ // Generate a UUID, and assign it to lastId.
+ // This way we can make sure we are always referencing the last chain of events.
+ lastEventChainId = UUID.randomUUID().toString();
+ // Then add it to result object, so the WebView can reference the correct events when needed.
+ result.put("eventChainId", lastEventChainId);
+
+ // Get the touched position.
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+
+ // Since pixels on a webpage are calculated differently, should convert them first.
+ // Convert it to 'real' pixels in WebView by using devicePixelRatio.
+ if (devicePixelRatio != null && devicePixelRatio > 0) {
+ point.put("x", x / devicePixelRatio);
+ point.put("y", y / devicePixelRatio);
+
+ // Then add it to result object.
+ result.put("point", point);
+ }
+
+ // Then notify the listener that we request to let the WebView determine
+ // if the element touched is the same node as where some MapView is attached to.
+ notifyListeners("didRequestElementFromPoint", result);
+ }
+
+ if (delegateTouchEventsToMapId != null) {
+ CustomMapView customMapView = customMapViews.get(delegateTouchEventsToMapId);
+ if (customMapView != null && !customMapView.isHidden()) {
+ // Apparently, all touch events should be delegated to a specific MapView.
+
+ // If previous events exist, we should execute those first
+ if (!previousEvents.isEmpty()) {
+ for (int i = 0; i < previousEvents.size(); i++) {
+ MotionEvent e = previousEvents.get(i);
+ if (e != null) {
+ // Delegate this previous event to the MapView.
+ dispatchTouchEvent(e, customMapView);
+ }
+ }
+ // Since we delegated all previous events, we can now forget about them.
+ previousEvents.clear();
+ }
+
+ // Finally delegate the current event to the MapView.
+ dispatchTouchEvent(event, customMapView);
+ }
+ } else {
+ // If delegateTouchEventsToMapId is not set, but it could still be set later!
+ // So we should save all past events.
+ // That way we can still execute them later on.
+ // It is important that we use MotionEvent.obtain() to copy the event first.
+ // Otherwise the event does not work properly when delegating it later on.
+ MotionEvent clonedEvent = MotionEvent.obtain(event);
+ previousEvents.add(clonedEvent);
+ }
+
+ return false;
+ }
+ });
+ }
+
+ /**
+ * dispatching event on selected MapView
+ */
+ private void dispatchTouchEvent(MotionEvent event, CustomMapView customMapView) {
+ Rect offsetViewBounds = new Rect();
+ // returns the visible bounds
+ customMapView.mapView.getDrawingRect(offsetViewBounds);
+ // calculates the relative coordinates to the parent
+ ViewGroup parentViewGroup = (ViewGroup) bridge.getWebView().getParent();
+ parentViewGroup.offsetDescendantRectToMyCoords(customMapView.mapView, offsetViewBounds);
+
+ int relativeTop = offsetViewBounds.top;
+ int relativeLeft = offsetViewBounds.left;
+
+ // Set location with offset points,
+ // because if a map is positioned with a different top and left point than the WebView,
+ // that should be accounted for.
+ event.setLocation(event.getX() - relativeLeft, event.getY() - relativeTop);
+
+ customMapView.mapView.dispatchTouchEvent(event);
}
@Override
protected void handleOnStart() {
super.handleOnStart();
- if (mapView != null) {
- mapView.onStart();
+ for (CustomMapView customMapView : customMapViews.values()) {
+ if (customMapView != null) {
+ customMapView.handleOnStart();
+ }
}
}
@Override
protected void handleOnResume() {
super.handleOnResume();
- if (mapView != null) {
- mapView.onResume();
+ for (CustomMapView customMapView : customMapViews.values()) {
+ if (customMapView != null) {
+ customMapView.handleOnResume();
+ }
}
}
@Override
- protected void handleOnStop() {
- super.handleOnStop();
- if (mapView != null) {
- mapView.onStop();
+ protected void handleOnPause() {
+ for (CustomMapView customMapView : customMapViews.values()) {
+ if (customMapView != null) {
+ customMapView.handleOnPause();
+ }
}
+ super.handleOnPause();
}
@Override
- protected void handleOnPause() {
- if (mapView != null) {
- mapView.onPause();
+ protected void handleOnStop() {
+ super.handleOnStop();
+ for (CustomMapView customMapView : customMapViews.values()) {
+ if (customMapView != null) {
+ customMapView.handleOnStop();
+ }
}
- super.handleOnPause();
}
@Override
protected void handleOnDestroy() {
- if (mapView != null) {
- mapView.onDestroy();
+ for (CustomMapView customMapView : customMapViews.values()) {
+ if (customMapView != null) {
+ customMapView.handleOnDestroy();
+ }
}
super.handleOnDestroy();
}
+
@PluginMethod()
public void initialize(PluginCall call) {
/*
* TODO: Check API key
*/
+ devicePixelRatio = call.getFloat("devicePixelRatio");
call.resolve();
}
@PluginMethod()
- public void create(PluginCall call) {
- final Integer width = call.getInt("width", DEFAULT_WIDTH);
- final Integer height = call.getInt("height", DEFAULT_HEIGHT);
- final Integer x = call.getInt("x", 0);
- final Integer y = call.getInt("y", 0);
+ public void createMap(final PluginCall call) {
+ final CapacitorGoogleMaps ctx = this;
- final Float zoom = call.getFloat("zoom", DEFAULT_ZOOM);
- final Double latitude = call.getDouble("latitude");
- final Double longitude = call.getDouble("longitude");
+ bridge.saveCall(call);
+ final String callbackId = call.getCallbackId();
- final boolean liteMode = call.getBoolean("enabled", false);
+ final BoundingRect boundingRect = new BoundingRect();
+ boundingRect.updateFromJSObject(call.getObject("boundingRect"));
- final CapacitorGoogleMaps ctx = this;
- getBridge().getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- LatLng latLng = new LatLng(latitude, longitude);
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(latLng)
- .zoom(zoom)
- .build();
+ final MapCameraPosition mapCameraPosition = new MapCameraPosition();
+ mapCameraPosition.updateFromJSObject(call.getObject("cameraPosition"), null);
- GoogleMapOptions googleMapOptions = new GoogleMapOptions();
- googleMapOptions.camera(cameraPosition);
- googleMapOptions.liteMode(liteMode);
+ final MapPreferences mapPreferences = new MapPreferences();
+ mapPreferences.updateFromJSObject(call.getObject("preferences"));
- FrameLayout mapViewParent = new FrameLayout(getBridge().getContext());
- mapViewParentId = View.generateViewId();
- mapViewParent.setId(mapViewParentId);
- mapView = new MapView(getBridge().getContext(), googleMapOptions);
+ getBridge().getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ CustomMapView customMapView = new CustomMapView(getBridge().getContext(), getBridge().getActivity(), ctx);
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(getScaledPixels(width), getScaledPixels(height));
- lp.topMargin = getScaledPixels(y);
- lp.leftMargin = getScaledPixels(x);
+ customMapViews.put(customMapView.getId(), customMapView);
- mapView.setLayoutParams(lp);
+ customMapView.createMap(callbackId, boundingRect, mapCameraPosition, mapPreferences);
- mapViewParent.addView(mapView);
+ customMapView.addToView(((ViewGroup) bridge.getWebView().getParent()));
- ((ViewGroup) getBridge().getWebView().getParent()).addView(mapViewParent);
+ // Bring the WebView in front of the MapView
+ // This allows us to overlay the MapView in HTML/CSS
+ bridge.getWebView().bringToFront();
- mapView.onCreate(null);
- mapView.onStart();
- mapView.getMapAsync(ctx);
+ // Hide the background
+ bridge.getWebView().setBackgroundColor(Color.TRANSPARENT);
+ bridge.getWebView().loadUrl("javascript:document.documentElement.style.backgroundColor = 'transparent';void(0);");
}
});
- call.resolve();
}
@PluginMethod()
- public void addMarker(final PluginCall call) {
- final Double latitude = call.getDouble("latitude", 0d);
- final Double longitude = call.getDouble("longitude", 0d);
- final Float opacity = call.getFloat("opacity", 1.0f);
- final String title = call.getString("title", "");
- final String snippet = call.getString("snippet", "");
- final Boolean isFlat = call.getBoolean("isFlat", true);
- final JSObject metadata = call.getObject("metadata");
+ public void updateMap(final PluginCall call) {
+ final String mapId = call.getString("mapId");
getBridge().getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
- LatLng latLng = new LatLng(latitude, longitude);
- MarkerOptions markerOptions = new MarkerOptions();
- markerOptions.position(latLng);
- markerOptions.alpha(opacity);
- markerOptions.title(title);
- markerOptions.snippet(snippet);
- markerOptions.flat(isFlat);
+ CustomMapView customMapView = customMapViews.get(mapId);
+
+ if (customMapView != null) {
+ final MapPreferences mapPreferences = customMapView.mapPreferences;
+ mapPreferences.updateFromJSObject(call.getObject("preferences"));
- Marker marker = googleMap.addMarker(markerOptions);
+ JSObject result = customMapView.invalidateMap();
- // set metadata to marker
- marker.setTag(metadata);
+ call.resolve(result);
+ } else {
+ call.reject("map not found");
+ }
+ }
+ });
+ }
- // get auto-generated id of the just added marker,
- // put this marker into a hashmap with the corresponding id,
- // so we can retrieve the marker by id later on
- mHashMap.put(marker.getId(), marker);
+ @PluginMethod()
+ public void getMap(final PluginCall call) {
+ final String mapId = call.getString("mapId");
- // initialize JSObject to return when resolving this call
- JSObject result = new JSObject();
- JSObject markerResult = new JSObject();
+ getBridge().getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ CustomMapView customMapView = customMapViews.get(mapId);
- // get marker specific values
- markerResult.put("id", marker.getId());
- result.put("marker", markerResult);
+ if (customMapView != null) {
+ JSObject result = customMapView.getMap();
- call.resolve(result);
+ call.resolve(result);
+ } else {
+ call.reject("map not found");
+ }
}
});
}
- @PluginMethod()
- public void removeMarker(final PluginCall call) {
+ @PluginMethod(returnType = PluginMethod.RETURN_NONE)
+ public void removeMap(final PluginCall call) {
+ final String mapId = call.getString("mapId");
+
getBridge().getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
- final String id = call.getString("id", null);
+ CustomMapView customMapView = customMapViews.get(mapId);
- if (id == null) {
- // todo
- return;
+ if (customMapView != null) {
+ customMapView.removeFromView(((ViewGroup) bridge.getWebView().getParent()));
+ customMapViews.remove(mapId);
+ call.resolve();
+ } else {
+ call.reject("map not found");
}
+ }
+ });
+ }
+
+ @PluginMethod(returnType = PluginMethod.RETURN_NONE)
+ public void clearMap(final PluginCall call) {
+ final String mapId = call.getString("mapId");
- Marker marker = mHashMap.get(id);
+ getBridge().getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ CustomMapView customMapView = customMapViews.get(mapId);
- if (marker != null) {
- marker.remove();
- mHashMap.remove(id);
+ if (customMapView != null) {
+ customMapView.clear();
+ call.resolve();
+ } else {
+ call.reject("map not found");
}
}
});
}
@PluginMethod()
- public void addPolyline(final PluginCall call) {
- final JSArray points = call.getArray("points", new JSArray());
+ public void close(PluginCall call) {
+ final String mapId = call.getString("mapId");
getBridge().executeOnMainThread(new Runnable() {
@Override
public void run() {
- PolylineOptions polylineOptions = new PolylineOptions();
-
- for (int i = 0; i < points.length(); i++) {
- try {
- JSONObject point = points.getJSONObject(i);
- LatLng latLng = new LatLng(point.getDouble("latitude"), point.getDouble("longitude"));
- polylineOptions.add(latLng);
- } catch (JSONException e) {
- e.printStackTrace();
+ CustomMapView customMapView = customMapViews.get(mapId);
+ if (customMapView != null){
+ View viewToRemove = customMapViews.get(mapId).mapView;
+ if (viewToRemove != null){
+ ((ViewGroup) getBridge().getWebView().getParent()).removeViewInLayout(viewToRemove);
+ call.resolve();
}
}
+ else {
+ call.reject("map not found");
+ }
- googleMap.addPolyline(polylineOptions);
-
- call.resolve();
}
});
}
@PluginMethod()
- public void addPolygon(final PluginCall call) {
- final JSArray points = call.getArray("points", new JSArray());
+ public void hide(PluginCall call) {
+ final String mapId = call.getString("mapId");
getBridge().executeOnMainThread(new Runnable() {
@Override
public void run() {
- PolygonOptions polygonOptions = new PolygonOptions();
-
- for (int i = 0; i < points.length(); i++) {
- try {
- JSONObject point = points.getJSONObject(i);
- LatLng latLng = new LatLng(point.getDouble("latitude"), point.getDouble("longitude"));
- polygonOptions.add(latLng);
- } catch (JSONException e) {
- e.printStackTrace();
+ CustomMapView customMapView = customMapViews.get(mapId);
+ if (customMapView != null){
+ View viewToHide = customMapViews.get(mapId).mapView;
+ if (viewToHide != null){
+ viewToHide.setVisibility(View.INVISIBLE);
+ customMapView.setHidden(true);
+ call.resolve();
}
}
-
- googleMap.addPolygon(polygonOptions);
- call.resolve();
+ else {
+ call.reject("map not found");
+ }
}
});
}
@PluginMethod()
- public void addCircle(final PluginCall call) {
- final int radius = call.getInt("radius", 0);
- final JSONObject center = call.getObject("center", new JSObject());
+ public void show(PluginCall call) {
+ final String mapId = call.getString("mapId");
getBridge().executeOnMainThread(new Runnable() {
+ CustomMapView customMapView = customMapViews.get(mapId);
@Override
public void run() {
- CircleOptions circleOptions = new CircleOptions();
- circleOptions.radius(radius);
- try {
- circleOptions.center(new LatLng(center.getDouble("latitude"), center.getDouble("longitude")));
- } catch (JSONException e) {
- e.printStackTrace();
+ CustomMapView customMapView = customMapViews.get(mapId);
+ if (customMapView != null){
+ View viewToShow = customMapViews.get(mapId).mapView;
+ if (viewToShow != null){
+ viewToShow.setVisibility(View.VISIBLE);
+ customMapView.setHidden(false);
+ call.resolve();
+ }
+ }
+ else {
+ call.reject("map not found");
}
-
- googleMap.addCircle(circleOptions);
-
- call.resolve();
}
});
}
- @PluginMethod()
- public void setMapType(PluginCall call) {
-
- String specifiedMapType = call.getString("type", "normal");
- Integer mapType;
-
- switch (specifiedMapType) {
- case "hybrid":
- mapType = GoogleMap.MAP_TYPE_HYBRID;
- break;
- case "satellite":
- mapType = GoogleMap.MAP_TYPE_SATELLITE;
- break;
- case "terrain":
- mapType = GoogleMap.MAP_TYPE_TERRAIN;
- break;
- case "none":
- mapType = GoogleMap.MAP_TYPE_NONE;
- break;
- default:
- mapType = GoogleMap.MAP_TYPE_NORMAL;
- }
- final Integer selectedMapType = mapType;
+ @PluginMethod
+ public void moveCamera(final PluginCall call) {
+ final String mapId = call.getString("mapId");
+
getBridge().getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
- googleMap.setMapType(selectedMapType);
- }
+ CustomMapView customMapView = customMapViews.get(mapId);
- });
+ if (customMapView != null) {
+ final MapCameraPosition mapCameraPosition = customMapView.mapCameraPosition;
- call.resolve();
- }
+ CameraPosition previousCameraPosition = null;
+ Boolean usePreviousCameraPositionAsBase = call.getBoolean("usePreviousCameraPositionAsBase", false);
+ if (usePreviousCameraPositionAsBase == null || !usePreviousCameraPositionAsBase) {
+ // if we should not use the previous one,
+ // use the current one
+ previousCameraPosition = customMapView.getCameraPosition();
+ }
- @PluginMethod()
- public void setIndoorEnabled(PluginCall call) {
- final Boolean indoorEnabled = call.getBoolean("enabled", false);
- getBridge().executeOnMainThread(new Runnable() {
- @Override
- public void run() {
- googleMap.setIndoorEnabled(indoorEnabled);
- }
- });
+ mapCameraPosition.updateFromJSObject(call.getObject("cameraPosition"), previousCameraPosition);
- call.resolve();
- }
+ Integer duration = call.getInt("duration", 0);
- @PluginMethod()
- public void setTrafficEnabled(PluginCall call) {
- final Boolean trafficEnabled = call.getBoolean("enabled", false);
- getBridge().executeOnMainThread(new Runnable() {
- @Override
- public void run() {
- googleMap.setTrafficEnabled(trafficEnabled);
+ // @TODO: add move listeners for movement
+ JSObject result = customMapView.moveCamera(duration);
+
+ call.resolve(result);
+ } else {
+ call.reject("map not found");
+ }
}
});
+ }
- call.resolve();
+ @Override
+ public void onMapReady(String callbackId, JSObject result) {
+ PluginCall call = bridge.getSavedCall(callbackId);
+ call.resolve(result);
+ bridge.releaseCall(call);
}
- @PluginMethod()
- public void padding(PluginCall call) {
- final Integer top = call.getInt("top", 0);
- final Integer left = call.getInt("left", 0);
- final Integer bottom = call.getInt("bottom", 0);
- final Integer right = call.getInt("right", 0);
+ // -- events of tapping
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didTapInfoWindow(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_TAP_INFO_WINDOW);
+ }
- getBridge().executeOnMainThread(new Runnable() {
- @Override
- public void run() {
- googleMap.setPadding(left, top, right, bottom);
- }
- });
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didCloseInfoWindow(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_CLOSE_INFO_WINDOW);
+ }
- call.resolve();
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didTapMap(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_TAP_MAP);
}
- @PluginMethod()
- public void clear(PluginCall call) {
- getBridge().executeOnMainThread(new Runnable() {
- @Override
- public void run() {
- googleMap.clear();
- mHashMap.clear();
- }
- });
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didLongPressMap(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_LONG_PRESS_MAP);
+ }
- call.resolve();
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didTapSingleMarker(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_TAP_SINGLE_MARKER);
}
- @PluginMethod()
- public void close(PluginCall call) {
- getBridge().executeOnMainThread(new Runnable() {
- @Override
- public void run() {
- View viewToRemove = ((ViewGroup) getBridge().getWebView().getParent()).findViewById(mapViewParentId);
- ((ViewGroup) getBridge().getWebView().getParent()).removeViewInLayout(viewToRemove);
- }
- });
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didTapCluster(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_TAP_CLUSTER);
+ }
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didTapClusterInfoWindow(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_TAP_CLUSTER_INFO_WINDOW);
}
- @PluginMethod()
- public void hide(PluginCall call) {
- getBridge().executeOnMainThread(new Runnable() {
- @Override
- public void run() {
- View viewToHide = ((ViewGroup) getBridge().getWebView().getParent()).findViewById(mapViewParentId);
- viewToHide.setVisibility(View.INVISIBLE);
- }
- });
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didTapMarker(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_TAP_MARKER);
}
- @PluginMethod()
- public void show(PluginCall call) {
- getBridge().executeOnMainThread(new Runnable() {
- @Override
- public void run() {
- View viewToShow = ((ViewGroup) getBridge().getWebView().getParent()).findViewById(mapViewParentId);
- viewToShow.setVisibility(View.VISIBLE);
- }
- });
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didTapClusterItemInfoWindow(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_TAP_CLUSTER_ITEM_INFO_WINDOW);
}
- @PluginMethod()
- public void viewBounds(final PluginCall call) {
- getBridge().executeOnMainThread(new Runnable() {
- @Override
- public void run() {
- JSObject result = new JSObject();
- JSObject bounds = new JSObject();
- JSObject farLeft = new JSObject();
- JSObject farRight = new JSObject();
- JSObject nearLeft = new JSObject();
- JSObject nearRight = new JSObject();
-
- farLeft.put("latitude",googleMap.getProjection().getVisibleRegion().farLeft.latitude);
- farLeft.put("longitude",googleMap.getProjection().getVisibleRegion().farLeft.longitude);
- farRight.put("latitude",googleMap.getProjection().getVisibleRegion().farRight.latitude);
- farRight.put("longitude",googleMap.getProjection().getVisibleRegion().farRight.longitude);
- nearLeft.put("latitude",googleMap.getProjection().getVisibleRegion().nearLeft.latitude);
- nearLeft.put("longitude",googleMap.getProjection().getVisibleRegion().nearLeft.longitude);
- nearRight.put("latitude",googleMap.getProjection().getVisibleRegion().nearRight.latitude);
- nearRight.put("longitude",googleMap.getProjection().getVisibleRegion().nearRight.longitude);
-
- bounds.put("farLeft",farLeft);
- bounds.put("farRight",farRight);
- bounds.put("nearLeft",nearLeft);
- bounds.put("nearRight",nearRight);
- result.put("bounds",bounds);
-
- call.resolve(result);
- }
- });
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didBeginDraggingMarker(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_BEGIN_DRAGGING_MARKER);
}
- @PluginMethod()
- public void reverseGeocodeCoordinate(final PluginCall call) {
- final Double latitude = call.getDouble("latitude", 0.0);
- final Double longitude = call.getDouble("longitude", 0.0);
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didDragMarker(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_DRAG_MARKER);
+ }
- getBridge().executeOnMainThread(new Runnable() {
- @Override
- public void run() {
- /*
- * TODO: Check if can be done without adding Places SDK
- *
- */
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didEndDraggingMarker(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_END_DRAGGING_MARKER);
+ }
- Geocoder geocoder = new Geocoder(getContext());
- try {
- List addressList = geocoder.getFromLocation(latitude, longitude, 5);
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didTapMyLocationButton(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_TAP_MY_LOCATION_BUTTON);
+ }
- JSObject results = new JSObject();
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didTapMyLocationDot(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_TAP_MY_LOCATION_DOT);
+ }
- int index = 0;
- for (Address address : addressList) {
- JSObject addressObject = new JSObject();
- addressObject.put("administrativeArea", address.getAdminArea());
- addressObject.put("lines", address.getAddressLine(0));
- addressObject.put("country", address.getCountryName());
- addressObject.put("locality", address.getLocality());
- addressObject.put("subLocality", address.getSubLocality());
- addressObject.put("thoroughFare", address.getThoroughfare());
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didTapPoi(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_TAP_POI);
+ }
- results.put(String.valueOf(index++), addressObject);
- }
- call.resolve(results);
- } catch (IOException e) {
- call.error("Error in Geocode!");
- }
- }
- });
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didBeginMovingCamera(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_BEGIN_MOVING_CAMERA);
+ }
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didMoveCamera(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_MOVE_CAMERA);
+ }
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
+ public void didEndMovingCamera(final PluginCall call) {
+ setCallbackIdForEvent(call, Events.EVENT_DID_END_MOVING_CAMERA);
}
- @PluginMethod()
- public void settings(final PluginCall call) {
- final boolean allowScrollGesturesDuringRotateOrZoom = call.getBoolean("allowScrollGesturesDuringRotateOrZoom", true);
+ public void setCallbackIdForEvent(final PluginCall call, final String eventName) {
+ call.setKeepAlive(true);
+ final String callbackId = call.getCallbackId();
+ String mapId = call.getString("mapId");
- final boolean compassButton = call.getBoolean("compassButton", false);
- final boolean zoomButton = call.getBoolean("zoomButton", true);
- final boolean myLocationButton = call.getBoolean("myLocationButton", false);
+ final CustomMapView customMapView = customMapViews.get(mapId);
- boolean consumesGesturesInView = call.getBoolean("consumesGesturesInView", true);
- final boolean indoorPicker = call.getBoolean("indoorPicker", false);
+ if (customMapView != null) {
+ final Boolean preventDefault = call.getBoolean("preventDefault", false);
- final boolean rotateGestures = call.getBoolean("rotateGestures", true);
- final boolean scrollGestures = call.getBoolean("scrollGestures", true);
- final boolean tiltGestures = call.getBoolean("tiltGestures", true);
- final boolean zoomGestures = call.getBoolean("zoomGestures", true);
- getBridge().executeOnMainThread(new Runnable() {
- @Override
- public void run() {
- UiSettings googleMapUISettings = googleMap.getUiSettings();
- googleMapUISettings.setScrollGesturesEnabledDuringRotateOrZoom(allowScrollGesturesDuringRotateOrZoom);
- googleMapUISettings.setCompassEnabled(compassButton);
- googleMapUISettings.setIndoorLevelPickerEnabled(indoorPicker);
- googleMapUISettings.setMyLocationButtonEnabled(myLocationButton);
- googleMapUISettings.setRotateGesturesEnabled(rotateGestures);
- googleMapUISettings.setScrollGesturesEnabled(scrollGestures);
- googleMapUISettings.setTiltGesturesEnabled(tiltGestures);
- googleMapUISettings.setZoomGesturesEnabled(zoomGestures);
- googleMapUISettings.setZoomControlsEnabled(zoomButton);
- googleMapUISettings.setMyLocationButtonEnabled(true);
- }
- });
+ getBridge().getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ customMapView.setCallbackIdForEvent(callbackId, eventName, preventDefault);
+ }
+ });
+ }
+ }
- call.resolve();
+ @Override
+ public void resultForCallbackId(String callbackId, JSObject result) {
+ PluginCall call = bridge.getSavedCall(callbackId);
+ call.resolve(result);
}
- @PluginMethod()
- public void setCamera(PluginCall call) {
- final float viewingAngle = call.getFloat("viewingAngle", googleMap.getCameraPosition().tilt);
- final float bearing = call.getFloat("bearing", googleMap.getCameraPosition().bearing);
- final Float zoom = call.getFloat("zoom", googleMap.getCameraPosition().zoom);
- final Double latitude = call.getDouble("latitude", googleMap.getCameraPosition().target.latitude);
- final Double longitude = call.getDouble("longitude", googleMap.getCameraPosition().target.longitude);
- final Boolean animate = call.getBoolean("animate", false);
- Double animationDuration = call.getDouble("animationDuration", 1000.0);
+ @PluginMethod()
+ public void addMarker(final PluginCall call) {
+ final String mapId = call.getString("mapId");
- getBridge().executeOnMainThread(new Runnable() {
+ getBridge().getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(new LatLng(latitude, longitude))
- .zoom(zoom)
- .tilt(viewingAngle)
- .bearing(bearing)
- .build();
-
- if (animate) {
- /*
- * TODO: Implement animationDuration
- * */
- googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
+ CustomMapView customMapView = customMapViews.get(mapId);
+
+ if (customMapView != null) {
+ JSObject preferences = JSObjectDefaults.getJSObjectSafe(call, "preferences", new JSObject());
+
+ CustomMarker customMarker = new CustomMarker();
+ customMarker.updateFromJSObject(preferences);
+
+ customMapView.addMarker(customMarker);
+
+ call.resolve(CustomMarker.getResultForMarker(customMarker, mapId));
} else {
- googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
+ call.reject("map not found");
}
}
});
-
- call.resolve();
}
@PluginMethod()
- public void setMapStyle(PluginCall call) {
- /*
- https://mapstyle.withgoogle.com/
- */
- final String mapStyle = call.getString("jsonString", "");
+ public void addMarkers(final PluginCall call) {
+ final String mapId = call.getString("mapId");
- getBridge().executeOnMainThread(new Runnable() {
+ getBridge().getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
- MapStyleOptions mapStyleOptions = new MapStyleOptions(mapStyle);
- googleMap.setMapStyle(mapStyleOptions);
+ CustomMapView customMapView = customMapViews.get(mapId);
+
+ if (customMapView != null) {
+ JSObject markersObj = call.getObject("markers");
+ try {
+ JSONArray arrayOfMarkers = markersObj.getJSONArray("arrayOfMarkers");
+ int length = arrayOfMarkers.length();
+ ArrayList customMarkers = new ArrayList<>(length);
+ for(int i =0; i markerCategoriesNamesAndIcons = fetchMarkersCategoriesFilesFromAssets(getContext());
+ List keys = new ArrayList(markerCategoriesNamesAndIcons.keySet());
+ Collections.sort(keys);
+ int i = 1;
+ for (String nameOfCategory :
+ keys) {
+ new MarkerCategory(i, nameOfCategory, markerCategoriesNamesAndIcons.get(nameOfCategory));
+ i++;
+ }
- final CapacitorGoogleMaps ctx = this;
+ }
- getBridge().executeOnMainThread(new Runnable() {
- @Override
- public void run() {
- googleMap.setOnPoiClickListener(new GoogleMap.OnPoiClickListener() {
- @Override
- public void onPoiClick(PointOfInterest pointOfInterest) {
- ctx.onPoiClick(pointOfInterest);
- }
- });
+ private HashMap fetchMarkersCategoriesFilesFromAssets(Context context) {
+ return getImagesFromAsset(MARKER_CATEGORY_DIRECTORY, context);
+ }
+
+ // method for reading pictures and their names from folder
+ private HashMap getImagesFromAsset(String path, Context context) {
+ HashMap stringBitmapHashMap = new HashMap<>();
+
+ AssetManager assetManager = context.getAssets();
+
+ // regex for getting full names of picture
+ // (cool.tank.png) -> cool.tank and .png
+ // (cool.tank) -> cool.tank and {null}
+ String textGroups = "(.+?)(\\.[^.]*$|$)";
+ Pattern textPattern = Pattern.compile(textGroups);
+
+ try {
+ // get array of names of files
+ String[] namesOfFiles = context.getAssets().list(path);
+ for (String nameOfFile :
+ namesOfFiles) {
+
+ Matcher textMatcher = textPattern.matcher(nameOfFile);
+ while(textMatcher.find()) {
+ File pathToFile = new File(path, nameOfFile);
+ InputStream istr = assetManager.open(pathToFile.getPath());
+ Bitmap bitmap = BitmapFactory.decodeStream(istr);
+ istr.close();
+ stringBitmapHashMap.put(textMatcher.group(1), bitmap);
+ }
}
- });
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
- call.resolve();
+ return stringBitmapHashMap;
}
- @PluginMethod()
- public void setOnMapClickListener(PluginCall call) {
- final CapacitorGoogleMaps ctx = this;
+ @PluginMethod
+ public void zoomInButtonClick(PluginCall call) {
+ final String mapId = call.getString("mapId");
- getBridge().executeOnMainThread(new Runnable() {
+ getBridge().getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
- googleMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
- @Override
- public void onMapClick(LatLng latLng) {
- ctx.onMapClick(latLng);
- }
- });
+ CustomMapView customMapView = customMapViews.get(mapId);
+
+ if (customMapView != null) {
+ customMapView.zoomIn();
+ call.resolve();
+ } else {
+ call.reject("map not found");
+ }
}
});
-
- call.resolve();
}
- @PluginMethod()
- public void enableCurrentLocation(final PluginCall call) {
+ @PluginMethod
+ public void zoomOutButtonClick(PluginCall call) {
+ final String mapId = call.getString("mapId");
- final boolean enableLocation = call.getBoolean("enabled", false);
- final CapacitorGoogleMaps context = this;
- getBridge().executeOnMainThread(new Runnable() {
- @SuppressLint("MissingPermission")
+ getBridge().getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
- if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
- call.error("Permission for location not granted");
- } else {
- googleMap.setMyLocationEnabled(enableLocation);
- googleMap.setOnMyLocationClickListener(context);
- googleMap.setOnMyLocationButtonClickListener(context);
+ CustomMapView customMapView = customMapViews.get(mapId);
+
+ if (customMapView != null) {
+ customMapView.zoomOut();
call.resolve();
+ } else {
+ call.reject("map not found");
}
}
});
}
- public void onPoiClick(PointOfInterest pointOfInterest) {
- JSObject result = new JSObject();
- JSObject location = new JSObject();
- JSObject coordinates = new JSObject();
-
- coordinates.put("latitude", pointOfInterest.latLng.latitude);
- coordinates.put("longitude", pointOfInterest.latLng.longitude);
+ @PluginMethod
+ public void myLocationButtonClick(PluginCall call) {
+ final String mapId = call.getString("mapId");
- location.put("coordinates", coordinates);
-
- result.put("name", pointOfInterest.name);
- result.put("placeID", pointOfInterest.placeId);
- result.put("result", location);
+ getBridge().getActivity().runOnUiThread(new Runnable() {
+ @SuppressLint("ResourceType")
+ @Override
+ public void run() {
+ CustomMapView customMapView = customMapViews.get(mapId);
- notifyListeners("didTapPOIWithPlaceID", result);
+ if (customMapView != null) {
+ locationRequest();
+ customMapView.locationButton.callOnClick();
+ call.resolve();
+ } else {
+ call.reject("map not found");
+ }
+ }
+ });
}
- public void onMapClick(LatLng latLng) {
- JSObject result = new JSObject();
- JSObject location = new JSObject();
- JSObject coordinates = new JSObject();
+ private void locationRequest() {
+ LocationRequest locationRequest = LocationRequest.create();
+ locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
+ LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
+ .addLocationRequest(locationRequest);
- coordinates.put("latitude", latLng.latitude);
- coordinates.put("longitude", latLng.longitude);
+ Task result =
+ LocationServices.getSettingsClient(getActivity()).checkLocationSettings(builder.build());
- location.put("coordinates", coordinates);
- result.put("result", location);
- notifyListeners("didTapAt", location);
+ result.addOnCompleteListener(new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ try {
+ LocationSettingsResponse response = task.getResult(ApiException.class);
+ // All location settings are satisfied. The client can initialize location
+ // requests here.
+ } catch (ApiException exception) {
+ switch (exception.getStatusCode()) {
+ case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
+ // Location settings are not satisfied. But could be fixed by showing the
+ // user a dialog.
+ try {
+ // Cast to a resolvable exception.
+ ResolvableApiException resolvable = (ResolvableApiException) exception;
+ // Show the dialog by calling startResolutionForResult(),
+ // and check the result in onActivityResult().
+ resolvable.startResolutionForResult(
+ getActivity(),
+ LocationRequest.PRIORITY_HIGH_ACCURACY);
+ } catch (IntentSender.SendIntentException e) {
+ // Ignore the error.
+ } catch (ClassCastException e) {
+ // Ignore, should be an impossible error.
+ }
+ break;
+ case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
+ // Location settings are not satisfied. However, we have no way to fix the
+ // settings so we won't show the dialog.
+ break;
+ }
+ }
+ }
+ });
}
- public void onMarkerClick(Marker marker) {
- JSObject result = new JSObject();
- JSObject location = new JSObject();
- JSObject coordinates = new JSObject();
- JSObject metadata = (JSObject) marker.getTag();
-
- coordinates.put("latitude", marker.getPosition().latitude);
- coordinates.put("longitude", marker.getPosition().longitude);
-
- location.put("coordinates", coordinates);
- result.put("id", marker.getId());
- result.put("title", marker.getTitle());
- result.put("snippet", marker.getSnippet());
- result.put("result", location);
- result.put("metadata", metadata);
- notifyListeners("didTap", result);
- }
-
- public boolean onMyLocationButtonClick() {
- /*
- * TODO: Add handler
- */
- return false;
- }
+}
- public void onMyLocationClick(Location location) {
- JSObject result = new JSObject();
- result.put("latitude", location.getLatitude());
- result.put("longitude", location.getLongitude());
- notifyListeners("onMyLocationClick", result);
- }
- public int getScaledPixels(float pixels) {
- // Get the screen's density scale
- final float scale = getBridge().getActivity().getResources().getDisplayMetrics().density;
- // Convert the dps to pixels, based on density scale
- return (int) (pixels * scale + 0.5f);
- }
-}
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/CustomMapView.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/CustomMapView.java
new file mode 100644
index 00000000..809fe05a
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/CustomMapView.java
@@ -0,0 +1,804 @@
+package com.hemangkumar.capacitorgooglemaps;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.Location;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.ActivityCompat;
+
+import com.getcapacitor.JSObject;
+import com.google.android.gms.maps.CameraUpdate;
+import com.google.android.gms.maps.CameraUpdateFactory;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.GoogleMapOptions;
+import com.google.android.gms.maps.MapView;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.UiSettings;
+import com.google.android.gms.maps.model.CameraPosition;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.LatLngBounds;
+import com.google.android.gms.maps.model.Marker;
+import com.google.android.gms.maps.model.PointOfInterest;
+import com.google.maps.android.clustering.Cluster;
+import com.google.maps.android.clustering.ClusterManager;
+import com.hemangkumar.capacitorgooglemaps.marker.CustomMarker;
+import com.hemangkumar.capacitorgooglemaps.marker.OwnIconRenderer;
+import com.hemangkumar.capacitorgooglemaps.mapsutility.Events;
+import com.hemangkumar.capacitorgooglemaps.mapsutility.MapPreferencesAppearance;
+import com.hemangkumar.capacitorgooglemaps.mapsutility.MapPreferencesControls;
+import com.hemangkumar.capacitorgooglemaps.mapsutility.MapPreferencesGestures;
+import com.hemangkumar.capacitorgooglemaps.utils.BoundingRect;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.UUID;
+
+public class CustomMapView implements OnMapReadyCallback,
+ GoogleMap.OnInfoWindowClickListener,
+ GoogleMap.OnInfoWindowCloseListener,
+ GoogleMap.OnMapClickListener,
+ GoogleMap.OnMapLongClickListener,
+ GoogleMap.OnMarkerClickListener,
+ GoogleMap.OnMarkerDragListener,
+ GoogleMap.OnMyLocationClickListener,
+ GoogleMap.OnMyLocationButtonClickListener,
+ GoogleMap.OnPoiClickListener,
+ GoogleMap.OnCameraMoveStartedListener,
+ GoogleMap.OnCameraMoveListener,
+ GoogleMap.OnCameraIdleListener,
+ ClusterManager.OnClusterClickListener,
+ ClusterManager.OnClusterInfoWindowClickListener,
+ ClusterManager.OnClusterItemClickListener,
+ ClusterManager.OnClusterItemInfoWindowClickListener
+{
+
+ // -- constants --
+ private final double EPSILON = 0.00001;
+
+ // -- private fields --
+ private final Context context;
+ private final Activity activity;
+ private final CustomMapViewEvents customMapViewEvents;
+ private final String id;
+ private ClusterManager mClusterManager;
+
+ MapView mapView;
+ private GoogleMap googleMap;
+
+ public View locationButton;
+
+
+ // -- public fields
+ public MapCameraPosition mapCameraPosition;
+ public MapPreferences mapPreferences;
+
+ // -- callbacks --
+
+ String savedCallbackIdForCreate;
+
+ String savedCallbackIdForDidTapInfoWindow;
+
+ String savedCallbackIdForDidCloseInfoWindow;
+
+ String savedCallbackIdForDidTapMap;
+
+ String savedCallbackIdForDidLongPressMap;
+
+ String savedCallbackIdForDidTapMarker;
+ Boolean preventDefaultForDidTapMarker = false;
+
+ String savedCallbackIdForDidBeginDraggingMarker;
+
+ String savedCallbackIdForDidDragMarker;
+
+ String savedCallbackIdForDidEndDraggingMarker;
+
+ String savedCallbackIdForDidTapMyLocationButton;
+ Boolean preventDefaultForDidTapMyLocationButton = false;
+
+ String savedCallbackIdForDidTapMyLocationDot;
+
+ String savedCallbackIdForDidTapPoi;
+
+ String savedCallbackIdForDidBeginMovingCamera;
+ String savedCallbackIdForDidMoveCamera;
+ String savedCallbackIdForDidEndMovingCamera;
+
+ String savedCallbackIdForDidTapCluster;
+ Boolean preventDefaultForDidTapCluster = true;
+ String savedCallbackIdForDidTapClusterInfoWindow;
+
+ String savedCallbackIdForDidTapClusterItem;
+ String savedCallbackIdForDidTapClusterItemInfoWindow;
+
+
+ private boolean isHidden = false;
+
+ // values for unclustering animation bounding box
+ // with - bounding box width in pixels (px)
+ // height - bounding box height in pixels (px)
+ private int widthOfTheMapCameraAnimationUnclustering;
+ private int heightOfTheMapCameraAnimationUnclustering;
+
+
+ public boolean isHidden() {
+ return isHidden;
+ }
+
+ public void setHidden(boolean hidden) {
+ isHidden = hidden;
+ }
+
+ public CustomMapView(@NonNull Context context, @NonNull Activity activity, CustomMapViewEvents customMapViewEvents) {
+ this.context = context;
+ this.activity = activity;
+ this.customMapViewEvents = customMapViewEvents;
+ this.id = UUID.randomUUID().toString();
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ private boolean hasPermission() {
+ return ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @SuppressLint({"MissingPermission", "ResourceType"})
+ @Override
+ public void onMapReady(GoogleMap googleMap) {
+ // populate `googleMap` variable for other methods to use
+ this.googleMap = googleMap;
+
+ // set ClusterManager
+ // Initialize the manager with the context and the map.
+ this.mClusterManager = new ClusterManager<>(this.context, this.googleMap);
+
+
+ // Point the map's listeners at the listeners implemented by the cluster
+ // manager.
+ this.googleMap.setOnCameraIdleListener(mClusterManager);
+
+ // using custom renderer here
+ mClusterManager.setRenderer(new OwnIconRenderer(
+ this.context, this.activity, this.googleMap, mClusterManager));
+
+ // set controls
+ UiSettings googleMapUISettings = this.googleMap.getUiSettings();
+ googleMapUISettings.setIndoorLevelPickerEnabled(this.mapPreferences.controls.getBoolean(MapPreferencesControls.INDOOR_LEVEL_PICKER_KEY));
+ googleMapUISettings.setMyLocationButtonEnabled(this.mapPreferences.controls.getBoolean(MapPreferencesControls.MY_LOCATION_BUTTON_KEY));
+
+
+ // set appearance
+ this.googleMap.setMapStyle(this.mapPreferences.appearance.style);
+ this.googleMap.setBuildingsEnabled(this.mapPreferences.appearance.isBuildingsShown);
+ this.googleMap.setIndoorEnabled(this.mapPreferences.appearance.isIndoorShown);
+ if (hasPermission()) {
+ this.googleMap.setMyLocationEnabled(this.mapPreferences.appearance.isMyLocationDotShown);
+ locationButton = mapView.findViewById(0x2);
+ locationButton.setVisibility(View.GONE);
+ }
+ this.googleMap.setTrafficEnabled(this.mapPreferences.appearance.isTrafficShown);
+
+
+ // execute callback
+ if (customMapViewEvents != null && savedCallbackIdForCreate != null) {
+ JSObject result = getResultForMap();
+ customMapViewEvents.onMapReady(savedCallbackIdForCreate, result);
+ }
+
+ }
+
+ protected void handleOnStart() {
+ if (mapView != null) {
+ mapView.onStart();
+ }
+ }
+
+ protected void handleOnResume() {
+ if (mapView != null) {
+ mapView.onResume();
+ }
+ }
+
+ protected void handleOnPause() {
+ if (mapView != null) {
+ mapView.onPause();
+ }
+ }
+
+ protected void handleOnStop() {
+ if (mapView != null) {
+ mapView.onStop();
+ }
+ }
+
+ protected void handleOnDestroy() {
+ if (mapView != null) {
+ mapView.onDestroy();
+ }
+ }
+
+
+ public void setCallbackIdForEvent(String callbackId, String eventName, Boolean preventDefault) {
+ if (callbackId != null && eventName != null) {
+ if (eventName.equals(Events.EVENT_DID_TAP_INFO_WINDOW)) {
+ this.googleMap.setOnInfoWindowClickListener(this);
+ savedCallbackIdForDidTapInfoWindow = callbackId;
+ } else if (eventName.equals(Events.EVENT_DID_CLOSE_INFO_WINDOW)) {
+ this.googleMap.setOnInfoWindowCloseListener(this);
+ savedCallbackIdForDidCloseInfoWindow = callbackId;
+ } else if (eventName.equals(Events.EVENT_DID_TAP_MAP)) {
+ this.googleMap.setOnMapClickListener(this);
+ savedCallbackIdForDidTapMap = callbackId;
+ } else if (eventName.equals(Events.EVENT_DID_LONG_PRESS_MAP)) {
+ this.googleMap.setOnMapLongClickListener(this);
+ savedCallbackIdForDidLongPressMap = callbackId;
+ } else if (eventName.equals(Events.EVENT_DID_TAP_SINGLE_MARKER)) {
+ this.googleMap.setOnMarkerClickListener(mClusterManager);
+ savedCallbackIdForDidTapMarker = callbackId;
+ if (preventDefault == null) {
+ preventDefault = false;
+ }
+ preventDefaultForDidTapMarker = preventDefault;
+ } else if(eventName.equals(Events.EVENT_DID_TAP_CLUSTER_ITEM_INFO_WINDOW)) {
+ this.mClusterManager.setOnClusterItemInfoWindowClickListener(this);
+ savedCallbackIdForDidTapClusterItemInfoWindow = callbackId;
+ } else if (eventName.equals(Events.EVENT_DID_BEGIN_DRAGGING_MARKER)) {
+ this.googleMap.setOnMarkerDragListener(this);
+ savedCallbackIdForDidBeginDraggingMarker = callbackId;
+ } else if (eventName.equals(Events.EVENT_DID_DRAG_MARKER)) {
+ this.googleMap.setOnMarkerDragListener(this);
+ savedCallbackIdForDidDragMarker = callbackId;
+ } else if (eventName.equals(Events.EVENT_DID_END_DRAGGING_MARKER)) {
+ this.googleMap.setOnMarkerDragListener(this);
+ savedCallbackIdForDidEndDraggingMarker = callbackId;
+ } else if (eventName.equals(Events.EVENT_DID_TAP_CLUSTER)) {
+ this.mClusterManager.setOnClusterClickListener(this);
+ savedCallbackIdForDidTapCluster = callbackId;
+ if (preventDefault == null) {
+ preventDefault = false;
+ }
+ preventDefaultForDidTapCluster = preventDefault;
+ } else if (eventName.equals(Events.EVENT_DID_TAP_CLUSTER_INFO_WINDOW)) {
+ this.mClusterManager.setOnClusterInfoWindowClickListener(this);
+ savedCallbackIdForDidTapClusterInfoWindow = callbackId;
+ } else if (eventName.equals(Events.EVENT_DID_TAP_MARKER)) {
+ this.mClusterManager.setOnClusterItemClickListener(this);
+ savedCallbackIdForDidTapClusterItem = callbackId;
+
+ } else if (eventName.equals(Events.EVENT_DID_TAP_MY_LOCATION_BUTTON)) {
+ this.googleMap.setOnMyLocationButtonClickListener(this);
+ savedCallbackIdForDidTapMyLocationButton = callbackId;
+ if (preventDefault == null) {
+ preventDefault = false;
+ }
+ preventDefaultForDidTapMyLocationButton = preventDefault;
+ } else if (eventName.equals(Events.EVENT_DID_TAP_MY_LOCATION_DOT)) {
+ this.googleMap.setOnMyLocationClickListener(this);
+ savedCallbackIdForDidTapMyLocationDot = callbackId;
+ } else if (eventName.equals((Events.EVENT_DID_TAP_POI))) {
+ this.googleMap.setOnPoiClickListener(this);
+ savedCallbackIdForDidTapPoi = callbackId;
+ } else if (eventName.equals((Events.EVENT_DID_BEGIN_MOVING_CAMERA))) {
+ this.googleMap.setOnCameraMoveStartedListener(this);
+ savedCallbackIdForDidBeginMovingCamera = callbackId;
+ } else if (eventName.equals((Events.EVENT_DID_MOVE_CAMERA))) {
+ this.googleMap.setOnCameraMoveListener(this);
+ savedCallbackIdForDidMoveCamera = callbackId;
+ } else if (eventName.equals((Events.EVENT_DID_END_MOVING_CAMERA))) {
+ this.googleMap.setOnCameraIdleListener(this);
+ savedCallbackIdForDidEndMovingCamera = callbackId;
+ }
+ }
+ }
+
+ public void createMap(String callbackId, BoundingRect boundingRect, MapCameraPosition mapCameraPosition, MapPreferences mapPreferences) {
+ savedCallbackIdForCreate = callbackId;
+
+ this.mapCameraPosition = mapCameraPosition;
+ this.mapPreferences = mapPreferences;
+
+ GoogleMapOptions googleMapOptions = this.mapPreferences.generateGoogleMapOptions();
+ googleMapOptions.camera(this.mapCameraPosition.cameraPosition);
+
+ mapView = new MapView(context, googleMapOptions);
+
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(getScaledPixels(boundingRect.width), getScaledPixels(boundingRect.height));
+ lp.topMargin = getScaledPixels(boundingRect.y);
+ lp.leftMargin = getScaledPixels(boundingRect.x);
+
+ mapView.setLayoutParams(lp);
+
+ mapView.onCreate(null);
+ mapView.onStart();
+ mapView.getMapAsync(this);
+
+ widthOfTheMapCameraAnimationUnclustering = getScaledPixels(boundingRect.width) - (getScaledPixels(boundingRect.width) /5);
+ heightOfTheMapCameraAnimationUnclustering = getScaledPixels(boundingRect.height) - (getScaledPixels(boundingRect.height) /5);
+ }
+
+ @SuppressLint("MissingPermission")
+ public JSObject invalidateMap() {
+ if (this.googleMap == null) {
+ return null;
+ }
+
+ UiSettings googleMapUISettings = this.googleMap.getUiSettings();
+
+ // set gestures
+ googleMapUISettings.setRotateGesturesEnabled(this.mapPreferences.gestures.getBoolean(MapPreferencesGestures.ROTATE_ALLOWED_KEY));
+ googleMapUISettings.setScrollGesturesEnabled(this.mapPreferences.gestures.getBoolean(MapPreferencesGestures.SCROLL_ALLOWED_KEY));
+ googleMapUISettings.setScrollGesturesEnabledDuringRotateOrZoom(this.mapPreferences.gestures.getBoolean(MapPreferencesGestures.SCROLL_ALLOWED_DURING_ROTATE_OR_ZOOM_KEY));
+ googleMapUISettings.setTiltGesturesEnabled(this.mapPreferences.gestures.getBoolean(MapPreferencesGestures.TILT_ALLOWED_KEY));
+ googleMapUISettings.setZoomGesturesEnabled(this.mapPreferences.gestures.getBoolean(MapPreferencesGestures.ZOOM_ALLOWED_KEY));
+
+ // set controls
+ googleMapUISettings.setCompassEnabled(this.mapPreferences.controls.getBoolean(MapPreferencesControls.COMPASS_BUTTON_KEY));
+ googleMapUISettings.setIndoorLevelPickerEnabled(this.mapPreferences.controls.getBoolean(MapPreferencesControls.INDOOR_LEVEL_PICKER_KEY));
+ googleMapUISettings.setMapToolbarEnabled(this.mapPreferences.controls.getBoolean(MapPreferencesControls.MAP_TOOLBAR_KEY));
+ googleMapUISettings.setMyLocationButtonEnabled(this.mapPreferences.controls.getBoolean(MapPreferencesControls.MY_LOCATION_BUTTON_KEY));
+ googleMapUISettings.setZoomControlsEnabled(this.mapPreferences.controls.getBoolean(MapPreferencesControls.ZOOM_BUTTONS_KEY));
+
+ // set appearance
+ this.googleMap.setMapType(this.mapPreferences.appearance.type);
+ this.googleMap.setMapStyle(this.mapPreferences.appearance.style);
+ this.googleMap.setBuildingsEnabled(this.mapPreferences.appearance.isBuildingsShown);
+ this.googleMap.setIndoorEnabled(this.mapPreferences.appearance.isIndoorShown);
+ if (hasPermission()) {
+ this.googleMap.setMyLocationEnabled(this.mapPreferences.appearance.isMyLocationDotShown);
+ }
+ this.googleMap.setTrafficEnabled(this.mapPreferences.appearance.isTrafficShown);
+
+ return getResultForMap();
+ }
+
+ public JSObject getMap() {
+ return getResultForMap();
+ }
+
+
+ private JSObject getResultForMap() {
+ if (this.mapView != null && this.googleMap != null) {
+ // initialize JSObjects
+ JSObject result = new JSObject();
+
+ JSObject resultGoogleMap = new JSObject();
+ result.put("googleMap", resultGoogleMap);
+
+ JSObject resultPreferences = new JSObject();
+ resultGoogleMap.put("preferences", resultPreferences);
+
+ JSObject resultGestures = new JSObject();
+ resultPreferences.put("gestures", resultGestures);
+
+ JSObject resultControls = new JSObject();
+ resultPreferences.put("controls", resultControls);
+
+ JSObject resultAppearance = new JSObject();
+ resultPreferences.put("appearance", resultAppearance);
+
+ // get UISettings
+ UiSettings googleMapUISettings = this.googleMap.getUiSettings();
+
+ // return mapId
+ resultGoogleMap.put("mapId", id);
+
+ // return cameraPosition
+ this.getResultForCameraPosition(resultGoogleMap);
+
+ // return gestures
+ resultGestures.put(MapPreferencesGestures.ROTATE_ALLOWED_KEY, googleMapUISettings.isRotateGesturesEnabled());
+ resultGestures.put(MapPreferencesGestures.SCROLL_ALLOWED_KEY, googleMapUISettings.isScrollGesturesEnabled());
+ resultGestures.put(MapPreferencesGestures.SCROLL_ALLOWED_DURING_ROTATE_OR_ZOOM_KEY, googleMapUISettings.isScrollGesturesEnabledDuringRotateOrZoom());
+ resultGestures.put(MapPreferencesGestures.TILT_ALLOWED_KEY, googleMapUISettings.isTiltGesturesEnabled());
+ resultGestures.put(MapPreferencesGestures.ZOOM_ALLOWED_KEY, googleMapUISettings.isZoomGesturesEnabled());
+
+ // return controls
+ resultControls.put(MapPreferencesControls.COMPASS_BUTTON_KEY, googleMapUISettings.isCompassEnabled());
+ resultControls.put(MapPreferencesControls.INDOOR_LEVEL_PICKER_KEY, googleMapUISettings.isIndoorLevelPickerEnabled());
+ resultControls.put(MapPreferencesControls.MAP_TOOLBAR_KEY, googleMapUISettings.isMapToolbarEnabled());
+ resultControls.put(MapPreferencesControls.MY_LOCATION_BUTTON_KEY, googleMapUISettings.isMyLocationButtonEnabled());
+ resultControls.put(MapPreferencesControls.ZOOM_BUTTONS_KEY, googleMapUISettings.isZoomControlsEnabled());
+
+ // return appearance
+ resultAppearance.put(MapPreferencesAppearance.TYPE_KEY, this.googleMap.getMapType());
+ resultAppearance.put(MapPreferencesAppearance.BUILDINGS_SHOWN_KEY, this.googleMap.isBuildingsEnabled());
+ resultAppearance.put(MapPreferencesAppearance.INDOOR_SHOWN_KEY, this.googleMap.isIndoorEnabled());
+ resultAppearance.put(MapPreferencesAppearance.MY_LOCATION_DOT_SHOWN_KEY, this.googleMap.isMyLocationEnabled());
+ resultAppearance.put(MapPreferencesAppearance.TRAFFIC_SHOWN_KEY, this.googleMap.isTrafficEnabled());
+
+ return result;
+ }
+ return null;
+ }
+
+
+ @Override
+ public void onCameraIdle() {
+ if (customMapViewEvents != null && savedCallbackIdForDidEndMovingCamera != null) {
+ this.mClusterManager.onCameraIdle();
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidEndMovingCamera, this.getResultForCameraPosition(new JSObject()));
+ }
+ }
+
+ @Override
+ public void onCameraMove() {
+ if (customMapViewEvents != null && savedCallbackIdForDidMoveCamera != null) {
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidMoveCamera, null);
+ }
+ }
+
+
+ @Override
+ public void onCameraMoveStarted(int i) {
+ if (customMapViewEvents != null && savedCallbackIdForDidBeginMovingCamera != null) {
+ int reason = 2;
+ if (i == REASON_GESTURE) {
+ // Camera motion initiated in response to user gestures on the map.
+ // For example: pan, tilt, pinch to zoom, or rotate.
+ reason = 1;
+ }
+ JSObject result = new JSObject();
+ result.put("reason", reason);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidBeginMovingCamera, result);
+ }
+ }
+
+ @Override
+ public void onInfoWindowClick(Marker marker) {
+ if (customMapViewEvents != null && savedCallbackIdForDidTapInfoWindow != null) {
+ JSObject result = CustomMarker.getResultForMarker(marker, this.id);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidTapInfoWindow, result);
+ }
+ }
+
+ @Override
+ public void onInfoWindowClose(Marker marker) {
+ if (customMapViewEvents != null && savedCallbackIdForDidCloseInfoWindow != null) {
+ JSObject result = CustomMarker.getResultForMarker(marker, this.id);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidCloseInfoWindow, result);
+ }
+ }
+
+ @Override
+ public void onMapClick(LatLng latLng) {
+ if (customMapViewEvents != null && savedCallbackIdForDidTapMap != null) {
+ JSObject result = getResultForPosition(latLng);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidTapMap, result);
+ }
+ }
+
+ @Override
+ public void onMapLongClick(LatLng latLng) {
+ if (customMapViewEvents != null && savedCallbackIdForDidLongPressMap != null) {
+ JSObject result = getResultForPosition(latLng);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidLongPressMap, result);
+ }
+ }
+
+ @Override
+ public boolean onMarkerClick(Marker marker) {
+// if (customMapViewEvents != null && savedCallbackIdForDidTapMarker != null) {
+// JSObject result = CustomMarker.getResultForMarker(marker, this.id);
+// customMapViewEvents.resultForCallbackId(savedCallbackIdForDidTapMarker, result);
+// }
+// return preventDefaultForDidTapMarker;
+ return true;
+ }
+
+ @Override
+ public void onMarkerDrag(Marker marker) {
+ if (customMapViewEvents != null && savedCallbackIdForDidDragMarker != null) {
+ JSObject result = CustomMarker.getResultForMarker(marker, this.id);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidDragMarker, result);
+ }
+ }
+
+ @Override
+ public void onMarkerDragEnd(Marker marker) {
+ if (customMapViewEvents != null && savedCallbackIdForDidEndDraggingMarker != null) {
+ JSObject result = CustomMarker.getResultForMarker(marker, this.id);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidEndDraggingMarker, result);
+ }
+ }
+
+ @Override
+ public void onMarkerDragStart(Marker marker) {
+ if (customMapViewEvents != null && savedCallbackIdForDidBeginDraggingMarker != null) {
+ JSObject result = CustomMarker.getResultForMarker(marker, this.id);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidBeginDraggingMarker, result);
+ }
+ }
+
+ @Override
+ public boolean onMyLocationButtonClick() {
+ if (customMapViewEvents != null && savedCallbackIdForDidTapMyLocationButton != null) {
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidTapMyLocationButton, null);
+ }
+ return preventDefaultForDidTapMyLocationButton;
+ }
+
+ @Override
+ public void onMyLocationClick(@NonNull Location location) {
+ if (customMapViewEvents != null && savedCallbackIdForDidTapMyLocationDot != null) {
+ JSObject result = getResultForPosition(location);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidTapMyLocationDot, result);
+ }
+ }
+
+ @Override
+ public void onPoiClick(PointOfInterest pointOfInterest) {
+ if (customMapViewEvents != null && savedCallbackIdForDidTapPoi != null) {
+ JSObject result = getResultForPoi(pointOfInterest);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidTapPoi, result);
+ }
+ }
+
+
+ @Override
+ public boolean onClusterClick(Cluster cluster) {
+ LatLngBounds.Builder builder = LatLngBounds.builder();
+ for (CustomMarker item : cluster.getItems()) {
+ builder.include(item.getPosition());
+ }
+ final LatLngBounds bounds = builder.build();
+ // if bounds very small than
+ boolean getMarkersData = unclusterCluster(bounds);
+
+ if (customMapViewEvents != null && savedCallbackIdForDidTapCluster != null) {
+ JSObject result = getResultForCluster(cluster, this.id, getMarkersData);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidTapCluster, result);
+ }
+
+
+ return preventDefaultForDidTapCluster;
+ }
+
+ private boolean unclusterCluster(LatLngBounds bounds) {
+ // check if cluster can uncluster
+ if((Math.abs(bounds.northeast.latitude - bounds.southwest.latitude) > EPSILON) &&
+ (Math.abs(bounds.northeast.longitude - bounds.southwest.longitude) > EPSILON)) {
+ CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds,
+ widthOfTheMapCameraAnimationUnclustering,
+ heightOfTheMapCameraAnimationUnclustering,
+ 100);
+ googleMap.animateCamera(cameraUpdate);
+ return false;
+ } else {
+ // if all markers inside cluster is have same position
+ return true;
+ }
+ }
+
+ @Override
+ public void onClusterInfoWindowClick(Cluster cluster) {
+ if (customMapViewEvents != null && savedCallbackIdForDidTapClusterInfoWindow != null) {
+ JSObject result = getResultForCluster(cluster, this.id, false);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidTapClusterInfoWindow, result);
+ }
+ }
+
+
+ @Override
+ public boolean onClusterItemClick(CustomMarker item) {
+ if (customMapViewEvents != null && savedCallbackIdForDidTapMarker != null) {
+ JSObject result = CustomMarker.getResultForMarker(item, this.id);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidTapMarker, result);
+ }
+ return preventDefaultForDidTapMarker;
+ }
+
+ @Override
+ public void onClusterItemInfoWindowClick(CustomMarker item) {
+ if (customMapViewEvents != null && savedCallbackIdForDidTapInfoWindow != null) {
+ JSObject result = CustomMarker.getResultForMarker(item, this.id);
+ customMapViewEvents.resultForCallbackId(savedCallbackIdForDidTapInfoWindow, result);
+ }
+ }
+
+
+ private static JSObject getResultForCluster(Cluster cluster,
+ String mapId,
+ boolean getMarkersData) {
+ // initialize JSObjects to return
+ JSObject result = new JSObject();
+ JSObject positionResult = new JSObject();
+
+ // get map id
+ positionResult.put("mapId", mapId);
+
+ // get position values
+ positionResult.put("latitude", cluster.getPosition().latitude);
+ positionResult.put("longitude", cluster.getPosition().longitude);
+
+ // return result
+ result.put("position", positionResult);
+
+ if (getMarkersData) {
+ JSObject markersData = new JSObject();
+ for (CustomMarker marker:
+ cluster.getItems()) {
+ markersData.put(marker.getId(), CustomMarker.getResultForMarker(marker, mapId));
+
+ }
+ result.put("markersData", markersData);
+ }
+ return result;
+ }
+
+ private int getScaledPixels(float pixels) {
+ // Get the screen's density scale
+ final float scale = context.getResources().getDisplayMetrics().density;
+ // Convert the dps to pixels, based on density scale
+ return (int) (pixels * scale + 0.5f);
+ }
+
+ public void addToView(ViewGroup parent) {
+ parent.addView(mapView);
+ }
+
+ public void removeFromView(ViewGroup parent) {
+ parent.removeView(mapView);
+ }
+
+ public void clear() {
+ googleMap.clear();
+ mClusterManager.clearItems();
+ }
+
+ public void addMarker(CustomMarker customMarker) {
+// customMarker.setProfilePhoto(R.drawable.ic_launcher);
+ this.mClusterManager.addItem(customMarker);
+ this.mClusterManager.cluster();
+
+// Iterator iterator = this.mClusterManager.getMarkerCollection().getMarkers().iterator();
+// while (iterator.hasNext()) {
+// Marker marker = iterator.next();
+// if(marker.getId().equals(customMarker.getMarkerId())) {
+// return marker;
+// }
+// }
+ }
+
+ public void addMarkers(Collection arrayOfCustomMarkers) {
+
+ this.mClusterManager.addItems(arrayOfCustomMarkers);
+ this.mClusterManager.cluster();
+
+ }
+
+ public boolean updateMarker(String markerId, JSObject newPreferences) {
+ boolean isUpdated = false;
+ Iterator iterator = this.mClusterManager.getAlgorithm().getItems().iterator();
+ while (iterator.hasNext()) {
+ CustomMarker customMarker = iterator.next();
+ if (customMarker.getId().equals(markerId)) {
+ customMarker.updateFromJSObject(newPreferences);
+ isUpdated = mClusterManager.updateItem(customMarker);
+ mClusterManager.cluster();
+ }
+ }
+
+ return isUpdated;
+ }
+
+ public void removeMarker(String markerId) {
+ Iterator iterator = this.mClusterManager.getAlgorithm().getItems().iterator();
+ while (iterator.hasNext()) {
+ CustomMarker customMarker = iterator.next();
+ if (customMarker.getId().equals(markerId)) {
+ mClusterManager.removeItem(customMarker);
+ mClusterManager.cluster();
+ }
+ }
+ }
+
+
+ public CameraPosition getCameraPosition() {
+ if (this.googleMap != null) {
+ return this.googleMap.getCameraPosition();
+ }
+ return null;
+ }
+
+ public JSObject moveCamera(Integer duration) {
+ CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(this.mapCameraPosition.cameraPosition);
+
+ if (duration == null || duration <= 0) {
+ googleMap.moveCamera(cameraUpdate);
+ } else {
+ googleMap.animateCamera(cameraUpdate, duration, null);
+ }
+
+ return getResultForMap();
+ }
+
+
+ private JSObject getResultForCameraPosition(JSObject resultObjectToExtend) {
+ JSObject resultCameraPosition = new JSObject();
+ resultObjectToExtend.put("cameraPosition", resultCameraPosition);
+
+ JSObject resultCameraPositionTarget = new JSObject();
+ resultCameraPosition.put("target", resultCameraPositionTarget);
+
+ // get CameraPosition
+ CameraPosition cameraPosition = this.getCameraPosition();
+
+ // return cameraPosition
+ resultCameraPositionTarget.put("latitude", cameraPosition.target.latitude);
+ resultCameraPositionTarget.put("longitude", cameraPosition.target.longitude);
+ resultCameraPosition.put("bearing", cameraPosition.bearing);
+ resultCameraPosition.put("tilt", cameraPosition.tilt);
+ resultCameraPosition.put("zoom", cameraPosition.zoom);
+
+ return resultObjectToExtend;
+ }
+
+
+ private JSObject getResultForPosition(Location location) {
+
+ // initialize JSObjects to return
+ JSObject result = new JSObject();
+ JSObject positionResult = new JSObject();
+ result.put("position", positionResult);
+
+ // get position values
+ positionResult.put("latitude", location.getLatitude());
+ positionResult.put("longitude", location.getLongitude());
+
+ // return result
+ return result;
+ }
+
+ private JSObject getResultForPosition(LatLng latLng) {
+
+ // initialize JSObjects to return
+ JSObject result = new JSObject();
+ JSObject positionResult = new JSObject();
+ result.put("position", positionResult);
+
+ // get position values
+ positionResult.put("latitude", latLng.latitude);
+ positionResult.put("longitude", latLng.longitude);
+
+ // return result
+ return result;
+ }
+
+ private JSObject getResultForPoi(PointOfInterest pointOfInterest) {
+ // initialize JSObjects to return
+ JSObject result = new JSObject();
+ JSObject positionResult = new JSObject();
+ JSObject poiResult = new JSObject();
+
+ // get position values
+ positionResult.put("latitude", pointOfInterest.latLng.latitude);
+ positionResult.put("longitude", pointOfInterest.latLng.longitude);
+
+ // get other values
+ poiResult.put("name", pointOfInterest.name);
+ poiResult.put("placeId", pointOfInterest.placeId);
+
+ // return result
+ result.put("position", positionResult);
+ result.put("poi", poiResult);
+ return result;
+ }
+
+ public void zoomIn() {
+ this.googleMap.animateCamera(CameraUpdateFactory.zoomIn());
+ }
+
+ public void zoomOut() {
+ this.googleMap.animateCamera(CameraUpdateFactory.zoomOut());
+ }
+
+}
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/CustomMapViewEvents.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/CustomMapViewEvents.java
new file mode 100644
index 00000000..f6871f16
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/CustomMapViewEvents.java
@@ -0,0 +1,9 @@
+package com.hemangkumar.capacitorgooglemaps;
+
+import com.getcapacitor.JSObject;
+
+public interface CustomMapViewEvents {
+ void onMapReady(String callbackId, JSObject result);
+
+ void resultForCallbackId(String callbackId, JSObject result);
+}
\ No newline at end of file
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/JSObjectDefaults.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/JSObjectDefaults.java
new file mode 100644
index 00000000..4ef14399
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/JSObjectDefaults.java
@@ -0,0 +1,121 @@
+package com.hemangkumar.capacitorgooglemaps;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.getcapacitor.JSObject;
+import com.getcapacitor.PluginCall;
+
+import org.json.JSONException;
+
+import java.util.HashMap;
+
+public abstract class JSObjectDefaults {
+ private final HashMap defaults;
+
+ private final HashMap actualValues = new HashMap(){};
+
+ public JSObjectDefaults(HashMap defaults) {
+ this.defaults = defaults;
+ }
+
+ private Object getObject(String key) {
+ // get default value
+ Object defaultObject = this.defaults.get(key);
+
+ // then get actual value
+ if (this.actualValues.containsKey(key)) {
+ Object object = this.actualValues.get(key);
+ if (object != null) {
+ return object;
+ }
+ }
+
+ if (defaultObject != null) {
+ return defaultObject;
+ }
+
+ return null;
+ }
+
+ public boolean getBoolean(String key) {
+ Object object = getObject(key);
+
+ if (object != null) {
+ return (boolean) object;
+ }
+
+ return false;
+ }
+
+ public void updateFromJSObject(@Nullable JSObject jsObject) {
+ if (jsObject != null) {
+ for (HashMap.Entry entry : defaults.entrySet()) {
+ String key = entry.getKey();
+ Object defaultValue = entry.getValue();
+
+ if (defaultValue instanceof Boolean) {
+ if (jsObject.has(key)) {
+ Boolean newValue = jsObject.getBoolean(key, (Boolean) defaultValue);
+ if (newValue == null) {
+ newValue = (Boolean) defaultValue;
+ }
+ actualValues.put(key, (boolean) newValue);
+ }
+ }
+ }
+ }
+ }
+
+ @NonNull
+ public static JSObject getJSObjectSafe(JSObject jsObject, @NonNull String name, @NonNull JSObject defaultValue) {
+ JSObject returnedJsObject = jsObject.getJSObject(name);
+ if (returnedJsObject != null) {
+ return returnedJsObject;
+ }
+ return defaultValue;
+ }
+
+ @NonNull
+ public static JSObject getJSObjectSafe(PluginCall call, @NonNull String name, @NonNull JSObject defaultValue) {
+ if (call != null) {
+ JSObject jsObject = call.getObject(name, defaultValue);
+ if (jsObject != null) {
+ return jsObject;
+ }
+ }
+ return new JSObject();
+ }
+
+ @NonNull
+ public static Double getDoubleSafe(JSObject jsObject, @NonNull String name, @NonNull Double defaultValue) {
+ try {
+ return jsObject.getDouble(name);
+ } catch (JSONException ignored) {}
+ return defaultValue;
+ }
+
+ @NonNull
+ public static Integer getIntegerSafe(JSObject jsObject, @NonNull String name, @NonNull Integer defaultValue) {
+ try {
+ return jsObject.getInt(name);
+ } catch (JSONException ignored) {}
+ return defaultValue;
+ }
+
+ @NonNull
+ public static Float getFloatSafe(JSObject jsObject, @NonNull String name, @NonNull Float defaultValue) {
+ Double returnedDouble = JSObjectDefaults.getDoubleSafe(jsObject, name, (double) defaultValue);
+ return returnedDouble.floatValue();
+ }
+
+ @NonNull
+ public static Boolean getBooleanSafe(JSObject jsObject, @NonNull String name, @NonNull Boolean defaultValue) {
+ Boolean returnedBoolean = jsObject.getBoolean(name, false);
+ if (returnedBoolean != null) {
+ return returnedBoolean;
+ }
+ return defaultValue;
+ }
+}
+
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/MapCameraPosition.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/MapCameraPosition.java
new file mode 100644
index 00000000..48e11322
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/MapCameraPosition.java
@@ -0,0 +1,95 @@
+package com.hemangkumar.capacitorgooglemaps;
+
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.getcapacitor.JSObject;
+import com.google.android.gms.maps.model.CameraPosition;
+import com.google.android.gms.maps.model.LatLng;
+
+import org.json.JSONException;
+
+public class MapCameraPosition {
+ public CameraPosition cameraPosition;
+
+ public MapCameraPosition() {
+ this.cameraPosition = CameraPosition.builder().target(new LatLng(0, 0)).build();
+ }
+
+ public void updateFromJSObject(@Nullable JSObject cameraPosition, CameraPosition previousCameraPosition) {
+ CameraPosition.Builder cameraPositionBuilder;
+ if (previousCameraPosition != null) {
+ // use given cameraPosition as the base
+ cameraPositionBuilder = new CameraPosition.Builder(previousCameraPosition);
+ } else if (this.cameraPosition != null) {
+ // use oldCameraPosition as the base
+ cameraPositionBuilder = new CameraPosition.Builder(this.cameraPosition);
+ } else {
+ cameraPositionBuilder = new CameraPosition.Builder();
+ }
+
+ LatLng latLng = null;
+ if (cameraPosition != null) {
+ JSObject target = cameraPosition.getJSObject("target");
+
+ if (target != null) {
+ try {
+ if (target.has("latitude") && target.has("longitude")) {
+ double latitude = target.getDouble("latitude");
+ double longitude = target.getDouble("longitude");
+
+ latLng = new LatLng(latitude, longitude);
+ cameraPositionBuilder.target(latLng);
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (cameraPosition.has("bearing")) {
+ try {
+ float bearing = (float) cameraPosition.getDouble("bearing");
+ cameraPositionBuilder.bearing(bearing);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (cameraPosition.has("tilt")) {
+ try {
+ float tilt = (float) cameraPosition.getDouble("tilt");
+ if (tilt < 0.0F || tilt > 90.0F) {
+ Log.d("GoogleMap", "Tilt needs to be between 0 and 90 inclusive: " + tilt);
+ } else {
+ cameraPositionBuilder.tilt(tilt);
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (cameraPosition.has("zoom")) {
+ try {
+ float zoom = (float) cameraPosition.getDouble("zoom");
+ cameraPositionBuilder.zoom(zoom);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ if (this.cameraPosition == null && latLng == null) {
+ // at the very least a camera target should be set
+ Log.d("GoogleMap", "Target of camera has been set to (0,0) automatically.");
+ latLng = new LatLng(0, 0);
+ cameraPositionBuilder.target(latLng);
+ }
+
+ try {
+ this.cameraPosition = cameraPositionBuilder.build();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/MapPreferences.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/MapPreferences.java
new file mode 100644
index 00000000..fa3acfce
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/MapPreferences.java
@@ -0,0 +1,72 @@
+package com.hemangkumar.capacitorgooglemaps;
+
+import androidx.annotation.Nullable;
+
+import com.getcapacitor.JSObject;
+import com.google.android.gms.maps.GoogleMapOptions;
+import com.hemangkumar.capacitorgooglemaps.mapsutility.MapPreferencesAppearance;
+import com.hemangkumar.capacitorgooglemaps.mapsutility.MapPreferencesControls;
+import com.hemangkumar.capacitorgooglemaps.mapsutility.MapPreferencesGestures;
+
+public class MapPreferences {
+ public MapPreferencesGestures gestures;
+ public MapPreferencesControls controls;
+ public MapPreferencesAppearance appearance;
+
+ public MapPreferences() {
+ this.gestures = new MapPreferencesGestures();
+ this.controls = new MapPreferencesControls();
+ this.appearance = new MapPreferencesAppearance();
+ }
+
+ public void updateFromJSObject(@Nullable JSObject preferences) {
+ if (preferences != null) {
+ // update gestures
+ JSObject gesturesObject = preferences.getJSObject("gestures");
+ this.gestures.updateFromJSObject(gesturesObject);
+ // update controls
+ JSObject controlsObject = preferences.getJSObject("controls");
+ this.controls.updateFromJSObject(controlsObject);
+ // update appearance
+ JSObject appearanceObject = preferences.getJSObject("appearance");
+ this.appearance.updateFromJSObject(appearanceObject);
+ }
+ }
+
+ public GoogleMapOptions generateGoogleMapOptions() {
+ GoogleMapOptions googleMapOptions = new GoogleMapOptions();
+
+ // set gestures
+ if (this.gestures != null) {
+ googleMapOptions.rotateGesturesEnabled(gestures.getBoolean(MapPreferencesGestures.ROTATE_ALLOWED_KEY));
+ googleMapOptions.scrollGesturesEnabled(gestures.getBoolean(MapPreferencesGestures.SCROLL_ALLOWED_KEY));
+ googleMapOptions.scrollGesturesEnabledDuringRotateOrZoom(gestures.getBoolean(MapPreferencesGestures.SCROLL_ALLOWED_DURING_ROTATE_OR_ZOOM_KEY));
+ googleMapOptions.tiltGesturesEnabled(gestures.getBoolean(MapPreferencesGestures.TILT_ALLOWED_KEY));
+ googleMapOptions.zoomGesturesEnabled(gestures.getBoolean(MapPreferencesGestures.ZOOM_ALLOWED_KEY));
+ }
+
+ // set controls
+ if (this.controls != null) {
+ googleMapOptions.compassEnabled(controls.getBoolean(MapPreferencesControls.COMPASS_BUTTON_KEY));
+ googleMapOptions.mapToolbarEnabled(controls.getBoolean(MapPreferencesControls.MAP_TOOLBAR_KEY));
+ googleMapOptions.zoomControlsEnabled(controls.getBoolean(MapPreferencesControls.ZOOM_BUTTONS_KEY));
+
+ // controls.isIndoorLevelPickerEnabled can only be set through `UiSettings`
+ // controls.isMyLocationButtonEnabled can only be set through `UiSettings`
+ }
+
+ // set appearance
+ if (this.appearance != null) {
+ // set mapType
+ googleMapOptions.mapType(this.appearance.type);
+
+ // appearance.style can only be set through `GoogleMap`
+ // appearance.isIndoorShown can only be set through `GoogleMap`
+ // appearance.isBuildingsShown can only be set through `GoogleMap`
+ // appearance.isMyLocationDotShown can only be set through `GoogleMap`
+ // appearance.isTrafficShown can only be set through `GoogleMap`
+ }
+
+ return googleMapOptions;
+ }
+}
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/Events.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/Events.java
new file mode 100644
index 00000000..38a09692
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/Events.java
@@ -0,0 +1,23 @@
+package com.hemangkumar.capacitorgooglemaps.mapsutility;
+
+public abstract class Events {
+
+ public static final String EVENT_DID_TAP_INFO_WINDOW = "didTapInfoWindow";
+ public static final String EVENT_DID_CLOSE_INFO_WINDOW = "didCloseInfoWindow";
+ public static final String EVENT_DID_TAP_MAP = "didTapMap";
+ public static final String EVENT_DID_LONG_PRESS_MAP = "didLongPressMap";
+ public static final String EVENT_DID_TAP_SINGLE_MARKER = "didTapMarker";
+ public static final String EVENT_DID_BEGIN_DRAGGING_MARKER = "didBeginDraggingMarker";
+ public static final String EVENT_DID_DRAG_MARKER = "didDragMarker";
+ public static final String EVENT_DID_END_DRAGGING_MARKER = "didEndDraggingMarker";
+ public static final String EVENT_DID_TAP_CLUSTER = "didTapCluster";
+ public static final String EVENT_DID_TAP_CLUSTER_INFO_WINDOW = "didTapClusterInfoWindow";
+ public static final String EVENT_DID_TAP_MARKER = "didTapClusterItem";
+ public static final String EVENT_DID_TAP_CLUSTER_ITEM_INFO_WINDOW = "didTapClusterItemInfoWindow";
+ public static final String EVENT_DID_TAP_MY_LOCATION_BUTTON = "didTapMyLocationButton";
+ public static final String EVENT_DID_TAP_MY_LOCATION_DOT = "didTapMyLocationDot";
+ public static final String EVENT_DID_TAP_POI = "didTapPoi";
+ public static final String EVENT_DID_BEGIN_MOVING_CAMERA = "didBeginMovingCamera";
+ public static final String EVENT_DID_MOVE_CAMERA = "didMoveCamera";
+ public static final String EVENT_DID_END_MOVING_CAMERA = "didEndMovingCamera";
+}
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/MapPreferencesAppearance.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/MapPreferencesAppearance.java
new file mode 100644
index 00000000..0d6dbc17
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/MapPreferencesAppearance.java
@@ -0,0 +1,67 @@
+package com.hemangkumar.capacitorgooglemaps.mapsutility;
+
+import androidx.annotation.Nullable;
+
+import com.getcapacitor.JSObject;
+import com.google.android.gms.maps.model.MapStyleOptions;
+
+public class MapPreferencesAppearance {
+ public Integer type;
+ public MapStyleOptions style;
+ public Boolean isBuildingsShown;
+ public Boolean isIndoorShown;
+ public Boolean isMyLocationDotShown;
+ public Boolean isTrafficShown;
+
+ public static final String TYPE_KEY = "type";
+ public static final String STYLE_KEY = "style";
+ public static final String BUILDINGS_SHOWN_KEY = "isBuildingsShown";
+ public static final String INDOOR_SHOWN_KEY = "isIndoorShown";
+ public static final String MY_LOCATION_DOT_SHOWN_KEY = "isMyLocationDotShown";
+ public static final String TRAFFIC_SHOWN_KEY = "isTrafficShown";
+
+ public MapPreferencesAppearance() {
+ this.type = 1;
+ this.style = null;
+ this.isBuildingsShown = true;
+ this.isIndoorShown = true;
+ this.isMyLocationDotShown = false;
+ this.isTrafficShown = false;
+ }
+
+ public void updateFromJSObject(@Nullable JSObject jsObject) {
+ if (jsObject != null) {
+ // update mapType
+ if (jsObject.has(TYPE_KEY)) {
+ Integer mapType = jsObject.getInteger(TYPE_KEY, 1);
+ if (mapType != null) {
+ if (mapType < 0 || mapType > 4) {
+ mapType = 1;
+ }
+ this.type = mapType;
+ }
+ }
+ if (jsObject.has(STYLE_KEY)) {
+ String mapStyle = jsObject.getString(STYLE_KEY, null);
+ if (mapStyle == null) {
+ this.style = null;
+ } else {
+ this.style = new MapStyleOptions(mapStyle);
+ }
+ }
+ if (jsObject.has(BUILDINGS_SHOWN_KEY)) {
+ this.isBuildingsShown = jsObject.getBool(BUILDINGS_SHOWN_KEY);
+ }
+ if (jsObject.has(INDOOR_SHOWN_KEY)) {
+ this.isIndoorShown = jsObject.getBool(INDOOR_SHOWN_KEY);
+ }
+ if (jsObject.has(MY_LOCATION_DOT_SHOWN_KEY)) {
+ this.isMyLocationDotShown = jsObject.getBool(MY_LOCATION_DOT_SHOWN_KEY);
+ }
+ if (jsObject.has(TRAFFIC_SHOWN_KEY)) {
+ this.isTrafficShown = jsObject.getBool(TRAFFIC_SHOWN_KEY);
+ }
+ }
+ }
+}
+
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/MapPreferencesControls.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/MapPreferencesControls.java
new file mode 100644
index 00000000..b01d45c3
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/MapPreferencesControls.java
@@ -0,0 +1,25 @@
+package com.hemangkumar.capacitorgooglemaps.mapsutility;
+
+import com.hemangkumar.capacitorgooglemaps.JSObjectDefaults;
+
+import java.util.HashMap;
+
+public class MapPreferencesControls extends JSObjectDefaults {
+ public static final String COMPASS_BUTTON_KEY = "isCompassButtonEnabled";
+ public static final String INDOOR_LEVEL_PICKER_KEY = "isIndoorLevelPickerEnabled";
+ public static final String MAP_TOOLBAR_KEY = "isMapToolbarEnabled";
+ public static final String MY_LOCATION_BUTTON_KEY = "isMyLocationButtonEnabled";
+ public static final String ZOOM_BUTTONS_KEY = "isZoomButtonsEnabled";
+
+ public MapPreferencesControls() {
+ super(new HashMap(){{
+ put(COMPASS_BUTTON_KEY, Boolean.FALSE);
+ put(INDOOR_LEVEL_PICKER_KEY, Boolean.FALSE);
+ put(MAP_TOOLBAR_KEY, Boolean.FALSE);
+ put(MY_LOCATION_BUTTON_KEY, Boolean.FALSE);
+ put(ZOOM_BUTTONS_KEY, Boolean.FALSE);
+ }});
+ }
+
+
+}
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/MapPreferencesGestures.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/MapPreferencesGestures.java
new file mode 100644
index 00000000..a224e491
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/mapsutility/MapPreferencesGestures.java
@@ -0,0 +1,23 @@
+package com.hemangkumar.capacitorgooglemaps.mapsutility;
+
+import com.hemangkumar.capacitorgooglemaps.JSObjectDefaults;
+
+import java.util.HashMap;
+
+public class MapPreferencesGestures extends JSObjectDefaults {
+ public static final String ROTATE_ALLOWED_KEY = "isRotateAllowed";
+ public static final String SCROLL_ALLOWED_KEY = "isScrollAllowed";
+ public static final String SCROLL_ALLOWED_DURING_ROTATE_OR_ZOOM_KEY = "isScrollAllowedDuringRotateOrZoom";
+ public static final String TILT_ALLOWED_KEY = "isTiltAllowed";
+ public static final String ZOOM_ALLOWED_KEY = "isZoomAllowed";
+
+ public MapPreferencesGestures() {
+ super(new HashMap(){{
+ put(ROTATE_ALLOWED_KEY, Boolean.TRUE);
+ put(SCROLL_ALLOWED_KEY, Boolean.TRUE);
+ put(SCROLL_ALLOWED_DURING_ROTATE_OR_ZOOM_KEY, Boolean.TRUE);
+ put(TILT_ALLOWED_KEY, Boolean.TRUE);
+ put(ZOOM_ALLOWED_KEY, Boolean.TRUE);
+ }});
+ }
+}
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/marker/CustomMarker.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/marker/CustomMarker.java
new file mode 100644
index 00000000..8a84692b
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/marker/CustomMarker.java
@@ -0,0 +1,210 @@
+package com.hemangkumar.capacitorgooglemaps.marker;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.getcapacitor.JSObject;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.Marker;
+import com.google.android.gms.maps.model.MarkerOptions;
+import com.google.maps.android.clustering.ClusterItem;
+import com.hemangkumar.capacitorgooglemaps.JSObjectDefaults;
+
+import org.json.JSONException;
+
+import java.util.Objects;
+import java.util.UUID;
+
+public class CustomMarker implements ClusterItem {
+ // generate id for the just added marker,
+ // put this marker into a hashmap with the corresponding id,
+ // so we can retrieve the marker by id later on
+ private String id = UUID.randomUUID().toString();
+
+ private final MarkerOptions markerOptions = new MarkerOptions();
+ private JSObject tag = new JSObject();
+
+ private int markerCategoryId;
+
+ public void updateFromJSObject(JSObject preferences) {
+ final JSObject position = JSObjectDefaults.getJSObjectSafe(preferences, "position", new JSObject());
+ final Double latitude = JSObjectDefaults.getDoubleSafe(position, "latitude", 0d);
+ final Double longitude = JSObjectDefaults.getDoubleSafe(position, "longitude", 0d);
+ LatLng latLng = new LatLng(latitude, longitude);
+
+ final String markerId = preferences.getString("id", null);
+ final String title = preferences.getString("title", "");
+ final String snippet = preferences.getString("snippet", "");
+ final Float opacity = JSObjectDefaults.getFloatSafe(preferences, "opacity", 1f);
+ final Boolean isFlat = JSObjectDefaults.getBooleanSafe(preferences,"isFlat", false);
+ final Boolean isDraggable = JSObjectDefaults.getBooleanSafe(preferences,"isDraggable", false);
+ final Integer markerCategoryId = JSObjectDefaults.getIntegerSafe(preferences, "category", 0);
+
+ // if marker id setted than use it
+ if(markerId != null)
+ this.id = markerId;
+
+ this.markerCategoryId = markerCategoryId;
+
+ this.setMetadata(JSObjectDefaults.getJSObjectSafe(preferences, "metadata", new JSObject()));
+
+ this.markerOptions.position(latLng);
+ this.markerOptions.title(title);
+ this.markerOptions.snippet(snippet);
+ this.markerOptions.alpha(opacity);
+ this.markerOptions.flat(isFlat);
+ this.markerOptions.draggable(isDraggable);
+
+
+
+ this.markerOptions.zIndex(21);
+ }
+
+
+ private void setMetadata(@NonNull JSObject jsObject) {
+ JSObject tag = new JSObject();
+ // set id to tag
+ tag.put("id", this.id);
+ // set iconId to tag
+ tag.put("iconId", this.markerCategoryId);
+ // then set metadata to tag
+ tag.put("metadata", jsObject);
+ // save in tag variable
+ this.tag = tag;
+ }
+
+ public static JSObject getResultForMarker(Marker marker, String mapId) {
+ // initialize JSObjects to return
+ JSObject result = new JSObject();
+ JSObject positionResult = new JSObject();
+ JSObject markerResult = new JSObject();
+
+ // get map id
+ positionResult.put("mapId", mapId);
+
+ // get position values
+ positionResult.put("latitude", marker.getPosition().latitude);
+ positionResult.put("longitude", marker.getPosition().longitude);
+
+ // get marker specific values
+// markerResult.put("title", marker.getTitle());
+// markerResult.put("snippet", marker.getSnippet());
+// markerResult.put("opacity", marker.getAlpha());
+// markerResult.put("isFlat", marker.isFlat());
+// markerResult.put("isDraggable", marker.isDraggable());
+ markerResult.put("metadata", new JSObject());
+
+ JSObject tag = (JSObject) marker.getTag();
+ if (tag != null) {
+ // get and set markerId to marker
+ String markerId = tag.getString("id");
+ markerResult.put("id", markerId);
+
+ // get and set iconId to marker
+ int iconId = JSObjectDefaults.getIntegerSafe(tag, "iconId", -1);
+ markerResult.put("iconId", iconId);
+
+ // get and set metadata to marker
+ try {
+ JSObject metadata = tag.getJSObject("metadata", new JSObject());
+ markerResult.put("metadata", metadata);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // return result
+ result.put("position", positionResult);
+ result.put("marker", markerResult);
+
+ return result;
+ }
+
+ public static JSObject getResultForMarker(CustomMarker marker, String mapId) {
+
+ // initialize JSObjects to return
+ JSObject result = new JSObject();
+ JSObject positionResult = new JSObject();
+ JSObject markerResult = new JSObject();
+
+ // get map id
+ positionResult.put("mapId", mapId);
+
+ // get position values
+ positionResult.put("latitude", marker.getPosition().latitude);
+ positionResult.put("longitude", marker.getPosition().longitude);
+
+ // get marker specific values
+// markerResult.put("title", marker.getTitle());
+// markerResult.put("snippet", marker.getSnippet());
+// markerResult.put("opacity", marker.markerOptions.getAlpha());
+// markerResult.put("isFlat", marker.markerOptions.isFlat());
+// markerResult.put("isDraggable", marker.markerOptions.isDraggable());
+ markerResult.put("metadata", new JSObject());
+
+ JSObject tag = (JSObject) marker.tag;
+ if (tag != null) {
+ // get and set markerId to marker
+ String markerId = tag.getString("id");
+ markerResult.put("id", markerId);
+
+ // get and set iconId to marker
+ int iconId = JSObjectDefaults.getIntegerSafe(tag, "iconId", -1);
+ markerResult.put("iconId", iconId);
+
+ // get and set metadata to marker
+ try {
+ JSObject metadata = tag.getJSObject("metadata", new JSObject());
+ markerResult.put("metadata", metadata);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // return result
+ result.put("position", positionResult);
+ result.put("marker", markerResult);
+
+ return result;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ @NonNull
+ @Override
+ public LatLng getPosition() {
+ return this.markerOptions.getPosition();
+ }
+
+ @Nullable
+ @Override
+ public String getTitle() {
+ return this.markerOptions.getTitle();
+ }
+
+ @Nullable
+ @Override
+ public String getSnippet() {
+ return this.markerOptions.getSnippet();
+ }
+
+ public MarkerCategory getMarkerCategory() {
+ return MarkerCategory.getMarkerCategoryById(this.markerCategoryId);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CustomMarker that = (CustomMarker) o;
+ return id.equals(that.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+
+}
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/marker/MarkerCategory.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/marker/MarkerCategory.java
new file mode 100644
index 00000000..abd9c263
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/marker/MarkerCategory.java
@@ -0,0 +1,55 @@
+package com.hemangkumar.capacitorgooglemaps.marker;
+
+import android.graphics.Bitmap;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MarkerCategory {
+
+ private int id;
+ private String title;
+ private Bitmap icon;
+
+ static private final Map markerCategories = new HashMap();
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public Bitmap getIcon() {
+ return icon;
+ }
+
+ public void setIcon(Bitmap icon) {
+ this.icon = icon;
+ }
+
+ public MarkerCategory(int id, String title, Bitmap icon) {
+ this.id = id;
+ this.title = title;
+ this.icon = icon;
+
+ markerCategories.put(this.id, this);
+ }
+
+ public static MarkerCategory getMarkerCategoryById(int id) {
+ if(markerCategories.containsKey(id)) {
+ return markerCategories.get(id);
+ } else {
+ return markerCategories.get(0);
+ }
+ }
+}
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/marker/OwnIconRenderer.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/marker/OwnIconRenderer.java
new file mode 100644
index 00000000..5e7e6291
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/marker/OwnIconRenderer.java
@@ -0,0 +1,168 @@
+package com.hemangkumar.capacitorgooglemaps.marker;
+
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.model.BitmapDescriptor;
+import com.google.android.gms.maps.model.BitmapDescriptorFactory;
+import com.google.android.gms.maps.model.Marker;
+import com.google.android.gms.maps.model.MarkerOptions;
+import com.google.maps.android.clustering.Cluster;
+import com.google.maps.android.clustering.ClusterManager;
+import com.google.maps.android.clustering.view.DefaultClusterRenderer;
+import com.google.maps.android.ui.IconGenerator;
+import com.hemangkumar.capacitorgooglemaps.capacitorgooglemaps.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class OwnIconRenderer extends DefaultClusterRenderer {
+
+ // -- constants --
+ public static final int MIN_COUNT_ELEMENTS_IN_CLUSTER = 2;
+
+
+ // cluster sizes must be equal buckets count
+ private static final int[] CLUSTER_SIZES = { 64, 68, 72, 76, 80, 84, 88, 92 };
+ private static final int[] BUCKETS = { 5, 10, 15, 20, 25, 30, 35, 40 };
+
+
+ // == fields ==
+ private final IconGenerator mIconGenerator;
+ private final ImageView mImageView;
+ private final int mDimension;
+
+ private Context context;
+ private GoogleMap map;
+
+ private List mClusterIconGenerators = new ArrayList<>();
+
+ // == constructor ==
+ /**
+ * Draws profile photos inside markers (using IconGenerator).
+ * When there are multiple people in the cluster, draw multiple photos (using MultiDrawable).
+ */
+ public OwnIconRenderer(Context context, Activity activity, GoogleMap map,
+ ClusterManager clusterManager) {
+ super(context, map, clusterManager);
+ this.context = context;
+ this.map = map;
+
+ mIconGenerator = new IconGenerator(context);
+
+ final float scale = context.getResources().getDisplayMetrics().density;
+
+ for (int i = 0; i < BUCKETS.length; i++) {
+ int dps = CLUSTER_SIZES[i];
+ int pixels = (int) (dps * scale + 0.5f);
+
+ View multiProfile = activity.getLayoutInflater().inflate(R.layout.multi_profile, null);
+ ImageView mClusterImageView = multiProfile.findViewById(R.id.image);
+ mClusterImageView.setImageResource(R.drawable.cluster_icon);
+
+ multiProfile.setLayoutParams(new ViewGroup.LayoutParams(pixels, pixels));
+
+ IconGenerator mClusterIconGenerator = new IconGenerator(context);
+
+ mClusterIconGenerator.setContentView(multiProfile);
+ mClusterIconGenerator.setBackground(null);
+
+ mClusterIconGenerators.add(mClusterIconGenerator);
+ }
+
+ mImageView = new ImageView(context);
+ mDimension = (int) context.getResources().getDimension(R.dimen.custom_profile_image);
+ mImageView.setLayoutParams(new ViewGroup.LayoutParams(mDimension, mDimension));
+ int padding = (int) context.getResources().getDimension(R.dimen.custom_profile_padding);
+ mImageView.setPadding(padding, padding, padding, padding);
+ mIconGenerator.setContentView(mImageView);
+ mIconGenerator.setBackground(null);
+
+ }
+
+ @Override
+ protected void onBeforeClusterItemRendered(@NonNull CustomMarker customMarker,
+ MarkerOptions markerOptions) {
+ // Draw a single marker - set the info window to show their name
+ markerOptions
+ .icon(getItemIcon(customMarker))
+ .title(customMarker.getTitle());
+ }
+
+
+ @Override
+ protected void onClusterItemUpdated(@NonNull CustomMarker customMarker, Marker marker) {
+ // Same implementation as onBeforeClusterItemRendered() (to update cached markers)
+ marker.setIcon(getItemIcon(customMarker));
+ marker.setTitle(customMarker.getTitle());
+ }
+
+
+ private BitmapDescriptor getItemIcon(CustomMarker customMarker) {
+ if(customMarker.getMarkerCategory().getIcon() == null) {
+ return null;
+ }
+
+
+ mImageView.setImageBitmap(customMarker.getMarkerCategory().getIcon());
+ Bitmap icon = mIconGenerator.makeIcon();
+ return BitmapDescriptorFactory.fromBitmap(icon);
+ }
+
+ @Override
+ protected void onBeforeClusterRendered(@NonNull Cluster cluster, MarkerOptions markerOptions) {
+ // Note: this method runs on the UI thread. Don't spend too much time in here (like in this example).
+ markerOptions
+ .zIndex((int) map.getCameraPosition().zoom)
+ .icon(getClusterIcon(cluster));
+ }
+
+
+ @Override
+ protected void onClusterUpdated(@NonNull Cluster cluster, Marker marker) {
+ // Same implementation as onBeforeClusterRendered() (to update cached markers)
+ marker.setIcon(getClusterIcon(cluster));
+ }
+
+ /**
+ * Note: this
+ * method runs on the UI thread. Don't spend too much time in here (like in this example).
+ *
+ * @param cluster cluster to draw a BitmapDescriptor for
+ * @return a BitmapDescriptor representing a cluster
+ */
+ private BitmapDescriptor getClusterIcon(Cluster cluster) {
+ int index = getBucket(cluster.getSize());
+ Bitmap icon = mClusterIconGenerators.get(index).makeIcon(String.valueOf(cluster.getSize()));
+ return BitmapDescriptorFactory.fromBitmap(icon);
+ }
+
+ /**
+ * Gets the "bucket" for a particular cluster. By default, uses the number of
+ * points within the
+ * cluster, bucketed to some set points.
+ */
+ private int getBucket(@NonNull int sizeOfCluster) {
+ int index = 0;
+ while ((index+1 < BUCKETS.length) && (BUCKETS[index+1] <= sizeOfCluster)) {
+ index++;
+ }
+ return index;
+ }
+
+ @Override
+ protected boolean shouldRenderAsCluster(Cluster cluster) {
+ // Always render clusters.
+ return cluster.getSize() >= MIN_COUNT_ELEMENTS_IN_CLUSTER;
+ }
+
+}
\ No newline at end of file
diff --git a/android/src/main/java/com/hemangkumar/capacitorgooglemaps/utils/BoundingRect.java b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/utils/BoundingRect.java
new file mode 100644
index 00000000..7e317a88
--- /dev/null
+++ b/android/src/main/java/com/hemangkumar/capacitorgooglemaps/utils/BoundingRect.java
@@ -0,0 +1,37 @@
+package com.hemangkumar.capacitorgooglemaps.utils;
+
+import androidx.annotation.Nullable;
+
+import com.getcapacitor.JSObject;
+
+public class BoundingRect {
+ public Integer width;
+ public Integer height;
+ public Integer x;
+ public Integer y;
+
+ public BoundingRect() {
+ this.width = 500;
+ this.height = 500;
+ this.x = 0;
+ this.y = 0;
+ }
+
+ public void updateFromJSObject(@Nullable JSObject jsObject) {
+ if (jsObject != null) {
+ if (jsObject.has("width")) {
+ this.width = jsObject.getInteger("width");
+ }
+ if (jsObject.has("height")) {
+ this.height = jsObject.getInteger("height");
+ }
+ if (jsObject.has("x")) {
+ this.x = jsObject.getInteger("x");
+ }
+ if (jsObject.has("y")) {
+ this.y = jsObject.getInteger("y");
+ }
+ }
+ }
+}
+
diff --git a/android/src/main/res/drawable/cluster.png b/android/src/main/res/drawable/cluster.png
new file mode 100644
index 00000000..43fcbab0
Binary files /dev/null and b/android/src/main/res/drawable/cluster.png differ
diff --git a/android/src/main/res/drawable/cluster_icon.png b/android/src/main/res/drawable/cluster_icon.png
new file mode 100644
index 00000000..bdba2e1c
Binary files /dev/null and b/android/src/main/res/drawable/cluster_icon.png differ
diff --git a/android/src/main/res/drawable/gran.jpg b/android/src/main/res/drawable/gran.jpg
new file mode 100644
index 00000000..b2b4fcfc
Binary files /dev/null and b/android/src/main/res/drawable/gran.jpg differ
diff --git a/android/src/main/res/drawable/ic_launcher.png b/android/src/main/res/drawable/ic_launcher.png
new file mode 100644
index 00000000..71ae1e72
Binary files /dev/null and b/android/src/main/res/drawable/ic_launcher.png differ
diff --git a/android/src/main/res/drawable/john.jpg b/android/src/main/res/drawable/john.jpg
new file mode 100644
index 00000000..131a3e4d
Binary files /dev/null and b/android/src/main/res/drawable/john.jpg differ
diff --git a/android/src/main/res/drawable/mechanic.jpg b/android/src/main/res/drawable/mechanic.jpg
new file mode 100644
index 00000000..c8d26baf
Binary files /dev/null and b/android/src/main/res/drawable/mechanic.jpg differ
diff --git a/android/src/main/res/drawable/ruth.jpg b/android/src/main/res/drawable/ruth.jpg
new file mode 100644
index 00000000..7a91fe2c
Binary files /dev/null and b/android/src/main/res/drawable/ruth.jpg differ
diff --git a/android/src/main/res/drawable/stefan.jpg b/android/src/main/res/drawable/stefan.jpg
new file mode 100644
index 00000000..3926a344
Binary files /dev/null and b/android/src/main/res/drawable/stefan.jpg differ
diff --git a/android/src/main/res/drawable/teacher.jpg b/android/src/main/res/drawable/teacher.jpg
new file mode 100644
index 00000000..1147f664
Binary files /dev/null and b/android/src/main/res/drawable/teacher.jpg differ
diff --git a/android/src/main/res/drawable/turtle.jpg b/android/src/main/res/drawable/turtle.jpg
new file mode 100644
index 00000000..15fc2e96
Binary files /dev/null and b/android/src/main/res/drawable/turtle.jpg differ
diff --git a/android/src/main/res/drawable/walter.jpg b/android/src/main/res/drawable/walter.jpg
new file mode 100644
index 00000000..f1d34ec1
Binary files /dev/null and b/android/src/main/res/drawable/walter.jpg differ
diff --git a/android/src/main/res/drawable/yeats.jpg b/android/src/main/res/drawable/yeats.jpg
new file mode 100644
index 00000000..45651c31
Binary files /dev/null and b/android/src/main/res/drawable/yeats.jpg differ
diff --git a/android/src/main/res/layout/multi_profile.xml b/android/src/main/res/layout/multi_profile.xml
new file mode 100644
index 00000000..dd9ce2cd
--- /dev/null
+++ b/android/src/main/res/layout/multi_profile.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/android/src/main/res/values/dimensions.xml b/android/src/main/res/values/dimensions.xml
new file mode 100644
index 00000000..b56c8236
--- /dev/null
+++ b/android/src/main/res/values/dimensions.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ 64dp
+ 0dp
+
diff --git a/ios/Plugin/Controllers/CustomMapViewController.swift b/ios/Plugin/Controllers/CustomMapViewController.swift
new file mode 100644
index 00000000..59500841
--- /dev/null
+++ b/ios/Plugin/Controllers/CustomMapViewController.swift
@@ -0,0 +1,426 @@
+//
+// CustomMapViewController.swift
+// Capacitor
+//
+// Created by Admin on 27.12.2021.
+//
+
+import Capacitor
+import GoogleMaps
+import GoogleMapsUtils
+import UIKit
+
+let kClusterItemCount = 10000
+
+
+class CustomMapViewController: UIViewController, GMSMapViewDelegate {
+
+ // -- constants --
+ private var EPSILON : Double = 0.00001;
+
+ var customMapViewEvents: CustomMapViewEvents!;
+
+ var id: String!;
+
+ var mapView: GMSMapView!;
+
+ var locationButton : UIButton!;
+
+
+
+ var boundingRect = BoundingRect();
+ var mapCameraPosition = MapCameraPosition();
+ var mapPreferences = MapPreferences();
+
+
+ private var mClusterManager: GMUClusterManager!
+ private var mMarkersList: [CustomMarker]!
+
+
+ var savedCallbackIdForCreate: String!;
+
+ var savedCallbackIdForDidTapInfoWindow: String!;
+
+ var savedCallbackIdForDidCloseInfoWindow: String!;
+
+ var savedCallbackIdForDidTapMap: String!;
+
+ var savedCallbackIdForDidLongPressMap: String!;
+
+ var savedCallbackIdForDidTapMarker: String!;
+ var preventDefaultForDidTapMarker: Bool = false;
+
+ var savedCallbackIdForDidTapCluster: String!;
+
+ var savedCallbackIdForDidTapMyLocationButton: String!;
+ var preventDefaultForDidTapMyLocationButton: Bool = false;
+
+ var savedCallbackIdForDidTapMyLocationDot: String!;
+
+
+ private var widthOfTheMapCameraAnimationUnclustering : Double = 0;
+ private var heightOfTheMapCameraAnimationUnclustering : Double = 0;
+
+ // This allows you to initialise your custom UIViewController without a nib or bundle.
+ convenience init(customMapViewEvents: CustomMapViewEvents) {
+ self.init(nibName:nil, bundle:nil)
+ self.customMapViewEvents = customMapViewEvents
+ self.id = NSUUID().uuidString.lowercased()
+ }
+
+
+ // This extends the superclass.
+ override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
+ super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ super.init(coder: aDecoder)
+ }
+
+
+ override func loadView() {
+ let frame = CGRect(x: self.boundingRect.x, y: self.boundingRect.y, width: self.boundingRect.width, height: self.boundingRect.height);
+
+ let camera = GMSCameraPosition.camera(withLatitude: self.mapCameraPosition.latitude, longitude: self.mapCameraPosition.longitude, zoom: self.mapCameraPosition.zoom, bearing: self.mapCameraPosition.bearing, viewingAngle: self.mapCameraPosition.tilt);
+
+ self.mapView = GMSMapView.map(withFrame: frame, camera: camera);
+
+ self.view = mapView;
+
+
+ widthOfTheMapCameraAnimationUnclustering = boundingRect.width * 0.25;
+ heightOfTheMapCameraAnimationUnclustering = boundingRect.height * 0.125;
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad();
+
+ setupClustering()
+
+ mMarkersList = [CustomMarker]();
+
+ // Register self to listen to GMSMapViewDelegate events.
+ mClusterManager.setMapDelegate(self)
+
+ self.invalidateMap();
+
+ self.customMapViewEvents.lastResultForCallbackId(callbackId: savedCallbackIdForCreate, result: [
+ "googleMap": [
+ "mapId": self.id
+ ]
+ ]);
+ }
+
+ private func setupClustering() {
+ guard let mapView = self.mapView else { return }
+
+ let iconGenerator = CustomClusterIconGenerator()
+ let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
+ renderer.delegate = self;
+ renderer.zIndex = 10;
+ let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
+ self.mClusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
+ }
+
+ override func touchesBegan(_ touches: Set, with event: UIEvent?) {
+ print("CustomMapViewController getting touches and event")
+// var uir : UIResponder = UIResponder()
+// uir.touchesBegan(touches, with: event)
+
+ self.mapView.point(inside: (touches.first?.location(in: self.view))!, with: event)
+// self.GMapView.touchesBegan(touches, with: event)
+// super.touchesBegan(touches, with: event)
+ }
+
+
+ func invalidateMap() {
+ if (self.mapView == nil) {
+ return;
+ }
+
+ // set gestures
+ self.mapView.settings.rotateGestures = self.mapPreferences.gestures.isRotateAllowed;
+ self.mapView.settings.scrollGestures = self.mapPreferences.gestures.isScrollAllowed;
+ self.mapView.settings.allowScrollGesturesDuringRotateOrZoom = self.mapPreferences.gestures.isScrollAllowedDuringRotateOrZoom;
+ self.mapView.settings.tiltGestures = self.mapPreferences.gestures.isTiltAllowed;
+ self.mapView.settings.zoomGestures = self.mapPreferences.gestures.isZoomAllowed;
+
+ // set controls
+ self.mapView.settings.compassButton = self.mapPreferences.controls.isCompassButtonEnabled;
+ self.mapView.settings.indoorPicker = self.mapPreferences.controls.isIndoorLevelPickerEnabled;
+ self.mapView.settings.myLocationButton = self.mapPreferences.controls.isMyLocationButtonEnabled;
+
+ // set appearance
+ self.mapView.mapType = self.mapPreferences.appearance.type;
+ self.mapView.mapStyle = self.mapPreferences.appearance.style;
+ self.mapView.isBuildingsEnabled = self.mapPreferences.appearance.isBuildingsShown;
+ self.mapView.isIndoorEnabled = self.mapPreferences.appearance.isIndoorShown;
+ self.mapView.isMyLocationEnabled = self.mapPreferences.appearance.isMyLocationDotShown;
+ self.mapView.isTrafficEnabled = self.mapPreferences.appearance.isTrafficShown;
+
+
+ // if we have location button then
+ // hide default button
+ if(self.mapView.isMyLocationEnabled == true) {
+ locationButton = getLocationButtonFromMapView();
+ locationButton.isHidden = true;
+ }
+ }
+
+
+ public func setCallbackIdForEvent(callbackId: String!, eventName: String!,
+ preventDefault: Bool!) {
+ if (callbackId != nil && eventName != nil) {
+ switch eventName {
+ case Events.EVENT_DID_TAP_INFO_WINDOW:
+ savedCallbackIdForDidTapInfoWindow = callbackId;
+ case Events.EVENT_DID_CLOSE_INFO_WINDOW:
+ savedCallbackIdForDidCloseInfoWindow = callbackId;
+ case Events.EVENT_DID_TAP_MAP:
+ savedCallbackIdForDidTapMap = callbackId;
+ case Events.EVENT_DID_LONG_PRESS_MAP:
+ savedCallbackIdForDidLongPressMap = callbackId;
+ case Events.EVENT_DID_TAP_MARKER:
+ savedCallbackIdForDidTapMarker = callbackId
+ preventDefaultForDidTapMarker = preventDefault ?? false;
+ case Events.EVENT_DID_TAP_CLUSTER:
+ savedCallbackIdForDidTapCluster = callbackId
+ case Events.EVENT_DID_TAP_MY_LOCATION_BUTTON:
+ savedCallbackIdForDidTapMyLocationButton = callbackId;
+ preventDefaultForDidTapMyLocationButton = preventDefault ?? false;
+ case Events.EVENT_DID_TAP_MY_LOCATION_DOT:
+ savedCallbackIdForDidTapMyLocationDot = callbackId
+ default:
+ return
+ }
+
+ }
+
+ }
+
+ public func clear() {
+ mapView.clear();
+ mClusterManager.clearItems();
+ }
+
+ public func addMarker(_ customMarker: CustomMarker!) {
+ // TO-DO: Implement setting of custom icon
+ self.mClusterManager.add(customMarker);
+ self.mMarkersList.append(customMarker);
+ self.mClusterManager.cluster();
+ }
+
+ public func addMarkers(_ markerArray: [CustomMarker]!) {
+ self.mClusterManager.add(markerArray);
+ self.mMarkersList.append(contentsOf: markerArray);
+ self.mClusterManager.cluster();
+ }
+
+ public func updateMarker(_ markerId: String, _ preferences:JSObject) -> Bool {
+ for item in self.mMarkersList {
+ if(item.id == markerId) {
+ self.mClusterManager.remove(item);
+ item.updateFromJSObject(preferences: preferences);
+ self.mClusterManager.add(item);
+ self.mClusterManager.cluster();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public func removeMarker(_ markerId: String) {
+ var index : Int = 0;
+ for item in self.mMarkersList {
+ if(item.id == markerId) {
+ self.mClusterManager.remove(item);
+ self.mMarkersList.remove(at: index);
+ self.mClusterManager.cluster();
+ return;
+ }
+ index += 1;
+ }
+ }
+
+
+ // MARK: GMSMapViewDelegate
+
+ internal func mapView(_ mapView: GMSMapView, didTapInfoWindowOf marker: GMSMarker) {
+ if (customMapViewEvents != nil && savedCallbackIdForDidTapInfoWindow != nil) {
+ let result: PluginCallResultData = CustomMarker.getResultForMarker(marker, self.id);
+ customMapViewEvents.resultForCallbackId(callbackId: savedCallbackIdForDidTapInfoWindow, result: result);
+ }
+ }
+
+ internal func mapView(_ mapView: GMSMapView, didCloseInfoWindowOf marker: GMSMarker) {
+ if (customMapViewEvents != nil && savedCallbackIdForDidCloseInfoWindow != nil) {
+ let result: PluginCallResultData = CustomMarker.getResultForMarker(marker, self.id);
+ customMapViewEvents.resultForCallbackId(callbackId: savedCallbackIdForDidCloseInfoWindow, result: result);
+ }
+ }
+
+ internal func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
+ if (customMapViewEvents != nil && savedCallbackIdForDidTapMap != nil) {
+ let result: PluginCallResultData = self.getResultForPosition(coordinate: coordinate);
+ customMapViewEvents.resultForCallbackId(callbackId: savedCallbackIdForDidTapMap, result: result);
+ }
+ }
+
+ internal func mapView(_ mapView: GMSMapView, didLongPressAt coordinate: CLLocationCoordinate2D) {
+ if (customMapViewEvents != nil && savedCallbackIdForDidLongPressMap != nil) {
+ let result: PluginCallResultData = self.getResultForPosition(coordinate: coordinate);
+ customMapViewEvents.resultForCallbackId(callbackId: savedCallbackIdForDidLongPressMap, result: result);
+ }
+ }
+
+ internal func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
+ // check if was tapped cluster
+ if let cluster = marker.userData as? GMUCluster {
+ NSLog("Did tap cluster")
+ var bounds : GMSCoordinateBounds = GMSCoordinateBounds()
+ for item in cluster.items {
+ bounds = bounds.includingCoordinate(item.position)
+ }
+ var getMarkersData: Bool = unclusteringCluster(bounds);
+
+ if(customMapViewEvents != nil && savedCallbackIdForDidTapCluster != nil) {
+ let result: PluginCallResultData = CustomMapViewController.getResultForCluster(cluster, self.id, getMarkersData);
+ customMapViewEvents.resultForCallbackId(callbackId: savedCallbackIdForDidTapCluster, result: result);
+ }
+ return true
+ }
+
+ // if not than was tapped marker
+ NSLog("Did tap marker")
+ mapView.animate(toLocation: marker.position)
+
+ if (customMapViewEvents != nil && savedCallbackIdForDidTapMarker != nil) {
+ let result: PluginCallResultData = CustomMarker.getResultForMarker(marker, self.id);
+ customMapViewEvents.resultForCallbackId(callbackId: savedCallbackIdForDidTapMarker, result: result);
+ }
+ return preventDefaultForDidTapMarker;
+ }
+
+ private func unclusteringCluster(_ bounds: GMSCoordinateBounds) -> Bool {
+ // check if cluster can uncluster
+ if((fabs(bounds.northEast.latitude - bounds.southWest.latitude) > EPSILON) &&
+ (fabs(bounds.northEast.longitude - bounds.southWest.longitude) > EPSILON)) {
+
+
+ print("widthOfTheMapCameraAnimationUnclustering: \(widthOfTheMapCameraAnimationUnclustering)")
+ print("heightOfTheMapCameraAnimationUnclustering: \(heightOfTheMapCameraAnimationUnclustering)")
+ var update : GMSCameraUpdate = GMSCameraUpdate.fit(bounds, with: UIEdgeInsets.init(
+ top: heightOfTheMapCameraAnimationUnclustering,
+ left: widthOfTheMapCameraAnimationUnclustering/2,
+ bottom: heightOfTheMapCameraAnimationUnclustering/2,
+ right: widthOfTheMapCameraAnimationUnclustering))
+
+ mapView.animate(with: update)
+ return false;
+ } else {
+ // if all markers inside cluster is have same position
+ return true;
+ }
+ }
+
+ internal func mapView(_ mapView: GMSMapView, didTapMyLocation coordinate: CLLocationCoordinate2D) {
+ if (customMapViewEvents != nil && savedCallbackIdForDidTapMyLocationDot != nil) {
+ let result: PluginCallResultData = self.getResultForPosition(coordinate: coordinate);
+ customMapViewEvents.resultForCallbackId(callbackId: savedCallbackIdForDidTapMyLocationDot, result: result);
+ }
+ }
+
+ internal func didTapMyLocationButton(for mapView: GMSMapView) -> Bool {
+ if (customMapViewEvents != nil && savedCallbackIdForDidTapMyLocationButton != nil) {
+ customMapViewEvents.resultForCallbackId(callbackId: savedCallbackIdForDidTapMyLocationButton, result: nil);
+ }
+ return preventDefaultForDidTapMyLocationButton;
+ }
+
+ private func getResultForPosition(coordinate: CLLocationCoordinate2D) -> PluginCallResultData {
+ return [
+ "position": [
+ "latitude": coordinate.latitude,
+ "longitude": coordinate.longitude
+ ]
+ ]
+ }
+
+ private static func getResultForCluster(_ cluster : GMUCluster, _ mapId : String, _ getMarkersData : Bool) -> PluginCallResultData {
+ let clusterObj : GMUCluster = cluster;
+
+
+ // initialize JSObjects to return
+ var result: JSObject = JSObject();
+ var positionResult = JSObject();
+
+ // get map id
+ positionResult = [
+ "mapId" : mapId,
+ "latitude" : clusterObj.position.latitude,
+ "longitude" : clusterObj.position.longitude,
+ ]
+
+ result["position"] = positionResult;
+
+ if(getMarkersData) {
+ var markersData : JSObject = JSObject();
+ for item in cluster.items {
+ let marker = item as! CustomMarker;
+ markersData[marker.id] = CustomMarker.getResultForMarker((item as! GMSMarker), mapId) as! JSObject
+ }
+ result["markersData"] = markersData;
+ }
+
+ return result;
+ }
+
+
+ public func zoomIn() {
+ self.mapView.animate(toZoom: mapView.camera.zoom + 1);
+ }
+
+ public func zoomOut() {
+ self.mapView.animate(toZoom: mapView.camera.zoom - 1);
+ }
+
+ public func myLocationButtonClick() {
+ locationButton.sendActions(for: .touchUpInside);
+ }
+
+ private func getLocationButtonFromMapView() -> UIButton {
+ for object in self.mapView.subviews {
+ if(String(describing: type(of: object)) == "GMSUISettingsPaddingView") {
+ for view in object.subviews {
+ if(String(describing: type(of: view)) == "GMSUISettingsView") {
+ for btn in view.subviews {
+ if(String(describing: type(of: btn)) == "GMSx_MDCFloatingButton") {
+ if btn is UIButton {
+ let button = btn as! UIButton;
+ return button;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return UIButton();
+ }
+}
+
+
+extension CustomMapViewController: GMUClusterRendererDelegate {
+ func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
+ // if your marker is pointy you can change groundAnchor
+// marker.groundAnchor = CGPoint(x: 0.5, y: 1)
+ if let markerData = (marker.userData as? CustomMarker) {
+ let icon = MarkerCategory.markerCategories[markerData.markerCategoryId]?.getIcon ?? nil;
+ marker.icon = icon;
+ marker.zIndex = 5;
+ }
+ }
+}
+
+
diff --git a/ios/Plugin/CustomMapViewEvents.swift b/ios/Plugin/CustomMapViewEvents.swift
new file mode 100644
index 00000000..bb8afa0a
--- /dev/null
+++ b/ios/Plugin/CustomMapViewEvents.swift
@@ -0,0 +1,7 @@
+import Foundation
+import Capacitor
+
+public class CustomMapViewEvents: CAPPlugin {
+ func lastResultForCallbackId(callbackId: String, result: PluginCallResultData) {}
+ func resultForCallbackId(callbackId: String, result: PluginCallResultData?) {}
+}
diff --git a/ios/Plugin/Plugin.m b/ios/Plugin/Plugin.m
index 6ea0e4cf..195936d2 100644
--- a/ios/Plugin/Plugin.m
+++ b/ios/Plugin/Plugin.m
@@ -5,26 +5,29 @@
// each method the plugin supports using the CAP_PLUGIN_METHOD macro.
CAP_PLUGIN(CapacitorGoogleMaps, "CapacitorGoogleMaps",
CAP_PLUGIN_METHOD(initialize, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(create, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(createMap, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(updateMap, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(clearMap, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(addMarker, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(setMapType, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(setMapStyle, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(setCamera, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(isIndoorEnabled, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(accessibilityElementsHidden, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(isMyLocationEnabled, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(padding, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(clear, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(isTrafficEnabled, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(close, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(addMarkers, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(updateMarker, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(removeMarker, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(didTapInfoWindow, CAPPluginReturnCallback);
+ CAP_PLUGIN_METHOD(didCloseInfoWindow, CAPPluginReturnCallback);
+ CAP_PLUGIN_METHOD(didTapMap, CAPPluginReturnCallback);
+ CAP_PLUGIN_METHOD(didLongPressMap, CAPPluginReturnCallback);
+ CAP_PLUGIN_METHOD(didTapMarker, CAPPluginReturnCallback);
+ CAP_PLUGIN_METHOD(didTapCluster, CAPPluginReturnCallback);
+ CAP_PLUGIN_METHOD(didTapMyLocationButton, CAPPluginReturnCallback);
+ CAP_PLUGIN_METHOD(didTapMyLocationDot, CAPPluginReturnCallback);
+ CAP_PLUGIN_METHOD(blockMapViews, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(unblockMapViews, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(getArrayOfHTMLElements, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(setArrayOfHTMLElements, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(zoomInButtonClick, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(zoomOutButtonClick, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(myLocationButtonClick, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(addMarkerCategory, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(hide, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(show, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(reverseGeocodeCoordinate, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(settings, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(addPolyline, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(addPolygon, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(addCircle, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(enableCurrentLocation, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(myLocation, CAPPluginReturnPromise);
- CAP_PLUGIN_METHOD(viewBounds, CAPPluginReturnPromise);
)
diff --git a/ios/Plugin/Plugin.swift b/ios/Plugin/Plugin.swift
index b60787be..b1e5afc7 100644
--- a/ios/Plugin/Plugin.swift
+++ b/ios/Plugin/Plugin.swift
@@ -1,720 +1,705 @@
import Foundation
import Capacitor
import GoogleMaps
+import UIKit
@objc(CapacitorGoogleMaps)
-public class CapacitorGoogleMaps: CAPPlugin, GMSMapViewDelegate, GMSPanoramaViewDelegate {
-
+public class CapacitorGoogleMaps: CustomMapViewEvents {
+
+ // --constants--
+ let TAG_NUMBER_FOR_MAP_UIVIEW : Int = 10;
+ let TAG_NUMBER_FOR_TOP_OVERLAY_UIVIEW : Int = 20;
+ let TAG_NUMBER_FOR_DEFAULT_WEBVIEW_SUBVIEW_WITH_HTML_ELEMENTS : Int = 1;
+ let MARKER_CATEGORY_DIRECTORY : String = "marker-categories.bundle";
+ let SIZE_OF_MARKERS = CGSize(width: 64.0, height: 64.0)
+
+
var GOOGLE_MAPS_KEY: String = "";
- var mapViewController: GMViewController!;
- var streetViewController: GMStreetViewController!;
- var DEFAULT_ZOOM: Double = 12.0;
+
+ var customMapViewControllers = [String : CustomMapViewController]();
+
+ var hasTopView : Bool = false;
+
+ var arrayOfHTMLElements = [BoundingRect]();
+
+
+ // class for view that will receive touches and transmit them
+ class OverlayView: UIView {
+
+ public var mainClass: CapacitorGoogleMaps?;
+
+ override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
+
+ // make html view default off
+ mainClass?.bridge?.webView?.viewWithTag(mainClass!.TAG_NUMBER_FOR_DEFAULT_WEBVIEW_SUBVIEW_WITH_HTML_ELEMENTS)?.isUserInteractionEnabled = false;
+
+ // checking the hit in the elements
+ for boundingRect in mainClass?.arrayOfHTMLElements ?? [] {
+ // checking the hit in the element
+ if(Bool(point.x > boundingRect.x) &&
+ Bool(point.x < (boundingRect.x + boundingRect.width)) &&
+ Bool(point.y > boundingRect.y) &&
+ Bool(point.y < (boundingRect.y + boundingRect.height))) {
+ // touch point inside of html element
+ // then we make this subview is toucheable
+ mainClass?.bridge?.webView?.viewWithTag(mainClass!.TAG_NUMBER_FOR_DEFAULT_WEBVIEW_SUBVIEW_WITH_HTML_ELEMENTS)?.isUserInteractionEnabled = true
+ return false
+ }
+ }
- var hashMap = [Int : GMSMarker]();
+ // if we here, than html view off
+ let arrayOfMaps = [CustomMapViewController]((mainClass?.customMapViewControllers.values)!)
+
+ for mapview in arrayOfMaps {
+ // checking if map exists in this point
+ if(point.x > mapview.boundingRect.x &&
+ point.x < (mapview.boundingRect.x + mapview.boundingRect.width) &&
+ point.y > mapview.boundingRect.y &&
+ point.y < (mapview.boundingRect.y + mapview.boundingRect.height)) {
+ // if mapview exist in this point than doing nothing
+ // just go further
+ return false
+ } else {
+ // if there is no mapView than we on html view
+ }
+ }
+ mainClass?.bridge?.webView?.viewWithTag(mainClass!.TAG_NUMBER_FOR_DEFAULT_WEBVIEW_SUBVIEW_WITH_HTML_ELEMENTS)?.isUserInteractionEnabled = true
+ return false
+ }
+ }
+
+
@objc func initialize(_ call: CAPPluginCall) {
-
+
+ initMarkerCategories();
+
self.GOOGLE_MAPS_KEY = call.getString("key", "")
-
+
if self.GOOGLE_MAPS_KEY.isEmpty {
call.reject("GOOGLE MAPS API key missing!")
return
}
-
+
GMSServices.provideAPIKey(self.GOOGLE_MAPS_KEY)
+
call.resolve([
"initialized": true
])
}
-
- @objc func create(_ call: CAPPluginCall) {
-
- DispatchQueue.main.async {
- self.mapViewController = GMViewController();
- self.mapViewController.mapViewBounds = [
- "width": call.getDouble("width") ?? 500,
- "height": call.getDouble("height") ?? 500,
- "x": call.getDouble("x") ?? 0,
- "y": call.getDouble("y") ?? 0,
- ]
- self.mapViewController.cameraPosition = [
- "latitude": call.getDouble("latitude") ?? 0.0,
- "longitude": call.getDouble("longitude") ?? 0.0,
- "zoom": call.getDouble("zoom") ?? (self.DEFAULT_ZOOM)
- ]
- self.bridge?.viewController?.view.addSubview(self.mapViewController.view)
- self.mapViewController.GMapView.delegate = self
- self.notifyListeners("onMapReady", data: nil)
- }
- call.resolve([
- "created": true
- ])
- }
-
- @objc func addMarker(_ call: CAPPluginCall) {
-
- let latitude = call.getDouble("latitude") ?? 0
- let longitude = call.getDouble("longitude") ?? 0
- let opacity = call.getFloat("opacity") ?? 1
- let title = call.getString("title") ?? ""
- let snippet = call.getString("snippet") ?? ""
- let isFlat = call.getBool("isFlat") ?? false
- let metadata = call.getObject("metadata") ?? [:]
- let url = URL(string: call.getString("iconUrl", ""))
- var imageData: Data?
-
- DispatchQueue.global().async {
-
- if url != nil {
- /* https://stackoverflow.com/a/27517280/5056792 */
- imageData = try? Data(contentsOf: url!)
+
+
+
+ @objc func createMap(_ call: CAPPluginCall) {
+
+ DispatchQueue.main.sync{
+ let customMapViewController : CustomMapViewController = CustomMapViewController(customMapViewEvents: self);
+
+ self.bridge?.saveCall(call)
+ customMapViewController.savedCallbackIdForCreate = call.callbackId;
+
+ let boundingRect = call.getObject("boundingRect", JSObject());
+ customMapViewController.boundingRect.updateFromJSObject(boundingRect);
+
+ let mapCameraPosition = call.getObject("cameraPosition", JSObject());
+ customMapViewController.mapCameraPosition.updateFromJSObject(mapCameraPosition);
+
+ let preferences = call.getObject("preferences", JSObject());
+ customMapViewController.mapPreferences.updateFromJSObject(preferences);
+
+
+ self.bridge?.webView?.subviews[0].tag = TAG_NUMBER_FOR_DEFAULT_WEBVIEW_SUBVIEW_WITH_HTML_ELEMENTS;
+ customMapViewController.view.tag = self.TAG_NUMBER_FOR_MAP_UIVIEW;
+
+ // elements on the top of the mapView
+ // getting JSOobject
+ let elementsOnTop = call.getObject("elementsOnTop", JSObject());
+ // getting two dimensional array of child elements
+ let twoDArray = elementsOnTop["innerEles"] as? [[Int]];
+ for i in 0.. = []
- for address in response?.results() ?? [] {
- let addr = [
- "administrativeArea": address.administrativeArea ?? "",
- "lines": address.lines!,
- "country": address.country ?? "",
- "locality": address.locality ?? "",
- "postalCode": address.postalCode ?? "",
- "subLocality": address.subLocality ?? "",
- "thoroughFare": address.thoroughfare ?? ""
- ] as [String : Any]
- addressList.append(addr)
+ guard let customMapViewController: CustomMapViewController = self.customMapViewControllers[mapId]
+ else {
+ call.reject("map not found");
+ return
}
-
- call.resolve([
- "addresses": addressList
- ])
- }
+ self.locationRequest();
+ customMapViewController.myLocationButtonClick();
+ call.resolve();
+
}
}
-
- @objc func settings(_ call: CAPPluginCall) {
-
- let allowScrollGesturesDuringRotateOrZoom = call.getBool("allowScrollGesturesDuringRotateOrZoom") ?? true
- let compassButton = call.getBool("compassButton") ?? false
- let consumesGesturesInView = call.getBool("consumesGesturesInView") ?? true
- let indoorPicker = call.getBool("indoorPicker") ?? false
- let myLocationButton = call.getBool("myLocationButton") ?? false
- let rotateGestures = call.getBool("rotateGestures") ?? true
- let scrollGestures = call.getBool("scrollGestures") ?? true
- let tiltGestures = call.getBool("tiltGestures") ?? true
- let zoomGestures = call.getBool("zoomGestures") ?? true
-
-
- DispatchQueue.main.async {
- self.mapViewController.GMapView.settings.allowScrollGesturesDuringRotateOrZoom = allowScrollGesturesDuringRotateOrZoom
- self.mapViewController.GMapView.settings.compassButton = compassButton
- self.mapViewController.GMapView.settings.consumesGesturesInView = consumesGesturesInView
- self.mapViewController.GMapView.settings.indoorPicker = indoorPicker
- self.mapViewController.GMapView.settings.myLocationButton = myLocationButton
- self.mapViewController.GMapView.settings.rotateGestures = rotateGestures
- self.mapViewController.GMapView.settings.scrollGestures = scrollGestures
- self.mapViewController.GMapView.settings.tiltGestures = tiltGestures
- self.mapViewController.GMapView.settings.zoomGestures = zoomGestures
-
- call.resolve([
- "settingsApplied": true
- ])
+
+ private func locationRequest() {
+ // this open app specific settings
+ func openSettings(alert: UIAlertAction!) {
+ if let url = URL.init(string: UIApplication.openSettingsURLString) {
+ UIApplication.shared.open(url, options: [:], completionHandler: nil)
+ }
}
- }
-
- @objc func setCamera(_ call: CAPPluginCall) {
-
- let viewingAngle = call.getDouble("viewingAngle") ?? 45
- let bearing = call.getDouble("bearing") ?? 270
- let zoom = call.getFloat("zoom") ?? 1
- let latitude = call.getDouble("latitude") ?? 0
- let longitude = call.getDouble("longitude") ?? 0
-
- let animate = call.getBool("animate") ?? false
- let animationDuration = call.getDouble("animationDuration") ?? 1
-
- let coordinates = call.getArray("coordinates", AnyObject.self)
-
- DispatchQueue.main.async {
- let camera = GMSCameraPosition(latitude: latitude, longitude: longitude, zoom: zoom, bearing: bearing, viewingAngle: viewingAngle)
-
- if animationDuration != 0 && animate {
- /* FIXME: Use animation duration */
- if coordinates == nil {
- self.mapViewController.GMapView.animate(to: camera)
- call.resolve([
- "cameraSet": true
- ])
- } else {
- var gmsBounds = GMSCoordinateBounds()
- for coordinate in coordinates ?? [] {
- let coord = CLLocationCoordinate2D(latitude: coordinate["latitude"] as! CLLocationDegrees, longitude: coordinate["longitude"] as! CLLocationDegrees)
- let marker = GMSMarker(position: coord)
- gmsBounds = gmsBounds.includingCoordinate(marker.position)
- }
- CATransaction.begin()
- CATransaction.setValue(animationDuration, forKey: kCATransactionAnimationDuration)
- CATransaction.setCompletionBlock({
- call.resolve([
- "cameraSet": true
- ])
- })
- self.mapViewController.GMapView.animate(with: GMSCameraUpdate.fit(gmsBounds, withPadding: 30.0))
- CATransaction.commit()
+
+
+
+ let status = CLLocationManager.authorizationStatus()
+ if status == .notDetermined || status == .denied{
+
+ // present an alert indicating location authorization required
+ // and offer to take the user to Settings for the app via
+ // UIApplication -openUrl: and UIApplicationOpenSettingsURLString
+ DispatchQueue.main.async {
+ let title : String?;
+ let message : String?;
+ let titleOfSecondAction : String;
+ if(CLLocationManager.locationServicesEnabled() != true) {
+ title = "Location Services Off"
+ message = "Turn on Location Services in Settings > Privacy to allow \"" + Bundle.appName() + "\" to determine your current location";
+ titleOfSecondAction = "OK";
+ } else {
+ title = "GPS access is restricted. In order to Allow \"" + Bundle.appName() + "\" to Determine Your Location, please give GPS permissions to application";
+ message = nil
+ titleOfSecondAction = "Cancel";
+ }
+
+
+ let alert = UIAlertController(title: title,
+ message: message,
+ preferredStyle: .alert)
+
+ alert.addAction(UIAlertAction(title: "Settings",
+ style: UIAlertAction.Style.default,
+ handler: openSettings))
+
+ alert.addAction(UIAlertAction(title: titleOfSecondAction, style: UIAlertAction.Style.default, handler: nil))
+ self.bridge?.webView?.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
- } else {
- self.mapViewController.GMapView.camera = camera
- call.resolve([
- "cameraSet": true
- ])
}
- }
-
}
-
- @objc func setMapStyle(_ call: CAPPluginCall) {
- let styleJsonString = call.getString("jsonString") ?? ""
-
- DispatchQueue.main.async {
- do {
- self.mapViewController.GMapView.mapStyle = try GMSMapStyle(jsonString: styleJsonString)
- call.resolve([
- "stylesApplied": true
- ])
- } catch {
- call.reject("One or more of the map styles failed to load. \(error)")
- }
-
+
+ @objc func addMarkerCategory(_ call: CAPPluginCall) {
+ let id : Int = call.getInt("id", -1);
+ let title : String = call.getString("title", "");
+ let encodedImage : String = call.getString("base64Data", "");
+
+ if (id == -1 ) {
+ call.reject("dont have id for category");
+ return;
}
- }
-
- @objc func addPolyline(_ call: CAPPluginCall) {
-
- let points = call.getArray("points", AnyObject.self)
-
- DispatchQueue.main.async {
-
- let path = GMSMutablePath()
-
- for point in points ?? [] {
- let coords = CLLocationCoordinate2D(latitude: point["latitude"] as! CLLocationDegrees, longitude: point["longitude"] as! CLLocationDegrees)
- path.add(coords)
- }
-
- let polyline = GMSPolyline(path: path)
-
- polyline.map = self.mapViewController.GMapView
- call.resolve([
- "created": true
- ])
+
+ var image : UIImage = UIImage();
+ if let decodedData = Data(base64Encoded: encodedImage, options: .ignoreUnknownCharacters) {
+ image = UIImage(data: decodedData)!
+ image = image.resized(to: SIZE_OF_MARKERS);
}
- }
-
- @objc func addPolygon(_ call: CAPPluginCall) {
-
- let points = call.getArray("points", AnyObject.self)
-
- DispatchQueue.main.async {
-
- let path = GMSMutablePath()
-
- for point in points ?? [] {
- let coords = CLLocationCoordinate2D(latitude: point["latitude"] as! CLLocationDegrees, longitude: point["longitude"] as! CLLocationDegrees)
- path.add(coords)
- }
-
- let polygon = GMSPolygon(path: path)
- polygon.map = self.mapViewController.GMapView
-
- call.resolve([
- "created": true
- ])
+
+ MarkerCategory(id, title, image);
+
+ }
+
+
+ private func initMarkerCategories() {
+ // default marker icon for zero category
+ MarkerCategory(0, "default", nil);
+
+ // getting map of names of categories and icons of this
+ var markerCategoriesNamesAndIcons = fetchMarkersCategoriesFilesFromAssets();
+ // sorting keys in alphabetical order for adding catetories in the same order
+ var arrayOfKeys = Array(markerCategoriesNamesAndIcons.keys.map{ $0 })
+ arrayOfKeys = arrayOfKeys.sorted(by: <)
+
+ var i : Int = 1;
+ for nameOfCategory in arrayOfKeys {
+ MarkerCategory(i, nameOfCategory, markerCategoriesNamesAndIcons[nameOfCategory] as? UIImage ?? nil)
+ i += 1;
}
- }
-
- @objc func addCircle(_ call: CAPPluginCall) {
-
- let radius = call.getDouble("radius") ?? 0.0
-
- let center = call.getObject("center")
-
- let coordinates = CLLocationCoordinate2D(latitude: center?["latitude"] as! CLLocationDegrees, longitude: center?["longitude"] as! CLLocationDegrees)
-
+
+ }
+
+ private func fetchMarkersCategoriesFilesFromAssets() -> [String : UIImage?] {
+ var returnArray = [String : UIImage?]();
+
+ let fm = FileManager.default
+ let listImageName = fm.getListFileNameInBundle(bundlePath: MARKER_CATEGORY_DIRECTORY)
+ for imgName in listImageName {
+
+ let pattern = #"(.+?)(\.[^.]*$|$)"#
+ let regex = try! NSRegularExpression(pattern: pattern)
+ var result: [String] = [String]();
+ let stringRange = NSRange(location: 0, length: imgName.utf16.count)
+ if let firstMatch = regex.firstMatch(in: imgName, range: stringRange) {
+ result = (1 ..< firstMatch.numberOfRanges).map { (imgName as NSString).substring(with: firstMatch.range(at: $0)) }
+ } else {
+ result[0] = imgName;
+ }
+
+
+
+ var image = fm.getImageInBundle(bundlePath: MARKER_CATEGORY_DIRECTORY + "/\(result[0])")
+ image = image?.resized(to: SIZE_OF_MARKERS)
+ returnArray[imgName] = image;
+ }
+ return returnArray;
+ }
+
+
+ @objc func hide(_ call: CAPPluginCall) {
+ let mapId: String = call.getString("mapId", "");
+
DispatchQueue.main.async {
-
- let circleCenter = coordinates
- let circle = GMSCircle(position: circleCenter, radius: radius)
- circle.map = self.mapViewController.GMapView
-
- call.resolve([
- "created": true
- ])
+ guard let customMapViewController: CustomMapViewController = self.customMapViewControllers[mapId]
+ else {
+ call.reject("map not found");
+ return
+ }
+ customMapViewController.mapView.isHidden = true;
+ call.resolve();
}
}
-
-
- public func mapView(_ mapView: GMSMapView, didTapPOIWithPlaceID placeID: String, name: String, location: CLLocationCoordinate2D) {
- self.notifyListeners("didTapPOIWithPlaceID", data: [
- "results": [
- "name": name,
- "placeID": placeID,
- "location": [
- "latitude": location.latitude,
- "longitude": location.longitude
- ]
- ]
- ])
- }
-
-
- public func mapView(_ mapView: GMSMapView, didLongPressAt coordinate: CLLocationCoordinate2D) {
- self.notifyListeners("didLongPressAt", data: ["result": [
- "coordinates": [
- "latitude": coordinate.latitude,
- "longitude": coordinate.longitude
- ]
- ]])
- }
-
- public func mapView(_ mapView: GMSMapView, didTapInfoWindowOf marker: GMSMarker) {
- self.notifyListeners("didTapInfoWindowOf", data: ["result": [
- "coordinates": [
- "latitude": marker.position.latitude,
- "longitude": marker.position.longitude
- ]
- ]])
- }
-
- public func mapView(_ mapView: GMSMapView, didLongPressInfoWindowOf marker: GMSMarker) {
- self.notifyListeners("didLongPressInfoWindowOf", data: ["result": [
- "coordinates": [
- "latitude": marker.position.latitude,
- "longitude": marker.position.longitude
- ]
- ]])
- }
-
- public func mapView(_ mapView: GMSMapView, didCloseInfoWindowOf marker: GMSMarker) {
- self.notifyListeners("didCloseInfoWindowOf", data: ["result": [
- "coordinates": [
- "latitude": marker.position.latitude,
- "longitude": marker.position.longitude
- ]
- ]])
- }
-
- public func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
- self.notifyListeners("didTap", data: ["result": [
- "coordinates": [
- "latitude": marker.position.latitude,
- "longitude": marker.position.longitude
- ],
- "id": marker.hash.hashValue,
- "title": marker.title ?? "",
- "snippet": marker.snippet ?? "",
- "metadata": marker.userData
- ]])
- return false
- }
-
- public func mapView(_ mapView: GMSMapView, didBeginDragging marker: GMSMarker) {
- self.notifyListeners("didBeginDragging", data: ["result": [
- "coordinates": [
- "latitude": marker.position.latitude,
- "longitude": marker.position.longitude
- ]
- ]])
- }
-
- public func mapView(_ mapView: GMSMapView, didEndDragging marker: GMSMarker) {
- self.notifyListeners("didEndDragging", data: ["result": [
- "coordinates": [
- "latitude": marker.position.latitude,
- "longitude": marker.position.longitude
- ]
- ]])
- }
-
- public func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
- self.notifyListeners("idleAt", data: ["result": [
- "position": [
- "latitude": position.target.latitude,
- "longitude": position.target.longitude
- ],
- "zoom": position.zoom,
- ]])
- }
-
-
- public func didTapMyLocationButton(for mapView: GMSMapView) -> Bool {
- self.notifyListeners("didTapMyLocationButton", data: ["value": true])
- /*
- TODO: Add animation to user's current location
- */
- return false
- }
-
- public func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
- self.notifyListeners("didTapAt", data: ["result": [
- "coordinates": [
- "latitude": coordinate.latitude,
- "longitude": coordinate.longitude
- ]
- ]])
- }
-
- public func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
- self.notifyListeners("didChange", data: ["result": [
- "position": [
- "latitude": position.target.latitude,
- "longitude": position.target.longitude
- ],
- "zoom": position.zoom,
- ]])
- }
-
- // Street View
- @objc func createStreetView(_ call: CAPPluginCall) {
-
+
+ @objc func show(_ call: CAPPluginCall) {
+ let mapId: String = call.getString("mapId", "");
+
DispatchQueue.main.async {
- self.streetViewController = GMStreetViewController();
- self.streetViewController.mapViewBounds = [
- "width": call.getDouble("width") ?? 0,
- "height": call.getDouble("height") ?? 0,
- "x": call.getDouble("x") ?? 0,
- "y": call.getDouble("y") ?? 0,
- ]
- self.mapViewController.cameraPosition = [
- "heading": call.getDouble("heading") ?? 180,
- "pitch": call.getDouble("pitch") ?? -10,
- "zoom": call.getDouble("zoom") ?? Double(self.DEFAULT_ZOOM)
- ]
- self.bridge?.viewController?.view.addSubview(self.streetViewController.view)
- self.streetViewController.GMapStreetView.delegate = self
+ guard let customMapViewController: CustomMapViewController = self.customMapViewControllers[mapId]
+ else {
+ call.reject("map not found");
+ return
+ }
+ customMapViewController.mapView.isHidden = false;
+ call.resolve();
}
- call.resolve([
- "created": true
- ])
}
+
+}
- @objc func moveNearCoordinate(_ call: CAPPluginCall) {
- let latitude = call.getDouble("latitude") ?? 0
- let longitude = call.getDouble("longitude") ?? 0
-
- let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
-
- let radius = UInt(call.getInt("radius") ?? 0)
-
- DispatchQueue.main.async {
- self.streetViewController.GMapStreetView.moveNearCoordinate(coordinate, radius: (radius))
+extension Bundle {
+ static func appName() -> String {
+ guard let dictionary = Bundle.main.infoDictionary else {return ""}
+ if let appName : String = dictionary["CFBundleName"] as? String {
+ return appName
+ } else {
+ return ""
}
-
- call.resolve([
- "movedNearCoordinates": true
- ])
}
+}
- @objc func setStreetViewCamera(_ call: CAPPluginCall) {
- let heading = call.getDouble("heading") ?? 180
- let pitch = call.getDouble("pitch") ?? -10
- let zoom = call.getFloat("zoom") ?? 1
-
- let animationDuration = call.getDouble("animationDuration") ?? 0
- let camera = GMSPanoramaCamera(heading: heading, pitch: pitch, zoom: zoom)
+extension FileManager {
+ func getListFileNameInBundle(bundlePath: String) -> [String] {
- DispatchQueue.main.async {
- if animationDuration != 0 {
- self.streetViewController.GMapStreetView.animate(to: camera, animationDuration: animationDuration)
- } else {
- self.streetViewController.GMapStreetView.camera = camera
- }
+ let fileManager = FileManager.default
+ let bundleURL = Bundle.main.bundleURL
+ let assetURL = bundleURL.appendingPathComponent(bundlePath)
+ do {
+ let contents = try fileManager.contentsOfDirectory(at: assetURL, includingPropertiesForKeys: [URLResourceKey.nameKey, URLResourceKey.isDirectoryKey], options: .skipsHiddenFiles)
+ return contents.map{$0.lastPathComponent}
}
-
- call.resolve([
- "cameraSet": true
- ])
- }
-
- @objc func addStreetViewMarker(_ call: CAPPluginCall) {
- let latitude = call.getDouble("latitude") ?? 0
- let longitude = call.getDouble("longitude") ?? 0
-
- DispatchQueue.main.async {
- let marker = GMSMarker()
- marker.position = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
- marker.title = call.getString("markerTitle") ?? ""
- marker.snippet = call.getString("markerSnippet") ?? ""
- marker.panoramaView = self.streetViewController.GMapStreetView
+ catch {
+ return []
}
-
- call.resolve([
- "markerAdded": true
- ])
}
-}
-
-class GMViewController: UIViewController {
-
- var mapViewBounds: [String : Double]!
- var GMapView: GMSMapView!
- var cameraPosition: [String: Double]!
-
- var DEFAULT_ZOOM: Float = 12
-
- override func viewDidLoad() {
- super.viewDidLoad()
- let camera = GMSCameraPosition.camera(withLatitude: cameraPosition["latitude"] ?? 0, longitude: cameraPosition["longitude"] ?? 0, zoom: Float(cameraPosition["zoom"] ?? Double(DEFAULT_ZOOM)))
- let frame = CGRect(x: mapViewBounds["x"] ?? 0, y: mapViewBounds["y"]!, width: mapViewBounds["width"] ?? 0, height: mapViewBounds["height"] ?? 0)
- self.GMapView = GMSMapView.map(withFrame: frame, camera: camera)
- self.view = GMapView
+ func getImageInBundle(bundlePath: String) -> UIImage? {
+ let bundleURL = Bundle.main.bundleURL
+ let assetURL = bundleURL.appendingPathComponent(bundlePath)
+ return UIImage.init(contentsOfFile: assetURL.relativePath)
}
}
-class GMStreetViewController: UIViewController {
-
- var mapViewBounds: [String : Double]!
- var GMapStreetView: GMSPanoramaView!
- var cameraPosition: [String: Double]!
-
- var DEFAULT_HEADING: Double = 180
- var DEFAULT_PITCH: Double = -10
- var DEFAULT_ZOOM: Double = 12
-
- override func viewDidLoad() {
- super.viewDidLoad()
- let camera = GMSPanoramaCamera(
- heading: cameraPosition["heading"] ?? (DEFAULT_HEADING),
- pitch: cameraPosition["pitch"] ?? (DEFAULT_PITCH),
- zoom: Float(cameraPosition["zoom"] ?? (DEFAULT_ZOOM)))
- let frame = CGRect(x: mapViewBounds["x"] ?? 0, y: mapViewBounds["y"]!, width: mapViewBounds["width"] ?? 0, height: mapViewBounds["height"] ?? 0)
- self.GMapStreetView = GMSPanoramaView(frame: frame)
- self.GMapStreetView.camera = camera
- self.view = self.GMapStreetView
+extension UIImage {
+ public func resized(to target: CGSize) -> UIImage {
+ let ratio = min(
+ target.height / size.height, target.width / size.width
+ )
+ let new = CGSize(
+ width: size.width * ratio, height: size.height * ratio
+ )
+ let renderer = UIGraphicsImageRenderer(size: new)
+ return renderer.image { _ in
+ self.draw(in: CGRect(origin: .zero, size: new))
+ }
}
}
diff --git a/ios/Plugin/maps-utils/BoundingRect.swift b/ios/Plugin/maps-utils/BoundingRect.swift
new file mode 100644
index 00000000..b1a3dc8d
--- /dev/null
+++ b/ios/Plugin/maps-utils/BoundingRect.swift
@@ -0,0 +1,43 @@
+import Capacitor
+
+class BoundingRect {
+ public static let WIDTH_KEY: String! = "width";
+ public static let HEIGHT_KEY: String! = "height";
+ public static let X_KEY: String! = "x";
+ public static let Y_KEY: String! = "y";
+
+ private static let WIDTH_DEFAULT: Double! = 500.0;
+ private static let HEIGHT_DEFAULT: Double! = 500.0;
+ private static let X_DEFAULT: Double! = 0.0;
+ private static let Y_DEFAULT: Double! = 0.0;
+
+ public var width: Double!;
+ public var height: Double!;
+ public var x: Double!;
+ public var y: Double!;
+
+ public init () {
+ self.width = BoundingRect.WIDTH_DEFAULT;
+ self.height = BoundingRect.HEIGHT_DEFAULT;
+ self.x = BoundingRect.X_DEFAULT;
+ self.y = BoundingRect.Y_DEFAULT;
+ }
+
+ func updateFromJSObject(_ object: JSObject) {
+ self.width = object[BoundingRect.WIDTH_KEY] as? Double ?? BoundingRect.WIDTH_DEFAULT;
+ self.height = object[BoundingRect.HEIGHT_KEY] as? Double ?? BoundingRect.HEIGHT_DEFAULT;
+ self.x = object[BoundingRect.X_KEY] as? Double ?? BoundingRect.X_DEFAULT;
+ self.y = object[BoundingRect.Y_KEY] as? Double ?? BoundingRect.Y_DEFAULT;
+ }
+
+ func getJSObject() -> JSObject {
+
+ var rectangleAsJSObject = JSObject();
+ rectangleAsJSObject.updateValue(self.width, forKey: BoundingRect.WIDTH_KEY);
+ rectangleAsJSObject.updateValue(self.height, forKey: BoundingRect.HEIGHT_KEY);
+ rectangleAsJSObject.updateValue(self.x, forKey: BoundingRect.X_KEY);
+ rectangleAsJSObject.updateValue(self.y, forKey: BoundingRect.Y_KEY);
+
+ return rectangleAsJSObject;
+ }
+}
diff --git a/ios/Plugin/maps-utils/MapCameraPosition.swift b/ios/Plugin/maps-utils/MapCameraPosition.swift
new file mode 100644
index 00000000..63dd93d1
--- /dev/null
+++ b/ios/Plugin/maps-utils/MapCameraPosition.swift
@@ -0,0 +1,40 @@
+import Capacitor
+
+class MapCameraPosition {
+ public static let TARGET_KEY: String! = "target";
+ public static let LATITUDE_KEY: String! = "latitude";
+ public static let LONGITUDE_KEY: String! = "longitude";
+ public static let BEARING_KEY: String! = "bearing";
+ public static let TILT_KEY: String! = "tilt";
+ public static let ZOOM_KEY: String! = "zoom";
+
+ private static let TARGET_DEFAULT: JSObject! = JSObject();
+ private static let LATITUDE_DEFAULT: Double! = 0.0;
+ private static let LONGITUDE_DEFAULT: Double! = 0.0;
+ private static let BEARING_DEFAULT: Double! = 0.0;
+ private static let TILT_DEFAULT: Double! = 0.0;
+ private static let ZOOM_DEFAULT: Float! = 12.0;
+
+ public var latitude: Double!;
+ public var longitude: Double!;
+ public var bearing: Double!;
+ public var tilt: Double!;
+ public var zoom: Float!;
+
+ public init () {
+ self.latitude = MapCameraPosition.LATITUDE_DEFAULT;
+ self.longitude = MapCameraPosition.LONGITUDE_DEFAULT;
+ self.bearing = MapCameraPosition.BEARING_DEFAULT;
+ self.tilt = MapCameraPosition.TILT_DEFAULT;
+ self.zoom = MapCameraPosition.ZOOM_DEFAULT;
+ }
+
+ func updateFromJSObject(_ object: JSObject) {
+ let target: JSObject = object[MapCameraPosition.TARGET_KEY] as? JSObject ?? MapCameraPosition.TARGET_DEFAULT;
+ self.latitude = target[MapCameraPosition.LATITUDE_KEY] as? Double ?? MapCameraPosition.LATITUDE_DEFAULT;
+ self.longitude = target[MapCameraPosition.LONGITUDE_KEY] as? Double ?? MapCameraPosition.LONGITUDE_DEFAULT;
+ self.bearing = object[MapCameraPosition.BEARING_KEY] as? Double ?? MapCameraPosition.BEARING_DEFAULT;
+ self.tilt = object[MapCameraPosition.TILT_KEY] as? Double ?? MapCameraPosition.TILT_DEFAULT;
+ self.zoom = object[MapCameraPosition.ZOOM_KEY] as? Float ?? MapCameraPosition.ZOOM_DEFAULT;
+ }
+}
diff --git a/ios/Plugin/maps-utils/MapPreferences.swift b/ios/Plugin/maps-utils/MapPreferences.swift
new file mode 100644
index 00000000..81bf0420
--- /dev/null
+++ b/ios/Plugin/maps-utils/MapPreferences.swift
@@ -0,0 +1,27 @@
+import Capacitor
+
+class MapPreferences {
+ public var gestures: MapPreferencesGestures!
+ public var controls: MapPreferencesControls!
+ public var appearance: MapPreferencesAppearance!
+
+ public init() {
+ self.gestures = MapPreferencesGestures();
+ self.controls = MapPreferencesControls();
+ self.appearance = MapPreferencesAppearance();
+ }
+
+ open func updateFromJSObject(_ preferences: JSObject!) {
+ if preferences != nil {
+ // update gestures
+ let gesturesObject: JSObject! = preferences["gestures"] as? JSObject ?? JSObject();
+ self.gestures.updateFromJSObject(object: gesturesObject);
+ // update controls
+ let controlsObject: JSObject! = preferences["controls"] as? JSObject ?? JSObject();
+ self.controls.updateFromJSObject(object: controlsObject);
+ // update appearance
+ let appearanceObject: JSObject! = preferences["appearance"] as? JSObject ?? JSObject();
+ self.appearance.updateFromJSObject(object: appearanceObject);
+ }
+ }
+}
diff --git a/ios/Plugin/maps-utils/MapPreferencesAppearance.swift b/ios/Plugin/maps-utils/MapPreferencesAppearance.swift
new file mode 100644
index 00000000..3ce5094b
--- /dev/null
+++ b/ios/Plugin/maps-utils/MapPreferencesAppearance.swift
@@ -0,0 +1,94 @@
+import Capacitor
+import GoogleMaps
+
+class MapPreferencesAppearance {
+ public static let TYPE_KEY: String! = "type"
+ public static let STYLE_KEY: String! = "style"
+ public static let BUILDINGS_SHOWN_KEY: String! = "isBuildingsShown"
+ public static let INDOOR_SHOWN_KEY: String! = "isIndoorShown"
+ public static let MY_LOCATION_DOT_SHOWN_KEY: String! = "isMyLocationDotShown"
+ public static let TRAFFIC_SHOWN_KEY: String! = "isTrafficShown"
+
+ var type: GMSMapViewType = GMSMapViewType.normal;
+
+ var style: GMSMapStyle? = nil;
+
+ var _isBuildingsShown: Bool = true
+ var isBuildingsShown: Bool! {
+ get {
+ return _isBuildingsShown
+ }
+ set (newVal) {
+ _isBuildingsShown = newVal ?? true
+ }
+ }
+
+ var _isIndoorShown: Bool = true
+ var isIndoorShown: Bool! {
+ get {
+ return _isIndoorShown
+ }
+ set (newVal) {
+ _isIndoorShown = newVal ?? true
+ }
+ }
+
+ var _isMyLocationDotShown: Bool = false
+ var isMyLocationDotShown: Bool! {
+ get {
+ return _isMyLocationDotShown
+ }
+ set (newVal) {
+ _isMyLocationDotShown = newVal ?? false
+ }
+ }
+
+ var _isTrafficShown: Bool = false
+ var isTrafficShown: Bool! {
+ get {
+ return _isTrafficShown
+ }
+ set (newVal) {
+ _isTrafficShown = newVal ?? false
+ }
+ }
+
+ func updateFromJSObject(object: JSObject) {
+ if (object[MapPreferencesAppearance.TYPE_KEY] != nil) {
+ let type = object[MapPreferencesAppearance.TYPE_KEY] as? Int;
+ if (type != nil) {
+ if (type == 0) {
+ self.type = GMSMapViewType.none;
+ } else if (type == 1) {
+ self.type = GMSMapViewType.normal;
+ } else if (type == 2) {
+ self.type = GMSMapViewType.satellite;
+ } else if (type == 3) {
+ self.type = GMSMapViewType.terrain;
+ } else if (type == 4) {
+ self.type = GMSMapViewType.hybrid;
+ } else {
+ self.type = GMSMapViewType.normal;
+ }
+ }
+ }
+
+ if (object[MapPreferencesAppearance.STYLE_KEY] != nil) {
+ let style = object[MapPreferencesAppearance.STYLE_KEY] as? String;
+ if (style == nil) {
+ self.style = nil;
+ } else {
+ do {
+ try self.style = GMSMapStyle(jsonString: style ?? "{}");
+ } catch {
+ print("error parsing style json string");
+ }
+ }
+ }
+
+ self.isBuildingsShown = object[MapPreferencesAppearance.BUILDINGS_SHOWN_KEY] as? Bool;
+ self.isIndoorShown = object[MapPreferencesAppearance.INDOOR_SHOWN_KEY] as? Bool;
+ self.isMyLocationDotShown = object[MapPreferencesAppearance.MY_LOCATION_DOT_SHOWN_KEY] as? Bool;
+ self.isTrafficShown = object[MapPreferencesAppearance.TRAFFIC_SHOWN_KEY] as? Bool;
+ }
+}
diff --git a/ios/Plugin/maps-utils/MapPreferencesControls.swift b/ios/Plugin/maps-utils/MapPreferencesControls.swift
new file mode 100644
index 00000000..f18d1ff8
--- /dev/null
+++ b/ios/Plugin/maps-utils/MapPreferencesControls.swift
@@ -0,0 +1,45 @@
+import Capacitor
+
+class MapPreferencesControls {
+ public static let COMPASS_BUTTON_KEY: String! = "isCompassButtonEnabled"
+ public static let INDOOR_LEVEL_PICKER_KEY: String! = "isIndoorLevelPickerEnabled"
+ // (android only) public static let MAP_TOOLBAR_KEY: String! = "isMapToolbarEnabled"
+ public static let MY_LOCATION_BUTTON_KEY: String! = "isMyLocationButtonEnabled"
+ // (android only) public static let ZOOM_BUTTONS_KEY: String! = "isZoomButtonsEnabled"
+
+ var _isCompassButtonEnabled: Bool = true
+ var isCompassButtonEnabled: Bool! {
+ get {
+ return _isCompassButtonEnabled
+ }
+ set (newVal) {
+ _isCompassButtonEnabled = newVal ?? true
+ }
+ }
+
+ var _isIndoorLevelPickerEnabled: Bool = false
+ var isIndoorLevelPickerEnabled: Bool! {
+ get {
+ return _isIndoorLevelPickerEnabled
+ }
+ set (newVal) {
+ _isIndoorLevelPickerEnabled = newVal ?? false
+ }
+ }
+
+ var _isMyLocationButtonEnabled: Bool = true
+ var isMyLocationButtonEnabled: Bool! {
+ get {
+ return _isMyLocationButtonEnabled
+ }
+ set (newVal) {
+ _isMyLocationButtonEnabled = newVal ?? true
+ }
+ }
+
+ func updateFromJSObject(object: JSObject) {
+ self.isCompassButtonEnabled = object[MapPreferencesControls.COMPASS_BUTTON_KEY] as? Bool;
+ self.isIndoorLevelPickerEnabled = object[MapPreferencesControls.INDOOR_LEVEL_PICKER_KEY] as? Bool;
+ self.isMyLocationButtonEnabled = object[MapPreferencesControls.MY_LOCATION_BUTTON_KEY] as? Bool;
+ }
+}
diff --git a/ios/Plugin/maps-utils/MapPreferencesGestures.swift b/ios/Plugin/maps-utils/MapPreferencesGestures.swift
new file mode 100644
index 00000000..5a49e86e
--- /dev/null
+++ b/ios/Plugin/maps-utils/MapPreferencesGestures.swift
@@ -0,0 +1,67 @@
+import Capacitor
+
+class MapPreferencesGestures {
+ public static let ROTATE_ALLOWED_KEY: String! = "isRotateAllowed"
+ public static let SCROLL_ALLOWED_KEY: String! = "isScrollAllowed"
+ public static let SCROLL_ALLOWED_DURING_ROTATE_OR_ZOOM_KEY: String! = "isScrollAllowedDuringRotateOrZoom"
+ public static let TILT_ALLOWED_KEY: String! = "isTiltAllowed"
+ public static let ZOOM_ALLOWED_KEY: String! = "isZoomAllowed"
+
+ var _isRotateAllowed: Bool = true
+ var isRotateAllowed: Bool! {
+ get {
+ return _isRotateAllowed
+ }
+ set (newVal) {
+ _isRotateAllowed = newVal ?? true
+ }
+ }
+
+ var _isScrollAllowed: Bool = true
+ var isScrollAllowed: Bool! {
+ get {
+ return _isScrollAllowed
+ }
+ set (newVal) {
+ _isScrollAllowed = newVal ?? true
+ }
+ }
+
+ var _isScrollAllowedDuringRotateOrZoom: Bool = true
+ var isScrollAllowedDuringRotateOrZoom: Bool! {
+ get {
+ return _isScrollAllowedDuringRotateOrZoom
+ }
+ set (newVal) {
+ _isScrollAllowedDuringRotateOrZoom = newVal ?? true
+ }
+ }
+
+ var _isTiltAllowed: Bool = true
+ var isTiltAllowed: Bool! {
+ get {
+ return _isTiltAllowed
+ }
+ set (newVal) {
+ _isTiltAllowed = newVal ?? true
+ }
+ }
+
+ var _isZoomAllowed: Bool = true
+ var isZoomAllowed: Bool! {
+ get {
+ return _isZoomAllowed
+ }
+ set (newVal) {
+ _isZoomAllowed = newVal ?? true
+ }
+ }
+
+ func updateFromJSObject(object: JSObject) {
+ self.isRotateAllowed = object[MapPreferencesGestures.ROTATE_ALLOWED_KEY] as? Bool;
+ self.isScrollAllowed = object[MapPreferencesGestures.SCROLL_ALLOWED_KEY] as? Bool;
+ self.isScrollAllowedDuringRotateOrZoom = object[MapPreferencesGestures.SCROLL_ALLOWED_DURING_ROTATE_OR_ZOOM_KEY] as? Bool;
+ self.isScrollAllowed = object[MapPreferencesGestures.TILT_ALLOWED_KEY] as? Bool;
+ self.isScrollAllowed = object[MapPreferencesGestures.ZOOM_ALLOWED_KEY] as? Bool;
+ }
+}
diff --git a/ios/Plugin/marker/CustomClusterIconGenerator.swift b/ios/Plugin/marker/CustomClusterIconGenerator.swift
new file mode 100644
index 00000000..ebaefff4
--- /dev/null
+++ b/ios/Plugin/marker/CustomClusterIconGenerator.swift
@@ -0,0 +1,74 @@
+//
+// MapClusterIconGenerator.swift
+// CapacitorCommunityCapacitorGooglemapsNative
+//
+// Created by Admin on 24.01.2022.
+//
+
+import GoogleMapsUtils
+import UIKit
+
+class CustomClusterIconGenerator: GMUDefaultClusterIconGenerator {
+
+ // clusterSizes count must be equal buckets count
+ let clusterSizes: [Int] = [64, 68, 72, 76, 80, 84, 88, 92];
+ let buckets: [NSNumber] = [5, 10, 15, 20, 25, 30, 35, 40];
+
+ var backgroundImages : [UIImage];
+
+ override init() {
+
+ backgroundImages = [UIImage](repeating: UIImage(), count: clusterSizes.count);
+ var index = 0;
+ for size in clusterSizes {
+ backgroundImages[index] = (UIImage(named: "cluster")?
+ .resized(to: CGSize(width: size, height: size)))!;
+ index += 1;
+ }
+ super.init();
+ }
+
+ override func icon(forSize size: UInt) -> UIImage {
+ let bucketIndex : Int = bucketIndex(forSize: Int(size));
+ var text : String = String(size);
+
+ if(backgroundImages != nil) {
+ let image : UIImage = backgroundImages[bucketIndex]
+ return textToImage(drawText: text as NSString, inImage: image, font: UIFont.systemFont(ofSize: 14))
+ } else {
+ let image = UIImage(named: "cluster");
+ return textToImage(drawText: text as NSString, inImage: image!, font: UIFont.systemFont(ofSize: 14))
+ }
+ }
+
+ private func bucketIndex(forSize size: Int) -> Int {
+ var index = 0
+ while index + 1 < buckets.count && Int(buckets[index + 1].uintValue) <= size {
+ index += 1
+ }
+ return index
+ }
+
+ private func textToImage(drawText text: NSString, inImage image: UIImage, font: UIFont) -> UIImage {
+
+ UIGraphicsBeginImageContext(image.size)
+ image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
+
+ let textStyle = NSMutableParagraphStyle()
+ textStyle.alignment = NSTextAlignment.center
+ let textColor = UIColor.white
+ let attributes = [
+ NSAttributedString.Key.font: font,
+ NSAttributedString.Key.paragraphStyle: textStyle,
+ NSAttributedString.Key.foregroundColor: textColor]
+
+ // vertically center (depending on font)
+ let textH = font.lineHeight
+ let textY = (image.size.height-textH)/2
+ let textRect = CGRect(x: 0, y: textY, width: image.size.width, height: textH)
+ text.draw(in: textRect.integral, withAttributes: attributes)
+ let result = UIGraphicsGetImageFromCurrentImageContext()
+ UIGraphicsEndImageContext()
+ return result!
+ }
+}
diff --git a/ios/Plugin/marker/CustomMarker.swift b/ios/Plugin/marker/CustomMarker.swift
new file mode 100644
index 00000000..2371caac
--- /dev/null
+++ b/ios/Plugin/marker/CustomMarker.swift
@@ -0,0 +1,83 @@
+import Capacitor
+import GoogleMaps
+
+class CustomMarker: GMSMarker {
+ private var markerId: String! = NSUUID().uuidString.lowercased()
+ public var id: String {
+ get {
+ return self.markerId;
+ }
+ }
+
+ var markerCategoryId : Int = 0;
+
+ public func updateFromJSObject(preferences: JSObject) {
+ let markerId = preferences["id"] as? String ?? nil;
+ if(markerId != nil) {
+ self.markerId = markerId as! String;
+ }
+
+ let position = preferences["position"] as? JSObject ?? JSObject();
+ let latitude = position["latitude"] as? Double ?? 0.0;
+ let longitude = position["longitude"] as? Double ?? 0.0;
+ self.position = CLLocationCoordinate2D(latitude: latitude, longitude: longitude);
+
+ self.title = preferences["title"] as? String ?? nil;
+ self.snippet = preferences["snippet"] as? String ?? nil;
+ self.opacity = preferences["opacity"] as? Float ?? 1.0;
+ self.isFlat = preferences["isFlat"] as? Bool ?? false;
+ self.isDraggable = preferences["isDraggable"] as? Bool ?? false;
+ self.markerCategoryId = preferences["category"] as? Int ?? 0;
+
+
+ let metadata: JSObject = preferences["metadata"] as? JSObject ?? JSObject();
+
+ var userData = JSObject()
+ userData["id"] = self.id
+ userData["iconId"] = self.markerCategoryId;
+ userData["metadata"] = metadata
+
+ self.userData = userData
+ }
+
+ public static func getResultForMarker(_ marker: GMSMarker, _ mapId: String) -> PluginCallResultData {
+ // check marker is marker from map or from clusterManager
+ let markerObj : GMSMarker;
+ if marker.userData is GMSMarker {
+ markerObj = marker.userData as! GMSMarker;
+ } else {
+ markerObj = marker as! GMSMarker
+ }
+
+ let tag: JSObject = markerObj.userData as! JSObject;
+
+ return [
+ "position": [
+ "mapId": mapId,
+ "latitude": marker.position.latitude,
+ "longitude": marker.position.longitude,
+ ] as! JSObject,
+ "marker": [
+ "metadata": tag["metadata"] ?? JSObject(),
+ "id": tag["id"] ?? "",
+ "iconId": tag["iconId"] ?? -1,
+ ]
+ ];
+
+// return [
+// "marker": [
+// "id": tag["id"] ?? "",
+// "title": marker.title ?? "",
+// "snippet": marker.snippet ?? "",
+// "opacity": marker.opacity,
+// "isFlat": marker.isFlat,
+// "isDraggable": marker.isDraggable,
+// "position": [
+// "latitude": marker.position.latitude,
+// "longitude": marker.position.longitude
+// ],
+// "metadata": tag["metadata"] ?? JSObject()
+// ]
+// ];
+ }
+}
diff --git a/ios/Plugin/marker/CustomRendererMarkers.swift b/ios/Plugin/marker/CustomRendererMarkers.swift
new file mode 100644
index 00000000..3fea4e60
--- /dev/null
+++ b/ios/Plugin/marker/CustomRendererMarkers.swift
@@ -0,0 +1,59 @@
+//
+// CustomRendererMarkers.swift
+// CapacitorCommunityCapacitorGooglemapsNative
+//
+// Created by Admin on 24.01.2022.
+//
+
+import Foundation
+import GoogleMapsUtils
+
+class CustomRendererMarkers: GMUDefaultClusterRenderer {
+
+ // -- constants --
+ public static let MIN_COUNT_ELEMENTS_IN_CLUSTER : Int = 2;
+
+var mapView:GMSMapView?
+let kGMUAnimationDuration: Double = 0.5
+
+override init(mapView: GMSMapView, clusterIconGenerator iconGenerator: GMUClusterIconGenerator) {
+
+ super.init(mapView: mapView, clusterIconGenerator: iconGenerator)
+}
+
+ override func shouldRender(as cluster: GMUCluster, atZoom zoom: Float) -> Bool {
+ return cluster.count >= CustomRendererMarkers.MIN_COUNT_ELEMENTS_IN_CLUSTER;
+ }
+
+
+// method dont working and dont override anything
+// TODO: rewrite super class for changing animation
+func markerWithPosition(position: CLLocationCoordinate2D, from: CLLocationCoordinate2D, userData: AnyObject, clusterIcon: UIImage, animated: Bool) -> GMSMarker {
+ let initialPosition = animated ? from : position
+ let marker = GMSMarker(position: initialPosition)
+ marker.userData! = userData
+ if clusterIcon.cgImage != nil {
+ marker.icon = clusterIcon
+ }
+ else {
+ marker.icon = self.getCustomTitleItem(userData: userData)
+
+ }
+ marker.map = mapView
+ if animated
+ {
+ CATransaction.begin()
+ CAAnimation.init().duration = kGMUAnimationDuration
+ marker.layer.latitude = position.latitude
+ marker.layer.longitude = position.longitude
+ CATransaction.commit()
+ }
+ return marker
+}
+
+func getCustomTitleItem(userData: AnyObject) -> UIImage {
+ let item = userData as! CustomMarker
+ return MarkerCategory.markerCategories[item.markerCategoryId]?.getIcon ?? item.icon!;
+// return item.icon!
+}
+}
diff --git a/ios/Plugin/marker/MarkerCategory.swift b/ios/Plugin/marker/MarkerCategory.swift
new file mode 100644
index 00000000..61c3b77e
--- /dev/null
+++ b/ios/Plugin/marker/MarkerCategory.swift
@@ -0,0 +1,31 @@
+//
+// MarkerCategory.swift
+// CapacitorCommunityCapacitorGooglemapsNative
+//
+// Created by Admin on 27.01.2022.
+//
+
+import Foundation
+
+class MarkerCategory {
+
+ private var id : Int?;
+ private var title : String?;
+ private var icon : UIImage?;
+
+ static var markerCategories = [Int : MarkerCategory]();
+
+ init(_ id: Int, _ title: String, _ icon: UIImage?) {
+ self.id = id;
+ self.title = title;
+ self.icon = icon;
+
+ MarkerCategory.markerCategories[self.id ?? 0] = self
+ }
+
+ var getIcon : UIImage? {
+ get {
+ return icon;
+ }
+ }
+}
diff --git a/ios/Plugin/utility/Events.swift b/ios/Plugin/utility/Events.swift
new file mode 100644
index 00000000..6f544098
--- /dev/null
+++ b/ios/Plugin/utility/Events.swift
@@ -0,0 +1,19 @@
+//
+// Events.swift
+// CapacitorCommunityCapacitorGooglemapsNative
+//
+// Created by Admin on 27.12.2021.
+//
+
+import Foundation
+
+class Events {
+ static var EVENT_DID_TAP_INFO_WINDOW: String = "didTapInfoWindow";
+ static var EVENT_DID_CLOSE_INFO_WINDOW: String = "didCloseInfoWindow";
+ static var EVENT_DID_TAP_MAP: String = "didTapMap";
+ static var EVENT_DID_LONG_PRESS_MAP: String = "didLongPressMap";
+ static var EVENT_DID_TAP_MARKER: String = "didTapMarker";
+ static var EVENT_DID_TAP_CLUSTER: String = "didTapCluster";
+ static var EVENT_DID_TAP_MY_LOCATION_BUTTON: String = "didTapMyLocationButton";
+ static var EVENT_DID_TAP_MY_LOCATION_DOT: String = "didTapMyLocationDot";
+}
diff --git a/package.json b/package.json
index 50b35d32..6c5558ff 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@capacitor-community/capacitor-googlemaps-native",
- "version": "1.1.0",
+ "version": "1.1.5",
"description": "Plugin using native Maps API for Android and iOS.",
"main": "dist/esm/index.js",
"module": "dist/esm/index.js",
@@ -46,7 +46,7 @@
},
"repository": {
"type": "git",
- "url": "https://github.com/capacitor-community/capacitor-googlemaps-native"
+ "url": "https://github.com/vorokami/capacitor-google-maps"
},
"bugs": {
"url": "https://github.com/capacitor-community/capacitor-googlemaps-native/issues"