+
+
\ No newline at end of file
diff --git a/.jscodeshiftignore b/.jscodeshiftignore
new file mode 100644
index 0000000000..a78536177b
--- /dev/null
+++ b/.jscodeshiftignore
@@ -0,0 +1,9 @@
+# To run a codeshift on the react-native-maps library, cd to the root dir and run:
+# jscodeshift -t PATH_TO_TRANSFORM . --ignore-config .jscodeshiftignore
+.idea
+android
+docs
+example
+gradle
+node_modules
+scripts
\ No newline at end of file
diff --git a/.npmignore b/.npmignore
index 33a9488b16..4ce7bc58bc 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1 +1,2 @@
example
+.babelrc
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c13e5ba07..5678d80526 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,62 @@
# Change Log
+## 0.17.0 (October 11, 2017)
+* iOS: [#1527](https://github.com/airbnb/react-native-maps/pull/1527) Added [iOS / Google Maps] support for showsIndoorLevelPicker
+* iOS/Android: [#1544](https://github.com/airbnb/react-native-maps/pull/1544) Adds support to animateToBearing and animateToViewingAngle ( IOS + Android )
+* JS: [#1503](https://github.com/airbnb/react-native-maps/pull/1503) Remove caret from "react": "^16.0.0-alpha.12
+* Android: [#1521](https://github.com/airbnb/react-native-maps/pull/1521) Fix rare android crashes when map size is 0
+* Common: [#1610](https://github.com/airbnb/react-native-maps/pull/1610) Added Typescript Definitions
+* Android: [#1612](https://github.com/airbnb/react-native-maps/pull/1612) Remove legalNotice from android AirMapModule
+
+## 0.16.4 (September 13, 2017)
+* Android: [#1643](https://github.com/airbnb/react-native-maps/pull/1643) [MapMarker] fix android release crash on custom marker
+
+## 0.16.3 (September 2, 2017)
+* iOS: [#1603](https://github.com/airbnb/react-native-maps/pull/1603) Added missing satellite option for iOS Google Maps
+* iOS: [#1579](https://github.com/airbnb/react-native-maps/pull/1579) Set initial region on view
+
+## 0.16.2 (August 17, 2017)
+* Android: [#1563](https://github.com/airbnb/react-native-maps/pull/#1563) Add missing native method for setting initial region
+* iOS: [#1187](https://github.com/airbnb/react-native-maps/pull/1187) Reverted due to build issues
+
+## 0.16.1 (August 15, 2017)
+* Android: [#1428](https://github.com/airbnb/react-native-maps/pull/#1428) Add ability to load marker image from drawable
+* iOS: [#1187](https://github.com/airbnb/react-native-maps/pull/1187) Improve marker performance
+* iOS/Android: [#1458](https://github.com/airbnb/react-native-maps/pull/1458) Add Google Maps legalNotice constant
+* JS: [#1546](https://github.com/airbnb/react-native-maps/pull/1546) Fix initial region native prop
+
+## 0.16.0 (August 9, 2017)
+* Android: [#1481](https://github.com/airbnb/react-native-maps/pull/1481) Handle Android RN 0.47 breaking change
+* iOS: [#1357](https://github.com/airbnb/react-native-maps/pull/1357) add MKTileOverlayRenderer
+* iOS: [#1369](https://github.com/airbnb/react-native-maps/pull/1369) Add onMapReady callback
+* Android/iOS/JS: [#1360](https://github.com/airbnb/react-native-maps/pull/1360) Add minZoom and maxZoom properties for android and ios
+* JS: [#1479](https://github.com/airbnb/react-native-maps/pull/1479) Fix timing function used in AnimatedRegion.spring
+
+## 0.15.3 (June 27, 2017)
+
+* iOS: [#1362](https://github.com/airbnb/react-native-maps/pull/1362) Updates for React 0.43-0.45 and React 16.
+* JS: [#1323](https://github.com/airbnb/react-native-maps/pull/1323) Updates for React 0.43-0.45 and React 16.
+* Android/iOS/JS: [#1440](https://github.com/airbnb/react-native-maps/pull/1440) Updates for React 0.43-0.45 and React 16.
+* iOS: [#1115](https://github.com/airbnb/react-native-maps/pull/1115) Fix animateToCoordinate and animateToRegion
+* Android: [#1403](https://github.com/airbnb/react-native-maps/pull/1403) Fix an NPE
+
+## 0.15.2 (May 20, 2017)
+
+* iOS: [#1351](https://github.com/airbnb/react-native-maps/pull/1351) Fix file references
+
+## 0.15.1 (May 19, 2017)
+
+* iOS: [#1341](https://github.com/airbnb/react-native-maps/pull/1341) Fix compile error in rn version >= 0.40
+* iOS: [#1194](https://github.com/airbnb/react-native-maps/pull/1194) Add onPress support for Google Maps Polyline
+* iOS: [#1326](https://github.com/airbnb/react-native-maps/pull/1326) Add Marker rotation for Google Maps on iOS
+* Android: [#1311](https://github.com/airbnb/react-native-maps/pull/1311) Fix overlay issue
+* Common [#1313](https://github.com/airbnb/react-native-maps/pull/1313) Fix Android sourceDir for react-native-link
+
+## 0.15.0 (May 8, 2017)
+
+* iOS: [#1195](https://github.com/airbnb/react-native-maps/pull/1195) Rename project file to fix iOS build error
+* Android: Update Google Play Services to version `10.2.4`
+
## 0.14.0 (April 4, 2017)
## Enhancements
diff --git a/README.md b/README.md
index 41139a4a02..91b22377be 100644
--- a/README.md
+++ b/README.md
@@ -207,7 +207,7 @@ Then add the AirGoogleMaps directory:
https://github.com/airbnb/react-native-maps/blob/1e71a21f39e7b88554852951f773c731c94680c9/docs/installation.md#ios
-An unoffical step-by-step guide is also available at https://gist.github.com/heron2014/e60fa003e9b117ce80d56bb1d5bfe9e0
+An unofficial step-by-step guide is also available at https://gist.github.com/heron2014/e60fa003e9b117ce80d56bb1d5bfe9e0
## Examples
@@ -405,7 +405,7 @@ getInitialState() {
takeSnapshot () {
// 'takeSnapshot' takes a config object with the
// following options
- const snapshot = this.refs.map.takeSnapshot({
+ const snapshot = this.map.takeSnapshot({
width: 300, // optional, when omitted the view-width is used
height: 300, // optional, when omitted the view-height is used
region: {..}, // iOS only, optional region to render
@@ -421,7 +421,7 @@ takeSnapshot () {
render() {
return (
-
+ { this.map = map }}>
@@ -494,7 +494,7 @@ Good:
License
--------
- Copyright (c) 2015 Airbnb
+ Copyright (c) 2017 Airbnb
Licensed under the The MIT License (MIT) (the "License");
you may not use this file except in compliance with the License.
diff --git a/build.gradle b/build.gradle
index f89d2c5948..588e5cb996 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.0'
+ classpath 'com.android.tools.build:gradle:2.3.2'
}
}
diff --git a/docs/circle.md b/docs/circle.md
index 70a5e078e5..992ac4567d 100644
--- a/docs/circle.md
+++ b/docs/circle.md
@@ -10,8 +10,8 @@
| `strokeColor` | `String` | `#000`, `rgba(r,g,b,0.5)` | The stroke color to use for the path.
| `fillColor` | `String` | `#000`, `rgba(r,g,b,0.5)` | The fill color to use for the path.
| `zIndex` | `Number` | 0 | The order in which this tile overlay is drawn with respect to other overlays. An overlay with a larger z-index is drawn over overlays with smaller z-indices. The order of overlays with the same z-index is arbitrary. The default zIndex is 0. (Android Only)
-| `lineCap` | `String` | `round` | The line cap style to apply to the open ends of the path.
-| `lineJoin` | `Array` | | The line join style to apply to corners of the path.
+| `lineCap` | `String` | `round` | The line cap style to apply to the open ends of the path. Other values : `butt`, `square`
+| `lineJoin` | `String` | | The line join style to apply to corners of the path. possible value: `miter`, `round`, `bevel`
| `miterLimit` | `Number` | | The limiting value that helps avoid spikes at junctions between connected line segments. The miter limit helps you avoid spikes in paths that use the `miter` `lineJoin` style. If the ratio of the miter length—that is, the diagonal length of the miter join—to the line thickness exceeds the miter limit, the joint is converted to a bevel join. The default miter limit is 10, which results in the conversion of miters whose angle at the joint is less than 11 degrees.
| `geodesic` | `Boolean` | | Boolean to indicate whether to draw each segment of the line as a geodesic as opposed to straight lines on the Mercator projection. A geodesic is the shortest path between two points on the Earth's surface. The geodesic curve is constructed assuming the Earth is a sphere.
| `lineDashPhase` | `Number` | `0` | (iOS only) The offset (in points) at which to start drawing the dash pattern. Use this property to start drawing a dashed line partway through a segment or gap. For example, a phase value of 6 for the patter 5-2-3-2 would cause drawing to begin in the middle of the first gap.
diff --git a/docs/installation.md b/docs/installation.md
index d3b6c331c1..ad9d4b3833 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -21,13 +21,13 @@ react-native link react-native-maps
### Option 1: CocoaPods - Same as the included AirMapsExplorer example
-1. Setup your `Podfile` like the included [example/ios/Podfile](../example/ios/Podfile), replace all references to `AirMapExplorer` with your project name, and then run `pod install`.
+1. Setup your `Podfile` like the included [example/ios/Podfile](../example/ios/Podfile), replace all references to `AirMapsExplorer` with your project name, and then run `pod install`.
(If you do not need `GoogleMaps` support for iOS, then you can probably completely skip this step.)
1. Open your project in Xcode workspace
1. If you need `GoogleMaps` support also
- - Drag this folder `node_modules/react-native-maps/ios/AirGoogleMaps/` into your project, and choose `Create groups` in the popup window.
+ - Drag this folder `node_modules/react-native-maps/lib/ios/AirGoogleMaps/` into your project, and choose `Create groups` in the popup window.
- In `AppDelegate.m`, add `@import GoogleMaps;` before `@implementation AppDelegate`. In `- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions`, add `[GMSServices provideAPIKey:@"YOUR_GOOGLE_MAP_API_KEY"];`
- - In your project's `Build Settings` > `Header Search Paths`, double click the value field. In the popup, add `$(SRCROOT)/../node_modules/react-native-maps/ios/AirMaps` and change `non-recursive` to `recursive`. (Dragging the folder `node_modules/react-native-maps/ios/AirMaps/` into your project introduces duplicate symbols. We should not do it.)
+ - In your project's `Build Settings` > `Header Search Paths`, double click the value field. In the popup, add `$(SRCROOT)/../node_modules/react-native-maps/lib/ios/AirMaps` and change `non-recursive` to `recursive`. (Dragging the folder `node_modules/react-native-maps/lib/ios/AirMaps/` into your project introduces duplicate symbols. We should not do it.)
Note: We recommend using a version of React Native >= .40. Newer versions (>= .40) require `package.json` to be set to `"react-native-maps": "^0.13.0"`, while older versions require `"react-native-maps": "^0.12.4"`.
@@ -52,7 +52,7 @@ After your `Podfile` is setup properly, run `pod install`.
>This was already done for you if you ran "react-native link"
1. Open your project in Xcode, right click on `Libraries` and click `Add
- Files to "Your Project Name"` Look under `node_modules/react-native-maps/ios` and add `AIRMaps.xcodeproj`.
+ Files to "Your Project Name"` Look under `node_modules/react-native-maps/lib/ios` and add `AIRMaps.xcodeproj`.
1. Add `libAIRMaps.a` to `Build Phases -> Link Binary With Libraries.
1. Click on `AIRMaps.xcodeproj` in `Libraries` and go the `Build
Settings` tab. Double click the text to the right of `Header Search
@@ -98,7 +98,7 @@ After your `Podfile` is setup properly, run `pod install`.
```groovy
...
include ':react-native-maps'
- project(':react-native-maps').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-maps/android')
+ project(':react-native-maps').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-maps/lib/android')
```
1. Specify your Google Maps API Key:
@@ -123,6 +123,8 @@ Source: https://developers.google.com/maps/documentation/android-api/signup
## Troubleshooting
+If you get the error `duplicate symbols for architecture x86_64` when building for iOS, you may need to reconfigure your linking and Podfile as [described in detail in this comment on issue #718](https://github.com/airbnb/react-native-maps/issues/718#issuecomment-295585410)
+
If you have a blank map issue, ([#118](https://github.com/airbnb/react-native-maps/issues/118), [#176](https://github.com/airbnb/react-native-maps/issues/176), [#684](https://github.com/airbnb/react-native-maps/issues/684)), try the following lines :
### On iOS:
@@ -213,7 +215,7 @@ Enter the name of the API key and create it.
1. If you encounter `com.android.dex.DexException: Multiple dex files define Landroid/support/v7/appcompat/R$anim`, then clear build folder.
```
cd android
- gradlew clean
+ ./gradlew clean
cd ..
```
diff --git a/docs/mapview.md b/docs/mapview.md
index 8e73a07738..2aa6a7086f 100644
--- a/docs/mapview.md
+++ b/docs/mapview.md
@@ -9,7 +9,7 @@
| `initialRegion` | `Region` | | The initial region to be displayed by the map. Use this prop instead of `region` only if you don't want to control the viewport of the map besides the initial region.
Changing this prop after the component has mounted will not result in a region change.
This is similar to the `initialValue` prop of a text input.
| `liteMode` | `Boolean` | `false` | Enable lite mode. **Note**: Android only.
| `mapType` | `String` | `"standard"` | The map type to be displayed.
- standard: standard road map (default) - satellite: satellite view - hybrid: satellite view with roads and points of interest overlayed - terrain: (Android only) topographic view
-| `customMapStyle` | `Array` | | Adds custom styling to the map component. See [README](https://github.com/airbnb/react-native-maps#customizing-the-map-style) for more information.
+| `customMapStyle` | `Array` | | Adds custom styling to the map component. See [README](https://github.com/airbnb/react-native-maps#customizing-the-map-style) for more information.
| `showsUserLocation` | `Boolean` | `false` | If `true` the app will ask for the user's location. **NOTE**: You need to add `NSLocationWhenInUseUsageDescription` key in Info.plist to enable geolocation, otherwise it is going to *fail silently*!
| `userLocationAnnotationTitle` | `String` | | The title of the annotation for current user location. This only works if `showsUserLocation` is true. There is a default value `My Location` set by MapView. **Note**: iOS only.
| `followsUserLocation` | `Boolean` | `false` | If `true` the map will focus on the user's location. This only works if `showsUserLocation` is true and the user has shared their location. **Note**: iOS only.
@@ -20,8 +20,10 @@
| `showsBuildings` | `Boolean` | `true` | A Boolean indicating whether the map displays extruded building information.
| `showsTraffic` | `Boolean` | `true` | A Boolean value indicating whether the map displays traffic information.
| `showsIndoors` | `Boolean` | `true` | A Boolean indicating whether indoor maps should be enabled.
-| `showsIndoorLevelPicker` | `Boolean` | `false` | A Boolean indicating whether indoor level picker should be enabled. **Note:** Android only.
+| `showsIndoorLevelPicker` | `Boolean` | `false` | A Boolean indicating whether indoor level picker should be enabled. **Note:** Google Maps only (either Android or iOS with `PROVIDER_GOOGLE`).
| `zoomEnabled` | `Boolean` | `true` | If `false` the user won't be able to pinch/zoom the map.
+| `minZoomLevel` | `Number` | `0` | Minimum zoom value for the map, must be between 0 and 20
+| `maxZoomLevel` | `Number` | `20` | Maximum zoom value for the map, must be between 0 and 20
| `rotateEnabled` | `Boolean` | `true` | If `false` the user won't be able to pinch/rotate the map.
| `scrollEnabled` | `Boolean` | `true` | If `false` the user won't be able to change the map region being displayed.
| `pitchEnabled` | `Boolean` | `true` | If `false` the user won't be able to adjust the camera’s pitch angle.
@@ -36,8 +38,11 @@
## Events
+To access event data, you will need to use `e.nativeEvent`. For example, `onPress={e => console.log(e.nativeEvent)}` will log the entire event object to your console.
+
| Event Name | Returns | Notes
|---|---|---|
+| `onMapReady` | | Callback that is called once the map is fully loaded.
| `onRegionChange` | `Region` | Callback that is called continuously when the region changes, such as when a user is dragging the map.
| `onRegionChangeComplete` | `Region` | Callback that is called once when the region changes, such as when the user is done moving the map.
| `onPress` | `{ coordinate: LatLng, position: Point }` | Callback that is called when user taps on the map.
@@ -59,9 +64,11 @@
|---|---|---|
| `animateToRegion` | `region: Region`, `duration: Number` |
| `animateToCoordinate` | `coordinate: LatLng`, `duration: Number` |
-| `fitToElements` | `animated: Boolean` |
+| `animateToBearing` | `bearing: Number`, `duration: Number` |
+| `animateToViewingAngle` | `angle: Number`, `duration: Number` |
+| `fitToElements` | `animated: Boolean` |
| `fitToSuppliedMarkers` | `markerIDs: String[]`, `animated: Boolean` | If you need to use this in `ComponentDidMount`, make sure you put it in a timeout or it will cause performance problems.
-| `fitToCoordinates` | `coordinates: Array, options: { edgePadding: EdgePadding, animated: Boolean }` | If called in `ComponentDidMount` in android, it will cause an exception. It is recommended to call it from the MapView `onLayout` event.
+| `fitToCoordinates` | `coordinates: Array, options: { edgePadding: EdgePadding, animated: Boolean }` | If called in `ComponentDidMount` in android, it will cause an exception. It is recommended to call it from the MapView `onLayout` event.
diff --git a/docs/marker.md b/docs/marker.md
index 4246befc36..4214e4d6e8 100644
--- a/docs/marker.md
+++ b/docs/marker.md
@@ -15,11 +15,13 @@
| `calloutAnchor` | `Point` | | Specifies the point in the marker image at which to anchor the callout when it is displayed. This is specified in the same coordinate system as the anchor. See the `anchor` prop for more details.
The default is the top middle of the image.
For ios, see the `calloutOffset` prop.
| `flat` | `Boolean` | | Sets whether this marker should be flat against the map true or a billboard facing the camera false.
| `identifier` | `String` | | An identifier used to reference this marker at a later date.
-| `rotation` | `Float` | | A float number indicating marker's rotation angle.
+| `rotation` | `Float` | | A float number indicating marker's rotation angle, in degrees.
| `draggable` | `` | | This is a non-value based prop. Adding this allows the marker to be draggable (re-positioned).
## Events
+To access event data, you will need to use `e.nativeEvent`. For example, `onPress={e => console.log(e.nativeEvent)}` will log the entire event object to your console.
+
| Event Name | Returns | Notes
|---|---|---|
| `onPress` | `{ coordinate: LatLng, position: Point }` | Callback that is called when the user presses on the marker
diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle
index c5bdebe4cb..7a8e8b7dd7 100644
--- a/example/android/app/build.gradle
+++ b/example/android/app/build.gradle
@@ -85,7 +85,7 @@ def enableProguardInReleaseBuilds = false
android {
compileSdkVersion 25
- buildToolsVersion "25.0.2"
+ buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.airbnb.android.react.maps.example"
@@ -127,8 +127,8 @@ android {
}
dependencies {
- compile 'com.facebook.react:react-native:0.42.+'
- compile 'com.android.support:appcompat-v7:25.1.1'
- compile 'com.android.support:support-annotations:25.1.1'
+ compile 'com.facebook.react:react-native:0.45.+'
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ compile 'com.android.support:support-annotations:25.3.0'
compile project(':react-native-maps-lib')
}
diff --git a/example/android/app/src/main/java/com/airbnb/android/react/maps/example/MainActivity.java b/example/android/app/src/main/java/com/airbnb/android/react/maps/example/MainActivity.java
index eec2349a48..12149c20e1 100644
--- a/example/android/app/src/main/java/com/airbnb/android/react/maps/example/MainActivity.java
+++ b/example/android/app/src/main/java/com/airbnb/android/react/maps/example/MainActivity.java
@@ -4,14 +4,12 @@
public class MainActivity extends ReactActivity {
-
- /**
- * Returns the name of the main component registered from JavaScript.
- * This is used to schedule rendering of the component.
- */
- @Override
- protected String getMainComponentName() {
- return "AirMapsExplorer";
- }
-
+ /**
+ * Returns the name of the main component registered from JavaScript.
+ * This is used to schedule rendering of the component.
+ */
+ @Override
+ protected String getMainComponentName() {
+ return "AirMapsExplorer";
+ }
}
diff --git a/example/examples/DisplayLatLng.js b/example/examples/DisplayLatLng.js
index 573617d419..688427b35b 100644
--- a/example/examples/DisplayLatLng.js
+++ b/example/examples/DisplayLatLng.js
@@ -43,15 +43,37 @@ class DisplayLatLng extends React.Component {
this.map.animateToRegion(this.randomRegion());
}
- randomRegion() {
- const { region } = this.state;
+ animateRandomCoordinate() {
+ this.map.animateToCoordinate(this.randomCoordinate());
+ }
+
+ animateToRandomBearing() {
+ this.map.animateToBearing(this.getRandomFloat(-360, 360));
+ }
+
+ animateToRandomViewingAngle() {
+ this.map.animateToViewingAngle(this.getRandomFloat(0, 90));
+ }
+
+ getRandomFloat(min, max) {
+ return (Math.random() * (max - min)) + min;
+ }
+
+ randomCoordinate() {
+ const region = this.state.region;
return {
- ...this.state.region,
latitude: region.latitude + ((Math.random() - 0.5) * (region.latitudeDelta / 2)),
longitude: region.longitude + ((Math.random() - 0.5) * (region.longitudeDelta / 2)),
};
}
+ randomRegion() {
+ return {
+ ...this.state.region,
+ ...this.randomCoordinate(),
+ };
+ }
+
render() {
return (
@@ -74,13 +96,31 @@ class DisplayLatLng extends React.Component {
onPress={() => this.jumpRandom()}
style={[styles.bubble, styles.button]}
>
- Jump
+ Jump this.animateRandom()}
style={[styles.bubble, styles.button]}
>
- Animate
+ Animate (Region)
+
+ this.animateRandomCoordinate()}
+ style={[styles.bubble, styles.button]}
+ >
+ Animate (Coordinate)
+
+ this.animateToRandomBearing()}
+ style={[styles.bubble, styles.button]}
+ >
+ Animate (Bearing)
+
+ this.animateToRandomViewingAngle()}
+ style={[styles.bubble, styles.button]}
+ >
+ Animate (View Angle)
@@ -112,16 +152,20 @@ const styles = StyleSheet.create({
alignItems: 'stretch',
},
button: {
- width: 80,
- paddingHorizontal: 12,
+ width: 100,
+ paddingHorizontal: 8,
alignItems: 'center',
- marginHorizontal: 10,
+ justifyContent: 'center',
+ marginHorizontal: 5,
},
buttonContainer: {
flexDirection: 'row',
marginVertical: 20,
backgroundColor: 'transparent',
},
+ buttonText: {
+ textAlign: 'center',
+ },
});
module.exports = DisplayLatLng;
diff --git a/example/examples/EventListener.js b/example/examples/EventListener.js
index 005894a0d5..19a3930637 100644
--- a/example/examples/EventListener.js
+++ b/example/examples/EventListener.js
@@ -7,7 +7,7 @@ import {
ScrollView,
} from 'react-native';
// eslint-disable-next-line max-len
-import SyntheticEvent from 'react-native/Libraries/Renderer/src/renderers/shared/stack/event/SyntheticEvent';
+import SyntheticEvent from 'react-native/Libraries/Renderer/src/renderers/shared/shared/event/SyntheticEvent';
import MapView from 'react-native-maps';
import PriceMarker from './PriceMarker';
@@ -145,6 +145,7 @@ class EventListener extends React.Component {
'../../node_modules/react-native/ReactCommon/yoga/Yoga.podspec'
- pod 'React', path: '../../node_modules/react-native', :subspecs => [
+ pod 'Yoga', path: "#{rn_path}/ReactCommon/yoga/Yoga.podspec"
+ pod 'React', path: rn_path, subspecs: [
'Core',
'RCTActionSheet',
'RCTAnimation',
@@ -18,22 +19,25 @@ target 'AirMapsExplorer' do
'RCTSettings',
'RCTText',
'RCTVibration',
- 'RCTWebSocket'
+ 'RCTWebSocket',
+ 'BatchedBridge'
]
- pod 'GoogleMaps' # <~~ remove this line if you do not want to support GoogleMaps on iOS
+ pod 'GoogleMaps' # Remove this line if you don't want to support GoogleMaps on iOS
pod 'react-native-maps', path: '../../'
- pod 'react-native-google-maps', path: '../../' # <~~ if you need GoogleMaps support on iOS
-
+ pod 'react-native-google-maps', path: '../../' # If you need GoogleMaps support on iOS
end
-
post_install do |installer|
installer.pods_project.targets.each do |target|
- if target.name == "react-native-google-maps"
+ if target.name == 'react-native-google-maps'
target.build_configurations.each do |config|
config.build_settings['CLANG_ENABLE_MODULES'] = 'No'
end
end
+
+ if target.name == "React"
+ target.remove_from_project
+ end
end
end
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index ff6774aa29..3eec18feec 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -4,46 +4,49 @@ PODS:
- GoogleMaps/Base (2.1.1)
- GoogleMaps/Maps (2.1.1):
- GoogleMaps/Base
- - React (0.42.3):
- - React/Core (= 0.42.3)
- - react-native-google-maps (0.13.1):
+ - React (0.45.1):
+ - React/Core (= 0.45.1)
+ - react-native-google-maps (0.15.2):
- GoogleMaps (= 2.1.1)
- React
- - react-native-maps (0.13.1):
+ - react-native-maps (0.15.2):
- React
- - React/Core (0.42.3):
- - React/cxxreact
- - Yoga (= 0.42.3.React)
- - React/cxxreact (0.42.3):
- - React/jschelpers
- - React/jschelpers (0.42.3)
- - React/RCTActionSheet (0.42.3):
+ - React/BatchedBridge (0.45.1):
- React/Core
- - React/RCTAnimation (0.42.3):
+ - React/cxxreact_legacy
+ - React/Core (0.45.1):
+ - Yoga (= 0.45.1.React)
+ - React/cxxreact_legacy (0.45.1):
+ - React/jschelpers_legacy
+ - React/jschelpers_legacy (0.45.1)
+ - React/RCTActionSheet (0.45.1):
- React/Core
- - React/RCTGeolocation (0.42.3):
+ - React/RCTAnimation (0.45.1):
- React/Core
- - React/RCTImage (0.42.3):
+ - React/RCTGeolocation (0.45.1):
+ - React/Core
+ - React/RCTImage (0.45.1):
- React/Core
- React/RCTNetwork
- - React/RCTLinkingIOS (0.42.3):
+ - React/RCTLinkingIOS (0.45.1):
- React/Core
- - React/RCTNetwork (0.42.3):
+ - React/RCTNetwork (0.45.1):
- React/Core
- - React/RCTSettings (0.42.3):
+ - React/RCTSettings (0.45.1):
- React/Core
- - React/RCTText (0.42.3):
+ - React/RCTText (0.45.1):
- React/Core
- - React/RCTVibration (0.42.3):
+ - React/RCTVibration (0.45.1):
- React/Core
- - React/RCTWebSocket (0.42.3):
+ - React/RCTWebSocket (0.45.1):
- React/Core
- - Yoga (0.42.3.React)
+ - Yoga (0.45.1.React)
DEPENDENCIES:
- GoogleMaps
- react-native-google-maps (from `../../`)
- react-native-maps (from `../../`)
+ - React/BatchedBridge (from `../../node_modules/react-native`)
- React/Core (from `../../node_modules/react-native`)
- React/RCTActionSheet (from `../../node_modules/react-native`)
- React/RCTAnimation (from `../../node_modules/react-native`)
@@ -69,11 +72,11 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
GoogleMaps: a5b5bbe47734e2443bde781a6aa64e69fdb6d785
- React: 35e039680feacd0563677d49ba410112d2748559
- react-native-google-maps: b2668747ec289759993dc2411a7078afafa8adea
- react-native-maps: 326ddbaaea8f6044b1817fb028c40950c71cc38a
- Yoga: 86ce777665c8259b94ef8dbea76b84634237f4ea
+ React: 0c9191a8b0c843d7004f950ac6b5f6cba9d125c7
+ react-native-google-maps: d0b8772eb76e1615ea32c73bc9d573360b8c0817
+ react-native-maps: fe2e4680b4d3fcfd84d636ccedd470fe358d55e1
+ Yoga: 89c8738d42a0b46a113acb4e574336d61cba2985
-PODFILE CHECKSUM: 222d08e48f834b6a3de650b72786105af7a9d331
+PODFILE CHECKSUM: 8b3eb68ef6553bf1fcb8a467e0e63000b37ec692
COCOAPODS: 1.2.0
diff --git a/examples/ios/bundler b/examples/ios/bundler
new file mode 100755
index 0000000000..905387619f
--- /dev/null
+++ b/examples/ios/bundler
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+#
+# This file was generated by Bundler.
+#
+# The application 'bundler' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require "pathname"
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile",
+ Pathname.new(__FILE__).realpath)
+
+require "rubygems"
+require "bundler/setup"
+
+load Gem.bin_path("bundler", "bundler")
diff --git a/examples/ios/fuzzy_match b/examples/ios/fuzzy_match
new file mode 100755
index 0000000000..f715473935
--- /dev/null
+++ b/examples/ios/fuzzy_match
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+#
+# This file was generated by Bundler.
+#
+# The application 'fuzzy_match' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require "pathname"
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile",
+ Pathname.new(__FILE__).realpath)
+
+require "rubygems"
+require "bundler/setup"
+
+load Gem.bin_path("fuzzy_match", "fuzzy_match")
diff --git a/examples/ios/pod b/examples/ios/pod
new file mode 100755
index 0000000000..3c4a4d04c1
--- /dev/null
+++ b/examples/ios/pod
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+#
+# This file was generated by Bundler.
+#
+# The application 'pod' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require "pathname"
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile",
+ Pathname.new(__FILE__).realpath)
+
+require "rubygems"
+require "bundler/setup"
+
+load Gem.bin_path("cocoapods", "pod")
diff --git a/examples/ios/sandbox-pod b/examples/ios/sandbox-pod
new file mode 100755
index 0000000000..c76cfd0a59
--- /dev/null
+++ b/examples/ios/sandbox-pod
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+#
+# This file was generated by Bundler.
+#
+# The application 'sandbox-pod' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require "pathname"
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile",
+ Pathname.new(__FILE__).realpath)
+
+require "rubygems"
+require "bundler/setup"
+
+load Gem.bin_path("cocoapods", "sandbox-pod")
diff --git a/examples/ios/xcodeproj b/examples/ios/xcodeproj
new file mode 100755
index 0000000000..3c3452c175
--- /dev/null
+++ b/examples/ios/xcodeproj
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+#
+# This file was generated by Bundler.
+#
+# The application 'xcodeproj' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require "pathname"
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile",
+ Pathname.new(__FILE__).realpath)
+
+require "rubygems"
+require "bundler/setup"
+
+load Gem.bin_path("xcodeproj", "xcodeproj")
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 3b6c26cf77..e5fa88cc4c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Mar 02 17:03:11 PST 2017
+#Mon May 08 11:03:28 PDT 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip
diff --git a/index.d.ts b/index.d.ts
new file mode 100644
index 0000000000..d885759b25
--- /dev/null
+++ b/index.d.ts
@@ -0,0 +1,159 @@
+import * as React from 'react';
+
+declare module "react-native-maps" {
+
+ export type ProviderType = 'google';
+ export type MapType = 'standard' | 'satellite' | 'hybrid' | 'terrain' | 'none';
+ export type LineCapType = 'butt' | 'round' | 'square';
+ export type LineJoinType = 'miter' | 'round' | 'bevel';
+
+ export interface MapViewProperties {
+ provider?: ProviderType;
+ style?: any;
+ customMapStyle?: any[];
+ customMapStyleString?: string;
+ showsUserLocation?: boolean;
+ userLocationAnnotationTitle?: string;
+ showsMyLocationButton?: boolean;
+ followsUserLocation?: boolean;
+ showsPointsOfInterest?: boolean;
+ showsCompass?: boolean;
+ zoomEnabled?: boolean;
+ rotateEnabled?: boolean;
+ cacheEnabled?: boolean;
+ loadingEnabled?: boolean;
+ loadingBackgroundColor?: any;
+ loadingIndicatorColor?: any;
+ scrollEnabled?: boolean;
+ pitchEnabled?: boolean;
+ toolbarEnabled?: boolean;
+ moveOnMarkerPress?: boolean;
+ showsScale?: boolean;
+ showsBuildings?: boolean;
+ showsTraffic?: boolean;
+ showsIndoors?: boolean;
+ showsIndoorLevelPicker?: boolean;
+ mapType?: MapType;
+ region?: { latitude: number; longitude: number; latitudeDelta: number; longitudeDelta: number; };
+ initialRegion?: { latitude: number; longitude: number; latitudeDelta: number; longitudeDelta: number; };
+ liteMode?: boolean;
+ maxDelta?: number;
+ minDelta?: number;
+ legalLabelInsets?: any;
+ onChange?: Function;
+ onMapReady?: Function;
+ onRegionChange?: Function;
+ onRegionChangeComplete?: Function;
+ onPress?: Function;
+ onLayout?: Function;
+ onLongPress?: Function;
+ onPanDrag?: Function;
+ onMarkerPress?: Function;
+ onMarkerSelect?: Function;
+ onMarkerDeselect?: Function;
+ onCalloutPress?: Function;
+ onMarkerDragStart?: Function;
+ onMarkerDrag?: Function;
+ onMarkerDragEnd?: Function;
+ minZoomLevel?: number;
+ maxZoomLevel?: number;
+ }
+
+ export interface MarkerProperties {
+ identifier?: string;
+ reuseIdentifier?: string;
+ title?: string;
+ description?: string;
+ image?: any;
+ opacity?: number;
+ pinColor?: string;
+ coordinate: { latitude: number; longitude: number };
+ centerOffset?: { x: number; y: number };
+ calloutOffset?: { x: number; y: number };
+ anchor?: { x: number; y: number };
+ calloutAnchor?: { x: number; y: number };
+ flat?: boolean;
+ draggable?: boolean;
+ onPress?: Function;
+ onSelect?: Function;
+ onDeselect?: Function;
+ onCalloutPress?: Function;
+ onDragStart?: Function;
+ onDrag?: Function;
+ onDragEnd?: Function;
+ }
+
+ export interface MapPolylineProperties {
+ coordinates?: { latitude: number; longitude: number; }[];
+ onPress?: Function;
+ tappable?: boolean;
+ fillColor?: string;
+ strokeWidth?: number;
+ strokeColor?: string;
+ zIndex?: number;
+ lineCap?: LineCapType;
+ lineJoin?: LineJoinType;
+ miterLimit?: number;
+ geodesic?: boolean;
+ lineDashPhase?: number;
+ lineDashPattern?: number[];
+ }
+
+ export interface MapPolygonProperties {
+ coordinates?: { latitude: number; longitude: number; }[];
+ holes?: { latitude: number; longitude: number; }[][];
+ onPress?: Function;
+ tappable?: boolean;
+ strokeWidth?: number;
+ strokeColor?: string;
+ fillColor?: string;
+ zIndex?: number;
+ lineCap?: LineCapType;
+ lineJoin?: LineJoinType;
+ miterLimit?: number;
+ geodesic?: boolean;
+ lineDashPhase?: number;
+ lineDashPattern?: number[];
+ }
+
+ export interface MapCircleProperties {
+ center: { latitude: number; longitude: number };
+ radius: number;
+ onPress?: Function;
+ strokeWidth?: number;
+ strokeColor?: string;
+ fillColor?: string;
+ zIndex?: number;
+ lineCap?: LineCapType;
+ lineJoin?: LineJoinType;
+ miterLimit?: number;
+ lineDashPhase?: number;
+ lineDashPattern?: number[];
+ }
+
+ export interface MapUrlTitleProperties {
+ urlTemplate: string;
+ zIndex?: number;
+ }
+
+ export interface MapCalloutProperties {
+ tooltip?: boolean;
+ onPress?: Function;
+ }
+
+ class MapView extends React.Component {
+ static Animated: any;
+ static AnimatedRegion: any;
+ }
+
+ namespace MapView {
+ class Marker extends React.Component {}
+ class Polyline extends React.Component {}
+ class Polygon extends React.Component {}
+ class Circle extends React.Component {}
+ class UrlTile extends React.Component {}
+ class Callout extends React.Component {}
+ }
+
+ export default MapView;
+}
diff --git a/lib/android/build.gradle b/lib/android/build.gradle
index 85cb5b4d24..326ab4bfa5 100644
--- a/lib/android/build.gradle
+++ b/lib/android/build.gradle
@@ -3,7 +3,7 @@ apply from: 'gradle-maven-push.gradle'
android {
compileSdkVersion 25
- buildToolsVersion "25.0.2"
+ buildToolsVersion "25.0.3"
defaultConfig {
minSdkVersion 16
@@ -35,6 +35,6 @@ android {
dependencies {
provided "com.facebook.react:react-native:+"
- compile "com.google.android.gms:play-services-base:10.2.0"
- compile "com.google.android.gms:play-services-maps:10.2.0"
+ compile "com.google.android.gms:play-services-base:10.2.4"
+ compile "com.google.android.gms:play-services-maps:10.2.4"
}
diff --git a/lib/android/gradle.properties b/lib/android/gradle.properties
index 9a87aa52e5..7047e93ba3 100644
--- a/lib/android/gradle.properties
+++ b/lib/android/gradle.properties
@@ -1,5 +1,5 @@
VERSION_CODE=4
-VERSION_NAME=0.14.0
+VERSION_NAME=0.17.0
GROUP=com.airbnb.android
POM_DESCRIPTION=React Native Map view component for Android
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCallout.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCallout.java
index c434c9c123..6f96e15da7 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCallout.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCallout.java
@@ -5,19 +5,19 @@
import com.facebook.react.views.view.ReactViewGroup;
public class AirMapCallout extends ReactViewGroup {
- private boolean tooltip = false;
- public int width;
- public int height;
+ private boolean tooltip = false;
+ public int width;
+ public int height;
- public AirMapCallout(Context context) {
- super(context);
- }
+ public AirMapCallout(Context context) {
+ super(context);
+ }
- public void setTooltip(boolean tooltip) {
- this.tooltip = tooltip;
- }
+ public void setTooltip(boolean tooltip) {
+ this.tooltip = tooltip;
+ }
- public boolean getTooltip() {
- return this.tooltip;
- }
+ public boolean getTooltip() {
+ return this.tooltip;
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCalloutManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCalloutManager.java
index 359e6847b5..b63ea29ffa 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCalloutManager.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCalloutManager.java
@@ -12,45 +12,45 @@
public class AirMapCalloutManager extends ViewGroupManager {
- @Override
- public String getName() {
- return "AIRMapCallout";
- }
-
- @Override
- public AirMapCallout createViewInstance(ThemedReactContext context) {
- return new AirMapCallout(context);
- }
-
- @ReactProp(name = "tooltip", defaultBoolean = false)
- public void setTooltip(AirMapCallout view, boolean tooltip) {
- view.setTooltip(tooltip);
- }
-
- @Override
- @Nullable
- public Map getExportedCustomDirectEventTypeConstants() {
- return MapBuilder.of("onPress", MapBuilder.of("registrationName", "onPress"));
- }
-
- @Override
- public LayoutShadowNode createShadowNodeInstance() {
- // we use a custom shadow node that emits the width/height of the view
- // after layout with the updateExtraData method. Without this, we can't generate
- // a bitmap of the appropriate width/height of the rendered view.
- return new SizeReportingShadowNode();
- }
-
- @Override
- public void updateExtraData(AirMapCallout view, Object extraData) {
- // This method is called from the shadow node with the width/height of the rendered
- // marker view.
- //noinspection unchecked
- Map data = (Map) extraData;
- float width = data.get("width");
- float height = data.get("height");
- view.width = (int) width;
- view.height = (int) height;
- }
+ @Override
+ public String getName() {
+ return "AIRMapCallout";
+ }
+
+ @Override
+ public AirMapCallout createViewInstance(ThemedReactContext context) {
+ return new AirMapCallout(context);
+ }
+
+ @ReactProp(name = "tooltip", defaultBoolean = false)
+ public void setTooltip(AirMapCallout view, boolean tooltip) {
+ view.setTooltip(tooltip);
+ }
+
+ @Override
+ @Nullable
+ public Map getExportedCustomDirectEventTypeConstants() {
+ return MapBuilder.of("onPress", MapBuilder.of("registrationName", "onPress"));
+ }
+
+ @Override
+ public LayoutShadowNode createShadowNodeInstance() {
+ // we use a custom shadow node that emits the width/height of the view
+ // after layout with the updateExtraData method. Without this, we can't generate
+ // a bitmap of the appropriate width/height of the rendered view.
+ return new SizeReportingShadowNode();
+ }
+
+ @Override
+ public void updateExtraData(AirMapCallout view, Object extraData) {
+ // This method is called from the shadow node with the width/height of the rendered
+ // marker view.
+ //noinspection unchecked
+ Map data = (Map) extraData;
+ float width = data.get("width");
+ float height = data.get("height");
+ view.width = (int) width;
+ view.height = (int) height;
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCircle.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCircle.java
index e428b04f69..a70d9146ad 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCircle.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCircle.java
@@ -9,92 +9,92 @@
public class AirMapCircle extends AirMapFeature {
- private CircleOptions circleOptions;
- private Circle circle;
-
- private LatLng center;
- private double radius;
- private int strokeColor;
- private int fillColor;
- private float strokeWidth;
- private float zIndex;
-
- public AirMapCircle(Context context) {
- super(context);
+ private CircleOptions circleOptions;
+ private Circle circle;
+
+ private LatLng center;
+ private double radius;
+ private int strokeColor;
+ private int fillColor;
+ private float strokeWidth;
+ private float zIndex;
+
+ public AirMapCircle(Context context) {
+ super(context);
+ }
+
+ public void setCenter(LatLng center) {
+ this.center = center;
+ if (circle != null) {
+ circle.setCenter(this.center);
}
+ }
- public void setCenter(LatLng center) {
- this.center = center;
- if (circle != null) {
- circle.setCenter(this.center);
- }
+ public void setRadius(double radius) {
+ this.radius = radius;
+ if (circle != null) {
+ circle.setRadius(this.radius);
}
+ }
- public void setRadius(double radius) {
- this.radius = radius;
- if (circle != null) {
- circle.setRadius(this.radius);
- }
+ public void setFillColor(int color) {
+ this.fillColor = color;
+ if (circle != null) {
+ circle.setFillColor(color);
}
+ }
- public void setFillColor(int color) {
- this.fillColor = color;
- if (circle != null) {
- circle.setFillColor(color);
- }
+ public void setStrokeColor(int color) {
+ this.strokeColor = color;
+ if (circle != null) {
+ circle.setStrokeColor(color);
}
+ }
- public void setStrokeColor(int color) {
- this.strokeColor = color;
- if (circle != null) {
- circle.setStrokeColor(color);
- }
+ public void setStrokeWidth(float width) {
+ this.strokeWidth = width;
+ if (circle != null) {
+ circle.setStrokeWidth(width);
}
+ }
- public void setStrokeWidth(float width) {
- this.strokeWidth = width;
- if (circle != null) {
- circle.setStrokeWidth(width);
- }
+ public void setZIndex(float zIndex) {
+ this.zIndex = zIndex;
+ if (circle != null) {
+ circle.setZIndex(zIndex);
}
+ }
- public void setZIndex(float zIndex) {
- this.zIndex = zIndex;
- if (circle != null) {
- circle.setZIndex(zIndex);
- }
- }
-
- public CircleOptions getCircleOptions() {
- if (circleOptions == null) {
- circleOptions = createCircleOptions();
- }
- return circleOptions;
- }
-
- private CircleOptions createCircleOptions() {
- CircleOptions options = new CircleOptions();
- options.center(center);
- options.radius(radius);
- options.fillColor(fillColor);
- options.strokeColor(strokeColor);
- options.strokeWidth(strokeWidth);
- options.zIndex(zIndex);
- return options;
- }
-
- @Override
- public Object getFeature() {
- return circle;
- }
-
- @Override
- public void addToMap(GoogleMap map) {
- circle = map.addCircle(getCircleOptions());
- }
-
- @Override
- public void removeFromMap(GoogleMap map) {
- circle.remove();
+ public CircleOptions getCircleOptions() {
+ if (circleOptions == null) {
+ circleOptions = createCircleOptions();
}
+ return circleOptions;
+ }
+
+ private CircleOptions createCircleOptions() {
+ CircleOptions options = new CircleOptions();
+ options.center(center);
+ options.radius(radius);
+ options.fillColor(fillColor);
+ options.strokeColor(strokeColor);
+ options.strokeWidth(strokeWidth);
+ options.zIndex(zIndex);
+ return options;
+ }
+
+ @Override
+ public Object getFeature() {
+ return circle;
+ }
+
+ @Override
+ public void addToMap(GoogleMap map) {
+ circle = map.addCircle(getCircleOptions());
+ }
+
+ @Override
+ public void removeFromMap(GoogleMap map) {
+ circle.remove();
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCircleManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCircleManager.java
index c0eaf8f149..c8eabf2d12 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCircleManager.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapCircleManager.java
@@ -14,59 +14,59 @@
import com.google.android.gms.maps.model.LatLng;
public class AirMapCircleManager extends ViewGroupManager {
- private final DisplayMetrics metrics;
+ private final DisplayMetrics metrics;
- public AirMapCircleManager(ReactApplicationContext reactContext) {
- super();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- metrics = new DisplayMetrics();
- ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE))
- .getDefaultDisplay()
- .getRealMetrics(metrics);
- } else {
- metrics = reactContext.getResources().getDisplayMetrics();
- }
+ public AirMapCircleManager(ReactApplicationContext reactContext) {
+ super();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ metrics = new DisplayMetrics();
+ ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay()
+ .getRealMetrics(metrics);
+ } else {
+ metrics = reactContext.getResources().getDisplayMetrics();
}
+ }
- @Override
- public String getName() {
- return "AIRMapCircle";
- }
+ @Override
+ public String getName() {
+ return "AIRMapCircle";
+ }
- @Override
- public AirMapCircle createViewInstance(ThemedReactContext context) {
- return new AirMapCircle(context);
- }
+ @Override
+ public AirMapCircle createViewInstance(ThemedReactContext context) {
+ return new AirMapCircle(context);
+ }
- @ReactProp(name = "center")
- public void setCenter(AirMapCircle view, ReadableMap center) {
- view.setCenter(new LatLng(center.getDouble("latitude"), center.getDouble("longitude")));
- }
+ @ReactProp(name = "center")
+ public void setCenter(AirMapCircle view, ReadableMap center) {
+ view.setCenter(new LatLng(center.getDouble("latitude"), center.getDouble("longitude")));
+ }
- @ReactProp(name = "radius", defaultDouble = 0)
- public void setRadius(AirMapCircle view, double radius) {
- view.setRadius(radius);
- }
+ @ReactProp(name = "radius", defaultDouble = 0)
+ public void setRadius(AirMapCircle view, double radius) {
+ view.setRadius(radius);
+ }
- @ReactProp(name = "strokeWidth", defaultFloat = 1f)
- public void setStrokeWidth(AirMapCircle view, float widthInPoints) {
- float widthInScreenPx = metrics.density * widthInPoints; // done for parity with iOS
- view.setStrokeWidth(widthInScreenPx);
- }
+ @ReactProp(name = "strokeWidth", defaultFloat = 1f)
+ public void setStrokeWidth(AirMapCircle view, float widthInPoints) {
+ float widthInScreenPx = metrics.density * widthInPoints; // done for parity with iOS
+ view.setStrokeWidth(widthInScreenPx);
+ }
- @ReactProp(name = "fillColor", defaultInt = Color.RED, customType = "Color")
- public void setFillColor(AirMapCircle view, int color) {
- view.setFillColor(color);
- }
+ @ReactProp(name = "fillColor", defaultInt = Color.RED, customType = "Color")
+ public void setFillColor(AirMapCircle view, int color) {
+ view.setFillColor(color);
+ }
- @ReactProp(name = "strokeColor", defaultInt = Color.RED, customType = "Color")
- public void setStrokeColor(AirMapCircle view, int color) {
- view.setStrokeColor(color);
- }
+ @ReactProp(name = "strokeColor", defaultInt = Color.RED, customType = "Color")
+ public void setStrokeColor(AirMapCircle view, int color) {
+ view.setStrokeColor(color);
+ }
- @ReactProp(name = "zIndex", defaultFloat = 1.0f)
- public void setZIndex(AirMapCircle view, float zIndex) {
- view.setZIndex(zIndex);
- }
+ @ReactProp(name = "zIndex", defaultFloat = 1.0f)
+ public void setZIndex(AirMapCircle view, float zIndex) {
+ view.setZIndex(zIndex);
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapFeature.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapFeature.java
index 1c15ade5fa..70484c1a46 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapFeature.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapFeature.java
@@ -6,13 +6,13 @@
import com.google.android.gms.maps.GoogleMap;
public abstract class AirMapFeature extends ReactViewGroup {
- public AirMapFeature(Context context) {
- super(context);
- }
+ public AirMapFeature(Context context) {
+ super(context);
+ }
- public abstract void addToMap(GoogleMap map);
+ public abstract void addToMap(GoogleMap map);
- public abstract void removeFromMap(GoogleMap map);
+ public abstract void removeFromMap(GoogleMap map);
- public abstract Object getFeature();
+ public abstract Object getFeature();
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapLiteManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapLiteManager.java
index 62495c5e42..619e36435a 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapLiteManager.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapLiteManager.java
@@ -5,16 +5,16 @@
public class AirMapLiteManager extends AirMapManager {
- private static final String REACT_CLASS = "AIRMapLite";
+ private static final String REACT_CLASS = "AIRMapLite";
- @Override
- public String getName() {
- return REACT_CLASS;
- }
+ @Override
+ public String getName() {
+ return REACT_CLASS;
+ }
- public AirMapLiteManager(ReactApplicationContext context) {
- super(context);
- this.googleMapOptions = new GoogleMapOptions().liteMode(true);
- }
+ public AirMapLiteManager(ReactApplicationContext context) {
+ super(context);
+ this.googleMapOptions = new GoogleMapOptions().liteMode(true);
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapManager.java
index 3ac1f4de79..6749e9a859 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapManager.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapManager.java
@@ -4,7 +4,6 @@
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
-import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
@@ -17,7 +16,6 @@
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMapOptions;
-import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.MapStyleOptions;
@@ -28,280 +26,313 @@
public class AirMapManager extends ViewGroupManager {
- private static final String REACT_CLASS = "AIRMap";
- private static final int ANIMATE_TO_REGION = 1;
- private static final int ANIMATE_TO_COORDINATE = 2;
- private static final int FIT_TO_ELEMENTS = 3;
- private static final int FIT_TO_SUPPLIED_MARKERS = 4;
- private static final int FIT_TO_COORDINATES = 5;
-
- private final Map MAP_TYPES = MapBuilder.of(
- "standard", GoogleMap.MAP_TYPE_NORMAL,
- "satellite", GoogleMap.MAP_TYPE_SATELLITE,
- "hybrid", GoogleMap.MAP_TYPE_HYBRID,
- "terrain", GoogleMap.MAP_TYPE_TERRAIN,
- "none", GoogleMap.MAP_TYPE_NONE
- );
-
- private final ReactApplicationContext appContext;
-
- protected GoogleMapOptions googleMapOptions;
-
- public AirMapManager(ReactApplicationContext context) {
- this.appContext = context;
- this.googleMapOptions = new GoogleMapOptions();
- }
-
- @Override
- public String getName() {
- return REACT_CLASS;
- }
-
- @Override
- protected AirMapView createViewInstance(ThemedReactContext context) {
- return new AirMapView(context, this, googleMapOptions);
- }
-
- private void emitMapError(ThemedReactContext context, String message, String type) {
- WritableMap error = Arguments.createMap();
- error.putString("message", message);
- error.putString("type", type);
-
- context
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
- .emit("onError", error);
- }
-
- @ReactProp(name = "region")
- public void setRegion(AirMapView view, ReadableMap region) {
- view.setRegion(region);
- }
-
- @ReactProp(name = "mapType")
- public void setMapType(AirMapView view, @Nullable String mapType) {
- int typeId = MAP_TYPES.get(mapType);
- view.map.setMapType(typeId);
- }
-
- @ReactProp(name = "customMapStyleString")
- public void setMapStyle(AirMapView view, @Nullable String customMapStyleString) {
- view.map.setMapStyle(new MapStyleOptions(customMapStyleString));
- }
-
- @ReactProp(name = "showsUserLocation", defaultBoolean = false)
- public void setShowsUserLocation(AirMapView view, boolean showUserLocation) {
- view.setShowsUserLocation(showUserLocation);
- }
-
- @ReactProp(name = "showsMyLocationButton", defaultBoolean = true)
- public void setShowsMyLocationButton(AirMapView view, boolean showMyLocationButton) {
- view.setShowsMyLocationButton(showMyLocationButton);
- }
-
- @ReactProp(name = "toolbarEnabled", defaultBoolean = true)
- public void setToolbarEnabled(AirMapView view, boolean toolbarEnabled) {
- view.setToolbarEnabled(toolbarEnabled);
- }
-
- // This is a private prop to improve performance of panDrag by disabling it when the callback is not set
- @ReactProp(name = "handlePanDrag", defaultBoolean = false)
- public void setHandlePanDrag(AirMapView view, boolean handlePanDrag) {
- view.setHandlePanDrag(handlePanDrag);
- }
-
- @ReactProp(name = "showsTraffic", defaultBoolean = false)
- public void setShowTraffic(AirMapView view, boolean showTraffic) {
- view.map.setTrafficEnabled(showTraffic);
- }
-
- @ReactProp(name = "showsBuildings", defaultBoolean = false)
- public void setShowBuildings(AirMapView view, boolean showBuildings) {
- view.map.setBuildingsEnabled(showBuildings);
- }
-
- @ReactProp(name = "showsIndoors", defaultBoolean = false)
- public void setShowIndoors(AirMapView view, boolean showIndoors) {
- view.map.setIndoorEnabled(showIndoors);
- }
-
- @ReactProp(name = "showsIndoorLevelPicker", defaultBoolean = false)
- public void setShowsIndoorLevelPicker(AirMapView view, boolean showsIndoorLevelPicker) {
- view.map.getUiSettings().setIndoorLevelPickerEnabled(showsIndoorLevelPicker);
- }
-
- @ReactProp(name = "showsCompass", defaultBoolean = false)
- public void setShowsCompass(AirMapView view, boolean showsCompass) {
- view.map.getUiSettings().setCompassEnabled(showsCompass);
- }
-
- @ReactProp(name = "scrollEnabled", defaultBoolean = false)
- public void setScrollEnabled(AirMapView view, boolean scrollEnabled) {
- view.map.getUiSettings().setScrollGesturesEnabled(scrollEnabled);
- }
-
- @ReactProp(name = "zoomEnabled", defaultBoolean = false)
- public void setZoomEnabled(AirMapView view, boolean zoomEnabled) {
- view.map.getUiSettings().setZoomGesturesEnabled(zoomEnabled);
- }
-
- @ReactProp(name = "rotateEnabled", defaultBoolean = false)
- public void setRotateEnabled(AirMapView view, boolean rotateEnabled) {
- view.map.getUiSettings().setRotateGesturesEnabled(rotateEnabled);
- }
-
- @ReactProp(name = "cacheEnabled", defaultBoolean = false)
- public void setCacheEnabled(AirMapView view, boolean cacheEnabled) {
- view.setCacheEnabled(cacheEnabled);
- }
-
- @ReactProp(name = "loadingEnabled", defaultBoolean = false)
- public void setLoadingEnabled(AirMapView view, boolean loadingEnabled) {
- view.enableMapLoading(loadingEnabled);
- }
-
- @ReactProp(name = "moveOnMarkerPress", defaultBoolean = true)
- public void setMoveOnMarkerPress(AirMapView view, boolean moveOnPress) {
- view.setMoveOnMarkerPress(moveOnPress);
- }
-
- @ReactProp(name = "loadingBackgroundColor", customType = "Color")
- public void setLoadingBackgroundColor(AirMapView view, @Nullable Integer loadingBackgroundColor) {
- view.setLoadingBackgroundColor(loadingBackgroundColor);
- }
-
- @ReactProp(name = "loadingIndicatorColor", customType = "Color")
- public void setLoadingIndicatorColor(AirMapView view, @Nullable Integer loadingIndicatorColor) {
- view.setLoadingIndicatorColor(loadingIndicatorColor);
- }
-
- @ReactProp(name = "pitchEnabled", defaultBoolean = false)
- public void setPitchEnabled(AirMapView view, boolean pitchEnabled) {
- view.map.getUiSettings().setTiltGesturesEnabled(pitchEnabled);
- }
-
- @Override
- public void receiveCommand(AirMapView view, int commandId, @Nullable ReadableArray args) {
- Integer duration;
- Double lat;
- Double lng;
- Double lngDelta;
- Double latDelta;
- ReadableMap region;
-
- switch (commandId) {
- case ANIMATE_TO_REGION:
- region = args.getMap(0);
- duration = args.getInt(1);
- lng = region.getDouble("longitude");
- lat = region.getDouble("latitude");
- lngDelta = region.getDouble("longitudeDelta");
- latDelta = region.getDouble("latitudeDelta");
- LatLngBounds bounds = new LatLngBounds(
- new LatLng(lat - latDelta / 2, lng - lngDelta / 2), // southwest
- new LatLng(lat + latDelta / 2, lng + lngDelta / 2) // northeast
- );
- view.animateToRegion(bounds, duration);
- break;
-
- case ANIMATE_TO_COORDINATE:
- region = args.getMap(0);
- duration = args.getInt(1);
- lng = region.getDouble("longitude");
- lat = region.getDouble("latitude");
- view.animateToCoordinate(new LatLng(lat, lng), duration);
- break;
-
- case FIT_TO_ELEMENTS:
- view.fitToElements(args.getBoolean(0));
- break;
-
- case FIT_TO_SUPPLIED_MARKERS:
- view.fitToSuppliedMarkers(args.getArray(0), args.getBoolean(1));
- break;
- case FIT_TO_COORDINATES:
- view.fitToCoordinates(args.getArray(0), args.getMap(1), args.getBoolean(2));
- break;
- }
- }
-
- @Override
- @Nullable
- public Map getExportedCustomDirectEventTypeConstants() {
- Map> map = MapBuilder.of(
- "onMapReady", MapBuilder.of("registrationName", "onMapReady"),
- "onPress", MapBuilder.of("registrationName", "onPress"),
- "onLongPress", MapBuilder.of("registrationName", "onLongPress"),
- "onMarkerPress", MapBuilder.of("registrationName", "onMarkerPress"),
- "onMarkerSelect", MapBuilder.of("registrationName", "onMarkerSelect"),
- "onMarkerDeselect", MapBuilder.of("registrationName", "onMarkerDeselect"),
- "onCalloutPress", MapBuilder.of("registrationName", "onCalloutPress")
- );
-
- map.putAll(MapBuilder.of(
- "onMarkerDragStart", MapBuilder.of("registrationName", "onMarkerDragStart"),
- "onMarkerDrag", MapBuilder.of("registrationName", "onMarkerDrag"),
- "onMarkerDragEnd", MapBuilder.of("registrationName", "onMarkerDragEnd"),
- "onPanDrag", MapBuilder.of("registrationName", "onPanDrag")
- ));
-
- return map;
- }
-
- @Override
- @Nullable
- public Map getCommandsMap() {
- return MapBuilder.of(
- "animateToRegion", ANIMATE_TO_REGION,
- "animateToCoordinate", ANIMATE_TO_COORDINATE,
- "fitToElements", FIT_TO_ELEMENTS,
- "fitToSuppliedMarkers", FIT_TO_SUPPLIED_MARKERS,
- "fitToCoordinates", FIT_TO_COORDINATES
+ private static final String REACT_CLASS = "AIRMap";
+ private static final int ANIMATE_TO_REGION = 1;
+ private static final int ANIMATE_TO_COORDINATE = 2;
+ private static final int ANIMATE_TO_VIEWING_ANGLE = 3;
+ private static final int ANIMATE_TO_BEARING = 4;
+ private static final int FIT_TO_ELEMENTS = 5;
+ private static final int FIT_TO_SUPPLIED_MARKERS = 6;
+ private static final int FIT_TO_COORDINATES = 7;
+
+ private final Map MAP_TYPES = MapBuilder.of(
+ "standard", GoogleMap.MAP_TYPE_NORMAL,
+ "satellite", GoogleMap.MAP_TYPE_SATELLITE,
+ "hybrid", GoogleMap.MAP_TYPE_HYBRID,
+ "terrain", GoogleMap.MAP_TYPE_TERRAIN,
+ "none", GoogleMap.MAP_TYPE_NONE
+ );
+
+ private final ReactApplicationContext appContext;
+
+ protected GoogleMapOptions googleMapOptions;
+
+ public AirMapManager(ReactApplicationContext context) {
+ this.appContext = context;
+ this.googleMapOptions = new GoogleMapOptions();
+ }
+
+ @Override
+ public String getName() {
+ return REACT_CLASS;
+ }
+
+ @Override
+ protected AirMapView createViewInstance(ThemedReactContext context) {
+ return new AirMapView(context, this.appContext, this, googleMapOptions);
+ }
+
+ private void emitMapError(ThemedReactContext context, String message, String type) {
+ WritableMap error = Arguments.createMap();
+ error.putString("message", message);
+ error.putString("type", type);
+
+ context
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
+ .emit("onError", error);
+ }
+
+ @ReactProp(name = "region")
+ public void setRegion(AirMapView view, ReadableMap region) {
+ view.setRegion(region);
+ }
+
+ @ReactProp(name = "initialRegion")
+ public void setInitialRegion(AirMapView view, ReadableMap initialRegion) {
+ view.setInitialRegion(initialRegion);
+ }
+
+ @ReactProp(name = "mapType")
+ public void setMapType(AirMapView view, @Nullable String mapType) {
+ int typeId = MAP_TYPES.get(mapType);
+ view.map.setMapType(typeId);
+ }
+
+ @ReactProp(name = "customMapStyleString")
+ public void setMapStyle(AirMapView view, @Nullable String customMapStyleString) {
+ view.map.setMapStyle(new MapStyleOptions(customMapStyleString));
+ }
+
+ @ReactProp(name = "showsUserLocation", defaultBoolean = false)
+ public void setShowsUserLocation(AirMapView view, boolean showUserLocation) {
+ view.setShowsUserLocation(showUserLocation);
+ }
+
+ @ReactProp(name = "showsMyLocationButton", defaultBoolean = true)
+ public void setShowsMyLocationButton(AirMapView view, boolean showMyLocationButton) {
+ view.setShowsMyLocationButton(showMyLocationButton);
+ }
+
+ @ReactProp(name = "toolbarEnabled", defaultBoolean = true)
+ public void setToolbarEnabled(AirMapView view, boolean toolbarEnabled) {
+ view.setToolbarEnabled(toolbarEnabled);
+ }
+
+ // This is a private prop to improve performance of panDrag by disabling it when the callback
+ // is not set
+ @ReactProp(name = "handlePanDrag", defaultBoolean = false)
+ public void setHandlePanDrag(AirMapView view, boolean handlePanDrag) {
+ view.setHandlePanDrag(handlePanDrag);
+ }
+
+ @ReactProp(name = "showsTraffic", defaultBoolean = false)
+ public void setShowTraffic(AirMapView view, boolean showTraffic) {
+ view.map.setTrafficEnabled(showTraffic);
+ }
+
+ @ReactProp(name = "showsBuildings", defaultBoolean = false)
+ public void setShowBuildings(AirMapView view, boolean showBuildings) {
+ view.map.setBuildingsEnabled(showBuildings);
+ }
+
+ @ReactProp(name = "showsIndoors", defaultBoolean = false)
+ public void setShowIndoors(AirMapView view, boolean showIndoors) {
+ view.map.setIndoorEnabled(showIndoors);
+ }
+
+ @ReactProp(name = "showsIndoorLevelPicker", defaultBoolean = false)
+ public void setShowsIndoorLevelPicker(AirMapView view, boolean showsIndoorLevelPicker) {
+ view.map.getUiSettings().setIndoorLevelPickerEnabled(showsIndoorLevelPicker);
+ }
+
+ @ReactProp(name = "showsCompass", defaultBoolean = false)
+ public void setShowsCompass(AirMapView view, boolean showsCompass) {
+ view.map.getUiSettings().setCompassEnabled(showsCompass);
+ }
+
+ @ReactProp(name = "scrollEnabled", defaultBoolean = false)
+ public void setScrollEnabled(AirMapView view, boolean scrollEnabled) {
+ view.map.getUiSettings().setScrollGesturesEnabled(scrollEnabled);
+ }
+
+ @ReactProp(name = "zoomEnabled", defaultBoolean = false)
+ public void setZoomEnabled(AirMapView view, boolean zoomEnabled) {
+ view.map.getUiSettings().setZoomGesturesEnabled(zoomEnabled);
+ }
+
+ @ReactProp(name = "rotateEnabled", defaultBoolean = false)
+ public void setRotateEnabled(AirMapView view, boolean rotateEnabled) {
+ view.map.getUiSettings().setRotateGesturesEnabled(rotateEnabled);
+ }
+
+ @ReactProp(name = "cacheEnabled", defaultBoolean = false)
+ public void setCacheEnabled(AirMapView view, boolean cacheEnabled) {
+ view.setCacheEnabled(cacheEnabled);
+ }
+
+ @ReactProp(name = "loadingEnabled", defaultBoolean = false)
+ public void setLoadingEnabled(AirMapView view, boolean loadingEnabled) {
+ view.enableMapLoading(loadingEnabled);
+ }
+
+ @ReactProp(name = "moveOnMarkerPress", defaultBoolean = true)
+ public void setMoveOnMarkerPress(AirMapView view, boolean moveOnPress) {
+ view.setMoveOnMarkerPress(moveOnPress);
+ }
+
+ @ReactProp(name = "loadingBackgroundColor", customType = "Color")
+ public void setLoadingBackgroundColor(AirMapView view, @Nullable Integer loadingBackgroundColor) {
+ view.setLoadingBackgroundColor(loadingBackgroundColor);
+ }
+
+ @ReactProp(name = "loadingIndicatorColor", customType = "Color")
+ public void setLoadingIndicatorColor(AirMapView view, @Nullable Integer loadingIndicatorColor) {
+ view.setLoadingIndicatorColor(loadingIndicatorColor);
+ }
+
+ @ReactProp(name = "pitchEnabled", defaultBoolean = false)
+ public void setPitchEnabled(AirMapView view, boolean pitchEnabled) {
+ view.map.getUiSettings().setTiltGesturesEnabled(pitchEnabled);
+ }
+
+ @ReactProp(name = "minZoomLevel")
+ public void setMinZoomLevel(AirMapView view, float minZoomLevel) {
+ view.map.setMinZoomPreference(minZoomLevel);
+ }
+
+ @ReactProp(name = "maxZoomLevel")
+ public void setMaxZoomLevel(AirMapView view, float maxZoomLevel) {
+ view.map.setMaxZoomPreference(maxZoomLevel);
+ }
+
+ @Override
+ public void receiveCommand(AirMapView view, int commandId, @Nullable ReadableArray args) {
+ Integer duration;
+ Double lat;
+ Double lng;
+ Double lngDelta;
+ Double latDelta;
+ float bearing;
+ float angle;
+ ReadableMap region;
+
+ switch (commandId) {
+ case ANIMATE_TO_REGION:
+ region = args.getMap(0);
+ duration = args.getInt(1);
+ lng = region.getDouble("longitude");
+ lat = region.getDouble("latitude");
+ lngDelta = region.getDouble("longitudeDelta");
+ latDelta = region.getDouble("latitudeDelta");
+ LatLngBounds bounds = new LatLngBounds(
+ new LatLng(lat - latDelta / 2, lng - lngDelta / 2), // southwest
+ new LatLng(lat + latDelta / 2, lng + lngDelta / 2) // northeast
);
- }
-
- @Override
- public LayoutShadowNode createShadowNodeInstance() {
- // A custom shadow node is needed in order to pass back the width/height of the map to the
- // view manager so that it can start applying camera moves with bounds.
- return new SizeReportingShadowNode();
- }
-
- @Override
- public void addView(AirMapView parent, View child, int index) {
- parent.addFeature(child, index);
- }
-
- @Override
- public int getChildCount(AirMapView view) {
- return view.getFeatureCount();
- }
-
- @Override
- public View getChildAt(AirMapView view, int index) {
- return view.getFeatureAt(index);
- }
-
- @Override
- public void removeViewAt(AirMapView parent, int index) {
- parent.removeFeatureAt(index);
- }
-
- @Override
- public void updateExtraData(AirMapView view, Object extraData) {
- view.updateExtraData(extraData);
- }
-
- void pushEvent(ThemedReactContext context, View view, String name, WritableMap data) {
- context.getJSModule(RCTEventEmitter.class)
- .receiveEvent(view.getId(), name, data);
- }
-
-
+ view.animateToRegion(bounds, duration);
+ break;
+
+ case ANIMATE_TO_COORDINATE:
+ region = args.getMap(0);
+ duration = args.getInt(1);
+ lng = region.getDouble("longitude");
+ lat = region.getDouble("latitude");
+ view.animateToCoordinate(new LatLng(lat, lng), duration);
+ break;
+
+ case ANIMATE_TO_VIEWING_ANGLE:
+ angle = (float)args.getDouble(0);
+ duration = args.getInt(1);
+ view.animateToViewingAngle(angle, duration);
+ break;
+
+ case ANIMATE_TO_BEARING:
+ bearing = (float)args.getDouble(0);
+ duration = args.getInt(1);
+ view.animateToBearing(bearing, duration);
+ break;
+
+ case FIT_TO_ELEMENTS:
+ view.fitToElements(args.getBoolean(0));
+ break;
+
+ case FIT_TO_SUPPLIED_MARKERS:
+ view.fitToSuppliedMarkers(args.getArray(0), args.getBoolean(1));
+ break;
+ case FIT_TO_COORDINATES:
+ view.fitToCoordinates(args.getArray(0), args.getMap(1), args.getBoolean(2));
+ break;
+ }
+ }
+
+ @Override
+ @Nullable
+ public Map getExportedCustomDirectEventTypeConstants() {
+ Map> map = MapBuilder.of(
+ "onMapReady", MapBuilder.of("registrationName", "onMapReady"),
+ "onPress", MapBuilder.of("registrationName", "onPress"),
+ "onLongPress", MapBuilder.of("registrationName", "onLongPress"),
+ "onMarkerPress", MapBuilder.of("registrationName", "onMarkerPress"),
+ "onMarkerSelect", MapBuilder.of("registrationName", "onMarkerSelect"),
+ "onMarkerDeselect", MapBuilder.of("registrationName", "onMarkerDeselect"),
+ "onCalloutPress", MapBuilder.of("registrationName", "onCalloutPress")
+ );
- @Override
- public void onDropViewInstance(AirMapView view) {
- view.doDestroy();
- super.onDropViewInstance(view);
- }
+ map.putAll(MapBuilder.of(
+ "onMarkerDragStart", MapBuilder.of("registrationName", "onMarkerDragStart"),
+ "onMarkerDrag", MapBuilder.of("registrationName", "onMarkerDrag"),
+ "onMarkerDragEnd", MapBuilder.of("registrationName", "onMarkerDragEnd"),
+ "onPanDrag", MapBuilder.of("registrationName", "onPanDrag")
+ ));
+
+ return map;
+ }
+
+ @Override
+ @Nullable
+ public Map getCommandsMap() {
+ return MapBuilder.of(
+ "animateToRegion", ANIMATE_TO_REGION,
+ "animateToCoordinate", ANIMATE_TO_COORDINATE,
+ "animateToViewingAngle", ANIMATE_TO_VIEWING_ANGLE,
+ "animateToBearing", ANIMATE_TO_BEARING,
+ "fitToElements", FIT_TO_ELEMENTS,
+ "fitToSuppliedMarkers", FIT_TO_SUPPLIED_MARKERS,
+ "fitToCoordinates", FIT_TO_COORDINATES
+ );
+ }
+
+ @Override
+ public LayoutShadowNode createShadowNodeInstance() {
+ // A custom shadow node is needed in order to pass back the width/height of the map to the
+ // view manager so that it can start applying camera moves with bounds.
+ return new SizeReportingShadowNode();
+ }
+
+ @Override
+ public void addView(AirMapView parent, View child, int index) {
+ parent.addFeature(child, index);
+ }
+
+ @Override
+ public int getChildCount(AirMapView view) {
+ return view.getFeatureCount();
+ }
+
+ @Override
+ public View getChildAt(AirMapView view, int index) {
+ return view.getFeatureAt(index);
+ }
+
+ @Override
+ public void removeViewAt(AirMapView parent, int index) {
+ parent.removeFeatureAt(index);
+ }
+
+ @Override
+ public void updateExtraData(AirMapView view, Object extraData) {
+ view.updateExtraData(extraData);
+ }
+
+ void pushEvent(ThemedReactContext context, View view, String name, WritableMap data) {
+ context.getJSModule(RCTEventEmitter.class)
+ .receiveEvent(view.getId(), name, data);
+ }
+
+
+ @Override
+ public void onDropViewInstance(AirMapView view) {
+ view.doDestroy();
+ super.onDropViewInstance(view);
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java
index 4041d91ac5..cb0274bfa4 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java
@@ -2,6 +2,7 @@
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.Animatable;
import android.net.Uri;
@@ -36,395 +37,398 @@
public class AirMapMarker extends AirMapFeature {
- private MarkerOptions markerOptions;
- private Marker marker;
- private int width;
- private int height;
- private String identifier;
-
- private LatLng position;
- private String title;
- private String snippet;
-
- private boolean anchorIsSet;
- private float anchorX;
- private float anchorY;
-
- private AirMapCallout calloutView;
- private View wrappedCalloutView;
- private final Context context;
-
- private float markerHue = 0.0f; // should be between 0 and 360
- private BitmapDescriptor iconBitmapDescriptor;
- private Bitmap iconBitmap;
-
- private float rotation = 0.0f;
- private boolean flat = false;
- private boolean draggable = false;
- private int zIndex = 0;
- private float opacity = 1.0f;
-
- private float calloutAnchorX;
- private float calloutAnchorY;
- private boolean calloutAnchorIsSet;
-
- private boolean hasCustomMarkerView = false;
-
- private final DraweeHolder> logoHolder;
- private DataSource> dataSource;
- private final ControllerListener mLogoControllerListener =
- new BaseControllerListener() {
- @Override
- public void onFinalImageSet(
- String id,
- @Nullable final ImageInfo imageInfo,
- @Nullable Animatable animatable) {
- CloseableReference imageReference = null;
- try {
- imageReference = dataSource.getResult();
- if (imageReference != null) {
- CloseableImage image = imageReference.get();
- if (image != null && image instanceof CloseableStaticBitmap) {
- CloseableStaticBitmap closeableStaticBitmap = (CloseableStaticBitmap) image;
- Bitmap bitmap = closeableStaticBitmap.getUnderlyingBitmap();
- if (bitmap != null) {
- bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
- iconBitmap = bitmap;
- iconBitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap);
- }
- }
- }
- } finally {
- dataSource.close();
- if (imageReference != null) {
- CloseableReference.closeSafely(imageReference);
- }
- }
- update();
+ private MarkerOptions markerOptions;
+ private Marker marker;
+ private int width;
+ private int height;
+ private String identifier;
+
+ private LatLng position;
+ private String title;
+ private String snippet;
+
+ private boolean anchorIsSet;
+ private float anchorX;
+ private float anchorY;
+
+ private AirMapCallout calloutView;
+ private View wrappedCalloutView;
+ private final Context context;
+
+ private float markerHue = 0.0f; // should be between 0 and 360
+ private BitmapDescriptor iconBitmapDescriptor;
+ private Bitmap iconBitmap;
+
+ private float rotation = 0.0f;
+ private boolean flat = false;
+ private boolean draggable = false;
+ private int zIndex = 0;
+ private float opacity = 1.0f;
+
+ private float calloutAnchorX;
+ private float calloutAnchorY;
+ private boolean calloutAnchorIsSet;
+
+ private boolean hasCustomMarkerView = false;
+
+ private final DraweeHolder> logoHolder;
+ private DataSource> dataSource;
+ private final ControllerListener mLogoControllerListener =
+ new BaseControllerListener() {
+ @Override
+ public void onFinalImageSet(
+ String id,
+ @Nullable final ImageInfo imageInfo,
+ @Nullable Animatable animatable) {
+ CloseableReference imageReference = null;
+ try {
+ imageReference = dataSource.getResult();
+ if (imageReference != null) {
+ CloseableImage image = imageReference.get();
+ if (image != null && image instanceof CloseableStaticBitmap) {
+ CloseableStaticBitmap closeableStaticBitmap = (CloseableStaticBitmap) image;
+ Bitmap bitmap = closeableStaticBitmap.getUnderlyingBitmap();
+ if (bitmap != null) {
+ bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
+ iconBitmap = bitmap;
+ iconBitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap);
}
- };
-
- public AirMapMarker(Context context) {
- super(context);
- this.context = context;
- logoHolder = DraweeHolder.create(createDraweeHierarchy(), context);
- logoHolder.onAttach();
- }
-
- private GenericDraweeHierarchy createDraweeHierarchy() {
- return new GenericDraweeHierarchyBuilder(getResources())
- .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
- .setFadeDuration(0)
- .build();
- }
-
- public void setCoordinate(ReadableMap coordinate) {
- position = new LatLng(coordinate.getDouble("latitude"), coordinate.getDouble("longitude"));
- if (marker != null) {
- marker.setPosition(position);
- }
- update();
- }
-
- public void setIdentifier(String identifier) {
- this.identifier = identifier;
- update();
- }
-
- public String getIdentifier() {
- return this.identifier;
- }
-
- public void setTitle(String title) {
- this.title = title;
- if (marker != null) {
- marker.setTitle(title);
- }
- update();
- }
-
- public void setSnippet(String snippet) {
- this.snippet = snippet;
- if (marker != null) {
- marker.setSnippet(snippet);
- }
- update();
- }
-
- public void setRotation(float rotation) {
- this.rotation = rotation;
- if (marker != null) {
- marker.setRotation(rotation);
- }
- update();
- }
-
- public void setFlat(boolean flat) {
- this.flat = flat;
- if (marker != null) {
- marker.setFlat(flat);
- }
- update();
- }
-
- public void setDraggable(boolean draggable) {
- this.draggable = draggable;
- if (marker != null) {
- marker.setDraggable(draggable);
- }
- update();
- }
-
- public void setZIndex(int zIndex) {
- this.zIndex = zIndex;
- if (marker != null) {
- marker.setZIndex(zIndex);
- }
- update();
- }
-
- public void setOpacity(float opacity) {
- this.opacity = opacity;
- if (marker != null) {
- marker.setAlpha(opacity);
- }
- update();
- }
-
- public void setMarkerHue(float markerHue) {
- this.markerHue = markerHue;
- update();
- }
-
- public void setAnchor(double x, double y) {
- anchorIsSet = true;
- anchorX = (float) x;
- anchorY = (float) y;
- if (marker != null) {
- marker.setAnchor(anchorX, anchorY);
- }
- update();
- }
-
- public void setCalloutAnchor(double x, double y) {
- calloutAnchorIsSet = true;
- calloutAnchorX = (float) x;
- calloutAnchorY = (float) y;
- if (marker != null) {
- marker.setInfoWindowAnchor(calloutAnchorX, calloutAnchorY);
- }
- update();
- }
-
- public void setImage(String uri) {
- if (uri == null) {
- iconBitmapDescriptor = null;
- update();
- } else if (uri.startsWith("http://") || uri.startsWith("https://") ||
- uri.startsWith("file://")) {
- ImageRequest imageRequest = ImageRequestBuilder
- .newBuilderWithSource(Uri.parse(uri))
- .build();
-
- ImagePipeline imagePipeline = Fresco.getImagePipeline();
- dataSource = imagePipeline.fetchDecodedImage(imageRequest, this);
- DraweeController controller = Fresco.newDraweeControllerBuilder()
- .setImageRequest(imageRequest)
- .setControllerListener(mLogoControllerListener)
- .setOldController(logoHolder.getController())
- .build();
- logoHolder.setController(controller);
- } else {
- iconBitmapDescriptor = getBitmapDescriptorByName(uri);
- update();
- }
- }
-
- public MarkerOptions getMarkerOptions() {
- if (markerOptions == null) {
- markerOptions = createMarkerOptions();
- }
- return markerOptions;
- }
-
- @Override
- public void addView(View child, int index) {
- super.addView(child, index);
- // if children are added, it means we are rendering a custom marker
- if (!(child instanceof AirMapCallout)) {
- hasCustomMarkerView = true;
- }
- update();
- }
-
- @Override
- public Object getFeature() {
- return marker;
- }
-
- @Override
- public void addToMap(GoogleMap map) {
- marker = map.addMarker(getMarkerOptions());
- }
-
- @Override
- public void removeFromMap(GoogleMap map) {
- marker.remove();
- marker = null;
- }
-
- private BitmapDescriptor getIcon() {
- if (hasCustomMarkerView) {
- // creating a bitmap from an arbitrary view
- if (iconBitmapDescriptor != null) {
- Bitmap viewBitmap = createDrawable();
- int width = Math.max(iconBitmap.getWidth(), viewBitmap.getWidth());
- int height = Math.max(iconBitmap.getHeight(), viewBitmap.getHeight());
- Bitmap combinedBitmap = Bitmap.createBitmap(width, height, iconBitmap.getConfig());
- Canvas canvas = new Canvas(combinedBitmap);
- canvas.drawBitmap(iconBitmap, 0, 0, null);
- canvas.drawBitmap(viewBitmap, 0, 0, null);
- return BitmapDescriptorFactory.fromBitmap(combinedBitmap);
- } else {
- return BitmapDescriptorFactory.fromBitmap(createDrawable());
+ }
}
- } else if (iconBitmapDescriptor != null) {
- // use local image as a marker
- return iconBitmapDescriptor;
- } else {
- // render the default marker pin
- return BitmapDescriptorFactory.defaultMarker(this.markerHue);
- }
- }
-
- private MarkerOptions createMarkerOptions() {
- MarkerOptions options = new MarkerOptions().position(position);
- if (anchorIsSet) options.anchor(anchorX, anchorY);
- if (calloutAnchorIsSet) options.infoWindowAnchor(calloutAnchorX, calloutAnchorY);
- options.title(title);
- options.snippet(snippet);
- options.rotation(rotation);
- options.flat(flat);
- options.draggable(draggable);
- options.zIndex(zIndex);
- options.alpha(opacity);
- options.icon(getIcon());
- return options;
- }
-
- public void update() {
- if (marker == null) {
- return;
- }
-
- marker.setIcon(getIcon());
-
- if (anchorIsSet) {
- marker.setAnchor(anchorX, anchorY);
- } else {
- marker.setAnchor(0.5f, 1.0f);
- }
-
- if (calloutAnchorIsSet) {
- marker.setInfoWindowAnchor(calloutAnchorX, calloutAnchorY);
- } else {
- marker.setInfoWindowAnchor(0.5f, 0);
- }
- }
-
- public void update(int width, int height) {
- this.width = width;
- this.height = height;
- update();
- }
-
- private Bitmap createDrawable() {
- int width = this.width <= 0 ? 100 : this.width;
- int height = this.height <= 0 ? 100 : this.height;
- this.buildDrawingCache();
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-
- Canvas canvas = new Canvas(bitmap);
- this.draw(canvas);
-
- return bitmap;
- }
-
- public void setCalloutView(AirMapCallout view) {
- this.calloutView = view;
- }
-
- public AirMapCallout getCalloutView() {
- return this.calloutView;
- }
-
- public View getCallout() {
- if (this.calloutView == null) return null;
-
- if (this.wrappedCalloutView == null) {
- this.wrapCalloutView();
- }
-
- if (this.calloutView.getTooltip()) {
- return this.wrappedCalloutView;
- } else {
- return null;
- }
- }
-
- public View getInfoContents() {
- if (this.calloutView == null) return null;
-
- if (this.wrappedCalloutView == null) {
- this.wrapCalloutView();
- }
-
- if (this.calloutView.getTooltip()) {
- return null;
- } else {
- return this.wrappedCalloutView;
+ } finally {
+ dataSource.close();
+ if (imageReference != null) {
+ CloseableReference.closeSafely(imageReference);
+ }
+ }
+ update();
}
+ };
+
+ public AirMapMarker(Context context) {
+ super(context);
+ this.context = context;
+ logoHolder = DraweeHolder.create(createDraweeHierarchy(), context);
+ logoHolder.onAttach();
+ }
+
+ private GenericDraweeHierarchy createDraweeHierarchy() {
+ return new GenericDraweeHierarchyBuilder(getResources())
+ .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
+ .setFadeDuration(0)
+ .build();
+ }
+
+ public void setCoordinate(ReadableMap coordinate) {
+ position = new LatLng(coordinate.getDouble("latitude"), coordinate.getDouble("longitude"));
+ if (marker != null) {
+ marker.setPosition(position);
+ }
+ update();
+ }
+
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ update();
+ }
+
+ public String getIdentifier() {
+ return this.identifier;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ if (marker != null) {
+ marker.setTitle(title);
+ }
+ update();
+ }
+
+ public void setSnippet(String snippet) {
+ this.snippet = snippet;
+ if (marker != null) {
+ marker.setSnippet(snippet);
+ }
+ update();
+ }
+
+ public void setRotation(float rotation) {
+ this.rotation = rotation;
+ if (marker != null) {
+ marker.setRotation(rotation);
+ }
+ update();
+ }
+
+ public void setFlat(boolean flat) {
+ this.flat = flat;
+ if (marker != null) {
+ marker.setFlat(flat);
+ }
+ update();
+ }
+
+ public void setDraggable(boolean draggable) {
+ this.draggable = draggable;
+ if (marker != null) {
+ marker.setDraggable(draggable);
+ }
+ update();
+ }
+
+ public void setZIndex(int zIndex) {
+ this.zIndex = zIndex;
+ if (marker != null) {
+ marker.setZIndex(zIndex);
+ }
+ update();
+ }
+
+ public void setOpacity(float opacity) {
+ this.opacity = opacity;
+ if (marker != null) {
+ marker.setAlpha(opacity);
+ }
+ update();
+ }
+
+ public void setMarkerHue(float markerHue) {
+ this.markerHue = markerHue;
+ update();
+ }
+
+ public void setAnchor(double x, double y) {
+ anchorIsSet = true;
+ anchorX = (float) x;
+ anchorY = (float) y;
+ if (marker != null) {
+ marker.setAnchor(anchorX, anchorY);
+ }
+ update();
+ }
+
+ public void setCalloutAnchor(double x, double y) {
+ calloutAnchorIsSet = true;
+ calloutAnchorX = (float) x;
+ calloutAnchorY = (float) y;
+ if (marker != null) {
+ marker.setInfoWindowAnchor(calloutAnchorX, calloutAnchorY);
+ }
+ update();
+ }
+
+ public void setImage(String uri) {
+ if (uri == null) {
+ iconBitmapDescriptor = null;
+ update();
+ } else if (uri.startsWith("http://") || uri.startsWith("https://") ||
+ uri.startsWith("file://")) {
+ ImageRequest imageRequest = ImageRequestBuilder
+ .newBuilderWithSource(Uri.parse(uri))
+ .build();
+
+ ImagePipeline imagePipeline = Fresco.getImagePipeline();
+ dataSource = imagePipeline.fetchDecodedImage(imageRequest, this);
+ DraweeController controller = Fresco.newDraweeControllerBuilder()
+ .setImageRequest(imageRequest)
+ .setControllerListener(mLogoControllerListener)
+ .setOldController(logoHolder.getController())
+ .build();
+ logoHolder.setController(controller);
+ } else {
+ iconBitmapDescriptor = getBitmapDescriptorByName(uri);
+ if (iconBitmapDescriptor != null) {
+ iconBitmap = BitmapFactory.decodeResource(getResources(), getDrawableResourceByName(uri));
+ }
+ update();
+ }
+ }
+
+ public MarkerOptions getMarkerOptions() {
+ if (markerOptions == null) {
+ markerOptions = createMarkerOptions();
+ }
+ return markerOptions;
+ }
+
+ @Override
+ public void addView(View child, int index) {
+ super.addView(child, index);
+ // if children are added, it means we are rendering a custom marker
+ if (!(child instanceof AirMapCallout)) {
+ hasCustomMarkerView = true;
+ }
+ update();
+ }
+
+ @Override
+ public Object getFeature() {
+ return marker;
+ }
+
+ @Override
+ public void addToMap(GoogleMap map) {
+ marker = map.addMarker(getMarkerOptions());
+ }
+
+ @Override
+ public void removeFromMap(GoogleMap map) {
+ marker.remove();
+ marker = null;
+ }
+
+ private BitmapDescriptor getIcon() {
+ if (hasCustomMarkerView) {
+ // creating a bitmap from an arbitrary view
+ if (iconBitmapDescriptor != null) {
+ Bitmap viewBitmap = createDrawable();
+ int width = Math.max(iconBitmap.getWidth(), viewBitmap.getWidth());
+ int height = Math.max(iconBitmap.getHeight(), viewBitmap.getHeight());
+ Bitmap combinedBitmap = Bitmap.createBitmap(width, height, iconBitmap.getConfig());
+ Canvas canvas = new Canvas(combinedBitmap);
+ canvas.drawBitmap(iconBitmap, 0, 0, null);
+ canvas.drawBitmap(viewBitmap, 0, 0, null);
+ return BitmapDescriptorFactory.fromBitmap(combinedBitmap);
+ } else {
+ return BitmapDescriptorFactory.fromBitmap(createDrawable());
+ }
+ } else if (iconBitmapDescriptor != null) {
+ // use local image as a marker
+ return iconBitmapDescriptor;
+ } else {
+ // render the default marker pin
+ return BitmapDescriptorFactory.defaultMarker(this.markerHue);
+ }
+ }
+
+ private MarkerOptions createMarkerOptions() {
+ MarkerOptions options = new MarkerOptions().position(position);
+ if (anchorIsSet) options.anchor(anchorX, anchorY);
+ if (calloutAnchorIsSet) options.infoWindowAnchor(calloutAnchorX, calloutAnchorY);
+ options.title(title);
+ options.snippet(snippet);
+ options.rotation(rotation);
+ options.flat(flat);
+ options.draggable(draggable);
+ options.zIndex(zIndex);
+ options.alpha(opacity);
+ options.icon(getIcon());
+ return options;
+ }
+
+ public void update() {
+ if (marker == null) {
+ return;
+ }
+
+ marker.setIcon(getIcon());
+
+ if (anchorIsSet) {
+ marker.setAnchor(anchorX, anchorY);
+ } else {
+ marker.setAnchor(0.5f, 1.0f);
+ }
+
+ if (calloutAnchorIsSet) {
+ marker.setInfoWindowAnchor(calloutAnchorX, calloutAnchorY);
+ } else {
+ marker.setInfoWindowAnchor(0.5f, 0);
+ }
+ }
+
+ public void update(int width, int height) {
+ this.width = width;
+ this.height = height;
+ update();
+ }
+
+ private Bitmap createDrawable() {
+ int width = this.width <= 0 ? 100 : this.width;
+ int height = this.height <= 0 ? 100 : this.height;
+ this.buildDrawingCache();
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+ Canvas canvas = new Canvas(bitmap);
+ this.draw(canvas);
+
+ return bitmap;
+ }
+
+ public void setCalloutView(AirMapCallout view) {
+ this.calloutView = view;
+ }
+
+ public AirMapCallout getCalloutView() {
+ return this.calloutView;
+ }
+
+ public View getCallout() {
+ if (this.calloutView == null) return null;
+
+ if (this.wrappedCalloutView == null) {
+ this.wrapCalloutView();
+ }
+
+ if (this.calloutView.getTooltip()) {
+ return this.wrappedCalloutView;
+ } else {
+ return null;
+ }
+ }
+
+ public View getInfoContents() {
+ if (this.calloutView == null) return null;
+
+ if (this.wrappedCalloutView == null) {
+ this.wrapCalloutView();
+ }
+
+ if (this.calloutView.getTooltip()) {
+ return null;
+ } else {
+ return this.wrappedCalloutView;
+ }
+ }
+
+ private void wrapCalloutView() {
+ // some hackery is needed to get the arbitrary infowindow view to render centered, and
+ // with only the width/height that it needs.
+ if (this.calloutView == null || this.calloutView.getChildCount() == 0) {
+ return;
}
- private void wrapCalloutView() {
- // some hackery is needed to get the arbitrary infowindow view to render centered, and
- // with only the width/height that it needs.
- if (this.calloutView == null || this.calloutView.getChildCount() == 0) {
- return;
- }
+ LinearLayout LL = new LinearLayout(context);
+ LL.setOrientation(LinearLayout.VERTICAL);
+ LL.setLayoutParams(new LinearLayout.LayoutParams(
+ this.calloutView.width,
+ this.calloutView.height,
+ 0f
+ ));
- LinearLayout LL = new LinearLayout(context);
- LL.setOrientation(LinearLayout.VERTICAL);
- LL.setLayoutParams(new LinearLayout.LayoutParams(
- this.calloutView.width,
- this.calloutView.height,
- 0f
- ));
+ LinearLayout LL2 = new LinearLayout(context);
+ LL2.setOrientation(LinearLayout.HORIZONTAL);
+ LL2.setLayoutParams(new LinearLayout.LayoutParams(
+ this.calloutView.width,
+ this.calloutView.height,
+ 0f
+ ));
- LinearLayout LL2 = new LinearLayout(context);
- LL2.setOrientation(LinearLayout.HORIZONTAL);
- LL2.setLayoutParams(new LinearLayout.LayoutParams(
- this.calloutView.width,
- this.calloutView.height,
- 0f
- ));
+ LL.addView(LL2);
+ LL2.addView(this.calloutView);
- LL.addView(LL2);
- LL2.addView(this.calloutView);
+ this.wrappedCalloutView = LL;
+ }
- this.wrappedCalloutView = LL;
- }
+ private int getDrawableResourceByName(String name) {
+ return getResources().getIdentifier(
+ name,
+ "drawable",
+ getContext().getPackageName());
+ }
- private int getDrawableResourceByName(String name) {
- return getResources().getIdentifier(
- name,
- "drawable",
- getContext().getPackageName());
- }
-
- private BitmapDescriptor getBitmapDescriptorByName(String name) {
- return BitmapDescriptorFactory.fromResource(getDrawableResourceByName(name));
- }
+ private BitmapDescriptor getBitmapDescriptorByName(String name) {
+ return BitmapDescriptorFactory.fromResource(getDrawableResourceByName(name));
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarkerManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarkerManager.java
index e035e1e144..3aef3148cd 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarkerManager.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMarkerManager.java
@@ -19,46 +19,46 @@
public class AirMapMarkerManager extends ViewGroupManager {
- private static final int SHOW_INFO_WINDOW = 1;
- private static final int HIDE_INFO_WINDOW = 2;
-
- public AirMapMarkerManager() {
- }
-
- @Override
- public String getName() {
- return "AIRMapMarker";
- }
-
- @Override
- public AirMapMarker createViewInstance(ThemedReactContext context) {
- return new AirMapMarker(context);
- }
-
- @ReactProp(name = "coordinate")
- public void setCoordinate(AirMapMarker view, ReadableMap map) {
- view.setCoordinate(map);
- }
-
- @ReactProp(name = "title")
- public void setTitle(AirMapMarker view, String title) {
- view.setTitle(title);
- }
-
- @ReactProp(name = "identifier")
- public void setIdentifier(AirMapMarker view, String identifier) {
- view.setIdentifier(identifier);
- }
-
- @ReactProp(name = "description")
- public void setDescription(AirMapMarker view, String description) {
- view.setSnippet(description);
- }
-
- // NOTE(lmr):
- // android uses normalized coordinate systems for this, and is provided through the
- // `anchor` property and `calloutAnchor` instead. Perhaps some work could be done
- // to normalize iOS and android to use just one of the systems.
+ private static final int SHOW_INFO_WINDOW = 1;
+ private static final int HIDE_INFO_WINDOW = 2;
+
+ public AirMapMarkerManager() {
+ }
+
+ @Override
+ public String getName() {
+ return "AIRMapMarker";
+ }
+
+ @Override
+ public AirMapMarker createViewInstance(ThemedReactContext context) {
+ return new AirMapMarker(context);
+ }
+
+ @ReactProp(name = "coordinate")
+ public void setCoordinate(AirMapMarker view, ReadableMap map) {
+ view.setCoordinate(map);
+ }
+
+ @ReactProp(name = "title")
+ public void setTitle(AirMapMarker view, String title) {
+ view.setTitle(title);
+ }
+
+ @ReactProp(name = "identifier")
+ public void setIdentifier(AirMapMarker view, String identifier) {
+ view.setIdentifier(identifier);
+ }
+
+ @ReactProp(name = "description")
+ public void setDescription(AirMapMarker view, String description) {
+ view.setSnippet(description);
+ }
+
+ // NOTE(lmr):
+ // android uses normalized coordinate systems for this, and is provided through the
+ // `anchor` property and `calloutAnchor` instead. Perhaps some work could be done
+ // to normalize iOS and android to use just one of the systems.
// @ReactProp(name = "centerOffset")
// public void setCenterOffset(AirMapMarker view, ReadableMap map) {
//
@@ -69,143 +69,143 @@ public void setDescription(AirMapMarker view, String description) {
//
// }
- @ReactProp(name = "anchor")
- public void setAnchor(AirMapMarker view, ReadableMap map) {
- // should default to (0.5, 1) (bottom middle)
- double x = map != null && map.hasKey("x") ? map.getDouble("x") : 0.5;
- double y = map != null && map.hasKey("y") ? map.getDouble("y") : 1.0;
- view.setAnchor(x, y);
- }
-
- @ReactProp(name = "calloutAnchor")
- public void setCalloutAnchor(AirMapMarker view, ReadableMap map) {
- // should default to (0.5, 0) (top middle)
- double x = map != null && map.hasKey("x") ? map.getDouble("x") : 0.5;
- double y = map != null && map.hasKey("y") ? map.getDouble("y") : 0.0;
- view.setCalloutAnchor(x, y);
- }
-
- @ReactProp(name = "image")
- public void setImage(AirMapMarker view, @Nullable String source) {
- view.setImage(source);
- }
+ @ReactProp(name = "anchor")
+ public void setAnchor(AirMapMarker view, ReadableMap map) {
+ // should default to (0.5, 1) (bottom middle)
+ double x = map != null && map.hasKey("x") ? map.getDouble("x") : 0.5;
+ double y = map != null && map.hasKey("y") ? map.getDouble("y") : 1.0;
+ view.setAnchor(x, y);
+ }
+
+ @ReactProp(name = "calloutAnchor")
+ public void setCalloutAnchor(AirMapMarker view, ReadableMap map) {
+ // should default to (0.5, 0) (top middle)
+ double x = map != null && map.hasKey("x") ? map.getDouble("x") : 0.5;
+ double y = map != null && map.hasKey("y") ? map.getDouble("y") : 0.0;
+ view.setCalloutAnchor(x, y);
+ }
+
+ @ReactProp(name = "image")
+ public void setImage(AirMapMarker view, @Nullable String source) {
+ view.setImage(source);
+ }
// public void setImage(AirMapMarker view, ReadableMap image) {
// view.setImage(image);
// }
- @ReactProp(name = "pinColor", defaultInt = Color.RED, customType = "Color")
- public void setPinColor(AirMapMarker view, int pinColor) {
- float[] hsv = new float[3];
- Color.colorToHSV(pinColor, hsv);
- // NOTE: android only supports a hue
- view.setMarkerHue(hsv[0]);
- }
-
- @ReactProp(name = "rotation", defaultFloat = 0.0f)
- public void setMarkerRotation(AirMapMarker view, float rotation) {
- view.setRotation(rotation);
- }
-
- @ReactProp(name = "flat", defaultBoolean = false)
- public void setFlat(AirMapMarker view, boolean flat) {
- view.setFlat(flat);
- }
-
- @ReactProp(name = "draggable", defaultBoolean = false)
- public void setDraggable(AirMapMarker view, boolean draggable) {
- view.setDraggable(draggable);
- }
-
- @Override
- @ReactProp(name = "zIndex", defaultFloat = 0.0f)
- public void setZIndex(AirMapMarker view, float zIndex) {
- super.setZIndex(view, zIndex);
- int integerZIndex = Math.round(zIndex);
- view.setZIndex(integerZIndex);
- }
-
- @Override
- @ReactProp(name = "opacity", defaultFloat = 1.0f)
- public void setOpacity(AirMapMarker view, float opacity) {
- super.setOpacity(view, opacity);
- view.setOpacity(opacity);
- }
-
- @Override
- public void addView(AirMapMarker parent, View child, int index) {
- // if an component is a child, then it is a callout view, NOT part of the
- // marker.
- if (child instanceof AirMapCallout) {
- parent.setCalloutView((AirMapCallout) child);
- } else {
- super.addView(parent, child, index);
- parent.update();
- }
- }
-
- @Override
- public void removeViewAt(AirMapMarker parent, int index) {
- super.removeViewAt(parent, index);
- parent.update();
- }
-
- @Override
- @Nullable
- public Map getCommandsMap() {
- return MapBuilder.of(
- "showCallout", SHOW_INFO_WINDOW,
- "hideCallout", HIDE_INFO_WINDOW
- );
- }
-
- @Override
- public void receiveCommand(AirMapMarker view, int commandId, @Nullable ReadableArray args) {
- switch (commandId) {
- case SHOW_INFO_WINDOW:
- ((Marker) view.getFeature()).showInfoWindow();
- break;
-
- case HIDE_INFO_WINDOW:
- ((Marker) view.getFeature()).hideInfoWindow();
- break;
- }
- }
-
- @Override
- @Nullable
- public Map getExportedCustomDirectEventTypeConstants() {
- Map> map = MapBuilder.of(
- "onPress", MapBuilder.of("registrationName", "onPress"),
- "onCalloutPress", MapBuilder.of("registrationName", "onCalloutPress"),
- "onDragStart", MapBuilder.of("registrationName", "onDragStart"),
- "onDrag", MapBuilder.of("registrationName", "onDrag"),
- "onDragEnd", MapBuilder.of("registrationName", "onDragEnd")
- );
-
- map.putAll(MapBuilder.of(
- "onDragStart", MapBuilder.of("registrationName", "onDragStart"),
- "onDrag", MapBuilder.of("registrationName", "onDrag"),
- "onDragEnd", MapBuilder.of("registrationName", "onDragEnd")
- ));
-
- return map;
- }
-
- @Override
- public LayoutShadowNode createShadowNodeInstance() {
- // we use a custom shadow node that emits the width/height of the view
- // after layout with the updateExtraData method. Without this, we can't generate
- // a bitmap of the appropriate width/height of the rendered view.
- return new SizeReportingShadowNode();
- }
-
- @Override
- public void updateExtraData(AirMapMarker view, Object extraData) {
- // This method is called from the shadow node with the width/height of the rendered
- // marker view.
- HashMap data = (HashMap) extraData;
- float width = data.get("width");
- float height = data.get("height");
- view.update((int) width, (int) height);
- }
+ @ReactProp(name = "pinColor", defaultInt = Color.RED, customType = "Color")
+ public void setPinColor(AirMapMarker view, int pinColor) {
+ float[] hsv = new float[3];
+ Color.colorToHSV(pinColor, hsv);
+ // NOTE: android only supports a hue
+ view.setMarkerHue(hsv[0]);
+ }
+
+ @ReactProp(name = "rotation", defaultFloat = 0.0f)
+ public void setMarkerRotation(AirMapMarker view, float rotation) {
+ view.setRotation(rotation);
+ }
+
+ @ReactProp(name = "flat", defaultBoolean = false)
+ public void setFlat(AirMapMarker view, boolean flat) {
+ view.setFlat(flat);
+ }
+
+ @ReactProp(name = "draggable", defaultBoolean = false)
+ public void setDraggable(AirMapMarker view, boolean draggable) {
+ view.setDraggable(draggable);
+ }
+
+ @Override
+ @ReactProp(name = "zIndex", defaultFloat = 0.0f)
+ public void setZIndex(AirMapMarker view, float zIndex) {
+ super.setZIndex(view, zIndex);
+ int integerZIndex = Math.round(zIndex);
+ view.setZIndex(integerZIndex);
+ }
+
+ @Override
+ @ReactProp(name = "opacity", defaultFloat = 1.0f)
+ public void setOpacity(AirMapMarker view, float opacity) {
+ super.setOpacity(view, opacity);
+ view.setOpacity(opacity);
+ }
+
+ @Override
+ public void addView(AirMapMarker parent, View child, int index) {
+ // if an component is a child, then it is a callout view, NOT part of the
+ // marker.
+ if (child instanceof AirMapCallout) {
+ parent.setCalloutView((AirMapCallout) child);
+ } else {
+ super.addView(parent, child, index);
+ parent.update();
+ }
+ }
+
+ @Override
+ public void removeViewAt(AirMapMarker parent, int index) {
+ super.removeViewAt(parent, index);
+ parent.update();
+ }
+
+ @Override
+ @Nullable
+ public Map getCommandsMap() {
+ return MapBuilder.of(
+ "showCallout", SHOW_INFO_WINDOW,
+ "hideCallout", HIDE_INFO_WINDOW
+ );
+ }
+
+ @Override
+ public void receiveCommand(AirMapMarker view, int commandId, @Nullable ReadableArray args) {
+ switch (commandId) {
+ case SHOW_INFO_WINDOW:
+ ((Marker) view.getFeature()).showInfoWindow();
+ break;
+
+ case HIDE_INFO_WINDOW:
+ ((Marker) view.getFeature()).hideInfoWindow();
+ break;
+ }
+ }
+
+ @Override
+ @Nullable
+ public Map getExportedCustomDirectEventTypeConstants() {
+ Map> map = MapBuilder.of(
+ "onPress", MapBuilder.of("registrationName", "onPress"),
+ "onCalloutPress", MapBuilder.of("registrationName", "onCalloutPress"),
+ "onDragStart", MapBuilder.of("registrationName", "onDragStart"),
+ "onDrag", MapBuilder.of("registrationName", "onDrag"),
+ "onDragEnd", MapBuilder.of("registrationName", "onDragEnd")
+ );
+
+ map.putAll(MapBuilder.of(
+ "onDragStart", MapBuilder.of("registrationName", "onDragStart"),
+ "onDrag", MapBuilder.of("registrationName", "onDrag"),
+ "onDragEnd", MapBuilder.of("registrationName", "onDragEnd")
+ ));
+
+ return map;
+ }
+
+ @Override
+ public LayoutShadowNode createShadowNodeInstance() {
+ // we use a custom shadow node that emits the width/height of the view
+ // after layout with the updateExtraData method. Without this, we can't generate
+ // a bitmap of the appropriate width/height of the rendered view.
+ return new SizeReportingShadowNode();
+ }
+
+ @Override
+ public void updateExtraData(AirMapMarker view, Object extraData) {
+ // This method is called from the shadow node with the width/height of the rendered
+ // marker view.
+ HashMap data = (HashMap) extraData;
+ float width = data.get("width");
+ float height = data.get("height");
+ view.update((int) width, (int) height);
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapModule.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapModule.java
index 2cd1ba6860..dfc71d6d9c 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapModule.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapModule.java
@@ -1,128 +1,138 @@
package com.airbnb.android.react.maps;
import android.app.Activity;
-import android.util.DisplayMetrics;
-import android.util.Base64;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.view.View;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.Closeable;
-
-import javax.annotation.Nullable;
+import android.util.Base64;
+import android.util.DisplayMetrics;
+import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactMethod;
-import com.facebook.react.uimanager.UIManagerModule;
-import com.facebook.react.uimanager.UIBlock;
+import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.NativeViewHierarchyManager;
-
+import com.facebook.react.uimanager.UIBlock;
+import com.facebook.react.uimanager.UIManagerModule;
import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.common.GoogleApiAvailability;
-public class AirMapModule extends ReactContextBaseJavaModule {
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
- private static final String SNAPSHOT_RESULT_FILE = "file";
- private static final String SNAPSHOT_RESULT_BASE64 = "base64";
- private static final String SNAPSHOT_FORMAT_PNG = "png";
- private static final String SNAPSHOT_FORMAT_JPG = "jpg";
+import java.util.Map;
+import java.util.HashMap;
- public AirMapModule(ReactApplicationContext reactContext) {
- super(reactContext);
- }
+import javax.annotation.Nullable;
- @Override
- public String getName() {
- return "AirMapModule";
- }
+public class AirMapModule extends ReactContextBaseJavaModule {
- public Activity getActivity() {
- return getCurrentActivity();
+ private static final String SNAPSHOT_RESULT_FILE = "file";
+ private static final String SNAPSHOT_RESULT_BASE64 = "base64";
+ private static final String SNAPSHOT_FORMAT_PNG = "png";
+ private static final String SNAPSHOT_FORMAT_JPG = "jpg";
+
+ public AirMapModule(ReactApplicationContext reactContext) {
+ super(reactContext);
+ }
+
+ @Override
+ public String getName() {
+ return "AirMapModule";
+ }
+
+ @Override
+ public Map getConstants() {
+ final Map constants = new HashMap<>();
+ constants.put("legalNotice", "This license information is displayed in Settings > Google > Open Source on any device running Google Play services.");
+ return constants;
+ }
+
+ public Activity getActivity() {
+ return getCurrentActivity();
+ }
+
+ public static void closeQuietly(Closeable closeable) {
+ if (closeable == null) return;
+ try {
+ closeable.close();
+ } catch (IOException ignored) {
}
+ }
+
+ @ReactMethod
+ public void takeSnapshot(final int tag, final ReadableMap options, final Promise promise) {
- public static void closeQuietly(Closeable closeable) {
- if (closeable == null) return;
- try {
- closeable.close();
- } catch (IOException ignored) {
+ // Parse and verity options
+ final ReactApplicationContext context = getReactApplicationContext();
+ final String format = options.hasKey("format") ? options.getString("format") : "png";
+ final Bitmap.CompressFormat compressFormat =
+ format.equals(SNAPSHOT_FORMAT_PNG) ? Bitmap.CompressFormat.PNG :
+ format.equals(SNAPSHOT_FORMAT_JPG) ? Bitmap.CompressFormat.JPEG : null;
+ final double quality = options.hasKey("quality") ? options.getDouble("quality") : 1.0;
+ final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+ final Integer width =
+ options.hasKey("width") ? (int) (displayMetrics.density * options.getDouble("width")) : 0;
+ final Integer height =
+ options.hasKey("height") ? (int) (displayMetrics.density * options.getDouble("height")) : 0;
+ final String result = options.hasKey("result") ? options.getString("result") : "file";
+
+ // Add UI-block so we can get a valid reference to the map-view
+ UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
+ uiManager.addUIBlock(new UIBlock() {
+ public void execute(NativeViewHierarchyManager nvhm) {
+ AirMapView view = (AirMapView) nvhm.resolveView(tag);
+ if (view == null) {
+ promise.reject("AirMapView not found");
+ return;
}
- }
+ if (view.map == null) {
+ promise.reject("AirMapView.map is not valid");
+ return;
+ }
+ view.map.snapshot(new GoogleMap.SnapshotReadyCallback() {
+ public void onSnapshotReady(@Nullable Bitmap snapshot) {
- @ReactMethod
- public void takeSnapshot(final int tag, final ReadableMap options, final Promise promise) {
+ // Convert image to requested width/height if necessary
+ if (snapshot == null) {
+ promise.reject("Failed to generate bitmap, snapshot = null");
+ return;
+ }
+ if ((width != 0) && (height != 0) &&
+ (width != snapshot.getWidth() || height != snapshot.getHeight())) {
+ snapshot = Bitmap.createScaledBitmap(snapshot, width, height, true);
+ }
- // Parse and verity options
- final ReactApplicationContext context = getReactApplicationContext();
- final String format = options.hasKey("format") ? options.getString("format") : "png";
- final Bitmap.CompressFormat compressFormat =
- format.equals(SNAPSHOT_FORMAT_PNG) ? Bitmap.CompressFormat.PNG :
- format.equals(SNAPSHOT_FORMAT_JPG) ? Bitmap.CompressFormat.JPEG : null;
- final double quality = options.hasKey("quality") ? options.getDouble("quality") : 1.0;
- final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
- final Integer width = options.hasKey("width") ? (int)(displayMetrics.density * options.getDouble("width")) : 0;
- final Integer height = options.hasKey("height") ? (int)(displayMetrics.density * options.getDouble("height")) : 0;
- final String result = options.hasKey("result") ? options.getString("result") : "file";
-
- // Add UI-block so we can get a valid reference to the map-view
- UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
- uiManager.addUIBlock(new UIBlock() {
- public void execute (NativeViewHierarchyManager nvhm) {
- AirMapView view = (AirMapView) nvhm.resolveView(tag);
- if (view == null) {
- promise.reject("AirMapView not found");
- return;
- }
- if (view.map == null) {
- promise.reject("AirMapView.map is not valid");
- return;
- }
- view.map.snapshot(new GoogleMap.SnapshotReadyCallback() {
- public void onSnapshotReady(@Nullable Bitmap snapshot) {
-
- // Convert image to requested width/height if neccesary
- if (snapshot == null) {
- promise.reject("Failed to generate bitmap, snapshot = null");
- return;
- }
- if ((width != 0) && (height != 0) && (width != snapshot.getWidth() || height != snapshot.getHeight())) {
- snapshot = Bitmap.createScaledBitmap(snapshot, width, height, true);
- }
-
- // Save the snapshot to disk
- if (result.equals(SNAPSHOT_RESULT_FILE)) {
- File tempFile;
- FileOutputStream outputStream;
- try {
- tempFile = File.createTempFile("AirMapSnapshot", "." + format, context.getCacheDir());
- outputStream = new FileOutputStream(tempFile);
- }
- catch (Exception e) {
- promise.reject(e);
- return;
- }
- snapshot.compress(compressFormat, (int)(100.0 * quality), outputStream);
- closeQuietly(outputStream);
- String uri = Uri.fromFile(tempFile).toString();
- promise.resolve(uri);
- }
- else if (result.equals(SNAPSHOT_RESULT_BASE64)) {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- snapshot.compress(compressFormat, (int)(100.0 * quality), outputStream);
- closeQuietly(outputStream);
- byte[] bytes = outputStream.toByteArray();
- String data = Base64.encodeToString(bytes, Base64.NO_WRAP);
- promise.resolve(data);
- }
- }
- });
+ // Save the snapshot to disk
+ if (result.equals(SNAPSHOT_RESULT_FILE)) {
+ File tempFile;
+ FileOutputStream outputStream;
+ try {
+ tempFile =
+ File.createTempFile("AirMapSnapshot", "." + format, context.getCacheDir());
+ outputStream = new FileOutputStream(tempFile);
+ } catch (Exception e) {
+ promise.reject(e);
+ return;
+ }
+ snapshot.compress(compressFormat, (int) (100.0 * quality), outputStream);
+ closeQuietly(outputStream);
+ String uri = Uri.fromFile(tempFile).toString();
+ promise.resolve(uri);
+ } else if (result.equals(SNAPSHOT_RESULT_BASE64)) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ snapshot.compress(compressFormat, (int) (100.0 * quality), outputStream);
+ closeQuietly(outputStream);
+ byte[] bytes = outputStream.toByteArray();
+ String data = Base64.encodeToString(bytes, Base64.NO_WRAP);
+ promise.resolve(data);
}
+ }
});
- }
+ }
+ });
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolygon.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolygon.java
index 226bc241cb..41257b32e6 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolygon.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolygon.java
@@ -14,99 +14,99 @@
public class AirMapPolygon extends AirMapFeature {
- private PolygonOptions polygonOptions;
- private Polygon polygon;
-
- private List coordinates;
- private int strokeColor;
- private int fillColor;
- private float strokeWidth;
- private boolean geodesic;
- private float zIndex;
-
- public AirMapPolygon(Context context) {
- super(context);
+ private PolygonOptions polygonOptions;
+ private Polygon polygon;
+
+ private List coordinates;
+ private int strokeColor;
+ private int fillColor;
+ private float strokeWidth;
+ private boolean geodesic;
+ private float zIndex;
+
+ public AirMapPolygon(Context context) {
+ super(context);
+ }
+
+ public void setCoordinates(ReadableArray coordinates) {
+ // it's kind of a bummer that we can't run map() or anything on the ReadableArray
+ this.coordinates = new ArrayList<>(coordinates.size());
+ for (int i = 0; i < coordinates.size(); i++) {
+ ReadableMap coordinate = coordinates.getMap(i);
+ this.coordinates.add(i,
+ new LatLng(coordinate.getDouble("latitude"), coordinate.getDouble("longitude")));
}
-
- public void setCoordinates(ReadableArray coordinates) {
- // it's kind of a bummer that we can't run map() or anything on the ReadableArray
- this.coordinates = new ArrayList<>(coordinates.size());
- for (int i = 0; i < coordinates.size(); i++) {
- ReadableMap coordinate = coordinates.getMap(i);
- this.coordinates.add(i,
- new LatLng(coordinate.getDouble("latitude"), coordinate.getDouble("longitude")));
- }
- if (polygon != null) {
- polygon.setPoints(this.coordinates);
- }
- }
-
- public void setFillColor(int color) {
- this.fillColor = color;
- if (polygon != null) {
- polygon.setFillColor(color);
- }
- }
-
- public void setStrokeColor(int color) {
- this.strokeColor = color;
- if (polygon != null) {
- polygon.setStrokeColor(color);
- }
- }
-
- public void setStrokeWidth(float width) {
- this.strokeWidth = width;
- if (polygon != null) {
- polygon.setStrokeWidth(width);
- }
- }
-
- public void setGeodesic(boolean geodesic) {
- this.geodesic = geodesic;
- if (polygon != null) {
- polygon.setGeodesic(geodesic);
- }
+ if (polygon != null) {
+ polygon.setPoints(this.coordinates);
}
+ }
- public void setZIndex(float zIndex) {
- this.zIndex = zIndex;
- if (polygon != null) {
- polygon.setZIndex(zIndex);
- }
+ public void setFillColor(int color) {
+ this.fillColor = color;
+ if (polygon != null) {
+ polygon.setFillColor(color);
}
+ }
- public PolygonOptions getPolygonOptions() {
- if (polygonOptions == null) {
- polygonOptions = createPolygonOptions();
- }
- return polygonOptions;
+ public void setStrokeColor(int color) {
+ this.strokeColor = color;
+ if (polygon != null) {
+ polygon.setStrokeColor(color);
}
+ }
- private PolygonOptions createPolygonOptions() {
- PolygonOptions options = new PolygonOptions();
- options.addAll(coordinates);
- options.fillColor(fillColor);
- options.strokeColor(strokeColor);
- options.strokeWidth(strokeWidth);
- options.geodesic(geodesic);
- options.zIndex(zIndex);
- return options;
+ public void setStrokeWidth(float width) {
+ this.strokeWidth = width;
+ if (polygon != null) {
+ polygon.setStrokeWidth(width);
}
+ }
- @Override
- public Object getFeature() {
- return polygon;
+ public void setGeodesic(boolean geodesic) {
+ this.geodesic = geodesic;
+ if (polygon != null) {
+ polygon.setGeodesic(geodesic);
}
+ }
- @Override
- public void addToMap(GoogleMap map) {
- polygon = map.addPolygon(getPolygonOptions());
- polygon.setClickable(true);
+ public void setZIndex(float zIndex) {
+ this.zIndex = zIndex;
+ if (polygon != null) {
+ polygon.setZIndex(zIndex);
}
+ }
- @Override
- public void removeFromMap(GoogleMap map) {
- polygon.remove();
+ public PolygonOptions getPolygonOptions() {
+ if (polygonOptions == null) {
+ polygonOptions = createPolygonOptions();
}
+ return polygonOptions;
+ }
+
+ private PolygonOptions createPolygonOptions() {
+ PolygonOptions options = new PolygonOptions();
+ options.addAll(coordinates);
+ options.fillColor(fillColor);
+ options.strokeColor(strokeColor);
+ options.strokeWidth(strokeWidth);
+ options.geodesic(geodesic);
+ options.zIndex(zIndex);
+ return options;
+ }
+
+ @Override
+ public Object getFeature() {
+ return polygon;
+ }
+
+ @Override
+ public void addToMap(GoogleMap map) {
+ polygon = map.addPolygon(getPolygonOptions());
+ polygon.setClickable(true);
+ }
+
+ @Override
+ public void removeFromMap(GoogleMap map) {
+ polygon.remove();
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolygonManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolygonManager.java
index 3eaa0e5508..6f16057585 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolygonManager.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolygonManager.java
@@ -13,71 +13,71 @@
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.annotations.ReactProp;
-import java.util.HashMap;
import java.util.Map;
+
import javax.annotation.Nullable;
public class AirMapPolygonManager extends ViewGroupManager {
- private final DisplayMetrics metrics;
+ private final DisplayMetrics metrics;
- public AirMapPolygonManager(ReactApplicationContext reactContext) {
- super();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- metrics = new DisplayMetrics();
- ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE))
- .getDefaultDisplay()
- .getRealMetrics(metrics);
- } else {
- metrics = reactContext.getResources().getDisplayMetrics();
- }
+ public AirMapPolygonManager(ReactApplicationContext reactContext) {
+ super();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ metrics = new DisplayMetrics();
+ ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay()
+ .getRealMetrics(metrics);
+ } else {
+ metrics = reactContext.getResources().getDisplayMetrics();
}
+ }
- @Override
- public String getName() {
- return "AIRMapPolygon";
- }
+ @Override
+ public String getName() {
+ return "AIRMapPolygon";
+ }
- @Override
- public AirMapPolygon createViewInstance(ThemedReactContext context) {
- return new AirMapPolygon(context);
- }
+ @Override
+ public AirMapPolygon createViewInstance(ThemedReactContext context) {
+ return new AirMapPolygon(context);
+ }
- @ReactProp(name = "coordinates")
- public void setCoordinate(AirMapPolygon view, ReadableArray coordinates) {
- view.setCoordinates(coordinates);
- }
+ @ReactProp(name = "coordinates")
+ public void setCoordinate(AirMapPolygon view, ReadableArray coordinates) {
+ view.setCoordinates(coordinates);
+ }
- @ReactProp(name = "strokeWidth", defaultFloat = 1f)
- public void setStrokeWidth(AirMapPolygon view, float widthInPoints) {
- float widthInScreenPx = metrics.density * widthInPoints; // done for parity with iOS
- view.setStrokeWidth(widthInScreenPx);
- }
+ @ReactProp(name = "strokeWidth", defaultFloat = 1f)
+ public void setStrokeWidth(AirMapPolygon view, float widthInPoints) {
+ float widthInScreenPx = metrics.density * widthInPoints; // done for parity with iOS
+ view.setStrokeWidth(widthInScreenPx);
+ }
- @ReactProp(name = "fillColor", defaultInt = Color.RED, customType = "Color")
- public void setFillColor(AirMapPolygon view, int color) {
- view.setFillColor(color);
- }
+ @ReactProp(name = "fillColor", defaultInt = Color.RED, customType = "Color")
+ public void setFillColor(AirMapPolygon view, int color) {
+ view.setFillColor(color);
+ }
- @ReactProp(name = "strokeColor", defaultInt = Color.RED, customType = "Color")
- public void setStrokeColor(AirMapPolygon view, int color) {
- view.setStrokeColor(color);
- }
+ @ReactProp(name = "strokeColor", defaultInt = Color.RED, customType = "Color")
+ public void setStrokeColor(AirMapPolygon view, int color) {
+ view.setStrokeColor(color);
+ }
- @ReactProp(name = "geodesic", defaultBoolean = false)
- public void setGeodesic(AirMapPolygon view, boolean geodesic) {
- view.setGeodesic(geodesic);
- }
+ @ReactProp(name = "geodesic", defaultBoolean = false)
+ public void setGeodesic(AirMapPolygon view, boolean geodesic) {
+ view.setGeodesic(geodesic);
+ }
- @ReactProp(name = "zIndex", defaultFloat = 1.0f)
- public void setZIndex(AirMapPolygon view, float zIndex) {
- view.setZIndex(zIndex);
- }
+ @ReactProp(name = "zIndex", defaultFloat = 1.0f)
+ public void setZIndex(AirMapPolygon view, float zIndex) {
+ view.setZIndex(zIndex);
+ }
- @Override
- @Nullable
- public Map getExportedCustomDirectEventTypeConstants() {
- return MapBuilder.of(
- "onPress", MapBuilder.of("registrationName", "onPress")
- );
- }
+ @Override
+ @Nullable
+ public Map getExportedCustomDirectEventTypeConstants() {
+ return MapBuilder.of(
+ "onPress", MapBuilder.of("registrationName", "onPress")
+ );
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolyline.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolyline.java
index 9a15fdc99d..488e2972f4 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolyline.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolyline.java
@@ -14,89 +14,89 @@
public class AirMapPolyline extends AirMapFeature {
- private PolylineOptions polylineOptions;
- private Polyline polyline;
-
- private List coordinates;
- private int color;
- private float width;
- private boolean geodesic;
- private float zIndex;
-
- public AirMapPolyline(Context context) {
- super(context);
- }
-
- public void setCoordinates(ReadableArray coordinates) {
- this.coordinates = new ArrayList<>(coordinates.size());
- for (int i = 0; i < coordinates.size(); i++) {
- ReadableMap coordinate = coordinates.getMap(i);
- this.coordinates.add(i,
- new LatLng(coordinate.getDouble("latitude"), coordinate.getDouble("longitude")));
- }
- if (polyline != null) {
- polyline.setPoints(this.coordinates);
- }
- }
-
- public void setColor(int color) {
- this.color = color;
- if (polyline != null) {
- polyline.setColor(color);
- }
+ private PolylineOptions polylineOptions;
+ private Polyline polyline;
+
+ private List coordinates;
+ private int color;
+ private float width;
+ private boolean geodesic;
+ private float zIndex;
+
+ public AirMapPolyline(Context context) {
+ super(context);
+ }
+
+ public void setCoordinates(ReadableArray coordinates) {
+ this.coordinates = new ArrayList<>(coordinates.size());
+ for (int i = 0; i < coordinates.size(); i++) {
+ ReadableMap coordinate = coordinates.getMap(i);
+ this.coordinates.add(i,
+ new LatLng(coordinate.getDouble("latitude"), coordinate.getDouble("longitude")));
}
-
- public void setWidth(float width) {
- this.width = width;
- if (polyline != null) {
- polyline.setWidth(width);
- }
- }
-
- public void setZIndex(float zIndex) {
- this.zIndex = zIndex;
- if (polyline != null) {
- polyline.setZIndex(zIndex);
- }
- }
-
- public void setGeodesic(boolean geodesic) {
- this.geodesic = geodesic;
- if (polyline != null) {
- polyline.setGeodesic(geodesic);
- }
+ if (polyline != null) {
+ polyline.setPoints(this.coordinates);
}
+ }
- public PolylineOptions getPolylineOptions() {
- if (polylineOptions == null) {
- polylineOptions = createPolylineOptions();
- }
- return polylineOptions;
+ public void setColor(int color) {
+ this.color = color;
+ if (polyline != null) {
+ polyline.setColor(color);
}
+ }
- private PolylineOptions createPolylineOptions() {
- PolylineOptions options = new PolylineOptions();
- options.addAll(coordinates);
- options.color(color);
- options.width(width);
- options.geodesic(geodesic);
- options.zIndex(zIndex);
- return options;
+ public void setWidth(float width) {
+ this.width = width;
+ if (polyline != null) {
+ polyline.setWidth(width);
}
+ }
- @Override
- public Object getFeature() {
- return polyline;
+ public void setZIndex(float zIndex) {
+ this.zIndex = zIndex;
+ if (polyline != null) {
+ polyline.setZIndex(zIndex);
}
+ }
- @Override
- public void addToMap(GoogleMap map) {
- polyline = map.addPolyline(getPolylineOptions());
- polyline.setClickable(true);
+ public void setGeodesic(boolean geodesic) {
+ this.geodesic = geodesic;
+ if (polyline != null) {
+ polyline.setGeodesic(geodesic);
}
+ }
- @Override
- public void removeFromMap(GoogleMap map) {
- polyline.remove();
+ public PolylineOptions getPolylineOptions() {
+ if (polylineOptions == null) {
+ polylineOptions = createPolylineOptions();
}
+ return polylineOptions;
+ }
+
+ private PolylineOptions createPolylineOptions() {
+ PolylineOptions options = new PolylineOptions();
+ options.addAll(coordinates);
+ options.color(color);
+ options.width(width);
+ options.geodesic(geodesic);
+ options.zIndex(zIndex);
+ return options;
+ }
+
+ @Override
+ public Object getFeature() {
+ return polyline;
+ }
+
+ @Override
+ public void addToMap(GoogleMap map) {
+ polyline = map.addPolyline(getPolylineOptions());
+ polyline.setClickable(true);
+ }
+
+ @Override
+ public void removeFromMap(GoogleMap map) {
+ polyline.remove();
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolylineManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolylineManager.java
index c5afc61e0f..be80acfbc0 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolylineManager.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapPolylineManager.java
@@ -13,66 +13,66 @@
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.annotations.ReactProp;
-import java.util.HashMap;
import java.util.Map;
+
import javax.annotation.Nullable;
public class AirMapPolylineManager extends ViewGroupManager {
- private final DisplayMetrics metrics;
+ private final DisplayMetrics metrics;
- public AirMapPolylineManager(ReactApplicationContext reactContext) {
- super();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- metrics = new DisplayMetrics();
- ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE))
- .getDefaultDisplay()
- .getRealMetrics(metrics);
- } else {
- metrics = reactContext.getResources().getDisplayMetrics();
- }
+ public AirMapPolylineManager(ReactApplicationContext reactContext) {
+ super();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ metrics = new DisplayMetrics();
+ ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay()
+ .getRealMetrics(metrics);
+ } else {
+ metrics = reactContext.getResources().getDisplayMetrics();
}
+ }
- @Override
- public String getName() {
- return "AIRMapPolyline";
- }
+ @Override
+ public String getName() {
+ return "AIRMapPolyline";
+ }
- @Override
- public AirMapPolyline createViewInstance(ThemedReactContext context) {
- return new AirMapPolyline(context);
- }
+ @Override
+ public AirMapPolyline createViewInstance(ThemedReactContext context) {
+ return new AirMapPolyline(context);
+ }
- @ReactProp(name = "coordinates")
- public void setCoordinate(AirMapPolyline view, ReadableArray coordinates) {
- view.setCoordinates(coordinates);
- }
+ @ReactProp(name = "coordinates")
+ public void setCoordinate(AirMapPolyline view, ReadableArray coordinates) {
+ view.setCoordinates(coordinates);
+ }
- @ReactProp(name = "strokeWidth", defaultFloat = 1f)
- public void setStrokeWidth(AirMapPolyline view, float widthInPoints) {
- float widthInScreenPx = metrics.density * widthInPoints; // done for parity with iOS
- view.setWidth(widthInScreenPx);
- }
+ @ReactProp(name = "strokeWidth", defaultFloat = 1f)
+ public void setStrokeWidth(AirMapPolyline view, float widthInPoints) {
+ float widthInScreenPx = metrics.density * widthInPoints; // done for parity with iOS
+ view.setWidth(widthInScreenPx);
+ }
- @ReactProp(name = "strokeColor", defaultInt = Color.RED, customType = "Color")
- public void setStrokeColor(AirMapPolyline view, int color) {
- view.setColor(color);
- }
+ @ReactProp(name = "strokeColor", defaultInt = Color.RED, customType = "Color")
+ public void setStrokeColor(AirMapPolyline view, int color) {
+ view.setColor(color);
+ }
- @ReactProp(name = "geodesic", defaultBoolean = false)
- public void setGeodesic(AirMapPolyline view, boolean geodesic) {
- view.setGeodesic(geodesic);
- }
+ @ReactProp(name = "geodesic", defaultBoolean = false)
+ public void setGeodesic(AirMapPolyline view, boolean geodesic) {
+ view.setGeodesic(geodesic);
+ }
- @ReactProp(name = "zIndex", defaultFloat = 1.0f)
- public void setZIndex(AirMapPolyline view, float zIndex) {
- view.setZIndex(zIndex);
- }
+ @ReactProp(name = "zIndex", defaultFloat = 1.0f)
+ public void setZIndex(AirMapPolyline view, float zIndex) {
+ view.setZIndex(zIndex);
+ }
- @Override
- @Nullable
- public Map getExportedCustomDirectEventTypeConstants() {
- return MapBuilder.of(
- "onPress", MapBuilder.of("registrationName", "onPress")
- );
- }
+ @Override
+ @Nullable
+ public Map getExportedCustomDirectEventTypeConstants() {
+ return MapBuilder.of(
+ "onPress", MapBuilder.of("registrationName", "onPress")
+ );
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapUrlTile.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapUrlTile.java
index 691c2fd3c9..ae51a63b4d 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapUrlTile.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapUrlTile.java
@@ -12,89 +12,90 @@
public class AirMapUrlTile extends AirMapFeature {
- class AIRMapUrlTileProvider extends UrlTileProvider
- {
- private String urlTemplate;
- public AIRMapUrlTileProvider(int width, int height, String urlTemplate) {
- super(width, height);
- this.urlTemplate = urlTemplate;
- }
- @Override
- public synchronized URL getTileUrl(int x, int y, int zoom) {
-
- String s = this.urlTemplate
- .replace("{x}", Integer.toString(x))
- .replace("{y}", Integer.toString(y))
- .replace("{z}", Integer.toString(zoom));
- URL url = null;
- try {
- url = new URL(s);
- } catch (MalformedURLException e) {
- throw new AssertionError(e);
- }
- return url;
- }
-
- public void setUrlTemplate(String urlTemplate) {
- this.urlTemplate = urlTemplate;
- }
- }
-
- private TileOverlayOptions tileOverlayOptions;
- private TileOverlay tileOverlay;
- private AIRMapUrlTileProvider tileProvider;
-
+ class AIRMapUrlTileProvider extends UrlTileProvider {
private String urlTemplate;
- private float zIndex;
- public AirMapUrlTile(Context context) {
- super(context);
+ public AIRMapUrlTileProvider(int width, int height, String urlTemplate) {
+ super(width, height);
+ this.urlTemplate = urlTemplate;
}
- public void setUrlTemplate(String urlTemplate) {
- this.urlTemplate = urlTemplate;
- if (tileProvider != null) {
- tileProvider.setUrlTemplate(urlTemplate);
- }
- if (tileOverlay != null) {
- tileOverlay.clearTileCache();
- }
+ @Override
+ public synchronized URL getTileUrl(int x, int y, int zoom) {
+
+ String s = this.urlTemplate
+ .replace("{x}", Integer.toString(x))
+ .replace("{y}", Integer.toString(y))
+ .replace("{z}", Integer.toString(zoom));
+ URL url = null;
+ try {
+ url = new URL(s);
+ } catch (MalformedURLException e) {
+ throw new AssertionError(e);
+ }
+ return url;
}
- public void setZIndex(float zIndex) {
- this.zIndex = zIndex;
- if (tileOverlay != null) {
- tileOverlay.setZIndex(zIndex);
- }
+ public void setUrlTemplate(String urlTemplate) {
+ this.urlTemplate = urlTemplate;
}
+ }
- public TileOverlayOptions getTileOverlayOptions() {
- if (tileOverlayOptions == null) {
- tileOverlayOptions = createTileOverlayOptions();
- }
- return tileOverlayOptions;
- }
+ private TileOverlayOptions tileOverlayOptions;
+ private TileOverlay tileOverlay;
+ private AIRMapUrlTileProvider tileProvider;
- private TileOverlayOptions createTileOverlayOptions() {
- TileOverlayOptions options = new TileOverlayOptions();
- options.zIndex(zIndex);
- this.tileProvider = new AIRMapUrlTileProvider(256, 256, this.urlTemplate);
- options.tileProvider(this.tileProvider);
- return options;
- }
+ private String urlTemplate;
+ private float zIndex;
- @Override
- public Object getFeature() {
- return tileOverlay;
+ public AirMapUrlTile(Context context) {
+ super(context);
+ }
+
+ public void setUrlTemplate(String urlTemplate) {
+ this.urlTemplate = urlTemplate;
+ if (tileProvider != null) {
+ tileProvider.setUrlTemplate(urlTemplate);
}
+ if (tileOverlay != null) {
+ tileOverlay.clearTileCache();
+ }
+ }
- @Override
- public void addToMap(GoogleMap map) {
- this.tileOverlay = map.addTileOverlay(getTileOverlayOptions());
+ public void setZIndex(float zIndex) {
+ this.zIndex = zIndex;
+ if (tileOverlay != null) {
+ tileOverlay.setZIndex(zIndex);
}
+ }
- @Override
- public void removeFromMap(GoogleMap map) {
- tileOverlay.remove();
+ public TileOverlayOptions getTileOverlayOptions() {
+ if (tileOverlayOptions == null) {
+ tileOverlayOptions = createTileOverlayOptions();
}
+ return tileOverlayOptions;
+ }
+
+ private TileOverlayOptions createTileOverlayOptions() {
+ TileOverlayOptions options = new TileOverlayOptions();
+ options.zIndex(zIndex);
+ this.tileProvider = new AIRMapUrlTileProvider(256, 256, this.urlTemplate);
+ options.tileProvider(this.tileProvider);
+ return options;
+ }
+
+ @Override
+ public Object getFeature() {
+ return tileOverlay;
+ }
+
+ @Override
+ public void addToMap(GoogleMap map) {
+ this.tileOverlay = map.addTileOverlay(getTileOverlayOptions());
+ }
+
+ @Override
+ public void removeFromMap(GoogleMap map) {
+ tileOverlay.remove();
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapUrlTileManager.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapUrlTileManager.java
index 7ea0726dc8..68bf07342c 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapUrlTileManager.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapUrlTileManager.java
@@ -11,38 +11,38 @@
import com.facebook.react.uimanager.annotations.ReactProp;
public class AirMapUrlTileManager extends ViewGroupManager {
- private DisplayMetrics metrics;
-
- public AirMapUrlTileManager(ReactApplicationContext reactContext) {
- super();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- metrics = new DisplayMetrics();
- ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE))
- .getDefaultDisplay()
- .getRealMetrics(metrics);
- } else {
- metrics = reactContext.getResources().getDisplayMetrics();
- }
- }
-
- @Override
- public String getName() {
- return "AIRMapUrlTile";
- }
-
- @Override
- public AirMapUrlTile createViewInstance(ThemedReactContext context) {
- return new AirMapUrlTile(context);
- }
-
- @ReactProp(name = "urlTemplate")
- public void setUrlTemplate(AirMapUrlTile view, String urlTemplate) {
- view.setUrlTemplate(urlTemplate);
- }
-
- @ReactProp(name = "zIndex", defaultFloat = -1.0f)
- public void setZIndex(AirMapUrlTile view, float zIndex) {
- view.setZIndex(zIndex);
+ private DisplayMetrics metrics;
+
+ public AirMapUrlTileManager(ReactApplicationContext reactContext) {
+ super();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ metrics = new DisplayMetrics();
+ ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay()
+ .getRealMetrics(metrics);
+ } else {
+ metrics = reactContext.getResources().getDisplayMetrics();
}
+ }
+
+ @Override
+ public String getName() {
+ return "AIRMapUrlTile";
+ }
+
+ @Override
+ public AirMapUrlTile createViewInstance(ThemedReactContext context) {
+ return new AirMapUrlTile(context);
+ }
+
+ @ReactProp(name = "urlTemplate")
+ public void setUrlTemplate(AirMapUrlTile view, String urlTemplate) {
+ view.setUrlTemplate(urlTemplate);
+ }
+
+ @ReactProp(name = "zIndex", defaultFloat = -1.0f)
+ public void setZIndex(AirMapUrlTile view, float zIndex) {
+ view.setZIndex(zIndex);
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java
index 772627e4a6..836fe0e35b 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java
@@ -1,6 +1,5 @@
package com.airbnb.android.react.maps;
-import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
@@ -22,6 +21,7 @@
import android.widget.RelativeLayout;
import com.facebook.react.bridge.LifecycleEventListener;
+import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
@@ -53,797 +53,840 @@
import static android.support.v4.content.PermissionChecker.checkSelfPermission;
public class AirMapView extends MapView implements GoogleMap.InfoWindowAdapter,
- GoogleMap.OnMarkerDragListener, OnMapReadyCallback {
- public GoogleMap map;
- private ProgressBar mapLoadingProgressBar;
- private RelativeLayout mapLoadingLayout;
- private ImageView cacheImageView;
- private Boolean isMapLoaded = false;
- private Integer loadingBackgroundColor = null;
- private Integer loadingIndicatorColor = null;
- private final int baseMapPadding = 50;
-
- private LatLngBounds boundsToMove;
- private boolean showUserLocation = false;
- private boolean isMonitoringRegion = false;
- private boolean isTouchDown = false;
- private boolean handlePanDrag = false;
- private boolean moveOnMarkerPress = true;
- private boolean cacheEnabled = false;
-
- private static final String[] PERMISSIONS = new String[] {
- "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION"};
-
- private final List features = new ArrayList<>();
- private final Map markerMap = new HashMap<>();
- private final Map polylineMap = new HashMap<>();
- private final Map polygonMap = new HashMap<>();
- private final ScaleGestureDetector scaleDetector;
- private final GestureDetectorCompat gestureDetector;
- private final AirMapManager manager;
- private LifecycleEventListener lifecycleListener;
- private boolean paused = false;
- private boolean destroyed = false;
- private final ThemedReactContext context;
- private final EventDispatcher eventDispatcher;
-
- private static boolean contextHasBug(Context context) {
- return context == null ||
- context.getResources() == null ||
- context.getResources().getConfiguration() == null;
- }
-
- // We do this to fix this bug:
- // https://github.com/airbnb/react-native-maps/issues/271
- //
- // which conflicts with another bug regarding the passed in context:
- // https://github.com/airbnb/react-native-maps/issues/1147
- //
- // Doing this allows us to avoid both bugs.
- private static Context getNonBuggyContext(ThemedReactContext reactContext) {
- Context superContext = reactContext;
-
- if (contextHasBug(superContext)) {
- // we have the bug! let's try to find a better context to use
- if (!contextHasBug(reactContext.getCurrentActivity())) {
- superContext = reactContext.getCurrentActivity();
- } else if (!contextHasBug(reactContext.getApplicationContext())) {
- superContext = reactContext.getApplicationContext();
- } else {
- // ¯\_(ツ)_/¯
- }
- }
- return superContext;
- }
-
- public AirMapView(ThemedReactContext reactContext, AirMapManager manager,
- GoogleMapOptions googleMapOptions) {
- super(getNonBuggyContext(reactContext), googleMapOptions);
-
- this.manager = manager;
- this.context = reactContext;
-
- super.onCreate(null);
- // TODO(lmr): what about onStart????
- super.onResume();
- super.getMapAsync(this);
-
- final AirMapView view = this;
- scaleDetector =
- new ScaleGestureDetector(reactContext, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
- @Override
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- view.startMonitoringRegion();
- return true; // stop recording this gesture. let mapview handle it.
- }
- });
-
- gestureDetector =
- new GestureDetectorCompat(reactContext, new GestureDetector.SimpleOnGestureListener() {
- @Override
- public boolean onDoubleTap(MotionEvent e) {
- view.startMonitoringRegion();
- return false;
- }
-
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
- float distanceY) {
- if (handlePanDrag) {
- onPanDrag(e2);
- }
- view.startMonitoringRegion();
- return false;
- }
- });
-
- this.addOnLayoutChangeListener(new OnLayoutChangeListener() {
- @Override public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if (!paused) {
- AirMapView.this.cacheView();
- }
- }
- });
-
- eventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
- }
-
- @Override
- public void onMapReady(final GoogleMap map) {
- if (destroyed) {
- return;
- }
- this.map = map;
- this.map.setInfoWindowAdapter(this);
- this.map.setOnMarkerDragListener(this);
-
- manager.pushEvent(context, this, "onMapReady", new WritableNativeMap());
-
- final AirMapView view = this;
-
- map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
- @Override
- public boolean onMarkerClick(Marker marker) {
- WritableMap event;
- AirMapMarker airMapMarker = markerMap.get(marker);
-
- event = makeClickEventData(marker.getPosition());
- event.putString("action", "marker-press");
- event.putString("id", airMapMarker.getIdentifier());
- manager.pushEvent(context, view, "onMarkerPress", event);
-
- event = makeClickEventData(marker.getPosition());
- event.putString("action", "marker-press");
- event.putString("id", airMapMarker.getIdentifier());
- manager.pushEvent(context, markerMap.get(marker), "onPress", event);
-
- // Return false to open the callout info window and center on the marker
- // https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap.OnMarkerClickListener
- if (view.moveOnMarkerPress) {
- return false;
- } else {
- marker.showInfoWindow();
- return true;
- }
- }
- });
-
- map.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() {
- @Override
- public void onPolygonClick(Polygon polygon) {
- WritableMap event = makeClickEventData(polygon.getPoints().get(0));
- event.putString("action", "polygon-press");
- manager.pushEvent(context, polygonMap.get(polygon), "onPress", event);
- }
- });
-
- map.setOnPolylineClickListener(new GoogleMap.OnPolylineClickListener() {
- @Override
- public void onPolylineClick(Polyline polyline) {
- WritableMap event = makeClickEventData(polyline.getPoints().get(0));
- event.putString("action", "polyline-press");
- manager.pushEvent(context, polylineMap.get(polyline), "onPress", event);
- }
- });
-
- map.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
- @Override
- public void onInfoWindowClick(Marker marker) {
- WritableMap event;
-
- event = makeClickEventData(marker.getPosition());
- event.putString("action", "callout-press");
- manager.pushEvent(context, view, "onCalloutPress", event);
-
- event = makeClickEventData(marker.getPosition());
- event.putString("action", "callout-press");
- AirMapMarker markerView = markerMap.get(marker);
- manager.pushEvent(context, markerView, "onCalloutPress", event);
-
- event = makeClickEventData(marker.getPosition());
- event.putString("action", "callout-press");
- AirMapCallout infoWindow = markerView.getCalloutView();
- if (infoWindow != null) manager.pushEvent(context, infoWindow, "onPress", event);
- }
- });
-
- map.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
- @Override
- public void onMapClick(LatLng point) {
- WritableMap event = makeClickEventData(point);
- event.putString("action", "press");
- manager.pushEvent(context, view, "onPress", event);
- }
- });
-
- map.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
- @Override
- public void onMapLongClick(LatLng point) {
- WritableMap event = makeClickEventData(point);
- event.putString("action", "long-press");
- manager.pushEvent(context, view, "onLongPress", makeClickEventData(point));
- }
- });
-
- map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
- @Override
- public void onCameraChange(CameraPosition position) {
- LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds;
- LatLng center = position.target;
- lastBoundsEmitted = bounds;
- eventDispatcher.dispatchEvent(new RegionChangeEvent(getId(), bounds, center, isTouchDown));
- view.stopMonitoringRegion();
- }
- });
-
- map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
- @Override public void onMapLoaded() {
- isMapLoaded = true;
- AirMapView.this.cacheView();
- }
- });
-
- // We need to be sure to disable location-tracking when app enters background, in-case some
- // other module
- // has acquired a wake-lock and is controlling location-updates, otherwise, location-manager
- // will be left
- // updating location constantly, killing the battery, even though some other location-mgmt
- // module may
- // desire to shut-down location-services.
- lifecycleListener = new LifecycleEventListener() {
- @Override
- public void onHostResume() {
- if (hasPermissions()) {
- //noinspection MissingPermission
- map.setMyLocationEnabled(showUserLocation);
+ GoogleMap.OnMarkerDragListener, OnMapReadyCallback {
+ public GoogleMap map;
+ private ProgressBar mapLoadingProgressBar;
+ private RelativeLayout mapLoadingLayout;
+ private ImageView cacheImageView;
+ private Boolean isMapLoaded = false;
+ private Integer loadingBackgroundColor = null;
+ private Integer loadingIndicatorColor = null;
+ private final int baseMapPadding = 50;
+
+ private LatLngBounds boundsToMove;
+ private boolean showUserLocation = false;
+ private boolean isMonitoringRegion = false;
+ private boolean isTouchDown = false;
+ private boolean handlePanDrag = false;
+ private boolean moveOnMarkerPress = true;
+ private boolean cacheEnabled = false;
+ private boolean initialRegionSet = false;
+
+ private static final String[] PERMISSIONS = new String[]{
+ "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION"};
+
+ private final List features = new ArrayList<>();
+ private final Map markerMap = new HashMap<>();
+ private final Map polylineMap = new HashMap<>();
+ private final Map polygonMap = new HashMap<>();
+ private final ScaleGestureDetector scaleDetector;
+ private final GestureDetectorCompat gestureDetector;
+ private final AirMapManager manager;
+ private LifecycleEventListener lifecycleListener;
+ private boolean paused = false;
+ private boolean destroyed = false;
+ private final ThemedReactContext context;
+ private final EventDispatcher eventDispatcher;
+
+ private static boolean contextHasBug(Context context) {
+ return context == null ||
+ context.getResources() == null ||
+ context.getResources().getConfiguration() == null;
+ }
+
+ // We do this to fix this bug:
+ // https://github.com/airbnb/react-native-maps/issues/271
+ //
+ // which conflicts with another bug regarding the passed in context:
+ // https://github.com/airbnb/react-native-maps/issues/1147
+ //
+ // Doing this allows us to avoid both bugs.
+ private static Context getNonBuggyContext(ThemedReactContext reactContext,
+ ReactApplicationContext appContext) {
+ Context superContext = reactContext;
+ if (!contextHasBug(appContext.getCurrentActivity())) {
+ superContext = appContext.getCurrentActivity();
+ } else if (contextHasBug(superContext)) {
+ // we have the bug! let's try to find a better context to use
+ if (!contextHasBug(reactContext.getCurrentActivity())) {
+ superContext = reactContext.getCurrentActivity();
+ } else if (!contextHasBug(reactContext.getApplicationContext())) {
+ superContext = reactContext.getApplicationContext();
+ } else {
+ // ¯\_(ツ)_/¯
+ }
+ }
+ return superContext;
+ }
+
+ public AirMapView(ThemedReactContext reactContext, ReactApplicationContext appContext,
+ AirMapManager manager,
+ GoogleMapOptions googleMapOptions) {
+ super(getNonBuggyContext(reactContext, appContext), googleMapOptions);
+
+ this.manager = manager;
+ this.context = reactContext;
+
+ super.onCreate(null);
+ // TODO(lmr): what about onStart????
+ super.onResume();
+ super.getMapAsync(this);
+
+ final AirMapView view = this;
+ scaleDetector =
+ new ScaleGestureDetector(reactContext,
+ new ScaleGestureDetector.SimpleOnScaleGestureListener() {
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ view.startMonitoringRegion();
+ return true; // stop recording this gesture. let mapview handle it.
+ }
+ });
+
+ gestureDetector =
+ new GestureDetectorCompat(reactContext, new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ view.startMonitoringRegion();
+ return false;
}
- synchronized (AirMapView.this) {
- AirMapView.this.onResume();
- paused = false;
- }
- }
- @Override
- public void onHostPause() {
- if (hasPermissions()) {
- //noinspection MissingPermission
- map.setMyLocationEnabled(false);
- }
- synchronized (AirMapView.this) {
- AirMapView.this.onPause();
- paused = true;
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ if (handlePanDrag) {
+ onPanDrag(e2);
}
- }
-
- @Override
- public void onHostDestroy() {
- AirMapView.this.doDestroy();
- }
- };
-
- context.addLifecycleEventListener(lifecycleListener);
- }
-
- private boolean hasPermissions() {
- return checkSelfPermission(getContext(), PERMISSIONS[0]) == PackageManager.PERMISSION_GRANTED ||
- checkSelfPermission(getContext(), PERMISSIONS[1]) == PackageManager.PERMISSION_GRANTED;
- }
-
-
-
- /*
- onDestroy is final method so I can't override it.
- */
- public synchronized void doDestroy() {
- if (destroyed) {
- return;
- }
- destroyed = true;
+ view.startMonitoringRegion();
+ return false;
+ }
+ });
- if (lifecycleListener != null && context != null) {
- context.removeLifecycleEventListener(lifecycleListener);
- lifecycleListener = null;
- }
+ this.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (!paused) {
- onPause();
- paused = true;
- }
- onDestroy();
- }
-
- public void setRegion(ReadableMap region) {
- if (region == null) return;
-
- Double lng = region.getDouble("longitude");
- Double lat = region.getDouble("latitude");
- Double lngDelta = region.getDouble("longitudeDelta");
- Double latDelta = region.getDouble("latitudeDelta");
- LatLngBounds bounds = new LatLngBounds(
- new LatLng(lat - latDelta / 2, lng - lngDelta / 2), // southwest
- new LatLng(lat + latDelta / 2, lng + lngDelta / 2) // northeast
- );
- if (super.getHeight() <= 0 || super.getWidth() <= 0) {
- // in this case, our map has not been laid out yet, so we save the bounds in a local
- // variable, and make a guess of zoomLevel 10. Not to worry, though: as soon as layout
- // occurs, we will move the camera to the saved bounds. Note that if we tried to move
- // to the bounds now, it would trigger an exception.
- map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lng), 10));
- boundsToMove = bounds;
- } else {
- map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
- boundsToMove = null;
- }
- }
-
- public void setShowsUserLocation(boolean showUserLocation) {
- this.showUserLocation = showUserLocation; // hold onto this for lifecycle handling
- if (hasPermissions()) {
- //noinspection MissingPermission
- map.setMyLocationEnabled(showUserLocation);
- }
- }
-
- public void setShowsMyLocationButton(boolean showMyLocationButton) {
- if (hasPermissions()) {
- map.getUiSettings().setMyLocationButtonEnabled(showMyLocationButton);
- }
- }
-
- public void setToolbarEnabled(boolean toolbarEnabled) {
- if (hasPermissions()) {
- map.getUiSettings().setMapToolbarEnabled(toolbarEnabled);
- }
- }
-
- public void setCacheEnabled(boolean cacheEnabled) {
- this.cacheEnabled = cacheEnabled;
- this.cacheView();
- }
-
- public void enableMapLoading(boolean loadingEnabled) {
- if (loadingEnabled && !this.isMapLoaded) {
- this.getMapLoadingLayoutView().setVisibility(View.VISIBLE);
- }
- }
-
- public void setMoveOnMarkerPress(boolean moveOnPress) {
- this.moveOnMarkerPress = moveOnPress;
- }
-
- public void setLoadingBackgroundColor(Integer loadingBackgroundColor) {
- this.loadingBackgroundColor = loadingBackgroundColor;
-
- if (this.mapLoadingLayout != null) {
- if (loadingBackgroundColor == null) {
- this.mapLoadingLayout.setBackgroundColor(Color.WHITE);
- } else {
- this.mapLoadingLayout.setBackgroundColor(this.loadingBackgroundColor);
- }
- }
- }
-
- public void setLoadingIndicatorColor(Integer loadingIndicatorColor) {
- this.loadingIndicatorColor = loadingIndicatorColor;
- if (this.mapLoadingProgressBar != null) {
- Integer color = loadingIndicatorColor;
- if (color == null) {
- color = Color.parseColor("#606060");
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- ColorStateList progressTintList = ColorStateList.valueOf(loadingIndicatorColor);
- ColorStateList secondaryProgressTintList = ColorStateList.valueOf(loadingIndicatorColor);
- ColorStateList indeterminateTintList = ColorStateList.valueOf(loadingIndicatorColor);
-
- this.mapLoadingProgressBar.setProgressTintList(progressTintList);
- this.mapLoadingProgressBar.setSecondaryProgressTintList(secondaryProgressTintList);
- this.mapLoadingProgressBar.setIndeterminateTintList(indeterminateTintList);
- } else {
- PorterDuff.Mode mode = PorterDuff.Mode.SRC_IN;
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
- mode = PorterDuff.Mode.MULTIPLY;
- }
- if (this.mapLoadingProgressBar.getIndeterminateDrawable() != null)
- this.mapLoadingProgressBar.getIndeterminateDrawable().setColorFilter(color, mode);
- if (this.mapLoadingProgressBar.getProgressDrawable() != null)
- this.mapLoadingProgressBar.getProgressDrawable().setColorFilter(color, mode);
- }
- }
- }
-
- public void setHandlePanDrag(boolean handlePanDrag) {
- this.handlePanDrag = handlePanDrag;
- }
-
- public void addFeature(View child, int index) {
- // Our desired API is to pass up annotations/overlays as children to the mapview component.
- // This is where we intercept them and do the appropriate underlying mapview action.
- if (child instanceof AirMapMarker) {
- AirMapMarker annotation = (AirMapMarker) child;
- annotation.addToMap(map);
- features.add(index, annotation);
- Marker marker = (Marker) annotation.getFeature();
- markerMap.put(marker, annotation);
- } else if (child instanceof AirMapPolyline) {
- AirMapPolyline polylineView = (AirMapPolyline) child;
- polylineView.addToMap(map);
- features.add(index, polylineView);
- Polyline polyline = (Polyline) polylineView.getFeature();
- polylineMap.put(polyline, polylineView);
- } else if (child instanceof AirMapPolygon) {
- AirMapPolygon polygonView = (AirMapPolygon) child;
- polygonView.addToMap(map);
- features.add(index, polygonView);
- Polygon polygon = (Polygon) polygonView.getFeature();
- polygonMap.put(polygon, polygonView);
- } else if (child instanceof AirMapCircle) {
- AirMapCircle circleView = (AirMapCircle) child;
- circleView.addToMap(map);
- features.add(index, circleView);
- } else if (child instanceof AirMapUrlTile) {
- AirMapUrlTile urlTileView = (AirMapUrlTile) child;
- urlTileView.addToMap(map);
- features.add(index, urlTileView);
- } else {
- ViewGroup children = (ViewGroup) child;
- for (int i = 0; i < children.getChildCount(); i++) {
- addFeature(children.getChildAt(i), index);
- }
- }
- }
-
- public int getFeatureCount() {
- return features.size();
- }
-
- public View getFeatureAt(int index) {
- return features.get(index);
- }
-
- public void removeFeatureAt(int index) {
- AirMapFeature feature = features.remove(index);
- if (feature instanceof AirMapMarker) {
- markerMap.remove(feature.getFeature());
+ AirMapView.this.cacheView();
}
- feature.removeFromMap(map);
- }
-
- public WritableMap makeClickEventData(LatLng point) {
- WritableMap event = new WritableNativeMap();
-
- WritableMap coordinate = new WritableNativeMap();
- coordinate.putDouble("latitude", point.latitude);
- coordinate.putDouble("longitude", point.longitude);
- event.putMap("coordinate", coordinate);
-
- Projection projection = map.getProjection();
- Point screenPoint = projection.toScreenLocation(point);
-
- WritableMap position = new WritableNativeMap();
- position.putDouble("x", screenPoint.x);
- position.putDouble("y", screenPoint.y);
- event.putMap("position", position);
-
- return event;
- }
-
- public void updateExtraData(Object extraData) {
- // if boundsToMove is not null, we now have the MapView's width/height, so we can apply
- // a proper camera move
- if (boundsToMove != null) {
- HashMap data = (HashMap) extraData;
- float width = data.get("width");
- float height = data.get("height");
- map.moveCamera(
- CameraUpdateFactory.newLatLngBounds(
- boundsToMove,
- (int) width,
- (int) height,
- 0
- )
- );
- boundsToMove = null;
- }
- }
+ }
+ });
- public void animateToRegion(LatLngBounds bounds, int duration) {
- if (map != null) {
- startMonitoringRegion();
- map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0), duration, null);
- }
- }
+ eventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
+ }
- public void animateToCoordinate(LatLng coordinate, int duration) {
- if (map != null) {
- startMonitoringRegion();
- map.animateCamera(CameraUpdateFactory.newLatLng(coordinate), duration, null);
- }
+ @Override
+ public void onMapReady(final GoogleMap map) {
+ if (destroyed) {
+ return;
}
+ this.map = map;
+ this.map.setInfoWindowAdapter(this);
+ this.map.setOnMarkerDragListener(this);
- public void fitToElements(boolean animated) {
- LatLngBounds.Builder builder = new LatLngBounds.Builder();
+ manager.pushEvent(context, this, "onMapReady", new WritableNativeMap());
- boolean addedPosition = false;
+ final AirMapView view = this;
- for (AirMapFeature feature : features) {
- if (feature instanceof AirMapMarker) {
- Marker marker = (Marker) feature.getFeature();
- builder.include(marker.getPosition());
- addedPosition = true;
- }
- // TODO(lmr): may want to include shapes / etc.
- }
- if (addedPosition) {
- LatLngBounds bounds = builder.build();
- CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, baseMapPadding);
- if (animated) {
- startMonitoringRegion();
- map.animateCamera(cu);
- } else {
- map.moveCamera(cu);
- }
- }
- }
-
- public void fitToSuppliedMarkers(ReadableArray markerIDsArray, boolean animated) {
- LatLngBounds.Builder builder = new LatLngBounds.Builder();
-
- String[] markerIDs = new String[markerIDsArray.size()];
- for (int i = 0; i < markerIDsArray.size(); i++) {
- markerIDs[i] = markerIDsArray.getString(i);
- }
-
- boolean addedPosition = false;
-
- List markerIDList = Arrays.asList(markerIDs);
-
- for (AirMapFeature feature : features) {
- if (feature instanceof AirMapMarker) {
- String identifier = ((AirMapMarker)feature).getIdentifier();
- Marker marker = (Marker)feature.getFeature();
- if (markerIDList.contains(identifier)) {
- builder.include(marker.getPosition());
- addedPosition = true;
- }
- }
- }
-
- if (addedPosition) {
- LatLngBounds bounds = builder.build();
- CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, baseMapPadding);
- if (animated) {
- startMonitoringRegion();
- map.animateCamera(cu);
- } else {
- map.moveCamera(cu);
- }
- }
- }
+ map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
+ @Override
+ public boolean onMarkerClick(Marker marker) {
+ WritableMap event;
+ AirMapMarker airMapMarker = markerMap.get(marker);
- public void fitToCoordinates(ReadableArray coordinatesArray, ReadableMap edgePadding, boolean animated) {
- LatLngBounds.Builder builder = new LatLngBounds.Builder();
-
- for (int i = 0; i < coordinatesArray.size(); i++) {
- ReadableMap latLng = coordinatesArray.getMap(i);
- Double lat = latLng.getDouble("latitude");
- Double lng = latLng.getDouble("longitude");
- builder.include(new LatLng(lat, lng));
- }
-
- LatLngBounds bounds = builder.build();
- CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, baseMapPadding);
-
- if (edgePadding != null) {
- map.setPadding(edgePadding.getInt("left"), edgePadding.getInt("top"), edgePadding.getInt("right"), edgePadding.getInt("bottom"));
- }
+ event = makeClickEventData(marker.getPosition());
+ event.putString("action", "marker-press");
+ event.putString("id", airMapMarker.getIdentifier());
+ manager.pushEvent(context, view, "onMarkerPress", event);
- if (animated) {
- startMonitoringRegion();
- map.animateCamera(cu);
+ event = makeClickEventData(marker.getPosition());
+ event.putString("action", "marker-press");
+ event.putString("id", airMapMarker.getIdentifier());
+ manager.pushEvent(context, markerMap.get(marker), "onPress", event);
+
+ // Return false to open the callout info window and center on the marker
+ // https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap
+ // .OnMarkerClickListener
+ if (view.moveOnMarkerPress) {
+ return false;
} else {
- map.moveCamera(cu);
- }
- map.setPadding(0, 0, 0, 0); // Without this, the Google logo is moved up by the value of edgePadding.bottom
- }
-
- // InfoWindowAdapter interface
-
- @Override
- public View getInfoWindow(Marker marker) {
- AirMapMarker markerView = markerMap.get(marker);
- return markerView.getCallout();
- }
-
- @Override
- public View getInfoContents(Marker marker) {
- AirMapMarker markerView = markerMap.get(marker);
- return markerView.getInfoContents();
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- scaleDetector.onTouchEvent(ev);
- gestureDetector.onTouchEvent(ev);
-
- int action = MotionEventCompat.getActionMasked(ev);
-
- switch (action) {
- case (MotionEvent.ACTION_DOWN):
- this.getParent().requestDisallowInterceptTouchEvent(
- map != null && map.getUiSettings().isScrollGesturesEnabled());
- isTouchDown = true;
- break;
- case (MotionEvent.ACTION_MOVE):
- startMonitoringRegion();
- break;
- case (MotionEvent.ACTION_UP):
- // Clear this regardless, since isScrollGesturesEnabled() may have been updated
- this.getParent().requestDisallowInterceptTouchEvent(false);
- isTouchDown = false;
- break;
- }
- super.dispatchTouchEvent(ev);
- return true;
- }
-
- // Timer Implementation
-
- public void startMonitoringRegion() {
- if (isMonitoringRegion) return;
- timerHandler.postDelayed(timerRunnable, 100);
- isMonitoringRegion = true;
- }
-
- public void stopMonitoringRegion() {
- if (!isMonitoringRegion) return;
- timerHandler.removeCallbacks(timerRunnable);
- isMonitoringRegion = false;
- }
-
- private LatLngBounds lastBoundsEmitted;
-
- Handler timerHandler = new Handler();
- Runnable timerRunnable = new Runnable() {
-
- @Override
- public void run() {
-
- Projection projection = map.getProjection();
- VisibleRegion region = (projection != null) ? projection.getVisibleRegion() : null;
- LatLngBounds bounds = (region != null) ? region.latLngBounds : null;
-
- if ((bounds != null) &&
- (lastBoundsEmitted == null || LatLngBoundsUtils.BoundsAreDifferent(bounds, lastBoundsEmitted))) {
- LatLng center = map.getCameraPosition().target;
- lastBoundsEmitted = bounds;
- eventDispatcher.dispatchEvent(new RegionChangeEvent(getId(), bounds, center, true));
- }
+ marker.showInfoWindow();
+ return true;
+ }
+ }
+ });
+
+ map.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() {
+ @Override
+ public void onPolygonClick(Polygon polygon) {
+ WritableMap event = makeClickEventData(polygon.getPoints().get(0));
+ event.putString("action", "polygon-press");
+ manager.pushEvent(context, polygonMap.get(polygon), "onPress", event);
+ }
+ });
+
+ map.setOnPolylineClickListener(new GoogleMap.OnPolylineClickListener() {
+ @Override
+ public void onPolylineClick(Polyline polyline) {
+ WritableMap event = makeClickEventData(polyline.getPoints().get(0));
+ event.putString("action", "polyline-press");
+ manager.pushEvent(context, polylineMap.get(polyline), "onPress", event);
+ }
+ });
+
+ map.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
+ @Override
+ public void onInfoWindowClick(Marker marker) {
+ WritableMap event;
- timerHandler.postDelayed(this, 100);
- }
- };
-
- @Override
- public void onMarkerDragStart(Marker marker) {
- WritableMap event = makeClickEventData(marker.getPosition());
- manager.pushEvent(context, this, "onMarkerDragStart", event);
-
- AirMapMarker markerView = markerMap.get(marker);
event = makeClickEventData(marker.getPosition());
- manager.pushEvent(context, markerView, "onDragStart", event);
- }
+ event.putString("action", "callout-press");
+ manager.pushEvent(context, view, "onCalloutPress", event);
- @Override
- public void onMarkerDrag(Marker marker) {
- WritableMap event = makeClickEventData(marker.getPosition());
- manager.pushEvent(context, this, "onMarkerDrag", event);
-
- AirMapMarker markerView = markerMap.get(marker);
event = makeClickEventData(marker.getPosition());
- manager.pushEvent(context, markerView, "onDrag", event);
- }
-
- @Override
- public void onMarkerDragEnd(Marker marker) {
- WritableMap event = makeClickEventData(marker.getPosition());
- manager.pushEvent(context, this, "onMarkerDragEnd", event);
-
+ event.putString("action", "callout-press");
AirMapMarker markerView = markerMap.get(marker);
- event = makeClickEventData(marker.getPosition());
- manager.pushEvent(context, markerView, "onDragEnd", event);
- }
+ manager.pushEvent(context, markerView, "onCalloutPress", event);
- private ProgressBar getMapLoadingProgressBar() {
- if (this.mapLoadingProgressBar == null) {
- this.mapLoadingProgressBar = new ProgressBar(getContext());
- this.mapLoadingProgressBar.setIndeterminate(true);
+ event = makeClickEventData(marker.getPosition());
+ event.putString("action", "callout-press");
+ AirMapCallout infoWindow = markerView.getCalloutView();
+ if (infoWindow != null) manager.pushEvent(context, infoWindow, "onPress", event);
+ }
+ });
+
+ map.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
+ @Override
+ public void onMapClick(LatLng point) {
+ WritableMap event = makeClickEventData(point);
+ event.putString("action", "press");
+ manager.pushEvent(context, view, "onPress", event);
+ }
+ });
+
+ map.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
+ @Override
+ public void onMapLongClick(LatLng point) {
+ WritableMap event = makeClickEventData(point);
+ event.putString("action", "long-press");
+ manager.pushEvent(context, view, "onLongPress", makeClickEventData(point));
+ }
+ });
+
+ map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
+ @Override
+ public void onCameraChange(CameraPosition position) {
+ LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds;
+ LatLng center = position.target;
+ lastBoundsEmitted = bounds;
+ eventDispatcher.dispatchEvent(new RegionChangeEvent(getId(), bounds, center, isTouchDown));
+ view.stopMonitoringRegion();
+ }
+ });
+
+ map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
+ @Override public void onMapLoaded() {
+ isMapLoaded = true;
+ AirMapView.this.cacheView();
+ }
+ });
+
+ // We need to be sure to disable location-tracking when app enters background, in-case some
+ // other module
+ // has acquired a wake-lock and is controlling location-updates, otherwise, location-manager
+ // will be left
+ // updating location constantly, killing the battery, even though some other location-mgmt
+ // module may
+ // desire to shut-down location-services.
+ lifecycleListener = new LifecycleEventListener() {
+ @Override
+ public void onHostResume() {
+ if (hasPermissions()) {
+ //noinspection MissingPermission
+ map.setMyLocationEnabled(showUserLocation);
}
- if (this.loadingIndicatorColor != null) {
- this.setLoadingIndicatorColor(this.loadingIndicatorColor);
+ synchronized (AirMapView.this) {
+ if (!destroyed) {
+ AirMapView.this.onResume();
+ }
+ paused = false;
}
- return this.mapLoadingProgressBar;
- }
+ }
- private RelativeLayout getMapLoadingLayoutView() {
- if (this.mapLoadingLayout == null) {
- this.mapLoadingLayout = new RelativeLayout(getContext());
- this.mapLoadingLayout.setBackgroundColor(Color.LTGRAY);
- this.addView(this.mapLoadingLayout,
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
+ @Override
+ public void onHostPause() {
+ if (hasPermissions()) {
+ //noinspection MissingPermission
+ map.setMyLocationEnabled(false);
+ }
+ synchronized (AirMapView.this) {
+ if (!destroyed) {
+ AirMapView.this.onPause();
+ }
+ paused = true;
+ }
+ }
- RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
- RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
- params.addRule(RelativeLayout.CENTER_IN_PARENT);
- this.mapLoadingLayout.addView(this.getMapLoadingProgressBar(), params);
+ @Override
+ public void onHostDestroy() {
+ AirMapView.this.doDestroy();
+ }
+ };
- this.mapLoadingLayout.setVisibility(View.INVISIBLE);
- }
- this.setLoadingBackgroundColor(this.loadingBackgroundColor);
- return this.mapLoadingLayout;
- }
+ context.addLifecycleEventListener(lifecycleListener);
+ }
+
+ private boolean hasPermissions() {
+ return checkSelfPermission(getContext(), PERMISSIONS[0]) == PackageManager.PERMISSION_GRANTED ||
+ checkSelfPermission(getContext(), PERMISSIONS[1]) == PackageManager.PERMISSION_GRANTED;
+ }
- private ImageView getCacheImageView() {
- if (this.cacheImageView == null) {
- this.cacheImageView = new ImageView(getContext());
- this.addView(this.cacheImageView,
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- this.cacheImageView.setVisibility(View.INVISIBLE);
- }
- return this.cacheImageView;
- }
- private void removeCacheImageView() {
- if (this.cacheImageView != null) {
- ((ViewGroup)this.cacheImageView.getParent()).removeView(this.cacheImageView);
- this.cacheImageView = null;
- }
- }
+ /*
+ onDestroy is final method so I can't override it.
+ */
+ public synchronized void doDestroy() {
+ if (destroyed) {
+ return;
+ }
+ destroyed = true;
+
+ if (lifecycleListener != null && context != null) {
+ context.removeLifecycleEventListener(lifecycleListener);
+ lifecycleListener = null;
+ }
+ if (!paused) {
+ onPause();
+ paused = true;
+ }
+ onDestroy();
+ }
+
+ public void setInitialRegion(ReadableMap initialRegion) {
+ if (!initialRegionSet && initialRegion != null) {
+ setRegion(initialRegion);
+ initialRegionSet = true;
+ }
+ }
+
+ public void setRegion(ReadableMap region) {
+ if (region == null) return;
+
+ Double lng = region.getDouble("longitude");
+ Double lat = region.getDouble("latitude");
+ Double lngDelta = region.getDouble("longitudeDelta");
+ Double latDelta = region.getDouble("latitudeDelta");
+ LatLngBounds bounds = new LatLngBounds(
+ new LatLng(lat - latDelta / 2, lng - lngDelta / 2), // southwest
+ new LatLng(lat + latDelta / 2, lng + lngDelta / 2) // northeast
+ );
+ if (super.getHeight() <= 0 || super.getWidth() <= 0) {
+ // in this case, our map has not been laid out yet, so we save the bounds in a local
+ // variable, and make a guess of zoomLevel 10. Not to worry, though: as soon as layout
+ // occurs, we will move the camera to the saved bounds. Note that if we tried to move
+ // to the bounds now, it would trigger an exception.
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lng), 10));
+ boundsToMove = bounds;
+ } else {
+ map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
+ boundsToMove = null;
+ }
+ }
+
+ public void setShowsUserLocation(boolean showUserLocation) {
+ this.showUserLocation = showUserLocation; // hold onto this for lifecycle handling
+ if (hasPermissions()) {
+ //noinspection MissingPermission
+ map.setMyLocationEnabled(showUserLocation);
+ }
+ }
+
+ public void setShowsMyLocationButton(boolean showMyLocationButton) {
+ if (hasPermissions()) {
+ map.getUiSettings().setMyLocationButtonEnabled(showMyLocationButton);
+ }
+ }
+
+ public void setToolbarEnabled(boolean toolbarEnabled) {
+ if (hasPermissions()) {
+ map.getUiSettings().setMapToolbarEnabled(toolbarEnabled);
+ }
+ }
+
+ public void setCacheEnabled(boolean cacheEnabled) {
+ this.cacheEnabled = cacheEnabled;
+ this.cacheView();
+ }
+
+ public void enableMapLoading(boolean loadingEnabled) {
+ if (loadingEnabled && !this.isMapLoaded) {
+ this.getMapLoadingLayoutView().setVisibility(View.VISIBLE);
+ }
+ }
+
+ public void setMoveOnMarkerPress(boolean moveOnPress) {
+ this.moveOnMarkerPress = moveOnPress;
+ }
+
+ public void setLoadingBackgroundColor(Integer loadingBackgroundColor) {
+ this.loadingBackgroundColor = loadingBackgroundColor;
+
+ if (this.mapLoadingLayout != null) {
+ if (loadingBackgroundColor == null) {
+ this.mapLoadingLayout.setBackgroundColor(Color.WHITE);
+ } else {
+ this.mapLoadingLayout.setBackgroundColor(this.loadingBackgroundColor);
+ }
+ }
+ }
+
+ public void setLoadingIndicatorColor(Integer loadingIndicatorColor) {
+ this.loadingIndicatorColor = loadingIndicatorColor;
+ if (this.mapLoadingProgressBar != null) {
+ Integer color = loadingIndicatorColor;
+ if (color == null) {
+ color = Color.parseColor("#606060");
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ ColorStateList progressTintList = ColorStateList.valueOf(loadingIndicatorColor);
+ ColorStateList secondaryProgressTintList = ColorStateList.valueOf(loadingIndicatorColor);
+ ColorStateList indeterminateTintList = ColorStateList.valueOf(loadingIndicatorColor);
+
+ this.mapLoadingProgressBar.setProgressTintList(progressTintList);
+ this.mapLoadingProgressBar.setSecondaryProgressTintList(secondaryProgressTintList);
+ this.mapLoadingProgressBar.setIndeterminateTintList(indeterminateTintList);
+ } else {
+ PorterDuff.Mode mode = PorterDuff.Mode.SRC_IN;
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
+ mode = PorterDuff.Mode.MULTIPLY;
+ }
+ if (this.mapLoadingProgressBar.getIndeterminateDrawable() != null)
+ this.mapLoadingProgressBar.getIndeterminateDrawable().setColorFilter(color, mode);
+ if (this.mapLoadingProgressBar.getProgressDrawable() != null)
+ this.mapLoadingProgressBar.getProgressDrawable().setColorFilter(color, mode);
+ }
+ }
+ }
+
+ public void setHandlePanDrag(boolean handlePanDrag) {
+ this.handlePanDrag = handlePanDrag;
+ }
+
+ public void addFeature(View child, int index) {
+ // Our desired API is to pass up annotations/overlays as children to the mapview component.
+ // This is where we intercept them and do the appropriate underlying mapview action.
+ if (child instanceof AirMapMarker) {
+ AirMapMarker annotation = (AirMapMarker) child;
+ annotation.addToMap(map);
+ features.add(index, annotation);
+ Marker marker = (Marker) annotation.getFeature();
+ markerMap.put(marker, annotation);
+ } else if (child instanceof AirMapPolyline) {
+ AirMapPolyline polylineView = (AirMapPolyline) child;
+ polylineView.addToMap(map);
+ features.add(index, polylineView);
+ Polyline polyline = (Polyline) polylineView.getFeature();
+ polylineMap.put(polyline, polylineView);
+ } else if (child instanceof AirMapPolygon) {
+ AirMapPolygon polygonView = (AirMapPolygon) child;
+ polygonView.addToMap(map);
+ features.add(index, polygonView);
+ Polygon polygon = (Polygon) polygonView.getFeature();
+ polygonMap.put(polygon, polygonView);
+ } else if (child instanceof AirMapCircle) {
+ AirMapCircle circleView = (AirMapCircle) child;
+ circleView.addToMap(map);
+ features.add(index, circleView);
+ } else if (child instanceof AirMapUrlTile) {
+ AirMapUrlTile urlTileView = (AirMapUrlTile) child;
+ urlTileView.addToMap(map);
+ features.add(index, urlTileView);
+ } else {
+ ViewGroup children = (ViewGroup) child;
+ for (int i = 0; i < children.getChildCount(); i++) {
+ addFeature(children.getChildAt(i), index);
+ }
+ }
+ }
+
+ public int getFeatureCount() {
+ return features.size();
+ }
+
+ public View getFeatureAt(int index) {
+ return features.get(index);
+ }
+
+ public void removeFeatureAt(int index) {
+ AirMapFeature feature = features.remove(index);
+ if (feature instanceof AirMapMarker) {
+ markerMap.remove(feature.getFeature());
+ }
+ feature.removeFromMap(map);
+ }
+
+ public WritableMap makeClickEventData(LatLng point) {
+ WritableMap event = new WritableNativeMap();
+
+ WritableMap coordinate = new WritableNativeMap();
+ coordinate.putDouble("latitude", point.latitude);
+ coordinate.putDouble("longitude", point.longitude);
+ event.putMap("coordinate", coordinate);
+
+ Projection projection = map.getProjection();
+ Point screenPoint = projection.toScreenLocation(point);
+
+ WritableMap position = new WritableNativeMap();
+ position.putDouble("x", screenPoint.x);
+ position.putDouble("y", screenPoint.y);
+ event.putMap("position", position);
+
+ return event;
+ }
+
+ public void updateExtraData(Object extraData) {
+ // if boundsToMove is not null, we now have the MapView's width/height, so we can apply
+ // a proper camera move
+ if (boundsToMove != null) {
+ HashMap data = (HashMap) extraData;
+ int width = data.get("width") == null ? 0 : data.get("width").intValue();
+ int height = data.get("height") == null ? 0 : data.get("height").intValue();
+
+ //fix for https://github.com/airbnb/react-native-maps/issues/245,
+ //it's not guaranteed the passed-in height and width would be greater than 0.
+ if (width <= 0 || height <= 0) {
+ map.moveCamera(CameraUpdateFactory.newLatLngBounds(boundsToMove, 0));
+ } else {
+ map.moveCamera(CameraUpdateFactory.newLatLngBounds(boundsToMove, width, height, 0));
+ }
+
+ boundsToMove = null;
+ }
+ }
+
+ public void animateToRegion(LatLngBounds bounds, int duration) {
+ if (map != null) {
+ startMonitoringRegion();
+ map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0), duration, null);
+ }
+ }
+
+ public void animateToViewingAngle(float angle, int duration) {
+ if (map != null) {
+ startMonitoringRegion();
+ CameraPosition cameraPosition = new CameraPosition.Builder(map.getCameraPosition())
+ .tilt(angle)
+ .build();
+ map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), duration, null);
+ }
+ }
+
+ public void animateToBearing(float bearing, int duration) {
+ if (map != null) {
+ startMonitoringRegion();
+ CameraPosition cameraPosition = new CameraPosition.Builder(map.getCameraPosition())
+ .bearing(bearing)
+ .build();
+ map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), duration, null);
+ }
+ }
+
+ public void animateToCoordinate(LatLng coordinate, int duration) {
+ if (map != null) {
+ startMonitoringRegion();
+ map.animateCamera(CameraUpdateFactory.newLatLng(coordinate), duration, null);
+ }
+ }
+
+ public void fitToElements(boolean animated) {
+ LatLngBounds.Builder builder = new LatLngBounds.Builder();
+
+ boolean addedPosition = false;
+
+ for (AirMapFeature feature : features) {
+ if (feature instanceof AirMapMarker) {
+ Marker marker = (Marker) feature.getFeature();
+ builder.include(marker.getPosition());
+ addedPosition = true;
+ }
+ // TODO(lmr): may want to include shapes / etc.
+ }
+ if (addedPosition) {
+ LatLngBounds bounds = builder.build();
+ CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, baseMapPadding);
+ if (animated) {
+ startMonitoringRegion();
+ map.animateCamera(cu);
+ } else {
+ map.moveCamera(cu);
+ }
+ }
+ }
+
+ public void fitToSuppliedMarkers(ReadableArray markerIDsArray, boolean animated) {
+ LatLngBounds.Builder builder = new LatLngBounds.Builder();
+
+ String[] markerIDs = new String[markerIDsArray.size()];
+ for (int i = 0; i < markerIDsArray.size(); i++) {
+ markerIDs[i] = markerIDsArray.getString(i);
+ }
+
+ boolean addedPosition = false;
+
+ List markerIDList = Arrays.asList(markerIDs);
+
+ for (AirMapFeature feature : features) {
+ if (feature instanceof AirMapMarker) {
+ String identifier = ((AirMapMarker) feature).getIdentifier();
+ Marker marker = (Marker) feature.getFeature();
+ if (markerIDList.contains(identifier)) {
+ builder.include(marker.getPosition());
+ addedPosition = true;
+ }
+ }
+ }
+
+ if (addedPosition) {
+ LatLngBounds bounds = builder.build();
+ CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, baseMapPadding);
+ if (animated) {
+ startMonitoringRegion();
+ map.animateCamera(cu);
+ } else {
+ map.moveCamera(cu);
+ }
+ }
+ }
+
+ public void fitToCoordinates(ReadableArray coordinatesArray, ReadableMap edgePadding,
+ boolean animated) {
+ LatLngBounds.Builder builder = new LatLngBounds.Builder();
+
+ for (int i = 0; i < coordinatesArray.size(); i++) {
+ ReadableMap latLng = coordinatesArray.getMap(i);
+ Double lat = latLng.getDouble("latitude");
+ Double lng = latLng.getDouble("longitude");
+ builder.include(new LatLng(lat, lng));
+ }
+
+ LatLngBounds bounds = builder.build();
+ CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, baseMapPadding);
+
+ if (edgePadding != null) {
+ map.setPadding(edgePadding.getInt("left"), edgePadding.getInt("top"),
+ edgePadding.getInt("right"), edgePadding.getInt("bottom"));
+ }
+
+ if (animated) {
+ startMonitoringRegion();
+ map.animateCamera(cu);
+ } else {
+ map.moveCamera(cu);
+ }
+ map.setPadding(0, 0, 0,
+ 0); // Without this, the Google logo is moved up by the value of edgePadding.bottom
+ }
+
+ // InfoWindowAdapter interface
- private void removeMapLoadingProgressBar() {
- if (this.mapLoadingProgressBar != null) {
- ((ViewGroup)this.mapLoadingProgressBar.getParent()).removeView(this.mapLoadingProgressBar);
- this.mapLoadingProgressBar = null;
- }
+ @Override
+ public View getInfoWindow(Marker marker) {
+ AirMapMarker markerView = markerMap.get(marker);
+ return markerView.getCallout();
+ }
+
+ @Override
+ public View getInfoContents(Marker marker) {
+ AirMapMarker markerView = markerMap.get(marker);
+ return markerView.getInfoContents();
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ scaleDetector.onTouchEvent(ev);
+ gestureDetector.onTouchEvent(ev);
+
+ int action = MotionEventCompat.getActionMasked(ev);
+
+ switch (action) {
+ case (MotionEvent.ACTION_DOWN):
+ this.getParent().requestDisallowInterceptTouchEvent(
+ map != null && map.getUiSettings().isScrollGesturesEnabled());
+ isTouchDown = true;
+ break;
+ case (MotionEvent.ACTION_MOVE):
+ startMonitoringRegion();
+ break;
+ case (MotionEvent.ACTION_UP):
+ // Clear this regardless, since isScrollGesturesEnabled() may have been updated
+ this.getParent().requestDisallowInterceptTouchEvent(false);
+ isTouchDown = false;
+ break;
}
+ super.dispatchTouchEvent(ev);
+ return true;
+ }
+
+ // Timer Implementation
- private void removeMapLoadingLayoutView() {
- this.removeMapLoadingProgressBar();
- if (this.mapLoadingLayout != null) {
- ((ViewGroup)this.mapLoadingLayout.getParent()).removeView(this.mapLoadingLayout);
- this.mapLoadingLayout = null;
- }
- }
+ public void startMonitoringRegion() {
+ if (map == null || isMonitoringRegion) return;
+ timerHandler.postDelayed(timerRunnable, 100);
+ isMonitoringRegion = true;
+ }
+
+ public void stopMonitoringRegion() {
+ if (map == null || !isMonitoringRegion) return;
+ timerHandler.removeCallbacks(timerRunnable);
+ isMonitoringRegion = false;
+ }
+
+ private LatLngBounds lastBoundsEmitted;
+
+ Handler timerHandler = new Handler();
+ Runnable timerRunnable = new Runnable() {
- private void cacheView() {
- if (this.cacheEnabled) {
- final ImageView cacheImageView = this.getCacheImageView();
- final RelativeLayout mapLoadingLayout = this.getMapLoadingLayoutView();
- cacheImageView.setVisibility(View.INVISIBLE);
- mapLoadingLayout.setVisibility(View.VISIBLE);
- if (this.isMapLoaded) {
- this.map.snapshot(new GoogleMap.SnapshotReadyCallback() {
- @Override public void onSnapshotReady(Bitmap bitmap) {
- cacheImageView.setImageBitmap(bitmap);
- cacheImageView.setVisibility(View.VISIBLE);
- mapLoadingLayout.setVisibility(View.INVISIBLE);
- }
- });
- }
- }
- else {
- this.removeCacheImageView();
- if (this.isMapLoaded) {
- this.removeMapLoadingLayoutView();
- }
- }
- }
+ @Override
+ public void run() {
- public void onPanDrag(MotionEvent ev) {
- Point point = new Point((int) ev.getX(), (int) ev.getY());
- LatLng coords = this.map.getProjection().fromScreenLocation(point);
- WritableMap event = makeClickEventData(coords);
- manager.pushEvent(context, this, "onPanDrag", event);
- }
+ if (map != null) {
+ Projection projection = map.getProjection();
+ VisibleRegion region = (projection != null) ? projection.getVisibleRegion() : null;
+ LatLngBounds bounds = (region != null) ? region.latLngBounds : null;
+
+ if ((bounds != null) &&
+ (lastBoundsEmitted == null ||
+ LatLngBoundsUtils.BoundsAreDifferent(bounds, lastBoundsEmitted))) {
+ LatLng center = map.getCameraPosition().target;
+ lastBoundsEmitted = bounds;
+ eventDispatcher.dispatchEvent(new RegionChangeEvent(getId(), bounds, center, true));
+ }
+ }
+
+ timerHandler.postDelayed(this, 100);
+ }
+ };
+
+ @Override
+ public void onMarkerDragStart(Marker marker) {
+ WritableMap event = makeClickEventData(marker.getPosition());
+ manager.pushEvent(context, this, "onMarkerDragStart", event);
+
+ AirMapMarker markerView = markerMap.get(marker);
+ event = makeClickEventData(marker.getPosition());
+ manager.pushEvent(context, markerView, "onDragStart", event);
+ }
+
+ @Override
+ public void onMarkerDrag(Marker marker) {
+ WritableMap event = makeClickEventData(marker.getPosition());
+ manager.pushEvent(context, this, "onMarkerDrag", event);
+
+ AirMapMarker markerView = markerMap.get(marker);
+ event = makeClickEventData(marker.getPosition());
+ manager.pushEvent(context, markerView, "onDrag", event);
+ }
+
+ @Override
+ public void onMarkerDragEnd(Marker marker) {
+ WritableMap event = makeClickEventData(marker.getPosition());
+ manager.pushEvent(context, this, "onMarkerDragEnd", event);
+
+ AirMapMarker markerView = markerMap.get(marker);
+ event = makeClickEventData(marker.getPosition());
+ manager.pushEvent(context, markerView, "onDragEnd", event);
+ }
+
+ private ProgressBar getMapLoadingProgressBar() {
+ if (this.mapLoadingProgressBar == null) {
+ this.mapLoadingProgressBar = new ProgressBar(getContext());
+ this.mapLoadingProgressBar.setIndeterminate(true);
+ }
+ if (this.loadingIndicatorColor != null) {
+ this.setLoadingIndicatorColor(this.loadingIndicatorColor);
+ }
+ return this.mapLoadingProgressBar;
+ }
+
+ private RelativeLayout getMapLoadingLayoutView() {
+ if (this.mapLoadingLayout == null) {
+ this.mapLoadingLayout = new RelativeLayout(getContext());
+ this.mapLoadingLayout.setBackgroundColor(Color.LTGRAY);
+ this.addView(this.mapLoadingLayout,
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
+ params.addRule(RelativeLayout.CENTER_IN_PARENT);
+ this.mapLoadingLayout.addView(this.getMapLoadingProgressBar(), params);
+
+ this.mapLoadingLayout.setVisibility(View.INVISIBLE);
+ }
+ this.setLoadingBackgroundColor(this.loadingBackgroundColor);
+ return this.mapLoadingLayout;
+ }
+
+ private ImageView getCacheImageView() {
+ if (this.cacheImageView == null) {
+ this.cacheImageView = new ImageView(getContext());
+ this.addView(this.cacheImageView,
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ this.cacheImageView.setVisibility(View.INVISIBLE);
+ }
+ return this.cacheImageView;
+ }
+
+ private void removeCacheImageView() {
+ if (this.cacheImageView != null) {
+ ((ViewGroup) this.cacheImageView.getParent()).removeView(this.cacheImageView);
+ this.cacheImageView = null;
+ }
+ }
+
+ private void removeMapLoadingProgressBar() {
+ if (this.mapLoadingProgressBar != null) {
+ ((ViewGroup) this.mapLoadingProgressBar.getParent()).removeView(this.mapLoadingProgressBar);
+ this.mapLoadingProgressBar = null;
+ }
+ }
+
+ private void removeMapLoadingLayoutView() {
+ this.removeMapLoadingProgressBar();
+ if (this.mapLoadingLayout != null) {
+ ((ViewGroup) this.mapLoadingLayout.getParent()).removeView(this.mapLoadingLayout);
+ this.mapLoadingLayout = null;
+ }
+ }
+
+ private void cacheView() {
+ if (this.cacheEnabled) {
+ final ImageView cacheImageView = this.getCacheImageView();
+ final RelativeLayout mapLoadingLayout = this.getMapLoadingLayoutView();
+ cacheImageView.setVisibility(View.INVISIBLE);
+ mapLoadingLayout.setVisibility(View.VISIBLE);
+ if (this.isMapLoaded) {
+ this.map.snapshot(new GoogleMap.SnapshotReadyCallback() {
+ @Override public void onSnapshotReady(Bitmap bitmap) {
+ cacheImageView.setImageBitmap(bitmap);
+ cacheImageView.setVisibility(View.VISIBLE);
+ mapLoadingLayout.setVisibility(View.INVISIBLE);
+ }
+ });
+ }
+ } else {
+ this.removeCacheImageView();
+ if (this.isMapLoaded) {
+ this.removeMapLoadingLayoutView();
+ }
+ }
+ }
+
+ public void onPanDrag(MotionEvent ev) {
+ Point point = new Point((int) ev.getX(), (int) ev.getY());
+ LatLng coords = this.map.getProjection().fromScreenLocation(point);
+ WritableMap event = makeClickEventData(coords);
+ manager.pushEvent(context, this, "onPanDrag", event);
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/LatLngBoundsUtils.java b/lib/android/src/main/java/com/airbnb/android/react/maps/LatLngBoundsUtils.java
index ed1058c88d..d32e0dc726 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/LatLngBoundsUtils.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/LatLngBoundsUtils.java
@@ -4,44 +4,44 @@
import com.google.android.gms.maps.model.LatLngBounds;
public class LatLngBoundsUtils {
- public static boolean BoundsAreDifferent(LatLngBounds a, LatLngBounds b) {
- LatLng centerA = a.getCenter();
- double latA = centerA.latitude;
- double lngA = centerA.longitude;
- double latDeltaA = a.northeast.latitude - a.southwest.latitude;
- double lngDeltaA = a.northeast.longitude - a.southwest.longitude;
-
- LatLng centerB = b.getCenter();
- double latB = centerB.latitude;
- double lngB = centerB.longitude;
- double latDeltaB = b.northeast.latitude - b.southwest.latitude;
- double lngDeltaB = b.northeast.longitude - b.southwest.longitude;
-
- double latEps = LatitudeEpsilon(a, b);
- double lngEps = LongitudeEpsilon(a, b);
-
- return
- different(latA, latB, latEps) ||
- different(lngA, lngB, lngEps) ||
- different(latDeltaA, latDeltaB, latEps) ||
- different(lngDeltaA, lngDeltaB, lngEps);
- }
-
- private static boolean different(double a, double b, double epsilon) {
- return Math.abs(a - b) > epsilon;
- }
-
- private static double LatitudeEpsilon(LatLngBounds a, LatLngBounds b) {
- double sizeA = a.northeast.latitude - a.southwest.latitude; // something mod 180?
- double sizeB = b.northeast.latitude - b.southwest.latitude; // something mod 180?
- double size = Math.min(Math.abs(sizeA), Math.abs(sizeB));
- return size / 2560;
- }
-
- private static double LongitudeEpsilon(LatLngBounds a, LatLngBounds b) {
- double sizeA = a.northeast.longitude - a.southwest.longitude;
- double sizeB = b.northeast.longitude - b.southwest.longitude;
- double size = Math.min(Math.abs(sizeA), Math.abs(sizeB));
- return size / 2560;
- }
+ public static boolean BoundsAreDifferent(LatLngBounds a, LatLngBounds b) {
+ LatLng centerA = a.getCenter();
+ double latA = centerA.latitude;
+ double lngA = centerA.longitude;
+ double latDeltaA = a.northeast.latitude - a.southwest.latitude;
+ double lngDeltaA = a.northeast.longitude - a.southwest.longitude;
+
+ LatLng centerB = b.getCenter();
+ double latB = centerB.latitude;
+ double lngB = centerB.longitude;
+ double latDeltaB = b.northeast.latitude - b.southwest.latitude;
+ double lngDeltaB = b.northeast.longitude - b.southwest.longitude;
+
+ double latEps = LatitudeEpsilon(a, b);
+ double lngEps = LongitudeEpsilon(a, b);
+
+ return
+ different(latA, latB, latEps) ||
+ different(lngA, lngB, lngEps) ||
+ different(latDeltaA, latDeltaB, latEps) ||
+ different(lngDeltaA, lngDeltaB, lngEps);
+ }
+
+ private static boolean different(double a, double b, double epsilon) {
+ return Math.abs(a - b) > epsilon;
+ }
+
+ private static double LatitudeEpsilon(LatLngBounds a, LatLngBounds b) {
+ double sizeA = a.northeast.latitude - a.southwest.latitude; // something mod 180?
+ double sizeB = b.northeast.latitude - b.southwest.latitude; // something mod 180?
+ double size = Math.min(Math.abs(sizeA), Math.abs(sizeB));
+ return size / 2560;
+ }
+
+ private static double LongitudeEpsilon(LatLngBounds a, LatLngBounds b) {
+ double sizeA = a.northeast.longitude - a.southwest.longitude;
+ double sizeB = b.northeast.longitude - b.southwest.longitude;
+ double size = Math.min(Math.abs(sizeA), Math.abs(sizeB));
+ return size / 2560;
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java b/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java
index e76f63aa27..6ecc551658 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/MapsPackage.java
@@ -13,41 +13,41 @@
import java.util.List;
public class MapsPackage implements ReactPackage {
- public MapsPackage(Activity activity) {
- } // backwards compatibility
-
- public MapsPackage() {
- }
-
- @Override
- public List createNativeModules(ReactApplicationContext reactContext) {
- return Arrays.asList(new AirMapModule(reactContext));
- }
-
- @Override
- public List> createJSModules() {
- return Collections.emptyList();
- }
-
- @Override
- public List createViewManagers(ReactApplicationContext reactContext) {
- AirMapCalloutManager calloutManager = new AirMapCalloutManager();
- AirMapMarkerManager annotationManager = new AirMapMarkerManager();
- AirMapPolylineManager polylineManager = new AirMapPolylineManager(reactContext);
- AirMapPolygonManager polygonManager = new AirMapPolygonManager(reactContext);
- AirMapCircleManager circleManager = new AirMapCircleManager(reactContext);
- AirMapManager mapManager = new AirMapManager(reactContext);
- AirMapLiteManager mapLiteManager = new AirMapLiteManager(reactContext);
- AirMapUrlTileManager tileManager = new AirMapUrlTileManager(reactContext);
-
- return Arrays.asList(
- calloutManager,
- annotationManager,
- polylineManager,
- polygonManager,
- circleManager,
- mapManager,
- mapLiteManager,
- tileManager);
- }
+ public MapsPackage(Activity activity) {
+ } // backwards compatibility
+
+ public MapsPackage() {
+ }
+
+ @Override
+ public List createNativeModules(ReactApplicationContext reactContext) {
+ return Arrays.asList(new AirMapModule(reactContext));
+ }
+
+ // Deprecated RN 0.47
+ public List> createJSModules() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List createViewManagers(ReactApplicationContext reactContext) {
+ AirMapCalloutManager calloutManager = new AirMapCalloutManager();
+ AirMapMarkerManager annotationManager = new AirMapMarkerManager();
+ AirMapPolylineManager polylineManager = new AirMapPolylineManager(reactContext);
+ AirMapPolygonManager polygonManager = new AirMapPolygonManager(reactContext);
+ AirMapCircleManager circleManager = new AirMapCircleManager(reactContext);
+ AirMapManager mapManager = new AirMapManager(reactContext);
+ AirMapLiteManager mapLiteManager = new AirMapLiteManager(reactContext);
+ AirMapUrlTileManager tileManager = new AirMapUrlTileManager(reactContext);
+
+ return Arrays.asList(
+ calloutManager,
+ annotationManager,
+ polylineManager,
+ polygonManager,
+ circleManager,
+ mapManager,
+ mapLiteManager,
+ tileManager);
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/RegionChangeEvent.java b/lib/android/src/main/java/com/airbnb/android/react/maps/RegionChangeEvent.java
index 28a3b322b8..43b1f678e6 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/RegionChangeEvent.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/RegionChangeEvent.java
@@ -8,40 +8,40 @@
import com.google.android.gms.maps.model.LatLngBounds;
public class RegionChangeEvent extends Event {
- private final LatLngBounds bounds;
- private final LatLng center;
- private final boolean continuous;
-
- public RegionChangeEvent(int id, LatLngBounds bounds, LatLng center, boolean continuous) {
- super(id);
- this.bounds = bounds;
- this.center = center;
- this.continuous = continuous;
- }
-
- @Override
- public String getEventName() {
- return "topChange";
- }
-
- @Override
- public boolean canCoalesce() {
- return false;
- }
-
- @Override
- public void dispatch(RCTEventEmitter rctEventEmitter) {
-
- WritableMap event = new WritableNativeMap();
- event.putBoolean("continuous", continuous);
-
- WritableMap region = new WritableNativeMap();
- region.putDouble("latitude", center.latitude);
- region.putDouble("longitude", center.longitude);
- region.putDouble("latitudeDelta", bounds.northeast.latitude - bounds.southwest.latitude);
- region.putDouble("longitudeDelta", bounds.northeast.longitude - bounds.southwest.longitude);
- event.putMap("region", region);
-
- rctEventEmitter.receiveEvent(getViewTag(), getEventName(), event);
- }
+ private final LatLngBounds bounds;
+ private final LatLng center;
+ private final boolean continuous;
+
+ public RegionChangeEvent(int id, LatLngBounds bounds, LatLng center, boolean continuous) {
+ super(id);
+ this.bounds = bounds;
+ this.center = center;
+ this.continuous = continuous;
+ }
+
+ @Override
+ public String getEventName() {
+ return "topChange";
+ }
+
+ @Override
+ public boolean canCoalesce() {
+ return false;
+ }
+
+ @Override
+ public void dispatch(RCTEventEmitter rctEventEmitter) {
+
+ WritableMap event = new WritableNativeMap();
+ event.putBoolean("continuous", continuous);
+
+ WritableMap region = new WritableNativeMap();
+ region.putDouble("latitude", center.latitude);
+ region.putDouble("longitude", center.longitude);
+ region.putDouble("latitudeDelta", bounds.northeast.latitude - bounds.southwest.latitude);
+ region.putDouble("longitudeDelta", bounds.northeast.longitude - bounds.southwest.longitude);
+ event.putMap("region", region);
+
+ rctEventEmitter.receiveEvent(getViewTag(), getEventName(), event);
+ }
}
diff --git a/lib/android/src/main/java/com/airbnb/android/react/maps/SizeReportingShadowNode.java b/lib/android/src/main/java/com/airbnb/android/react/maps/SizeReportingShadowNode.java
index 40ace480f5..bf6c29d652 100644
--- a/lib/android/src/main/java/com/airbnb/android/react/maps/SizeReportingShadowNode.java
+++ b/lib/android/src/main/java/com/airbnb/android/react/maps/SizeReportingShadowNode.java
@@ -18,14 +18,14 @@
// which sends the width/height of the view after layout occurs.
public class SizeReportingShadowNode extends LayoutShadowNode {
- @Override
- public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
- super.onCollectExtraUpdates(uiViewOperationQueue);
+ @Override
+ public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
+ super.onCollectExtraUpdates(uiViewOperationQueue);
- Map data = new HashMap<>();
- data.put("width", getLayoutWidth());
- data.put("height", getLayoutHeight());
+ Map data = new HashMap<>();
+ data.put("width", getLayoutWidth());
+ data.put("height", getLayoutHeight());
- uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), data);
- }
+ uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), data);
+ }
}
diff --git a/lib/components/AnimatedRegion.js b/lib/components/AnimatedRegion.js
index 29356ca9e3..ad18c5e077 100644
--- a/lib/components/AnimatedRegion.js
+++ b/lib/components/AnimatedRegion.js
@@ -111,25 +111,25 @@ export default class AnimatedMapRegion extends AnimatedWithChildren {
spring(config) {
var animations = [];
config.hasOwnProperty('latitude') &&
- animations.push(Animated.timing(this.latitude, {
+ animations.push(Animated.spring(this.latitude, {
...config,
toValue: config.latitude
}));
config.hasOwnProperty('longitude') &&
- animations.push(Animated.timing(this.longitude, {
+ animations.push(Animated.spring(this.longitude, {
...config,
toValue: config.longitude
}));
config.hasOwnProperty('latitudeDelta') &&
- animations.push(Animated.timing(this.latitudeDelta, {
+ animations.push(Animated.spring(this.latitudeDelta, {
...config,
toValue: config.latitudeDelta
}));
config.hasOwnProperty('longitudeDelta') &&
- animations.push(Animated.timing(this.longitudeDelta, {
+ animations.push(Animated.spring(this.longitudeDelta, {
...config,
toValue: config.longitudeDelta
}));
diff --git a/lib/components/MapCallout.js b/lib/components/MapCallout.js
index cf4bb9cef8..226b52fe2d 100644
--- a/lib/components/MapCallout.js
+++ b/lib/components/MapCallout.js
@@ -1,7 +1,8 @@
-import React, { PropTypes } from 'react';
+import PropTypes from 'prop-types';
+import React from 'react';
import {
- View,
StyleSheet,
+ ViewPropTypes,
} from 'react-native';
import decorateMapComponent, {
SUPPORTED,
@@ -9,7 +10,7 @@ import decorateMapComponent, {
} from './decorateMapComponent';
const propTypes = {
- ...View.propTypes,
+ ...ViewPropTypes,
tooltip: PropTypes.bool,
onPress: PropTypes.func,
};
diff --git a/lib/components/MapCircle.js b/lib/components/MapCircle.js
index aab1ba8f19..acf066b248 100644
--- a/lib/components/MapCircle.js
+++ b/lib/components/MapCircle.js
@@ -1,6 +1,7 @@
-import React, { PropTypes } from 'react';
+import PropTypes from 'prop-types';
+import React from 'react';
import {
- View,
+ ViewPropTypes,
} from 'react-native';
import decorateMapComponent, {
USES_DEFAULT_IMPLEMENTATION,
@@ -8,7 +9,7 @@ import decorateMapComponent, {
} from './decorateMapComponent';
const propTypes = {
- ...View.propTypes,
+ ...ViewPropTypes,
/**
* The coordinate of the center of the circle
diff --git a/lib/components/MapMarker.js b/lib/components/MapMarker.js
index be54e42db3..b81bab87af 100644
--- a/lib/components/MapMarker.js
+++ b/lib/components/MapMarker.js
@@ -1,11 +1,12 @@
-import React, { PropTypes } from 'react';
+import PropTypes from 'prop-types';
+import React from 'react';
import {
- View,
StyleSheet,
Platform,
NativeModules,
Animated,
findNodeHandle,
+ ViewPropTypes,
} from 'react-native';
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
@@ -22,7 +23,7 @@ const viewConfig = {
};
const propTypes = {
- ...View.propTypes,
+ ...ViewPropTypes,
// TODO(lmr): get rid of these?
identifier: PropTypes.string,
@@ -256,7 +257,7 @@ class MapMarker extends React.Component {
let image;
if (this.props.image) {
image = resolveAssetSource(this.props.image) || {};
- image = image.uri;
+ image = image.uri || this.props.image;
}
const AIRMapMarker = this.getAirComponent();
diff --git a/lib/components/MapPolygon.js b/lib/components/MapPolygon.js
index 6901979db2..27f2aa322a 100644
--- a/lib/components/MapPolygon.js
+++ b/lib/components/MapPolygon.js
@@ -1,6 +1,7 @@
-import React, { PropTypes } from 'react';
+import PropTypes from 'prop-types';
+import React from 'react';
import {
- View,
+ ViewPropTypes,
} from 'react-native';
import decorateMapComponent, {
USES_DEFAULT_IMPLEMENTATION,
@@ -8,7 +9,7 @@ import decorateMapComponent, {
} from './decorateMapComponent';
const propTypes = {
- ...View.propTypes,
+ ...ViewPropTypes,
/**
* An array of coordinates to describe the polygon
diff --git a/lib/components/MapPolyline.js b/lib/components/MapPolyline.js
index 35b4c60484..af1573eff4 100644
--- a/lib/components/MapPolyline.js
+++ b/lib/components/MapPolyline.js
@@ -1,6 +1,7 @@
-import React, { PropTypes } from 'react';
+import PropTypes from 'prop-types';
+import React from 'react';
import {
- View,
+ ViewPropTypes,
} from 'react-native';
import decorateMapComponent, {
USES_DEFAULT_IMPLEMENTATION,
@@ -8,7 +9,7 @@ import decorateMapComponent, {
} from './decorateMapComponent';
const propTypes = {
- ...View.propTypes,
+ ...ViewPropTypes,
/**
* An array of coordinates to describe the polygon
@@ -26,6 +27,11 @@ const propTypes = {
*/
onPress: PropTypes.func,
+ /* Boolean to allow a polyline to be tappable and use the
+ * onPress function
+ */
+ tappable: PropTypes.bool,
+
/**
* The fill color to use for the path.
*/
diff --git a/lib/components/MapUrlTile.js b/lib/components/MapUrlTile.js
index cf27703348..f61c1e0d36 100644
--- a/lib/components/MapUrlTile.js
+++ b/lib/components/MapUrlTile.js
@@ -1,7 +1,8 @@
-import React, { PropTypes } from 'react';
+import PropTypes from 'prop-types';
+import React from 'react';
import {
- View,
+ ViewPropTypes,
} from 'react-native';
import decorateMapComponent, {
@@ -10,7 +11,7 @@ import decorateMapComponent, {
} from './decorateMapComponent';
const propTypes = {
- ...View.propTypes,
+ ...ViewPropTypes,
/**
* The url template of the tile server. The patterns {x} {y} {z} will be replaced at runtime
diff --git a/lib/components/MapView.js b/lib/components/MapView.js
index f25003b843..123d34f60f 100644
--- a/lib/components/MapView.js
+++ b/lib/components/MapView.js
@@ -1,13 +1,15 @@
-import React, { PropTypes } from 'react';
+import PropTypes from 'prop-types';
+import React from 'react';
import {
EdgeInsetsPropType,
Platform,
- View,
Animated,
requireNativeComponent,
NativeModules,
ColorPropType,
findNodeHandle,
+ View,
+ ViewPropTypes,
} from 'react-native';
import MapMarker from './MapMarker';
import MapPolyline from './MapPolyline';
@@ -44,8 +46,11 @@ const viewConfig = {
},
};
+// if ViewPropTypes is not defined fall back to View.propType (to support RN < 0.44)
+const viewPropTypes = ViewPropTypes || View.propTypes;
+
const propTypes = {
- ...View.propTypes,
+ ...viewPropTypes,
/**
* When provider is "google", we will use GoogleMaps.
* Any value other than "google" will default to using
@@ -59,7 +64,7 @@ const propTypes = {
* Used to style and layout the `MapView`. See `StyleSheet.js` and
* `ViewStylePropTypes.js` for more info.
*/
- style: View.propTypes.style,
+ style: viewPropTypes.style,
/**
* A json object that describes the style of the map. This is transformed to a string
@@ -317,6 +322,11 @@ const propTypes = {
*/
legalLabelInsets: EdgeInsetsPropType,
+ /**
+ * Callback that is called once the map is fully loaded.
+ */
+ onMapReady: PropTypes.func,
+
/**
* Callback that is called continuously when the user is dragging the map.
*/
@@ -384,6 +394,16 @@ const propTypes = {
*/
onMarkerDragEnd: PropTypes.func,
+ /**
+ * Minimum zoom value for the map, must be between 0 and 20
+ */
+ minZoomLevel: PropTypes.number,
+
+ /**
+ * Maximum zoom value for the map, must be between 0 and 20
+ */
+ maxZoomLevel: PropTypes.number,
+
};
class MapView extends React.Component {
@@ -430,24 +450,29 @@ class MapView extends React.Component {
}
_onMapReady() {
- const { region, initialRegion } = this.props;
+ const { region, initialRegion, onMapReady } = this.props;
if (region) {
this.map.setNativeProps({ region });
} else if (initialRegion) {
- this.map.setNativeProps({ region: initialRegion });
+ this.map.setNativeProps({ initialRegion });
}
this._updateStyle();
- this.setState({ isReady: true });
+ this.setState({ isReady: true }, () => {
+ if (onMapReady) onMapReady();
+ });
}
_onLayout(e) {
const { layout } = e.nativeEvent;
if (!layout.width || !layout.height) return;
if (this.state.isReady && !this.__layoutCalled) {
- const region = this.props.region || this.props.initialRegion;
+ const { region, initialRegion } = this.props;
if (region) {
this.__layoutCalled = true;
this.map.setNativeProps({ region });
+ } else if (initialRegion) {
+ this.__layoutCalled = true;
+ this.map.setNativeProps({ initialRegion });
}
}
if (this.props.onLayout) {
@@ -474,6 +499,14 @@ class MapView extends React.Component {
this._runCommand('animateToCoordinate', [latLng, duration || 500]);
}
+ animateToBearing(bearing, duration) {
+ this._runCommand('animateToBearing', [bearing, duration || 500]);
+ }
+
+ animateToViewingAngle(angle, duration) {
+ this._runCommand('animateToViewingAngle', [angle, duration || 500]);
+ }
+
fitToElements(animated) {
this._runCommand('fitToElements', [animated]);
}
diff --git a/lib/components/decorateMapComponent.js b/lib/components/decorateMapComponent.js
index e655c4c33c..168d4f1226 100644
--- a/lib/components/decorateMapComponent.js
+++ b/lib/components/decorateMapComponent.js
@@ -1,4 +1,4 @@
-import { PropTypes } from 'react';
+import PropTypes from 'prop-types';
import {
requireNativeComponent,
NativeModules,
diff --git a/lib/ios/AirGoogleMaps/AIRGMSPolygon.h b/lib/ios/AirGoogleMaps/AIRGMSPolygon.h
index 3466b36915..d41c87d5e4 100644
--- a/lib/ios/AirGoogleMaps/AIRGMSPolygon.h
+++ b/lib/ios/AirGoogleMaps/AIRGMSPolygon.h
@@ -6,7 +6,7 @@
//
#import
-#import "UIView+React.h"
+#import
@class AIRGoogleMapPolygon;
diff --git a/lib/ios/AirGoogleMaps/AIRGMSPolyline.h b/lib/ios/AirGoogleMaps/AIRGMSPolyline.h
new file mode 100644
index 0000000000..d7ee197831
--- /dev/null
+++ b/lib/ios/AirGoogleMaps/AIRGMSPolyline.h
@@ -0,0 +1,16 @@
+//
+// AIRGMSPolyline.h
+// AirMaps
+//
+// Created by Guilherme Pontes 04/05/2017.
+//
+
+#import
+#import
+
+@class AIRGoogleMapPolyline;
+
+@interface AIRGMSPolyline : GMSPolyline
+@property (nonatomic, strong) NSString *identifier;
+@property (nonatomic, copy) RCTBubblingEventBlock onPress;
+@end
diff --git a/lib/ios/AirGoogleMaps/AIRGMSPolyline.m b/lib/ios/AirGoogleMaps/AIRGMSPolyline.m
new file mode 100644
index 0000000000..86e7ac0555
--- /dev/null
+++ b/lib/ios/AirGoogleMaps/AIRGMSPolyline.m
@@ -0,0 +1,11 @@
+//
+// AIRGMSPolyline.m
+// AirMaps
+//
+// Created by Guilherme Pontes 04/05/2017.
+//
+
+#import "AIRGMSPolyline.h"
+
+@implementation AIRGMSPolyline
+@end
diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMap.h b/lib/ios/AirGoogleMaps/AIRGoogleMap.h
index 9e6ae8b799..d2a8e3e6f3 100644
--- a/lib/ios/AirGoogleMaps/AIRGoogleMap.h
+++ b/lib/ios/AirGoogleMaps/AIRGoogleMap.h
@@ -18,6 +18,7 @@
@property (nonatomic, assign) MKCoordinateRegion initialRegion;
@property (nonatomic, assign) MKCoordinateRegion region;
@property (nonatomic, assign) NSString *customMapStyleString;
+@property (nonatomic, copy) RCTBubblingEventBlock onMapReady;
@property (nonatomic, copy) RCTBubblingEventBlock onPress;
@property (nonatomic, copy) RCTBubblingEventBlock onLongPress;
@property (nonatomic, copy) RCTBubblingEventBlock onMarkerPress;
@@ -39,8 +40,11 @@
@property (nonatomic, assign) BOOL pitchEnabled;
@property (nonatomic, assign) BOOL showsUserLocation;
@property (nonatomic, assign) BOOL showsMyLocationButton;
+@property (nonatomic, assign) BOOL showsIndoorLevelPicker;
+- (void)didFinishTileRendering;
- (BOOL)didTapMarker:(GMSMarker *)marker;
+- (void)didTapPolyline:(GMSPolyline *)polyline;
- (void)didTapPolygon:(GMSPolygon *)polygon;
- (void)didTapAtCoordinate:(CLLocationCoordinate2D)coordinate;
- (void)didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate;
diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMap.m b/lib/ios/AirGoogleMaps/AIRGoogleMap.m
index ca15da04e5..f102385315 100644
--- a/lib/ios/AirGoogleMaps/AIRGoogleMap.m
+++ b/lib/ios/AirGoogleMaps/AIRGoogleMap.m
@@ -155,6 +155,10 @@ - (void)setRegion:(MKCoordinateRegion)region {
self.camera = [AIRGoogleMap makeGMSCameraPositionFromMap:self andMKCoordinateRegion:region];
}
+- (void)didFinishTileRendering {
+ if (self.onMapReady) self.onMapReady(@{});
+}
+
- (BOOL)didTapMarker:(GMSMarker *)marker {
AIRGMSMarker *airMarker = (AIRGMSMarker *)marker;
@@ -170,6 +174,16 @@ - (BOOL)didTapMarker:(GMSMarker *)marker {
return NO;
}
+- (void)didTapPolyline:(GMSOverlay *)polyline {
+ AIRGMSPolyline *airPolyline = (AIRGMSPolyline *)polyline;
+
+ id event = @{@"action": @"polyline-press",
+ @"id": airPolyline.identifier ?: @"unknown",
+ };
+
+ if (airPolyline.onPress) airPolyline.onPress(event);
+}
+
- (void)didTapPolygon:(GMSOverlay *)polygon {
AIRGMSPolygon *airPolygon = (AIRGMSPolygon *)polygon;
@@ -290,6 +304,21 @@ - (BOOL)showsMyLocationButton {
return self.settings.myLocationButton;
}
+- (void)setMinZoomLevel:(CGFloat)minZoomLevel {
+ [self setMinZoom:minZoomLevel maxZoom:self.maxZoom ];
+}
+
+- (void)setMaxZoomLevel:(CGFloat)maxZoomLevel {
+ [self setMinZoom:self.minZoom maxZoom:maxZoomLevel ];
+}
+
+- (void)setShowsIndoorLevelPicker:(BOOL)showsIndoorLevelPicker {
+ self.settings.indoorPicker = showsIndoorLevelPicker;
+}
+
+- (BOOL)showsIndoorLevelPicker {
+ return self.settings.indoorPicker;
+}
+ (MKCoordinateRegion) makeGMSCameraPositionFromMap:(GMSMapView *)map andGMSCameraPosition:(GMSCameraPosition *)position {
// solution from here: http://stackoverflow.com/a/16587735/1102215
diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m b/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m
index 785d58d4d4..12d63e1042 100644
--- a/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m
+++ b/lib/ios/AirGoogleMaps/AIRGoogleMapManager.m
@@ -26,6 +26,7 @@
#import "RCTConvert+AirMap.h"
#import
+#import
static NSString *const RCTMapViewKey = @"MapView";
@@ -57,7 +58,9 @@ - (UIView *)view
RCT_EXPORT_VIEW_PROPERTY(pitchEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(showsUserLocation, BOOL)
RCT_EXPORT_VIEW_PROPERTY(showsMyLocationButton, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(showsIndoorLevelPicker, BOOL)
RCT_EXPORT_VIEW_PROPERTY(customMapStyleString, NSString)
+RCT_EXPORT_VIEW_PROPERTY(onMapReady, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLongPress, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
@@ -65,6 +68,8 @@ - (UIView *)view
RCT_EXPORT_VIEW_PROPERTY(onRegionChange, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onRegionChangeComplete, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(mapType, GMSMapViewType)
+RCT_EXPORT_VIEW_PROPERTY(minZoomLevel, CGFloat)
+RCT_EXPORT_VIEW_PROPERTY(maxZoomLevel, CGFloat)
RCT_EXPORT_METHOD(animateToRegion:(nonnull NSNumber *)reactTag
withRegion:(MKCoordinateRegion)region
@@ -75,10 +80,67 @@ - (UIView *)view
if (![view isKindOfClass:[AIRGoogleMap class]]) {
RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view);
} else {
- [AIRGoogleMap animateWithDuration:duration/1000 animations:^{
- GMSCameraPosition* camera = [AIRGoogleMap makeGMSCameraPositionFromMap:(AIRGoogleMap *)view andMKCoordinateRegion:region];
- [(AIRGoogleMap *)view animateToCameraPosition:camera];
- }];
+ // Core Animation must be used to control the animation's duration
+ // See http://stackoverflow.com/a/15663039/171744
+ [CATransaction begin];
+ [CATransaction setAnimationDuration:duration/1000];
+ AIRGoogleMap *mapView = (AIRGoogleMap *)view;
+ GMSCameraPosition *camera = [AIRGoogleMap makeGMSCameraPositionFromMap:mapView andMKCoordinateRegion:region];
+ [mapView animateToCameraPosition:camera];
+ [CATransaction commit];
+ }
+ }];
+}
+
+RCT_EXPORT_METHOD(animateToCoordinate:(nonnull NSNumber *)reactTag
+ withRegion:(CLLocationCoordinate2D)latlng
+ withDuration:(CGFloat)duration)
+{
+ [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
+ id view = viewRegistry[reactTag];
+ if (![view isKindOfClass:[AIRGoogleMap class]]) {
+ RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view);
+ } else {
+ [CATransaction begin];
+ [CATransaction setAnimationDuration:duration/1000];
+ [(AIRGoogleMap *)view animateToLocation:latlng];
+ [CATransaction commit];
+ }
+ }];
+}
+
+RCT_EXPORT_METHOD(animateToViewingAngle:(nonnull NSNumber *)reactTag
+ withAngle:(double)angle
+ withDuration:(CGFloat)duration)
+{
+ [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
+ id view = viewRegistry[reactTag];
+ if (![view isKindOfClass:[AIRGoogleMap class]]) {
+ RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view);
+ } else {
+ [CATransaction begin];
+ [CATransaction setAnimationDuration:duration/1000];
+ AIRGoogleMap *mapView = (AIRGoogleMap *)view;
+ [mapView animateToViewingAngle:angle];
+ [CATransaction commit];
+ }
+ }];
+}
+
+RCT_EXPORT_METHOD(animateToBearing:(nonnull NSNumber *)reactTag
+ withBearing:(CGFloat)bearing
+ withDuration:(CGFloat)duration)
+{
+ [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
+ id view = viewRegistry[reactTag];
+ if (![view isKindOfClass:[AIRGoogleMap class]]) {
+ RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view);
+ } else {
+ [CATransaction begin];
+ [CATransaction setAnimationDuration:duration/1000];
+ AIRGoogleMap *mapView = (AIRGoogleMap *)view;
+ [mapView animateToBearing:bearing];
+ [CATransaction commit];
}
}];
}
@@ -197,6 +259,14 @@ - (UIView *)view
}];
}
+- (NSDictionary *)constantsToExport {
+ return @{ @"legalNotice": [GMSServices openSourceLicenseInfo] };
+}
+
+- (void)mapViewDidFinishTileRendering:(GMSMapView *)mapView {
+ AIRGoogleMap *googleMapView = (AIRGoogleMap *)mapView;
+ [googleMapView didFinishTileRendering];
+}
- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker {
AIRGoogleMap *googleMapView = (AIRGoogleMap *)mapView;
diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapMarker.h b/lib/ios/AirGoogleMaps/AIRGoogleMapMarker.h
index 36190e7cb6..298884f7d2 100644
--- a/lib/ios/AirGoogleMaps/AIRGoogleMapMarker.h
+++ b/lib/ios/AirGoogleMaps/AIRGoogleMapMarker.h
@@ -17,6 +17,7 @@
@property (nonatomic, strong) AIRGoogleMapCallout *calloutView;
@property (nonatomic, strong) NSString *identifier;
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
+@property (nonatomic, assign) CLLocationDegrees rotation;
@property (nonatomic, strong) AIRGMSMarker* realMarker;
@property (nonatomic, copy) RCTBubblingEventBlock onPress;
@property (nonatomic, copy) RCTDirectEventBlock onDragStart;
diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapMarker.m b/lib/ios/AirGoogleMaps/AIRGoogleMapMarker.m
index 2c6080e9ef..c41be55bbb 100644
--- a/lib/ios/AirGoogleMaps/AIRGoogleMapMarker.m
+++ b/lib/ios/AirGoogleMaps/AIRGoogleMapMarker.m
@@ -157,6 +157,14 @@ - (CLLocationCoordinate2D)coordinate {
return _realMarker.position;
}
+- (void)setRotation:(CLLocationDegrees)rotation {
+ _realMarker.rotation = rotation;
+}
+
+- (CLLocationDegrees)rotation {
+ return _realMarker.rotation;
+}
+
- (void)setIdentifier:(NSString *)identifier {
_realMarker.identifier = identifier;
}
diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m b/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m
index 8be4ba53e9..b5572bf9d2 100644
--- a/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m
+++ b/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m
@@ -28,6 +28,7 @@ - (UIView *)view
RCT_EXPORT_VIEW_PROPERTY(identifier, NSString)
RCT_EXPORT_VIEW_PROPERTY(coordinate, CLLocationCoordinate2D)
+RCT_EXPORT_VIEW_PROPERTY(rotation, CLLocationDegrees)
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
RCT_REMAP_VIEW_PROPERTY(image, imageSrc, NSString)
RCT_EXPORT_VIEW_PROPERTY(title, NSString)
diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapPolyline.h b/lib/ios/AirGoogleMaps/AIRGoogleMapPolyline.h
index b127567a52..adebc40d6c 100644
--- a/lib/ios/AirGoogleMaps/AIRGoogleMapPolyline.h
+++ b/lib/ios/AirGoogleMaps/AIRGoogleMapPolyline.h
@@ -5,18 +5,25 @@
//
#import
#import
+#import
+#import "AIRGMSPolyline.h"
#import "AIRMapCoordinate.h"
#import "AIRGoogleMapMarker.h"
@interface AIRGoogleMapPolyline : UIView
-@property (nonatomic, strong) GMSPolyline* polyline;
+@property (nonatomic, weak) RCTBridge *bridge;
+@property (nonatomic, strong) NSString *identifier;
+@property (nonatomic, strong) AIRGMSPolyline *polyline;
@property (nonatomic, strong) NSArray *coordinates;
+@property (nonatomic, copy) RCTBubblingEventBlock onPress;
+
@property (nonatomic, strong) UIColor *strokeColor;
@property (nonatomic, assign) double strokeWidth;
@property (nonatomic, assign) UIColor *fillColor;
@property (nonatomic, assign) BOOL geodesic;
@property (nonatomic, assign) NSString *title;
@property (nonatomic, assign) int zIndex;
+@property (nonatomic, assign) BOOL tappable;
@end
diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapPolyline.m b/lib/ios/AirGoogleMaps/AIRGoogleMapPolyline.m
index 009d90bfba..881f17be68 100644
--- a/lib/ios/AirGoogleMaps/AIRGoogleMapPolyline.m
+++ b/lib/ios/AirGoogleMaps/AIRGoogleMapPolyline.m
@@ -5,6 +5,7 @@
//
#import
#import "AIRGoogleMapPolyline.h"
+#import "AIRGMSPolyline.h"
#import "AIRMapCoordinate.h"
#import "AIRGoogleMapMarker.h"
#import "AIRGoogleMapMarkerManager.h"
@@ -16,7 +17,7 @@ @implementation AIRGoogleMapPolyline
- (instancetype)init
{
if (self = [super init]) {
- _polyline = [[GMSPolyline alloc] init];
+ _polyline = [[AIRGMSPolyline alloc] init];
}
return self;
}
@@ -24,13 +25,13 @@ - (instancetype)init
-(void)setCoordinates:(NSArray *)coordinates
{
_coordinates = coordinates;
-
+
GMSMutablePath *path = [GMSMutablePath path];
for(int i = 0; i < coordinates.count; i++)
{
[path addCoordinate:coordinates[i].coordinate];
}
-
+
_polyline.path = path;
}
@@ -70,4 +71,14 @@ -(void) setZIndex:(int)zIndex
_polyline.zIndex = zIndex;
}
+-(void)setTappable:(BOOL)tappable
+{
+ _tappable = tappable;
+ _polyline.tappable = tappable;
+}
+
+- (void)setOnPress:(RCTBubblingEventBlock)onPress {
+ _polyline.onPress = onPress;
+}
+
@end
diff --git a/lib/ios/AirGoogleMaps/AIRGoogleMapPolylineManager.m b/lib/ios/AirGoogleMaps/AIRGoogleMapPolylineManager.m
index acad1631bd..a7515c207d 100644
--- a/lib/ios/AirGoogleMaps/AIRGoogleMapPolylineManager.m
+++ b/lib/ios/AirGoogleMaps/AIRGoogleMapPolylineManager.m
@@ -26,6 +26,7 @@ @implementation AIRGoogleMapPolylineManager
- (UIView *)view
{
AIRGoogleMapPolyline *polyline = [AIRGoogleMapPolyline new];
+ polyline.bridge = self.bridge;
return polyline;
}
@@ -35,5 +36,7 @@ - (UIView *)view
RCT_EXPORT_VIEW_PROPERTY(strokeWidth, double)
RCT_EXPORT_VIEW_PROPERTY(geodesic, BOOL)
RCT_EXPORT_VIEW_PROPERTY(zIndex, int)
+RCT_EXPORT_VIEW_PROPERTY(tappable, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
@end
diff --git a/lib/ios/AirGoogleMaps/RCTConvert+GMSMapViewType.m b/lib/ios/AirGoogleMaps/RCTConvert+GMSMapViewType.m
index bd339f30e1..cfceba5590 100644
--- a/lib/ios/AirGoogleMaps/RCTConvert+GMSMapViewType.m
+++ b/lib/ios/AirGoogleMaps/RCTConvert+GMSMapViewType.m
@@ -13,6 +13,7 @@ @implementation RCTConvert (GMSMapViewType)
(
@{
@"standard": @(kGMSTypeNormal),
+ @"satellite": @(kGMSTypeSatellite),
@"hybrid": @(kGMSTypeHybrid),
@"terrain": @(kGMSTypeTerrain),
@"none": @(kGMSTypeNone)
diff --git a/lib/ios/AirMaps.xcodeproj/project.pbxproj b/lib/ios/AirMaps.xcodeproj/project.pbxproj
index 39b1256e20..ac641aff72 100644
--- a/lib/ios/AirMaps.xcodeproj/project.pbxproj
+++ b/lib/ios/AirMaps.xcodeproj/project.pbxproj
@@ -21,7 +21,7 @@
1125B2E51C4AD3DA007D0023 /* AIRMapPolyline.m in Sources */ = {isa = PBXBuildFile; fileRef = 1125B2D41C4AD3DA007D0023 /* AIRMapPolyline.m */; };
1125B2E61C4AD3DA007D0023 /* AIRMapPolylineManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1125B2D61C4AD3DA007D0023 /* AIRMapPolylineManager.m */; };
1125B2F21C4AD445007D0023 /* SMCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1125B2F11C4AD445007D0023 /* SMCalloutView.m */; };
- 19DABC7F1E7C9D3C00F41150 /* RCTConvert+MapKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 19DABC7E1E7C9D3C00F41150 /* RCTConvert+MapKit.m */; };
+ 19DABC7F1E7C9D3C00F41150 /* RCTConvert+AirMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 19DABC7E1E7C9D3C00F41150 /* RCTConvert+AirMap.m */; };
DA6C26381C9E2AFE0035349F /* AIRMapUrlTile.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6C26371C9E2AFE0035349F /* AIRMapUrlTile.m */; };
DA6C263E1C9E324A0035349F /* AIRMapUrlTileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6C263D1C9E324A0035349F /* AIRMapUrlTileManager.m */; };
/* End PBXBuildFile section */
@@ -68,8 +68,8 @@
1125B2F01C4AD445007D0023 /* SMCalloutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SMCalloutView.h; path = AirMaps/Callout/SMCalloutView.h; sourceTree = SOURCE_ROOT; };
1125B2F11C4AD445007D0023 /* SMCalloutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SMCalloutView.m; path = AirMaps/Callout/SMCalloutView.m; sourceTree = SOURCE_ROOT; };
11FA5C511C4A1296003AC2EE /* libAirMaps.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libAirMaps.a; sourceTree = BUILT_PRODUCTS_DIR; };
- 19DABC7D1E7C9D3C00F41150 /* RCTConvert+MapKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+MapKit.h"; sourceTree = ""; };
- 19DABC7E1E7C9D3C00F41150 /* RCTConvert+MapKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+MapKit.m"; sourceTree = ""; };
+ 19DABC7D1E7C9D3C00F41150 /* RCTConvert+AirMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+AirMap.h"; sourceTree = ""; };
+ 19DABC7E1E7C9D3C00F41150 /* RCTConvert+AirMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+AirMap.m"; sourceTree = ""; };
DA6C26361C9E2AFE0035349F /* AIRMapUrlTile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AIRMapUrlTile.h; sourceTree = ""; };
DA6C26371C9E2AFE0035349F /* AIRMapUrlTile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AIRMapUrlTile.m; sourceTree = ""; };
DA6C263C1C9E324A0035349F /* AIRMapUrlTileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AIRMapUrlTileManager.h; sourceTree = ""; };
@@ -134,8 +134,8 @@
1125B2D61C4AD3DA007D0023 /* AIRMapPolylineManager.m */,
1125B2F01C4AD445007D0023 /* SMCalloutView.h */,
1125B2F11C4AD445007D0023 /* SMCalloutView.m */,
- 19DABC7D1E7C9D3C00F41150 /* RCTConvert+MapKit.h */,
- 19DABC7E1E7C9D3C00F41150 /* RCTConvert+MapKit.m */,
+ 19DABC7D1E7C9D3C00F41150 /* RCTConvert+AirMap.h */,
+ 19DABC7E1E7C9D3C00F41150 /* RCTConvert+AirMap.m */,
DA6C26361C9E2AFE0035349F /* AIRMapUrlTile.h */,
DA6C26371C9E2AFE0035349F /* AIRMapUrlTile.m */,
DA6C263C1C9E324A0035349F /* AIRMapUrlTileManager.h */,
@@ -206,7 +206,7 @@
1125B2E01C4AD3DA007D0023 /* AIRMapManager.m in Sources */,
1125B2E61C4AD3DA007D0023 /* AIRMapPolylineManager.m in Sources */,
1125B2DD1C4AD3DA007D0023 /* AIRMapCircle.m in Sources */,
- 19DABC7F1E7C9D3C00F41150 /* RCTConvert+MapKit.m in Sources */,
+ 19DABC7F1E7C9D3C00F41150 /* RCTConvert+AirMap.m in Sources */,
1125B2E51C4AD3DA007D0023 /* AIRMapPolyline.m in Sources */,
DA6C263E1C9E324A0035349F /* AIRMapUrlTileManager.m in Sources */,
1125B2DA1C4AD3DA007D0023 /* AIRMap.m in Sources */,
diff --git a/lib/ios/AirMaps/AIRMap.h b/lib/ios/AirMaps/AIRMap.h
index 18c9a0bc05..1d5f92cdcc 100644
--- a/lib/ios/AirMaps/AIRMap.h
+++ b/lib/ios/AirMaps/AIRMap.h
@@ -17,6 +17,7 @@
extern const CLLocationDegrees AIRMapDefaultSpan;
extern const NSTimeInterval AIRMapRegionChangeObserveInterval;
extern const CGFloat AIRMapZoomBoundBuffer;
+extern const NSInteger AIRMapMaxZoomLevel;
@interface AIRMap: MKMapView
@@ -37,6 +38,8 @@ extern const CGFloat AIRMapZoomBoundBuffer;
@property (nonatomic, assign) UIEdgeInsets legalLabelInsets;
@property (nonatomic, strong) NSTimer *regionChangeObserveTimer;
@property (nonatomic, assign) MKCoordinateRegion initialRegion;
+@property (nonatomic, assign) CGFloat minZoomLevel;
+@property (nonatomic, assign) CGFloat maxZoomLevel;
@property (nonatomic, assign) CLLocationCoordinate2D pendingCenter;
@property (nonatomic, assign) MKCoordinateSpan pendingSpan;
@@ -44,6 +47,7 @@ extern const CGFloat AIRMapZoomBoundBuffer;
@property (nonatomic, assign) BOOL ignoreRegionChanges;
+@property (nonatomic, copy) RCTBubblingEventBlock onMapReady;
@property (nonatomic, copy) RCTBubblingEventBlock onChange;
@property (nonatomic, copy) RCTBubblingEventBlock onPress;
@property (nonatomic, copy) RCTBubblingEventBlock onPanDrag;
diff --git a/lib/ios/AirMaps/AIRMap.m b/lib/ios/AirMaps/AIRMap.m
index a9db64ab45..647be4b8a5 100644
--- a/lib/ios/AirMaps/AIRMap.m
+++ b/lib/ios/AirMaps/AIRMap.m
@@ -21,6 +21,7 @@
const CLLocationDegrees AIRMapDefaultSpan = 0.005;
const NSTimeInterval AIRMapRegionChangeObserveInterval = 0.1;
const CGFloat AIRMapZoomBoundBuffer = 0.01;
+const NSInteger AIRMapMaxZoomLevel = 20;
@interface MKMapView (UIGestureRecognizer)
@@ -79,6 +80,9 @@ - (instancetype)init
// be identical to the built-in callout view (which has a private API)
self.calloutView = [SMCalloutView platformCalloutView];
self.calloutView.delegate = self;
+
+ self.minZoomLevel = 0;
+ self.maxZoomLevel = AIRMapMaxZoomLevel;
}
return self;
}
diff --git a/lib/ios/AirMaps/AIRMapManager.h b/lib/ios/AirMaps/AIRMapManager.h
index cc9a8c75b5..1d73b405e7 100644
--- a/lib/ios/AirMaps/AIRMapManager.h
+++ b/lib/ios/AirMaps/AIRMapManager.h
@@ -8,7 +8,22 @@
*/
#import
+#import "AIRMap.h"
+
+#define MERCATOR_RADIUS 85445659.44705395
+#define MERCATOR_OFFSET 268435456
@interface AIRMapManager : RCTViewManager
+
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
+ zoomLevel:(double)zoomLevel
+ animated:(BOOL)animated
+ mapView:(AIRMap *)mapView;
+
+- (MKCoordinateRegion)coordinateRegionWithMapView:(AIRMap *)mapView
+ centerCoordinate:(CLLocationCoordinate2D)centerCoordinate
+ andZoomLevel:(double)zoomLevel;
+- (double) zoomLevel:(AIRMap *)mapView;
+
@end
diff --git a/lib/ios/AirMaps/AIRMapManager.m b/lib/ios/AirMaps/AIRMapManager.m
index 619142bd66..4838343693 100644
--- a/lib/ios/AirMaps/AIRMapManager.m
+++ b/lib/ios/AirMaps/AIRMapManager.m
@@ -85,6 +85,7 @@ - (UIView *)view
RCT_EXPORT_VIEW_PROPERTY(minDelta, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(legalLabelInsets, UIEdgeInsets)
RCT_EXPORT_VIEW_PROPERTY(mapType, MKMapType)
+RCT_EXPORT_VIEW_PROPERTY(onMapReady, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPanDrag, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
@@ -96,7 +97,20 @@ - (UIView *)view
RCT_EXPORT_VIEW_PROPERTY(onMarkerDrag, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onMarkerDragEnd, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onCalloutPress, RCTDirectEventBlock)
-RCT_EXPORT_VIEW_PROPERTY(initialRegion, MKCoordinateRegion)
+RCT_CUSTOM_VIEW_PROPERTY(initialRegion, MKCoordinateRegion, AIRMap)
+{
+ if (json == nil) return;
+
+ // don't emit region change events when we are setting the initialRegion
+ BOOL originalIgnore = view.ignoreRegionChanges;
+ view.ignoreRegionChanges = YES;
+ [view setInitialRegion:[RCTConvert MKCoordinateRegion:json]];
+ view.ignoreRegionChanges = originalIgnore;
+}
+
+RCT_EXPORT_VIEW_PROPERTY(minZoomLevel, CGFloat)
+RCT_EXPORT_VIEW_PROPERTY(maxZoomLevel, CGFloat)
+
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, AIRMap)
{
@@ -148,6 +162,48 @@ - (UIView *)view
}];
}
+RCT_EXPORT_METHOD(animateToViewingAngle:(nonnull NSNumber *)reactTag
+ withAngle:(double)angle
+ withDuration:(CGFloat)duration)
+{
+ [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
+ id view = viewRegistry[reactTag];
+ if (![view isKindOfClass:[AIRMap class]]) {
+ RCTLogError(@"Invalid view returned from registry, expecting AIRMap, got: %@", view);
+ } else {
+ AIRMap *mapView = (AIRMap *)view;
+
+ MKMapCamera *mapCamera = [[mapView camera] copy];
+ [mapCamera setPitch:angle];
+
+ [AIRMap animateWithDuration:duration/1000 animations:^{
+ [mapView setCamera:mapCamera animated:YES];
+ }];
+ }
+ }];
+}
+
+RCT_EXPORT_METHOD(animateToBearing:(nonnull NSNumber *)reactTag
+ withBearing:(CGFloat)bearing
+ withDuration:(CGFloat)duration)
+{
+ [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
+ id view = viewRegistry[reactTag];
+ if (![view isKindOfClass:[AIRMap class]]) {
+ RCTLogError(@"Invalid view returned from registry, expecting AIRMap, got: %@", view);
+ } else {
+ AIRMap *mapView = (AIRMap *)view;
+
+ MKMapCamera *mapCamera = [[mapView camera] copy];
+ [mapCamera setHeading:bearing];
+
+ [AIRMap animateWithDuration:duration/1000 animations:^{
+ [mapView setCamera:mapCamera animated:YES];
+ }];
+ }
+ }];
+}
+
RCT_EXPORT_METHOD(fitToElements:(nonnull NSNumber *)reactTag
animated:(BOOL)animated)
{
@@ -478,6 +534,8 @@ - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id mapView.maxZoomLevel) {
+ [self setCenterCoordinate:[mapView centerCoordinate] zoomLevel:mapView.maxZoomLevel animated:TRUE mapView:mapView];
+ }
+
// Don't send region did change events until map has
// started rendering, as these won't represent the final location
if (mapView.hasStartedRendering) {
@@ -651,6 +717,8 @@ - (void)mapViewDidFinishRenderingMap:(AIRMap *)mapView fullyRendered:(BOOL)fully
{
[mapView finishLoading];
[mapView cacheViewIfNeeded];
+
+ mapView.onMapReady(@{});
}
#pragma mark Private
@@ -665,6 +733,7 @@ - (void)_regionChanged:(AIRMap *)mapView
BOOL needZoom = NO;
CGFloat newLongitudeDelta = 0.0f;
MKCoordinateRegion region = mapView.region;
+ CGFloat zoomLevel = [self zoomLevel:mapView];
// On iOS 7, it's possible that we observe invalid locations during initialization of the map.
// Filter those out.
if (!CLLocationCoordinate2DIsValid(region.center)) {
@@ -759,4 +828,161 @@ - (double)metersFromPixel:(NSUInteger)px atPoint:(CGPoint)pt forMap:(AIRMap *)ma
return MKMetersBetweenMapPoints(MKMapPointForCoordinate(coordA), MKMapPointForCoordinate(coordB));
}
++ (double)longitudeToPixelSpaceX:(double)longitude
+{
+ return round(MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * M_PI / 180.0);
+}
+
++ (double)latitudeToPixelSpaceY:(double)latitude
+{
+ if (latitude == 90.0) {
+ return 0;
+ } else if (latitude == -90.0) {
+ return MERCATOR_OFFSET * 2;
+ } else {
+ return round(MERCATOR_OFFSET - MERCATOR_RADIUS * logf((1 + sinf(latitude * M_PI / 180.0)) / (1 - sinf(latitude * M_PI / 180.0))) / 2.0);
+ }
+}
+
++ (double)pixelSpaceXToLongitude:(double)pixelX
+{
+ return ((round(pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / M_PI;
+}
+
++ (double)pixelSpaceYToLatitude:(double)pixelY
+{
+ return (M_PI / 2.0 - 2.0 * atan(exp((round(pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / M_PI;
+}
+
+#pragma mark -
+#pragma mark Helper methods
+
+- (MKCoordinateSpan)coordinateSpanWithMapView:(AIRMap *)mapView
+ centerCoordinate:(CLLocationCoordinate2D)centerCoordinate
+ andZoomLevel:(double)zoomLevel
+{
+ // convert center coordiate to pixel space
+ double centerPixelX = [AIRMapManager longitudeToPixelSpaceX:centerCoordinate.longitude];
+ double centerPixelY = [AIRMapManager latitudeToPixelSpaceY:centerCoordinate.latitude];
+
+ // determine the scale value from the zoom level
+ double zoomExponent = AIRMapMaxZoomLevel - zoomLevel;
+ double zoomScale = pow(2, zoomExponent);
+
+ // scale the map’s size in pixel space
+ CGSize mapSizeInPixels = mapView.bounds.size;
+ double scaledMapWidth = mapSizeInPixels.width * zoomScale;
+ double scaledMapHeight = mapSizeInPixels.height * zoomScale;
+
+ // figure out the position of the top-left pixel
+ double topLeftPixelX = centerPixelX - (scaledMapWidth / 2);
+ double topLeftPixelY = centerPixelY - (scaledMapHeight / 2);
+
+ // find delta between left and right longitudes
+ CLLocationDegrees minLng = [AIRMapManager pixelSpaceXToLongitude:topLeftPixelX];
+ CLLocationDegrees maxLng = [AIRMapManager pixelSpaceXToLongitude:topLeftPixelX + scaledMapWidth];
+ CLLocationDegrees longitudeDelta = maxLng - minLng;
+
+ // find delta between top and bottom latitudes
+ CLLocationDegrees minLat = [AIRMapManager pixelSpaceYToLatitude:topLeftPixelY];
+ CLLocationDegrees maxLat = [AIRMapManager pixelSpaceYToLatitude:topLeftPixelY + scaledMapHeight];
+ CLLocationDegrees latitudeDelta = -1 * (maxLat - minLat);
+
+ // create and return the lat/lng span
+ MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
+ return span;
+}
+
+#pragma mark -
+#pragma mark Public methods
+
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
+ zoomLevel:(double)zoomLevel
+ animated:(BOOL)animated
+ mapView:(AIRMap *)mapView
+{
+ // clamp large numbers to 28
+ zoomLevel = MIN(zoomLevel, AIRMapMaxZoomLevel);
+
+ // use the zoom level to compute the region
+ MKCoordinateSpan span = [self coordinateSpanWithMapView:mapView centerCoordinate:centerCoordinate andZoomLevel:zoomLevel];
+ MKCoordinateRegion region = MKCoordinateRegionMake(centerCoordinate, span);
+
+ // set the region like normal
+ [mapView setRegion:region animated:animated];
+}
+
+//KMapView cannot display tiles that cross the pole (as these would involve wrapping the map from top to bottom, something that a Mercator projection just cannot do).
+-(MKCoordinateRegion)coordinateRegionWithMapView:(AIRMap *)mapView
+ centerCoordinate:(CLLocationCoordinate2D)centerCoordinate
+ andZoomLevel:(double)zoomLevel
+{
+ // clamp lat/long values to appropriate ranges
+ centerCoordinate.latitude = MIN(MAX(-90.0, centerCoordinate.latitude), 90.0);
+ centerCoordinate.longitude = fmod(centerCoordinate.longitude, 180.0);
+
+ // convert center coordiate to pixel space
+ double centerPixelX = [AIRMapManager longitudeToPixelSpaceX:centerCoordinate.longitude];
+ double centerPixelY = [AIRMapManager latitudeToPixelSpaceY:centerCoordinate.latitude];
+
+ // determine the scale value from the zoom level
+ double zoomExponent = AIRMapMaxZoomLevel - zoomLevel;
+ double zoomScale = pow(2, zoomExponent);
+
+ // scale the map’s size in pixel space
+ CGSize mapSizeInPixels = mapView.bounds.size;
+ double scaledMapWidth = mapSizeInPixels.width * zoomScale;
+ double scaledMapHeight = mapSizeInPixels.height * zoomScale;
+
+ // figure out the position of the left pixel
+ double topLeftPixelX = centerPixelX - (scaledMapWidth / 2);
+
+ // find delta between left and right longitudes
+ CLLocationDegrees minLng = [AIRMapManager pixelSpaceXToLongitude:topLeftPixelX];
+ CLLocationDegrees maxLng = [AIRMapManager pixelSpaceXToLongitude:topLeftPixelX + scaledMapWidth];
+ CLLocationDegrees longitudeDelta = maxLng - minLng;
+
+ // if we’re at a pole then calculate the distance from the pole towards the equator
+ // as MKMapView doesn’t like drawing boxes over the poles
+ double topPixelY = centerPixelY - (scaledMapHeight / 2);
+ double bottomPixelY = centerPixelY + (scaledMapHeight / 2);
+ BOOL adjustedCenterPoint = NO;
+ if (topPixelY > MERCATOR_OFFSET * 2) {
+ topPixelY = centerPixelY - scaledMapHeight;
+ bottomPixelY = MERCATOR_OFFSET * 2;
+ adjustedCenterPoint = YES;
+ }
+
+ // find delta between top and bottom latitudes
+ CLLocationDegrees minLat = [AIRMapManager pixelSpaceYToLatitude:topPixelY];
+ CLLocationDegrees maxLat = [AIRMapManager pixelSpaceYToLatitude:bottomPixelY];
+ CLLocationDegrees latitudeDelta = -1 * (maxLat - minLat);
+
+ // create and return the lat/lng span
+ MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
+ MKCoordinateRegion region = MKCoordinateRegionMake(centerCoordinate, span);
+ // once again, MKMapView doesn’t like drawing boxes over the poles
+ // so adjust the center coordinate to the center of the resulting region
+ if (adjustedCenterPoint) {
+ region.center.latitude = [AIRMapManager pixelSpaceYToLatitude:((bottomPixelY + topPixelY) / 2.0)];
+ }
+
+ return region;
+}
+
+- (double) zoomLevel:(AIRMap *)mapView {
+ MKCoordinateRegion region = mapView.region;
+
+ double centerPixelX = [AIRMapManager longitudeToPixelSpaceX: region.center.longitude];
+ double topLeftPixelX = [AIRMapManager longitudeToPixelSpaceX: region.center.longitude - region.span.longitudeDelta / 2];
+
+ double scaledMapWidth = (centerPixelX - topLeftPixelX) * 2;
+ CGSize mapSizeInPixels = mapView.bounds.size;
+ double zoomScale = scaledMapWidth / mapSizeInPixels.width;
+ double zoomExponent = log(zoomScale) / log(2);
+ double zoomLevel = AIRMapMaxZoomLevel - zoomExponent;
+
+ return zoomLevel;
+}
+
@end
diff --git a/package.json b/package.json
index 9bbc2e1dd4..66e8be298c 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"description": "React Native Mapview component for iOS + Android",
"main": "index.js",
"author": "Leland Richardson ",
- "version": "0.14.0",
+ "version": "0.17.0",
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"run:packager": "./node_modules/react-native/packager/packager.sh",
@@ -32,8 +32,9 @@
"mapkit"
],
"peerDependencies": {
- "react": ">=15.4.0",
- "react-native": ">=0.40"
+ "react": ">=15.4.0 || ^16.0.0-alpha",
+ "react-native": ">=0.40",
+ "prop-types": "^15.5.10"
},
"devDependencies": {
"babel-eslint": "^6.1.2",
@@ -48,12 +49,13 @@
"eslint-plugin-react": "^6.1.2",
"gitbook-cli": "^2.3.0",
"lodash": "^4.17.2",
- "react": "~15.4.1",
- "react-native": "^0.42.0"
+ "prop-types": "^15.5.10",
+ "react": "16.0.0-alpha.12",
+ "react-native": "^0.45.1"
},
"rnpm": {
"android": {
- "sourceDir": "./android"
+ "sourceDir": "./lib/android"
}
}
}