diff --git a/plugin/README.md b/plugin/README.md index 2964128..6dea13a 100644 --- a/plugin/README.md +++ b/plugin/README.md @@ -1183,6 +1183,13 @@ but the current specification only allows X, Y, and (optionally) Z to be defined number[] +#### Marker + +Supports markers of either either "legacy" or "advanced" types. + +google.maps.Marker | google.maps.marker.AdvancedMarkerElement + + ### Enums diff --git a/plugin/e2e-tests/android/app/src/main/AndroidManifest.xml b/plugin/e2e-tests/android/app/src/main/AndroidManifest.xml index 49f93ce..bd137e1 100644 --- a/plugin/e2e-tests/android/app/src/main/AndroidManifest.xml +++ b/plugin/e2e-tests/android/app/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ android:theme="@style/AppTheme"> /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +134,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/plugin/e2e-tests/android/gradlew.bat b/plugin/e2e-tests/android/gradlew.bat index 6689b85..9b42019 100644 --- a/plugin/e2e-tests/android/gradlew.bat +++ b/plugin/e2e-tests/android/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/plugin/e2e-tests/android/variables.gradle b/plugin/e2e-tests/android/variables.gradle index 06370a5..14eafdf 100644 --- a/plugin/e2e-tests/android/variables.gradle +++ b/plugin/e2e-tests/android/variables.gradle @@ -2,12 +2,12 @@ ext { minSdkVersion = 23 compileSdkVersion = 35 targetSdkVersion = 35 - androidxActivityVersion = '1.8.0' + androidxActivityVersion = '1.9.2' androidxAppCompatVersion = '1.7.0' androidxCoordinatorLayoutVersion = '1.2.0' - androidxCoreVersion = '1.12.0' - androidxFragmentVersion = '1.6.2' - androidxWebkitVersion = '1.9.0' + androidxCoreVersion = '1.15.0' + androidxFragmentVersion = '1.8.4' + androidxWebkitVersion = '1.12.1' androidxJunitVersion = '1.2.1' androidxEspressoCoreVersion = '3.6.1' cordovaAndroidVersion = '10.1.1' diff --git a/plugin/e2e-tests/ios/App/App.xcodeproj/project.pbxproj b/plugin/e2e-tests/ios/App/App.xcodeproj/project.pbxproj index e584de3..fedc3e1 100644 --- a/plugin/e2e-tests/ios/App/App.xcodeproj/project.pbxproj +++ b/plugin/e2e-tests/ios/App/App.xcodeproj/project.pbxproj @@ -297,7 +297,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -348,7 +348,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -364,7 +364,7 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 9YN2HU59K8; INFOPLIST_FILE = App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = io.ionic.googlemaps; @@ -383,7 +383,7 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 9YN2HU59K8; INFOPLIST_FILE = App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = io.ionic.googlemaps; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/plugin/e2e-tests/package.json b/plugin/e2e-tests/package.json index 3f3292b..6ace8ba 100644 --- a/plugin/e2e-tests/package.json +++ b/plugin/e2e-tests/package.json @@ -3,17 +3,14 @@ "version": "0.0.1", "private": true, "dependencies": { - "@capacitor/android": "^6.0.0", - "@capacitor/app": "^6.0.0", - "@capacitor/core": "^6.0.0", - "@capacitor/android": "next", - "@capacitor/core": "next", + "@capacitor/app": "^7.0.0", + "@capacitor/android": "^7.0.0", + "@capacitor/core": "^7.0.0", "@capacitor/google-maps": "file:..", - "@capacitor/haptics": "^6.0.0", - "@capacitor/ios": "^6.0.0", - "@capacitor/keyboard": "^6.0.0", - "@capacitor/status-bar": "^6.0.0", - "@capacitor/ios": "next", + "@capacitor/haptics": "^7.0.0", + "@capacitor/ios": "^7.0.0", + "@capacitor/keyboard": "^7.0.0", + "@capacitor/status-bar": "^7.0.0", "@ionic/react": "^6.0.0", "@ionic/react-router": "^6.0.0", "@testing-library/jest-dom": "^5.11.9", @@ -80,11 +77,10 @@ ] }, "devDependencies": { - "@capacitor/cli": "^6.0.0", + "@capacitor/cli": "^7.0.0", "@ionic/e2e": "0.2.0-next.6", "@ionic/e2e-components-ionic": "0.2.0-next.6", - "@capacitor/cli": "next", "appium": "^1.22.1" }, "description": "An Ionic project" -} +} \ No newline at end of file diff --git a/plugin/package.json b/plugin/package.json index 0967fff..85c8117 100644 --- a/plugin/package.json +++ b/plugin/package.json @@ -56,7 +56,7 @@ "devDependencies": { "@capacitor/android": "next", "@capacitor/core": "next", - "@capacitor/docgen": "0.2.2", + "@capacitor/docgen": "0.3.0", "@capacitor/ios": "next", "@ionic/prettier-config": "^1.0.1", "@types/resize-observer-browser": "^0.1.7", @@ -89,8 +89,8 @@ "access": "public" }, "dependencies": { - "@googlemaps/js-api-loader": "~1.13.7", - "@googlemaps/markerclusterer": "~2.0.7", - "@types/google.maps": "~3.50.5" + "@googlemaps/js-api-loader": "~1.16.8", + "@googlemaps/markerclusterer": "~2.5.3", + "@types/google.maps": "~3.58.1" } } diff --git a/plugin/src/web.ts b/plugin/src/web.ts index 87cea93..f1ba7c0 100644 --- a/plugin/src/web.ts +++ b/plugin/src/web.ts @@ -31,12 +31,14 @@ import type { export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogleMapsPlugin { private gMapsRef: typeof google.maps | undefined = undefined; + private AdvancedMarkerElement: typeof google.maps.marker.AdvancedMarkerElement | undefined = undefined; + private PinElement: typeof google.maps.marker.PinElement | undefined = undefined; private maps: { [id: string]: { element: HTMLElement; map: google.maps.Map; markers: { - [id: string]: google.maps.Marker; + [id: string]: google.maps.marker.AdvancedMarkerElement; }; polygons: { [id: string]: google.maps.Polygon; @@ -55,6 +57,7 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle private currPolygonId = 0; private currCircleId = 0; private currPolylineId = 0; + private currMapId = 0; private onClusterClickHandler: onClusterClickHandler = ( _: google.maps.MapMouseEvent, @@ -64,24 +67,27 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle const mapId = this.getIdFromMap(map); const items: any[] = []; - if (cluster.markers != undefined) { + if (cluster.markers != undefined && this.AdvancedMarkerElement) { for (const marker of cluster.markers) { - const markerId = this.getIdFromMarker(mapId, marker); - - items.push({ - markerId: markerId, - latitude: marker.getPosition()?.lat(), - longitude: marker.getPosition()?.lng(), - title: marker.getTitle(), - snippet: '', - }); + if (marker instanceof this.AdvancedMarkerElement) { + const markerId = this.getIdFromMarker(mapId, marker); + const position = marker.position as google.maps.LatLngLiteral; + + items.push({ + markerId: markerId, + latitude: position.lat, + longitude: position.lng, + title: marker.title ?? '', + snippet: '', + }); + } } } this.notifyListeners('onClusterClick', { mapId: mapId, - latitude: cluster.position.lat(), - longitude: cluster.position.lng(), + latitude: cluster.position.lat, + longitude: cluster.position.lng, size: cluster.count, items: items, }); @@ -97,7 +103,7 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle return ''; } - private getIdFromMarker(mapId: string, marker: google.maps.Marker): string { + private getIdFromMarker(mapId: string, marker: google.maps.marker.AdvancedMarkerElement): string { for (const id in this.maps[mapId].markers) { if (this.maps[mapId].markers[id] == marker) { return id; @@ -119,6 +125,12 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle }); const google = await loader.load(); this.gMapsRef = google.maps; + + // Import marker library once + const { AdvancedMarkerElement, PinElement } = await google.maps.importLibrary('marker') as google.maps.MarkerLibrary; + this.AdvancedMarkerElement = AdvancedMarkerElement; + this.PinElement = PinElement; + console.log('Loaded google maps API'); } } @@ -251,13 +263,12 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle const map = this.maps[_args.id]; for (const markerArgs of _args.markers) { - const markerOpts = this.buildMarkerOpts(markerArgs, map.map); - const marker = new google.maps.Marker(markerOpts); + const advancedMarker = this.buildMarkerOpts(markerArgs, map.map); const id = '' + this.currMarkerId; - map.markers[id] = marker; - this.setMarkerListeners(_args.id, id, marker); + map.markers[id] = advancedMarker; + await this.setMarkerListeners(_args.id, id, advancedMarker); markerIds.push(id); this.currMarkerId++; @@ -267,14 +278,12 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle } async addMarker(_args: AddMarkerArgs): Promise<{ id: string }> { - const markerOpts = this.buildMarkerOpts(_args.marker, this.maps[_args.id].map); - - const marker = new google.maps.Marker(markerOpts); + const advancedMarker = this.buildMarkerOpts(_args.marker, this.maps[_args.id].map); const id = '' + this.currMarkerId; - this.maps[_args.id].markers[id] = marker; - this.setMarkerListeners(_args.id, id, marker); + this.maps[_args.id].markers[id] = advancedMarker; + await this.setMarkerListeners(_args.id, id, advancedMarker); this.currMarkerId++; @@ -285,14 +294,18 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle const map = this.maps[_args.id]; for (const id of _args.markerIds) { - map.markers[id].setMap(null); - delete map.markers[id]; + if (map.markers[id]) { + map.markers[id].map = null; + delete map.markers[id]; + } } } async removeMarker(_args: RemoveMarkerArgs): Promise { - this.maps[_args.id].markers[_args.markerId].setMap(null); - delete this.maps[_args.id].markers[_args.markerId]; + if (this.maps[_args.id].markers[_args.markerId]) { + this.maps[_args.id].markers[_args.markerId].map = null; + delete this.maps[_args.id].markers[_args.markerId]; + } } async addPolygons(args: AddPolygonsArgs): Promise<{ ids: string[] }> { @@ -383,7 +396,7 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle } async enableClustering(_args: EnableClusteringArgs): Promise { - const markers: google.maps.Marker[] = []; + const markers: google.maps.marker.AdvancedMarkerElement[] = []; for (const id in this.maps[_args.id].markers) { markers.push(this.maps[_args.id].markers[id]); @@ -400,8 +413,17 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle } async disableClustering(_args: { id: string }): Promise { - this.maps[_args.id].markerClusterer?.setMap(null); - this.maps[_args.id].markerClusterer = undefined; + const mapInstance = this.maps[_args.id]; + if (mapInstance.markerClusterer) { + const markers = Object.values(mapInstance.markers); + + mapInstance.markerClusterer.setMap(null); + mapInstance.markerClusterer = undefined; + + for (const marker of markers) { + marker.map = mapInstance.map; + } + } } async onScroll(): Promise { @@ -420,8 +442,14 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle console.log(`Create map: ${_args.id}`); await this.importGoogleLib(_args.apiKey, _args.region, _args.language); + // Ensure we have a Map ID for Advanced Markers + const config = { ..._args.config }; + if (!config.mapId) { + config.mapId = `capacitor_map_${this.currMapId++}`; + } + this.maps[_args.id] = { - map: new window.google.maps.Map(_args.element, { ..._args.config }), + map: new window.google.maps.Map(_args.element, config), element: _args.element, markers: {}, polygons: {}, @@ -503,50 +531,56 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle }); } - async setMarkerListeners(mapId: string, markerId: string, marker: google.maps.Marker): Promise { + async setMarkerListeners(mapId: string, markerId: string, marker: google.maps.marker.AdvancedMarkerElement): Promise { marker.addListener('click', () => { + const position = marker.position as google.maps.LatLngLiteral; this.notifyListeners('onMarkerClick', { mapId: mapId, markerId: markerId, - latitude: marker.getPosition()?.lat(), - longitude: marker.getPosition()?.lng(), - title: marker.getTitle(), + latitude: position.lat, + longitude: position.lng, + title: marker.title ?? '', snippet: '', }); }); - marker.addListener('dragstart', () => { - this.notifyListeners('onMarkerDragStart', { - mapId: mapId, - markerId: markerId, - latitude: marker.getPosition()?.lat(), - longitude: marker.getPosition()?.lng(), - title: marker.getTitle(), - snippet: '', + if (marker.gmpDraggable) { + marker.addListener('dragstart', () => { + const position = marker.position as google.maps.LatLngLiteral; + this.notifyListeners('onMarkerDragStart', { + mapId: mapId, + markerId: markerId, + latitude: position.lat, + longitude: position.lng, + title: marker.title ?? '', + snippet: '', + }); }); - }); - marker.addListener('drag', () => { - this.notifyListeners('onMarkerDrag', { - mapId: mapId, - markerId: markerId, - latitude: marker.getPosition()?.lat(), - longitude: marker.getPosition()?.lng(), - title: marker.getTitle(), - snippet: '', + marker.addListener('drag', () => { + const position = marker.position as google.maps.LatLngLiteral; + this.notifyListeners('onMarkerDrag', { + mapId: mapId, + markerId: markerId, + latitude: position.lat, + longitude: position.lng, + title: marker.title ?? '', + snippet: '', + }); }); - }); - marker.addListener('dragend', () => { - this.notifyListeners('onMarkerDragEnd', { - mapId: mapId, - markerId: markerId, - latitude: marker.getPosition()?.lat(), - longitude: marker.getPosition()?.lng(), - title: marker.getTitle(), - snippet: '', + marker.addListener('dragend', () => { + const position = marker.position as google.maps.LatLngLiteral; + this.notifyListeners('onMarkerDragEnd', { + mapId: mapId, + markerId: markerId, + latitude: position.lat, + longitude: position.lng, + title: marker.title ?? '', + snippet: '', + }); }); - }); + } } async setMapListeners(mapId: string): Promise { @@ -598,31 +632,40 @@ export class CapacitorGoogleMapsWeb extends WebPlugin implements CapacitorGoogle }); } - private buildMarkerOpts(marker: Marker, map: google.maps.Map): google.maps.MarkerOptions { - let iconImage: google.maps.Icon | undefined = undefined; + private buildMarkerOpts(marker: Marker, map: google.maps.Map): google.maps.marker.AdvancedMarkerElement { + if (!this.AdvancedMarkerElement || !this.PinElement) { + throw new Error('Marker library not loaded'); + } + + let content: HTMLElement | undefined = undefined; + if (marker.iconUrl) { - iconImage = { - url: marker.iconUrl, - scaledSize: marker.iconSize ? new google.maps.Size(marker.iconSize.width, marker.iconSize.height) : null, - anchor: marker.iconAnchor - ? new google.maps.Point(marker.iconAnchor.x, marker.iconAnchor.y) - : new google.maps.Point(0, 0), - origin: marker.iconOrigin - ? new google.maps.Point(marker.iconOrigin.x, marker.iconOrigin.y) - : new google.maps.Point(0, 0), + const img = document.createElement('img'); + img.src = marker.iconUrl; + if (marker.iconSize) { + img.style.width = `${marker.iconSize.width}px`; + img.style.height = `${marker.iconSize.height}px`; + } + content = img; + } else { + const pinOptions: google.maps.marker.PinElementOptions = { + scale: marker.opacity ?? 1, + glyph: marker.title, + background: marker.tintColor ? `rgb(${marker.tintColor.r}, ${marker.tintColor.g}, ${marker.tintColor.b})` : undefined, }; + + const pin = new this.PinElement(pinOptions); + content = pin.element; } - const opts: google.maps.MarkerOptions = { + const advancedMarker = new this.AdvancedMarkerElement({ position: marker.coordinate, map: map, - opacity: marker.opacity, + content: content, title: marker.title, - icon: iconImage, - draggable: marker.draggable, - zIndex: marker.zIndex ?? 0, - }; + gmpDraggable: marker.draggable, + }); - return opts; + return advancedMarker; } }